blob: b7ae7485d90abe15fd4ea7483dcc925f4017d76e [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 Maier8dc35972021-07-14 11:20:16 +020049/* Request data passed to the request handler */
50struct mgcp_request_data {
51 /* request name (e.g. "MDCX") */
52 char name[4+1];
53
54 /* parsing results from the MGCP header (trans id, endpoint name ...) */
55 struct mgcp_parse_data *pdata;
56
57 /* pointer to endpoint resource (may be NULL for wildcarded requests) */
58 struct mgcp_endpoint *endp;
59
60 /* pointer to trunk resource */
61 struct mgcp_trunk *trunk;
62
63 /* set to true when the request has been classified as wildcarded */
64 bool wildcarded;
65
66 /* contains cause code in case of problems during endp/trunk resolution */
67 int mgcp_cause;
68};
69
Philipp Maier33d97f72021-07-14 14:53:45 +020070/* Request handler specification, here we specify an array with function
71 * pointers to the various MGCP requests implemented below */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020072struct mgcp_request {
Philipp Maier33d97f72021-07-14 14:53:45 +020073 /* request name (e.g. "MDCX") */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020074 char *name;
Philipp Maier33d97f72021-07-14 14:53:45 +020075
76 /* function pointer to the request handler */
Philipp Maier8dc35972021-07-14 11:20:16 +020077 struct msgb *(*handle_request)(struct mgcp_request_data *data);
78
79 /* true if the request requires an endpoint, false if only a trunk
80 * is sufficient. (corner cases, e.g. wildcarded DLCX) */
81 bool require_endp;
Philipp Maier33d97f72021-07-14 14:53:45 +020082
83 /* a human readable name that describes the request */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020084 char *debug_name;
85};
86
Philipp Maier8dc35972021-07-14 11:20:16 +020087static struct msgb *handle_audit_endpoint(struct mgcp_request_data *data);
88static struct msgb *handle_create_con(struct mgcp_request_data *data);
89static struct msgb *handle_delete_con(struct mgcp_request_data *data);
90static struct msgb *handle_modify_con(struct mgcp_request_data *data);
91static struct msgb *handle_rsip(struct mgcp_request_data *data);
92static struct msgb *handle_noti_req(struct mgcp_request_data *data);
Philipp Maier33d97f72021-07-14 14:53:45 +020093static const struct mgcp_request mgcp_requests[] = {
94 { .name = "AUEP",
95 .handle_request = handle_audit_endpoint,
Philipp Maier8dc35972021-07-14 11:20:16 +020096 .debug_name = "AuditEndpoint",
97 .require_endp = true },
Philipp Maier33d97f72021-07-14 14:53:45 +020098 { .name = "CRCX",
99 .handle_request = handle_create_con,
Philipp Maier8dc35972021-07-14 11:20:16 +0200100 .debug_name = "CreateConnection",
101 .require_endp = true },
Philipp Maier33d97f72021-07-14 14:53:45 +0200102 { .name = "DLCX",
103 .handle_request = handle_delete_con,
Philipp Maier8dc35972021-07-14 11:20:16 +0200104 .debug_name = "DeleteConnection",
105 .require_endp = true },
Philipp Maier33d97f72021-07-14 14:53:45 +0200106 { .name = "MDCX",
107 .handle_request = handle_modify_con,
Philipp Maier8dc35972021-07-14 11:20:16 +0200108 .debug_name = "ModifiyConnection",
109 .require_endp = true },
Philipp Maier33d97f72021-07-14 14:53:45 +0200110 { .name = "RQNT",
111 .handle_request = handle_noti_req,
Philipp Maier8dc35972021-07-14 11:20:16 +0200112 .debug_name = "NotificationRequest",
113 .require_endp = true },
Philipp Maier33d97f72021-07-14 14:53:45 +0200114
115 /* SPEC extension */
116 { .name = "RSIP",
117 .handle_request = handle_rsip,
Philipp Maier8dc35972021-07-14 11:20:16 +0200118 .debug_name = "ReSetInProgress",
119 .require_endp = true },
Philipp Maier33d97f72021-07-14 14:53:45 +0200120};
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200121
Philipp Maier87bd9be2017-08-22 16:35:41 +0200122/* Initalize transcoder */
123static int setup_rtp_processing(struct mgcp_endpoint *endp,
124 struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200125{
Philipp Maier87bd9be2017-08-22 16:35:41 +0200126 struct mgcp_config *cfg = endp->cfg;
127 struct mgcp_conn_rtp *conn_src = NULL;
128 struct mgcp_conn_rtp *conn_dst = conn;
129 struct mgcp_conn *_conn;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200130
Pau Espin Pedrolfa810e82019-05-06 18:54:10 +0200131 if (conn->type != MGCP_RTP_DEFAULT && !mgcp_conn_rtp_is_osmux(conn)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200132 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
133 "RTP-setup: Endpoint is not configured as RTP default, stopping here!\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200134 return 0;
135 }
136
Philipp Maier87bd9be2017-08-22 16:35:41 +0200137 if (conn->conn->mode == MGCP_CONN_LOOPBACK) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200138 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
139 "RTP-setup: Endpoint is in loopback mode, stopping here!\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +0200140 return 0;
141 }
142
143 /* Find the "sister" connection */
144 llist_for_each_entry(_conn, &endp->conns, entry) {
145 if (_conn->id != conn->conn->id) {
146 conn_src = &_conn->u.rtp;
147 break;
148 }
149 }
150
Philipp Maieracc10352018-07-19 18:07:57 +0200151 return cfg->setup_rtp_processing_cb(endp, conn_dst, conn_src);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200152}
153
Philipp Maier87bd9be2017-08-22 16:35:41 +0200154/* Helper function to allocate some memory for responses and retransmissions */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200155static struct msgb *mgcp_msgb_alloc(void)
156{
157 struct msgb *msg;
158 msg = msgb_alloc_headroom(4096, 128, "MGCP msg");
159 if (!msg)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200160 LOGP(DLMGCP, LOGL_ERROR, "Failed to msgb for MGCP data.\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200161
162 return msg;
163}
164
Philipp Maier87bd9be2017-08-22 16:35:41 +0200165/* Helper function for do_retransmission() and create_resp() */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200166static struct msgb *do_retransmission(const struct mgcp_endpoint *endp)
167{
168 struct msgb *msg = mgcp_msgb_alloc();
169 if (!msg)
170 return NULL;
171
172 msg->l2h = msgb_put(msg, strlen(endp->last_response));
173 memcpy(msg->l2h, endp->last_response, msgb_l2len(msg));
Philipp Maier87bd9be2017-08-22 16:35:41 +0200174 mgcp_disp_msg(msg->l2h, msgb_l2len(msg), "Retransmitted response");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200175 return msg;
176}
177
178static struct msgb *create_resp(struct mgcp_endpoint *endp, int code,
179 const char *txt, const char *msg,
180 const char *trans, const char *param,
181 const char *sdp)
182{
183 int len;
184 struct msgb *res;
185
186 res = mgcp_msgb_alloc();
187 if (!res)
188 return NULL;
189
Philipp Maier87bd9be2017-08-22 16:35:41 +0200190 len = snprintf((char *)res->data, 2048, "%d %s%s%s\r\n%s",
191 code, trans, txt, param ? param : "", sdp ? sdp : "");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200192 if (len < 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200193 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "Failed to sprintf MGCP response.\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200194 msgb_free(res);
195 return NULL;
196 }
197
198 res->l2h = msgb_put(res, len);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200199 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "Generated response: code=%d\n", code);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200200 mgcp_disp_msg(res->l2h, msgb_l2len(res), "Generated response");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200201
202 /*
203 * Remember the last transmission per endpoint.
204 */
205 if (endp) {
Philipp Maier14b27a82020-06-02 20:15:30 +0200206 struct mgcp_trunk *trunk = endp->trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200207 talloc_free(endp->last_response);
208 talloc_free(endp->last_trans);
Philipp Maier14b27a82020-06-02 20:15:30 +0200209 endp->last_trans = talloc_strdup(trunk->endpoints, trans);
210 endp->last_response = talloc_strndup(trunk->endpoints,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200211 (const char *)res->l2h,
212 msgb_l2len(res));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200213 }
214
215 return res;
216}
217
218static struct msgb *create_ok_resp_with_param(struct mgcp_endpoint *endp,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200219 int code, const char *msg,
220 const char *trans,
221 const char *param)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200222{
223 return create_resp(endp, code, " OK", msg, trans, param, NULL);
224}
225
226static struct msgb *create_ok_response(struct mgcp_endpoint *endp,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200227 int code, const char *msg,
228 const char *trans)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200229{
230 return create_ok_resp_with_param(endp, code, msg, trans, NULL);
231}
232
233static struct msgb *create_err_response(struct mgcp_endpoint *endp,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200234 int code, const char *msg,
235 const char *trans)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200236{
237 return create_resp(endp, code, " FAIL", msg, trans, NULL, NULL);
238}
239
Philipp Maier55295f72018-01-15 14:00:28 +0100240/* Add MGCP parameters to a message buffer */
241static int add_params(struct msgb *msg, const struct mgcp_endpoint *endp,
242 const struct mgcp_conn_rtp *conn)
243{
244 int rc;
245
Philipp Maier7f0966c2018-01-17 18:18:12 +0100246 /* NOTE: Only in the virtual trunk we allow dynamic endpoint names */
Philipp Maier207ab512018-02-02 14:19:26 +0100247 if (endp->wildcarded_req
Philipp Maier14b27a82020-06-02 20:15:30 +0200248 && endp->trunk->trunk_type == MGCP_TRUNK_VIRTUAL) {
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200249 rc = msgb_printf(msg, "Z: %s\r\n", endp->name);
Philipp Maier55295f72018-01-15 14:00:28 +0100250 if (rc < 0)
251 return -EINVAL;
252 }
253
Philipp Maierc3cfae22018-01-22 12:03:03 +0100254 rc = msgb_printf(msg, "I: %s\r\n", conn->conn->id);
Philipp Maier55295f72018-01-15 14:00:28 +0100255 if (rc < 0)
256 return -EINVAL;
257
258 return 0;
259}
260
Philipp Maier87bd9be2017-08-22 16:35:41 +0200261/* Format MGCP response string (with SDP attached) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200262static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200263 struct mgcp_conn_rtp *conn,
264 const char *msg,
Philipp Maier55295f72018-01-15 14:00:28 +0100265 const char *trans_id,
266 bool add_conn_params)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200267{
Pau Espin Pedrol71d42e72020-09-03 14:20:07 +0200268 /* cfg->local_ip allows overwritting the announced IP address with
269 * regards to the one we actually bind to. Useful in behind-NAT
270 * scenarios.
271 * TODO: we may want to define another local_ip_osmux var to
272 * us for OSMUX connections. Perhaps adding a new internal API to get it
273 * based on conn type.
274 */
275 const char *addr = endp->cfg->local_ip ? : conn->end.local_addr;
Philipp Maier8970c492017-10-11 13:33:42 +0200276 struct msgb *sdp;
277 int rc;
278 struct msgb *result;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200279
Philipp Maier8970c492017-10-11 13:33:42 +0200280 sdp = msgb_alloc_headroom(4096, 128, "sdp record");
281 if (!sdp)
282 return NULL;
283
Philipp Maier55295f72018-01-15 14:00:28 +0100284 /* Attach optional connection parameters */
285 if (add_conn_params) {
286 rc = add_params(sdp, endp, conn);
287 if (rc < 0)
288 goto error;
289 }
290
Philipp Maier3cbfb8a2018-01-22 11:39:59 +0100291 /* Attach optional OSMUX parameters */
Pau Espin Pedrolc63f15a2019-05-10 16:52:08 +0200292 if (mgcp_conn_rtp_is_osmux(conn)) {
Pau Espin Pedrol5e8d7992019-04-24 19:56:43 +0200293 rc = msgb_printf(sdp, "X-Osmux: %u\r\n", conn->osmux.cid);
Philipp Maier3cbfb8a2018-01-22 11:39:59 +0100294 if (rc < 0)
295 goto error;
296 }
297
298 /* Attach line break to separate the parameters from the SDP block */
Philipp Maierc3cfae22018-01-22 12:03:03 +0100299 rc = msgb_printf(sdp, "\r\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200300
Philipp Maier8970c492017-10-11 13:33:42 +0200301 rc = mgcp_write_response_sdp(endp, conn, sdp, addr);
302 if (rc < 0)
303 goto error;
304 result = create_resp(endp, 200, " OK", msg, trans_id, NULL, (char*) sdp->data);
305 msgb_free(sdp);
306 return result;
307error:
308 msgb_free(sdp);
309 return NULL;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200310}
311
Philipp Maier87bd9be2017-08-22 16:35:41 +0200312/* Send out dummy packet to keep the connection open, if the connection is an
313 * osmux connection, send the dummy packet via OSMUX */
314static void send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200315{
Philipp Maier87bd9be2017-08-22 16:35:41 +0200316 if (conn->osmux.state != OSMUX_STATE_DISABLED)
317 osmux_send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200318 else
Philipp Maier87bd9be2017-08-22 16:35:41 +0200319 mgcp_send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200320}
321
Philipp Maier87bd9be2017-08-22 16:35:41 +0200322/* handle incoming messages:
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200323 * - this can be a command (four letters, space, transaction id)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200324 * - or a response (three numbers, space, transaction id) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200325struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
326{
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200327 struct rate_ctr_group *rate_ctrs = cfg->ratectr.mgcp_general_ctr_group;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200328 struct mgcp_parse_data pdata;
Philipp Maier8dc35972021-07-14 11:20:16 +0200329 struct mgcp_request_data rq;
Harald Weltee35eeae2017-12-28 13:47:37 +0100330 int rc, i, code, handled = 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200331 struct msgb *resp = NULL;
332 char *data;
333
Alexander Chemeris63866002020-05-05 17:18:40 +0300334 /* Count all messages, even incorect ones */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200335 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_TOTAL));
Alexander Chemeris63866002020-05-05 17:18:40 +0300336
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200337 if (msgb_l2len(msg) < 4) {
338 LOGP(DLMGCP, LOGL_ERROR, "msg too short: %d\n", msg->len);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200339 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200340 return NULL;
341 }
342
Alexander Chemeris63866002020-05-05 17:18:40 +0300343 if (mgcp_msg_terminate_nul(msg)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200344 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200345 return NULL;
Alexander Chemeris63866002020-05-05 17:18:40 +0300346 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200347
Philipp Maier87bd9be2017-08-22 16:35:41 +0200348 mgcp_disp_msg(msg->l2h, msgb_l2len(msg), "Received message");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200349
Philipp Maier87bd9be2017-08-22 16:35:41 +0200350 /* attempt to treat it as a response */
351 if (sscanf((const char *)&msg->l2h[0], "%3d %*s", &code) == 1) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200352 LOGP(DLMGCP, LOGL_DEBUG, "Response: Code: %d\n", code);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200353 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200354 return NULL;
355 }
356
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200357
Philipp Maier8dc35972021-07-14 11:20:16 +0200358 /* Parse message, extract endpoint name and transaction identifier and request name etc. */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200359 memset(&pdata, 0, sizeof(pdata));
Philipp Maier8dc35972021-07-14 11:20:16 +0200360 memset(&rq, 0, sizeof(rq));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200361 pdata.cfg = cfg;
Philipp Maier8dc35972021-07-14 11:20:16 +0200362 memcpy(rq.name, (const char *)&msg->l2h[0], sizeof(rq.name)-1);
363 msg->l3h = &msg->l2h[4];
Philipp Maier87bd9be2017-08-22 16:35:41 +0200364 data = mgcp_strline((char *)msg->l3h, &pdata.save);
Harald Weltee35eeae2017-12-28 13:47:37 +0100365 rc = mgcp_parse_header(&pdata, data);
Harald Weltee35eeae2017-12-28 13:47:37 +0100366 if (rc < 0) {
Philipp Maier8dc35972021-07-14 11:20:16 +0200367 LOGP(DLMGCP, LOGL_ERROR, "%s: failed to parse MCGP message\n", rq.name);
368 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
369 return create_err_response(NULL, -rc, rq.name, "000000");
Harald Welteabbb6b92017-12-28 13:13:50 +0100370 }
371
Philipp Maier8dc35972021-07-14 11:20:16 +0200372 /* Locate endpoint and trunk, if no endpoint can be located try at least to identify the trunk. */
373 rq.pdata = &pdata;
374 rq.wildcarded = mgcp_endp_is_wildcarded(pdata.epname);
375 rq.endp = mgcp_endp_by_name(&rc, pdata.epname, pdata.cfg);
376 rq.mgcp_cause = rc;
377 if (!rq.endp) {
378 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_NO_ENDPOINT));
379 if (rq.wildcarded) {
380 /* If we are unable to find the endpoint we still may be able to identify the trunk. Some
381 * request handlers will still be able to perform a useful action if the request refers to
382 * the whole trunk (wildcarded request). */
383 LOGP(DLMGCP, LOGL_NOTICE,
384 "%s: cannot find endpoint \"%s\", cause=%d -- trying to identify trunk...\n", rq.name,
385 pdata.epname, -rq.mgcp_cause);
386 rq.trunk = mgcp_trunk_by_name(pdata.cfg, pdata.epname);
387 if (!rq.trunk) {
388 LOGP(DLMGCP, LOGL_ERROR, "%s: failed to identify trunk for endpoint \"%s\" -- abort\n",
389 rq.name, pdata.epname);
390 return create_err_response(NULL, -rq.mgcp_cause, rq.name, pdata.trans);
391 }
392 } else {
393 /* If the endpoint name suggests that the request refers to a specific endpoint, then the
394 * request cannot be handled and we must stop early. */
395 LOGP(DLMGCP, LOGL_NOTICE,
396 "%s: cannot find endpoint \"%s\", cause=%d -- abort\n", rq.name,
397 pdata.epname, -rq.mgcp_cause);
398 return create_err_response(NULL, -rq.mgcp_cause, rq.name, pdata.trans);
399 }
400 } else {
401 rq.trunk = rq.endp->trunk;
402 rq.mgcp_cause = 0;
403
404 /* Check if we have to retransmit a response from a previous transaction */
405 if (pdata.trans && rq.endp->last_trans && strcmp(rq.endp->last_trans, pdata.trans) == 0) {
406 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_RETRANSMITTED));
407 return do_retransmission(rq.endp);
408 }
409 }
410
411 /* Find an appropriate handler for the current request and execute it */
412 for (i = 0; i < ARRAY_SIZE(mgcp_requests); i++) {
413 if (strcmp(mgcp_requests[i].name, rq.name) == 0) {
414 /* Check if the request requires and endpoint, if yes, check if we have it, otherwise don't
415 * execute the request handler. */
416 if (mgcp_requests[i].require_endp && !rq.endp) {
417 LOGP(DLMGCP, LOGL_ERROR,
418 "%s: the request handler \"%s\" requires an endpoint resource for \"%s\", which is not available -- abort\n",
419 rq.name, mgcp_requests[i].debug_name, pdata.epname);
420 return create_err_response(NULL, -rq.mgcp_cause, rq.name, pdata.trans);
421 }
422
423 /* Execute request handler */
424 if (rq.endp)
425 LOGP(DLMGCP, LOGL_INFO,
426 "%s: executing request handler \"%s\" for endpoint resource \"%s\"\n", rq.name,
427 mgcp_requests[i].debug_name, rq.endp->name);
428 else
429 LOGP(DLMGCP, LOGL_INFO,
430 "%s: executing request handler \"%s\" for trunk resource of endpoint \"%s\"\n",
431 rq.name, mgcp_requests[i].debug_name, pdata.epname);
432 resp = mgcp_requests[i].handle_request(&rq);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200433 handled = 1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200434 break;
435 }
436 }
437
Philipp Maier8dc35972021-07-14 11:20:16 +0200438 /* Check if the MGCP request was handled and increment rate counters accordingly. */
Alexander Chemeris63866002020-05-05 17:18:40 +0300439 if (handled) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200440 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_HANDLED));
Alexander Chemeris63866002020-05-05 17:18:40 +0300441 } else {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200442 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_UNHANDLED));
Philipp Maier8dc35972021-07-14 11:20:16 +0200443 LOGP(DLMGCP, LOGL_ERROR, "MSG with type: '%.4s' not handled\n", &msg->l2h[0]);
Alexander Chemeris63866002020-05-05 17:18:40 +0300444 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200445
446 return resp;
447}
448
Philipp Maier87bd9be2017-08-22 16:35:41 +0200449/* AUEP command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +0200450static struct msgb *handle_audit_endpoint(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200451{
Philipp Maier8dc35972021-07-14 11:20:16 +0200452 LOGPENDP(rq->endp, DLMGCP, LOGL_NOTICE, "AUEP: auditing endpoint ...\n");
453 return create_ok_response(rq->endp, 200, "AUEP", rq->pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200454}
455
Harald Welte1d1b98f2017-12-25 10:03:40 +0100456/* Try to find a free port by attempting to bind on it. Also handle the
Philipp Maier87bd9be2017-08-22 16:35:41 +0200457 * counter that points on the next free port. Since we have a pointer
Philipp Maierb38fb892018-05-22 13:52:21 +0200458 * to the next free port, binding should in work on the first attempt in
Pau Espin Pedrolfc806732019-04-23 00:18:43 +0200459 * general. In case of failure the next port is tried until the whole port
460 * range is tried once. */
Philipp Maier87bd9be2017-08-22 16:35:41 +0200461static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200462{
463 int i;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200464 struct mgcp_port_range *range;
Philipp Maierb38fb892018-05-22 13:52:21 +0200465 unsigned int tries;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200466
Philipp Maier87bd9be2017-08-22 16:35:41 +0200467 OSMO_ASSERT(conn);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200468
469 range = &endp->cfg->net_ports;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200470
471 /* attempt to find a port */
Philipp Maierb38fb892018-05-22 13:52:21 +0200472 tries = (range->range_end - range->range_start) / 2;
473 for (i = 0; i < tries; ++i) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200474 int rc;
475
476 if (range->last_port >= range->range_end)
477 range->last_port = range->range_start;
478
Philipp Maier87bd9be2017-08-22 16:35:41 +0200479 rc = mgcp_bind_net_rtp_port(endp, range->last_port, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200480
481 range->last_port += 2;
482 if (rc == 0) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200483 return 0;
484 }
485
486 }
487
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200488 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
489 "Allocating a RTP/RTCP port failed %u times.\n",
490 tries);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200491 return -1;
492}
493
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200494/*! Helper function for check_local_cx_options() to get a pointer of the next
495 * lco option identifier
496 * \param[in] lco string
497 * \returns pointer to the beginning of the LCO identifier, NULL on failure */
498char *get_lco_identifier(const char *options)
499{
500 char *ptr;
501 unsigned int count = 0;
502
503 /* Jump to the end of the lco identifier */
504 ptr = strstr(options, ":");
505 if (!ptr)
506 return NULL;
507
508 /* Walk backwards until the pointer points to the beginning of the
509 * lco identifier. We know that we stand at the beginning when we
510 * are either at the beginning of the memory or see a space or
511 * comma. (this is tolerant, it will accept a:10, b:11 as well as
512 * a:10,b:11) */
513 while (1) {
514 /* Endless loop protection */
515 if (count > 10000)
516 return NULL;
517 else if (ptr < options || *ptr == ' ' || *ptr == ',') {
518 ptr++;
519 break;
520 }
521 ptr--;
522 count++;
523 }
524
525 /* Check if we got any result */
526 if (*ptr == ':')
527 return NULL;
528
529 return ptr;
530}
531
532/*! Check the LCO option. This function checks for multiple appearence of LCO
533 * options, which is illegal
534 * \param[in] ctx talloc context
535 * \param[in] lco string
536 * \returns 0 on success, -1 on failure */
537int check_local_cx_options(void *ctx, const char *options)
538{
539 int i;
540 char *options_copy;
541 char *lco_identifier;
542 char *lco_identifier_end;
543 char *next_lco_identifier;
544
545 char **lco_seen;
546 unsigned int lco_seen_n = 0;
547
548 if (!options)
549 return -1;
550
551 lco_seen =
552 (char **)talloc_zero_size(ctx, strlen(options) * sizeof(char *));
553 options_copy = talloc_strdup(ctx, options);
554 lco_identifier = options_copy;
555
556 do {
557 /* Move the lco_identifier pointer to the beginning of the
558 * current lco option identifier */
559 lco_identifier = get_lco_identifier(lco_identifier);
560 if (!lco_identifier)
561 goto error;
562
563 /* Look ahead to the next LCO option early, since we
564 * will parse destructively */
565 next_lco_identifier = strstr(lco_identifier + 1, ",");
566
567 /* Pinch off the end of the lco field identifier name
568 * and see if we still got something, also check if
569 * there is some value after the colon. */
570 lco_identifier_end = strstr(lco_identifier, ":");
571 if (!lco_identifier_end)
572 goto error;
573 if (*(lco_identifier_end + 1) == ' '
574 || *(lco_identifier_end + 1) == ','
575 || *(lco_identifier_end + 1) == '\0')
576 goto error;
577 *lco_identifier_end = '\0';
578 if (strlen(lco_identifier) == 0)
579 goto error;
580
581 /* Check if we have already seen the current field identifier
582 * before. If yes, we must bail, an LCO must only appear once
583 * in the LCO string */
584 for (i = 0; i < lco_seen_n; i++) {
Pau Espin Pedrol7eb6f2c2019-06-26 13:00:52 +0200585 if (strcasecmp(lco_seen[i], lco_identifier) == 0)
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200586 goto error;
587 }
588 lco_seen[lco_seen_n] = lco_identifier;
589 lco_seen_n++;
590
591 /* The first identifier must always be found at the beginnning
592 * of the LCO string */
593 if (lco_seen[0] != options_copy)
594 goto error;
595
596 /* Go to the next lco option */
597 lco_identifier = next_lco_identifier;
598 } while (lco_identifier);
599
600 talloc_free(lco_seen);
601 talloc_free(options_copy);
602 return 0;
603error:
604 talloc_free(lco_seen);
605 talloc_free(options_copy);
606 return -1;
607}
608
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200609/* Set the LCO from a string (see RFC 3435).
Harald Welte1d1b98f2017-12-25 10:03:40 +0100610 * The string is stored in the 'string' field. A NULL string is handled exactly
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200611 * like an empty string, the 'string' field is never NULL after this function
612 * has been called. */
Philipp Maiera390d0b2018-01-31 17:30:19 +0100613static int set_local_cx_options(void *ctx, struct mgcp_lco *lco,
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200614 const char *options)
615{
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200616 char *lco_id;
Philipp Maier8dbc9ed2018-10-26 14:50:25 +0200617 char codec[17];
Pau Espin Pedrol19539862020-09-21 11:43:41 +0200618 char nt[17];
Pau Espin Pedrol83fd8a52019-06-26 12:55:26 +0200619 int len;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200620
Philipp Maier604410c2018-06-06 10:02:16 +0200621 if (!options)
622 return 0;
623 if (strlen(options) == 0)
624 return 0;
625
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200626 /* Make sure the encoding of the LCO is consistant before we proceed */
627 if (check_local_cx_options(ctx, options) != 0) {
628 LOGP(DLMGCP, LOGL_ERROR,
629 "local CX options: Internal inconsistency in Local Connection Options!\n");
630 return 524;
631 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200632
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200633 talloc_free(lco->string);
634 lco->string = talloc_strdup(ctx, options);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200635
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200636 lco_id = lco->string;
637 while ((lco_id = get_lco_identifier(lco_id))) {
638 switch (tolower(lco_id[0])) {
639 case 'p':
640 if (sscanf(lco_id + 1, ":%d-%d",
641 &lco->pkt_period_min, &lco->pkt_period_max) == 1)
642 lco->pkt_period_max = lco->pkt_period_min;
643 break;
644 case 'a':
Pau Espin Pedrol1dc2dce2020-09-21 11:25:15 +0200645 /* FIXME: LCO also supports the negotiation of more than one codec.
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200646 * (e.g. a:PCMU;G726-32) But this implementation only supports a single
647 * codec only. */
648 if (sscanf(lco_id + 1, ":%16[^,]", codec) == 1) {
649 talloc_free(lco->codec);
Pau Espin Pedrol83fd8a52019-06-26 12:55:26 +0200650 /* MGCP header is case insensive, and we'll need
651 codec in uppercase when using it later: */
652 len = strlen(codec);
653 lco->codec = talloc_size(ctx, len + 1);
654 osmo_str_toupper_buf(lco->codec, len + 1, codec);
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200655 }
656 break;
Pau Espin Pedrol19539862020-09-21 11:43:41 +0200657 case 'n':
658 if (lco_id[1] == 't' && sscanf(lco_id + 2, ":%16[^,]", nt) == 1)
659 break;
660 /* else: fall throught to print notice log */
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200661 default:
662 LOGP(DLMGCP, LOGL_NOTICE,
663 "LCO: unhandled option: '%c'/%d in \"%s\"\n",
664 *lco_id, *lco_id, lco->string);
665 break;
666 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200667
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200668 lco_id = strchr(lco_id, ',');
669 if (!lco_id)
670 break;
Philipp Maier604410c2018-06-06 10:02:16 +0200671 }
Philipp Maier87bd9be2017-08-22 16:35:41 +0200672
673 LOGP(DLMGCP, LOGL_DEBUG,
674 "local CX options: lco->pkt_period_max: %i, lco->codec: %s\n",
675 lco->pkt_period_max, lco->codec);
Philipp Maiera390d0b2018-01-31 17:30:19 +0100676
677 /* Check if the packetization fits the 20ms raster */
678 if (lco->pkt_period_min % 20 && lco->pkt_period_max % 20) {
679 LOGP(DLMGCP, LOGL_ERROR,
680 "local CX options: packetization interval is not a multiple of 20ms!\n");
681 return 535;
682 }
683
684 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200685}
686
687void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
688 struct mgcp_rtp_end *rtp)
689{
Philipp Maier14b27a82020-06-02 20:15:30 +0200690 struct mgcp_trunk *trunk = endp->trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200691
Philipp Maier14b27a82020-06-02 20:15:30 +0200692 int patch_ssrc = expect_ssrc_change && trunk->force_constant_ssrc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200693
Philipp Maier14b27a82020-06-02 20:15:30 +0200694 rtp->force_aligned_timing = trunk->force_aligned_timing;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200695 rtp->force_constant_ssrc = patch_ssrc ? 1 : 0;
Philipp Maier14b27a82020-06-02 20:15:30 +0200696 rtp->rfc5993_hr_convert = trunk->rfc5993_hr_convert;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200697
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200698 LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
699 "Configuring RTP endpoint: local port %d%s%s\n",
700 ntohs(rtp->rtp_port),
701 rtp->force_aligned_timing ? ", force constant timing" : "",
702 rtp->force_constant_ssrc ? ", force constant ssrc" : "");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200703}
704
Pau Espin Pedrol8358c4b2021-07-07 12:41:38 +0200705uint32_t mgcp_rtp_packet_duration(const struct mgcp_endpoint *endp,
706 const struct mgcp_rtp_end *rtp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200707{
708 int f = 0;
709
710 /* Get the number of frames per channel and packet */
711 if (rtp->frames_per_packet)
712 f = rtp->frames_per_packet;
Philipp Maierbc0346e2018-06-07 09:52:16 +0200713 else if (rtp->packet_duration_ms && rtp->codec->frame_duration_num) {
714 int den = 1000 * rtp->codec->frame_duration_num;
715 f = (rtp->packet_duration_ms * rtp->codec->frame_duration_den +
Philipp Maier87bd9be2017-08-22 16:35:41 +0200716 den / 2)
717 / den;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200718 }
719
Philipp Maierbc0346e2018-06-07 09:52:16 +0200720 return rtp->codec->rate * f * rtp->codec->frame_duration_num /
721 rtp->codec->frame_duration_den;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200722}
723
Pau Espin Pedrol9fb8ddf2019-05-08 15:35:36 +0200724/*! Initializes osmux socket if not yet initialized. Parses Osmux CID from MGCP line.
725 * \param[in] endp Endpoint willing to initialize osmux
726 * \param[in] line Line X-Osmux from MGCP header msg to parse
727 * \returns OSMUX CID, -1 for wildcard, -2 on parse error, -3 on osmux initalize error
728 */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200729static int mgcp_osmux_setup(struct mgcp_endpoint *endp, const char *line)
730{
731 if (!endp->cfg->osmux_init) {
732 if (osmux_init(OSMUX_ROLE_BSC, endp->cfg) < 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200733 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "Cannot init OSMUX\n");
Pau Espin Pedrol9fb8ddf2019-05-08 15:35:36 +0200734 return -3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200735 }
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200736 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "OSMUX socket has been set up\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200737 }
738
739 return mgcp_parse_osmux_cid(line);
740}
741
Philipp Maierbc0346e2018-06-07 09:52:16 +0200742/* Process codec information contained in CRCX/MDCX */
743static int handle_codec_info(struct mgcp_conn_rtp *conn,
Philipp Maier8dc35972021-07-14 11:20:16 +0200744 struct mgcp_request_data *rq, int have_sdp, bool crcx)
Philipp Maierbc0346e2018-06-07 09:52:16 +0200745{
Philipp Maier8dc35972021-07-14 11:20:16 +0200746 struct mgcp_endpoint *endp = rq->endp;
Philipp Maierbc0346e2018-06-07 09:52:16 +0200747 int rc;
748 char *cmd;
749
750 if (crcx)
751 cmd = "CRCX";
752 else
753 cmd = "MDCX";
754
755 /* Collect codec information */
756 if (have_sdp) {
757 /* If we have SDP, we ignore the local connection options and
758 * use only the SDP information. */
759 mgcp_codec_reset_all(conn);
Philipp Maier8dc35972021-07-14 11:20:16 +0200760 rc = mgcp_parse_sdp_data(endp, conn, rq->pdata);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200761 if (rc != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200762 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
763 "%s: sdp not parseable\n", cmd);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200764
765 /* See also RFC 3661: Protocol error */
766 return 510;
767 }
768 } else if (endp->local_options.codec) {
769 /* When no SDP is available, we use the codec information from
770 * the local connection options (if present) */
771 mgcp_codec_reset_all(conn);
Philipp Maier228e5912019-03-05 13:56:59 +0100772 rc = mgcp_codec_add(conn, PTYPE_UNDEFINED, endp->local_options.codec, NULL);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200773 if (rc != 0)
774 goto error;
775 }
776
777 /* Make sure we always set a sane default codec */
778 if (conn->end.codecs_assigned == 0) {
779 /* When SDP and/or LCO did not supply any codec information,
780 * than it makes sense to pick a sane default: (payload-type 0,
781 * PCMU), see also: OS#2658 */
782 mgcp_codec_reset_all(conn);
Philipp Maier228e5912019-03-05 13:56:59 +0100783 rc = mgcp_codec_add(conn, 0, NULL, NULL);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200784 if (rc != 0)
785 goto error;
786 }
787
788 /* Make codec decision */
789 if (mgcp_codec_decide(conn) != 0)
790 goto error;
791
792 return 0;
793
794error:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200795 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
796 "%s: codec negotiation failure\n", cmd);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200797
798 /* See also RFC 3661: Codec negotiation failure */
799 return 534;
800}
801
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200802static bool parse_x_osmo_ign(struct mgcp_endpoint *endp, char *line)
803{
804 char *saveptr = NULL;
805
Pau Espin Pedrol6049a632020-09-21 11:03:21 +0200806 if (strncasecmp(line, MGCP_X_OSMO_IGN_HEADER, strlen(MGCP_X_OSMO_IGN_HEADER)))
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200807 return false;
808 line += strlen(MGCP_X_OSMO_IGN_HEADER);
809
810 while (1) {
811 char *token = strtok_r(line, " ", &saveptr);
812 line = NULL;
813 if (!token)
814 break;
815
Pau Espin Pedrol6e26c702019-06-26 13:09:39 +0200816 if (!strcasecmp(token, "C"))
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200817 endp->x_osmo_ign |= MGCP_X_OSMO_IGN_CALLID;
818 else
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200819 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "received unknown X-Osmo-IGN item '%s'\n", token);
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200820 }
821
822 return true;
823}
824
Philipp Maier87bd9be2017-08-22 16:35:41 +0200825/* CRCX command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +0200826static struct msgb *handle_create_con(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200827{
Philipp Maier8dc35972021-07-14 11:20:16 +0200828 struct mgcp_parse_data *pdata = rq->pdata;
829 struct mgcp_trunk *trunk = rq->trunk;
830 struct mgcp_endpoint *endp = rq->endp;
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200831 struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_crcx_ctr_group;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200832 int error_code = 400;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200833 const char *local_options = NULL;
834 const char *callid = NULL;
835 const char *mode = NULL;
836 char *line;
Pau Espin Pedrol2b896172019-04-24 13:47:23 +0200837 int have_sdp = 0, osmux_cid = -2;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200838 struct mgcp_conn_rtp *conn = NULL;
Philipp Maierffd75e42017-11-22 11:44:50 +0100839 struct mgcp_conn *_conn = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200840 char conn_name[512];
Philipp Maiera390d0b2018-01-31 17:30:19 +0100841 int rc;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200842
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200843 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "CRCX: creating new connection ...\n");
Philipp Maier246233d2020-08-18 15:15:24 +0200844
Philipp Maier8d6a1932020-06-18 12:19:31 +0200845 if (!mgcp_endp_avail(endp)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200846 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_AVAIL));
Philipp Maiera910a812020-08-18 15:13:33 +0200847 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
848 "CRCX: selected endpoint not available!\n");
Philipp Maier8dc35972021-07-14 11:20:16 +0200849 return create_err_response(NULL, 501, "CRCX", pdata->trans);
Philipp Maier8d6a1932020-06-18 12:19:31 +0200850 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200851
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200852 /* parse CallID C: and LocalParameters L: */
Philipp Maier8dc35972021-07-14 11:20:16 +0200853 for_each_line(line, pdata->save) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200854 if (!mgcp_check_param(endp, line))
855 continue;
856
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +0200857 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200858 case 'L':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200859 local_options = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200860 break;
861 case 'C':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200862 callid = (const char *)line + 3;
863 break;
864 case 'I':
Philipp Maierffd75e42017-11-22 11:44:50 +0100865 /* It is illegal to send a connection identifier
866 * together with a CRCX, the MGW will assign the
867 * connection identifier by itself on CRCX */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200868 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BAD_ACTION));
Philipp Maier8dc35972021-07-14 11:20:16 +0200869 return create_err_response(NULL, 523, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200870 break;
871 case 'M':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200872 mode = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200873 break;
874 case 'X':
Pau Espin Pedrolc1bf4692019-05-14 16:23:24 +0200875 if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200876 /* If osmux is disabled, just skip setting it up */
Philipp Maier8dc35972021-07-14 11:20:16 +0200877 if (!rq->endp->cfg->osmux)
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200878 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200879 osmux_cid = mgcp_osmux_setup(endp, line);
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200880 break;
881 }
882
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200883 if (parse_x_osmo_ign(endp, line))
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200884 break;
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200885
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200886 /* Ignore unknown X-headers */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200887 break;
888 case '\0':
889 have_sdp = 1;
890 goto mgcp_header_done;
891 default:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200892 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
893 "CRCX: unhandled option: '%c'/%d\n", *line, *line);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200894 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_UNHANDLED_PARAM));
Philipp Maier8dc35972021-07-14 11:20:16 +0200895 return create_err_response(NULL, 539, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200896 break;
897 }
898 }
899
900mgcp_header_done:
Philipp Maier87bd9be2017-08-22 16:35:41 +0200901 /* Check parameters */
902 if (!callid) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200903 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
904 "CRCX: insufficient parameters, missing callid\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200905 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_MISSING_CALLID));
Philipp Maier8dc35972021-07-14 11:20:16 +0200906 return create_err_response(endp, 516, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200907 }
908
Philipp Maier87bd9be2017-08-22 16:35:41 +0200909 if (!mode) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200910 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
911 "CRCX: insufficient parameters, missing mode\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200912 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_MODE));
Philipp Maier8dc35972021-07-14 11:20:16 +0200913 return create_err_response(endp, 517, "CRCX", pdata->trans);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200914 }
915
Philipp Maier87bd9be2017-08-22 16:35:41 +0200916 /* Check if we are able to accept the creation of another connection */
917 if (llist_count(&endp->conns) >= endp->type->max_conns) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200918 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
919 "CRCX: endpoint full, max. %i connections allowed!\n",
920 endp->type->max_conns);
Philipp Maier14b27a82020-06-02 20:15:30 +0200921 if (trunk->force_realloc) {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200922 /* There is no more room for a connection, make some
923 * room by blindly tossing the oldest of the two two
924 * connections */
925 mgcp_conn_free_oldest(endp);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200926 } else {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200927 /* There is no more room for a connection, leave
928 * everything as it is and return with an error */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200929 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_LIMIT_EXCEEDED));
Philipp Maier8dc35972021-07-14 11:20:16 +0200930 return create_err_response(endp, 540, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200931 }
932 }
933
Philipp Maier87bd9be2017-08-22 16:35:41 +0200934 /* Check if this endpoint already serves a call, if so, check if the
935 * callids match up so that we are sure that this is our call */
936 if (endp->callid && mgcp_verify_call_id(endp, callid)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200937 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
938 "CRCX: already seized by other call (%s)\n",
939 endp->callid);
Philipp Maier14b27a82020-06-02 20:15:30 +0200940 if (trunk->force_realloc)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200941 /* This is not our call, toss everything by releasing
942 * the entire endpoint. (rude!) */
Philipp Maier1355d7e2018-02-01 14:30:06 +0100943 mgcp_endp_release(endp);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200944 else {
945 /* This is not our call, leave everything as it is and
946 * return with an error. */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200947 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_UNKNOWN_CALLID));
Philipp Maier8dc35972021-07-14 11:20:16 +0200948 return create_err_response(endp, 400, "CRCX", pdata->trans);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200949 }
950 }
951
Philipp Maier889fe7f2020-07-06 17:44:12 +0200952 if (!endp->callid) {
953 /* Claim endpoint resources. This will also set the callid,
954 * creating additional connections will only be possible if
955 * the callid matches up (see above). */
956 rc = mgcp_endp_claim(endp, callid);
957 if (rc != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200958 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_CLAIM));
Philipp Maier8dc35972021-07-14 11:20:16 +0200959 return create_err_response(endp, 502, "CRCX", pdata->trans);
Philipp Maier889fe7f2020-07-06 17:44:12 +0200960 }
961 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200962
Philipp Maierffd75e42017-11-22 11:44:50 +0100963 snprintf(conn_name, sizeof(conn_name), "%s", callid);
Philipp Maier14b27a82020-06-02 20:15:30 +0200964 _conn = mgcp_conn_alloc(trunk->endpoints, endp, MGCP_CONN_TYPE_RTP, conn_name);
Philipp Maierffd75e42017-11-22 11:44:50 +0100965 if (!_conn) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200966 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
967 "CRCX: unable to allocate RTP connection\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200968 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_ALLOC_CONN));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200969 goto error2;
970
Philipp Maier87bd9be2017-08-22 16:35:41 +0200971 }
Philipp Maier889fe7f2020-07-06 17:44:12 +0200972
Philipp Maierffd75e42017-11-22 11:44:50 +0100973 conn = mgcp_conn_get_rtp(endp, _conn->id);
974 OSMO_ASSERT(conn);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200975
976 if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
977 error_code = 517;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200978 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_MODE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200979 goto error2;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200980 }
981
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200982 /* Annotate Osmux circuit ID and set it to negotiating state until this
Philipp Maier87bd9be2017-08-22 16:35:41 +0200983 * is fully set up from the dummy load. */
984 conn->osmux.state = OSMUX_STATE_DISABLED;
Pau Espin Pedrol2b896172019-04-24 13:47:23 +0200985 if (osmux_cid >= -1) { /* -1 is wilcard, alloc next avail CID */
Pau Espin Pedrol14f8a082019-05-13 13:10:06 +0200986 conn->osmux.state = OSMUX_STATE_ACTIVATING;
Pau Espin Pedrol2b896172019-04-24 13:47:23 +0200987 if (conn_osmux_allocate_cid(conn, osmux_cid) == -1) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200988 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_OSMUX));
Pau Espin Pedrol2b896172019-04-24 13:47:23 +0200989 goto error2;
990 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200991 } else if (endp->cfg->osmux == OSMUX_USAGE_ONLY) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200992 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
993 "CRCX: osmux only and no osmux offered\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200994 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_OSMUX));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200995 goto error2;
996 }
997
Philipp Maierbc0346e2018-06-07 09:52:16 +0200998 /* Set local connection options, if present */
999 if (local_options) {
Philipp Maier8dc35972021-07-14 11:20:16 +02001000 rc = set_local_cx_options(trunk->endpoints,
Philipp Maierbc0346e2018-06-07 09:52:16 +02001001 &endp->local_options, local_options);
1002 if (rc != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001003 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
1004 "CRCX: inavlid local connection options!\n");
Philipp Maierbc0346e2018-06-07 09:52:16 +02001005 error_code = rc;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001006 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS));
Philipp Maierbc0346e2018-06-07 09:52:16 +02001007 goto error2;
1008 }
1009 }
1010
1011 /* Handle codec information and decide for a suitable codec */
Philipp Maier8dc35972021-07-14 11:20:16 +02001012 rc = handle_codec_info(conn, rq, have_sdp, true);
Philipp Maierbc0346e2018-06-07 09:52:16 +02001013 mgcp_codec_summary(conn);
1014 if (rc) {
1015 error_code = rc;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001016 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_CODEC_NEGOTIATION));
Philipp Maierbc0346e2018-06-07 09:52:16 +02001017 goto error2;
1018 }
1019
Philipp Maier14b27a82020-06-02 20:15:30 +02001020 conn->end.fmtp_extra = talloc_strdup(trunk->endpoints,
1021 trunk->audio_fmtp_extra);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001022
Philipp Maier8dc35972021-07-14 11:20:16 +02001023 if (pdata->cfg->force_ptime) {
1024 conn->end.packet_duration_ms = pdata->cfg->force_ptime;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001025 conn->end.force_output_ptime = 1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001026 }
1027
Philipp Maier1cb1e382017-11-02 17:16:04 +01001028 mgcp_rtp_end_config(endp, 0, &conn->end);
1029
Philipp Maierc3cc6542018-02-02 12:58:42 +01001030 /* check connection mode setting */
1031 if (conn->conn->mode != MGCP_CONN_LOOPBACK
1032 && conn->conn->mode != MGCP_CONN_RECV_ONLY
1033 && conn->end.rtp_port == 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001034 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
1035 "CRCX: selected connection mode type requires an opposite end!\n");
Philipp Maierc3cc6542018-02-02 12:58:42 +01001036 error_code = 527;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001037 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC));
Philipp Maierc3cc6542018-02-02 12:58:42 +01001038 goto error2;
1039 }
1040
Pau Espin Pedrol71d42e72020-09-03 14:20:07 +02001041 /* Find a local address for conn based on policy and initial SDP remote
1042 information, then find a free port for it */
1043 mgcp_get_local_addr(conn->end.local_addr, conn);
Philipp Maier1cb1e382017-11-02 17:16:04 +01001044 if (allocate_port(endp, conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001045 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
Philipp Maier1cb1e382017-11-02 17:16:04 +01001046 goto error2;
1047 }
1048
Philipp Maier87bd9be2017-08-22 16:35:41 +02001049 if (setup_rtp_processing(endp, conn) != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001050 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
1051 "CRCX: could not start RTP processing!\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001052 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_START_RTP));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001053 goto error2;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001054 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001055
1056 /* policy CB */
Philipp Maier8dc35972021-07-14 11:20:16 +02001057 if (pdata->cfg->policy_cb) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001058 int rc;
Philipp Maier8dc35972021-07-14 11:20:16 +02001059 rc = pdata->cfg->policy_cb(endp, MGCP_ENDP_CRCX, pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001060 switch (rc) {
1061 case MGCP_POLICY_REJECT:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001062 LOGPCONN(_conn, DLMGCP, LOGL_NOTICE,
1063 "CRCX: CRCX rejected by policy\n");
Philipp Maier1355d7e2018-02-01 14:30:06 +01001064 mgcp_endp_release(endp);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001065 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_REJECTED_BY_POLICY));
Philipp Maier8dc35972021-07-14 11:20:16 +02001066 return create_err_response(endp, 400, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001067 break;
1068 case MGCP_POLICY_DEFER:
1069 /* stop processing */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001070 return NULL;
1071 break;
1072 case MGCP_POLICY_CONT:
1073 /* just continue */
1074 break;
1075 }
1076 }
1077
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001078 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
1079 "CRCX: Creating connection: port: %u\n", conn->end.local_port);
Philipp Maier8dc35972021-07-14 11:20:16 +02001080 if (pdata->cfg->change_cb)
1081 pdata->cfg->change_cb(endp, MGCP_ENDP_CRCX);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001082
Philipp Maiere726d4f2017-11-01 10:41:34 +01001083 /* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
Philipp Maier14b27a82020-06-02 20:15:30 +02001084 OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Pau Espin Pedrolca280a12021-07-06 18:15:35 +02001085 if (conn->conn->mode & MGCP_CONN_RECV_ONLY &&
1086 mgcp_rtp_end_remote_addr_available(&conn->end) &&
1087 trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001088 send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001089
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001090 LOGPCONN(_conn, DLMGCP, LOGL_NOTICE,
1091 "CRCX: connection successfully created\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001092 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_SUCCESS));
Philipp Maier889fe7f2020-07-06 17:44:12 +02001093 mgcp_endp_update(endp);
Philipp Maier8dc35972021-07-14 11:20:16 +02001094 return create_response_with_sdp(endp, conn, "CRCX", pdata->trans, 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) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001146 if (!mgcp_check_param(endp, line))
1147 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 Maier8dc35972021-07-14 11:20:16 +02001342 return create_response_with_sdp(endp, conn, "MDCX", pdata->trans, 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 Maier87bd9be2017-08-22 16:35:41 +02001364
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001365 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1366 "DLCX: deleting connection ...\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001367
Philipp Maier8d6a1932020-06-18 12:19:31 +02001368 if (!mgcp_endp_avail(endp)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001369 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_AVAIL));
Philipp Maiera910a812020-08-18 15:13:33 +02001370 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1371 "DLCX: selected endpoint not available!\n");
Philipp Maier8dc35972021-07-14 11:20:16 +02001372 return create_err_response(NULL, 501, "DLCX", pdata->trans);
Philipp Maier8d6a1932020-06-18 12:19:31 +02001373 }
1374
Philipp Maier5656fbf2018-02-02 14:41:58 +01001375 /* Prohibit wildcarded requests */
1376 if (endp->wildcarded_req) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001377 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1378 "DLCX: wildcarded endpoint names not supported.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001379 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_WILDCARD));
Philipp Maier8dc35972021-07-14 11:20:16 +02001380 return create_err_response(endp, 507, "DLCX", pdata->trans);
Philipp Maier5656fbf2018-02-02 14:41:58 +01001381 }
1382
Philipp Maier87bd9be2017-08-22 16:35:41 +02001383 if (llist_count(&endp->conns) <= 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001384 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1385 "DLCX: endpoint is not holding a connection.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001386 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_NO_CONN));
Philipp Maier8dc35972021-07-14 11:20:16 +02001387 return create_err_response(endp, 515, "DLCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001388 }
1389
Philipp Maier8dc35972021-07-14 11:20:16 +02001390 for_each_line(line, pdata->save) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001391 if (!mgcp_check_param(endp, line))
1392 continue;
1393
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001394 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001395 case 'C':
Harald Weltee35eeae2017-12-28 13:47:37 +01001396 if (mgcp_verify_call_id(endp, line + 3) != 0) {
1397 error_code = 516;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001398 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CALLID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001399 goto error3;
Harald Weltee35eeae2017-12-28 13:47:37 +01001400 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001401 break;
1402 case 'I':
Philipp Maier01d24a32017-11-21 17:26:09 +01001403 conn_id = (const char *)line + 3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001404 if ((error_code = mgcp_verify_ci(endp, conn_id))) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001405 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CONNID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001406 goto error3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001407 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001408 break;
1409 case 'Z':
Pau Espin Pedrol9b508f62019-06-26 13:11:22 +02001410 silent = strcasecmp("noanswer", line + 3) == 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001411 break;
1412 default:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001413 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1414 "DLCX: Unhandled MGCP option: '%c'/%d\n",
1415 line[0], line[0]);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001416 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));
Philipp Maier8dc35972021-07-14 11:20:16 +02001417 return create_err_response(NULL, 539, "DLCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001418 break;
1419 }
1420 }
1421
1422 /* policy CB */
Philipp Maier8dc35972021-07-14 11:20:16 +02001423 if (pdata->cfg->policy_cb) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001424 int rc;
Philipp Maier8dc35972021-07-14 11:20:16 +02001425 rc = pdata->cfg->policy_cb(endp, MGCP_ENDP_DLCX, pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001426 switch (rc) {
1427 case MGCP_POLICY_REJECT:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001428 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "DLCX: rejected by policy\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001429 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_REJECTED_BY_POLICY));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001430 if (silent)
1431 goto out_silent;
Philipp Maier8dc35972021-07-14 11:20:16 +02001432 return create_err_response(endp, 400, "DLCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001433 break;
1434 case MGCP_POLICY_DEFER:
1435 /* stop processing */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001436 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_DEFERRED_BY_POLICY));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001437 return NULL;
1438 break;
1439 case MGCP_POLICY_CONT:
1440 /* just continue */
1441 break;
1442 }
1443 }
1444
Philipp Maierf4c0e372017-10-11 16:06:45 +02001445 /* When no connection id is supplied, we will interpret this as a
1446 * wildcarded DLCX and drop all connections at once. (See also
1447 * RFC3435 Section F.7) */
Philipp Maier01d24a32017-11-21 17:26:09 +01001448 if (!conn_id) {
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001449 int num_conns = llist_count(&endp->conns);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001450 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1451 "DLCX: missing ci (connectionIdentifier), will remove all connections (%d total) at once\n",
1452 num_conns);
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001453
1454 if (num_conns > 0)
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001455 rate_ctr_add(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS), num_conns);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001456
Philipp Maier1355d7e2018-02-01 14:30:06 +01001457 mgcp_endp_release(endp);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001458
1459 /* Note: In this case we do not return any statistics,
1460 * as we assume that the client is not interested in
1461 * this case. */
Philipp Maier8dc35972021-07-14 11:20:16 +02001462 return create_ok_response(endp, 200, "DLCX", pdata->trans);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001463 }
1464
Philipp Maierf4c0e372017-10-11 16:06:45 +02001465 /* Find the connection */
Philipp Maier87bd9be2017-08-22 16:35:41 +02001466 conn = mgcp_conn_get_rtp(endp, conn_id);
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001467 if (!conn) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001468 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CONNID));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001469 goto error3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001470 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001471 /* save the statistics of the current connection */
1472 mgcp_format_stats(stats, sizeof(stats), conn->conn);
1473
1474 /* delete connection */
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001475 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG, "DLCX: deleting conn:%s\n",
1476 mgcp_conn_dump(conn->conn));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001477 mgcp_conn_free(endp, conn_id);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001478 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1479 "DLCX: connection successfully deleted\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +02001480
1481 /* When all connections are closed, the endpoint will be released
1482 * in order to be ready to be used by another call. */
1483 if (llist_count(&endp->conns) <= 0) {
Philipp Maier1355d7e2018-02-01 14:30:06 +01001484 mgcp_endp_release(endp);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001485 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: endpoint released\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +02001486 }
1487
Philipp Maier8dc35972021-07-14 11:20:16 +02001488 if (pdata->cfg->change_cb)
1489 pdata->cfg->change_cb(endp, MGCP_ENDP_DLCX);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001490
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001491 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001492 if (silent)
1493 goto out_silent;
Philipp Maier8dc35972021-07-14 11:20:16 +02001494 return create_ok_resp_with_param(endp, 250, "DLCX", pdata->trans, stats);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001495
1496error3:
Philipp Maier8dc35972021-07-14 11:20:16 +02001497 return create_err_response(endp, error_code, "DLCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001498
1499out_silent:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001500 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: silent exit\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001501 return NULL;
1502}
1503
Philipp Maier87bd9be2017-08-22 16:35:41 +02001504/* RSIP command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +02001505static struct msgb *handle_rsip(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001506{
Philipp Maier87bd9be2017-08-22 16:35:41 +02001507 /* TODO: Also implement the resetting of a specific endpoint
1508 * to make mgcp_send_reset_ep() work. Currently this will call
1509 * mgcp_rsip_cb() in mgw_main.c, which sets reset_endpoints=1
1510 * to make read_call_agent() reset all endpoints when called
1511 * next time. In order to selectively reset endpoints some
1512 * mechanism to distinguish which endpoint shall be resetted
1513 * is needed */
1514
1515 LOGP(DLMGCP, LOGL_NOTICE, "RSIP: resetting all endpoints ...\n");
1516
Philipp Maier8dc35972021-07-14 11:20:16 +02001517 if (rq->pdata->cfg->reset_cb)
1518 rq->pdata->cfg->reset_cb(rq->endp->trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001519 return NULL;
1520}
1521
1522static char extract_tone(const char *line)
1523{
1524 const char *str = strstr(line, "D/");
1525 if (!str)
1526 return CHAR_MAX;
1527
1528 return str[2];
1529}
1530
Philipp Maier87bd9be2017-08-22 16:35:41 +02001531/* This can request like DTMF detection and forward, fax detection... it
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001532 * can also request when the notification should be send and such. We don't
Philipp Maier87bd9be2017-08-22 16:35:41 +02001533 * do this right now. */
Philipp Maier8dc35972021-07-14 11:20:16 +02001534static struct msgb *handle_noti_req(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001535{
1536 int res = 0;
1537 char *line;
1538 char tone = CHAR_MAX;
1539
Philipp Maier87bd9be2017-08-22 16:35:41 +02001540 LOGP(DLMGCP, LOGL_NOTICE, "RQNT: processing request for notification ...\n");
1541
Philipp Maier8dc35972021-07-14 11:20:16 +02001542 for_each_line(line, rq->pdata->save) {
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001543 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001544 case 'S':
1545 tone = extract_tone(line);
1546 break;
1547 }
1548 }
1549
1550 /* we didn't see a signal request with a tone */
1551 if (tone == CHAR_MAX)
Philipp Maier8dc35972021-07-14 11:20:16 +02001552 return create_ok_response(rq->endp, 200, "RQNT", rq->pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001553
Philipp Maier8dc35972021-07-14 11:20:16 +02001554 if (rq->pdata->cfg->rqnt_cb)
1555 res = rq->pdata->cfg->rqnt_cb(rq->endp, tone);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001556
1557 return res == 0 ?
Philipp Maier8dc35972021-07-14 11:20:16 +02001558 create_ok_response(rq->endp, 200, "RQNT", rq->pdata->trans) :
1559 create_err_response(rq->endp, res, "RQNT", rq->pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001560}
1561
Philipp Maier87bd9be2017-08-22 16:35:41 +02001562/* Connection keepalive timer, will take care that dummy packets are send
Harald Welte1d1b98f2017-12-25 10:03:40 +01001563 * regularly, so that NAT connections stay open */
Philipp Maier14b27a82020-06-02 20:15:30 +02001564static void mgcp_keepalive_timer_cb(void *_trunk)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001565{
Philipp Maier14b27a82020-06-02 20:15:30 +02001566 struct mgcp_trunk *trunk = _trunk;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001567 struct mgcp_conn *conn;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001568 int i;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001569
Philipp Maiere726d4f2017-11-01 10:41:34 +01001570 LOGP(DLMGCP, LOGL_DEBUG, "triggered trunk %d keepalive timer\n",
Philipp Maier14b27a82020-06-02 20:15:30 +02001571 trunk->trunk_nr);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001572
Philipp Maiere726d4f2017-11-01 10:41:34 +01001573 /* Do not accept invalid configuration values
1574 * valid is MGCP_KEEPALIVE_NEVER, MGCP_KEEPALIVE_ONCE and
1575 * values greater 0 */
Philipp Maier14b27a82020-06-02 20:15:30 +02001576 OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Philipp Maiere726d4f2017-11-01 10:41:34 +01001577
1578 /* The dummy packet functionality has been disabled, we will exit
1579 * immediately, no further timer is scheduled, which means we will no
1580 * longer send dummy packets even when we did before */
Philipp Maier14b27a82020-06-02 20:15:30 +02001581 if (trunk->keepalive_interval == MGCP_KEEPALIVE_NEVER)
Philipp Maiere726d4f2017-11-01 10:41:34 +01001582 return;
1583
1584 /* In cases where only one dummy packet is sent, we do not need
1585 * the timer since the functions that handle the CRCX and MDCX are
1586 * triggering the sending of the dummy packet. So we behave like in
1587 * the MGCP_KEEPALIVE_NEVER case */
Philipp Maier14b27a82020-06-02 20:15:30 +02001588 if (trunk->keepalive_interval == MGCP_KEEPALIVE_ONCE)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001589 return;
1590
Philipp Maier87bd9be2017-08-22 16:35:41 +02001591 /* Send walk over all endpoints and send out dummy packets through
1592 * every connection present on each endpoint */
Philipp Maier4131a652021-07-07 14:04:34 +02001593 for (i = 0; i < trunk->number_endpoints; ++i) {
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001594 struct mgcp_endpoint *endp = trunk->endpoints[i];
Philipp Maier87bd9be2017-08-22 16:35:41 +02001595 llist_for_each_entry(conn, &endp->conns, entry) {
Pau Espin Pedrolca280a12021-07-06 18:15:35 +02001596 if (conn->type == MGCP_CONN_TYPE_RTP &&
1597 conn->mode == MGCP_CONN_RECV_ONLY &&
1598 mgcp_rtp_end_remote_addr_available(&conn->u.rtp.end))
Philipp Maier87bd9be2017-08-22 16:35:41 +02001599 send_dummy(endp, &conn->u.rtp);
1600 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001601 }
1602
Philipp Maiere726d4f2017-11-01 10:41:34 +01001603 /* Schedule the keepalive timer for the next round */
1604 LOGP(DLMGCP, LOGL_DEBUG, "rescheduling trunk %d keepalive timer\n",
Philipp Maier14b27a82020-06-02 20:15:30 +02001605 trunk->trunk_nr);
1606 osmo_timer_schedule(&trunk->keepalive_timer, trunk->keepalive_interval,
Philipp Maier87bd9be2017-08-22 16:35:41 +02001607 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001608}
1609
Philipp Maier14b27a82020-06-02 20:15:30 +02001610void mgcp_trunk_set_keepalive(struct mgcp_trunk *trunk, int interval)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001611{
Philipp Maier14b27a82020-06-02 20:15:30 +02001612 trunk->keepalive_interval = interval;
1613 osmo_timer_setup(&trunk->keepalive_timer, mgcp_keepalive_timer_cb, trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001614
1615 if (interval <= 0)
Philipp Maier14b27a82020-06-02 20:15:30 +02001616 osmo_timer_del(&trunk->keepalive_timer);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001617 else
Philipp Maier14b27a82020-06-02 20:15:30 +02001618 osmo_timer_schedule(&trunk->keepalive_timer,
1619 trunk->keepalive_interval, 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001620}
1621
Philipp Maier87bd9be2017-08-22 16:35:41 +02001622/*! allocate configuration with default values.
1623 * (called once at startup by main function) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001624struct mgcp_config *mgcp_config_alloc(void)
1625{
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001626 /* FIXME: This is unrelated to the protocol, put this in some
1627 * appropiate place! */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001628 struct mgcp_config *cfg;
1629
1630 cfg = talloc_zero(NULL, struct mgcp_config);
1631 if (!cfg) {
1632 LOGP(DLMGCP, LOGL_FATAL, "Failed to allocate config.\n");
1633 return NULL;
1634 }
1635
Philipp Maier12943ea2018-01-17 15:40:25 +01001636 osmo_strlcpy(cfg->domain, "mgw", sizeof(cfg->domain));
1637
Philipp Maier87bd9be2017-08-22 16:35:41 +02001638 cfg->net_ports.range_start = RTP_PORT_DEFAULT_RANGE_START;
1639 cfg->net_ports.range_end = RTP_PORT_DEFAULT_RANGE_END;
1640 cfg->net_ports.last_port = cfg->net_ports.range_start;
1641
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001642 cfg->source_port = 2427;
1643 cfg->source_addr = talloc_strdup(cfg, "0.0.0.0");
1644 cfg->osmux_addr = talloc_strdup(cfg, "0.0.0.0");
1645
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001646 cfg->rtp_processing_cb = &mgcp_rtp_processing_default;
1647 cfg->setup_rtp_processing_cb = &mgcp_setup_rtp_processing_default;
1648
1649 cfg->get_net_downlink_format_cb = &mgcp_get_net_downlink_format_default;
1650
Philipp Maierd19de2e2020-06-03 13:55:33 +02001651 INIT_LLIST_HEAD(&cfg->trunks);
1652
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001653 /* Allocate virtual trunk */
Philipp Maierd19de2e2020-06-03 13:55:33 +02001654 if (!mgcp_trunk_alloc(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID)) {
Harald Welte3ac604e2019-05-08 14:07:41 +02001655 talloc_free(cfg);
1656 return NULL;
1657 }
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001658
Philipp Maiera065e632021-07-09 13:22:42 +02001659 mgcp_ratectr_global_alloc(cfg);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001660
1661 return cfg;
1662}
1663
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001664static int send_agent(struct mgcp_config *cfg, const char *buf, int len)
1665{
1666 return write(cfg->gw_fd.bfd.fd, buf, len);
1667}
1668
Philipp Maier87bd9be2017-08-22 16:35:41 +02001669/*! Reset all endpoints by sending RSIP message to self.
1670 * (called by VTY)
1671 * \param[in] endp trunk endpoint
1672 * \param[in] endpoint number
1673 * \returns 0 on success, -1 on error */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001674int mgcp_send_reset_all(struct mgcp_config *cfg)
1675{
Philipp Maier12943ea2018-01-17 15:40:25 +01001676 char buf[MGCP_ENDPOINT_MAXLEN + 128];
1677 int len;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001678 int rc;
1679
Philipp Maier12943ea2018-01-17 15:40:25 +01001680 len = snprintf(buf, sizeof(buf),
1681 "RSIP 1 *@%s MGCP 1.0\r\n", cfg->domain);
1682 if (len < 0)
1683 return -1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001684
Philipp Maier12943ea2018-01-17 15:40:25 +01001685 rc = send_agent(cfg, buf, len);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001686 if (rc <= 0)
1687 return -1;
1688
1689 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001690}
1691
Philipp Maier87bd9be2017-08-22 16:35:41 +02001692/*! Reset a single endpoint by sending RSIP message to self.
1693 * (called by VTY)
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001694 * \param[in] endp to reset
Philipp Maier87bd9be2017-08-22 16:35:41 +02001695 * \returns 0 on success, -1 on error */
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001696int mgcp_send_reset_ep(struct mgcp_endpoint *endp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001697{
Philipp Maier12943ea2018-01-17 15:40:25 +01001698 char buf[MGCP_ENDPOINT_MAXLEN + 128];
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001699 int len;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001700 int rc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001701
1702 len = snprintf(buf, sizeof(buf),
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001703 "RSIP 39 %s MGCP 1.0\r\n", endp->name);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001704 if (len < 0)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001705 return -1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001706
Philipp Maier87bd9be2017-08-22 16:35:41 +02001707 rc = send_agent(endp->cfg, buf, len);
1708 if (rc <= 0)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001709 return -1;
1710
Philipp Maier87bd9be2017-08-22 16:35:41 +02001711 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001712}