blob: 1fa345b861feb8f78aa5c7c51a69be6c69f7b9d4 [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>
Pau Espin Pedrolbb3ccde2021-12-23 19:49:26 +010049#include <osmocom/mgcp/mgcp_iuup.h>
Pau Espin Pedrol8e0d1012022-10-06 10:29:11 +020050#include <osmocom/mgcp/debug.h>
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020051
Philipp Maier39889e42021-08-04 17:42:57 +020052/* Contains the last successfully resolved endpoint name. This variable is used
53 * for the unit-tests to verify that the endpoint was correctly resolved. */
54static char debug_last_endpoint_name[MGCP_ENDPOINT_MAXLEN];
55
56/* Called from unit-tests only */
57char *mgcp_debug_get_last_endpoint_name(void)
58{
59 return debug_last_endpoint_name;
60}
61
Philipp Maierf486e742021-07-19 14:56:29 +020062/* A combination of LOGPENDP and LOGPTRUNK that automatically falls back to
63 * LOGPTRUNK when the endp parameter is NULL */
64#define LOGPEPTR(endp, trunk, cat, level, fmt, args...) \
65do { \
66 if (endp) \
67 LOGPENDP(endp, cat, level, fmt, ## args); \
68 else \
69 LOGPTRUNK(trunk, cat, level, fmt, ## args); \
70} while (0)
71
Philipp Maier8dc35972021-07-14 11:20:16 +020072/* Request data passed to the request handler */
73struct mgcp_request_data {
74 /* request name (e.g. "MDCX") */
75 char name[4+1];
76
77 /* parsing results from the MGCP header (trans id, endpoint name ...) */
78 struct mgcp_parse_data *pdata;
79
80 /* pointer to endpoint resource (may be NULL for wildcarded requests) */
81 struct mgcp_endpoint *endp;
82
83 /* pointer to trunk resource */
84 struct mgcp_trunk *trunk;
85
86 /* set to true when the request has been classified as wildcarded */
87 bool wildcarded;
88
89 /* contains cause code in case of problems during endp/trunk resolution */
90 int mgcp_cause;
91};
92
Philipp Maier33d97f72021-07-14 14:53:45 +020093/* Request handler specification, here we specify an array with function
94 * pointers to the various MGCP requests implemented below */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020095struct mgcp_request {
Philipp Maier33d97f72021-07-14 14:53:45 +020096 /* request name (e.g. "MDCX") */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020097 char *name;
Philipp Maier33d97f72021-07-14 14:53:45 +020098
99 /* function pointer to the request handler */
Philipp Maier8dc35972021-07-14 11:20:16 +0200100 struct msgb *(*handle_request)(struct mgcp_request_data *data);
101
Philipp Maier33d97f72021-07-14 14:53:45 +0200102 /* a human readable name that describes the request */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200103 char *debug_name;
104};
105
Philipp Maier8dc35972021-07-14 11:20:16 +0200106static struct msgb *handle_audit_endpoint(struct mgcp_request_data *data);
107static struct msgb *handle_create_con(struct mgcp_request_data *data);
108static struct msgb *handle_delete_con(struct mgcp_request_data *data);
109static struct msgb *handle_modify_con(struct mgcp_request_data *data);
110static struct msgb *handle_rsip(struct mgcp_request_data *data);
111static struct msgb *handle_noti_req(struct mgcp_request_data *data);
Philipp Maier33d97f72021-07-14 14:53:45 +0200112static const struct mgcp_request mgcp_requests[] = {
Ericee6958c2021-09-14 18:36:53 +0200113 { .name = "AUEP", .handle_request = handle_audit_endpoint, .debug_name = "AuditEndpoint" },
114 {
115 .name = "CRCX",
116 .handle_request = handle_create_con,
117 .debug_name = "CreateConnection",
118 },
119 {
120 .name = "DLCX",
121 .handle_request = handle_delete_con,
122 .debug_name = "DeleteConnection",
123 },
124 {
125 .name = "MDCX",
126 .handle_request = handle_modify_con,
127 .debug_name = "ModifiyConnection",
128 },
129 {
130 .name = "RQNT",
131 .handle_request = handle_noti_req,
132 .debug_name = "NotificationRequest",
133 },
Philipp Maier33d97f72021-07-14 14:53:45 +0200134
135 /* SPEC extension */
Ericee6958c2021-09-14 18:36:53 +0200136 {
137 .name = "RSIP",
138 .handle_request = handle_rsip,
139 .debug_name = "ReSetInProgress",
140 },
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 Pedrolbb3ccde2021-12-23 19:49:26 +0100152 switch (conn->type) {
153 case MGCP_RTP_DEFAULT:
Pau Espin Pedrol9d939b62022-10-03 16:59:20 +0200154 case MGCP_RTP_OSMUX:
Pau Espin Pedrolbb3ccde2021-12-23 19:49:26 +0100155 case MGCP_RTP_IUUP:
156 break;
157 default:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200158 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
159 "RTP-setup: Endpoint is not configured as RTP default, stopping here!\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200160 return 0;
161 }
162
Philipp Maier87bd9be2017-08-22 16:35:41 +0200163 if (conn->conn->mode == MGCP_CONN_LOOPBACK) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200164 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
165 "RTP-setup: Endpoint is in loopback mode, stopping here!\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +0200166 return 0;
167 }
168
169 /* Find the "sister" connection */
170 llist_for_each_entry(_conn, &endp->conns, entry) {
171 if (_conn->id != conn->conn->id) {
172 conn_src = &_conn->u.rtp;
173 break;
174 }
175 }
176
Philipp Maieracc10352018-07-19 18:07:57 +0200177 return cfg->setup_rtp_processing_cb(endp, conn_dst, conn_src);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200178}
179
Philipp Maier87bd9be2017-08-22 16:35:41 +0200180/* Helper function to allocate some memory for responses and retransmissions */
Ericfbcf4a62021-09-09 18:02:31 +0200181static struct msgb *mgcp_msgb_alloc(void *ctx)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200182{
183 struct msgb *msg;
Ericfbcf4a62021-09-09 18:02:31 +0200184 msg = msgb_alloc_headroom_c(ctx, 4096, 128, "MGCP msg");
185
186 if (!msg) {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200187 LOGP(DLMGCP, LOGL_ERROR, "Failed to msgb for MGCP data.\n");
Ericfbcf4a62021-09-09 18:02:31 +0200188 return NULL;
189 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200190
191 return msg;
192}
193
Philipp Maier87bd9be2017-08-22 16:35:41 +0200194/* Helper function for do_retransmission() and create_resp() */
Eric958f5e72021-08-03 23:00:17 +0200195static struct msgb *create_retransmission_response(const struct mgcp_endpoint *endp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200196{
Ericfbcf4a62021-09-09 18:02:31 +0200197 struct msgb *msg = mgcp_msgb_alloc(endp->trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200198 if (!msg)
199 return NULL;
200
201 msg->l2h = msgb_put(msg, strlen(endp->last_response));
202 memcpy(msg->l2h, endp->last_response, msgb_l2len(msg));
Philipp Maier87bd9be2017-08-22 16:35:41 +0200203 mgcp_disp_msg(msg->l2h, msgb_l2len(msg), "Retransmitted response");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200204 return msg;
205}
206
Ericfbcf4a62021-09-09 18:02:31 +0200207static struct msgb *create_resp(void *msgctx, struct mgcp_endpoint *endp, int code, const char *txt, const char *msg,
208 const char *trans, const char *param, const char *sdp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200209{
210 int len;
211 struct msgb *res;
212
Ericfbcf4a62021-09-09 18:02:31 +0200213 OSMO_ASSERT(msgctx != 0);
214 res = mgcp_msgb_alloc(msgctx);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200215 if (!res)
216 return NULL;
217
Philipp Maier87bd9be2017-08-22 16:35:41 +0200218 len = snprintf((char *)res->data, 2048, "%d %s%s%s\r\n%s",
219 code, trans, txt, param ? param : "", sdp ? sdp : "");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200220 if (len < 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200221 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "Failed to sprintf MGCP response.\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200222 msgb_free(res);
223 return NULL;
224 }
225
226 res->l2h = msgb_put(res, len);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200227 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "Generated response: code=%d\n", code);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200228 mgcp_disp_msg(res->l2h, msgb_l2len(res), "Generated response");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200229
230 /*
231 * Remember the last transmission per endpoint.
232 */
233 if (endp) {
Philipp Maier14b27a82020-06-02 20:15:30 +0200234 struct mgcp_trunk *trunk = endp->trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200235 talloc_free(endp->last_response);
236 talloc_free(endp->last_trans);
Philipp Maier14b27a82020-06-02 20:15:30 +0200237 endp->last_trans = talloc_strdup(trunk->endpoints, trans);
238 endp->last_response = talloc_strndup(trunk->endpoints,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200239 (const char *)res->l2h,
240 msgb_l2len(res));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200241 }
242
243 return res;
244}
245
Ericfbcf4a62021-09-09 18:02:31 +0200246static struct msgb *create_ok_resp_with_param(void *msgctx, struct mgcp_endpoint *endp, int code, const char *msg,
247 const char *trans, const char *param)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200248{
Ericfbcf4a62021-09-09 18:02:31 +0200249 return create_resp(msgctx, endp, code, " OK", msg, trans, param, NULL);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200250}
251
Ericfbcf4a62021-09-09 18:02:31 +0200252static struct msgb *create_ok_response(void *msgctx, struct mgcp_endpoint *endp, int code, const char *msg,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200253 const char *trans)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200254{
Ericfbcf4a62021-09-09 18:02:31 +0200255 return create_ok_resp_with_param(msgctx, endp, code, msg, trans, NULL);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200256}
257
Ericfbcf4a62021-09-09 18:02:31 +0200258static struct msgb *create_err_response(void *msgctx, struct mgcp_endpoint *endp, int code, const char *msg,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200259 const char *trans)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200260{
Ericfbcf4a62021-09-09 18:02:31 +0200261 return create_resp(msgctx, endp, code, " FAIL", msg, trans, NULL, NULL);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200262}
263
Philipp Maier87bd9be2017-08-22 16:35:41 +0200264/* Format MGCP response string (with SDP attached) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200265static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200266 struct mgcp_conn_rtp *conn,
267 const char *msg,
Philipp Maier55295f72018-01-15 14:00:28 +0100268 const char *trans_id,
Philipp Maier41d59202021-07-20 15:49:00 +0200269 bool add_epname,
270 bool add_conn_id)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200271{
Pau Espin Pedrol71d42e72020-09-03 14:20:07 +0200272 /* cfg->local_ip allows overwritting the announced IP address with
273 * regards to the one we actually bind to. Useful in behind-NAT
274 * scenarios.
275 * TODO: we may want to define another local_ip_osmux var to
276 * us for OSMUX connections. Perhaps adding a new internal API to get it
277 * based on conn type.
278 */
Ericfbf78d12021-08-23 22:31:39 +0200279 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 +0200280 struct msgb *sdp;
281 int rc;
282 struct msgb *result;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200283
Ericfbcf4a62021-09-09 18:02:31 +0200284 sdp = msgb_alloc_headroom_c(endp->trunk, 4096, 128, "sdp record");
Philipp Maier8970c492017-10-11 13:33:42 +0200285 if (!sdp)
286 return NULL;
287
Philipp Maier41d59202021-07-20 15:49:00 +0200288 /* Attach optional endpoint name */
289 if (add_epname) {
290 rc = msgb_printf(sdp, "Z: %s\r\n", endp->name);
291 if (rc < 0)
292 goto error;
293 }
294
295 /* Attach optional connection id */
296 if (add_conn_id) {
297 rc = msgb_printf(sdp, "I: %s\r\n", conn->conn->id);
Philipp Maier55295f72018-01-15 14:00:28 +0100298 if (rc < 0)
299 goto error;
300 }
301
Philipp Maier3cbfb8a2018-01-22 11:39:59 +0100302 /* Attach optional OSMUX parameters */
Pau Espin Pedrolc63f15a2019-05-10 16:52:08 +0200303 if (mgcp_conn_rtp_is_osmux(conn)) {
Pau Espin Pedrol21779192022-09-23 16:46:33 +0200304 rc = msgb_printf(sdp, MGCP_X_OSMO_OSMUX_HEADER " %u\r\n", conn->osmux.local_cid);
Philipp Maier3cbfb8a2018-01-22 11:39:59 +0100305 if (rc < 0)
306 goto error;
307 }
308
309 /* Attach line break to separate the parameters from the SDP block */
Philipp Maierc3cfae22018-01-22 12:03:03 +0100310 rc = msgb_printf(sdp, "\r\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200311
Philipp Maier8970c492017-10-11 13:33:42 +0200312 rc = mgcp_write_response_sdp(endp, conn, sdp, addr);
313 if (rc < 0)
314 goto error;
Ericfbcf4a62021-09-09 18:02:31 +0200315 result = create_resp(endp->trunk, endp, 200, " OK", msg, trans_id, NULL, (char *)sdp->data);
Philipp Maier8970c492017-10-11 13:33:42 +0200316 msgb_free(sdp);
317 return result;
318error:
319 msgb_free(sdp);
320 return NULL;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200321}
322
Philipp Maier87bd9be2017-08-22 16:35:41 +0200323/* Send out dummy packet to keep the connection open, if the connection is an
324 * osmux connection, send the dummy packet via OSMUX */
325static void send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200326{
Pau Espin Pedrol2ca48822022-10-06 10:58:10 +0200327 /* Avoid sending dummy packet if the remote address was not yet
328 * configured through CRCX/MDCX: */
329 if (!mgcp_rtp_end_remote_addr_available(&conn->end))
330 return;
331
Pau Espin Pedrolabb9d472022-10-04 12:09:14 +0200332 if (mgcp_conn_rtp_is_osmux(conn))
Pau Espin Pedrol887c93e2022-10-06 12:36:03 +0200333 osmux_send_dummy(conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200334 else
Philipp Maier87bd9be2017-08-22 16:35:41 +0200335 mgcp_send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200336}
337
Philipp Maier87bd9be2017-08-22 16:35:41 +0200338/* handle incoming messages:
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200339 * - this can be a command (four letters, space, transaction id)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200340 * - or a response (three numbers, space, transaction id) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200341struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
342{
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200343 struct rate_ctr_group *rate_ctrs = cfg->ratectr.mgcp_general_ctr_group;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200344 struct mgcp_parse_data pdata;
Philipp Maier8dc35972021-07-14 11:20:16 +0200345 struct mgcp_request_data rq;
Harald Weltee35eeae2017-12-28 13:47:37 +0100346 int rc, i, code, handled = 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200347 struct msgb *resp = NULL;
348 char *data;
349
Philipp Maier39889e42021-08-04 17:42:57 +0200350 debug_last_endpoint_name[0] = '\0';
351
Alexander Chemeris63866002020-05-05 17:18:40 +0300352 /* Count all messages, even incorect ones */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200353 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_TOTAL));
Alexander Chemeris63866002020-05-05 17:18:40 +0300354
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200355 if (msgb_l2len(msg) < 4) {
356 LOGP(DLMGCP, LOGL_ERROR, "msg too short: %d\n", msg->len);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200357 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200358 return NULL;
359 }
360
Alexander Chemeris63866002020-05-05 17:18:40 +0300361 if (mgcp_msg_terminate_nul(msg)) {
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;
Alexander Chemeris63866002020-05-05 17:18:40 +0300364 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200365
Philipp Maier87bd9be2017-08-22 16:35:41 +0200366 mgcp_disp_msg(msg->l2h, msgb_l2len(msg), "Received message");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200367
Philipp Maier87bd9be2017-08-22 16:35:41 +0200368 /* attempt to treat it as a response */
369 if (sscanf((const char *)&msg->l2h[0], "%3d %*s", &code) == 1) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200370 LOGP(DLMGCP, LOGL_DEBUG, "Response: Code: %d\n", code);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200371 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200372 return NULL;
373 }
374
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200375
Philipp Maier8dc35972021-07-14 11:20:16 +0200376 /* Parse message, extract endpoint name and transaction identifier and request name etc. */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200377 memset(&pdata, 0, sizeof(pdata));
Philipp Maier8dc35972021-07-14 11:20:16 +0200378 memset(&rq, 0, sizeof(rq));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200379 pdata.cfg = cfg;
Philipp Maier8dc35972021-07-14 11:20:16 +0200380 memcpy(rq.name, (const char *)&msg->l2h[0], sizeof(rq.name)-1);
381 msg->l3h = &msg->l2h[4];
Philipp Maier87bd9be2017-08-22 16:35:41 +0200382 data = mgcp_strline((char *)msg->l3h, &pdata.save);
Harald Weltee35eeae2017-12-28 13:47:37 +0100383 rc = mgcp_parse_header(&pdata, data);
Harald Weltee35eeae2017-12-28 13:47:37 +0100384 if (rc < 0) {
Philipp Maier8dc35972021-07-14 11:20:16 +0200385 LOGP(DLMGCP, LOGL_ERROR, "%s: failed to parse MCGP message\n", rq.name);
386 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
Ericfbcf4a62021-09-09 18:02:31 +0200387 return create_err_response(cfg, NULL, -rc, rq.name, "000000");
Harald Welteabbb6b92017-12-28 13:13:50 +0100388 }
389
Philipp Maier8dc35972021-07-14 11:20:16 +0200390 /* Locate endpoint and trunk, if no endpoint can be located try at least to identify the trunk. */
391 rq.pdata = &pdata;
392 rq.wildcarded = mgcp_endp_is_wildcarded(pdata.epname);
393 rq.endp = mgcp_endp_by_name(&rc, pdata.epname, pdata.cfg);
394 rq.mgcp_cause = rc;
395 if (!rq.endp) {
396 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_NO_ENDPOINT));
397 if (rq.wildcarded) {
398 /* If we are unable to find the endpoint we still may be able to identify the trunk. Some
399 * request handlers will still be able to perform a useful action if the request refers to
400 * the whole trunk (wildcarded request). */
401 LOGP(DLMGCP, LOGL_NOTICE,
402 "%s: cannot find endpoint \"%s\", cause=%d -- trying to identify trunk...\n", rq.name,
403 pdata.epname, -rq.mgcp_cause);
404 rq.trunk = mgcp_trunk_by_name(pdata.cfg, pdata.epname);
405 if (!rq.trunk) {
406 LOGP(DLMGCP, LOGL_ERROR, "%s: failed to identify trunk for endpoint \"%s\" -- abort\n",
407 rq.name, pdata.epname);
Ericfbcf4a62021-09-09 18:02:31 +0200408 return create_err_response(cfg, NULL, -rq.mgcp_cause, rq.name, pdata.trans);
Philipp Maier8dc35972021-07-14 11:20:16 +0200409 }
410 } else {
411 /* If the endpoint name suggests that the request refers to a specific endpoint, then the
412 * request cannot be handled and we must stop early. */
413 LOGP(DLMGCP, LOGL_NOTICE,
414 "%s: cannot find endpoint \"%s\", cause=%d -- abort\n", rq.name,
415 pdata.epname, -rq.mgcp_cause);
Ericfbcf4a62021-09-09 18:02:31 +0200416 return create_err_response(cfg, NULL, -rq.mgcp_cause, rq.name, pdata.trans);
Philipp Maier8dc35972021-07-14 11:20:16 +0200417 }
418 } else {
Philipp Maier39889e42021-08-04 17:42:57 +0200419 osmo_strlcpy(debug_last_endpoint_name, rq.endp->name, sizeof(debug_last_endpoint_name));
Philipp Maier8dc35972021-07-14 11:20:16 +0200420 rq.trunk = rq.endp->trunk;
421 rq.mgcp_cause = 0;
422
423 /* Check if we have to retransmit a response from a previous transaction */
424 if (pdata.trans && rq.endp->last_trans && strcmp(rq.endp->last_trans, pdata.trans) == 0) {
425 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_RETRANSMITTED));
Eric958f5e72021-08-03 23:00:17 +0200426 return create_retransmission_response(rq.endp);
Philipp Maier8dc35972021-07-14 11:20:16 +0200427 }
428 }
429
430 /* Find an appropriate handler for the current request and execute it */
431 for (i = 0; i < ARRAY_SIZE(mgcp_requests); i++) {
432 if (strcmp(mgcp_requests[i].name, rq.name) == 0) {
Philipp Maier8dc35972021-07-14 11:20:16 +0200433 /* 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");
Ericee6958c2021-09-14 18:36:53 +0200463 if (!rq->endp || !mgcp_endp_avail(rq->endp)) {
464 LOGPENDP(rq->endp, DLMGCP, LOGL_ERROR, "AUEP: selected endpoint not available!\n");
465 return create_err_response(rq->trunk, NULL, 501, "AUEP", rq->pdata->trans);
466 }
467
Ericfbcf4a62021-09-09 18:02:31 +0200468 return create_ok_response(rq->trunk, rq->endp, 200, "AUEP", rq->pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200469}
470
Harald Welte1d1b98f2017-12-25 10:03:40 +0100471/* Try to find a free port by attempting to bind on it. Also handle the
Philipp Maier87bd9be2017-08-22 16:35:41 +0200472 * counter that points on the next free port. Since we have a pointer
Philipp Maierb38fb892018-05-22 13:52:21 +0200473 * to the next free port, binding should in work on the first attempt in
Pau Espin Pedrolfc806732019-04-23 00:18:43 +0200474 * general. In case of failure the next port is tried until the whole port
475 * range is tried once. */
Philipp Maier87bd9be2017-08-22 16:35:41 +0200476static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200477{
478 int i;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200479 struct mgcp_port_range *range;
Philipp Maierb38fb892018-05-22 13:52:21 +0200480 unsigned int tries;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200481
Philipp Maier87bd9be2017-08-22 16:35:41 +0200482 OSMO_ASSERT(conn);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200483
Ericfbf78d12021-08-23 22:31:39 +0200484 range = &endp->trunk->cfg->net_ports;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200485
Eric55fdfc22021-08-13 00:14:18 +0200486 pthread_mutex_lock(&range->lock);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200487 /* attempt to find a port */
Philipp Maierb38fb892018-05-22 13:52:21 +0200488 tries = (range->range_end - range->range_start) / 2;
489 for (i = 0; i < tries; ++i) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200490 int rc;
491
492 if (range->last_port >= range->range_end)
493 range->last_port = range->range_start;
494
Philipp Maier87bd9be2017-08-22 16:35:41 +0200495 rc = mgcp_bind_net_rtp_port(endp, range->last_port, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200496
497 range->last_port += 2;
498 if (rc == 0) {
Eric55fdfc22021-08-13 00:14:18 +0200499 pthread_mutex_unlock(&range->lock);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200500 return 0;
501 }
502
503 }
Eric55fdfc22021-08-13 00:14:18 +0200504 pthread_mutex_unlock(&range->lock);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200505 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
506 "Allocating a RTP/RTCP port failed %u times.\n",
507 tries);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200508 return -1;
509}
510
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200511/*! Helper function for check_local_cx_options() to get a pointer of the next
512 * lco option identifier
513 * \param[in] lco string
514 * \returns pointer to the beginning of the LCO identifier, NULL on failure */
515char *get_lco_identifier(const char *options)
516{
517 char *ptr;
518 unsigned int count = 0;
519
520 /* Jump to the end of the lco identifier */
521 ptr = strstr(options, ":");
522 if (!ptr)
523 return NULL;
524
525 /* Walk backwards until the pointer points to the beginning of the
526 * lco identifier. We know that we stand at the beginning when we
527 * are either at the beginning of the memory or see a space or
528 * comma. (this is tolerant, it will accept a:10, b:11 as well as
529 * a:10,b:11) */
530 while (1) {
531 /* Endless loop protection */
532 if (count > 10000)
533 return NULL;
534 else if (ptr < options || *ptr == ' ' || *ptr == ',') {
535 ptr++;
536 break;
537 }
538 ptr--;
539 count++;
540 }
541
542 /* Check if we got any result */
543 if (*ptr == ':')
544 return NULL;
545
546 return ptr;
547}
548
549/*! Check the LCO option. This function checks for multiple appearence of LCO
550 * options, which is illegal
551 * \param[in] ctx talloc context
552 * \param[in] lco string
553 * \returns 0 on success, -1 on failure */
554int check_local_cx_options(void *ctx, const char *options)
555{
556 int i;
557 char *options_copy;
558 char *lco_identifier;
559 char *lco_identifier_end;
560 char *next_lco_identifier;
561
562 char **lco_seen;
563 unsigned int lco_seen_n = 0;
564
565 if (!options)
566 return -1;
567
568 lco_seen =
569 (char **)talloc_zero_size(ctx, strlen(options) * sizeof(char *));
570 options_copy = talloc_strdup(ctx, options);
571 lco_identifier = options_copy;
572
573 do {
574 /* Move the lco_identifier pointer to the beginning of the
575 * current lco option identifier */
576 lco_identifier = get_lco_identifier(lco_identifier);
577 if (!lco_identifier)
578 goto error;
579
580 /* Look ahead to the next LCO option early, since we
581 * will parse destructively */
582 next_lco_identifier = strstr(lco_identifier + 1, ",");
583
584 /* Pinch off the end of the lco field identifier name
585 * and see if we still got something, also check if
586 * there is some value after the colon. */
587 lco_identifier_end = strstr(lco_identifier, ":");
588 if (!lco_identifier_end)
589 goto error;
590 if (*(lco_identifier_end + 1) == ' '
591 || *(lco_identifier_end + 1) == ','
592 || *(lco_identifier_end + 1) == '\0')
593 goto error;
594 *lco_identifier_end = '\0';
595 if (strlen(lco_identifier) == 0)
596 goto error;
597
598 /* Check if we have already seen the current field identifier
599 * before. If yes, we must bail, an LCO must only appear once
600 * in the LCO string */
601 for (i = 0; i < lco_seen_n; i++) {
Pau Espin Pedrol7eb6f2c2019-06-26 13:00:52 +0200602 if (strcasecmp(lco_seen[i], lco_identifier) == 0)
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200603 goto error;
604 }
605 lco_seen[lco_seen_n] = lco_identifier;
606 lco_seen_n++;
607
608 /* The first identifier must always be found at the beginnning
609 * of the LCO string */
610 if (lco_seen[0] != options_copy)
611 goto error;
612
613 /* Go to the next lco option */
614 lco_identifier = next_lco_identifier;
615 } while (lco_identifier);
616
617 talloc_free(lco_seen);
618 talloc_free(options_copy);
619 return 0;
620error:
621 talloc_free(lco_seen);
622 talloc_free(options_copy);
623 return -1;
624}
625
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200626/* Set the LCO from a string (see RFC 3435).
Harald Welte1d1b98f2017-12-25 10:03:40 +0100627 * The string is stored in the 'string' field. A NULL string is handled exactly
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200628 * like an empty string, the 'string' field is never NULL after this function
629 * has been called. */
Philipp Maiera390d0b2018-01-31 17:30:19 +0100630static int set_local_cx_options(void *ctx, struct mgcp_lco *lco,
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200631 const char *options)
632{
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200633 char *lco_id;
Philipp Maier8dbc9ed2018-10-26 14:50:25 +0200634 char codec[17];
Pau Espin Pedrol19539862020-09-21 11:43:41 +0200635 char nt[17];
Pau Espin Pedrol83fd8a52019-06-26 12:55:26 +0200636 int len;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200637
Philipp Maier604410c2018-06-06 10:02:16 +0200638 if (!options)
639 return 0;
640 if (strlen(options) == 0)
641 return 0;
642
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200643 /* Make sure the encoding of the LCO is consistant before we proceed */
644 if (check_local_cx_options(ctx, options) != 0) {
645 LOGP(DLMGCP, LOGL_ERROR,
646 "local CX options: Internal inconsistency in Local Connection Options!\n");
647 return 524;
648 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200649
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200650 talloc_free(lco->string);
651 lco->string = talloc_strdup(ctx, options);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200652
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200653 lco_id = lco->string;
654 while ((lco_id = get_lco_identifier(lco_id))) {
655 switch (tolower(lco_id[0])) {
656 case 'p':
657 if (sscanf(lco_id + 1, ":%d-%d",
658 &lco->pkt_period_min, &lco->pkt_period_max) == 1)
659 lco->pkt_period_max = lco->pkt_period_min;
660 break;
661 case 'a':
Pau Espin Pedrol1dc2dce2020-09-21 11:25:15 +0200662 /* FIXME: LCO also supports the negotiation of more than one codec.
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200663 * (e.g. a:PCMU;G726-32) But this implementation only supports a single
664 * codec only. */
665 if (sscanf(lco_id + 1, ":%16[^,]", codec) == 1) {
666 talloc_free(lco->codec);
Pau Espin Pedrol83fd8a52019-06-26 12:55:26 +0200667 /* MGCP header is case insensive, and we'll need
668 codec in uppercase when using it later: */
669 len = strlen(codec);
670 lco->codec = talloc_size(ctx, len + 1);
671 osmo_str_toupper_buf(lco->codec, len + 1, codec);
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200672 }
673 break;
Pau Espin Pedrol19539862020-09-21 11:43:41 +0200674 case 'n':
675 if (lco_id[1] == 't' && sscanf(lco_id + 2, ":%16[^,]", nt) == 1)
676 break;
677 /* else: fall throught to print notice log */
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200678 default:
679 LOGP(DLMGCP, LOGL_NOTICE,
680 "LCO: unhandled option: '%c'/%d in \"%s\"\n",
681 *lco_id, *lco_id, lco->string);
682 break;
683 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200684
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200685 lco_id = strchr(lco_id, ',');
686 if (!lco_id)
687 break;
Philipp Maier604410c2018-06-06 10:02:16 +0200688 }
Philipp Maier87bd9be2017-08-22 16:35:41 +0200689
690 LOGP(DLMGCP, LOGL_DEBUG,
691 "local CX options: lco->pkt_period_max: %i, lco->codec: %s\n",
692 lco->pkt_period_max, lco->codec);
Philipp Maiera390d0b2018-01-31 17:30:19 +0100693
694 /* Check if the packetization fits the 20ms raster */
695 if (lco->pkt_period_min % 20 && lco->pkt_period_max % 20) {
696 LOGP(DLMGCP, LOGL_ERROR,
697 "local CX options: packetization interval is not a multiple of 20ms!\n");
698 return 535;
699 }
700
701 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200702}
703
704void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
705 struct mgcp_rtp_end *rtp)
706{
Philipp Maier14b27a82020-06-02 20:15:30 +0200707 struct mgcp_trunk *trunk = endp->trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200708
Philipp Maier14b27a82020-06-02 20:15:30 +0200709 int patch_ssrc = expect_ssrc_change && trunk->force_constant_ssrc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200710
Philipp Maier14b27a82020-06-02 20:15:30 +0200711 rtp->force_aligned_timing = trunk->force_aligned_timing;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200712 rtp->force_constant_ssrc = patch_ssrc ? 1 : 0;
Philipp Maier14b27a82020-06-02 20:15:30 +0200713 rtp->rfc5993_hr_convert = trunk->rfc5993_hr_convert;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200714
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200715 LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
716 "Configuring RTP endpoint: local port %d%s%s\n",
Pau Espin Pedrol5ffd1272022-10-04 13:45:48 +0200717 osmo_sockaddr_port(&rtp->addr.u.sa),
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200718 rtp->force_aligned_timing ? ", force constant timing" : "",
719 rtp->force_constant_ssrc ? ", force constant ssrc" : "");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200720}
721
Pau Espin Pedrol8358c4b2021-07-07 12:41:38 +0200722uint32_t mgcp_rtp_packet_duration(const struct mgcp_endpoint *endp,
723 const struct mgcp_rtp_end *rtp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200724{
725 int f = 0;
726
727 /* Get the number of frames per channel and packet */
728 if (rtp->frames_per_packet)
729 f = rtp->frames_per_packet;
Philipp Maierbc0346e2018-06-07 09:52:16 +0200730 else if (rtp->packet_duration_ms && rtp->codec->frame_duration_num) {
731 int den = 1000 * rtp->codec->frame_duration_num;
732 f = (rtp->packet_duration_ms * rtp->codec->frame_duration_den +
Philipp Maier87bd9be2017-08-22 16:35:41 +0200733 den / 2)
734 / den;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200735 }
736
Philipp Maierbc0346e2018-06-07 09:52:16 +0200737 return rtp->codec->rate * f * rtp->codec->frame_duration_num /
738 rtp->codec->frame_duration_den;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200739}
740
Pau Espin Pedrol9fb8ddf2019-05-08 15:35:36 +0200741/*! Initializes osmux socket if not yet initialized. Parses Osmux CID from MGCP line.
742 * \param[in] endp Endpoint willing to initialize osmux
743 * \param[in] line Line X-Osmux from MGCP header msg to parse
744 * \returns OSMUX CID, -1 for wildcard, -2 on parse error, -3 on osmux initalize error
745 */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200746static int mgcp_osmux_setup(struct mgcp_endpoint *endp, const char *line)
747{
Pau Espin Pedrol36413c02022-10-12 17:58:17 +0200748 if (!endp->trunk->cfg->osmux.initialized) {
Pau Espin Pedrolc1ad7fd2022-10-06 10:32:07 +0200749 if (osmux_init(endp->trunk) < 0) {
Pau Espin Pedrol8e0d1012022-10-06 10:29:11 +0200750 LOGPENDP(endp, DOSMUX, LOGL_ERROR, "Cannot init OSMUX\n");
Pau Espin Pedrol9fb8ddf2019-05-08 15:35:36 +0200751 return -3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200752 }
Pau Espin Pedrol8e0d1012022-10-06 10:29:11 +0200753 LOGPENDP(endp, DOSMUX, LOGL_NOTICE, "OSMUX socket has been set up\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200754 }
755
756 return mgcp_parse_osmux_cid(line);
757}
758
Philipp Maierbc0346e2018-06-07 09:52:16 +0200759/* Process codec information contained in CRCX/MDCX */
760static int handle_codec_info(struct mgcp_conn_rtp *conn,
Philipp Maier8dc35972021-07-14 11:20:16 +0200761 struct mgcp_request_data *rq, int have_sdp, bool crcx)
Philipp Maierbc0346e2018-06-07 09:52:16 +0200762{
Philipp Maier8dc35972021-07-14 11:20:16 +0200763 struct mgcp_endpoint *endp = rq->endp;
Philipp Maier4c4d2272023-04-26 15:45:17 +0200764 struct mgcp_conn *conn_dst;
765 struct mgcp_conn_rtp *conn_dst_rtp;
766
Philipp Maierbc0346e2018-06-07 09:52:16 +0200767 int rc;
768 char *cmd;
769
770 if (crcx)
771 cmd = "CRCX";
772 else
773 cmd = "MDCX";
774
775 /* Collect codec information */
776 if (have_sdp) {
777 /* If we have SDP, we ignore the local connection options and
778 * use only the SDP information. */
779 mgcp_codec_reset_all(conn);
Philipp Maier8dc35972021-07-14 11:20:16 +0200780 rc = mgcp_parse_sdp_data(endp, conn, rq->pdata);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200781 if (rc != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200782 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
783 "%s: sdp not parseable\n", cmd);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200784
785 /* See also RFC 3661: Protocol error */
786 return 510;
787 }
788 } else if (endp->local_options.codec) {
789 /* When no SDP is available, we use the codec information from
790 * the local connection options (if present) */
791 mgcp_codec_reset_all(conn);
Philipp Maier228e5912019-03-05 13:56:59 +0100792 rc = mgcp_codec_add(conn, PTYPE_UNDEFINED, endp->local_options.codec, NULL);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200793 if (rc != 0)
794 goto error;
795 }
796
797 /* Make sure we always set a sane default codec */
798 if (conn->end.codecs_assigned == 0) {
799 /* When SDP and/or LCO did not supply any codec information,
800 * than it makes sense to pick a sane default: (payload-type 0,
801 * PCMU), see also: OS#2658 */
802 mgcp_codec_reset_all(conn);
Philipp Maier228e5912019-03-05 13:56:59 +0100803 rc = mgcp_codec_add(conn, 0, NULL, NULL);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200804 if (rc != 0)
805 goto error;
806 }
807
Philipp Maier4c4d2272023-04-26 15:45:17 +0200808 /* Try to find an destination RTP connection that we can include in the codec decision. */
809 conn_dst = mgcp_find_dst_conn(conn->conn);
810 if (conn_dst && conn_dst->type == MGCP_CONN_TYPE_RTP)
811 conn_dst_rtp = &conn_dst->u.rtp;
812 else
813 conn_dst_rtp = NULL;
814
Philipp Maierbc0346e2018-06-07 09:52:16 +0200815 /* Make codec decision */
Philipp Maier4c4d2272023-04-26 15:45:17 +0200816 if (mgcp_codec_decide(conn, conn_dst_rtp) != 0)
Philipp Maierbc0346e2018-06-07 09:52:16 +0200817 goto error;
818
819 return 0;
820
821error:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200822 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
823 "%s: codec negotiation failure\n", cmd);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200824
825 /* See also RFC 3661: Codec negotiation failure */
826 return 534;
827}
828
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200829static bool parse_x_osmo_ign(struct mgcp_endpoint *endp, char *line)
830{
831 char *saveptr = NULL;
832
Pau Espin Pedrol6049a632020-09-21 11:03:21 +0200833 if (strncasecmp(line, MGCP_X_OSMO_IGN_HEADER, strlen(MGCP_X_OSMO_IGN_HEADER)))
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200834 return false;
835 line += strlen(MGCP_X_OSMO_IGN_HEADER);
836
837 while (1) {
838 char *token = strtok_r(line, " ", &saveptr);
839 line = NULL;
840 if (!token)
841 break;
842
Pau Espin Pedrol6e26c702019-06-26 13:09:39 +0200843 if (!strcasecmp(token, "C"))
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200844 endp->x_osmo_ign |= MGCP_X_OSMO_IGN_CALLID;
845 else
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200846 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "received unknown X-Osmo-IGN item '%s'\n", token);
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200847 }
848
849 return true;
850}
851
Philipp Maier87bd9be2017-08-22 16:35:41 +0200852/* CRCX command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +0200853static struct msgb *handle_create_con(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200854{
Philipp Maier8dc35972021-07-14 11:20:16 +0200855 struct mgcp_parse_data *pdata = rq->pdata;
856 struct mgcp_trunk *trunk = rq->trunk;
857 struct mgcp_endpoint *endp = rq->endp;
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200858 struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_crcx_ctr_group;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200859 int error_code = 400;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200860 const char *local_options = NULL;
861 const char *callid = NULL;
862 const char *mode = NULL;
863 char *line;
Pau Espin Pedrol21779192022-09-23 16:46:33 +0200864 int have_sdp = 0, remote_osmux_cid = -2;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200865 struct mgcp_conn_rtp *conn = NULL;
Philipp Maierffd75e42017-11-22 11:44:50 +0100866 struct mgcp_conn *_conn = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200867 char conn_name[512];
Philipp Maiera390d0b2018-01-31 17:30:19 +0100868 int rc;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200869
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200870 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "CRCX: creating new connection ...\n");
Philipp Maier246233d2020-08-18 15:15:24 +0200871
Ericee6958c2021-09-14 18:36:53 +0200872 /* we must have a free ep */
873 if (!endp) {
874 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_AVAIL));
875 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "CRCX: no free endpoints available!\n");
876 return create_err_response(rq->trunk, NULL, 403, "CRCX", pdata->trans);
877 }
878
Philipp Maier8d6a1932020-06-18 12:19:31 +0200879 if (!mgcp_endp_avail(endp)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200880 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_AVAIL));
Philipp Maiera910a812020-08-18 15:13:33 +0200881 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
882 "CRCX: selected endpoint not available!\n");
Ericfbcf4a62021-09-09 18:02:31 +0200883 return create_err_response(rq->trunk, NULL, 501, "CRCX", pdata->trans);
Philipp Maier8d6a1932020-06-18 12:19:31 +0200884 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200885
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200886 /* parse CallID C: and LocalParameters L: */
Philipp Maier8dc35972021-07-14 11:20:16 +0200887 for_each_line(line, pdata->save) {
Philipp Maier036612b2021-07-19 17:47:49 +0200888 if (!mgcp_check_param(endp, trunk, line))
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200889 continue;
890
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +0200891 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200892 case 'L':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200893 local_options = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200894 break;
895 case 'C':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200896 callid = (const char *)line + 3;
897 break;
898 case 'I':
Philipp Maierffd75e42017-11-22 11:44:50 +0100899 /* It is illegal to send a connection identifier
900 * together with a CRCX, the MGW will assign the
901 * connection identifier by itself on CRCX */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200902 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BAD_ACTION));
Ericfbcf4a62021-09-09 18:02:31 +0200903 return create_err_response(rq->trunk, NULL, 523, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200904 break;
905 case 'M':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200906 mode = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200907 break;
908 case 'X':
Pau Espin Pedrolc1bf4692019-05-14 16:23:24 +0200909 if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200910 /* If osmux is disabled, just skip setting it up */
Pau Espin Pedrol36413c02022-10-12 17:58:17 +0200911 if (rq->endp->trunk->cfg->osmux.usage == OSMUX_USAGE_OFF)
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200912 break;
Pau Espin Pedrol21779192022-09-23 16:46:33 +0200913 remote_osmux_cid = mgcp_osmux_setup(endp, line);
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200914 break;
915 }
916
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200917 if (parse_x_osmo_ign(endp, line))
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200918 break;
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200919
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200920 /* Ignore unknown X-headers */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200921 break;
922 case '\0':
923 have_sdp = 1;
924 goto mgcp_header_done;
925 default:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200926 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
927 "CRCX: unhandled option: '%c'/%d\n", *line, *line);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200928 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_UNHANDLED_PARAM));
Ericfbcf4a62021-09-09 18:02:31 +0200929 return create_err_response(rq->trunk, NULL, 539, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200930 break;
931 }
932 }
933
934mgcp_header_done:
Philipp Maier87bd9be2017-08-22 16:35:41 +0200935 /* Check parameters */
936 if (!callid) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200937 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
938 "CRCX: insufficient parameters, missing callid\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200939 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_MISSING_CALLID));
Ericfbcf4a62021-09-09 18:02:31 +0200940 return create_err_response(endp, endp, 516, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200941 }
942
Philipp Maier87bd9be2017-08-22 16:35:41 +0200943 if (!mode) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200944 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
945 "CRCX: insufficient parameters, missing mode\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200946 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_MODE));
Ericfbcf4a62021-09-09 18:02:31 +0200947 return create_err_response(endp, endp, 517, "CRCX", pdata->trans);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200948 }
949
Philipp Maier87bd9be2017-08-22 16:35:41 +0200950 /* Check if we are able to accept the creation of another connection */
951 if (llist_count(&endp->conns) >= endp->type->max_conns) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200952 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
953 "CRCX: endpoint full, max. %i connections allowed!\n",
954 endp->type->max_conns);
Philipp Maier14b27a82020-06-02 20:15:30 +0200955 if (trunk->force_realloc) {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200956 /* There is no more room for a connection, make some
957 * room by blindly tossing the oldest of the two two
958 * connections */
959 mgcp_conn_free_oldest(endp);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200960 } else {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200961 /* There is no more room for a connection, leave
962 * everything as it is and return with an error */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200963 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_LIMIT_EXCEEDED));
Ericfbcf4a62021-09-09 18:02:31 +0200964 return create_err_response(endp, endp, 540, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200965 }
966 }
967
Philipp Maier87bd9be2017-08-22 16:35:41 +0200968 /* Check if this endpoint already serves a call, if so, check if the
969 * callids match up so that we are sure that this is our call */
970 if (endp->callid && mgcp_verify_call_id(endp, callid)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200971 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
972 "CRCX: already seized by other call (%s)\n",
973 endp->callid);
Philipp Maier14b27a82020-06-02 20:15:30 +0200974 if (trunk->force_realloc)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200975 /* This is not our call, toss everything by releasing
976 * the entire endpoint. (rude!) */
Philipp Maier1355d7e2018-02-01 14:30:06 +0100977 mgcp_endp_release(endp);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200978 else {
979 /* This is not our call, leave everything as it is and
980 * return with an error. */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200981 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_UNKNOWN_CALLID));
Ericfbcf4a62021-09-09 18:02:31 +0200982 return create_err_response(endp, endp, 400, "CRCX", pdata->trans);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200983 }
984 }
985
Philipp Maier889fe7f2020-07-06 17:44:12 +0200986 if (!endp->callid) {
987 /* Claim endpoint resources. This will also set the callid,
988 * creating additional connections will only be possible if
989 * the callid matches up (see above). */
990 rc = mgcp_endp_claim(endp, callid);
991 if (rc != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200992 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_CLAIM));
Ericfbcf4a62021-09-09 18:02:31 +0200993 return create_err_response(endp, endp, 502, "CRCX", pdata->trans);
Philipp Maier889fe7f2020-07-06 17:44:12 +0200994 }
995 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200996
Philipp Maierffd75e42017-11-22 11:44:50 +0100997 snprintf(conn_name, sizeof(conn_name), "%s", callid);
Philipp Maier14b27a82020-06-02 20:15:30 +0200998 _conn = mgcp_conn_alloc(trunk->endpoints, endp, MGCP_CONN_TYPE_RTP, conn_name);
Philipp Maierffd75e42017-11-22 11:44:50 +0100999 if (!_conn) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001000 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1001 "CRCX: unable to allocate RTP connection\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001002 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_ALLOC_CONN));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001003 goto error2;
1004
Philipp Maier87bd9be2017-08-22 16:35:41 +02001005 }
Philipp Maier889fe7f2020-07-06 17:44:12 +02001006
Philipp Maierffd75e42017-11-22 11:44:50 +01001007 conn = mgcp_conn_get_rtp(endp, _conn->id);
1008 OSMO_ASSERT(conn);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001009
1010 if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
1011 error_code = 517;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001012 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_MODE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001013 goto error2;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001014 }
1015
Pau Espin Pedrol21779192022-09-23 16:46:33 +02001016 /* If X-Osmux (remote CID) was received (-1 is wilcard), alloc next avail CID as local CID */
1017 if (remote_osmux_cid >= -1) {
Pau Espin Pedrold48a8112022-09-27 12:37:53 +02001018 if (osmux_init_conn(conn) < 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001019 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_OSMUX));
Pau Espin Pedrol2b896172019-04-24 13:47:23 +02001020 goto error2;
1021 }
Pau Espin Pedrol21779192022-09-23 16:46:33 +02001022 if (remote_osmux_cid >= 0) {
1023 conn->osmux.remote_cid_present = true;
1024 conn->osmux.remote_cid = remote_osmux_cid;
1025 }
Pau Espin Pedrol36413c02022-10-12 17:58:17 +02001026 } else if (endp->trunk->cfg->osmux.usage == OSMUX_USAGE_ONLY) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001027 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
1028 "CRCX: osmux only and no osmux offered\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001029 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_OSMUX));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001030 goto error2;
1031 }
1032
Philipp Maierbc0346e2018-06-07 09:52:16 +02001033 /* Set local connection options, if present */
1034 if (local_options) {
Philipp Maier8dc35972021-07-14 11:20:16 +02001035 rc = set_local_cx_options(trunk->endpoints,
Philipp Maierbc0346e2018-06-07 09:52:16 +02001036 &endp->local_options, local_options);
1037 if (rc != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001038 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
Oliver Smith169d50e2023-01-24 13:12:54 +01001039 "CRCX: invalid local connection options!\n");
Philipp Maierbc0346e2018-06-07 09:52:16 +02001040 error_code = rc;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001041 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS));
Philipp Maierbc0346e2018-06-07 09:52:16 +02001042 goto error2;
1043 }
1044 }
1045
1046 /* Handle codec information and decide for a suitable codec */
Philipp Maier8dc35972021-07-14 11:20:16 +02001047 rc = handle_codec_info(conn, rq, have_sdp, true);
Philipp Maierbc0346e2018-06-07 09:52:16 +02001048 mgcp_codec_summary(conn);
1049 if (rc) {
1050 error_code = rc;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001051 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_CODEC_NEGOTIATION));
Philipp Maierbc0346e2018-06-07 09:52:16 +02001052 goto error2;
1053 }
Pau Espin Pedrolbb3ccde2021-12-23 19:49:26 +01001054 /* Upgrade the conn type RTP_DEFAULT->RTP_IUUP if needed based on requested codec: */
1055 /* TODO: "codec" probably needs to be moved from endp to conn */
1056 if (conn->type == MGCP_RTP_DEFAULT && strcmp(conn->end.codec->subtype_name, "VND.3GPP.IUFP") == 0) {
1057 rc = mgcp_conn_iuup_init(conn);
1058 }
Philipp Maierbc0346e2018-06-07 09:52:16 +02001059
Philipp Maier14b27a82020-06-02 20:15:30 +02001060 conn->end.fmtp_extra = talloc_strdup(trunk->endpoints,
1061 trunk->audio_fmtp_extra);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001062
Philipp Maier8dc35972021-07-14 11:20:16 +02001063 if (pdata->cfg->force_ptime) {
1064 conn->end.packet_duration_ms = pdata->cfg->force_ptime;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001065 conn->end.force_output_ptime = 1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001066 }
1067
Philipp Maier1cb1e382017-11-02 17:16:04 +01001068 mgcp_rtp_end_config(endp, 0, &conn->end);
1069
Philipp Maierc3cc6542018-02-02 12:58:42 +01001070 /* check connection mode setting */
1071 if (conn->conn->mode != MGCP_CONN_LOOPBACK
1072 && conn->conn->mode != MGCP_CONN_RECV_ONLY
Pau Espin Pedrol5ffd1272022-10-04 13:45:48 +02001073 && osmo_sockaddr_port(&conn->end.addr.u.sa) == 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001074 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
1075 "CRCX: selected connection mode type requires an opposite end!\n");
Philipp Maierc3cc6542018-02-02 12:58:42 +01001076 error_code = 527;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001077 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC));
Philipp Maierc3cc6542018-02-02 12:58:42 +01001078 goto error2;
1079 }
1080
Pau Espin Pedrol71d42e72020-09-03 14:20:07 +02001081 /* Find a local address for conn based on policy and initial SDP remote
1082 information, then find a free port for it */
Pau Espin Pedrol70c03f52022-10-04 16:49:41 +02001083 if (mgcp_get_local_addr(conn->end.local_addr, conn) < 0) {
1084 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
1085 goto error2;
1086 }
Philipp Maier1cb1e382017-11-02 17:16:04 +01001087 if (allocate_port(endp, conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001088 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
Philipp Maier1cb1e382017-11-02 17:16:04 +01001089 goto error2;
1090 }
1091
Philipp Maier87bd9be2017-08-22 16:35:41 +02001092 if (setup_rtp_processing(endp, conn) != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001093 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
1094 "CRCX: could not start RTP processing!\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001095 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_START_RTP));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001096 goto error2;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001097 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001098
Pau Espin Pedrol833281d2022-10-06 17:41:22 +02001099 /* Notify Osmux conn that CRCX was received */
1100 if (mgcp_conn_rtp_is_osmux(conn)) {
1101 if (conn_osmux_event_rx_crcx_mdcx(conn) < 0) {
1102 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR, "CRCX: Osmux handling failed!\n");
1103 goto error2;
1104 }
1105 }
1106
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001107 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
1108 "CRCX: Creating connection: port: %u\n", conn->end.local_port);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001109
Philipp Maiere726d4f2017-11-01 10:41:34 +01001110 /* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
Philipp Maier14b27a82020-06-02 20:15:30 +02001111 OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Pau Espin Pedrolca280a12021-07-06 18:15:35 +02001112 if (conn->conn->mode & MGCP_CONN_RECV_ONLY &&
Pau Espin Pedrolca280a12021-07-06 18:15:35 +02001113 trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001114 send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001115
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001116 LOGPCONN(_conn, DLMGCP, LOGL_NOTICE,
1117 "CRCX: connection successfully created\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001118 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_SUCCESS));
Philipp Maier889fe7f2020-07-06 17:44:12 +02001119 mgcp_endp_update(endp);
Philipp Maier41d59202021-07-20 15:49:00 +02001120
1121 /* NOTE: Only in the virtual trunk we allow dynamic endpoint names */
1122 bool add_epname = rq->wildcarded && trunk->trunk_type == MGCP_TRUNK_VIRTUAL;
1123 return create_response_with_sdp(endp, conn, "CRCX", pdata->trans, add_epname, true);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001124error2:
Philipp Maier1355d7e2018-02-01 14:30:06 +01001125 mgcp_endp_release(endp);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001126 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1127 "CRCX: unable to create connection\n");
Ericfbcf4a62021-09-09 18:02:31 +02001128 return create_err_response(endp, endp, error_code, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001129}
1130
Philipp Maier87bd9be2017-08-22 16:35:41 +02001131/* MDCX command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +02001132static struct msgb *handle_modify_con(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001133{
Philipp Maier8dc35972021-07-14 11:20:16 +02001134 struct mgcp_parse_data *pdata = rq->pdata;
1135 struct mgcp_trunk *trunk = rq->trunk;
1136 struct mgcp_endpoint *endp = rq->endp;
1137 struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_mdcx_ctr_group;
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001138 char new_local_addr[INET6_ADDRSTRLEN];
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001139 int error_code = 500;
1140 int silent = 0;
1141 int have_sdp = 0;
1142 char *line;
1143 const char *local_options = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001144 const char *mode = NULL;
1145 struct mgcp_conn_rtp *conn = NULL;
Philipp Maier01d24a32017-11-21 17:26:09 +01001146 const char *conn_id = NULL;
Pau Espin Pedrol21779192022-09-23 16:46:33 +02001147 int remote_osmux_cid = -2;
Philipp Maiera390d0b2018-01-31 17:30:19 +01001148 int rc;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001149
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001150 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "MDCX: modifying existing connection ...\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001151
Philipp Maier5656fbf2018-02-02 14:41:58 +01001152 /* Prohibit wildcarded requests */
Philipp Maier8dc35972021-07-14 11:20:16 +02001153 if (rq->wildcarded) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001154 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1155 "MDCX: wildcarded endpoint names not supported.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001156 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_WILDCARD));
Ericfbcf4a62021-09-09 18:02:31 +02001157 return create_err_response(rq->trunk, endp, 507, "MDCX", pdata->trans);
Philipp Maier5656fbf2018-02-02 14:41:58 +01001158 }
1159
Ericee6958c2021-09-14 18:36:53 +02001160 if (!endp || !mgcp_endp_avail(endp)) {
1161 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_AVAIL));
1162 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "MDCX: selected endpoint not available!\n");
1163 return create_err_response(rq->trunk, NULL, 501, "MDCX", pdata->trans);
1164 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001165 if (llist_count(&endp->conns) <= 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001166 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1167 "MDCX: endpoint is not holding a connection.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001168 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_CONN));
Ericfbcf4a62021-09-09 18:02:31 +02001169 return create_err_response(endp, endp, 400, "MDCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001170 }
1171
Philipp Maier8dc35972021-07-14 11:20:16 +02001172 for_each_line(line, pdata->save) {
Philipp Maier036612b2021-07-19 17:47:49 +02001173 if (!mgcp_check_param(endp, trunk, line))
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001174 continue;
1175
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001176 switch (toupper(line[0])) {
Philipp Maier87bd9be2017-08-22 16:35:41 +02001177 case 'C':
Harald Weltee35eeae2017-12-28 13:47:37 +01001178 if (mgcp_verify_call_id(endp, line + 3) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001179 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CALLID));
Harald Weltee35eeae2017-12-28 13:47:37 +01001180 error_code = 516;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001181 goto error3;
Harald Weltee35eeae2017-12-28 13:47:37 +01001182 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001183 break;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001184 case 'I':
Philipp Maier01d24a32017-11-21 17:26:09 +01001185 conn_id = (const char *)line + 3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001186 if ((error_code = mgcp_verify_ci(endp, conn_id))) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001187 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CONNID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001188 goto error3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001189 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001190 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001191 case 'L':
Philipp Maier87bd9be2017-08-22 16:35:41 +02001192 local_options = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001193 break;
1194 case 'M':
Philipp Maier87bd9be2017-08-22 16:35:41 +02001195 mode = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001196 break;
1197 case 'Z':
Pau Espin Pedrol9b508f62019-06-26 13:11:22 +02001198 silent = strcasecmp("noanswer", line + 3) == 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001199 break;
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001200 case 'X':
Pau Espin Pedrolc1bf4692019-05-14 16:23:24 +02001201 if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001202 /* If osmux is disabled, just skip setting it up */
Pau Espin Pedrol36413c02022-10-12 17:58:17 +02001203 if (endp->trunk->cfg->osmux.usage == OSMUX_USAGE_OFF)
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001204 break;
Pau Espin Pedrol21779192022-09-23 16:46:33 +02001205 remote_osmux_cid = mgcp_osmux_setup(endp, line);
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001206 break;
1207 }
1208 /* Ignore unknown X-headers */
1209 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001210 case '\0':
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001211 have_sdp = 1;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001212 goto mgcp_header_done;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001213 break;
1214 default:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001215 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1216 "MDCX: Unhandled MGCP option: '%c'/%d\n",
1217 line[0], line[0]);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001218 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_UNHANDLED_PARAM));
Ericfbcf4a62021-09-09 18:02:31 +02001219 return create_err_response(rq->trunk, NULL, 539, "MDCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001220 break;
1221 }
1222 }
1223
Philipp Maier87bd9be2017-08-22 16:35:41 +02001224mgcp_header_done:
Philipp Maier01d24a32017-11-21 17:26:09 +01001225 if (!conn_id) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001226 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1227 "MDCX: insufficient parameters, missing ci (connectionIdentifier)\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001228 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_CONNID));
Ericfbcf4a62021-09-09 18:02:31 +02001229 return create_err_response(endp, endp, 515, "MDCX", pdata->trans);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001230 }
1231
1232 conn = mgcp_conn_get_rtp(endp, conn_id);
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001233 if (!conn) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001234 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_CONN_NOT_FOUND));
Ericfbcf4a62021-09-09 18:02:31 +02001235 return create_err_response(endp, endp, 400, "MDCX", pdata->trans);
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001236 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001237
Oliver Smithe36b7752019-01-22 16:31:36 +01001238 mgcp_conn_watchdog_kick(conn->conn);
1239
Philipp Maier87bd9be2017-08-22 16:35:41 +02001240 if (mode) {
1241 if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001242 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_MODE));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001243 error_code = 517;
1244 goto error3;
1245 }
1246 } else
Pau Espin Pedrol209eb9f2019-04-24 12:03:04 +02001247 conn->conn->mode = conn->conn->mode_orig;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001248
Philipp Maierbc0346e2018-06-07 09:52:16 +02001249 /* Set local connection options, if present */
1250 if (local_options) {
Philipp Maier8dc35972021-07-14 11:20:16 +02001251 rc = set_local_cx_options(trunk->endpoints,
Philipp Maierbc0346e2018-06-07 09:52:16 +02001252 &endp->local_options, local_options);
1253 if (rc != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001254 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1255 "MDCX: invalid local connection options!\n");
Philipp Maierbc0346e2018-06-07 09:52:16 +02001256 error_code = rc;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001257 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS));
Philipp Maierbc0346e2018-06-07 09:52:16 +02001258 goto error3;
1259 }
1260 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001261
Philipp Maierbc0346e2018-06-07 09:52:16 +02001262 /* Handle codec information and decide for a suitable codec */
Philipp Maier8dc35972021-07-14 11:20:16 +02001263 rc = handle_codec_info(conn, rq, have_sdp, false);
Philipp Maierbc0346e2018-06-07 09:52:16 +02001264 mgcp_codec_summary(conn);
1265 if (rc) {
Philipp Maieraf07f662018-02-02 11:34:02 +01001266 error_code = rc;
1267 goto error3;
Philipp Maiera390d0b2018-01-31 17:30:19 +01001268 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001269
Philipp Maierc3cc6542018-02-02 12:58:42 +01001270 /* check connection mode setting */
1271 if (conn->conn->mode != MGCP_CONN_LOOPBACK
1272 && conn->conn->mode != MGCP_CONN_RECV_ONLY
Pau Espin Pedrold6769ea2021-07-06 19:44:27 +02001273 && !mgcp_rtp_end_remote_addr_available(&conn->end)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001274 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1275 "MDCX: selected connection mode type requires an opposite end!\n");
Philipp Maierc3cc6542018-02-02 12:58:42 +01001276 error_code = 527;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001277 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC));
Philipp Maierc3cc6542018-02-02 12:58:42 +01001278 goto error3;
1279 }
1280
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001281 if (mgcp_conn_rtp_is_osmux(conn)) {
Pau Espin Pedrol21779192022-09-23 16:46:33 +02001282 OSMO_ASSERT(conn->osmux.local_cid_allocated);
1283 if (remote_osmux_cid < -1) {
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001284 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1285 "MDCX: Failed to parse Osmux CID!\n");
1286 goto error3;
Pau Espin Pedrol21779192022-09-23 16:46:33 +02001287 } else if (remote_osmux_cid == -1) {
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001288 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1289 "MDCX: wilcard in MDCX is not supported!\n");
1290 goto error3;
Pau Espin Pedrol21779192022-09-23 16:46:33 +02001291 } else if (conn->osmux.remote_cid_present &&
Pau Espin Pedrol833281d2022-10-06 17:41:22 +02001292 remote_osmux_cid != conn->osmux.remote_cid) {
1293 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1294 "MDCX: changing already allocated CID is not supported!\n");
1295 goto error3;
1296 } else {
1297 conn->osmux.remote_cid_present = true;
1298 conn->osmux.remote_cid = remote_osmux_cid;
1299 if (conn_osmux_event_rx_crcx_mdcx(conn) < 0) {
1300 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1301 "MDCX: Osmux handling failed!\n");
1302 goto error3;
1303 }
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001304 }
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001305 }
Philipp Maierbc0346e2018-06-07 09:52:16 +02001306
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001307 /* MDCX may have provided a new remote address, which means we may need
1308 to update our announced IP addr and re-bind our local end. This can
1309 happen for instance if MGW initially provided an IPv4 during CRCX
1310 ACK, and now MDCX tells us the remote has an IPv6 address. */
Pau Espin Pedrol70c03f52022-10-04 16:49:41 +02001311 if (mgcp_get_local_addr(new_local_addr, conn) < 0) {
1312 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
1313 goto error3;
1314 }
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001315 if (strcmp(new_local_addr, conn->end.local_addr)) {
1316 osmo_strlcpy(conn->end.local_addr, new_local_addr, sizeof(conn->end.local_addr));
1317 mgcp_free_rtp_port(&conn->end);
1318 if (allocate_port(endp, conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001319 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001320 goto error3;
1321 }
1322 }
1323
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001324 if (setup_rtp_processing(endp, conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001325 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_START_RTP));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001326 goto error3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001327 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001328
Philipp Maier87bd9be2017-08-22 16:35:41 +02001329 mgcp_rtp_end_config(endp, 1, &conn->end);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001330
1331 /* modify */
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001332 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
1333 "MDCX: modified conn:%s\n", mgcp_conn_dump(conn->conn));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001334
Philipp Maiere726d4f2017-11-01 10:41:34 +01001335 /* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
Philipp Maier8dc35972021-07-14 11:20:16 +02001336 OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Pau Espin Pedrolca280a12021-07-06 18:15:35 +02001337 if (conn->conn->mode & MGCP_CONN_RECV_ONLY &&
Philipp Maier8dc35972021-07-14 11:20:16 +02001338 trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001339 send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001340
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001341 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_SUCCESS));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001342 if (silent)
1343 goto out_silent;
1344
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001345 LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
1346 "MDCX: connection successfully modified\n");
Philipp Maier889fe7f2020-07-06 17:44:12 +02001347 mgcp_endp_update(endp);
Philipp Maier41d59202021-07-20 15:49:00 +02001348 return create_response_with_sdp(endp, conn, "MDCX", pdata->trans, false, false);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001349error3:
Ericfbcf4a62021-09-09 18:02:31 +02001350 return create_err_response(endp, endp, error_code, "MDCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001351
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001352out_silent:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001353 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "MDCX: silent exit\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001354 return NULL;
1355}
1356
Philipp Maier87bd9be2017-08-22 16:35:41 +02001357/* DLCX command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +02001358static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001359{
Philipp Maier8dc35972021-07-14 11:20:16 +02001360 struct mgcp_parse_data *pdata = rq->pdata;
1361 struct mgcp_trunk *trunk = rq->trunk;
1362 struct mgcp_endpoint *endp = rq->endp;
1363 struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_dlcx_ctr_group;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001364 int error_code = 400;
1365 int silent = 0;
1366 char *line;
1367 char stats[1048];
Philipp Maier01d24a32017-11-21 17:26:09 +01001368 const char *conn_id = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001369 struct mgcp_conn_rtp *conn = NULL;
Philipp Maierf486e742021-07-19 14:56:29 +02001370 unsigned int i;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001371
Philipp Maierf486e742021-07-19 14:56:29 +02001372 /* NOTE: In this handler we can not take it for granted that the endp
1373 * pointer will be populated, however a trunk is always guaranteed. */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001374
Philipp Maierf486e742021-07-19 14:56:29 +02001375 LOGPEPTR(endp, trunk, DLMGCP, LOGL_NOTICE, "DLCX: deleting connection(s) ...\n");
1376
1377 if (endp && !mgcp_endp_avail(endp)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001378 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_AVAIL));
Philipp Maiera910a812020-08-18 15:13:33 +02001379 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1380 "DLCX: selected endpoint not available!\n");
Ericfbcf4a62021-09-09 18:02:31 +02001381 return create_err_response(rq->trunk, NULL, 501, "DLCX", pdata->trans);
Philipp Maier8d6a1932020-06-18 12:19:31 +02001382 }
1383
Philipp Maierf486e742021-07-19 14:56:29 +02001384 if (endp && !rq->wildcarded && llist_empty(&endp->conns)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001385 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1386 "DLCX: endpoint is not holding a connection.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001387 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_NO_CONN));
Ericfbcf4a62021-09-09 18:02:31 +02001388 return create_err_response(endp, endp, 515, "DLCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001389 }
1390
Ericee6958c2021-09-14 18:36:53 +02001391 /* Handle wildcarded DLCX that refers to the whole trunk. This means
1392 * that we walk over all endpoints on the trunk in order to drop all
1393 * connections on the trunk. (see also RFC3435 Annex F.7) */
1394 if (rq->wildcarded) {
1395 int num_conns = 0;
1396 for (i = 0; i < trunk->number_endpoints; i++) {
1397 num_conns += llist_count(&trunk->endpoints[i]->conns);
1398 mgcp_endp_release(trunk->endpoints[i]);
1399 }
1400 rate_ctr_add(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS), num_conns);
1401 return create_ok_response(trunk, NULL, 200, "DLCX", pdata->trans);
1402 }
1403
Philipp Maier8dc35972021-07-14 11:20:16 +02001404 for_each_line(line, pdata->save) {
Philipp Maier036612b2021-07-19 17:47:49 +02001405 if (!mgcp_check_param(endp, trunk, line))
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001406 continue;
1407
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001408 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001409 case 'C':
Philipp Maierf486e742021-07-19 14:56:29 +02001410 /* If we have no endpoint, but a call id in the request,
1411 then this request cannot be handled */
1412 if (!endp) {
1413 LOGPTRUNK(trunk, DLMGCP, LOGL_NOTICE,
1414 "cannot handle requests with call-id (C) without endpoint -- abort!");
1415 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));
Ericfbcf4a62021-09-09 18:02:31 +02001416 return create_err_response(rq->trunk, NULL, 539, "DLCX", pdata->trans);
Philipp Maierf486e742021-07-19 14:56:29 +02001417 }
1418
Harald Weltee35eeae2017-12-28 13:47:37 +01001419 if (mgcp_verify_call_id(endp, line + 3) != 0) {
1420 error_code = 516;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001421 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CALLID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001422 goto error3;
Harald Weltee35eeae2017-12-28 13:47:37 +01001423 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001424 break;
1425 case 'I':
Philipp Maierf486e742021-07-19 14:56:29 +02001426 /* If we have no endpoint, but a connection id in the request,
1427 then this request cannot be handled */
1428 if (!endp) {
1429 LOGPTRUNK(trunk, DLMGCP, LOGL_NOTICE,
1430 "cannot handle requests with conn-id (I) without endpoint -- abort!");
1431 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));
Ericfbcf4a62021-09-09 18:02:31 +02001432 return create_err_response(rq->trunk, NULL, 539, "DLCX", pdata->trans);
Philipp Maierf486e742021-07-19 14:56:29 +02001433 }
1434
Philipp Maier01d24a32017-11-21 17:26:09 +01001435 conn_id = (const char *)line + 3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001436 if ((error_code = mgcp_verify_ci(endp, conn_id))) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001437 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CONNID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001438 goto error3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001439 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001440 break;
1441 case 'Z':
Pau Espin Pedrol9b508f62019-06-26 13:11:22 +02001442 silent = strcasecmp("noanswer", line + 3) == 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001443 break;
1444 default:
Philipp Maierf486e742021-07-19 14:56:29 +02001445 LOGPEPTR(endp, trunk, DLMGCP, LOGL_NOTICE, "DLCX: Unhandled MGCP option: '%c'/%d\n",
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001446 line[0], line[0]);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001447 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));
Ericfbcf4a62021-09-09 18:02:31 +02001448 return create_err_response(rq->trunk, NULL, 539, "DLCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001449 break;
1450 }
1451 }
1452
Philipp Maierce187052021-07-23 10:58:19 +02001453 /* The logic does not permit to go past this point without having the
1454 * the endp pointer populated. */
1455 OSMO_ASSERT(endp);
1456
Philipp Maierf4c0e372017-10-11 16:06:45 +02001457 /* When no connection id is supplied, we will interpret this as a
Philipp Maierf486e742021-07-19 14:56:29 +02001458 * wildcarded DLCX that refers to the selected endpoint. This means
1459 * that we drop all connections on that specific endpoint at once.
1460 * (See also RFC3435 Section F.7) */
Philipp Maier01d24a32017-11-21 17:26:09 +01001461 if (!conn_id) {
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001462 int num_conns = llist_count(&endp->conns);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001463 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1464 "DLCX: missing ci (connectionIdentifier), will remove all connections (%d total) at once\n",
1465 num_conns);
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001466
1467 if (num_conns > 0)
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001468 rate_ctr_add(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS), num_conns);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001469
Philipp Maier1355d7e2018-02-01 14:30:06 +01001470 mgcp_endp_release(endp);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001471
1472 /* Note: In this case we do not return any statistics,
1473 * as we assume that the client is not interested in
1474 * this case. */
Ericfbcf4a62021-09-09 18:02:31 +02001475 return create_ok_response(endp, endp, 200, "DLCX", pdata->trans);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001476 }
1477
Philipp Maierf4c0e372017-10-11 16:06:45 +02001478 /* Find the connection */
Philipp Maier87bd9be2017-08-22 16:35:41 +02001479 conn = mgcp_conn_get_rtp(endp, conn_id);
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001480 if (!conn) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001481 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CONNID));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001482 goto error3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001483 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001484 /* save the statistics of the current connection */
1485 mgcp_format_stats(stats, sizeof(stats), conn->conn);
1486
1487 /* delete connection */
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001488 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG, "DLCX: deleting conn:%s\n",
1489 mgcp_conn_dump(conn->conn));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001490 mgcp_conn_free(endp, conn_id);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001491 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1492 "DLCX: connection successfully deleted\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +02001493
1494 /* When all connections are closed, the endpoint will be released
1495 * in order to be ready to be used by another call. */
1496 if (llist_count(&endp->conns) <= 0) {
Philipp Maier1355d7e2018-02-01 14:30:06 +01001497 mgcp_endp_release(endp);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001498 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: endpoint released\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +02001499 }
1500
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001501 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001502 if (silent)
1503 goto out_silent;
Ericfbcf4a62021-09-09 18:02:31 +02001504 return create_ok_resp_with_param(endp, endp, 250, "DLCX", pdata->trans, stats);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001505
1506error3:
Ericfbcf4a62021-09-09 18:02:31 +02001507 return create_err_response(endp, endp, error_code, "DLCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001508
1509out_silent:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001510 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: silent exit\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001511 return NULL;
1512}
1513
Philipp Maier87bd9be2017-08-22 16:35:41 +02001514/* RSIP command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +02001515static struct msgb *handle_rsip(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001516{
Philipp Maier87bd9be2017-08-22 16:35:41 +02001517 /* TODO: Also implement the resetting of a specific endpoint
1518 * to make mgcp_send_reset_ep() work. Currently this will call
1519 * mgcp_rsip_cb() in mgw_main.c, which sets reset_endpoints=1
1520 * to make read_call_agent() reset all endpoints when called
1521 * next time. In order to selectively reset endpoints some
1522 * mechanism to distinguish which endpoint shall be resetted
1523 * is needed */
1524
1525 LOGP(DLMGCP, LOGL_NOTICE, "RSIP: resetting all endpoints ...\n");
1526
Philipp Maier8dc35972021-07-14 11:20:16 +02001527 if (rq->pdata->cfg->reset_cb)
1528 rq->pdata->cfg->reset_cb(rq->endp->trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001529 return NULL;
1530}
1531
1532static char extract_tone(const char *line)
1533{
1534 const char *str = strstr(line, "D/");
1535 if (!str)
1536 return CHAR_MAX;
1537
1538 return str[2];
1539}
1540
Philipp Maier87bd9be2017-08-22 16:35:41 +02001541/* This can request like DTMF detection and forward, fax detection... it
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001542 * can also request when the notification should be send and such. We don't
Philipp Maier87bd9be2017-08-22 16:35:41 +02001543 * do this right now. */
Philipp Maier8dc35972021-07-14 11:20:16 +02001544static struct msgb *handle_noti_req(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001545{
1546 int res = 0;
1547 char *line;
1548 char tone = CHAR_MAX;
1549
Philipp Maier87bd9be2017-08-22 16:35:41 +02001550 LOGP(DLMGCP, LOGL_NOTICE, "RQNT: processing request for notification ...\n");
1551
Philipp Maier8dc35972021-07-14 11:20:16 +02001552 for_each_line(line, rq->pdata->save) {
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001553 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001554 case 'S':
1555 tone = extract_tone(line);
1556 break;
1557 }
1558 }
1559
1560 /* we didn't see a signal request with a tone */
1561 if (tone == CHAR_MAX)
Ericfbcf4a62021-09-09 18:02:31 +02001562 return create_ok_response(rq->endp, rq->endp, 200, "RQNT", rq->pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001563
Philipp Maier8dc35972021-07-14 11:20:16 +02001564 if (rq->pdata->cfg->rqnt_cb)
1565 res = rq->pdata->cfg->rqnt_cb(rq->endp, tone);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001566
Ericfbcf4a62021-09-09 18:02:31 +02001567 return res == 0 ? create_ok_response(rq->endp, rq->endp, 200, "RQNT", rq->pdata->trans) :
1568 create_err_response(rq->endp, rq->endp, res, "RQNT", rq->pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001569}
1570
Philipp Maier87bd9be2017-08-22 16:35:41 +02001571/* Connection keepalive timer, will take care that dummy packets are send
Harald Welte1d1b98f2017-12-25 10:03:40 +01001572 * regularly, so that NAT connections stay open */
Philipp Maier14b27a82020-06-02 20:15:30 +02001573static void mgcp_keepalive_timer_cb(void *_trunk)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001574{
Philipp Maier14b27a82020-06-02 20:15:30 +02001575 struct mgcp_trunk *trunk = _trunk;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001576 struct mgcp_conn *conn;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001577 int i;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001578
Philipp Maiere726d4f2017-11-01 10:41:34 +01001579 LOGP(DLMGCP, LOGL_DEBUG, "triggered trunk %d keepalive timer\n",
Philipp Maier14b27a82020-06-02 20:15:30 +02001580 trunk->trunk_nr);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001581
Philipp Maiere726d4f2017-11-01 10:41:34 +01001582 /* Do not accept invalid configuration values
1583 * valid is MGCP_KEEPALIVE_NEVER, MGCP_KEEPALIVE_ONCE and
1584 * values greater 0 */
Philipp Maier14b27a82020-06-02 20:15:30 +02001585 OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Philipp Maiere726d4f2017-11-01 10:41:34 +01001586
1587 /* The dummy packet functionality has been disabled, we will exit
1588 * immediately, no further timer is scheduled, which means we will no
1589 * longer send dummy packets even when we did before */
Philipp Maier14b27a82020-06-02 20:15:30 +02001590 if (trunk->keepalive_interval == MGCP_KEEPALIVE_NEVER)
Philipp Maiere726d4f2017-11-01 10:41:34 +01001591 return;
1592
1593 /* In cases where only one dummy packet is sent, we do not need
1594 * the timer since the functions that handle the CRCX and MDCX are
1595 * triggering the sending of the dummy packet. So we behave like in
1596 * the MGCP_KEEPALIVE_NEVER case */
Philipp Maier14b27a82020-06-02 20:15:30 +02001597 if (trunk->keepalive_interval == MGCP_KEEPALIVE_ONCE)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001598 return;
1599
Philipp Maier87bd9be2017-08-22 16:35:41 +02001600 /* Send walk over all endpoints and send out dummy packets through
1601 * every connection present on each endpoint */
Philipp Maier4131a652021-07-07 14:04:34 +02001602 for (i = 0; i < trunk->number_endpoints; ++i) {
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001603 struct mgcp_endpoint *endp = trunk->endpoints[i];
Philipp Maier87bd9be2017-08-22 16:35:41 +02001604 llist_for_each_entry(conn, &endp->conns, entry) {
Pau Espin Pedrolca280a12021-07-06 18:15:35 +02001605 if (conn->type == MGCP_CONN_TYPE_RTP &&
Pau Espin Pedrol2ca48822022-10-06 10:58:10 +02001606 conn->mode == MGCP_CONN_RECV_ONLY)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001607 send_dummy(endp, &conn->u.rtp);
1608 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001609 }
1610
Philipp Maiere726d4f2017-11-01 10:41:34 +01001611 /* Schedule the keepalive timer for the next round */
1612 LOGP(DLMGCP, LOGL_DEBUG, "rescheduling trunk %d keepalive timer\n",
Philipp Maier14b27a82020-06-02 20:15:30 +02001613 trunk->trunk_nr);
1614 osmo_timer_schedule(&trunk->keepalive_timer, trunk->keepalive_interval,
Philipp Maier87bd9be2017-08-22 16:35:41 +02001615 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001616}
1617
Philipp Maier14b27a82020-06-02 20:15:30 +02001618void mgcp_trunk_set_keepalive(struct mgcp_trunk *trunk, int interval)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001619{
Philipp Maier14b27a82020-06-02 20:15:30 +02001620 trunk->keepalive_interval = interval;
1621 osmo_timer_setup(&trunk->keepalive_timer, mgcp_keepalive_timer_cb, trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001622
1623 if (interval <= 0)
Philipp Maier14b27a82020-06-02 20:15:30 +02001624 osmo_timer_del(&trunk->keepalive_timer);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001625 else
Philipp Maier14b27a82020-06-02 20:15:30 +02001626 osmo_timer_schedule(&trunk->keepalive_timer,
1627 trunk->keepalive_interval, 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001628}
1629
Philipp Maier38533ba2021-07-29 17:38:34 +02001630/* Free config, this function is automatically called by talloc_free when the configuration is freed. */
1631static int config_free_talloc_destructor(struct mgcp_config *cfg)
1632{
1633 mgcp_ratectr_global_free(cfg);
1634 return 0;
1635}
1636
Philipp Maier87bd9be2017-08-22 16:35:41 +02001637/*! allocate configuration with default values.
1638 * (called once at startup by main function) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001639struct mgcp_config *mgcp_config_alloc(void)
1640{
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001641 /* FIXME: This is unrelated to the protocol, put this in some
1642 * appropiate place! */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001643 struct mgcp_config *cfg;
1644
1645 cfg = talloc_zero(NULL, struct mgcp_config);
1646 if (!cfg) {
1647 LOGP(DLMGCP, LOGL_FATAL, "Failed to allocate config.\n");
1648 return NULL;
1649 }
1650
Philipp Maier12943ea2018-01-17 15:40:25 +01001651 osmo_strlcpy(cfg->domain, "mgw", sizeof(cfg->domain));
1652
Eric55fdfc22021-08-13 00:14:18 +02001653 cfg->net_ports.lock = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001654 cfg->net_ports.range_start = RTP_PORT_DEFAULT_RANGE_START;
1655 cfg->net_ports.range_end = RTP_PORT_DEFAULT_RANGE_END;
1656 cfg->net_ports.last_port = cfg->net_ports.range_start;
1657
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001658 cfg->source_port = 2427;
Eric2764bdb2021-08-23 22:11:47 +02001659 osmo_strlcpy(cfg->source_addr, "0.0.0.0", sizeof(cfg->source_addr));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001660
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001661 cfg->rtp_processing_cb = &mgcp_rtp_processing_default;
1662 cfg->setup_rtp_processing_cb = &mgcp_setup_rtp_processing_default;
1663
1664 cfg->get_net_downlink_format_cb = &mgcp_get_net_downlink_format_default;
1665
Philipp Maierd19de2e2020-06-03 13:55:33 +02001666 INIT_LLIST_HEAD(&cfg->trunks);
1667
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001668 /* Allocate virtual trunk */
Philipp Maierd19de2e2020-06-03 13:55:33 +02001669 if (!mgcp_trunk_alloc(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID)) {
Harald Welte3ac604e2019-05-08 14:07:41 +02001670 talloc_free(cfg);
1671 return NULL;
1672 }
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001673
Philipp Maiera065e632021-07-09 13:22:42 +02001674 mgcp_ratectr_global_alloc(cfg);
Philipp Maier38533ba2021-07-29 17:38:34 +02001675 talloc_set_destructor(cfg, config_free_talloc_destructor);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001676
1677 return cfg;
1678}
1679
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001680static int send_agent(struct mgcp_config *cfg, const char *buf, int len)
1681{
1682 return write(cfg->gw_fd.bfd.fd, buf, len);
1683}
1684
Philipp Maier87bd9be2017-08-22 16:35:41 +02001685/*! Reset all endpoints by sending RSIP message to self.
1686 * (called by VTY)
1687 * \param[in] endp trunk endpoint
1688 * \param[in] endpoint number
1689 * \returns 0 on success, -1 on error */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001690int mgcp_send_reset_all(struct mgcp_config *cfg)
1691{
Philipp Maier12943ea2018-01-17 15:40:25 +01001692 char buf[MGCP_ENDPOINT_MAXLEN + 128];
1693 int len;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001694 int rc;
1695
Philipp Maier12943ea2018-01-17 15:40:25 +01001696 len = snprintf(buf, sizeof(buf),
1697 "RSIP 1 *@%s MGCP 1.0\r\n", cfg->domain);
1698 if (len < 0)
1699 return -1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001700
Philipp Maier12943ea2018-01-17 15:40:25 +01001701 rc = send_agent(cfg, buf, len);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001702 if (rc <= 0)
1703 return -1;
1704
1705 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001706}
1707
Philipp Maier87bd9be2017-08-22 16:35:41 +02001708/*! Reset a single endpoint by sending RSIP message to self.
1709 * (called by VTY)
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001710 * \param[in] endp to reset
Philipp Maier87bd9be2017-08-22 16:35:41 +02001711 * \returns 0 on success, -1 on error */
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001712int mgcp_send_reset_ep(struct mgcp_endpoint *endp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001713{
Philipp Maier12943ea2018-01-17 15:40:25 +01001714 char buf[MGCP_ENDPOINT_MAXLEN + 128];
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001715 int len;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001716 int rc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001717
1718 len = snprintf(buf, sizeof(buf),
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001719 "RSIP 39 %s MGCP 1.0\r\n", endp->name);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001720 if (len < 0)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001721 return -1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001722
Ericfbf78d12021-08-23 22:31:39 +02001723 rc = send_agent(endp->trunk->cfg, buf, len);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001724 if (rc <= 0)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001725 return -1;
1726
Philipp Maier87bd9be2017-08-22 16:35:41 +02001727 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001728}