blob: f6daffc6e5d5ec007f8540b3c39382c4c6004d89 [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>
31
32#include <osmocom/core/msgb.h>
33#include <osmocom/core/talloc.h>
34#include <osmocom/core/select.h>
35
Philipp Maier87bd9be2017-08-22 16:35:41 +020036#include <osmocom/mgcp/mgcp.h>
Neels Hofmeyr67793542017-09-08 04:25:16 +020037#include <osmocom/mgcp/mgcp_common.h>
Philipp Maier993ea6b2020-08-04 18:26:50 +020038#include <osmocom/mgcp/osmux.h>
39#include <osmocom/mgcp/mgcp_network.h>
40#include <osmocom/mgcp/mgcp_protocol.h>
Philipp Maier87bd9be2017-08-22 16:35:41 +020041#include <osmocom/mgcp/mgcp_stat.h>
42#include <osmocom/mgcp/mgcp_msg.h>
Philipp Maier37d11c82018-02-01 14:38:12 +010043#include <osmocom/mgcp/mgcp_endp.h>
Philipp Maierc66ab2c2020-06-02 20:55:34 +020044#include <osmocom/mgcp/mgcp_trunk.h>
Philipp Maier8970c492017-10-11 13:33:42 +020045#include <osmocom/mgcp/mgcp_sdp.h>
Philipp Maierbc0346e2018-06-07 09:52:16 +020046#include <osmocom/mgcp/mgcp_codec.h>
Stefan Sperlingba25eab2018-10-30 14:32:31 +010047#include <osmocom/mgcp/mgcp_conn.h>
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020048
Philipp Maier33d97f72021-07-14 14:53:45 +020049/* Request handler specification, here we specify an array with function
50 * pointers to the various MGCP requests implemented below */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020051struct mgcp_request {
Philipp Maier33d97f72021-07-14 14:53:45 +020052 /* request name (e.g. "MDCX") */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020053 char *name;
Philipp Maier33d97f72021-07-14 14:53:45 +020054
55 /* function pointer to the request handler */
Philipp Maier87bd9be2017-08-22 16:35:41 +020056 struct msgb *(*handle_request) (struct mgcp_parse_data * data);
Philipp Maier33d97f72021-07-14 14:53:45 +020057
58 /* a human readable name that describes the request */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020059 char *debug_name;
60};
61
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020062static struct msgb *handle_audit_endpoint(struct mgcp_parse_data *data);
63static struct msgb *handle_create_con(struct mgcp_parse_data *data);
64static struct msgb *handle_delete_con(struct mgcp_parse_data *data);
65static struct msgb *handle_modify_con(struct mgcp_parse_data *data);
66static struct msgb *handle_rsip(struct mgcp_parse_data *data);
67static struct msgb *handle_noti_req(struct mgcp_parse_data *data);
Philipp Maier33d97f72021-07-14 14:53:45 +020068static const struct mgcp_request mgcp_requests[] = {
69 { .name = "AUEP",
70 .handle_request = handle_audit_endpoint,
71 .debug_name = "AuditEndpoint" },
72 { .name = "CRCX",
73 .handle_request = handle_create_con,
74 .debug_name = "CreateConnection" },
75 { .name = "DLCX",
76 .handle_request = handle_delete_con,
77 .debug_name = "DeleteConnection" },
78 { .name = "MDCX",
79 .handle_request = handle_modify_con,
80 .debug_name = "ModifiyConnection" },
81 { .name = "RQNT",
82 .handle_request = handle_noti_req,
83 .debug_name = "NotificationRequest" },
84
85 /* SPEC extension */
86 { .name = "RSIP",
87 .handle_request = handle_rsip,
88 .debug_name = "ReSetInProgress" },
89};
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020090
Philipp Maier87bd9be2017-08-22 16:35:41 +020091/* Initalize transcoder */
92static int setup_rtp_processing(struct mgcp_endpoint *endp,
93 struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020094{
Philipp Maier87bd9be2017-08-22 16:35:41 +020095 struct mgcp_config *cfg = endp->cfg;
96 struct mgcp_conn_rtp *conn_src = NULL;
97 struct mgcp_conn_rtp *conn_dst = conn;
98 struct mgcp_conn *_conn;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020099
Pau Espin Pedrolfa810e82019-05-06 18:54:10 +0200100 if (conn->type != MGCP_RTP_DEFAULT && !mgcp_conn_rtp_is_osmux(conn)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200101 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
102 "RTP-setup: Endpoint is not configured as RTP default, stopping here!\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200103 return 0;
104 }
105
Philipp Maier87bd9be2017-08-22 16:35:41 +0200106 if (conn->conn->mode == MGCP_CONN_LOOPBACK) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200107 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
108 "RTP-setup: Endpoint is in loopback mode, stopping here!\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +0200109 return 0;
110 }
111
112 /* Find the "sister" connection */
113 llist_for_each_entry(_conn, &endp->conns, entry) {
114 if (_conn->id != conn->conn->id) {
115 conn_src = &_conn->u.rtp;
116 break;
117 }
118 }
119
Philipp Maieracc10352018-07-19 18:07:57 +0200120 return cfg->setup_rtp_processing_cb(endp, conn_dst, conn_src);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200121}
122
Philipp Maier87bd9be2017-08-22 16:35:41 +0200123/* Helper function to allocate some memory for responses and retransmissions */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200124static struct msgb *mgcp_msgb_alloc(void)
125{
126 struct msgb *msg;
127 msg = msgb_alloc_headroom(4096, 128, "MGCP msg");
128 if (!msg)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200129 LOGP(DLMGCP, LOGL_ERROR, "Failed to msgb for MGCP data.\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200130
131 return msg;
132}
133
Philipp Maier87bd9be2017-08-22 16:35:41 +0200134/* Helper function for do_retransmission() and create_resp() */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200135static struct msgb *do_retransmission(const struct mgcp_endpoint *endp)
136{
137 struct msgb *msg = mgcp_msgb_alloc();
138 if (!msg)
139 return NULL;
140
141 msg->l2h = msgb_put(msg, strlen(endp->last_response));
142 memcpy(msg->l2h, endp->last_response, msgb_l2len(msg));
Philipp Maier87bd9be2017-08-22 16:35:41 +0200143 mgcp_disp_msg(msg->l2h, msgb_l2len(msg), "Retransmitted response");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200144 return msg;
145}
146
147static struct msgb *create_resp(struct mgcp_endpoint *endp, int code,
148 const char *txt, const char *msg,
149 const char *trans, const char *param,
150 const char *sdp)
151{
152 int len;
153 struct msgb *res;
154
155 res = mgcp_msgb_alloc();
156 if (!res)
157 return NULL;
158
Philipp Maier87bd9be2017-08-22 16:35:41 +0200159 len = snprintf((char *)res->data, 2048, "%d %s%s%s\r\n%s",
160 code, trans, txt, param ? param : "", sdp ? sdp : "");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200161 if (len < 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200162 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "Failed to sprintf MGCP response.\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200163 msgb_free(res);
164 return NULL;
165 }
166
167 res->l2h = msgb_put(res, len);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200168 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "Generated response: code=%d\n", code);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200169 mgcp_disp_msg(res->l2h, msgb_l2len(res), "Generated response");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200170
171 /*
172 * Remember the last transmission per endpoint.
173 */
174 if (endp) {
Philipp Maier14b27a82020-06-02 20:15:30 +0200175 struct mgcp_trunk *trunk = endp->trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200176 talloc_free(endp->last_response);
177 talloc_free(endp->last_trans);
Philipp Maier14b27a82020-06-02 20:15:30 +0200178 endp->last_trans = talloc_strdup(trunk->endpoints, trans);
179 endp->last_response = talloc_strndup(trunk->endpoints,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200180 (const char *)res->l2h,
181 msgb_l2len(res));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200182 }
183
184 return res;
185}
186
187static struct msgb *create_ok_resp_with_param(struct mgcp_endpoint *endp,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200188 int code, const char *msg,
189 const char *trans,
190 const char *param)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200191{
192 return create_resp(endp, code, " OK", msg, trans, param, NULL);
193}
194
195static struct msgb *create_ok_response(struct mgcp_endpoint *endp,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200196 int code, const char *msg,
197 const char *trans)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200198{
199 return create_ok_resp_with_param(endp, code, msg, trans, NULL);
200}
201
202static struct msgb *create_err_response(struct mgcp_endpoint *endp,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200203 int code, const char *msg,
204 const char *trans)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200205{
206 return create_resp(endp, code, " FAIL", msg, trans, NULL, NULL);
207}
208
Philipp Maier55295f72018-01-15 14:00:28 +0100209/* Add MGCP parameters to a message buffer */
210static int add_params(struct msgb *msg, const struct mgcp_endpoint *endp,
211 const struct mgcp_conn_rtp *conn)
212{
213 int rc;
214
Philipp Maier7f0966c2018-01-17 18:18:12 +0100215 /* NOTE: Only in the virtual trunk we allow dynamic endpoint names */
Philipp Maier207ab512018-02-02 14:19:26 +0100216 if (endp->wildcarded_req
Philipp Maier14b27a82020-06-02 20:15:30 +0200217 && endp->trunk->trunk_type == MGCP_TRUNK_VIRTUAL) {
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200218 rc = msgb_printf(msg, "Z: %s\r\n", endp->name);
Philipp Maier55295f72018-01-15 14:00:28 +0100219 if (rc < 0)
220 return -EINVAL;
221 }
222
Philipp Maierc3cfae22018-01-22 12:03:03 +0100223 rc = msgb_printf(msg, "I: %s\r\n", conn->conn->id);
Philipp Maier55295f72018-01-15 14:00:28 +0100224 if (rc < 0)
225 return -EINVAL;
226
227 return 0;
228}
229
Philipp Maier87bd9be2017-08-22 16:35:41 +0200230/* Format MGCP response string (with SDP attached) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200231static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200232 struct mgcp_conn_rtp *conn,
233 const char *msg,
Philipp Maier55295f72018-01-15 14:00:28 +0100234 const char *trans_id,
235 bool add_conn_params)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200236{
Pau Espin Pedrol71d42e72020-09-03 14:20:07 +0200237 /* cfg->local_ip allows overwritting the announced IP address with
238 * regards to the one we actually bind to. Useful in behind-NAT
239 * scenarios.
240 * TODO: we may want to define another local_ip_osmux var to
241 * us for OSMUX connections. Perhaps adding a new internal API to get it
242 * based on conn type.
243 */
244 const char *addr = endp->cfg->local_ip ? : conn->end.local_addr;
Philipp Maier8970c492017-10-11 13:33:42 +0200245 struct msgb *sdp;
246 int rc;
247 struct msgb *result;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200248
Philipp Maier8970c492017-10-11 13:33:42 +0200249 sdp = msgb_alloc_headroom(4096, 128, "sdp record");
250 if (!sdp)
251 return NULL;
252
Philipp Maier55295f72018-01-15 14:00:28 +0100253 /* Attach optional connection parameters */
254 if (add_conn_params) {
255 rc = add_params(sdp, endp, conn);
256 if (rc < 0)
257 goto error;
258 }
259
Philipp Maier3cbfb8a2018-01-22 11:39:59 +0100260 /* Attach optional OSMUX parameters */
Pau Espin Pedrolc63f15a2019-05-10 16:52:08 +0200261 if (mgcp_conn_rtp_is_osmux(conn)) {
Pau Espin Pedrol5e8d7992019-04-24 19:56:43 +0200262 rc = msgb_printf(sdp, "X-Osmux: %u\r\n", conn->osmux.cid);
Philipp Maier3cbfb8a2018-01-22 11:39:59 +0100263 if (rc < 0)
264 goto error;
265 }
266
267 /* Attach line break to separate the parameters from the SDP block */
Philipp Maierc3cfae22018-01-22 12:03:03 +0100268 rc = msgb_printf(sdp, "\r\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200269
Philipp Maier8970c492017-10-11 13:33:42 +0200270 rc = mgcp_write_response_sdp(endp, conn, sdp, addr);
271 if (rc < 0)
272 goto error;
273 result = create_resp(endp, 200, " OK", msg, trans_id, NULL, (char*) sdp->data);
274 msgb_free(sdp);
275 return result;
276error:
277 msgb_free(sdp);
278 return NULL;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200279}
280
Philipp Maier87bd9be2017-08-22 16:35:41 +0200281/* Send out dummy packet to keep the connection open, if the connection is an
282 * osmux connection, send the dummy packet via OSMUX */
283static void send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200284{
Philipp Maier87bd9be2017-08-22 16:35:41 +0200285 if (conn->osmux.state != OSMUX_STATE_DISABLED)
286 osmux_send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200287 else
Philipp Maier87bd9be2017-08-22 16:35:41 +0200288 mgcp_send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200289}
290
Philipp Maier87bd9be2017-08-22 16:35:41 +0200291/* handle incoming messages:
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200292 * - this can be a command (four letters, space, transaction id)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200293 * - or a response (three numbers, space, transaction id) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200294struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
295{
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200296 struct rate_ctr_group *rate_ctrs = cfg->ratectr.mgcp_general_ctr_group;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200297 struct mgcp_parse_data pdata;
Harald Weltee35eeae2017-12-28 13:47:37 +0100298 int rc, i, code, handled = 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200299 struct msgb *resp = NULL;
300 char *data;
301
Alexander Chemeris63866002020-05-05 17:18:40 +0300302 /* Count all messages, even incorect ones */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200303 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_TOTAL));
Alexander Chemeris63866002020-05-05 17:18:40 +0300304
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200305 if (msgb_l2len(msg) < 4) {
306 LOGP(DLMGCP, LOGL_ERROR, "msg too short: %d\n", msg->len);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200307 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200308 return NULL;
309 }
310
Alexander Chemeris63866002020-05-05 17:18:40 +0300311 if (mgcp_msg_terminate_nul(msg)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200312 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200313 return NULL;
Alexander Chemeris63866002020-05-05 17:18:40 +0300314 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200315
Philipp Maier87bd9be2017-08-22 16:35:41 +0200316 mgcp_disp_msg(msg->l2h, msgb_l2len(msg), "Received message");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200317
Philipp Maier87bd9be2017-08-22 16:35:41 +0200318 /* attempt to treat it as a response */
319 if (sscanf((const char *)&msg->l2h[0], "%3d %*s", &code) == 1) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200320 LOGP(DLMGCP, LOGL_DEBUG, "Response: Code: %d\n", code);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200321 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200322 return NULL;
323 }
324
325 msg->l3h = &msg->l2h[4];
326
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200327 /*
328 * Check for a duplicate message and respond.
329 */
330 memset(&pdata, 0, sizeof(pdata));
331 pdata.cfg = cfg;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200332 data = mgcp_strline((char *)msg->l3h, &pdata.save);
Harald Weltee35eeae2017-12-28 13:47:37 +0100333 rc = mgcp_parse_header(&pdata, data);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200334 if (pdata.endp && pdata.trans
Philipp Maier87bd9be2017-08-22 16:35:41 +0200335 && pdata.endp->last_trans
336 && strcmp(pdata.endp->last_trans, pdata.trans) == 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200337 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_RETRANSMITTED));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200338 return do_retransmission(pdata.endp);
339 }
340
Harald Welteabbb6b92017-12-28 13:13:50 +0100341 /* check for general parser failure */
Harald Weltee35eeae2017-12-28 13:47:37 +0100342 if (rc < 0) {
Harald Welteabbb6b92017-12-28 13:13:50 +0100343 LOGP(DLMGCP, LOGL_NOTICE, "%s: failed to find the endpoint\n", msg->l2h);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200344 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_NO_ENDPOINT));
Harald Weltee35eeae2017-12-28 13:47:37 +0100345 return create_err_response(NULL, -rc, (const char *) msg->l2h, pdata.trans);
Harald Welteabbb6b92017-12-28 13:13:50 +0100346 }
347
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200348 for (i = 0; i < ARRAY_SIZE(mgcp_requests); ++i) {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200349 if (strncmp
350 (mgcp_requests[i].name, (const char *)&msg->l2h[0],
351 4) == 0) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200352 handled = 1;
353 resp = mgcp_requests[i].handle_request(&pdata);
354 break;
355 }
356 }
357
Alexander Chemeris63866002020-05-05 17:18:40 +0300358 if (handled) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200359 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_HANDLED));
Alexander Chemeris63866002020-05-05 17:18:40 +0300360 } else {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200361 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_UNHANDLED));
Philipp Maier87bd9be2017-08-22 16:35:41 +0200362 LOGP(DLMGCP, LOGL_NOTICE, "MSG with type: '%.4s' not handled\n",
363 &msg->l2h[0]);
Alexander Chemeris63866002020-05-05 17:18:40 +0300364 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200365
366 return resp;
367}
368
Philipp Maier87bd9be2017-08-22 16:35:41 +0200369/* AUEP command handler, processes the received command */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200370static struct msgb *handle_audit_endpoint(struct mgcp_parse_data *p)
371{
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200372 LOGPENDP(p->endp, DLMGCP, LOGL_NOTICE, "AUEP: auditing endpoint ...\n");
Harald Welteabbb6b92017-12-28 13:13:50 +0100373 return create_ok_response(p->endp, 200, "AUEP", p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200374}
375
Harald Welte1d1b98f2017-12-25 10:03:40 +0100376/* Try to find a free port by attempting to bind on it. Also handle the
Philipp Maier87bd9be2017-08-22 16:35:41 +0200377 * counter that points on the next free port. Since we have a pointer
Philipp Maierb38fb892018-05-22 13:52:21 +0200378 * to the next free port, binding should in work on the first attempt in
Pau Espin Pedrolfc806732019-04-23 00:18:43 +0200379 * general. In case of failure the next port is tried until the whole port
380 * range is tried once. */
Philipp Maier87bd9be2017-08-22 16:35:41 +0200381static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200382{
383 int i;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200384 struct mgcp_port_range *range;
Philipp Maierb38fb892018-05-22 13:52:21 +0200385 unsigned int tries;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200386
Philipp Maier87bd9be2017-08-22 16:35:41 +0200387 OSMO_ASSERT(conn);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200388
389 range = &endp->cfg->net_ports;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200390
391 /* attempt to find a port */
Philipp Maierb38fb892018-05-22 13:52:21 +0200392 tries = (range->range_end - range->range_start) / 2;
393 for (i = 0; i < tries; ++i) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200394 int rc;
395
396 if (range->last_port >= range->range_end)
397 range->last_port = range->range_start;
398
Philipp Maier87bd9be2017-08-22 16:35:41 +0200399 rc = mgcp_bind_net_rtp_port(endp, range->last_port, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200400
401 range->last_port += 2;
402 if (rc == 0) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200403 return 0;
404 }
405
406 }
407
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200408 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
409 "Allocating a RTP/RTCP port failed %u times.\n",
410 tries);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200411 return -1;
412}
413
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200414/*! Helper function for check_local_cx_options() to get a pointer of the next
415 * lco option identifier
416 * \param[in] lco string
417 * \returns pointer to the beginning of the LCO identifier, NULL on failure */
418char *get_lco_identifier(const char *options)
419{
420 char *ptr;
421 unsigned int count = 0;
422
423 /* Jump to the end of the lco identifier */
424 ptr = strstr(options, ":");
425 if (!ptr)
426 return NULL;
427
428 /* Walk backwards until the pointer points to the beginning of the
429 * lco identifier. We know that we stand at the beginning when we
430 * are either at the beginning of the memory or see a space or
431 * comma. (this is tolerant, it will accept a:10, b:11 as well as
432 * a:10,b:11) */
433 while (1) {
434 /* Endless loop protection */
435 if (count > 10000)
436 return NULL;
437 else if (ptr < options || *ptr == ' ' || *ptr == ',') {
438 ptr++;
439 break;
440 }
441 ptr--;
442 count++;
443 }
444
445 /* Check if we got any result */
446 if (*ptr == ':')
447 return NULL;
448
449 return ptr;
450}
451
452/*! Check the LCO option. This function checks for multiple appearence of LCO
453 * options, which is illegal
454 * \param[in] ctx talloc context
455 * \param[in] lco string
456 * \returns 0 on success, -1 on failure */
457int check_local_cx_options(void *ctx, const char *options)
458{
459 int i;
460 char *options_copy;
461 char *lco_identifier;
462 char *lco_identifier_end;
463 char *next_lco_identifier;
464
465 char **lco_seen;
466 unsigned int lco_seen_n = 0;
467
468 if (!options)
469 return -1;
470
471 lco_seen =
472 (char **)talloc_zero_size(ctx, strlen(options) * sizeof(char *));
473 options_copy = talloc_strdup(ctx, options);
474 lco_identifier = options_copy;
475
476 do {
477 /* Move the lco_identifier pointer to the beginning of the
478 * current lco option identifier */
479 lco_identifier = get_lco_identifier(lco_identifier);
480 if (!lco_identifier)
481 goto error;
482
483 /* Look ahead to the next LCO option early, since we
484 * will parse destructively */
485 next_lco_identifier = strstr(lco_identifier + 1, ",");
486
487 /* Pinch off the end of the lco field identifier name
488 * and see if we still got something, also check if
489 * there is some value after the colon. */
490 lco_identifier_end = strstr(lco_identifier, ":");
491 if (!lco_identifier_end)
492 goto error;
493 if (*(lco_identifier_end + 1) == ' '
494 || *(lco_identifier_end + 1) == ','
495 || *(lco_identifier_end + 1) == '\0')
496 goto error;
497 *lco_identifier_end = '\0';
498 if (strlen(lco_identifier) == 0)
499 goto error;
500
501 /* Check if we have already seen the current field identifier
502 * before. If yes, we must bail, an LCO must only appear once
503 * in the LCO string */
504 for (i = 0; i < lco_seen_n; i++) {
Pau Espin Pedrol7eb6f2c2019-06-26 13:00:52 +0200505 if (strcasecmp(lco_seen[i], lco_identifier) == 0)
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200506 goto error;
507 }
508 lco_seen[lco_seen_n] = lco_identifier;
509 lco_seen_n++;
510
511 /* The first identifier must always be found at the beginnning
512 * of the LCO string */
513 if (lco_seen[0] != options_copy)
514 goto error;
515
516 /* Go to the next lco option */
517 lco_identifier = next_lco_identifier;
518 } while (lco_identifier);
519
520 talloc_free(lco_seen);
521 talloc_free(options_copy);
522 return 0;
523error:
524 talloc_free(lco_seen);
525 talloc_free(options_copy);
526 return -1;
527}
528
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200529/* Set the LCO from a string (see RFC 3435).
Harald Welte1d1b98f2017-12-25 10:03:40 +0100530 * The string is stored in the 'string' field. A NULL string is handled exactly
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200531 * like an empty string, the 'string' field is never NULL after this function
532 * has been called. */
Philipp Maiera390d0b2018-01-31 17:30:19 +0100533static int set_local_cx_options(void *ctx, struct mgcp_lco *lco,
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200534 const char *options)
535{
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200536 char *lco_id;
Philipp Maier8dbc9ed2018-10-26 14:50:25 +0200537 char codec[17];
Pau Espin Pedrol19539862020-09-21 11:43:41 +0200538 char nt[17];
Pau Espin Pedrol83fd8a52019-06-26 12:55:26 +0200539 int len;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200540
Philipp Maier604410c2018-06-06 10:02:16 +0200541 if (!options)
542 return 0;
543 if (strlen(options) == 0)
544 return 0;
545
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200546 /* Make sure the encoding of the LCO is consistant before we proceed */
547 if (check_local_cx_options(ctx, options) != 0) {
548 LOGP(DLMGCP, LOGL_ERROR,
549 "local CX options: Internal inconsistency in Local Connection Options!\n");
550 return 524;
551 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200552
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200553 talloc_free(lco->string);
554 lco->string = talloc_strdup(ctx, options);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200555
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200556 lco_id = lco->string;
557 while ((lco_id = get_lco_identifier(lco_id))) {
558 switch (tolower(lco_id[0])) {
559 case 'p':
560 if (sscanf(lco_id + 1, ":%d-%d",
561 &lco->pkt_period_min, &lco->pkt_period_max) == 1)
562 lco->pkt_period_max = lco->pkt_period_min;
563 break;
564 case 'a':
Pau Espin Pedrol1dc2dce2020-09-21 11:25:15 +0200565 /* FIXME: LCO also supports the negotiation of more than one codec.
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200566 * (e.g. a:PCMU;G726-32) But this implementation only supports a single
567 * codec only. */
568 if (sscanf(lco_id + 1, ":%16[^,]", codec) == 1) {
569 talloc_free(lco->codec);
Pau Espin Pedrol83fd8a52019-06-26 12:55:26 +0200570 /* MGCP header is case insensive, and we'll need
571 codec in uppercase when using it later: */
572 len = strlen(codec);
573 lco->codec = talloc_size(ctx, len + 1);
574 osmo_str_toupper_buf(lco->codec, len + 1, codec);
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200575 }
576 break;
Pau Espin Pedrol19539862020-09-21 11:43:41 +0200577 case 'n':
578 if (lco_id[1] == 't' && sscanf(lco_id + 2, ":%16[^,]", nt) == 1)
579 break;
580 /* else: fall throught to print notice log */
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200581 default:
582 LOGP(DLMGCP, LOGL_NOTICE,
583 "LCO: unhandled option: '%c'/%d in \"%s\"\n",
584 *lco_id, *lco_id, lco->string);
585 break;
586 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200587
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200588 lco_id = strchr(lco_id, ',');
589 if (!lco_id)
590 break;
Philipp Maier604410c2018-06-06 10:02:16 +0200591 }
Philipp Maier87bd9be2017-08-22 16:35:41 +0200592
593 LOGP(DLMGCP, LOGL_DEBUG,
594 "local CX options: lco->pkt_period_max: %i, lco->codec: %s\n",
595 lco->pkt_period_max, lco->codec);
Philipp Maiera390d0b2018-01-31 17:30:19 +0100596
597 /* Check if the packetization fits the 20ms raster */
598 if (lco->pkt_period_min % 20 && lco->pkt_period_max % 20) {
599 LOGP(DLMGCP, LOGL_ERROR,
600 "local CX options: packetization interval is not a multiple of 20ms!\n");
601 return 535;
602 }
603
604 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200605}
606
607void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
608 struct mgcp_rtp_end *rtp)
609{
Philipp Maier14b27a82020-06-02 20:15:30 +0200610 struct mgcp_trunk *trunk = endp->trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200611
Philipp Maier14b27a82020-06-02 20:15:30 +0200612 int patch_ssrc = expect_ssrc_change && trunk->force_constant_ssrc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200613
Philipp Maier14b27a82020-06-02 20:15:30 +0200614 rtp->force_aligned_timing = trunk->force_aligned_timing;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200615 rtp->force_constant_ssrc = patch_ssrc ? 1 : 0;
Philipp Maier14b27a82020-06-02 20:15:30 +0200616 rtp->rfc5993_hr_convert = trunk->rfc5993_hr_convert;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200617
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200618 LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
619 "Configuring RTP endpoint: local port %d%s%s\n",
620 ntohs(rtp->rtp_port),
621 rtp->force_aligned_timing ? ", force constant timing" : "",
622 rtp->force_constant_ssrc ? ", force constant ssrc" : "");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200623}
624
Pau Espin Pedrol8358c4b2021-07-07 12:41:38 +0200625uint32_t mgcp_rtp_packet_duration(const struct mgcp_endpoint *endp,
626 const struct mgcp_rtp_end *rtp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200627{
628 int f = 0;
629
630 /* Get the number of frames per channel and packet */
631 if (rtp->frames_per_packet)
632 f = rtp->frames_per_packet;
Philipp Maierbc0346e2018-06-07 09:52:16 +0200633 else if (rtp->packet_duration_ms && rtp->codec->frame_duration_num) {
634 int den = 1000 * rtp->codec->frame_duration_num;
635 f = (rtp->packet_duration_ms * rtp->codec->frame_duration_den +
Philipp Maier87bd9be2017-08-22 16:35:41 +0200636 den / 2)
637 / den;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200638 }
639
Philipp Maierbc0346e2018-06-07 09:52:16 +0200640 return rtp->codec->rate * f * rtp->codec->frame_duration_num /
641 rtp->codec->frame_duration_den;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200642}
643
Pau Espin Pedrol9fb8ddf2019-05-08 15:35:36 +0200644/*! Initializes osmux socket if not yet initialized. Parses Osmux CID from MGCP line.
645 * \param[in] endp Endpoint willing to initialize osmux
646 * \param[in] line Line X-Osmux from MGCP header msg to parse
647 * \returns OSMUX CID, -1 for wildcard, -2 on parse error, -3 on osmux initalize error
648 */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200649static int mgcp_osmux_setup(struct mgcp_endpoint *endp, const char *line)
650{
651 if (!endp->cfg->osmux_init) {
652 if (osmux_init(OSMUX_ROLE_BSC, endp->cfg) < 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200653 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "Cannot init OSMUX\n");
Pau Espin Pedrol9fb8ddf2019-05-08 15:35:36 +0200654 return -3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200655 }
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200656 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "OSMUX socket has been set up\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200657 }
658
659 return mgcp_parse_osmux_cid(line);
660}
661
Philipp Maierbc0346e2018-06-07 09:52:16 +0200662/* Process codec information contained in CRCX/MDCX */
663static int handle_codec_info(struct mgcp_conn_rtp *conn,
664 struct mgcp_parse_data *p, int have_sdp, bool crcx)
665{
666 struct mgcp_endpoint *endp = p->endp;
667 int rc;
668 char *cmd;
669
670 if (crcx)
671 cmd = "CRCX";
672 else
673 cmd = "MDCX";
674
675 /* Collect codec information */
676 if (have_sdp) {
677 /* If we have SDP, we ignore the local connection options and
678 * use only the SDP information. */
679 mgcp_codec_reset_all(conn);
680 rc = mgcp_parse_sdp_data(endp, conn, p);
681 if (rc != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200682 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
683 "%s: sdp not parseable\n", cmd);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200684
685 /* See also RFC 3661: Protocol error */
686 return 510;
687 }
688 } else if (endp->local_options.codec) {
689 /* When no SDP is available, we use the codec information from
690 * the local connection options (if present) */
691 mgcp_codec_reset_all(conn);
Philipp Maier228e5912019-03-05 13:56:59 +0100692 rc = mgcp_codec_add(conn, PTYPE_UNDEFINED, endp->local_options.codec, NULL);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200693 if (rc != 0)
694 goto error;
695 }
696
697 /* Make sure we always set a sane default codec */
698 if (conn->end.codecs_assigned == 0) {
699 /* When SDP and/or LCO did not supply any codec information,
700 * than it makes sense to pick a sane default: (payload-type 0,
701 * PCMU), see also: OS#2658 */
702 mgcp_codec_reset_all(conn);
Philipp Maier228e5912019-03-05 13:56:59 +0100703 rc = mgcp_codec_add(conn, 0, NULL, NULL);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200704 if (rc != 0)
705 goto error;
706 }
707
708 /* Make codec decision */
709 if (mgcp_codec_decide(conn) != 0)
710 goto error;
711
712 return 0;
713
714error:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200715 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
716 "%s: codec negotiation failure\n", cmd);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200717
718 /* See also RFC 3661: Codec negotiation failure */
719 return 534;
720}
721
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200722static bool parse_x_osmo_ign(struct mgcp_endpoint *endp, char *line)
723{
724 char *saveptr = NULL;
725
Pau Espin Pedrol6049a632020-09-21 11:03:21 +0200726 if (strncasecmp(line, MGCP_X_OSMO_IGN_HEADER, strlen(MGCP_X_OSMO_IGN_HEADER)))
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200727 return false;
728 line += strlen(MGCP_X_OSMO_IGN_HEADER);
729
730 while (1) {
731 char *token = strtok_r(line, " ", &saveptr);
732 line = NULL;
733 if (!token)
734 break;
735
Pau Espin Pedrol6e26c702019-06-26 13:09:39 +0200736 if (!strcasecmp(token, "C"))
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200737 endp->x_osmo_ign |= MGCP_X_OSMO_IGN_CALLID;
738 else
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200739 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "received unknown X-Osmo-IGN item '%s'\n", token);
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200740 }
741
742 return true;
743}
744
Philipp Maier87bd9be2017-08-22 16:35:41 +0200745/* CRCX command handler, processes the received command */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200746static struct msgb *handle_create_con(struct mgcp_parse_data *p)
747{
Philipp Maier14b27a82020-06-02 20:15:30 +0200748 struct mgcp_trunk *trunk = p->endp->trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200749 struct mgcp_endpoint *endp = p->endp;
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200750 struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_crcx_ctr_group;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200751 int error_code = 400;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200752 const char *local_options = NULL;
753 const char *callid = NULL;
754 const char *mode = NULL;
755 char *line;
Pau Espin Pedrol2b896172019-04-24 13:47:23 +0200756 int have_sdp = 0, osmux_cid = -2;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200757 struct mgcp_conn_rtp *conn = NULL;
Philipp Maierffd75e42017-11-22 11:44:50 +0100758 struct mgcp_conn *_conn = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200759 char conn_name[512];
Philipp Maiera390d0b2018-01-31 17:30:19 +0100760 int rc;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200761
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200762 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "CRCX: creating new connection ...\n");
Philipp Maier246233d2020-08-18 15:15:24 +0200763
Philipp Maier8d6a1932020-06-18 12:19:31 +0200764 if (!mgcp_endp_avail(endp)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200765 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_AVAIL));
Philipp Maiera910a812020-08-18 15:13:33 +0200766 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
767 "CRCX: selected endpoint not available!\n");
Philipp Maier8d6a1932020-06-18 12:19:31 +0200768 return create_err_response(NULL, 501, "CRCX", p->trans);
769 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200770
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200771 /* parse CallID C: and LocalParameters L: */
772 for_each_line(line, p->save) {
773 if (!mgcp_check_param(endp, line))
774 continue;
775
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +0200776 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200777 case 'L':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200778 local_options = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200779 break;
780 case 'C':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200781 callid = (const char *)line + 3;
782 break;
783 case 'I':
Philipp Maierffd75e42017-11-22 11:44:50 +0100784 /* It is illegal to send a connection identifier
785 * together with a CRCX, the MGW will assign the
786 * connection identifier by itself on CRCX */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200787 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BAD_ACTION));
Philipp Maierffd75e42017-11-22 11:44:50 +0100788 return create_err_response(NULL, 523, "CRCX", p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200789 break;
790 case 'M':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200791 mode = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200792 break;
793 case 'X':
Pau Espin Pedrolc1bf4692019-05-14 16:23:24 +0200794 if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200795 /* If osmux is disabled, just skip setting it up */
796 if (!p->endp->cfg->osmux)
797 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200798 osmux_cid = mgcp_osmux_setup(endp, line);
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200799 break;
800 }
801
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200802 if (parse_x_osmo_ign(endp, line))
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200803 break;
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200804
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200805 /* Ignore unknown X-headers */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200806 break;
807 case '\0':
808 have_sdp = 1;
809 goto mgcp_header_done;
810 default:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200811 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
812 "CRCX: unhandled option: '%c'/%d\n", *line, *line);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200813 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_UNHANDLED_PARAM));
Philipp Maierdd0c5222018-02-02 11:08:48 +0100814 return create_err_response(NULL, 539, "CRCX", p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200815 break;
816 }
817 }
818
819mgcp_header_done:
Philipp Maier87bd9be2017-08-22 16:35:41 +0200820 /* Check parameters */
821 if (!callid) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200822 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
823 "CRCX: insufficient parameters, missing callid\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200824 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_MISSING_CALLID));
Harald Weltee35eeae2017-12-28 13:47:37 +0100825 return create_err_response(endp, 516, "CRCX", p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200826 }
827
Philipp Maier87bd9be2017-08-22 16:35:41 +0200828 if (!mode) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200829 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
830 "CRCX: insufficient parameters, missing mode\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200831 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_MODE));
Harald Weltee35eeae2017-12-28 13:47:37 +0100832 return create_err_response(endp, 517, "CRCX", p->trans);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200833 }
834
Philipp Maier87bd9be2017-08-22 16:35:41 +0200835 /* Check if we are able to accept the creation of another connection */
836 if (llist_count(&endp->conns) >= endp->type->max_conns) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200837 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
838 "CRCX: endpoint full, max. %i connections allowed!\n",
839 endp->type->max_conns);
Philipp Maier14b27a82020-06-02 20:15:30 +0200840 if (trunk->force_realloc) {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200841 /* There is no more room for a connection, make some
842 * room by blindly tossing the oldest of the two two
843 * connections */
844 mgcp_conn_free_oldest(endp);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200845 } else {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200846 /* There is no more room for a connection, leave
847 * everything as it is and return with an error */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200848 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_LIMIT_EXCEEDED));
Harald Weltee35eeae2017-12-28 13:47:37 +0100849 return create_err_response(endp, 540, "CRCX", p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200850 }
851 }
852
Philipp Maier87bd9be2017-08-22 16:35:41 +0200853 /* Check if this endpoint already serves a call, if so, check if the
854 * callids match up so that we are sure that this is our call */
855 if (endp->callid && mgcp_verify_call_id(endp, callid)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200856 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
857 "CRCX: already seized by other call (%s)\n",
858 endp->callid);
Philipp Maier14b27a82020-06-02 20:15:30 +0200859 if (trunk->force_realloc)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200860 /* This is not our call, toss everything by releasing
861 * the entire endpoint. (rude!) */
Philipp Maier1355d7e2018-02-01 14:30:06 +0100862 mgcp_endp_release(endp);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200863 else {
864 /* This is not our call, leave everything as it is and
865 * return with an error. */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200866 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_UNKNOWN_CALLID));
Philipp Maier87bd9be2017-08-22 16:35:41 +0200867 return create_err_response(endp, 400, "CRCX", p->trans);
868 }
869 }
870
Philipp Maier889fe7f2020-07-06 17:44:12 +0200871 if (!endp->callid) {
872 /* Claim endpoint resources. This will also set the callid,
873 * creating additional connections will only be possible if
874 * the callid matches up (see above). */
875 rc = mgcp_endp_claim(endp, callid);
876 if (rc != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200877 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_CLAIM));
Philipp Maier889fe7f2020-07-06 17:44:12 +0200878 return create_err_response(endp, 502, "CRCX", p->trans);
879 }
880 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200881
Philipp Maierffd75e42017-11-22 11:44:50 +0100882 snprintf(conn_name, sizeof(conn_name), "%s", callid);
Philipp Maier14b27a82020-06-02 20:15:30 +0200883 _conn = mgcp_conn_alloc(trunk->endpoints, endp, MGCP_CONN_TYPE_RTP, conn_name);
Philipp Maierffd75e42017-11-22 11:44:50 +0100884 if (!_conn) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200885 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
886 "CRCX: unable to allocate RTP connection\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200887 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_ALLOC_CONN));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200888 goto error2;
889
Philipp Maier87bd9be2017-08-22 16:35:41 +0200890 }
Philipp Maier889fe7f2020-07-06 17:44:12 +0200891
Philipp Maierffd75e42017-11-22 11:44:50 +0100892 conn = mgcp_conn_get_rtp(endp, _conn->id);
893 OSMO_ASSERT(conn);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200894
895 if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
896 error_code = 517;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200897 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_MODE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200898 goto error2;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200899 }
900
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200901 /* Annotate Osmux circuit ID and set it to negotiating state until this
Philipp Maier87bd9be2017-08-22 16:35:41 +0200902 * is fully set up from the dummy load. */
903 conn->osmux.state = OSMUX_STATE_DISABLED;
Pau Espin Pedrol2b896172019-04-24 13:47:23 +0200904 if (osmux_cid >= -1) { /* -1 is wilcard, alloc next avail CID */
Pau Espin Pedrol14f8a082019-05-13 13:10:06 +0200905 conn->osmux.state = OSMUX_STATE_ACTIVATING;
Pau Espin Pedrol2b896172019-04-24 13:47:23 +0200906 if (conn_osmux_allocate_cid(conn, osmux_cid) == -1) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200907 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_OSMUX));
Pau Espin Pedrol2b896172019-04-24 13:47:23 +0200908 goto error2;
909 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200910 } else if (endp->cfg->osmux == OSMUX_USAGE_ONLY) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200911 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
912 "CRCX: osmux only and no osmux offered\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200913 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_OSMUX));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200914 goto error2;
915 }
916
Philipp Maierbc0346e2018-06-07 09:52:16 +0200917 /* Set local connection options, if present */
918 if (local_options) {
Philipp Maier14b27a82020-06-02 20:15:30 +0200919 rc = set_local_cx_options(endp->trunk->endpoints,
Philipp Maierbc0346e2018-06-07 09:52:16 +0200920 &endp->local_options, local_options);
921 if (rc != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200922 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
923 "CRCX: inavlid local connection options!\n");
Philipp Maierbc0346e2018-06-07 09:52:16 +0200924 error_code = rc;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200925 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS));
Philipp Maierbc0346e2018-06-07 09:52:16 +0200926 goto error2;
927 }
928 }
929
930 /* Handle codec information and decide for a suitable codec */
931 rc = handle_codec_info(conn, p, have_sdp, true);
932 mgcp_codec_summary(conn);
933 if (rc) {
934 error_code = rc;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200935 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_CODEC_NEGOTIATION));
Philipp Maierbc0346e2018-06-07 09:52:16 +0200936 goto error2;
937 }
938
Philipp Maier14b27a82020-06-02 20:15:30 +0200939 conn->end.fmtp_extra = talloc_strdup(trunk->endpoints,
940 trunk->audio_fmtp_extra);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200941
Philipp Maier87bd9be2017-08-22 16:35:41 +0200942 if (p->cfg->force_ptime) {
943 conn->end.packet_duration_ms = p->cfg->force_ptime;
944 conn->end.force_output_ptime = 1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200945 }
946
Philipp Maier1cb1e382017-11-02 17:16:04 +0100947 mgcp_rtp_end_config(endp, 0, &conn->end);
948
Philipp Maierc3cc6542018-02-02 12:58:42 +0100949 /* check connection mode setting */
950 if (conn->conn->mode != MGCP_CONN_LOOPBACK
951 && conn->conn->mode != MGCP_CONN_RECV_ONLY
952 && conn->end.rtp_port == 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200953 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
954 "CRCX: selected connection mode type requires an opposite end!\n");
Philipp Maierc3cc6542018-02-02 12:58:42 +0100955 error_code = 527;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200956 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC));
Philipp Maierc3cc6542018-02-02 12:58:42 +0100957 goto error2;
958 }
959
Pau Espin Pedrol71d42e72020-09-03 14:20:07 +0200960 /* Find a local address for conn based on policy and initial SDP remote
961 information, then find a free port for it */
962 mgcp_get_local_addr(conn->end.local_addr, conn);
Philipp Maier1cb1e382017-11-02 17:16:04 +0100963 if (allocate_port(endp, conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200964 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
Philipp Maier1cb1e382017-11-02 17:16:04 +0100965 goto error2;
966 }
967
Philipp Maier87bd9be2017-08-22 16:35:41 +0200968 if (setup_rtp_processing(endp, conn) != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200969 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
970 "CRCX: could not start RTP processing!\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200971 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_START_RTP));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200972 goto error2;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200973 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200974
975 /* policy CB */
976 if (p->cfg->policy_cb) {
977 int rc;
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200978 rc = p->cfg->policy_cb(endp, MGCP_ENDP_CRCX, p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200979 switch (rc) {
980 case MGCP_POLICY_REJECT:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200981 LOGPCONN(_conn, DLMGCP, LOGL_NOTICE,
982 "CRCX: CRCX rejected by policy\n");
Philipp Maier1355d7e2018-02-01 14:30:06 +0100983 mgcp_endp_release(endp);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200984 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_REJECTED_BY_POLICY));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200985 return create_err_response(endp, 400, "CRCX", p->trans);
986 break;
987 case MGCP_POLICY_DEFER:
988 /* stop processing */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200989 return NULL;
990 break;
991 case MGCP_POLICY_CONT:
992 /* just continue */
993 break;
994 }
995 }
996
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200997 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
998 "CRCX: Creating connection: port: %u\n", conn->end.local_port);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200999 if (p->cfg->change_cb)
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001000 p->cfg->change_cb(endp, MGCP_ENDP_CRCX);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001001
Philipp Maiere726d4f2017-11-01 10:41:34 +01001002 /* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
Philipp Maier14b27a82020-06-02 20:15:30 +02001003 OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Pau Espin Pedrolca280a12021-07-06 18:15:35 +02001004 if (conn->conn->mode & MGCP_CONN_RECV_ONLY &&
1005 mgcp_rtp_end_remote_addr_available(&conn->end) &&
1006 trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001007 send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001008
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001009 LOGPCONN(_conn, DLMGCP, LOGL_NOTICE,
1010 "CRCX: connection successfully created\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001011 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_SUCCESS));
Philipp Maier889fe7f2020-07-06 17:44:12 +02001012 mgcp_endp_update(endp);
Philipp Maier55295f72018-01-15 14:00:28 +01001013 return create_response_with_sdp(endp, conn, "CRCX", p->trans, true);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001014error2:
Philipp Maier1355d7e2018-02-01 14:30:06 +01001015 mgcp_endp_release(endp);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001016 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1017 "CRCX: unable to create connection\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001018 return create_err_response(endp, error_code, "CRCX", p->trans);
1019}
1020
Philipp Maier87bd9be2017-08-22 16:35:41 +02001021/* MDCX command handler, processes the received command */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001022static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
1023{
1024 struct mgcp_endpoint *endp = p->endp;
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001025 struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.mgcp_mdcx_ctr_group;
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001026 char new_local_addr[INET6_ADDRSTRLEN];
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001027 int error_code = 500;
1028 int silent = 0;
1029 int have_sdp = 0;
1030 char *line;
1031 const char *local_options = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001032 const char *mode = NULL;
1033 struct mgcp_conn_rtp *conn = NULL;
Philipp Maier01d24a32017-11-21 17:26:09 +01001034 const char *conn_id = NULL;
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001035 int osmux_cid = -2;
Philipp Maiera390d0b2018-01-31 17:30:19 +01001036 int rc;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001037
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001038 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "MDCX: modifying existing connection ...\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001039
Philipp Maier8d6a1932020-06-18 12:19:31 +02001040 if (!mgcp_endp_avail(endp)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001041 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_AVAIL));
Philipp Maiera910a812020-08-18 15:13:33 +02001042 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1043 "MDCX: selected endpoint not available!\n");
Philipp Maier8d6a1932020-06-18 12:19:31 +02001044 return create_err_response(NULL, 501, "MDCX", p->trans);
1045 }
1046
Philipp Maier5656fbf2018-02-02 14:41:58 +01001047 /* Prohibit wildcarded requests */
1048 if (endp->wildcarded_req) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001049 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1050 "MDCX: wildcarded endpoint names not supported.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001051 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_WILDCARD));
Philipp Maier5656fbf2018-02-02 14:41:58 +01001052 return create_err_response(endp, 507, "MDCX", p->trans);
1053 }
1054
Philipp Maier87bd9be2017-08-22 16:35:41 +02001055 if (llist_count(&endp->conns) <= 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001056 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1057 "MDCX: endpoint is not holding a connection.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001058 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_CONN));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001059 return create_err_response(endp, 400, "MDCX", p->trans);
1060 }
1061
1062 for_each_line(line, p->save) {
1063 if (!mgcp_check_param(endp, line))
1064 continue;
1065
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001066 switch (toupper(line[0])) {
Philipp Maier87bd9be2017-08-22 16:35:41 +02001067 case 'C':
Harald Weltee35eeae2017-12-28 13:47:37 +01001068 if (mgcp_verify_call_id(endp, line + 3) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001069 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CALLID));
Harald Weltee35eeae2017-12-28 13:47:37 +01001070 error_code = 516;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001071 goto error3;
Harald Weltee35eeae2017-12-28 13:47:37 +01001072 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001073 break;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001074 case 'I':
Philipp Maier01d24a32017-11-21 17:26:09 +01001075 conn_id = (const char *)line + 3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001076 if ((error_code = mgcp_verify_ci(endp, conn_id))) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001077 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CONNID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001078 goto error3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001079 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001080 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001081 case 'L':
Philipp Maier87bd9be2017-08-22 16:35:41 +02001082 local_options = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001083 break;
1084 case 'M':
Philipp Maier87bd9be2017-08-22 16:35:41 +02001085 mode = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001086 break;
1087 case 'Z':
Pau Espin Pedrol9b508f62019-06-26 13:11:22 +02001088 silent = strcasecmp("noanswer", line + 3) == 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001089 break;
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001090 case 'X':
Pau Espin Pedrolc1bf4692019-05-14 16:23:24 +02001091 if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001092 /* If osmux is disabled, just skip setting it up */
1093 if (!p->endp->cfg->osmux)
1094 break;
1095 osmux_cid = mgcp_osmux_setup(endp, line);
1096 break;
1097 }
1098 /* Ignore unknown X-headers */
1099 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001100 case '\0':
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001101 have_sdp = 1;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001102 goto mgcp_header_done;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001103 break;
1104 default:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001105 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1106 "MDCX: Unhandled MGCP option: '%c'/%d\n",
1107 line[0], line[0]);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001108 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_UNHANDLED_PARAM));
Philipp Maierdd0c5222018-02-02 11:08:48 +01001109 return create_err_response(NULL, 539, "MDCX", p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001110 break;
1111 }
1112 }
1113
Philipp Maier87bd9be2017-08-22 16:35:41 +02001114mgcp_header_done:
Philipp Maier01d24a32017-11-21 17:26:09 +01001115 if (!conn_id) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001116 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1117 "MDCX: insufficient parameters, missing ci (connectionIdentifier)\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001118 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_CONNID));
Harald Weltee35eeae2017-12-28 13:47:37 +01001119 return create_err_response(endp, 515, "MDCX", p->trans);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001120 }
1121
1122 conn = mgcp_conn_get_rtp(endp, conn_id);
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001123 if (!conn) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001124 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_CONN_NOT_FOUND));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001125 return create_err_response(endp, 400, "MDCX", p->trans);
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001126 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001127
Oliver Smithe36b7752019-01-22 16:31:36 +01001128 mgcp_conn_watchdog_kick(conn->conn);
1129
Philipp Maier87bd9be2017-08-22 16:35:41 +02001130 if (mode) {
1131 if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001132 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_MODE));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001133 error_code = 517;
1134 goto error3;
1135 }
1136 } else
Pau Espin Pedrol209eb9f2019-04-24 12:03:04 +02001137 conn->conn->mode = conn->conn->mode_orig;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001138
Philipp Maierbc0346e2018-06-07 09:52:16 +02001139 /* Set local connection options, if present */
1140 if (local_options) {
Philipp Maier14b27a82020-06-02 20:15:30 +02001141 rc = set_local_cx_options(endp->trunk->endpoints,
Philipp Maierbc0346e2018-06-07 09:52:16 +02001142 &endp->local_options, local_options);
1143 if (rc != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001144 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1145 "MDCX: invalid local connection options!\n");
Philipp Maierbc0346e2018-06-07 09:52:16 +02001146 error_code = rc;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001147 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS));
Philipp Maierbc0346e2018-06-07 09:52:16 +02001148 goto error3;
1149 }
1150 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001151
Philipp Maierbc0346e2018-06-07 09:52:16 +02001152 /* Handle codec information and decide for a suitable codec */
1153 rc = handle_codec_info(conn, p, have_sdp, false);
1154 mgcp_codec_summary(conn);
1155 if (rc) {
Philipp Maieraf07f662018-02-02 11:34:02 +01001156 error_code = rc;
1157 goto error3;
Philipp Maiera390d0b2018-01-31 17:30:19 +01001158 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001159
Philipp Maierc3cc6542018-02-02 12:58:42 +01001160 /* check connection mode setting */
1161 if (conn->conn->mode != MGCP_CONN_LOOPBACK
1162 && conn->conn->mode != MGCP_CONN_RECV_ONLY
Pau Espin Pedrold6769ea2021-07-06 19:44:27 +02001163 && !mgcp_rtp_end_remote_addr_available(&conn->end)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001164 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1165 "MDCX: selected connection mode type requires an opposite end!\n");
Philipp Maierc3cc6542018-02-02 12:58:42 +01001166 error_code = 527;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001167 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC));
Philipp Maierc3cc6542018-02-02 12:58:42 +01001168 goto error3;
1169 }
1170
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001171 if (mgcp_conn_rtp_is_osmux(conn)) {
1172 OSMO_ASSERT(conn->osmux.cid_allocated);
1173 if (osmux_cid < -1) {
1174 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1175 "MDCX: Failed to parse Osmux CID!\n");
1176 goto error3;
1177 } else if (osmux_cid == -1) {
1178 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1179 "MDCX: wilcard in MDCX is not supported!\n");
1180 goto error3;
1181 } else if (osmux_cid != (int) conn->osmux.cid) {
1182 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1183 "MDCX: changing already allocated CID is not supported!\n");
1184 goto error3;
1185 }
1186 /* TODO: In the future (when we have recvCID!=sendCID), we need to
1187 tell Osmux code that osmux_cid is to be used as sendCID for
1188 that conn. */
1189 }
Philipp Maierbc0346e2018-06-07 09:52:16 +02001190
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001191 /* MDCX may have provided a new remote address, which means we may need
1192 to update our announced IP addr and re-bind our local end. This can
1193 happen for instance if MGW initially provided an IPv4 during CRCX
1194 ACK, and now MDCX tells us the remote has an IPv6 address. */
1195 mgcp_get_local_addr(new_local_addr, conn);
1196 if (strcmp(new_local_addr, conn->end.local_addr)) {
1197 osmo_strlcpy(conn->end.local_addr, new_local_addr, sizeof(conn->end.local_addr));
1198 mgcp_free_rtp_port(&conn->end);
1199 if (allocate_port(endp, conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001200 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001201 goto error3;
1202 }
1203 }
1204
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001205 if (setup_rtp_processing(endp, conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001206 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_START_RTP));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001207 goto error3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001208 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001209
Philipp Maier87bd9be2017-08-22 16:35:41 +02001210
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001211 /* policy CB */
1212 if (p->cfg->policy_cb) {
1213 int rc;
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001214 rc = p->cfg->policy_cb(endp, MGCP_ENDP_MDCX, p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001215 switch (rc) {
1216 case MGCP_POLICY_REJECT:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001217 LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
1218 "MDCX: rejected by policy\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001219 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_REJECTED_BY_POLICY));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001220 if (silent)
1221 goto out_silent;
1222 return create_err_response(endp, 400, "MDCX", p->trans);
1223 break;
1224 case MGCP_POLICY_DEFER:
1225 /* stop processing */
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001226 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
1227 "MDCX: deferred by policy\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001228 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_DEFERRED_BY_POLICY));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001229 return NULL;
1230 break;
1231 case MGCP_POLICY_CONT:
1232 /* just continue */
1233 break;
1234 }
1235 }
1236
Philipp Maier87bd9be2017-08-22 16:35:41 +02001237 mgcp_rtp_end_config(endp, 1, &conn->end);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001238
1239 /* modify */
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001240 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
1241 "MDCX: modified conn:%s\n", mgcp_conn_dump(conn->conn));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001242 if (p->cfg->change_cb)
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001243 p->cfg->change_cb(endp, MGCP_ENDP_MDCX);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001244
Philipp Maiere726d4f2017-11-01 10:41:34 +01001245 /* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
Philipp Maier14b27a82020-06-02 20:15:30 +02001246 OSMO_ASSERT(endp->trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Pau Espin Pedrolca280a12021-07-06 18:15:35 +02001247 if (conn->conn->mode & MGCP_CONN_RECV_ONLY &&
1248 mgcp_rtp_end_remote_addr_available(&conn->end) &&
1249 endp->trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001250 send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001251
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001252 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_SUCCESS));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001253 if (silent)
1254 goto out_silent;
1255
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001256 LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
1257 "MDCX: connection successfully modified\n");
Philipp Maier889fe7f2020-07-06 17:44:12 +02001258 mgcp_endp_update(endp);
Philipp Maier55295f72018-01-15 14:00:28 +01001259 return create_response_with_sdp(endp, conn, "MDCX", p->trans, false);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001260error3:
1261 return create_err_response(endp, error_code, "MDCX", p->trans);
1262
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001263out_silent:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001264 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "MDCX: silent exit\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001265 return NULL;
1266}
1267
Philipp Maier87bd9be2017-08-22 16:35:41 +02001268/* DLCX command handler, processes the received command */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001269static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
1270{
1271 struct mgcp_endpoint *endp = p->endp;
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001272 struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.mgcp_dlcx_ctr_group;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001273 int error_code = 400;
1274 int silent = 0;
1275 char *line;
1276 char stats[1048];
Philipp Maier01d24a32017-11-21 17:26:09 +01001277 const char *conn_id = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001278 struct mgcp_conn_rtp *conn = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001279
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001280 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1281 "DLCX: deleting connection ...\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001282
Philipp Maier8d6a1932020-06-18 12:19:31 +02001283 if (!mgcp_endp_avail(endp)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001284 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_AVAIL));
Philipp Maiera910a812020-08-18 15:13:33 +02001285 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1286 "DLCX: selected endpoint not available!\n");
Philipp Maier8d6a1932020-06-18 12:19:31 +02001287 return create_err_response(NULL, 501, "DLCX", p->trans);
1288 }
1289
Philipp Maier5656fbf2018-02-02 14:41:58 +01001290 /* Prohibit wildcarded requests */
1291 if (endp->wildcarded_req) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001292 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1293 "DLCX: wildcarded endpoint names not supported.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001294 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_WILDCARD));
Philipp Maier5656fbf2018-02-02 14:41:58 +01001295 return create_err_response(endp, 507, "DLCX", p->trans);
1296 }
1297
Philipp Maier87bd9be2017-08-22 16:35:41 +02001298 if (llist_count(&endp->conns) <= 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001299 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1300 "DLCX: endpoint is not holding a connection.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001301 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_NO_CONN));
Harald Weltee35eeae2017-12-28 13:47:37 +01001302 return create_err_response(endp, 515, "DLCX", p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001303 }
1304
1305 for_each_line(line, p->save) {
1306 if (!mgcp_check_param(endp, line))
1307 continue;
1308
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001309 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001310 case 'C':
Harald Weltee35eeae2017-12-28 13:47:37 +01001311 if (mgcp_verify_call_id(endp, line + 3) != 0) {
1312 error_code = 516;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001313 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CALLID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001314 goto error3;
Harald Weltee35eeae2017-12-28 13:47:37 +01001315 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001316 break;
1317 case 'I':
Philipp Maier01d24a32017-11-21 17:26:09 +01001318 conn_id = (const char *)line + 3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001319 if ((error_code = mgcp_verify_ci(endp, conn_id))) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001320 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CONNID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001321 goto error3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001322 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001323 break;
1324 case 'Z':
Pau Espin Pedrol9b508f62019-06-26 13:11:22 +02001325 silent = strcasecmp("noanswer", line + 3) == 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001326 break;
1327 default:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001328 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1329 "DLCX: Unhandled MGCP option: '%c'/%d\n",
1330 line[0], line[0]);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001331 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));
Philipp Maierdd0c5222018-02-02 11:08:48 +01001332 return create_err_response(NULL, 539, "DLCX", p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001333 break;
1334 }
1335 }
1336
1337 /* policy CB */
1338 if (p->cfg->policy_cb) {
1339 int rc;
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001340 rc = p->cfg->policy_cb(endp, MGCP_ENDP_DLCX, p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001341 switch (rc) {
1342 case MGCP_POLICY_REJECT:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001343 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "DLCX: rejected by policy\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001344 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_REJECTED_BY_POLICY));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001345 if (silent)
1346 goto out_silent;
1347 return create_err_response(endp, 400, "DLCX", p->trans);
1348 break;
1349 case MGCP_POLICY_DEFER:
1350 /* stop processing */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001351 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_DEFERRED_BY_POLICY));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001352 return NULL;
1353 break;
1354 case MGCP_POLICY_CONT:
1355 /* just continue */
1356 break;
1357 }
1358 }
1359
Philipp Maierf4c0e372017-10-11 16:06:45 +02001360 /* When no connection id is supplied, we will interpret this as a
1361 * wildcarded DLCX and drop all connections at once. (See also
1362 * RFC3435 Section F.7) */
Philipp Maier01d24a32017-11-21 17:26:09 +01001363 if (!conn_id) {
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001364 int num_conns = llist_count(&endp->conns);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001365 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1366 "DLCX: missing ci (connectionIdentifier), will remove all connections (%d total) at once\n",
1367 num_conns);
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001368
1369 if (num_conns > 0)
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001370 rate_ctr_add(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS), num_conns);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001371
Philipp Maier1355d7e2018-02-01 14:30:06 +01001372 mgcp_endp_release(endp);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001373
1374 /* Note: In this case we do not return any statistics,
1375 * as we assume that the client is not interested in
1376 * this case. */
1377 return create_ok_response(endp, 200, "DLCX", p->trans);
1378 }
1379
Philipp Maierf4c0e372017-10-11 16:06:45 +02001380 /* Find the connection */
Philipp Maier87bd9be2017-08-22 16:35:41 +02001381 conn = mgcp_conn_get_rtp(endp, conn_id);
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001382 if (!conn) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001383 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CONNID));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001384 goto error3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001385 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001386 /* save the statistics of the current connection */
1387 mgcp_format_stats(stats, sizeof(stats), conn->conn);
1388
1389 /* delete connection */
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001390 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG, "DLCX: deleting conn:%s\n",
1391 mgcp_conn_dump(conn->conn));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001392 mgcp_conn_free(endp, conn_id);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001393 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1394 "DLCX: connection successfully deleted\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +02001395
1396 /* When all connections are closed, the endpoint will be released
1397 * in order to be ready to be used by another call. */
1398 if (llist_count(&endp->conns) <= 0) {
Philipp Maier1355d7e2018-02-01 14:30:06 +01001399 mgcp_endp_release(endp);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001400 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: endpoint released\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +02001401 }
1402
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001403 if (p->cfg->change_cb)
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001404 p->cfg->change_cb(endp, MGCP_ENDP_DLCX);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001405
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001406 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001407 if (silent)
1408 goto out_silent;
1409 return create_ok_resp_with_param(endp, 250, "DLCX", p->trans, stats);
1410
1411error3:
1412 return create_err_response(endp, error_code, "DLCX", p->trans);
1413
1414out_silent:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001415 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: silent exit\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001416 return NULL;
1417}
1418
Philipp Maier87bd9be2017-08-22 16:35:41 +02001419/* RSIP command handler, processes the received command */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001420static struct msgb *handle_rsip(struct mgcp_parse_data *p)
1421{
Philipp Maier87bd9be2017-08-22 16:35:41 +02001422 /* TODO: Also implement the resetting of a specific endpoint
1423 * to make mgcp_send_reset_ep() work. Currently this will call
1424 * mgcp_rsip_cb() in mgw_main.c, which sets reset_endpoints=1
1425 * to make read_call_agent() reset all endpoints when called
1426 * next time. In order to selectively reset endpoints some
1427 * mechanism to distinguish which endpoint shall be resetted
1428 * is needed */
1429
1430 LOGP(DLMGCP, LOGL_NOTICE, "RSIP: resetting all endpoints ...\n");
1431
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001432 if (p->cfg->reset_cb)
Philipp Maier14b27a82020-06-02 20:15:30 +02001433 p->cfg->reset_cb(p->endp->trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001434 return NULL;
1435}
1436
1437static char extract_tone(const char *line)
1438{
1439 const char *str = strstr(line, "D/");
1440 if (!str)
1441 return CHAR_MAX;
1442
1443 return str[2];
1444}
1445
Philipp Maier87bd9be2017-08-22 16:35:41 +02001446/* This can request like DTMF detection and forward, fax detection... it
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001447 * can also request when the notification should be send and such. We don't
Philipp Maier87bd9be2017-08-22 16:35:41 +02001448 * do this right now. */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001449static struct msgb *handle_noti_req(struct mgcp_parse_data *p)
1450{
1451 int res = 0;
1452 char *line;
1453 char tone = CHAR_MAX;
1454
Philipp Maier87bd9be2017-08-22 16:35:41 +02001455 LOGP(DLMGCP, LOGL_NOTICE, "RQNT: processing request for notification ...\n");
1456
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001457 for_each_line(line, p->save) {
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001458 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001459 case 'S':
1460 tone = extract_tone(line);
1461 break;
1462 }
1463 }
1464
1465 /* we didn't see a signal request with a tone */
1466 if (tone == CHAR_MAX)
1467 return create_ok_response(p->endp, 200, "RQNT", p->trans);
1468
1469 if (p->cfg->rqnt_cb)
1470 res = p->cfg->rqnt_cb(p->endp, tone);
1471
1472 return res == 0 ?
Philipp Maier87bd9be2017-08-22 16:35:41 +02001473 create_ok_response(p->endp, 200, "RQNT", p->trans) :
1474 create_err_response(p->endp, res, "RQNT", p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001475}
1476
Philipp Maier87bd9be2017-08-22 16:35:41 +02001477/* Connection keepalive timer, will take care that dummy packets are send
Harald Welte1d1b98f2017-12-25 10:03:40 +01001478 * regularly, so that NAT connections stay open */
Philipp Maier14b27a82020-06-02 20:15:30 +02001479static void mgcp_keepalive_timer_cb(void *_trunk)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001480{
Philipp Maier14b27a82020-06-02 20:15:30 +02001481 struct mgcp_trunk *trunk = _trunk;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001482 struct mgcp_conn *conn;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001483 int i;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001484
Philipp Maiere726d4f2017-11-01 10:41:34 +01001485 LOGP(DLMGCP, LOGL_DEBUG, "triggered trunk %d keepalive timer\n",
Philipp Maier14b27a82020-06-02 20:15:30 +02001486 trunk->trunk_nr);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001487
Philipp Maiere726d4f2017-11-01 10:41:34 +01001488 /* Do not accept invalid configuration values
1489 * valid is MGCP_KEEPALIVE_NEVER, MGCP_KEEPALIVE_ONCE and
1490 * values greater 0 */
Philipp Maier14b27a82020-06-02 20:15:30 +02001491 OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Philipp Maiere726d4f2017-11-01 10:41:34 +01001492
1493 /* The dummy packet functionality has been disabled, we will exit
1494 * immediately, no further timer is scheduled, which means we will no
1495 * longer send dummy packets even when we did before */
Philipp Maier14b27a82020-06-02 20:15:30 +02001496 if (trunk->keepalive_interval == MGCP_KEEPALIVE_NEVER)
Philipp Maiere726d4f2017-11-01 10:41:34 +01001497 return;
1498
1499 /* In cases where only one dummy packet is sent, we do not need
1500 * the timer since the functions that handle the CRCX and MDCX are
1501 * triggering the sending of the dummy packet. So we behave like in
1502 * the MGCP_KEEPALIVE_NEVER case */
Philipp Maier14b27a82020-06-02 20:15:30 +02001503 if (trunk->keepalive_interval == MGCP_KEEPALIVE_ONCE)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001504 return;
1505
Philipp Maier87bd9be2017-08-22 16:35:41 +02001506 /* Send walk over all endpoints and send out dummy packets through
1507 * every connection present on each endpoint */
Philipp Maier4131a652021-07-07 14:04:34 +02001508 for (i = 0; i < trunk->number_endpoints; ++i) {
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001509 struct mgcp_endpoint *endp = trunk->endpoints[i];
Philipp Maier87bd9be2017-08-22 16:35:41 +02001510 llist_for_each_entry(conn, &endp->conns, entry) {
Pau Espin Pedrolca280a12021-07-06 18:15:35 +02001511 if (conn->type == MGCP_CONN_TYPE_RTP &&
1512 conn->mode == MGCP_CONN_RECV_ONLY &&
1513 mgcp_rtp_end_remote_addr_available(&conn->u.rtp.end))
Philipp Maier87bd9be2017-08-22 16:35:41 +02001514 send_dummy(endp, &conn->u.rtp);
1515 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001516 }
1517
Philipp Maiere726d4f2017-11-01 10:41:34 +01001518 /* Schedule the keepalive timer for the next round */
1519 LOGP(DLMGCP, LOGL_DEBUG, "rescheduling trunk %d keepalive timer\n",
Philipp Maier14b27a82020-06-02 20:15:30 +02001520 trunk->trunk_nr);
1521 osmo_timer_schedule(&trunk->keepalive_timer, trunk->keepalive_interval,
Philipp Maier87bd9be2017-08-22 16:35:41 +02001522 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001523}
1524
Philipp Maier14b27a82020-06-02 20:15:30 +02001525void mgcp_trunk_set_keepalive(struct mgcp_trunk *trunk, int interval)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001526{
Philipp Maier14b27a82020-06-02 20:15:30 +02001527 trunk->keepalive_interval = interval;
1528 osmo_timer_setup(&trunk->keepalive_timer, mgcp_keepalive_timer_cb, trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001529
1530 if (interval <= 0)
Philipp Maier14b27a82020-06-02 20:15:30 +02001531 osmo_timer_del(&trunk->keepalive_timer);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001532 else
Philipp Maier14b27a82020-06-02 20:15:30 +02001533 osmo_timer_schedule(&trunk->keepalive_timer,
1534 trunk->keepalive_interval, 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001535}
1536
Philipp Maier87bd9be2017-08-22 16:35:41 +02001537/*! allocate configuration with default values.
1538 * (called once at startup by main function) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001539struct mgcp_config *mgcp_config_alloc(void)
1540{
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001541 /* FIXME: This is unrelated to the protocol, put this in some
1542 * appropiate place! */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001543 struct mgcp_config *cfg;
1544
1545 cfg = talloc_zero(NULL, struct mgcp_config);
1546 if (!cfg) {
1547 LOGP(DLMGCP, LOGL_FATAL, "Failed to allocate config.\n");
1548 return NULL;
1549 }
1550
Philipp Maier12943ea2018-01-17 15:40:25 +01001551 osmo_strlcpy(cfg->domain, "mgw", sizeof(cfg->domain));
1552
Philipp Maier87bd9be2017-08-22 16:35:41 +02001553 cfg->net_ports.range_start = RTP_PORT_DEFAULT_RANGE_START;
1554 cfg->net_ports.range_end = RTP_PORT_DEFAULT_RANGE_END;
1555 cfg->net_ports.last_port = cfg->net_ports.range_start;
1556
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001557 cfg->source_port = 2427;
1558 cfg->source_addr = talloc_strdup(cfg, "0.0.0.0");
1559 cfg->osmux_addr = talloc_strdup(cfg, "0.0.0.0");
1560
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001561 cfg->rtp_processing_cb = &mgcp_rtp_processing_default;
1562 cfg->setup_rtp_processing_cb = &mgcp_setup_rtp_processing_default;
1563
1564 cfg->get_net_downlink_format_cb = &mgcp_get_net_downlink_format_default;
1565
Philipp Maierd19de2e2020-06-03 13:55:33 +02001566 INIT_LLIST_HEAD(&cfg->trunks);
1567
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001568 /* Allocate virtual trunk */
Philipp Maierd19de2e2020-06-03 13:55:33 +02001569 if (!mgcp_trunk_alloc(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID)) {
Harald Welte3ac604e2019-05-08 14:07:41 +02001570 talloc_free(cfg);
1571 return NULL;
1572 }
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001573
Philipp Maiera065e632021-07-09 13:22:42 +02001574 mgcp_ratectr_global_alloc(cfg);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001575
1576 return cfg;
1577}
1578
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001579static int send_agent(struct mgcp_config *cfg, const char *buf, int len)
1580{
1581 return write(cfg->gw_fd.bfd.fd, buf, len);
1582}
1583
Philipp Maier87bd9be2017-08-22 16:35:41 +02001584/*! Reset all endpoints by sending RSIP message to self.
1585 * (called by VTY)
1586 * \param[in] endp trunk endpoint
1587 * \param[in] endpoint number
1588 * \returns 0 on success, -1 on error */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001589int mgcp_send_reset_all(struct mgcp_config *cfg)
1590{
Philipp Maier12943ea2018-01-17 15:40:25 +01001591 char buf[MGCP_ENDPOINT_MAXLEN + 128];
1592 int len;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001593 int rc;
1594
Philipp Maier12943ea2018-01-17 15:40:25 +01001595 len = snprintf(buf, sizeof(buf),
1596 "RSIP 1 *@%s MGCP 1.0\r\n", cfg->domain);
1597 if (len < 0)
1598 return -1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001599
Philipp Maier12943ea2018-01-17 15:40:25 +01001600 rc = send_agent(cfg, buf, len);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001601 if (rc <= 0)
1602 return -1;
1603
1604 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001605}
1606
Philipp Maier87bd9be2017-08-22 16:35:41 +02001607/*! Reset a single endpoint by sending RSIP message to self.
1608 * (called by VTY)
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001609 * \param[in] endp to reset
Philipp Maier87bd9be2017-08-22 16:35:41 +02001610 * \returns 0 on success, -1 on error */
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001611int mgcp_send_reset_ep(struct mgcp_endpoint *endp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001612{
Philipp Maier12943ea2018-01-17 15:40:25 +01001613 char buf[MGCP_ENDPOINT_MAXLEN + 128];
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001614 int len;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001615 int rc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001616
1617 len = snprintf(buf, sizeof(buf),
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001618 "RSIP 39 %s MGCP 1.0\r\n", endp->name);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001619 if (len < 0)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001620 return -1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001621
Philipp Maier87bd9be2017-08-22 16:35:41 +02001622 rc = send_agent(endp->cfg, buf, len);
1623 if (rc <= 0)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001624 return -1;
1625
Philipp Maier87bd9be2017-08-22 16:35:41 +02001626 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001627}