blob: 46e1d6bee3397183f89db1c835bd17f7c8b37510 [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>
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020050
Philipp Maier39889e42021-08-04 17:42:57 +020051/* Contains the last successfully resolved endpoint name. This variable is used
52 * for the unit-tests to verify that the endpoint was correctly resolved. */
53static char debug_last_endpoint_name[MGCP_ENDPOINT_MAXLEN];
54
55/* Called from unit-tests only */
56char *mgcp_debug_get_last_endpoint_name(void)
57{
58 return debug_last_endpoint_name;
59}
60
Philipp Maierf486e742021-07-19 14:56:29 +020061/* A combination of LOGPENDP and LOGPTRUNK that automatically falls back to
62 * LOGPTRUNK when the endp parameter is NULL */
63#define LOGPEPTR(endp, trunk, cat, level, fmt, args...) \
64do { \
65 if (endp) \
66 LOGPENDP(endp, cat, level, fmt, ## args); \
67 else \
68 LOGPTRUNK(trunk, cat, level, fmt, ## args); \
69} while (0)
70
Philipp Maier8dc35972021-07-14 11:20:16 +020071/* Request data passed to the request handler */
72struct mgcp_request_data {
73 /* request name (e.g. "MDCX") */
74 char name[4+1];
75
76 /* parsing results from the MGCP header (trans id, endpoint name ...) */
77 struct mgcp_parse_data *pdata;
78
79 /* pointer to endpoint resource (may be NULL for wildcarded requests) */
80 struct mgcp_endpoint *endp;
81
82 /* pointer to trunk resource */
83 struct mgcp_trunk *trunk;
84
85 /* set to true when the request has been classified as wildcarded */
86 bool wildcarded;
87
88 /* contains cause code in case of problems during endp/trunk resolution */
89 int mgcp_cause;
90};
91
Philipp Maier33d97f72021-07-14 14:53:45 +020092/* Request handler specification, here we specify an array with function
93 * pointers to the various MGCP requests implemented below */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020094struct mgcp_request {
Philipp Maier33d97f72021-07-14 14:53:45 +020095 /* request name (e.g. "MDCX") */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020096 char *name;
Philipp Maier33d97f72021-07-14 14:53:45 +020097
98 /* function pointer to the request handler */
Philipp Maier8dc35972021-07-14 11:20:16 +020099 struct msgb *(*handle_request)(struct mgcp_request_data *data);
100
Philipp Maier33d97f72021-07-14 14:53:45 +0200101 /* a human readable name that describes the request */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200102 char *debug_name;
103};
104
Philipp Maier8dc35972021-07-14 11:20:16 +0200105static struct msgb *handle_audit_endpoint(struct mgcp_request_data *data);
106static struct msgb *handle_create_con(struct mgcp_request_data *data);
107static struct msgb *handle_delete_con(struct mgcp_request_data *data);
108static struct msgb *handle_modify_con(struct mgcp_request_data *data);
109static struct msgb *handle_rsip(struct mgcp_request_data *data);
110static struct msgb *handle_noti_req(struct mgcp_request_data *data);
Philipp Maier33d97f72021-07-14 14:53:45 +0200111static const struct mgcp_request mgcp_requests[] = {
Ericee6958c2021-09-14 18:36:53 +0200112 { .name = "AUEP", .handle_request = handle_audit_endpoint, .debug_name = "AuditEndpoint" },
113 {
114 .name = "CRCX",
115 .handle_request = handle_create_con,
116 .debug_name = "CreateConnection",
117 },
118 {
119 .name = "DLCX",
120 .handle_request = handle_delete_con,
121 .debug_name = "DeleteConnection",
122 },
123 {
124 .name = "MDCX",
125 .handle_request = handle_modify_con,
126 .debug_name = "ModifiyConnection",
127 },
128 {
129 .name = "RQNT",
130 .handle_request = handle_noti_req,
131 .debug_name = "NotificationRequest",
132 },
Philipp Maier33d97f72021-07-14 14:53:45 +0200133
134 /* SPEC extension */
Ericee6958c2021-09-14 18:36:53 +0200135 {
136 .name = "RSIP",
137 .handle_request = handle_rsip,
138 .debug_name = "ReSetInProgress",
139 },
Philipp Maier33d97f72021-07-14 14:53:45 +0200140};
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200141
Philipp Maier87bd9be2017-08-22 16:35:41 +0200142/* Initalize transcoder */
143static int setup_rtp_processing(struct mgcp_endpoint *endp,
144 struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200145{
Ericfbf78d12021-08-23 22:31:39 +0200146 struct mgcp_config *cfg = endp->trunk->cfg;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200147 struct mgcp_conn_rtp *conn_src = NULL;
148 struct mgcp_conn_rtp *conn_dst = conn;
149 struct mgcp_conn *_conn;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200150
Pau Espin Pedrolbb3ccde2021-12-23 19:49:26 +0100151 switch (conn->type) {
152 case MGCP_RTP_DEFAULT:
Pau Espin Pedrol9d939b62022-10-03 16:59:20 +0200153 case MGCP_RTP_OSMUX:
Pau Espin Pedrolbb3ccde2021-12-23 19:49:26 +0100154 case MGCP_RTP_IUUP:
155 break;
156 default:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200157 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
158 "RTP-setup: Endpoint is not configured as RTP default, stopping here!\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200159 return 0;
160 }
161
Philipp Maier87bd9be2017-08-22 16:35:41 +0200162 if (conn->conn->mode == MGCP_CONN_LOOPBACK) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200163 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
164 "RTP-setup: Endpoint is in loopback mode, stopping here!\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +0200165 return 0;
166 }
167
168 /* Find the "sister" connection */
169 llist_for_each_entry(_conn, &endp->conns, entry) {
170 if (_conn->id != conn->conn->id) {
171 conn_src = &_conn->u.rtp;
172 break;
173 }
174 }
175
Philipp Maieracc10352018-07-19 18:07:57 +0200176 return cfg->setup_rtp_processing_cb(endp, conn_dst, conn_src);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200177}
178
Philipp Maier87bd9be2017-08-22 16:35:41 +0200179/* Helper function to allocate some memory for responses and retransmissions */
Ericfbcf4a62021-09-09 18:02:31 +0200180static struct msgb *mgcp_msgb_alloc(void *ctx)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200181{
182 struct msgb *msg;
Ericfbcf4a62021-09-09 18:02:31 +0200183 msg = msgb_alloc_headroom_c(ctx, 4096, 128, "MGCP msg");
184
185 if (!msg) {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200186 LOGP(DLMGCP, LOGL_ERROR, "Failed to msgb for MGCP data.\n");
Ericfbcf4a62021-09-09 18:02:31 +0200187 return NULL;
188 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200189
190 return msg;
191}
192
Philipp Maier87bd9be2017-08-22 16:35:41 +0200193/* Helper function for do_retransmission() and create_resp() */
Eric958f5e72021-08-03 23:00:17 +0200194static struct msgb *create_retransmission_response(const struct mgcp_endpoint *endp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200195{
Ericfbcf4a62021-09-09 18:02:31 +0200196 struct msgb *msg = mgcp_msgb_alloc(endp->trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200197 if (!msg)
198 return NULL;
199
200 msg->l2h = msgb_put(msg, strlen(endp->last_response));
201 memcpy(msg->l2h, endp->last_response, msgb_l2len(msg));
Philipp Maier87bd9be2017-08-22 16:35:41 +0200202 mgcp_disp_msg(msg->l2h, msgb_l2len(msg), "Retransmitted response");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200203 return msg;
204}
205
Ericfbcf4a62021-09-09 18:02:31 +0200206static struct msgb *create_resp(void *msgctx, struct mgcp_endpoint *endp, int code, const char *txt, const char *msg,
207 const char *trans, const char *param, const char *sdp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200208{
209 int len;
210 struct msgb *res;
211
Ericfbcf4a62021-09-09 18:02:31 +0200212 OSMO_ASSERT(msgctx != 0);
213 res = mgcp_msgb_alloc(msgctx);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200214 if (!res)
215 return NULL;
216
Philipp Maier87bd9be2017-08-22 16:35:41 +0200217 len = snprintf((char *)res->data, 2048, "%d %s%s%s\r\n%s",
218 code, trans, txt, param ? param : "", sdp ? sdp : "");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200219 if (len < 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200220 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "Failed to sprintf MGCP response.\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200221 msgb_free(res);
222 return NULL;
223 }
224
225 res->l2h = msgb_put(res, len);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200226 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "Generated response: code=%d\n", code);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200227 mgcp_disp_msg(res->l2h, msgb_l2len(res), "Generated response");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200228
229 /*
230 * Remember the last transmission per endpoint.
231 */
232 if (endp) {
Philipp Maier14b27a82020-06-02 20:15:30 +0200233 struct mgcp_trunk *trunk = endp->trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200234 talloc_free(endp->last_response);
235 talloc_free(endp->last_trans);
Philipp Maier14b27a82020-06-02 20:15:30 +0200236 endp->last_trans = talloc_strdup(trunk->endpoints, trans);
237 endp->last_response = talloc_strndup(trunk->endpoints,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200238 (const char *)res->l2h,
239 msgb_l2len(res));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200240 }
241
242 return res;
243}
244
Ericfbcf4a62021-09-09 18:02:31 +0200245static struct msgb *create_ok_resp_with_param(void *msgctx, struct mgcp_endpoint *endp, int code, const char *msg,
246 const char *trans, const char *param)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200247{
Ericfbcf4a62021-09-09 18:02:31 +0200248 return create_resp(msgctx, endp, code, " OK", msg, trans, param, NULL);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200249}
250
Ericfbcf4a62021-09-09 18:02:31 +0200251static struct msgb *create_ok_response(void *msgctx, struct mgcp_endpoint *endp, int code, const char *msg,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200252 const char *trans)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200253{
Ericfbcf4a62021-09-09 18:02:31 +0200254 return create_ok_resp_with_param(msgctx, endp, code, msg, trans, NULL);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200255}
256
Ericfbcf4a62021-09-09 18:02:31 +0200257static struct msgb *create_err_response(void *msgctx, struct mgcp_endpoint *endp, int code, const char *msg,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200258 const char *trans)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200259{
Ericfbcf4a62021-09-09 18:02:31 +0200260 return create_resp(msgctx, endp, code, " FAIL", msg, trans, NULL, NULL);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200261}
262
Philipp Maier87bd9be2017-08-22 16:35:41 +0200263/* Format MGCP response string (with SDP attached) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200264static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200265 struct mgcp_conn_rtp *conn,
266 const char *msg,
Philipp Maier55295f72018-01-15 14:00:28 +0100267 const char *trans_id,
Philipp Maier41d59202021-07-20 15:49:00 +0200268 bool add_epname,
269 bool add_conn_id)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200270{
Pau Espin Pedrol71d42e72020-09-03 14:20:07 +0200271 /* cfg->local_ip allows overwritting the announced IP address with
272 * regards to the one we actually bind to. Useful in behind-NAT
273 * scenarios.
274 * TODO: we may want to define another local_ip_osmux var to
275 * us for OSMUX connections. Perhaps adding a new internal API to get it
276 * based on conn type.
277 */
Ericfbf78d12021-08-23 22:31:39 +0200278 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 +0200279 struct msgb *sdp;
280 int rc;
281 struct msgb *result;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200282
Ericfbcf4a62021-09-09 18:02:31 +0200283 sdp = msgb_alloc_headroom_c(endp->trunk, 4096, 128, "sdp record");
Philipp Maier8970c492017-10-11 13:33:42 +0200284 if (!sdp)
285 return NULL;
286
Philipp Maier41d59202021-07-20 15:49:00 +0200287 /* Attach optional endpoint name */
288 if (add_epname) {
289 rc = msgb_printf(sdp, "Z: %s\r\n", endp->name);
290 if (rc < 0)
291 goto error;
292 }
293
294 /* Attach optional connection id */
295 if (add_conn_id) {
296 rc = msgb_printf(sdp, "I: %s\r\n", conn->conn->id);
Philipp Maier55295f72018-01-15 14:00:28 +0100297 if (rc < 0)
298 goto error;
299 }
300
Philipp Maier3cbfb8a2018-01-22 11:39:59 +0100301 /* Attach optional OSMUX parameters */
Pau Espin Pedrolc63f15a2019-05-10 16:52:08 +0200302 if (mgcp_conn_rtp_is_osmux(conn)) {
Pau Espin Pedrol21779192022-09-23 16:46:33 +0200303 rc = msgb_printf(sdp, MGCP_X_OSMO_OSMUX_HEADER " %u\r\n", conn->osmux.local_cid);
Philipp Maier3cbfb8a2018-01-22 11:39:59 +0100304 if (rc < 0)
305 goto error;
306 }
307
308 /* Attach line break to separate the parameters from the SDP block */
Philipp Maierc3cfae22018-01-22 12:03:03 +0100309 rc = msgb_printf(sdp, "\r\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200310
Philipp Maier8970c492017-10-11 13:33:42 +0200311 rc = mgcp_write_response_sdp(endp, conn, sdp, addr);
312 if (rc < 0)
313 goto error;
Ericfbcf4a62021-09-09 18:02:31 +0200314 result = create_resp(endp->trunk, endp, 200, " OK", msg, trans_id, NULL, (char *)sdp->data);
Philipp Maier8970c492017-10-11 13:33:42 +0200315 msgb_free(sdp);
316 return result;
317error:
318 msgb_free(sdp);
319 return NULL;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200320}
321
Philipp Maier87bd9be2017-08-22 16:35:41 +0200322/* Send out dummy packet to keep the connection open, if the connection is an
323 * osmux connection, send the dummy packet via OSMUX */
324static void send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200325{
Pau Espin Pedrolabb9d472022-10-04 12:09:14 +0200326 if (mgcp_conn_rtp_is_osmux(conn))
Philipp Maier87bd9be2017-08-22 16:35:41 +0200327 osmux_send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200328 else
Philipp Maier87bd9be2017-08-22 16:35:41 +0200329 mgcp_send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200330}
331
Philipp Maier87bd9be2017-08-22 16:35:41 +0200332/* handle incoming messages:
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200333 * - this can be a command (four letters, space, transaction id)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200334 * - or a response (three numbers, space, transaction id) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200335struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
336{
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200337 struct rate_ctr_group *rate_ctrs = cfg->ratectr.mgcp_general_ctr_group;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200338 struct mgcp_parse_data pdata;
Philipp Maier8dc35972021-07-14 11:20:16 +0200339 struct mgcp_request_data rq;
Harald Weltee35eeae2017-12-28 13:47:37 +0100340 int rc, i, code, handled = 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200341 struct msgb *resp = NULL;
342 char *data;
343
Philipp Maier39889e42021-08-04 17:42:57 +0200344 debug_last_endpoint_name[0] = '\0';
345
Alexander Chemeris63866002020-05-05 17:18:40 +0300346 /* Count all messages, even incorect ones */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200347 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_TOTAL));
Alexander Chemeris63866002020-05-05 17:18:40 +0300348
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200349 if (msgb_l2len(msg) < 4) {
350 LOGP(DLMGCP, LOGL_ERROR, "msg too short: %d\n", msg->len);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200351 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200352 return NULL;
353 }
354
Alexander Chemeris63866002020-05-05 17:18:40 +0300355 if (mgcp_msg_terminate_nul(msg)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200356 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200357 return NULL;
Alexander Chemeris63866002020-05-05 17:18:40 +0300358 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200359
Philipp Maier87bd9be2017-08-22 16:35:41 +0200360 mgcp_disp_msg(msg->l2h, msgb_l2len(msg), "Received message");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200361
Philipp Maier87bd9be2017-08-22 16:35:41 +0200362 /* attempt to treat it as a response */
363 if (sscanf((const char *)&msg->l2h[0], "%3d %*s", &code) == 1) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200364 LOGP(DLMGCP, LOGL_DEBUG, "Response: Code: %d\n", code);
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;
367 }
368
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200369
Philipp Maier8dc35972021-07-14 11:20:16 +0200370 /* Parse message, extract endpoint name and transaction identifier and request name etc. */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200371 memset(&pdata, 0, sizeof(pdata));
Philipp Maier8dc35972021-07-14 11:20:16 +0200372 memset(&rq, 0, sizeof(rq));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200373 pdata.cfg = cfg;
Philipp Maier8dc35972021-07-14 11:20:16 +0200374 memcpy(rq.name, (const char *)&msg->l2h[0], sizeof(rq.name)-1);
375 msg->l3h = &msg->l2h[4];
Philipp Maier87bd9be2017-08-22 16:35:41 +0200376 data = mgcp_strline((char *)msg->l3h, &pdata.save);
Harald Weltee35eeae2017-12-28 13:47:37 +0100377 rc = mgcp_parse_header(&pdata, data);
Harald Weltee35eeae2017-12-28 13:47:37 +0100378 if (rc < 0) {
Philipp Maier8dc35972021-07-14 11:20:16 +0200379 LOGP(DLMGCP, LOGL_ERROR, "%s: failed to parse MCGP message\n", rq.name);
380 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
Ericfbcf4a62021-09-09 18:02:31 +0200381 return create_err_response(cfg, NULL, -rc, rq.name, "000000");
Harald Welteabbb6b92017-12-28 13:13:50 +0100382 }
383
Philipp Maier8dc35972021-07-14 11:20:16 +0200384 /* Locate endpoint and trunk, if no endpoint can be located try at least to identify the trunk. */
385 rq.pdata = &pdata;
386 rq.wildcarded = mgcp_endp_is_wildcarded(pdata.epname);
387 rq.endp = mgcp_endp_by_name(&rc, pdata.epname, pdata.cfg);
388 rq.mgcp_cause = rc;
389 if (!rq.endp) {
390 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_NO_ENDPOINT));
391 if (rq.wildcarded) {
392 /* If we are unable to find the endpoint we still may be able to identify the trunk. Some
393 * request handlers will still be able to perform a useful action if the request refers to
394 * the whole trunk (wildcarded request). */
395 LOGP(DLMGCP, LOGL_NOTICE,
396 "%s: cannot find endpoint \"%s\", cause=%d -- trying to identify trunk...\n", rq.name,
397 pdata.epname, -rq.mgcp_cause);
398 rq.trunk = mgcp_trunk_by_name(pdata.cfg, pdata.epname);
399 if (!rq.trunk) {
400 LOGP(DLMGCP, LOGL_ERROR, "%s: failed to identify trunk for endpoint \"%s\" -- abort\n",
401 rq.name, pdata.epname);
Ericfbcf4a62021-09-09 18:02:31 +0200402 return create_err_response(cfg, NULL, -rq.mgcp_cause, rq.name, pdata.trans);
Philipp Maier8dc35972021-07-14 11:20:16 +0200403 }
404 } else {
405 /* If the endpoint name suggests that the request refers to a specific endpoint, then the
406 * request cannot be handled and we must stop early. */
407 LOGP(DLMGCP, LOGL_NOTICE,
408 "%s: cannot find endpoint \"%s\", cause=%d -- abort\n", rq.name,
409 pdata.epname, -rq.mgcp_cause);
Ericfbcf4a62021-09-09 18:02:31 +0200410 return create_err_response(cfg, NULL, -rq.mgcp_cause, rq.name, pdata.trans);
Philipp Maier8dc35972021-07-14 11:20:16 +0200411 }
412 } else {
Philipp Maier39889e42021-08-04 17:42:57 +0200413 osmo_strlcpy(debug_last_endpoint_name, rq.endp->name, sizeof(debug_last_endpoint_name));
Philipp Maier8dc35972021-07-14 11:20:16 +0200414 rq.trunk = rq.endp->trunk;
415 rq.mgcp_cause = 0;
416
417 /* Check if we have to retransmit a response from a previous transaction */
418 if (pdata.trans && rq.endp->last_trans && strcmp(rq.endp->last_trans, pdata.trans) == 0) {
419 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_RETRANSMITTED));
Eric958f5e72021-08-03 23:00:17 +0200420 return create_retransmission_response(rq.endp);
Philipp Maier8dc35972021-07-14 11:20:16 +0200421 }
422 }
423
424 /* Find an appropriate handler for the current request and execute it */
425 for (i = 0; i < ARRAY_SIZE(mgcp_requests); i++) {
426 if (strcmp(mgcp_requests[i].name, rq.name) == 0) {
Philipp Maier8dc35972021-07-14 11:20:16 +0200427 /* Execute request handler */
428 if (rq.endp)
429 LOGP(DLMGCP, LOGL_INFO,
430 "%s: executing request handler \"%s\" for endpoint resource \"%s\"\n", rq.name,
431 mgcp_requests[i].debug_name, rq.endp->name);
432 else
433 LOGP(DLMGCP, LOGL_INFO,
434 "%s: executing request handler \"%s\" for trunk resource of endpoint \"%s\"\n",
435 rq.name, mgcp_requests[i].debug_name, pdata.epname);
436 resp = mgcp_requests[i].handle_request(&rq);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200437 handled = 1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200438 break;
439 }
440 }
441
Philipp Maier8dc35972021-07-14 11:20:16 +0200442 /* Check if the MGCP request was handled and increment rate counters accordingly. */
Alexander Chemeris63866002020-05-05 17:18:40 +0300443 if (handled) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200444 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_HANDLED));
Alexander Chemeris63866002020-05-05 17:18:40 +0300445 } else {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200446 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_UNHANDLED));
Philipp Maier8dc35972021-07-14 11:20:16 +0200447 LOGP(DLMGCP, LOGL_ERROR, "MSG with type: '%.4s' not handled\n", &msg->l2h[0]);
Alexander Chemeris63866002020-05-05 17:18:40 +0300448 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200449
450 return resp;
451}
452
Philipp Maier87bd9be2017-08-22 16:35:41 +0200453/* AUEP command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +0200454static struct msgb *handle_audit_endpoint(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200455{
Philipp Maier8dc35972021-07-14 11:20:16 +0200456 LOGPENDP(rq->endp, DLMGCP, LOGL_NOTICE, "AUEP: auditing endpoint ...\n");
Ericee6958c2021-09-14 18:36:53 +0200457 if (!rq->endp || !mgcp_endp_avail(rq->endp)) {
458 LOGPENDP(rq->endp, DLMGCP, LOGL_ERROR, "AUEP: selected endpoint not available!\n");
459 return create_err_response(rq->trunk, NULL, 501, "AUEP", rq->pdata->trans);
460 }
461
Ericfbcf4a62021-09-09 18:02:31 +0200462 return create_ok_response(rq->trunk, rq->endp, 200, "AUEP", rq->pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200463}
464
Harald Welte1d1b98f2017-12-25 10:03:40 +0100465/* Try to find a free port by attempting to bind on it. Also handle the
Philipp Maier87bd9be2017-08-22 16:35:41 +0200466 * counter that points on the next free port. Since we have a pointer
Philipp Maierb38fb892018-05-22 13:52:21 +0200467 * to the next free port, binding should in work on the first attempt in
Pau Espin Pedrolfc806732019-04-23 00:18:43 +0200468 * general. In case of failure the next port is tried until the whole port
469 * range is tried once. */
Philipp Maier87bd9be2017-08-22 16:35:41 +0200470static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200471{
472 int i;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200473 struct mgcp_port_range *range;
Philipp Maierb38fb892018-05-22 13:52:21 +0200474 unsigned int tries;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200475
Philipp Maier87bd9be2017-08-22 16:35:41 +0200476 OSMO_ASSERT(conn);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200477
Ericfbf78d12021-08-23 22:31:39 +0200478 range = &endp->trunk->cfg->net_ports;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200479
Eric55fdfc22021-08-13 00:14:18 +0200480 pthread_mutex_lock(&range->lock);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200481 /* attempt to find a port */
Philipp Maierb38fb892018-05-22 13:52:21 +0200482 tries = (range->range_end - range->range_start) / 2;
483 for (i = 0; i < tries; ++i) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200484 int rc;
485
486 if (range->last_port >= range->range_end)
487 range->last_port = range->range_start;
488
Philipp Maier87bd9be2017-08-22 16:35:41 +0200489 rc = mgcp_bind_net_rtp_port(endp, range->last_port, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200490
491 range->last_port += 2;
492 if (rc == 0) {
Eric55fdfc22021-08-13 00:14:18 +0200493 pthread_mutex_unlock(&range->lock);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200494 return 0;
495 }
496
497 }
Eric55fdfc22021-08-13 00:14:18 +0200498 pthread_mutex_unlock(&range->lock);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200499 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
500 "Allocating a RTP/RTCP port failed %u times.\n",
501 tries);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200502 return -1;
503}
504
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200505/*! Helper function for check_local_cx_options() to get a pointer of the next
506 * lco option identifier
507 * \param[in] lco string
508 * \returns pointer to the beginning of the LCO identifier, NULL on failure */
509char *get_lco_identifier(const char *options)
510{
511 char *ptr;
512 unsigned int count = 0;
513
514 /* Jump to the end of the lco identifier */
515 ptr = strstr(options, ":");
516 if (!ptr)
517 return NULL;
518
519 /* Walk backwards until the pointer points to the beginning of the
520 * lco identifier. We know that we stand at the beginning when we
521 * are either at the beginning of the memory or see a space or
522 * comma. (this is tolerant, it will accept a:10, b:11 as well as
523 * a:10,b:11) */
524 while (1) {
525 /* Endless loop protection */
526 if (count > 10000)
527 return NULL;
528 else if (ptr < options || *ptr == ' ' || *ptr == ',') {
529 ptr++;
530 break;
531 }
532 ptr--;
533 count++;
534 }
535
536 /* Check if we got any result */
537 if (*ptr == ':')
538 return NULL;
539
540 return ptr;
541}
542
543/*! Check the LCO option. This function checks for multiple appearence of LCO
544 * options, which is illegal
545 * \param[in] ctx talloc context
546 * \param[in] lco string
547 * \returns 0 on success, -1 on failure */
548int check_local_cx_options(void *ctx, const char *options)
549{
550 int i;
551 char *options_copy;
552 char *lco_identifier;
553 char *lco_identifier_end;
554 char *next_lco_identifier;
555
556 char **lco_seen;
557 unsigned int lco_seen_n = 0;
558
559 if (!options)
560 return -1;
561
562 lco_seen =
563 (char **)talloc_zero_size(ctx, strlen(options) * sizeof(char *));
564 options_copy = talloc_strdup(ctx, options);
565 lco_identifier = options_copy;
566
567 do {
568 /* Move the lco_identifier pointer to the beginning of the
569 * current lco option identifier */
570 lco_identifier = get_lco_identifier(lco_identifier);
571 if (!lco_identifier)
572 goto error;
573
574 /* Look ahead to the next LCO option early, since we
575 * will parse destructively */
576 next_lco_identifier = strstr(lco_identifier + 1, ",");
577
578 /* Pinch off the end of the lco field identifier name
579 * and see if we still got something, also check if
580 * there is some value after the colon. */
581 lco_identifier_end = strstr(lco_identifier, ":");
582 if (!lco_identifier_end)
583 goto error;
584 if (*(lco_identifier_end + 1) == ' '
585 || *(lco_identifier_end + 1) == ','
586 || *(lco_identifier_end + 1) == '\0')
587 goto error;
588 *lco_identifier_end = '\0';
589 if (strlen(lco_identifier) == 0)
590 goto error;
591
592 /* Check if we have already seen the current field identifier
593 * before. If yes, we must bail, an LCO must only appear once
594 * in the LCO string */
595 for (i = 0; i < lco_seen_n; i++) {
Pau Espin Pedrol7eb6f2c2019-06-26 13:00:52 +0200596 if (strcasecmp(lco_seen[i], lco_identifier) == 0)
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200597 goto error;
598 }
599 lco_seen[lco_seen_n] = lco_identifier;
600 lco_seen_n++;
601
602 /* The first identifier must always be found at the beginnning
603 * of the LCO string */
604 if (lco_seen[0] != options_copy)
605 goto error;
606
607 /* Go to the next lco option */
608 lco_identifier = next_lco_identifier;
609 } while (lco_identifier);
610
611 talloc_free(lco_seen);
612 talloc_free(options_copy);
613 return 0;
614error:
615 talloc_free(lco_seen);
616 talloc_free(options_copy);
617 return -1;
618}
619
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200620/* Set the LCO from a string (see RFC 3435).
Harald Welte1d1b98f2017-12-25 10:03:40 +0100621 * The string is stored in the 'string' field. A NULL string is handled exactly
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200622 * like an empty string, the 'string' field is never NULL after this function
623 * has been called. */
Philipp Maiera390d0b2018-01-31 17:30:19 +0100624static int set_local_cx_options(void *ctx, struct mgcp_lco *lco,
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200625 const char *options)
626{
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200627 char *lco_id;
Philipp Maier8dbc9ed2018-10-26 14:50:25 +0200628 char codec[17];
Pau Espin Pedrol19539862020-09-21 11:43:41 +0200629 char nt[17];
Pau Espin Pedrol83fd8a52019-06-26 12:55:26 +0200630 int len;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200631
Philipp Maier604410c2018-06-06 10:02:16 +0200632 if (!options)
633 return 0;
634 if (strlen(options) == 0)
635 return 0;
636
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200637 /* Make sure the encoding of the LCO is consistant before we proceed */
638 if (check_local_cx_options(ctx, options) != 0) {
639 LOGP(DLMGCP, LOGL_ERROR,
640 "local CX options: Internal inconsistency in Local Connection Options!\n");
641 return 524;
642 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200643
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200644 talloc_free(lco->string);
645 lco->string = talloc_strdup(ctx, options);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200646
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200647 lco_id = lco->string;
648 while ((lco_id = get_lco_identifier(lco_id))) {
649 switch (tolower(lco_id[0])) {
650 case 'p':
651 if (sscanf(lco_id + 1, ":%d-%d",
652 &lco->pkt_period_min, &lco->pkt_period_max) == 1)
653 lco->pkt_period_max = lco->pkt_period_min;
654 break;
655 case 'a':
Pau Espin Pedrol1dc2dce2020-09-21 11:25:15 +0200656 /* FIXME: LCO also supports the negotiation of more than one codec.
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200657 * (e.g. a:PCMU;G726-32) But this implementation only supports a single
658 * codec only. */
659 if (sscanf(lco_id + 1, ":%16[^,]", codec) == 1) {
660 talloc_free(lco->codec);
Pau Espin Pedrol83fd8a52019-06-26 12:55:26 +0200661 /* MGCP header is case insensive, and we'll need
662 codec in uppercase when using it later: */
663 len = strlen(codec);
664 lco->codec = talloc_size(ctx, len + 1);
665 osmo_str_toupper_buf(lco->codec, len + 1, codec);
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200666 }
667 break;
Pau Espin Pedrol19539862020-09-21 11:43:41 +0200668 case 'n':
669 if (lco_id[1] == 't' && sscanf(lco_id + 2, ":%16[^,]", nt) == 1)
670 break;
671 /* else: fall throught to print notice log */
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200672 default:
673 LOGP(DLMGCP, LOGL_NOTICE,
674 "LCO: unhandled option: '%c'/%d in \"%s\"\n",
675 *lco_id, *lco_id, lco->string);
676 break;
677 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200678
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200679 lco_id = strchr(lco_id, ',');
680 if (!lco_id)
681 break;
Philipp Maier604410c2018-06-06 10:02:16 +0200682 }
Philipp Maier87bd9be2017-08-22 16:35:41 +0200683
684 LOGP(DLMGCP, LOGL_DEBUG,
685 "local CX options: lco->pkt_period_max: %i, lco->codec: %s\n",
686 lco->pkt_period_max, lco->codec);
Philipp Maiera390d0b2018-01-31 17:30:19 +0100687
688 /* Check if the packetization fits the 20ms raster */
689 if (lco->pkt_period_min % 20 && lco->pkt_period_max % 20) {
690 LOGP(DLMGCP, LOGL_ERROR,
691 "local CX options: packetization interval is not a multiple of 20ms!\n");
692 return 535;
693 }
694
695 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200696}
697
698void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
699 struct mgcp_rtp_end *rtp)
700{
Philipp Maier14b27a82020-06-02 20:15:30 +0200701 struct mgcp_trunk *trunk = endp->trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200702
Philipp Maier14b27a82020-06-02 20:15:30 +0200703 int patch_ssrc = expect_ssrc_change && trunk->force_constant_ssrc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200704
Philipp Maier14b27a82020-06-02 20:15:30 +0200705 rtp->force_aligned_timing = trunk->force_aligned_timing;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200706 rtp->force_constant_ssrc = patch_ssrc ? 1 : 0;
Philipp Maier14b27a82020-06-02 20:15:30 +0200707 rtp->rfc5993_hr_convert = trunk->rfc5993_hr_convert;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200708
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200709 LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
710 "Configuring RTP endpoint: local port %d%s%s\n",
Pau Espin Pedrol5ffd1272022-10-04 13:45:48 +0200711 osmo_sockaddr_port(&rtp->addr.u.sa),
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200712 rtp->force_aligned_timing ? ", force constant timing" : "",
713 rtp->force_constant_ssrc ? ", force constant ssrc" : "");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200714}
715
Pau Espin Pedrol8358c4b2021-07-07 12:41:38 +0200716uint32_t mgcp_rtp_packet_duration(const struct mgcp_endpoint *endp,
717 const struct mgcp_rtp_end *rtp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200718{
719 int f = 0;
720
721 /* Get the number of frames per channel and packet */
722 if (rtp->frames_per_packet)
723 f = rtp->frames_per_packet;
Philipp Maierbc0346e2018-06-07 09:52:16 +0200724 else if (rtp->packet_duration_ms && rtp->codec->frame_duration_num) {
725 int den = 1000 * rtp->codec->frame_duration_num;
726 f = (rtp->packet_duration_ms * rtp->codec->frame_duration_den +
Philipp Maier87bd9be2017-08-22 16:35:41 +0200727 den / 2)
728 / den;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200729 }
730
Philipp Maierbc0346e2018-06-07 09:52:16 +0200731 return rtp->codec->rate * f * rtp->codec->frame_duration_num /
732 rtp->codec->frame_duration_den;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200733}
734
Pau Espin Pedrol9fb8ddf2019-05-08 15:35:36 +0200735/*! Initializes osmux socket if not yet initialized. Parses Osmux CID from MGCP line.
736 * \param[in] endp Endpoint willing to initialize osmux
737 * \param[in] line Line X-Osmux from MGCP header msg to parse
738 * \returns OSMUX CID, -1 for wildcard, -2 on parse error, -3 on osmux initalize error
739 */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200740static int mgcp_osmux_setup(struct mgcp_endpoint *endp, const char *line)
741{
Pau Espin Pedrolb7f52b42022-09-25 00:14:03 +0200742 if (!endp->trunk->cfg->osmux_initialized) {
Pau Espin Pedrol21bcf6a2022-09-22 18:23:04 +0200743 if (osmux_init(OSMUX_ROLE_BSC, endp->trunk) < 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200744 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "Cannot init OSMUX\n");
Pau Espin Pedrol9fb8ddf2019-05-08 15:35:36 +0200745 return -3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200746 }
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200747 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "OSMUX socket has been set up\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200748 }
749
750 return mgcp_parse_osmux_cid(line);
751}
752
Philipp Maierbc0346e2018-06-07 09:52:16 +0200753/* Process codec information contained in CRCX/MDCX */
754static int handle_codec_info(struct mgcp_conn_rtp *conn,
Philipp Maier8dc35972021-07-14 11:20:16 +0200755 struct mgcp_request_data *rq, int have_sdp, bool crcx)
Philipp Maierbc0346e2018-06-07 09:52:16 +0200756{
Philipp Maier8dc35972021-07-14 11:20:16 +0200757 struct mgcp_endpoint *endp = rq->endp;
Philipp Maierbc0346e2018-06-07 09:52:16 +0200758 int rc;
759 char *cmd;
760
761 if (crcx)
762 cmd = "CRCX";
763 else
764 cmd = "MDCX";
765
766 /* Collect codec information */
767 if (have_sdp) {
768 /* If we have SDP, we ignore the local connection options and
769 * use only the SDP information. */
770 mgcp_codec_reset_all(conn);
Philipp Maier8dc35972021-07-14 11:20:16 +0200771 rc = mgcp_parse_sdp_data(endp, conn, rq->pdata);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200772 if (rc != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200773 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
774 "%s: sdp not parseable\n", cmd);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200775
776 /* See also RFC 3661: Protocol error */
777 return 510;
778 }
779 } else if (endp->local_options.codec) {
780 /* When no SDP is available, we use the codec information from
781 * the local connection options (if present) */
782 mgcp_codec_reset_all(conn);
Philipp Maier228e5912019-03-05 13:56:59 +0100783 rc = mgcp_codec_add(conn, PTYPE_UNDEFINED, endp->local_options.codec, NULL);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200784 if (rc != 0)
785 goto error;
786 }
787
788 /* Make sure we always set a sane default codec */
789 if (conn->end.codecs_assigned == 0) {
790 /* When SDP and/or LCO did not supply any codec information,
791 * than it makes sense to pick a sane default: (payload-type 0,
792 * PCMU), see also: OS#2658 */
793 mgcp_codec_reset_all(conn);
Philipp Maier228e5912019-03-05 13:56:59 +0100794 rc = mgcp_codec_add(conn, 0, NULL, NULL);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200795 if (rc != 0)
796 goto error;
797 }
798
799 /* Make codec decision */
800 if (mgcp_codec_decide(conn) != 0)
801 goto error;
802
803 return 0;
804
805error:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200806 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
807 "%s: codec negotiation failure\n", cmd);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200808
809 /* See also RFC 3661: Codec negotiation failure */
810 return 534;
811}
812
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200813static bool parse_x_osmo_ign(struct mgcp_endpoint *endp, char *line)
814{
815 char *saveptr = NULL;
816
Pau Espin Pedrol6049a632020-09-21 11:03:21 +0200817 if (strncasecmp(line, MGCP_X_OSMO_IGN_HEADER, strlen(MGCP_X_OSMO_IGN_HEADER)))
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200818 return false;
819 line += strlen(MGCP_X_OSMO_IGN_HEADER);
820
821 while (1) {
822 char *token = strtok_r(line, " ", &saveptr);
823 line = NULL;
824 if (!token)
825 break;
826
Pau Espin Pedrol6e26c702019-06-26 13:09:39 +0200827 if (!strcasecmp(token, "C"))
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200828 endp->x_osmo_ign |= MGCP_X_OSMO_IGN_CALLID;
829 else
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200830 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "received unknown X-Osmo-IGN item '%s'\n", token);
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200831 }
832
833 return true;
834}
835
Philipp Maier87bd9be2017-08-22 16:35:41 +0200836/* CRCX command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +0200837static struct msgb *handle_create_con(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200838{
Philipp Maier8dc35972021-07-14 11:20:16 +0200839 struct mgcp_parse_data *pdata = rq->pdata;
840 struct mgcp_trunk *trunk = rq->trunk;
841 struct mgcp_endpoint *endp = rq->endp;
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200842 struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_crcx_ctr_group;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200843 int error_code = 400;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200844 const char *local_options = NULL;
845 const char *callid = NULL;
846 const char *mode = NULL;
847 char *line;
Pau Espin Pedrol21779192022-09-23 16:46:33 +0200848 int have_sdp = 0, remote_osmux_cid = -2;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200849 struct mgcp_conn_rtp *conn = NULL;
Philipp Maierffd75e42017-11-22 11:44:50 +0100850 struct mgcp_conn *_conn = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200851 char conn_name[512];
Philipp Maiera390d0b2018-01-31 17:30:19 +0100852 int rc;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200853
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200854 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "CRCX: creating new connection ...\n");
Philipp Maier246233d2020-08-18 15:15:24 +0200855
Ericee6958c2021-09-14 18:36:53 +0200856 /* we must have a free ep */
857 if (!endp) {
858 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_AVAIL));
859 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "CRCX: no free endpoints available!\n");
860 return create_err_response(rq->trunk, NULL, 403, "CRCX", pdata->trans);
861 }
862
Philipp Maier8d6a1932020-06-18 12:19:31 +0200863 if (!mgcp_endp_avail(endp)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200864 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_AVAIL));
Philipp Maiera910a812020-08-18 15:13:33 +0200865 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
866 "CRCX: selected endpoint not available!\n");
Ericfbcf4a62021-09-09 18:02:31 +0200867 return create_err_response(rq->trunk, NULL, 501, "CRCX", pdata->trans);
Philipp Maier8d6a1932020-06-18 12:19:31 +0200868 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200869
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200870 /* parse CallID C: and LocalParameters L: */
Philipp Maier8dc35972021-07-14 11:20:16 +0200871 for_each_line(line, pdata->save) {
Philipp Maier036612b2021-07-19 17:47:49 +0200872 if (!mgcp_check_param(endp, trunk, line))
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200873 continue;
874
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +0200875 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200876 case 'L':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200877 local_options = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200878 break;
879 case 'C':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200880 callid = (const char *)line + 3;
881 break;
882 case 'I':
Philipp Maierffd75e42017-11-22 11:44:50 +0100883 /* It is illegal to send a connection identifier
884 * together with a CRCX, the MGW will assign the
885 * connection identifier by itself on CRCX */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200886 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BAD_ACTION));
Ericfbcf4a62021-09-09 18:02:31 +0200887 return create_err_response(rq->trunk, NULL, 523, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200888 break;
889 case 'M':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200890 mode = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200891 break;
892 case 'X':
Pau Espin Pedrolc1bf4692019-05-14 16:23:24 +0200893 if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200894 /* If osmux is disabled, just skip setting it up */
Pau Espin Pedrol928a20b2022-09-23 15:38:24 +0200895 if (rq->endp->trunk->cfg->osmux_use == OSMUX_USAGE_OFF)
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200896 break;
Pau Espin Pedrol21779192022-09-23 16:46:33 +0200897 remote_osmux_cid = mgcp_osmux_setup(endp, line);
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200898 break;
899 }
900
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200901 if (parse_x_osmo_ign(endp, line))
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200902 break;
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200903
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200904 /* Ignore unknown X-headers */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200905 break;
906 case '\0':
907 have_sdp = 1;
908 goto mgcp_header_done;
909 default:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200910 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
911 "CRCX: unhandled option: '%c'/%d\n", *line, *line);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200912 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_UNHANDLED_PARAM));
Ericfbcf4a62021-09-09 18:02:31 +0200913 return create_err_response(rq->trunk, NULL, 539, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200914 break;
915 }
916 }
917
918mgcp_header_done:
Philipp Maier87bd9be2017-08-22 16:35:41 +0200919 /* Check parameters */
920 if (!callid) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200921 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
922 "CRCX: insufficient parameters, missing callid\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200923 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_MISSING_CALLID));
Ericfbcf4a62021-09-09 18:02:31 +0200924 return create_err_response(endp, endp, 516, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200925 }
926
Philipp Maier87bd9be2017-08-22 16:35:41 +0200927 if (!mode) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200928 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
929 "CRCX: insufficient parameters, missing mode\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200930 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_MODE));
Ericfbcf4a62021-09-09 18:02:31 +0200931 return create_err_response(endp, endp, 517, "CRCX", pdata->trans);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200932 }
933
Philipp Maier87bd9be2017-08-22 16:35:41 +0200934 /* Check if we are able to accept the creation of another connection */
935 if (llist_count(&endp->conns) >= endp->type->max_conns) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200936 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
937 "CRCX: endpoint full, max. %i connections allowed!\n",
938 endp->type->max_conns);
Philipp Maier14b27a82020-06-02 20:15:30 +0200939 if (trunk->force_realloc) {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200940 /* There is no more room for a connection, make some
941 * room by blindly tossing the oldest of the two two
942 * connections */
943 mgcp_conn_free_oldest(endp);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200944 } else {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200945 /* There is no more room for a connection, leave
946 * everything as it is and return with an error */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200947 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_LIMIT_EXCEEDED));
Ericfbcf4a62021-09-09 18:02:31 +0200948 return create_err_response(endp, endp, 540, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200949 }
950 }
951
Philipp Maier87bd9be2017-08-22 16:35:41 +0200952 /* Check if this endpoint already serves a call, if so, check if the
953 * callids match up so that we are sure that this is our call */
954 if (endp->callid && mgcp_verify_call_id(endp, callid)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200955 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
956 "CRCX: already seized by other call (%s)\n",
957 endp->callid);
Philipp Maier14b27a82020-06-02 20:15:30 +0200958 if (trunk->force_realloc)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200959 /* This is not our call, toss everything by releasing
960 * the entire endpoint. (rude!) */
Philipp Maier1355d7e2018-02-01 14:30:06 +0100961 mgcp_endp_release(endp);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200962 else {
963 /* This is not our call, leave everything as it is and
964 * return with an error. */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200965 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_UNKNOWN_CALLID));
Ericfbcf4a62021-09-09 18:02:31 +0200966 return create_err_response(endp, endp, 400, "CRCX", pdata->trans);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200967 }
968 }
969
Philipp Maier889fe7f2020-07-06 17:44:12 +0200970 if (!endp->callid) {
971 /* Claim endpoint resources. This will also set the callid,
972 * creating additional connections will only be possible if
973 * the callid matches up (see above). */
974 rc = mgcp_endp_claim(endp, callid);
975 if (rc != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200976 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_CLAIM));
Ericfbcf4a62021-09-09 18:02:31 +0200977 return create_err_response(endp, endp, 502, "CRCX", pdata->trans);
Philipp Maier889fe7f2020-07-06 17:44:12 +0200978 }
979 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200980
Philipp Maierffd75e42017-11-22 11:44:50 +0100981 snprintf(conn_name, sizeof(conn_name), "%s", callid);
Philipp Maier14b27a82020-06-02 20:15:30 +0200982 _conn = mgcp_conn_alloc(trunk->endpoints, endp, MGCP_CONN_TYPE_RTP, conn_name);
Philipp Maierffd75e42017-11-22 11:44:50 +0100983 if (!_conn) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200984 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
985 "CRCX: unable to allocate RTP connection\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200986 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_ALLOC_CONN));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200987 goto error2;
988
Philipp Maier87bd9be2017-08-22 16:35:41 +0200989 }
Philipp Maier889fe7f2020-07-06 17:44:12 +0200990
Philipp Maierffd75e42017-11-22 11:44:50 +0100991 conn = mgcp_conn_get_rtp(endp, _conn->id);
992 OSMO_ASSERT(conn);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200993
994 if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
995 error_code = 517;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200996 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_MODE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200997 goto error2;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200998 }
999
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001000 /* Annotate Osmux circuit ID and set it to negotiating state until this
Philipp Maier87bd9be2017-08-22 16:35:41 +02001001 * is fully set up from the dummy load. */
1002 conn->osmux.state = OSMUX_STATE_DISABLED;
Pau Espin Pedrol21779192022-09-23 16:46:33 +02001003 /* If X-Osmux (remote CID) was received (-1 is wilcard), alloc next avail CID as local CID */
1004 if (remote_osmux_cid >= -1) {
Pau Espin Pedrold48a8112022-09-27 12:37:53 +02001005 if (osmux_init_conn(conn) < 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001006 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_OSMUX));
Pau Espin Pedrol2b896172019-04-24 13:47:23 +02001007 goto error2;
1008 }
Pau Espin Pedrol21779192022-09-23 16:46:33 +02001009 if (remote_osmux_cid >= 0) {
1010 conn->osmux.remote_cid_present = true;
1011 conn->osmux.remote_cid = remote_osmux_cid;
1012 }
Pau Espin Pedrol928a20b2022-09-23 15:38:24 +02001013 } else if (endp->trunk->cfg->osmux_use == OSMUX_USAGE_ONLY) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001014 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
1015 "CRCX: osmux only and no osmux offered\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001016 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_OSMUX));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001017 goto error2;
1018 }
1019
Philipp Maierbc0346e2018-06-07 09:52:16 +02001020 /* Set local connection options, if present */
1021 if (local_options) {
Philipp Maier8dc35972021-07-14 11:20:16 +02001022 rc = set_local_cx_options(trunk->endpoints,
Philipp Maierbc0346e2018-06-07 09:52:16 +02001023 &endp->local_options, local_options);
1024 if (rc != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001025 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
1026 "CRCX: inavlid local connection options!\n");
Philipp Maierbc0346e2018-06-07 09:52:16 +02001027 error_code = rc;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001028 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS));
Philipp Maierbc0346e2018-06-07 09:52:16 +02001029 goto error2;
1030 }
1031 }
1032
1033 /* Handle codec information and decide for a suitable codec */
Philipp Maier8dc35972021-07-14 11:20:16 +02001034 rc = handle_codec_info(conn, rq, have_sdp, true);
Philipp Maierbc0346e2018-06-07 09:52:16 +02001035 mgcp_codec_summary(conn);
1036 if (rc) {
1037 error_code = rc;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001038 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_CODEC_NEGOTIATION));
Philipp Maierbc0346e2018-06-07 09:52:16 +02001039 goto error2;
1040 }
Pau Espin Pedrolbb3ccde2021-12-23 19:49:26 +01001041 /* Upgrade the conn type RTP_DEFAULT->RTP_IUUP if needed based on requested codec: */
1042 /* TODO: "codec" probably needs to be moved from endp to conn */
1043 if (conn->type == MGCP_RTP_DEFAULT && strcmp(conn->end.codec->subtype_name, "VND.3GPP.IUFP") == 0) {
1044 rc = mgcp_conn_iuup_init(conn);
1045 }
Philipp Maierbc0346e2018-06-07 09:52:16 +02001046
Philipp Maier14b27a82020-06-02 20:15:30 +02001047 conn->end.fmtp_extra = talloc_strdup(trunk->endpoints,
1048 trunk->audio_fmtp_extra);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001049
Philipp Maier8dc35972021-07-14 11:20:16 +02001050 if (pdata->cfg->force_ptime) {
1051 conn->end.packet_duration_ms = pdata->cfg->force_ptime;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001052 conn->end.force_output_ptime = 1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001053 }
1054
Philipp Maier1cb1e382017-11-02 17:16:04 +01001055 mgcp_rtp_end_config(endp, 0, &conn->end);
1056
Philipp Maierc3cc6542018-02-02 12:58:42 +01001057 /* check connection mode setting */
1058 if (conn->conn->mode != MGCP_CONN_LOOPBACK
1059 && conn->conn->mode != MGCP_CONN_RECV_ONLY
Pau Espin Pedrol5ffd1272022-10-04 13:45:48 +02001060 && osmo_sockaddr_port(&conn->end.addr.u.sa) == 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001061 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
1062 "CRCX: selected connection mode type requires an opposite end!\n");
Philipp Maierc3cc6542018-02-02 12:58:42 +01001063 error_code = 527;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001064 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC));
Philipp Maierc3cc6542018-02-02 12:58:42 +01001065 goto error2;
1066 }
1067
Pau Espin Pedrol71d42e72020-09-03 14:20:07 +02001068 /* Find a local address for conn based on policy and initial SDP remote
1069 information, then find a free port for it */
Pau Espin Pedrol70c03f52022-10-04 16:49:41 +02001070 if (mgcp_get_local_addr(conn->end.local_addr, conn) < 0) {
1071 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
1072 goto error2;
1073 }
Philipp Maier1cb1e382017-11-02 17:16:04 +01001074 if (allocate_port(endp, conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001075 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
Philipp Maier1cb1e382017-11-02 17:16:04 +01001076 goto error2;
1077 }
1078
Philipp Maier87bd9be2017-08-22 16:35:41 +02001079 if (setup_rtp_processing(endp, conn) != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001080 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
1081 "CRCX: could not start RTP processing!\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001082 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_START_RTP));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001083 goto error2;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001084 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001085
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001086 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
1087 "CRCX: Creating connection: port: %u\n", conn->end.local_port);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001088
Philipp Maiere726d4f2017-11-01 10:41:34 +01001089 /* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
Philipp Maier14b27a82020-06-02 20:15:30 +02001090 OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Pau Espin Pedrolca280a12021-07-06 18:15:35 +02001091 if (conn->conn->mode & MGCP_CONN_RECV_ONLY &&
1092 mgcp_rtp_end_remote_addr_available(&conn->end) &&
1093 trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001094 send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001095
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001096 LOGPCONN(_conn, DLMGCP, LOGL_NOTICE,
1097 "CRCX: connection successfully created\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001098 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_SUCCESS));
Philipp Maier889fe7f2020-07-06 17:44:12 +02001099 mgcp_endp_update(endp);
Philipp Maier41d59202021-07-20 15:49:00 +02001100
1101 /* NOTE: Only in the virtual trunk we allow dynamic endpoint names */
1102 bool add_epname = rq->wildcarded && trunk->trunk_type == MGCP_TRUNK_VIRTUAL;
1103 return create_response_with_sdp(endp, conn, "CRCX", pdata->trans, add_epname, true);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001104error2:
Philipp Maier1355d7e2018-02-01 14:30:06 +01001105 mgcp_endp_release(endp);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001106 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1107 "CRCX: unable to create connection\n");
Ericfbcf4a62021-09-09 18:02:31 +02001108 return create_err_response(endp, endp, error_code, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001109}
1110
Philipp Maier87bd9be2017-08-22 16:35:41 +02001111/* MDCX command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +02001112static struct msgb *handle_modify_con(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001113{
Philipp Maier8dc35972021-07-14 11:20:16 +02001114 struct mgcp_parse_data *pdata = rq->pdata;
1115 struct mgcp_trunk *trunk = rq->trunk;
1116 struct mgcp_endpoint *endp = rq->endp;
1117 struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_mdcx_ctr_group;
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001118 char new_local_addr[INET6_ADDRSTRLEN];
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001119 int error_code = 500;
1120 int silent = 0;
1121 int have_sdp = 0;
1122 char *line;
1123 const char *local_options = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001124 const char *mode = NULL;
1125 struct mgcp_conn_rtp *conn = NULL;
Philipp Maier01d24a32017-11-21 17:26:09 +01001126 const char *conn_id = NULL;
Pau Espin Pedrol21779192022-09-23 16:46:33 +02001127 int remote_osmux_cid = -2;
Philipp Maiera390d0b2018-01-31 17:30:19 +01001128 int rc;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001129
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001130 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "MDCX: modifying existing connection ...\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001131
Philipp Maier5656fbf2018-02-02 14:41:58 +01001132 /* Prohibit wildcarded requests */
Philipp Maier8dc35972021-07-14 11:20:16 +02001133 if (rq->wildcarded) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001134 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1135 "MDCX: wildcarded endpoint names not supported.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001136 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_WILDCARD));
Ericfbcf4a62021-09-09 18:02:31 +02001137 return create_err_response(rq->trunk, endp, 507, "MDCX", pdata->trans);
Philipp Maier5656fbf2018-02-02 14:41:58 +01001138 }
1139
Ericee6958c2021-09-14 18:36:53 +02001140 if (!endp || !mgcp_endp_avail(endp)) {
1141 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_AVAIL));
1142 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "MDCX: selected endpoint not available!\n");
1143 return create_err_response(rq->trunk, NULL, 501, "MDCX", pdata->trans);
1144 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001145 if (llist_count(&endp->conns) <= 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001146 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1147 "MDCX: endpoint is not holding a connection.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001148 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_CONN));
Ericfbcf4a62021-09-09 18:02:31 +02001149 return create_err_response(endp, endp, 400, "MDCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001150 }
1151
Philipp Maier8dc35972021-07-14 11:20:16 +02001152 for_each_line(line, pdata->save) {
Philipp Maier036612b2021-07-19 17:47:49 +02001153 if (!mgcp_check_param(endp, trunk, line))
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001154 continue;
1155
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001156 switch (toupper(line[0])) {
Philipp Maier87bd9be2017-08-22 16:35:41 +02001157 case 'C':
Harald Weltee35eeae2017-12-28 13:47:37 +01001158 if (mgcp_verify_call_id(endp, line + 3) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001159 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CALLID));
Harald Weltee35eeae2017-12-28 13:47:37 +01001160 error_code = 516;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001161 goto error3;
Harald Weltee35eeae2017-12-28 13:47:37 +01001162 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001163 break;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001164 case 'I':
Philipp Maier01d24a32017-11-21 17:26:09 +01001165 conn_id = (const char *)line + 3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001166 if ((error_code = mgcp_verify_ci(endp, conn_id))) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001167 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CONNID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001168 goto error3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001169 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001170 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001171 case 'L':
Philipp Maier87bd9be2017-08-22 16:35:41 +02001172 local_options = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001173 break;
1174 case 'M':
Philipp Maier87bd9be2017-08-22 16:35:41 +02001175 mode = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001176 break;
1177 case 'Z':
Pau Espin Pedrol9b508f62019-06-26 13:11:22 +02001178 silent = strcasecmp("noanswer", line + 3) == 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001179 break;
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001180 case 'X':
Pau Espin Pedrolc1bf4692019-05-14 16:23:24 +02001181 if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001182 /* If osmux is disabled, just skip setting it up */
Pau Espin Pedrol928a20b2022-09-23 15:38:24 +02001183 if (endp->trunk->cfg->osmux_use == OSMUX_USAGE_OFF)
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001184 break;
Pau Espin Pedrol21779192022-09-23 16:46:33 +02001185 remote_osmux_cid = mgcp_osmux_setup(endp, line);
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001186 break;
1187 }
1188 /* Ignore unknown X-headers */
1189 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001190 case '\0':
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001191 have_sdp = 1;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001192 goto mgcp_header_done;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001193 break;
1194 default:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001195 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1196 "MDCX: Unhandled MGCP option: '%c'/%d\n",
1197 line[0], line[0]);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001198 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_UNHANDLED_PARAM));
Ericfbcf4a62021-09-09 18:02:31 +02001199 return create_err_response(rq->trunk, NULL, 539, "MDCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001200 break;
1201 }
1202 }
1203
Philipp Maier87bd9be2017-08-22 16:35:41 +02001204mgcp_header_done:
Philipp Maier01d24a32017-11-21 17:26:09 +01001205 if (!conn_id) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001206 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1207 "MDCX: insufficient parameters, missing ci (connectionIdentifier)\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001208 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_CONNID));
Ericfbcf4a62021-09-09 18:02:31 +02001209 return create_err_response(endp, endp, 515, "MDCX", pdata->trans);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001210 }
1211
1212 conn = mgcp_conn_get_rtp(endp, conn_id);
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001213 if (!conn) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001214 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_CONN_NOT_FOUND));
Ericfbcf4a62021-09-09 18:02:31 +02001215 return create_err_response(endp, endp, 400, "MDCX", pdata->trans);
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001216 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001217
Oliver Smithe36b7752019-01-22 16:31:36 +01001218 mgcp_conn_watchdog_kick(conn->conn);
1219
Philipp Maier87bd9be2017-08-22 16:35:41 +02001220 if (mode) {
1221 if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001222 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_MODE));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001223 error_code = 517;
1224 goto error3;
1225 }
1226 } else
Pau Espin Pedrol209eb9f2019-04-24 12:03:04 +02001227 conn->conn->mode = conn->conn->mode_orig;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001228
Philipp Maierbc0346e2018-06-07 09:52:16 +02001229 /* Set local connection options, if present */
1230 if (local_options) {
Philipp Maier8dc35972021-07-14 11:20:16 +02001231 rc = set_local_cx_options(trunk->endpoints,
Philipp Maierbc0346e2018-06-07 09:52:16 +02001232 &endp->local_options, local_options);
1233 if (rc != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001234 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1235 "MDCX: invalid local connection options!\n");
Philipp Maierbc0346e2018-06-07 09:52:16 +02001236 error_code = rc;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001237 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS));
Philipp Maierbc0346e2018-06-07 09:52:16 +02001238 goto error3;
1239 }
1240 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001241
Philipp Maierbc0346e2018-06-07 09:52:16 +02001242 /* Handle codec information and decide for a suitable codec */
Philipp Maier8dc35972021-07-14 11:20:16 +02001243 rc = handle_codec_info(conn, rq, have_sdp, false);
Philipp Maierbc0346e2018-06-07 09:52:16 +02001244 mgcp_codec_summary(conn);
1245 if (rc) {
Philipp Maieraf07f662018-02-02 11:34:02 +01001246 error_code = rc;
1247 goto error3;
Philipp Maiera390d0b2018-01-31 17:30:19 +01001248 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001249
Philipp Maierc3cc6542018-02-02 12:58:42 +01001250 /* check connection mode setting */
1251 if (conn->conn->mode != MGCP_CONN_LOOPBACK
1252 && conn->conn->mode != MGCP_CONN_RECV_ONLY
Pau Espin Pedrold6769ea2021-07-06 19:44:27 +02001253 && !mgcp_rtp_end_remote_addr_available(&conn->end)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001254 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1255 "MDCX: selected connection mode type requires an opposite end!\n");
Philipp Maierc3cc6542018-02-02 12:58:42 +01001256 error_code = 527;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001257 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC));
Philipp Maierc3cc6542018-02-02 12:58:42 +01001258 goto error3;
1259 }
1260
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001261 if (mgcp_conn_rtp_is_osmux(conn)) {
Pau Espin Pedrol21779192022-09-23 16:46:33 +02001262 OSMO_ASSERT(conn->osmux.local_cid_allocated);
1263 if (remote_osmux_cid < -1) {
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001264 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1265 "MDCX: Failed to parse Osmux CID!\n");
1266 goto error3;
Pau Espin Pedrol21779192022-09-23 16:46:33 +02001267 } else if (remote_osmux_cid == -1) {
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001268 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1269 "MDCX: wilcard in MDCX is not supported!\n");
1270 goto error3;
Pau Espin Pedrol21779192022-09-23 16:46:33 +02001271 } else if (conn->osmux.remote_cid_present &&
1272 remote_osmux_cid != (int) conn->osmux.remote_cid) {
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001273 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1274 "MDCX: changing already allocated CID is not supported!\n");
1275 goto error3;
1276 }
Pau Espin Pedrol21779192022-09-23 16:46:33 +02001277 conn->osmux.remote_cid_present = true;
1278 conn->osmux.remote_cid = remote_osmux_cid;
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001279 }
Philipp Maierbc0346e2018-06-07 09:52:16 +02001280
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001281 /* MDCX may have provided a new remote address, which means we may need
1282 to update our announced IP addr and re-bind our local end. This can
1283 happen for instance if MGW initially provided an IPv4 during CRCX
1284 ACK, and now MDCX tells us the remote has an IPv6 address. */
Pau Espin Pedrol70c03f52022-10-04 16:49:41 +02001285 if (mgcp_get_local_addr(new_local_addr, conn) < 0) {
1286 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
1287 goto error3;
1288 }
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001289 if (strcmp(new_local_addr, conn->end.local_addr)) {
1290 osmo_strlcpy(conn->end.local_addr, new_local_addr, sizeof(conn->end.local_addr));
1291 mgcp_free_rtp_port(&conn->end);
1292 if (allocate_port(endp, conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001293 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001294 goto error3;
1295 }
1296 }
1297
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001298 if (setup_rtp_processing(endp, conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001299 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_START_RTP));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001300 goto error3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001301 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001302
Philipp Maier87bd9be2017-08-22 16:35:41 +02001303 mgcp_rtp_end_config(endp, 1, &conn->end);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001304
1305 /* modify */
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001306 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
1307 "MDCX: modified conn:%s\n", mgcp_conn_dump(conn->conn));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001308
Philipp Maiere726d4f2017-11-01 10:41:34 +01001309 /* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
Philipp Maier8dc35972021-07-14 11:20:16 +02001310 OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Pau Espin Pedrolca280a12021-07-06 18:15:35 +02001311 if (conn->conn->mode & MGCP_CONN_RECV_ONLY &&
1312 mgcp_rtp_end_remote_addr_available(&conn->end) &&
Philipp Maier8dc35972021-07-14 11:20:16 +02001313 trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001314 send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001315
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001316 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_SUCCESS));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001317 if (silent)
1318 goto out_silent;
1319
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001320 LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
1321 "MDCX: connection successfully modified\n");
Philipp Maier889fe7f2020-07-06 17:44:12 +02001322 mgcp_endp_update(endp);
Philipp Maier41d59202021-07-20 15:49:00 +02001323 return create_response_with_sdp(endp, conn, "MDCX", pdata->trans, false, false);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001324error3:
Ericfbcf4a62021-09-09 18:02:31 +02001325 return create_err_response(endp, endp, error_code, "MDCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001326
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001327out_silent:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001328 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "MDCX: silent exit\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001329 return NULL;
1330}
1331
Philipp Maier87bd9be2017-08-22 16:35:41 +02001332/* DLCX command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +02001333static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001334{
Philipp Maier8dc35972021-07-14 11:20:16 +02001335 struct mgcp_parse_data *pdata = rq->pdata;
1336 struct mgcp_trunk *trunk = rq->trunk;
1337 struct mgcp_endpoint *endp = rq->endp;
1338 struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_dlcx_ctr_group;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001339 int error_code = 400;
1340 int silent = 0;
1341 char *line;
1342 char stats[1048];
Philipp Maier01d24a32017-11-21 17:26:09 +01001343 const char *conn_id = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001344 struct mgcp_conn_rtp *conn = NULL;
Philipp Maierf486e742021-07-19 14:56:29 +02001345 unsigned int i;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001346
Philipp Maierf486e742021-07-19 14:56:29 +02001347 /* NOTE: In this handler we can not take it for granted that the endp
1348 * pointer will be populated, however a trunk is always guaranteed. */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001349
Philipp Maierf486e742021-07-19 14:56:29 +02001350 LOGPEPTR(endp, trunk, DLMGCP, LOGL_NOTICE, "DLCX: deleting connection(s) ...\n");
1351
1352 if (endp && !mgcp_endp_avail(endp)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001353 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_AVAIL));
Philipp Maiera910a812020-08-18 15:13:33 +02001354 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1355 "DLCX: selected endpoint not available!\n");
Ericfbcf4a62021-09-09 18:02:31 +02001356 return create_err_response(rq->trunk, NULL, 501, "DLCX", pdata->trans);
Philipp Maier8d6a1932020-06-18 12:19:31 +02001357 }
1358
Philipp Maierf486e742021-07-19 14:56:29 +02001359 if (endp && !rq->wildcarded && llist_empty(&endp->conns)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001360 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1361 "DLCX: endpoint is not holding a connection.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001362 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_NO_CONN));
Ericfbcf4a62021-09-09 18:02:31 +02001363 return create_err_response(endp, endp, 515, "DLCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001364 }
1365
Ericee6958c2021-09-14 18:36:53 +02001366 /* Handle wildcarded DLCX that refers to the whole trunk. This means
1367 * that we walk over all endpoints on the trunk in order to drop all
1368 * connections on the trunk. (see also RFC3435 Annex F.7) */
1369 if (rq->wildcarded) {
1370 int num_conns = 0;
1371 for (i = 0; i < trunk->number_endpoints; i++) {
1372 num_conns += llist_count(&trunk->endpoints[i]->conns);
1373 mgcp_endp_release(trunk->endpoints[i]);
1374 }
1375 rate_ctr_add(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS), num_conns);
1376 return create_ok_response(trunk, NULL, 200, "DLCX", pdata->trans);
1377 }
1378
Philipp Maier8dc35972021-07-14 11:20:16 +02001379 for_each_line(line, pdata->save) {
Philipp Maier036612b2021-07-19 17:47:49 +02001380 if (!mgcp_check_param(endp, trunk, line))
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001381 continue;
1382
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001383 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001384 case 'C':
Philipp Maierf486e742021-07-19 14:56:29 +02001385 /* If we have no endpoint, but a call id in the request,
1386 then this request cannot be handled */
1387 if (!endp) {
1388 LOGPTRUNK(trunk, DLMGCP, LOGL_NOTICE,
1389 "cannot handle requests with call-id (C) without endpoint -- abort!");
1390 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));
Ericfbcf4a62021-09-09 18:02:31 +02001391 return create_err_response(rq->trunk, NULL, 539, "DLCX", pdata->trans);
Philipp Maierf486e742021-07-19 14:56:29 +02001392 }
1393
Harald Weltee35eeae2017-12-28 13:47:37 +01001394 if (mgcp_verify_call_id(endp, line + 3) != 0) {
1395 error_code = 516;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001396 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CALLID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001397 goto error3;
Harald Weltee35eeae2017-12-28 13:47:37 +01001398 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001399 break;
1400 case 'I':
Philipp Maierf486e742021-07-19 14:56:29 +02001401 /* If we have no endpoint, but a connection id in the request,
1402 then this request cannot be handled */
1403 if (!endp) {
1404 LOGPTRUNK(trunk, DLMGCP, LOGL_NOTICE,
1405 "cannot handle requests with conn-id (I) without endpoint -- abort!");
1406 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));
Ericfbcf4a62021-09-09 18:02:31 +02001407 return create_err_response(rq->trunk, NULL, 539, "DLCX", pdata->trans);
Philipp Maierf486e742021-07-19 14:56:29 +02001408 }
1409
Philipp Maier01d24a32017-11-21 17:26:09 +01001410 conn_id = (const char *)line + 3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001411 if ((error_code = mgcp_verify_ci(endp, conn_id))) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001412 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CONNID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001413 goto error3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001414 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001415 break;
1416 case 'Z':
Pau Espin Pedrol9b508f62019-06-26 13:11:22 +02001417 silent = strcasecmp("noanswer", line + 3) == 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001418 break;
1419 default:
Philipp Maierf486e742021-07-19 14:56:29 +02001420 LOGPEPTR(endp, trunk, DLMGCP, LOGL_NOTICE, "DLCX: Unhandled MGCP option: '%c'/%d\n",
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001421 line[0], line[0]);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001422 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));
Ericfbcf4a62021-09-09 18:02:31 +02001423 return create_err_response(rq->trunk, NULL, 539, "DLCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001424 break;
1425 }
1426 }
1427
Philipp Maierce187052021-07-23 10:58:19 +02001428 /* The logic does not permit to go past this point without having the
1429 * the endp pointer populated. */
1430 OSMO_ASSERT(endp);
1431
Philipp Maierf4c0e372017-10-11 16:06:45 +02001432 /* When no connection id is supplied, we will interpret this as a
Philipp Maierf486e742021-07-19 14:56:29 +02001433 * wildcarded DLCX that refers to the selected endpoint. This means
1434 * that we drop all connections on that specific endpoint at once.
1435 * (See also RFC3435 Section F.7) */
Philipp Maier01d24a32017-11-21 17:26:09 +01001436 if (!conn_id) {
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001437 int num_conns = llist_count(&endp->conns);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001438 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1439 "DLCX: missing ci (connectionIdentifier), will remove all connections (%d total) at once\n",
1440 num_conns);
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001441
1442 if (num_conns > 0)
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001443 rate_ctr_add(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS), num_conns);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001444
Philipp Maier1355d7e2018-02-01 14:30:06 +01001445 mgcp_endp_release(endp);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001446
1447 /* Note: In this case we do not return any statistics,
1448 * as we assume that the client is not interested in
1449 * this case. */
Ericfbcf4a62021-09-09 18:02:31 +02001450 return create_ok_response(endp, endp, 200, "DLCX", pdata->trans);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001451 }
1452
Philipp Maierf4c0e372017-10-11 16:06:45 +02001453 /* Find the connection */
Philipp Maier87bd9be2017-08-22 16:35:41 +02001454 conn = mgcp_conn_get_rtp(endp, conn_id);
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001455 if (!conn) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001456 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CONNID));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001457 goto error3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001458 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001459 /* save the statistics of the current connection */
1460 mgcp_format_stats(stats, sizeof(stats), conn->conn);
1461
1462 /* delete connection */
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001463 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG, "DLCX: deleting conn:%s\n",
1464 mgcp_conn_dump(conn->conn));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001465 mgcp_conn_free(endp, conn_id);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001466 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1467 "DLCX: connection successfully deleted\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +02001468
1469 /* When all connections are closed, the endpoint will be released
1470 * in order to be ready to be used by another call. */
1471 if (llist_count(&endp->conns) <= 0) {
Philipp Maier1355d7e2018-02-01 14:30:06 +01001472 mgcp_endp_release(endp);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001473 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: endpoint released\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +02001474 }
1475
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001476 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001477 if (silent)
1478 goto out_silent;
Ericfbcf4a62021-09-09 18:02:31 +02001479 return create_ok_resp_with_param(endp, endp, 250, "DLCX", pdata->trans, stats);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001480
1481error3:
Ericfbcf4a62021-09-09 18:02:31 +02001482 return create_err_response(endp, endp, error_code, "DLCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001483
1484out_silent:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001485 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: silent exit\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001486 return NULL;
1487}
1488
Philipp Maier87bd9be2017-08-22 16:35:41 +02001489/* RSIP command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +02001490static struct msgb *handle_rsip(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001491{
Philipp Maier87bd9be2017-08-22 16:35:41 +02001492 /* TODO: Also implement the resetting of a specific endpoint
1493 * to make mgcp_send_reset_ep() work. Currently this will call
1494 * mgcp_rsip_cb() in mgw_main.c, which sets reset_endpoints=1
1495 * to make read_call_agent() reset all endpoints when called
1496 * next time. In order to selectively reset endpoints some
1497 * mechanism to distinguish which endpoint shall be resetted
1498 * is needed */
1499
1500 LOGP(DLMGCP, LOGL_NOTICE, "RSIP: resetting all endpoints ...\n");
1501
Philipp Maier8dc35972021-07-14 11:20:16 +02001502 if (rq->pdata->cfg->reset_cb)
1503 rq->pdata->cfg->reset_cb(rq->endp->trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001504 return NULL;
1505}
1506
1507static char extract_tone(const char *line)
1508{
1509 const char *str = strstr(line, "D/");
1510 if (!str)
1511 return CHAR_MAX;
1512
1513 return str[2];
1514}
1515
Philipp Maier87bd9be2017-08-22 16:35:41 +02001516/* This can request like DTMF detection and forward, fax detection... it
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001517 * can also request when the notification should be send and such. We don't
Philipp Maier87bd9be2017-08-22 16:35:41 +02001518 * do this right now. */
Philipp Maier8dc35972021-07-14 11:20:16 +02001519static struct msgb *handle_noti_req(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001520{
1521 int res = 0;
1522 char *line;
1523 char tone = CHAR_MAX;
1524
Philipp Maier87bd9be2017-08-22 16:35:41 +02001525 LOGP(DLMGCP, LOGL_NOTICE, "RQNT: processing request for notification ...\n");
1526
Philipp Maier8dc35972021-07-14 11:20:16 +02001527 for_each_line(line, rq->pdata->save) {
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001528 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001529 case 'S':
1530 tone = extract_tone(line);
1531 break;
1532 }
1533 }
1534
1535 /* we didn't see a signal request with a tone */
1536 if (tone == CHAR_MAX)
Ericfbcf4a62021-09-09 18:02:31 +02001537 return create_ok_response(rq->endp, rq->endp, 200, "RQNT", rq->pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001538
Philipp Maier8dc35972021-07-14 11:20:16 +02001539 if (rq->pdata->cfg->rqnt_cb)
1540 res = rq->pdata->cfg->rqnt_cb(rq->endp, tone);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001541
Ericfbcf4a62021-09-09 18:02:31 +02001542 return res == 0 ? create_ok_response(rq->endp, rq->endp, 200, "RQNT", rq->pdata->trans) :
1543 create_err_response(rq->endp, rq->endp, res, "RQNT", rq->pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001544}
1545
Philipp Maier87bd9be2017-08-22 16:35:41 +02001546/* Connection keepalive timer, will take care that dummy packets are send
Harald Welte1d1b98f2017-12-25 10:03:40 +01001547 * regularly, so that NAT connections stay open */
Philipp Maier14b27a82020-06-02 20:15:30 +02001548static void mgcp_keepalive_timer_cb(void *_trunk)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001549{
Philipp Maier14b27a82020-06-02 20:15:30 +02001550 struct mgcp_trunk *trunk = _trunk;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001551 struct mgcp_conn *conn;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001552 int i;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001553
Philipp Maiere726d4f2017-11-01 10:41:34 +01001554 LOGP(DLMGCP, LOGL_DEBUG, "triggered trunk %d keepalive timer\n",
Philipp Maier14b27a82020-06-02 20:15:30 +02001555 trunk->trunk_nr);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001556
Philipp Maiere726d4f2017-11-01 10:41:34 +01001557 /* Do not accept invalid configuration values
1558 * valid is MGCP_KEEPALIVE_NEVER, MGCP_KEEPALIVE_ONCE and
1559 * values greater 0 */
Philipp Maier14b27a82020-06-02 20:15:30 +02001560 OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Philipp Maiere726d4f2017-11-01 10:41:34 +01001561
1562 /* The dummy packet functionality has been disabled, we will exit
1563 * immediately, no further timer is scheduled, which means we will no
1564 * longer send dummy packets even when we did before */
Philipp Maier14b27a82020-06-02 20:15:30 +02001565 if (trunk->keepalive_interval == MGCP_KEEPALIVE_NEVER)
Philipp Maiere726d4f2017-11-01 10:41:34 +01001566 return;
1567
1568 /* In cases where only one dummy packet is sent, we do not need
1569 * the timer since the functions that handle the CRCX and MDCX are
1570 * triggering the sending of the dummy packet. So we behave like in
1571 * the MGCP_KEEPALIVE_NEVER case */
Philipp Maier14b27a82020-06-02 20:15:30 +02001572 if (trunk->keepalive_interval == MGCP_KEEPALIVE_ONCE)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001573 return;
1574
Philipp Maier87bd9be2017-08-22 16:35:41 +02001575 /* Send walk over all endpoints and send out dummy packets through
1576 * every connection present on each endpoint */
Philipp Maier4131a652021-07-07 14:04:34 +02001577 for (i = 0; i < trunk->number_endpoints; ++i) {
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001578 struct mgcp_endpoint *endp = trunk->endpoints[i];
Philipp Maier87bd9be2017-08-22 16:35:41 +02001579 llist_for_each_entry(conn, &endp->conns, entry) {
Pau Espin Pedrolca280a12021-07-06 18:15:35 +02001580 if (conn->type == MGCP_CONN_TYPE_RTP &&
1581 conn->mode == MGCP_CONN_RECV_ONLY &&
1582 mgcp_rtp_end_remote_addr_available(&conn->u.rtp.end))
Philipp Maier87bd9be2017-08-22 16:35:41 +02001583 send_dummy(endp, &conn->u.rtp);
1584 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001585 }
1586
Philipp Maiere726d4f2017-11-01 10:41:34 +01001587 /* Schedule the keepalive timer for the next round */
1588 LOGP(DLMGCP, LOGL_DEBUG, "rescheduling trunk %d keepalive timer\n",
Philipp Maier14b27a82020-06-02 20:15:30 +02001589 trunk->trunk_nr);
1590 osmo_timer_schedule(&trunk->keepalive_timer, trunk->keepalive_interval,
Philipp Maier87bd9be2017-08-22 16:35:41 +02001591 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001592}
1593
Philipp Maier14b27a82020-06-02 20:15:30 +02001594void mgcp_trunk_set_keepalive(struct mgcp_trunk *trunk, int interval)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001595{
Philipp Maier14b27a82020-06-02 20:15:30 +02001596 trunk->keepalive_interval = interval;
1597 osmo_timer_setup(&trunk->keepalive_timer, mgcp_keepalive_timer_cb, trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001598
1599 if (interval <= 0)
Philipp Maier14b27a82020-06-02 20:15:30 +02001600 osmo_timer_del(&trunk->keepalive_timer);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001601 else
Philipp Maier14b27a82020-06-02 20:15:30 +02001602 osmo_timer_schedule(&trunk->keepalive_timer,
1603 trunk->keepalive_interval, 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001604}
1605
Philipp Maier38533ba2021-07-29 17:38:34 +02001606/* Free config, this function is automatically called by talloc_free when the configuration is freed. */
1607static int config_free_talloc_destructor(struct mgcp_config *cfg)
1608{
1609 mgcp_ratectr_global_free(cfg);
1610 return 0;
1611}
1612
Philipp Maier87bd9be2017-08-22 16:35:41 +02001613/*! allocate configuration with default values.
1614 * (called once at startup by main function) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001615struct mgcp_config *mgcp_config_alloc(void)
1616{
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001617 /* FIXME: This is unrelated to the protocol, put this in some
1618 * appropiate place! */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001619 struct mgcp_config *cfg;
1620
1621 cfg = talloc_zero(NULL, struct mgcp_config);
1622 if (!cfg) {
1623 LOGP(DLMGCP, LOGL_FATAL, "Failed to allocate config.\n");
1624 return NULL;
1625 }
1626
Philipp Maier12943ea2018-01-17 15:40:25 +01001627 osmo_strlcpy(cfg->domain, "mgw", sizeof(cfg->domain));
1628
Eric55fdfc22021-08-13 00:14:18 +02001629 cfg->net_ports.lock = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001630 cfg->net_ports.range_start = RTP_PORT_DEFAULT_RANGE_START;
1631 cfg->net_ports.range_end = RTP_PORT_DEFAULT_RANGE_END;
1632 cfg->net_ports.last_port = cfg->net_ports.range_start;
1633
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001634 cfg->source_port = 2427;
Eric2764bdb2021-08-23 22:11:47 +02001635 osmo_strlcpy(cfg->source_addr, "0.0.0.0", sizeof(cfg->source_addr));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001636
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001637 cfg->rtp_processing_cb = &mgcp_rtp_processing_default;
1638 cfg->setup_rtp_processing_cb = &mgcp_setup_rtp_processing_default;
1639
1640 cfg->get_net_downlink_format_cb = &mgcp_get_net_downlink_format_default;
1641
Philipp Maierd19de2e2020-06-03 13:55:33 +02001642 INIT_LLIST_HEAD(&cfg->trunks);
1643
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001644 /* Allocate virtual trunk */
Philipp Maierd19de2e2020-06-03 13:55:33 +02001645 if (!mgcp_trunk_alloc(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID)) {
Harald Welte3ac604e2019-05-08 14:07:41 +02001646 talloc_free(cfg);
1647 return NULL;
1648 }
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001649
Philipp Maiera065e632021-07-09 13:22:42 +02001650 mgcp_ratectr_global_alloc(cfg);
Philipp Maier38533ba2021-07-29 17:38:34 +02001651 talloc_set_destructor(cfg, config_free_talloc_destructor);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001652
1653 return cfg;
1654}
1655
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001656static int send_agent(struct mgcp_config *cfg, const char *buf, int len)
1657{
1658 return write(cfg->gw_fd.bfd.fd, buf, len);
1659}
1660
Philipp Maier87bd9be2017-08-22 16:35:41 +02001661/*! Reset all endpoints by sending RSIP message to self.
1662 * (called by VTY)
1663 * \param[in] endp trunk endpoint
1664 * \param[in] endpoint number
1665 * \returns 0 on success, -1 on error */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001666int mgcp_send_reset_all(struct mgcp_config *cfg)
1667{
Philipp Maier12943ea2018-01-17 15:40:25 +01001668 char buf[MGCP_ENDPOINT_MAXLEN + 128];
1669 int len;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001670 int rc;
1671
Philipp Maier12943ea2018-01-17 15:40:25 +01001672 len = snprintf(buf, sizeof(buf),
1673 "RSIP 1 *@%s MGCP 1.0\r\n", cfg->domain);
1674 if (len < 0)
1675 return -1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001676
Philipp Maier12943ea2018-01-17 15:40:25 +01001677 rc = send_agent(cfg, buf, len);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001678 if (rc <= 0)
1679 return -1;
1680
1681 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001682}
1683
Philipp Maier87bd9be2017-08-22 16:35:41 +02001684/*! Reset a single endpoint by sending RSIP message to self.
1685 * (called by VTY)
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001686 * \param[in] endp to reset
Philipp Maier87bd9be2017-08-22 16:35:41 +02001687 * \returns 0 on success, -1 on error */
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001688int mgcp_send_reset_ep(struct mgcp_endpoint *endp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001689{
Philipp Maier12943ea2018-01-17 15:40:25 +01001690 char buf[MGCP_ENDPOINT_MAXLEN + 128];
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001691 int len;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001692 int rc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001693
1694 len = snprintf(buf, sizeof(buf),
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001695 "RSIP 39 %s MGCP 1.0\r\n", endp->name);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001696 if (len < 0)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001697 return -1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001698
Ericfbf78d12021-08-23 22:31:39 +02001699 rc = send_agent(endp->trunk->cfg, buf, len);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001700 if (rc <= 0)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001701 return -1;
1702
Philipp Maier87bd9be2017-08-22 16:35:41 +02001703 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001704}