blob: 80e0f8a406746949d36f2fa62c6b731124935d36 [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
675 * codec only. */
676 if (sscanf(lco_id + 1, ":%16[^,]", codec) == 1) {
677 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 */
970 if (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 Maier14b27a82020-06-02 20:15:30 +02001079 conn->end.fmtp_extra = talloc_strdup(trunk->endpoints,
1080 trunk->audio_fmtp_extra);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001081
Philipp Maier8dc35972021-07-14 11:20:16 +02001082 if (pdata->cfg->force_ptime) {
1083 conn->end.packet_duration_ms = pdata->cfg->force_ptime;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001084 conn->end.force_output_ptime = 1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001085 }
1086
Philipp Maier1cb1e382017-11-02 17:16:04 +01001087 mgcp_rtp_end_config(endp, 0, &conn->end);
1088
Philipp Maierc3cc6542018-02-02 12:58:42 +01001089 /* check connection mode setting */
1090 if (conn->conn->mode != MGCP_CONN_LOOPBACK
1091 && conn->conn->mode != MGCP_CONN_RECV_ONLY
Pau Espin Pedrol5ffd1272022-10-04 13:45:48 +02001092 && osmo_sockaddr_port(&conn->end.addr.u.sa) == 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001093 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
1094 "CRCX: selected connection mode type requires an opposite end!\n");
Philipp Maierc3cc6542018-02-02 12:58:42 +01001095 error_code = 527;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001096 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC));
Philipp Maierc3cc6542018-02-02 12:58:42 +01001097 goto error2;
1098 }
1099
Pau Espin Pedrol71d42e72020-09-03 14:20:07 +02001100 /* Find a local address for conn based on policy and initial SDP remote
1101 information, then find a free port for it */
Pau Espin Pedrol70c03f52022-10-04 16:49:41 +02001102 if (mgcp_get_local_addr(conn->end.local_addr, conn) < 0) {
1103 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
1104 goto error2;
1105 }
Philipp Maier1cb1e382017-11-02 17:16:04 +01001106 if (allocate_port(endp, conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001107 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
Philipp Maier1cb1e382017-11-02 17:16:04 +01001108 goto error2;
1109 }
1110
Philipp Maier87bd9be2017-08-22 16:35:41 +02001111 if (setup_rtp_processing(endp, conn) != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001112 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
1113 "CRCX: could not start RTP processing!\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001114 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_START_RTP));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001115 goto error2;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001116 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001117
Pau Espin Pedrol833281d2022-10-06 17:41:22 +02001118 /* Notify Osmux conn that CRCX was received */
1119 if (mgcp_conn_rtp_is_osmux(conn)) {
1120 if (conn_osmux_event_rx_crcx_mdcx(conn) < 0) {
1121 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR, "CRCX: Osmux handling failed!\n");
1122 goto error2;
1123 }
1124 }
1125
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001126 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
1127 "CRCX: Creating connection: port: %u\n", conn->end.local_port);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001128
Philipp Maiere726d4f2017-11-01 10:41:34 +01001129 /* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
Philipp Maier14b27a82020-06-02 20:15:30 +02001130 OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Pau Espin Pedrolca280a12021-07-06 18:15:35 +02001131 if (conn->conn->mode & MGCP_CONN_RECV_ONLY &&
Pau Espin Pedrolca280a12021-07-06 18:15:35 +02001132 trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001133 send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001134
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001135 LOGPCONN(_conn, DLMGCP, LOGL_NOTICE,
1136 "CRCX: connection successfully created\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001137 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_SUCCESS));
Philipp Maier889fe7f2020-07-06 17:44:12 +02001138 mgcp_endp_update(endp);
Philipp Maier41d59202021-07-20 15:49:00 +02001139
1140 /* NOTE: Only in the virtual trunk we allow dynamic endpoint names */
1141 bool add_epname = rq->wildcarded && trunk->trunk_type == MGCP_TRUNK_VIRTUAL;
1142 return create_response_with_sdp(endp, conn, "CRCX", pdata->trans, add_epname, true);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001143error2:
Philipp Maier1355d7e2018-02-01 14:30:06 +01001144 mgcp_endp_release(endp);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001145 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1146 "CRCX: unable to create connection\n");
Ericfbcf4a62021-09-09 18:02:31 +02001147 return create_err_response(endp, endp, error_code, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001148}
1149
Philipp Maier87bd9be2017-08-22 16:35:41 +02001150/* MDCX command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +02001151static struct msgb *handle_modify_con(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001152{
Philipp Maier8dc35972021-07-14 11:20:16 +02001153 struct mgcp_parse_data *pdata = rq->pdata;
1154 struct mgcp_trunk *trunk = rq->trunk;
1155 struct mgcp_endpoint *endp = rq->endp;
Pau Espin Pedrolaf0f58f2023-06-14 12:21:26 +02001156 struct rate_ctr_group *rate_ctrs;
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001157 char new_local_addr[INET6_ADDRSTRLEN];
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001158 int error_code = 500;
1159 int silent = 0;
1160 int have_sdp = 0;
1161 char *line;
1162 const char *local_options = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001163 const char *mode = NULL;
1164 struct mgcp_conn_rtp *conn = NULL;
Philipp Maier01d24a32017-11-21 17:26:09 +01001165 const char *conn_id = NULL;
Pau Espin Pedrol21779192022-09-23 16:46:33 +02001166 int remote_osmux_cid = -2;
Philipp Maiera390d0b2018-01-31 17:30:19 +01001167 int rc;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001168
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001169 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "MDCX: modifying existing connection ...\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001170
Pau Espin Pedrolaf0f58f2023-06-14 12:21:26 +02001171 if (rq->null_endp) {
1172 /* trunk not available so rate_ctr aren't available either. */
1173 LOGP(DLMGCP, LOGL_ERROR, "MDCX: Not allowed in 'null' endpoint!\n");
1174 return create_err_response(pdata->cfg, NULL, 502, "MDCX", pdata->trans);
1175 }
1176
1177 rate_ctrs = trunk->ratectr.mgcp_mdcx_ctr_group;
1178
Philipp Maier5656fbf2018-02-02 14:41:58 +01001179 /* Prohibit wildcarded requests */
Philipp Maier8dc35972021-07-14 11:20:16 +02001180 if (rq->wildcarded) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001181 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1182 "MDCX: wildcarded endpoint names not supported.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001183 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_WILDCARD));
Ericfbcf4a62021-09-09 18:02:31 +02001184 return create_err_response(rq->trunk, endp, 507, "MDCX", pdata->trans);
Philipp Maier5656fbf2018-02-02 14:41:58 +01001185 }
1186
Ericee6958c2021-09-14 18:36:53 +02001187 if (!endp || !mgcp_endp_avail(endp)) {
1188 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_AVAIL));
1189 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "MDCX: selected endpoint not available!\n");
1190 return create_err_response(rq->trunk, NULL, 501, "MDCX", pdata->trans);
1191 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001192 if (llist_count(&endp->conns) <= 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001193 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1194 "MDCX: endpoint is not holding a connection.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001195 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_CONN));
Ericfbcf4a62021-09-09 18:02:31 +02001196 return create_err_response(endp, endp, 400, "MDCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001197 }
1198
Philipp Maier8dc35972021-07-14 11:20:16 +02001199 for_each_line(line, pdata->save) {
Philipp Maier036612b2021-07-19 17:47:49 +02001200 if (!mgcp_check_param(endp, trunk, line))
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001201 continue;
1202
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001203 switch (toupper(line[0])) {
Philipp Maier87bd9be2017-08-22 16:35:41 +02001204 case 'C':
Harald Weltee35eeae2017-12-28 13:47:37 +01001205 if (mgcp_verify_call_id(endp, line + 3) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001206 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CALLID));
Harald Weltee35eeae2017-12-28 13:47:37 +01001207 error_code = 516;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001208 goto error3;
Harald Weltee35eeae2017-12-28 13:47:37 +01001209 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001210 break;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001211 case 'I':
Philipp Maier01d24a32017-11-21 17:26:09 +01001212 conn_id = (const char *)line + 3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001213 if ((error_code = mgcp_verify_ci(endp, conn_id))) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001214 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CONNID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001215 goto error3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001216 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001217 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001218 case 'L':
Philipp Maier87bd9be2017-08-22 16:35:41 +02001219 local_options = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001220 break;
1221 case 'M':
Philipp Maier87bd9be2017-08-22 16:35:41 +02001222 mode = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001223 break;
1224 case 'Z':
Pau Espin Pedrol9b508f62019-06-26 13:11:22 +02001225 silent = strcasecmp("noanswer", line + 3) == 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001226 break;
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001227 case 'X':
Pau Espin Pedrolc1bf4692019-05-14 16:23:24 +02001228 if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001229 /* If osmux is disabled, just skip setting it up */
Pau Espin Pedrol36413c02022-10-12 17:58:17 +02001230 if (endp->trunk->cfg->osmux.usage == OSMUX_USAGE_OFF)
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001231 break;
Pau Espin Pedrol21779192022-09-23 16:46:33 +02001232 remote_osmux_cid = mgcp_osmux_setup(endp, line);
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001233 break;
1234 }
1235 /* Ignore unknown X-headers */
1236 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001237 case '\0':
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001238 have_sdp = 1;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001239 goto mgcp_header_done;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001240 break;
1241 default:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001242 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1243 "MDCX: Unhandled MGCP option: '%c'/%d\n",
1244 line[0], line[0]);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001245 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_UNHANDLED_PARAM));
Ericfbcf4a62021-09-09 18:02:31 +02001246 return create_err_response(rq->trunk, NULL, 539, "MDCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001247 break;
1248 }
1249 }
1250
Philipp Maier87bd9be2017-08-22 16:35:41 +02001251mgcp_header_done:
Philipp Maier01d24a32017-11-21 17:26:09 +01001252 if (!conn_id) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001253 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1254 "MDCX: insufficient parameters, missing ci (connectionIdentifier)\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001255 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_CONNID));
Ericfbcf4a62021-09-09 18:02:31 +02001256 return create_err_response(endp, endp, 515, "MDCX", pdata->trans);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001257 }
1258
1259 conn = mgcp_conn_get_rtp(endp, conn_id);
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001260 if (!conn) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001261 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_CONN_NOT_FOUND));
Ericfbcf4a62021-09-09 18:02:31 +02001262 return create_err_response(endp, endp, 400, "MDCX", pdata->trans);
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001263 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001264
Oliver Smithe36b7752019-01-22 16:31:36 +01001265 mgcp_conn_watchdog_kick(conn->conn);
1266
Philipp Maier87bd9be2017-08-22 16:35:41 +02001267 if (mode) {
1268 if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001269 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_MODE));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001270 error_code = 517;
1271 goto error3;
1272 }
1273 } else
Pau Espin Pedrol209eb9f2019-04-24 12:03:04 +02001274 conn->conn->mode = conn->conn->mode_orig;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001275
Philipp Maierbc0346e2018-06-07 09:52:16 +02001276 /* Set local connection options, if present */
1277 if (local_options) {
Philipp Maier8dc35972021-07-14 11:20:16 +02001278 rc = set_local_cx_options(trunk->endpoints,
Philipp Maierbc0346e2018-06-07 09:52:16 +02001279 &endp->local_options, local_options);
1280 if (rc != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001281 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1282 "MDCX: invalid local connection options!\n");
Philipp Maierbc0346e2018-06-07 09:52:16 +02001283 error_code = rc;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001284 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS));
Philipp Maierbc0346e2018-06-07 09:52:16 +02001285 goto error3;
1286 }
1287 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001288
Philipp Maierbc0346e2018-06-07 09:52:16 +02001289 /* Handle codec information and decide for a suitable codec */
Philipp Maier8dc35972021-07-14 11:20:16 +02001290 rc = handle_codec_info(conn, rq, have_sdp, false);
Philipp Maierbc0346e2018-06-07 09:52:16 +02001291 mgcp_codec_summary(conn);
1292 if (rc) {
Philipp Maieraf07f662018-02-02 11:34:02 +01001293 error_code = rc;
1294 goto error3;
Philipp Maiera390d0b2018-01-31 17:30:19 +01001295 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001296
Philipp Maierc3cc6542018-02-02 12:58:42 +01001297 /* check connection mode setting */
1298 if (conn->conn->mode != MGCP_CONN_LOOPBACK
1299 && conn->conn->mode != MGCP_CONN_RECV_ONLY
Pau Espin Pedrold6769ea2021-07-06 19:44:27 +02001300 && !mgcp_rtp_end_remote_addr_available(&conn->end)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001301 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1302 "MDCX: selected connection mode type requires an opposite end!\n");
Philipp Maierc3cc6542018-02-02 12:58:42 +01001303 error_code = 527;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001304 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC));
Philipp Maierc3cc6542018-02-02 12:58:42 +01001305 goto error3;
1306 }
1307
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001308 if (mgcp_conn_rtp_is_osmux(conn)) {
Pau Espin Pedrol21779192022-09-23 16:46:33 +02001309 OSMO_ASSERT(conn->osmux.local_cid_allocated);
1310 if (remote_osmux_cid < -1) {
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001311 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1312 "MDCX: Failed to parse Osmux CID!\n");
1313 goto error3;
Pau Espin Pedrol21779192022-09-23 16:46:33 +02001314 } else if (remote_osmux_cid == -1) {
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001315 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1316 "MDCX: wilcard in MDCX is not supported!\n");
1317 goto error3;
Pau Espin Pedrol21779192022-09-23 16:46:33 +02001318 } else if (conn->osmux.remote_cid_present &&
Pau Espin Pedrol833281d2022-10-06 17:41:22 +02001319 remote_osmux_cid != conn->osmux.remote_cid) {
1320 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1321 "MDCX: changing already allocated CID is not supported!\n");
1322 goto error3;
1323 } else {
1324 conn->osmux.remote_cid_present = true;
1325 conn->osmux.remote_cid = remote_osmux_cid;
1326 if (conn_osmux_event_rx_crcx_mdcx(conn) < 0) {
1327 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1328 "MDCX: Osmux handling failed!\n");
1329 goto error3;
1330 }
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001331 }
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001332 }
Philipp Maierbc0346e2018-06-07 09:52:16 +02001333
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001334 /* MDCX may have provided a new remote address, which means we may need
1335 to update our announced IP addr and re-bind our local end. This can
1336 happen for instance if MGW initially provided an IPv4 during CRCX
1337 ACK, and now MDCX tells us the remote has an IPv6 address. */
Pau Espin Pedrol70c03f52022-10-04 16:49:41 +02001338 if (mgcp_get_local_addr(new_local_addr, conn) < 0) {
1339 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
1340 goto error3;
1341 }
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001342 if (strcmp(new_local_addr, conn->end.local_addr)) {
1343 osmo_strlcpy(conn->end.local_addr, new_local_addr, sizeof(conn->end.local_addr));
1344 mgcp_free_rtp_port(&conn->end);
1345 if (allocate_port(endp, conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001346 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001347 goto error3;
1348 }
1349 }
1350
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001351 if (setup_rtp_processing(endp, conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001352 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_START_RTP));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001353 goto error3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001354 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001355
Philipp Maier87bd9be2017-08-22 16:35:41 +02001356 mgcp_rtp_end_config(endp, 1, &conn->end);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001357
1358 /* modify */
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001359 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
1360 "MDCX: modified conn:%s\n", mgcp_conn_dump(conn->conn));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001361
Philipp Maiere726d4f2017-11-01 10:41:34 +01001362 /* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
Philipp Maier8dc35972021-07-14 11:20:16 +02001363 OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Pau Espin Pedrolca280a12021-07-06 18:15:35 +02001364 if (conn->conn->mode & MGCP_CONN_RECV_ONLY &&
Philipp Maier8dc35972021-07-14 11:20:16 +02001365 trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001366 send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001367
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001368 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_SUCCESS));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001369 if (silent)
1370 goto out_silent;
1371
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001372 LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
1373 "MDCX: connection successfully modified\n");
Philipp Maier889fe7f2020-07-06 17:44:12 +02001374 mgcp_endp_update(endp);
Philipp Maier41d59202021-07-20 15:49:00 +02001375 return create_response_with_sdp(endp, conn, "MDCX", pdata->trans, false, false);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001376error3:
Ericfbcf4a62021-09-09 18:02:31 +02001377 return create_err_response(endp, endp, error_code, "MDCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001378
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001379out_silent:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001380 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "MDCX: silent exit\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001381 return NULL;
1382}
1383
Philipp Maier87bd9be2017-08-22 16:35:41 +02001384/* DLCX command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +02001385static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001386{
Philipp Maier8dc35972021-07-14 11:20:16 +02001387 struct mgcp_parse_data *pdata = rq->pdata;
1388 struct mgcp_trunk *trunk = rq->trunk;
1389 struct mgcp_endpoint *endp = rq->endp;
Pau Espin Pedrolaf0f58f2023-06-14 12:21:26 +02001390 struct rate_ctr_group *rate_ctrs;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001391 int error_code = 400;
1392 int silent = 0;
1393 char *line;
1394 char stats[1048];
Philipp Maier01d24a32017-11-21 17:26:09 +01001395 const char *conn_id = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001396 struct mgcp_conn_rtp *conn = NULL;
Philipp Maierf486e742021-07-19 14:56:29 +02001397 unsigned int i;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001398
Philipp Maierf486e742021-07-19 14:56:29 +02001399 /* NOTE: In this handler we can not take it for granted that the endp
Pau Espin Pedrolaf0f58f2023-06-14 12:21:26 +02001400 * pointer will be populated, however a trunk is always guaranteed (except for 'null' endp).
1401 */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001402
Philipp Maierf486e742021-07-19 14:56:29 +02001403 LOGPEPTR(endp, trunk, DLMGCP, LOGL_NOTICE, "DLCX: deleting connection(s) ...\n");
1404
Pau Espin Pedrolaf0f58f2023-06-14 12:21:26 +02001405 if (rq->null_endp) {
1406 /* trunk not available so rate_ctr aren't available either. */
1407 LOGP(DLMGCP, LOGL_ERROR, "DLCX: Not allowed in 'null' endpoint!\n");
1408 return create_err_response(pdata->cfg, NULL, 502, "DLCX", pdata->trans);
1409 }
1410
1411 rate_ctrs = trunk->ratectr.mgcp_dlcx_ctr_group;
Philipp Maierf486e742021-07-19 14:56:29 +02001412 if (endp && !mgcp_endp_avail(endp)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001413 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_AVAIL));
Philipp Maiera910a812020-08-18 15:13:33 +02001414 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1415 "DLCX: selected endpoint not available!\n");
Ericfbcf4a62021-09-09 18:02:31 +02001416 return create_err_response(rq->trunk, NULL, 501, "DLCX", pdata->trans);
Philipp Maier8d6a1932020-06-18 12:19:31 +02001417 }
1418
Philipp Maierf486e742021-07-19 14:56:29 +02001419 if (endp && !rq->wildcarded && llist_empty(&endp->conns)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001420 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1421 "DLCX: endpoint is not holding a connection.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001422 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_NO_CONN));
Ericfbcf4a62021-09-09 18:02:31 +02001423 return create_err_response(endp, endp, 515, "DLCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001424 }
1425
Ericee6958c2021-09-14 18:36:53 +02001426 /* Handle wildcarded DLCX that refers to the whole trunk. This means
1427 * that we walk over all endpoints on the trunk in order to drop all
1428 * connections on the trunk. (see also RFC3435 Annex F.7) */
1429 if (rq->wildcarded) {
1430 int num_conns = 0;
1431 for (i = 0; i < trunk->number_endpoints; i++) {
1432 num_conns += llist_count(&trunk->endpoints[i]->conns);
1433 mgcp_endp_release(trunk->endpoints[i]);
1434 }
1435 rate_ctr_add(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS), num_conns);
1436 return create_ok_response(trunk, NULL, 200, "DLCX", pdata->trans);
1437 }
1438
Philipp Maier8dc35972021-07-14 11:20:16 +02001439 for_each_line(line, pdata->save) {
Philipp Maier036612b2021-07-19 17:47:49 +02001440 if (!mgcp_check_param(endp, trunk, line))
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001441 continue;
1442
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001443 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001444 case 'C':
Philipp Maierf486e742021-07-19 14:56:29 +02001445 /* If we have no endpoint, but a call id in the request,
1446 then this request cannot be handled */
1447 if (!endp) {
1448 LOGPTRUNK(trunk, DLMGCP, LOGL_NOTICE,
1449 "cannot handle requests with call-id (C) without endpoint -- abort!");
1450 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));
Ericfbcf4a62021-09-09 18:02:31 +02001451 return create_err_response(rq->trunk, NULL, 539, "DLCX", pdata->trans);
Philipp Maierf486e742021-07-19 14:56:29 +02001452 }
1453
Harald Weltee35eeae2017-12-28 13:47:37 +01001454 if (mgcp_verify_call_id(endp, line + 3) != 0) {
1455 error_code = 516;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001456 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CALLID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001457 goto error3;
Harald Weltee35eeae2017-12-28 13:47:37 +01001458 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001459 break;
1460 case 'I':
Philipp Maierf486e742021-07-19 14:56:29 +02001461 /* If we have no endpoint, but a connection id in the request,
1462 then this request cannot be handled */
1463 if (!endp) {
1464 LOGPTRUNK(trunk, DLMGCP, LOGL_NOTICE,
1465 "cannot handle requests with conn-id (I) without endpoint -- abort!");
1466 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));
Ericfbcf4a62021-09-09 18:02:31 +02001467 return create_err_response(rq->trunk, NULL, 539, "DLCX", pdata->trans);
Philipp Maierf486e742021-07-19 14:56:29 +02001468 }
1469
Philipp Maier01d24a32017-11-21 17:26:09 +01001470 conn_id = (const char *)line + 3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001471 if ((error_code = mgcp_verify_ci(endp, conn_id))) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001472 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CONNID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001473 goto error3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001474 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001475 break;
1476 case 'Z':
Pau Espin Pedrol9b508f62019-06-26 13:11:22 +02001477 silent = strcasecmp("noanswer", line + 3) == 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001478 break;
1479 default:
Philipp Maierf486e742021-07-19 14:56:29 +02001480 LOGPEPTR(endp, trunk, DLMGCP, LOGL_NOTICE, "DLCX: Unhandled MGCP option: '%c'/%d\n",
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001481 line[0], line[0]);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001482 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));
Ericfbcf4a62021-09-09 18:02:31 +02001483 return create_err_response(rq->trunk, NULL, 539, "DLCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001484 break;
1485 }
1486 }
1487
Philipp Maierce187052021-07-23 10:58:19 +02001488 /* The logic does not permit to go past this point without having the
1489 * the endp pointer populated. */
1490 OSMO_ASSERT(endp);
1491
Philipp Maierf4c0e372017-10-11 16:06:45 +02001492 /* When no connection id is supplied, we will interpret this as a
Philipp Maierf486e742021-07-19 14:56:29 +02001493 * wildcarded DLCX that refers to the selected endpoint. This means
1494 * that we drop all connections on that specific endpoint at once.
1495 * (See also RFC3435 Section F.7) */
Philipp Maier01d24a32017-11-21 17:26:09 +01001496 if (!conn_id) {
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001497 int num_conns = llist_count(&endp->conns);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001498 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1499 "DLCX: missing ci (connectionIdentifier), will remove all connections (%d total) at once\n",
1500 num_conns);
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001501
1502 if (num_conns > 0)
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001503 rate_ctr_add(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS), num_conns);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001504
Philipp Maier1355d7e2018-02-01 14:30:06 +01001505 mgcp_endp_release(endp);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001506
1507 /* Note: In this case we do not return any statistics,
1508 * as we assume that the client is not interested in
1509 * this case. */
Ericfbcf4a62021-09-09 18:02:31 +02001510 return create_ok_response(endp, endp, 200, "DLCX", pdata->trans);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001511 }
1512
Philipp Maierf4c0e372017-10-11 16:06:45 +02001513 /* Find the connection */
Philipp Maier87bd9be2017-08-22 16:35:41 +02001514 conn = mgcp_conn_get_rtp(endp, conn_id);
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001515 if (!conn) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001516 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CONNID));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001517 goto error3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001518 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001519 /* save the statistics of the current connection */
1520 mgcp_format_stats(stats, sizeof(stats), conn->conn);
1521
1522 /* delete connection */
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001523 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG, "DLCX: deleting conn:%s\n",
1524 mgcp_conn_dump(conn->conn));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001525 mgcp_conn_free(endp, conn_id);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001526 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1527 "DLCX: connection successfully deleted\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +02001528
1529 /* When all connections are closed, the endpoint will be released
1530 * in order to be ready to be used by another call. */
1531 if (llist_count(&endp->conns) <= 0) {
Philipp Maier1355d7e2018-02-01 14:30:06 +01001532 mgcp_endp_release(endp);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001533 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: endpoint released\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +02001534 }
1535
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001536 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001537 if (silent)
1538 goto out_silent;
Ericfbcf4a62021-09-09 18:02:31 +02001539 return create_ok_resp_with_param(endp, endp, 250, "DLCX", pdata->trans, stats);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001540
1541error3:
Ericfbcf4a62021-09-09 18:02:31 +02001542 return create_err_response(endp, endp, error_code, "DLCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001543
1544out_silent:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001545 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: silent exit\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001546 return NULL;
1547}
1548
Philipp Maier87bd9be2017-08-22 16:35:41 +02001549/* RSIP command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +02001550static struct msgb *handle_rsip(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001551{
Philipp Maier87bd9be2017-08-22 16:35:41 +02001552 /* TODO: Also implement the resetting of a specific endpoint
1553 * to make mgcp_send_reset_ep() work. Currently this will call
1554 * mgcp_rsip_cb() in mgw_main.c, which sets reset_endpoints=1
1555 * to make read_call_agent() reset all endpoints when called
1556 * next time. In order to selectively reset endpoints some
1557 * mechanism to distinguish which endpoint shall be resetted
1558 * is needed */
1559
1560 LOGP(DLMGCP, LOGL_NOTICE, "RSIP: resetting all endpoints ...\n");
1561
Pau Espin Pedrolaf0f58f2023-06-14 12:21:26 +02001562 if (rq->null_endp) {
1563 /* trunk not available so rate_ctr aren't available either. */
1564 LOGP(DLMGCP, LOGL_ERROR, "RSIP: Not allowed in 'null' endpoint!\n");
1565 return create_err_response(rq->pdata->cfg, NULL, 502, "RSIP", rq->pdata->trans);
1566 }
1567
Philipp Maier8dc35972021-07-14 11:20:16 +02001568 if (rq->pdata->cfg->reset_cb)
1569 rq->pdata->cfg->reset_cb(rq->endp->trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001570 return NULL;
1571}
1572
1573static char extract_tone(const char *line)
1574{
1575 const char *str = strstr(line, "D/");
1576 if (!str)
1577 return CHAR_MAX;
1578
1579 return str[2];
1580}
1581
Philipp Maier87bd9be2017-08-22 16:35:41 +02001582/* This can request like DTMF detection and forward, fax detection... it
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001583 * can also request when the notification should be send and such. We don't
Philipp Maier87bd9be2017-08-22 16:35:41 +02001584 * do this right now. */
Philipp Maier8dc35972021-07-14 11:20:16 +02001585static struct msgb *handle_noti_req(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001586{
1587 int res = 0;
1588 char *line;
1589 char tone = CHAR_MAX;
1590
Philipp Maier87bd9be2017-08-22 16:35:41 +02001591 LOGP(DLMGCP, LOGL_NOTICE, "RQNT: processing request for notification ...\n");
1592
Pau Espin Pedrolaf0f58f2023-06-14 12:21:26 +02001593 if (rq->null_endp) {
1594 /* trunk not available so rate_ctr aren't available either. */
1595 LOGP(DLMGCP, LOGL_ERROR, "RQNT: Not allowed in 'null' endpoint!\n");
1596 return create_err_response(rq->pdata->cfg, NULL, 502, "RQNT", rq->pdata->trans);
1597 }
1598
Philipp Maier8dc35972021-07-14 11:20:16 +02001599 for_each_line(line, rq->pdata->save) {
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001600 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001601 case 'S':
1602 tone = extract_tone(line);
1603 break;
1604 }
1605 }
1606
1607 /* we didn't see a signal request with a tone */
1608 if (tone == CHAR_MAX)
Ericfbcf4a62021-09-09 18:02:31 +02001609 return create_ok_response(rq->endp, rq->endp, 200, "RQNT", rq->pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001610
Philipp Maier8dc35972021-07-14 11:20:16 +02001611 if (rq->pdata->cfg->rqnt_cb)
1612 res = rq->pdata->cfg->rqnt_cb(rq->endp, tone);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001613
Ericfbcf4a62021-09-09 18:02:31 +02001614 return res == 0 ? create_ok_response(rq->endp, rq->endp, 200, "RQNT", rq->pdata->trans) :
1615 create_err_response(rq->endp, rq->endp, res, "RQNT", rq->pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001616}
1617
Philipp Maier87bd9be2017-08-22 16:35:41 +02001618/* Connection keepalive timer, will take care that dummy packets are send
Harald Welte1d1b98f2017-12-25 10:03:40 +01001619 * regularly, so that NAT connections stay open */
Philipp Maier14b27a82020-06-02 20:15:30 +02001620static void mgcp_keepalive_timer_cb(void *_trunk)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001621{
Philipp Maier14b27a82020-06-02 20:15:30 +02001622 struct mgcp_trunk *trunk = _trunk;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001623 struct mgcp_conn *conn;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001624 int i;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001625
Philipp Maiere726d4f2017-11-01 10:41:34 +01001626 LOGP(DLMGCP, LOGL_DEBUG, "triggered trunk %d keepalive timer\n",
Philipp Maier14b27a82020-06-02 20:15:30 +02001627 trunk->trunk_nr);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001628
Philipp Maiere726d4f2017-11-01 10:41:34 +01001629 /* Do not accept invalid configuration values
1630 * valid is MGCP_KEEPALIVE_NEVER, MGCP_KEEPALIVE_ONCE and
1631 * values greater 0 */
Philipp Maier14b27a82020-06-02 20:15:30 +02001632 OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Philipp Maiere726d4f2017-11-01 10:41:34 +01001633
1634 /* The dummy packet functionality has been disabled, we will exit
1635 * immediately, no further timer is scheduled, which means we will no
1636 * longer send dummy packets even when we did before */
Philipp Maier14b27a82020-06-02 20:15:30 +02001637 if (trunk->keepalive_interval == MGCP_KEEPALIVE_NEVER)
Philipp Maiere726d4f2017-11-01 10:41:34 +01001638 return;
1639
1640 /* In cases where only one dummy packet is sent, we do not need
1641 * the timer since the functions that handle the CRCX and MDCX are
1642 * triggering the sending of the dummy packet. So we behave like in
1643 * the MGCP_KEEPALIVE_NEVER case */
Philipp Maier14b27a82020-06-02 20:15:30 +02001644 if (trunk->keepalive_interval == MGCP_KEEPALIVE_ONCE)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001645 return;
1646
Philipp Maier87bd9be2017-08-22 16:35:41 +02001647 /* Send walk over all endpoints and send out dummy packets through
1648 * every connection present on each endpoint */
Philipp Maier4131a652021-07-07 14:04:34 +02001649 for (i = 0; i < trunk->number_endpoints; ++i) {
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001650 struct mgcp_endpoint *endp = trunk->endpoints[i];
Philipp Maier87bd9be2017-08-22 16:35:41 +02001651 llist_for_each_entry(conn, &endp->conns, entry) {
Pau Espin Pedrolca280a12021-07-06 18:15:35 +02001652 if (conn->type == MGCP_CONN_TYPE_RTP &&
Pau Espin Pedrol2ca48822022-10-06 10:58:10 +02001653 conn->mode == MGCP_CONN_RECV_ONLY)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001654 send_dummy(endp, &conn->u.rtp);
1655 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001656 }
1657
Philipp Maiere726d4f2017-11-01 10:41:34 +01001658 /* Schedule the keepalive timer for the next round */
1659 LOGP(DLMGCP, LOGL_DEBUG, "rescheduling trunk %d keepalive timer\n",
Philipp Maier14b27a82020-06-02 20:15:30 +02001660 trunk->trunk_nr);
1661 osmo_timer_schedule(&trunk->keepalive_timer, trunk->keepalive_interval,
Philipp Maier87bd9be2017-08-22 16:35:41 +02001662 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001663}
1664
Philipp Maier14b27a82020-06-02 20:15:30 +02001665void mgcp_trunk_set_keepalive(struct mgcp_trunk *trunk, int interval)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001666{
Philipp Maier14b27a82020-06-02 20:15:30 +02001667 trunk->keepalive_interval = interval;
1668 osmo_timer_setup(&trunk->keepalive_timer, mgcp_keepalive_timer_cb, trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001669
1670 if (interval <= 0)
Philipp Maier14b27a82020-06-02 20:15:30 +02001671 osmo_timer_del(&trunk->keepalive_timer);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001672 else
Philipp Maier14b27a82020-06-02 20:15:30 +02001673 osmo_timer_schedule(&trunk->keepalive_timer,
1674 trunk->keepalive_interval, 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001675}
1676
Philipp Maier38533ba2021-07-29 17:38:34 +02001677/* Free config, this function is automatically called by talloc_free when the configuration is freed. */
1678static int config_free_talloc_destructor(struct mgcp_config *cfg)
1679{
1680 mgcp_ratectr_global_free(cfg);
1681 return 0;
1682}
1683
Philipp Maier87bd9be2017-08-22 16:35:41 +02001684/*! allocate configuration with default values.
1685 * (called once at startup by main function) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001686struct mgcp_config *mgcp_config_alloc(void)
1687{
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001688 /* FIXME: This is unrelated to the protocol, put this in some
1689 * appropiate place! */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001690 struct mgcp_config *cfg;
1691
1692 cfg = talloc_zero(NULL, struct mgcp_config);
1693 if (!cfg) {
1694 LOGP(DLMGCP, LOGL_FATAL, "Failed to allocate config.\n");
1695 return NULL;
1696 }
1697
Philipp Maier12943ea2018-01-17 15:40:25 +01001698 osmo_strlcpy(cfg->domain, "mgw", sizeof(cfg->domain));
1699
Eric55fdfc22021-08-13 00:14:18 +02001700 cfg->net_ports.lock = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001701 cfg->net_ports.range_start = RTP_PORT_DEFAULT_RANGE_START;
1702 cfg->net_ports.range_end = RTP_PORT_DEFAULT_RANGE_END;
1703 cfg->net_ports.last_port = cfg->net_ports.range_start;
1704
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001705 cfg->source_port = 2427;
Eric2764bdb2021-08-23 22:11:47 +02001706 osmo_strlcpy(cfg->source_addr, "0.0.0.0", sizeof(cfg->source_addr));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001707
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001708 cfg->rtp_processing_cb = &mgcp_rtp_processing_default;
1709 cfg->setup_rtp_processing_cb = &mgcp_setup_rtp_processing_default;
1710
1711 cfg->get_net_downlink_format_cb = &mgcp_get_net_downlink_format_default;
1712
Philipp Maierd19de2e2020-06-03 13:55:33 +02001713 INIT_LLIST_HEAD(&cfg->trunks);
1714
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001715 /* Allocate virtual trunk */
Philipp Maierd19de2e2020-06-03 13:55:33 +02001716 if (!mgcp_trunk_alloc(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID)) {
Harald Welte3ac604e2019-05-08 14:07:41 +02001717 talloc_free(cfg);
1718 return NULL;
1719 }
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001720
Philipp Maiera065e632021-07-09 13:22:42 +02001721 mgcp_ratectr_global_alloc(cfg);
Philipp Maier38533ba2021-07-29 17:38:34 +02001722 talloc_set_destructor(cfg, config_free_talloc_destructor);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001723
1724 return cfg;
1725}
1726
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001727static int send_agent(struct mgcp_config *cfg, const char *buf, int len)
1728{
1729 return write(cfg->gw_fd.bfd.fd, buf, len);
1730}
1731
Philipp Maier87bd9be2017-08-22 16:35:41 +02001732/*! Reset all endpoints by sending RSIP message to self.
1733 * (called by VTY)
1734 * \param[in] endp trunk endpoint
1735 * \param[in] endpoint number
1736 * \returns 0 on success, -1 on error */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001737int mgcp_send_reset_all(struct mgcp_config *cfg)
1738{
Philipp Maier12943ea2018-01-17 15:40:25 +01001739 char buf[MGCP_ENDPOINT_MAXLEN + 128];
1740 int len;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001741 int rc;
1742
Philipp Maier12943ea2018-01-17 15:40:25 +01001743 len = snprintf(buf, sizeof(buf),
1744 "RSIP 1 *@%s MGCP 1.0\r\n", cfg->domain);
1745 if (len < 0)
1746 return -1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001747
Philipp Maier12943ea2018-01-17 15:40:25 +01001748 rc = send_agent(cfg, buf, len);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001749 if (rc <= 0)
1750 return -1;
1751
1752 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001753}
1754
Philipp Maier87bd9be2017-08-22 16:35:41 +02001755/*! Reset a single endpoint by sending RSIP message to self.
1756 * (called by VTY)
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001757 * \param[in] endp to reset
Philipp Maier87bd9be2017-08-22 16:35:41 +02001758 * \returns 0 on success, -1 on error */
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001759int mgcp_send_reset_ep(struct mgcp_endpoint *endp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001760{
Philipp Maier12943ea2018-01-17 15:40:25 +01001761 char buf[MGCP_ENDPOINT_MAXLEN + 128];
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001762 int len;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001763 int rc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001764
1765 len = snprintf(buf, sizeof(buf),
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001766 "RSIP 39 %s MGCP 1.0\r\n", endp->name);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001767 if (len < 0)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001768 return -1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001769
Ericfbf78d12021-08-23 22:31:39 +02001770 rc = send_agent(endp->trunk->cfg, buf, len);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001771 if (rc <= 0)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001772 return -1;
1773
Philipp Maier87bd9be2017-08-22 16:35:41 +02001774 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001775}