blob: ac5c6824193a7b6258b3d893dbc069e8a2e1970c [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
Pau Espin Pedrolaf0f58f2023-06-14 12:21:26 +020089 /* Set to true when the request is targeted at the "null" endpoint */
90 bool null_endp;
91
Philipp Maier8dc35972021-07-14 11:20:16 +020092 /* contains cause code in case of problems during endp/trunk resolution */
93 int mgcp_cause;
94};
95
Philipp Maier33d97f72021-07-14 14:53:45 +020096/* Request handler specification, here we specify an array with function
97 * pointers to the various MGCP requests implemented below */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020098struct mgcp_request {
Philipp Maier33d97f72021-07-14 14:53:45 +020099 /* request name (e.g. "MDCX") */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200100 char *name;
Philipp Maier33d97f72021-07-14 14:53:45 +0200101
102 /* function pointer to the request handler */
Philipp Maier8dc35972021-07-14 11:20:16 +0200103 struct msgb *(*handle_request)(struct mgcp_request_data *data);
104
Philipp Maier33d97f72021-07-14 14:53:45 +0200105 /* a human readable name that describes the request */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200106 char *debug_name;
107};
108
Philipp Maier8dc35972021-07-14 11:20:16 +0200109static struct msgb *handle_audit_endpoint(struct mgcp_request_data *data);
110static struct msgb *handle_create_con(struct mgcp_request_data *data);
111static struct msgb *handle_delete_con(struct mgcp_request_data *data);
112static struct msgb *handle_modify_con(struct mgcp_request_data *data);
113static struct msgb *handle_rsip(struct mgcp_request_data *data);
114static struct msgb *handle_noti_req(struct mgcp_request_data *data);
Philipp Maier33d97f72021-07-14 14:53:45 +0200115static const struct mgcp_request mgcp_requests[] = {
Ericee6958c2021-09-14 18:36:53 +0200116 { .name = "AUEP", .handle_request = handle_audit_endpoint, .debug_name = "AuditEndpoint" },
117 {
118 .name = "CRCX",
119 .handle_request = handle_create_con,
120 .debug_name = "CreateConnection",
121 },
122 {
123 .name = "DLCX",
124 .handle_request = handle_delete_con,
125 .debug_name = "DeleteConnection",
126 },
127 {
128 .name = "MDCX",
129 .handle_request = handle_modify_con,
130 .debug_name = "ModifiyConnection",
131 },
132 {
133 .name = "RQNT",
134 .handle_request = handle_noti_req,
135 .debug_name = "NotificationRequest",
136 },
Philipp Maier33d97f72021-07-14 14:53:45 +0200137
138 /* SPEC extension */
Ericee6958c2021-09-14 18:36:53 +0200139 {
140 .name = "RSIP",
141 .handle_request = handle_rsip,
142 .debug_name = "ReSetInProgress",
143 },
Philipp Maier33d97f72021-07-14 14:53:45 +0200144};
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200145
Philipp Maier87bd9be2017-08-22 16:35:41 +0200146/* Initalize transcoder */
147static int setup_rtp_processing(struct mgcp_endpoint *endp,
148 struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200149{
Ericfbf78d12021-08-23 22:31:39 +0200150 struct mgcp_config *cfg = endp->trunk->cfg;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200151 struct mgcp_conn_rtp *conn_src = NULL;
152 struct mgcp_conn_rtp *conn_dst = conn;
153 struct mgcp_conn *_conn;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200154
Pau Espin Pedrolbb3ccde2021-12-23 19:49:26 +0100155 switch (conn->type) {
156 case MGCP_RTP_DEFAULT:
Pau Espin Pedrol9d939b62022-10-03 16:59:20 +0200157 case MGCP_RTP_OSMUX:
Pau Espin Pedrolbb3ccde2021-12-23 19:49:26 +0100158 case MGCP_RTP_IUUP:
159 break;
160 default:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200161 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
162 "RTP-setup: Endpoint is not configured as RTP default, stopping here!\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200163 return 0;
164 }
165
Philipp Maier87bd9be2017-08-22 16:35:41 +0200166 if (conn->conn->mode == MGCP_CONN_LOOPBACK) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200167 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
168 "RTP-setup: Endpoint is in loopback mode, stopping here!\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +0200169 return 0;
170 }
171
172 /* Find the "sister" connection */
173 llist_for_each_entry(_conn, &endp->conns, entry) {
174 if (_conn->id != conn->conn->id) {
175 conn_src = &_conn->u.rtp;
176 break;
177 }
178 }
179
Philipp Maieracc10352018-07-19 18:07:57 +0200180 return cfg->setup_rtp_processing_cb(endp, conn_dst, conn_src);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200181}
182
Philipp Maier87bd9be2017-08-22 16:35:41 +0200183/* Helper function to allocate some memory for responses and retransmissions */
Ericfbcf4a62021-09-09 18:02:31 +0200184static struct msgb *mgcp_msgb_alloc(void *ctx)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200185{
186 struct msgb *msg;
Ericfbcf4a62021-09-09 18:02:31 +0200187 msg = msgb_alloc_headroom_c(ctx, 4096, 128, "MGCP msg");
188
189 if (!msg) {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200190 LOGP(DLMGCP, LOGL_ERROR, "Failed to msgb for MGCP data.\n");
Ericfbcf4a62021-09-09 18:02:31 +0200191 return NULL;
192 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200193
194 return msg;
195}
196
Philipp Maier87bd9be2017-08-22 16:35:41 +0200197/* Helper function for do_retransmission() and create_resp() */
Eric958f5e72021-08-03 23:00:17 +0200198static struct msgb *create_retransmission_response(const struct mgcp_endpoint *endp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200199{
Ericfbcf4a62021-09-09 18:02:31 +0200200 struct msgb *msg = mgcp_msgb_alloc(endp->trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200201 if (!msg)
202 return NULL;
203
204 msg->l2h = msgb_put(msg, strlen(endp->last_response));
205 memcpy(msg->l2h, endp->last_response, msgb_l2len(msg));
Philipp Maier87bd9be2017-08-22 16:35:41 +0200206 mgcp_disp_msg(msg->l2h, msgb_l2len(msg), "Retransmitted response");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200207 return msg;
208}
209
Ericfbcf4a62021-09-09 18:02:31 +0200210static struct msgb *create_resp(void *msgctx, struct mgcp_endpoint *endp, int code, const char *txt, const char *msg,
211 const char *trans, const char *param, const char *sdp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200212{
213 int len;
214 struct msgb *res;
215
Ericfbcf4a62021-09-09 18:02:31 +0200216 OSMO_ASSERT(msgctx != 0);
217 res = mgcp_msgb_alloc(msgctx);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200218 if (!res)
219 return NULL;
220
Philipp Maier87bd9be2017-08-22 16:35:41 +0200221 len = snprintf((char *)res->data, 2048, "%d %s%s%s\r\n%s",
222 code, trans, txt, param ? param : "", sdp ? sdp : "");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200223 if (len < 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200224 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "Failed to sprintf MGCP response.\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200225 msgb_free(res);
226 return NULL;
227 }
228
229 res->l2h = msgb_put(res, len);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200230 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "Generated response: code=%d\n", code);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200231 mgcp_disp_msg(res->l2h, msgb_l2len(res), "Generated response");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200232
233 /*
234 * Remember the last transmission per endpoint.
235 */
236 if (endp) {
Philipp Maier14b27a82020-06-02 20:15:30 +0200237 struct mgcp_trunk *trunk = endp->trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200238 talloc_free(endp->last_response);
239 talloc_free(endp->last_trans);
Philipp Maier14b27a82020-06-02 20:15:30 +0200240 endp->last_trans = talloc_strdup(trunk->endpoints, trans);
241 endp->last_response = talloc_strndup(trunk->endpoints,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200242 (const char *)res->l2h,
243 msgb_l2len(res));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200244 }
245
246 return res;
247}
248
Ericfbcf4a62021-09-09 18:02:31 +0200249static struct msgb *create_ok_resp_with_param(void *msgctx, struct mgcp_endpoint *endp, int code, const char *msg,
250 const char *trans, const char *param)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200251{
Ericfbcf4a62021-09-09 18:02:31 +0200252 return create_resp(msgctx, endp, code, " OK", msg, trans, param, NULL);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200253}
254
Ericfbcf4a62021-09-09 18:02:31 +0200255static struct msgb *create_ok_response(void *msgctx, struct mgcp_endpoint *endp, int code, const char *msg,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200256 const char *trans)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200257{
Ericfbcf4a62021-09-09 18:02:31 +0200258 return create_ok_resp_with_param(msgctx, endp, code, msg, trans, NULL);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200259}
260
Ericfbcf4a62021-09-09 18:02:31 +0200261static struct msgb *create_err_response(void *msgctx, struct mgcp_endpoint *endp, int code, const char *msg,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200262 const char *trans)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200263{
Ericfbcf4a62021-09-09 18:02:31 +0200264 return create_resp(msgctx, endp, code, " FAIL", msg, trans, NULL, NULL);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200265}
266
Philipp Maier87bd9be2017-08-22 16:35:41 +0200267/* Format MGCP response string (with SDP attached) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200268static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200269 struct mgcp_conn_rtp *conn,
270 const char *msg,
Philipp Maier55295f72018-01-15 14:00:28 +0100271 const char *trans_id,
Philipp Maier41d59202021-07-20 15:49:00 +0200272 bool add_epname,
273 bool add_conn_id)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200274{
Pau Espin Pedrol71d42e72020-09-03 14:20:07 +0200275 /* cfg->local_ip allows overwritting the announced IP address with
276 * regards to the one we actually bind to. Useful in behind-NAT
277 * scenarios.
278 * TODO: we may want to define another local_ip_osmux var to
279 * us for OSMUX connections. Perhaps adding a new internal API to get it
280 * based on conn type.
281 */
Ericfbf78d12021-08-23 22:31:39 +0200282 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 +0200283 struct msgb *sdp;
284 int rc;
285 struct msgb *result;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200286
Ericfbcf4a62021-09-09 18:02:31 +0200287 sdp = msgb_alloc_headroom_c(endp->trunk, 4096, 128, "sdp record");
Philipp Maier8970c492017-10-11 13:33:42 +0200288 if (!sdp)
289 return NULL;
290
Philipp Maier41d59202021-07-20 15:49:00 +0200291 /* Attach optional endpoint name */
292 if (add_epname) {
293 rc = msgb_printf(sdp, "Z: %s\r\n", endp->name);
294 if (rc < 0)
295 goto error;
296 }
297
298 /* Attach optional connection id */
299 if (add_conn_id) {
300 rc = msgb_printf(sdp, "I: %s\r\n", conn->conn->id);
Philipp Maier55295f72018-01-15 14:00:28 +0100301 if (rc < 0)
302 goto error;
303 }
304
Philipp Maier3cbfb8a2018-01-22 11:39:59 +0100305 /* Attach optional OSMUX parameters */
Pau Espin Pedrolc63f15a2019-05-10 16:52:08 +0200306 if (mgcp_conn_rtp_is_osmux(conn)) {
Pau Espin Pedrol21779192022-09-23 16:46:33 +0200307 rc = msgb_printf(sdp, MGCP_X_OSMO_OSMUX_HEADER " %u\r\n", conn->osmux.local_cid);
Philipp Maier3cbfb8a2018-01-22 11:39:59 +0100308 if (rc < 0)
309 goto error;
310 }
311
312 /* Attach line break to separate the parameters from the SDP block */
Philipp Maierc3cfae22018-01-22 12:03:03 +0100313 rc = msgb_printf(sdp, "\r\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200314
Philipp Maier8970c492017-10-11 13:33:42 +0200315 rc = mgcp_write_response_sdp(endp, conn, sdp, addr);
316 if (rc < 0)
317 goto error;
Ericfbcf4a62021-09-09 18:02:31 +0200318 result = create_resp(endp->trunk, endp, 200, " OK", msg, trans_id, NULL, (char *)sdp->data);
Philipp Maier8970c492017-10-11 13:33:42 +0200319 msgb_free(sdp);
320 return result;
321error:
322 msgb_free(sdp);
323 return NULL;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200324}
325
Philipp Maier87bd9be2017-08-22 16:35:41 +0200326/* Send out dummy packet to keep the connection open, if the connection is an
327 * osmux connection, send the dummy packet via OSMUX */
328static void send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200329{
Pau Espin Pedrol2ca48822022-10-06 10:58:10 +0200330 /* Avoid sending dummy packet if the remote address was not yet
331 * configured through CRCX/MDCX: */
332 if (!mgcp_rtp_end_remote_addr_available(&conn->end))
333 return;
334
Pau Espin Pedrolabb9d472022-10-04 12:09:14 +0200335 if (mgcp_conn_rtp_is_osmux(conn))
Pau Espin Pedrol887c93e2022-10-06 12:36:03 +0200336 osmux_send_dummy(conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200337 else
Philipp Maier87bd9be2017-08-22 16:35:41 +0200338 mgcp_send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200339}
340
Philipp Maier87bd9be2017-08-22 16:35:41 +0200341/* handle incoming messages:
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200342 * - this can be a command (four letters, space, transaction id)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200343 * - or a response (three numbers, space, transaction id) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200344struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
345{
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200346 struct rate_ctr_group *rate_ctrs = cfg->ratectr.mgcp_general_ctr_group;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200347 struct mgcp_parse_data pdata;
Philipp Maier8dc35972021-07-14 11:20:16 +0200348 struct mgcp_request_data rq;
Harald Weltee35eeae2017-12-28 13:47:37 +0100349 int rc, i, code, handled = 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200350 struct msgb *resp = NULL;
351 char *data;
352
Philipp Maier39889e42021-08-04 17:42:57 +0200353 debug_last_endpoint_name[0] = '\0';
354
Alexander Chemeris63866002020-05-05 17:18:40 +0300355 /* Count all messages, even incorect ones */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200356 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_TOTAL));
Alexander Chemeris63866002020-05-05 17:18:40 +0300357
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200358 if (msgb_l2len(msg) < 4) {
359 LOGP(DLMGCP, LOGL_ERROR, "msg too short: %d\n", msg->len);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200360 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200361 return NULL;
362 }
363
Alexander Chemeris63866002020-05-05 17:18:40 +0300364 if (mgcp_msg_terminate_nul(msg)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200365 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200366 return NULL;
Alexander Chemeris63866002020-05-05 17:18:40 +0300367 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200368
Philipp Maier87bd9be2017-08-22 16:35:41 +0200369 mgcp_disp_msg(msg->l2h, msgb_l2len(msg), "Received message");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200370
Philipp Maier87bd9be2017-08-22 16:35:41 +0200371 /* attempt to treat it as a response */
372 if (sscanf((const char *)&msg->l2h[0], "%3d %*s", &code) == 1) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200373 LOGP(DLMGCP, LOGL_DEBUG, "Response: Code: %d\n", code);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200374 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200375 return NULL;
376 }
377
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200378
Philipp Maier8dc35972021-07-14 11:20:16 +0200379 /* Parse message, extract endpoint name and transaction identifier and request name etc. */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200380 memset(&pdata, 0, sizeof(pdata));
Philipp Maier8dc35972021-07-14 11:20:16 +0200381 memset(&rq, 0, sizeof(rq));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200382 pdata.cfg = cfg;
Philipp Maier8dc35972021-07-14 11:20:16 +0200383 memcpy(rq.name, (const char *)&msg->l2h[0], sizeof(rq.name)-1);
384 msg->l3h = &msg->l2h[4];
Philipp Maier87bd9be2017-08-22 16:35:41 +0200385 data = mgcp_strline((char *)msg->l3h, &pdata.save);
Harald Weltee35eeae2017-12-28 13:47:37 +0100386 rc = mgcp_parse_header(&pdata, data);
Harald Weltee35eeae2017-12-28 13:47:37 +0100387 if (rc < 0) {
Philipp Maier8dc35972021-07-14 11:20:16 +0200388 LOGP(DLMGCP, LOGL_ERROR, "%s: failed to parse MCGP message\n", rq.name);
389 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
Ericfbcf4a62021-09-09 18:02:31 +0200390 return create_err_response(cfg, NULL, -rc, rq.name, "000000");
Harald Welteabbb6b92017-12-28 13:13:50 +0100391 }
392
Philipp Maier8dc35972021-07-14 11:20:16 +0200393 /* Locate endpoint and trunk, if no endpoint can be located try at least to identify the trunk. */
394 rq.pdata = &pdata;
395 rq.wildcarded = mgcp_endp_is_wildcarded(pdata.epname);
Pau Espin Pedrolaf0f58f2023-06-14 12:21:26 +0200396 if (!rq.wildcarded)
397 rq.null_endp = mgcp_endp_is_null(pdata.epname);
398 if (!rq.null_endp)
399 rq.endp = mgcp_endp_by_name(&rc, pdata.epname, pdata.cfg);
Philipp Maier8dc35972021-07-14 11:20:16 +0200400 rq.mgcp_cause = rc;
401 if (!rq.endp) {
402 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_NO_ENDPOINT));
403 if (rq.wildcarded) {
404 /* If we are unable to find the endpoint we still may be able to identify the trunk. Some
405 * request handlers will still be able to perform a useful action if the request refers to
406 * the whole trunk (wildcarded request). */
407 LOGP(DLMGCP, LOGL_NOTICE,
408 "%s: cannot find endpoint \"%s\", cause=%d -- trying to identify trunk...\n", rq.name,
409 pdata.epname, -rq.mgcp_cause);
410 rq.trunk = mgcp_trunk_by_name(pdata.cfg, pdata.epname);
411 if (!rq.trunk) {
412 LOGP(DLMGCP, LOGL_ERROR, "%s: failed to identify trunk for endpoint \"%s\" -- abort\n",
413 rq.name, pdata.epname);
Ericfbcf4a62021-09-09 18:02:31 +0200414 return create_err_response(cfg, NULL, -rq.mgcp_cause, rq.name, pdata.trans);
Philipp Maier8dc35972021-07-14 11:20:16 +0200415 }
Pau Espin Pedrolaf0f58f2023-06-14 12:21:26 +0200416 } else if (!rq.null_endp) {
Philipp Maier8dc35972021-07-14 11:20:16 +0200417 /* If the endpoint name suggests that the request refers to a specific endpoint, then the
418 * request cannot be handled and we must stop early. */
419 LOGP(DLMGCP, LOGL_NOTICE,
420 "%s: cannot find endpoint \"%s\", cause=%d -- abort\n", rq.name,
421 pdata.epname, -rq.mgcp_cause);
Ericfbcf4a62021-09-09 18:02:31 +0200422 return create_err_response(cfg, NULL, -rq.mgcp_cause, rq.name, pdata.trans);
Pau Espin Pedrolaf0f58f2023-06-14 12:21:26 +0200423 } /* else: Handle special "null" endpoint below (with rq.endp=NULL, rq.trunk=NULL) */
Philipp Maier8dc35972021-07-14 11:20:16 +0200424 } else {
Philipp Maier39889e42021-08-04 17:42:57 +0200425 osmo_strlcpy(debug_last_endpoint_name, rq.endp->name, sizeof(debug_last_endpoint_name));
Philipp Maier8dc35972021-07-14 11:20:16 +0200426 rq.trunk = rq.endp->trunk;
427 rq.mgcp_cause = 0;
428
429 /* Check if we have to retransmit a response from a previous transaction */
430 if (pdata.trans && rq.endp->last_trans && strcmp(rq.endp->last_trans, pdata.trans) == 0) {
431 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_RETRANSMITTED));
Eric958f5e72021-08-03 23:00:17 +0200432 return create_retransmission_response(rq.endp);
Philipp Maier8dc35972021-07-14 11:20:16 +0200433 }
434 }
435
436 /* Find an appropriate handler for the current request and execute it */
437 for (i = 0; i < ARRAY_SIZE(mgcp_requests); i++) {
438 if (strcmp(mgcp_requests[i].name, rq.name) == 0) {
Philipp Maier8dc35972021-07-14 11:20:16 +0200439 /* Execute request handler */
440 if (rq.endp)
441 LOGP(DLMGCP, LOGL_INFO,
442 "%s: executing request handler \"%s\" for endpoint resource \"%s\"\n", rq.name,
443 mgcp_requests[i].debug_name, rq.endp->name);
444 else
445 LOGP(DLMGCP, LOGL_INFO,
446 "%s: executing request handler \"%s\" for trunk resource of endpoint \"%s\"\n",
447 rq.name, mgcp_requests[i].debug_name, pdata.epname);
448 resp = mgcp_requests[i].handle_request(&rq);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200449 handled = 1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200450 break;
451 }
452 }
453
Philipp Maier8dc35972021-07-14 11:20:16 +0200454 /* Check if the MGCP request was handled and increment rate counters accordingly. */
Alexander Chemeris63866002020-05-05 17:18:40 +0300455 if (handled) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200456 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_HANDLED));
Alexander Chemeris63866002020-05-05 17:18:40 +0300457 } else {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200458 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_UNHANDLED));
Philipp Maier8dc35972021-07-14 11:20:16 +0200459 LOGP(DLMGCP, LOGL_ERROR, "MSG with type: '%.4s' not handled\n", &msg->l2h[0]);
Alexander Chemeris63866002020-05-05 17:18:40 +0300460 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200461
462 return resp;
463}
464
Philipp Maier87bd9be2017-08-22 16:35:41 +0200465/* AUEP command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +0200466static struct msgb *handle_audit_endpoint(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200467{
Philipp Maier8dc35972021-07-14 11:20:16 +0200468 LOGPENDP(rq->endp, DLMGCP, LOGL_NOTICE, "AUEP: auditing endpoint ...\n");
Pau Espin Pedrolaf0f58f2023-06-14 12:21:26 +0200469
470 /* Auditing "null" endpoint is allowed for keepalive purposes. There's no rq->endp nor rq->trunk in this case. */
471 if (rq->null_endp)
472 return create_ok_response(rq->pdata->cfg, NULL, 200, "AUEP", rq->pdata->trans);
473
Ericee6958c2021-09-14 18:36:53 +0200474 if (!rq->endp || !mgcp_endp_avail(rq->endp)) {
475 LOGPENDP(rq->endp, DLMGCP, LOGL_ERROR, "AUEP: selected endpoint not available!\n");
476 return create_err_response(rq->trunk, NULL, 501, "AUEP", rq->pdata->trans);
477 }
478
Ericfbcf4a62021-09-09 18:02:31 +0200479 return create_ok_response(rq->trunk, rq->endp, 200, "AUEP", rq->pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200480}
481
Harald Welte1d1b98f2017-12-25 10:03:40 +0100482/* Try to find a free port by attempting to bind on it. Also handle the
Philipp Maier87bd9be2017-08-22 16:35:41 +0200483 * counter that points on the next free port. Since we have a pointer
Philipp Maierb38fb892018-05-22 13:52:21 +0200484 * to the next free port, binding should in work on the first attempt in
Pau Espin Pedrolfc806732019-04-23 00:18:43 +0200485 * general. In case of failure the next port is tried until the whole port
486 * range is tried once. */
Philipp Maier87bd9be2017-08-22 16:35:41 +0200487static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200488{
489 int i;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200490 struct mgcp_port_range *range;
Philipp Maierb38fb892018-05-22 13:52:21 +0200491 unsigned int tries;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200492
Philipp Maier87bd9be2017-08-22 16:35:41 +0200493 OSMO_ASSERT(conn);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200494
Ericfbf78d12021-08-23 22:31:39 +0200495 range = &endp->trunk->cfg->net_ports;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200496
Eric55fdfc22021-08-13 00:14:18 +0200497 pthread_mutex_lock(&range->lock);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200498 /* attempt to find a port */
Philipp Maierb38fb892018-05-22 13:52:21 +0200499 tries = (range->range_end - range->range_start) / 2;
500 for (i = 0; i < tries; ++i) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200501 int rc;
502
503 if (range->last_port >= range->range_end)
504 range->last_port = range->range_start;
505
Philipp Maier87bd9be2017-08-22 16:35:41 +0200506 rc = mgcp_bind_net_rtp_port(endp, range->last_port, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200507
508 range->last_port += 2;
509 if (rc == 0) {
Eric55fdfc22021-08-13 00:14:18 +0200510 pthread_mutex_unlock(&range->lock);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200511 return 0;
512 }
513
514 }
Eric55fdfc22021-08-13 00:14:18 +0200515 pthread_mutex_unlock(&range->lock);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200516 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
517 "Allocating a RTP/RTCP port failed %u times.\n",
518 tries);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200519 return -1;
520}
521
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200522/*! Helper function for check_local_cx_options() to get a pointer of the next
523 * lco option identifier
524 * \param[in] lco string
525 * \returns pointer to the beginning of the LCO identifier, NULL on failure */
526char *get_lco_identifier(const char *options)
527{
528 char *ptr;
529 unsigned int count = 0;
530
531 /* Jump to the end of the lco identifier */
532 ptr = strstr(options, ":");
533 if (!ptr)
534 return NULL;
535
536 /* Walk backwards until the pointer points to the beginning of the
537 * lco identifier. We know that we stand at the beginning when we
538 * are either at the beginning of the memory or see a space or
539 * comma. (this is tolerant, it will accept a:10, b:11 as well as
540 * a:10,b:11) */
541 while (1) {
542 /* Endless loop protection */
543 if (count > 10000)
544 return NULL;
545 else if (ptr < options || *ptr == ' ' || *ptr == ',') {
546 ptr++;
547 break;
548 }
549 ptr--;
550 count++;
551 }
552
553 /* Check if we got any result */
554 if (*ptr == ':')
555 return NULL;
556
557 return ptr;
558}
559
560/*! Check the LCO option. This function checks for multiple appearence of LCO
561 * options, which is illegal
562 * \param[in] ctx talloc context
563 * \param[in] lco string
564 * \returns 0 on success, -1 on failure */
565int check_local_cx_options(void *ctx, const char *options)
566{
567 int i;
568 char *options_copy;
569 char *lco_identifier;
570 char *lco_identifier_end;
571 char *next_lco_identifier;
572
573 char **lco_seen;
574 unsigned int lco_seen_n = 0;
575
576 if (!options)
577 return -1;
578
579 lco_seen =
580 (char **)talloc_zero_size(ctx, strlen(options) * sizeof(char *));
581 options_copy = talloc_strdup(ctx, options);
582 lco_identifier = options_copy;
583
584 do {
585 /* Move the lco_identifier pointer to the beginning of the
586 * current lco option identifier */
587 lco_identifier = get_lco_identifier(lco_identifier);
588 if (!lco_identifier)
589 goto error;
590
591 /* Look ahead to the next LCO option early, since we
592 * will parse destructively */
593 next_lco_identifier = strstr(lco_identifier + 1, ",");
594
595 /* Pinch off the end of the lco field identifier name
596 * and see if we still got something, also check if
597 * there is some value after the colon. */
598 lco_identifier_end = strstr(lco_identifier, ":");
599 if (!lco_identifier_end)
600 goto error;
601 if (*(lco_identifier_end + 1) == ' '
602 || *(lco_identifier_end + 1) == ','
603 || *(lco_identifier_end + 1) == '\0')
604 goto error;
605 *lco_identifier_end = '\0';
606 if (strlen(lco_identifier) == 0)
607 goto error;
608
609 /* Check if we have already seen the current field identifier
610 * before. If yes, we must bail, an LCO must only appear once
611 * in the LCO string */
612 for (i = 0; i < lco_seen_n; i++) {
Pau Espin Pedrol7eb6f2c2019-06-26 13:00:52 +0200613 if (strcasecmp(lco_seen[i], lco_identifier) == 0)
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200614 goto error;
615 }
616 lco_seen[lco_seen_n] = lco_identifier;
617 lco_seen_n++;
618
619 /* The first identifier must always be found at the beginnning
620 * of the LCO string */
621 if (lco_seen[0] != options_copy)
622 goto error;
623
624 /* Go to the next lco option */
625 lco_identifier = next_lco_identifier;
626 } while (lco_identifier);
627
628 talloc_free(lco_seen);
629 talloc_free(options_copy);
630 return 0;
631error:
632 talloc_free(lco_seen);
633 talloc_free(options_copy);
634 return -1;
635}
636
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200637/* Set the LCO from a string (see RFC 3435).
Harald Welte1d1b98f2017-12-25 10:03:40 +0100638 * The string is stored in the 'string' field. A NULL string is handled exactly
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200639 * like an empty string, the 'string' field is never NULL after this function
640 * has been called. */
Philipp Maiera390d0b2018-01-31 17:30:19 +0100641static int set_local_cx_options(void *ctx, struct mgcp_lco *lco,
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200642 const char *options)
643{
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200644 char *lco_id;
Philipp Maier8dbc9ed2018-10-26 14:50:25 +0200645 char codec[17];
Pau Espin Pedrol19539862020-09-21 11:43:41 +0200646 char nt[17];
Pau Espin Pedrol83fd8a52019-06-26 12:55:26 +0200647 int len;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200648
Philipp Maier604410c2018-06-06 10:02:16 +0200649 if (!options)
650 return 0;
651 if (strlen(options) == 0)
652 return 0;
653
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200654 /* Make sure the encoding of the LCO is consistant before we proceed */
655 if (check_local_cx_options(ctx, options) != 0) {
656 LOGP(DLMGCP, LOGL_ERROR,
657 "local CX options: Internal inconsistency in Local Connection Options!\n");
658 return 524;
659 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200660
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200661 talloc_free(lco->string);
662 lco->string = talloc_strdup(ctx, options);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200663
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200664 lco_id = lco->string;
665 while ((lco_id = get_lco_identifier(lco_id))) {
666 switch (tolower(lco_id[0])) {
667 case 'p':
668 if (sscanf(lco_id + 1, ":%d-%d",
669 &lco->pkt_period_min, &lco->pkt_period_max) == 1)
670 lco->pkt_period_max = lco->pkt_period_min;
671 break;
672 case 'a':
Pau Espin Pedrol1dc2dce2020-09-21 11:25:15 +0200673 /* FIXME: LCO also supports the negotiation of more than one codec.
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200674 * (e.g. a:PCMU;G726-32) But this implementation only supports a single
Neels Hofmeyre9989b92023-06-30 05:12:54 +0200675 * codec only. Ignoring all but the first codec. */
676 if (sscanf(lco_id + 1, ":%16[^,;]", codec) == 1) {
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200677 talloc_free(lco->codec);
Pau Espin Pedrol83fd8a52019-06-26 12:55:26 +0200678 /* MGCP header is case insensive, and we'll need
679 codec in uppercase when using it later: */
680 len = strlen(codec);
681 lco->codec = talloc_size(ctx, len + 1);
682 osmo_str_toupper_buf(lco->codec, len + 1, codec);
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200683 }
684 break;
Pau Espin Pedrol19539862020-09-21 11:43:41 +0200685 case 'n':
686 if (lco_id[1] == 't' && sscanf(lco_id + 2, ":%16[^,]", nt) == 1)
687 break;
688 /* else: fall throught to print notice log */
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200689 default:
690 LOGP(DLMGCP, LOGL_NOTICE,
691 "LCO: unhandled option: '%c'/%d in \"%s\"\n",
692 *lco_id, *lco_id, lco->string);
693 break;
694 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200695
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200696 lco_id = strchr(lco_id, ',');
697 if (!lco_id)
698 break;
Philipp Maier604410c2018-06-06 10:02:16 +0200699 }
Philipp Maier87bd9be2017-08-22 16:35:41 +0200700
701 LOGP(DLMGCP, LOGL_DEBUG,
702 "local CX options: lco->pkt_period_max: %i, lco->codec: %s\n",
703 lco->pkt_period_max, lco->codec);
Philipp Maiera390d0b2018-01-31 17:30:19 +0100704
705 /* Check if the packetization fits the 20ms raster */
706 if (lco->pkt_period_min % 20 && lco->pkt_period_max % 20) {
707 LOGP(DLMGCP, LOGL_ERROR,
708 "local CX options: packetization interval is not a multiple of 20ms!\n");
709 return 535;
710 }
711
712 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200713}
714
715void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
716 struct mgcp_rtp_end *rtp)
717{
Philipp Maier14b27a82020-06-02 20:15:30 +0200718 struct mgcp_trunk *trunk = endp->trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200719
Philipp Maier14b27a82020-06-02 20:15:30 +0200720 int patch_ssrc = expect_ssrc_change && trunk->force_constant_ssrc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200721
Philipp Maier14b27a82020-06-02 20:15:30 +0200722 rtp->force_aligned_timing = trunk->force_aligned_timing;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200723 rtp->force_constant_ssrc = patch_ssrc ? 1 : 0;
Philipp Maier14b27a82020-06-02 20:15:30 +0200724 rtp->rfc5993_hr_convert = trunk->rfc5993_hr_convert;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200725
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200726 LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
727 "Configuring RTP endpoint: local port %d%s%s\n",
Pau Espin Pedrol5ffd1272022-10-04 13:45:48 +0200728 osmo_sockaddr_port(&rtp->addr.u.sa),
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200729 rtp->force_aligned_timing ? ", force constant timing" : "",
730 rtp->force_constant_ssrc ? ", force constant ssrc" : "");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200731}
732
Pau Espin Pedrol8358c4b2021-07-07 12:41:38 +0200733uint32_t mgcp_rtp_packet_duration(const struct mgcp_endpoint *endp,
734 const struct mgcp_rtp_end *rtp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200735{
736 int f = 0;
737
738 /* Get the number of frames per channel and packet */
739 if (rtp->frames_per_packet)
740 f = rtp->frames_per_packet;
Philipp Maierbc0346e2018-06-07 09:52:16 +0200741 else if (rtp->packet_duration_ms && rtp->codec->frame_duration_num) {
742 int den = 1000 * rtp->codec->frame_duration_num;
743 f = (rtp->packet_duration_ms * rtp->codec->frame_duration_den +
Philipp Maier87bd9be2017-08-22 16:35:41 +0200744 den / 2)
745 / den;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200746 }
747
Philipp Maierbc0346e2018-06-07 09:52:16 +0200748 return rtp->codec->rate * f * rtp->codec->frame_duration_num /
749 rtp->codec->frame_duration_den;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200750}
751
Pau Espin Pedrol9fb8ddf2019-05-08 15:35:36 +0200752/*! Initializes osmux socket if not yet initialized. Parses Osmux CID from MGCP line.
753 * \param[in] endp Endpoint willing to initialize osmux
754 * \param[in] line Line X-Osmux from MGCP header msg to parse
755 * \returns OSMUX CID, -1 for wildcard, -2 on parse error, -3 on osmux initalize error
756 */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200757static int mgcp_osmux_setup(struct mgcp_endpoint *endp, const char *line)
758{
Pau Espin Pedrol36413c02022-10-12 17:58:17 +0200759 if (!endp->trunk->cfg->osmux.initialized) {
Pau Espin Pedrolc1ad7fd2022-10-06 10:32:07 +0200760 if (osmux_init(endp->trunk) < 0) {
Pau Espin Pedrol8e0d1012022-10-06 10:29:11 +0200761 LOGPENDP(endp, DOSMUX, LOGL_ERROR, "Cannot init OSMUX\n");
Pau Espin Pedrol9fb8ddf2019-05-08 15:35:36 +0200762 return -3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200763 }
Pau Espin Pedrol8e0d1012022-10-06 10:29:11 +0200764 LOGPENDP(endp, DOSMUX, LOGL_NOTICE, "OSMUX socket has been set up\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200765 }
766
767 return mgcp_parse_osmux_cid(line);
768}
769
Philipp Maierbc0346e2018-06-07 09:52:16 +0200770/* Process codec information contained in CRCX/MDCX */
771static int handle_codec_info(struct mgcp_conn_rtp *conn,
Philipp Maier8dc35972021-07-14 11:20:16 +0200772 struct mgcp_request_data *rq, int have_sdp, bool crcx)
Philipp Maierbc0346e2018-06-07 09:52:16 +0200773{
Philipp Maier8dc35972021-07-14 11:20:16 +0200774 struct mgcp_endpoint *endp = rq->endp;
Philipp Maier4c4d2272023-04-26 15:45:17 +0200775 struct mgcp_conn *conn_dst;
776 struct mgcp_conn_rtp *conn_dst_rtp;
777
Philipp Maierbc0346e2018-06-07 09:52:16 +0200778 int rc;
779 char *cmd;
780
781 if (crcx)
782 cmd = "CRCX";
783 else
784 cmd = "MDCX";
785
786 /* Collect codec information */
787 if (have_sdp) {
788 /* If we have SDP, we ignore the local connection options and
789 * use only the SDP information. */
790 mgcp_codec_reset_all(conn);
Philipp Maier8dc35972021-07-14 11:20:16 +0200791 rc = mgcp_parse_sdp_data(endp, conn, rq->pdata);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200792 if (rc != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200793 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
794 "%s: sdp not parseable\n", cmd);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200795
796 /* See also RFC 3661: Protocol error */
797 return 510;
798 }
799 } else if (endp->local_options.codec) {
800 /* When no SDP is available, we use the codec information from
801 * the local connection options (if present) */
802 mgcp_codec_reset_all(conn);
Philipp Maier228e5912019-03-05 13:56:59 +0100803 rc = mgcp_codec_add(conn, PTYPE_UNDEFINED, endp->local_options.codec, NULL);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200804 if (rc != 0)
805 goto error;
806 }
807
808 /* Make sure we always set a sane default codec */
809 if (conn->end.codecs_assigned == 0) {
810 /* When SDP and/or LCO did not supply any codec information,
811 * than it makes sense to pick a sane default: (payload-type 0,
812 * PCMU), see also: OS#2658 */
813 mgcp_codec_reset_all(conn);
Philipp Maier228e5912019-03-05 13:56:59 +0100814 rc = mgcp_codec_add(conn, 0, NULL, NULL);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200815 if (rc != 0)
816 goto error;
817 }
818
Philipp Maier4c4d2272023-04-26 15:45:17 +0200819 /* Try to find an destination RTP connection that we can include in the codec decision. */
820 conn_dst = mgcp_find_dst_conn(conn->conn);
821 if (conn_dst && conn_dst->type == MGCP_CONN_TYPE_RTP)
822 conn_dst_rtp = &conn_dst->u.rtp;
823 else
824 conn_dst_rtp = NULL;
825
Philipp Maierbc0346e2018-06-07 09:52:16 +0200826 /* Make codec decision */
Philipp Maier4c4d2272023-04-26 15:45:17 +0200827 if (mgcp_codec_decide(conn, conn_dst_rtp) != 0)
Philipp Maierbc0346e2018-06-07 09:52:16 +0200828 goto error;
829
830 return 0;
831
832error:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200833 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
834 "%s: codec negotiation failure\n", cmd);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200835
836 /* See also RFC 3661: Codec negotiation failure */
837 return 534;
838}
839
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200840static bool parse_x_osmo_ign(struct mgcp_endpoint *endp, char *line)
841{
842 char *saveptr = NULL;
843
Pau Espin Pedrol6049a632020-09-21 11:03:21 +0200844 if (strncasecmp(line, MGCP_X_OSMO_IGN_HEADER, strlen(MGCP_X_OSMO_IGN_HEADER)))
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200845 return false;
846 line += strlen(MGCP_X_OSMO_IGN_HEADER);
847
848 while (1) {
849 char *token = strtok_r(line, " ", &saveptr);
850 line = NULL;
851 if (!token)
852 break;
853
Pau Espin Pedrol6e26c702019-06-26 13:09:39 +0200854 if (!strcasecmp(token, "C"))
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200855 endp->x_osmo_ign |= MGCP_X_OSMO_IGN_CALLID;
856 else
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200857 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "received unknown X-Osmo-IGN item '%s'\n", token);
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200858 }
859
860 return true;
861}
862
Philipp Maier87bd9be2017-08-22 16:35:41 +0200863/* CRCX command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +0200864static struct msgb *handle_create_con(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200865{
Philipp Maier8dc35972021-07-14 11:20:16 +0200866 struct mgcp_parse_data *pdata = rq->pdata;
867 struct mgcp_trunk *trunk = rq->trunk;
868 struct mgcp_endpoint *endp = rq->endp;
Pau Espin Pedrolaf0f58f2023-06-14 12:21:26 +0200869 struct rate_ctr_group *rate_ctrs;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200870 int error_code = 400;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200871 const char *local_options = NULL;
872 const char *callid = NULL;
873 const char *mode = NULL;
874 char *line;
Pau Espin Pedrol21779192022-09-23 16:46:33 +0200875 int have_sdp = 0, remote_osmux_cid = -2;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200876 struct mgcp_conn_rtp *conn = NULL;
Philipp Maierffd75e42017-11-22 11:44:50 +0100877 struct mgcp_conn *_conn = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200878 char conn_name[512];
Philipp Maiera390d0b2018-01-31 17:30:19 +0100879 int rc;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200880
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200881 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "CRCX: creating new connection ...\n");
Philipp Maier246233d2020-08-18 15:15:24 +0200882
Pau Espin Pedrolaf0f58f2023-06-14 12:21:26 +0200883 if (rq->null_endp) {
884 /* trunk not available so rate_ctr aren't available either. */
885 LOGP(DLMGCP, LOGL_ERROR, "CRCX: Not allowed in 'null' endpoint!\n");
886 return create_err_response(pdata->cfg, NULL, 502, "CRCX", pdata->trans);
887 }
888
889 rate_ctrs = trunk->ratectr.mgcp_crcx_ctr_group;
890
Ericee6958c2021-09-14 18:36:53 +0200891 /* we must have a free ep */
892 if (!endp) {
893 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_AVAIL));
894 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "CRCX: no free endpoints available!\n");
895 return create_err_response(rq->trunk, NULL, 403, "CRCX", pdata->trans);
896 }
897
Philipp Maier8d6a1932020-06-18 12:19:31 +0200898 if (!mgcp_endp_avail(endp)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200899 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_AVAIL));
Philipp Maiera910a812020-08-18 15:13:33 +0200900 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
901 "CRCX: selected endpoint not available!\n");
Ericfbcf4a62021-09-09 18:02:31 +0200902 return create_err_response(rq->trunk, NULL, 501, "CRCX", pdata->trans);
Philipp Maier8d6a1932020-06-18 12:19:31 +0200903 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200904
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200905 /* parse CallID C: and LocalParameters L: */
Philipp Maier8dc35972021-07-14 11:20:16 +0200906 for_each_line(line, pdata->save) {
Philipp Maier036612b2021-07-19 17:47:49 +0200907 if (!mgcp_check_param(endp, trunk, line))
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200908 continue;
909
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +0200910 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200911 case 'L':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200912 local_options = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200913 break;
914 case 'C':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200915 callid = (const char *)line + 3;
916 break;
917 case 'I':
Philipp Maierffd75e42017-11-22 11:44:50 +0100918 /* It is illegal to send a connection identifier
919 * together with a CRCX, the MGW will assign the
920 * connection identifier by itself on CRCX */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200921 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BAD_ACTION));
Ericfbcf4a62021-09-09 18:02:31 +0200922 return create_err_response(rq->trunk, NULL, 523, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200923 break;
924 case 'M':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200925 mode = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200926 break;
927 case 'X':
Pau Espin Pedrolc1bf4692019-05-14 16:23:24 +0200928 if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200929 /* If osmux is disabled, just skip setting it up */
Pau Espin Pedrol36413c02022-10-12 17:58:17 +0200930 if (rq->endp->trunk->cfg->osmux.usage == OSMUX_USAGE_OFF)
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200931 break;
Pau Espin Pedrol21779192022-09-23 16:46:33 +0200932 remote_osmux_cid = mgcp_osmux_setup(endp, line);
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200933 break;
934 }
935
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200936 if (parse_x_osmo_ign(endp, line))
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200937 break;
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200938
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200939 /* Ignore unknown X-headers */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200940 break;
941 case '\0':
942 have_sdp = 1;
943 goto mgcp_header_done;
944 default:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200945 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
946 "CRCX: unhandled option: '%c'/%d\n", *line, *line);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200947 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_UNHANDLED_PARAM));
Ericfbcf4a62021-09-09 18:02:31 +0200948 return create_err_response(rq->trunk, NULL, 539, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200949 break;
950 }
951 }
952
953mgcp_header_done:
Philipp Maier87bd9be2017-08-22 16:35:41 +0200954 /* Check parameters */
955 if (!callid) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200956 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
957 "CRCX: insufficient parameters, missing callid\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200958 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_MISSING_CALLID));
Ericfbcf4a62021-09-09 18:02:31 +0200959 return create_err_response(endp, endp, 516, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200960 }
961
Philipp Maier87bd9be2017-08-22 16:35:41 +0200962 if (!mode) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200963 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
964 "CRCX: insufficient parameters, missing mode\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200965 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_MODE));
Ericfbcf4a62021-09-09 18:02:31 +0200966 return create_err_response(endp, endp, 517, "CRCX", pdata->trans);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200967 }
968
Philipp Maier87bd9be2017-08-22 16:35:41 +0200969 /* Check if we are able to accept the creation of another connection */
Andreas Eversbergaf671782023-07-05 12:51:40 +0200970 if (endp->type->max_conns > 0 && llist_count(&endp->conns) >= endp->type->max_conns) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200971 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
972 "CRCX: endpoint full, max. %i connections allowed!\n",
973 endp->type->max_conns);
Philipp Maier14b27a82020-06-02 20:15:30 +0200974 if (trunk->force_realloc) {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200975 /* There is no more room for a connection, make some
976 * room by blindly tossing the oldest of the two two
977 * connections */
978 mgcp_conn_free_oldest(endp);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200979 } else {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200980 /* There is no more room for a connection, leave
981 * everything as it is and return with an error */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200982 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_LIMIT_EXCEEDED));
Ericfbcf4a62021-09-09 18:02:31 +0200983 return create_err_response(endp, endp, 540, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200984 }
985 }
986
Philipp Maier87bd9be2017-08-22 16:35:41 +0200987 /* Check if this endpoint already serves a call, if so, check if the
988 * callids match up so that we are sure that this is our call */
989 if (endp->callid && mgcp_verify_call_id(endp, callid)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200990 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
991 "CRCX: already seized by other call (%s)\n",
992 endp->callid);
Philipp Maier14b27a82020-06-02 20:15:30 +0200993 if (trunk->force_realloc)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200994 /* This is not our call, toss everything by releasing
995 * the entire endpoint. (rude!) */
Philipp Maier1355d7e2018-02-01 14:30:06 +0100996 mgcp_endp_release(endp);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200997 else {
998 /* This is not our call, leave everything as it is and
999 * return with an error. */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001000 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_UNKNOWN_CALLID));
Ericfbcf4a62021-09-09 18:02:31 +02001001 return create_err_response(endp, endp, 400, "CRCX", pdata->trans);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001002 }
1003 }
1004
Philipp Maier889fe7f2020-07-06 17:44:12 +02001005 if (!endp->callid) {
1006 /* Claim endpoint resources. This will also set the callid,
1007 * creating additional connections will only be possible if
1008 * the callid matches up (see above). */
1009 rc = mgcp_endp_claim(endp, callid);
1010 if (rc != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001011 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_CLAIM));
Ericfbcf4a62021-09-09 18:02:31 +02001012 return create_err_response(endp, endp, 502, "CRCX", pdata->trans);
Philipp Maier889fe7f2020-07-06 17:44:12 +02001013 }
1014 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001015
Philipp Maierffd75e42017-11-22 11:44:50 +01001016 snprintf(conn_name, sizeof(conn_name), "%s", callid);
Philipp Maier14b27a82020-06-02 20:15:30 +02001017 _conn = mgcp_conn_alloc(trunk->endpoints, endp, MGCP_CONN_TYPE_RTP, conn_name);
Philipp Maierffd75e42017-11-22 11:44:50 +01001018 if (!_conn) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001019 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1020 "CRCX: unable to allocate RTP connection\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001021 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_ALLOC_CONN));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001022 goto error2;
1023
Philipp Maier87bd9be2017-08-22 16:35:41 +02001024 }
Philipp Maier889fe7f2020-07-06 17:44:12 +02001025
Philipp Maierffd75e42017-11-22 11:44:50 +01001026 conn = mgcp_conn_get_rtp(endp, _conn->id);
1027 OSMO_ASSERT(conn);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001028
1029 if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
1030 error_code = 517;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001031 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_MODE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001032 goto error2;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001033 }
1034
Pau Espin Pedrol21779192022-09-23 16:46:33 +02001035 /* If X-Osmux (remote CID) was received (-1 is wilcard), alloc next avail CID as local CID */
1036 if (remote_osmux_cid >= -1) {
Pau Espin Pedrold48a8112022-09-27 12:37:53 +02001037 if (osmux_init_conn(conn) < 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001038 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_OSMUX));
Pau Espin Pedrol2b896172019-04-24 13:47:23 +02001039 goto error2;
1040 }
Pau Espin Pedrol21779192022-09-23 16:46:33 +02001041 if (remote_osmux_cid >= 0) {
1042 conn->osmux.remote_cid_present = true;
1043 conn->osmux.remote_cid = remote_osmux_cid;
1044 }
Pau Espin Pedrol36413c02022-10-12 17:58:17 +02001045 } else if (endp->trunk->cfg->osmux.usage == OSMUX_USAGE_ONLY) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001046 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
1047 "CRCX: osmux only and no osmux offered\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001048 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_OSMUX));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001049 goto error2;
1050 }
1051
Philipp Maierbc0346e2018-06-07 09:52:16 +02001052 /* Set local connection options, if present */
1053 if (local_options) {
Philipp Maier8dc35972021-07-14 11:20:16 +02001054 rc = set_local_cx_options(trunk->endpoints,
Philipp Maierbc0346e2018-06-07 09:52:16 +02001055 &endp->local_options, local_options);
1056 if (rc != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001057 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
Oliver Smith169d50e2023-01-24 13:12:54 +01001058 "CRCX: invalid local connection options!\n");
Philipp Maierbc0346e2018-06-07 09:52:16 +02001059 error_code = rc;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001060 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS));
Philipp Maierbc0346e2018-06-07 09:52:16 +02001061 goto error2;
1062 }
1063 }
1064
1065 /* Handle codec information and decide for a suitable codec */
Philipp Maier8dc35972021-07-14 11:20:16 +02001066 rc = handle_codec_info(conn, rq, have_sdp, true);
Philipp Maierbc0346e2018-06-07 09:52:16 +02001067 mgcp_codec_summary(conn);
1068 if (rc) {
1069 error_code = rc;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001070 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_CODEC_NEGOTIATION));
Philipp Maierbc0346e2018-06-07 09:52:16 +02001071 goto error2;
1072 }
Pau Espin Pedrolbb3ccde2021-12-23 19:49:26 +01001073 /* Upgrade the conn type RTP_DEFAULT->RTP_IUUP if needed based on requested codec: */
1074 /* TODO: "codec" probably needs to be moved from endp to conn */
1075 if (conn->type == MGCP_RTP_DEFAULT && strcmp(conn->end.codec->subtype_name, "VND.3GPP.IUFP") == 0) {
1076 rc = mgcp_conn_iuup_init(conn);
1077 }
Philipp Maierbc0346e2018-06-07 09:52:16 +02001078
Philipp Maier8dc35972021-07-14 11:20:16 +02001079 if (pdata->cfg->force_ptime) {
1080 conn->end.packet_duration_ms = pdata->cfg->force_ptime;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001081 conn->end.force_output_ptime = 1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001082 }
1083
Philipp Maier1cb1e382017-11-02 17:16:04 +01001084 mgcp_rtp_end_config(endp, 0, &conn->end);
1085
Philipp Maierc3cc6542018-02-02 12:58:42 +01001086 /* check connection mode setting */
1087 if (conn->conn->mode != MGCP_CONN_LOOPBACK
1088 && conn->conn->mode != MGCP_CONN_RECV_ONLY
Pau Espin Pedrol5ffd1272022-10-04 13:45:48 +02001089 && osmo_sockaddr_port(&conn->end.addr.u.sa) == 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001090 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
1091 "CRCX: selected connection mode type requires an opposite end!\n");
Philipp Maierc3cc6542018-02-02 12:58:42 +01001092 error_code = 527;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001093 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC));
Philipp Maierc3cc6542018-02-02 12:58:42 +01001094 goto error2;
1095 }
1096
Pau Espin Pedrol71d42e72020-09-03 14:20:07 +02001097 /* Find a local address for conn based on policy and initial SDP remote
1098 information, then find a free port for it */
Pau Espin Pedrol70c03f52022-10-04 16:49:41 +02001099 if (mgcp_get_local_addr(conn->end.local_addr, conn) < 0) {
1100 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
1101 goto error2;
1102 }
Philipp Maier1cb1e382017-11-02 17:16:04 +01001103 if (allocate_port(endp, conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001104 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
Philipp Maier1cb1e382017-11-02 17:16:04 +01001105 goto error2;
1106 }
1107
Philipp Maier87bd9be2017-08-22 16:35:41 +02001108 if (setup_rtp_processing(endp, conn) != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001109 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
1110 "CRCX: could not start RTP processing!\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001111 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_START_RTP));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001112 goto error2;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001113 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001114
Pau Espin Pedrol833281d2022-10-06 17:41:22 +02001115 /* Notify Osmux conn that CRCX was received */
1116 if (mgcp_conn_rtp_is_osmux(conn)) {
1117 if (conn_osmux_event_rx_crcx_mdcx(conn) < 0) {
1118 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR, "CRCX: Osmux handling failed!\n");
1119 goto error2;
1120 }
1121 }
1122
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001123 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
1124 "CRCX: Creating connection: port: %u\n", conn->end.local_port);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001125
Philipp Maiere726d4f2017-11-01 10:41:34 +01001126 /* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
Philipp Maier14b27a82020-06-02 20:15:30 +02001127 OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Pau Espin Pedrolca280a12021-07-06 18:15:35 +02001128 if (conn->conn->mode & MGCP_CONN_RECV_ONLY &&
Pau Espin Pedrolca280a12021-07-06 18:15:35 +02001129 trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001130 send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001131
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001132 LOGPCONN(_conn, DLMGCP, LOGL_NOTICE,
1133 "CRCX: connection successfully created\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001134 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_SUCCESS));
Philipp Maier889fe7f2020-07-06 17:44:12 +02001135 mgcp_endp_update(endp);
Philipp Maier41d59202021-07-20 15:49:00 +02001136
1137 /* NOTE: Only in the virtual trunk we allow dynamic endpoint names */
1138 bool add_epname = rq->wildcarded && trunk->trunk_type == MGCP_TRUNK_VIRTUAL;
1139 return create_response_with_sdp(endp, conn, "CRCX", pdata->trans, add_epname, true);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001140error2:
Philipp Maier1355d7e2018-02-01 14:30:06 +01001141 mgcp_endp_release(endp);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001142 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1143 "CRCX: unable to create connection\n");
Ericfbcf4a62021-09-09 18:02:31 +02001144 return create_err_response(endp, endp, error_code, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001145}
1146
Philipp Maier87bd9be2017-08-22 16:35:41 +02001147/* MDCX command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +02001148static struct msgb *handle_modify_con(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001149{
Philipp Maier8dc35972021-07-14 11:20:16 +02001150 struct mgcp_parse_data *pdata = rq->pdata;
1151 struct mgcp_trunk *trunk = rq->trunk;
1152 struct mgcp_endpoint *endp = rq->endp;
Pau Espin Pedrolaf0f58f2023-06-14 12:21:26 +02001153 struct rate_ctr_group *rate_ctrs;
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001154 char new_local_addr[INET6_ADDRSTRLEN];
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001155 int error_code = 500;
1156 int silent = 0;
1157 int have_sdp = 0;
1158 char *line;
1159 const char *local_options = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001160 const char *mode = NULL;
1161 struct mgcp_conn_rtp *conn = NULL;
Philipp Maier01d24a32017-11-21 17:26:09 +01001162 const char *conn_id = NULL;
Pau Espin Pedrol21779192022-09-23 16:46:33 +02001163 int remote_osmux_cid = -2;
Philipp Maiera390d0b2018-01-31 17:30:19 +01001164 int rc;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001165
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001166 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "MDCX: modifying existing connection ...\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001167
Pau Espin Pedrolaf0f58f2023-06-14 12:21:26 +02001168 if (rq->null_endp) {
1169 /* trunk not available so rate_ctr aren't available either. */
1170 LOGP(DLMGCP, LOGL_ERROR, "MDCX: Not allowed in 'null' endpoint!\n");
1171 return create_err_response(pdata->cfg, NULL, 502, "MDCX", pdata->trans);
1172 }
1173
1174 rate_ctrs = trunk->ratectr.mgcp_mdcx_ctr_group;
1175
Philipp Maier5656fbf2018-02-02 14:41:58 +01001176 /* Prohibit wildcarded requests */
Philipp Maier8dc35972021-07-14 11:20:16 +02001177 if (rq->wildcarded) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001178 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1179 "MDCX: wildcarded endpoint names not supported.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001180 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_WILDCARD));
Ericfbcf4a62021-09-09 18:02:31 +02001181 return create_err_response(rq->trunk, endp, 507, "MDCX", pdata->trans);
Philipp Maier5656fbf2018-02-02 14:41:58 +01001182 }
1183
Ericee6958c2021-09-14 18:36:53 +02001184 if (!endp || !mgcp_endp_avail(endp)) {
1185 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_AVAIL));
1186 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "MDCX: selected endpoint not available!\n");
1187 return create_err_response(rq->trunk, NULL, 501, "MDCX", pdata->trans);
1188 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001189 if (llist_count(&endp->conns) <= 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001190 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1191 "MDCX: endpoint is not holding a connection.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001192 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_CONN));
Ericfbcf4a62021-09-09 18:02:31 +02001193 return create_err_response(endp, endp, 400, "MDCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001194 }
1195
Philipp Maier8dc35972021-07-14 11:20:16 +02001196 for_each_line(line, pdata->save) {
Philipp Maier036612b2021-07-19 17:47:49 +02001197 if (!mgcp_check_param(endp, trunk, line))
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001198 continue;
1199
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001200 switch (toupper(line[0])) {
Philipp Maier87bd9be2017-08-22 16:35:41 +02001201 case 'C':
Harald Weltee35eeae2017-12-28 13:47:37 +01001202 if (mgcp_verify_call_id(endp, line + 3) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001203 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CALLID));
Harald Weltee35eeae2017-12-28 13:47:37 +01001204 error_code = 516;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001205 goto error3;
Harald Weltee35eeae2017-12-28 13:47:37 +01001206 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001207 break;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001208 case 'I':
Philipp Maier01d24a32017-11-21 17:26:09 +01001209 conn_id = (const char *)line + 3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001210 if ((error_code = mgcp_verify_ci(endp, conn_id))) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001211 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CONNID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001212 goto error3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001213 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001214 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001215 case 'L':
Philipp Maier87bd9be2017-08-22 16:35:41 +02001216 local_options = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001217 break;
1218 case 'M':
Philipp Maier87bd9be2017-08-22 16:35:41 +02001219 mode = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001220 break;
1221 case 'Z':
Pau Espin Pedrol9b508f62019-06-26 13:11:22 +02001222 silent = strcasecmp("noanswer", line + 3) == 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001223 break;
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001224 case 'X':
Pau Espin Pedrolc1bf4692019-05-14 16:23:24 +02001225 if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001226 /* If osmux is disabled, just skip setting it up */
Pau Espin Pedrol36413c02022-10-12 17:58:17 +02001227 if (endp->trunk->cfg->osmux.usage == OSMUX_USAGE_OFF)
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001228 break;
Pau Espin Pedrol21779192022-09-23 16:46:33 +02001229 remote_osmux_cid = mgcp_osmux_setup(endp, line);
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001230 break;
1231 }
1232 /* Ignore unknown X-headers */
1233 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001234 case '\0':
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001235 have_sdp = 1;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001236 goto mgcp_header_done;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001237 break;
1238 default:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001239 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1240 "MDCX: Unhandled MGCP option: '%c'/%d\n",
1241 line[0], line[0]);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001242 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_UNHANDLED_PARAM));
Ericfbcf4a62021-09-09 18:02:31 +02001243 return create_err_response(rq->trunk, NULL, 539, "MDCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001244 break;
1245 }
1246 }
1247
Philipp Maier87bd9be2017-08-22 16:35:41 +02001248mgcp_header_done:
Philipp Maier01d24a32017-11-21 17:26:09 +01001249 if (!conn_id) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001250 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1251 "MDCX: insufficient parameters, missing ci (connectionIdentifier)\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001252 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_CONNID));
Ericfbcf4a62021-09-09 18:02:31 +02001253 return create_err_response(endp, endp, 515, "MDCX", pdata->trans);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001254 }
1255
1256 conn = mgcp_conn_get_rtp(endp, conn_id);
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001257 if (!conn) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001258 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_CONN_NOT_FOUND));
Ericfbcf4a62021-09-09 18:02:31 +02001259 return create_err_response(endp, endp, 400, "MDCX", pdata->trans);
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001260 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001261
Oliver Smithe36b7752019-01-22 16:31:36 +01001262 mgcp_conn_watchdog_kick(conn->conn);
1263
Philipp Maier87bd9be2017-08-22 16:35:41 +02001264 if (mode) {
1265 if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001266 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_MODE));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001267 error_code = 517;
1268 goto error3;
1269 }
1270 } else
Pau Espin Pedrol209eb9f2019-04-24 12:03:04 +02001271 conn->conn->mode = conn->conn->mode_orig;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001272
Philipp Maierbc0346e2018-06-07 09:52:16 +02001273 /* Set local connection options, if present */
1274 if (local_options) {
Philipp Maier8dc35972021-07-14 11:20:16 +02001275 rc = set_local_cx_options(trunk->endpoints,
Philipp Maierbc0346e2018-06-07 09:52:16 +02001276 &endp->local_options, local_options);
1277 if (rc != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001278 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1279 "MDCX: invalid local connection options!\n");
Philipp Maierbc0346e2018-06-07 09:52:16 +02001280 error_code = rc;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001281 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS));
Philipp Maierbc0346e2018-06-07 09:52:16 +02001282 goto error3;
1283 }
1284 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001285
Philipp Maierbc0346e2018-06-07 09:52:16 +02001286 /* Handle codec information and decide for a suitable codec */
Philipp Maier8dc35972021-07-14 11:20:16 +02001287 rc = handle_codec_info(conn, rq, have_sdp, false);
Philipp Maierbc0346e2018-06-07 09:52:16 +02001288 mgcp_codec_summary(conn);
1289 if (rc) {
Philipp Maieraf07f662018-02-02 11:34:02 +01001290 error_code = rc;
1291 goto error3;
Philipp Maiera390d0b2018-01-31 17:30:19 +01001292 }
Pau Espin Pedrol58d5b972023-09-27 16:21:50 +02001293 /* Upgrade the conn type RTP_DEFAULT->RTP_IUUP if needed based on requested codec: */
1294 /* TODO: "codec" probably needs to be moved from endp to conn */
1295 if (conn->type == MGCP_RTP_DEFAULT && strcmp(conn->end.codec->subtype_name, "VND.3GPP.IUFP") == 0)
1296 rc = mgcp_conn_iuup_init(conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001297
Philipp Maierc3cc6542018-02-02 12:58:42 +01001298 /* check connection mode setting */
1299 if (conn->conn->mode != MGCP_CONN_LOOPBACK
1300 && conn->conn->mode != MGCP_CONN_RECV_ONLY
Pau Espin Pedrold6769ea2021-07-06 19:44:27 +02001301 && !mgcp_rtp_end_remote_addr_available(&conn->end)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001302 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1303 "MDCX: selected connection mode type requires an opposite end!\n");
Philipp Maierc3cc6542018-02-02 12:58:42 +01001304 error_code = 527;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001305 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC));
Philipp Maierc3cc6542018-02-02 12:58:42 +01001306 goto error3;
1307 }
1308
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001309 if (mgcp_conn_rtp_is_osmux(conn)) {
Pau Espin Pedrol21779192022-09-23 16:46:33 +02001310 OSMO_ASSERT(conn->osmux.local_cid_allocated);
1311 if (remote_osmux_cid < -1) {
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001312 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1313 "MDCX: Failed to parse Osmux CID!\n");
1314 goto error3;
Pau Espin Pedrol21779192022-09-23 16:46:33 +02001315 } else if (remote_osmux_cid == -1) {
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001316 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1317 "MDCX: wilcard in MDCX is not supported!\n");
1318 goto error3;
Pau Espin Pedrol21779192022-09-23 16:46:33 +02001319 } else if (conn->osmux.remote_cid_present &&
Pau Espin Pedrol833281d2022-10-06 17:41:22 +02001320 remote_osmux_cid != conn->osmux.remote_cid) {
1321 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1322 "MDCX: changing already allocated CID is not supported!\n");
1323 goto error3;
1324 } else {
1325 conn->osmux.remote_cid_present = true;
1326 conn->osmux.remote_cid = remote_osmux_cid;
1327 if (conn_osmux_event_rx_crcx_mdcx(conn) < 0) {
1328 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1329 "MDCX: Osmux handling failed!\n");
1330 goto error3;
1331 }
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001332 }
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001333 }
Philipp Maierbc0346e2018-06-07 09:52:16 +02001334
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001335 /* MDCX may have provided a new remote address, which means we may need
1336 to update our announced IP addr and re-bind our local end. This can
1337 happen for instance if MGW initially provided an IPv4 during CRCX
1338 ACK, and now MDCX tells us the remote has an IPv6 address. */
Pau Espin Pedrol70c03f52022-10-04 16:49:41 +02001339 if (mgcp_get_local_addr(new_local_addr, conn) < 0) {
1340 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
1341 goto error3;
1342 }
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001343 if (strcmp(new_local_addr, conn->end.local_addr)) {
1344 osmo_strlcpy(conn->end.local_addr, new_local_addr, sizeof(conn->end.local_addr));
1345 mgcp_free_rtp_port(&conn->end);
1346 if (allocate_port(endp, conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001347 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001348 goto error3;
1349 }
1350 }
1351
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001352 if (setup_rtp_processing(endp, conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001353 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_START_RTP));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001354 goto error3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001355 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001356
Philipp Maier87bd9be2017-08-22 16:35:41 +02001357 mgcp_rtp_end_config(endp, 1, &conn->end);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001358
1359 /* modify */
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001360 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
1361 "MDCX: modified conn:%s\n", mgcp_conn_dump(conn->conn));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001362
Philipp Maiere726d4f2017-11-01 10:41:34 +01001363 /* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
Philipp Maier8dc35972021-07-14 11:20:16 +02001364 OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Pau Espin Pedrolca280a12021-07-06 18:15:35 +02001365 if (conn->conn->mode & MGCP_CONN_RECV_ONLY &&
Philipp Maier8dc35972021-07-14 11:20:16 +02001366 trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001367 send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001368
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001369 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_SUCCESS));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001370 if (silent)
1371 goto out_silent;
1372
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001373 LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
1374 "MDCX: connection successfully modified\n");
Philipp Maier889fe7f2020-07-06 17:44:12 +02001375 mgcp_endp_update(endp);
Philipp Maier41d59202021-07-20 15:49:00 +02001376 return create_response_with_sdp(endp, conn, "MDCX", pdata->trans, false, false);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001377error3:
Ericfbcf4a62021-09-09 18:02:31 +02001378 return create_err_response(endp, endp, error_code, "MDCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001379
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001380out_silent:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001381 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "MDCX: silent exit\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001382 return NULL;
1383}
1384
Philipp Maier87bd9be2017-08-22 16:35:41 +02001385/* DLCX command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +02001386static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001387{
Philipp Maier8dc35972021-07-14 11:20:16 +02001388 struct mgcp_parse_data *pdata = rq->pdata;
1389 struct mgcp_trunk *trunk = rq->trunk;
1390 struct mgcp_endpoint *endp = rq->endp;
Pau Espin Pedrolaf0f58f2023-06-14 12:21:26 +02001391 struct rate_ctr_group *rate_ctrs;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001392 int error_code = 400;
1393 int silent = 0;
1394 char *line;
1395 char stats[1048];
Philipp Maier01d24a32017-11-21 17:26:09 +01001396 const char *conn_id = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001397 struct mgcp_conn_rtp *conn = NULL;
Philipp Maierf486e742021-07-19 14:56:29 +02001398 unsigned int i;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001399
Philipp Maierf486e742021-07-19 14:56:29 +02001400 /* NOTE: In this handler we can not take it for granted that the endp
Pau Espin Pedrolaf0f58f2023-06-14 12:21:26 +02001401 * pointer will be populated, however a trunk is always guaranteed (except for 'null' endp).
1402 */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001403
Philipp Maierf486e742021-07-19 14:56:29 +02001404 LOGPEPTR(endp, trunk, DLMGCP, LOGL_NOTICE, "DLCX: deleting connection(s) ...\n");
1405
Pau Espin Pedrolaf0f58f2023-06-14 12:21:26 +02001406 if (rq->null_endp) {
1407 /* trunk not available so rate_ctr aren't available either. */
1408 LOGP(DLMGCP, LOGL_ERROR, "DLCX: Not allowed in 'null' endpoint!\n");
1409 return create_err_response(pdata->cfg, NULL, 502, "DLCX", pdata->trans);
1410 }
1411
1412 rate_ctrs = trunk->ratectr.mgcp_dlcx_ctr_group;
Philipp Maierf486e742021-07-19 14:56:29 +02001413 if (endp && !mgcp_endp_avail(endp)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001414 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_AVAIL));
Philipp Maiera910a812020-08-18 15:13:33 +02001415 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1416 "DLCX: selected endpoint not available!\n");
Ericfbcf4a62021-09-09 18:02:31 +02001417 return create_err_response(rq->trunk, NULL, 501, "DLCX", pdata->trans);
Philipp Maier8d6a1932020-06-18 12:19:31 +02001418 }
1419
Philipp Maierf486e742021-07-19 14:56:29 +02001420 if (endp && !rq->wildcarded && llist_empty(&endp->conns)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001421 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1422 "DLCX: endpoint is not holding a connection.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001423 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_NO_CONN));
Ericfbcf4a62021-09-09 18:02:31 +02001424 return create_err_response(endp, endp, 515, "DLCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001425 }
1426
Ericee6958c2021-09-14 18:36:53 +02001427 /* Handle wildcarded DLCX that refers to the whole trunk. This means
1428 * that we walk over all endpoints on the trunk in order to drop all
1429 * connections on the trunk. (see also RFC3435 Annex F.7) */
1430 if (rq->wildcarded) {
1431 int num_conns = 0;
1432 for (i = 0; i < trunk->number_endpoints; i++) {
1433 num_conns += llist_count(&trunk->endpoints[i]->conns);
1434 mgcp_endp_release(trunk->endpoints[i]);
1435 }
1436 rate_ctr_add(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS), num_conns);
1437 return create_ok_response(trunk, NULL, 200, "DLCX", pdata->trans);
1438 }
1439
Philipp Maier8dc35972021-07-14 11:20:16 +02001440 for_each_line(line, pdata->save) {
Philipp Maier036612b2021-07-19 17:47:49 +02001441 if (!mgcp_check_param(endp, trunk, line))
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001442 continue;
1443
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001444 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001445 case 'C':
Philipp Maierf486e742021-07-19 14:56:29 +02001446 /* If we have no endpoint, but a call id in the request,
1447 then this request cannot be handled */
1448 if (!endp) {
1449 LOGPTRUNK(trunk, DLMGCP, LOGL_NOTICE,
1450 "cannot handle requests with call-id (C) without endpoint -- abort!");
1451 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));
Ericfbcf4a62021-09-09 18:02:31 +02001452 return create_err_response(rq->trunk, NULL, 539, "DLCX", pdata->trans);
Philipp Maierf486e742021-07-19 14:56:29 +02001453 }
1454
Harald Weltee35eeae2017-12-28 13:47:37 +01001455 if (mgcp_verify_call_id(endp, line + 3) != 0) {
1456 error_code = 516;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001457 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CALLID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001458 goto error3;
Harald Weltee35eeae2017-12-28 13:47:37 +01001459 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001460 break;
1461 case 'I':
Philipp Maierf486e742021-07-19 14:56:29 +02001462 /* If we have no endpoint, but a connection id in the request,
1463 then this request cannot be handled */
1464 if (!endp) {
1465 LOGPTRUNK(trunk, DLMGCP, LOGL_NOTICE,
1466 "cannot handle requests with conn-id (I) without endpoint -- abort!");
1467 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));
Ericfbcf4a62021-09-09 18:02:31 +02001468 return create_err_response(rq->trunk, NULL, 539, "DLCX", pdata->trans);
Philipp Maierf486e742021-07-19 14:56:29 +02001469 }
1470
Philipp Maier01d24a32017-11-21 17:26:09 +01001471 conn_id = (const char *)line + 3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001472 if ((error_code = mgcp_verify_ci(endp, conn_id))) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001473 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CONNID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001474 goto error3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001475 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001476 break;
1477 case 'Z':
Pau Espin Pedrol9b508f62019-06-26 13:11:22 +02001478 silent = strcasecmp("noanswer", line + 3) == 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001479 break;
1480 default:
Philipp Maierf486e742021-07-19 14:56:29 +02001481 LOGPEPTR(endp, trunk, DLMGCP, LOGL_NOTICE, "DLCX: Unhandled MGCP option: '%c'/%d\n",
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001482 line[0], line[0]);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001483 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));
Ericfbcf4a62021-09-09 18:02:31 +02001484 return create_err_response(rq->trunk, NULL, 539, "DLCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001485 break;
1486 }
1487 }
1488
Philipp Maierce187052021-07-23 10:58:19 +02001489 /* The logic does not permit to go past this point without having the
1490 * the endp pointer populated. */
1491 OSMO_ASSERT(endp);
1492
Philipp Maierf4c0e372017-10-11 16:06:45 +02001493 /* When no connection id is supplied, we will interpret this as a
Philipp Maierf486e742021-07-19 14:56:29 +02001494 * wildcarded DLCX that refers to the selected endpoint. This means
1495 * that we drop all connections on that specific endpoint at once.
1496 * (See also RFC3435 Section F.7) */
Philipp Maier01d24a32017-11-21 17:26:09 +01001497 if (!conn_id) {
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001498 int num_conns = llist_count(&endp->conns);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001499 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1500 "DLCX: missing ci (connectionIdentifier), will remove all connections (%d total) at once\n",
1501 num_conns);
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001502
1503 if (num_conns > 0)
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001504 rate_ctr_add(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS), num_conns);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001505
Philipp Maier1355d7e2018-02-01 14:30:06 +01001506 mgcp_endp_release(endp);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001507
1508 /* Note: In this case we do not return any statistics,
1509 * as we assume that the client is not interested in
1510 * this case. */
Ericfbcf4a62021-09-09 18:02:31 +02001511 return create_ok_response(endp, endp, 200, "DLCX", pdata->trans);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001512 }
1513
Philipp Maierf4c0e372017-10-11 16:06:45 +02001514 /* Find the connection */
Philipp Maier87bd9be2017-08-22 16:35:41 +02001515 conn = mgcp_conn_get_rtp(endp, conn_id);
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001516 if (!conn) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001517 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CONNID));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001518 goto error3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001519 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001520 /* save the statistics of the current connection */
1521 mgcp_format_stats(stats, sizeof(stats), conn->conn);
1522
1523 /* delete connection */
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001524 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG, "DLCX: deleting conn:%s\n",
1525 mgcp_conn_dump(conn->conn));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001526 mgcp_conn_free(endp, conn_id);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001527 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1528 "DLCX: connection successfully deleted\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +02001529
1530 /* When all connections are closed, the endpoint will be released
1531 * in order to be ready to be used by another call. */
1532 if (llist_count(&endp->conns) <= 0) {
Philipp Maier1355d7e2018-02-01 14:30:06 +01001533 mgcp_endp_release(endp);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001534 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: endpoint released\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +02001535 }
1536
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001537 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001538 if (silent)
1539 goto out_silent;
Ericfbcf4a62021-09-09 18:02:31 +02001540 return create_ok_resp_with_param(endp, endp, 250, "DLCX", pdata->trans, stats);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001541
1542error3:
Ericfbcf4a62021-09-09 18:02:31 +02001543 return create_err_response(endp, endp, error_code, "DLCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001544
1545out_silent:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001546 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: silent exit\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001547 return NULL;
1548}
1549
Philipp Maier87bd9be2017-08-22 16:35:41 +02001550/* RSIP command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +02001551static struct msgb *handle_rsip(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001552{
Philipp Maier87bd9be2017-08-22 16:35:41 +02001553 /* TODO: Also implement the resetting of a specific endpoint
1554 * to make mgcp_send_reset_ep() work. Currently this will call
1555 * mgcp_rsip_cb() in mgw_main.c, which sets reset_endpoints=1
1556 * to make read_call_agent() reset all endpoints when called
1557 * next time. In order to selectively reset endpoints some
1558 * mechanism to distinguish which endpoint shall be resetted
1559 * is needed */
1560
1561 LOGP(DLMGCP, LOGL_NOTICE, "RSIP: resetting all endpoints ...\n");
1562
Pau Espin Pedrolaf0f58f2023-06-14 12:21:26 +02001563 if (rq->null_endp) {
1564 /* trunk not available so rate_ctr aren't available either. */
1565 LOGP(DLMGCP, LOGL_ERROR, "RSIP: Not allowed in 'null' endpoint!\n");
1566 return create_err_response(rq->pdata->cfg, NULL, 502, "RSIP", rq->pdata->trans);
1567 }
1568
Philipp Maier8dc35972021-07-14 11:20:16 +02001569 if (rq->pdata->cfg->reset_cb)
1570 rq->pdata->cfg->reset_cb(rq->endp->trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001571 return NULL;
1572}
1573
1574static char extract_tone(const char *line)
1575{
1576 const char *str = strstr(line, "D/");
1577 if (!str)
1578 return CHAR_MAX;
1579
1580 return str[2];
1581}
1582
Philipp Maier87bd9be2017-08-22 16:35:41 +02001583/* This can request like DTMF detection and forward, fax detection... it
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001584 * can also request when the notification should be send and such. We don't
Philipp Maier87bd9be2017-08-22 16:35:41 +02001585 * do this right now. */
Philipp Maier8dc35972021-07-14 11:20:16 +02001586static struct msgb *handle_noti_req(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001587{
1588 int res = 0;
1589 char *line;
1590 char tone = CHAR_MAX;
1591
Philipp Maier87bd9be2017-08-22 16:35:41 +02001592 LOGP(DLMGCP, LOGL_NOTICE, "RQNT: processing request for notification ...\n");
1593
Pau Espin Pedrolaf0f58f2023-06-14 12:21:26 +02001594 if (rq->null_endp) {
1595 /* trunk not available so rate_ctr aren't available either. */
1596 LOGP(DLMGCP, LOGL_ERROR, "RQNT: Not allowed in 'null' endpoint!\n");
1597 return create_err_response(rq->pdata->cfg, NULL, 502, "RQNT", rq->pdata->trans);
1598 }
1599
Philipp Maier8dc35972021-07-14 11:20:16 +02001600 for_each_line(line, rq->pdata->save) {
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001601 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001602 case 'S':
1603 tone = extract_tone(line);
1604 break;
1605 }
1606 }
1607
1608 /* we didn't see a signal request with a tone */
1609 if (tone == CHAR_MAX)
Ericfbcf4a62021-09-09 18:02:31 +02001610 return create_ok_response(rq->endp, rq->endp, 200, "RQNT", rq->pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001611
Philipp Maier8dc35972021-07-14 11:20:16 +02001612 if (rq->pdata->cfg->rqnt_cb)
1613 res = rq->pdata->cfg->rqnt_cb(rq->endp, tone);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001614
Ericfbcf4a62021-09-09 18:02:31 +02001615 return res == 0 ? create_ok_response(rq->endp, rq->endp, 200, "RQNT", rq->pdata->trans) :
1616 create_err_response(rq->endp, rq->endp, res, "RQNT", rq->pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001617}
1618
Philipp Maier87bd9be2017-08-22 16:35:41 +02001619/* Connection keepalive timer, will take care that dummy packets are send
Harald Welte1d1b98f2017-12-25 10:03:40 +01001620 * regularly, so that NAT connections stay open */
Philipp Maier14b27a82020-06-02 20:15:30 +02001621static void mgcp_keepalive_timer_cb(void *_trunk)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001622{
Philipp Maier14b27a82020-06-02 20:15:30 +02001623 struct mgcp_trunk *trunk = _trunk;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001624 struct mgcp_conn *conn;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001625 int i;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001626
Philipp Maiere726d4f2017-11-01 10:41:34 +01001627 LOGP(DLMGCP, LOGL_DEBUG, "triggered trunk %d keepalive timer\n",
Philipp Maier14b27a82020-06-02 20:15:30 +02001628 trunk->trunk_nr);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001629
Philipp Maiere726d4f2017-11-01 10:41:34 +01001630 /* Do not accept invalid configuration values
1631 * valid is MGCP_KEEPALIVE_NEVER, MGCP_KEEPALIVE_ONCE and
1632 * values greater 0 */
Philipp Maier14b27a82020-06-02 20:15:30 +02001633 OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Philipp Maiere726d4f2017-11-01 10:41:34 +01001634
1635 /* The dummy packet functionality has been disabled, we will exit
1636 * immediately, no further timer is scheduled, which means we will no
1637 * longer send dummy packets even when we did before */
Philipp Maier14b27a82020-06-02 20:15:30 +02001638 if (trunk->keepalive_interval == MGCP_KEEPALIVE_NEVER)
Philipp Maiere726d4f2017-11-01 10:41:34 +01001639 return;
1640
1641 /* In cases where only one dummy packet is sent, we do not need
1642 * the timer since the functions that handle the CRCX and MDCX are
1643 * triggering the sending of the dummy packet. So we behave like in
1644 * the MGCP_KEEPALIVE_NEVER case */
Philipp Maier14b27a82020-06-02 20:15:30 +02001645 if (trunk->keepalive_interval == MGCP_KEEPALIVE_ONCE)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001646 return;
1647
Philipp Maier87bd9be2017-08-22 16:35:41 +02001648 /* Send walk over all endpoints and send out dummy packets through
1649 * every connection present on each endpoint */
Philipp Maier4131a652021-07-07 14:04:34 +02001650 for (i = 0; i < trunk->number_endpoints; ++i) {
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001651 struct mgcp_endpoint *endp = trunk->endpoints[i];
Philipp Maier87bd9be2017-08-22 16:35:41 +02001652 llist_for_each_entry(conn, &endp->conns, entry) {
Pau Espin Pedrolca280a12021-07-06 18:15:35 +02001653 if (conn->type == MGCP_CONN_TYPE_RTP &&
Pau Espin Pedrol2ca48822022-10-06 10:58:10 +02001654 conn->mode == MGCP_CONN_RECV_ONLY)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001655 send_dummy(endp, &conn->u.rtp);
1656 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001657 }
1658
Philipp Maiere726d4f2017-11-01 10:41:34 +01001659 /* Schedule the keepalive timer for the next round */
1660 LOGP(DLMGCP, LOGL_DEBUG, "rescheduling trunk %d keepalive timer\n",
Philipp Maier14b27a82020-06-02 20:15:30 +02001661 trunk->trunk_nr);
1662 osmo_timer_schedule(&trunk->keepalive_timer, trunk->keepalive_interval,
Philipp Maier87bd9be2017-08-22 16:35:41 +02001663 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001664}
1665
Philipp Maier14b27a82020-06-02 20:15:30 +02001666void mgcp_trunk_set_keepalive(struct mgcp_trunk *trunk, int interval)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001667{
Philipp Maier14b27a82020-06-02 20:15:30 +02001668 trunk->keepalive_interval = interval;
1669 osmo_timer_setup(&trunk->keepalive_timer, mgcp_keepalive_timer_cb, trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001670
1671 if (interval <= 0)
Philipp Maier14b27a82020-06-02 20:15:30 +02001672 osmo_timer_del(&trunk->keepalive_timer);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001673 else
Philipp Maier14b27a82020-06-02 20:15:30 +02001674 osmo_timer_schedule(&trunk->keepalive_timer,
1675 trunk->keepalive_interval, 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001676}
1677
Philipp Maier38533ba2021-07-29 17:38:34 +02001678/* Free config, this function is automatically called by talloc_free when the configuration is freed. */
1679static int config_free_talloc_destructor(struct mgcp_config *cfg)
1680{
1681 mgcp_ratectr_global_free(cfg);
1682 return 0;
1683}
1684
Philipp Maier87bd9be2017-08-22 16:35:41 +02001685/*! allocate configuration with default values.
1686 * (called once at startup by main function) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001687struct mgcp_config *mgcp_config_alloc(void)
1688{
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001689 /* FIXME: This is unrelated to the protocol, put this in some
1690 * appropiate place! */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001691 struct mgcp_config *cfg;
1692
1693 cfg = talloc_zero(NULL, struct mgcp_config);
1694 if (!cfg) {
1695 LOGP(DLMGCP, LOGL_FATAL, "Failed to allocate config.\n");
1696 return NULL;
1697 }
1698
Philipp Maier12943ea2018-01-17 15:40:25 +01001699 osmo_strlcpy(cfg->domain, "mgw", sizeof(cfg->domain));
1700
Eric55fdfc22021-08-13 00:14:18 +02001701 cfg->net_ports.lock = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001702 cfg->net_ports.range_start = RTP_PORT_DEFAULT_RANGE_START;
1703 cfg->net_ports.range_end = RTP_PORT_DEFAULT_RANGE_END;
1704 cfg->net_ports.last_port = cfg->net_ports.range_start;
1705
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001706 cfg->source_port = 2427;
Eric2764bdb2021-08-23 22:11:47 +02001707 osmo_strlcpy(cfg->source_addr, "0.0.0.0", sizeof(cfg->source_addr));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001708
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001709 cfg->rtp_processing_cb = &mgcp_rtp_processing_default;
1710 cfg->setup_rtp_processing_cb = &mgcp_setup_rtp_processing_default;
1711
Philipp Maierd19de2e2020-06-03 13:55:33 +02001712 INIT_LLIST_HEAD(&cfg->trunks);
1713
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001714 /* Allocate virtual trunk */
Philipp Maierd19de2e2020-06-03 13:55:33 +02001715 if (!mgcp_trunk_alloc(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID)) {
Harald Welte3ac604e2019-05-08 14:07:41 +02001716 talloc_free(cfg);
1717 return NULL;
1718 }
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001719
Philipp Maiera065e632021-07-09 13:22:42 +02001720 mgcp_ratectr_global_alloc(cfg);
Philipp Maier38533ba2021-07-29 17:38:34 +02001721 talloc_set_destructor(cfg, config_free_talloc_destructor);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001722
1723 return cfg;
1724}
1725
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001726static int send_agent(struct mgcp_config *cfg, const char *buf, int len)
1727{
1728 return write(cfg->gw_fd.bfd.fd, buf, len);
1729}
1730
Philipp Maier87bd9be2017-08-22 16:35:41 +02001731/*! Reset all endpoints by sending RSIP message to self.
1732 * (called by VTY)
1733 * \param[in] endp trunk endpoint
1734 * \param[in] endpoint number
1735 * \returns 0 on success, -1 on error */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001736int mgcp_send_reset_all(struct mgcp_config *cfg)
1737{
Philipp Maier12943ea2018-01-17 15:40:25 +01001738 char buf[MGCP_ENDPOINT_MAXLEN + 128];
1739 int len;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001740 int rc;
1741
Philipp Maier12943ea2018-01-17 15:40:25 +01001742 len = snprintf(buf, sizeof(buf),
1743 "RSIP 1 *@%s MGCP 1.0\r\n", cfg->domain);
1744 if (len < 0)
1745 return -1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001746
Philipp Maier12943ea2018-01-17 15:40:25 +01001747 rc = send_agent(cfg, buf, len);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001748 if (rc <= 0)
1749 return -1;
1750
1751 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001752}
1753
Philipp Maier87bd9be2017-08-22 16:35:41 +02001754/*! Reset a single endpoint by sending RSIP message to self.
1755 * (called by VTY)
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001756 * \param[in] endp to reset
Philipp Maier87bd9be2017-08-22 16:35:41 +02001757 * \returns 0 on success, -1 on error */
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001758int mgcp_send_reset_ep(struct mgcp_endpoint *endp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001759{
Philipp Maier12943ea2018-01-17 15:40:25 +01001760 char buf[MGCP_ENDPOINT_MAXLEN + 128];
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001761 int len;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001762 int rc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001763
1764 len = snprintf(buf, sizeof(buf),
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001765 "RSIP 39 %s MGCP 1.0\r\n", endp->name);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001766 if (len < 0)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001767 return -1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001768
Ericfbf78d12021-08-23 22:31:39 +02001769 rc = send_agent(endp->trunk->cfg, buf, len);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001770 if (rc <= 0)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001771 return -1;
1772
Philipp Maier87bd9be2017-08-22 16:35:41 +02001773 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001774}