blob: 3af87d07111067a9f92096b65d502d68bbb1eb48 [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 Maier87bd9be2017-08-22 16:35:41 +0200240/* Format MGCP response string (with SDP attached) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200241static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200242 struct mgcp_conn_rtp *conn,
243 const char *msg,
Philipp Maier55295f72018-01-15 14:00:28 +0100244 const char *trans_id,
Philipp Maier41d59202021-07-20 15:49:00 +0200245 bool add_epname,
246 bool add_conn_id)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200247{
Pau Espin Pedrol71d42e72020-09-03 14:20:07 +0200248 /* cfg->local_ip allows overwritting the announced IP address with
249 * regards to the one we actually bind to. Useful in behind-NAT
250 * scenarios.
251 * TODO: we may want to define another local_ip_osmux var to
252 * us for OSMUX connections. Perhaps adding a new internal API to get it
253 * based on conn type.
254 */
255 const char *addr = endp->cfg->local_ip ? : conn->end.local_addr;
Philipp Maier8970c492017-10-11 13:33:42 +0200256 struct msgb *sdp;
257 int rc;
258 struct msgb *result;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200259
Philipp Maier8970c492017-10-11 13:33:42 +0200260 sdp = msgb_alloc_headroom(4096, 128, "sdp record");
261 if (!sdp)
262 return NULL;
263
Philipp Maier41d59202021-07-20 15:49:00 +0200264 /* Attach optional endpoint name */
265 if (add_epname) {
266 rc = msgb_printf(sdp, "Z: %s\r\n", endp->name);
267 if (rc < 0)
268 goto error;
269 }
270
271 /* Attach optional connection id */
272 if (add_conn_id) {
273 rc = msgb_printf(sdp, "I: %s\r\n", conn->conn->id);
Philipp Maier55295f72018-01-15 14:00:28 +0100274 if (rc < 0)
275 goto error;
276 }
277
Philipp Maier3cbfb8a2018-01-22 11:39:59 +0100278 /* Attach optional OSMUX parameters */
Pau Espin Pedrolc63f15a2019-05-10 16:52:08 +0200279 if (mgcp_conn_rtp_is_osmux(conn)) {
Pau Espin Pedrol5e8d7992019-04-24 19:56:43 +0200280 rc = msgb_printf(sdp, "X-Osmux: %u\r\n", conn->osmux.cid);
Philipp Maier3cbfb8a2018-01-22 11:39:59 +0100281 if (rc < 0)
282 goto error;
283 }
284
285 /* Attach line break to separate the parameters from the SDP block */
Philipp Maierc3cfae22018-01-22 12:03:03 +0100286 rc = msgb_printf(sdp, "\r\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200287
Philipp Maier8970c492017-10-11 13:33:42 +0200288 rc = mgcp_write_response_sdp(endp, conn, sdp, addr);
289 if (rc < 0)
290 goto error;
291 result = create_resp(endp, 200, " OK", msg, trans_id, NULL, (char*) sdp->data);
292 msgb_free(sdp);
293 return result;
294error:
295 msgb_free(sdp);
296 return NULL;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200297}
298
Philipp Maier87bd9be2017-08-22 16:35:41 +0200299/* Send out dummy packet to keep the connection open, if the connection is an
300 * osmux connection, send the dummy packet via OSMUX */
301static void send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200302{
Philipp Maier87bd9be2017-08-22 16:35:41 +0200303 if (conn->osmux.state != OSMUX_STATE_DISABLED)
304 osmux_send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200305 else
Philipp Maier87bd9be2017-08-22 16:35:41 +0200306 mgcp_send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200307}
308
Philipp Maier87bd9be2017-08-22 16:35:41 +0200309/* handle incoming messages:
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200310 * - this can be a command (four letters, space, transaction id)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200311 * - or a response (three numbers, space, transaction id) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200312struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
313{
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200314 struct rate_ctr_group *rate_ctrs = cfg->ratectr.mgcp_general_ctr_group;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200315 struct mgcp_parse_data pdata;
Philipp Maier8dc35972021-07-14 11:20:16 +0200316 struct mgcp_request_data rq;
Harald Weltee35eeae2017-12-28 13:47:37 +0100317 int rc, i, code, handled = 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200318 struct msgb *resp = NULL;
319 char *data;
320
Alexander Chemeris63866002020-05-05 17:18:40 +0300321 /* Count all messages, even incorect ones */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200322 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_TOTAL));
Alexander Chemeris63866002020-05-05 17:18:40 +0300323
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200324 if (msgb_l2len(msg) < 4) {
325 LOGP(DLMGCP, LOGL_ERROR, "msg too short: %d\n", msg->len);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200326 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200327 return NULL;
328 }
329
Alexander Chemeris63866002020-05-05 17:18:40 +0300330 if (mgcp_msg_terminate_nul(msg)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200331 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200332 return NULL;
Alexander Chemeris63866002020-05-05 17:18:40 +0300333 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200334
Philipp Maier87bd9be2017-08-22 16:35:41 +0200335 mgcp_disp_msg(msg->l2h, msgb_l2len(msg), "Received message");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200336
Philipp Maier87bd9be2017-08-22 16:35:41 +0200337 /* attempt to treat it as a response */
338 if (sscanf((const char *)&msg->l2h[0], "%3d %*s", &code) == 1) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200339 LOGP(DLMGCP, LOGL_DEBUG, "Response: Code: %d\n", code);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200340 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200341 return NULL;
342 }
343
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200344
Philipp Maier8dc35972021-07-14 11:20:16 +0200345 /* Parse message, extract endpoint name and transaction identifier and request name etc. */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200346 memset(&pdata, 0, sizeof(pdata));
Philipp Maier8dc35972021-07-14 11:20:16 +0200347 memset(&rq, 0, sizeof(rq));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200348 pdata.cfg = cfg;
Philipp Maier8dc35972021-07-14 11:20:16 +0200349 memcpy(rq.name, (const char *)&msg->l2h[0], sizeof(rq.name)-1);
350 msg->l3h = &msg->l2h[4];
Philipp Maier87bd9be2017-08-22 16:35:41 +0200351 data = mgcp_strline((char *)msg->l3h, &pdata.save);
Harald Weltee35eeae2017-12-28 13:47:37 +0100352 rc = mgcp_parse_header(&pdata, data);
Harald Weltee35eeae2017-12-28 13:47:37 +0100353 if (rc < 0) {
Philipp Maier8dc35972021-07-14 11:20:16 +0200354 LOGP(DLMGCP, LOGL_ERROR, "%s: failed to parse MCGP message\n", rq.name);
355 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
356 return create_err_response(NULL, -rc, rq.name, "000000");
Harald Welteabbb6b92017-12-28 13:13:50 +0100357 }
358
Philipp Maier8dc35972021-07-14 11:20:16 +0200359 /* Locate endpoint and trunk, if no endpoint can be located try at least to identify the trunk. */
360 rq.pdata = &pdata;
361 rq.wildcarded = mgcp_endp_is_wildcarded(pdata.epname);
362 rq.endp = mgcp_endp_by_name(&rc, pdata.epname, pdata.cfg);
363 rq.mgcp_cause = rc;
364 if (!rq.endp) {
365 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_NO_ENDPOINT));
366 if (rq.wildcarded) {
367 /* If we are unable to find the endpoint we still may be able to identify the trunk. Some
368 * request handlers will still be able to perform a useful action if the request refers to
369 * the whole trunk (wildcarded request). */
370 LOGP(DLMGCP, LOGL_NOTICE,
371 "%s: cannot find endpoint \"%s\", cause=%d -- trying to identify trunk...\n", rq.name,
372 pdata.epname, -rq.mgcp_cause);
373 rq.trunk = mgcp_trunk_by_name(pdata.cfg, pdata.epname);
374 if (!rq.trunk) {
375 LOGP(DLMGCP, LOGL_ERROR, "%s: failed to identify trunk for endpoint \"%s\" -- abort\n",
376 rq.name, pdata.epname);
377 return create_err_response(NULL, -rq.mgcp_cause, rq.name, pdata.trans);
378 }
379 } else {
380 /* If the endpoint name suggests that the request refers to a specific endpoint, then the
381 * request cannot be handled and we must stop early. */
382 LOGP(DLMGCP, LOGL_NOTICE,
383 "%s: cannot find endpoint \"%s\", cause=%d -- abort\n", rq.name,
384 pdata.epname, -rq.mgcp_cause);
385 return create_err_response(NULL, -rq.mgcp_cause, rq.name, pdata.trans);
386 }
387 } else {
388 rq.trunk = rq.endp->trunk;
389 rq.mgcp_cause = 0;
390
391 /* Check if we have to retransmit a response from a previous transaction */
392 if (pdata.trans && rq.endp->last_trans && strcmp(rq.endp->last_trans, pdata.trans) == 0) {
393 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_RETRANSMITTED));
394 return do_retransmission(rq.endp);
395 }
396 }
397
398 /* Find an appropriate handler for the current request and execute it */
399 for (i = 0; i < ARRAY_SIZE(mgcp_requests); i++) {
400 if (strcmp(mgcp_requests[i].name, rq.name) == 0) {
401 /* Check if the request requires and endpoint, if yes, check if we have it, otherwise don't
402 * execute the request handler. */
403 if (mgcp_requests[i].require_endp && !rq.endp) {
404 LOGP(DLMGCP, LOGL_ERROR,
405 "%s: the request handler \"%s\" requires an endpoint resource for \"%s\", which is not available -- abort\n",
406 rq.name, mgcp_requests[i].debug_name, pdata.epname);
407 return create_err_response(NULL, -rq.mgcp_cause, rq.name, pdata.trans);
408 }
409
410 /* Execute request handler */
411 if (rq.endp)
412 LOGP(DLMGCP, LOGL_INFO,
413 "%s: executing request handler \"%s\" for endpoint resource \"%s\"\n", rq.name,
414 mgcp_requests[i].debug_name, rq.endp->name);
415 else
416 LOGP(DLMGCP, LOGL_INFO,
417 "%s: executing request handler \"%s\" for trunk resource of endpoint \"%s\"\n",
418 rq.name, mgcp_requests[i].debug_name, pdata.epname);
419 resp = mgcp_requests[i].handle_request(&rq);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200420 handled = 1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200421 break;
422 }
423 }
424
Philipp Maier8dc35972021-07-14 11:20:16 +0200425 /* Check if the MGCP request was handled and increment rate counters accordingly. */
Alexander Chemeris63866002020-05-05 17:18:40 +0300426 if (handled) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200427 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_HANDLED));
Alexander Chemeris63866002020-05-05 17:18:40 +0300428 } else {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200429 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_UNHANDLED));
Philipp Maier8dc35972021-07-14 11:20:16 +0200430 LOGP(DLMGCP, LOGL_ERROR, "MSG with type: '%.4s' not handled\n", &msg->l2h[0]);
Alexander Chemeris63866002020-05-05 17:18:40 +0300431 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200432
433 return resp;
434}
435
Philipp Maier87bd9be2017-08-22 16:35:41 +0200436/* AUEP command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +0200437static struct msgb *handle_audit_endpoint(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200438{
Philipp Maier8dc35972021-07-14 11:20:16 +0200439 LOGPENDP(rq->endp, DLMGCP, LOGL_NOTICE, "AUEP: auditing endpoint ...\n");
440 return create_ok_response(rq->endp, 200, "AUEP", rq->pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200441}
442
Harald Welte1d1b98f2017-12-25 10:03:40 +0100443/* Try to find a free port by attempting to bind on it. Also handle the
Philipp Maier87bd9be2017-08-22 16:35:41 +0200444 * counter that points on the next free port. Since we have a pointer
Philipp Maierb38fb892018-05-22 13:52:21 +0200445 * to the next free port, binding should in work on the first attempt in
Pau Espin Pedrolfc806732019-04-23 00:18:43 +0200446 * general. In case of failure the next port is tried until the whole port
447 * range is tried once. */
Philipp Maier87bd9be2017-08-22 16:35:41 +0200448static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200449{
450 int i;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200451 struct mgcp_port_range *range;
Philipp Maierb38fb892018-05-22 13:52:21 +0200452 unsigned int tries;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200453
Philipp Maier87bd9be2017-08-22 16:35:41 +0200454 OSMO_ASSERT(conn);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200455
456 range = &endp->cfg->net_ports;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200457
458 /* attempt to find a port */
Philipp Maierb38fb892018-05-22 13:52:21 +0200459 tries = (range->range_end - range->range_start) / 2;
460 for (i = 0; i < tries; ++i) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200461 int rc;
462
463 if (range->last_port >= range->range_end)
464 range->last_port = range->range_start;
465
Philipp Maier87bd9be2017-08-22 16:35:41 +0200466 rc = mgcp_bind_net_rtp_port(endp, range->last_port, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200467
468 range->last_port += 2;
469 if (rc == 0) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200470 return 0;
471 }
472
473 }
474
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200475 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
476 "Allocating a RTP/RTCP port failed %u times.\n",
477 tries);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200478 return -1;
479}
480
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200481/*! Helper function for check_local_cx_options() to get a pointer of the next
482 * lco option identifier
483 * \param[in] lco string
484 * \returns pointer to the beginning of the LCO identifier, NULL on failure */
485char *get_lco_identifier(const char *options)
486{
487 char *ptr;
488 unsigned int count = 0;
489
490 /* Jump to the end of the lco identifier */
491 ptr = strstr(options, ":");
492 if (!ptr)
493 return NULL;
494
495 /* Walk backwards until the pointer points to the beginning of the
496 * lco identifier. We know that we stand at the beginning when we
497 * are either at the beginning of the memory or see a space or
498 * comma. (this is tolerant, it will accept a:10, b:11 as well as
499 * a:10,b:11) */
500 while (1) {
501 /* Endless loop protection */
502 if (count > 10000)
503 return NULL;
504 else if (ptr < options || *ptr == ' ' || *ptr == ',') {
505 ptr++;
506 break;
507 }
508 ptr--;
509 count++;
510 }
511
512 /* Check if we got any result */
513 if (*ptr == ':')
514 return NULL;
515
516 return ptr;
517}
518
519/*! Check the LCO option. This function checks for multiple appearence of LCO
520 * options, which is illegal
521 * \param[in] ctx talloc context
522 * \param[in] lco string
523 * \returns 0 on success, -1 on failure */
524int check_local_cx_options(void *ctx, const char *options)
525{
526 int i;
527 char *options_copy;
528 char *lco_identifier;
529 char *lco_identifier_end;
530 char *next_lco_identifier;
531
532 char **lco_seen;
533 unsigned int lco_seen_n = 0;
534
535 if (!options)
536 return -1;
537
538 lco_seen =
539 (char **)talloc_zero_size(ctx, strlen(options) * sizeof(char *));
540 options_copy = talloc_strdup(ctx, options);
541 lco_identifier = options_copy;
542
543 do {
544 /* Move the lco_identifier pointer to the beginning of the
545 * current lco option identifier */
546 lco_identifier = get_lco_identifier(lco_identifier);
547 if (!lco_identifier)
548 goto error;
549
550 /* Look ahead to the next LCO option early, since we
551 * will parse destructively */
552 next_lco_identifier = strstr(lco_identifier + 1, ",");
553
554 /* Pinch off the end of the lco field identifier name
555 * and see if we still got something, also check if
556 * there is some value after the colon. */
557 lco_identifier_end = strstr(lco_identifier, ":");
558 if (!lco_identifier_end)
559 goto error;
560 if (*(lco_identifier_end + 1) == ' '
561 || *(lco_identifier_end + 1) == ','
562 || *(lco_identifier_end + 1) == '\0')
563 goto error;
564 *lco_identifier_end = '\0';
565 if (strlen(lco_identifier) == 0)
566 goto error;
567
568 /* Check if we have already seen the current field identifier
569 * before. If yes, we must bail, an LCO must only appear once
570 * in the LCO string */
571 for (i = 0; i < lco_seen_n; i++) {
Pau Espin Pedrol7eb6f2c2019-06-26 13:00:52 +0200572 if (strcasecmp(lco_seen[i], lco_identifier) == 0)
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200573 goto error;
574 }
575 lco_seen[lco_seen_n] = lco_identifier;
576 lco_seen_n++;
577
578 /* The first identifier must always be found at the beginnning
579 * of the LCO string */
580 if (lco_seen[0] != options_copy)
581 goto error;
582
583 /* Go to the next lco option */
584 lco_identifier = next_lco_identifier;
585 } while (lco_identifier);
586
587 talloc_free(lco_seen);
588 talloc_free(options_copy);
589 return 0;
590error:
591 talloc_free(lco_seen);
592 talloc_free(options_copy);
593 return -1;
594}
595
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200596/* Set the LCO from a string (see RFC 3435).
Harald Welte1d1b98f2017-12-25 10:03:40 +0100597 * The string is stored in the 'string' field. A NULL string is handled exactly
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200598 * like an empty string, the 'string' field is never NULL after this function
599 * has been called. */
Philipp Maiera390d0b2018-01-31 17:30:19 +0100600static int set_local_cx_options(void *ctx, struct mgcp_lco *lco,
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200601 const char *options)
602{
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200603 char *lco_id;
Philipp Maier8dbc9ed2018-10-26 14:50:25 +0200604 char codec[17];
Pau Espin Pedrol19539862020-09-21 11:43:41 +0200605 char nt[17];
Pau Espin Pedrol83fd8a52019-06-26 12:55:26 +0200606 int len;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200607
Philipp Maier604410c2018-06-06 10:02:16 +0200608 if (!options)
609 return 0;
610 if (strlen(options) == 0)
611 return 0;
612
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200613 /* Make sure the encoding of the LCO is consistant before we proceed */
614 if (check_local_cx_options(ctx, options) != 0) {
615 LOGP(DLMGCP, LOGL_ERROR,
616 "local CX options: Internal inconsistency in Local Connection Options!\n");
617 return 524;
618 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200619
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200620 talloc_free(lco->string);
621 lco->string = talloc_strdup(ctx, options);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200622
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200623 lco_id = lco->string;
624 while ((lco_id = get_lco_identifier(lco_id))) {
625 switch (tolower(lco_id[0])) {
626 case 'p':
627 if (sscanf(lco_id + 1, ":%d-%d",
628 &lco->pkt_period_min, &lco->pkt_period_max) == 1)
629 lco->pkt_period_max = lco->pkt_period_min;
630 break;
631 case 'a':
Pau Espin Pedrol1dc2dce2020-09-21 11:25:15 +0200632 /* FIXME: LCO also supports the negotiation of more than one codec.
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200633 * (e.g. a:PCMU;G726-32) But this implementation only supports a single
634 * codec only. */
635 if (sscanf(lco_id + 1, ":%16[^,]", codec) == 1) {
636 talloc_free(lco->codec);
Pau Espin Pedrol83fd8a52019-06-26 12:55:26 +0200637 /* MGCP header is case insensive, and we'll need
638 codec in uppercase when using it later: */
639 len = strlen(codec);
640 lco->codec = talloc_size(ctx, len + 1);
641 osmo_str_toupper_buf(lco->codec, len + 1, codec);
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200642 }
643 break;
Pau Espin Pedrol19539862020-09-21 11:43:41 +0200644 case 'n':
645 if (lco_id[1] == 't' && sscanf(lco_id + 2, ":%16[^,]", nt) == 1)
646 break;
647 /* else: fall throught to print notice log */
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200648 default:
649 LOGP(DLMGCP, LOGL_NOTICE,
650 "LCO: unhandled option: '%c'/%d in \"%s\"\n",
651 *lco_id, *lco_id, lco->string);
652 break;
653 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200654
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200655 lco_id = strchr(lco_id, ',');
656 if (!lco_id)
657 break;
Philipp Maier604410c2018-06-06 10:02:16 +0200658 }
Philipp Maier87bd9be2017-08-22 16:35:41 +0200659
660 LOGP(DLMGCP, LOGL_DEBUG,
661 "local CX options: lco->pkt_period_max: %i, lco->codec: %s\n",
662 lco->pkt_period_max, lco->codec);
Philipp Maiera390d0b2018-01-31 17:30:19 +0100663
664 /* Check if the packetization fits the 20ms raster */
665 if (lco->pkt_period_min % 20 && lco->pkt_period_max % 20) {
666 LOGP(DLMGCP, LOGL_ERROR,
667 "local CX options: packetization interval is not a multiple of 20ms!\n");
668 return 535;
669 }
670
671 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200672}
673
674void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
675 struct mgcp_rtp_end *rtp)
676{
Philipp Maier14b27a82020-06-02 20:15:30 +0200677 struct mgcp_trunk *trunk = endp->trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200678
Philipp Maier14b27a82020-06-02 20:15:30 +0200679 int patch_ssrc = expect_ssrc_change && trunk->force_constant_ssrc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200680
Philipp Maier14b27a82020-06-02 20:15:30 +0200681 rtp->force_aligned_timing = trunk->force_aligned_timing;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200682 rtp->force_constant_ssrc = patch_ssrc ? 1 : 0;
Philipp Maier14b27a82020-06-02 20:15:30 +0200683 rtp->rfc5993_hr_convert = trunk->rfc5993_hr_convert;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200684
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200685 LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
686 "Configuring RTP endpoint: local port %d%s%s\n",
687 ntohs(rtp->rtp_port),
688 rtp->force_aligned_timing ? ", force constant timing" : "",
689 rtp->force_constant_ssrc ? ", force constant ssrc" : "");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200690}
691
Pau Espin Pedrol8358c4b2021-07-07 12:41:38 +0200692uint32_t mgcp_rtp_packet_duration(const struct mgcp_endpoint *endp,
693 const struct mgcp_rtp_end *rtp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200694{
695 int f = 0;
696
697 /* Get the number of frames per channel and packet */
698 if (rtp->frames_per_packet)
699 f = rtp->frames_per_packet;
Philipp Maierbc0346e2018-06-07 09:52:16 +0200700 else if (rtp->packet_duration_ms && rtp->codec->frame_duration_num) {
701 int den = 1000 * rtp->codec->frame_duration_num;
702 f = (rtp->packet_duration_ms * rtp->codec->frame_duration_den +
Philipp Maier87bd9be2017-08-22 16:35:41 +0200703 den / 2)
704 / den;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200705 }
706
Philipp Maierbc0346e2018-06-07 09:52:16 +0200707 return rtp->codec->rate * f * rtp->codec->frame_duration_num /
708 rtp->codec->frame_duration_den;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200709}
710
Pau Espin Pedrol9fb8ddf2019-05-08 15:35:36 +0200711/*! Initializes osmux socket if not yet initialized. Parses Osmux CID from MGCP line.
712 * \param[in] endp Endpoint willing to initialize osmux
713 * \param[in] line Line X-Osmux from MGCP header msg to parse
714 * \returns OSMUX CID, -1 for wildcard, -2 on parse error, -3 on osmux initalize error
715 */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200716static int mgcp_osmux_setup(struct mgcp_endpoint *endp, const char *line)
717{
718 if (!endp->cfg->osmux_init) {
719 if (osmux_init(OSMUX_ROLE_BSC, endp->cfg) < 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200720 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "Cannot init OSMUX\n");
Pau Espin Pedrol9fb8ddf2019-05-08 15:35:36 +0200721 return -3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200722 }
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200723 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "OSMUX socket has been set up\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200724 }
725
726 return mgcp_parse_osmux_cid(line);
727}
728
Philipp Maierbc0346e2018-06-07 09:52:16 +0200729/* Process codec information contained in CRCX/MDCX */
730static int handle_codec_info(struct mgcp_conn_rtp *conn,
Philipp Maier8dc35972021-07-14 11:20:16 +0200731 struct mgcp_request_data *rq, int have_sdp, bool crcx)
Philipp Maierbc0346e2018-06-07 09:52:16 +0200732{
Philipp Maier8dc35972021-07-14 11:20:16 +0200733 struct mgcp_endpoint *endp = rq->endp;
Philipp Maierbc0346e2018-06-07 09:52:16 +0200734 int rc;
735 char *cmd;
736
737 if (crcx)
738 cmd = "CRCX";
739 else
740 cmd = "MDCX";
741
742 /* Collect codec information */
743 if (have_sdp) {
744 /* If we have SDP, we ignore the local connection options and
745 * use only the SDP information. */
746 mgcp_codec_reset_all(conn);
Philipp Maier8dc35972021-07-14 11:20:16 +0200747 rc = mgcp_parse_sdp_data(endp, conn, rq->pdata);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200748 if (rc != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200749 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
750 "%s: sdp not parseable\n", cmd);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200751
752 /* See also RFC 3661: Protocol error */
753 return 510;
754 }
755 } else if (endp->local_options.codec) {
756 /* When no SDP is available, we use the codec information from
757 * the local connection options (if present) */
758 mgcp_codec_reset_all(conn);
Philipp Maier228e5912019-03-05 13:56:59 +0100759 rc = mgcp_codec_add(conn, PTYPE_UNDEFINED, endp->local_options.codec, NULL);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200760 if (rc != 0)
761 goto error;
762 }
763
764 /* Make sure we always set a sane default codec */
765 if (conn->end.codecs_assigned == 0) {
766 /* When SDP and/or LCO did not supply any codec information,
767 * than it makes sense to pick a sane default: (payload-type 0,
768 * PCMU), see also: OS#2658 */
769 mgcp_codec_reset_all(conn);
Philipp Maier228e5912019-03-05 13:56:59 +0100770 rc = mgcp_codec_add(conn, 0, NULL, NULL);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200771 if (rc != 0)
772 goto error;
773 }
774
775 /* Make codec decision */
776 if (mgcp_codec_decide(conn) != 0)
777 goto error;
778
779 return 0;
780
781error:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200782 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
783 "%s: codec negotiation failure\n", cmd);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200784
785 /* See also RFC 3661: Codec negotiation failure */
786 return 534;
787}
788
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200789static bool parse_x_osmo_ign(struct mgcp_endpoint *endp, char *line)
790{
791 char *saveptr = NULL;
792
Pau Espin Pedrol6049a632020-09-21 11:03:21 +0200793 if (strncasecmp(line, MGCP_X_OSMO_IGN_HEADER, strlen(MGCP_X_OSMO_IGN_HEADER)))
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200794 return false;
795 line += strlen(MGCP_X_OSMO_IGN_HEADER);
796
797 while (1) {
798 char *token = strtok_r(line, " ", &saveptr);
799 line = NULL;
800 if (!token)
801 break;
802
Pau Espin Pedrol6e26c702019-06-26 13:09:39 +0200803 if (!strcasecmp(token, "C"))
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200804 endp->x_osmo_ign |= MGCP_X_OSMO_IGN_CALLID;
805 else
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200806 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "received unknown X-Osmo-IGN item '%s'\n", token);
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200807 }
808
809 return true;
810}
811
Philipp Maier87bd9be2017-08-22 16:35:41 +0200812/* CRCX command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +0200813static struct msgb *handle_create_con(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200814{
Philipp Maier8dc35972021-07-14 11:20:16 +0200815 struct mgcp_parse_data *pdata = rq->pdata;
816 struct mgcp_trunk *trunk = rq->trunk;
817 struct mgcp_endpoint *endp = rq->endp;
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200818 struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_crcx_ctr_group;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200819 int error_code = 400;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200820 const char *local_options = NULL;
821 const char *callid = NULL;
822 const char *mode = NULL;
823 char *line;
Pau Espin Pedrol2b896172019-04-24 13:47:23 +0200824 int have_sdp = 0, osmux_cid = -2;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200825 struct mgcp_conn_rtp *conn = NULL;
Philipp Maierffd75e42017-11-22 11:44:50 +0100826 struct mgcp_conn *_conn = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200827 char conn_name[512];
Philipp Maiera390d0b2018-01-31 17:30:19 +0100828 int rc;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200829
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200830 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "CRCX: creating new connection ...\n");
Philipp Maier246233d2020-08-18 15:15:24 +0200831
Philipp Maier8d6a1932020-06-18 12:19:31 +0200832 if (!mgcp_endp_avail(endp)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200833 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_AVAIL));
Philipp Maiera910a812020-08-18 15:13:33 +0200834 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
835 "CRCX: selected endpoint not available!\n");
Philipp Maier8dc35972021-07-14 11:20:16 +0200836 return create_err_response(NULL, 501, "CRCX", pdata->trans);
Philipp Maier8d6a1932020-06-18 12:19:31 +0200837 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200838
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200839 /* parse CallID C: and LocalParameters L: */
Philipp Maier8dc35972021-07-14 11:20:16 +0200840 for_each_line(line, pdata->save) {
Philipp Maier036612b2021-07-19 17:47:49 +0200841 if (!mgcp_check_param(endp, trunk, line))
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200842 continue;
843
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +0200844 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200845 case 'L':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200846 local_options = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200847 break;
848 case 'C':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200849 callid = (const char *)line + 3;
850 break;
851 case 'I':
Philipp Maierffd75e42017-11-22 11:44:50 +0100852 /* It is illegal to send a connection identifier
853 * together with a CRCX, the MGW will assign the
854 * connection identifier by itself on CRCX */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200855 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BAD_ACTION));
Philipp Maier8dc35972021-07-14 11:20:16 +0200856 return create_err_response(NULL, 523, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200857 break;
858 case 'M':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200859 mode = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200860 break;
861 case 'X':
Pau Espin Pedrolc1bf4692019-05-14 16:23:24 +0200862 if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200863 /* If osmux is disabled, just skip setting it up */
Philipp Maier8dc35972021-07-14 11:20:16 +0200864 if (!rq->endp->cfg->osmux)
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200865 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200866 osmux_cid = mgcp_osmux_setup(endp, line);
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200867 break;
868 }
869
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200870 if (parse_x_osmo_ign(endp, line))
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200871 break;
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200872
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200873 /* Ignore unknown X-headers */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200874 break;
875 case '\0':
876 have_sdp = 1;
877 goto mgcp_header_done;
878 default:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200879 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
880 "CRCX: unhandled option: '%c'/%d\n", *line, *line);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200881 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_UNHANDLED_PARAM));
Philipp Maier8dc35972021-07-14 11:20:16 +0200882 return create_err_response(NULL, 539, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200883 break;
884 }
885 }
886
887mgcp_header_done:
Philipp Maier87bd9be2017-08-22 16:35:41 +0200888 /* Check parameters */
889 if (!callid) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200890 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
891 "CRCX: insufficient parameters, missing callid\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200892 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_MISSING_CALLID));
Philipp Maier8dc35972021-07-14 11:20:16 +0200893 return create_err_response(endp, 516, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200894 }
895
Philipp Maier87bd9be2017-08-22 16:35:41 +0200896 if (!mode) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200897 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
898 "CRCX: insufficient parameters, missing mode\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200899 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_MODE));
Philipp Maier8dc35972021-07-14 11:20:16 +0200900 return create_err_response(endp, 517, "CRCX", pdata->trans);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200901 }
902
Philipp Maier87bd9be2017-08-22 16:35:41 +0200903 /* Check if we are able to accept the creation of another connection */
904 if (llist_count(&endp->conns) >= endp->type->max_conns) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200905 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
906 "CRCX: endpoint full, max. %i connections allowed!\n",
907 endp->type->max_conns);
Philipp Maier14b27a82020-06-02 20:15:30 +0200908 if (trunk->force_realloc) {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200909 /* There is no more room for a connection, make some
910 * room by blindly tossing the oldest of the two two
911 * connections */
912 mgcp_conn_free_oldest(endp);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200913 } else {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200914 /* There is no more room for a connection, leave
915 * everything as it is and return with an error */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200916 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_LIMIT_EXCEEDED));
Philipp Maier8dc35972021-07-14 11:20:16 +0200917 return create_err_response(endp, 540, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200918 }
919 }
920
Philipp Maier87bd9be2017-08-22 16:35:41 +0200921 /* Check if this endpoint already serves a call, if so, check if the
922 * callids match up so that we are sure that this is our call */
923 if (endp->callid && mgcp_verify_call_id(endp, callid)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200924 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
925 "CRCX: already seized by other call (%s)\n",
926 endp->callid);
Philipp Maier14b27a82020-06-02 20:15:30 +0200927 if (trunk->force_realloc)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200928 /* This is not our call, toss everything by releasing
929 * the entire endpoint. (rude!) */
Philipp Maier1355d7e2018-02-01 14:30:06 +0100930 mgcp_endp_release(endp);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200931 else {
932 /* This is not our call, leave everything as it is and
933 * return with an error. */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200934 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_UNKNOWN_CALLID));
Philipp Maier8dc35972021-07-14 11:20:16 +0200935 return create_err_response(endp, 400, "CRCX", pdata->trans);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200936 }
937 }
938
Philipp Maier889fe7f2020-07-06 17:44:12 +0200939 if (!endp->callid) {
940 /* Claim endpoint resources. This will also set the callid,
941 * creating additional connections will only be possible if
942 * the callid matches up (see above). */
943 rc = mgcp_endp_claim(endp, callid);
944 if (rc != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200945 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_CLAIM));
Philipp Maier8dc35972021-07-14 11:20:16 +0200946 return create_err_response(endp, 502, "CRCX", pdata->trans);
Philipp Maier889fe7f2020-07-06 17:44:12 +0200947 }
948 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200949
Philipp Maierffd75e42017-11-22 11:44:50 +0100950 snprintf(conn_name, sizeof(conn_name), "%s", callid);
Philipp Maier14b27a82020-06-02 20:15:30 +0200951 _conn = mgcp_conn_alloc(trunk->endpoints, endp, MGCP_CONN_TYPE_RTP, conn_name);
Philipp Maierffd75e42017-11-22 11:44:50 +0100952 if (!_conn) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200953 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
954 "CRCX: unable to allocate RTP connection\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200955 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_ALLOC_CONN));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200956 goto error2;
957
Philipp Maier87bd9be2017-08-22 16:35:41 +0200958 }
Philipp Maier889fe7f2020-07-06 17:44:12 +0200959
Philipp Maierffd75e42017-11-22 11:44:50 +0100960 conn = mgcp_conn_get_rtp(endp, _conn->id);
961 OSMO_ASSERT(conn);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200962
963 if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
964 error_code = 517;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200965 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_MODE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200966 goto error2;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200967 }
968
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200969 /* Annotate Osmux circuit ID and set it to negotiating state until this
Philipp Maier87bd9be2017-08-22 16:35:41 +0200970 * is fully set up from the dummy load. */
971 conn->osmux.state = OSMUX_STATE_DISABLED;
Pau Espin Pedrol2b896172019-04-24 13:47:23 +0200972 if (osmux_cid >= -1) { /* -1 is wilcard, alloc next avail CID */
Pau Espin Pedrol14f8a082019-05-13 13:10:06 +0200973 conn->osmux.state = OSMUX_STATE_ACTIVATING;
Pau Espin Pedrol2b896172019-04-24 13:47:23 +0200974 if (conn_osmux_allocate_cid(conn, osmux_cid) == -1) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200975 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_OSMUX));
Pau Espin Pedrol2b896172019-04-24 13:47:23 +0200976 goto error2;
977 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200978 } else if (endp->cfg->osmux == OSMUX_USAGE_ONLY) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200979 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
980 "CRCX: osmux only and no osmux offered\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200981 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_OSMUX));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200982 goto error2;
983 }
984
Philipp Maierbc0346e2018-06-07 09:52:16 +0200985 /* Set local connection options, if present */
986 if (local_options) {
Philipp Maier8dc35972021-07-14 11:20:16 +0200987 rc = set_local_cx_options(trunk->endpoints,
Philipp Maierbc0346e2018-06-07 09:52:16 +0200988 &endp->local_options, local_options);
989 if (rc != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200990 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
991 "CRCX: inavlid local connection options!\n");
Philipp Maierbc0346e2018-06-07 09:52:16 +0200992 error_code = rc;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200993 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS));
Philipp Maierbc0346e2018-06-07 09:52:16 +0200994 goto error2;
995 }
996 }
997
998 /* Handle codec information and decide for a suitable codec */
Philipp Maier8dc35972021-07-14 11:20:16 +0200999 rc = handle_codec_info(conn, rq, have_sdp, true);
Philipp Maierbc0346e2018-06-07 09:52:16 +02001000 mgcp_codec_summary(conn);
1001 if (rc) {
1002 error_code = rc;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001003 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_CODEC_NEGOTIATION));
Philipp Maierbc0346e2018-06-07 09:52:16 +02001004 goto error2;
1005 }
1006
Philipp Maier14b27a82020-06-02 20:15:30 +02001007 conn->end.fmtp_extra = talloc_strdup(trunk->endpoints,
1008 trunk->audio_fmtp_extra);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001009
Philipp Maier8dc35972021-07-14 11:20:16 +02001010 if (pdata->cfg->force_ptime) {
1011 conn->end.packet_duration_ms = pdata->cfg->force_ptime;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001012 conn->end.force_output_ptime = 1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001013 }
1014
Philipp Maier1cb1e382017-11-02 17:16:04 +01001015 mgcp_rtp_end_config(endp, 0, &conn->end);
1016
Philipp Maierc3cc6542018-02-02 12:58:42 +01001017 /* check connection mode setting */
1018 if (conn->conn->mode != MGCP_CONN_LOOPBACK
1019 && conn->conn->mode != MGCP_CONN_RECV_ONLY
1020 && conn->end.rtp_port == 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001021 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
1022 "CRCX: selected connection mode type requires an opposite end!\n");
Philipp Maierc3cc6542018-02-02 12:58:42 +01001023 error_code = 527;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001024 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC));
Philipp Maierc3cc6542018-02-02 12:58:42 +01001025 goto error2;
1026 }
1027
Pau Espin Pedrol71d42e72020-09-03 14:20:07 +02001028 /* Find a local address for conn based on policy and initial SDP remote
1029 information, then find a free port for it */
1030 mgcp_get_local_addr(conn->end.local_addr, conn);
Philipp Maier1cb1e382017-11-02 17:16:04 +01001031 if (allocate_port(endp, conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001032 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
Philipp Maier1cb1e382017-11-02 17:16:04 +01001033 goto error2;
1034 }
1035
Philipp Maier87bd9be2017-08-22 16:35:41 +02001036 if (setup_rtp_processing(endp, conn) != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001037 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
1038 "CRCX: could not start RTP processing!\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001039 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_START_RTP));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001040 goto error2;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001041 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001042
1043 /* policy CB */
Philipp Maier8dc35972021-07-14 11:20:16 +02001044 if (pdata->cfg->policy_cb) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001045 int rc;
Philipp Maier8dc35972021-07-14 11:20:16 +02001046 rc = pdata->cfg->policy_cb(endp, MGCP_ENDP_CRCX, pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001047 switch (rc) {
1048 case MGCP_POLICY_REJECT:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001049 LOGPCONN(_conn, DLMGCP, LOGL_NOTICE,
1050 "CRCX: CRCX rejected by policy\n");
Philipp Maier1355d7e2018-02-01 14:30:06 +01001051 mgcp_endp_release(endp);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001052 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_REJECTED_BY_POLICY));
Philipp Maier8dc35972021-07-14 11:20:16 +02001053 return create_err_response(endp, 400, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001054 break;
1055 case MGCP_POLICY_DEFER:
1056 /* stop processing */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001057 return NULL;
1058 break;
1059 case MGCP_POLICY_CONT:
1060 /* just continue */
1061 break;
1062 }
1063 }
1064
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001065 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
1066 "CRCX: Creating connection: port: %u\n", conn->end.local_port);
Philipp Maier8dc35972021-07-14 11:20:16 +02001067 if (pdata->cfg->change_cb)
1068 pdata->cfg->change_cb(endp, MGCP_ENDP_CRCX);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001069
Philipp Maiere726d4f2017-11-01 10:41:34 +01001070 /* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
Philipp Maier14b27a82020-06-02 20:15:30 +02001071 OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Pau Espin Pedrolca280a12021-07-06 18:15:35 +02001072 if (conn->conn->mode & MGCP_CONN_RECV_ONLY &&
1073 mgcp_rtp_end_remote_addr_available(&conn->end) &&
1074 trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001075 send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001076
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001077 LOGPCONN(_conn, DLMGCP, LOGL_NOTICE,
1078 "CRCX: connection successfully created\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001079 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_SUCCESS));
Philipp Maier889fe7f2020-07-06 17:44:12 +02001080 mgcp_endp_update(endp);
Philipp Maier41d59202021-07-20 15:49:00 +02001081
1082 /* NOTE: Only in the virtual trunk we allow dynamic endpoint names */
1083 bool add_epname = rq->wildcarded && trunk->trunk_type == MGCP_TRUNK_VIRTUAL;
1084 return create_response_with_sdp(endp, conn, "CRCX", pdata->trans, add_epname, true);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001085error2:
Philipp Maier1355d7e2018-02-01 14:30:06 +01001086 mgcp_endp_release(endp);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001087 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1088 "CRCX: unable to create connection\n");
Philipp Maier8dc35972021-07-14 11:20:16 +02001089 return create_err_response(endp, error_code, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001090}
1091
Philipp Maier87bd9be2017-08-22 16:35:41 +02001092/* MDCX command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +02001093static struct msgb *handle_modify_con(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001094{
Philipp Maier8dc35972021-07-14 11:20:16 +02001095 struct mgcp_parse_data *pdata = rq->pdata;
1096 struct mgcp_trunk *trunk = rq->trunk;
1097 struct mgcp_endpoint *endp = rq->endp;
1098 struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_mdcx_ctr_group;
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001099 char new_local_addr[INET6_ADDRSTRLEN];
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001100 int error_code = 500;
1101 int silent = 0;
1102 int have_sdp = 0;
1103 char *line;
1104 const char *local_options = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001105 const char *mode = NULL;
1106 struct mgcp_conn_rtp *conn = NULL;
Philipp Maier01d24a32017-11-21 17:26:09 +01001107 const char *conn_id = NULL;
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001108 int osmux_cid = -2;
Philipp Maiera390d0b2018-01-31 17:30:19 +01001109 int rc;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001110
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001111 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "MDCX: modifying existing connection ...\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001112
Philipp Maier8d6a1932020-06-18 12:19:31 +02001113 if (!mgcp_endp_avail(endp)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001114 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_AVAIL));
Philipp Maiera910a812020-08-18 15:13:33 +02001115 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1116 "MDCX: selected endpoint not available!\n");
Philipp Maier8dc35972021-07-14 11:20:16 +02001117 return create_err_response(NULL, 501, "MDCX", pdata->trans);
Philipp Maier8d6a1932020-06-18 12:19:31 +02001118 }
1119
Philipp Maier5656fbf2018-02-02 14:41:58 +01001120 /* Prohibit wildcarded requests */
Philipp Maier8dc35972021-07-14 11:20:16 +02001121 if (rq->wildcarded) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001122 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1123 "MDCX: wildcarded endpoint names not supported.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001124 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_WILDCARD));
Philipp Maier8dc35972021-07-14 11:20:16 +02001125 return create_err_response(endp, 507, "MDCX", pdata->trans);
Philipp Maier5656fbf2018-02-02 14:41:58 +01001126 }
1127
Philipp Maier87bd9be2017-08-22 16:35:41 +02001128 if (llist_count(&endp->conns) <= 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001129 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1130 "MDCX: endpoint is not holding a connection.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001131 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_CONN));
Philipp Maier8dc35972021-07-14 11:20:16 +02001132 return create_err_response(endp, 400, "MDCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001133 }
1134
Philipp Maier8dc35972021-07-14 11:20:16 +02001135 for_each_line(line, pdata->save) {
Philipp Maier036612b2021-07-19 17:47:49 +02001136 if (!mgcp_check_param(endp, trunk, line))
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001137 continue;
1138
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001139 switch (toupper(line[0])) {
Philipp Maier87bd9be2017-08-22 16:35:41 +02001140 case 'C':
Harald Weltee35eeae2017-12-28 13:47:37 +01001141 if (mgcp_verify_call_id(endp, line + 3) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001142 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CALLID));
Harald Weltee35eeae2017-12-28 13:47:37 +01001143 error_code = 516;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001144 goto error3;
Harald Weltee35eeae2017-12-28 13:47:37 +01001145 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001146 break;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001147 case 'I':
Philipp Maier01d24a32017-11-21 17:26:09 +01001148 conn_id = (const char *)line + 3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001149 if ((error_code = mgcp_verify_ci(endp, conn_id))) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001150 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CONNID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001151 goto error3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001152 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001153 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001154 case 'L':
Philipp Maier87bd9be2017-08-22 16:35:41 +02001155 local_options = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001156 break;
1157 case 'M':
Philipp Maier87bd9be2017-08-22 16:35:41 +02001158 mode = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001159 break;
1160 case 'Z':
Pau Espin Pedrol9b508f62019-06-26 13:11:22 +02001161 silent = strcasecmp("noanswer", line + 3) == 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001162 break;
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001163 case 'X':
Pau Espin Pedrolc1bf4692019-05-14 16:23:24 +02001164 if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001165 /* If osmux is disabled, just skip setting it up */
Philipp Maier8dc35972021-07-14 11:20:16 +02001166 if (!endp->cfg->osmux)
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001167 break;
1168 osmux_cid = mgcp_osmux_setup(endp, line);
1169 break;
1170 }
1171 /* Ignore unknown X-headers */
1172 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001173 case '\0':
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001174 have_sdp = 1;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001175 goto mgcp_header_done;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001176 break;
1177 default:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001178 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1179 "MDCX: Unhandled MGCP option: '%c'/%d\n",
1180 line[0], line[0]);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001181 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_UNHANDLED_PARAM));
Philipp Maier8dc35972021-07-14 11:20:16 +02001182 return create_err_response(NULL, 539, "MDCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001183 break;
1184 }
1185 }
1186
Philipp Maier87bd9be2017-08-22 16:35:41 +02001187mgcp_header_done:
Philipp Maier01d24a32017-11-21 17:26:09 +01001188 if (!conn_id) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001189 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1190 "MDCX: insufficient parameters, missing ci (connectionIdentifier)\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001191 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_CONNID));
Philipp Maier8dc35972021-07-14 11:20:16 +02001192 return create_err_response(endp, 515, "MDCX", pdata->trans);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001193 }
1194
1195 conn = mgcp_conn_get_rtp(endp, conn_id);
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001196 if (!conn) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001197 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_CONN_NOT_FOUND));
Philipp Maier8dc35972021-07-14 11:20:16 +02001198 return create_err_response(endp, 400, "MDCX", pdata->trans);
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001199 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001200
Oliver Smithe36b7752019-01-22 16:31:36 +01001201 mgcp_conn_watchdog_kick(conn->conn);
1202
Philipp Maier87bd9be2017-08-22 16:35:41 +02001203 if (mode) {
1204 if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001205 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_MODE));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001206 error_code = 517;
1207 goto error3;
1208 }
1209 } else
Pau Espin Pedrol209eb9f2019-04-24 12:03:04 +02001210 conn->conn->mode = conn->conn->mode_orig;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001211
Philipp Maierbc0346e2018-06-07 09:52:16 +02001212 /* Set local connection options, if present */
1213 if (local_options) {
Philipp Maier8dc35972021-07-14 11:20:16 +02001214 rc = set_local_cx_options(trunk->endpoints,
Philipp Maierbc0346e2018-06-07 09:52:16 +02001215 &endp->local_options, local_options);
1216 if (rc != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001217 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1218 "MDCX: invalid local connection options!\n");
Philipp Maierbc0346e2018-06-07 09:52:16 +02001219 error_code = rc;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001220 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS));
Philipp Maierbc0346e2018-06-07 09:52:16 +02001221 goto error3;
1222 }
1223 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001224
Philipp Maierbc0346e2018-06-07 09:52:16 +02001225 /* Handle codec information and decide for a suitable codec */
Philipp Maier8dc35972021-07-14 11:20:16 +02001226 rc = handle_codec_info(conn, rq, have_sdp, false);
Philipp Maierbc0346e2018-06-07 09:52:16 +02001227 mgcp_codec_summary(conn);
1228 if (rc) {
Philipp Maieraf07f662018-02-02 11:34:02 +01001229 error_code = rc;
1230 goto error3;
Philipp Maiera390d0b2018-01-31 17:30:19 +01001231 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001232
Philipp Maierc3cc6542018-02-02 12:58:42 +01001233 /* check connection mode setting */
1234 if (conn->conn->mode != MGCP_CONN_LOOPBACK
1235 && conn->conn->mode != MGCP_CONN_RECV_ONLY
Pau Espin Pedrold6769ea2021-07-06 19:44:27 +02001236 && !mgcp_rtp_end_remote_addr_available(&conn->end)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001237 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1238 "MDCX: selected connection mode type requires an opposite end!\n");
Philipp Maierc3cc6542018-02-02 12:58:42 +01001239 error_code = 527;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001240 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC));
Philipp Maierc3cc6542018-02-02 12:58:42 +01001241 goto error3;
1242 }
1243
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001244 if (mgcp_conn_rtp_is_osmux(conn)) {
1245 OSMO_ASSERT(conn->osmux.cid_allocated);
1246 if (osmux_cid < -1) {
1247 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1248 "MDCX: Failed to parse Osmux CID!\n");
1249 goto error3;
1250 } else if (osmux_cid == -1) {
1251 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1252 "MDCX: wilcard in MDCX is not supported!\n");
1253 goto error3;
1254 } else if (osmux_cid != (int) conn->osmux.cid) {
1255 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1256 "MDCX: changing already allocated CID is not supported!\n");
1257 goto error3;
1258 }
1259 /* TODO: In the future (when we have recvCID!=sendCID), we need to
1260 tell Osmux code that osmux_cid is to be used as sendCID for
1261 that conn. */
1262 }
Philipp Maierbc0346e2018-06-07 09:52:16 +02001263
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001264 /* MDCX may have provided a new remote address, which means we may need
1265 to update our announced IP addr and re-bind our local end. This can
1266 happen for instance if MGW initially provided an IPv4 during CRCX
1267 ACK, and now MDCX tells us the remote has an IPv6 address. */
1268 mgcp_get_local_addr(new_local_addr, conn);
1269 if (strcmp(new_local_addr, conn->end.local_addr)) {
1270 osmo_strlcpy(conn->end.local_addr, new_local_addr, sizeof(conn->end.local_addr));
1271 mgcp_free_rtp_port(&conn->end);
1272 if (allocate_port(endp, conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001273 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001274 goto error3;
1275 }
1276 }
1277
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001278 if (setup_rtp_processing(endp, conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001279 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_START_RTP));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001280 goto error3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001281 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001282
Philipp Maier87bd9be2017-08-22 16:35:41 +02001283
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001284 /* policy CB */
Philipp Maier8dc35972021-07-14 11:20:16 +02001285 if (pdata->cfg->policy_cb) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001286 int rc;
Philipp Maier8dc35972021-07-14 11:20:16 +02001287 rc = pdata->cfg->policy_cb(endp, MGCP_ENDP_MDCX, pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001288 switch (rc) {
1289 case MGCP_POLICY_REJECT:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001290 LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
1291 "MDCX: rejected by policy\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001292 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_REJECTED_BY_POLICY));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001293 if (silent)
1294 goto out_silent;
Philipp Maier8dc35972021-07-14 11:20:16 +02001295 return create_err_response(endp, 400, "MDCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001296 break;
1297 case MGCP_POLICY_DEFER:
1298 /* stop processing */
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001299 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
1300 "MDCX: deferred by policy\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001301 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_DEFERRED_BY_POLICY));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001302 return NULL;
1303 break;
1304 case MGCP_POLICY_CONT:
1305 /* just continue */
1306 break;
1307 }
1308 }
1309
Philipp Maier87bd9be2017-08-22 16:35:41 +02001310 mgcp_rtp_end_config(endp, 1, &conn->end);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001311
1312 /* modify */
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001313 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
1314 "MDCX: modified conn:%s\n", mgcp_conn_dump(conn->conn));
Philipp Maier8dc35972021-07-14 11:20:16 +02001315 if (pdata->cfg->change_cb)
1316 pdata->cfg->change_cb(endp, MGCP_ENDP_MDCX);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001317
Philipp Maiere726d4f2017-11-01 10:41:34 +01001318 /* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
Philipp Maier8dc35972021-07-14 11:20:16 +02001319 OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Pau Espin Pedrolca280a12021-07-06 18:15:35 +02001320 if (conn->conn->mode & MGCP_CONN_RECV_ONLY &&
1321 mgcp_rtp_end_remote_addr_available(&conn->end) &&
Philipp Maier8dc35972021-07-14 11:20:16 +02001322 trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001323 send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001324
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001325 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_SUCCESS));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001326 if (silent)
1327 goto out_silent;
1328
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001329 LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
1330 "MDCX: connection successfully modified\n");
Philipp Maier889fe7f2020-07-06 17:44:12 +02001331 mgcp_endp_update(endp);
Philipp Maier41d59202021-07-20 15:49:00 +02001332 return create_response_with_sdp(endp, conn, "MDCX", pdata->trans, false, false);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001333error3:
Philipp Maier8dc35972021-07-14 11:20:16 +02001334 return create_err_response(endp, error_code, "MDCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001335
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001336out_silent:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001337 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "MDCX: silent exit\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001338 return NULL;
1339}
1340
Philipp Maier87bd9be2017-08-22 16:35:41 +02001341/* DLCX command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +02001342static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001343{
Philipp Maier8dc35972021-07-14 11:20:16 +02001344 struct mgcp_parse_data *pdata = rq->pdata;
1345 struct mgcp_trunk *trunk = rq->trunk;
1346 struct mgcp_endpoint *endp = rq->endp;
1347 struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_dlcx_ctr_group;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001348 int error_code = 400;
1349 int silent = 0;
1350 char *line;
1351 char stats[1048];
Philipp Maier01d24a32017-11-21 17:26:09 +01001352 const char *conn_id = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001353 struct mgcp_conn_rtp *conn = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001354
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001355 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1356 "DLCX: deleting connection ...\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001357
Philipp Maier8d6a1932020-06-18 12:19:31 +02001358 if (!mgcp_endp_avail(endp)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001359 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_AVAIL));
Philipp Maiera910a812020-08-18 15:13:33 +02001360 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1361 "DLCX: selected endpoint not available!\n");
Philipp Maier8dc35972021-07-14 11:20:16 +02001362 return create_err_response(NULL, 501, "DLCX", pdata->trans);
Philipp Maier8d6a1932020-06-18 12:19:31 +02001363 }
1364
Philipp Maier5656fbf2018-02-02 14:41:58 +01001365 /* Prohibit wildcarded requests */
1366 if (endp->wildcarded_req) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001367 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1368 "DLCX: wildcarded endpoint names not supported.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001369 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_WILDCARD));
Philipp Maier8dc35972021-07-14 11:20:16 +02001370 return create_err_response(endp, 507, "DLCX", pdata->trans);
Philipp Maier5656fbf2018-02-02 14:41:58 +01001371 }
1372
Philipp Maier87bd9be2017-08-22 16:35:41 +02001373 if (llist_count(&endp->conns) <= 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001374 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1375 "DLCX: endpoint is not holding a connection.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001376 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_NO_CONN));
Philipp Maier8dc35972021-07-14 11:20:16 +02001377 return create_err_response(endp, 515, "DLCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001378 }
1379
Philipp Maier8dc35972021-07-14 11:20:16 +02001380 for_each_line(line, pdata->save) {
Philipp Maier036612b2021-07-19 17:47:49 +02001381 if (!mgcp_check_param(endp, trunk, line))
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001382 continue;
1383
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001384 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001385 case 'C':
Harald Weltee35eeae2017-12-28 13:47:37 +01001386 if (mgcp_verify_call_id(endp, line + 3) != 0) {
1387 error_code = 516;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001388 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CALLID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001389 goto error3;
Harald Weltee35eeae2017-12-28 13:47:37 +01001390 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001391 break;
1392 case 'I':
Philipp Maier01d24a32017-11-21 17:26:09 +01001393 conn_id = (const char *)line + 3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001394 if ((error_code = mgcp_verify_ci(endp, conn_id))) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001395 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CONNID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001396 goto error3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001397 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001398 break;
1399 case 'Z':
Pau Espin Pedrol9b508f62019-06-26 13:11:22 +02001400 silent = strcasecmp("noanswer", line + 3) == 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001401 break;
1402 default:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001403 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1404 "DLCX: Unhandled MGCP option: '%c'/%d\n",
1405 line[0], line[0]);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001406 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));
Philipp Maier8dc35972021-07-14 11:20:16 +02001407 return create_err_response(NULL, 539, "DLCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001408 break;
1409 }
1410 }
1411
1412 /* policy CB */
Philipp Maier8dc35972021-07-14 11:20:16 +02001413 if (pdata->cfg->policy_cb) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001414 int rc;
Philipp Maier8dc35972021-07-14 11:20:16 +02001415 rc = pdata->cfg->policy_cb(endp, MGCP_ENDP_DLCX, pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001416 switch (rc) {
1417 case MGCP_POLICY_REJECT:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001418 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "DLCX: rejected by policy\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001419 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_REJECTED_BY_POLICY));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001420 if (silent)
1421 goto out_silent;
Philipp Maier8dc35972021-07-14 11:20:16 +02001422 return create_err_response(endp, 400, "DLCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001423 break;
1424 case MGCP_POLICY_DEFER:
1425 /* stop processing */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001426 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_DEFERRED_BY_POLICY));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001427 return NULL;
1428 break;
1429 case MGCP_POLICY_CONT:
1430 /* just continue */
1431 break;
1432 }
1433 }
1434
Philipp Maierf4c0e372017-10-11 16:06:45 +02001435 /* When no connection id is supplied, we will interpret this as a
1436 * wildcarded DLCX and drop all connections at once. (See also
1437 * RFC3435 Section F.7) */
Philipp Maier01d24a32017-11-21 17:26:09 +01001438 if (!conn_id) {
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001439 int num_conns = llist_count(&endp->conns);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001440 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1441 "DLCX: missing ci (connectionIdentifier), will remove all connections (%d total) at once\n",
1442 num_conns);
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001443
1444 if (num_conns > 0)
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001445 rate_ctr_add(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS), num_conns);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001446
Philipp Maier1355d7e2018-02-01 14:30:06 +01001447 mgcp_endp_release(endp);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001448
1449 /* Note: In this case we do not return any statistics,
1450 * as we assume that the client is not interested in
1451 * this case. */
Philipp Maier8dc35972021-07-14 11:20:16 +02001452 return create_ok_response(endp, 200, "DLCX", pdata->trans);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001453 }
1454
Philipp Maierf4c0e372017-10-11 16:06:45 +02001455 /* Find the connection */
Philipp Maier87bd9be2017-08-22 16:35:41 +02001456 conn = mgcp_conn_get_rtp(endp, conn_id);
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001457 if (!conn) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001458 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CONNID));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001459 goto error3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001460 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001461 /* save the statistics of the current connection */
1462 mgcp_format_stats(stats, sizeof(stats), conn->conn);
1463
1464 /* delete connection */
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001465 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG, "DLCX: deleting conn:%s\n",
1466 mgcp_conn_dump(conn->conn));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001467 mgcp_conn_free(endp, conn_id);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001468 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1469 "DLCX: connection successfully deleted\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +02001470
1471 /* When all connections are closed, the endpoint will be released
1472 * in order to be ready to be used by another call. */
1473 if (llist_count(&endp->conns) <= 0) {
Philipp Maier1355d7e2018-02-01 14:30:06 +01001474 mgcp_endp_release(endp);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001475 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: endpoint released\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +02001476 }
1477
Philipp Maier8dc35972021-07-14 11:20:16 +02001478 if (pdata->cfg->change_cb)
1479 pdata->cfg->change_cb(endp, MGCP_ENDP_DLCX);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001480
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001481 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001482 if (silent)
1483 goto out_silent;
Philipp Maier8dc35972021-07-14 11:20:16 +02001484 return create_ok_resp_with_param(endp, 250, "DLCX", pdata->trans, stats);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001485
1486error3:
Philipp Maier8dc35972021-07-14 11:20:16 +02001487 return create_err_response(endp, error_code, "DLCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001488
1489out_silent:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001490 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: silent exit\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001491 return NULL;
1492}
1493
Philipp Maier87bd9be2017-08-22 16:35:41 +02001494/* RSIP command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +02001495static struct msgb *handle_rsip(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001496{
Philipp Maier87bd9be2017-08-22 16:35:41 +02001497 /* TODO: Also implement the resetting of a specific endpoint
1498 * to make mgcp_send_reset_ep() work. Currently this will call
1499 * mgcp_rsip_cb() in mgw_main.c, which sets reset_endpoints=1
1500 * to make read_call_agent() reset all endpoints when called
1501 * next time. In order to selectively reset endpoints some
1502 * mechanism to distinguish which endpoint shall be resetted
1503 * is needed */
1504
1505 LOGP(DLMGCP, LOGL_NOTICE, "RSIP: resetting all endpoints ...\n");
1506
Philipp Maier8dc35972021-07-14 11:20:16 +02001507 if (rq->pdata->cfg->reset_cb)
1508 rq->pdata->cfg->reset_cb(rq->endp->trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001509 return NULL;
1510}
1511
1512static char extract_tone(const char *line)
1513{
1514 const char *str = strstr(line, "D/");
1515 if (!str)
1516 return CHAR_MAX;
1517
1518 return str[2];
1519}
1520
Philipp Maier87bd9be2017-08-22 16:35:41 +02001521/* This can request like DTMF detection and forward, fax detection... it
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001522 * can also request when the notification should be send and such. We don't
Philipp Maier87bd9be2017-08-22 16:35:41 +02001523 * do this right now. */
Philipp Maier8dc35972021-07-14 11:20:16 +02001524static struct msgb *handle_noti_req(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001525{
1526 int res = 0;
1527 char *line;
1528 char tone = CHAR_MAX;
1529
Philipp Maier87bd9be2017-08-22 16:35:41 +02001530 LOGP(DLMGCP, LOGL_NOTICE, "RQNT: processing request for notification ...\n");
1531
Philipp Maier8dc35972021-07-14 11:20:16 +02001532 for_each_line(line, rq->pdata->save) {
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001533 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001534 case 'S':
1535 tone = extract_tone(line);
1536 break;
1537 }
1538 }
1539
1540 /* we didn't see a signal request with a tone */
1541 if (tone == CHAR_MAX)
Philipp Maier8dc35972021-07-14 11:20:16 +02001542 return create_ok_response(rq->endp, 200, "RQNT", rq->pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001543
Philipp Maier8dc35972021-07-14 11:20:16 +02001544 if (rq->pdata->cfg->rqnt_cb)
1545 res = rq->pdata->cfg->rqnt_cb(rq->endp, tone);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001546
1547 return res == 0 ?
Philipp Maier8dc35972021-07-14 11:20:16 +02001548 create_ok_response(rq->endp, 200, "RQNT", rq->pdata->trans) :
1549 create_err_response(rq->endp, res, "RQNT", rq->pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001550}
1551
Philipp Maier87bd9be2017-08-22 16:35:41 +02001552/* Connection keepalive timer, will take care that dummy packets are send
Harald Welte1d1b98f2017-12-25 10:03:40 +01001553 * regularly, so that NAT connections stay open */
Philipp Maier14b27a82020-06-02 20:15:30 +02001554static void mgcp_keepalive_timer_cb(void *_trunk)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001555{
Philipp Maier14b27a82020-06-02 20:15:30 +02001556 struct mgcp_trunk *trunk = _trunk;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001557 struct mgcp_conn *conn;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001558 int i;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001559
Philipp Maiere726d4f2017-11-01 10:41:34 +01001560 LOGP(DLMGCP, LOGL_DEBUG, "triggered trunk %d keepalive timer\n",
Philipp Maier14b27a82020-06-02 20:15:30 +02001561 trunk->trunk_nr);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001562
Philipp Maiere726d4f2017-11-01 10:41:34 +01001563 /* Do not accept invalid configuration values
1564 * valid is MGCP_KEEPALIVE_NEVER, MGCP_KEEPALIVE_ONCE and
1565 * values greater 0 */
Philipp Maier14b27a82020-06-02 20:15:30 +02001566 OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Philipp Maiere726d4f2017-11-01 10:41:34 +01001567
1568 /* The dummy packet functionality has been disabled, we will exit
1569 * immediately, no further timer is scheduled, which means we will no
1570 * longer send dummy packets even when we did before */
Philipp Maier14b27a82020-06-02 20:15:30 +02001571 if (trunk->keepalive_interval == MGCP_KEEPALIVE_NEVER)
Philipp Maiere726d4f2017-11-01 10:41:34 +01001572 return;
1573
1574 /* In cases where only one dummy packet is sent, we do not need
1575 * the timer since the functions that handle the CRCX and MDCX are
1576 * triggering the sending of the dummy packet. So we behave like in
1577 * the MGCP_KEEPALIVE_NEVER case */
Philipp Maier14b27a82020-06-02 20:15:30 +02001578 if (trunk->keepalive_interval == MGCP_KEEPALIVE_ONCE)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001579 return;
1580
Philipp Maier87bd9be2017-08-22 16:35:41 +02001581 /* Send walk over all endpoints and send out dummy packets through
1582 * every connection present on each endpoint */
Philipp Maier4131a652021-07-07 14:04:34 +02001583 for (i = 0; i < trunk->number_endpoints; ++i) {
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001584 struct mgcp_endpoint *endp = trunk->endpoints[i];
Philipp Maier87bd9be2017-08-22 16:35:41 +02001585 llist_for_each_entry(conn, &endp->conns, entry) {
Pau Espin Pedrolca280a12021-07-06 18:15:35 +02001586 if (conn->type == MGCP_CONN_TYPE_RTP &&
1587 conn->mode == MGCP_CONN_RECV_ONLY &&
1588 mgcp_rtp_end_remote_addr_available(&conn->u.rtp.end))
Philipp Maier87bd9be2017-08-22 16:35:41 +02001589 send_dummy(endp, &conn->u.rtp);
1590 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001591 }
1592
Philipp Maiere726d4f2017-11-01 10:41:34 +01001593 /* Schedule the keepalive timer for the next round */
1594 LOGP(DLMGCP, LOGL_DEBUG, "rescheduling trunk %d keepalive timer\n",
Philipp Maier14b27a82020-06-02 20:15:30 +02001595 trunk->trunk_nr);
1596 osmo_timer_schedule(&trunk->keepalive_timer, trunk->keepalive_interval,
Philipp Maier87bd9be2017-08-22 16:35:41 +02001597 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001598}
1599
Philipp Maier14b27a82020-06-02 20:15:30 +02001600void mgcp_trunk_set_keepalive(struct mgcp_trunk *trunk, int interval)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001601{
Philipp Maier14b27a82020-06-02 20:15:30 +02001602 trunk->keepalive_interval = interval;
1603 osmo_timer_setup(&trunk->keepalive_timer, mgcp_keepalive_timer_cb, trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001604
1605 if (interval <= 0)
Philipp Maier14b27a82020-06-02 20:15:30 +02001606 osmo_timer_del(&trunk->keepalive_timer);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001607 else
Philipp Maier14b27a82020-06-02 20:15:30 +02001608 osmo_timer_schedule(&trunk->keepalive_timer,
1609 trunk->keepalive_interval, 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001610}
1611
Philipp Maier87bd9be2017-08-22 16:35:41 +02001612/*! allocate configuration with default values.
1613 * (called once at startup by main function) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001614struct mgcp_config *mgcp_config_alloc(void)
1615{
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001616 /* FIXME: This is unrelated to the protocol, put this in some
1617 * appropiate place! */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001618 struct mgcp_config *cfg;
1619
1620 cfg = talloc_zero(NULL, struct mgcp_config);
1621 if (!cfg) {
1622 LOGP(DLMGCP, LOGL_FATAL, "Failed to allocate config.\n");
1623 return NULL;
1624 }
1625
Philipp Maier12943ea2018-01-17 15:40:25 +01001626 osmo_strlcpy(cfg->domain, "mgw", sizeof(cfg->domain));
1627
Philipp Maier87bd9be2017-08-22 16:35:41 +02001628 cfg->net_ports.range_start = RTP_PORT_DEFAULT_RANGE_START;
1629 cfg->net_ports.range_end = RTP_PORT_DEFAULT_RANGE_END;
1630 cfg->net_ports.last_port = cfg->net_ports.range_start;
1631
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001632 cfg->source_port = 2427;
1633 cfg->source_addr = talloc_strdup(cfg, "0.0.0.0");
1634 cfg->osmux_addr = talloc_strdup(cfg, "0.0.0.0");
1635
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001636 cfg->rtp_processing_cb = &mgcp_rtp_processing_default;
1637 cfg->setup_rtp_processing_cb = &mgcp_setup_rtp_processing_default;
1638
1639 cfg->get_net_downlink_format_cb = &mgcp_get_net_downlink_format_default;
1640
Philipp Maierd19de2e2020-06-03 13:55:33 +02001641 INIT_LLIST_HEAD(&cfg->trunks);
1642
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001643 /* Allocate virtual trunk */
Philipp Maierd19de2e2020-06-03 13:55:33 +02001644 if (!mgcp_trunk_alloc(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID)) {
Harald Welte3ac604e2019-05-08 14:07:41 +02001645 talloc_free(cfg);
1646 return NULL;
1647 }
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001648
Philipp Maiera065e632021-07-09 13:22:42 +02001649 mgcp_ratectr_global_alloc(cfg);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001650
1651 return cfg;
1652}
1653
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001654static int send_agent(struct mgcp_config *cfg, const char *buf, int len)
1655{
1656 return write(cfg->gw_fd.bfd.fd, buf, len);
1657}
1658
Philipp Maier87bd9be2017-08-22 16:35:41 +02001659/*! Reset all endpoints by sending RSIP message to self.
1660 * (called by VTY)
1661 * \param[in] endp trunk endpoint
1662 * \param[in] endpoint number
1663 * \returns 0 on success, -1 on error */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001664int mgcp_send_reset_all(struct mgcp_config *cfg)
1665{
Philipp Maier12943ea2018-01-17 15:40:25 +01001666 char buf[MGCP_ENDPOINT_MAXLEN + 128];
1667 int len;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001668 int rc;
1669
Philipp Maier12943ea2018-01-17 15:40:25 +01001670 len = snprintf(buf, sizeof(buf),
1671 "RSIP 1 *@%s MGCP 1.0\r\n", cfg->domain);
1672 if (len < 0)
1673 return -1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001674
Philipp Maier12943ea2018-01-17 15:40:25 +01001675 rc = send_agent(cfg, buf, len);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001676 if (rc <= 0)
1677 return -1;
1678
1679 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001680}
1681
Philipp Maier87bd9be2017-08-22 16:35:41 +02001682/*! Reset a single endpoint by sending RSIP message to self.
1683 * (called by VTY)
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001684 * \param[in] endp to reset
Philipp Maier87bd9be2017-08-22 16:35:41 +02001685 * \returns 0 on success, -1 on error */
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001686int mgcp_send_reset_ep(struct mgcp_endpoint *endp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001687{
Philipp Maier12943ea2018-01-17 15:40:25 +01001688 char buf[MGCP_ENDPOINT_MAXLEN + 128];
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001689 int len;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001690 int rc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001691
1692 len = snprintf(buf, sizeof(buf),
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001693 "RSIP 39 %s MGCP 1.0\r\n", endp->name);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001694 if (len < 0)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001695 return -1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001696
Philipp Maier87bd9be2017-08-22 16:35:41 +02001697 rc = send_agent(endp->cfg, buf, len);
1698 if (rc <= 0)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001699 return -1;
1700
Philipp Maier87bd9be2017-08-22 16:35:41 +02001701 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001702}