blob: 506de3462f0bed1f195fef5afd682e84ca177a22 [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:
153 case MGCP_OSMUX_BSC:
154 case MGCP_OSMUX_BSC_NAT:
155 case MGCP_RTP_IUUP:
156 break;
157 default:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200158 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
159 "RTP-setup: Endpoint is not configured as RTP default, stopping here!\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200160 return 0;
161 }
162
Philipp Maier87bd9be2017-08-22 16:35:41 +0200163 if (conn->conn->mode == MGCP_CONN_LOOPBACK) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200164 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
165 "RTP-setup: Endpoint is in loopback mode, stopping here!\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +0200166 return 0;
167 }
168
169 /* Find the "sister" connection */
170 llist_for_each_entry(_conn, &endp->conns, entry) {
171 if (_conn->id != conn->conn->id) {
172 conn_src = &_conn->u.rtp;
173 break;
174 }
175 }
176
Philipp Maieracc10352018-07-19 18:07:57 +0200177 return cfg->setup_rtp_processing_cb(endp, conn_dst, conn_src);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200178}
179
Philipp Maier87bd9be2017-08-22 16:35:41 +0200180/* Helper function to allocate some memory for responses and retransmissions */
Ericfbcf4a62021-09-09 18:02:31 +0200181static struct msgb *mgcp_msgb_alloc(void *ctx)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200182{
183 struct msgb *msg;
Ericfbcf4a62021-09-09 18:02:31 +0200184 msg = msgb_alloc_headroom_c(ctx, 4096, 128, "MGCP msg");
185
186 if (!msg) {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200187 LOGP(DLMGCP, LOGL_ERROR, "Failed to msgb for MGCP data.\n");
Ericfbcf4a62021-09-09 18:02:31 +0200188 return NULL;
189 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200190
191 return msg;
192}
193
Philipp Maier87bd9be2017-08-22 16:35:41 +0200194/* Helper function for do_retransmission() and create_resp() */
Eric958f5e72021-08-03 23:00:17 +0200195static struct msgb *create_retransmission_response(const struct mgcp_endpoint *endp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200196{
Ericfbcf4a62021-09-09 18:02:31 +0200197 struct msgb *msg = mgcp_msgb_alloc(endp->trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200198 if (!msg)
199 return NULL;
200
201 msg->l2h = msgb_put(msg, strlen(endp->last_response));
202 memcpy(msg->l2h, endp->last_response, msgb_l2len(msg));
Philipp Maier87bd9be2017-08-22 16:35:41 +0200203 mgcp_disp_msg(msg->l2h, msgb_l2len(msg), "Retransmitted response");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200204 return msg;
205}
206
Ericfbcf4a62021-09-09 18:02:31 +0200207static struct msgb *create_resp(void *msgctx, struct mgcp_endpoint *endp, int code, const char *txt, const char *msg,
208 const char *trans, const char *param, const char *sdp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200209{
210 int len;
211 struct msgb *res;
212
Ericfbcf4a62021-09-09 18:02:31 +0200213 OSMO_ASSERT(msgctx != 0);
214 res = mgcp_msgb_alloc(msgctx);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200215 if (!res)
216 return NULL;
217
Philipp Maier87bd9be2017-08-22 16:35:41 +0200218 len = snprintf((char *)res->data, 2048, "%d %s%s%s\r\n%s",
219 code, trans, txt, param ? param : "", sdp ? sdp : "");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200220 if (len < 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200221 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "Failed to sprintf MGCP response.\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200222 msgb_free(res);
223 return NULL;
224 }
225
226 res->l2h = msgb_put(res, len);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200227 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "Generated response: code=%d\n", code);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200228 mgcp_disp_msg(res->l2h, msgb_l2len(res), "Generated response");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200229
230 /*
231 * Remember the last transmission per endpoint.
232 */
233 if (endp) {
Philipp Maier14b27a82020-06-02 20:15:30 +0200234 struct mgcp_trunk *trunk = endp->trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200235 talloc_free(endp->last_response);
236 talloc_free(endp->last_trans);
Philipp Maier14b27a82020-06-02 20:15:30 +0200237 endp->last_trans = talloc_strdup(trunk->endpoints, trans);
238 endp->last_response = talloc_strndup(trunk->endpoints,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200239 (const char *)res->l2h,
240 msgb_l2len(res));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200241 }
242
243 return res;
244}
245
Ericfbcf4a62021-09-09 18:02:31 +0200246static struct msgb *create_ok_resp_with_param(void *msgctx, struct mgcp_endpoint *endp, int code, const char *msg,
247 const char *trans, const char *param)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200248{
Ericfbcf4a62021-09-09 18:02:31 +0200249 return create_resp(msgctx, endp, code, " OK", msg, trans, param, NULL);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200250}
251
Ericfbcf4a62021-09-09 18:02:31 +0200252static struct msgb *create_ok_response(void *msgctx, struct mgcp_endpoint *endp, int code, const char *msg,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200253 const char *trans)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200254{
Ericfbcf4a62021-09-09 18:02:31 +0200255 return create_ok_resp_with_param(msgctx, endp, code, msg, trans, NULL);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200256}
257
Ericfbcf4a62021-09-09 18:02:31 +0200258static struct msgb *create_err_response(void *msgctx, struct mgcp_endpoint *endp, int code, const char *msg,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200259 const char *trans)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200260{
Ericfbcf4a62021-09-09 18:02:31 +0200261 return create_resp(msgctx, endp, code, " FAIL", msg, trans, NULL, NULL);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200262}
263
Philipp Maier87bd9be2017-08-22 16:35:41 +0200264/* Format MGCP response string (with SDP attached) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200265static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200266 struct mgcp_conn_rtp *conn,
267 const char *msg,
Philipp Maier55295f72018-01-15 14:00:28 +0100268 const char *trans_id,
Philipp Maier41d59202021-07-20 15:49:00 +0200269 bool add_epname,
270 bool add_conn_id)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200271{
Pau Espin Pedrol71d42e72020-09-03 14:20:07 +0200272 /* cfg->local_ip allows overwritting the announced IP address with
273 * regards to the one we actually bind to. Useful in behind-NAT
274 * scenarios.
275 * TODO: we may want to define another local_ip_osmux var to
276 * us for OSMUX connections. Perhaps adding a new internal API to get it
277 * based on conn type.
278 */
Ericfbf78d12021-08-23 22:31:39 +0200279 const char *addr = strlen(endp->trunk->cfg->local_ip) ? endp->trunk->cfg->local_ip : conn->end.local_addr;
Philipp Maier8970c492017-10-11 13:33:42 +0200280 struct msgb *sdp;
281 int rc;
282 struct msgb *result;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200283
Ericfbcf4a62021-09-09 18:02:31 +0200284 sdp = msgb_alloc_headroom_c(endp->trunk, 4096, 128, "sdp record");
Philipp Maier8970c492017-10-11 13:33:42 +0200285 if (!sdp)
286 return NULL;
287
Philipp Maier41d59202021-07-20 15:49:00 +0200288 /* Attach optional endpoint name */
289 if (add_epname) {
290 rc = msgb_printf(sdp, "Z: %s\r\n", endp->name);
291 if (rc < 0)
292 goto error;
293 }
294
295 /* Attach optional connection id */
296 if (add_conn_id) {
297 rc = msgb_printf(sdp, "I: %s\r\n", conn->conn->id);
Philipp Maier55295f72018-01-15 14:00:28 +0100298 if (rc < 0)
299 goto error;
300 }
301
Philipp Maier3cbfb8a2018-01-22 11:39:59 +0100302 /* Attach optional OSMUX parameters */
Pau Espin Pedrolc63f15a2019-05-10 16:52:08 +0200303 if (mgcp_conn_rtp_is_osmux(conn)) {
Pau Espin Pedrol21779192022-09-23 16:46:33 +0200304 rc = msgb_printf(sdp, MGCP_X_OSMO_OSMUX_HEADER " %u\r\n", conn->osmux.local_cid);
Philipp Maier3cbfb8a2018-01-22 11:39:59 +0100305 if (rc < 0)
306 goto error;
307 }
308
309 /* Attach line break to separate the parameters from the SDP block */
Philipp Maierc3cfae22018-01-22 12:03:03 +0100310 rc = msgb_printf(sdp, "\r\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200311
Philipp Maier8970c492017-10-11 13:33:42 +0200312 rc = mgcp_write_response_sdp(endp, conn, sdp, addr);
313 if (rc < 0)
314 goto error;
Ericfbcf4a62021-09-09 18:02:31 +0200315 result = create_resp(endp->trunk, endp, 200, " OK", msg, trans_id, NULL, (char *)sdp->data);
Philipp Maier8970c492017-10-11 13:33:42 +0200316 msgb_free(sdp);
317 return result;
318error:
319 msgb_free(sdp);
320 return NULL;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200321}
322
Philipp Maier87bd9be2017-08-22 16:35:41 +0200323/* Send out dummy packet to keep the connection open, if the connection is an
324 * osmux connection, send the dummy packet via OSMUX */
325static void send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200326{
Philipp Maier87bd9be2017-08-22 16:35:41 +0200327 if (conn->osmux.state != OSMUX_STATE_DISABLED)
328 osmux_send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200329 else
Philipp Maier87bd9be2017-08-22 16:35:41 +0200330 mgcp_send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200331}
332
Philipp Maier87bd9be2017-08-22 16:35:41 +0200333/* handle incoming messages:
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200334 * - this can be a command (four letters, space, transaction id)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200335 * - or a response (three numbers, space, transaction id) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200336struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
337{
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200338 struct rate_ctr_group *rate_ctrs = cfg->ratectr.mgcp_general_ctr_group;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200339 struct mgcp_parse_data pdata;
Philipp Maier8dc35972021-07-14 11:20:16 +0200340 struct mgcp_request_data rq;
Harald Weltee35eeae2017-12-28 13:47:37 +0100341 int rc, i, code, handled = 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200342 struct msgb *resp = NULL;
343 char *data;
344
Philipp Maier39889e42021-08-04 17:42:57 +0200345 debug_last_endpoint_name[0] = '\0';
346
Alexander Chemeris63866002020-05-05 17:18:40 +0300347 /* Count all messages, even incorect ones */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200348 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_TOTAL));
Alexander Chemeris63866002020-05-05 17:18:40 +0300349
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200350 if (msgb_l2len(msg) < 4) {
351 LOGP(DLMGCP, LOGL_ERROR, "msg too short: %d\n", msg->len);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200352 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200353 return NULL;
354 }
355
Alexander Chemeris63866002020-05-05 17:18:40 +0300356 if (mgcp_msg_terminate_nul(msg)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200357 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200358 return NULL;
Alexander Chemeris63866002020-05-05 17:18:40 +0300359 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200360
Philipp Maier87bd9be2017-08-22 16:35:41 +0200361 mgcp_disp_msg(msg->l2h, msgb_l2len(msg), "Received message");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200362
Philipp Maier87bd9be2017-08-22 16:35:41 +0200363 /* attempt to treat it as a response */
364 if (sscanf((const char *)&msg->l2h[0], "%3d %*s", &code) == 1) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200365 LOGP(DLMGCP, LOGL_DEBUG, "Response: Code: %d\n", code);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200366 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200367 return NULL;
368 }
369
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200370
Philipp Maier8dc35972021-07-14 11:20:16 +0200371 /* Parse message, extract endpoint name and transaction identifier and request name etc. */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200372 memset(&pdata, 0, sizeof(pdata));
Philipp Maier8dc35972021-07-14 11:20:16 +0200373 memset(&rq, 0, sizeof(rq));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200374 pdata.cfg = cfg;
Philipp Maier8dc35972021-07-14 11:20:16 +0200375 memcpy(rq.name, (const char *)&msg->l2h[0], sizeof(rq.name)-1);
376 msg->l3h = &msg->l2h[4];
Philipp Maier87bd9be2017-08-22 16:35:41 +0200377 data = mgcp_strline((char *)msg->l3h, &pdata.save);
Harald Weltee35eeae2017-12-28 13:47:37 +0100378 rc = mgcp_parse_header(&pdata, data);
Harald Weltee35eeae2017-12-28 13:47:37 +0100379 if (rc < 0) {
Philipp Maier8dc35972021-07-14 11:20:16 +0200380 LOGP(DLMGCP, LOGL_ERROR, "%s: failed to parse MCGP message\n", rq.name);
381 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
Ericfbcf4a62021-09-09 18:02:31 +0200382 return create_err_response(cfg, NULL, -rc, rq.name, "000000");
Harald Welteabbb6b92017-12-28 13:13:50 +0100383 }
384
Philipp Maier8dc35972021-07-14 11:20:16 +0200385 /* Locate endpoint and trunk, if no endpoint can be located try at least to identify the trunk. */
386 rq.pdata = &pdata;
387 rq.wildcarded = mgcp_endp_is_wildcarded(pdata.epname);
388 rq.endp = mgcp_endp_by_name(&rc, pdata.epname, pdata.cfg);
389 rq.mgcp_cause = rc;
390 if (!rq.endp) {
391 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_NO_ENDPOINT));
392 if (rq.wildcarded) {
393 /* If we are unable to find the endpoint we still may be able to identify the trunk. Some
394 * request handlers will still be able to perform a useful action if the request refers to
395 * the whole trunk (wildcarded request). */
396 LOGP(DLMGCP, LOGL_NOTICE,
397 "%s: cannot find endpoint \"%s\", cause=%d -- trying to identify trunk...\n", rq.name,
398 pdata.epname, -rq.mgcp_cause);
399 rq.trunk = mgcp_trunk_by_name(pdata.cfg, pdata.epname);
400 if (!rq.trunk) {
401 LOGP(DLMGCP, LOGL_ERROR, "%s: failed to identify trunk for endpoint \"%s\" -- abort\n",
402 rq.name, pdata.epname);
Ericfbcf4a62021-09-09 18:02:31 +0200403 return create_err_response(cfg, NULL, -rq.mgcp_cause, rq.name, pdata.trans);
Philipp Maier8dc35972021-07-14 11:20:16 +0200404 }
405 } else {
406 /* If the endpoint name suggests that the request refers to a specific endpoint, then the
407 * request cannot be handled and we must stop early. */
408 LOGP(DLMGCP, LOGL_NOTICE,
409 "%s: cannot find endpoint \"%s\", cause=%d -- abort\n", rq.name,
410 pdata.epname, -rq.mgcp_cause);
Ericfbcf4a62021-09-09 18:02:31 +0200411 return create_err_response(cfg, NULL, -rq.mgcp_cause, rq.name, pdata.trans);
Philipp Maier8dc35972021-07-14 11:20:16 +0200412 }
413 } else {
Philipp Maier39889e42021-08-04 17:42:57 +0200414 osmo_strlcpy(debug_last_endpoint_name, rq.endp->name, sizeof(debug_last_endpoint_name));
Philipp Maier8dc35972021-07-14 11:20:16 +0200415 rq.trunk = rq.endp->trunk;
416 rq.mgcp_cause = 0;
417
418 /* Check if we have to retransmit a response from a previous transaction */
419 if (pdata.trans && rq.endp->last_trans && strcmp(rq.endp->last_trans, pdata.trans) == 0) {
420 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_RETRANSMITTED));
Eric958f5e72021-08-03 23:00:17 +0200421 return create_retransmission_response(rq.endp);
Philipp Maier8dc35972021-07-14 11:20:16 +0200422 }
423 }
424
425 /* Find an appropriate handler for the current request and execute it */
426 for (i = 0; i < ARRAY_SIZE(mgcp_requests); i++) {
427 if (strcmp(mgcp_requests[i].name, rq.name) == 0) {
Philipp Maier8dc35972021-07-14 11:20:16 +0200428 /* Execute request handler */
429 if (rq.endp)
430 LOGP(DLMGCP, LOGL_INFO,
431 "%s: executing request handler \"%s\" for endpoint resource \"%s\"\n", rq.name,
432 mgcp_requests[i].debug_name, rq.endp->name);
433 else
434 LOGP(DLMGCP, LOGL_INFO,
435 "%s: executing request handler \"%s\" for trunk resource of endpoint \"%s\"\n",
436 rq.name, mgcp_requests[i].debug_name, pdata.epname);
437 resp = mgcp_requests[i].handle_request(&rq);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200438 handled = 1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200439 break;
440 }
441 }
442
Philipp Maier8dc35972021-07-14 11:20:16 +0200443 /* Check if the MGCP request was handled and increment rate counters accordingly. */
Alexander Chemeris63866002020-05-05 17:18:40 +0300444 if (handled) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200445 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_HANDLED));
Alexander Chemeris63866002020-05-05 17:18:40 +0300446 } else {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200447 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_UNHANDLED));
Philipp Maier8dc35972021-07-14 11:20:16 +0200448 LOGP(DLMGCP, LOGL_ERROR, "MSG with type: '%.4s' not handled\n", &msg->l2h[0]);
Alexander Chemeris63866002020-05-05 17:18:40 +0300449 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200450
451 return resp;
452}
453
Philipp Maier87bd9be2017-08-22 16:35:41 +0200454/* AUEP command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +0200455static struct msgb *handle_audit_endpoint(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200456{
Philipp Maier8dc35972021-07-14 11:20:16 +0200457 LOGPENDP(rq->endp, DLMGCP, LOGL_NOTICE, "AUEP: auditing endpoint ...\n");
Ericee6958c2021-09-14 18:36:53 +0200458 if (!rq->endp || !mgcp_endp_avail(rq->endp)) {
459 LOGPENDP(rq->endp, DLMGCP, LOGL_ERROR, "AUEP: selected endpoint not available!\n");
460 return create_err_response(rq->trunk, NULL, 501, "AUEP", rq->pdata->trans);
461 }
462
Ericfbcf4a62021-09-09 18:02:31 +0200463 return create_ok_response(rq->trunk, rq->endp, 200, "AUEP", rq->pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200464}
465
Harald Welte1d1b98f2017-12-25 10:03:40 +0100466/* Try to find a free port by attempting to bind on it. Also handle the
Philipp Maier87bd9be2017-08-22 16:35:41 +0200467 * counter that points on the next free port. Since we have a pointer
Philipp Maierb38fb892018-05-22 13:52:21 +0200468 * to the next free port, binding should in work on the first attempt in
Pau Espin Pedrolfc806732019-04-23 00:18:43 +0200469 * general. In case of failure the next port is tried until the whole port
470 * range is tried once. */
Philipp Maier87bd9be2017-08-22 16:35:41 +0200471static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200472{
473 int i;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200474 struct mgcp_port_range *range;
Philipp Maierb38fb892018-05-22 13:52:21 +0200475 unsigned int tries;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200476
Philipp Maier87bd9be2017-08-22 16:35:41 +0200477 OSMO_ASSERT(conn);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200478
Ericfbf78d12021-08-23 22:31:39 +0200479 range = &endp->trunk->cfg->net_ports;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200480
Eric55fdfc22021-08-13 00:14:18 +0200481 pthread_mutex_lock(&range->lock);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200482 /* attempt to find a port */
Philipp Maierb38fb892018-05-22 13:52:21 +0200483 tries = (range->range_end - range->range_start) / 2;
484 for (i = 0; i < tries; ++i) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200485 int rc;
486
487 if (range->last_port >= range->range_end)
488 range->last_port = range->range_start;
489
Philipp Maier87bd9be2017-08-22 16:35:41 +0200490 rc = mgcp_bind_net_rtp_port(endp, range->last_port, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200491
492 range->last_port += 2;
493 if (rc == 0) {
Eric55fdfc22021-08-13 00:14:18 +0200494 pthread_mutex_unlock(&range->lock);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200495 return 0;
496 }
497
498 }
Eric55fdfc22021-08-13 00:14:18 +0200499 pthread_mutex_unlock(&range->lock);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200500 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
501 "Allocating a RTP/RTCP port failed %u times.\n",
502 tries);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200503 return -1;
504}
505
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200506/*! Helper function for check_local_cx_options() to get a pointer of the next
507 * lco option identifier
508 * \param[in] lco string
509 * \returns pointer to the beginning of the LCO identifier, NULL on failure */
510char *get_lco_identifier(const char *options)
511{
512 char *ptr;
513 unsigned int count = 0;
514
515 /* Jump to the end of the lco identifier */
516 ptr = strstr(options, ":");
517 if (!ptr)
518 return NULL;
519
520 /* Walk backwards until the pointer points to the beginning of the
521 * lco identifier. We know that we stand at the beginning when we
522 * are either at the beginning of the memory or see a space or
523 * comma. (this is tolerant, it will accept a:10, b:11 as well as
524 * a:10,b:11) */
525 while (1) {
526 /* Endless loop protection */
527 if (count > 10000)
528 return NULL;
529 else if (ptr < options || *ptr == ' ' || *ptr == ',') {
530 ptr++;
531 break;
532 }
533 ptr--;
534 count++;
535 }
536
537 /* Check if we got any result */
538 if (*ptr == ':')
539 return NULL;
540
541 return ptr;
542}
543
544/*! Check the LCO option. This function checks for multiple appearence of LCO
545 * options, which is illegal
546 * \param[in] ctx talloc context
547 * \param[in] lco string
548 * \returns 0 on success, -1 on failure */
549int check_local_cx_options(void *ctx, const char *options)
550{
551 int i;
552 char *options_copy;
553 char *lco_identifier;
554 char *lco_identifier_end;
555 char *next_lco_identifier;
556
557 char **lco_seen;
558 unsigned int lco_seen_n = 0;
559
560 if (!options)
561 return -1;
562
563 lco_seen =
564 (char **)talloc_zero_size(ctx, strlen(options) * sizeof(char *));
565 options_copy = talloc_strdup(ctx, options);
566 lco_identifier = options_copy;
567
568 do {
569 /* Move the lco_identifier pointer to the beginning of the
570 * current lco option identifier */
571 lco_identifier = get_lco_identifier(lco_identifier);
572 if (!lco_identifier)
573 goto error;
574
575 /* Look ahead to the next LCO option early, since we
576 * will parse destructively */
577 next_lco_identifier = strstr(lco_identifier + 1, ",");
578
579 /* Pinch off the end of the lco field identifier name
580 * and see if we still got something, also check if
581 * there is some value after the colon. */
582 lco_identifier_end = strstr(lco_identifier, ":");
583 if (!lco_identifier_end)
584 goto error;
585 if (*(lco_identifier_end + 1) == ' '
586 || *(lco_identifier_end + 1) == ','
587 || *(lco_identifier_end + 1) == '\0')
588 goto error;
589 *lco_identifier_end = '\0';
590 if (strlen(lco_identifier) == 0)
591 goto error;
592
593 /* Check if we have already seen the current field identifier
594 * before. If yes, we must bail, an LCO must only appear once
595 * in the LCO string */
596 for (i = 0; i < lco_seen_n; i++) {
Pau Espin Pedrol7eb6f2c2019-06-26 13:00:52 +0200597 if (strcasecmp(lco_seen[i], lco_identifier) == 0)
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200598 goto error;
599 }
600 lco_seen[lco_seen_n] = lco_identifier;
601 lco_seen_n++;
602
603 /* The first identifier must always be found at the beginnning
604 * of the LCO string */
605 if (lco_seen[0] != options_copy)
606 goto error;
607
608 /* Go to the next lco option */
609 lco_identifier = next_lco_identifier;
610 } while (lco_identifier);
611
612 talloc_free(lco_seen);
613 talloc_free(options_copy);
614 return 0;
615error:
616 talloc_free(lco_seen);
617 talloc_free(options_copy);
618 return -1;
619}
620
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200621/* Set the LCO from a string (see RFC 3435).
Harald Welte1d1b98f2017-12-25 10:03:40 +0100622 * The string is stored in the 'string' field. A NULL string is handled exactly
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200623 * like an empty string, the 'string' field is never NULL after this function
624 * has been called. */
Philipp Maiera390d0b2018-01-31 17:30:19 +0100625static int set_local_cx_options(void *ctx, struct mgcp_lco *lco,
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200626 const char *options)
627{
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200628 char *lco_id;
Philipp Maier8dbc9ed2018-10-26 14:50:25 +0200629 char codec[17];
Pau Espin Pedrol19539862020-09-21 11:43:41 +0200630 char nt[17];
Pau Espin Pedrol83fd8a52019-06-26 12:55:26 +0200631 int len;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200632
Philipp Maier604410c2018-06-06 10:02:16 +0200633 if (!options)
634 return 0;
635 if (strlen(options) == 0)
636 return 0;
637
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200638 /* Make sure the encoding of the LCO is consistant before we proceed */
639 if (check_local_cx_options(ctx, options) != 0) {
640 LOGP(DLMGCP, LOGL_ERROR,
641 "local CX options: Internal inconsistency in Local Connection Options!\n");
642 return 524;
643 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200644
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200645 talloc_free(lco->string);
646 lco->string = talloc_strdup(ctx, options);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200647
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200648 lco_id = lco->string;
649 while ((lco_id = get_lco_identifier(lco_id))) {
650 switch (tolower(lco_id[0])) {
651 case 'p':
652 if (sscanf(lco_id + 1, ":%d-%d",
653 &lco->pkt_period_min, &lco->pkt_period_max) == 1)
654 lco->pkt_period_max = lco->pkt_period_min;
655 break;
656 case 'a':
Pau Espin Pedrol1dc2dce2020-09-21 11:25:15 +0200657 /* FIXME: LCO also supports the negotiation of more than one codec.
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200658 * (e.g. a:PCMU;G726-32) But this implementation only supports a single
659 * codec only. */
660 if (sscanf(lco_id + 1, ":%16[^,]", codec) == 1) {
661 talloc_free(lco->codec);
Pau Espin Pedrol83fd8a52019-06-26 12:55:26 +0200662 /* MGCP header is case insensive, and we'll need
663 codec in uppercase when using it later: */
664 len = strlen(codec);
665 lco->codec = talloc_size(ctx, len + 1);
666 osmo_str_toupper_buf(lco->codec, len + 1, codec);
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200667 }
668 break;
Pau Espin Pedrol19539862020-09-21 11:43:41 +0200669 case 'n':
670 if (lco_id[1] == 't' && sscanf(lco_id + 2, ":%16[^,]", nt) == 1)
671 break;
672 /* else: fall throught to print notice log */
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200673 default:
674 LOGP(DLMGCP, LOGL_NOTICE,
675 "LCO: unhandled option: '%c'/%d in \"%s\"\n",
676 *lco_id, *lco_id, lco->string);
677 break;
678 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200679
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200680 lco_id = strchr(lco_id, ',');
681 if (!lco_id)
682 break;
Philipp Maier604410c2018-06-06 10:02:16 +0200683 }
Philipp Maier87bd9be2017-08-22 16:35:41 +0200684
685 LOGP(DLMGCP, LOGL_DEBUG,
686 "local CX options: lco->pkt_period_max: %i, lco->codec: %s\n",
687 lco->pkt_period_max, lco->codec);
Philipp Maiera390d0b2018-01-31 17:30:19 +0100688
689 /* Check if the packetization fits the 20ms raster */
690 if (lco->pkt_period_min % 20 && lco->pkt_period_max % 20) {
691 LOGP(DLMGCP, LOGL_ERROR,
692 "local CX options: packetization interval is not a multiple of 20ms!\n");
693 return 535;
694 }
695
696 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200697}
698
699void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
700 struct mgcp_rtp_end *rtp)
701{
Philipp Maier14b27a82020-06-02 20:15:30 +0200702 struct mgcp_trunk *trunk = endp->trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200703
Philipp Maier14b27a82020-06-02 20:15:30 +0200704 int patch_ssrc = expect_ssrc_change && trunk->force_constant_ssrc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200705
Philipp Maier14b27a82020-06-02 20:15:30 +0200706 rtp->force_aligned_timing = trunk->force_aligned_timing;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200707 rtp->force_constant_ssrc = patch_ssrc ? 1 : 0;
Philipp Maier14b27a82020-06-02 20:15:30 +0200708 rtp->rfc5993_hr_convert = trunk->rfc5993_hr_convert;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200709
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200710 LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
711 "Configuring RTP endpoint: local port %d%s%s\n",
712 ntohs(rtp->rtp_port),
713 rtp->force_aligned_timing ? ", force constant timing" : "",
714 rtp->force_constant_ssrc ? ", force constant ssrc" : "");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200715}
716
Pau Espin Pedrol8358c4b2021-07-07 12:41:38 +0200717uint32_t mgcp_rtp_packet_duration(const struct mgcp_endpoint *endp,
718 const struct mgcp_rtp_end *rtp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200719{
720 int f = 0;
721
722 /* Get the number of frames per channel and packet */
723 if (rtp->frames_per_packet)
724 f = rtp->frames_per_packet;
Philipp Maierbc0346e2018-06-07 09:52:16 +0200725 else if (rtp->packet_duration_ms && rtp->codec->frame_duration_num) {
726 int den = 1000 * rtp->codec->frame_duration_num;
727 f = (rtp->packet_duration_ms * rtp->codec->frame_duration_den +
Philipp Maier87bd9be2017-08-22 16:35:41 +0200728 den / 2)
729 / den;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200730 }
731
Philipp Maierbc0346e2018-06-07 09:52:16 +0200732 return rtp->codec->rate * f * rtp->codec->frame_duration_num /
733 rtp->codec->frame_duration_den;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200734}
735
Pau Espin Pedrol9fb8ddf2019-05-08 15:35:36 +0200736/*! Initializes osmux socket if not yet initialized. Parses Osmux CID from MGCP line.
737 * \param[in] endp Endpoint willing to initialize osmux
738 * \param[in] line Line X-Osmux from MGCP header msg to parse
739 * \returns OSMUX CID, -1 for wildcard, -2 on parse error, -3 on osmux initalize error
740 */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200741static int mgcp_osmux_setup(struct mgcp_endpoint *endp, const char *line)
742{
Ericfbf78d12021-08-23 22:31:39 +0200743 if (!endp->trunk->cfg->osmux_init) {
Pau Espin Pedrol21bcf6a2022-09-22 18:23:04 +0200744 if (osmux_init(OSMUX_ROLE_BSC, endp->trunk) < 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200745 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "Cannot init OSMUX\n");
Pau Espin Pedrol9fb8ddf2019-05-08 15:35:36 +0200746 return -3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200747 }
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200748 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "OSMUX socket has been set up\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200749 }
750
751 return mgcp_parse_osmux_cid(line);
752}
753
Philipp Maierbc0346e2018-06-07 09:52:16 +0200754/* Process codec information contained in CRCX/MDCX */
755static int handle_codec_info(struct mgcp_conn_rtp *conn,
Philipp Maier8dc35972021-07-14 11:20:16 +0200756 struct mgcp_request_data *rq, int have_sdp, bool crcx)
Philipp Maierbc0346e2018-06-07 09:52:16 +0200757{
Philipp Maier8dc35972021-07-14 11:20:16 +0200758 struct mgcp_endpoint *endp = rq->endp;
Philipp Maierbc0346e2018-06-07 09:52:16 +0200759 int rc;
760 char *cmd;
761
762 if (crcx)
763 cmd = "CRCX";
764 else
765 cmd = "MDCX";
766
767 /* Collect codec information */
768 if (have_sdp) {
769 /* If we have SDP, we ignore the local connection options and
770 * use only the SDP information. */
771 mgcp_codec_reset_all(conn);
Philipp Maier8dc35972021-07-14 11:20:16 +0200772 rc = mgcp_parse_sdp_data(endp, conn, rq->pdata);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200773 if (rc != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200774 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
775 "%s: sdp not parseable\n", cmd);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200776
777 /* See also RFC 3661: Protocol error */
778 return 510;
779 }
780 } else if (endp->local_options.codec) {
781 /* When no SDP is available, we use the codec information from
782 * the local connection options (if present) */
783 mgcp_codec_reset_all(conn);
Philipp Maier228e5912019-03-05 13:56:59 +0100784 rc = mgcp_codec_add(conn, PTYPE_UNDEFINED, endp->local_options.codec, NULL);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200785 if (rc != 0)
786 goto error;
787 }
788
789 /* Make sure we always set a sane default codec */
790 if (conn->end.codecs_assigned == 0) {
791 /* When SDP and/or LCO did not supply any codec information,
792 * than it makes sense to pick a sane default: (payload-type 0,
793 * PCMU), see also: OS#2658 */
794 mgcp_codec_reset_all(conn);
Philipp Maier228e5912019-03-05 13:56:59 +0100795 rc = mgcp_codec_add(conn, 0, NULL, NULL);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200796 if (rc != 0)
797 goto error;
798 }
799
800 /* Make codec decision */
801 if (mgcp_codec_decide(conn) != 0)
802 goto error;
803
804 return 0;
805
806error:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200807 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
808 "%s: codec negotiation failure\n", cmd);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200809
810 /* See also RFC 3661: Codec negotiation failure */
811 return 534;
812}
813
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200814static bool parse_x_osmo_ign(struct mgcp_endpoint *endp, char *line)
815{
816 char *saveptr = NULL;
817
Pau Espin Pedrol6049a632020-09-21 11:03:21 +0200818 if (strncasecmp(line, MGCP_X_OSMO_IGN_HEADER, strlen(MGCP_X_OSMO_IGN_HEADER)))
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200819 return false;
820 line += strlen(MGCP_X_OSMO_IGN_HEADER);
821
822 while (1) {
823 char *token = strtok_r(line, " ", &saveptr);
824 line = NULL;
825 if (!token)
826 break;
827
Pau Espin Pedrol6e26c702019-06-26 13:09:39 +0200828 if (!strcasecmp(token, "C"))
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200829 endp->x_osmo_ign |= MGCP_X_OSMO_IGN_CALLID;
830 else
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200831 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "received unknown X-Osmo-IGN item '%s'\n", token);
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200832 }
833
834 return true;
835}
836
Philipp Maier87bd9be2017-08-22 16:35:41 +0200837/* CRCX command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +0200838static struct msgb *handle_create_con(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200839{
Philipp Maier8dc35972021-07-14 11:20:16 +0200840 struct mgcp_parse_data *pdata = rq->pdata;
841 struct mgcp_trunk *trunk = rq->trunk;
842 struct mgcp_endpoint *endp = rq->endp;
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200843 struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_crcx_ctr_group;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200844 int error_code = 400;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200845 const char *local_options = NULL;
846 const char *callid = NULL;
847 const char *mode = NULL;
848 char *line;
Pau Espin Pedrol21779192022-09-23 16:46:33 +0200849 int have_sdp = 0, remote_osmux_cid = -2;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200850 struct mgcp_conn_rtp *conn = NULL;
Philipp Maierffd75e42017-11-22 11:44:50 +0100851 struct mgcp_conn *_conn = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200852 char conn_name[512];
Philipp Maiera390d0b2018-01-31 17:30:19 +0100853 int rc;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200854
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200855 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "CRCX: creating new connection ...\n");
Philipp Maier246233d2020-08-18 15:15:24 +0200856
Ericee6958c2021-09-14 18:36:53 +0200857 /* we must have a free ep */
858 if (!endp) {
859 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_AVAIL));
860 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "CRCX: no free endpoints available!\n");
861 return create_err_response(rq->trunk, NULL, 403, "CRCX", pdata->trans);
862 }
863
Philipp Maier8d6a1932020-06-18 12:19:31 +0200864 if (!mgcp_endp_avail(endp)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200865 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_AVAIL));
Philipp Maiera910a812020-08-18 15:13:33 +0200866 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
867 "CRCX: selected endpoint not available!\n");
Ericfbcf4a62021-09-09 18:02:31 +0200868 return create_err_response(rq->trunk, NULL, 501, "CRCX", pdata->trans);
Philipp Maier8d6a1932020-06-18 12:19:31 +0200869 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200870
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200871 /* parse CallID C: and LocalParameters L: */
Philipp Maier8dc35972021-07-14 11:20:16 +0200872 for_each_line(line, pdata->save) {
Philipp Maier036612b2021-07-19 17:47:49 +0200873 if (!mgcp_check_param(endp, trunk, line))
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200874 continue;
875
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +0200876 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200877 case 'L':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200878 local_options = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200879 break;
880 case 'C':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200881 callid = (const char *)line + 3;
882 break;
883 case 'I':
Philipp Maierffd75e42017-11-22 11:44:50 +0100884 /* It is illegal to send a connection identifier
885 * together with a CRCX, the MGW will assign the
886 * connection identifier by itself on CRCX */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200887 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BAD_ACTION));
Ericfbcf4a62021-09-09 18:02:31 +0200888 return create_err_response(rq->trunk, NULL, 523, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200889 break;
890 case 'M':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200891 mode = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200892 break;
893 case 'X':
Pau Espin Pedrolc1bf4692019-05-14 16:23:24 +0200894 if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200895 /* If osmux is disabled, just skip setting it up */
Pau Espin Pedrol928a20b2022-09-23 15:38:24 +0200896 if (rq->endp->trunk->cfg->osmux_use == OSMUX_USAGE_OFF)
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200897 break;
Pau Espin Pedrol21779192022-09-23 16:46:33 +0200898 remote_osmux_cid = mgcp_osmux_setup(endp, line);
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200899 break;
900 }
901
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200902 if (parse_x_osmo_ign(endp, line))
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200903 break;
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200904
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200905 /* Ignore unknown X-headers */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200906 break;
907 case '\0':
908 have_sdp = 1;
909 goto mgcp_header_done;
910 default:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200911 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
912 "CRCX: unhandled option: '%c'/%d\n", *line, *line);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200913 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_UNHANDLED_PARAM));
Ericfbcf4a62021-09-09 18:02:31 +0200914 return create_err_response(rq->trunk, NULL, 539, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200915 break;
916 }
917 }
918
919mgcp_header_done:
Philipp Maier87bd9be2017-08-22 16:35:41 +0200920 /* Check parameters */
921 if (!callid) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200922 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
923 "CRCX: insufficient parameters, missing callid\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200924 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_MISSING_CALLID));
Ericfbcf4a62021-09-09 18:02:31 +0200925 return create_err_response(endp, endp, 516, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200926 }
927
Philipp Maier87bd9be2017-08-22 16:35:41 +0200928 if (!mode) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200929 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
930 "CRCX: insufficient parameters, missing mode\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200931 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_MODE));
Ericfbcf4a62021-09-09 18:02:31 +0200932 return create_err_response(endp, endp, 517, "CRCX", pdata->trans);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200933 }
934
Philipp Maier87bd9be2017-08-22 16:35:41 +0200935 /* Check if we are able to accept the creation of another connection */
936 if (llist_count(&endp->conns) >= endp->type->max_conns) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200937 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
938 "CRCX: endpoint full, max. %i connections allowed!\n",
939 endp->type->max_conns);
Philipp Maier14b27a82020-06-02 20:15:30 +0200940 if (trunk->force_realloc) {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200941 /* There is no more room for a connection, make some
942 * room by blindly tossing the oldest of the two two
943 * connections */
944 mgcp_conn_free_oldest(endp);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200945 } else {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200946 /* There is no more room for a connection, leave
947 * everything as it is and return with an error */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200948 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_LIMIT_EXCEEDED));
Ericfbcf4a62021-09-09 18:02:31 +0200949 return create_err_response(endp, endp, 540, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200950 }
951 }
952
Philipp Maier87bd9be2017-08-22 16:35:41 +0200953 /* Check if this endpoint already serves a call, if so, check if the
954 * callids match up so that we are sure that this is our call */
955 if (endp->callid && mgcp_verify_call_id(endp, callid)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200956 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
957 "CRCX: already seized by other call (%s)\n",
958 endp->callid);
Philipp Maier14b27a82020-06-02 20:15:30 +0200959 if (trunk->force_realloc)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200960 /* This is not our call, toss everything by releasing
961 * the entire endpoint. (rude!) */
Philipp Maier1355d7e2018-02-01 14:30:06 +0100962 mgcp_endp_release(endp);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200963 else {
964 /* This is not our call, leave everything as it is and
965 * return with an error. */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200966 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_UNKNOWN_CALLID));
Ericfbcf4a62021-09-09 18:02:31 +0200967 return create_err_response(endp, endp, 400, "CRCX", pdata->trans);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200968 }
969 }
970
Philipp Maier889fe7f2020-07-06 17:44:12 +0200971 if (!endp->callid) {
972 /* Claim endpoint resources. This will also set the callid,
973 * creating additional connections will only be possible if
974 * the callid matches up (see above). */
975 rc = mgcp_endp_claim(endp, callid);
976 if (rc != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200977 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_CLAIM));
Ericfbcf4a62021-09-09 18:02:31 +0200978 return create_err_response(endp, endp, 502, "CRCX", pdata->trans);
Philipp Maier889fe7f2020-07-06 17:44:12 +0200979 }
980 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200981
Philipp Maierffd75e42017-11-22 11:44:50 +0100982 snprintf(conn_name, sizeof(conn_name), "%s", callid);
Philipp Maier14b27a82020-06-02 20:15:30 +0200983 _conn = mgcp_conn_alloc(trunk->endpoints, endp, MGCP_CONN_TYPE_RTP, conn_name);
Philipp Maierffd75e42017-11-22 11:44:50 +0100984 if (!_conn) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200985 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
986 "CRCX: unable to allocate RTP connection\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200987 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_ALLOC_CONN));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200988 goto error2;
989
Philipp Maier87bd9be2017-08-22 16:35:41 +0200990 }
Philipp Maier889fe7f2020-07-06 17:44:12 +0200991
Philipp Maierffd75e42017-11-22 11:44:50 +0100992 conn = mgcp_conn_get_rtp(endp, _conn->id);
993 OSMO_ASSERT(conn);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200994
995 if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
996 error_code = 517;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200997 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_MODE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200998 goto error2;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200999 }
1000
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001001 /* Annotate Osmux circuit ID and set it to negotiating state until this
Philipp Maier87bd9be2017-08-22 16:35:41 +02001002 * is fully set up from the dummy load. */
1003 conn->osmux.state = OSMUX_STATE_DISABLED;
Pau Espin Pedrol21779192022-09-23 16:46:33 +02001004 /* If X-Osmux (remote CID) was received (-1 is wilcard), alloc next avail CID as local CID */
1005 if (remote_osmux_cid >= -1) {
Pau Espin Pedrol14f8a082019-05-13 13:10:06 +02001006 conn->osmux.state = OSMUX_STATE_ACTIVATING;
Pau Espin Pedrol21779192022-09-23 16:46:33 +02001007 if (conn_osmux_allocate_local_cid(conn) == -1) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001008 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_OSMUX));
Pau Espin Pedrol2b896172019-04-24 13:47:23 +02001009 goto error2;
1010 }
Pau Espin Pedrol21779192022-09-23 16:46:33 +02001011 if (remote_osmux_cid >= 0) {
1012 conn->osmux.remote_cid_present = true;
1013 conn->osmux.remote_cid = remote_osmux_cid;
1014 }
Pau Espin Pedrol928a20b2022-09-23 15:38:24 +02001015 } else if (endp->trunk->cfg->osmux_use == OSMUX_USAGE_ONLY) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001016 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
1017 "CRCX: osmux only and no osmux offered\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001018 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_OSMUX));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001019 goto error2;
1020 }
1021
Philipp Maierbc0346e2018-06-07 09:52:16 +02001022 /* Set local connection options, if present */
1023 if (local_options) {
Philipp Maier8dc35972021-07-14 11:20:16 +02001024 rc = set_local_cx_options(trunk->endpoints,
Philipp Maierbc0346e2018-06-07 09:52:16 +02001025 &endp->local_options, local_options);
1026 if (rc != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001027 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
1028 "CRCX: inavlid local connection options!\n");
Philipp Maierbc0346e2018-06-07 09:52:16 +02001029 error_code = rc;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001030 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS));
Philipp Maierbc0346e2018-06-07 09:52:16 +02001031 goto error2;
1032 }
1033 }
1034
1035 /* Handle codec information and decide for a suitable codec */
Philipp Maier8dc35972021-07-14 11:20:16 +02001036 rc = handle_codec_info(conn, rq, have_sdp, true);
Philipp Maierbc0346e2018-06-07 09:52:16 +02001037 mgcp_codec_summary(conn);
1038 if (rc) {
1039 error_code = rc;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001040 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_CODEC_NEGOTIATION));
Philipp Maierbc0346e2018-06-07 09:52:16 +02001041 goto error2;
1042 }
Pau Espin Pedrolbb3ccde2021-12-23 19:49:26 +01001043 /* Upgrade the conn type RTP_DEFAULT->RTP_IUUP if needed based on requested codec: */
1044 /* TODO: "codec" probably needs to be moved from endp to conn */
1045 if (conn->type == MGCP_RTP_DEFAULT && strcmp(conn->end.codec->subtype_name, "VND.3GPP.IUFP") == 0) {
1046 rc = mgcp_conn_iuup_init(conn);
1047 }
Philipp Maierbc0346e2018-06-07 09:52:16 +02001048
Philipp Maier14b27a82020-06-02 20:15:30 +02001049 conn->end.fmtp_extra = talloc_strdup(trunk->endpoints,
1050 trunk->audio_fmtp_extra);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001051
Philipp Maier8dc35972021-07-14 11:20:16 +02001052 if (pdata->cfg->force_ptime) {
1053 conn->end.packet_duration_ms = pdata->cfg->force_ptime;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001054 conn->end.force_output_ptime = 1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001055 }
1056
Philipp Maier1cb1e382017-11-02 17:16:04 +01001057 mgcp_rtp_end_config(endp, 0, &conn->end);
1058
Philipp Maierc3cc6542018-02-02 12:58:42 +01001059 /* check connection mode setting */
1060 if (conn->conn->mode != MGCP_CONN_LOOPBACK
1061 && conn->conn->mode != MGCP_CONN_RECV_ONLY
1062 && conn->end.rtp_port == 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001063 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
1064 "CRCX: selected connection mode type requires an opposite end!\n");
Philipp Maierc3cc6542018-02-02 12:58:42 +01001065 error_code = 527;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001066 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC));
Philipp Maierc3cc6542018-02-02 12:58:42 +01001067 goto error2;
1068 }
1069
Pau Espin Pedrol71d42e72020-09-03 14:20:07 +02001070 /* Find a local address for conn based on policy and initial SDP remote
1071 information, then find a free port for it */
1072 mgcp_get_local_addr(conn->end.local_addr, conn);
Philipp Maier1cb1e382017-11-02 17:16:04 +01001073 if (allocate_port(endp, conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001074 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
Philipp Maier1cb1e382017-11-02 17:16:04 +01001075 goto error2;
1076 }
1077
Philipp Maier87bd9be2017-08-22 16:35:41 +02001078 if (setup_rtp_processing(endp, conn) != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001079 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
1080 "CRCX: could not start RTP processing!\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001081 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_START_RTP));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001082 goto error2;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001083 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001084
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001085 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
1086 "CRCX: Creating connection: port: %u\n", conn->end.local_port);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001087
Philipp Maiere726d4f2017-11-01 10:41:34 +01001088 /* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
Philipp Maier14b27a82020-06-02 20:15:30 +02001089 OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Pau Espin Pedrolca280a12021-07-06 18:15:35 +02001090 if (conn->conn->mode & MGCP_CONN_RECV_ONLY &&
1091 mgcp_rtp_end_remote_addr_available(&conn->end) &&
1092 trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001093 send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001094
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001095 LOGPCONN(_conn, DLMGCP, LOGL_NOTICE,
1096 "CRCX: connection successfully created\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001097 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_SUCCESS));
Philipp Maier889fe7f2020-07-06 17:44:12 +02001098 mgcp_endp_update(endp);
Philipp Maier41d59202021-07-20 15:49:00 +02001099
1100 /* NOTE: Only in the virtual trunk we allow dynamic endpoint names */
1101 bool add_epname = rq->wildcarded && trunk->trunk_type == MGCP_TRUNK_VIRTUAL;
1102 return create_response_with_sdp(endp, conn, "CRCX", pdata->trans, add_epname, true);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001103error2:
Philipp Maier1355d7e2018-02-01 14:30:06 +01001104 mgcp_endp_release(endp);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001105 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1106 "CRCX: unable to create connection\n");
Ericfbcf4a62021-09-09 18:02:31 +02001107 return create_err_response(endp, endp, error_code, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001108}
1109
Philipp Maier87bd9be2017-08-22 16:35:41 +02001110/* MDCX command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +02001111static struct msgb *handle_modify_con(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001112{
Philipp Maier8dc35972021-07-14 11:20:16 +02001113 struct mgcp_parse_data *pdata = rq->pdata;
1114 struct mgcp_trunk *trunk = rq->trunk;
1115 struct mgcp_endpoint *endp = rq->endp;
1116 struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_mdcx_ctr_group;
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001117 char new_local_addr[INET6_ADDRSTRLEN];
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001118 int error_code = 500;
1119 int silent = 0;
1120 int have_sdp = 0;
1121 char *line;
1122 const char *local_options = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001123 const char *mode = NULL;
1124 struct mgcp_conn_rtp *conn = NULL;
Philipp Maier01d24a32017-11-21 17:26:09 +01001125 const char *conn_id = NULL;
Pau Espin Pedrol21779192022-09-23 16:46:33 +02001126 int remote_osmux_cid = -2;
Philipp Maiera390d0b2018-01-31 17:30:19 +01001127 int rc;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001128
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001129 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "MDCX: modifying existing connection ...\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001130
Philipp Maier5656fbf2018-02-02 14:41:58 +01001131 /* Prohibit wildcarded requests */
Philipp Maier8dc35972021-07-14 11:20:16 +02001132 if (rq->wildcarded) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001133 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1134 "MDCX: wildcarded endpoint names not supported.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001135 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_WILDCARD));
Ericfbcf4a62021-09-09 18:02:31 +02001136 return create_err_response(rq->trunk, endp, 507, "MDCX", pdata->trans);
Philipp Maier5656fbf2018-02-02 14:41:58 +01001137 }
1138
Ericee6958c2021-09-14 18:36:53 +02001139 if (!endp || !mgcp_endp_avail(endp)) {
1140 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_AVAIL));
1141 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "MDCX: selected endpoint not available!\n");
1142 return create_err_response(rq->trunk, NULL, 501, "MDCX", pdata->trans);
1143 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001144 if (llist_count(&endp->conns) <= 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001145 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1146 "MDCX: endpoint is not holding a connection.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001147 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_CONN));
Ericfbcf4a62021-09-09 18:02:31 +02001148 return create_err_response(endp, endp, 400, "MDCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001149 }
1150
Philipp Maier8dc35972021-07-14 11:20:16 +02001151 for_each_line(line, pdata->save) {
Philipp Maier036612b2021-07-19 17:47:49 +02001152 if (!mgcp_check_param(endp, trunk, line))
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001153 continue;
1154
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001155 switch (toupper(line[0])) {
Philipp Maier87bd9be2017-08-22 16:35:41 +02001156 case 'C':
Harald Weltee35eeae2017-12-28 13:47:37 +01001157 if (mgcp_verify_call_id(endp, line + 3) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001158 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CALLID));
Harald Weltee35eeae2017-12-28 13:47:37 +01001159 error_code = 516;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001160 goto error3;
Harald Weltee35eeae2017-12-28 13:47:37 +01001161 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001162 break;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001163 case 'I':
Philipp Maier01d24a32017-11-21 17:26:09 +01001164 conn_id = (const char *)line + 3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001165 if ((error_code = mgcp_verify_ci(endp, conn_id))) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001166 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CONNID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001167 goto error3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001168 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001169 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001170 case 'L':
Philipp Maier87bd9be2017-08-22 16:35:41 +02001171 local_options = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001172 break;
1173 case 'M':
Philipp Maier87bd9be2017-08-22 16:35:41 +02001174 mode = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001175 break;
1176 case 'Z':
Pau Espin Pedrol9b508f62019-06-26 13:11:22 +02001177 silent = strcasecmp("noanswer", line + 3) == 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001178 break;
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001179 case 'X':
Pau Espin Pedrolc1bf4692019-05-14 16:23:24 +02001180 if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001181 /* If osmux is disabled, just skip setting it up */
Pau Espin Pedrol928a20b2022-09-23 15:38:24 +02001182 if (endp->trunk->cfg->osmux_use == OSMUX_USAGE_OFF)
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001183 break;
Pau Espin Pedrol21779192022-09-23 16:46:33 +02001184 remote_osmux_cid = mgcp_osmux_setup(endp, line);
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001185 break;
1186 }
1187 /* Ignore unknown X-headers */
1188 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001189 case '\0':
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001190 have_sdp = 1;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001191 goto mgcp_header_done;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001192 break;
1193 default:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001194 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1195 "MDCX: Unhandled MGCP option: '%c'/%d\n",
1196 line[0], line[0]);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001197 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_UNHANDLED_PARAM));
Ericfbcf4a62021-09-09 18:02:31 +02001198 return create_err_response(rq->trunk, NULL, 539, "MDCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001199 break;
1200 }
1201 }
1202
Philipp Maier87bd9be2017-08-22 16:35:41 +02001203mgcp_header_done:
Philipp Maier01d24a32017-11-21 17:26:09 +01001204 if (!conn_id) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001205 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1206 "MDCX: insufficient parameters, missing ci (connectionIdentifier)\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001207 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_CONNID));
Ericfbcf4a62021-09-09 18:02:31 +02001208 return create_err_response(endp, endp, 515, "MDCX", pdata->trans);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001209 }
1210
1211 conn = mgcp_conn_get_rtp(endp, conn_id);
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001212 if (!conn) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001213 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_CONN_NOT_FOUND));
Ericfbcf4a62021-09-09 18:02:31 +02001214 return create_err_response(endp, endp, 400, "MDCX", pdata->trans);
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001215 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001216
Oliver Smithe36b7752019-01-22 16:31:36 +01001217 mgcp_conn_watchdog_kick(conn->conn);
1218
Philipp Maier87bd9be2017-08-22 16:35:41 +02001219 if (mode) {
1220 if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001221 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_MODE));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001222 error_code = 517;
1223 goto error3;
1224 }
1225 } else
Pau Espin Pedrol209eb9f2019-04-24 12:03:04 +02001226 conn->conn->mode = conn->conn->mode_orig;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001227
Philipp Maierbc0346e2018-06-07 09:52:16 +02001228 /* Set local connection options, if present */
1229 if (local_options) {
Philipp Maier8dc35972021-07-14 11:20:16 +02001230 rc = set_local_cx_options(trunk->endpoints,
Philipp Maierbc0346e2018-06-07 09:52:16 +02001231 &endp->local_options, local_options);
1232 if (rc != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001233 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1234 "MDCX: invalid local connection options!\n");
Philipp Maierbc0346e2018-06-07 09:52:16 +02001235 error_code = rc;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001236 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS));
Philipp Maierbc0346e2018-06-07 09:52:16 +02001237 goto error3;
1238 }
1239 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001240
Philipp Maierbc0346e2018-06-07 09:52:16 +02001241 /* Handle codec information and decide for a suitable codec */
Philipp Maier8dc35972021-07-14 11:20:16 +02001242 rc = handle_codec_info(conn, rq, have_sdp, false);
Philipp Maierbc0346e2018-06-07 09:52:16 +02001243 mgcp_codec_summary(conn);
1244 if (rc) {
Philipp Maieraf07f662018-02-02 11:34:02 +01001245 error_code = rc;
1246 goto error3;
Philipp Maiera390d0b2018-01-31 17:30:19 +01001247 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001248
Philipp Maierc3cc6542018-02-02 12:58:42 +01001249 /* check connection mode setting */
1250 if (conn->conn->mode != MGCP_CONN_LOOPBACK
1251 && conn->conn->mode != MGCP_CONN_RECV_ONLY
Pau Espin Pedrold6769ea2021-07-06 19:44:27 +02001252 && !mgcp_rtp_end_remote_addr_available(&conn->end)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001253 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1254 "MDCX: selected connection mode type requires an opposite end!\n");
Philipp Maierc3cc6542018-02-02 12:58:42 +01001255 error_code = 527;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001256 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC));
Philipp Maierc3cc6542018-02-02 12:58:42 +01001257 goto error3;
1258 }
1259
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001260 if (mgcp_conn_rtp_is_osmux(conn)) {
Pau Espin Pedrol21779192022-09-23 16:46:33 +02001261 OSMO_ASSERT(conn->osmux.local_cid_allocated);
1262 if (remote_osmux_cid < -1) {
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001263 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1264 "MDCX: Failed to parse Osmux CID!\n");
1265 goto error3;
Pau Espin Pedrol21779192022-09-23 16:46:33 +02001266 } else if (remote_osmux_cid == -1) {
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001267 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1268 "MDCX: wilcard in MDCX is not supported!\n");
1269 goto error3;
Pau Espin Pedrol21779192022-09-23 16:46:33 +02001270 } else if (conn->osmux.remote_cid_present &&
1271 remote_osmux_cid != (int) conn->osmux.remote_cid) {
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001272 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1273 "MDCX: changing already allocated CID is not supported!\n");
1274 goto error3;
1275 }
Pau Espin Pedrol21779192022-09-23 16:46:33 +02001276 conn->osmux.remote_cid_present = true;
1277 conn->osmux.remote_cid = remote_osmux_cid;
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001278 }
Philipp Maierbc0346e2018-06-07 09:52:16 +02001279
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001280 /* MDCX may have provided a new remote address, which means we may need
1281 to update our announced IP addr and re-bind our local end. This can
1282 happen for instance if MGW initially provided an IPv4 during CRCX
1283 ACK, and now MDCX tells us the remote has an IPv6 address. */
1284 mgcp_get_local_addr(new_local_addr, conn);
1285 if (strcmp(new_local_addr, conn->end.local_addr)) {
1286 osmo_strlcpy(conn->end.local_addr, new_local_addr, sizeof(conn->end.local_addr));
1287 mgcp_free_rtp_port(&conn->end);
1288 if (allocate_port(endp, conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001289 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001290 goto error3;
1291 }
1292 }
1293
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001294 if (setup_rtp_processing(endp, conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001295 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_START_RTP));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001296 goto error3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001297 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001298
Philipp Maier87bd9be2017-08-22 16:35:41 +02001299 mgcp_rtp_end_config(endp, 1, &conn->end);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001300
1301 /* modify */
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001302 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
1303 "MDCX: modified conn:%s\n", mgcp_conn_dump(conn->conn));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001304
Philipp Maiere726d4f2017-11-01 10:41:34 +01001305 /* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
Philipp Maier8dc35972021-07-14 11:20:16 +02001306 OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Pau Espin Pedrolca280a12021-07-06 18:15:35 +02001307 if (conn->conn->mode & MGCP_CONN_RECV_ONLY &&
1308 mgcp_rtp_end_remote_addr_available(&conn->end) &&
Philipp Maier8dc35972021-07-14 11:20:16 +02001309 trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001310 send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001311
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001312 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_SUCCESS));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001313 if (silent)
1314 goto out_silent;
1315
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001316 LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
1317 "MDCX: connection successfully modified\n");
Philipp Maier889fe7f2020-07-06 17:44:12 +02001318 mgcp_endp_update(endp);
Philipp Maier41d59202021-07-20 15:49:00 +02001319 return create_response_with_sdp(endp, conn, "MDCX", pdata->trans, false, false);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001320error3:
Ericfbcf4a62021-09-09 18:02:31 +02001321 return create_err_response(endp, endp, error_code, "MDCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001322
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001323out_silent:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001324 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "MDCX: silent exit\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001325 return NULL;
1326}
1327
Philipp Maier87bd9be2017-08-22 16:35:41 +02001328/* DLCX command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +02001329static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001330{
Philipp Maier8dc35972021-07-14 11:20:16 +02001331 struct mgcp_parse_data *pdata = rq->pdata;
1332 struct mgcp_trunk *trunk = rq->trunk;
1333 struct mgcp_endpoint *endp = rq->endp;
1334 struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_dlcx_ctr_group;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001335 int error_code = 400;
1336 int silent = 0;
1337 char *line;
1338 char stats[1048];
Philipp Maier01d24a32017-11-21 17:26:09 +01001339 const char *conn_id = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001340 struct mgcp_conn_rtp *conn = NULL;
Philipp Maierf486e742021-07-19 14:56:29 +02001341 unsigned int i;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001342
Philipp Maierf486e742021-07-19 14:56:29 +02001343 /* NOTE: In this handler we can not take it for granted that the endp
1344 * pointer will be populated, however a trunk is always guaranteed. */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001345
Philipp Maierf486e742021-07-19 14:56:29 +02001346 LOGPEPTR(endp, trunk, DLMGCP, LOGL_NOTICE, "DLCX: deleting connection(s) ...\n");
1347
1348 if (endp && !mgcp_endp_avail(endp)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001349 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_AVAIL));
Philipp Maiera910a812020-08-18 15:13:33 +02001350 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1351 "DLCX: selected endpoint not available!\n");
Ericfbcf4a62021-09-09 18:02:31 +02001352 return create_err_response(rq->trunk, NULL, 501, "DLCX", pdata->trans);
Philipp Maier8d6a1932020-06-18 12:19:31 +02001353 }
1354
Philipp Maierf486e742021-07-19 14:56:29 +02001355 if (endp && !rq->wildcarded && llist_empty(&endp->conns)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001356 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1357 "DLCX: endpoint is not holding a connection.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001358 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_NO_CONN));
Ericfbcf4a62021-09-09 18:02:31 +02001359 return create_err_response(endp, endp, 515, "DLCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001360 }
1361
Ericee6958c2021-09-14 18:36:53 +02001362 /* Handle wildcarded DLCX that refers to the whole trunk. This means
1363 * that we walk over all endpoints on the trunk in order to drop all
1364 * connections on the trunk. (see also RFC3435 Annex F.7) */
1365 if (rq->wildcarded) {
1366 int num_conns = 0;
1367 for (i = 0; i < trunk->number_endpoints; i++) {
1368 num_conns += llist_count(&trunk->endpoints[i]->conns);
1369 mgcp_endp_release(trunk->endpoints[i]);
1370 }
1371 rate_ctr_add(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS), num_conns);
1372 return create_ok_response(trunk, NULL, 200, "DLCX", pdata->trans);
1373 }
1374
Philipp Maier8dc35972021-07-14 11:20:16 +02001375 for_each_line(line, pdata->save) {
Philipp Maier036612b2021-07-19 17:47:49 +02001376 if (!mgcp_check_param(endp, trunk, line))
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001377 continue;
1378
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001379 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001380 case 'C':
Philipp Maierf486e742021-07-19 14:56:29 +02001381 /* If we have no endpoint, but a call id in the request,
1382 then this request cannot be handled */
1383 if (!endp) {
1384 LOGPTRUNK(trunk, DLMGCP, LOGL_NOTICE,
1385 "cannot handle requests with call-id (C) without endpoint -- abort!");
1386 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));
Ericfbcf4a62021-09-09 18:02:31 +02001387 return create_err_response(rq->trunk, NULL, 539, "DLCX", pdata->trans);
Philipp Maierf486e742021-07-19 14:56:29 +02001388 }
1389
Harald Weltee35eeae2017-12-28 13:47:37 +01001390 if (mgcp_verify_call_id(endp, line + 3) != 0) {
1391 error_code = 516;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001392 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CALLID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001393 goto error3;
Harald Weltee35eeae2017-12-28 13:47:37 +01001394 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001395 break;
1396 case 'I':
Philipp Maierf486e742021-07-19 14:56:29 +02001397 /* If we have no endpoint, but a connection id in the request,
1398 then this request cannot be handled */
1399 if (!endp) {
1400 LOGPTRUNK(trunk, DLMGCP, LOGL_NOTICE,
1401 "cannot handle requests with conn-id (I) without endpoint -- abort!");
1402 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));
Ericfbcf4a62021-09-09 18:02:31 +02001403 return create_err_response(rq->trunk, NULL, 539, "DLCX", pdata->trans);
Philipp Maierf486e742021-07-19 14:56:29 +02001404 }
1405
Philipp Maier01d24a32017-11-21 17:26:09 +01001406 conn_id = (const char *)line + 3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001407 if ((error_code = mgcp_verify_ci(endp, conn_id))) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001408 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CONNID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001409 goto error3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001410 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001411 break;
1412 case 'Z':
Pau Espin Pedrol9b508f62019-06-26 13:11:22 +02001413 silent = strcasecmp("noanswer", line + 3) == 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001414 break;
1415 default:
Philipp Maierf486e742021-07-19 14:56:29 +02001416 LOGPEPTR(endp, trunk, DLMGCP, LOGL_NOTICE, "DLCX: Unhandled MGCP option: '%c'/%d\n",
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001417 line[0], line[0]);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001418 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));
Ericfbcf4a62021-09-09 18:02:31 +02001419 return create_err_response(rq->trunk, NULL, 539, "DLCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001420 break;
1421 }
1422 }
1423
Philipp Maierce187052021-07-23 10:58:19 +02001424 /* The logic does not permit to go past this point without having the
1425 * the endp pointer populated. */
1426 OSMO_ASSERT(endp);
1427
Philipp Maierf4c0e372017-10-11 16:06:45 +02001428 /* When no connection id is supplied, we will interpret this as a
Philipp Maierf486e742021-07-19 14:56:29 +02001429 * wildcarded DLCX that refers to the selected endpoint. This means
1430 * that we drop all connections on that specific endpoint at once.
1431 * (See also RFC3435 Section F.7) */
Philipp Maier01d24a32017-11-21 17:26:09 +01001432 if (!conn_id) {
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001433 int num_conns = llist_count(&endp->conns);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001434 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1435 "DLCX: missing ci (connectionIdentifier), will remove all connections (%d total) at once\n",
1436 num_conns);
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001437
1438 if (num_conns > 0)
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001439 rate_ctr_add(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS), num_conns);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001440
Philipp Maier1355d7e2018-02-01 14:30:06 +01001441 mgcp_endp_release(endp);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001442
1443 /* Note: In this case we do not return any statistics,
1444 * as we assume that the client is not interested in
1445 * this case. */
Ericfbcf4a62021-09-09 18:02:31 +02001446 return create_ok_response(endp, endp, 200, "DLCX", pdata->trans);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001447 }
1448
Philipp Maierf4c0e372017-10-11 16:06:45 +02001449 /* Find the connection */
Philipp Maier87bd9be2017-08-22 16:35:41 +02001450 conn = mgcp_conn_get_rtp(endp, conn_id);
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001451 if (!conn) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001452 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CONNID));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001453 goto error3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001454 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001455 /* save the statistics of the current connection */
1456 mgcp_format_stats(stats, sizeof(stats), conn->conn);
1457
1458 /* delete connection */
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001459 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG, "DLCX: deleting conn:%s\n",
1460 mgcp_conn_dump(conn->conn));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001461 mgcp_conn_free(endp, conn_id);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001462 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1463 "DLCX: connection successfully deleted\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +02001464
1465 /* When all connections are closed, the endpoint will be released
1466 * in order to be ready to be used by another call. */
1467 if (llist_count(&endp->conns) <= 0) {
Philipp Maier1355d7e2018-02-01 14:30:06 +01001468 mgcp_endp_release(endp);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001469 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: endpoint released\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +02001470 }
1471
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001472 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001473 if (silent)
1474 goto out_silent;
Ericfbcf4a62021-09-09 18:02:31 +02001475 return create_ok_resp_with_param(endp, endp, 250, "DLCX", pdata->trans, stats);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001476
1477error3:
Ericfbcf4a62021-09-09 18:02:31 +02001478 return create_err_response(endp, endp, error_code, "DLCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001479
1480out_silent:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001481 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: silent exit\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001482 return NULL;
1483}
1484
Philipp Maier87bd9be2017-08-22 16:35:41 +02001485/* RSIP command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +02001486static struct msgb *handle_rsip(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001487{
Philipp Maier87bd9be2017-08-22 16:35:41 +02001488 /* TODO: Also implement the resetting of a specific endpoint
1489 * to make mgcp_send_reset_ep() work. Currently this will call
1490 * mgcp_rsip_cb() in mgw_main.c, which sets reset_endpoints=1
1491 * to make read_call_agent() reset all endpoints when called
1492 * next time. In order to selectively reset endpoints some
1493 * mechanism to distinguish which endpoint shall be resetted
1494 * is needed */
1495
1496 LOGP(DLMGCP, LOGL_NOTICE, "RSIP: resetting all endpoints ...\n");
1497
Philipp Maier8dc35972021-07-14 11:20:16 +02001498 if (rq->pdata->cfg->reset_cb)
1499 rq->pdata->cfg->reset_cb(rq->endp->trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001500 return NULL;
1501}
1502
1503static char extract_tone(const char *line)
1504{
1505 const char *str = strstr(line, "D/");
1506 if (!str)
1507 return CHAR_MAX;
1508
1509 return str[2];
1510}
1511
Philipp Maier87bd9be2017-08-22 16:35:41 +02001512/* This can request like DTMF detection and forward, fax detection... it
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001513 * can also request when the notification should be send and such. We don't
Philipp Maier87bd9be2017-08-22 16:35:41 +02001514 * do this right now. */
Philipp Maier8dc35972021-07-14 11:20:16 +02001515static struct msgb *handle_noti_req(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001516{
1517 int res = 0;
1518 char *line;
1519 char tone = CHAR_MAX;
1520
Philipp Maier87bd9be2017-08-22 16:35:41 +02001521 LOGP(DLMGCP, LOGL_NOTICE, "RQNT: processing request for notification ...\n");
1522
Philipp Maier8dc35972021-07-14 11:20:16 +02001523 for_each_line(line, rq->pdata->save) {
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001524 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001525 case 'S':
1526 tone = extract_tone(line);
1527 break;
1528 }
1529 }
1530
1531 /* we didn't see a signal request with a tone */
1532 if (tone == CHAR_MAX)
Ericfbcf4a62021-09-09 18:02:31 +02001533 return create_ok_response(rq->endp, rq->endp, 200, "RQNT", rq->pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001534
Philipp Maier8dc35972021-07-14 11:20:16 +02001535 if (rq->pdata->cfg->rqnt_cb)
1536 res = rq->pdata->cfg->rqnt_cb(rq->endp, tone);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001537
Ericfbcf4a62021-09-09 18:02:31 +02001538 return res == 0 ? create_ok_response(rq->endp, rq->endp, 200, "RQNT", rq->pdata->trans) :
1539 create_err_response(rq->endp, rq->endp, res, "RQNT", rq->pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001540}
1541
Philipp Maier87bd9be2017-08-22 16:35:41 +02001542/* Connection keepalive timer, will take care that dummy packets are send
Harald Welte1d1b98f2017-12-25 10:03:40 +01001543 * regularly, so that NAT connections stay open */
Philipp Maier14b27a82020-06-02 20:15:30 +02001544static void mgcp_keepalive_timer_cb(void *_trunk)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001545{
Philipp Maier14b27a82020-06-02 20:15:30 +02001546 struct mgcp_trunk *trunk = _trunk;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001547 struct mgcp_conn *conn;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001548 int i;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001549
Philipp Maiere726d4f2017-11-01 10:41:34 +01001550 LOGP(DLMGCP, LOGL_DEBUG, "triggered trunk %d keepalive timer\n",
Philipp Maier14b27a82020-06-02 20:15:30 +02001551 trunk->trunk_nr);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001552
Philipp Maiere726d4f2017-11-01 10:41:34 +01001553 /* Do not accept invalid configuration values
1554 * valid is MGCP_KEEPALIVE_NEVER, MGCP_KEEPALIVE_ONCE and
1555 * values greater 0 */
Philipp Maier14b27a82020-06-02 20:15:30 +02001556 OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Philipp Maiere726d4f2017-11-01 10:41:34 +01001557
1558 /* The dummy packet functionality has been disabled, we will exit
1559 * immediately, no further timer is scheduled, which means we will no
1560 * longer send dummy packets even when we did before */
Philipp Maier14b27a82020-06-02 20:15:30 +02001561 if (trunk->keepalive_interval == MGCP_KEEPALIVE_NEVER)
Philipp Maiere726d4f2017-11-01 10:41:34 +01001562 return;
1563
1564 /* In cases where only one dummy packet is sent, we do not need
1565 * the timer since the functions that handle the CRCX and MDCX are
1566 * triggering the sending of the dummy packet. So we behave like in
1567 * the MGCP_KEEPALIVE_NEVER case */
Philipp Maier14b27a82020-06-02 20:15:30 +02001568 if (trunk->keepalive_interval == MGCP_KEEPALIVE_ONCE)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001569 return;
1570
Philipp Maier87bd9be2017-08-22 16:35:41 +02001571 /* Send walk over all endpoints and send out dummy packets through
1572 * every connection present on each endpoint */
Philipp Maier4131a652021-07-07 14:04:34 +02001573 for (i = 0; i < trunk->number_endpoints; ++i) {
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001574 struct mgcp_endpoint *endp = trunk->endpoints[i];
Philipp Maier87bd9be2017-08-22 16:35:41 +02001575 llist_for_each_entry(conn, &endp->conns, entry) {
Pau Espin Pedrolca280a12021-07-06 18:15:35 +02001576 if (conn->type == MGCP_CONN_TYPE_RTP &&
1577 conn->mode == MGCP_CONN_RECV_ONLY &&
1578 mgcp_rtp_end_remote_addr_available(&conn->u.rtp.end))
Philipp Maier87bd9be2017-08-22 16:35:41 +02001579 send_dummy(endp, &conn->u.rtp);
1580 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001581 }
1582
Philipp Maiere726d4f2017-11-01 10:41:34 +01001583 /* Schedule the keepalive timer for the next round */
1584 LOGP(DLMGCP, LOGL_DEBUG, "rescheduling trunk %d keepalive timer\n",
Philipp Maier14b27a82020-06-02 20:15:30 +02001585 trunk->trunk_nr);
1586 osmo_timer_schedule(&trunk->keepalive_timer, trunk->keepalive_interval,
Philipp Maier87bd9be2017-08-22 16:35:41 +02001587 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001588}
1589
Philipp Maier14b27a82020-06-02 20:15:30 +02001590void mgcp_trunk_set_keepalive(struct mgcp_trunk *trunk, int interval)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001591{
Philipp Maier14b27a82020-06-02 20:15:30 +02001592 trunk->keepalive_interval = interval;
1593 osmo_timer_setup(&trunk->keepalive_timer, mgcp_keepalive_timer_cb, trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001594
1595 if (interval <= 0)
Philipp Maier14b27a82020-06-02 20:15:30 +02001596 osmo_timer_del(&trunk->keepalive_timer);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001597 else
Philipp Maier14b27a82020-06-02 20:15:30 +02001598 osmo_timer_schedule(&trunk->keepalive_timer,
1599 trunk->keepalive_interval, 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001600}
1601
Philipp Maier38533ba2021-07-29 17:38:34 +02001602/* Free config, this function is automatically called by talloc_free when the configuration is freed. */
1603static int config_free_talloc_destructor(struct mgcp_config *cfg)
1604{
1605 mgcp_ratectr_global_free(cfg);
1606 return 0;
1607}
1608
Philipp Maier87bd9be2017-08-22 16:35:41 +02001609/*! allocate configuration with default values.
1610 * (called once at startup by main function) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001611struct mgcp_config *mgcp_config_alloc(void)
1612{
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001613 /* FIXME: This is unrelated to the protocol, put this in some
1614 * appropiate place! */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001615 struct mgcp_config *cfg;
1616
1617 cfg = talloc_zero(NULL, struct mgcp_config);
1618 if (!cfg) {
1619 LOGP(DLMGCP, LOGL_FATAL, "Failed to allocate config.\n");
1620 return NULL;
1621 }
1622
Philipp Maier12943ea2018-01-17 15:40:25 +01001623 osmo_strlcpy(cfg->domain, "mgw", sizeof(cfg->domain));
1624
Eric55fdfc22021-08-13 00:14:18 +02001625 cfg->net_ports.lock = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001626 cfg->net_ports.range_start = RTP_PORT_DEFAULT_RANGE_START;
1627 cfg->net_ports.range_end = RTP_PORT_DEFAULT_RANGE_END;
1628 cfg->net_ports.last_port = cfg->net_ports.range_start;
1629
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001630 cfg->source_port = 2427;
Eric2764bdb2021-08-23 22:11:47 +02001631 osmo_strlcpy(cfg->source_addr, "0.0.0.0", sizeof(cfg->source_addr));
1632 osmo_strlcpy(cfg->osmux_addr, "0.0.0.0", sizeof(cfg->osmux_addr));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001633
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001634 cfg->rtp_processing_cb = &mgcp_rtp_processing_default;
1635 cfg->setup_rtp_processing_cb = &mgcp_setup_rtp_processing_default;
1636
1637 cfg->get_net_downlink_format_cb = &mgcp_get_net_downlink_format_default;
1638
Philipp Maierd19de2e2020-06-03 13:55:33 +02001639 INIT_LLIST_HEAD(&cfg->trunks);
1640
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001641 /* Allocate virtual trunk */
Philipp Maierd19de2e2020-06-03 13:55:33 +02001642 if (!mgcp_trunk_alloc(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID)) {
Harald Welte3ac604e2019-05-08 14:07:41 +02001643 talloc_free(cfg);
1644 return NULL;
1645 }
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001646
Philipp Maiera065e632021-07-09 13:22:42 +02001647 mgcp_ratectr_global_alloc(cfg);
Philipp Maier38533ba2021-07-29 17:38:34 +02001648 talloc_set_destructor(cfg, config_free_talloc_destructor);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001649
1650 return cfg;
1651}
1652
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001653static int send_agent(struct mgcp_config *cfg, const char *buf, int len)
1654{
1655 return write(cfg->gw_fd.bfd.fd, buf, len);
1656}
1657
Philipp Maier87bd9be2017-08-22 16:35:41 +02001658/*! Reset all endpoints by sending RSIP message to self.
1659 * (called by VTY)
1660 * \param[in] endp trunk endpoint
1661 * \param[in] endpoint number
1662 * \returns 0 on success, -1 on error */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001663int mgcp_send_reset_all(struct mgcp_config *cfg)
1664{
Philipp Maier12943ea2018-01-17 15:40:25 +01001665 char buf[MGCP_ENDPOINT_MAXLEN + 128];
1666 int len;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001667 int rc;
1668
Philipp Maier12943ea2018-01-17 15:40:25 +01001669 len = snprintf(buf, sizeof(buf),
1670 "RSIP 1 *@%s MGCP 1.0\r\n", cfg->domain);
1671 if (len < 0)
1672 return -1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001673
Philipp Maier12943ea2018-01-17 15:40:25 +01001674 rc = send_agent(cfg, buf, len);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001675 if (rc <= 0)
1676 return -1;
1677
1678 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001679}
1680
Philipp Maier87bd9be2017-08-22 16:35:41 +02001681/*! Reset a single endpoint by sending RSIP message to self.
1682 * (called by VTY)
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001683 * \param[in] endp to reset
Philipp Maier87bd9be2017-08-22 16:35:41 +02001684 * \returns 0 on success, -1 on error */
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001685int mgcp_send_reset_ep(struct mgcp_endpoint *endp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001686{
Philipp Maier12943ea2018-01-17 15:40:25 +01001687 char buf[MGCP_ENDPOINT_MAXLEN + 128];
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001688 int len;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001689 int rc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001690
1691 len = snprintf(buf, sizeof(buf),
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001692 "RSIP 39 %s MGCP 1.0\r\n", endp->name);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001693 if (len < 0)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001694 return -1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001695
Ericfbf78d12021-08-23 22:31:39 +02001696 rc = send_agent(endp->trunk->cfg, buf, len);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001697 if (rc <= 0)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001698 return -1;
1699
Philipp Maier87bd9be2017-08-22 16:35:41 +02001700 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001701}