blob: b7368b26cdd4e9a4f84d5db88e7e7033facf493a [file] [log] [blame]
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
2/* The protocol implementation */
3
4/*
5 * (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
6 * (C) 2009-2012 by On-Waves
7 * All Rights Reserved
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU Affero General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Affero General Public License for more details.
18 *
19 * You should have received a copy of the GNU Affero General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 *
22 */
23
24#include <ctype.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <time.h>
28#include <limits.h>
29#include <unistd.h>
30#include <errno.h>
31
32#include <osmocom/core/msgb.h>
33#include <osmocom/core/talloc.h>
34#include <osmocom/core/select.h>
35
Philipp Maier87bd9be2017-08-22 16:35:41 +020036#include <osmocom/mgcp/mgcp.h>
Neels Hofmeyr67793542017-09-08 04:25:16 +020037#include <osmocom/mgcp/mgcp_common.h>
Philipp Maier993ea6b2020-08-04 18:26:50 +020038#include <osmocom/mgcp/osmux.h>
39#include <osmocom/mgcp/mgcp_network.h>
40#include <osmocom/mgcp/mgcp_protocol.h>
Philipp Maier87bd9be2017-08-22 16:35:41 +020041#include <osmocom/mgcp/mgcp_stat.h>
42#include <osmocom/mgcp/mgcp_msg.h>
Philipp Maier37d11c82018-02-01 14:38:12 +010043#include <osmocom/mgcp/mgcp_endp.h>
Philipp Maierc66ab2c2020-06-02 20:55:34 +020044#include <osmocom/mgcp/mgcp_trunk.h>
Philipp Maier8970c492017-10-11 13:33:42 +020045#include <osmocom/mgcp/mgcp_sdp.h>
Philipp Maierbc0346e2018-06-07 09:52:16 +020046#include <osmocom/mgcp/mgcp_codec.h>
Stefan Sperlingba25eab2018-10-30 14:32:31 +010047#include <osmocom/mgcp/mgcp_conn.h>
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020048
Philipp Maier39889e42021-08-04 17:42:57 +020049/* Contains the last successfully resolved endpoint name. This variable is used
50 * for the unit-tests to verify that the endpoint was correctly resolved. */
51static char debug_last_endpoint_name[MGCP_ENDPOINT_MAXLEN];
52
53/* Called from unit-tests only */
54char *mgcp_debug_get_last_endpoint_name(void)
55{
56 return debug_last_endpoint_name;
57}
58
Philipp Maierf486e742021-07-19 14:56:29 +020059/* A combination of LOGPENDP and LOGPTRUNK that automatically falls back to
60 * LOGPTRUNK when the endp parameter is NULL */
61#define LOGPEPTR(endp, trunk, cat, level, fmt, args...) \
62do { \
63 if (endp) \
64 LOGPENDP(endp, cat, level, fmt, ## args); \
65 else \
66 LOGPTRUNK(trunk, cat, level, fmt, ## args); \
67} while (0)
68
Philipp Maier8dc35972021-07-14 11:20:16 +020069/* Request data passed to the request handler */
70struct mgcp_request_data {
71 /* request name (e.g. "MDCX") */
72 char name[4+1];
73
74 /* parsing results from the MGCP header (trans id, endpoint name ...) */
75 struct mgcp_parse_data *pdata;
76
77 /* pointer to endpoint resource (may be NULL for wildcarded requests) */
78 struct mgcp_endpoint *endp;
79
80 /* pointer to trunk resource */
81 struct mgcp_trunk *trunk;
82
83 /* set to true when the request has been classified as wildcarded */
84 bool wildcarded;
85
86 /* contains cause code in case of problems during endp/trunk resolution */
87 int mgcp_cause;
88};
89
Philipp Maier33d97f72021-07-14 14:53:45 +020090/* Request handler specification, here we specify an array with function
91 * pointers to the various MGCP requests implemented below */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020092struct mgcp_request {
Philipp Maier33d97f72021-07-14 14:53:45 +020093 /* request name (e.g. "MDCX") */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020094 char *name;
Philipp Maier33d97f72021-07-14 14:53:45 +020095
96 /* function pointer to the request handler */
Philipp Maier8dc35972021-07-14 11:20:16 +020097 struct msgb *(*handle_request)(struct mgcp_request_data *data);
98
99 /* true if the request requires an endpoint, false if only a trunk
100 * is sufficient. (corner cases, e.g. wildcarded DLCX) */
101 bool require_endp;
Philipp Maier33d97f72021-07-14 14:53:45 +0200102
103 /* a human readable name that describes the request */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200104 char *debug_name;
105};
106
Philipp Maier8dc35972021-07-14 11:20:16 +0200107static struct msgb *handle_audit_endpoint(struct mgcp_request_data *data);
108static struct msgb *handle_create_con(struct mgcp_request_data *data);
109static struct msgb *handle_delete_con(struct mgcp_request_data *data);
110static struct msgb *handle_modify_con(struct mgcp_request_data *data);
111static struct msgb *handle_rsip(struct mgcp_request_data *data);
112static struct msgb *handle_noti_req(struct mgcp_request_data *data);
Philipp Maier33d97f72021-07-14 14:53:45 +0200113static const struct mgcp_request mgcp_requests[] = {
114 { .name = "AUEP",
115 .handle_request = handle_audit_endpoint,
Philipp Maier8dc35972021-07-14 11:20:16 +0200116 .debug_name = "AuditEndpoint",
117 .require_endp = true },
Philipp Maier33d97f72021-07-14 14:53:45 +0200118 { .name = "CRCX",
119 .handle_request = handle_create_con,
Philipp Maier8dc35972021-07-14 11:20:16 +0200120 .debug_name = "CreateConnection",
121 .require_endp = true },
Philipp Maier33d97f72021-07-14 14:53:45 +0200122 { .name = "DLCX",
123 .handle_request = handle_delete_con,
Philipp Maier8dc35972021-07-14 11:20:16 +0200124 .debug_name = "DeleteConnection",
Philipp Maierf486e742021-07-19 14:56:29 +0200125 .require_endp = false },
Philipp Maier33d97f72021-07-14 14:53:45 +0200126 { .name = "MDCX",
127 .handle_request = handle_modify_con,
Philipp Maier8dc35972021-07-14 11:20:16 +0200128 .debug_name = "ModifiyConnection",
129 .require_endp = true },
Philipp Maier33d97f72021-07-14 14:53:45 +0200130 { .name = "RQNT",
131 .handle_request = handle_noti_req,
Philipp Maier8dc35972021-07-14 11:20:16 +0200132 .debug_name = "NotificationRequest",
133 .require_endp = true },
Philipp Maier33d97f72021-07-14 14:53:45 +0200134
135 /* SPEC extension */
136 { .name = "RSIP",
137 .handle_request = handle_rsip,
Philipp Maier8dc35972021-07-14 11:20:16 +0200138 .debug_name = "ReSetInProgress",
139 .require_endp = true },
Philipp Maier33d97f72021-07-14 14:53:45 +0200140};
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200141
Philipp Maier87bd9be2017-08-22 16:35:41 +0200142/* Initalize transcoder */
143static int setup_rtp_processing(struct mgcp_endpoint *endp,
144 struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200145{
Philipp Maier87bd9be2017-08-22 16:35:41 +0200146 struct mgcp_config *cfg = endp->cfg;
147 struct mgcp_conn_rtp *conn_src = NULL;
148 struct mgcp_conn_rtp *conn_dst = conn;
149 struct mgcp_conn *_conn;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200150
Pau Espin Pedrolfa810e82019-05-06 18:54:10 +0200151 if (conn->type != MGCP_RTP_DEFAULT && !mgcp_conn_rtp_is_osmux(conn)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200152 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
153 "RTP-setup: Endpoint is not configured as RTP default, stopping here!\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200154 return 0;
155 }
156
Philipp Maier87bd9be2017-08-22 16:35:41 +0200157 if (conn->conn->mode == MGCP_CONN_LOOPBACK) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200158 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
159 "RTP-setup: Endpoint is in loopback mode, stopping here!\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +0200160 return 0;
161 }
162
163 /* Find the "sister" connection */
164 llist_for_each_entry(_conn, &endp->conns, entry) {
165 if (_conn->id != conn->conn->id) {
166 conn_src = &_conn->u.rtp;
167 break;
168 }
169 }
170
Philipp Maieracc10352018-07-19 18:07:57 +0200171 return cfg->setup_rtp_processing_cb(endp, conn_dst, conn_src);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200172}
173
Philipp Maier87bd9be2017-08-22 16:35:41 +0200174/* Helper function to allocate some memory for responses and retransmissions */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200175static struct msgb *mgcp_msgb_alloc(void)
176{
177 struct msgb *msg;
178 msg = msgb_alloc_headroom(4096, 128, "MGCP msg");
179 if (!msg)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200180 LOGP(DLMGCP, LOGL_ERROR, "Failed to msgb for MGCP data.\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200181
182 return msg;
183}
184
Philipp Maier87bd9be2017-08-22 16:35:41 +0200185/* Helper function for do_retransmission() and create_resp() */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200186static struct msgb *do_retransmission(const struct mgcp_endpoint *endp)
187{
188 struct msgb *msg = mgcp_msgb_alloc();
189 if (!msg)
190 return NULL;
191
192 msg->l2h = msgb_put(msg, strlen(endp->last_response));
193 memcpy(msg->l2h, endp->last_response, msgb_l2len(msg));
Philipp Maier87bd9be2017-08-22 16:35:41 +0200194 mgcp_disp_msg(msg->l2h, msgb_l2len(msg), "Retransmitted response");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200195 return msg;
196}
197
198static struct msgb *create_resp(struct mgcp_endpoint *endp, int code,
199 const char *txt, const char *msg,
200 const char *trans, const char *param,
201 const char *sdp)
202{
203 int len;
204 struct msgb *res;
205
206 res = mgcp_msgb_alloc();
207 if (!res)
208 return NULL;
209
Philipp Maier87bd9be2017-08-22 16:35:41 +0200210 len = snprintf((char *)res->data, 2048, "%d %s%s%s\r\n%s",
211 code, trans, txt, param ? param : "", sdp ? sdp : "");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200212 if (len < 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200213 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "Failed to sprintf MGCP response.\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200214 msgb_free(res);
215 return NULL;
216 }
217
218 res->l2h = msgb_put(res, len);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200219 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "Generated response: code=%d\n", code);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200220 mgcp_disp_msg(res->l2h, msgb_l2len(res), "Generated response");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200221
222 /*
223 * Remember the last transmission per endpoint.
224 */
225 if (endp) {
Philipp Maier14b27a82020-06-02 20:15:30 +0200226 struct mgcp_trunk *trunk = endp->trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200227 talloc_free(endp->last_response);
228 talloc_free(endp->last_trans);
Philipp Maier14b27a82020-06-02 20:15:30 +0200229 endp->last_trans = talloc_strdup(trunk->endpoints, trans);
230 endp->last_response = talloc_strndup(trunk->endpoints,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200231 (const char *)res->l2h,
232 msgb_l2len(res));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200233 }
234
235 return res;
236}
237
238static struct msgb *create_ok_resp_with_param(struct mgcp_endpoint *endp,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200239 int code, const char *msg,
240 const char *trans,
241 const char *param)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200242{
243 return create_resp(endp, code, " OK", msg, trans, param, NULL);
244}
245
246static struct msgb *create_ok_response(struct mgcp_endpoint *endp,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200247 int code, const char *msg,
248 const char *trans)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200249{
250 return create_ok_resp_with_param(endp, code, msg, trans, NULL);
251}
252
253static struct msgb *create_err_response(struct mgcp_endpoint *endp,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200254 int code, const char *msg,
255 const char *trans)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200256{
257 return create_resp(endp, code, " FAIL", msg, trans, NULL, NULL);
258}
259
Philipp Maier87bd9be2017-08-22 16:35:41 +0200260/* Format MGCP response string (with SDP attached) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200261static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200262 struct mgcp_conn_rtp *conn,
263 const char *msg,
Philipp Maier55295f72018-01-15 14:00:28 +0100264 const char *trans_id,
Philipp Maier41d59202021-07-20 15:49:00 +0200265 bool add_epname,
266 bool add_conn_id)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200267{
Pau Espin Pedrol71d42e72020-09-03 14:20:07 +0200268 /* cfg->local_ip allows overwritting the announced IP address with
269 * regards to the one we actually bind to. Useful in behind-NAT
270 * scenarios.
271 * TODO: we may want to define another local_ip_osmux var to
272 * us for OSMUX connections. Perhaps adding a new internal API to get it
273 * based on conn type.
274 */
275 const char *addr = endp->cfg->local_ip ? : conn->end.local_addr;
Philipp Maier8970c492017-10-11 13:33:42 +0200276 struct msgb *sdp;
277 int rc;
278 struct msgb *result;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200279
Philipp Maier8970c492017-10-11 13:33:42 +0200280 sdp = msgb_alloc_headroom(4096, 128, "sdp record");
281 if (!sdp)
282 return NULL;
283
Philipp Maier41d59202021-07-20 15:49:00 +0200284 /* Attach optional endpoint name */
285 if (add_epname) {
286 rc = msgb_printf(sdp, "Z: %s\r\n", endp->name);
287 if (rc < 0)
288 goto error;
289 }
290
291 /* Attach optional connection id */
292 if (add_conn_id) {
293 rc = msgb_printf(sdp, "I: %s\r\n", conn->conn->id);
Philipp Maier55295f72018-01-15 14:00:28 +0100294 if (rc < 0)
295 goto error;
296 }
297
Philipp Maier3cbfb8a2018-01-22 11:39:59 +0100298 /* Attach optional OSMUX parameters */
Pau Espin Pedrolc63f15a2019-05-10 16:52:08 +0200299 if (mgcp_conn_rtp_is_osmux(conn)) {
Pau Espin Pedrol5e8d7992019-04-24 19:56:43 +0200300 rc = msgb_printf(sdp, "X-Osmux: %u\r\n", conn->osmux.cid);
Philipp Maier3cbfb8a2018-01-22 11:39:59 +0100301 if (rc < 0)
302 goto error;
303 }
304
305 /* Attach line break to separate the parameters from the SDP block */
Philipp Maierc3cfae22018-01-22 12:03:03 +0100306 rc = msgb_printf(sdp, "\r\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200307
Philipp Maier8970c492017-10-11 13:33:42 +0200308 rc = mgcp_write_response_sdp(endp, conn, sdp, addr);
309 if (rc < 0)
310 goto error;
311 result = create_resp(endp, 200, " OK", msg, trans_id, NULL, (char*) sdp->data);
312 msgb_free(sdp);
313 return result;
314error:
315 msgb_free(sdp);
316 return NULL;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200317}
318
Philipp Maier87bd9be2017-08-22 16:35:41 +0200319/* Send out dummy packet to keep the connection open, if the connection is an
320 * osmux connection, send the dummy packet via OSMUX */
321static void send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200322{
Philipp Maier87bd9be2017-08-22 16:35:41 +0200323 if (conn->osmux.state != OSMUX_STATE_DISABLED)
324 osmux_send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200325 else
Philipp Maier87bd9be2017-08-22 16:35:41 +0200326 mgcp_send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200327}
328
Philipp Maier87bd9be2017-08-22 16:35:41 +0200329/* handle incoming messages:
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200330 * - this can be a command (four letters, space, transaction id)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200331 * - or a response (three numbers, space, transaction id) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200332struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
333{
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200334 struct rate_ctr_group *rate_ctrs = cfg->ratectr.mgcp_general_ctr_group;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200335 struct mgcp_parse_data pdata;
Philipp Maier8dc35972021-07-14 11:20:16 +0200336 struct mgcp_request_data rq;
Harald Weltee35eeae2017-12-28 13:47:37 +0100337 int rc, i, code, handled = 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200338 struct msgb *resp = NULL;
339 char *data;
340
Philipp Maier39889e42021-08-04 17:42:57 +0200341 debug_last_endpoint_name[0] = '\0';
342
Alexander Chemeris63866002020-05-05 17:18:40 +0300343 /* Count all messages, even incorect ones */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200344 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_TOTAL));
Alexander Chemeris63866002020-05-05 17:18:40 +0300345
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200346 if (msgb_l2len(msg) < 4) {
347 LOGP(DLMGCP, LOGL_ERROR, "msg too short: %d\n", msg->len);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200348 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200349 return NULL;
350 }
351
Alexander Chemeris63866002020-05-05 17:18:40 +0300352 if (mgcp_msg_terminate_nul(msg)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200353 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200354 return NULL;
Alexander Chemeris63866002020-05-05 17:18:40 +0300355 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200356
Philipp Maier87bd9be2017-08-22 16:35:41 +0200357 mgcp_disp_msg(msg->l2h, msgb_l2len(msg), "Received message");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200358
Philipp Maier87bd9be2017-08-22 16:35:41 +0200359 /* attempt to treat it as a response */
360 if (sscanf((const char *)&msg->l2h[0], "%3d %*s", &code) == 1) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200361 LOGP(DLMGCP, LOGL_DEBUG, "Response: Code: %d\n", code);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200362 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200363 return NULL;
364 }
365
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200366
Philipp Maier8dc35972021-07-14 11:20:16 +0200367 /* Parse message, extract endpoint name and transaction identifier and request name etc. */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200368 memset(&pdata, 0, sizeof(pdata));
Philipp Maier8dc35972021-07-14 11:20:16 +0200369 memset(&rq, 0, sizeof(rq));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200370 pdata.cfg = cfg;
Philipp Maier8dc35972021-07-14 11:20:16 +0200371 memcpy(rq.name, (const char *)&msg->l2h[0], sizeof(rq.name)-1);
372 msg->l3h = &msg->l2h[4];
Philipp Maier87bd9be2017-08-22 16:35:41 +0200373 data = mgcp_strline((char *)msg->l3h, &pdata.save);
Harald Weltee35eeae2017-12-28 13:47:37 +0100374 rc = mgcp_parse_header(&pdata, data);
Harald Weltee35eeae2017-12-28 13:47:37 +0100375 if (rc < 0) {
Philipp Maier8dc35972021-07-14 11:20:16 +0200376 LOGP(DLMGCP, LOGL_ERROR, "%s: failed to parse MCGP message\n", rq.name);
377 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
378 return create_err_response(NULL, -rc, rq.name, "000000");
Harald Welteabbb6b92017-12-28 13:13:50 +0100379 }
380
Philipp Maier8dc35972021-07-14 11:20:16 +0200381 /* Locate endpoint and trunk, if no endpoint can be located try at least to identify the trunk. */
382 rq.pdata = &pdata;
383 rq.wildcarded = mgcp_endp_is_wildcarded(pdata.epname);
384 rq.endp = mgcp_endp_by_name(&rc, pdata.epname, pdata.cfg);
385 rq.mgcp_cause = rc;
386 if (!rq.endp) {
387 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_NO_ENDPOINT));
388 if (rq.wildcarded) {
389 /* If we are unable to find the endpoint we still may be able to identify the trunk. Some
390 * request handlers will still be able to perform a useful action if the request refers to
391 * the whole trunk (wildcarded request). */
392 LOGP(DLMGCP, LOGL_NOTICE,
393 "%s: cannot find endpoint \"%s\", cause=%d -- trying to identify trunk...\n", rq.name,
394 pdata.epname, -rq.mgcp_cause);
395 rq.trunk = mgcp_trunk_by_name(pdata.cfg, pdata.epname);
396 if (!rq.trunk) {
397 LOGP(DLMGCP, LOGL_ERROR, "%s: failed to identify trunk for endpoint \"%s\" -- abort\n",
398 rq.name, pdata.epname);
399 return create_err_response(NULL, -rq.mgcp_cause, rq.name, pdata.trans);
400 }
401 } else {
402 /* If the endpoint name suggests that the request refers to a specific endpoint, then the
403 * request cannot be handled and we must stop early. */
404 LOGP(DLMGCP, LOGL_NOTICE,
405 "%s: cannot find endpoint \"%s\", cause=%d -- abort\n", rq.name,
406 pdata.epname, -rq.mgcp_cause);
407 return create_err_response(NULL, -rq.mgcp_cause, rq.name, pdata.trans);
408 }
409 } else {
Philipp Maier39889e42021-08-04 17:42:57 +0200410 osmo_strlcpy(debug_last_endpoint_name, rq.endp->name, sizeof(debug_last_endpoint_name));
Philipp Maier8dc35972021-07-14 11:20:16 +0200411 rq.trunk = rq.endp->trunk;
412 rq.mgcp_cause = 0;
413
414 /* Check if we have to retransmit a response from a previous transaction */
415 if (pdata.trans && rq.endp->last_trans && strcmp(rq.endp->last_trans, pdata.trans) == 0) {
416 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_RETRANSMITTED));
417 return do_retransmission(rq.endp);
418 }
419 }
420
421 /* Find an appropriate handler for the current request and execute it */
422 for (i = 0; i < ARRAY_SIZE(mgcp_requests); i++) {
423 if (strcmp(mgcp_requests[i].name, rq.name) == 0) {
424 /* Check if the request requires and endpoint, if yes, check if we have it, otherwise don't
425 * execute the request handler. */
426 if (mgcp_requests[i].require_endp && !rq.endp) {
427 LOGP(DLMGCP, LOGL_ERROR,
428 "%s: the request handler \"%s\" requires an endpoint resource for \"%s\", which is not available -- abort\n",
429 rq.name, mgcp_requests[i].debug_name, pdata.epname);
430 return create_err_response(NULL, -rq.mgcp_cause, rq.name, pdata.trans);
431 }
432
433 /* Execute request handler */
434 if (rq.endp)
435 LOGP(DLMGCP, LOGL_INFO,
436 "%s: executing request handler \"%s\" for endpoint resource \"%s\"\n", rq.name,
437 mgcp_requests[i].debug_name, rq.endp->name);
438 else
439 LOGP(DLMGCP, LOGL_INFO,
440 "%s: executing request handler \"%s\" for trunk resource of endpoint \"%s\"\n",
441 rq.name, mgcp_requests[i].debug_name, pdata.epname);
442 resp = mgcp_requests[i].handle_request(&rq);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200443 handled = 1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200444 break;
445 }
446 }
447
Philipp Maier8dc35972021-07-14 11:20:16 +0200448 /* Check if the MGCP request was handled and increment rate counters accordingly. */
Alexander Chemeris63866002020-05-05 17:18:40 +0300449 if (handled) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200450 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_HANDLED));
Alexander Chemeris63866002020-05-05 17:18:40 +0300451 } else {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200452 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_UNHANDLED));
Philipp Maier8dc35972021-07-14 11:20:16 +0200453 LOGP(DLMGCP, LOGL_ERROR, "MSG with type: '%.4s' not handled\n", &msg->l2h[0]);
Alexander Chemeris63866002020-05-05 17:18:40 +0300454 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200455
456 return resp;
457}
458
Philipp Maier87bd9be2017-08-22 16:35:41 +0200459/* AUEP command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +0200460static struct msgb *handle_audit_endpoint(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200461{
Philipp Maier8dc35972021-07-14 11:20:16 +0200462 LOGPENDP(rq->endp, DLMGCP, LOGL_NOTICE, "AUEP: auditing endpoint ...\n");
463 return create_ok_response(rq->endp, 200, "AUEP", rq->pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200464}
465
Harald Welte1d1b98f2017-12-25 10:03:40 +0100466/* Try to find a free port by attempting to bind on it. Also handle the
Philipp Maier87bd9be2017-08-22 16:35:41 +0200467 * counter that points on the next free port. Since we have a pointer
Philipp Maierb38fb892018-05-22 13:52:21 +0200468 * to the next free port, binding should in work on the first attempt in
Pau Espin Pedrolfc806732019-04-23 00:18:43 +0200469 * general. In case of failure the next port is tried until the whole port
470 * range is tried once. */
Philipp Maier87bd9be2017-08-22 16:35:41 +0200471static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200472{
473 int i;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200474 struct mgcp_port_range *range;
Philipp Maierb38fb892018-05-22 13:52:21 +0200475 unsigned int tries;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200476
Philipp Maier87bd9be2017-08-22 16:35:41 +0200477 OSMO_ASSERT(conn);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200478
479 range = &endp->cfg->net_ports;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200480
481 /* attempt to find a port */
Philipp Maierb38fb892018-05-22 13:52:21 +0200482 tries = (range->range_end - range->range_start) / 2;
483 for (i = 0; i < tries; ++i) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200484 int rc;
485
486 if (range->last_port >= range->range_end)
487 range->last_port = range->range_start;
488
Philipp Maier87bd9be2017-08-22 16:35:41 +0200489 rc = mgcp_bind_net_rtp_port(endp, range->last_port, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200490
491 range->last_port += 2;
492 if (rc == 0) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200493 return 0;
494 }
495
496 }
497
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200498 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
499 "Allocating a RTP/RTCP port failed %u times.\n",
500 tries);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200501 return -1;
502}
503
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200504/*! Helper function for check_local_cx_options() to get a pointer of the next
505 * lco option identifier
506 * \param[in] lco string
507 * \returns pointer to the beginning of the LCO identifier, NULL on failure */
508char *get_lco_identifier(const char *options)
509{
510 char *ptr;
511 unsigned int count = 0;
512
513 /* Jump to the end of the lco identifier */
514 ptr = strstr(options, ":");
515 if (!ptr)
516 return NULL;
517
518 /* Walk backwards until the pointer points to the beginning of the
519 * lco identifier. We know that we stand at the beginning when we
520 * are either at the beginning of the memory or see a space or
521 * comma. (this is tolerant, it will accept a:10, b:11 as well as
522 * a:10,b:11) */
523 while (1) {
524 /* Endless loop protection */
525 if (count > 10000)
526 return NULL;
527 else if (ptr < options || *ptr == ' ' || *ptr == ',') {
528 ptr++;
529 break;
530 }
531 ptr--;
532 count++;
533 }
534
535 /* Check if we got any result */
536 if (*ptr == ':')
537 return NULL;
538
539 return ptr;
540}
541
542/*! Check the LCO option. This function checks for multiple appearence of LCO
543 * options, which is illegal
544 * \param[in] ctx talloc context
545 * \param[in] lco string
546 * \returns 0 on success, -1 on failure */
547int check_local_cx_options(void *ctx, const char *options)
548{
549 int i;
550 char *options_copy;
551 char *lco_identifier;
552 char *lco_identifier_end;
553 char *next_lco_identifier;
554
555 char **lco_seen;
556 unsigned int lco_seen_n = 0;
557
558 if (!options)
559 return -1;
560
561 lco_seen =
562 (char **)talloc_zero_size(ctx, strlen(options) * sizeof(char *));
563 options_copy = talloc_strdup(ctx, options);
564 lco_identifier = options_copy;
565
566 do {
567 /* Move the lco_identifier pointer to the beginning of the
568 * current lco option identifier */
569 lco_identifier = get_lco_identifier(lco_identifier);
570 if (!lco_identifier)
571 goto error;
572
573 /* Look ahead to the next LCO option early, since we
574 * will parse destructively */
575 next_lco_identifier = strstr(lco_identifier + 1, ",");
576
577 /* Pinch off the end of the lco field identifier name
578 * and see if we still got something, also check if
579 * there is some value after the colon. */
580 lco_identifier_end = strstr(lco_identifier, ":");
581 if (!lco_identifier_end)
582 goto error;
583 if (*(lco_identifier_end + 1) == ' '
584 || *(lco_identifier_end + 1) == ','
585 || *(lco_identifier_end + 1) == '\0')
586 goto error;
587 *lco_identifier_end = '\0';
588 if (strlen(lco_identifier) == 0)
589 goto error;
590
591 /* Check if we have already seen the current field identifier
592 * before. If yes, we must bail, an LCO must only appear once
593 * in the LCO string */
594 for (i = 0; i < lco_seen_n; i++) {
Pau Espin Pedrol7eb6f2c2019-06-26 13:00:52 +0200595 if (strcasecmp(lco_seen[i], lco_identifier) == 0)
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200596 goto error;
597 }
598 lco_seen[lco_seen_n] = lco_identifier;
599 lco_seen_n++;
600
601 /* The first identifier must always be found at the beginnning
602 * of the LCO string */
603 if (lco_seen[0] != options_copy)
604 goto error;
605
606 /* Go to the next lco option */
607 lco_identifier = next_lco_identifier;
608 } while (lco_identifier);
609
610 talloc_free(lco_seen);
611 talloc_free(options_copy);
612 return 0;
613error:
614 talloc_free(lco_seen);
615 talloc_free(options_copy);
616 return -1;
617}
618
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200619/* Set the LCO from a string (see RFC 3435).
Harald Welte1d1b98f2017-12-25 10:03:40 +0100620 * The string is stored in the 'string' field. A NULL string is handled exactly
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200621 * like an empty string, the 'string' field is never NULL after this function
622 * has been called. */
Philipp Maiera390d0b2018-01-31 17:30:19 +0100623static int set_local_cx_options(void *ctx, struct mgcp_lco *lco,
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200624 const char *options)
625{
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200626 char *lco_id;
Philipp Maier8dbc9ed2018-10-26 14:50:25 +0200627 char codec[17];
Pau Espin Pedrol19539862020-09-21 11:43:41 +0200628 char nt[17];
Pau Espin Pedrol83fd8a52019-06-26 12:55:26 +0200629 int len;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200630
Philipp Maier604410c2018-06-06 10:02:16 +0200631 if (!options)
632 return 0;
633 if (strlen(options) == 0)
634 return 0;
635
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200636 /* Make sure the encoding of the LCO is consistant before we proceed */
637 if (check_local_cx_options(ctx, options) != 0) {
638 LOGP(DLMGCP, LOGL_ERROR,
639 "local CX options: Internal inconsistency in Local Connection Options!\n");
640 return 524;
641 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200642
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200643 talloc_free(lco->string);
644 lco->string = talloc_strdup(ctx, options);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200645
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200646 lco_id = lco->string;
647 while ((lco_id = get_lco_identifier(lco_id))) {
648 switch (tolower(lco_id[0])) {
649 case 'p':
650 if (sscanf(lco_id + 1, ":%d-%d",
651 &lco->pkt_period_min, &lco->pkt_period_max) == 1)
652 lco->pkt_period_max = lco->pkt_period_min;
653 break;
654 case 'a':
Pau Espin Pedrol1dc2dce2020-09-21 11:25:15 +0200655 /* FIXME: LCO also supports the negotiation of more than one codec.
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200656 * (e.g. a:PCMU;G726-32) But this implementation only supports a single
657 * codec only. */
658 if (sscanf(lco_id + 1, ":%16[^,]", codec) == 1) {
659 talloc_free(lco->codec);
Pau Espin Pedrol83fd8a52019-06-26 12:55:26 +0200660 /* MGCP header is case insensive, and we'll need
661 codec in uppercase when using it later: */
662 len = strlen(codec);
663 lco->codec = talloc_size(ctx, len + 1);
664 osmo_str_toupper_buf(lco->codec, len + 1, codec);
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200665 }
666 break;
Pau Espin Pedrol19539862020-09-21 11:43:41 +0200667 case 'n':
668 if (lco_id[1] == 't' && sscanf(lco_id + 2, ":%16[^,]", nt) == 1)
669 break;
670 /* else: fall throught to print notice log */
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200671 default:
672 LOGP(DLMGCP, LOGL_NOTICE,
673 "LCO: unhandled option: '%c'/%d in \"%s\"\n",
674 *lco_id, *lco_id, lco->string);
675 break;
676 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200677
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200678 lco_id = strchr(lco_id, ',');
679 if (!lco_id)
680 break;
Philipp Maier604410c2018-06-06 10:02:16 +0200681 }
Philipp Maier87bd9be2017-08-22 16:35:41 +0200682
683 LOGP(DLMGCP, LOGL_DEBUG,
684 "local CX options: lco->pkt_period_max: %i, lco->codec: %s\n",
685 lco->pkt_period_max, lco->codec);
Philipp Maiera390d0b2018-01-31 17:30:19 +0100686
687 /* Check if the packetization fits the 20ms raster */
688 if (lco->pkt_period_min % 20 && lco->pkt_period_max % 20) {
689 LOGP(DLMGCP, LOGL_ERROR,
690 "local CX options: packetization interval is not a multiple of 20ms!\n");
691 return 535;
692 }
693
694 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200695}
696
697void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
698 struct mgcp_rtp_end *rtp)
699{
Philipp Maier14b27a82020-06-02 20:15:30 +0200700 struct mgcp_trunk *trunk = endp->trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200701
Philipp Maier14b27a82020-06-02 20:15:30 +0200702 int patch_ssrc = expect_ssrc_change && trunk->force_constant_ssrc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200703
Philipp Maier14b27a82020-06-02 20:15:30 +0200704 rtp->force_aligned_timing = trunk->force_aligned_timing;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200705 rtp->force_constant_ssrc = patch_ssrc ? 1 : 0;
Philipp Maier14b27a82020-06-02 20:15:30 +0200706 rtp->rfc5993_hr_convert = trunk->rfc5993_hr_convert;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200707
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200708 LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
709 "Configuring RTP endpoint: local port %d%s%s\n",
710 ntohs(rtp->rtp_port),
711 rtp->force_aligned_timing ? ", force constant timing" : "",
712 rtp->force_constant_ssrc ? ", force constant ssrc" : "");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200713}
714
Pau Espin Pedrol8358c4b2021-07-07 12:41:38 +0200715uint32_t mgcp_rtp_packet_duration(const struct mgcp_endpoint *endp,
716 const struct mgcp_rtp_end *rtp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200717{
718 int f = 0;
719
720 /* Get the number of frames per channel and packet */
721 if (rtp->frames_per_packet)
722 f = rtp->frames_per_packet;
Philipp Maierbc0346e2018-06-07 09:52:16 +0200723 else if (rtp->packet_duration_ms && rtp->codec->frame_duration_num) {
724 int den = 1000 * rtp->codec->frame_duration_num;
725 f = (rtp->packet_duration_ms * rtp->codec->frame_duration_den +
Philipp Maier87bd9be2017-08-22 16:35:41 +0200726 den / 2)
727 / den;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200728 }
729
Philipp Maierbc0346e2018-06-07 09:52:16 +0200730 return rtp->codec->rate * f * rtp->codec->frame_duration_num /
731 rtp->codec->frame_duration_den;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200732}
733
Pau Espin Pedrol9fb8ddf2019-05-08 15:35:36 +0200734/*! Initializes osmux socket if not yet initialized. Parses Osmux CID from MGCP line.
735 * \param[in] endp Endpoint willing to initialize osmux
736 * \param[in] line Line X-Osmux from MGCP header msg to parse
737 * \returns OSMUX CID, -1 for wildcard, -2 on parse error, -3 on osmux initalize error
738 */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200739static int mgcp_osmux_setup(struct mgcp_endpoint *endp, const char *line)
740{
741 if (!endp->cfg->osmux_init) {
742 if (osmux_init(OSMUX_ROLE_BSC, endp->cfg) < 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200743 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "Cannot init OSMUX\n");
Pau Espin Pedrol9fb8ddf2019-05-08 15:35:36 +0200744 return -3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200745 }
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200746 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "OSMUX socket has been set up\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200747 }
748
749 return mgcp_parse_osmux_cid(line);
750}
751
Philipp Maierbc0346e2018-06-07 09:52:16 +0200752/* Process codec information contained in CRCX/MDCX */
753static int handle_codec_info(struct mgcp_conn_rtp *conn,
Philipp Maier8dc35972021-07-14 11:20:16 +0200754 struct mgcp_request_data *rq, int have_sdp, bool crcx)
Philipp Maierbc0346e2018-06-07 09:52:16 +0200755{
Philipp Maier8dc35972021-07-14 11:20:16 +0200756 struct mgcp_endpoint *endp = rq->endp;
Philipp Maierbc0346e2018-06-07 09:52:16 +0200757 int rc;
758 char *cmd;
759
760 if (crcx)
761 cmd = "CRCX";
762 else
763 cmd = "MDCX";
764
765 /* Collect codec information */
766 if (have_sdp) {
767 /* If we have SDP, we ignore the local connection options and
768 * use only the SDP information. */
769 mgcp_codec_reset_all(conn);
Philipp Maier8dc35972021-07-14 11:20:16 +0200770 rc = mgcp_parse_sdp_data(endp, conn, rq->pdata);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200771 if (rc != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200772 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
773 "%s: sdp not parseable\n", cmd);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200774
775 /* See also RFC 3661: Protocol error */
776 return 510;
777 }
778 } else if (endp->local_options.codec) {
779 /* When no SDP is available, we use the codec information from
780 * the local connection options (if present) */
781 mgcp_codec_reset_all(conn);
Philipp Maier228e5912019-03-05 13:56:59 +0100782 rc = mgcp_codec_add(conn, PTYPE_UNDEFINED, endp->local_options.codec, NULL);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200783 if (rc != 0)
784 goto error;
785 }
786
787 /* Make sure we always set a sane default codec */
788 if (conn->end.codecs_assigned == 0) {
789 /* When SDP and/or LCO did not supply any codec information,
790 * than it makes sense to pick a sane default: (payload-type 0,
791 * PCMU), see also: OS#2658 */
792 mgcp_codec_reset_all(conn);
Philipp Maier228e5912019-03-05 13:56:59 +0100793 rc = mgcp_codec_add(conn, 0, NULL, NULL);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200794 if (rc != 0)
795 goto error;
796 }
797
798 /* Make codec decision */
799 if (mgcp_codec_decide(conn) != 0)
800 goto error;
801
802 return 0;
803
804error:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200805 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
806 "%s: codec negotiation failure\n", cmd);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200807
808 /* See also RFC 3661: Codec negotiation failure */
809 return 534;
810}
811
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200812static bool parse_x_osmo_ign(struct mgcp_endpoint *endp, char *line)
813{
814 char *saveptr = NULL;
815
Pau Espin Pedrol6049a632020-09-21 11:03:21 +0200816 if (strncasecmp(line, MGCP_X_OSMO_IGN_HEADER, strlen(MGCP_X_OSMO_IGN_HEADER)))
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200817 return false;
818 line += strlen(MGCP_X_OSMO_IGN_HEADER);
819
820 while (1) {
821 char *token = strtok_r(line, " ", &saveptr);
822 line = NULL;
823 if (!token)
824 break;
825
Pau Espin Pedrol6e26c702019-06-26 13:09:39 +0200826 if (!strcasecmp(token, "C"))
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200827 endp->x_osmo_ign |= MGCP_X_OSMO_IGN_CALLID;
828 else
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200829 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "received unknown X-Osmo-IGN item '%s'\n", token);
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200830 }
831
832 return true;
833}
834
Philipp Maier87bd9be2017-08-22 16:35:41 +0200835/* CRCX command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +0200836static struct msgb *handle_create_con(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200837{
Philipp Maier8dc35972021-07-14 11:20:16 +0200838 struct mgcp_parse_data *pdata = rq->pdata;
839 struct mgcp_trunk *trunk = rq->trunk;
840 struct mgcp_endpoint *endp = rq->endp;
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200841 struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_crcx_ctr_group;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200842 int error_code = 400;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200843 const char *local_options = NULL;
844 const char *callid = NULL;
845 const char *mode = NULL;
846 char *line;
Pau Espin Pedrol2b896172019-04-24 13:47:23 +0200847 int have_sdp = 0, osmux_cid = -2;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200848 struct mgcp_conn_rtp *conn = NULL;
Philipp Maierffd75e42017-11-22 11:44:50 +0100849 struct mgcp_conn *_conn = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200850 char conn_name[512];
Philipp Maiera390d0b2018-01-31 17:30:19 +0100851 int rc;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200852
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200853 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "CRCX: creating new connection ...\n");
Philipp Maier246233d2020-08-18 15:15:24 +0200854
Philipp Maier8d6a1932020-06-18 12:19:31 +0200855 if (!mgcp_endp_avail(endp)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200856 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_AVAIL));
Philipp Maiera910a812020-08-18 15:13:33 +0200857 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
858 "CRCX: selected endpoint not available!\n");
Philipp Maier8dc35972021-07-14 11:20:16 +0200859 return create_err_response(NULL, 501, "CRCX", pdata->trans);
Philipp Maier8d6a1932020-06-18 12:19:31 +0200860 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200861
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200862 /* parse CallID C: and LocalParameters L: */
Philipp Maier8dc35972021-07-14 11:20:16 +0200863 for_each_line(line, pdata->save) {
Philipp Maier036612b2021-07-19 17:47:49 +0200864 if (!mgcp_check_param(endp, trunk, line))
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200865 continue;
866
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +0200867 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200868 case 'L':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200869 local_options = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200870 break;
871 case 'C':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200872 callid = (const char *)line + 3;
873 break;
874 case 'I':
Philipp Maierffd75e42017-11-22 11:44:50 +0100875 /* It is illegal to send a connection identifier
876 * together with a CRCX, the MGW will assign the
877 * connection identifier by itself on CRCX */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200878 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BAD_ACTION));
Philipp Maier8dc35972021-07-14 11:20:16 +0200879 return create_err_response(NULL, 523, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200880 break;
881 case 'M':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200882 mode = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200883 break;
884 case 'X':
Pau Espin Pedrolc1bf4692019-05-14 16:23:24 +0200885 if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200886 /* If osmux is disabled, just skip setting it up */
Philipp Maier8dc35972021-07-14 11:20:16 +0200887 if (!rq->endp->cfg->osmux)
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200888 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200889 osmux_cid = mgcp_osmux_setup(endp, line);
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200890 break;
891 }
892
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200893 if (parse_x_osmo_ign(endp, line))
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200894 break;
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200895
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200896 /* Ignore unknown X-headers */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200897 break;
898 case '\0':
899 have_sdp = 1;
900 goto mgcp_header_done;
901 default:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200902 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
903 "CRCX: unhandled option: '%c'/%d\n", *line, *line);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200904 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_UNHANDLED_PARAM));
Philipp Maier8dc35972021-07-14 11:20:16 +0200905 return create_err_response(NULL, 539, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200906 break;
907 }
908 }
909
910mgcp_header_done:
Philipp Maier87bd9be2017-08-22 16:35:41 +0200911 /* Check parameters */
912 if (!callid) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200913 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
914 "CRCX: insufficient parameters, missing callid\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200915 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_MISSING_CALLID));
Philipp Maier8dc35972021-07-14 11:20:16 +0200916 return create_err_response(endp, 516, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200917 }
918
Philipp Maier87bd9be2017-08-22 16:35:41 +0200919 if (!mode) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200920 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
921 "CRCX: insufficient parameters, missing mode\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200922 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_MODE));
Philipp Maier8dc35972021-07-14 11:20:16 +0200923 return create_err_response(endp, 517, "CRCX", pdata->trans);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200924 }
925
Philipp Maier87bd9be2017-08-22 16:35:41 +0200926 /* Check if we are able to accept the creation of another connection */
927 if (llist_count(&endp->conns) >= endp->type->max_conns) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200928 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
929 "CRCX: endpoint full, max. %i connections allowed!\n",
930 endp->type->max_conns);
Philipp Maier14b27a82020-06-02 20:15:30 +0200931 if (trunk->force_realloc) {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200932 /* There is no more room for a connection, make some
933 * room by blindly tossing the oldest of the two two
934 * connections */
935 mgcp_conn_free_oldest(endp);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200936 } else {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200937 /* There is no more room for a connection, leave
938 * everything as it is and return with an error */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200939 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_LIMIT_EXCEEDED));
Philipp Maier8dc35972021-07-14 11:20:16 +0200940 return create_err_response(endp, 540, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200941 }
942 }
943
Philipp Maier87bd9be2017-08-22 16:35:41 +0200944 /* Check if this endpoint already serves a call, if so, check if the
945 * callids match up so that we are sure that this is our call */
946 if (endp->callid && mgcp_verify_call_id(endp, callid)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200947 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
948 "CRCX: already seized by other call (%s)\n",
949 endp->callid);
Philipp Maier14b27a82020-06-02 20:15:30 +0200950 if (trunk->force_realloc)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200951 /* This is not our call, toss everything by releasing
952 * the entire endpoint. (rude!) */
Philipp Maier1355d7e2018-02-01 14:30:06 +0100953 mgcp_endp_release(endp);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200954 else {
955 /* This is not our call, leave everything as it is and
956 * return with an error. */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200957 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_UNKNOWN_CALLID));
Philipp Maier8dc35972021-07-14 11:20:16 +0200958 return create_err_response(endp, 400, "CRCX", pdata->trans);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200959 }
960 }
961
Philipp Maier889fe7f2020-07-06 17:44:12 +0200962 if (!endp->callid) {
963 /* Claim endpoint resources. This will also set the callid,
964 * creating additional connections will only be possible if
965 * the callid matches up (see above). */
966 rc = mgcp_endp_claim(endp, callid);
967 if (rc != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200968 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_CLAIM));
Philipp Maier8dc35972021-07-14 11:20:16 +0200969 return create_err_response(endp, 502, "CRCX", pdata->trans);
Philipp Maier889fe7f2020-07-06 17:44:12 +0200970 }
971 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200972
Philipp Maierffd75e42017-11-22 11:44:50 +0100973 snprintf(conn_name, sizeof(conn_name), "%s", callid);
Philipp Maier14b27a82020-06-02 20:15:30 +0200974 _conn = mgcp_conn_alloc(trunk->endpoints, endp, MGCP_CONN_TYPE_RTP, conn_name);
Philipp Maierffd75e42017-11-22 11:44:50 +0100975 if (!_conn) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200976 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
977 "CRCX: unable to allocate RTP connection\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200978 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_ALLOC_CONN));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200979 goto error2;
980
Philipp Maier87bd9be2017-08-22 16:35:41 +0200981 }
Philipp Maier889fe7f2020-07-06 17:44:12 +0200982
Philipp Maierffd75e42017-11-22 11:44:50 +0100983 conn = mgcp_conn_get_rtp(endp, _conn->id);
984 OSMO_ASSERT(conn);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200985
986 if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
987 error_code = 517;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200988 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_MODE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200989 goto error2;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200990 }
991
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200992 /* Annotate Osmux circuit ID and set it to negotiating state until this
Philipp Maier87bd9be2017-08-22 16:35:41 +0200993 * is fully set up from the dummy load. */
994 conn->osmux.state = OSMUX_STATE_DISABLED;
Pau Espin Pedrol2b896172019-04-24 13:47:23 +0200995 if (osmux_cid >= -1) { /* -1 is wilcard, alloc next avail CID */
Pau Espin Pedrol14f8a082019-05-13 13:10:06 +0200996 conn->osmux.state = OSMUX_STATE_ACTIVATING;
Pau Espin Pedrol2b896172019-04-24 13:47:23 +0200997 if (conn_osmux_allocate_cid(conn, osmux_cid) == -1) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200998 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_OSMUX));
Pau Espin Pedrol2b896172019-04-24 13:47:23 +0200999 goto error2;
1000 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001001 } else if (endp->cfg->osmux == OSMUX_USAGE_ONLY) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001002 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
1003 "CRCX: osmux only and no osmux offered\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001004 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_OSMUX));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001005 goto error2;
1006 }
1007
Philipp Maierbc0346e2018-06-07 09:52:16 +02001008 /* Set local connection options, if present */
1009 if (local_options) {
Philipp Maier8dc35972021-07-14 11:20:16 +02001010 rc = set_local_cx_options(trunk->endpoints,
Philipp Maierbc0346e2018-06-07 09:52:16 +02001011 &endp->local_options, local_options);
1012 if (rc != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001013 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
1014 "CRCX: inavlid local connection options!\n");
Philipp Maierbc0346e2018-06-07 09:52:16 +02001015 error_code = rc;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001016 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS));
Philipp Maierbc0346e2018-06-07 09:52:16 +02001017 goto error2;
1018 }
1019 }
1020
1021 /* Handle codec information and decide for a suitable codec */
Philipp Maier8dc35972021-07-14 11:20:16 +02001022 rc = handle_codec_info(conn, rq, have_sdp, true);
Philipp Maierbc0346e2018-06-07 09:52:16 +02001023 mgcp_codec_summary(conn);
1024 if (rc) {
1025 error_code = rc;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001026 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_CODEC_NEGOTIATION));
Philipp Maierbc0346e2018-06-07 09:52:16 +02001027 goto error2;
1028 }
1029
Philipp Maier14b27a82020-06-02 20:15:30 +02001030 conn->end.fmtp_extra = talloc_strdup(trunk->endpoints,
1031 trunk->audio_fmtp_extra);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001032
Philipp Maier8dc35972021-07-14 11:20:16 +02001033 if (pdata->cfg->force_ptime) {
1034 conn->end.packet_duration_ms = pdata->cfg->force_ptime;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001035 conn->end.force_output_ptime = 1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001036 }
1037
Philipp Maier1cb1e382017-11-02 17:16:04 +01001038 mgcp_rtp_end_config(endp, 0, &conn->end);
1039
Philipp Maierc3cc6542018-02-02 12:58:42 +01001040 /* check connection mode setting */
1041 if (conn->conn->mode != MGCP_CONN_LOOPBACK
1042 && conn->conn->mode != MGCP_CONN_RECV_ONLY
1043 && conn->end.rtp_port == 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001044 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
1045 "CRCX: selected connection mode type requires an opposite end!\n");
Philipp Maierc3cc6542018-02-02 12:58:42 +01001046 error_code = 527;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001047 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC));
Philipp Maierc3cc6542018-02-02 12:58:42 +01001048 goto error2;
1049 }
1050
Pau Espin Pedrol71d42e72020-09-03 14:20:07 +02001051 /* Find a local address for conn based on policy and initial SDP remote
1052 information, then find a free port for it */
1053 mgcp_get_local_addr(conn->end.local_addr, conn);
Philipp Maier1cb1e382017-11-02 17:16:04 +01001054 if (allocate_port(endp, conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001055 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
Philipp Maier1cb1e382017-11-02 17:16:04 +01001056 goto error2;
1057 }
1058
Philipp Maier87bd9be2017-08-22 16:35:41 +02001059 if (setup_rtp_processing(endp, conn) != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001060 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
1061 "CRCX: could not start RTP processing!\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001062 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_START_RTP));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001063 goto error2;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001064 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001065
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001066 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
1067 "CRCX: Creating connection: port: %u\n", conn->end.local_port);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001068
Philipp Maiere726d4f2017-11-01 10:41:34 +01001069 /* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
Philipp Maier14b27a82020-06-02 20:15:30 +02001070 OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Pau Espin Pedrolca280a12021-07-06 18:15:35 +02001071 if (conn->conn->mode & MGCP_CONN_RECV_ONLY &&
1072 mgcp_rtp_end_remote_addr_available(&conn->end) &&
1073 trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001074 send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001075
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001076 LOGPCONN(_conn, DLMGCP, LOGL_NOTICE,
1077 "CRCX: connection successfully created\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001078 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_SUCCESS));
Philipp Maier889fe7f2020-07-06 17:44:12 +02001079 mgcp_endp_update(endp);
Philipp Maier41d59202021-07-20 15:49:00 +02001080
1081 /* NOTE: Only in the virtual trunk we allow dynamic endpoint names */
1082 bool add_epname = rq->wildcarded && trunk->trunk_type == MGCP_TRUNK_VIRTUAL;
1083 return create_response_with_sdp(endp, conn, "CRCX", pdata->trans, add_epname, true);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001084error2:
Philipp Maier1355d7e2018-02-01 14:30:06 +01001085 mgcp_endp_release(endp);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001086 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1087 "CRCX: unable to create connection\n");
Philipp Maier8dc35972021-07-14 11:20:16 +02001088 return create_err_response(endp, error_code, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001089}
1090
Philipp Maier87bd9be2017-08-22 16:35:41 +02001091/* MDCX command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +02001092static struct msgb *handle_modify_con(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001093{
Philipp Maier8dc35972021-07-14 11:20:16 +02001094 struct mgcp_parse_data *pdata = rq->pdata;
1095 struct mgcp_trunk *trunk = rq->trunk;
1096 struct mgcp_endpoint *endp = rq->endp;
1097 struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_mdcx_ctr_group;
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001098 char new_local_addr[INET6_ADDRSTRLEN];
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001099 int error_code = 500;
1100 int silent = 0;
1101 int have_sdp = 0;
1102 char *line;
1103 const char *local_options = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001104 const char *mode = NULL;
1105 struct mgcp_conn_rtp *conn = NULL;
Philipp Maier01d24a32017-11-21 17:26:09 +01001106 const char *conn_id = NULL;
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001107 int osmux_cid = -2;
Philipp Maiera390d0b2018-01-31 17:30:19 +01001108 int rc;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001109
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001110 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "MDCX: modifying existing connection ...\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001111
Philipp Maier8d6a1932020-06-18 12:19:31 +02001112 if (!mgcp_endp_avail(endp)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001113 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_AVAIL));
Philipp Maiera910a812020-08-18 15:13:33 +02001114 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1115 "MDCX: selected endpoint not available!\n");
Philipp Maier8dc35972021-07-14 11:20:16 +02001116 return create_err_response(NULL, 501, "MDCX", pdata->trans);
Philipp Maier8d6a1932020-06-18 12:19:31 +02001117 }
1118
Philipp Maier5656fbf2018-02-02 14:41:58 +01001119 /* Prohibit wildcarded requests */
Philipp Maier8dc35972021-07-14 11:20:16 +02001120 if (rq->wildcarded) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001121 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1122 "MDCX: wildcarded endpoint names not supported.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001123 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_WILDCARD));
Philipp Maier8dc35972021-07-14 11:20:16 +02001124 return create_err_response(endp, 507, "MDCX", pdata->trans);
Philipp Maier5656fbf2018-02-02 14:41:58 +01001125 }
1126
Philipp Maier87bd9be2017-08-22 16:35:41 +02001127 if (llist_count(&endp->conns) <= 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001128 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1129 "MDCX: endpoint is not holding a connection.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001130 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_CONN));
Philipp Maier8dc35972021-07-14 11:20:16 +02001131 return create_err_response(endp, 400, "MDCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001132 }
1133
Philipp Maier8dc35972021-07-14 11:20:16 +02001134 for_each_line(line, pdata->save) {
Philipp Maier036612b2021-07-19 17:47:49 +02001135 if (!mgcp_check_param(endp, trunk, line))
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001136 continue;
1137
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001138 switch (toupper(line[0])) {
Philipp Maier87bd9be2017-08-22 16:35:41 +02001139 case 'C':
Harald Weltee35eeae2017-12-28 13:47:37 +01001140 if (mgcp_verify_call_id(endp, line + 3) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001141 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CALLID));
Harald Weltee35eeae2017-12-28 13:47:37 +01001142 error_code = 516;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001143 goto error3;
Harald Weltee35eeae2017-12-28 13:47:37 +01001144 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001145 break;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001146 case 'I':
Philipp Maier01d24a32017-11-21 17:26:09 +01001147 conn_id = (const char *)line + 3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001148 if ((error_code = mgcp_verify_ci(endp, conn_id))) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001149 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CONNID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001150 goto error3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001151 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001152 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001153 case 'L':
Philipp Maier87bd9be2017-08-22 16:35:41 +02001154 local_options = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001155 break;
1156 case 'M':
Philipp Maier87bd9be2017-08-22 16:35:41 +02001157 mode = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001158 break;
1159 case 'Z':
Pau Espin Pedrol9b508f62019-06-26 13:11:22 +02001160 silent = strcasecmp("noanswer", line + 3) == 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001161 break;
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001162 case 'X':
Pau Espin Pedrolc1bf4692019-05-14 16:23:24 +02001163 if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001164 /* If osmux is disabled, just skip setting it up */
Philipp Maier8dc35972021-07-14 11:20:16 +02001165 if (!endp->cfg->osmux)
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001166 break;
1167 osmux_cid = mgcp_osmux_setup(endp, line);
1168 break;
1169 }
1170 /* Ignore unknown X-headers */
1171 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001172 case '\0':
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001173 have_sdp = 1;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001174 goto mgcp_header_done;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001175 break;
1176 default:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001177 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1178 "MDCX: Unhandled MGCP option: '%c'/%d\n",
1179 line[0], line[0]);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001180 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_UNHANDLED_PARAM));
Philipp Maier8dc35972021-07-14 11:20:16 +02001181 return create_err_response(NULL, 539, "MDCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001182 break;
1183 }
1184 }
1185
Philipp Maier87bd9be2017-08-22 16:35:41 +02001186mgcp_header_done:
Philipp Maier01d24a32017-11-21 17:26:09 +01001187 if (!conn_id) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001188 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1189 "MDCX: insufficient parameters, missing ci (connectionIdentifier)\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001190 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_CONNID));
Philipp Maier8dc35972021-07-14 11:20:16 +02001191 return create_err_response(endp, 515, "MDCX", pdata->trans);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001192 }
1193
1194 conn = mgcp_conn_get_rtp(endp, conn_id);
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001195 if (!conn) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001196 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_CONN_NOT_FOUND));
Philipp Maier8dc35972021-07-14 11:20:16 +02001197 return create_err_response(endp, 400, "MDCX", pdata->trans);
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001198 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001199
Oliver Smithe36b7752019-01-22 16:31:36 +01001200 mgcp_conn_watchdog_kick(conn->conn);
1201
Philipp Maier87bd9be2017-08-22 16:35:41 +02001202 if (mode) {
1203 if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001204 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_MODE));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001205 error_code = 517;
1206 goto error3;
1207 }
1208 } else
Pau Espin Pedrol209eb9f2019-04-24 12:03:04 +02001209 conn->conn->mode = conn->conn->mode_orig;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001210
Philipp Maierbc0346e2018-06-07 09:52:16 +02001211 /* Set local connection options, if present */
1212 if (local_options) {
Philipp Maier8dc35972021-07-14 11:20:16 +02001213 rc = set_local_cx_options(trunk->endpoints,
Philipp Maierbc0346e2018-06-07 09:52:16 +02001214 &endp->local_options, local_options);
1215 if (rc != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001216 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1217 "MDCX: invalid local connection options!\n");
Philipp Maierbc0346e2018-06-07 09:52:16 +02001218 error_code = rc;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001219 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS));
Philipp Maierbc0346e2018-06-07 09:52:16 +02001220 goto error3;
1221 }
1222 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001223
Philipp Maierbc0346e2018-06-07 09:52:16 +02001224 /* Handle codec information and decide for a suitable codec */
Philipp Maier8dc35972021-07-14 11:20:16 +02001225 rc = handle_codec_info(conn, rq, have_sdp, false);
Philipp Maierbc0346e2018-06-07 09:52:16 +02001226 mgcp_codec_summary(conn);
1227 if (rc) {
Philipp Maieraf07f662018-02-02 11:34:02 +01001228 error_code = rc;
1229 goto error3;
Philipp Maiera390d0b2018-01-31 17:30:19 +01001230 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001231
Philipp Maierc3cc6542018-02-02 12:58:42 +01001232 /* check connection mode setting */
1233 if (conn->conn->mode != MGCP_CONN_LOOPBACK
1234 && conn->conn->mode != MGCP_CONN_RECV_ONLY
Pau Espin Pedrold6769ea2021-07-06 19:44:27 +02001235 && !mgcp_rtp_end_remote_addr_available(&conn->end)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001236 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1237 "MDCX: selected connection mode type requires an opposite end!\n");
Philipp Maierc3cc6542018-02-02 12:58:42 +01001238 error_code = 527;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001239 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC));
Philipp Maierc3cc6542018-02-02 12:58:42 +01001240 goto error3;
1241 }
1242
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001243 if (mgcp_conn_rtp_is_osmux(conn)) {
1244 OSMO_ASSERT(conn->osmux.cid_allocated);
1245 if (osmux_cid < -1) {
1246 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1247 "MDCX: Failed to parse Osmux CID!\n");
1248 goto error3;
1249 } else if (osmux_cid == -1) {
1250 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1251 "MDCX: wilcard in MDCX is not supported!\n");
1252 goto error3;
1253 } else if (osmux_cid != (int) conn->osmux.cid) {
1254 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1255 "MDCX: changing already allocated CID is not supported!\n");
1256 goto error3;
1257 }
1258 /* TODO: In the future (when we have recvCID!=sendCID), we need to
1259 tell Osmux code that osmux_cid is to be used as sendCID for
1260 that conn. */
1261 }
Philipp Maierbc0346e2018-06-07 09:52:16 +02001262
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001263 /* MDCX may have provided a new remote address, which means we may need
1264 to update our announced IP addr and re-bind our local end. This can
1265 happen for instance if MGW initially provided an IPv4 during CRCX
1266 ACK, and now MDCX tells us the remote has an IPv6 address. */
1267 mgcp_get_local_addr(new_local_addr, conn);
1268 if (strcmp(new_local_addr, conn->end.local_addr)) {
1269 osmo_strlcpy(conn->end.local_addr, new_local_addr, sizeof(conn->end.local_addr));
1270 mgcp_free_rtp_port(&conn->end);
1271 if (allocate_port(endp, conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001272 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001273 goto error3;
1274 }
1275 }
1276
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001277 if (setup_rtp_processing(endp, conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001278 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_START_RTP));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001279 goto error3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001280 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001281
Philipp Maier87bd9be2017-08-22 16:35:41 +02001282 mgcp_rtp_end_config(endp, 1, &conn->end);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001283
1284 /* modify */
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001285 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
1286 "MDCX: modified conn:%s\n", mgcp_conn_dump(conn->conn));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001287
Philipp Maiere726d4f2017-11-01 10:41:34 +01001288 /* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
Philipp Maier8dc35972021-07-14 11:20:16 +02001289 OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Pau Espin Pedrolca280a12021-07-06 18:15:35 +02001290 if (conn->conn->mode & MGCP_CONN_RECV_ONLY &&
1291 mgcp_rtp_end_remote_addr_available(&conn->end) &&
Philipp Maier8dc35972021-07-14 11:20:16 +02001292 trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001293 send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001294
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001295 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_SUCCESS));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001296 if (silent)
1297 goto out_silent;
1298
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001299 LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
1300 "MDCX: connection successfully modified\n");
Philipp Maier889fe7f2020-07-06 17:44:12 +02001301 mgcp_endp_update(endp);
Philipp Maier41d59202021-07-20 15:49:00 +02001302 return create_response_with_sdp(endp, conn, "MDCX", pdata->trans, false, false);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001303error3:
Philipp Maier8dc35972021-07-14 11:20:16 +02001304 return create_err_response(endp, error_code, "MDCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001305
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001306out_silent:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001307 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "MDCX: silent exit\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001308 return NULL;
1309}
1310
Philipp Maier87bd9be2017-08-22 16:35:41 +02001311/* DLCX command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +02001312static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001313{
Philipp Maier8dc35972021-07-14 11:20:16 +02001314 struct mgcp_parse_data *pdata = rq->pdata;
1315 struct mgcp_trunk *trunk = rq->trunk;
1316 struct mgcp_endpoint *endp = rq->endp;
1317 struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_dlcx_ctr_group;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001318 int error_code = 400;
1319 int silent = 0;
1320 char *line;
1321 char stats[1048];
Philipp Maier01d24a32017-11-21 17:26:09 +01001322 const char *conn_id = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001323 struct mgcp_conn_rtp *conn = NULL;
Philipp Maierf486e742021-07-19 14:56:29 +02001324 unsigned int i;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001325
Philipp Maierf486e742021-07-19 14:56:29 +02001326 /* NOTE: In this handler we can not take it for granted that the endp
1327 * pointer will be populated, however a trunk is always guaranteed. */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001328
Philipp Maierf486e742021-07-19 14:56:29 +02001329 LOGPEPTR(endp, trunk, DLMGCP, LOGL_NOTICE, "DLCX: deleting connection(s) ...\n");
1330
1331 if (endp && !mgcp_endp_avail(endp)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001332 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_AVAIL));
Philipp Maiera910a812020-08-18 15:13:33 +02001333 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1334 "DLCX: selected endpoint not available!\n");
Philipp Maier8dc35972021-07-14 11:20:16 +02001335 return create_err_response(NULL, 501, "DLCX", pdata->trans);
Philipp Maier8d6a1932020-06-18 12:19:31 +02001336 }
1337
Philipp Maierf486e742021-07-19 14:56:29 +02001338 if (endp && !rq->wildcarded && llist_empty(&endp->conns)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001339 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1340 "DLCX: endpoint is not holding a connection.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001341 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_NO_CONN));
Philipp Maier8dc35972021-07-14 11:20:16 +02001342 return create_err_response(endp, 515, "DLCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001343 }
1344
Philipp Maier8dc35972021-07-14 11:20:16 +02001345 for_each_line(line, pdata->save) {
Philipp Maier036612b2021-07-19 17:47:49 +02001346 if (!mgcp_check_param(endp, trunk, line))
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001347 continue;
1348
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001349 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001350 case 'C':
Philipp Maierf486e742021-07-19 14:56:29 +02001351 /* If we have no endpoint, but a call id in the request,
1352 then this request cannot be handled */
1353 if (!endp) {
1354 LOGPTRUNK(trunk, DLMGCP, LOGL_NOTICE,
1355 "cannot handle requests with call-id (C) without endpoint -- abort!");
1356 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));
1357 return create_err_response(NULL, 539, "DLCX", pdata->trans);
1358 }
1359
Harald Weltee35eeae2017-12-28 13:47:37 +01001360 if (mgcp_verify_call_id(endp, line + 3) != 0) {
1361 error_code = 516;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001362 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CALLID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001363 goto error3;
Harald Weltee35eeae2017-12-28 13:47:37 +01001364 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001365 break;
1366 case 'I':
Philipp Maierf486e742021-07-19 14:56:29 +02001367 /* If we have no endpoint, but a connection id in the request,
1368 then this request cannot be handled */
1369 if (!endp) {
1370 LOGPTRUNK(trunk, DLMGCP, LOGL_NOTICE,
1371 "cannot handle requests with conn-id (I) without endpoint -- abort!");
1372 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));
1373 return create_err_response(NULL, 539, "DLCX", pdata->trans);
1374 }
1375
Philipp Maier01d24a32017-11-21 17:26:09 +01001376 conn_id = (const char *)line + 3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001377 if ((error_code = mgcp_verify_ci(endp, conn_id))) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001378 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CONNID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001379 goto error3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001380 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001381 break;
1382 case 'Z':
Pau Espin Pedrol9b508f62019-06-26 13:11:22 +02001383 silent = strcasecmp("noanswer", line + 3) == 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001384 break;
1385 default:
Philipp Maierf486e742021-07-19 14:56:29 +02001386 LOGPEPTR(endp, trunk, DLMGCP, LOGL_NOTICE, "DLCX: Unhandled MGCP option: '%c'/%d\n",
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001387 line[0], line[0]);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001388 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));
Philipp Maier8dc35972021-07-14 11:20:16 +02001389 return create_err_response(NULL, 539, "DLCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001390 break;
1391 }
1392 }
1393
Philipp Maierf486e742021-07-19 14:56:29 +02001394 /* Handle wildcarded DLCX that refers to the whole trunk. This means
1395 * that we walk over all endpoints on the trunk in order to drop all
1396 * connections on the trunk. (see also RFC3435 Annex F.7) */
1397 if (rq->wildcarded) {
1398 int num_conns = 0;
1399 for (i = 0; i < trunk->number_endpoints; i++) {
1400 num_conns += llist_count(&trunk->endpoints[i]->conns);
1401 mgcp_endp_release(trunk->endpoints[i]);
1402 }
1403 rate_ctr_add(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS), num_conns);
1404 return create_ok_response(NULL, 200, "DLCX", pdata->trans);
1405 }
1406
Philipp Maierce187052021-07-23 10:58:19 +02001407 /* The logic does not permit to go past this point without having the
1408 * the endp pointer populated. */
1409 OSMO_ASSERT(endp);
1410
Philipp Maierf4c0e372017-10-11 16:06:45 +02001411 /* When no connection id is supplied, we will interpret this as a
Philipp Maierf486e742021-07-19 14:56:29 +02001412 * wildcarded DLCX that refers to the selected endpoint. This means
1413 * that we drop all connections on that specific endpoint at once.
1414 * (See also RFC3435 Section F.7) */
Philipp Maier01d24a32017-11-21 17:26:09 +01001415 if (!conn_id) {
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001416 int num_conns = llist_count(&endp->conns);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001417 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1418 "DLCX: missing ci (connectionIdentifier), will remove all connections (%d total) at once\n",
1419 num_conns);
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001420
1421 if (num_conns > 0)
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001422 rate_ctr_add(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS), num_conns);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001423
Philipp Maier1355d7e2018-02-01 14:30:06 +01001424 mgcp_endp_release(endp);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001425
1426 /* Note: In this case we do not return any statistics,
1427 * as we assume that the client is not interested in
1428 * this case. */
Philipp Maier8dc35972021-07-14 11:20:16 +02001429 return create_ok_response(endp, 200, "DLCX", pdata->trans);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001430 }
1431
Philipp Maierf4c0e372017-10-11 16:06:45 +02001432 /* Find the connection */
Philipp Maier87bd9be2017-08-22 16:35:41 +02001433 conn = mgcp_conn_get_rtp(endp, conn_id);
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001434 if (!conn) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001435 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CONNID));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001436 goto error3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001437 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001438 /* save the statistics of the current connection */
1439 mgcp_format_stats(stats, sizeof(stats), conn->conn);
1440
1441 /* delete connection */
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001442 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG, "DLCX: deleting conn:%s\n",
1443 mgcp_conn_dump(conn->conn));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001444 mgcp_conn_free(endp, conn_id);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001445 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1446 "DLCX: connection successfully deleted\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +02001447
1448 /* When all connections are closed, the endpoint will be released
1449 * in order to be ready to be used by another call. */
1450 if (llist_count(&endp->conns) <= 0) {
Philipp Maier1355d7e2018-02-01 14:30:06 +01001451 mgcp_endp_release(endp);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001452 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: endpoint released\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +02001453 }
1454
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001455 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001456 if (silent)
1457 goto out_silent;
Philipp Maier8dc35972021-07-14 11:20:16 +02001458 return create_ok_resp_with_param(endp, 250, "DLCX", pdata->trans, stats);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001459
1460error3:
Philipp Maier8dc35972021-07-14 11:20:16 +02001461 return create_err_response(endp, error_code, "DLCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001462
1463out_silent:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001464 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: silent exit\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001465 return NULL;
1466}
1467
Philipp Maier87bd9be2017-08-22 16:35:41 +02001468/* RSIP command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +02001469static struct msgb *handle_rsip(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001470{
Philipp Maier87bd9be2017-08-22 16:35:41 +02001471 /* TODO: Also implement the resetting of a specific endpoint
1472 * to make mgcp_send_reset_ep() work. Currently this will call
1473 * mgcp_rsip_cb() in mgw_main.c, which sets reset_endpoints=1
1474 * to make read_call_agent() reset all endpoints when called
1475 * next time. In order to selectively reset endpoints some
1476 * mechanism to distinguish which endpoint shall be resetted
1477 * is needed */
1478
1479 LOGP(DLMGCP, LOGL_NOTICE, "RSIP: resetting all endpoints ...\n");
1480
Philipp Maier8dc35972021-07-14 11:20:16 +02001481 if (rq->pdata->cfg->reset_cb)
1482 rq->pdata->cfg->reset_cb(rq->endp->trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001483 return NULL;
1484}
1485
1486static char extract_tone(const char *line)
1487{
1488 const char *str = strstr(line, "D/");
1489 if (!str)
1490 return CHAR_MAX;
1491
1492 return str[2];
1493}
1494
Philipp Maier87bd9be2017-08-22 16:35:41 +02001495/* This can request like DTMF detection and forward, fax detection... it
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001496 * can also request when the notification should be send and such. We don't
Philipp Maier87bd9be2017-08-22 16:35:41 +02001497 * do this right now. */
Philipp Maier8dc35972021-07-14 11:20:16 +02001498static struct msgb *handle_noti_req(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001499{
1500 int res = 0;
1501 char *line;
1502 char tone = CHAR_MAX;
1503
Philipp Maier87bd9be2017-08-22 16:35:41 +02001504 LOGP(DLMGCP, LOGL_NOTICE, "RQNT: processing request for notification ...\n");
1505
Philipp Maier8dc35972021-07-14 11:20:16 +02001506 for_each_line(line, rq->pdata->save) {
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001507 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001508 case 'S':
1509 tone = extract_tone(line);
1510 break;
1511 }
1512 }
1513
1514 /* we didn't see a signal request with a tone */
1515 if (tone == CHAR_MAX)
Philipp Maier8dc35972021-07-14 11:20:16 +02001516 return create_ok_response(rq->endp, 200, "RQNT", rq->pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001517
Philipp Maier8dc35972021-07-14 11:20:16 +02001518 if (rq->pdata->cfg->rqnt_cb)
1519 res = rq->pdata->cfg->rqnt_cb(rq->endp, tone);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001520
1521 return res == 0 ?
Philipp Maier8dc35972021-07-14 11:20:16 +02001522 create_ok_response(rq->endp, 200, "RQNT", rq->pdata->trans) :
1523 create_err_response(rq->endp, res, "RQNT", rq->pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001524}
1525
Philipp Maier87bd9be2017-08-22 16:35:41 +02001526/* Connection keepalive timer, will take care that dummy packets are send
Harald Welte1d1b98f2017-12-25 10:03:40 +01001527 * regularly, so that NAT connections stay open */
Philipp Maier14b27a82020-06-02 20:15:30 +02001528static void mgcp_keepalive_timer_cb(void *_trunk)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001529{
Philipp Maier14b27a82020-06-02 20:15:30 +02001530 struct mgcp_trunk *trunk = _trunk;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001531 struct mgcp_conn *conn;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001532 int i;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001533
Philipp Maiere726d4f2017-11-01 10:41:34 +01001534 LOGP(DLMGCP, LOGL_DEBUG, "triggered trunk %d keepalive timer\n",
Philipp Maier14b27a82020-06-02 20:15:30 +02001535 trunk->trunk_nr);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001536
Philipp Maiere726d4f2017-11-01 10:41:34 +01001537 /* Do not accept invalid configuration values
1538 * valid is MGCP_KEEPALIVE_NEVER, MGCP_KEEPALIVE_ONCE and
1539 * values greater 0 */
Philipp Maier14b27a82020-06-02 20:15:30 +02001540 OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Philipp Maiere726d4f2017-11-01 10:41:34 +01001541
1542 /* The dummy packet functionality has been disabled, we will exit
1543 * immediately, no further timer is scheduled, which means we will no
1544 * longer send dummy packets even when we did before */
Philipp Maier14b27a82020-06-02 20:15:30 +02001545 if (trunk->keepalive_interval == MGCP_KEEPALIVE_NEVER)
Philipp Maiere726d4f2017-11-01 10:41:34 +01001546 return;
1547
1548 /* In cases where only one dummy packet is sent, we do not need
1549 * the timer since the functions that handle the CRCX and MDCX are
1550 * triggering the sending of the dummy packet. So we behave like in
1551 * the MGCP_KEEPALIVE_NEVER case */
Philipp Maier14b27a82020-06-02 20:15:30 +02001552 if (trunk->keepalive_interval == MGCP_KEEPALIVE_ONCE)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001553 return;
1554
Philipp Maier87bd9be2017-08-22 16:35:41 +02001555 /* Send walk over all endpoints and send out dummy packets through
1556 * every connection present on each endpoint */
Philipp Maier4131a652021-07-07 14:04:34 +02001557 for (i = 0; i < trunk->number_endpoints; ++i) {
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001558 struct mgcp_endpoint *endp = trunk->endpoints[i];
Philipp Maier87bd9be2017-08-22 16:35:41 +02001559 llist_for_each_entry(conn, &endp->conns, entry) {
Pau Espin Pedrolca280a12021-07-06 18:15:35 +02001560 if (conn->type == MGCP_CONN_TYPE_RTP &&
1561 conn->mode == MGCP_CONN_RECV_ONLY &&
1562 mgcp_rtp_end_remote_addr_available(&conn->u.rtp.end))
Philipp Maier87bd9be2017-08-22 16:35:41 +02001563 send_dummy(endp, &conn->u.rtp);
1564 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001565 }
1566
Philipp Maiere726d4f2017-11-01 10:41:34 +01001567 /* Schedule the keepalive timer for the next round */
1568 LOGP(DLMGCP, LOGL_DEBUG, "rescheduling trunk %d keepalive timer\n",
Philipp Maier14b27a82020-06-02 20:15:30 +02001569 trunk->trunk_nr);
1570 osmo_timer_schedule(&trunk->keepalive_timer, trunk->keepalive_interval,
Philipp Maier87bd9be2017-08-22 16:35:41 +02001571 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001572}
1573
Philipp Maier14b27a82020-06-02 20:15:30 +02001574void mgcp_trunk_set_keepalive(struct mgcp_trunk *trunk, int interval)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001575{
Philipp Maier14b27a82020-06-02 20:15:30 +02001576 trunk->keepalive_interval = interval;
1577 osmo_timer_setup(&trunk->keepalive_timer, mgcp_keepalive_timer_cb, trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001578
1579 if (interval <= 0)
Philipp Maier14b27a82020-06-02 20:15:30 +02001580 osmo_timer_del(&trunk->keepalive_timer);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001581 else
Philipp Maier14b27a82020-06-02 20:15:30 +02001582 osmo_timer_schedule(&trunk->keepalive_timer,
1583 trunk->keepalive_interval, 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001584}
1585
Philipp Maier87bd9be2017-08-22 16:35:41 +02001586/*! allocate configuration with default values.
1587 * (called once at startup by main function) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001588struct mgcp_config *mgcp_config_alloc(void)
1589{
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001590 /* FIXME: This is unrelated to the protocol, put this in some
1591 * appropiate place! */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001592 struct mgcp_config *cfg;
1593
1594 cfg = talloc_zero(NULL, struct mgcp_config);
1595 if (!cfg) {
1596 LOGP(DLMGCP, LOGL_FATAL, "Failed to allocate config.\n");
1597 return NULL;
1598 }
1599
Philipp Maier12943ea2018-01-17 15:40:25 +01001600 osmo_strlcpy(cfg->domain, "mgw", sizeof(cfg->domain));
1601
Philipp Maier87bd9be2017-08-22 16:35:41 +02001602 cfg->net_ports.range_start = RTP_PORT_DEFAULT_RANGE_START;
1603 cfg->net_ports.range_end = RTP_PORT_DEFAULT_RANGE_END;
1604 cfg->net_ports.last_port = cfg->net_ports.range_start;
1605
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001606 cfg->source_port = 2427;
1607 cfg->source_addr = talloc_strdup(cfg, "0.0.0.0");
1608 cfg->osmux_addr = talloc_strdup(cfg, "0.0.0.0");
1609
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001610 cfg->rtp_processing_cb = &mgcp_rtp_processing_default;
1611 cfg->setup_rtp_processing_cb = &mgcp_setup_rtp_processing_default;
1612
1613 cfg->get_net_downlink_format_cb = &mgcp_get_net_downlink_format_default;
1614
Philipp Maierd19de2e2020-06-03 13:55:33 +02001615 INIT_LLIST_HEAD(&cfg->trunks);
1616
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001617 /* Allocate virtual trunk */
Philipp Maierd19de2e2020-06-03 13:55:33 +02001618 if (!mgcp_trunk_alloc(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID)) {
Harald Welte3ac604e2019-05-08 14:07:41 +02001619 talloc_free(cfg);
1620 return NULL;
1621 }
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001622
Philipp Maiera065e632021-07-09 13:22:42 +02001623 mgcp_ratectr_global_alloc(cfg);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001624
1625 return cfg;
1626}
1627
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001628static int send_agent(struct mgcp_config *cfg, const char *buf, int len)
1629{
1630 return write(cfg->gw_fd.bfd.fd, buf, len);
1631}
1632
Philipp Maier87bd9be2017-08-22 16:35:41 +02001633/*! Reset all endpoints by sending RSIP message to self.
1634 * (called by VTY)
1635 * \param[in] endp trunk endpoint
1636 * \param[in] endpoint number
1637 * \returns 0 on success, -1 on error */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001638int mgcp_send_reset_all(struct mgcp_config *cfg)
1639{
Philipp Maier12943ea2018-01-17 15:40:25 +01001640 char buf[MGCP_ENDPOINT_MAXLEN + 128];
1641 int len;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001642 int rc;
1643
Philipp Maier12943ea2018-01-17 15:40:25 +01001644 len = snprintf(buf, sizeof(buf),
1645 "RSIP 1 *@%s MGCP 1.0\r\n", cfg->domain);
1646 if (len < 0)
1647 return -1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001648
Philipp Maier12943ea2018-01-17 15:40:25 +01001649 rc = send_agent(cfg, buf, len);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001650 if (rc <= 0)
1651 return -1;
1652
1653 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001654}
1655
Philipp Maier87bd9be2017-08-22 16:35:41 +02001656/*! Reset a single endpoint by sending RSIP message to self.
1657 * (called by VTY)
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001658 * \param[in] endp to reset
Philipp Maier87bd9be2017-08-22 16:35:41 +02001659 * \returns 0 on success, -1 on error */
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001660int mgcp_send_reset_ep(struct mgcp_endpoint *endp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001661{
Philipp Maier12943ea2018-01-17 15:40:25 +01001662 char buf[MGCP_ENDPOINT_MAXLEN + 128];
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001663 int len;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001664 int rc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001665
1666 len = snprintf(buf, sizeof(buf),
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001667 "RSIP 39 %s MGCP 1.0\r\n", endp->name);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001668 if (len < 0)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001669 return -1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001670
Philipp Maier87bd9be2017-08-22 16:35:41 +02001671 rc = send_agent(endp->cfg, buf, len);
1672 if (rc <= 0)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001673 return -1;
1674
Philipp Maier87bd9be2017-08-22 16:35:41 +02001675 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001676}