blob: 6341f07cfbe96bb61048c474e094ab1cacabed0b [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>
Eric55fdfc22021-08-13 00:14:18 +020031#include <pthread.h>
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020032
33#include <osmocom/core/msgb.h>
34#include <osmocom/core/talloc.h>
35#include <osmocom/core/select.h>
36
Philipp Maier87bd9be2017-08-22 16:35:41 +020037#include <osmocom/mgcp/mgcp.h>
Neels Hofmeyr67793542017-09-08 04:25:16 +020038#include <osmocom/mgcp/mgcp_common.h>
Philipp Maier993ea6b2020-08-04 18:26:50 +020039#include <osmocom/mgcp/osmux.h>
40#include <osmocom/mgcp/mgcp_network.h>
41#include <osmocom/mgcp/mgcp_protocol.h>
Philipp Maier87bd9be2017-08-22 16:35:41 +020042#include <osmocom/mgcp/mgcp_stat.h>
43#include <osmocom/mgcp/mgcp_msg.h>
Philipp Maier37d11c82018-02-01 14:38:12 +010044#include <osmocom/mgcp/mgcp_endp.h>
Philipp Maierc66ab2c2020-06-02 20:55:34 +020045#include <osmocom/mgcp/mgcp_trunk.h>
Philipp Maier8970c492017-10-11 13:33:42 +020046#include <osmocom/mgcp/mgcp_sdp.h>
Philipp Maierbc0346e2018-06-07 09:52:16 +020047#include <osmocom/mgcp/mgcp_codec.h>
Stefan Sperlingba25eab2018-10-30 14:32:31 +010048#include <osmocom/mgcp/mgcp_conn.h>
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020049
Philipp Maier39889e42021-08-04 17:42:57 +020050/* Contains the last successfully resolved endpoint name. This variable is used
51 * for the unit-tests to verify that the endpoint was correctly resolved. */
52static char debug_last_endpoint_name[MGCP_ENDPOINT_MAXLEN];
53
54/* Called from unit-tests only */
55char *mgcp_debug_get_last_endpoint_name(void)
56{
57 return debug_last_endpoint_name;
58}
59
Philipp Maierf486e742021-07-19 14:56:29 +020060/* A combination of LOGPENDP and LOGPTRUNK that automatically falls back to
61 * LOGPTRUNK when the endp parameter is NULL */
62#define LOGPEPTR(endp, trunk, cat, level, fmt, args...) \
63do { \
64 if (endp) \
65 LOGPENDP(endp, cat, level, fmt, ## args); \
66 else \
67 LOGPTRUNK(trunk, cat, level, fmt, ## args); \
68} while (0)
69
Philipp Maier8dc35972021-07-14 11:20:16 +020070/* Request data passed to the request handler */
71struct mgcp_request_data {
72 /* request name (e.g. "MDCX") */
73 char name[4+1];
74
75 /* parsing results from the MGCP header (trans id, endpoint name ...) */
76 struct mgcp_parse_data *pdata;
77
78 /* pointer to endpoint resource (may be NULL for wildcarded requests) */
79 struct mgcp_endpoint *endp;
80
81 /* pointer to trunk resource */
82 struct mgcp_trunk *trunk;
83
84 /* set to true when the request has been classified as wildcarded */
85 bool wildcarded;
86
87 /* contains cause code in case of problems during endp/trunk resolution */
88 int mgcp_cause;
89};
90
Philipp Maier33d97f72021-07-14 14:53:45 +020091/* Request handler specification, here we specify an array with function
92 * pointers to the various MGCP requests implemented below */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020093struct mgcp_request {
Philipp Maier33d97f72021-07-14 14:53:45 +020094 /* request name (e.g. "MDCX") */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020095 char *name;
Philipp Maier33d97f72021-07-14 14:53:45 +020096
97 /* function pointer to the request handler */
Philipp Maier8dc35972021-07-14 11:20:16 +020098 struct msgb *(*handle_request)(struct mgcp_request_data *data);
99
100 /* true if the request requires an endpoint, false if only a trunk
101 * is sufficient. (corner cases, e.g. wildcarded DLCX) */
102 bool require_endp;
Philipp Maier33d97f72021-07-14 14:53:45 +0200103
104 /* a human readable name that describes the request */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200105 char *debug_name;
106};
107
Philipp Maier8dc35972021-07-14 11:20:16 +0200108static struct msgb *handle_audit_endpoint(struct mgcp_request_data *data);
109static struct msgb *handle_create_con(struct mgcp_request_data *data);
110static struct msgb *handle_delete_con(struct mgcp_request_data *data);
111static struct msgb *handle_modify_con(struct mgcp_request_data *data);
112static struct msgb *handle_rsip(struct mgcp_request_data *data);
113static struct msgb *handle_noti_req(struct mgcp_request_data *data);
Philipp Maier33d97f72021-07-14 14:53:45 +0200114static const struct mgcp_request mgcp_requests[] = {
115 { .name = "AUEP",
116 .handle_request = handle_audit_endpoint,
Philipp Maier8dc35972021-07-14 11:20:16 +0200117 .debug_name = "AuditEndpoint",
118 .require_endp = true },
Philipp Maier33d97f72021-07-14 14:53:45 +0200119 { .name = "CRCX",
120 .handle_request = handle_create_con,
Philipp Maier8dc35972021-07-14 11:20:16 +0200121 .debug_name = "CreateConnection",
122 .require_endp = true },
Philipp Maier33d97f72021-07-14 14:53:45 +0200123 { .name = "DLCX",
124 .handle_request = handle_delete_con,
Philipp Maier8dc35972021-07-14 11:20:16 +0200125 .debug_name = "DeleteConnection",
Philipp Maierf486e742021-07-19 14:56:29 +0200126 .require_endp = false },
Philipp Maier33d97f72021-07-14 14:53:45 +0200127 { .name = "MDCX",
128 .handle_request = handle_modify_con,
Philipp Maier8dc35972021-07-14 11:20:16 +0200129 .debug_name = "ModifiyConnection",
130 .require_endp = true },
Philipp Maier33d97f72021-07-14 14:53:45 +0200131 { .name = "RQNT",
132 .handle_request = handle_noti_req,
Philipp Maier8dc35972021-07-14 11:20:16 +0200133 .debug_name = "NotificationRequest",
134 .require_endp = true },
Philipp Maier33d97f72021-07-14 14:53:45 +0200135
136 /* SPEC extension */
137 { .name = "RSIP",
138 .handle_request = handle_rsip,
Philipp Maier8dc35972021-07-14 11:20:16 +0200139 .debug_name = "ReSetInProgress",
140 .require_endp = true },
Philipp Maier33d97f72021-07-14 14:53:45 +0200141};
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200142
Philipp Maier87bd9be2017-08-22 16:35:41 +0200143/* Initalize transcoder */
144static int setup_rtp_processing(struct mgcp_endpoint *endp,
145 struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200146{
Ericfbf78d12021-08-23 22:31:39 +0200147 struct mgcp_config *cfg = endp->trunk->cfg;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200148 struct mgcp_conn_rtp *conn_src = NULL;
149 struct mgcp_conn_rtp *conn_dst = conn;
150 struct mgcp_conn *_conn;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200151
Pau Espin Pedrolfa810e82019-05-06 18:54:10 +0200152 if (conn->type != MGCP_RTP_DEFAULT && !mgcp_conn_rtp_is_osmux(conn)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200153 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
154 "RTP-setup: Endpoint is not configured as RTP default, stopping here!\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200155 return 0;
156 }
157
Philipp Maier87bd9be2017-08-22 16:35:41 +0200158 if (conn->conn->mode == MGCP_CONN_LOOPBACK) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200159 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
160 "RTP-setup: Endpoint is in loopback mode, stopping here!\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +0200161 return 0;
162 }
163
164 /* Find the "sister" connection */
165 llist_for_each_entry(_conn, &endp->conns, entry) {
166 if (_conn->id != conn->conn->id) {
167 conn_src = &_conn->u.rtp;
168 break;
169 }
170 }
171
Philipp Maieracc10352018-07-19 18:07:57 +0200172 return cfg->setup_rtp_processing_cb(endp, conn_dst, conn_src);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200173}
174
Philipp Maier87bd9be2017-08-22 16:35:41 +0200175/* Helper function to allocate some memory for responses and retransmissions */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200176static struct msgb *mgcp_msgb_alloc(void)
177{
178 struct msgb *msg;
179 msg = msgb_alloc_headroom(4096, 128, "MGCP msg");
180 if (!msg)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200181 LOGP(DLMGCP, LOGL_ERROR, "Failed to msgb for MGCP data.\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200182
183 return msg;
184}
185
Philipp Maier87bd9be2017-08-22 16:35:41 +0200186/* Helper function for do_retransmission() and create_resp() */
Eric958f5e72021-08-03 23:00:17 +0200187static struct msgb *create_retransmission_response(const struct mgcp_endpoint *endp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200188{
189 struct msgb *msg = mgcp_msgb_alloc();
190 if (!msg)
191 return NULL;
192
193 msg->l2h = msgb_put(msg, strlen(endp->last_response));
194 memcpy(msg->l2h, endp->last_response, msgb_l2len(msg));
Philipp Maier87bd9be2017-08-22 16:35:41 +0200195 mgcp_disp_msg(msg->l2h, msgb_l2len(msg), "Retransmitted response");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200196 return msg;
197}
198
199static struct msgb *create_resp(struct mgcp_endpoint *endp, int code,
200 const char *txt, const char *msg,
201 const char *trans, const char *param,
202 const char *sdp)
203{
204 int len;
205 struct msgb *res;
206
207 res = mgcp_msgb_alloc();
208 if (!res)
209 return NULL;
210
Philipp Maier87bd9be2017-08-22 16:35:41 +0200211 len = snprintf((char *)res->data, 2048, "%d %s%s%s\r\n%s",
212 code, trans, txt, param ? param : "", sdp ? sdp : "");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200213 if (len < 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200214 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "Failed to sprintf MGCP response.\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200215 msgb_free(res);
216 return NULL;
217 }
218
219 res->l2h = msgb_put(res, len);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200220 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "Generated response: code=%d\n", code);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200221 mgcp_disp_msg(res->l2h, msgb_l2len(res), "Generated response");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200222
223 /*
224 * Remember the last transmission per endpoint.
225 */
226 if (endp) {
Philipp Maier14b27a82020-06-02 20:15:30 +0200227 struct mgcp_trunk *trunk = endp->trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200228 talloc_free(endp->last_response);
229 talloc_free(endp->last_trans);
Philipp Maier14b27a82020-06-02 20:15:30 +0200230 endp->last_trans = talloc_strdup(trunk->endpoints, trans);
231 endp->last_response = talloc_strndup(trunk->endpoints,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200232 (const char *)res->l2h,
233 msgb_l2len(res));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200234 }
235
236 return res;
237}
238
239static struct msgb *create_ok_resp_with_param(struct mgcp_endpoint *endp,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200240 int code, const char *msg,
241 const char *trans,
242 const char *param)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200243{
244 return create_resp(endp, code, " OK", msg, trans, param, NULL);
245}
246
247static struct msgb *create_ok_response(struct mgcp_endpoint *endp,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200248 int code, const char *msg,
249 const char *trans)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200250{
251 return create_ok_resp_with_param(endp, code, msg, trans, NULL);
252}
253
254static struct msgb *create_err_response(struct mgcp_endpoint *endp,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200255 int code, const char *msg,
256 const char *trans)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200257{
258 return create_resp(endp, code, " FAIL", msg, trans, NULL, NULL);
259}
260
Philipp Maier87bd9be2017-08-22 16:35:41 +0200261/* Format MGCP response string (with SDP attached) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200262static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200263 struct mgcp_conn_rtp *conn,
264 const char *msg,
Philipp Maier55295f72018-01-15 14:00:28 +0100265 const char *trans_id,
Philipp Maier41d59202021-07-20 15:49:00 +0200266 bool add_epname,
267 bool add_conn_id)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200268{
Pau Espin Pedrol71d42e72020-09-03 14:20:07 +0200269 /* cfg->local_ip allows overwritting the announced IP address with
270 * regards to the one we actually bind to. Useful in behind-NAT
271 * scenarios.
272 * TODO: we may want to define another local_ip_osmux var to
273 * us for OSMUX connections. Perhaps adding a new internal API to get it
274 * based on conn type.
275 */
Ericfbf78d12021-08-23 22:31:39 +0200276 const char *addr = strlen(endp->trunk->cfg->local_ip) ? endp->trunk->cfg->local_ip : conn->end.local_addr;
Philipp Maier8970c492017-10-11 13:33:42 +0200277 struct msgb *sdp;
278 int rc;
279 struct msgb *result;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200280
Philipp Maier8970c492017-10-11 13:33:42 +0200281 sdp = msgb_alloc_headroom(4096, 128, "sdp record");
282 if (!sdp)
283 return NULL;
284
Philipp Maier41d59202021-07-20 15:49:00 +0200285 /* Attach optional endpoint name */
286 if (add_epname) {
287 rc = msgb_printf(sdp, "Z: %s\r\n", endp->name);
288 if (rc < 0)
289 goto error;
290 }
291
292 /* Attach optional connection id */
293 if (add_conn_id) {
294 rc = msgb_printf(sdp, "I: %s\r\n", conn->conn->id);
Philipp Maier55295f72018-01-15 14:00:28 +0100295 if (rc < 0)
296 goto error;
297 }
298
Philipp Maier3cbfb8a2018-01-22 11:39:59 +0100299 /* Attach optional OSMUX parameters */
Pau Espin Pedrolc63f15a2019-05-10 16:52:08 +0200300 if (mgcp_conn_rtp_is_osmux(conn)) {
Pau Espin Pedrol5e8d7992019-04-24 19:56:43 +0200301 rc = msgb_printf(sdp, "X-Osmux: %u\r\n", conn->osmux.cid);
Philipp Maier3cbfb8a2018-01-22 11:39:59 +0100302 if (rc < 0)
303 goto error;
304 }
305
306 /* Attach line break to separate the parameters from the SDP block */
Philipp Maierc3cfae22018-01-22 12:03:03 +0100307 rc = msgb_printf(sdp, "\r\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200308
Philipp Maier8970c492017-10-11 13:33:42 +0200309 rc = mgcp_write_response_sdp(endp, conn, sdp, addr);
310 if (rc < 0)
311 goto error;
312 result = create_resp(endp, 200, " OK", msg, trans_id, NULL, (char*) sdp->data);
313 msgb_free(sdp);
314 return result;
315error:
316 msgb_free(sdp);
317 return NULL;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200318}
319
Philipp Maier87bd9be2017-08-22 16:35:41 +0200320/* Send out dummy packet to keep the connection open, if the connection is an
321 * osmux connection, send the dummy packet via OSMUX */
322static void send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200323{
Philipp Maier87bd9be2017-08-22 16:35:41 +0200324 if (conn->osmux.state != OSMUX_STATE_DISABLED)
325 osmux_send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200326 else
Philipp Maier87bd9be2017-08-22 16:35:41 +0200327 mgcp_send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200328}
329
Philipp Maier87bd9be2017-08-22 16:35:41 +0200330/* handle incoming messages:
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200331 * - this can be a command (four letters, space, transaction id)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200332 * - or a response (three numbers, space, transaction id) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200333struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
334{
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200335 struct rate_ctr_group *rate_ctrs = cfg->ratectr.mgcp_general_ctr_group;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200336 struct mgcp_parse_data pdata;
Philipp Maier8dc35972021-07-14 11:20:16 +0200337 struct mgcp_request_data rq;
Harald Weltee35eeae2017-12-28 13:47:37 +0100338 int rc, i, code, handled = 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200339 struct msgb *resp = NULL;
340 char *data;
341
Philipp Maier39889e42021-08-04 17:42:57 +0200342 debug_last_endpoint_name[0] = '\0';
343
Alexander Chemeris63866002020-05-05 17:18:40 +0300344 /* Count all messages, even incorect ones */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200345 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_TOTAL));
Alexander Chemeris63866002020-05-05 17:18:40 +0300346
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200347 if (msgb_l2len(msg) < 4) {
348 LOGP(DLMGCP, LOGL_ERROR, "msg too short: %d\n", msg->len);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200349 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200350 return NULL;
351 }
352
Alexander Chemeris63866002020-05-05 17:18:40 +0300353 if (mgcp_msg_terminate_nul(msg)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200354 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200355 return NULL;
Alexander Chemeris63866002020-05-05 17:18:40 +0300356 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200357
Philipp Maier87bd9be2017-08-22 16:35:41 +0200358 mgcp_disp_msg(msg->l2h, msgb_l2len(msg), "Received message");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200359
Philipp Maier87bd9be2017-08-22 16:35:41 +0200360 /* attempt to treat it as a response */
361 if (sscanf((const char *)&msg->l2h[0], "%3d %*s", &code) == 1) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200362 LOGP(DLMGCP, LOGL_DEBUG, "Response: Code: %d\n", code);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200363 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200364 return NULL;
365 }
366
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200367
Philipp Maier8dc35972021-07-14 11:20:16 +0200368 /* Parse message, extract endpoint name and transaction identifier and request name etc. */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200369 memset(&pdata, 0, sizeof(pdata));
Philipp Maier8dc35972021-07-14 11:20:16 +0200370 memset(&rq, 0, sizeof(rq));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200371 pdata.cfg = cfg;
Philipp Maier8dc35972021-07-14 11:20:16 +0200372 memcpy(rq.name, (const char *)&msg->l2h[0], sizeof(rq.name)-1);
373 msg->l3h = &msg->l2h[4];
Philipp Maier87bd9be2017-08-22 16:35:41 +0200374 data = mgcp_strline((char *)msg->l3h, &pdata.save);
Harald Weltee35eeae2017-12-28 13:47:37 +0100375 rc = mgcp_parse_header(&pdata, data);
Harald Weltee35eeae2017-12-28 13:47:37 +0100376 if (rc < 0) {
Philipp Maier8dc35972021-07-14 11:20:16 +0200377 LOGP(DLMGCP, LOGL_ERROR, "%s: failed to parse MCGP message\n", rq.name);
378 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
379 return create_err_response(NULL, -rc, rq.name, "000000");
Harald Welteabbb6b92017-12-28 13:13:50 +0100380 }
381
Philipp Maier8dc35972021-07-14 11:20:16 +0200382 /* Locate endpoint and trunk, if no endpoint can be located try at least to identify the trunk. */
383 rq.pdata = &pdata;
384 rq.wildcarded = mgcp_endp_is_wildcarded(pdata.epname);
385 rq.endp = mgcp_endp_by_name(&rc, pdata.epname, pdata.cfg);
386 rq.mgcp_cause = rc;
387 if (!rq.endp) {
388 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_NO_ENDPOINT));
389 if (rq.wildcarded) {
390 /* If we are unable to find the endpoint we still may be able to identify the trunk. Some
391 * request handlers will still be able to perform a useful action if the request refers to
392 * the whole trunk (wildcarded request). */
393 LOGP(DLMGCP, LOGL_NOTICE,
394 "%s: cannot find endpoint \"%s\", cause=%d -- trying to identify trunk...\n", rq.name,
395 pdata.epname, -rq.mgcp_cause);
396 rq.trunk = mgcp_trunk_by_name(pdata.cfg, pdata.epname);
397 if (!rq.trunk) {
398 LOGP(DLMGCP, LOGL_ERROR, "%s: failed to identify trunk for endpoint \"%s\" -- abort\n",
399 rq.name, pdata.epname);
400 return create_err_response(NULL, -rq.mgcp_cause, rq.name, pdata.trans);
401 }
402 } else {
403 /* If the endpoint name suggests that the request refers to a specific endpoint, then the
404 * request cannot be handled and we must stop early. */
405 LOGP(DLMGCP, LOGL_NOTICE,
406 "%s: cannot find endpoint \"%s\", cause=%d -- abort\n", rq.name,
407 pdata.epname, -rq.mgcp_cause);
408 return create_err_response(NULL, -rq.mgcp_cause, rq.name, pdata.trans);
409 }
410 } else {
Philipp Maier39889e42021-08-04 17:42:57 +0200411 osmo_strlcpy(debug_last_endpoint_name, rq.endp->name, sizeof(debug_last_endpoint_name));
Philipp Maier8dc35972021-07-14 11:20:16 +0200412 rq.trunk = rq.endp->trunk;
413 rq.mgcp_cause = 0;
414
415 /* Check if we have to retransmit a response from a previous transaction */
416 if (pdata.trans && rq.endp->last_trans && strcmp(rq.endp->last_trans, pdata.trans) == 0) {
417 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_RETRANSMITTED));
Eric958f5e72021-08-03 23:00:17 +0200418 return create_retransmission_response(rq.endp);
Philipp Maier8dc35972021-07-14 11:20:16 +0200419 }
420 }
421
422 /* Find an appropriate handler for the current request and execute it */
423 for (i = 0; i < ARRAY_SIZE(mgcp_requests); i++) {
424 if (strcmp(mgcp_requests[i].name, rq.name) == 0) {
425 /* Check if the request requires and endpoint, if yes, check if we have it, otherwise don't
426 * execute the request handler. */
427 if (mgcp_requests[i].require_endp && !rq.endp) {
428 LOGP(DLMGCP, LOGL_ERROR,
429 "%s: the request handler \"%s\" requires an endpoint resource for \"%s\", which is not available -- abort\n",
430 rq.name, mgcp_requests[i].debug_name, pdata.epname);
431 return create_err_response(NULL, -rq.mgcp_cause, rq.name, pdata.trans);
432 }
433
434 /* Execute request handler */
435 if (rq.endp)
436 LOGP(DLMGCP, LOGL_INFO,
437 "%s: executing request handler \"%s\" for endpoint resource \"%s\"\n", rq.name,
438 mgcp_requests[i].debug_name, rq.endp->name);
439 else
440 LOGP(DLMGCP, LOGL_INFO,
441 "%s: executing request handler \"%s\" for trunk resource of endpoint \"%s\"\n",
442 rq.name, mgcp_requests[i].debug_name, pdata.epname);
443 resp = mgcp_requests[i].handle_request(&rq);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200444 handled = 1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200445 break;
446 }
447 }
448
Philipp Maier8dc35972021-07-14 11:20:16 +0200449 /* Check if the MGCP request was handled and increment rate counters accordingly. */
Alexander Chemeris63866002020-05-05 17:18:40 +0300450 if (handled) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200451 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_HANDLED));
Alexander Chemeris63866002020-05-05 17:18:40 +0300452 } else {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200453 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_UNHANDLED));
Philipp Maier8dc35972021-07-14 11:20:16 +0200454 LOGP(DLMGCP, LOGL_ERROR, "MSG with type: '%.4s' not handled\n", &msg->l2h[0]);
Alexander Chemeris63866002020-05-05 17:18:40 +0300455 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200456
457 return resp;
458}
459
Philipp Maier87bd9be2017-08-22 16:35:41 +0200460/* AUEP command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +0200461static struct msgb *handle_audit_endpoint(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200462{
Philipp Maier8dc35972021-07-14 11:20:16 +0200463 LOGPENDP(rq->endp, DLMGCP, LOGL_NOTICE, "AUEP: auditing endpoint ...\n");
464 return create_ok_response(rq->endp, 200, "AUEP", rq->pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200465}
466
Harald Welte1d1b98f2017-12-25 10:03:40 +0100467/* Try to find a free port by attempting to bind on it. Also handle the
Philipp Maier87bd9be2017-08-22 16:35:41 +0200468 * counter that points on the next free port. Since we have a pointer
Philipp Maierb38fb892018-05-22 13:52:21 +0200469 * to the next free port, binding should in work on the first attempt in
Pau Espin Pedrolfc806732019-04-23 00:18:43 +0200470 * general. In case of failure the next port is tried until the whole port
471 * range is tried once. */
Philipp Maier87bd9be2017-08-22 16:35:41 +0200472static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200473{
474 int i;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200475 struct mgcp_port_range *range;
Philipp Maierb38fb892018-05-22 13:52:21 +0200476 unsigned int tries;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200477
Philipp Maier87bd9be2017-08-22 16:35:41 +0200478 OSMO_ASSERT(conn);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200479
Ericfbf78d12021-08-23 22:31:39 +0200480 range = &endp->trunk->cfg->net_ports;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200481
Eric55fdfc22021-08-13 00:14:18 +0200482 pthread_mutex_lock(&range->lock);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200483 /* attempt to find a port */
Philipp Maierb38fb892018-05-22 13:52:21 +0200484 tries = (range->range_end - range->range_start) / 2;
485 for (i = 0; i < tries; ++i) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200486 int rc;
487
488 if (range->last_port >= range->range_end)
489 range->last_port = range->range_start;
490
Philipp Maier87bd9be2017-08-22 16:35:41 +0200491 rc = mgcp_bind_net_rtp_port(endp, range->last_port, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200492
493 range->last_port += 2;
494 if (rc == 0) {
Eric55fdfc22021-08-13 00:14:18 +0200495 pthread_mutex_unlock(&range->lock);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200496 return 0;
497 }
498
499 }
Eric55fdfc22021-08-13 00:14:18 +0200500 pthread_mutex_unlock(&range->lock);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200501 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
502 "Allocating a RTP/RTCP port failed %u times.\n",
503 tries);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200504 return -1;
505}
506
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200507/*! Helper function for check_local_cx_options() to get a pointer of the next
508 * lco option identifier
509 * \param[in] lco string
510 * \returns pointer to the beginning of the LCO identifier, NULL on failure */
511char *get_lco_identifier(const char *options)
512{
513 char *ptr;
514 unsigned int count = 0;
515
516 /* Jump to the end of the lco identifier */
517 ptr = strstr(options, ":");
518 if (!ptr)
519 return NULL;
520
521 /* Walk backwards until the pointer points to the beginning of the
522 * lco identifier. We know that we stand at the beginning when we
523 * are either at the beginning of the memory or see a space or
524 * comma. (this is tolerant, it will accept a:10, b:11 as well as
525 * a:10,b:11) */
526 while (1) {
527 /* Endless loop protection */
528 if (count > 10000)
529 return NULL;
530 else if (ptr < options || *ptr == ' ' || *ptr == ',') {
531 ptr++;
532 break;
533 }
534 ptr--;
535 count++;
536 }
537
538 /* Check if we got any result */
539 if (*ptr == ':')
540 return NULL;
541
542 return ptr;
543}
544
545/*! Check the LCO option. This function checks for multiple appearence of LCO
546 * options, which is illegal
547 * \param[in] ctx talloc context
548 * \param[in] lco string
549 * \returns 0 on success, -1 on failure */
550int check_local_cx_options(void *ctx, const char *options)
551{
552 int i;
553 char *options_copy;
554 char *lco_identifier;
555 char *lco_identifier_end;
556 char *next_lco_identifier;
557
558 char **lco_seen;
559 unsigned int lco_seen_n = 0;
560
561 if (!options)
562 return -1;
563
564 lco_seen =
565 (char **)talloc_zero_size(ctx, strlen(options) * sizeof(char *));
566 options_copy = talloc_strdup(ctx, options);
567 lco_identifier = options_copy;
568
569 do {
570 /* Move the lco_identifier pointer to the beginning of the
571 * current lco option identifier */
572 lco_identifier = get_lco_identifier(lco_identifier);
573 if (!lco_identifier)
574 goto error;
575
576 /* Look ahead to the next LCO option early, since we
577 * will parse destructively */
578 next_lco_identifier = strstr(lco_identifier + 1, ",");
579
580 /* Pinch off the end of the lco field identifier name
581 * and see if we still got something, also check if
582 * there is some value after the colon. */
583 lco_identifier_end = strstr(lco_identifier, ":");
584 if (!lco_identifier_end)
585 goto error;
586 if (*(lco_identifier_end + 1) == ' '
587 || *(lco_identifier_end + 1) == ','
588 || *(lco_identifier_end + 1) == '\0')
589 goto error;
590 *lco_identifier_end = '\0';
591 if (strlen(lco_identifier) == 0)
592 goto error;
593
594 /* Check if we have already seen the current field identifier
595 * before. If yes, we must bail, an LCO must only appear once
596 * in the LCO string */
597 for (i = 0; i < lco_seen_n; i++) {
Pau Espin Pedrol7eb6f2c2019-06-26 13:00:52 +0200598 if (strcasecmp(lco_seen[i], lco_identifier) == 0)
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200599 goto error;
600 }
601 lco_seen[lco_seen_n] = lco_identifier;
602 lco_seen_n++;
603
604 /* The first identifier must always be found at the beginnning
605 * of the LCO string */
606 if (lco_seen[0] != options_copy)
607 goto error;
608
609 /* Go to the next lco option */
610 lco_identifier = next_lco_identifier;
611 } while (lco_identifier);
612
613 talloc_free(lco_seen);
614 talloc_free(options_copy);
615 return 0;
616error:
617 talloc_free(lco_seen);
618 talloc_free(options_copy);
619 return -1;
620}
621
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200622/* Set the LCO from a string (see RFC 3435).
Harald Welte1d1b98f2017-12-25 10:03:40 +0100623 * The string is stored in the 'string' field. A NULL string is handled exactly
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200624 * like an empty string, the 'string' field is never NULL after this function
625 * has been called. */
Philipp Maiera390d0b2018-01-31 17:30:19 +0100626static int set_local_cx_options(void *ctx, struct mgcp_lco *lco,
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200627 const char *options)
628{
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200629 char *lco_id;
Philipp Maier8dbc9ed2018-10-26 14:50:25 +0200630 char codec[17];
Pau Espin Pedrol19539862020-09-21 11:43:41 +0200631 char nt[17];
Pau Espin Pedrol83fd8a52019-06-26 12:55:26 +0200632 int len;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200633
Philipp Maier604410c2018-06-06 10:02:16 +0200634 if (!options)
635 return 0;
636 if (strlen(options) == 0)
637 return 0;
638
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200639 /* Make sure the encoding of the LCO is consistant before we proceed */
640 if (check_local_cx_options(ctx, options) != 0) {
641 LOGP(DLMGCP, LOGL_ERROR,
642 "local CX options: Internal inconsistency in Local Connection Options!\n");
643 return 524;
644 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200645
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200646 talloc_free(lco->string);
647 lco->string = talloc_strdup(ctx, options);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200648
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200649 lco_id = lco->string;
650 while ((lco_id = get_lco_identifier(lco_id))) {
651 switch (tolower(lco_id[0])) {
652 case 'p':
653 if (sscanf(lco_id + 1, ":%d-%d",
654 &lco->pkt_period_min, &lco->pkt_period_max) == 1)
655 lco->pkt_period_max = lco->pkt_period_min;
656 break;
657 case 'a':
Pau Espin Pedrol1dc2dce2020-09-21 11:25:15 +0200658 /* FIXME: LCO also supports the negotiation of more than one codec.
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200659 * (e.g. a:PCMU;G726-32) But this implementation only supports a single
660 * codec only. */
661 if (sscanf(lco_id + 1, ":%16[^,]", codec) == 1) {
662 talloc_free(lco->codec);
Pau Espin Pedrol83fd8a52019-06-26 12:55:26 +0200663 /* MGCP header is case insensive, and we'll need
664 codec in uppercase when using it later: */
665 len = strlen(codec);
666 lco->codec = talloc_size(ctx, len + 1);
667 osmo_str_toupper_buf(lco->codec, len + 1, codec);
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200668 }
669 break;
Pau Espin Pedrol19539862020-09-21 11:43:41 +0200670 case 'n':
671 if (lco_id[1] == 't' && sscanf(lco_id + 2, ":%16[^,]", nt) == 1)
672 break;
673 /* else: fall throught to print notice log */
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200674 default:
675 LOGP(DLMGCP, LOGL_NOTICE,
676 "LCO: unhandled option: '%c'/%d in \"%s\"\n",
677 *lco_id, *lco_id, lco->string);
678 break;
679 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200680
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200681 lco_id = strchr(lco_id, ',');
682 if (!lco_id)
683 break;
Philipp Maier604410c2018-06-06 10:02:16 +0200684 }
Philipp Maier87bd9be2017-08-22 16:35:41 +0200685
686 LOGP(DLMGCP, LOGL_DEBUG,
687 "local CX options: lco->pkt_period_max: %i, lco->codec: %s\n",
688 lco->pkt_period_max, lco->codec);
Philipp Maiera390d0b2018-01-31 17:30:19 +0100689
690 /* Check if the packetization fits the 20ms raster */
691 if (lco->pkt_period_min % 20 && lco->pkt_period_max % 20) {
692 LOGP(DLMGCP, LOGL_ERROR,
693 "local CX options: packetization interval is not a multiple of 20ms!\n");
694 return 535;
695 }
696
697 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200698}
699
700void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
701 struct mgcp_rtp_end *rtp)
702{
Philipp Maier14b27a82020-06-02 20:15:30 +0200703 struct mgcp_trunk *trunk = endp->trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200704
Philipp Maier14b27a82020-06-02 20:15:30 +0200705 int patch_ssrc = expect_ssrc_change && trunk->force_constant_ssrc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200706
Philipp Maier14b27a82020-06-02 20:15:30 +0200707 rtp->force_aligned_timing = trunk->force_aligned_timing;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200708 rtp->force_constant_ssrc = patch_ssrc ? 1 : 0;
Philipp Maier14b27a82020-06-02 20:15:30 +0200709 rtp->rfc5993_hr_convert = trunk->rfc5993_hr_convert;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200710
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200711 LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
712 "Configuring RTP endpoint: local port %d%s%s\n",
713 ntohs(rtp->rtp_port),
714 rtp->force_aligned_timing ? ", force constant timing" : "",
715 rtp->force_constant_ssrc ? ", force constant ssrc" : "");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200716}
717
Pau Espin Pedrol8358c4b2021-07-07 12:41:38 +0200718uint32_t mgcp_rtp_packet_duration(const struct mgcp_endpoint *endp,
719 const struct mgcp_rtp_end *rtp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200720{
721 int f = 0;
722
723 /* Get the number of frames per channel and packet */
724 if (rtp->frames_per_packet)
725 f = rtp->frames_per_packet;
Philipp Maierbc0346e2018-06-07 09:52:16 +0200726 else if (rtp->packet_duration_ms && rtp->codec->frame_duration_num) {
727 int den = 1000 * rtp->codec->frame_duration_num;
728 f = (rtp->packet_duration_ms * rtp->codec->frame_duration_den +
Philipp Maier87bd9be2017-08-22 16:35:41 +0200729 den / 2)
730 / den;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200731 }
732
Philipp Maierbc0346e2018-06-07 09:52:16 +0200733 return rtp->codec->rate * f * rtp->codec->frame_duration_num /
734 rtp->codec->frame_duration_den;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200735}
736
Pau Espin Pedrol9fb8ddf2019-05-08 15:35:36 +0200737/*! Initializes osmux socket if not yet initialized. Parses Osmux CID from MGCP line.
738 * \param[in] endp Endpoint willing to initialize osmux
739 * \param[in] line Line X-Osmux from MGCP header msg to parse
740 * \returns OSMUX CID, -1 for wildcard, -2 on parse error, -3 on osmux initalize error
741 */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200742static int mgcp_osmux_setup(struct mgcp_endpoint *endp, const char *line)
743{
Ericfbf78d12021-08-23 22:31:39 +0200744 if (!endp->trunk->cfg->osmux_init) {
745 if (osmux_init(OSMUX_ROLE_BSC, endp->trunk->cfg) < 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200746 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "Cannot init OSMUX\n");
Pau Espin Pedrol9fb8ddf2019-05-08 15:35:36 +0200747 return -3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200748 }
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200749 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "OSMUX socket has been set up\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200750 }
751
752 return mgcp_parse_osmux_cid(line);
753}
754
Philipp Maierbc0346e2018-06-07 09:52:16 +0200755/* Process codec information contained in CRCX/MDCX */
756static int handle_codec_info(struct mgcp_conn_rtp *conn,
Philipp Maier8dc35972021-07-14 11:20:16 +0200757 struct mgcp_request_data *rq, int have_sdp, bool crcx)
Philipp Maierbc0346e2018-06-07 09:52:16 +0200758{
Philipp Maier8dc35972021-07-14 11:20:16 +0200759 struct mgcp_endpoint *endp = rq->endp;
Philipp Maierbc0346e2018-06-07 09:52:16 +0200760 int rc;
761 char *cmd;
762
763 if (crcx)
764 cmd = "CRCX";
765 else
766 cmd = "MDCX";
767
768 /* Collect codec information */
769 if (have_sdp) {
770 /* If we have SDP, we ignore the local connection options and
771 * use only the SDP information. */
772 mgcp_codec_reset_all(conn);
Philipp Maier8dc35972021-07-14 11:20:16 +0200773 rc = mgcp_parse_sdp_data(endp, conn, rq->pdata);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200774 if (rc != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200775 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
776 "%s: sdp not parseable\n", cmd);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200777
778 /* See also RFC 3661: Protocol error */
779 return 510;
780 }
781 } else if (endp->local_options.codec) {
782 /* When no SDP is available, we use the codec information from
783 * the local connection options (if present) */
784 mgcp_codec_reset_all(conn);
Philipp Maier228e5912019-03-05 13:56:59 +0100785 rc = mgcp_codec_add(conn, PTYPE_UNDEFINED, endp->local_options.codec, NULL);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200786 if (rc != 0)
787 goto error;
788 }
789
790 /* Make sure we always set a sane default codec */
791 if (conn->end.codecs_assigned == 0) {
792 /* When SDP and/or LCO did not supply any codec information,
793 * than it makes sense to pick a sane default: (payload-type 0,
794 * PCMU), see also: OS#2658 */
795 mgcp_codec_reset_all(conn);
Philipp Maier228e5912019-03-05 13:56:59 +0100796 rc = mgcp_codec_add(conn, 0, NULL, NULL);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200797 if (rc != 0)
798 goto error;
799 }
800
801 /* Make codec decision */
802 if (mgcp_codec_decide(conn) != 0)
803 goto error;
804
805 return 0;
806
807error:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200808 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
809 "%s: codec negotiation failure\n", cmd);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200810
811 /* See also RFC 3661: Codec negotiation failure */
812 return 534;
813}
814
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200815static bool parse_x_osmo_ign(struct mgcp_endpoint *endp, char *line)
816{
817 char *saveptr = NULL;
818
Pau Espin Pedrol6049a632020-09-21 11:03:21 +0200819 if (strncasecmp(line, MGCP_X_OSMO_IGN_HEADER, strlen(MGCP_X_OSMO_IGN_HEADER)))
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200820 return false;
821 line += strlen(MGCP_X_OSMO_IGN_HEADER);
822
823 while (1) {
824 char *token = strtok_r(line, " ", &saveptr);
825 line = NULL;
826 if (!token)
827 break;
828
Pau Espin Pedrol6e26c702019-06-26 13:09:39 +0200829 if (!strcasecmp(token, "C"))
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200830 endp->x_osmo_ign |= MGCP_X_OSMO_IGN_CALLID;
831 else
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200832 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "received unknown X-Osmo-IGN item '%s'\n", token);
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200833 }
834
835 return true;
836}
837
Philipp Maier87bd9be2017-08-22 16:35:41 +0200838/* CRCX command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +0200839static struct msgb *handle_create_con(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200840{
Philipp Maier8dc35972021-07-14 11:20:16 +0200841 struct mgcp_parse_data *pdata = rq->pdata;
842 struct mgcp_trunk *trunk = rq->trunk;
843 struct mgcp_endpoint *endp = rq->endp;
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200844 struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_crcx_ctr_group;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200845 int error_code = 400;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200846 const char *local_options = NULL;
847 const char *callid = NULL;
848 const char *mode = NULL;
849 char *line;
Pau Espin Pedrol2b896172019-04-24 13:47:23 +0200850 int have_sdp = 0, osmux_cid = -2;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200851 struct mgcp_conn_rtp *conn = NULL;
Philipp Maierffd75e42017-11-22 11:44:50 +0100852 struct mgcp_conn *_conn = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200853 char conn_name[512];
Philipp Maiera390d0b2018-01-31 17:30:19 +0100854 int rc;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200855
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200856 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "CRCX: creating new connection ...\n");
Philipp Maier246233d2020-08-18 15:15:24 +0200857
Philipp Maier8d6a1932020-06-18 12:19:31 +0200858 if (!mgcp_endp_avail(endp)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200859 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_AVAIL));
Philipp Maiera910a812020-08-18 15:13:33 +0200860 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
861 "CRCX: selected endpoint not available!\n");
Philipp Maier8dc35972021-07-14 11:20:16 +0200862 return create_err_response(NULL, 501, "CRCX", pdata->trans);
Philipp Maier8d6a1932020-06-18 12:19:31 +0200863 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200864
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200865 /* parse CallID C: and LocalParameters L: */
Philipp Maier8dc35972021-07-14 11:20:16 +0200866 for_each_line(line, pdata->save) {
Philipp Maier036612b2021-07-19 17:47:49 +0200867 if (!mgcp_check_param(endp, trunk, line))
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200868 continue;
869
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +0200870 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200871 case 'L':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200872 local_options = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200873 break;
874 case 'C':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200875 callid = (const char *)line + 3;
876 break;
877 case 'I':
Philipp Maierffd75e42017-11-22 11:44:50 +0100878 /* It is illegal to send a connection identifier
879 * together with a CRCX, the MGW will assign the
880 * connection identifier by itself on CRCX */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200881 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BAD_ACTION));
Philipp Maier8dc35972021-07-14 11:20:16 +0200882 return create_err_response(NULL, 523, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200883 break;
884 case 'M':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200885 mode = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200886 break;
887 case 'X':
Pau Espin Pedrolc1bf4692019-05-14 16:23:24 +0200888 if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200889 /* If osmux is disabled, just skip setting it up */
Ericfbf78d12021-08-23 22:31:39 +0200890 if (!rq->endp->trunk->cfg->osmux)
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200891 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200892 osmux_cid = mgcp_osmux_setup(endp, line);
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200893 break;
894 }
895
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200896 if (parse_x_osmo_ign(endp, line))
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200897 break;
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200898
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200899 /* Ignore unknown X-headers */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200900 break;
901 case '\0':
902 have_sdp = 1;
903 goto mgcp_header_done;
904 default:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200905 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
906 "CRCX: unhandled option: '%c'/%d\n", *line, *line);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200907 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_UNHANDLED_PARAM));
Philipp Maier8dc35972021-07-14 11:20:16 +0200908 return create_err_response(NULL, 539, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200909 break;
910 }
911 }
912
913mgcp_header_done:
Philipp Maier87bd9be2017-08-22 16:35:41 +0200914 /* Check parameters */
915 if (!callid) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200916 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
917 "CRCX: insufficient parameters, missing callid\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200918 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_MISSING_CALLID));
Philipp Maier8dc35972021-07-14 11:20:16 +0200919 return create_err_response(endp, 516, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200920 }
921
Philipp Maier87bd9be2017-08-22 16:35:41 +0200922 if (!mode) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200923 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
924 "CRCX: insufficient parameters, missing mode\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200925 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_MODE));
Philipp Maier8dc35972021-07-14 11:20:16 +0200926 return create_err_response(endp, 517, "CRCX", pdata->trans);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200927 }
928
Philipp Maier87bd9be2017-08-22 16:35:41 +0200929 /* Check if we are able to accept the creation of another connection */
930 if (llist_count(&endp->conns) >= endp->type->max_conns) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200931 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
932 "CRCX: endpoint full, max. %i connections allowed!\n",
933 endp->type->max_conns);
Philipp Maier14b27a82020-06-02 20:15:30 +0200934 if (trunk->force_realloc) {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200935 /* There is no more room for a connection, make some
936 * room by blindly tossing the oldest of the two two
937 * connections */
938 mgcp_conn_free_oldest(endp);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200939 } else {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200940 /* There is no more room for a connection, leave
941 * everything as it is and return with an error */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200942 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_LIMIT_EXCEEDED));
Philipp Maier8dc35972021-07-14 11:20:16 +0200943 return create_err_response(endp, 540, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200944 }
945 }
946
Philipp Maier87bd9be2017-08-22 16:35:41 +0200947 /* Check if this endpoint already serves a call, if so, check if the
948 * callids match up so that we are sure that this is our call */
949 if (endp->callid && mgcp_verify_call_id(endp, callid)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200950 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
951 "CRCX: already seized by other call (%s)\n",
952 endp->callid);
Philipp Maier14b27a82020-06-02 20:15:30 +0200953 if (trunk->force_realloc)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200954 /* This is not our call, toss everything by releasing
955 * the entire endpoint. (rude!) */
Philipp Maier1355d7e2018-02-01 14:30:06 +0100956 mgcp_endp_release(endp);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200957 else {
958 /* This is not our call, leave everything as it is and
959 * return with an error. */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200960 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_UNKNOWN_CALLID));
Philipp Maier8dc35972021-07-14 11:20:16 +0200961 return create_err_response(endp, 400, "CRCX", pdata->trans);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200962 }
963 }
964
Philipp Maier889fe7f2020-07-06 17:44:12 +0200965 if (!endp->callid) {
966 /* Claim endpoint resources. This will also set the callid,
967 * creating additional connections will only be possible if
968 * the callid matches up (see above). */
969 rc = mgcp_endp_claim(endp, callid);
970 if (rc != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200971 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_CLAIM));
Philipp Maier8dc35972021-07-14 11:20:16 +0200972 return create_err_response(endp, 502, "CRCX", pdata->trans);
Philipp Maier889fe7f2020-07-06 17:44:12 +0200973 }
974 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200975
Philipp Maierffd75e42017-11-22 11:44:50 +0100976 snprintf(conn_name, sizeof(conn_name), "%s", callid);
Philipp Maier14b27a82020-06-02 20:15:30 +0200977 _conn = mgcp_conn_alloc(trunk->endpoints, endp, MGCP_CONN_TYPE_RTP, conn_name);
Philipp Maierffd75e42017-11-22 11:44:50 +0100978 if (!_conn) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200979 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
980 "CRCX: unable to allocate RTP connection\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200981 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_ALLOC_CONN));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200982 goto error2;
983
Philipp Maier87bd9be2017-08-22 16:35:41 +0200984 }
Philipp Maier889fe7f2020-07-06 17:44:12 +0200985
Philipp Maierffd75e42017-11-22 11:44:50 +0100986 conn = mgcp_conn_get_rtp(endp, _conn->id);
987 OSMO_ASSERT(conn);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200988
989 if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
990 error_code = 517;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200991 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_MODE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200992 goto error2;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200993 }
994
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200995 /* Annotate Osmux circuit ID and set it to negotiating state until this
Philipp Maier87bd9be2017-08-22 16:35:41 +0200996 * is fully set up from the dummy load. */
997 conn->osmux.state = OSMUX_STATE_DISABLED;
Pau Espin Pedrol2b896172019-04-24 13:47:23 +0200998 if (osmux_cid >= -1) { /* -1 is wilcard, alloc next avail CID */
Pau Espin Pedrol14f8a082019-05-13 13:10:06 +0200999 conn->osmux.state = OSMUX_STATE_ACTIVATING;
Pau Espin Pedrol2b896172019-04-24 13:47:23 +02001000 if (conn_osmux_allocate_cid(conn, osmux_cid) == -1) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001001 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_OSMUX));
Pau Espin Pedrol2b896172019-04-24 13:47:23 +02001002 goto error2;
1003 }
Ericfbf78d12021-08-23 22:31:39 +02001004 } else if (endp->trunk->cfg->osmux == OSMUX_USAGE_ONLY) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001005 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
1006 "CRCX: osmux only and no osmux offered\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001007 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_OSMUX));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001008 goto error2;
1009 }
1010
Philipp Maierbc0346e2018-06-07 09:52:16 +02001011 /* Set local connection options, if present */
1012 if (local_options) {
Philipp Maier8dc35972021-07-14 11:20:16 +02001013 rc = set_local_cx_options(trunk->endpoints,
Philipp Maierbc0346e2018-06-07 09:52:16 +02001014 &endp->local_options, local_options);
1015 if (rc != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001016 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
1017 "CRCX: inavlid local connection options!\n");
Philipp Maierbc0346e2018-06-07 09:52:16 +02001018 error_code = rc;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001019 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS));
Philipp Maierbc0346e2018-06-07 09:52:16 +02001020 goto error2;
1021 }
1022 }
1023
1024 /* Handle codec information and decide for a suitable codec */
Philipp Maier8dc35972021-07-14 11:20:16 +02001025 rc = handle_codec_info(conn, rq, have_sdp, true);
Philipp Maierbc0346e2018-06-07 09:52:16 +02001026 mgcp_codec_summary(conn);
1027 if (rc) {
1028 error_code = rc;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001029 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_CODEC_NEGOTIATION));
Philipp Maierbc0346e2018-06-07 09:52:16 +02001030 goto error2;
1031 }
1032
Philipp Maier14b27a82020-06-02 20:15:30 +02001033 conn->end.fmtp_extra = talloc_strdup(trunk->endpoints,
1034 trunk->audio_fmtp_extra);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001035
Philipp Maier8dc35972021-07-14 11:20:16 +02001036 if (pdata->cfg->force_ptime) {
1037 conn->end.packet_duration_ms = pdata->cfg->force_ptime;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001038 conn->end.force_output_ptime = 1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001039 }
1040
Philipp Maier1cb1e382017-11-02 17:16:04 +01001041 mgcp_rtp_end_config(endp, 0, &conn->end);
1042
Philipp Maierc3cc6542018-02-02 12:58:42 +01001043 /* check connection mode setting */
1044 if (conn->conn->mode != MGCP_CONN_LOOPBACK
1045 && conn->conn->mode != MGCP_CONN_RECV_ONLY
1046 && conn->end.rtp_port == 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001047 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
1048 "CRCX: selected connection mode type requires an opposite end!\n");
Philipp Maierc3cc6542018-02-02 12:58:42 +01001049 error_code = 527;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001050 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC));
Philipp Maierc3cc6542018-02-02 12:58:42 +01001051 goto error2;
1052 }
1053
Pau Espin Pedrol71d42e72020-09-03 14:20:07 +02001054 /* Find a local address for conn based on policy and initial SDP remote
1055 information, then find a free port for it */
1056 mgcp_get_local_addr(conn->end.local_addr, conn);
Philipp Maier1cb1e382017-11-02 17:16:04 +01001057 if (allocate_port(endp, conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001058 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
Philipp Maier1cb1e382017-11-02 17:16:04 +01001059 goto error2;
1060 }
1061
Philipp Maier87bd9be2017-08-22 16:35:41 +02001062 if (setup_rtp_processing(endp, conn) != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001063 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
1064 "CRCX: could not start RTP processing!\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001065 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_START_RTP));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001066 goto error2;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001067 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001068
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001069 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
1070 "CRCX: Creating connection: port: %u\n", conn->end.local_port);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001071
Philipp Maiere726d4f2017-11-01 10:41:34 +01001072 /* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
Philipp Maier14b27a82020-06-02 20:15:30 +02001073 OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Pau Espin Pedrolca280a12021-07-06 18:15:35 +02001074 if (conn->conn->mode & MGCP_CONN_RECV_ONLY &&
1075 mgcp_rtp_end_remote_addr_available(&conn->end) &&
1076 trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001077 send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001078
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001079 LOGPCONN(_conn, DLMGCP, LOGL_NOTICE,
1080 "CRCX: connection successfully created\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001081 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_SUCCESS));
Philipp Maier889fe7f2020-07-06 17:44:12 +02001082 mgcp_endp_update(endp);
Philipp Maier41d59202021-07-20 15:49:00 +02001083
1084 /* NOTE: Only in the virtual trunk we allow dynamic endpoint names */
1085 bool add_epname = rq->wildcarded && trunk->trunk_type == MGCP_TRUNK_VIRTUAL;
1086 return create_response_with_sdp(endp, conn, "CRCX", pdata->trans, add_epname, true);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001087error2:
Philipp Maier1355d7e2018-02-01 14:30:06 +01001088 mgcp_endp_release(endp);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001089 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1090 "CRCX: unable to create connection\n");
Philipp Maier8dc35972021-07-14 11:20:16 +02001091 return create_err_response(endp, error_code, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001092}
1093
Philipp Maier87bd9be2017-08-22 16:35:41 +02001094/* MDCX command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +02001095static struct msgb *handle_modify_con(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001096{
Philipp Maier8dc35972021-07-14 11:20:16 +02001097 struct mgcp_parse_data *pdata = rq->pdata;
1098 struct mgcp_trunk *trunk = rq->trunk;
1099 struct mgcp_endpoint *endp = rq->endp;
1100 struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_mdcx_ctr_group;
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001101 char new_local_addr[INET6_ADDRSTRLEN];
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001102 int error_code = 500;
1103 int silent = 0;
1104 int have_sdp = 0;
1105 char *line;
1106 const char *local_options = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001107 const char *mode = NULL;
1108 struct mgcp_conn_rtp *conn = NULL;
Philipp Maier01d24a32017-11-21 17:26:09 +01001109 const char *conn_id = NULL;
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001110 int osmux_cid = -2;
Philipp Maiera390d0b2018-01-31 17:30:19 +01001111 int rc;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001112
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001113 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "MDCX: modifying existing connection ...\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001114
Philipp Maier8d6a1932020-06-18 12:19:31 +02001115 if (!mgcp_endp_avail(endp)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001116 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_AVAIL));
Philipp Maiera910a812020-08-18 15:13:33 +02001117 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1118 "MDCX: selected endpoint not available!\n");
Philipp Maier8dc35972021-07-14 11:20:16 +02001119 return create_err_response(NULL, 501, "MDCX", pdata->trans);
Philipp Maier8d6a1932020-06-18 12:19:31 +02001120 }
1121
Philipp Maier5656fbf2018-02-02 14:41:58 +01001122 /* Prohibit wildcarded requests */
Philipp Maier8dc35972021-07-14 11:20:16 +02001123 if (rq->wildcarded) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001124 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1125 "MDCX: wildcarded endpoint names not supported.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001126 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_WILDCARD));
Philipp Maier8dc35972021-07-14 11:20:16 +02001127 return create_err_response(endp, 507, "MDCX", pdata->trans);
Philipp Maier5656fbf2018-02-02 14:41:58 +01001128 }
1129
Philipp Maier87bd9be2017-08-22 16:35:41 +02001130 if (llist_count(&endp->conns) <= 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001131 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1132 "MDCX: endpoint is not holding a connection.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001133 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_CONN));
Philipp Maier8dc35972021-07-14 11:20:16 +02001134 return create_err_response(endp, 400, "MDCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001135 }
1136
Philipp Maier8dc35972021-07-14 11:20:16 +02001137 for_each_line(line, pdata->save) {
Philipp Maier036612b2021-07-19 17:47:49 +02001138 if (!mgcp_check_param(endp, trunk, line))
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001139 continue;
1140
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001141 switch (toupper(line[0])) {
Philipp Maier87bd9be2017-08-22 16:35:41 +02001142 case 'C':
Harald Weltee35eeae2017-12-28 13:47:37 +01001143 if (mgcp_verify_call_id(endp, line + 3) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001144 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CALLID));
Harald Weltee35eeae2017-12-28 13:47:37 +01001145 error_code = 516;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001146 goto error3;
Harald Weltee35eeae2017-12-28 13:47:37 +01001147 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001148 break;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001149 case 'I':
Philipp Maier01d24a32017-11-21 17:26:09 +01001150 conn_id = (const char *)line + 3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001151 if ((error_code = mgcp_verify_ci(endp, conn_id))) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001152 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CONNID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001153 goto error3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001154 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001155 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001156 case 'L':
Philipp Maier87bd9be2017-08-22 16:35:41 +02001157 local_options = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001158 break;
1159 case 'M':
Philipp Maier87bd9be2017-08-22 16:35:41 +02001160 mode = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001161 break;
1162 case 'Z':
Pau Espin Pedrol9b508f62019-06-26 13:11:22 +02001163 silent = strcasecmp("noanswer", line + 3) == 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001164 break;
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001165 case 'X':
Pau Espin Pedrolc1bf4692019-05-14 16:23:24 +02001166 if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001167 /* If osmux is disabled, just skip setting it up */
Ericfbf78d12021-08-23 22:31:39 +02001168 if (!endp->trunk->cfg->osmux)
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001169 break;
1170 osmux_cid = mgcp_osmux_setup(endp, line);
1171 break;
1172 }
1173 /* Ignore unknown X-headers */
1174 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001175 case '\0':
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001176 have_sdp = 1;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001177 goto mgcp_header_done;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001178 break;
1179 default:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001180 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1181 "MDCX: Unhandled MGCP option: '%c'/%d\n",
1182 line[0], line[0]);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001183 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_UNHANDLED_PARAM));
Philipp Maier8dc35972021-07-14 11:20:16 +02001184 return create_err_response(NULL, 539, "MDCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001185 break;
1186 }
1187 }
1188
Philipp Maier87bd9be2017-08-22 16:35:41 +02001189mgcp_header_done:
Philipp Maier01d24a32017-11-21 17:26:09 +01001190 if (!conn_id) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001191 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1192 "MDCX: insufficient parameters, missing ci (connectionIdentifier)\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001193 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_CONNID));
Philipp Maier8dc35972021-07-14 11:20:16 +02001194 return create_err_response(endp, 515, "MDCX", pdata->trans);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001195 }
1196
1197 conn = mgcp_conn_get_rtp(endp, conn_id);
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001198 if (!conn) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001199 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_CONN_NOT_FOUND));
Philipp Maier8dc35972021-07-14 11:20:16 +02001200 return create_err_response(endp, 400, "MDCX", pdata->trans);
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001201 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001202
Oliver Smithe36b7752019-01-22 16:31:36 +01001203 mgcp_conn_watchdog_kick(conn->conn);
1204
Philipp Maier87bd9be2017-08-22 16:35:41 +02001205 if (mode) {
1206 if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001207 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_MODE));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001208 error_code = 517;
1209 goto error3;
1210 }
1211 } else
Pau Espin Pedrol209eb9f2019-04-24 12:03:04 +02001212 conn->conn->mode = conn->conn->mode_orig;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001213
Philipp Maierbc0346e2018-06-07 09:52:16 +02001214 /* Set local connection options, if present */
1215 if (local_options) {
Philipp Maier8dc35972021-07-14 11:20:16 +02001216 rc = set_local_cx_options(trunk->endpoints,
Philipp Maierbc0346e2018-06-07 09:52:16 +02001217 &endp->local_options, local_options);
1218 if (rc != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001219 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1220 "MDCX: invalid local connection options!\n");
Philipp Maierbc0346e2018-06-07 09:52:16 +02001221 error_code = rc;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001222 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS));
Philipp Maierbc0346e2018-06-07 09:52:16 +02001223 goto error3;
1224 }
1225 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001226
Philipp Maierbc0346e2018-06-07 09:52:16 +02001227 /* Handle codec information and decide for a suitable codec */
Philipp Maier8dc35972021-07-14 11:20:16 +02001228 rc = handle_codec_info(conn, rq, have_sdp, false);
Philipp Maierbc0346e2018-06-07 09:52:16 +02001229 mgcp_codec_summary(conn);
1230 if (rc) {
Philipp Maieraf07f662018-02-02 11:34:02 +01001231 error_code = rc;
1232 goto error3;
Philipp Maiera390d0b2018-01-31 17:30:19 +01001233 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001234
Philipp Maierc3cc6542018-02-02 12:58:42 +01001235 /* check connection mode setting */
1236 if (conn->conn->mode != MGCP_CONN_LOOPBACK
1237 && conn->conn->mode != MGCP_CONN_RECV_ONLY
Pau Espin Pedrold6769ea2021-07-06 19:44:27 +02001238 && !mgcp_rtp_end_remote_addr_available(&conn->end)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001239 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1240 "MDCX: selected connection mode type requires an opposite end!\n");
Philipp Maierc3cc6542018-02-02 12:58:42 +01001241 error_code = 527;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001242 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC));
Philipp Maierc3cc6542018-02-02 12:58:42 +01001243 goto error3;
1244 }
1245
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001246 if (mgcp_conn_rtp_is_osmux(conn)) {
1247 OSMO_ASSERT(conn->osmux.cid_allocated);
1248 if (osmux_cid < -1) {
1249 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1250 "MDCX: Failed to parse Osmux CID!\n");
1251 goto error3;
1252 } else if (osmux_cid == -1) {
1253 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1254 "MDCX: wilcard in MDCX is not supported!\n");
1255 goto error3;
1256 } else if (osmux_cid != (int) conn->osmux.cid) {
1257 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1258 "MDCX: changing already allocated CID is not supported!\n");
1259 goto error3;
1260 }
1261 /* TODO: In the future (when we have recvCID!=sendCID), we need to
1262 tell Osmux code that osmux_cid is to be used as sendCID for
1263 that conn. */
1264 }
Philipp Maierbc0346e2018-06-07 09:52:16 +02001265
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001266 /* MDCX may have provided a new remote address, which means we may need
1267 to update our announced IP addr and re-bind our local end. This can
1268 happen for instance if MGW initially provided an IPv4 during CRCX
1269 ACK, and now MDCX tells us the remote has an IPv6 address. */
1270 mgcp_get_local_addr(new_local_addr, conn);
1271 if (strcmp(new_local_addr, conn->end.local_addr)) {
1272 osmo_strlcpy(conn->end.local_addr, new_local_addr, sizeof(conn->end.local_addr));
1273 mgcp_free_rtp_port(&conn->end);
1274 if (allocate_port(endp, conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001275 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001276 goto error3;
1277 }
1278 }
1279
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001280 if (setup_rtp_processing(endp, conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001281 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_START_RTP));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001282 goto error3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001283 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001284
Philipp Maier87bd9be2017-08-22 16:35:41 +02001285 mgcp_rtp_end_config(endp, 1, &conn->end);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001286
1287 /* modify */
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001288 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
1289 "MDCX: modified conn:%s\n", mgcp_conn_dump(conn->conn));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001290
Philipp Maiere726d4f2017-11-01 10:41:34 +01001291 /* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
Philipp Maier8dc35972021-07-14 11:20:16 +02001292 OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Pau Espin Pedrolca280a12021-07-06 18:15:35 +02001293 if (conn->conn->mode & MGCP_CONN_RECV_ONLY &&
1294 mgcp_rtp_end_remote_addr_available(&conn->end) &&
Philipp Maier8dc35972021-07-14 11:20:16 +02001295 trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001296 send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001297
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001298 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_SUCCESS));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001299 if (silent)
1300 goto out_silent;
1301
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001302 LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
1303 "MDCX: connection successfully modified\n");
Philipp Maier889fe7f2020-07-06 17:44:12 +02001304 mgcp_endp_update(endp);
Philipp Maier41d59202021-07-20 15:49:00 +02001305 return create_response_with_sdp(endp, conn, "MDCX", pdata->trans, false, false);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001306error3:
Philipp Maier8dc35972021-07-14 11:20:16 +02001307 return create_err_response(endp, error_code, "MDCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001308
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001309out_silent:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001310 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "MDCX: silent exit\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001311 return NULL;
1312}
1313
Philipp Maier87bd9be2017-08-22 16:35:41 +02001314/* DLCX command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +02001315static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001316{
Philipp Maier8dc35972021-07-14 11:20:16 +02001317 struct mgcp_parse_data *pdata = rq->pdata;
1318 struct mgcp_trunk *trunk = rq->trunk;
1319 struct mgcp_endpoint *endp = rq->endp;
1320 struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_dlcx_ctr_group;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001321 int error_code = 400;
1322 int silent = 0;
1323 char *line;
1324 char stats[1048];
Philipp Maier01d24a32017-11-21 17:26:09 +01001325 const char *conn_id = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001326 struct mgcp_conn_rtp *conn = NULL;
Philipp Maierf486e742021-07-19 14:56:29 +02001327 unsigned int i;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001328
Philipp Maierf486e742021-07-19 14:56:29 +02001329 /* NOTE: In this handler we can not take it for granted that the endp
1330 * pointer will be populated, however a trunk is always guaranteed. */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001331
Philipp Maierf486e742021-07-19 14:56:29 +02001332 LOGPEPTR(endp, trunk, DLMGCP, LOGL_NOTICE, "DLCX: deleting connection(s) ...\n");
1333
1334 if (endp && !mgcp_endp_avail(endp)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001335 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_AVAIL));
Philipp Maiera910a812020-08-18 15:13:33 +02001336 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1337 "DLCX: selected endpoint not available!\n");
Philipp Maier8dc35972021-07-14 11:20:16 +02001338 return create_err_response(NULL, 501, "DLCX", pdata->trans);
Philipp Maier8d6a1932020-06-18 12:19:31 +02001339 }
1340
Philipp Maierf486e742021-07-19 14:56:29 +02001341 if (endp && !rq->wildcarded && llist_empty(&endp->conns)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001342 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1343 "DLCX: endpoint is not holding a connection.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001344 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_NO_CONN));
Philipp Maier8dc35972021-07-14 11:20:16 +02001345 return create_err_response(endp, 515, "DLCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001346 }
1347
Philipp Maier8dc35972021-07-14 11:20:16 +02001348 for_each_line(line, pdata->save) {
Philipp Maier036612b2021-07-19 17:47:49 +02001349 if (!mgcp_check_param(endp, trunk, line))
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001350 continue;
1351
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001352 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001353 case 'C':
Philipp Maierf486e742021-07-19 14:56:29 +02001354 /* If we have no endpoint, but a call id in the request,
1355 then this request cannot be handled */
1356 if (!endp) {
1357 LOGPTRUNK(trunk, DLMGCP, LOGL_NOTICE,
1358 "cannot handle requests with call-id (C) without endpoint -- abort!");
1359 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));
1360 return create_err_response(NULL, 539, "DLCX", pdata->trans);
1361 }
1362
Harald Weltee35eeae2017-12-28 13:47:37 +01001363 if (mgcp_verify_call_id(endp, line + 3) != 0) {
1364 error_code = 516;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001365 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CALLID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001366 goto error3;
Harald Weltee35eeae2017-12-28 13:47:37 +01001367 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001368 break;
1369 case 'I':
Philipp Maierf486e742021-07-19 14:56:29 +02001370 /* If we have no endpoint, but a connection id in the request,
1371 then this request cannot be handled */
1372 if (!endp) {
1373 LOGPTRUNK(trunk, DLMGCP, LOGL_NOTICE,
1374 "cannot handle requests with conn-id (I) without endpoint -- abort!");
1375 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));
1376 return create_err_response(NULL, 539, "DLCX", pdata->trans);
1377 }
1378
Philipp Maier01d24a32017-11-21 17:26:09 +01001379 conn_id = (const char *)line + 3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001380 if ((error_code = mgcp_verify_ci(endp, conn_id))) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001381 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CONNID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001382 goto error3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001383 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001384 break;
1385 case 'Z':
Pau Espin Pedrol9b508f62019-06-26 13:11:22 +02001386 silent = strcasecmp("noanswer", line + 3) == 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001387 break;
1388 default:
Philipp Maierf486e742021-07-19 14:56:29 +02001389 LOGPEPTR(endp, trunk, DLMGCP, LOGL_NOTICE, "DLCX: Unhandled MGCP option: '%c'/%d\n",
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001390 line[0], line[0]);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001391 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));
Philipp Maier8dc35972021-07-14 11:20:16 +02001392 return create_err_response(NULL, 539, "DLCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001393 break;
1394 }
1395 }
1396
Philipp Maierf486e742021-07-19 14:56:29 +02001397 /* Handle wildcarded DLCX that refers to the whole trunk. This means
1398 * that we walk over all endpoints on the trunk in order to drop all
1399 * connections on the trunk. (see also RFC3435 Annex F.7) */
1400 if (rq->wildcarded) {
1401 int num_conns = 0;
1402 for (i = 0; i < trunk->number_endpoints; i++) {
1403 num_conns += llist_count(&trunk->endpoints[i]->conns);
1404 mgcp_endp_release(trunk->endpoints[i]);
1405 }
1406 rate_ctr_add(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS), num_conns);
1407 return create_ok_response(NULL, 200, "DLCX", pdata->trans);
1408 }
1409
Philipp Maierce187052021-07-23 10:58:19 +02001410 /* The logic does not permit to go past this point without having the
1411 * the endp pointer populated. */
1412 OSMO_ASSERT(endp);
1413
Philipp Maierf4c0e372017-10-11 16:06:45 +02001414 /* When no connection id is supplied, we will interpret this as a
Philipp Maierf486e742021-07-19 14:56:29 +02001415 * wildcarded DLCX that refers to the selected endpoint. This means
1416 * that we drop all connections on that specific endpoint at once.
1417 * (See also RFC3435 Section F.7) */
Philipp Maier01d24a32017-11-21 17:26:09 +01001418 if (!conn_id) {
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001419 int num_conns = llist_count(&endp->conns);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001420 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1421 "DLCX: missing ci (connectionIdentifier), will remove all connections (%d total) at once\n",
1422 num_conns);
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001423
1424 if (num_conns > 0)
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001425 rate_ctr_add(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS), num_conns);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001426
Philipp Maier1355d7e2018-02-01 14:30:06 +01001427 mgcp_endp_release(endp);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001428
1429 /* Note: In this case we do not return any statistics,
1430 * as we assume that the client is not interested in
1431 * this case. */
Philipp Maier8dc35972021-07-14 11:20:16 +02001432 return create_ok_response(endp, 200, "DLCX", pdata->trans);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001433 }
1434
Philipp Maierf4c0e372017-10-11 16:06:45 +02001435 /* Find the connection */
Philipp Maier87bd9be2017-08-22 16:35:41 +02001436 conn = mgcp_conn_get_rtp(endp, conn_id);
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001437 if (!conn) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001438 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CONNID));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001439 goto error3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001440 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001441 /* save the statistics of the current connection */
1442 mgcp_format_stats(stats, sizeof(stats), conn->conn);
1443
1444 /* delete connection */
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001445 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG, "DLCX: deleting conn:%s\n",
1446 mgcp_conn_dump(conn->conn));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001447 mgcp_conn_free(endp, conn_id);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001448 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1449 "DLCX: connection successfully deleted\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +02001450
1451 /* When all connections are closed, the endpoint will be released
1452 * in order to be ready to be used by another call. */
1453 if (llist_count(&endp->conns) <= 0) {
Philipp Maier1355d7e2018-02-01 14:30:06 +01001454 mgcp_endp_release(endp);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001455 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: endpoint released\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +02001456 }
1457
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001458 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001459 if (silent)
1460 goto out_silent;
Philipp Maier8dc35972021-07-14 11:20:16 +02001461 return create_ok_resp_with_param(endp, 250, "DLCX", pdata->trans, stats);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001462
1463error3:
Philipp Maier8dc35972021-07-14 11:20:16 +02001464 return create_err_response(endp, error_code, "DLCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001465
1466out_silent:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001467 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: silent exit\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001468 return NULL;
1469}
1470
Philipp Maier87bd9be2017-08-22 16:35:41 +02001471/* RSIP command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +02001472static struct msgb *handle_rsip(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001473{
Philipp Maier87bd9be2017-08-22 16:35:41 +02001474 /* TODO: Also implement the resetting of a specific endpoint
1475 * to make mgcp_send_reset_ep() work. Currently this will call
1476 * mgcp_rsip_cb() in mgw_main.c, which sets reset_endpoints=1
1477 * to make read_call_agent() reset all endpoints when called
1478 * next time. In order to selectively reset endpoints some
1479 * mechanism to distinguish which endpoint shall be resetted
1480 * is needed */
1481
1482 LOGP(DLMGCP, LOGL_NOTICE, "RSIP: resetting all endpoints ...\n");
1483
Philipp Maier8dc35972021-07-14 11:20:16 +02001484 if (rq->pdata->cfg->reset_cb)
1485 rq->pdata->cfg->reset_cb(rq->endp->trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001486 return NULL;
1487}
1488
1489static char extract_tone(const char *line)
1490{
1491 const char *str = strstr(line, "D/");
1492 if (!str)
1493 return CHAR_MAX;
1494
1495 return str[2];
1496}
1497
Philipp Maier87bd9be2017-08-22 16:35:41 +02001498/* This can request like DTMF detection and forward, fax detection... it
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001499 * can also request when the notification should be send and such. We don't
Philipp Maier87bd9be2017-08-22 16:35:41 +02001500 * do this right now. */
Philipp Maier8dc35972021-07-14 11:20:16 +02001501static struct msgb *handle_noti_req(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001502{
1503 int res = 0;
1504 char *line;
1505 char tone = CHAR_MAX;
1506
Philipp Maier87bd9be2017-08-22 16:35:41 +02001507 LOGP(DLMGCP, LOGL_NOTICE, "RQNT: processing request for notification ...\n");
1508
Philipp Maier8dc35972021-07-14 11:20:16 +02001509 for_each_line(line, rq->pdata->save) {
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001510 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001511 case 'S':
1512 tone = extract_tone(line);
1513 break;
1514 }
1515 }
1516
1517 /* we didn't see a signal request with a tone */
1518 if (tone == CHAR_MAX)
Philipp Maier8dc35972021-07-14 11:20:16 +02001519 return create_ok_response(rq->endp, 200, "RQNT", rq->pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001520
Philipp Maier8dc35972021-07-14 11:20:16 +02001521 if (rq->pdata->cfg->rqnt_cb)
1522 res = rq->pdata->cfg->rqnt_cb(rq->endp, tone);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001523
1524 return res == 0 ?
Philipp Maier8dc35972021-07-14 11:20:16 +02001525 create_ok_response(rq->endp, 200, "RQNT", rq->pdata->trans) :
1526 create_err_response(rq->endp, res, "RQNT", rq->pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001527}
1528
Philipp Maier87bd9be2017-08-22 16:35:41 +02001529/* Connection keepalive timer, will take care that dummy packets are send
Harald Welte1d1b98f2017-12-25 10:03:40 +01001530 * regularly, so that NAT connections stay open */
Philipp Maier14b27a82020-06-02 20:15:30 +02001531static void mgcp_keepalive_timer_cb(void *_trunk)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001532{
Philipp Maier14b27a82020-06-02 20:15:30 +02001533 struct mgcp_trunk *trunk = _trunk;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001534 struct mgcp_conn *conn;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001535 int i;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001536
Philipp Maiere726d4f2017-11-01 10:41:34 +01001537 LOGP(DLMGCP, LOGL_DEBUG, "triggered trunk %d keepalive timer\n",
Philipp Maier14b27a82020-06-02 20:15:30 +02001538 trunk->trunk_nr);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001539
Philipp Maiere726d4f2017-11-01 10:41:34 +01001540 /* Do not accept invalid configuration values
1541 * valid is MGCP_KEEPALIVE_NEVER, MGCP_KEEPALIVE_ONCE and
1542 * values greater 0 */
Philipp Maier14b27a82020-06-02 20:15:30 +02001543 OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Philipp Maiere726d4f2017-11-01 10:41:34 +01001544
1545 /* The dummy packet functionality has been disabled, we will exit
1546 * immediately, no further timer is scheduled, which means we will no
1547 * longer send dummy packets even when we did before */
Philipp Maier14b27a82020-06-02 20:15:30 +02001548 if (trunk->keepalive_interval == MGCP_KEEPALIVE_NEVER)
Philipp Maiere726d4f2017-11-01 10:41:34 +01001549 return;
1550
1551 /* In cases where only one dummy packet is sent, we do not need
1552 * the timer since the functions that handle the CRCX and MDCX are
1553 * triggering the sending of the dummy packet. So we behave like in
1554 * the MGCP_KEEPALIVE_NEVER case */
Philipp Maier14b27a82020-06-02 20:15:30 +02001555 if (trunk->keepalive_interval == MGCP_KEEPALIVE_ONCE)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001556 return;
1557
Philipp Maier87bd9be2017-08-22 16:35:41 +02001558 /* Send walk over all endpoints and send out dummy packets through
1559 * every connection present on each endpoint */
Philipp Maier4131a652021-07-07 14:04:34 +02001560 for (i = 0; i < trunk->number_endpoints; ++i) {
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001561 struct mgcp_endpoint *endp = trunk->endpoints[i];
Philipp Maier87bd9be2017-08-22 16:35:41 +02001562 llist_for_each_entry(conn, &endp->conns, entry) {
Pau Espin Pedrolca280a12021-07-06 18:15:35 +02001563 if (conn->type == MGCP_CONN_TYPE_RTP &&
1564 conn->mode == MGCP_CONN_RECV_ONLY &&
1565 mgcp_rtp_end_remote_addr_available(&conn->u.rtp.end))
Philipp Maier87bd9be2017-08-22 16:35:41 +02001566 send_dummy(endp, &conn->u.rtp);
1567 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001568 }
1569
Philipp Maiere726d4f2017-11-01 10:41:34 +01001570 /* Schedule the keepalive timer for the next round */
1571 LOGP(DLMGCP, LOGL_DEBUG, "rescheduling trunk %d keepalive timer\n",
Philipp Maier14b27a82020-06-02 20:15:30 +02001572 trunk->trunk_nr);
1573 osmo_timer_schedule(&trunk->keepalive_timer, trunk->keepalive_interval,
Philipp Maier87bd9be2017-08-22 16:35:41 +02001574 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001575}
1576
Philipp Maier14b27a82020-06-02 20:15:30 +02001577void mgcp_trunk_set_keepalive(struct mgcp_trunk *trunk, int interval)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001578{
Philipp Maier14b27a82020-06-02 20:15:30 +02001579 trunk->keepalive_interval = interval;
1580 osmo_timer_setup(&trunk->keepalive_timer, mgcp_keepalive_timer_cb, trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001581
1582 if (interval <= 0)
Philipp Maier14b27a82020-06-02 20:15:30 +02001583 osmo_timer_del(&trunk->keepalive_timer);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001584 else
Philipp Maier14b27a82020-06-02 20:15:30 +02001585 osmo_timer_schedule(&trunk->keepalive_timer,
1586 trunk->keepalive_interval, 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001587}
1588
Philipp Maier38533ba2021-07-29 17:38:34 +02001589/* Free config, this function is automatically called by talloc_free when the configuration is freed. */
1590static int config_free_talloc_destructor(struct mgcp_config *cfg)
1591{
1592 mgcp_ratectr_global_free(cfg);
1593 return 0;
1594}
1595
Philipp Maier87bd9be2017-08-22 16:35:41 +02001596/*! allocate configuration with default values.
1597 * (called once at startup by main function) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001598struct mgcp_config *mgcp_config_alloc(void)
1599{
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001600 /* FIXME: This is unrelated to the protocol, put this in some
1601 * appropiate place! */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001602 struct mgcp_config *cfg;
1603
1604 cfg = talloc_zero(NULL, struct mgcp_config);
1605 if (!cfg) {
1606 LOGP(DLMGCP, LOGL_FATAL, "Failed to allocate config.\n");
1607 return NULL;
1608 }
1609
Philipp Maier12943ea2018-01-17 15:40:25 +01001610 osmo_strlcpy(cfg->domain, "mgw", sizeof(cfg->domain));
1611
Eric55fdfc22021-08-13 00:14:18 +02001612 cfg->net_ports.lock = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001613 cfg->net_ports.range_start = RTP_PORT_DEFAULT_RANGE_START;
1614 cfg->net_ports.range_end = RTP_PORT_DEFAULT_RANGE_END;
1615 cfg->net_ports.last_port = cfg->net_ports.range_start;
1616
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001617 cfg->source_port = 2427;
Eric2764bdb2021-08-23 22:11:47 +02001618 osmo_strlcpy(cfg->source_addr, "0.0.0.0", sizeof(cfg->source_addr));
1619 osmo_strlcpy(cfg->osmux_addr, "0.0.0.0", sizeof(cfg->osmux_addr));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001620
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001621 cfg->rtp_processing_cb = &mgcp_rtp_processing_default;
1622 cfg->setup_rtp_processing_cb = &mgcp_setup_rtp_processing_default;
1623
1624 cfg->get_net_downlink_format_cb = &mgcp_get_net_downlink_format_default;
1625
Philipp Maierd19de2e2020-06-03 13:55:33 +02001626 INIT_LLIST_HEAD(&cfg->trunks);
1627
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001628 /* Allocate virtual trunk */
Philipp Maierd19de2e2020-06-03 13:55:33 +02001629 if (!mgcp_trunk_alloc(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID)) {
Harald Welte3ac604e2019-05-08 14:07:41 +02001630 talloc_free(cfg);
1631 return NULL;
1632 }
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001633
Philipp Maiera065e632021-07-09 13:22:42 +02001634 mgcp_ratectr_global_alloc(cfg);
Philipp Maier38533ba2021-07-29 17:38:34 +02001635 talloc_set_destructor(cfg, config_free_talloc_destructor);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001636
1637 return cfg;
1638}
1639
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001640static int send_agent(struct mgcp_config *cfg, const char *buf, int len)
1641{
1642 return write(cfg->gw_fd.bfd.fd, buf, len);
1643}
1644
Philipp Maier87bd9be2017-08-22 16:35:41 +02001645/*! Reset all endpoints by sending RSIP message to self.
1646 * (called by VTY)
1647 * \param[in] endp trunk endpoint
1648 * \param[in] endpoint number
1649 * \returns 0 on success, -1 on error */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001650int mgcp_send_reset_all(struct mgcp_config *cfg)
1651{
Philipp Maier12943ea2018-01-17 15:40:25 +01001652 char buf[MGCP_ENDPOINT_MAXLEN + 128];
1653 int len;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001654 int rc;
1655
Philipp Maier12943ea2018-01-17 15:40:25 +01001656 len = snprintf(buf, sizeof(buf),
1657 "RSIP 1 *@%s MGCP 1.0\r\n", cfg->domain);
1658 if (len < 0)
1659 return -1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001660
Philipp Maier12943ea2018-01-17 15:40:25 +01001661 rc = send_agent(cfg, buf, len);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001662 if (rc <= 0)
1663 return -1;
1664
1665 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001666}
1667
Philipp Maier87bd9be2017-08-22 16:35:41 +02001668/*! Reset a single endpoint by sending RSIP message to self.
1669 * (called by VTY)
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001670 * \param[in] endp to reset
Philipp Maier87bd9be2017-08-22 16:35:41 +02001671 * \returns 0 on success, -1 on error */
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001672int mgcp_send_reset_ep(struct mgcp_endpoint *endp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001673{
Philipp Maier12943ea2018-01-17 15:40:25 +01001674 char buf[MGCP_ENDPOINT_MAXLEN + 128];
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001675 int len;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001676 int rc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001677
1678 len = snprintf(buf, sizeof(buf),
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001679 "RSIP 39 %s MGCP 1.0\r\n", endp->name);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001680 if (len < 0)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001681 return -1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001682
Ericfbf78d12021-08-23 22:31:39 +02001683 rc = send_agent(endp->trunk->cfg, buf, len);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001684 if (rc <= 0)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001685 return -1;
1686
Philipp Maier87bd9be2017-08-22 16:35:41 +02001687 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001688}