blob: 4c0014014920016a03d263d66684005f46fa4386 [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 */
292 rate_ctr_inc(&rate_ctrs->ctr[MGCP_GENERAL_RX_MSGS_TOTAL]);
293
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);
Alexander Chemeris63866002020-05-05 17:18:40 +0300296 rate_ctr_inc(&rate_ctrs->ctr[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)) {
301 rate_ctr_inc(&rate_ctrs->ctr[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);
Alexander Chemeris63866002020-05-05 17:18:40 +0300310 rate_ctr_inc(&rate_ctrs->ctr[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) {
Alexander Chemeris63866002020-05-05 17:18:40 +0300326 rate_ctr_inc(&rate_ctrs->ctr[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);
Alexander Chemeris63866002020-05-05 17:18:40 +0300333 rate_ctr_inc(&rate_ctrs->ctr[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) {
348 rate_ctr_inc(&rate_ctrs->ctr[MGCP_GENERAL_RX_MSGS_HANDLED]);
349 } else {
350 rate_ctr_inc(&rate_ctrs->ctr[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 Pedrol83fd8a52019-06-26 12:55:26 +0200527 int len;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200528
Philipp Maier604410c2018-06-06 10:02:16 +0200529 if (!options)
530 return 0;
531 if (strlen(options) == 0)
532 return 0;
533
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200534 /* Make sure the encoding of the LCO is consistant before we proceed */
535 if (check_local_cx_options(ctx, options) != 0) {
536 LOGP(DLMGCP, LOGL_ERROR,
537 "local CX options: Internal inconsistency in Local Connection Options!\n");
538 return 524;
539 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200540
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200541 talloc_free(lco->string);
542 lco->string = talloc_strdup(ctx, options);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200543
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200544 lco_id = lco->string;
545 while ((lco_id = get_lco_identifier(lco_id))) {
546 switch (tolower(lco_id[0])) {
547 case 'p':
548 if (sscanf(lco_id + 1, ":%d-%d",
549 &lco->pkt_period_min, &lco->pkt_period_max) == 1)
550 lco->pkt_period_max = lco->pkt_period_min;
551 break;
552 case 'a':
553 /* FIXME: LCO also supports the negotiation of more then one codec.
554 * (e.g. a:PCMU;G726-32) But this implementation only supports a single
555 * codec only. */
556 if (sscanf(lco_id + 1, ":%16[^,]", codec) == 1) {
557 talloc_free(lco->codec);
Pau Espin Pedrol83fd8a52019-06-26 12:55:26 +0200558 /* MGCP header is case insensive, and we'll need
559 codec in uppercase when using it later: */
560 len = strlen(codec);
561 lco->codec = talloc_size(ctx, len + 1);
562 osmo_str_toupper_buf(lco->codec, len + 1, codec);
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200563 }
564 break;
565 default:
566 LOGP(DLMGCP, LOGL_NOTICE,
567 "LCO: unhandled option: '%c'/%d in \"%s\"\n",
568 *lco_id, *lco_id, lco->string);
569 break;
570 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200571
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200572 lco_id = strchr(lco_id, ',');
573 if (!lco_id)
574 break;
Philipp Maier604410c2018-06-06 10:02:16 +0200575 }
Philipp Maier87bd9be2017-08-22 16:35:41 +0200576
577 LOGP(DLMGCP, LOGL_DEBUG,
578 "local CX options: lco->pkt_period_max: %i, lco->codec: %s\n",
579 lco->pkt_period_max, lco->codec);
Philipp Maiera390d0b2018-01-31 17:30:19 +0100580
581 /* Check if the packetization fits the 20ms raster */
582 if (lco->pkt_period_min % 20 && lco->pkt_period_max % 20) {
583 LOGP(DLMGCP, LOGL_ERROR,
584 "local CX options: packetization interval is not a multiple of 20ms!\n");
585 return 535;
586 }
587
588 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200589}
590
591void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
592 struct mgcp_rtp_end *rtp)
593{
Philipp Maier14b27a82020-06-02 20:15:30 +0200594 struct mgcp_trunk *trunk = endp->trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200595
Philipp Maier14b27a82020-06-02 20:15:30 +0200596 int patch_ssrc = expect_ssrc_change && trunk->force_constant_ssrc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200597
Philipp Maier14b27a82020-06-02 20:15:30 +0200598 rtp->force_aligned_timing = trunk->force_aligned_timing;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200599 rtp->force_constant_ssrc = patch_ssrc ? 1 : 0;
Philipp Maier14b27a82020-06-02 20:15:30 +0200600 rtp->rfc5993_hr_convert = trunk->rfc5993_hr_convert;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200601
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200602 LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
603 "Configuring RTP endpoint: local port %d%s%s\n",
604 ntohs(rtp->rtp_port),
605 rtp->force_aligned_timing ? ", force constant timing" : "",
606 rtp->force_constant_ssrc ? ", force constant ssrc" : "");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200607}
608
609uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp,
610 struct mgcp_rtp_end *rtp)
611{
612 int f = 0;
613
614 /* Get the number of frames per channel and packet */
615 if (rtp->frames_per_packet)
616 f = rtp->frames_per_packet;
Philipp Maierbc0346e2018-06-07 09:52:16 +0200617 else if (rtp->packet_duration_ms && rtp->codec->frame_duration_num) {
618 int den = 1000 * rtp->codec->frame_duration_num;
619 f = (rtp->packet_duration_ms * rtp->codec->frame_duration_den +
Philipp Maier87bd9be2017-08-22 16:35:41 +0200620 den / 2)
621 / den;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200622 }
623
Philipp Maierbc0346e2018-06-07 09:52:16 +0200624 return rtp->codec->rate * f * rtp->codec->frame_duration_num /
625 rtp->codec->frame_duration_den;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200626}
627
Pau Espin Pedrol9fb8ddf2019-05-08 15:35:36 +0200628/*! Initializes osmux socket if not yet initialized. Parses Osmux CID from MGCP line.
629 * \param[in] endp Endpoint willing to initialize osmux
630 * \param[in] line Line X-Osmux from MGCP header msg to parse
631 * \returns OSMUX CID, -1 for wildcard, -2 on parse error, -3 on osmux initalize error
632 */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200633static int mgcp_osmux_setup(struct mgcp_endpoint *endp, const char *line)
634{
635 if (!endp->cfg->osmux_init) {
636 if (osmux_init(OSMUX_ROLE_BSC, endp->cfg) < 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200637 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "Cannot init OSMUX\n");
Pau Espin Pedrol9fb8ddf2019-05-08 15:35:36 +0200638 return -3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200639 }
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200640 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "OSMUX socket has been set up\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200641 }
642
643 return mgcp_parse_osmux_cid(line);
644}
645
Philipp Maierbc0346e2018-06-07 09:52:16 +0200646/* Process codec information contained in CRCX/MDCX */
647static int handle_codec_info(struct mgcp_conn_rtp *conn,
648 struct mgcp_parse_data *p, int have_sdp, bool crcx)
649{
650 struct mgcp_endpoint *endp = p->endp;
651 int rc;
652 char *cmd;
653
654 if (crcx)
655 cmd = "CRCX";
656 else
657 cmd = "MDCX";
658
659 /* Collect codec information */
660 if (have_sdp) {
661 /* If we have SDP, we ignore the local connection options and
662 * use only the SDP information. */
663 mgcp_codec_reset_all(conn);
664 rc = mgcp_parse_sdp_data(endp, conn, p);
665 if (rc != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200666 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
667 "%s: sdp not parseable\n", cmd);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200668
669 /* See also RFC 3661: Protocol error */
670 return 510;
671 }
672 } else if (endp->local_options.codec) {
673 /* When no SDP is available, we use the codec information from
674 * the local connection options (if present) */
675 mgcp_codec_reset_all(conn);
Philipp Maier228e5912019-03-05 13:56:59 +0100676 rc = mgcp_codec_add(conn, PTYPE_UNDEFINED, endp->local_options.codec, NULL);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200677 if (rc != 0)
678 goto error;
679 }
680
681 /* Make sure we always set a sane default codec */
682 if (conn->end.codecs_assigned == 0) {
683 /* When SDP and/or LCO did not supply any codec information,
684 * than it makes sense to pick a sane default: (payload-type 0,
685 * PCMU), see also: OS#2658 */
686 mgcp_codec_reset_all(conn);
Philipp Maier228e5912019-03-05 13:56:59 +0100687 rc = mgcp_codec_add(conn, 0, NULL, NULL);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200688 if (rc != 0)
689 goto error;
690 }
691
692 /* Make codec decision */
693 if (mgcp_codec_decide(conn) != 0)
694 goto error;
695
696 return 0;
697
698error:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200699 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
700 "%s: codec negotiation failure\n", cmd);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200701
702 /* See also RFC 3661: Codec negotiation failure */
703 return 534;
704}
705
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200706static bool parse_x_osmo_ign(struct mgcp_endpoint *endp, char *line)
707{
708 char *saveptr = NULL;
709
710 if (strncmp(line, MGCP_X_OSMO_IGN_HEADER, strlen(MGCP_X_OSMO_IGN_HEADER)))
711 return false;
712 line += strlen(MGCP_X_OSMO_IGN_HEADER);
713
714 while (1) {
715 char *token = strtok_r(line, " ", &saveptr);
716 line = NULL;
717 if (!token)
718 break;
719
Pau Espin Pedrol6e26c702019-06-26 13:09:39 +0200720 if (!strcasecmp(token, "C"))
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200721 endp->x_osmo_ign |= MGCP_X_OSMO_IGN_CALLID;
722 else
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200723 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "received unknown X-Osmo-IGN item '%s'\n", token);
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200724 }
725
726 return true;
727}
728
Philipp Maier87bd9be2017-08-22 16:35:41 +0200729/* CRCX command handler, processes the received command */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200730static struct msgb *handle_create_con(struct mgcp_parse_data *p)
731{
Philipp Maier14b27a82020-06-02 20:15:30 +0200732 struct mgcp_trunk *trunk = p->endp->trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200733 struct mgcp_endpoint *endp = p->endp;
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200734 struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_crcx_ctr_group;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200735 int error_code = 400;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200736 const char *local_options = NULL;
737 const char *callid = NULL;
738 const char *mode = NULL;
739 char *line;
Pau Espin Pedrol2b896172019-04-24 13:47:23 +0200740 int have_sdp = 0, osmux_cid = -2;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200741 struct mgcp_conn_rtp *conn = NULL;
Philipp Maierffd75e42017-11-22 11:44:50 +0100742 struct mgcp_conn *_conn = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200743 char conn_name[512];
Philipp Maiera390d0b2018-01-31 17:30:19 +0100744 int rc;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200745
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200746 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "CRCX: creating new connection ...\n");
Philipp Maier246233d2020-08-18 15:15:24 +0200747
Philipp Maier8d6a1932020-06-18 12:19:31 +0200748 if (!mgcp_endp_avail(endp)) {
749 rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_AVAIL]);
Philipp Maiera910a812020-08-18 15:13:33 +0200750 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
751 "CRCX: selected endpoint not available!\n");
Philipp Maier8d6a1932020-06-18 12:19:31 +0200752 return create_err_response(NULL, 501, "CRCX", p->trans);
753 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200754
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200755 /* parse CallID C: and LocalParameters L: */
756 for_each_line(line, p->save) {
757 if (!mgcp_check_param(endp, line))
758 continue;
759
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +0200760 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200761 case 'L':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200762 local_options = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200763 break;
764 case 'C':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200765 callid = (const char *)line + 3;
766 break;
767 case 'I':
Philipp Maierffd75e42017-11-22 11:44:50 +0100768 /* It is illegal to send a connection identifier
769 * together with a CRCX, the MGW will assign the
770 * connection identifier by itself on CRCX */
Stefan Sperling9270e912018-10-29 14:10:00 +0100771 rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_BAD_ACTION]);
Philipp Maierffd75e42017-11-22 11:44:50 +0100772 return create_err_response(NULL, 523, "CRCX", p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200773 break;
774 case 'M':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200775 mode = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200776 break;
777 case 'X':
Pau Espin Pedrolc1bf4692019-05-14 16:23:24 +0200778 if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200779 /* If osmux is disabled, just skip setting it up */
780 if (!p->endp->cfg->osmux)
781 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200782 osmux_cid = mgcp_osmux_setup(endp, line);
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200783 break;
784 }
785
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200786 if (parse_x_osmo_ign(endp, line))
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200787 break;
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200788
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200789 /* Ignore unknown X-headers */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200790 break;
791 case '\0':
792 have_sdp = 1;
793 goto mgcp_header_done;
794 default:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200795 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
796 "CRCX: unhandled option: '%c'/%d\n", *line, *line);
Stefan Sperling9270e912018-10-29 14:10:00 +0100797 rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_UNHANDLED_PARAM]);
Philipp Maierdd0c5222018-02-02 11:08:48 +0100798 return create_err_response(NULL, 539, "CRCX", p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200799 break;
800 }
801 }
802
803mgcp_header_done:
Philipp Maier87bd9be2017-08-22 16:35:41 +0200804 /* Check parameters */
805 if (!callid) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200806 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
807 "CRCX: insufficient parameters, missing callid\n");
Stefan Sperling9270e912018-10-29 14:10:00 +0100808 rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_MISSING_CALLID]);
Harald Weltee35eeae2017-12-28 13:47:37 +0100809 return create_err_response(endp, 516, "CRCX", p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200810 }
811
Philipp Maier87bd9be2017-08-22 16:35:41 +0200812 if (!mode) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200813 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
814 "CRCX: insufficient parameters, missing mode\n");
Stefan Sperling9270e912018-10-29 14:10:00 +0100815 rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_INVALID_MODE]);
Harald Weltee35eeae2017-12-28 13:47:37 +0100816 return create_err_response(endp, 517, "CRCX", p->trans);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200817 }
818
Philipp Maier87bd9be2017-08-22 16:35:41 +0200819 /* Check if we are able to accept the creation of another connection */
820 if (llist_count(&endp->conns) >= endp->type->max_conns) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200821 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
822 "CRCX: endpoint full, max. %i connections allowed!\n",
823 endp->type->max_conns);
Philipp Maier14b27a82020-06-02 20:15:30 +0200824 if (trunk->force_realloc) {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200825 /* There is no more room for a connection, make some
826 * room by blindly tossing the oldest of the two two
827 * connections */
828 mgcp_conn_free_oldest(endp);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200829 } else {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200830 /* There is no more room for a connection, leave
831 * everything as it is and return with an error */
Stefan Sperling9270e912018-10-29 14:10:00 +0100832 rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_LIMIT_EXCEEDED]);
Harald Weltee35eeae2017-12-28 13:47:37 +0100833 return create_err_response(endp, 540, "CRCX", p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200834 }
835 }
836
Philipp Maier87bd9be2017-08-22 16:35:41 +0200837 /* Check if this endpoint already serves a call, if so, check if the
838 * callids match up so that we are sure that this is our call */
839 if (endp->callid && mgcp_verify_call_id(endp, callid)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200840 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
841 "CRCX: already seized by other call (%s)\n",
842 endp->callid);
Philipp Maier14b27a82020-06-02 20:15:30 +0200843 if (trunk->force_realloc)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200844 /* This is not our call, toss everything by releasing
845 * the entire endpoint. (rude!) */
Philipp Maier1355d7e2018-02-01 14:30:06 +0100846 mgcp_endp_release(endp);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200847 else {
848 /* This is not our call, leave everything as it is and
849 * return with an error. */
Stefan Sperling9270e912018-10-29 14:10:00 +0100850 rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_UNKNOWN_CALLID]);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200851 return create_err_response(endp, 400, "CRCX", p->trans);
852 }
853 }
854
Philipp Maier889fe7f2020-07-06 17:44:12 +0200855 if (!endp->callid) {
856 /* Claim endpoint resources. This will also set the callid,
857 * creating additional connections will only be possible if
858 * the callid matches up (see above). */
859 rc = mgcp_endp_claim(endp, callid);
860 if (rc != 0) {
861 rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_CLAIM]);
862 return create_err_response(endp, 502, "CRCX", p->trans);
863 }
864 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200865
Philipp Maierffd75e42017-11-22 11:44:50 +0100866 snprintf(conn_name, sizeof(conn_name), "%s", callid);
Philipp Maier14b27a82020-06-02 20:15:30 +0200867 _conn = mgcp_conn_alloc(trunk->endpoints, endp, MGCP_CONN_TYPE_RTP, conn_name);
Philipp Maierffd75e42017-11-22 11:44:50 +0100868 if (!_conn) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200869 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
870 "CRCX: unable to allocate RTP connection\n");
Stefan Sperling9270e912018-10-29 14:10:00 +0100871 rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_ALLOC_CONN]);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200872 goto error2;
873
Philipp Maier87bd9be2017-08-22 16:35:41 +0200874 }
Philipp Maier889fe7f2020-07-06 17:44:12 +0200875
Philipp Maierffd75e42017-11-22 11:44:50 +0100876 conn = mgcp_conn_get_rtp(endp, _conn->id);
877 OSMO_ASSERT(conn);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200878
879 if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
880 error_code = 517;
Stefan Sperlinga714abf2018-10-29 14:19:54 +0100881 rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_INVALID_MODE]);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200882 goto error2;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200883 }
884
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200885 /* Annotate Osmux circuit ID and set it to negotiating state until this
Philipp Maier87bd9be2017-08-22 16:35:41 +0200886 * is fully set up from the dummy load. */
887 conn->osmux.state = OSMUX_STATE_DISABLED;
Pau Espin Pedrol2b896172019-04-24 13:47:23 +0200888 if (osmux_cid >= -1) { /* -1 is wilcard, alloc next avail CID */
Pau Espin Pedrol14f8a082019-05-13 13:10:06 +0200889 conn->osmux.state = OSMUX_STATE_ACTIVATING;
Pau Espin Pedrol2b896172019-04-24 13:47:23 +0200890 if (conn_osmux_allocate_cid(conn, osmux_cid) == -1) {
891 rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_NO_OSMUX]);
892 goto error2;
893 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200894 } else if (endp->cfg->osmux == OSMUX_USAGE_ONLY) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200895 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
896 "CRCX: osmux only and no osmux offered\n");
Stefan Sperlinga714abf2018-10-29 14:19:54 +0100897 rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_NO_OSMUX]);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200898 goto error2;
899 }
900
Philipp Maierbc0346e2018-06-07 09:52:16 +0200901 /* Set local connection options, if present */
902 if (local_options) {
Philipp Maier14b27a82020-06-02 20:15:30 +0200903 rc = set_local_cx_options(endp->trunk->endpoints,
Philipp Maierbc0346e2018-06-07 09:52:16 +0200904 &endp->local_options, local_options);
905 if (rc != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200906 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
907 "CRCX: inavlid local connection options!\n");
Philipp Maierbc0346e2018-06-07 09:52:16 +0200908 error_code = rc;
Stefan Sperlinga714abf2018-10-29 14:19:54 +0100909 rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS]);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200910 goto error2;
911 }
912 }
913
914 /* Handle codec information and decide for a suitable codec */
915 rc = handle_codec_info(conn, p, have_sdp, true);
916 mgcp_codec_summary(conn);
917 if (rc) {
918 error_code = rc;
Stefan Sperlinga714abf2018-10-29 14:19:54 +0100919 rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_CODEC_NEGOTIATION]);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200920 goto error2;
921 }
922
Philipp Maier14b27a82020-06-02 20:15:30 +0200923 conn->end.fmtp_extra = talloc_strdup(trunk->endpoints,
924 trunk->audio_fmtp_extra);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200925
Philipp Maier87bd9be2017-08-22 16:35:41 +0200926 if (p->cfg->force_ptime) {
927 conn->end.packet_duration_ms = p->cfg->force_ptime;
928 conn->end.force_output_ptime = 1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200929 }
930
Philipp Maier1cb1e382017-11-02 17:16:04 +0100931 mgcp_rtp_end_config(endp, 0, &conn->end);
932
Philipp Maierc3cc6542018-02-02 12:58:42 +0100933 /* check connection mode setting */
934 if (conn->conn->mode != MGCP_CONN_LOOPBACK
935 && conn->conn->mode != MGCP_CONN_RECV_ONLY
936 && conn->end.rtp_port == 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200937 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
938 "CRCX: selected connection mode type requires an opposite end!\n");
Philipp Maierc3cc6542018-02-02 12:58:42 +0100939 error_code = 527;
Stefan Sperling9270e912018-10-29 14:10:00 +0100940 rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC]);
Philipp Maierc3cc6542018-02-02 12:58:42 +0100941 goto error2;
942 }
943
Pau Espin Pedrol71d42e72020-09-03 14:20:07 +0200944 /* Find a local address for conn based on policy and initial SDP remote
945 information, then find a free port for it */
946 mgcp_get_local_addr(conn->end.local_addr, conn);
Philipp Maier1cb1e382017-11-02 17:16:04 +0100947 if (allocate_port(endp, conn) != 0) {
Stefan Sperlinga714abf2018-10-29 14:19:54 +0100948 rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_BIND_PORT]);
Philipp Maier1cb1e382017-11-02 17:16:04 +0100949 goto error2;
950 }
951
Philipp Maier87bd9be2017-08-22 16:35:41 +0200952 if (setup_rtp_processing(endp, conn) != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200953 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
954 "CRCX: could not start RTP processing!\n");
Stefan Sperling9270e912018-10-29 14:10:00 +0100955 rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_START_RTP]);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200956 goto error2;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200957 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200958
959 /* policy CB */
960 if (p->cfg->policy_cb) {
961 int rc;
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200962 rc = p->cfg->policy_cb(endp, MGCP_ENDP_CRCX, p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200963 switch (rc) {
964 case MGCP_POLICY_REJECT:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200965 LOGPCONN(_conn, DLMGCP, LOGL_NOTICE,
966 "CRCX: CRCX rejected by policy\n");
Philipp Maier1355d7e2018-02-01 14:30:06 +0100967 mgcp_endp_release(endp);
Stefan Sperling9270e912018-10-29 14:10:00 +0100968 rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_REJECTED_BY_POLICY]);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200969 return create_err_response(endp, 400, "CRCX", p->trans);
970 break;
971 case MGCP_POLICY_DEFER:
972 /* stop processing */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200973 return NULL;
974 break;
975 case MGCP_POLICY_CONT:
976 /* just continue */
977 break;
978 }
979 }
980
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200981 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
982 "CRCX: Creating connection: port: %u\n", conn->end.local_port);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200983 if (p->cfg->change_cb)
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200984 p->cfg->change_cb(endp, MGCP_ENDP_CRCX);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200985
Philipp Maiere726d4f2017-11-01 10:41:34 +0100986 /* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
Philipp Maier14b27a82020-06-02 20:15:30 +0200987 OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200988 if (conn->conn->mode & MGCP_CONN_RECV_ONLY
Philipp Maier14b27a82020-06-02 20:15:30 +0200989 && trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200990 send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200991
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200992 LOGPCONN(_conn, DLMGCP, LOGL_NOTICE,
993 "CRCX: connection successfully created\n");
Stefan Sperling9270e912018-10-29 14:10:00 +0100994 rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_SUCCESS]);
Philipp Maier889fe7f2020-07-06 17:44:12 +0200995 mgcp_endp_update(endp);
Philipp Maier55295f72018-01-15 14:00:28 +0100996 return create_response_with_sdp(endp, conn, "CRCX", p->trans, true);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200997error2:
Philipp Maier1355d7e2018-02-01 14:30:06 +0100998 mgcp_endp_release(endp);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200999 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1000 "CRCX: unable to create connection\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001001 return create_err_response(endp, error_code, "CRCX", p->trans);
1002}
1003
Philipp Maier87bd9be2017-08-22 16:35:41 +02001004/* MDCX command handler, processes the received command */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001005static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
1006{
1007 struct mgcp_endpoint *endp = p->endp;
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001008 struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.mgcp_mdcx_ctr_group;
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001009 char new_local_addr[INET6_ADDRSTRLEN];
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001010 int error_code = 500;
1011 int silent = 0;
1012 int have_sdp = 0;
1013 char *line;
1014 const char *local_options = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001015 const char *mode = NULL;
1016 struct mgcp_conn_rtp *conn = NULL;
Philipp Maier01d24a32017-11-21 17:26:09 +01001017 const char *conn_id = NULL;
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001018 int osmux_cid = -2;
Philipp Maiera390d0b2018-01-31 17:30:19 +01001019 int rc;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001020
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001021 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "MDCX: modifying existing connection ...\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001022
Philipp Maier8d6a1932020-06-18 12:19:31 +02001023 if (!mgcp_endp_avail(endp)) {
1024 rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_AVAIL]);
Philipp Maiera910a812020-08-18 15:13:33 +02001025 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1026 "MDCX: selected endpoint not available!\n");
Philipp Maier8d6a1932020-06-18 12:19:31 +02001027 return create_err_response(NULL, 501, "MDCX", p->trans);
1028 }
1029
Philipp Maier5656fbf2018-02-02 14:41:58 +01001030 /* Prohibit wildcarded requests */
1031 if (endp->wildcarded_req) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001032 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1033 "MDCX: wildcarded endpoint names not supported.\n");
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001034 rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_WILDCARD]);
Philipp Maier5656fbf2018-02-02 14:41:58 +01001035 return create_err_response(endp, 507, "MDCX", p->trans);
1036 }
1037
Philipp Maier87bd9be2017-08-22 16:35:41 +02001038 if (llist_count(&endp->conns) <= 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001039 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1040 "MDCX: endpoint is not holding a connection.\n");
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001041 rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_NO_CONN]);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001042 return create_err_response(endp, 400, "MDCX", p->trans);
1043 }
1044
1045 for_each_line(line, p->save) {
1046 if (!mgcp_check_param(endp, line))
1047 continue;
1048
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001049 switch (toupper(line[0])) {
Philipp Maier87bd9be2017-08-22 16:35:41 +02001050 case 'C':
Harald Weltee35eeae2017-12-28 13:47:37 +01001051 if (mgcp_verify_call_id(endp, line + 3) != 0) {
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001052 rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_INVALID_CALLID]);
Harald Weltee35eeae2017-12-28 13:47:37 +01001053 error_code = 516;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001054 goto error3;
Harald Weltee35eeae2017-12-28 13:47:37 +01001055 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001056 break;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001057 case 'I':
Philipp Maier01d24a32017-11-21 17:26:09 +01001058 conn_id = (const char *)line + 3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001059 if ((error_code = mgcp_verify_ci(endp, conn_id))) {
1060 rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_INVALID_CONNID]);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001061 goto error3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001062 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001063 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001064 case 'L':
Philipp Maier87bd9be2017-08-22 16:35:41 +02001065 local_options = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001066 break;
1067 case 'M':
Philipp Maier87bd9be2017-08-22 16:35:41 +02001068 mode = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001069 break;
1070 case 'Z':
Pau Espin Pedrol9b508f62019-06-26 13:11:22 +02001071 silent = strcasecmp("noanswer", line + 3) == 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001072 break;
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001073 case 'X':
Pau Espin Pedrolc1bf4692019-05-14 16:23:24 +02001074 if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001075 /* If osmux is disabled, just skip setting it up */
1076 if (!p->endp->cfg->osmux)
1077 break;
1078 osmux_cid = mgcp_osmux_setup(endp, line);
1079 break;
1080 }
1081 /* Ignore unknown X-headers */
1082 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001083 case '\0':
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001084 have_sdp = 1;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001085 goto mgcp_header_done;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001086 break;
1087 default:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001088 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1089 "MDCX: Unhandled MGCP option: '%c'/%d\n",
1090 line[0], line[0]);
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001091 rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_UNHANDLED_PARAM]);
Philipp Maierdd0c5222018-02-02 11:08:48 +01001092 return create_err_response(NULL, 539, "MDCX", p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001093 break;
1094 }
1095 }
1096
Philipp Maier87bd9be2017-08-22 16:35:41 +02001097mgcp_header_done:
Philipp Maier01d24a32017-11-21 17:26:09 +01001098 if (!conn_id) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001099 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1100 "MDCX: insufficient parameters, missing ci (connectionIdentifier)\n");
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001101 rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_NO_CONNID]);
Harald Weltee35eeae2017-12-28 13:47:37 +01001102 return create_err_response(endp, 515, "MDCX", p->trans);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001103 }
1104
1105 conn = mgcp_conn_get_rtp(endp, conn_id);
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001106 if (!conn) {
1107 rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_CONN_NOT_FOUND]);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001108 return create_err_response(endp, 400, "MDCX", p->trans);
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001109 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001110
Oliver Smithe36b7752019-01-22 16:31:36 +01001111 mgcp_conn_watchdog_kick(conn->conn);
1112
Philipp Maier87bd9be2017-08-22 16:35:41 +02001113 if (mode) {
1114 if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001115 rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_INVALID_MODE]);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001116 error_code = 517;
1117 goto error3;
1118 }
1119 } else
Pau Espin Pedrol209eb9f2019-04-24 12:03:04 +02001120 conn->conn->mode = conn->conn->mode_orig;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001121
Philipp Maierbc0346e2018-06-07 09:52:16 +02001122 /* Set local connection options, if present */
1123 if (local_options) {
Philipp Maier14b27a82020-06-02 20:15:30 +02001124 rc = set_local_cx_options(endp->trunk->endpoints,
Philipp Maierbc0346e2018-06-07 09:52:16 +02001125 &endp->local_options, local_options);
1126 if (rc != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001127 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1128 "MDCX: invalid local connection options!\n");
Philipp Maierbc0346e2018-06-07 09:52:16 +02001129 error_code = rc;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001130 rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS]);
Philipp Maierbc0346e2018-06-07 09:52:16 +02001131 goto error3;
1132 }
1133 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001134
Philipp Maierbc0346e2018-06-07 09:52:16 +02001135 /* Handle codec information and decide for a suitable codec */
1136 rc = handle_codec_info(conn, p, have_sdp, false);
1137 mgcp_codec_summary(conn);
1138 if (rc) {
Philipp Maieraf07f662018-02-02 11:34:02 +01001139 error_code = rc;
1140 goto error3;
Philipp Maiera390d0b2018-01-31 17:30:19 +01001141 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001142
Philipp Maierc3cc6542018-02-02 12:58:42 +01001143 /* check connection mode setting */
1144 if (conn->conn->mode != MGCP_CONN_LOOPBACK
1145 && conn->conn->mode != MGCP_CONN_RECV_ONLY
1146 && conn->end.rtp_port == 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001147 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1148 "MDCX: selected connection mode type requires an opposite end!\n");
Philipp Maierc3cc6542018-02-02 12:58:42 +01001149 error_code = 527;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001150 rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC]);
Philipp Maierc3cc6542018-02-02 12:58:42 +01001151 goto error3;
1152 }
1153
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001154 if (mgcp_conn_rtp_is_osmux(conn)) {
1155 OSMO_ASSERT(conn->osmux.cid_allocated);
1156 if (osmux_cid < -1) {
1157 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1158 "MDCX: Failed to parse Osmux CID!\n");
1159 goto error3;
1160 } else if (osmux_cid == -1) {
1161 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1162 "MDCX: wilcard in MDCX is not supported!\n");
1163 goto error3;
1164 } else if (osmux_cid != (int) conn->osmux.cid) {
1165 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1166 "MDCX: changing already allocated CID is not supported!\n");
1167 goto error3;
1168 }
1169 /* TODO: In the future (when we have recvCID!=sendCID), we need to
1170 tell Osmux code that osmux_cid is to be used as sendCID for
1171 that conn. */
1172 }
Philipp Maierbc0346e2018-06-07 09:52:16 +02001173
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001174 /* MDCX may have provided a new remote address, which means we may need
1175 to update our announced IP addr and re-bind our local end. This can
1176 happen for instance if MGW initially provided an IPv4 during CRCX
1177 ACK, and now MDCX tells us the remote has an IPv6 address. */
1178 mgcp_get_local_addr(new_local_addr, conn);
1179 if (strcmp(new_local_addr, conn->end.local_addr)) {
1180 osmo_strlcpy(conn->end.local_addr, new_local_addr, sizeof(conn->end.local_addr));
1181 mgcp_free_rtp_port(&conn->end);
1182 if (allocate_port(endp, conn) != 0) {
1183 rate_ctr_inc(&rate_ctrs->ctr[MGCP_CRCX_FAIL_BIND_PORT]);
1184 goto error3;
1185 }
1186 }
1187
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001188 if (setup_rtp_processing(endp, conn) != 0) {
1189 rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_START_RTP]);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001190 goto error3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001191 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001192
Philipp Maier87bd9be2017-08-22 16:35:41 +02001193
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001194 /* policy CB */
1195 if (p->cfg->policy_cb) {
1196 int rc;
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001197 rc = p->cfg->policy_cb(endp, MGCP_ENDP_MDCX, p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001198 switch (rc) {
1199 case MGCP_POLICY_REJECT:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001200 LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
1201 "MDCX: rejected by policy\n");
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001202 rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_FAIL_REJECTED_BY_POLICY]);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001203 if (silent)
1204 goto out_silent;
1205 return create_err_response(endp, 400, "MDCX", p->trans);
1206 break;
1207 case MGCP_POLICY_DEFER:
1208 /* stop processing */
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001209 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
1210 "MDCX: deferred by policy\n");
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001211 rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_DEFERRED_BY_POLICY]);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001212 return NULL;
1213 break;
1214 case MGCP_POLICY_CONT:
1215 /* just continue */
1216 break;
1217 }
1218 }
1219
Philipp Maier87bd9be2017-08-22 16:35:41 +02001220 mgcp_rtp_end_config(endp, 1, &conn->end);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001221
1222 /* modify */
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001223 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
1224 "MDCX: modified conn:%s\n", mgcp_conn_dump(conn->conn));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001225 if (p->cfg->change_cb)
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001226 p->cfg->change_cb(endp, MGCP_ENDP_MDCX);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001227
Philipp Maiere726d4f2017-11-01 10:41:34 +01001228 /* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
Philipp Maier14b27a82020-06-02 20:15:30 +02001229 OSMO_ASSERT(endp->trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Philipp Maiere726d4f2017-11-01 10:41:34 +01001230 if (conn->conn->mode & MGCP_CONN_RECV_ONLY
Philipp Maier14b27a82020-06-02 20:15:30 +02001231 && endp->trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001232 send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001233
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001234 rate_ctr_inc(&rate_ctrs->ctr[MGCP_MDCX_SUCCESS]);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001235 if (silent)
1236 goto out_silent;
1237
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001238 LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
1239 "MDCX: connection successfully modified\n");
Philipp Maier889fe7f2020-07-06 17:44:12 +02001240 mgcp_endp_update(endp);
Philipp Maier55295f72018-01-15 14:00:28 +01001241 return create_response_with_sdp(endp, conn, "MDCX", p->trans, false);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001242error3:
1243 return create_err_response(endp, error_code, "MDCX", p->trans);
1244
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001245out_silent:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001246 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "MDCX: silent exit\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001247 return NULL;
1248}
1249
Philipp Maier87bd9be2017-08-22 16:35:41 +02001250/* DLCX command handler, processes the received command */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001251static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
1252{
1253 struct mgcp_endpoint *endp = p->endp;
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001254 struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.mgcp_dlcx_ctr_group;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001255 int error_code = 400;
1256 int silent = 0;
1257 char *line;
1258 char stats[1048];
Philipp Maier01d24a32017-11-21 17:26:09 +01001259 const char *conn_id = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001260 struct mgcp_conn_rtp *conn = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001261
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001262 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1263 "DLCX: deleting connection ...\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001264
Philipp Maier8d6a1932020-06-18 12:19:31 +02001265 if (!mgcp_endp_avail(endp)) {
1266 rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_FAIL_AVAIL]);
Philipp Maiera910a812020-08-18 15:13:33 +02001267 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1268 "DLCX: selected endpoint not available!\n");
Philipp Maier8d6a1932020-06-18 12:19:31 +02001269 return create_err_response(NULL, 501, "DLCX", p->trans);
1270 }
1271
Philipp Maier5656fbf2018-02-02 14:41:58 +01001272 /* Prohibit wildcarded requests */
1273 if (endp->wildcarded_req) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001274 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1275 "DLCX: wildcarded endpoint names not supported.\n");
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001276 rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_FAIL_WILDCARD]);
Philipp Maier5656fbf2018-02-02 14:41:58 +01001277 return create_err_response(endp, 507, "DLCX", p->trans);
1278 }
1279
Philipp Maier87bd9be2017-08-22 16:35:41 +02001280 if (llist_count(&endp->conns) <= 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001281 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1282 "DLCX: endpoint is not holding a connection.\n");
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001283 rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_FAIL_NO_CONN]);
Harald Weltee35eeae2017-12-28 13:47:37 +01001284 return create_err_response(endp, 515, "DLCX", p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001285 }
1286
1287 for_each_line(line, p->save) {
1288 if (!mgcp_check_param(endp, line))
1289 continue;
1290
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001291 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001292 case 'C':
Harald Weltee35eeae2017-12-28 13:47:37 +01001293 if (mgcp_verify_call_id(endp, line + 3) != 0) {
1294 error_code = 516;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001295 rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_FAIL_INVALID_CALLID]);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001296 goto error3;
Harald Weltee35eeae2017-12-28 13:47:37 +01001297 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001298 break;
1299 case 'I':
Philipp Maier01d24a32017-11-21 17:26:09 +01001300 conn_id = (const char *)line + 3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001301 if ((error_code = mgcp_verify_ci(endp, conn_id))) {
1302 rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_FAIL_INVALID_CONNID]);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001303 goto error3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001304 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001305 break;
1306 case 'Z':
Pau Espin Pedrol9b508f62019-06-26 13:11:22 +02001307 silent = strcasecmp("noanswer", line + 3) == 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001308 break;
1309 default:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001310 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1311 "DLCX: Unhandled MGCP option: '%c'/%d\n",
1312 line[0], line[0]);
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001313 rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_FAIL_UNHANDLED_PARAM]);
Philipp Maierdd0c5222018-02-02 11:08:48 +01001314 return create_err_response(NULL, 539, "DLCX", p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001315 break;
1316 }
1317 }
1318
1319 /* policy CB */
1320 if (p->cfg->policy_cb) {
1321 int rc;
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001322 rc = p->cfg->policy_cb(endp, MGCP_ENDP_DLCX, p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001323 switch (rc) {
1324 case MGCP_POLICY_REJECT:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001325 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "DLCX: rejected by policy\n");
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001326 rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_FAIL_REJECTED_BY_POLICY]);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001327 if (silent)
1328 goto out_silent;
1329 return create_err_response(endp, 400, "DLCX", p->trans);
1330 break;
1331 case MGCP_POLICY_DEFER:
1332 /* stop processing */
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001333 rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_DEFERRED_BY_POLICY]);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001334 return NULL;
1335 break;
1336 case MGCP_POLICY_CONT:
1337 /* just continue */
1338 break;
1339 }
1340 }
1341
Philipp Maierf4c0e372017-10-11 16:06:45 +02001342 /* When no connection id is supplied, we will interpret this as a
1343 * wildcarded DLCX and drop all connections at once. (See also
1344 * RFC3435 Section F.7) */
Philipp Maier01d24a32017-11-21 17:26:09 +01001345 if (!conn_id) {
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001346 int num_conns = llist_count(&endp->conns);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001347 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1348 "DLCX: missing ci (connectionIdentifier), will remove all connections (%d total) at once\n",
1349 num_conns);
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001350
1351 if (num_conns > 0)
1352 rate_ctr_add(&rate_ctrs->ctr[MGCP_DLCX_SUCCESS], num_conns);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001353
Philipp Maier1355d7e2018-02-01 14:30:06 +01001354 mgcp_endp_release(endp);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001355
1356 /* Note: In this case we do not return any statistics,
1357 * as we assume that the client is not interested in
1358 * this case. */
1359 return create_ok_response(endp, 200, "DLCX", p->trans);
1360 }
1361
Philipp Maierf4c0e372017-10-11 16:06:45 +02001362 /* Find the connection */
Philipp Maier87bd9be2017-08-22 16:35:41 +02001363 conn = mgcp_conn_get_rtp(endp, conn_id);
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001364 if (!conn) {
1365 rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_FAIL_INVALID_CONNID]);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001366 goto error3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001367 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001368 /* save the statistics of the current connection */
1369 mgcp_format_stats(stats, sizeof(stats), conn->conn);
1370
1371 /* delete connection */
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001372 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG, "DLCX: deleting conn:%s\n",
1373 mgcp_conn_dump(conn->conn));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001374 mgcp_conn_free(endp, conn_id);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001375 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1376 "DLCX: connection successfully deleted\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +02001377
1378 /* When all connections are closed, the endpoint will be released
1379 * in order to be ready to be used by another call. */
1380 if (llist_count(&endp->conns) <= 0) {
Philipp Maier1355d7e2018-02-01 14:30:06 +01001381 mgcp_endp_release(endp);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001382 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: endpoint released\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +02001383 }
1384
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001385 if (p->cfg->change_cb)
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001386 p->cfg->change_cb(endp, MGCP_ENDP_DLCX);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001387
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001388 rate_ctr_inc(&rate_ctrs->ctr[MGCP_DLCX_SUCCESS]);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001389 if (silent)
1390 goto out_silent;
1391 return create_ok_resp_with_param(endp, 250, "DLCX", p->trans, stats);
1392
1393error3:
1394 return create_err_response(endp, error_code, "DLCX", p->trans);
1395
1396out_silent:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001397 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: silent exit\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001398 return NULL;
1399}
1400
Philipp Maier87bd9be2017-08-22 16:35:41 +02001401/* RSIP command handler, processes the received command */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001402static struct msgb *handle_rsip(struct mgcp_parse_data *p)
1403{
Philipp Maier87bd9be2017-08-22 16:35:41 +02001404 /* TODO: Also implement the resetting of a specific endpoint
1405 * to make mgcp_send_reset_ep() work. Currently this will call
1406 * mgcp_rsip_cb() in mgw_main.c, which sets reset_endpoints=1
1407 * to make read_call_agent() reset all endpoints when called
1408 * next time. In order to selectively reset endpoints some
1409 * mechanism to distinguish which endpoint shall be resetted
1410 * is needed */
1411
1412 LOGP(DLMGCP, LOGL_NOTICE, "RSIP: resetting all endpoints ...\n");
1413
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001414 if (p->cfg->reset_cb)
Philipp Maier14b27a82020-06-02 20:15:30 +02001415 p->cfg->reset_cb(p->endp->trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001416 return NULL;
1417}
1418
1419static char extract_tone(const char *line)
1420{
1421 const char *str = strstr(line, "D/");
1422 if (!str)
1423 return CHAR_MAX;
1424
1425 return str[2];
1426}
1427
Philipp Maier87bd9be2017-08-22 16:35:41 +02001428/* This can request like DTMF detection and forward, fax detection... it
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001429 * can also request when the notification should be send and such. We don't
Philipp Maier87bd9be2017-08-22 16:35:41 +02001430 * do this right now. */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001431static struct msgb *handle_noti_req(struct mgcp_parse_data *p)
1432{
1433 int res = 0;
1434 char *line;
1435 char tone = CHAR_MAX;
1436
Philipp Maier87bd9be2017-08-22 16:35:41 +02001437 LOGP(DLMGCP, LOGL_NOTICE, "RQNT: processing request for notification ...\n");
1438
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001439 for_each_line(line, p->save) {
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001440 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001441 case 'S':
1442 tone = extract_tone(line);
1443 break;
1444 }
1445 }
1446
1447 /* we didn't see a signal request with a tone */
1448 if (tone == CHAR_MAX)
1449 return create_ok_response(p->endp, 200, "RQNT", p->trans);
1450
1451 if (p->cfg->rqnt_cb)
1452 res = p->cfg->rqnt_cb(p->endp, tone);
1453
1454 return res == 0 ?
Philipp Maier87bd9be2017-08-22 16:35:41 +02001455 create_ok_response(p->endp, 200, "RQNT", p->trans) :
1456 create_err_response(p->endp, res, "RQNT", p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001457}
1458
Philipp Maier87bd9be2017-08-22 16:35:41 +02001459/* Connection keepalive timer, will take care that dummy packets are send
Harald Welte1d1b98f2017-12-25 10:03:40 +01001460 * regularly, so that NAT connections stay open */
Philipp Maier14b27a82020-06-02 20:15:30 +02001461static void mgcp_keepalive_timer_cb(void *_trunk)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001462{
Philipp Maier14b27a82020-06-02 20:15:30 +02001463 struct mgcp_trunk *trunk = _trunk;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001464 struct mgcp_conn *conn;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001465 int i;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001466
Philipp Maiere726d4f2017-11-01 10:41:34 +01001467 LOGP(DLMGCP, LOGL_DEBUG, "triggered trunk %d keepalive timer\n",
Philipp Maier14b27a82020-06-02 20:15:30 +02001468 trunk->trunk_nr);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001469
Philipp Maiere726d4f2017-11-01 10:41:34 +01001470 /* Do not accept invalid configuration values
1471 * valid is MGCP_KEEPALIVE_NEVER, MGCP_KEEPALIVE_ONCE and
1472 * values greater 0 */
Philipp Maier14b27a82020-06-02 20:15:30 +02001473 OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Philipp Maiere726d4f2017-11-01 10:41:34 +01001474
1475 /* The dummy packet functionality has been disabled, we will exit
1476 * immediately, no further timer is scheduled, which means we will no
1477 * longer send dummy packets even when we did before */
Philipp Maier14b27a82020-06-02 20:15:30 +02001478 if (trunk->keepalive_interval == MGCP_KEEPALIVE_NEVER)
Philipp Maiere726d4f2017-11-01 10:41:34 +01001479 return;
1480
1481 /* In cases where only one dummy packet is sent, we do not need
1482 * the timer since the functions that handle the CRCX and MDCX are
1483 * triggering the sending of the dummy packet. So we behave like in
1484 * the MGCP_KEEPALIVE_NEVER case */
Philipp Maier14b27a82020-06-02 20:15:30 +02001485 if (trunk->keepalive_interval == MGCP_KEEPALIVE_ONCE)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001486 return;
1487
Philipp Maier87bd9be2017-08-22 16:35:41 +02001488 /* Send walk over all endpoints and send out dummy packets through
1489 * every connection present on each endpoint */
Philipp Maier14b27a82020-06-02 20:15:30 +02001490 for (i = 1; i < trunk->number_endpoints; ++i) {
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001491 struct mgcp_endpoint *endp = trunk->endpoints[i];
Philipp Maier87bd9be2017-08-22 16:35:41 +02001492 llist_for_each_entry(conn, &endp->conns, entry) {
1493 if (conn->mode == MGCP_CONN_RECV_ONLY)
1494 send_dummy(endp, &conn->u.rtp);
1495 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001496 }
1497
Philipp Maiere726d4f2017-11-01 10:41:34 +01001498 /* Schedule the keepalive timer for the next round */
1499 LOGP(DLMGCP, LOGL_DEBUG, "rescheduling trunk %d keepalive timer\n",
Philipp Maier14b27a82020-06-02 20:15:30 +02001500 trunk->trunk_nr);
1501 osmo_timer_schedule(&trunk->keepalive_timer, trunk->keepalive_interval,
Philipp Maier87bd9be2017-08-22 16:35:41 +02001502 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001503}
1504
Philipp Maier14b27a82020-06-02 20:15:30 +02001505void mgcp_trunk_set_keepalive(struct mgcp_trunk *trunk, int interval)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001506{
Philipp Maier14b27a82020-06-02 20:15:30 +02001507 trunk->keepalive_interval = interval;
1508 osmo_timer_setup(&trunk->keepalive_timer, mgcp_keepalive_timer_cb, trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001509
1510 if (interval <= 0)
Philipp Maier14b27a82020-06-02 20:15:30 +02001511 osmo_timer_del(&trunk->keepalive_timer);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001512 else
Philipp Maier14b27a82020-06-02 20:15:30 +02001513 osmo_timer_schedule(&trunk->keepalive_timer,
1514 trunk->keepalive_interval, 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001515}
1516
Philipp Maier87bd9be2017-08-22 16:35:41 +02001517/*! allocate configuration with default values.
1518 * (called once at startup by main function) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001519struct mgcp_config *mgcp_config_alloc(void)
1520{
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001521 /* FIXME: This is unrelated to the protocol, put this in some
1522 * appropiate place! */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001523 struct mgcp_config *cfg;
1524
1525 cfg = talloc_zero(NULL, struct mgcp_config);
1526 if (!cfg) {
1527 LOGP(DLMGCP, LOGL_FATAL, "Failed to allocate config.\n");
1528 return NULL;
1529 }
1530
Philipp Maier12943ea2018-01-17 15:40:25 +01001531 osmo_strlcpy(cfg->domain, "mgw", sizeof(cfg->domain));
1532
Philipp Maier87bd9be2017-08-22 16:35:41 +02001533 cfg->net_ports.range_start = RTP_PORT_DEFAULT_RANGE_START;
1534 cfg->net_ports.range_end = RTP_PORT_DEFAULT_RANGE_END;
1535 cfg->net_ports.last_port = cfg->net_ports.range_start;
1536
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001537 cfg->source_port = 2427;
1538 cfg->source_addr = talloc_strdup(cfg, "0.0.0.0");
1539 cfg->osmux_addr = talloc_strdup(cfg, "0.0.0.0");
1540
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001541 cfg->rtp_processing_cb = &mgcp_rtp_processing_default;
1542 cfg->setup_rtp_processing_cb = &mgcp_setup_rtp_processing_default;
1543
1544 cfg->get_net_downlink_format_cb = &mgcp_get_net_downlink_format_default;
1545
Philipp Maierd19de2e2020-06-03 13:55:33 +02001546 INIT_LLIST_HEAD(&cfg->trunks);
1547
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001548 /* Allocate virtual trunk */
Philipp Maierd19de2e2020-06-03 13:55:33 +02001549 if (!mgcp_trunk_alloc(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID)) {
Harald Welte3ac604e2019-05-08 14:07:41 +02001550 talloc_free(cfg);
1551 return NULL;
1552 }
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001553
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001554 mgcp_ratectr_global_alloc(cfg, &cfg->ratectr);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001555
1556 return cfg;
1557}
1558
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001559static int send_agent(struct mgcp_config *cfg, const char *buf, int len)
1560{
1561 return write(cfg->gw_fd.bfd.fd, buf, len);
1562}
1563
Philipp Maier87bd9be2017-08-22 16:35:41 +02001564/*! Reset all endpoints by sending RSIP message to self.
1565 * (called by VTY)
1566 * \param[in] endp trunk endpoint
1567 * \param[in] endpoint number
1568 * \returns 0 on success, -1 on error */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001569int mgcp_send_reset_all(struct mgcp_config *cfg)
1570{
Philipp Maier12943ea2018-01-17 15:40:25 +01001571 char buf[MGCP_ENDPOINT_MAXLEN + 128];
1572 int len;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001573 int rc;
1574
Philipp Maier12943ea2018-01-17 15:40:25 +01001575 len = snprintf(buf, sizeof(buf),
1576 "RSIP 1 *@%s MGCP 1.0\r\n", cfg->domain);
1577 if (len < 0)
1578 return -1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001579
Philipp Maier12943ea2018-01-17 15:40:25 +01001580 rc = send_agent(cfg, buf, len);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001581 if (rc <= 0)
1582 return -1;
1583
1584 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001585}
1586
Philipp Maier87bd9be2017-08-22 16:35:41 +02001587/*! Reset a single endpoint by sending RSIP message to self.
1588 * (called by VTY)
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001589 * \param[in] endp to reset
Philipp Maier87bd9be2017-08-22 16:35:41 +02001590 * \returns 0 on success, -1 on error */
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001591int mgcp_send_reset_ep(struct mgcp_endpoint *endp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001592{
Philipp Maier12943ea2018-01-17 15:40:25 +01001593 char buf[MGCP_ENDPOINT_MAXLEN + 128];
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001594 int len;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001595 int rc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001596
1597 len = snprintf(buf, sizeof(buf),
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001598 "RSIP 39 %s MGCP 1.0\r\n", endp->name);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001599 if (len < 0)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001600 return -1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001601
Philipp Maier87bd9be2017-08-22 16:35:41 +02001602 rc = send_agent(endp->cfg, buf, len);
1603 if (rc <= 0)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001604 return -1;
1605
Philipp Maier87bd9be2017-08-22 16:35:41 +02001606 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001607}