blob: 3014048b7936815fb8abeef08e043fa238b0e201 [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
49struct mgcp_request {
50 char *name;
Philipp Maier87bd9be2017-08-22 16:35:41 +020051 struct msgb *(*handle_request) (struct mgcp_parse_data * data);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020052 char *debug_name;
53};
54
55#define MGCP_REQUEST(NAME, REQ, DEBUG_NAME) \
56 { .name = NAME, .handle_request = REQ, .debug_name = DEBUG_NAME },
57
Stefan Sperlingba25eab2018-10-30 14:32:31 +010058
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020059static struct msgb *handle_audit_endpoint(struct mgcp_parse_data *data);
60static struct msgb *handle_create_con(struct mgcp_parse_data *data);
61static struct msgb *handle_delete_con(struct mgcp_parse_data *data);
62static struct msgb *handle_modify_con(struct mgcp_parse_data *data);
63static struct msgb *handle_rsip(struct mgcp_parse_data *data);
64static struct msgb *handle_noti_req(struct mgcp_parse_data *data);
65
Philipp Maier87bd9be2017-08-22 16:35:41 +020066/* Initalize transcoder */
67static int setup_rtp_processing(struct mgcp_endpoint *endp,
68 struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020069{
Philipp Maier87bd9be2017-08-22 16:35:41 +020070 struct mgcp_config *cfg = endp->cfg;
71 struct mgcp_conn_rtp *conn_src = NULL;
72 struct mgcp_conn_rtp *conn_dst = conn;
73 struct mgcp_conn *_conn;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020074
Pau Espin Pedrolfa810e82019-05-06 18:54:10 +020075 if (conn->type != MGCP_RTP_DEFAULT && !mgcp_conn_rtp_is_osmux(conn)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +020076 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
77 "RTP-setup: Endpoint is not configured as RTP default, stopping here!\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020078 return 0;
79 }
80
Philipp Maier87bd9be2017-08-22 16:35:41 +020081 if (conn->conn->mode == MGCP_CONN_LOOPBACK) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +020082 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
83 "RTP-setup: Endpoint is in loopback mode, stopping here!\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +020084 return 0;
85 }
86
87 /* Find the "sister" connection */
88 llist_for_each_entry(_conn, &endp->conns, entry) {
89 if (_conn->id != conn->conn->id) {
90 conn_src = &_conn->u.rtp;
91 break;
92 }
93 }
94
Philipp Maieracc10352018-07-19 18:07:57 +020095 return cfg->setup_rtp_processing_cb(endp, conn_dst, conn_src);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020096}
97
Philipp Maier87bd9be2017-08-22 16:35:41 +020098/* array of function pointers for handling various
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020099 * messages. In the future this might be binary sorted
Philipp Maier87bd9be2017-08-22 16:35:41 +0200100 * for performance reasons. */
101static const struct mgcp_request mgcp_requests[] = {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200102 MGCP_REQUEST("AUEP", handle_audit_endpoint, "AuditEndpoint")
Oliver Smith622dd612019-01-30 14:14:45 +0100103 MGCP_REQUEST("CRCX", handle_create_con, "CreateConnection")
104 MGCP_REQUEST("DLCX", handle_delete_con, "DeleteConnection")
105 MGCP_REQUEST("MDCX", handle_modify_con, "ModifiyConnection")
106 MGCP_REQUEST("RQNT", handle_noti_req, "NotificationRequest")
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200107
Oliver Smith622dd612019-01-30 14:14:45 +0100108 /* SPEC extension */
109 MGCP_REQUEST("RSIP", handle_rsip, "ReSetInProgress")
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200110};
111
Philipp Maier87bd9be2017-08-22 16:35:41 +0200112/* Helper function to allocate some memory for responses and retransmissions */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200113static struct msgb *mgcp_msgb_alloc(void)
114{
115 struct msgb *msg;
116 msg = msgb_alloc_headroom(4096, 128, "MGCP msg");
117 if (!msg)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200118 LOGP(DLMGCP, LOGL_ERROR, "Failed to msgb for MGCP data.\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200119
120 return msg;
121}
122
Philipp Maier87bd9be2017-08-22 16:35:41 +0200123/* Helper function for do_retransmission() and create_resp() */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200124static struct msgb *do_retransmission(const struct mgcp_endpoint *endp)
125{
126 struct msgb *msg = mgcp_msgb_alloc();
127 if (!msg)
128 return NULL;
129
130 msg->l2h = msgb_put(msg, strlen(endp->last_response));
131 memcpy(msg->l2h, endp->last_response, msgb_l2len(msg));
Philipp Maier87bd9be2017-08-22 16:35:41 +0200132 mgcp_disp_msg(msg->l2h, msgb_l2len(msg), "Retransmitted response");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200133 return msg;
134}
135
136static struct msgb *create_resp(struct mgcp_endpoint *endp, int code,
137 const char *txt, const char *msg,
138 const char *trans, const char *param,
139 const char *sdp)
140{
141 int len;
142 struct msgb *res;
143
144 res = mgcp_msgb_alloc();
145 if (!res)
146 return NULL;
147
Philipp Maier87bd9be2017-08-22 16:35:41 +0200148 len = snprintf((char *)res->data, 2048, "%d %s%s%s\r\n%s",
149 code, trans, txt, param ? param : "", sdp ? sdp : "");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200150 if (len < 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200151 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "Failed to sprintf MGCP response.\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200152 msgb_free(res);
153 return NULL;
154 }
155
156 res->l2h = msgb_put(res, len);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200157 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "Generated response: code=%d\n", code);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200158 mgcp_disp_msg(res->l2h, msgb_l2len(res), "Generated response");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200159
160 /*
161 * Remember the last transmission per endpoint.
162 */
163 if (endp) {
Philipp Maier14b27a82020-06-02 20:15:30 +0200164 struct mgcp_trunk *trunk = endp->trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200165 talloc_free(endp->last_response);
166 talloc_free(endp->last_trans);
Philipp Maier14b27a82020-06-02 20:15:30 +0200167 endp->last_trans = talloc_strdup(trunk->endpoints, trans);
168 endp->last_response = talloc_strndup(trunk->endpoints,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200169 (const char *)res->l2h,
170 msgb_l2len(res));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200171 }
172
173 return res;
174}
175
176static struct msgb *create_ok_resp_with_param(struct mgcp_endpoint *endp,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200177 int code, const char *msg,
178 const char *trans,
179 const char *param)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200180{
181 return create_resp(endp, code, " OK", msg, trans, param, NULL);
182}
183
184static struct msgb *create_ok_response(struct mgcp_endpoint *endp,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200185 int code, const char *msg,
186 const char *trans)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200187{
188 return create_ok_resp_with_param(endp, code, msg, trans, NULL);
189}
190
191static struct msgb *create_err_response(struct mgcp_endpoint *endp,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200192 int code, const char *msg,
193 const char *trans)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200194{
195 return create_resp(endp, code, " FAIL", msg, trans, NULL, NULL);
196}
197
Philipp Maier55295f72018-01-15 14:00:28 +0100198/* Add MGCP parameters to a message buffer */
199static int add_params(struct msgb *msg, const struct mgcp_endpoint *endp,
200 const struct mgcp_conn_rtp *conn)
201{
202 int rc;
203
Philipp Maier7f0966c2018-01-17 18:18:12 +0100204 /* NOTE: Only in the virtual trunk we allow dynamic endpoint names */
Philipp Maier207ab512018-02-02 14:19:26 +0100205 if (endp->wildcarded_req
Philipp Maier14b27a82020-06-02 20:15:30 +0200206 && endp->trunk->trunk_type == MGCP_TRUNK_VIRTUAL) {
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200207 rc = msgb_printf(msg, "Z: %s\r\n", endp->name);
Philipp Maier55295f72018-01-15 14:00:28 +0100208 if (rc < 0)
209 return -EINVAL;
210 }
211
Philipp Maierc3cfae22018-01-22 12:03:03 +0100212 rc = msgb_printf(msg, "I: %s\r\n", conn->conn->id);
Philipp Maier55295f72018-01-15 14:00:28 +0100213 if (rc < 0)
214 return -EINVAL;
215
216 return 0;
217}
218
Philipp Maier87bd9be2017-08-22 16:35:41 +0200219/* Format MGCP response string (with SDP attached) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200220static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200221 struct mgcp_conn_rtp *conn,
222 const char *msg,
Philipp Maier55295f72018-01-15 14:00:28 +0100223 const char *trans_id,
224 bool add_conn_params)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200225{
Pau Espin Pedrol71d42e72020-09-03 14:20:07 +0200226 /* cfg->local_ip allows overwritting the announced IP address with
227 * regards to the one we actually bind to. Useful in behind-NAT
228 * scenarios.
229 * TODO: we may want to define another local_ip_osmux var to
230 * us for OSMUX connections. Perhaps adding a new internal API to get it
231 * based on conn type.
232 */
233 const char *addr = endp->cfg->local_ip ? : conn->end.local_addr;
Philipp Maier8970c492017-10-11 13:33:42 +0200234 struct msgb *sdp;
235 int rc;
236 struct msgb *result;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200237
Philipp Maier8970c492017-10-11 13:33:42 +0200238 sdp = msgb_alloc_headroom(4096, 128, "sdp record");
239 if (!sdp)
240 return NULL;
241
Philipp Maier55295f72018-01-15 14:00:28 +0100242 /* Attach optional connection parameters */
243 if (add_conn_params) {
244 rc = add_params(sdp, endp, conn);
245 if (rc < 0)
246 goto error;
247 }
248
Philipp Maier3cbfb8a2018-01-22 11:39:59 +0100249 /* Attach optional OSMUX parameters */
Pau Espin Pedrolc63f15a2019-05-10 16:52:08 +0200250 if (mgcp_conn_rtp_is_osmux(conn)) {
Pau Espin Pedrol5e8d7992019-04-24 19:56:43 +0200251 rc = msgb_printf(sdp, "X-Osmux: %u\r\n", conn->osmux.cid);
Philipp Maier3cbfb8a2018-01-22 11:39:59 +0100252 if (rc < 0)
253 goto error;
254 }
255
256 /* Attach line break to separate the parameters from the SDP block */
Philipp Maierc3cfae22018-01-22 12:03:03 +0100257 rc = msgb_printf(sdp, "\r\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200258
Philipp Maier8970c492017-10-11 13:33:42 +0200259 rc = mgcp_write_response_sdp(endp, conn, sdp, addr);
260 if (rc < 0)
261 goto error;
262 result = create_resp(endp, 200, " OK", msg, trans_id, NULL, (char*) sdp->data);
263 msgb_free(sdp);
264 return result;
265error:
266 msgb_free(sdp);
267 return NULL;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200268}
269
Philipp Maier87bd9be2017-08-22 16:35:41 +0200270/* Send out dummy packet to keep the connection open, if the connection is an
271 * osmux connection, send the dummy packet via OSMUX */
272static void send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200273{
Philipp Maier87bd9be2017-08-22 16:35:41 +0200274 if (conn->osmux.state != OSMUX_STATE_DISABLED)
275 osmux_send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200276 else
Philipp Maier87bd9be2017-08-22 16:35:41 +0200277 mgcp_send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200278}
279
Philipp Maier87bd9be2017-08-22 16:35:41 +0200280/* handle incoming messages:
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200281 * - this can be a command (four letters, space, transaction id)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200282 * - or a response (three numbers, space, transaction id) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200283struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
284{
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200285 struct rate_ctr_group *rate_ctrs = cfg->ratectr.mgcp_general_ctr_group;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200286 struct mgcp_parse_data pdata;
Harald Weltee35eeae2017-12-28 13:47:37 +0100287 int rc, i, code, handled = 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200288 struct msgb *resp = NULL;
289 char *data;
290
Alexander Chemeris63866002020-05-05 17:18:40 +0300291 /* Count all messages, even incorect ones */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200292 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_TOTAL));
Alexander Chemeris63866002020-05-05 17:18:40 +0300293
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200294 if (msgb_l2len(msg) < 4) {
295 LOGP(DLMGCP, LOGL_ERROR, "msg too short: %d\n", msg->len);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200296 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200297 return NULL;
298 }
299
Alexander Chemeris63866002020-05-05 17:18:40 +0300300 if (mgcp_msg_terminate_nul(msg)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200301 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200302 return NULL;
Alexander Chemeris63866002020-05-05 17:18:40 +0300303 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200304
Philipp Maier87bd9be2017-08-22 16:35:41 +0200305 mgcp_disp_msg(msg->l2h, msgb_l2len(msg), "Received message");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200306
Philipp Maier87bd9be2017-08-22 16:35:41 +0200307 /* attempt to treat it as a response */
308 if (sscanf((const char *)&msg->l2h[0], "%3d %*s", &code) == 1) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200309 LOGP(DLMGCP, LOGL_DEBUG, "Response: Code: %d\n", code);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200310 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200311 return NULL;
312 }
313
314 msg->l3h = &msg->l2h[4];
315
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200316 /*
317 * Check for a duplicate message and respond.
318 */
319 memset(&pdata, 0, sizeof(pdata));
320 pdata.cfg = cfg;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200321 data = mgcp_strline((char *)msg->l3h, &pdata.save);
Harald Weltee35eeae2017-12-28 13:47:37 +0100322 rc = mgcp_parse_header(&pdata, data);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200323 if (pdata.endp && pdata.trans
Philipp Maier87bd9be2017-08-22 16:35:41 +0200324 && pdata.endp->last_trans
325 && strcmp(pdata.endp->last_trans, pdata.trans) == 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200326 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_RETRANSMITTED));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200327 return do_retransmission(pdata.endp);
328 }
329
Harald Welteabbb6b92017-12-28 13:13:50 +0100330 /* check for general parser failure */
Harald Weltee35eeae2017-12-28 13:47:37 +0100331 if (rc < 0) {
Harald Welteabbb6b92017-12-28 13:13:50 +0100332 LOGP(DLMGCP, LOGL_NOTICE, "%s: failed to find the endpoint\n", msg->l2h);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200333 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_NO_ENDPOINT));
Harald Weltee35eeae2017-12-28 13:47:37 +0100334 return create_err_response(NULL, -rc, (const char *) msg->l2h, pdata.trans);
Harald Welteabbb6b92017-12-28 13:13:50 +0100335 }
336
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200337 for (i = 0; i < ARRAY_SIZE(mgcp_requests); ++i) {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200338 if (strncmp
339 (mgcp_requests[i].name, (const char *)&msg->l2h[0],
340 4) == 0) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200341 handled = 1;
342 resp = mgcp_requests[i].handle_request(&pdata);
343 break;
344 }
345 }
346
Alexander Chemeris63866002020-05-05 17:18:40 +0300347 if (handled) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200348 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_HANDLED));
Alexander Chemeris63866002020-05-05 17:18:40 +0300349 } else {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200350 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_UNHANDLED));
Philipp Maier87bd9be2017-08-22 16:35:41 +0200351 LOGP(DLMGCP, LOGL_NOTICE, "MSG with type: '%.4s' not handled\n",
352 &msg->l2h[0]);
Alexander Chemeris63866002020-05-05 17:18:40 +0300353 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200354
355 return resp;
356}
357
Philipp Maier87bd9be2017-08-22 16:35:41 +0200358/* AUEP command handler, processes the received command */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200359static struct msgb *handle_audit_endpoint(struct mgcp_parse_data *p)
360{
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200361 LOGPENDP(p->endp, DLMGCP, LOGL_NOTICE, "AUEP: auditing endpoint ...\n");
Harald Welteabbb6b92017-12-28 13:13:50 +0100362 return create_ok_response(p->endp, 200, "AUEP", p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200363}
364
Harald Welte1d1b98f2017-12-25 10:03:40 +0100365/* Try to find a free port by attempting to bind on it. Also handle the
Philipp Maier87bd9be2017-08-22 16:35:41 +0200366 * counter that points on the next free port. Since we have a pointer
Philipp Maierb38fb892018-05-22 13:52:21 +0200367 * to the next free port, binding should in work on the first attempt in
Pau Espin Pedrolfc806732019-04-23 00:18:43 +0200368 * general. In case of failure the next port is tried until the whole port
369 * range is tried once. */
Philipp Maier87bd9be2017-08-22 16:35:41 +0200370static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200371{
372 int i;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200373 struct mgcp_port_range *range;
Philipp Maierb38fb892018-05-22 13:52:21 +0200374 unsigned int tries;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200375
Philipp Maier87bd9be2017-08-22 16:35:41 +0200376 OSMO_ASSERT(conn);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200377
378 range = &endp->cfg->net_ports;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200379
380 /* attempt to find a port */
Philipp Maierb38fb892018-05-22 13:52:21 +0200381 tries = (range->range_end - range->range_start) / 2;
382 for (i = 0; i < tries; ++i) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200383 int rc;
384
385 if (range->last_port >= range->range_end)
386 range->last_port = range->range_start;
387
Philipp Maier87bd9be2017-08-22 16:35:41 +0200388 rc = mgcp_bind_net_rtp_port(endp, range->last_port, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200389
390 range->last_port += 2;
391 if (rc == 0) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200392 return 0;
393 }
394
395 }
396
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200397 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
398 "Allocating a RTP/RTCP port failed %u times.\n",
399 tries);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200400 return -1;
401}
402
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200403/*! Helper function for check_local_cx_options() to get a pointer of the next
404 * lco option identifier
405 * \param[in] lco string
406 * \returns pointer to the beginning of the LCO identifier, NULL on failure */
407char *get_lco_identifier(const char *options)
408{
409 char *ptr;
410 unsigned int count = 0;
411
412 /* Jump to the end of the lco identifier */
413 ptr = strstr(options, ":");
414 if (!ptr)
415 return NULL;
416
417 /* Walk backwards until the pointer points to the beginning of the
418 * lco identifier. We know that we stand at the beginning when we
419 * are either at the beginning of the memory or see a space or
420 * comma. (this is tolerant, it will accept a:10, b:11 as well as
421 * a:10,b:11) */
422 while (1) {
423 /* Endless loop protection */
424 if (count > 10000)
425 return NULL;
426 else if (ptr < options || *ptr == ' ' || *ptr == ',') {
427 ptr++;
428 break;
429 }
430 ptr--;
431 count++;
432 }
433
434 /* Check if we got any result */
435 if (*ptr == ':')
436 return NULL;
437
438 return ptr;
439}
440
441/*! Check the LCO option. This function checks for multiple appearence of LCO
442 * options, which is illegal
443 * \param[in] ctx talloc context
444 * \param[in] lco string
445 * \returns 0 on success, -1 on failure */
446int check_local_cx_options(void *ctx, const char *options)
447{
448 int i;
449 char *options_copy;
450 char *lco_identifier;
451 char *lco_identifier_end;
452 char *next_lco_identifier;
453
454 char **lco_seen;
455 unsigned int lco_seen_n = 0;
456
457 if (!options)
458 return -1;
459
460 lco_seen =
461 (char **)talloc_zero_size(ctx, strlen(options) * sizeof(char *));
462 options_copy = talloc_strdup(ctx, options);
463 lco_identifier = options_copy;
464
465 do {
466 /* Move the lco_identifier pointer to the beginning of the
467 * current lco option identifier */
468 lco_identifier = get_lco_identifier(lco_identifier);
469 if (!lco_identifier)
470 goto error;
471
472 /* Look ahead to the next LCO option early, since we
473 * will parse destructively */
474 next_lco_identifier = strstr(lco_identifier + 1, ",");
475
476 /* Pinch off the end of the lco field identifier name
477 * and see if we still got something, also check if
478 * there is some value after the colon. */
479 lco_identifier_end = strstr(lco_identifier, ":");
480 if (!lco_identifier_end)
481 goto error;
482 if (*(lco_identifier_end + 1) == ' '
483 || *(lco_identifier_end + 1) == ','
484 || *(lco_identifier_end + 1) == '\0')
485 goto error;
486 *lco_identifier_end = '\0';
487 if (strlen(lco_identifier) == 0)
488 goto error;
489
490 /* Check if we have already seen the current field identifier
491 * before. If yes, we must bail, an LCO must only appear once
492 * in the LCO string */
493 for (i = 0; i < lco_seen_n; i++) {
Pau Espin Pedrol7eb6f2c2019-06-26 13:00:52 +0200494 if (strcasecmp(lco_seen[i], lco_identifier) == 0)
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200495 goto error;
496 }
497 lco_seen[lco_seen_n] = lco_identifier;
498 lco_seen_n++;
499
500 /* The first identifier must always be found at the beginnning
501 * of the LCO string */
502 if (lco_seen[0] != options_copy)
503 goto error;
504
505 /* Go to the next lco option */
506 lco_identifier = next_lco_identifier;
507 } while (lco_identifier);
508
509 talloc_free(lco_seen);
510 talloc_free(options_copy);
511 return 0;
512error:
513 talloc_free(lco_seen);
514 talloc_free(options_copy);
515 return -1;
516}
517
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200518/* Set the LCO from a string (see RFC 3435).
Harald Welte1d1b98f2017-12-25 10:03:40 +0100519 * The string is stored in the 'string' field. A NULL string is handled exactly
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200520 * like an empty string, the 'string' field is never NULL after this function
521 * has been called. */
Philipp Maiera390d0b2018-01-31 17:30:19 +0100522static int set_local_cx_options(void *ctx, struct mgcp_lco *lco,
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200523 const char *options)
524{
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200525 char *lco_id;
Philipp Maier8dbc9ed2018-10-26 14:50:25 +0200526 char codec[17];
Pau Espin Pedrol19539862020-09-21 11:43:41 +0200527 char nt[17];
Pau Espin Pedrol83fd8a52019-06-26 12:55:26 +0200528 int len;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200529
Philipp Maier604410c2018-06-06 10:02:16 +0200530 if (!options)
531 return 0;
532 if (strlen(options) == 0)
533 return 0;
534
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200535 /* Make sure the encoding of the LCO is consistant before we proceed */
536 if (check_local_cx_options(ctx, options) != 0) {
537 LOGP(DLMGCP, LOGL_ERROR,
538 "local CX options: Internal inconsistency in Local Connection Options!\n");
539 return 524;
540 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200541
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200542 talloc_free(lco->string);
543 lco->string = talloc_strdup(ctx, options);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200544
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200545 lco_id = lco->string;
546 while ((lco_id = get_lco_identifier(lco_id))) {
547 switch (tolower(lco_id[0])) {
548 case 'p':
549 if (sscanf(lco_id + 1, ":%d-%d",
550 &lco->pkt_period_min, &lco->pkt_period_max) == 1)
551 lco->pkt_period_max = lco->pkt_period_min;
552 break;
553 case 'a':
Pau Espin Pedrol1dc2dce2020-09-21 11:25:15 +0200554 /* FIXME: LCO also supports the negotiation of more than one codec.
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200555 * (e.g. a:PCMU;G726-32) But this implementation only supports a single
556 * codec only. */
557 if (sscanf(lco_id + 1, ":%16[^,]", codec) == 1) {
558 talloc_free(lco->codec);
Pau Espin Pedrol83fd8a52019-06-26 12:55:26 +0200559 /* MGCP header is case insensive, and we'll need
560 codec in uppercase when using it later: */
561 len = strlen(codec);
562 lco->codec = talloc_size(ctx, len + 1);
563 osmo_str_toupper_buf(lco->codec, len + 1, codec);
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200564 }
565 break;
Pau Espin Pedrol19539862020-09-21 11:43:41 +0200566 case 'n':
567 if (lco_id[1] == 't' && sscanf(lco_id + 2, ":%16[^,]", nt) == 1)
568 break;
569 /* else: fall throught to print notice log */
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200570 default:
571 LOGP(DLMGCP, LOGL_NOTICE,
572 "LCO: unhandled option: '%c'/%d in \"%s\"\n",
573 *lco_id, *lco_id, lco->string);
574 break;
575 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200576
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200577 lco_id = strchr(lco_id, ',');
578 if (!lco_id)
579 break;
Philipp Maier604410c2018-06-06 10:02:16 +0200580 }
Philipp Maier87bd9be2017-08-22 16:35:41 +0200581
582 LOGP(DLMGCP, LOGL_DEBUG,
583 "local CX options: lco->pkt_period_max: %i, lco->codec: %s\n",
584 lco->pkt_period_max, lco->codec);
Philipp Maiera390d0b2018-01-31 17:30:19 +0100585
586 /* Check if the packetization fits the 20ms raster */
587 if (lco->pkt_period_min % 20 && lco->pkt_period_max % 20) {
588 LOGP(DLMGCP, LOGL_ERROR,
589 "local CX options: packetization interval is not a multiple of 20ms!\n");
590 return 535;
591 }
592
593 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200594}
595
596void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
597 struct mgcp_rtp_end *rtp)
598{
Philipp Maier14b27a82020-06-02 20:15:30 +0200599 struct mgcp_trunk *trunk = endp->trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200600
Philipp Maier14b27a82020-06-02 20:15:30 +0200601 int patch_ssrc = expect_ssrc_change && trunk->force_constant_ssrc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200602
Philipp Maier14b27a82020-06-02 20:15:30 +0200603 rtp->force_aligned_timing = trunk->force_aligned_timing;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200604 rtp->force_constant_ssrc = patch_ssrc ? 1 : 0;
Philipp Maier14b27a82020-06-02 20:15:30 +0200605 rtp->rfc5993_hr_convert = trunk->rfc5993_hr_convert;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200606
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200607 LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
608 "Configuring RTP endpoint: local port %d%s%s\n",
609 ntohs(rtp->rtp_port),
610 rtp->force_aligned_timing ? ", force constant timing" : "",
611 rtp->force_constant_ssrc ? ", force constant ssrc" : "");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200612}
613
614uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp,
615 struct mgcp_rtp_end *rtp)
616{
617 int f = 0;
618
619 /* Get the number of frames per channel and packet */
620 if (rtp->frames_per_packet)
621 f = rtp->frames_per_packet;
Philipp Maierbc0346e2018-06-07 09:52:16 +0200622 else if (rtp->packet_duration_ms && rtp->codec->frame_duration_num) {
623 int den = 1000 * rtp->codec->frame_duration_num;
624 f = (rtp->packet_duration_ms * rtp->codec->frame_duration_den +
Philipp Maier87bd9be2017-08-22 16:35:41 +0200625 den / 2)
626 / den;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200627 }
628
Philipp Maierbc0346e2018-06-07 09:52:16 +0200629 return rtp->codec->rate * f * rtp->codec->frame_duration_num /
630 rtp->codec->frame_duration_den;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200631}
632
Pau Espin Pedrol9fb8ddf2019-05-08 15:35:36 +0200633/*! Initializes osmux socket if not yet initialized. Parses Osmux CID from MGCP line.
634 * \param[in] endp Endpoint willing to initialize osmux
635 * \param[in] line Line X-Osmux from MGCP header msg to parse
636 * \returns OSMUX CID, -1 for wildcard, -2 on parse error, -3 on osmux initalize error
637 */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200638static int mgcp_osmux_setup(struct mgcp_endpoint *endp, const char *line)
639{
640 if (!endp->cfg->osmux_init) {
641 if (osmux_init(OSMUX_ROLE_BSC, endp->cfg) < 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200642 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "Cannot init OSMUX\n");
Pau Espin Pedrol9fb8ddf2019-05-08 15:35:36 +0200643 return -3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200644 }
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200645 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "OSMUX socket has been set up\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200646 }
647
648 return mgcp_parse_osmux_cid(line);
649}
650
Philipp Maierbc0346e2018-06-07 09:52:16 +0200651/* Process codec information contained in CRCX/MDCX */
652static int handle_codec_info(struct mgcp_conn_rtp *conn,
653 struct mgcp_parse_data *p, int have_sdp, bool crcx)
654{
655 struct mgcp_endpoint *endp = p->endp;
656 int rc;
657 char *cmd;
658
659 if (crcx)
660 cmd = "CRCX";
661 else
662 cmd = "MDCX";
663
664 /* Collect codec information */
665 if (have_sdp) {
666 /* If we have SDP, we ignore the local connection options and
667 * use only the SDP information. */
668 mgcp_codec_reset_all(conn);
669 rc = mgcp_parse_sdp_data(endp, conn, p);
670 if (rc != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200671 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
672 "%s: sdp not parseable\n", cmd);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200673
674 /* See also RFC 3661: Protocol error */
675 return 510;
676 }
677 } else if (endp->local_options.codec) {
678 /* When no SDP is available, we use the codec information from
679 * the local connection options (if present) */
680 mgcp_codec_reset_all(conn);
Philipp Maier228e5912019-03-05 13:56:59 +0100681 rc = mgcp_codec_add(conn, PTYPE_UNDEFINED, endp->local_options.codec, NULL);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200682 if (rc != 0)
683 goto error;
684 }
685
686 /* Make sure we always set a sane default codec */
687 if (conn->end.codecs_assigned == 0) {
688 /* When SDP and/or LCO did not supply any codec information,
689 * than it makes sense to pick a sane default: (payload-type 0,
690 * PCMU), see also: OS#2658 */
691 mgcp_codec_reset_all(conn);
Philipp Maier228e5912019-03-05 13:56:59 +0100692 rc = mgcp_codec_add(conn, 0, NULL, NULL);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200693 if (rc != 0)
694 goto error;
695 }
696
697 /* Make codec decision */
698 if (mgcp_codec_decide(conn) != 0)
699 goto error;
700
701 return 0;
702
703error:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200704 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
705 "%s: codec negotiation failure\n", cmd);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200706
707 /* See also RFC 3661: Codec negotiation failure */
708 return 534;
709}
710
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200711static bool parse_x_osmo_ign(struct mgcp_endpoint *endp, char *line)
712{
713 char *saveptr = NULL;
714
Pau Espin Pedrol6049a632020-09-21 11:03:21 +0200715 if (strncasecmp(line, MGCP_X_OSMO_IGN_HEADER, strlen(MGCP_X_OSMO_IGN_HEADER)))
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200716 return false;
717 line += strlen(MGCP_X_OSMO_IGN_HEADER);
718
719 while (1) {
720 char *token = strtok_r(line, " ", &saveptr);
721 line = NULL;
722 if (!token)
723 break;
724
Pau Espin Pedrol6e26c702019-06-26 13:09:39 +0200725 if (!strcasecmp(token, "C"))
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200726 endp->x_osmo_ign |= MGCP_X_OSMO_IGN_CALLID;
727 else
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200728 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "received unknown X-Osmo-IGN item '%s'\n", token);
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200729 }
730
731 return true;
732}
733
Philipp Maier87bd9be2017-08-22 16:35:41 +0200734/* CRCX command handler, processes the received command */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200735static struct msgb *handle_create_con(struct mgcp_parse_data *p)
736{
Philipp Maier14b27a82020-06-02 20:15:30 +0200737 struct mgcp_trunk *trunk = p->endp->trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200738 struct mgcp_endpoint *endp = p->endp;
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200739 struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_crcx_ctr_group;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200740 int error_code = 400;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200741 const char *local_options = NULL;
742 const char *callid = NULL;
743 const char *mode = NULL;
744 char *line;
Pau Espin Pedrol2b896172019-04-24 13:47:23 +0200745 int have_sdp = 0, osmux_cid = -2;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200746 struct mgcp_conn_rtp *conn = NULL;
Philipp Maierffd75e42017-11-22 11:44:50 +0100747 struct mgcp_conn *_conn = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200748 char conn_name[512];
Philipp Maiera390d0b2018-01-31 17:30:19 +0100749 int rc;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200750
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200751 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "CRCX: creating new connection ...\n");
Philipp Maier246233d2020-08-18 15:15:24 +0200752
Philipp Maier8d6a1932020-06-18 12:19:31 +0200753 if (!mgcp_endp_avail(endp)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200754 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_AVAIL));
Philipp Maiera910a812020-08-18 15:13:33 +0200755 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
756 "CRCX: selected endpoint not available!\n");
Philipp Maier8d6a1932020-06-18 12:19:31 +0200757 return create_err_response(NULL, 501, "CRCX", p->trans);
758 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200759
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200760 /* parse CallID C: and LocalParameters L: */
761 for_each_line(line, p->save) {
762 if (!mgcp_check_param(endp, line))
763 continue;
764
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +0200765 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200766 case 'L':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200767 local_options = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200768 break;
769 case 'C':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200770 callid = (const char *)line + 3;
771 break;
772 case 'I':
Philipp Maierffd75e42017-11-22 11:44:50 +0100773 /* It is illegal to send a connection identifier
774 * together with a CRCX, the MGW will assign the
775 * connection identifier by itself on CRCX */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200776 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BAD_ACTION));
Philipp Maierffd75e42017-11-22 11:44:50 +0100777 return create_err_response(NULL, 523, "CRCX", p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200778 break;
779 case 'M':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200780 mode = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200781 break;
782 case 'X':
Pau Espin Pedrolc1bf4692019-05-14 16:23:24 +0200783 if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200784 /* If osmux is disabled, just skip setting it up */
785 if (!p->endp->cfg->osmux)
786 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200787 osmux_cid = mgcp_osmux_setup(endp, line);
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200788 break;
789 }
790
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200791 if (parse_x_osmo_ign(endp, line))
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200792 break;
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200793
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200794 /* Ignore unknown X-headers */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200795 break;
796 case '\0':
797 have_sdp = 1;
798 goto mgcp_header_done;
799 default:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200800 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
801 "CRCX: unhandled option: '%c'/%d\n", *line, *line);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200802 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_UNHANDLED_PARAM));
Philipp Maierdd0c5222018-02-02 11:08:48 +0100803 return create_err_response(NULL, 539, "CRCX", p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200804 break;
805 }
806 }
807
808mgcp_header_done:
Philipp Maier87bd9be2017-08-22 16:35:41 +0200809 /* Check parameters */
810 if (!callid) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200811 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
812 "CRCX: insufficient parameters, missing callid\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200813 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_MISSING_CALLID));
Harald Weltee35eeae2017-12-28 13:47:37 +0100814 return create_err_response(endp, 516, "CRCX", p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200815 }
816
Philipp Maier87bd9be2017-08-22 16:35:41 +0200817 if (!mode) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200818 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
819 "CRCX: insufficient parameters, missing mode\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200820 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_MODE));
Harald Weltee35eeae2017-12-28 13:47:37 +0100821 return create_err_response(endp, 517, "CRCX", p->trans);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200822 }
823
Philipp Maier87bd9be2017-08-22 16:35:41 +0200824 /* Check if we are able to accept the creation of another connection */
825 if (llist_count(&endp->conns) >= endp->type->max_conns) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200826 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
827 "CRCX: endpoint full, max. %i connections allowed!\n",
828 endp->type->max_conns);
Philipp Maier14b27a82020-06-02 20:15:30 +0200829 if (trunk->force_realloc) {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200830 /* There is no more room for a connection, make some
831 * room by blindly tossing the oldest of the two two
832 * connections */
833 mgcp_conn_free_oldest(endp);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200834 } else {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200835 /* There is no more room for a connection, leave
836 * everything as it is and return with an error */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200837 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_LIMIT_EXCEEDED));
Harald Weltee35eeae2017-12-28 13:47:37 +0100838 return create_err_response(endp, 540, "CRCX", p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200839 }
840 }
841
Philipp Maier87bd9be2017-08-22 16:35:41 +0200842 /* Check if this endpoint already serves a call, if so, check if the
843 * callids match up so that we are sure that this is our call */
844 if (endp->callid && mgcp_verify_call_id(endp, callid)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200845 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
846 "CRCX: already seized by other call (%s)\n",
847 endp->callid);
Philipp Maier14b27a82020-06-02 20:15:30 +0200848 if (trunk->force_realloc)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200849 /* This is not our call, toss everything by releasing
850 * the entire endpoint. (rude!) */
Philipp Maier1355d7e2018-02-01 14:30:06 +0100851 mgcp_endp_release(endp);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200852 else {
853 /* This is not our call, leave everything as it is and
854 * return with an error. */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200855 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_UNKNOWN_CALLID));
Philipp Maier87bd9be2017-08-22 16:35:41 +0200856 return create_err_response(endp, 400, "CRCX", p->trans);
857 }
858 }
859
Philipp Maier889fe7f2020-07-06 17:44:12 +0200860 if (!endp->callid) {
861 /* Claim endpoint resources. This will also set the callid,
862 * creating additional connections will only be possible if
863 * the callid matches up (see above). */
864 rc = mgcp_endp_claim(endp, callid);
865 if (rc != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200866 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_CLAIM));
Philipp Maier889fe7f2020-07-06 17:44:12 +0200867 return create_err_response(endp, 502, "CRCX", p->trans);
868 }
869 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200870
Philipp Maierffd75e42017-11-22 11:44:50 +0100871 snprintf(conn_name, sizeof(conn_name), "%s", callid);
Philipp Maier14b27a82020-06-02 20:15:30 +0200872 _conn = mgcp_conn_alloc(trunk->endpoints, endp, MGCP_CONN_TYPE_RTP, conn_name);
Philipp Maierffd75e42017-11-22 11:44:50 +0100873 if (!_conn) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200874 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
875 "CRCX: unable to allocate RTP connection\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200876 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_ALLOC_CONN));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200877 goto error2;
878
Philipp Maier87bd9be2017-08-22 16:35:41 +0200879 }
Philipp Maier889fe7f2020-07-06 17:44:12 +0200880
Philipp Maierffd75e42017-11-22 11:44:50 +0100881 conn = mgcp_conn_get_rtp(endp, _conn->id);
882 OSMO_ASSERT(conn);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200883
884 if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
885 error_code = 517;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200886 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_MODE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200887 goto error2;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200888 }
889
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200890 /* Annotate Osmux circuit ID and set it to negotiating state until this
Philipp Maier87bd9be2017-08-22 16:35:41 +0200891 * is fully set up from the dummy load. */
892 conn->osmux.state = OSMUX_STATE_DISABLED;
Pau Espin Pedrol2b896172019-04-24 13:47:23 +0200893 if (osmux_cid >= -1) { /* -1 is wilcard, alloc next avail CID */
Pau Espin Pedrol14f8a082019-05-13 13:10:06 +0200894 conn->osmux.state = OSMUX_STATE_ACTIVATING;
Pau Espin Pedrol2b896172019-04-24 13:47:23 +0200895 if (conn_osmux_allocate_cid(conn, osmux_cid) == -1) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200896 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_OSMUX));
Pau Espin Pedrol2b896172019-04-24 13:47:23 +0200897 goto error2;
898 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200899 } else if (endp->cfg->osmux == OSMUX_USAGE_ONLY) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200900 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
901 "CRCX: osmux only and no osmux offered\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200902 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_OSMUX));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200903 goto error2;
904 }
905
Philipp Maierbc0346e2018-06-07 09:52:16 +0200906 /* Set local connection options, if present */
907 if (local_options) {
Philipp Maier14b27a82020-06-02 20:15:30 +0200908 rc = set_local_cx_options(endp->trunk->endpoints,
Philipp Maierbc0346e2018-06-07 09:52:16 +0200909 &endp->local_options, local_options);
910 if (rc != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200911 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
912 "CRCX: inavlid local connection options!\n");
Philipp Maierbc0346e2018-06-07 09:52:16 +0200913 error_code = rc;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200914 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS));
Philipp Maierbc0346e2018-06-07 09:52:16 +0200915 goto error2;
916 }
917 }
918
919 /* Handle codec information and decide for a suitable codec */
920 rc = handle_codec_info(conn, p, have_sdp, true);
921 mgcp_codec_summary(conn);
922 if (rc) {
923 error_code = rc;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200924 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_CODEC_NEGOTIATION));
Philipp Maierbc0346e2018-06-07 09:52:16 +0200925 goto error2;
926 }
927
Philipp Maier14b27a82020-06-02 20:15:30 +0200928 conn->end.fmtp_extra = talloc_strdup(trunk->endpoints,
929 trunk->audio_fmtp_extra);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200930
Philipp Maier87bd9be2017-08-22 16:35:41 +0200931 if (p->cfg->force_ptime) {
932 conn->end.packet_duration_ms = p->cfg->force_ptime;
933 conn->end.force_output_ptime = 1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200934 }
935
Philipp Maier1cb1e382017-11-02 17:16:04 +0100936 mgcp_rtp_end_config(endp, 0, &conn->end);
937
Philipp Maierc3cc6542018-02-02 12:58:42 +0100938 /* check connection mode setting */
939 if (conn->conn->mode != MGCP_CONN_LOOPBACK
940 && conn->conn->mode != MGCP_CONN_RECV_ONLY
941 && conn->end.rtp_port == 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200942 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
943 "CRCX: selected connection mode type requires an opposite end!\n");
Philipp Maierc3cc6542018-02-02 12:58:42 +0100944 error_code = 527;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200945 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC));
Philipp Maierc3cc6542018-02-02 12:58:42 +0100946 goto error2;
947 }
948
Pau Espin Pedrol71d42e72020-09-03 14:20:07 +0200949 /* Find a local address for conn based on policy and initial SDP remote
950 information, then find a free port for it */
951 mgcp_get_local_addr(conn->end.local_addr, conn);
Philipp Maier1cb1e382017-11-02 17:16:04 +0100952 if (allocate_port(endp, conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200953 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
Philipp Maier1cb1e382017-11-02 17:16:04 +0100954 goto error2;
955 }
956
Philipp Maier87bd9be2017-08-22 16:35:41 +0200957 if (setup_rtp_processing(endp, conn) != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200958 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
959 "CRCX: could not start RTP processing!\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200960 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_START_RTP));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200961 goto error2;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200962 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200963
964 /* policy CB */
965 if (p->cfg->policy_cb) {
966 int rc;
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200967 rc = p->cfg->policy_cb(endp, MGCP_ENDP_CRCX, p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200968 switch (rc) {
969 case MGCP_POLICY_REJECT:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200970 LOGPCONN(_conn, DLMGCP, LOGL_NOTICE,
971 "CRCX: CRCX rejected by policy\n");
Philipp Maier1355d7e2018-02-01 14:30:06 +0100972 mgcp_endp_release(endp);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200973 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_REJECTED_BY_POLICY));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200974 return create_err_response(endp, 400, "CRCX", p->trans);
975 break;
976 case MGCP_POLICY_DEFER:
977 /* stop processing */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200978 return NULL;
979 break;
980 case MGCP_POLICY_CONT:
981 /* just continue */
982 break;
983 }
984 }
985
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200986 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
987 "CRCX: Creating connection: port: %u\n", conn->end.local_port);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200988 if (p->cfg->change_cb)
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200989 p->cfg->change_cb(endp, MGCP_ENDP_CRCX);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200990
Philipp Maiere726d4f2017-11-01 10:41:34 +0100991 /* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
Philipp Maier14b27a82020-06-02 20:15:30 +0200992 OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200993 if (conn->conn->mode & MGCP_CONN_RECV_ONLY
Philipp Maier14b27a82020-06-02 20:15:30 +0200994 && trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200995 send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200996
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200997 LOGPCONN(_conn, DLMGCP, LOGL_NOTICE,
998 "CRCX: connection successfully created\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200999 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_SUCCESS));
Philipp Maier889fe7f2020-07-06 17:44:12 +02001000 mgcp_endp_update(endp);
Philipp Maier55295f72018-01-15 14:00:28 +01001001 return create_response_with_sdp(endp, conn, "CRCX", p->trans, true);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001002error2:
Philipp Maier1355d7e2018-02-01 14:30:06 +01001003 mgcp_endp_release(endp);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001004 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1005 "CRCX: unable to create connection\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001006 return create_err_response(endp, error_code, "CRCX", p->trans);
1007}
1008
Philipp Maier87bd9be2017-08-22 16:35:41 +02001009/* MDCX command handler, processes the received command */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001010static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
1011{
1012 struct mgcp_endpoint *endp = p->endp;
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001013 struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.mgcp_mdcx_ctr_group;
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001014 char new_local_addr[INET6_ADDRSTRLEN];
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001015 int error_code = 500;
1016 int silent = 0;
1017 int have_sdp = 0;
1018 char *line;
1019 const char *local_options = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001020 const char *mode = NULL;
1021 struct mgcp_conn_rtp *conn = NULL;
Philipp Maier01d24a32017-11-21 17:26:09 +01001022 const char *conn_id = NULL;
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001023 int osmux_cid = -2;
Philipp Maiera390d0b2018-01-31 17:30:19 +01001024 int rc;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001025
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001026 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "MDCX: modifying existing connection ...\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001027
Philipp Maier8d6a1932020-06-18 12:19:31 +02001028 if (!mgcp_endp_avail(endp)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001029 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_AVAIL));
Philipp Maiera910a812020-08-18 15:13:33 +02001030 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1031 "MDCX: selected endpoint not available!\n");
Philipp Maier8d6a1932020-06-18 12:19:31 +02001032 return create_err_response(NULL, 501, "MDCX", p->trans);
1033 }
1034
Philipp Maier5656fbf2018-02-02 14:41:58 +01001035 /* Prohibit wildcarded requests */
1036 if (endp->wildcarded_req) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001037 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1038 "MDCX: wildcarded endpoint names not supported.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001039 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_WILDCARD));
Philipp Maier5656fbf2018-02-02 14:41:58 +01001040 return create_err_response(endp, 507, "MDCX", p->trans);
1041 }
1042
Philipp Maier87bd9be2017-08-22 16:35:41 +02001043 if (llist_count(&endp->conns) <= 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001044 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1045 "MDCX: endpoint is not holding a connection.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001046 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_CONN));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001047 return create_err_response(endp, 400, "MDCX", p->trans);
1048 }
1049
1050 for_each_line(line, p->save) {
1051 if (!mgcp_check_param(endp, line))
1052 continue;
1053
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001054 switch (toupper(line[0])) {
Philipp Maier87bd9be2017-08-22 16:35:41 +02001055 case 'C':
Harald Weltee35eeae2017-12-28 13:47:37 +01001056 if (mgcp_verify_call_id(endp, line + 3) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001057 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CALLID));
Harald Weltee35eeae2017-12-28 13:47:37 +01001058 error_code = 516;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001059 goto error3;
Harald Weltee35eeae2017-12-28 13:47:37 +01001060 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001061 break;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001062 case 'I':
Philipp Maier01d24a32017-11-21 17:26:09 +01001063 conn_id = (const char *)line + 3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001064 if ((error_code = mgcp_verify_ci(endp, conn_id))) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001065 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CONNID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001066 goto error3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001067 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001068 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001069 case 'L':
Philipp Maier87bd9be2017-08-22 16:35:41 +02001070 local_options = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001071 break;
1072 case 'M':
Philipp Maier87bd9be2017-08-22 16:35:41 +02001073 mode = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001074 break;
1075 case 'Z':
Pau Espin Pedrol9b508f62019-06-26 13:11:22 +02001076 silent = strcasecmp("noanswer", line + 3) == 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001077 break;
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001078 case 'X':
Pau Espin Pedrolc1bf4692019-05-14 16:23:24 +02001079 if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001080 /* If osmux is disabled, just skip setting it up */
1081 if (!p->endp->cfg->osmux)
1082 break;
1083 osmux_cid = mgcp_osmux_setup(endp, line);
1084 break;
1085 }
1086 /* Ignore unknown X-headers */
1087 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001088 case '\0':
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001089 have_sdp = 1;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001090 goto mgcp_header_done;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001091 break;
1092 default:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001093 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1094 "MDCX: Unhandled MGCP option: '%c'/%d\n",
1095 line[0], line[0]);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001096 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_UNHANDLED_PARAM));
Philipp Maierdd0c5222018-02-02 11:08:48 +01001097 return create_err_response(NULL, 539, "MDCX", p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001098 break;
1099 }
1100 }
1101
Philipp Maier87bd9be2017-08-22 16:35:41 +02001102mgcp_header_done:
Philipp Maier01d24a32017-11-21 17:26:09 +01001103 if (!conn_id) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001104 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1105 "MDCX: insufficient parameters, missing ci (connectionIdentifier)\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001106 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_CONNID));
Harald Weltee35eeae2017-12-28 13:47:37 +01001107 return create_err_response(endp, 515, "MDCX", p->trans);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001108 }
1109
1110 conn = mgcp_conn_get_rtp(endp, conn_id);
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001111 if (!conn) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001112 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_CONN_NOT_FOUND));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001113 return create_err_response(endp, 400, "MDCX", p->trans);
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001114 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001115
Oliver Smithe36b7752019-01-22 16:31:36 +01001116 mgcp_conn_watchdog_kick(conn->conn);
1117
Philipp Maier87bd9be2017-08-22 16:35:41 +02001118 if (mode) {
1119 if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001120 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_MODE));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001121 error_code = 517;
1122 goto error3;
1123 }
1124 } else
Pau Espin Pedrol209eb9f2019-04-24 12:03:04 +02001125 conn->conn->mode = conn->conn->mode_orig;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001126
Philipp Maierbc0346e2018-06-07 09:52:16 +02001127 /* Set local connection options, if present */
1128 if (local_options) {
Philipp Maier14b27a82020-06-02 20:15:30 +02001129 rc = set_local_cx_options(endp->trunk->endpoints,
Philipp Maierbc0346e2018-06-07 09:52:16 +02001130 &endp->local_options, local_options);
1131 if (rc != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001132 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1133 "MDCX: invalid local connection options!\n");
Philipp Maierbc0346e2018-06-07 09:52:16 +02001134 error_code = rc;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001135 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS));
Philipp Maierbc0346e2018-06-07 09:52:16 +02001136 goto error3;
1137 }
1138 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001139
Philipp Maierbc0346e2018-06-07 09:52:16 +02001140 /* Handle codec information and decide for a suitable codec */
1141 rc = handle_codec_info(conn, p, have_sdp, false);
1142 mgcp_codec_summary(conn);
1143 if (rc) {
Philipp Maieraf07f662018-02-02 11:34:02 +01001144 error_code = rc;
1145 goto error3;
Philipp Maiera390d0b2018-01-31 17:30:19 +01001146 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001147
Philipp Maierc3cc6542018-02-02 12:58:42 +01001148 /* check connection mode setting */
1149 if (conn->conn->mode != MGCP_CONN_LOOPBACK
1150 && conn->conn->mode != MGCP_CONN_RECV_ONLY
1151 && conn->end.rtp_port == 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001152 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1153 "MDCX: selected connection mode type requires an opposite end!\n");
Philipp Maierc3cc6542018-02-02 12:58:42 +01001154 error_code = 527;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001155 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC));
Philipp Maierc3cc6542018-02-02 12:58:42 +01001156 goto error3;
1157 }
1158
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001159 if (mgcp_conn_rtp_is_osmux(conn)) {
1160 OSMO_ASSERT(conn->osmux.cid_allocated);
1161 if (osmux_cid < -1) {
1162 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1163 "MDCX: Failed to parse Osmux CID!\n");
1164 goto error3;
1165 } else if (osmux_cid == -1) {
1166 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1167 "MDCX: wilcard in MDCX is not supported!\n");
1168 goto error3;
1169 } else if (osmux_cid != (int) conn->osmux.cid) {
1170 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1171 "MDCX: changing already allocated CID is not supported!\n");
1172 goto error3;
1173 }
1174 /* TODO: In the future (when we have recvCID!=sendCID), we need to
1175 tell Osmux code that osmux_cid is to be used as sendCID for
1176 that conn. */
1177 }
Philipp Maierbc0346e2018-06-07 09:52:16 +02001178
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001179 /* MDCX may have provided a new remote address, which means we may need
1180 to update our announced IP addr and re-bind our local end. This can
1181 happen for instance if MGW initially provided an IPv4 during CRCX
1182 ACK, and now MDCX tells us the remote has an IPv6 address. */
1183 mgcp_get_local_addr(new_local_addr, conn);
1184 if (strcmp(new_local_addr, conn->end.local_addr)) {
1185 osmo_strlcpy(conn->end.local_addr, new_local_addr, sizeof(conn->end.local_addr));
1186 mgcp_free_rtp_port(&conn->end);
1187 if (allocate_port(endp, conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001188 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001189 goto error3;
1190 }
1191 }
1192
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001193 if (setup_rtp_processing(endp, conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001194 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_START_RTP));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001195 goto error3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001196 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001197
Philipp Maier87bd9be2017-08-22 16:35:41 +02001198
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001199 /* policy CB */
1200 if (p->cfg->policy_cb) {
1201 int rc;
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001202 rc = p->cfg->policy_cb(endp, MGCP_ENDP_MDCX, p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001203 switch (rc) {
1204 case MGCP_POLICY_REJECT:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001205 LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
1206 "MDCX: rejected by policy\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001207 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_REJECTED_BY_POLICY));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001208 if (silent)
1209 goto out_silent;
1210 return create_err_response(endp, 400, "MDCX", p->trans);
1211 break;
1212 case MGCP_POLICY_DEFER:
1213 /* stop processing */
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001214 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
1215 "MDCX: deferred by policy\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001216 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_DEFERRED_BY_POLICY));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001217 return NULL;
1218 break;
1219 case MGCP_POLICY_CONT:
1220 /* just continue */
1221 break;
1222 }
1223 }
1224
Philipp Maier87bd9be2017-08-22 16:35:41 +02001225 mgcp_rtp_end_config(endp, 1, &conn->end);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001226
1227 /* modify */
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001228 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
1229 "MDCX: modified conn:%s\n", mgcp_conn_dump(conn->conn));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001230 if (p->cfg->change_cb)
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001231 p->cfg->change_cb(endp, MGCP_ENDP_MDCX);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001232
Philipp Maiere726d4f2017-11-01 10:41:34 +01001233 /* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
Philipp Maier14b27a82020-06-02 20:15:30 +02001234 OSMO_ASSERT(endp->trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Philipp Maiere726d4f2017-11-01 10:41:34 +01001235 if (conn->conn->mode & MGCP_CONN_RECV_ONLY
Philipp Maier14b27a82020-06-02 20:15:30 +02001236 && endp->trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001237 send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001238
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001239 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_SUCCESS));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001240 if (silent)
1241 goto out_silent;
1242
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001243 LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
1244 "MDCX: connection successfully modified\n");
Philipp Maier889fe7f2020-07-06 17:44:12 +02001245 mgcp_endp_update(endp);
Philipp Maier55295f72018-01-15 14:00:28 +01001246 return create_response_with_sdp(endp, conn, "MDCX", p->trans, false);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001247error3:
1248 return create_err_response(endp, error_code, "MDCX", p->trans);
1249
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001250out_silent:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001251 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "MDCX: silent exit\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001252 return NULL;
1253}
1254
Philipp Maier87bd9be2017-08-22 16:35:41 +02001255/* DLCX command handler, processes the received command */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001256static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
1257{
1258 struct mgcp_endpoint *endp = p->endp;
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001259 struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.mgcp_dlcx_ctr_group;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001260 int error_code = 400;
1261 int silent = 0;
1262 char *line;
1263 char stats[1048];
Philipp Maier01d24a32017-11-21 17:26:09 +01001264 const char *conn_id = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001265 struct mgcp_conn_rtp *conn = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001266
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001267 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1268 "DLCX: deleting connection ...\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001269
Philipp Maier8d6a1932020-06-18 12:19:31 +02001270 if (!mgcp_endp_avail(endp)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001271 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_AVAIL));
Philipp Maiera910a812020-08-18 15:13:33 +02001272 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1273 "DLCX: selected endpoint not available!\n");
Philipp Maier8d6a1932020-06-18 12:19:31 +02001274 return create_err_response(NULL, 501, "DLCX", p->trans);
1275 }
1276
Philipp Maier5656fbf2018-02-02 14:41:58 +01001277 /* Prohibit wildcarded requests */
1278 if (endp->wildcarded_req) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001279 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1280 "DLCX: wildcarded endpoint names not supported.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001281 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_WILDCARD));
Philipp Maier5656fbf2018-02-02 14:41:58 +01001282 return create_err_response(endp, 507, "DLCX", p->trans);
1283 }
1284
Philipp Maier87bd9be2017-08-22 16:35:41 +02001285 if (llist_count(&endp->conns) <= 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001286 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1287 "DLCX: endpoint is not holding a connection.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001288 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_NO_CONN));
Harald Weltee35eeae2017-12-28 13:47:37 +01001289 return create_err_response(endp, 515, "DLCX", p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001290 }
1291
1292 for_each_line(line, p->save) {
1293 if (!mgcp_check_param(endp, line))
1294 continue;
1295
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001296 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001297 case 'C':
Harald Weltee35eeae2017-12-28 13:47:37 +01001298 if (mgcp_verify_call_id(endp, line + 3) != 0) {
1299 error_code = 516;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001300 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CALLID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001301 goto error3;
Harald Weltee35eeae2017-12-28 13:47:37 +01001302 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001303 break;
1304 case 'I':
Philipp Maier01d24a32017-11-21 17:26:09 +01001305 conn_id = (const char *)line + 3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001306 if ((error_code = mgcp_verify_ci(endp, conn_id))) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001307 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CONNID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001308 goto error3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001309 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001310 break;
1311 case 'Z':
Pau Espin Pedrol9b508f62019-06-26 13:11:22 +02001312 silent = strcasecmp("noanswer", line + 3) == 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001313 break;
1314 default:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001315 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1316 "DLCX: Unhandled MGCP option: '%c'/%d\n",
1317 line[0], line[0]);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001318 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));
Philipp Maierdd0c5222018-02-02 11:08:48 +01001319 return create_err_response(NULL, 539, "DLCX", p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001320 break;
1321 }
1322 }
1323
1324 /* policy CB */
1325 if (p->cfg->policy_cb) {
1326 int rc;
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001327 rc = p->cfg->policy_cb(endp, MGCP_ENDP_DLCX, p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001328 switch (rc) {
1329 case MGCP_POLICY_REJECT:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001330 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "DLCX: rejected by policy\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001331 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_REJECTED_BY_POLICY));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001332 if (silent)
1333 goto out_silent;
1334 return create_err_response(endp, 400, "DLCX", p->trans);
1335 break;
1336 case MGCP_POLICY_DEFER:
1337 /* stop processing */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001338 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_DEFERRED_BY_POLICY));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001339 return NULL;
1340 break;
1341 case MGCP_POLICY_CONT:
1342 /* just continue */
1343 break;
1344 }
1345 }
1346
Philipp Maierf4c0e372017-10-11 16:06:45 +02001347 /* When no connection id is supplied, we will interpret this as a
1348 * wildcarded DLCX and drop all connections at once. (See also
1349 * RFC3435 Section F.7) */
Philipp Maier01d24a32017-11-21 17:26:09 +01001350 if (!conn_id) {
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001351 int num_conns = llist_count(&endp->conns);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001352 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1353 "DLCX: missing ci (connectionIdentifier), will remove all connections (%d total) at once\n",
1354 num_conns);
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001355
1356 if (num_conns > 0)
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001357 rate_ctr_add(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS), num_conns);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001358
Philipp Maier1355d7e2018-02-01 14:30:06 +01001359 mgcp_endp_release(endp);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001360
1361 /* Note: In this case we do not return any statistics,
1362 * as we assume that the client is not interested in
1363 * this case. */
1364 return create_ok_response(endp, 200, "DLCX", p->trans);
1365 }
1366
Philipp Maierf4c0e372017-10-11 16:06:45 +02001367 /* Find the connection */
Philipp Maier87bd9be2017-08-22 16:35:41 +02001368 conn = mgcp_conn_get_rtp(endp, conn_id);
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001369 if (!conn) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001370 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CONNID));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001371 goto error3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001372 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001373 /* save the statistics of the current connection */
1374 mgcp_format_stats(stats, sizeof(stats), conn->conn);
1375
1376 /* delete connection */
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001377 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG, "DLCX: deleting conn:%s\n",
1378 mgcp_conn_dump(conn->conn));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001379 mgcp_conn_free(endp, conn_id);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001380 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1381 "DLCX: connection successfully deleted\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +02001382
1383 /* When all connections are closed, the endpoint will be released
1384 * in order to be ready to be used by another call. */
1385 if (llist_count(&endp->conns) <= 0) {
Philipp Maier1355d7e2018-02-01 14:30:06 +01001386 mgcp_endp_release(endp);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001387 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: endpoint released\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +02001388 }
1389
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001390 if (p->cfg->change_cb)
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001391 p->cfg->change_cb(endp, MGCP_ENDP_DLCX);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001392
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001393 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001394 if (silent)
1395 goto out_silent;
1396 return create_ok_resp_with_param(endp, 250, "DLCX", p->trans, stats);
1397
1398error3:
1399 return create_err_response(endp, error_code, "DLCX", p->trans);
1400
1401out_silent:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001402 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: silent exit\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001403 return NULL;
1404}
1405
Philipp Maier87bd9be2017-08-22 16:35:41 +02001406/* RSIP command handler, processes the received command */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001407static struct msgb *handle_rsip(struct mgcp_parse_data *p)
1408{
Philipp Maier87bd9be2017-08-22 16:35:41 +02001409 /* TODO: Also implement the resetting of a specific endpoint
1410 * to make mgcp_send_reset_ep() work. Currently this will call
1411 * mgcp_rsip_cb() in mgw_main.c, which sets reset_endpoints=1
1412 * to make read_call_agent() reset all endpoints when called
1413 * next time. In order to selectively reset endpoints some
1414 * mechanism to distinguish which endpoint shall be resetted
1415 * is needed */
1416
1417 LOGP(DLMGCP, LOGL_NOTICE, "RSIP: resetting all endpoints ...\n");
1418
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001419 if (p->cfg->reset_cb)
Philipp Maier14b27a82020-06-02 20:15:30 +02001420 p->cfg->reset_cb(p->endp->trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001421 return NULL;
1422}
1423
1424static char extract_tone(const char *line)
1425{
1426 const char *str = strstr(line, "D/");
1427 if (!str)
1428 return CHAR_MAX;
1429
1430 return str[2];
1431}
1432
Philipp Maier87bd9be2017-08-22 16:35:41 +02001433/* This can request like DTMF detection and forward, fax detection... it
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001434 * can also request when the notification should be send and such. We don't
Philipp Maier87bd9be2017-08-22 16:35:41 +02001435 * do this right now. */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001436static struct msgb *handle_noti_req(struct mgcp_parse_data *p)
1437{
1438 int res = 0;
1439 char *line;
1440 char tone = CHAR_MAX;
1441
Philipp Maier87bd9be2017-08-22 16:35:41 +02001442 LOGP(DLMGCP, LOGL_NOTICE, "RQNT: processing request for notification ...\n");
1443
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001444 for_each_line(line, p->save) {
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001445 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001446 case 'S':
1447 tone = extract_tone(line);
1448 break;
1449 }
1450 }
1451
1452 /* we didn't see a signal request with a tone */
1453 if (tone == CHAR_MAX)
1454 return create_ok_response(p->endp, 200, "RQNT", p->trans);
1455
1456 if (p->cfg->rqnt_cb)
1457 res = p->cfg->rqnt_cb(p->endp, tone);
1458
1459 return res == 0 ?
Philipp Maier87bd9be2017-08-22 16:35:41 +02001460 create_ok_response(p->endp, 200, "RQNT", p->trans) :
1461 create_err_response(p->endp, res, "RQNT", p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001462}
1463
Philipp Maier87bd9be2017-08-22 16:35:41 +02001464/* Connection keepalive timer, will take care that dummy packets are send
Harald Welte1d1b98f2017-12-25 10:03:40 +01001465 * regularly, so that NAT connections stay open */
Philipp Maier14b27a82020-06-02 20:15:30 +02001466static void mgcp_keepalive_timer_cb(void *_trunk)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001467{
Philipp Maier14b27a82020-06-02 20:15:30 +02001468 struct mgcp_trunk *trunk = _trunk;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001469 struct mgcp_conn *conn;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001470 int i;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001471
Philipp Maiere726d4f2017-11-01 10:41:34 +01001472 LOGP(DLMGCP, LOGL_DEBUG, "triggered trunk %d keepalive timer\n",
Philipp Maier14b27a82020-06-02 20:15:30 +02001473 trunk->trunk_nr);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001474
Philipp Maiere726d4f2017-11-01 10:41:34 +01001475 /* Do not accept invalid configuration values
1476 * valid is MGCP_KEEPALIVE_NEVER, MGCP_KEEPALIVE_ONCE and
1477 * values greater 0 */
Philipp Maier14b27a82020-06-02 20:15:30 +02001478 OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Philipp Maiere726d4f2017-11-01 10:41:34 +01001479
1480 /* The dummy packet functionality has been disabled, we will exit
1481 * immediately, no further timer is scheduled, which means we will no
1482 * longer send dummy packets even when we did before */
Philipp Maier14b27a82020-06-02 20:15:30 +02001483 if (trunk->keepalive_interval == MGCP_KEEPALIVE_NEVER)
Philipp Maiere726d4f2017-11-01 10:41:34 +01001484 return;
1485
1486 /* In cases where only one dummy packet is sent, we do not need
1487 * the timer since the functions that handle the CRCX and MDCX are
1488 * triggering the sending of the dummy packet. So we behave like in
1489 * the MGCP_KEEPALIVE_NEVER case */
Philipp Maier14b27a82020-06-02 20:15:30 +02001490 if (trunk->keepalive_interval == MGCP_KEEPALIVE_ONCE)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001491 return;
1492
Philipp Maier87bd9be2017-08-22 16:35:41 +02001493 /* Send walk over all endpoints and send out dummy packets through
1494 * every connection present on each endpoint */
Philipp Maier14b27a82020-06-02 20:15:30 +02001495 for (i = 1; i < trunk->number_endpoints; ++i) {
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001496 struct mgcp_endpoint *endp = trunk->endpoints[i];
Philipp Maier87bd9be2017-08-22 16:35:41 +02001497 llist_for_each_entry(conn, &endp->conns, entry) {
1498 if (conn->mode == MGCP_CONN_RECV_ONLY)
1499 send_dummy(endp, &conn->u.rtp);
1500 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001501 }
1502
Philipp Maiere726d4f2017-11-01 10:41:34 +01001503 /* Schedule the keepalive timer for the next round */
1504 LOGP(DLMGCP, LOGL_DEBUG, "rescheduling trunk %d keepalive timer\n",
Philipp Maier14b27a82020-06-02 20:15:30 +02001505 trunk->trunk_nr);
1506 osmo_timer_schedule(&trunk->keepalive_timer, trunk->keepalive_interval,
Philipp Maier87bd9be2017-08-22 16:35:41 +02001507 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001508}
1509
Philipp Maier14b27a82020-06-02 20:15:30 +02001510void mgcp_trunk_set_keepalive(struct mgcp_trunk *trunk, int interval)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001511{
Philipp Maier14b27a82020-06-02 20:15:30 +02001512 trunk->keepalive_interval = interval;
1513 osmo_timer_setup(&trunk->keepalive_timer, mgcp_keepalive_timer_cb, trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001514
1515 if (interval <= 0)
Philipp Maier14b27a82020-06-02 20:15:30 +02001516 osmo_timer_del(&trunk->keepalive_timer);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001517 else
Philipp Maier14b27a82020-06-02 20:15:30 +02001518 osmo_timer_schedule(&trunk->keepalive_timer,
1519 trunk->keepalive_interval, 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001520}
1521
Philipp Maier87bd9be2017-08-22 16:35:41 +02001522/*! allocate configuration with default values.
1523 * (called once at startup by main function) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001524struct mgcp_config *mgcp_config_alloc(void)
1525{
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001526 /* FIXME: This is unrelated to the protocol, put this in some
1527 * appropiate place! */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001528 struct mgcp_config *cfg;
1529
1530 cfg = talloc_zero(NULL, struct mgcp_config);
1531 if (!cfg) {
1532 LOGP(DLMGCP, LOGL_FATAL, "Failed to allocate config.\n");
1533 return NULL;
1534 }
1535
Philipp Maier12943ea2018-01-17 15:40:25 +01001536 osmo_strlcpy(cfg->domain, "mgw", sizeof(cfg->domain));
1537
Philipp Maier87bd9be2017-08-22 16:35:41 +02001538 cfg->net_ports.range_start = RTP_PORT_DEFAULT_RANGE_START;
1539 cfg->net_ports.range_end = RTP_PORT_DEFAULT_RANGE_END;
1540 cfg->net_ports.last_port = cfg->net_ports.range_start;
1541
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001542 cfg->source_port = 2427;
1543 cfg->source_addr = talloc_strdup(cfg, "0.0.0.0");
1544 cfg->osmux_addr = talloc_strdup(cfg, "0.0.0.0");
1545
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001546 cfg->rtp_processing_cb = &mgcp_rtp_processing_default;
1547 cfg->setup_rtp_processing_cb = &mgcp_setup_rtp_processing_default;
1548
1549 cfg->get_net_downlink_format_cb = &mgcp_get_net_downlink_format_default;
1550
Philipp Maierd19de2e2020-06-03 13:55:33 +02001551 INIT_LLIST_HEAD(&cfg->trunks);
1552
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001553 /* Allocate virtual trunk */
Philipp Maierd19de2e2020-06-03 13:55:33 +02001554 if (!mgcp_trunk_alloc(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID)) {
Harald Welte3ac604e2019-05-08 14:07:41 +02001555 talloc_free(cfg);
1556 return NULL;
1557 }
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001558
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001559 mgcp_ratectr_global_alloc(cfg, &cfg->ratectr);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001560
1561 return cfg;
1562}
1563
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001564static int send_agent(struct mgcp_config *cfg, const char *buf, int len)
1565{
1566 return write(cfg->gw_fd.bfd.fd, buf, len);
1567}
1568
Philipp Maier87bd9be2017-08-22 16:35:41 +02001569/*! Reset all endpoints by sending RSIP message to self.
1570 * (called by VTY)
1571 * \param[in] endp trunk endpoint
1572 * \param[in] endpoint number
1573 * \returns 0 on success, -1 on error */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001574int mgcp_send_reset_all(struct mgcp_config *cfg)
1575{
Philipp Maier12943ea2018-01-17 15:40:25 +01001576 char buf[MGCP_ENDPOINT_MAXLEN + 128];
1577 int len;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001578 int rc;
1579
Philipp Maier12943ea2018-01-17 15:40:25 +01001580 len = snprintf(buf, sizeof(buf),
1581 "RSIP 1 *@%s MGCP 1.0\r\n", cfg->domain);
1582 if (len < 0)
1583 return -1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001584
Philipp Maier12943ea2018-01-17 15:40:25 +01001585 rc = send_agent(cfg, buf, len);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001586 if (rc <= 0)
1587 return -1;
1588
1589 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001590}
1591
Philipp Maier87bd9be2017-08-22 16:35:41 +02001592/*! Reset a single endpoint by sending RSIP message to self.
1593 * (called by VTY)
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001594 * \param[in] endp to reset
Philipp Maier87bd9be2017-08-22 16:35:41 +02001595 * \returns 0 on success, -1 on error */
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001596int mgcp_send_reset_ep(struct mgcp_endpoint *endp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001597{
Philipp Maier12943ea2018-01-17 15:40:25 +01001598 char buf[MGCP_ENDPOINT_MAXLEN + 128];
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001599 int len;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001600 int rc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001601
1602 len = snprintf(buf, sizeof(buf),
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001603 "RSIP 39 %s MGCP 1.0\r\n", endp->name);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001604 if (len < 0)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001605 return -1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001606
Philipp Maier87bd9be2017-08-22 16:35:41 +02001607 rc = send_agent(endp->cfg, buf, len);
1608 if (rc <= 0)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001609 return -1;
1610
Philipp Maier87bd9be2017-08-22 16:35:41 +02001611 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001612}