blob: 4fb93b0b02eabfa86d35a7395a0e6967a65e0fb5 [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
Pau Espin Pedrol8358c4b2021-07-07 12:41:38 +0200614uint32_t mgcp_rtp_packet_duration(const struct mgcp_endpoint *endp,
615 const struct mgcp_rtp_end *rtp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200616{
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);
Pau Espin Pedrolca280a12021-07-06 18:15:35 +0200993 if (conn->conn->mode & MGCP_CONN_RECV_ONLY &&
994 mgcp_rtp_end_remote_addr_available(&conn->end) &&
995 trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200996 send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200997
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200998 LOGPCONN(_conn, DLMGCP, LOGL_NOTICE,
999 "CRCX: connection successfully created\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001000 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_SUCCESS));
Philipp Maier889fe7f2020-07-06 17:44:12 +02001001 mgcp_endp_update(endp);
Philipp Maier55295f72018-01-15 14:00:28 +01001002 return create_response_with_sdp(endp, conn, "CRCX", p->trans, true);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001003error2:
Philipp Maier1355d7e2018-02-01 14:30:06 +01001004 mgcp_endp_release(endp);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001005 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1006 "CRCX: unable to create connection\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001007 return create_err_response(endp, error_code, "CRCX", p->trans);
1008}
1009
Philipp Maier87bd9be2017-08-22 16:35:41 +02001010/* MDCX command handler, processes the received command */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001011static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
1012{
1013 struct mgcp_endpoint *endp = p->endp;
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001014 struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.mgcp_mdcx_ctr_group;
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001015 char new_local_addr[INET6_ADDRSTRLEN];
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001016 int error_code = 500;
1017 int silent = 0;
1018 int have_sdp = 0;
1019 char *line;
1020 const char *local_options = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001021 const char *mode = NULL;
1022 struct mgcp_conn_rtp *conn = NULL;
Philipp Maier01d24a32017-11-21 17:26:09 +01001023 const char *conn_id = NULL;
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001024 int osmux_cid = -2;
Philipp Maiera390d0b2018-01-31 17:30:19 +01001025 int rc;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001026
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001027 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "MDCX: modifying existing connection ...\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001028
Philipp Maier8d6a1932020-06-18 12:19:31 +02001029 if (!mgcp_endp_avail(endp)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001030 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_AVAIL));
Philipp Maiera910a812020-08-18 15:13:33 +02001031 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1032 "MDCX: selected endpoint not available!\n");
Philipp Maier8d6a1932020-06-18 12:19:31 +02001033 return create_err_response(NULL, 501, "MDCX", p->trans);
1034 }
1035
Philipp Maier5656fbf2018-02-02 14:41:58 +01001036 /* Prohibit wildcarded requests */
1037 if (endp->wildcarded_req) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001038 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1039 "MDCX: wildcarded endpoint names not supported.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001040 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_WILDCARD));
Philipp Maier5656fbf2018-02-02 14:41:58 +01001041 return create_err_response(endp, 507, "MDCX", p->trans);
1042 }
1043
Philipp Maier87bd9be2017-08-22 16:35:41 +02001044 if (llist_count(&endp->conns) <= 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001045 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1046 "MDCX: endpoint is not holding a connection.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001047 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_CONN));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001048 return create_err_response(endp, 400, "MDCX", p->trans);
1049 }
1050
1051 for_each_line(line, p->save) {
1052 if (!mgcp_check_param(endp, line))
1053 continue;
1054
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001055 switch (toupper(line[0])) {
Philipp Maier87bd9be2017-08-22 16:35:41 +02001056 case 'C':
Harald Weltee35eeae2017-12-28 13:47:37 +01001057 if (mgcp_verify_call_id(endp, line + 3) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001058 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CALLID));
Harald Weltee35eeae2017-12-28 13:47:37 +01001059 error_code = 516;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001060 goto error3;
Harald Weltee35eeae2017-12-28 13:47:37 +01001061 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001062 break;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001063 case 'I':
Philipp Maier01d24a32017-11-21 17:26:09 +01001064 conn_id = (const char *)line + 3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001065 if ((error_code = mgcp_verify_ci(endp, conn_id))) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001066 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CONNID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001067 goto error3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001068 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001069 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001070 case 'L':
Philipp Maier87bd9be2017-08-22 16:35:41 +02001071 local_options = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001072 break;
1073 case 'M':
Philipp Maier87bd9be2017-08-22 16:35:41 +02001074 mode = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001075 break;
1076 case 'Z':
Pau Espin Pedrol9b508f62019-06-26 13:11:22 +02001077 silent = strcasecmp("noanswer", line + 3) == 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001078 break;
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001079 case 'X':
Pau Espin Pedrolc1bf4692019-05-14 16:23:24 +02001080 if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001081 /* If osmux is disabled, just skip setting it up */
1082 if (!p->endp->cfg->osmux)
1083 break;
1084 osmux_cid = mgcp_osmux_setup(endp, line);
1085 break;
1086 }
1087 /* Ignore unknown X-headers */
1088 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001089 case '\0':
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001090 have_sdp = 1;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001091 goto mgcp_header_done;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001092 break;
1093 default:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001094 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1095 "MDCX: Unhandled MGCP option: '%c'/%d\n",
1096 line[0], line[0]);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001097 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_UNHANDLED_PARAM));
Philipp Maierdd0c5222018-02-02 11:08:48 +01001098 return create_err_response(NULL, 539, "MDCX", p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001099 break;
1100 }
1101 }
1102
Philipp Maier87bd9be2017-08-22 16:35:41 +02001103mgcp_header_done:
Philipp Maier01d24a32017-11-21 17:26:09 +01001104 if (!conn_id) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001105 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1106 "MDCX: insufficient parameters, missing ci (connectionIdentifier)\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001107 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_CONNID));
Harald Weltee35eeae2017-12-28 13:47:37 +01001108 return create_err_response(endp, 515, "MDCX", p->trans);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001109 }
1110
1111 conn = mgcp_conn_get_rtp(endp, conn_id);
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001112 if (!conn) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001113 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_CONN_NOT_FOUND));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001114 return create_err_response(endp, 400, "MDCX", p->trans);
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001115 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001116
Oliver Smithe36b7752019-01-22 16:31:36 +01001117 mgcp_conn_watchdog_kick(conn->conn);
1118
Philipp Maier87bd9be2017-08-22 16:35:41 +02001119 if (mode) {
1120 if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001121 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_MODE));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001122 error_code = 517;
1123 goto error3;
1124 }
1125 } else
Pau Espin Pedrol209eb9f2019-04-24 12:03:04 +02001126 conn->conn->mode = conn->conn->mode_orig;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001127
Philipp Maierbc0346e2018-06-07 09:52:16 +02001128 /* Set local connection options, if present */
1129 if (local_options) {
Philipp Maier14b27a82020-06-02 20:15:30 +02001130 rc = set_local_cx_options(endp->trunk->endpoints,
Philipp Maierbc0346e2018-06-07 09:52:16 +02001131 &endp->local_options, local_options);
1132 if (rc != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001133 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1134 "MDCX: invalid local connection options!\n");
Philipp Maierbc0346e2018-06-07 09:52:16 +02001135 error_code = rc;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001136 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS));
Philipp Maierbc0346e2018-06-07 09:52:16 +02001137 goto error3;
1138 }
1139 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001140
Philipp Maierbc0346e2018-06-07 09:52:16 +02001141 /* Handle codec information and decide for a suitable codec */
1142 rc = handle_codec_info(conn, p, have_sdp, false);
1143 mgcp_codec_summary(conn);
1144 if (rc) {
Philipp Maieraf07f662018-02-02 11:34:02 +01001145 error_code = rc;
1146 goto error3;
Philipp Maiera390d0b2018-01-31 17:30:19 +01001147 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001148
Philipp Maierc3cc6542018-02-02 12:58:42 +01001149 /* check connection mode setting */
1150 if (conn->conn->mode != MGCP_CONN_LOOPBACK
1151 && conn->conn->mode != MGCP_CONN_RECV_ONLY
1152 && conn->end.rtp_port == 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001153 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1154 "MDCX: selected connection mode type requires an opposite end!\n");
Philipp Maierc3cc6542018-02-02 12:58:42 +01001155 error_code = 527;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001156 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC));
Philipp Maierc3cc6542018-02-02 12:58:42 +01001157 goto error3;
1158 }
1159
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001160 if (mgcp_conn_rtp_is_osmux(conn)) {
1161 OSMO_ASSERT(conn->osmux.cid_allocated);
1162 if (osmux_cid < -1) {
1163 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1164 "MDCX: Failed to parse Osmux CID!\n");
1165 goto error3;
1166 } else if (osmux_cid == -1) {
1167 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1168 "MDCX: wilcard in MDCX is not supported!\n");
1169 goto error3;
1170 } else if (osmux_cid != (int) conn->osmux.cid) {
1171 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1172 "MDCX: changing already allocated CID is not supported!\n");
1173 goto error3;
1174 }
1175 /* TODO: In the future (when we have recvCID!=sendCID), we need to
1176 tell Osmux code that osmux_cid is to be used as sendCID for
1177 that conn. */
1178 }
Philipp Maierbc0346e2018-06-07 09:52:16 +02001179
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001180 /* MDCX may have provided a new remote address, which means we may need
1181 to update our announced IP addr and re-bind our local end. This can
1182 happen for instance if MGW initially provided an IPv4 during CRCX
1183 ACK, and now MDCX tells us the remote has an IPv6 address. */
1184 mgcp_get_local_addr(new_local_addr, conn);
1185 if (strcmp(new_local_addr, conn->end.local_addr)) {
1186 osmo_strlcpy(conn->end.local_addr, new_local_addr, sizeof(conn->end.local_addr));
1187 mgcp_free_rtp_port(&conn->end);
1188 if (allocate_port(endp, conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001189 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001190 goto error3;
1191 }
1192 }
1193
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001194 if (setup_rtp_processing(endp, conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001195 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_START_RTP));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001196 goto error3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001197 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001198
Philipp Maier87bd9be2017-08-22 16:35:41 +02001199
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001200 /* policy CB */
1201 if (p->cfg->policy_cb) {
1202 int rc;
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001203 rc = p->cfg->policy_cb(endp, MGCP_ENDP_MDCX, p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001204 switch (rc) {
1205 case MGCP_POLICY_REJECT:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001206 LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
1207 "MDCX: rejected by policy\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001208 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_REJECTED_BY_POLICY));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001209 if (silent)
1210 goto out_silent;
1211 return create_err_response(endp, 400, "MDCX", p->trans);
1212 break;
1213 case MGCP_POLICY_DEFER:
1214 /* stop processing */
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001215 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
1216 "MDCX: deferred by policy\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001217 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_DEFERRED_BY_POLICY));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001218 return NULL;
1219 break;
1220 case MGCP_POLICY_CONT:
1221 /* just continue */
1222 break;
1223 }
1224 }
1225
Philipp Maier87bd9be2017-08-22 16:35:41 +02001226 mgcp_rtp_end_config(endp, 1, &conn->end);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001227
1228 /* modify */
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001229 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
1230 "MDCX: modified conn:%s\n", mgcp_conn_dump(conn->conn));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001231 if (p->cfg->change_cb)
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001232 p->cfg->change_cb(endp, MGCP_ENDP_MDCX);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001233
Philipp Maiere726d4f2017-11-01 10:41:34 +01001234 /* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
Philipp Maier14b27a82020-06-02 20:15:30 +02001235 OSMO_ASSERT(endp->trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Pau Espin Pedrolca280a12021-07-06 18:15:35 +02001236 if (conn->conn->mode & MGCP_CONN_RECV_ONLY &&
1237 mgcp_rtp_end_remote_addr_available(&conn->end) &&
1238 endp->trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001239 send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001240
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001241 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_SUCCESS));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001242 if (silent)
1243 goto out_silent;
1244
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001245 LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
1246 "MDCX: connection successfully modified\n");
Philipp Maier889fe7f2020-07-06 17:44:12 +02001247 mgcp_endp_update(endp);
Philipp Maier55295f72018-01-15 14:00:28 +01001248 return create_response_with_sdp(endp, conn, "MDCX", p->trans, false);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001249error3:
1250 return create_err_response(endp, error_code, "MDCX", p->trans);
1251
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001252out_silent:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001253 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "MDCX: silent exit\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001254 return NULL;
1255}
1256
Philipp Maier87bd9be2017-08-22 16:35:41 +02001257/* DLCX command handler, processes the received command */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001258static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
1259{
1260 struct mgcp_endpoint *endp = p->endp;
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001261 struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.mgcp_dlcx_ctr_group;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001262 int error_code = 400;
1263 int silent = 0;
1264 char *line;
1265 char stats[1048];
Philipp Maier01d24a32017-11-21 17:26:09 +01001266 const char *conn_id = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001267 struct mgcp_conn_rtp *conn = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001268
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001269 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1270 "DLCX: deleting connection ...\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001271
Philipp Maier8d6a1932020-06-18 12:19:31 +02001272 if (!mgcp_endp_avail(endp)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001273 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_AVAIL));
Philipp Maiera910a812020-08-18 15:13:33 +02001274 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1275 "DLCX: selected endpoint not available!\n");
Philipp Maier8d6a1932020-06-18 12:19:31 +02001276 return create_err_response(NULL, 501, "DLCX", p->trans);
1277 }
1278
Philipp Maier5656fbf2018-02-02 14:41:58 +01001279 /* Prohibit wildcarded requests */
1280 if (endp->wildcarded_req) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001281 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1282 "DLCX: wildcarded endpoint names not supported.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001283 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_WILDCARD));
Philipp Maier5656fbf2018-02-02 14:41:58 +01001284 return create_err_response(endp, 507, "DLCX", p->trans);
1285 }
1286
Philipp Maier87bd9be2017-08-22 16:35:41 +02001287 if (llist_count(&endp->conns) <= 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001288 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1289 "DLCX: endpoint is not holding a connection.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001290 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_NO_CONN));
Harald Weltee35eeae2017-12-28 13:47:37 +01001291 return create_err_response(endp, 515, "DLCX", p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001292 }
1293
1294 for_each_line(line, p->save) {
1295 if (!mgcp_check_param(endp, line))
1296 continue;
1297
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001298 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001299 case 'C':
Harald Weltee35eeae2017-12-28 13:47:37 +01001300 if (mgcp_verify_call_id(endp, line + 3) != 0) {
1301 error_code = 516;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001302 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CALLID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001303 goto error3;
Harald Weltee35eeae2017-12-28 13:47:37 +01001304 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001305 break;
1306 case 'I':
Philipp Maier01d24a32017-11-21 17:26:09 +01001307 conn_id = (const char *)line + 3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001308 if ((error_code = mgcp_verify_ci(endp, conn_id))) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001309 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CONNID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001310 goto error3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001311 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001312 break;
1313 case 'Z':
Pau Espin Pedrol9b508f62019-06-26 13:11:22 +02001314 silent = strcasecmp("noanswer", line + 3) == 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001315 break;
1316 default:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001317 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1318 "DLCX: Unhandled MGCP option: '%c'/%d\n",
1319 line[0], line[0]);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001320 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));
Philipp Maierdd0c5222018-02-02 11:08:48 +01001321 return create_err_response(NULL, 539, "DLCX", p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001322 break;
1323 }
1324 }
1325
1326 /* policy CB */
1327 if (p->cfg->policy_cb) {
1328 int rc;
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001329 rc = p->cfg->policy_cb(endp, MGCP_ENDP_DLCX, p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001330 switch (rc) {
1331 case MGCP_POLICY_REJECT:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001332 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "DLCX: rejected by policy\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001333 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_REJECTED_BY_POLICY));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001334 if (silent)
1335 goto out_silent;
1336 return create_err_response(endp, 400, "DLCX", p->trans);
1337 break;
1338 case MGCP_POLICY_DEFER:
1339 /* stop processing */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001340 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_DEFERRED_BY_POLICY));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001341 return NULL;
1342 break;
1343 case MGCP_POLICY_CONT:
1344 /* just continue */
1345 break;
1346 }
1347 }
1348
Philipp Maierf4c0e372017-10-11 16:06:45 +02001349 /* When no connection id is supplied, we will interpret this as a
1350 * wildcarded DLCX and drop all connections at once. (See also
1351 * RFC3435 Section F.7) */
Philipp Maier01d24a32017-11-21 17:26:09 +01001352 if (!conn_id) {
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001353 int num_conns = llist_count(&endp->conns);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001354 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1355 "DLCX: missing ci (connectionIdentifier), will remove all connections (%d total) at once\n",
1356 num_conns);
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001357
1358 if (num_conns > 0)
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001359 rate_ctr_add(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS), num_conns);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001360
Philipp Maier1355d7e2018-02-01 14:30:06 +01001361 mgcp_endp_release(endp);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001362
1363 /* Note: In this case we do not return any statistics,
1364 * as we assume that the client is not interested in
1365 * this case. */
1366 return create_ok_response(endp, 200, "DLCX", p->trans);
1367 }
1368
Philipp Maierf4c0e372017-10-11 16:06:45 +02001369 /* Find the connection */
Philipp Maier87bd9be2017-08-22 16:35:41 +02001370 conn = mgcp_conn_get_rtp(endp, conn_id);
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001371 if (!conn) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001372 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CONNID));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001373 goto error3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001374 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001375 /* save the statistics of the current connection */
1376 mgcp_format_stats(stats, sizeof(stats), conn->conn);
1377
1378 /* delete connection */
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001379 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG, "DLCX: deleting conn:%s\n",
1380 mgcp_conn_dump(conn->conn));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001381 mgcp_conn_free(endp, conn_id);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001382 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1383 "DLCX: connection successfully deleted\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +02001384
1385 /* When all connections are closed, the endpoint will be released
1386 * in order to be ready to be used by another call. */
1387 if (llist_count(&endp->conns) <= 0) {
Philipp Maier1355d7e2018-02-01 14:30:06 +01001388 mgcp_endp_release(endp);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001389 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: endpoint released\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +02001390 }
1391
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001392 if (p->cfg->change_cb)
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001393 p->cfg->change_cb(endp, MGCP_ENDP_DLCX);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001394
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001395 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001396 if (silent)
1397 goto out_silent;
1398 return create_ok_resp_with_param(endp, 250, "DLCX", p->trans, stats);
1399
1400error3:
1401 return create_err_response(endp, error_code, "DLCX", p->trans);
1402
1403out_silent:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001404 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: silent exit\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001405 return NULL;
1406}
1407
Philipp Maier87bd9be2017-08-22 16:35:41 +02001408/* RSIP command handler, processes the received command */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001409static struct msgb *handle_rsip(struct mgcp_parse_data *p)
1410{
Philipp Maier87bd9be2017-08-22 16:35:41 +02001411 /* TODO: Also implement the resetting of a specific endpoint
1412 * to make mgcp_send_reset_ep() work. Currently this will call
1413 * mgcp_rsip_cb() in mgw_main.c, which sets reset_endpoints=1
1414 * to make read_call_agent() reset all endpoints when called
1415 * next time. In order to selectively reset endpoints some
1416 * mechanism to distinguish which endpoint shall be resetted
1417 * is needed */
1418
1419 LOGP(DLMGCP, LOGL_NOTICE, "RSIP: resetting all endpoints ...\n");
1420
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001421 if (p->cfg->reset_cb)
Philipp Maier14b27a82020-06-02 20:15:30 +02001422 p->cfg->reset_cb(p->endp->trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001423 return NULL;
1424}
1425
1426static char extract_tone(const char *line)
1427{
1428 const char *str = strstr(line, "D/");
1429 if (!str)
1430 return CHAR_MAX;
1431
1432 return str[2];
1433}
1434
Philipp Maier87bd9be2017-08-22 16:35:41 +02001435/* This can request like DTMF detection and forward, fax detection... it
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001436 * can also request when the notification should be send and such. We don't
Philipp Maier87bd9be2017-08-22 16:35:41 +02001437 * do this right now. */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001438static struct msgb *handle_noti_req(struct mgcp_parse_data *p)
1439{
1440 int res = 0;
1441 char *line;
1442 char tone = CHAR_MAX;
1443
Philipp Maier87bd9be2017-08-22 16:35:41 +02001444 LOGP(DLMGCP, LOGL_NOTICE, "RQNT: processing request for notification ...\n");
1445
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001446 for_each_line(line, p->save) {
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001447 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001448 case 'S':
1449 tone = extract_tone(line);
1450 break;
1451 }
1452 }
1453
1454 /* we didn't see a signal request with a tone */
1455 if (tone == CHAR_MAX)
1456 return create_ok_response(p->endp, 200, "RQNT", p->trans);
1457
1458 if (p->cfg->rqnt_cb)
1459 res = p->cfg->rqnt_cb(p->endp, tone);
1460
1461 return res == 0 ?
Philipp Maier87bd9be2017-08-22 16:35:41 +02001462 create_ok_response(p->endp, 200, "RQNT", p->trans) :
1463 create_err_response(p->endp, res, "RQNT", p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001464}
1465
Philipp Maier87bd9be2017-08-22 16:35:41 +02001466/* Connection keepalive timer, will take care that dummy packets are send
Harald Welte1d1b98f2017-12-25 10:03:40 +01001467 * regularly, so that NAT connections stay open */
Philipp Maier14b27a82020-06-02 20:15:30 +02001468static void mgcp_keepalive_timer_cb(void *_trunk)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001469{
Philipp Maier14b27a82020-06-02 20:15:30 +02001470 struct mgcp_trunk *trunk = _trunk;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001471 struct mgcp_conn *conn;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001472 int i;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001473
Philipp Maiere726d4f2017-11-01 10:41:34 +01001474 LOGP(DLMGCP, LOGL_DEBUG, "triggered trunk %d keepalive timer\n",
Philipp Maier14b27a82020-06-02 20:15:30 +02001475 trunk->trunk_nr);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001476
Philipp Maiere726d4f2017-11-01 10:41:34 +01001477 /* Do not accept invalid configuration values
1478 * valid is MGCP_KEEPALIVE_NEVER, MGCP_KEEPALIVE_ONCE and
1479 * values greater 0 */
Philipp Maier14b27a82020-06-02 20:15:30 +02001480 OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Philipp Maiere726d4f2017-11-01 10:41:34 +01001481
1482 /* The dummy packet functionality has been disabled, we will exit
1483 * immediately, no further timer is scheduled, which means we will no
1484 * longer send dummy packets even when we did before */
Philipp Maier14b27a82020-06-02 20:15:30 +02001485 if (trunk->keepalive_interval == MGCP_KEEPALIVE_NEVER)
Philipp Maiere726d4f2017-11-01 10:41:34 +01001486 return;
1487
1488 /* In cases where only one dummy packet is sent, we do not need
1489 * the timer since the functions that handle the CRCX and MDCX are
1490 * triggering the sending of the dummy packet. So we behave like in
1491 * the MGCP_KEEPALIVE_NEVER case */
Philipp Maier14b27a82020-06-02 20:15:30 +02001492 if (trunk->keepalive_interval == MGCP_KEEPALIVE_ONCE)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001493 return;
1494
Philipp Maier87bd9be2017-08-22 16:35:41 +02001495 /* Send walk over all endpoints and send out dummy packets through
1496 * every connection present on each endpoint */
Philipp Maier14b27a82020-06-02 20:15:30 +02001497 for (i = 1; i < trunk->number_endpoints; ++i) {
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001498 struct mgcp_endpoint *endp = trunk->endpoints[i];
Philipp Maier87bd9be2017-08-22 16:35:41 +02001499 llist_for_each_entry(conn, &endp->conns, entry) {
Pau Espin Pedrolca280a12021-07-06 18:15:35 +02001500 if (conn->type == MGCP_CONN_TYPE_RTP &&
1501 conn->mode == MGCP_CONN_RECV_ONLY &&
1502 mgcp_rtp_end_remote_addr_available(&conn->u.rtp.end))
Philipp Maier87bd9be2017-08-22 16:35:41 +02001503 send_dummy(endp, &conn->u.rtp);
1504 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001505 }
1506
Philipp Maiere726d4f2017-11-01 10:41:34 +01001507 /* Schedule the keepalive timer for the next round */
1508 LOGP(DLMGCP, LOGL_DEBUG, "rescheduling trunk %d keepalive timer\n",
Philipp Maier14b27a82020-06-02 20:15:30 +02001509 trunk->trunk_nr);
1510 osmo_timer_schedule(&trunk->keepalive_timer, trunk->keepalive_interval,
Philipp Maier87bd9be2017-08-22 16:35:41 +02001511 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001512}
1513
Philipp Maier14b27a82020-06-02 20:15:30 +02001514void mgcp_trunk_set_keepalive(struct mgcp_trunk *trunk, int interval)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001515{
Philipp Maier14b27a82020-06-02 20:15:30 +02001516 trunk->keepalive_interval = interval;
1517 osmo_timer_setup(&trunk->keepalive_timer, mgcp_keepalive_timer_cb, trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001518
1519 if (interval <= 0)
Philipp Maier14b27a82020-06-02 20:15:30 +02001520 osmo_timer_del(&trunk->keepalive_timer);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001521 else
Philipp Maier14b27a82020-06-02 20:15:30 +02001522 osmo_timer_schedule(&trunk->keepalive_timer,
1523 trunk->keepalive_interval, 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001524}
1525
Philipp Maier87bd9be2017-08-22 16:35:41 +02001526/*! allocate configuration with default values.
1527 * (called once at startup by main function) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001528struct mgcp_config *mgcp_config_alloc(void)
1529{
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001530 /* FIXME: This is unrelated to the protocol, put this in some
1531 * appropiate place! */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001532 struct mgcp_config *cfg;
1533
1534 cfg = talloc_zero(NULL, struct mgcp_config);
1535 if (!cfg) {
1536 LOGP(DLMGCP, LOGL_FATAL, "Failed to allocate config.\n");
1537 return NULL;
1538 }
1539
Philipp Maier12943ea2018-01-17 15:40:25 +01001540 osmo_strlcpy(cfg->domain, "mgw", sizeof(cfg->domain));
1541
Philipp Maier87bd9be2017-08-22 16:35:41 +02001542 cfg->net_ports.range_start = RTP_PORT_DEFAULT_RANGE_START;
1543 cfg->net_ports.range_end = RTP_PORT_DEFAULT_RANGE_END;
1544 cfg->net_ports.last_port = cfg->net_ports.range_start;
1545
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001546 cfg->source_port = 2427;
1547 cfg->source_addr = talloc_strdup(cfg, "0.0.0.0");
1548 cfg->osmux_addr = talloc_strdup(cfg, "0.0.0.0");
1549
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001550 cfg->rtp_processing_cb = &mgcp_rtp_processing_default;
1551 cfg->setup_rtp_processing_cb = &mgcp_setup_rtp_processing_default;
1552
1553 cfg->get_net_downlink_format_cb = &mgcp_get_net_downlink_format_default;
1554
Philipp Maierd19de2e2020-06-03 13:55:33 +02001555 INIT_LLIST_HEAD(&cfg->trunks);
1556
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001557 /* Allocate virtual trunk */
Philipp Maierd19de2e2020-06-03 13:55:33 +02001558 if (!mgcp_trunk_alloc(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID)) {
Harald Welte3ac604e2019-05-08 14:07:41 +02001559 talloc_free(cfg);
1560 return NULL;
1561 }
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001562
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001563 mgcp_ratectr_global_alloc(cfg, &cfg->ratectr);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001564
1565 return cfg;
1566}
1567
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001568static int send_agent(struct mgcp_config *cfg, const char *buf, int len)
1569{
1570 return write(cfg->gw_fd.bfd.fd, buf, len);
1571}
1572
Philipp Maier87bd9be2017-08-22 16:35:41 +02001573/*! Reset all endpoints by sending RSIP message to self.
1574 * (called by VTY)
1575 * \param[in] endp trunk endpoint
1576 * \param[in] endpoint number
1577 * \returns 0 on success, -1 on error */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001578int mgcp_send_reset_all(struct mgcp_config *cfg)
1579{
Philipp Maier12943ea2018-01-17 15:40:25 +01001580 char buf[MGCP_ENDPOINT_MAXLEN + 128];
1581 int len;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001582 int rc;
1583
Philipp Maier12943ea2018-01-17 15:40:25 +01001584 len = snprintf(buf, sizeof(buf),
1585 "RSIP 1 *@%s MGCP 1.0\r\n", cfg->domain);
1586 if (len < 0)
1587 return -1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001588
Philipp Maier12943ea2018-01-17 15:40:25 +01001589 rc = send_agent(cfg, buf, len);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001590 if (rc <= 0)
1591 return -1;
1592
1593 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001594}
1595
Philipp Maier87bd9be2017-08-22 16:35:41 +02001596/*! Reset a single endpoint by sending RSIP message to self.
1597 * (called by VTY)
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001598 * \param[in] endp to reset
Philipp Maier87bd9be2017-08-22 16:35:41 +02001599 * \returns 0 on success, -1 on error */
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001600int mgcp_send_reset_ep(struct mgcp_endpoint *endp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001601{
Philipp Maier12943ea2018-01-17 15:40:25 +01001602 char buf[MGCP_ENDPOINT_MAXLEN + 128];
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001603 int len;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001604 int rc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001605
1606 len = snprintf(buf, sizeof(buf),
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001607 "RSIP 39 %s MGCP 1.0\r\n", endp->name);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001608 if (len < 0)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001609 return -1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001610
Philipp Maier87bd9be2017-08-22 16:35:41 +02001611 rc = send_agent(endp->cfg, buf, len);
1612 if (rc <= 0)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001613 return -1;
1614
Philipp Maier87bd9be2017-08-22 16:35:41 +02001615 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001616}