blob: e69c00f1fc4020e41adcdaba8c06d71c1cced06b [file] [log] [blame]
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
2/* The protocol implementation */
3
4/*
5 * (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
6 * (C) 2009-2012 by On-Waves
7 * All Rights Reserved
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU Affero General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Affero General Public License for more details.
18 *
19 * You should have received a copy of the GNU Affero General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 *
22 */
23
24#include <ctype.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <time.h>
28#include <limits.h>
29#include <unistd.h>
30#include <errno.h>
31
32#include <osmocom/core/msgb.h>
33#include <osmocom/core/talloc.h>
34#include <osmocom/core/select.h>
35
Philipp Maier87bd9be2017-08-22 16:35:41 +020036#include <osmocom/mgcp/mgcp.h>
Neels Hofmeyr67793542017-09-08 04:25:16 +020037#include <osmocom/mgcp/mgcp_common.h>
Philipp Maier993ea6b2020-08-04 18:26:50 +020038#include <osmocom/mgcp/osmux.h>
39#include <osmocom/mgcp/mgcp_network.h>
40#include <osmocom/mgcp/mgcp_protocol.h>
Philipp Maier87bd9be2017-08-22 16:35:41 +020041#include <osmocom/mgcp/mgcp_stat.h>
42#include <osmocom/mgcp/mgcp_msg.h>
Philipp Maier37d11c82018-02-01 14:38:12 +010043#include <osmocom/mgcp/mgcp_endp.h>
Philipp Maierc66ab2c2020-06-02 20:55:34 +020044#include <osmocom/mgcp/mgcp_trunk.h>
Philipp Maier8970c492017-10-11 13:33:42 +020045#include <osmocom/mgcp/mgcp_sdp.h>
Philipp Maierbc0346e2018-06-07 09:52:16 +020046#include <osmocom/mgcp/mgcp_codec.h>
Stefan Sperlingba25eab2018-10-30 14:32:31 +010047#include <osmocom/mgcp/mgcp_conn.h>
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020048
Philipp Maierf486e742021-07-19 14:56:29 +020049/* A combination of LOGPENDP and LOGPTRUNK that automatically falls back to
50 * LOGPTRUNK when the endp parameter is NULL */
51#define LOGPEPTR(endp, trunk, cat, level, fmt, args...) \
52do { \
53 if (endp) \
54 LOGPENDP(endp, cat, level, fmt, ## args); \
55 else \
56 LOGPTRUNK(trunk, cat, level, fmt, ## args); \
57} while (0)
58
Philipp Maier8dc35972021-07-14 11:20:16 +020059/* Request data passed to the request handler */
60struct mgcp_request_data {
61 /* request name (e.g. "MDCX") */
62 char name[4+1];
63
64 /* parsing results from the MGCP header (trans id, endpoint name ...) */
65 struct mgcp_parse_data *pdata;
66
67 /* pointer to endpoint resource (may be NULL for wildcarded requests) */
68 struct mgcp_endpoint *endp;
69
70 /* pointer to trunk resource */
71 struct mgcp_trunk *trunk;
72
73 /* set to true when the request has been classified as wildcarded */
74 bool wildcarded;
75
76 /* contains cause code in case of problems during endp/trunk resolution */
77 int mgcp_cause;
78};
79
Philipp Maier33d97f72021-07-14 14:53:45 +020080/* Request handler specification, here we specify an array with function
81 * pointers to the various MGCP requests implemented below */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020082struct mgcp_request {
Philipp Maier33d97f72021-07-14 14:53:45 +020083 /* request name (e.g. "MDCX") */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020084 char *name;
Philipp Maier33d97f72021-07-14 14:53:45 +020085
86 /* function pointer to the request handler */
Philipp Maier8dc35972021-07-14 11:20:16 +020087 struct msgb *(*handle_request)(struct mgcp_request_data *data);
88
89 /* true if the request requires an endpoint, false if only a trunk
90 * is sufficient. (corner cases, e.g. wildcarded DLCX) */
91 bool require_endp;
Philipp Maier33d97f72021-07-14 14:53:45 +020092
93 /* a human readable name that describes the request */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020094 char *debug_name;
95};
96
Philipp Maier8dc35972021-07-14 11:20:16 +020097static struct msgb *handle_audit_endpoint(struct mgcp_request_data *data);
98static struct msgb *handle_create_con(struct mgcp_request_data *data);
99static struct msgb *handle_delete_con(struct mgcp_request_data *data);
100static struct msgb *handle_modify_con(struct mgcp_request_data *data);
101static struct msgb *handle_rsip(struct mgcp_request_data *data);
102static struct msgb *handle_noti_req(struct mgcp_request_data *data);
Philipp Maier33d97f72021-07-14 14:53:45 +0200103static const struct mgcp_request mgcp_requests[] = {
104 { .name = "AUEP",
105 .handle_request = handle_audit_endpoint,
Philipp Maier8dc35972021-07-14 11:20:16 +0200106 .debug_name = "AuditEndpoint",
107 .require_endp = true },
Philipp Maier33d97f72021-07-14 14:53:45 +0200108 { .name = "CRCX",
109 .handle_request = handle_create_con,
Philipp Maier8dc35972021-07-14 11:20:16 +0200110 .debug_name = "CreateConnection",
111 .require_endp = true },
Philipp Maier33d97f72021-07-14 14:53:45 +0200112 { .name = "DLCX",
113 .handle_request = handle_delete_con,
Philipp Maier8dc35972021-07-14 11:20:16 +0200114 .debug_name = "DeleteConnection",
Philipp Maierf486e742021-07-19 14:56:29 +0200115 .require_endp = false },
Philipp Maier33d97f72021-07-14 14:53:45 +0200116 { .name = "MDCX",
117 .handle_request = handle_modify_con,
Philipp Maier8dc35972021-07-14 11:20:16 +0200118 .debug_name = "ModifiyConnection",
119 .require_endp = true },
Philipp Maier33d97f72021-07-14 14:53:45 +0200120 { .name = "RQNT",
121 .handle_request = handle_noti_req,
Philipp Maier8dc35972021-07-14 11:20:16 +0200122 .debug_name = "NotificationRequest",
123 .require_endp = true },
Philipp Maier33d97f72021-07-14 14:53:45 +0200124
125 /* SPEC extension */
126 { .name = "RSIP",
127 .handle_request = handle_rsip,
Philipp Maier8dc35972021-07-14 11:20:16 +0200128 .debug_name = "ReSetInProgress",
129 .require_endp = true },
Philipp Maier33d97f72021-07-14 14:53:45 +0200130};
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200131
Philipp Maier87bd9be2017-08-22 16:35:41 +0200132/* Initalize transcoder */
133static int setup_rtp_processing(struct mgcp_endpoint *endp,
134 struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200135{
Philipp Maier87bd9be2017-08-22 16:35:41 +0200136 struct mgcp_config *cfg = endp->cfg;
137 struct mgcp_conn_rtp *conn_src = NULL;
138 struct mgcp_conn_rtp *conn_dst = conn;
139 struct mgcp_conn *_conn;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200140
Pau Espin Pedrolfa810e82019-05-06 18:54:10 +0200141 if (conn->type != MGCP_RTP_DEFAULT && !mgcp_conn_rtp_is_osmux(conn)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200142 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
143 "RTP-setup: Endpoint is not configured as RTP default, stopping here!\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200144 return 0;
145 }
146
Philipp Maier87bd9be2017-08-22 16:35:41 +0200147 if (conn->conn->mode == MGCP_CONN_LOOPBACK) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200148 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
149 "RTP-setup: Endpoint is in loopback mode, stopping here!\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +0200150 return 0;
151 }
152
153 /* Find the "sister" connection */
154 llist_for_each_entry(_conn, &endp->conns, entry) {
155 if (_conn->id != conn->conn->id) {
156 conn_src = &_conn->u.rtp;
157 break;
158 }
159 }
160
Philipp Maieracc10352018-07-19 18:07:57 +0200161 return cfg->setup_rtp_processing_cb(endp, conn_dst, conn_src);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200162}
163
Philipp Maier87bd9be2017-08-22 16:35:41 +0200164/* Helper function to allocate some memory for responses and retransmissions */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200165static struct msgb *mgcp_msgb_alloc(void)
166{
167 struct msgb *msg;
168 msg = msgb_alloc_headroom(4096, 128, "MGCP msg");
169 if (!msg)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200170 LOGP(DLMGCP, LOGL_ERROR, "Failed to msgb for MGCP data.\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200171
172 return msg;
173}
174
Philipp Maier87bd9be2017-08-22 16:35:41 +0200175/* Helper function for do_retransmission() and create_resp() */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200176static struct msgb *do_retransmission(const struct mgcp_endpoint *endp)
177{
178 struct msgb *msg = mgcp_msgb_alloc();
179 if (!msg)
180 return NULL;
181
182 msg->l2h = msgb_put(msg, strlen(endp->last_response));
183 memcpy(msg->l2h, endp->last_response, msgb_l2len(msg));
Philipp Maier87bd9be2017-08-22 16:35:41 +0200184 mgcp_disp_msg(msg->l2h, msgb_l2len(msg), "Retransmitted response");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200185 return msg;
186}
187
188static struct msgb *create_resp(struct mgcp_endpoint *endp, int code,
189 const char *txt, const char *msg,
190 const char *trans, const char *param,
191 const char *sdp)
192{
193 int len;
194 struct msgb *res;
195
196 res = mgcp_msgb_alloc();
197 if (!res)
198 return NULL;
199
Philipp Maier87bd9be2017-08-22 16:35:41 +0200200 len = snprintf((char *)res->data, 2048, "%d %s%s%s\r\n%s",
201 code, trans, txt, param ? param : "", sdp ? sdp : "");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200202 if (len < 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200203 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "Failed to sprintf MGCP response.\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200204 msgb_free(res);
205 return NULL;
206 }
207
208 res->l2h = msgb_put(res, len);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200209 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "Generated response: code=%d\n", code);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200210 mgcp_disp_msg(res->l2h, msgb_l2len(res), "Generated response");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200211
212 /*
213 * Remember the last transmission per endpoint.
214 */
215 if (endp) {
Philipp Maier14b27a82020-06-02 20:15:30 +0200216 struct mgcp_trunk *trunk = endp->trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200217 talloc_free(endp->last_response);
218 talloc_free(endp->last_trans);
Philipp Maier14b27a82020-06-02 20:15:30 +0200219 endp->last_trans = talloc_strdup(trunk->endpoints, trans);
220 endp->last_response = talloc_strndup(trunk->endpoints,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200221 (const char *)res->l2h,
222 msgb_l2len(res));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200223 }
224
225 return res;
226}
227
228static struct msgb *create_ok_resp_with_param(struct mgcp_endpoint *endp,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200229 int code, const char *msg,
230 const char *trans,
231 const char *param)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200232{
233 return create_resp(endp, code, " OK", msg, trans, param, NULL);
234}
235
236static struct msgb *create_ok_response(struct mgcp_endpoint *endp,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200237 int code, const char *msg,
238 const char *trans)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200239{
240 return create_ok_resp_with_param(endp, code, msg, trans, NULL);
241}
242
243static struct msgb *create_err_response(struct mgcp_endpoint *endp,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200244 int code, const char *msg,
245 const char *trans)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200246{
247 return create_resp(endp, code, " FAIL", msg, trans, NULL, NULL);
248}
249
Philipp Maier87bd9be2017-08-22 16:35:41 +0200250/* Format MGCP response string (with SDP attached) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200251static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200252 struct mgcp_conn_rtp *conn,
253 const char *msg,
Philipp Maier55295f72018-01-15 14:00:28 +0100254 const char *trans_id,
Philipp Maier41d59202021-07-20 15:49:00 +0200255 bool add_epname,
256 bool add_conn_id)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200257{
Pau Espin Pedrol71d42e72020-09-03 14:20:07 +0200258 /* cfg->local_ip allows overwritting the announced IP address with
259 * regards to the one we actually bind to. Useful in behind-NAT
260 * scenarios.
261 * TODO: we may want to define another local_ip_osmux var to
262 * us for OSMUX connections. Perhaps adding a new internal API to get it
263 * based on conn type.
264 */
265 const char *addr = endp->cfg->local_ip ? : conn->end.local_addr;
Philipp Maier8970c492017-10-11 13:33:42 +0200266 struct msgb *sdp;
267 int rc;
268 struct msgb *result;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200269
Philipp Maier8970c492017-10-11 13:33:42 +0200270 sdp = msgb_alloc_headroom(4096, 128, "sdp record");
271 if (!sdp)
272 return NULL;
273
Philipp Maier41d59202021-07-20 15:49:00 +0200274 /* Attach optional endpoint name */
275 if (add_epname) {
276 rc = msgb_printf(sdp, "Z: %s\r\n", endp->name);
277 if (rc < 0)
278 goto error;
279 }
280
281 /* Attach optional connection id */
282 if (add_conn_id) {
283 rc = msgb_printf(sdp, "I: %s\r\n", conn->conn->id);
Philipp Maier55295f72018-01-15 14:00:28 +0100284 if (rc < 0)
285 goto error;
286 }
287
Philipp Maier3cbfb8a2018-01-22 11:39:59 +0100288 /* Attach optional OSMUX parameters */
Pau Espin Pedrolc63f15a2019-05-10 16:52:08 +0200289 if (mgcp_conn_rtp_is_osmux(conn)) {
Pau Espin Pedrol5e8d7992019-04-24 19:56:43 +0200290 rc = msgb_printf(sdp, "X-Osmux: %u\r\n", conn->osmux.cid);
Philipp Maier3cbfb8a2018-01-22 11:39:59 +0100291 if (rc < 0)
292 goto error;
293 }
294
295 /* Attach line break to separate the parameters from the SDP block */
Philipp Maierc3cfae22018-01-22 12:03:03 +0100296 rc = msgb_printf(sdp, "\r\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200297
Philipp Maier8970c492017-10-11 13:33:42 +0200298 rc = mgcp_write_response_sdp(endp, conn, sdp, addr);
299 if (rc < 0)
300 goto error;
301 result = create_resp(endp, 200, " OK", msg, trans_id, NULL, (char*) sdp->data);
302 msgb_free(sdp);
303 return result;
304error:
305 msgb_free(sdp);
306 return NULL;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200307}
308
Philipp Maier87bd9be2017-08-22 16:35:41 +0200309/* Send out dummy packet to keep the connection open, if the connection is an
310 * osmux connection, send the dummy packet via OSMUX */
311static void send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200312{
Philipp Maier87bd9be2017-08-22 16:35:41 +0200313 if (conn->osmux.state != OSMUX_STATE_DISABLED)
314 osmux_send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200315 else
Philipp Maier87bd9be2017-08-22 16:35:41 +0200316 mgcp_send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200317}
318
Philipp Maier87bd9be2017-08-22 16:35:41 +0200319/* handle incoming messages:
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200320 * - this can be a command (four letters, space, transaction id)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200321 * - or a response (three numbers, space, transaction id) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200322struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
323{
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200324 struct rate_ctr_group *rate_ctrs = cfg->ratectr.mgcp_general_ctr_group;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200325 struct mgcp_parse_data pdata;
Philipp Maier8dc35972021-07-14 11:20:16 +0200326 struct mgcp_request_data rq;
Harald Weltee35eeae2017-12-28 13:47:37 +0100327 int rc, i, code, handled = 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200328 struct msgb *resp = NULL;
329 char *data;
330
Alexander Chemeris63866002020-05-05 17:18:40 +0300331 /* Count all messages, even incorect ones */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200332 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_TOTAL));
Alexander Chemeris63866002020-05-05 17:18:40 +0300333
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200334 if (msgb_l2len(msg) < 4) {
335 LOGP(DLMGCP, LOGL_ERROR, "msg too short: %d\n", msg->len);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200336 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200337 return NULL;
338 }
339
Alexander Chemeris63866002020-05-05 17:18:40 +0300340 if (mgcp_msg_terminate_nul(msg)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200341 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200342 return NULL;
Alexander Chemeris63866002020-05-05 17:18:40 +0300343 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200344
Philipp Maier87bd9be2017-08-22 16:35:41 +0200345 mgcp_disp_msg(msg->l2h, msgb_l2len(msg), "Received message");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200346
Philipp Maier87bd9be2017-08-22 16:35:41 +0200347 /* attempt to treat it as a response */
348 if (sscanf((const char *)&msg->l2h[0], "%3d %*s", &code) == 1) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200349 LOGP(DLMGCP, LOGL_DEBUG, "Response: Code: %d\n", code);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200350 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200351 return NULL;
352 }
353
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200354
Philipp Maier8dc35972021-07-14 11:20:16 +0200355 /* Parse message, extract endpoint name and transaction identifier and request name etc. */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200356 memset(&pdata, 0, sizeof(pdata));
Philipp Maier8dc35972021-07-14 11:20:16 +0200357 memset(&rq, 0, sizeof(rq));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200358 pdata.cfg = cfg;
Philipp Maier8dc35972021-07-14 11:20:16 +0200359 memcpy(rq.name, (const char *)&msg->l2h[0], sizeof(rq.name)-1);
360 msg->l3h = &msg->l2h[4];
Philipp Maier87bd9be2017-08-22 16:35:41 +0200361 data = mgcp_strline((char *)msg->l3h, &pdata.save);
Harald Weltee35eeae2017-12-28 13:47:37 +0100362 rc = mgcp_parse_header(&pdata, data);
Harald Weltee35eeae2017-12-28 13:47:37 +0100363 if (rc < 0) {
Philipp Maier8dc35972021-07-14 11:20:16 +0200364 LOGP(DLMGCP, LOGL_ERROR, "%s: failed to parse MCGP message\n", rq.name);
365 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
366 return create_err_response(NULL, -rc, rq.name, "000000");
Harald Welteabbb6b92017-12-28 13:13:50 +0100367 }
368
Philipp Maier8dc35972021-07-14 11:20:16 +0200369 /* Locate endpoint and trunk, if no endpoint can be located try at least to identify the trunk. */
370 rq.pdata = &pdata;
371 rq.wildcarded = mgcp_endp_is_wildcarded(pdata.epname);
372 rq.endp = mgcp_endp_by_name(&rc, pdata.epname, pdata.cfg);
373 rq.mgcp_cause = rc;
374 if (!rq.endp) {
375 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_NO_ENDPOINT));
376 if (rq.wildcarded) {
377 /* If we are unable to find the endpoint we still may be able to identify the trunk. Some
378 * request handlers will still be able to perform a useful action if the request refers to
379 * the whole trunk (wildcarded request). */
380 LOGP(DLMGCP, LOGL_NOTICE,
381 "%s: cannot find endpoint \"%s\", cause=%d -- trying to identify trunk...\n", rq.name,
382 pdata.epname, -rq.mgcp_cause);
383 rq.trunk = mgcp_trunk_by_name(pdata.cfg, pdata.epname);
384 if (!rq.trunk) {
385 LOGP(DLMGCP, LOGL_ERROR, "%s: failed to identify trunk for endpoint \"%s\" -- abort\n",
386 rq.name, pdata.epname);
387 return create_err_response(NULL, -rq.mgcp_cause, rq.name, pdata.trans);
388 }
389 } else {
390 /* If the endpoint name suggests that the request refers to a specific endpoint, then the
391 * request cannot be handled and we must stop early. */
392 LOGP(DLMGCP, LOGL_NOTICE,
393 "%s: cannot find endpoint \"%s\", cause=%d -- abort\n", rq.name,
394 pdata.epname, -rq.mgcp_cause);
395 return create_err_response(NULL, -rq.mgcp_cause, rq.name, pdata.trans);
396 }
397 } else {
398 rq.trunk = rq.endp->trunk;
399 rq.mgcp_cause = 0;
400
401 /* Check if we have to retransmit a response from a previous transaction */
402 if (pdata.trans && rq.endp->last_trans && strcmp(rq.endp->last_trans, pdata.trans) == 0) {
403 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_RETRANSMITTED));
404 return do_retransmission(rq.endp);
405 }
406 }
407
408 /* Find an appropriate handler for the current request and execute it */
409 for (i = 0; i < ARRAY_SIZE(mgcp_requests); i++) {
410 if (strcmp(mgcp_requests[i].name, rq.name) == 0) {
411 /* Check if the request requires and endpoint, if yes, check if we have it, otherwise don't
412 * execute the request handler. */
413 if (mgcp_requests[i].require_endp && !rq.endp) {
414 LOGP(DLMGCP, LOGL_ERROR,
415 "%s: the request handler \"%s\" requires an endpoint resource for \"%s\", which is not available -- abort\n",
416 rq.name, mgcp_requests[i].debug_name, pdata.epname);
417 return create_err_response(NULL, -rq.mgcp_cause, rq.name, pdata.trans);
418 }
419
420 /* Execute request handler */
421 if (rq.endp)
422 LOGP(DLMGCP, LOGL_INFO,
423 "%s: executing request handler \"%s\" for endpoint resource \"%s\"\n", rq.name,
424 mgcp_requests[i].debug_name, rq.endp->name);
425 else
426 LOGP(DLMGCP, LOGL_INFO,
427 "%s: executing request handler \"%s\" for trunk resource of endpoint \"%s\"\n",
428 rq.name, mgcp_requests[i].debug_name, pdata.epname);
429 resp = mgcp_requests[i].handle_request(&rq);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200430 handled = 1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200431 break;
432 }
433 }
434
Philipp Maier8dc35972021-07-14 11:20:16 +0200435 /* Check if the MGCP request was handled and increment rate counters accordingly. */
Alexander Chemeris63866002020-05-05 17:18:40 +0300436 if (handled) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200437 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_HANDLED));
Alexander Chemeris63866002020-05-05 17:18:40 +0300438 } else {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200439 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_UNHANDLED));
Philipp Maier8dc35972021-07-14 11:20:16 +0200440 LOGP(DLMGCP, LOGL_ERROR, "MSG with type: '%.4s' not handled\n", &msg->l2h[0]);
Alexander Chemeris63866002020-05-05 17:18:40 +0300441 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200442
443 return resp;
444}
445
Philipp Maier87bd9be2017-08-22 16:35:41 +0200446/* AUEP command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +0200447static struct msgb *handle_audit_endpoint(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200448{
Philipp Maier8dc35972021-07-14 11:20:16 +0200449 LOGPENDP(rq->endp, DLMGCP, LOGL_NOTICE, "AUEP: auditing endpoint ...\n");
450 return create_ok_response(rq->endp, 200, "AUEP", rq->pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200451}
452
Harald Welte1d1b98f2017-12-25 10:03:40 +0100453/* Try to find a free port by attempting to bind on it. Also handle the
Philipp Maier87bd9be2017-08-22 16:35:41 +0200454 * counter that points on the next free port. Since we have a pointer
Philipp Maierb38fb892018-05-22 13:52:21 +0200455 * to the next free port, binding should in work on the first attempt in
Pau Espin Pedrolfc806732019-04-23 00:18:43 +0200456 * general. In case of failure the next port is tried until the whole port
457 * range is tried once. */
Philipp Maier87bd9be2017-08-22 16:35:41 +0200458static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200459{
460 int i;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200461 struct mgcp_port_range *range;
Philipp Maierb38fb892018-05-22 13:52:21 +0200462 unsigned int tries;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200463
Philipp Maier87bd9be2017-08-22 16:35:41 +0200464 OSMO_ASSERT(conn);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200465
466 range = &endp->cfg->net_ports;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200467
468 /* attempt to find a port */
Philipp Maierb38fb892018-05-22 13:52:21 +0200469 tries = (range->range_end - range->range_start) / 2;
470 for (i = 0; i < tries; ++i) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200471 int rc;
472
473 if (range->last_port >= range->range_end)
474 range->last_port = range->range_start;
475
Philipp Maier87bd9be2017-08-22 16:35:41 +0200476 rc = mgcp_bind_net_rtp_port(endp, range->last_port, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200477
478 range->last_port += 2;
479 if (rc == 0) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200480 return 0;
481 }
482
483 }
484
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200485 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
486 "Allocating a RTP/RTCP port failed %u times.\n",
487 tries);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200488 return -1;
489}
490
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200491/*! Helper function for check_local_cx_options() to get a pointer of the next
492 * lco option identifier
493 * \param[in] lco string
494 * \returns pointer to the beginning of the LCO identifier, NULL on failure */
495char *get_lco_identifier(const char *options)
496{
497 char *ptr;
498 unsigned int count = 0;
499
500 /* Jump to the end of the lco identifier */
501 ptr = strstr(options, ":");
502 if (!ptr)
503 return NULL;
504
505 /* Walk backwards until the pointer points to the beginning of the
506 * lco identifier. We know that we stand at the beginning when we
507 * are either at the beginning of the memory or see a space or
508 * comma. (this is tolerant, it will accept a:10, b:11 as well as
509 * a:10,b:11) */
510 while (1) {
511 /* Endless loop protection */
512 if (count > 10000)
513 return NULL;
514 else if (ptr < options || *ptr == ' ' || *ptr == ',') {
515 ptr++;
516 break;
517 }
518 ptr--;
519 count++;
520 }
521
522 /* Check if we got any result */
523 if (*ptr == ':')
524 return NULL;
525
526 return ptr;
527}
528
529/*! Check the LCO option. This function checks for multiple appearence of LCO
530 * options, which is illegal
531 * \param[in] ctx talloc context
532 * \param[in] lco string
533 * \returns 0 on success, -1 on failure */
534int check_local_cx_options(void *ctx, const char *options)
535{
536 int i;
537 char *options_copy;
538 char *lco_identifier;
539 char *lco_identifier_end;
540 char *next_lco_identifier;
541
542 char **lco_seen;
543 unsigned int lco_seen_n = 0;
544
545 if (!options)
546 return -1;
547
548 lco_seen =
549 (char **)talloc_zero_size(ctx, strlen(options) * sizeof(char *));
550 options_copy = talloc_strdup(ctx, options);
551 lco_identifier = options_copy;
552
553 do {
554 /* Move the lco_identifier pointer to the beginning of the
555 * current lco option identifier */
556 lco_identifier = get_lco_identifier(lco_identifier);
557 if (!lco_identifier)
558 goto error;
559
560 /* Look ahead to the next LCO option early, since we
561 * will parse destructively */
562 next_lco_identifier = strstr(lco_identifier + 1, ",");
563
564 /* Pinch off the end of the lco field identifier name
565 * and see if we still got something, also check if
566 * there is some value after the colon. */
567 lco_identifier_end = strstr(lco_identifier, ":");
568 if (!lco_identifier_end)
569 goto error;
570 if (*(lco_identifier_end + 1) == ' '
571 || *(lco_identifier_end + 1) == ','
572 || *(lco_identifier_end + 1) == '\0')
573 goto error;
574 *lco_identifier_end = '\0';
575 if (strlen(lco_identifier) == 0)
576 goto error;
577
578 /* Check if we have already seen the current field identifier
579 * before. If yes, we must bail, an LCO must only appear once
580 * in the LCO string */
581 for (i = 0; i < lco_seen_n; i++) {
Pau Espin Pedrol7eb6f2c2019-06-26 13:00:52 +0200582 if (strcasecmp(lco_seen[i], lco_identifier) == 0)
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200583 goto error;
584 }
585 lco_seen[lco_seen_n] = lco_identifier;
586 lco_seen_n++;
587
588 /* The first identifier must always be found at the beginnning
589 * of the LCO string */
590 if (lco_seen[0] != options_copy)
591 goto error;
592
593 /* Go to the next lco option */
594 lco_identifier = next_lco_identifier;
595 } while (lco_identifier);
596
597 talloc_free(lco_seen);
598 talloc_free(options_copy);
599 return 0;
600error:
601 talloc_free(lco_seen);
602 talloc_free(options_copy);
603 return -1;
604}
605
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200606/* Set the LCO from a string (see RFC 3435).
Harald Welte1d1b98f2017-12-25 10:03:40 +0100607 * The string is stored in the 'string' field. A NULL string is handled exactly
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200608 * like an empty string, the 'string' field is never NULL after this function
609 * has been called. */
Philipp Maiera390d0b2018-01-31 17:30:19 +0100610static int set_local_cx_options(void *ctx, struct mgcp_lco *lco,
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200611 const char *options)
612{
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200613 char *lco_id;
Philipp Maier8dbc9ed2018-10-26 14:50:25 +0200614 char codec[17];
Pau Espin Pedrol19539862020-09-21 11:43:41 +0200615 char nt[17];
Pau Espin Pedrol83fd8a52019-06-26 12:55:26 +0200616 int len;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200617
Philipp Maier604410c2018-06-06 10:02:16 +0200618 if (!options)
619 return 0;
620 if (strlen(options) == 0)
621 return 0;
622
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200623 /* Make sure the encoding of the LCO is consistant before we proceed */
624 if (check_local_cx_options(ctx, options) != 0) {
625 LOGP(DLMGCP, LOGL_ERROR,
626 "local CX options: Internal inconsistency in Local Connection Options!\n");
627 return 524;
628 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200629
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200630 talloc_free(lco->string);
631 lco->string = talloc_strdup(ctx, options);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200632
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200633 lco_id = lco->string;
634 while ((lco_id = get_lco_identifier(lco_id))) {
635 switch (tolower(lco_id[0])) {
636 case 'p':
637 if (sscanf(lco_id + 1, ":%d-%d",
638 &lco->pkt_period_min, &lco->pkt_period_max) == 1)
639 lco->pkt_period_max = lco->pkt_period_min;
640 break;
641 case 'a':
Pau Espin Pedrol1dc2dce2020-09-21 11:25:15 +0200642 /* FIXME: LCO also supports the negotiation of more than one codec.
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200643 * (e.g. a:PCMU;G726-32) But this implementation only supports a single
644 * codec only. */
645 if (sscanf(lco_id + 1, ":%16[^,]", codec) == 1) {
646 talloc_free(lco->codec);
Pau Espin Pedrol83fd8a52019-06-26 12:55:26 +0200647 /* MGCP header is case insensive, and we'll need
648 codec in uppercase when using it later: */
649 len = strlen(codec);
650 lco->codec = talloc_size(ctx, len + 1);
651 osmo_str_toupper_buf(lco->codec, len + 1, codec);
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200652 }
653 break;
Pau Espin Pedrol19539862020-09-21 11:43:41 +0200654 case 'n':
655 if (lco_id[1] == 't' && sscanf(lco_id + 2, ":%16[^,]", nt) == 1)
656 break;
657 /* else: fall throught to print notice log */
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200658 default:
659 LOGP(DLMGCP, LOGL_NOTICE,
660 "LCO: unhandled option: '%c'/%d in \"%s\"\n",
661 *lco_id, *lco_id, lco->string);
662 break;
663 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200664
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200665 lco_id = strchr(lco_id, ',');
666 if (!lco_id)
667 break;
Philipp Maier604410c2018-06-06 10:02:16 +0200668 }
Philipp Maier87bd9be2017-08-22 16:35:41 +0200669
670 LOGP(DLMGCP, LOGL_DEBUG,
671 "local CX options: lco->pkt_period_max: %i, lco->codec: %s\n",
672 lco->pkt_period_max, lco->codec);
Philipp Maiera390d0b2018-01-31 17:30:19 +0100673
674 /* Check if the packetization fits the 20ms raster */
675 if (lco->pkt_period_min % 20 && lco->pkt_period_max % 20) {
676 LOGP(DLMGCP, LOGL_ERROR,
677 "local CX options: packetization interval is not a multiple of 20ms!\n");
678 return 535;
679 }
680
681 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200682}
683
684void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
685 struct mgcp_rtp_end *rtp)
686{
Philipp Maier14b27a82020-06-02 20:15:30 +0200687 struct mgcp_trunk *trunk = endp->trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200688
Philipp Maier14b27a82020-06-02 20:15:30 +0200689 int patch_ssrc = expect_ssrc_change && trunk->force_constant_ssrc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200690
Philipp Maier14b27a82020-06-02 20:15:30 +0200691 rtp->force_aligned_timing = trunk->force_aligned_timing;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200692 rtp->force_constant_ssrc = patch_ssrc ? 1 : 0;
Philipp Maier14b27a82020-06-02 20:15:30 +0200693 rtp->rfc5993_hr_convert = trunk->rfc5993_hr_convert;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200694
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200695 LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
696 "Configuring RTP endpoint: local port %d%s%s\n",
697 ntohs(rtp->rtp_port),
698 rtp->force_aligned_timing ? ", force constant timing" : "",
699 rtp->force_constant_ssrc ? ", force constant ssrc" : "");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200700}
701
Pau Espin Pedrol8358c4b2021-07-07 12:41:38 +0200702uint32_t mgcp_rtp_packet_duration(const struct mgcp_endpoint *endp,
703 const struct mgcp_rtp_end *rtp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200704{
705 int f = 0;
706
707 /* Get the number of frames per channel and packet */
708 if (rtp->frames_per_packet)
709 f = rtp->frames_per_packet;
Philipp Maierbc0346e2018-06-07 09:52:16 +0200710 else if (rtp->packet_duration_ms && rtp->codec->frame_duration_num) {
711 int den = 1000 * rtp->codec->frame_duration_num;
712 f = (rtp->packet_duration_ms * rtp->codec->frame_duration_den +
Philipp Maier87bd9be2017-08-22 16:35:41 +0200713 den / 2)
714 / den;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200715 }
716
Philipp Maierbc0346e2018-06-07 09:52:16 +0200717 return rtp->codec->rate * f * rtp->codec->frame_duration_num /
718 rtp->codec->frame_duration_den;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200719}
720
Pau Espin Pedrol9fb8ddf2019-05-08 15:35:36 +0200721/*! Initializes osmux socket if not yet initialized. Parses Osmux CID from MGCP line.
722 * \param[in] endp Endpoint willing to initialize osmux
723 * \param[in] line Line X-Osmux from MGCP header msg to parse
724 * \returns OSMUX CID, -1 for wildcard, -2 on parse error, -3 on osmux initalize error
725 */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200726static int mgcp_osmux_setup(struct mgcp_endpoint *endp, const char *line)
727{
728 if (!endp->cfg->osmux_init) {
729 if (osmux_init(OSMUX_ROLE_BSC, endp->cfg) < 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200730 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "Cannot init OSMUX\n");
Pau Espin Pedrol9fb8ddf2019-05-08 15:35:36 +0200731 return -3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200732 }
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200733 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "OSMUX socket has been set up\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200734 }
735
736 return mgcp_parse_osmux_cid(line);
737}
738
Philipp Maierbc0346e2018-06-07 09:52:16 +0200739/* Process codec information contained in CRCX/MDCX */
740static int handle_codec_info(struct mgcp_conn_rtp *conn,
Philipp Maier8dc35972021-07-14 11:20:16 +0200741 struct mgcp_request_data *rq, int have_sdp, bool crcx)
Philipp Maierbc0346e2018-06-07 09:52:16 +0200742{
Philipp Maier8dc35972021-07-14 11:20:16 +0200743 struct mgcp_endpoint *endp = rq->endp;
Philipp Maierbc0346e2018-06-07 09:52:16 +0200744 int rc;
745 char *cmd;
746
747 if (crcx)
748 cmd = "CRCX";
749 else
750 cmd = "MDCX";
751
752 /* Collect codec information */
753 if (have_sdp) {
754 /* If we have SDP, we ignore the local connection options and
755 * use only the SDP information. */
756 mgcp_codec_reset_all(conn);
Philipp Maier8dc35972021-07-14 11:20:16 +0200757 rc = mgcp_parse_sdp_data(endp, conn, rq->pdata);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200758 if (rc != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200759 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
760 "%s: sdp not parseable\n", cmd);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200761
762 /* See also RFC 3661: Protocol error */
763 return 510;
764 }
765 } else if (endp->local_options.codec) {
766 /* When no SDP is available, we use the codec information from
767 * the local connection options (if present) */
768 mgcp_codec_reset_all(conn);
Philipp Maier228e5912019-03-05 13:56:59 +0100769 rc = mgcp_codec_add(conn, PTYPE_UNDEFINED, endp->local_options.codec, NULL);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200770 if (rc != 0)
771 goto error;
772 }
773
774 /* Make sure we always set a sane default codec */
775 if (conn->end.codecs_assigned == 0) {
776 /* When SDP and/or LCO did not supply any codec information,
777 * than it makes sense to pick a sane default: (payload-type 0,
778 * PCMU), see also: OS#2658 */
779 mgcp_codec_reset_all(conn);
Philipp Maier228e5912019-03-05 13:56:59 +0100780 rc = mgcp_codec_add(conn, 0, NULL, NULL);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200781 if (rc != 0)
782 goto error;
783 }
784
785 /* Make codec decision */
786 if (mgcp_codec_decide(conn) != 0)
787 goto error;
788
789 return 0;
790
791error:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200792 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
793 "%s: codec negotiation failure\n", cmd);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200794
795 /* See also RFC 3661: Codec negotiation failure */
796 return 534;
797}
798
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200799static bool parse_x_osmo_ign(struct mgcp_endpoint *endp, char *line)
800{
801 char *saveptr = NULL;
802
Pau Espin Pedrol6049a632020-09-21 11:03:21 +0200803 if (strncasecmp(line, MGCP_X_OSMO_IGN_HEADER, strlen(MGCP_X_OSMO_IGN_HEADER)))
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200804 return false;
805 line += strlen(MGCP_X_OSMO_IGN_HEADER);
806
807 while (1) {
808 char *token = strtok_r(line, " ", &saveptr);
809 line = NULL;
810 if (!token)
811 break;
812
Pau Espin Pedrol6e26c702019-06-26 13:09:39 +0200813 if (!strcasecmp(token, "C"))
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200814 endp->x_osmo_ign |= MGCP_X_OSMO_IGN_CALLID;
815 else
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200816 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "received unknown X-Osmo-IGN item '%s'\n", token);
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200817 }
818
819 return true;
820}
821
Philipp Maier87bd9be2017-08-22 16:35:41 +0200822/* CRCX command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +0200823static struct msgb *handle_create_con(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200824{
Philipp Maier8dc35972021-07-14 11:20:16 +0200825 struct mgcp_parse_data *pdata = rq->pdata;
826 struct mgcp_trunk *trunk = rq->trunk;
827 struct mgcp_endpoint *endp = rq->endp;
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200828 struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_crcx_ctr_group;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200829 int error_code = 400;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200830 const char *local_options = NULL;
831 const char *callid = NULL;
832 const char *mode = NULL;
833 char *line;
Pau Espin Pedrol2b896172019-04-24 13:47:23 +0200834 int have_sdp = 0, osmux_cid = -2;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200835 struct mgcp_conn_rtp *conn = NULL;
Philipp Maierffd75e42017-11-22 11:44:50 +0100836 struct mgcp_conn *_conn = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200837 char conn_name[512];
Philipp Maiera390d0b2018-01-31 17:30:19 +0100838 int rc;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200839
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200840 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "CRCX: creating new connection ...\n");
Philipp Maier246233d2020-08-18 15:15:24 +0200841
Philipp Maier8d6a1932020-06-18 12:19:31 +0200842 if (!mgcp_endp_avail(endp)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200843 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_AVAIL));
Philipp Maiera910a812020-08-18 15:13:33 +0200844 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
845 "CRCX: selected endpoint not available!\n");
Philipp Maier8dc35972021-07-14 11:20:16 +0200846 return create_err_response(NULL, 501, "CRCX", pdata->trans);
Philipp Maier8d6a1932020-06-18 12:19:31 +0200847 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200848
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200849 /* parse CallID C: and LocalParameters L: */
Philipp Maier8dc35972021-07-14 11:20:16 +0200850 for_each_line(line, pdata->save) {
Philipp Maier036612b2021-07-19 17:47:49 +0200851 if (!mgcp_check_param(endp, trunk, line))
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200852 continue;
853
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +0200854 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200855 case 'L':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200856 local_options = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200857 break;
858 case 'C':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200859 callid = (const char *)line + 3;
860 break;
861 case 'I':
Philipp Maierffd75e42017-11-22 11:44:50 +0100862 /* It is illegal to send a connection identifier
863 * together with a CRCX, the MGW will assign the
864 * connection identifier by itself on CRCX */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200865 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BAD_ACTION));
Philipp Maier8dc35972021-07-14 11:20:16 +0200866 return create_err_response(NULL, 523, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200867 break;
868 case 'M':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200869 mode = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200870 break;
871 case 'X':
Pau Espin Pedrolc1bf4692019-05-14 16:23:24 +0200872 if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200873 /* If osmux is disabled, just skip setting it up */
Philipp Maier8dc35972021-07-14 11:20:16 +0200874 if (!rq->endp->cfg->osmux)
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200875 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200876 osmux_cid = mgcp_osmux_setup(endp, line);
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200877 break;
878 }
879
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200880 if (parse_x_osmo_ign(endp, line))
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200881 break;
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200882
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200883 /* Ignore unknown X-headers */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200884 break;
885 case '\0':
886 have_sdp = 1;
887 goto mgcp_header_done;
888 default:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200889 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
890 "CRCX: unhandled option: '%c'/%d\n", *line, *line);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200891 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_UNHANDLED_PARAM));
Philipp Maier8dc35972021-07-14 11:20:16 +0200892 return create_err_response(NULL, 539, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200893 break;
894 }
895 }
896
897mgcp_header_done:
Philipp Maier87bd9be2017-08-22 16:35:41 +0200898 /* Check parameters */
899 if (!callid) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200900 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
901 "CRCX: insufficient parameters, missing callid\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200902 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_MISSING_CALLID));
Philipp Maier8dc35972021-07-14 11:20:16 +0200903 return create_err_response(endp, 516, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200904 }
905
Philipp Maier87bd9be2017-08-22 16:35:41 +0200906 if (!mode) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200907 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
908 "CRCX: insufficient parameters, missing mode\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200909 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_MODE));
Philipp Maier8dc35972021-07-14 11:20:16 +0200910 return create_err_response(endp, 517, "CRCX", pdata->trans);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200911 }
912
Philipp Maier87bd9be2017-08-22 16:35:41 +0200913 /* Check if we are able to accept the creation of another connection */
914 if (llist_count(&endp->conns) >= endp->type->max_conns) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200915 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
916 "CRCX: endpoint full, max. %i connections allowed!\n",
917 endp->type->max_conns);
Philipp Maier14b27a82020-06-02 20:15:30 +0200918 if (trunk->force_realloc) {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200919 /* There is no more room for a connection, make some
920 * room by blindly tossing the oldest of the two two
921 * connections */
922 mgcp_conn_free_oldest(endp);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200923 } else {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200924 /* There is no more room for a connection, leave
925 * everything as it is and return with an error */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200926 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_LIMIT_EXCEEDED));
Philipp Maier8dc35972021-07-14 11:20:16 +0200927 return create_err_response(endp, 540, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200928 }
929 }
930
Philipp Maier87bd9be2017-08-22 16:35:41 +0200931 /* Check if this endpoint already serves a call, if so, check if the
932 * callids match up so that we are sure that this is our call */
933 if (endp->callid && mgcp_verify_call_id(endp, callid)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200934 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
935 "CRCX: already seized by other call (%s)\n",
936 endp->callid);
Philipp Maier14b27a82020-06-02 20:15:30 +0200937 if (trunk->force_realloc)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200938 /* This is not our call, toss everything by releasing
939 * the entire endpoint. (rude!) */
Philipp Maier1355d7e2018-02-01 14:30:06 +0100940 mgcp_endp_release(endp);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200941 else {
942 /* This is not our call, leave everything as it is and
943 * return with an error. */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200944 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_UNKNOWN_CALLID));
Philipp Maier8dc35972021-07-14 11:20:16 +0200945 return create_err_response(endp, 400, "CRCX", pdata->trans);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200946 }
947 }
948
Philipp Maier889fe7f2020-07-06 17:44:12 +0200949 if (!endp->callid) {
950 /* Claim endpoint resources. This will also set the callid,
951 * creating additional connections will only be possible if
952 * the callid matches up (see above). */
953 rc = mgcp_endp_claim(endp, callid);
954 if (rc != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200955 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_CLAIM));
Philipp Maier8dc35972021-07-14 11:20:16 +0200956 return create_err_response(endp, 502, "CRCX", pdata->trans);
Philipp Maier889fe7f2020-07-06 17:44:12 +0200957 }
958 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200959
Philipp Maierffd75e42017-11-22 11:44:50 +0100960 snprintf(conn_name, sizeof(conn_name), "%s", callid);
Philipp Maier14b27a82020-06-02 20:15:30 +0200961 _conn = mgcp_conn_alloc(trunk->endpoints, endp, MGCP_CONN_TYPE_RTP, conn_name);
Philipp Maierffd75e42017-11-22 11:44:50 +0100962 if (!_conn) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200963 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
964 "CRCX: unable to allocate RTP connection\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200965 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_ALLOC_CONN));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200966 goto error2;
967
Philipp Maier87bd9be2017-08-22 16:35:41 +0200968 }
Philipp Maier889fe7f2020-07-06 17:44:12 +0200969
Philipp Maierffd75e42017-11-22 11:44:50 +0100970 conn = mgcp_conn_get_rtp(endp, _conn->id);
971 OSMO_ASSERT(conn);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200972
973 if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
974 error_code = 517;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200975 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_MODE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200976 goto error2;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200977 }
978
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200979 /* Annotate Osmux circuit ID and set it to negotiating state until this
Philipp Maier87bd9be2017-08-22 16:35:41 +0200980 * is fully set up from the dummy load. */
981 conn->osmux.state = OSMUX_STATE_DISABLED;
Pau Espin Pedrol2b896172019-04-24 13:47:23 +0200982 if (osmux_cid >= -1) { /* -1 is wilcard, alloc next avail CID */
Pau Espin Pedrol14f8a082019-05-13 13:10:06 +0200983 conn->osmux.state = OSMUX_STATE_ACTIVATING;
Pau Espin Pedrol2b896172019-04-24 13:47:23 +0200984 if (conn_osmux_allocate_cid(conn, osmux_cid) == -1) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200985 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_OSMUX));
Pau Espin Pedrol2b896172019-04-24 13:47:23 +0200986 goto error2;
987 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200988 } else if (endp->cfg->osmux == OSMUX_USAGE_ONLY) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200989 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
990 "CRCX: osmux only and no osmux offered\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200991 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_OSMUX));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200992 goto error2;
993 }
994
Philipp Maierbc0346e2018-06-07 09:52:16 +0200995 /* Set local connection options, if present */
996 if (local_options) {
Philipp Maier8dc35972021-07-14 11:20:16 +0200997 rc = set_local_cx_options(trunk->endpoints,
Philipp Maierbc0346e2018-06-07 09:52:16 +0200998 &endp->local_options, local_options);
999 if (rc != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001000 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
1001 "CRCX: inavlid local connection options!\n");
Philipp Maierbc0346e2018-06-07 09:52:16 +02001002 error_code = rc;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001003 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS));
Philipp Maierbc0346e2018-06-07 09:52:16 +02001004 goto error2;
1005 }
1006 }
1007
1008 /* Handle codec information and decide for a suitable codec */
Philipp Maier8dc35972021-07-14 11:20:16 +02001009 rc = handle_codec_info(conn, rq, have_sdp, true);
Philipp Maierbc0346e2018-06-07 09:52:16 +02001010 mgcp_codec_summary(conn);
1011 if (rc) {
1012 error_code = rc;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001013 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_CODEC_NEGOTIATION));
Philipp Maierbc0346e2018-06-07 09:52:16 +02001014 goto error2;
1015 }
1016
Philipp Maier14b27a82020-06-02 20:15:30 +02001017 conn->end.fmtp_extra = talloc_strdup(trunk->endpoints,
1018 trunk->audio_fmtp_extra);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001019
Philipp Maier8dc35972021-07-14 11:20:16 +02001020 if (pdata->cfg->force_ptime) {
1021 conn->end.packet_duration_ms = pdata->cfg->force_ptime;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001022 conn->end.force_output_ptime = 1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001023 }
1024
Philipp Maier1cb1e382017-11-02 17:16:04 +01001025 mgcp_rtp_end_config(endp, 0, &conn->end);
1026
Philipp Maierc3cc6542018-02-02 12:58:42 +01001027 /* check connection mode setting */
1028 if (conn->conn->mode != MGCP_CONN_LOOPBACK
1029 && conn->conn->mode != MGCP_CONN_RECV_ONLY
1030 && conn->end.rtp_port == 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001031 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
1032 "CRCX: selected connection mode type requires an opposite end!\n");
Philipp Maierc3cc6542018-02-02 12:58:42 +01001033 error_code = 527;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001034 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC));
Philipp Maierc3cc6542018-02-02 12:58:42 +01001035 goto error2;
1036 }
1037
Pau Espin Pedrol71d42e72020-09-03 14:20:07 +02001038 /* Find a local address for conn based on policy and initial SDP remote
1039 information, then find a free port for it */
1040 mgcp_get_local_addr(conn->end.local_addr, conn);
Philipp Maier1cb1e382017-11-02 17:16:04 +01001041 if (allocate_port(endp, conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001042 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
Philipp Maier1cb1e382017-11-02 17:16:04 +01001043 goto error2;
1044 }
1045
Philipp Maier87bd9be2017-08-22 16:35:41 +02001046 if (setup_rtp_processing(endp, conn) != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001047 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
1048 "CRCX: could not start RTP processing!\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001049 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_START_RTP));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001050 goto error2;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001051 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001052
1053 /* policy CB */
Philipp Maier8dc35972021-07-14 11:20:16 +02001054 if (pdata->cfg->policy_cb) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001055 int rc;
Philipp Maier8dc35972021-07-14 11:20:16 +02001056 rc = pdata->cfg->policy_cb(endp, MGCP_ENDP_CRCX, pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001057 switch (rc) {
1058 case MGCP_POLICY_REJECT:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001059 LOGPCONN(_conn, DLMGCP, LOGL_NOTICE,
1060 "CRCX: CRCX rejected by policy\n");
Philipp Maier1355d7e2018-02-01 14:30:06 +01001061 mgcp_endp_release(endp);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001062 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_REJECTED_BY_POLICY));
Philipp Maier8dc35972021-07-14 11:20:16 +02001063 return create_err_response(endp, 400, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001064 break;
1065 case MGCP_POLICY_DEFER:
1066 /* stop processing */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001067 return NULL;
1068 break;
1069 case MGCP_POLICY_CONT:
1070 /* just continue */
1071 break;
1072 }
1073 }
1074
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001075 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
1076 "CRCX: Creating connection: port: %u\n", conn->end.local_port);
Philipp Maier8dc35972021-07-14 11:20:16 +02001077 if (pdata->cfg->change_cb)
1078 pdata->cfg->change_cb(endp, MGCP_ENDP_CRCX);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001079
Philipp Maiere726d4f2017-11-01 10:41:34 +01001080 /* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
Philipp Maier14b27a82020-06-02 20:15:30 +02001081 OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Pau Espin Pedrolca280a12021-07-06 18:15:35 +02001082 if (conn->conn->mode & MGCP_CONN_RECV_ONLY &&
1083 mgcp_rtp_end_remote_addr_available(&conn->end) &&
1084 trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001085 send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001086
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001087 LOGPCONN(_conn, DLMGCP, LOGL_NOTICE,
1088 "CRCX: connection successfully created\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001089 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_SUCCESS));
Philipp Maier889fe7f2020-07-06 17:44:12 +02001090 mgcp_endp_update(endp);
Philipp Maier41d59202021-07-20 15:49:00 +02001091
1092 /* NOTE: Only in the virtual trunk we allow dynamic endpoint names */
1093 bool add_epname = rq->wildcarded && trunk->trunk_type == MGCP_TRUNK_VIRTUAL;
1094 return create_response_with_sdp(endp, conn, "CRCX", pdata->trans, add_epname, true);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001095error2:
Philipp Maier1355d7e2018-02-01 14:30:06 +01001096 mgcp_endp_release(endp);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001097 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1098 "CRCX: unable to create connection\n");
Philipp Maier8dc35972021-07-14 11:20:16 +02001099 return create_err_response(endp, error_code, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001100}
1101
Philipp Maier87bd9be2017-08-22 16:35:41 +02001102/* MDCX command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +02001103static struct msgb *handle_modify_con(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001104{
Philipp Maier8dc35972021-07-14 11:20:16 +02001105 struct mgcp_parse_data *pdata = rq->pdata;
1106 struct mgcp_trunk *trunk = rq->trunk;
1107 struct mgcp_endpoint *endp = rq->endp;
1108 struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_mdcx_ctr_group;
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001109 char new_local_addr[INET6_ADDRSTRLEN];
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001110 int error_code = 500;
1111 int silent = 0;
1112 int have_sdp = 0;
1113 char *line;
1114 const char *local_options = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001115 const char *mode = NULL;
1116 struct mgcp_conn_rtp *conn = NULL;
Philipp Maier01d24a32017-11-21 17:26:09 +01001117 const char *conn_id = NULL;
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001118 int osmux_cid = -2;
Philipp Maiera390d0b2018-01-31 17:30:19 +01001119 int rc;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001120
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001121 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "MDCX: modifying existing connection ...\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001122
Philipp Maier8d6a1932020-06-18 12:19:31 +02001123 if (!mgcp_endp_avail(endp)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001124 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_AVAIL));
Philipp Maiera910a812020-08-18 15:13:33 +02001125 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1126 "MDCX: selected endpoint not available!\n");
Philipp Maier8dc35972021-07-14 11:20:16 +02001127 return create_err_response(NULL, 501, "MDCX", pdata->trans);
Philipp Maier8d6a1932020-06-18 12:19:31 +02001128 }
1129
Philipp Maier5656fbf2018-02-02 14:41:58 +01001130 /* Prohibit wildcarded requests */
Philipp Maier8dc35972021-07-14 11:20:16 +02001131 if (rq->wildcarded) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001132 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1133 "MDCX: wildcarded endpoint names not supported.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001134 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_WILDCARD));
Philipp Maier8dc35972021-07-14 11:20:16 +02001135 return create_err_response(endp, 507, "MDCX", pdata->trans);
Philipp Maier5656fbf2018-02-02 14:41:58 +01001136 }
1137
Philipp Maier87bd9be2017-08-22 16:35:41 +02001138 if (llist_count(&endp->conns) <= 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001139 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1140 "MDCX: endpoint is not holding a connection.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001141 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_CONN));
Philipp Maier8dc35972021-07-14 11:20:16 +02001142 return create_err_response(endp, 400, "MDCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001143 }
1144
Philipp Maier8dc35972021-07-14 11:20:16 +02001145 for_each_line(line, pdata->save) {
Philipp Maier036612b2021-07-19 17:47:49 +02001146 if (!mgcp_check_param(endp, trunk, line))
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001147 continue;
1148
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001149 switch (toupper(line[0])) {
Philipp Maier87bd9be2017-08-22 16:35:41 +02001150 case 'C':
Harald Weltee35eeae2017-12-28 13:47:37 +01001151 if (mgcp_verify_call_id(endp, line + 3) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001152 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CALLID));
Harald Weltee35eeae2017-12-28 13:47:37 +01001153 error_code = 516;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001154 goto error3;
Harald Weltee35eeae2017-12-28 13:47:37 +01001155 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001156 break;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001157 case 'I':
Philipp Maier01d24a32017-11-21 17:26:09 +01001158 conn_id = (const char *)line + 3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001159 if ((error_code = mgcp_verify_ci(endp, conn_id))) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001160 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CONNID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001161 goto error3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001162 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001163 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001164 case 'L':
Philipp Maier87bd9be2017-08-22 16:35:41 +02001165 local_options = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001166 break;
1167 case 'M':
Philipp Maier87bd9be2017-08-22 16:35:41 +02001168 mode = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001169 break;
1170 case 'Z':
Pau Espin Pedrol9b508f62019-06-26 13:11:22 +02001171 silent = strcasecmp("noanswer", line + 3) == 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001172 break;
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001173 case 'X':
Pau Espin Pedrolc1bf4692019-05-14 16:23:24 +02001174 if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001175 /* If osmux is disabled, just skip setting it up */
Philipp Maier8dc35972021-07-14 11:20:16 +02001176 if (!endp->cfg->osmux)
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001177 break;
1178 osmux_cid = mgcp_osmux_setup(endp, line);
1179 break;
1180 }
1181 /* Ignore unknown X-headers */
1182 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001183 case '\0':
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001184 have_sdp = 1;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001185 goto mgcp_header_done;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001186 break;
1187 default:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001188 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1189 "MDCX: Unhandled MGCP option: '%c'/%d\n",
1190 line[0], line[0]);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001191 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_UNHANDLED_PARAM));
Philipp Maier8dc35972021-07-14 11:20:16 +02001192 return create_err_response(NULL, 539, "MDCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001193 break;
1194 }
1195 }
1196
Philipp Maier87bd9be2017-08-22 16:35:41 +02001197mgcp_header_done:
Philipp Maier01d24a32017-11-21 17:26:09 +01001198 if (!conn_id) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001199 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1200 "MDCX: insufficient parameters, missing ci (connectionIdentifier)\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001201 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_CONNID));
Philipp Maier8dc35972021-07-14 11:20:16 +02001202 return create_err_response(endp, 515, "MDCX", pdata->trans);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001203 }
1204
1205 conn = mgcp_conn_get_rtp(endp, conn_id);
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001206 if (!conn) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001207 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_CONN_NOT_FOUND));
Philipp Maier8dc35972021-07-14 11:20:16 +02001208 return create_err_response(endp, 400, "MDCX", pdata->trans);
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001209 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001210
Oliver Smithe36b7752019-01-22 16:31:36 +01001211 mgcp_conn_watchdog_kick(conn->conn);
1212
Philipp Maier87bd9be2017-08-22 16:35:41 +02001213 if (mode) {
1214 if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001215 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_MODE));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001216 error_code = 517;
1217 goto error3;
1218 }
1219 } else
Pau Espin Pedrol209eb9f2019-04-24 12:03:04 +02001220 conn->conn->mode = conn->conn->mode_orig;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001221
Philipp Maierbc0346e2018-06-07 09:52:16 +02001222 /* Set local connection options, if present */
1223 if (local_options) {
Philipp Maier8dc35972021-07-14 11:20:16 +02001224 rc = set_local_cx_options(trunk->endpoints,
Philipp Maierbc0346e2018-06-07 09:52:16 +02001225 &endp->local_options, local_options);
1226 if (rc != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001227 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1228 "MDCX: invalid local connection options!\n");
Philipp Maierbc0346e2018-06-07 09:52:16 +02001229 error_code = rc;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001230 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS));
Philipp Maierbc0346e2018-06-07 09:52:16 +02001231 goto error3;
1232 }
1233 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001234
Philipp Maierbc0346e2018-06-07 09:52:16 +02001235 /* Handle codec information and decide for a suitable codec */
Philipp Maier8dc35972021-07-14 11:20:16 +02001236 rc = handle_codec_info(conn, rq, have_sdp, false);
Philipp Maierbc0346e2018-06-07 09:52:16 +02001237 mgcp_codec_summary(conn);
1238 if (rc) {
Philipp Maieraf07f662018-02-02 11:34:02 +01001239 error_code = rc;
1240 goto error3;
Philipp Maiera390d0b2018-01-31 17:30:19 +01001241 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001242
Philipp Maierc3cc6542018-02-02 12:58:42 +01001243 /* check connection mode setting */
1244 if (conn->conn->mode != MGCP_CONN_LOOPBACK
1245 && conn->conn->mode != MGCP_CONN_RECV_ONLY
Pau Espin Pedrold6769ea2021-07-06 19:44:27 +02001246 && !mgcp_rtp_end_remote_addr_available(&conn->end)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001247 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1248 "MDCX: selected connection mode type requires an opposite end!\n");
Philipp Maierc3cc6542018-02-02 12:58:42 +01001249 error_code = 527;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001250 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC));
Philipp Maierc3cc6542018-02-02 12:58:42 +01001251 goto error3;
1252 }
1253
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001254 if (mgcp_conn_rtp_is_osmux(conn)) {
1255 OSMO_ASSERT(conn->osmux.cid_allocated);
1256 if (osmux_cid < -1) {
1257 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1258 "MDCX: Failed to parse Osmux CID!\n");
1259 goto error3;
1260 } else if (osmux_cid == -1) {
1261 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1262 "MDCX: wilcard in MDCX is not supported!\n");
1263 goto error3;
1264 } else if (osmux_cid != (int) conn->osmux.cid) {
1265 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1266 "MDCX: changing already allocated CID is not supported!\n");
1267 goto error3;
1268 }
1269 /* TODO: In the future (when we have recvCID!=sendCID), we need to
1270 tell Osmux code that osmux_cid is to be used as sendCID for
1271 that conn. */
1272 }
Philipp Maierbc0346e2018-06-07 09:52:16 +02001273
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001274 /* MDCX may have provided a new remote address, which means we may need
1275 to update our announced IP addr and re-bind our local end. This can
1276 happen for instance if MGW initially provided an IPv4 during CRCX
1277 ACK, and now MDCX tells us the remote has an IPv6 address. */
1278 mgcp_get_local_addr(new_local_addr, conn);
1279 if (strcmp(new_local_addr, conn->end.local_addr)) {
1280 osmo_strlcpy(conn->end.local_addr, new_local_addr, sizeof(conn->end.local_addr));
1281 mgcp_free_rtp_port(&conn->end);
1282 if (allocate_port(endp, conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001283 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001284 goto error3;
1285 }
1286 }
1287
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001288 if (setup_rtp_processing(endp, conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001289 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_START_RTP));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001290 goto error3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001291 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001292
Philipp Maier87bd9be2017-08-22 16:35:41 +02001293
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001294 /* policy CB */
Philipp Maier8dc35972021-07-14 11:20:16 +02001295 if (pdata->cfg->policy_cb) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001296 int rc;
Philipp Maier8dc35972021-07-14 11:20:16 +02001297 rc = pdata->cfg->policy_cb(endp, MGCP_ENDP_MDCX, pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001298 switch (rc) {
1299 case MGCP_POLICY_REJECT:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001300 LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
1301 "MDCX: rejected by policy\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001302 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_REJECTED_BY_POLICY));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001303 if (silent)
1304 goto out_silent;
Philipp Maier8dc35972021-07-14 11:20:16 +02001305 return create_err_response(endp, 400, "MDCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001306 break;
1307 case MGCP_POLICY_DEFER:
1308 /* stop processing */
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001309 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
1310 "MDCX: deferred by policy\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001311 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_DEFERRED_BY_POLICY));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001312 return NULL;
1313 break;
1314 case MGCP_POLICY_CONT:
1315 /* just continue */
1316 break;
1317 }
1318 }
1319
Philipp Maier87bd9be2017-08-22 16:35:41 +02001320 mgcp_rtp_end_config(endp, 1, &conn->end);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001321
1322 /* modify */
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001323 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
1324 "MDCX: modified conn:%s\n", mgcp_conn_dump(conn->conn));
Philipp Maier8dc35972021-07-14 11:20:16 +02001325 if (pdata->cfg->change_cb)
1326 pdata->cfg->change_cb(endp, MGCP_ENDP_MDCX);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001327
Philipp Maiere726d4f2017-11-01 10:41:34 +01001328 /* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
Philipp Maier8dc35972021-07-14 11:20:16 +02001329 OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Pau Espin Pedrolca280a12021-07-06 18:15:35 +02001330 if (conn->conn->mode & MGCP_CONN_RECV_ONLY &&
1331 mgcp_rtp_end_remote_addr_available(&conn->end) &&
Philipp Maier8dc35972021-07-14 11:20:16 +02001332 trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001333 send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001334
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001335 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_SUCCESS));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001336 if (silent)
1337 goto out_silent;
1338
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001339 LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
1340 "MDCX: connection successfully modified\n");
Philipp Maier889fe7f2020-07-06 17:44:12 +02001341 mgcp_endp_update(endp);
Philipp Maier41d59202021-07-20 15:49:00 +02001342 return create_response_with_sdp(endp, conn, "MDCX", pdata->trans, false, false);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001343error3:
Philipp Maier8dc35972021-07-14 11:20:16 +02001344 return create_err_response(endp, error_code, "MDCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001345
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001346out_silent:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001347 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "MDCX: silent exit\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001348 return NULL;
1349}
1350
Philipp Maier87bd9be2017-08-22 16:35:41 +02001351/* DLCX command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +02001352static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001353{
Philipp Maier8dc35972021-07-14 11:20:16 +02001354 struct mgcp_parse_data *pdata = rq->pdata;
1355 struct mgcp_trunk *trunk = rq->trunk;
1356 struct mgcp_endpoint *endp = rq->endp;
1357 struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_dlcx_ctr_group;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001358 int error_code = 400;
1359 int silent = 0;
1360 char *line;
1361 char stats[1048];
Philipp Maier01d24a32017-11-21 17:26:09 +01001362 const char *conn_id = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001363 struct mgcp_conn_rtp *conn = NULL;
Philipp Maierf486e742021-07-19 14:56:29 +02001364 unsigned int i;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001365
Philipp Maierf486e742021-07-19 14:56:29 +02001366 /* NOTE: In this handler we can not take it for granted that the endp
1367 * pointer will be populated, however a trunk is always guaranteed. */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001368
Philipp Maierf486e742021-07-19 14:56:29 +02001369 LOGPEPTR(endp, trunk, DLMGCP, LOGL_NOTICE, "DLCX: deleting connection(s) ...\n");
1370
1371 if (endp && !mgcp_endp_avail(endp)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001372 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_AVAIL));
Philipp Maiera910a812020-08-18 15:13:33 +02001373 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1374 "DLCX: selected endpoint not available!\n");
Philipp Maier8dc35972021-07-14 11:20:16 +02001375 return create_err_response(NULL, 501, "DLCX", pdata->trans);
Philipp Maier8d6a1932020-06-18 12:19:31 +02001376 }
1377
Philipp Maierf486e742021-07-19 14:56:29 +02001378 if (endp && !rq->wildcarded && llist_empty(&endp->conns)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001379 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1380 "DLCX: endpoint is not holding a connection.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001381 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_NO_CONN));
Philipp Maier8dc35972021-07-14 11:20:16 +02001382 return create_err_response(endp, 515, "DLCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001383 }
1384
Philipp Maier8dc35972021-07-14 11:20:16 +02001385 for_each_line(line, pdata->save) {
Philipp Maier036612b2021-07-19 17:47:49 +02001386 if (!mgcp_check_param(endp, trunk, line))
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001387 continue;
1388
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001389 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001390 case 'C':
Philipp Maierf486e742021-07-19 14:56:29 +02001391 /* If we have no endpoint, but a call id in the request,
1392 then this request cannot be handled */
1393 if (!endp) {
1394 LOGPTRUNK(trunk, DLMGCP, LOGL_NOTICE,
1395 "cannot handle requests with call-id (C) without endpoint -- abort!");
1396 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));
1397 return create_err_response(NULL, 539, "DLCX", pdata->trans);
1398 }
1399
Harald Weltee35eeae2017-12-28 13:47:37 +01001400 if (mgcp_verify_call_id(endp, line + 3) != 0) {
1401 error_code = 516;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001402 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CALLID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001403 goto error3;
Harald Weltee35eeae2017-12-28 13:47:37 +01001404 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001405 break;
1406 case 'I':
Philipp Maierf486e742021-07-19 14:56:29 +02001407 /* If we have no endpoint, but a connection id in the request,
1408 then this request cannot be handled */
1409 if (!endp) {
1410 LOGPTRUNK(trunk, DLMGCP, LOGL_NOTICE,
1411 "cannot handle requests with conn-id (I) without endpoint -- abort!");
1412 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));
1413 return create_err_response(NULL, 539, "DLCX", pdata->trans);
1414 }
1415
Philipp Maier01d24a32017-11-21 17:26:09 +01001416 conn_id = (const char *)line + 3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001417 if ((error_code = mgcp_verify_ci(endp, conn_id))) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001418 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CONNID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001419 goto error3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001420 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001421 break;
1422 case 'Z':
Pau Espin Pedrol9b508f62019-06-26 13:11:22 +02001423 silent = strcasecmp("noanswer", line + 3) == 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001424 break;
1425 default:
Philipp Maierf486e742021-07-19 14:56:29 +02001426 LOGPEPTR(endp, trunk, DLMGCP, LOGL_NOTICE, "DLCX: Unhandled MGCP option: '%c'/%d\n",
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001427 line[0], line[0]);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001428 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));
Philipp Maier8dc35972021-07-14 11:20:16 +02001429 return create_err_response(NULL, 539, "DLCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001430 break;
1431 }
1432 }
1433
1434 /* policy CB */
Philipp Maier8dc35972021-07-14 11:20:16 +02001435 if (pdata->cfg->policy_cb) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001436 int rc;
Philipp Maier8dc35972021-07-14 11:20:16 +02001437 rc = pdata->cfg->policy_cb(endp, MGCP_ENDP_DLCX, pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001438 switch (rc) {
1439 case MGCP_POLICY_REJECT:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001440 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "DLCX: rejected by policy\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001441 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_REJECTED_BY_POLICY));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001442 if (silent)
1443 goto out_silent;
Philipp Maier8dc35972021-07-14 11:20:16 +02001444 return create_err_response(endp, 400, "DLCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001445 break;
1446 case MGCP_POLICY_DEFER:
1447 /* stop processing */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001448 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_DEFERRED_BY_POLICY));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001449 return NULL;
1450 break;
1451 case MGCP_POLICY_CONT:
1452 /* just continue */
1453 break;
1454 }
1455 }
1456
Philipp Maierf486e742021-07-19 14:56:29 +02001457 /* Handle wildcarded DLCX that refers to the whole trunk. This means
1458 * that we walk over all endpoints on the trunk in order to drop all
1459 * connections on the trunk. (see also RFC3435 Annex F.7) */
1460 if (rq->wildcarded) {
1461 int num_conns = 0;
1462 for (i = 0; i < trunk->number_endpoints; i++) {
1463 num_conns += llist_count(&trunk->endpoints[i]->conns);
1464 mgcp_endp_release(trunk->endpoints[i]);
1465 }
1466 rate_ctr_add(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS), num_conns);
1467 return create_ok_response(NULL, 200, "DLCX", pdata->trans);
1468 }
1469
Philipp Maierce187052021-07-23 10:58:19 +02001470 /* The logic does not permit to go past this point without having the
1471 * the endp pointer populated. */
1472 OSMO_ASSERT(endp);
1473
Philipp Maierf4c0e372017-10-11 16:06:45 +02001474 /* When no connection id is supplied, we will interpret this as a
Philipp Maierf486e742021-07-19 14:56:29 +02001475 * wildcarded DLCX that refers to the selected endpoint. This means
1476 * that we drop all connections on that specific endpoint at once.
1477 * (See also RFC3435 Section F.7) */
Philipp Maier01d24a32017-11-21 17:26:09 +01001478 if (!conn_id) {
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001479 int num_conns = llist_count(&endp->conns);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001480 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1481 "DLCX: missing ci (connectionIdentifier), will remove all connections (%d total) at once\n",
1482 num_conns);
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001483
1484 if (num_conns > 0)
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001485 rate_ctr_add(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS), num_conns);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001486
Philipp Maier1355d7e2018-02-01 14:30:06 +01001487 mgcp_endp_release(endp);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001488
1489 /* Note: In this case we do not return any statistics,
1490 * as we assume that the client is not interested in
1491 * this case. */
Philipp Maier8dc35972021-07-14 11:20:16 +02001492 return create_ok_response(endp, 200, "DLCX", pdata->trans);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001493 }
1494
Philipp Maierf4c0e372017-10-11 16:06:45 +02001495 /* Find the connection */
Philipp Maier87bd9be2017-08-22 16:35:41 +02001496 conn = mgcp_conn_get_rtp(endp, conn_id);
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001497 if (!conn) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001498 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CONNID));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001499 goto error3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001500 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001501 /* save the statistics of the current connection */
1502 mgcp_format_stats(stats, sizeof(stats), conn->conn);
1503
1504 /* delete connection */
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001505 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG, "DLCX: deleting conn:%s\n",
1506 mgcp_conn_dump(conn->conn));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001507 mgcp_conn_free(endp, conn_id);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001508 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1509 "DLCX: connection successfully deleted\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +02001510
1511 /* When all connections are closed, the endpoint will be released
1512 * in order to be ready to be used by another call. */
1513 if (llist_count(&endp->conns) <= 0) {
Philipp Maier1355d7e2018-02-01 14:30:06 +01001514 mgcp_endp_release(endp);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001515 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: endpoint released\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +02001516 }
1517
Philipp Maier8dc35972021-07-14 11:20:16 +02001518 if (pdata->cfg->change_cb)
1519 pdata->cfg->change_cb(endp, MGCP_ENDP_DLCX);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001520
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001521 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001522 if (silent)
1523 goto out_silent;
Philipp Maier8dc35972021-07-14 11:20:16 +02001524 return create_ok_resp_with_param(endp, 250, "DLCX", pdata->trans, stats);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001525
1526error3:
Philipp Maier8dc35972021-07-14 11:20:16 +02001527 return create_err_response(endp, error_code, "DLCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001528
1529out_silent:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001530 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: silent exit\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001531 return NULL;
1532}
1533
Philipp Maier87bd9be2017-08-22 16:35:41 +02001534/* RSIP command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +02001535static struct msgb *handle_rsip(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001536{
Philipp Maier87bd9be2017-08-22 16:35:41 +02001537 /* TODO: Also implement the resetting of a specific endpoint
1538 * to make mgcp_send_reset_ep() work. Currently this will call
1539 * mgcp_rsip_cb() in mgw_main.c, which sets reset_endpoints=1
1540 * to make read_call_agent() reset all endpoints when called
1541 * next time. In order to selectively reset endpoints some
1542 * mechanism to distinguish which endpoint shall be resetted
1543 * is needed */
1544
1545 LOGP(DLMGCP, LOGL_NOTICE, "RSIP: resetting all endpoints ...\n");
1546
Philipp Maier8dc35972021-07-14 11:20:16 +02001547 if (rq->pdata->cfg->reset_cb)
1548 rq->pdata->cfg->reset_cb(rq->endp->trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001549 return NULL;
1550}
1551
1552static char extract_tone(const char *line)
1553{
1554 const char *str = strstr(line, "D/");
1555 if (!str)
1556 return CHAR_MAX;
1557
1558 return str[2];
1559}
1560
Philipp Maier87bd9be2017-08-22 16:35:41 +02001561/* This can request like DTMF detection and forward, fax detection... it
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001562 * can also request when the notification should be send and such. We don't
Philipp Maier87bd9be2017-08-22 16:35:41 +02001563 * do this right now. */
Philipp Maier8dc35972021-07-14 11:20:16 +02001564static struct msgb *handle_noti_req(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001565{
1566 int res = 0;
1567 char *line;
1568 char tone = CHAR_MAX;
1569
Philipp Maier87bd9be2017-08-22 16:35:41 +02001570 LOGP(DLMGCP, LOGL_NOTICE, "RQNT: processing request for notification ...\n");
1571
Philipp Maier8dc35972021-07-14 11:20:16 +02001572 for_each_line(line, rq->pdata->save) {
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001573 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001574 case 'S':
1575 tone = extract_tone(line);
1576 break;
1577 }
1578 }
1579
1580 /* we didn't see a signal request with a tone */
1581 if (tone == CHAR_MAX)
Philipp Maier8dc35972021-07-14 11:20:16 +02001582 return create_ok_response(rq->endp, 200, "RQNT", rq->pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001583
Philipp Maier8dc35972021-07-14 11:20:16 +02001584 if (rq->pdata->cfg->rqnt_cb)
1585 res = rq->pdata->cfg->rqnt_cb(rq->endp, tone);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001586
1587 return res == 0 ?
Philipp Maier8dc35972021-07-14 11:20:16 +02001588 create_ok_response(rq->endp, 200, "RQNT", rq->pdata->trans) :
1589 create_err_response(rq->endp, res, "RQNT", rq->pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001590}
1591
Philipp Maier87bd9be2017-08-22 16:35:41 +02001592/* Connection keepalive timer, will take care that dummy packets are send
Harald Welte1d1b98f2017-12-25 10:03:40 +01001593 * regularly, so that NAT connections stay open */
Philipp Maier14b27a82020-06-02 20:15:30 +02001594static void mgcp_keepalive_timer_cb(void *_trunk)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001595{
Philipp Maier14b27a82020-06-02 20:15:30 +02001596 struct mgcp_trunk *trunk = _trunk;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001597 struct mgcp_conn *conn;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001598 int i;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001599
Philipp Maiere726d4f2017-11-01 10:41:34 +01001600 LOGP(DLMGCP, LOGL_DEBUG, "triggered trunk %d keepalive timer\n",
Philipp Maier14b27a82020-06-02 20:15:30 +02001601 trunk->trunk_nr);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001602
Philipp Maiere726d4f2017-11-01 10:41:34 +01001603 /* Do not accept invalid configuration values
1604 * valid is MGCP_KEEPALIVE_NEVER, MGCP_KEEPALIVE_ONCE and
1605 * values greater 0 */
Philipp Maier14b27a82020-06-02 20:15:30 +02001606 OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Philipp Maiere726d4f2017-11-01 10:41:34 +01001607
1608 /* The dummy packet functionality has been disabled, we will exit
1609 * immediately, no further timer is scheduled, which means we will no
1610 * longer send dummy packets even when we did before */
Philipp Maier14b27a82020-06-02 20:15:30 +02001611 if (trunk->keepalive_interval == MGCP_KEEPALIVE_NEVER)
Philipp Maiere726d4f2017-11-01 10:41:34 +01001612 return;
1613
1614 /* In cases where only one dummy packet is sent, we do not need
1615 * the timer since the functions that handle the CRCX and MDCX are
1616 * triggering the sending of the dummy packet. So we behave like in
1617 * the MGCP_KEEPALIVE_NEVER case */
Philipp Maier14b27a82020-06-02 20:15:30 +02001618 if (trunk->keepalive_interval == MGCP_KEEPALIVE_ONCE)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001619 return;
1620
Philipp Maier87bd9be2017-08-22 16:35:41 +02001621 /* Send walk over all endpoints and send out dummy packets through
1622 * every connection present on each endpoint */
Philipp Maier4131a652021-07-07 14:04:34 +02001623 for (i = 0; i < trunk->number_endpoints; ++i) {
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001624 struct mgcp_endpoint *endp = trunk->endpoints[i];
Philipp Maier87bd9be2017-08-22 16:35:41 +02001625 llist_for_each_entry(conn, &endp->conns, entry) {
Pau Espin Pedrolca280a12021-07-06 18:15:35 +02001626 if (conn->type == MGCP_CONN_TYPE_RTP &&
1627 conn->mode == MGCP_CONN_RECV_ONLY &&
1628 mgcp_rtp_end_remote_addr_available(&conn->u.rtp.end))
Philipp Maier87bd9be2017-08-22 16:35:41 +02001629 send_dummy(endp, &conn->u.rtp);
1630 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001631 }
1632
Philipp Maiere726d4f2017-11-01 10:41:34 +01001633 /* Schedule the keepalive timer for the next round */
1634 LOGP(DLMGCP, LOGL_DEBUG, "rescheduling trunk %d keepalive timer\n",
Philipp Maier14b27a82020-06-02 20:15:30 +02001635 trunk->trunk_nr);
1636 osmo_timer_schedule(&trunk->keepalive_timer, trunk->keepalive_interval,
Philipp Maier87bd9be2017-08-22 16:35:41 +02001637 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001638}
1639
Philipp Maier14b27a82020-06-02 20:15:30 +02001640void mgcp_trunk_set_keepalive(struct mgcp_trunk *trunk, int interval)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001641{
Philipp Maier14b27a82020-06-02 20:15:30 +02001642 trunk->keepalive_interval = interval;
1643 osmo_timer_setup(&trunk->keepalive_timer, mgcp_keepalive_timer_cb, trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001644
1645 if (interval <= 0)
Philipp Maier14b27a82020-06-02 20:15:30 +02001646 osmo_timer_del(&trunk->keepalive_timer);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001647 else
Philipp Maier14b27a82020-06-02 20:15:30 +02001648 osmo_timer_schedule(&trunk->keepalive_timer,
1649 trunk->keepalive_interval, 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001650}
1651
Philipp Maier87bd9be2017-08-22 16:35:41 +02001652/*! allocate configuration with default values.
1653 * (called once at startup by main function) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001654struct mgcp_config *mgcp_config_alloc(void)
1655{
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001656 /* FIXME: This is unrelated to the protocol, put this in some
1657 * appropiate place! */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001658 struct mgcp_config *cfg;
1659
1660 cfg = talloc_zero(NULL, struct mgcp_config);
1661 if (!cfg) {
1662 LOGP(DLMGCP, LOGL_FATAL, "Failed to allocate config.\n");
1663 return NULL;
1664 }
1665
Philipp Maier12943ea2018-01-17 15:40:25 +01001666 osmo_strlcpy(cfg->domain, "mgw", sizeof(cfg->domain));
1667
Philipp Maier87bd9be2017-08-22 16:35:41 +02001668 cfg->net_ports.range_start = RTP_PORT_DEFAULT_RANGE_START;
1669 cfg->net_ports.range_end = RTP_PORT_DEFAULT_RANGE_END;
1670 cfg->net_ports.last_port = cfg->net_ports.range_start;
1671
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001672 cfg->source_port = 2427;
1673 cfg->source_addr = talloc_strdup(cfg, "0.0.0.0");
1674 cfg->osmux_addr = talloc_strdup(cfg, "0.0.0.0");
1675
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001676 cfg->rtp_processing_cb = &mgcp_rtp_processing_default;
1677 cfg->setup_rtp_processing_cb = &mgcp_setup_rtp_processing_default;
1678
1679 cfg->get_net_downlink_format_cb = &mgcp_get_net_downlink_format_default;
1680
Philipp Maierd19de2e2020-06-03 13:55:33 +02001681 INIT_LLIST_HEAD(&cfg->trunks);
1682
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001683 /* Allocate virtual trunk */
Philipp Maierd19de2e2020-06-03 13:55:33 +02001684 if (!mgcp_trunk_alloc(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID)) {
Harald Welte3ac604e2019-05-08 14:07:41 +02001685 talloc_free(cfg);
1686 return NULL;
1687 }
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001688
Philipp Maiera065e632021-07-09 13:22:42 +02001689 mgcp_ratectr_global_alloc(cfg);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001690
1691 return cfg;
1692}
1693
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001694static int send_agent(struct mgcp_config *cfg, const char *buf, int len)
1695{
1696 return write(cfg->gw_fd.bfd.fd, buf, len);
1697}
1698
Philipp Maier87bd9be2017-08-22 16:35:41 +02001699/*! Reset all endpoints by sending RSIP message to self.
1700 * (called by VTY)
1701 * \param[in] endp trunk endpoint
1702 * \param[in] endpoint number
1703 * \returns 0 on success, -1 on error */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001704int mgcp_send_reset_all(struct mgcp_config *cfg)
1705{
Philipp Maier12943ea2018-01-17 15:40:25 +01001706 char buf[MGCP_ENDPOINT_MAXLEN + 128];
1707 int len;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001708 int rc;
1709
Philipp Maier12943ea2018-01-17 15:40:25 +01001710 len = snprintf(buf, sizeof(buf),
1711 "RSIP 1 *@%s MGCP 1.0\r\n", cfg->domain);
1712 if (len < 0)
1713 return -1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001714
Philipp Maier12943ea2018-01-17 15:40:25 +01001715 rc = send_agent(cfg, buf, len);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001716 if (rc <= 0)
1717 return -1;
1718
1719 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001720}
1721
Philipp Maier87bd9be2017-08-22 16:35:41 +02001722/*! Reset a single endpoint by sending RSIP message to self.
1723 * (called by VTY)
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001724 * \param[in] endp to reset
Philipp Maier87bd9be2017-08-22 16:35:41 +02001725 * \returns 0 on success, -1 on error */
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001726int mgcp_send_reset_ep(struct mgcp_endpoint *endp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001727{
Philipp Maier12943ea2018-01-17 15:40:25 +01001728 char buf[MGCP_ENDPOINT_MAXLEN + 128];
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001729 int len;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001730 int rc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001731
1732 len = snprintf(buf, sizeof(buf),
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001733 "RSIP 39 %s MGCP 1.0\r\n", endp->name);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001734 if (len < 0)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001735 return -1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001736
Philipp Maier87bd9be2017-08-22 16:35:41 +02001737 rc = send_agent(endp->cfg, buf, len);
1738 if (rc <= 0)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001739 return -1;
1740
Philipp Maier87bd9be2017-08-22 16:35:41 +02001741 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001742}