blob: 5b88c7ac056e74ea5fadf3516b5c9d0cfecd5c8b [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>
Eric55fdfc22021-08-13 00:14:18 +020031#include <pthread.h>
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020032
33#include <osmocom/core/msgb.h>
34#include <osmocom/core/talloc.h>
35#include <osmocom/core/select.h>
36
Philipp Maier87bd9be2017-08-22 16:35:41 +020037#include <osmocom/mgcp/mgcp.h>
Neels Hofmeyr67793542017-09-08 04:25:16 +020038#include <osmocom/mgcp/mgcp_common.h>
Philipp Maier993ea6b2020-08-04 18:26:50 +020039#include <osmocom/mgcp/osmux.h>
40#include <osmocom/mgcp/mgcp_network.h>
41#include <osmocom/mgcp/mgcp_protocol.h>
Philipp Maier87bd9be2017-08-22 16:35:41 +020042#include <osmocom/mgcp/mgcp_stat.h>
43#include <osmocom/mgcp/mgcp_msg.h>
Philipp Maier37d11c82018-02-01 14:38:12 +010044#include <osmocom/mgcp/mgcp_endp.h>
Philipp Maierc66ab2c2020-06-02 20:55:34 +020045#include <osmocom/mgcp/mgcp_trunk.h>
Philipp Maier8970c492017-10-11 13:33:42 +020046#include <osmocom/mgcp/mgcp_sdp.h>
Philipp Maierbc0346e2018-06-07 09:52:16 +020047#include <osmocom/mgcp/mgcp_codec.h>
Stefan Sperlingba25eab2018-10-30 14:32:31 +010048#include <osmocom/mgcp/mgcp_conn.h>
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020049
Philipp Maier39889e42021-08-04 17:42:57 +020050/* Contains the last successfully resolved endpoint name. This variable is used
51 * for the unit-tests to verify that the endpoint was correctly resolved. */
52static char debug_last_endpoint_name[MGCP_ENDPOINT_MAXLEN];
53
54/* Called from unit-tests only */
55char *mgcp_debug_get_last_endpoint_name(void)
56{
57 return debug_last_endpoint_name;
58}
59
Philipp Maierf486e742021-07-19 14:56:29 +020060/* A combination of LOGPENDP and LOGPTRUNK that automatically falls back to
61 * LOGPTRUNK when the endp parameter is NULL */
62#define LOGPEPTR(endp, trunk, cat, level, fmt, args...) \
63do { \
64 if (endp) \
65 LOGPENDP(endp, cat, level, fmt, ## args); \
66 else \
67 LOGPTRUNK(trunk, cat, level, fmt, ## args); \
68} while (0)
69
Philipp Maier8dc35972021-07-14 11:20:16 +020070/* Request data passed to the request handler */
71struct mgcp_request_data {
72 /* request name (e.g. "MDCX") */
73 char name[4+1];
74
75 /* parsing results from the MGCP header (trans id, endpoint name ...) */
76 struct mgcp_parse_data *pdata;
77
78 /* pointer to endpoint resource (may be NULL for wildcarded requests) */
79 struct mgcp_endpoint *endp;
80
81 /* pointer to trunk resource */
82 struct mgcp_trunk *trunk;
83
84 /* set to true when the request has been classified as wildcarded */
85 bool wildcarded;
86
87 /* contains cause code in case of problems during endp/trunk resolution */
88 int mgcp_cause;
89};
90
Philipp Maier33d97f72021-07-14 14:53:45 +020091/* Request handler specification, here we specify an array with function
92 * pointers to the various MGCP requests implemented below */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020093struct mgcp_request {
Philipp Maier33d97f72021-07-14 14:53:45 +020094 /* request name (e.g. "MDCX") */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020095 char *name;
Philipp Maier33d97f72021-07-14 14:53:45 +020096
97 /* function pointer to the request handler */
Philipp Maier8dc35972021-07-14 11:20:16 +020098 struct msgb *(*handle_request)(struct mgcp_request_data *data);
99
Philipp Maier33d97f72021-07-14 14:53:45 +0200100 /* a human readable name that describes the request */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200101 char *debug_name;
102};
103
Philipp Maier8dc35972021-07-14 11:20:16 +0200104static struct msgb *handle_audit_endpoint(struct mgcp_request_data *data);
105static struct msgb *handle_create_con(struct mgcp_request_data *data);
106static struct msgb *handle_delete_con(struct mgcp_request_data *data);
107static struct msgb *handle_modify_con(struct mgcp_request_data *data);
108static struct msgb *handle_rsip(struct mgcp_request_data *data);
109static struct msgb *handle_noti_req(struct mgcp_request_data *data);
Philipp Maier33d97f72021-07-14 14:53:45 +0200110static const struct mgcp_request mgcp_requests[] = {
Ericee6958c2021-09-14 18:36:53 +0200111 { .name = "AUEP", .handle_request = handle_audit_endpoint, .debug_name = "AuditEndpoint" },
112 {
113 .name = "CRCX",
114 .handle_request = handle_create_con,
115 .debug_name = "CreateConnection",
116 },
117 {
118 .name = "DLCX",
119 .handle_request = handle_delete_con,
120 .debug_name = "DeleteConnection",
121 },
122 {
123 .name = "MDCX",
124 .handle_request = handle_modify_con,
125 .debug_name = "ModifiyConnection",
126 },
127 {
128 .name = "RQNT",
129 .handle_request = handle_noti_req,
130 .debug_name = "NotificationRequest",
131 },
Philipp Maier33d97f72021-07-14 14:53:45 +0200132
133 /* SPEC extension */
Ericee6958c2021-09-14 18:36:53 +0200134 {
135 .name = "RSIP",
136 .handle_request = handle_rsip,
137 .debug_name = "ReSetInProgress",
138 },
Philipp Maier33d97f72021-07-14 14:53:45 +0200139};
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200140
Philipp Maier87bd9be2017-08-22 16:35:41 +0200141/* Initalize transcoder */
142static int setup_rtp_processing(struct mgcp_endpoint *endp,
143 struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200144{
Ericfbf78d12021-08-23 22:31:39 +0200145 struct mgcp_config *cfg = endp->trunk->cfg;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200146 struct mgcp_conn_rtp *conn_src = NULL;
147 struct mgcp_conn_rtp *conn_dst = conn;
148 struct mgcp_conn *_conn;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200149
Pau Espin Pedrolfa810e82019-05-06 18:54:10 +0200150 if (conn->type != MGCP_RTP_DEFAULT && !mgcp_conn_rtp_is_osmux(conn)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200151 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
152 "RTP-setup: Endpoint is not configured as RTP default, stopping here!\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200153 return 0;
154 }
155
Philipp Maier87bd9be2017-08-22 16:35:41 +0200156 if (conn->conn->mode == MGCP_CONN_LOOPBACK) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200157 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
158 "RTP-setup: Endpoint is in loopback mode, stopping here!\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +0200159 return 0;
160 }
161
162 /* Find the "sister" connection */
163 llist_for_each_entry(_conn, &endp->conns, entry) {
164 if (_conn->id != conn->conn->id) {
165 conn_src = &_conn->u.rtp;
166 break;
167 }
168 }
169
Philipp Maieracc10352018-07-19 18:07:57 +0200170 return cfg->setup_rtp_processing_cb(endp, conn_dst, conn_src);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200171}
172
Philipp Maier87bd9be2017-08-22 16:35:41 +0200173/* Helper function to allocate some memory for responses and retransmissions */
Ericfbcf4a62021-09-09 18:02:31 +0200174static struct msgb *mgcp_msgb_alloc(void *ctx)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200175{
176 struct msgb *msg;
Ericfbcf4a62021-09-09 18:02:31 +0200177 msg = msgb_alloc_headroom_c(ctx, 4096, 128, "MGCP msg");
178
179 if (!msg) {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200180 LOGP(DLMGCP, LOGL_ERROR, "Failed to msgb for MGCP data.\n");
Ericfbcf4a62021-09-09 18:02:31 +0200181 return NULL;
182 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200183
184 return msg;
185}
186
Philipp Maier87bd9be2017-08-22 16:35:41 +0200187/* Helper function for do_retransmission() and create_resp() */
Eric958f5e72021-08-03 23:00:17 +0200188static struct msgb *create_retransmission_response(const struct mgcp_endpoint *endp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200189{
Ericfbcf4a62021-09-09 18:02:31 +0200190 struct msgb *msg = mgcp_msgb_alloc(endp->trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200191 if (!msg)
192 return NULL;
193
194 msg->l2h = msgb_put(msg, strlen(endp->last_response));
195 memcpy(msg->l2h, endp->last_response, msgb_l2len(msg));
Philipp Maier87bd9be2017-08-22 16:35:41 +0200196 mgcp_disp_msg(msg->l2h, msgb_l2len(msg), "Retransmitted response");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200197 return msg;
198}
199
Ericfbcf4a62021-09-09 18:02:31 +0200200static struct msgb *create_resp(void *msgctx, struct mgcp_endpoint *endp, int code, const char *txt, const char *msg,
201 const char *trans, const char *param, const char *sdp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200202{
203 int len;
204 struct msgb *res;
205
Ericfbcf4a62021-09-09 18:02:31 +0200206 OSMO_ASSERT(msgctx != 0);
207 res = mgcp_msgb_alloc(msgctx);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200208 if (!res)
209 return NULL;
210
Philipp Maier87bd9be2017-08-22 16:35:41 +0200211 len = snprintf((char *)res->data, 2048, "%d %s%s%s\r\n%s",
212 code, trans, txt, param ? param : "", sdp ? sdp : "");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200213 if (len < 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200214 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "Failed to sprintf MGCP response.\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200215 msgb_free(res);
216 return NULL;
217 }
218
219 res->l2h = msgb_put(res, len);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200220 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "Generated response: code=%d\n", code);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200221 mgcp_disp_msg(res->l2h, msgb_l2len(res), "Generated response");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200222
223 /*
224 * Remember the last transmission per endpoint.
225 */
226 if (endp) {
Philipp Maier14b27a82020-06-02 20:15:30 +0200227 struct mgcp_trunk *trunk = endp->trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200228 talloc_free(endp->last_response);
229 talloc_free(endp->last_trans);
Philipp Maier14b27a82020-06-02 20:15:30 +0200230 endp->last_trans = talloc_strdup(trunk->endpoints, trans);
231 endp->last_response = talloc_strndup(trunk->endpoints,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200232 (const char *)res->l2h,
233 msgb_l2len(res));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200234 }
235
236 return res;
237}
238
Ericfbcf4a62021-09-09 18:02:31 +0200239static struct msgb *create_ok_resp_with_param(void *msgctx, struct mgcp_endpoint *endp, int code, const char *msg,
240 const char *trans, const char *param)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200241{
Ericfbcf4a62021-09-09 18:02:31 +0200242 return create_resp(msgctx, endp, code, " OK", msg, trans, param, NULL);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200243}
244
Ericfbcf4a62021-09-09 18:02:31 +0200245static struct msgb *create_ok_response(void *msgctx, struct mgcp_endpoint *endp, int code, const char *msg,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200246 const char *trans)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200247{
Ericfbcf4a62021-09-09 18:02:31 +0200248 return create_ok_resp_with_param(msgctx, endp, code, msg, trans, NULL);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200249}
250
Ericfbcf4a62021-09-09 18:02:31 +0200251static struct msgb *create_err_response(void *msgctx, struct mgcp_endpoint *endp, int code, const char *msg,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200252 const char *trans)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200253{
Ericfbcf4a62021-09-09 18:02:31 +0200254 return create_resp(msgctx, endp, code, " FAIL", msg, trans, NULL, NULL);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200255}
256
Philipp Maier87bd9be2017-08-22 16:35:41 +0200257/* Format MGCP response string (with SDP attached) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200258static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200259 struct mgcp_conn_rtp *conn,
260 const char *msg,
Philipp Maier55295f72018-01-15 14:00:28 +0100261 const char *trans_id,
Philipp Maier41d59202021-07-20 15:49:00 +0200262 bool add_epname,
263 bool add_conn_id)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200264{
Pau Espin Pedrol71d42e72020-09-03 14:20:07 +0200265 /* cfg->local_ip allows overwritting the announced IP address with
266 * regards to the one we actually bind to. Useful in behind-NAT
267 * scenarios.
268 * TODO: we may want to define another local_ip_osmux var to
269 * us for OSMUX connections. Perhaps adding a new internal API to get it
270 * based on conn type.
271 */
Ericfbf78d12021-08-23 22:31:39 +0200272 const char *addr = strlen(endp->trunk->cfg->local_ip) ? endp->trunk->cfg->local_ip : conn->end.local_addr;
Philipp Maier8970c492017-10-11 13:33:42 +0200273 struct msgb *sdp;
274 int rc;
275 struct msgb *result;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200276
Ericfbcf4a62021-09-09 18:02:31 +0200277 sdp = msgb_alloc_headroom_c(endp->trunk, 4096, 128, "sdp record");
Philipp Maier8970c492017-10-11 13:33:42 +0200278 if (!sdp)
279 return NULL;
280
Philipp Maier41d59202021-07-20 15:49:00 +0200281 /* Attach optional endpoint name */
282 if (add_epname) {
283 rc = msgb_printf(sdp, "Z: %s\r\n", endp->name);
284 if (rc < 0)
285 goto error;
286 }
287
288 /* Attach optional connection id */
289 if (add_conn_id) {
290 rc = msgb_printf(sdp, "I: %s\r\n", conn->conn->id);
Philipp Maier55295f72018-01-15 14:00:28 +0100291 if (rc < 0)
292 goto error;
293 }
294
Philipp Maier3cbfb8a2018-01-22 11:39:59 +0100295 /* Attach optional OSMUX parameters */
Pau Espin Pedrolc63f15a2019-05-10 16:52:08 +0200296 if (mgcp_conn_rtp_is_osmux(conn)) {
Pau Espin Pedrol5e8d7992019-04-24 19:56:43 +0200297 rc = msgb_printf(sdp, "X-Osmux: %u\r\n", conn->osmux.cid);
Philipp Maier3cbfb8a2018-01-22 11:39:59 +0100298 if (rc < 0)
299 goto error;
300 }
301
302 /* Attach line break to separate the parameters from the SDP block */
Philipp Maierc3cfae22018-01-22 12:03:03 +0100303 rc = msgb_printf(sdp, "\r\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200304
Philipp Maier8970c492017-10-11 13:33:42 +0200305 rc = mgcp_write_response_sdp(endp, conn, sdp, addr);
306 if (rc < 0)
307 goto error;
Ericfbcf4a62021-09-09 18:02:31 +0200308 result = create_resp(endp->trunk, endp, 200, " OK", msg, trans_id, NULL, (char *)sdp->data);
Philipp Maier8970c492017-10-11 13:33:42 +0200309 msgb_free(sdp);
310 return result;
311error:
312 msgb_free(sdp);
313 return NULL;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200314}
315
Philipp Maier87bd9be2017-08-22 16:35:41 +0200316/* Send out dummy packet to keep the connection open, if the connection is an
317 * osmux connection, send the dummy packet via OSMUX */
318static void send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200319{
Philipp Maier87bd9be2017-08-22 16:35:41 +0200320 if (conn->osmux.state != OSMUX_STATE_DISABLED)
321 osmux_send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200322 else
Philipp Maier87bd9be2017-08-22 16:35:41 +0200323 mgcp_send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200324}
325
Philipp Maier87bd9be2017-08-22 16:35:41 +0200326/* handle incoming messages:
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200327 * - this can be a command (four letters, space, transaction id)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200328 * - or a response (three numbers, space, transaction id) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200329struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
330{
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200331 struct rate_ctr_group *rate_ctrs = cfg->ratectr.mgcp_general_ctr_group;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200332 struct mgcp_parse_data pdata;
Philipp Maier8dc35972021-07-14 11:20:16 +0200333 struct mgcp_request_data rq;
Harald Weltee35eeae2017-12-28 13:47:37 +0100334 int rc, i, code, handled = 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200335 struct msgb *resp = NULL;
336 char *data;
337
Philipp Maier39889e42021-08-04 17:42:57 +0200338 debug_last_endpoint_name[0] = '\0';
339
Alexander Chemeris63866002020-05-05 17:18:40 +0300340 /* Count all messages, even incorect ones */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200341 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_TOTAL));
Alexander Chemeris63866002020-05-05 17:18:40 +0300342
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200343 if (msgb_l2len(msg) < 4) {
344 LOGP(DLMGCP, LOGL_ERROR, "msg too short: %d\n", msg->len);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200345 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200346 return NULL;
347 }
348
Alexander Chemeris63866002020-05-05 17:18:40 +0300349 if (mgcp_msg_terminate_nul(msg)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200350 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200351 return NULL;
Alexander Chemeris63866002020-05-05 17:18:40 +0300352 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200353
Philipp Maier87bd9be2017-08-22 16:35:41 +0200354 mgcp_disp_msg(msg->l2h, msgb_l2len(msg), "Received message");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200355
Philipp Maier87bd9be2017-08-22 16:35:41 +0200356 /* attempt to treat it as a response */
357 if (sscanf((const char *)&msg->l2h[0], "%3d %*s", &code) == 1) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200358 LOGP(DLMGCP, LOGL_DEBUG, "Response: Code: %d\n", code);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200359 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200360 return NULL;
361 }
362
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200363
Philipp Maier8dc35972021-07-14 11:20:16 +0200364 /* Parse message, extract endpoint name and transaction identifier and request name etc. */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200365 memset(&pdata, 0, sizeof(pdata));
Philipp Maier8dc35972021-07-14 11:20:16 +0200366 memset(&rq, 0, sizeof(rq));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200367 pdata.cfg = cfg;
Philipp Maier8dc35972021-07-14 11:20:16 +0200368 memcpy(rq.name, (const char *)&msg->l2h[0], sizeof(rq.name)-1);
369 msg->l3h = &msg->l2h[4];
Philipp Maier87bd9be2017-08-22 16:35:41 +0200370 data = mgcp_strline((char *)msg->l3h, &pdata.save);
Harald Weltee35eeae2017-12-28 13:47:37 +0100371 rc = mgcp_parse_header(&pdata, data);
Harald Weltee35eeae2017-12-28 13:47:37 +0100372 if (rc < 0) {
Philipp Maier8dc35972021-07-14 11:20:16 +0200373 LOGP(DLMGCP, LOGL_ERROR, "%s: failed to parse MCGP message\n", rq.name);
374 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
Ericfbcf4a62021-09-09 18:02:31 +0200375 return create_err_response(cfg, NULL, -rc, rq.name, "000000");
Harald Welteabbb6b92017-12-28 13:13:50 +0100376 }
377
Philipp Maier8dc35972021-07-14 11:20:16 +0200378 /* Locate endpoint and trunk, if no endpoint can be located try at least to identify the trunk. */
379 rq.pdata = &pdata;
380 rq.wildcarded = mgcp_endp_is_wildcarded(pdata.epname);
381 rq.endp = mgcp_endp_by_name(&rc, pdata.epname, pdata.cfg);
382 rq.mgcp_cause = rc;
383 if (!rq.endp) {
384 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_NO_ENDPOINT));
385 if (rq.wildcarded) {
386 /* If we are unable to find the endpoint we still may be able to identify the trunk. Some
387 * request handlers will still be able to perform a useful action if the request refers to
388 * the whole trunk (wildcarded request). */
389 LOGP(DLMGCP, LOGL_NOTICE,
390 "%s: cannot find endpoint \"%s\", cause=%d -- trying to identify trunk...\n", rq.name,
391 pdata.epname, -rq.mgcp_cause);
392 rq.trunk = mgcp_trunk_by_name(pdata.cfg, pdata.epname);
393 if (!rq.trunk) {
394 LOGP(DLMGCP, LOGL_ERROR, "%s: failed to identify trunk for endpoint \"%s\" -- abort\n",
395 rq.name, pdata.epname);
Ericfbcf4a62021-09-09 18:02:31 +0200396 return create_err_response(cfg, NULL, -rq.mgcp_cause, rq.name, pdata.trans);
Philipp Maier8dc35972021-07-14 11:20:16 +0200397 }
398 } else {
399 /* If the endpoint name suggests that the request refers to a specific endpoint, then the
400 * request cannot be handled and we must stop early. */
401 LOGP(DLMGCP, LOGL_NOTICE,
402 "%s: cannot find endpoint \"%s\", cause=%d -- abort\n", rq.name,
403 pdata.epname, -rq.mgcp_cause);
Ericfbcf4a62021-09-09 18:02:31 +0200404 return create_err_response(cfg, NULL, -rq.mgcp_cause, rq.name, pdata.trans);
Philipp Maier8dc35972021-07-14 11:20:16 +0200405 }
406 } else {
Philipp Maier39889e42021-08-04 17:42:57 +0200407 osmo_strlcpy(debug_last_endpoint_name, rq.endp->name, sizeof(debug_last_endpoint_name));
Philipp Maier8dc35972021-07-14 11:20:16 +0200408 rq.trunk = rq.endp->trunk;
409 rq.mgcp_cause = 0;
410
411 /* Check if we have to retransmit a response from a previous transaction */
412 if (pdata.trans && rq.endp->last_trans && strcmp(rq.endp->last_trans, pdata.trans) == 0) {
413 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_RETRANSMITTED));
Eric958f5e72021-08-03 23:00:17 +0200414 return create_retransmission_response(rq.endp);
Philipp Maier8dc35972021-07-14 11:20:16 +0200415 }
416 }
417
418 /* Find an appropriate handler for the current request and execute it */
419 for (i = 0; i < ARRAY_SIZE(mgcp_requests); i++) {
420 if (strcmp(mgcp_requests[i].name, rq.name) == 0) {
Philipp Maier8dc35972021-07-14 11:20:16 +0200421 /* Execute request handler */
422 if (rq.endp)
423 LOGP(DLMGCP, LOGL_INFO,
424 "%s: executing request handler \"%s\" for endpoint resource \"%s\"\n", rq.name,
425 mgcp_requests[i].debug_name, rq.endp->name);
426 else
427 LOGP(DLMGCP, LOGL_INFO,
428 "%s: executing request handler \"%s\" for trunk resource of endpoint \"%s\"\n",
429 rq.name, mgcp_requests[i].debug_name, pdata.epname);
430 resp = mgcp_requests[i].handle_request(&rq);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200431 handled = 1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200432 break;
433 }
434 }
435
Philipp Maier8dc35972021-07-14 11:20:16 +0200436 /* Check if the MGCP request was handled and increment rate counters accordingly. */
Alexander Chemeris63866002020-05-05 17:18:40 +0300437 if (handled) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200438 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_HANDLED));
Alexander Chemeris63866002020-05-05 17:18:40 +0300439 } else {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200440 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_UNHANDLED));
Philipp Maier8dc35972021-07-14 11:20:16 +0200441 LOGP(DLMGCP, LOGL_ERROR, "MSG with type: '%.4s' not handled\n", &msg->l2h[0]);
Alexander Chemeris63866002020-05-05 17:18:40 +0300442 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200443
444 return resp;
445}
446
Philipp Maier87bd9be2017-08-22 16:35:41 +0200447/* AUEP command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +0200448static struct msgb *handle_audit_endpoint(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200449{
Philipp Maier8dc35972021-07-14 11:20:16 +0200450 LOGPENDP(rq->endp, DLMGCP, LOGL_NOTICE, "AUEP: auditing endpoint ...\n");
Ericee6958c2021-09-14 18:36:53 +0200451 if (!rq->endp || !mgcp_endp_avail(rq->endp)) {
452 LOGPENDP(rq->endp, DLMGCP, LOGL_ERROR, "AUEP: selected endpoint not available!\n");
453 return create_err_response(rq->trunk, NULL, 501, "AUEP", rq->pdata->trans);
454 }
455
Ericfbcf4a62021-09-09 18:02:31 +0200456 return create_ok_response(rq->trunk, rq->endp, 200, "AUEP", rq->pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200457}
458
Harald Welte1d1b98f2017-12-25 10:03:40 +0100459/* Try to find a free port by attempting to bind on it. Also handle the
Philipp Maier87bd9be2017-08-22 16:35:41 +0200460 * counter that points on the next free port. Since we have a pointer
Philipp Maierb38fb892018-05-22 13:52:21 +0200461 * to the next free port, binding should in work on the first attempt in
Pau Espin Pedrolfc806732019-04-23 00:18:43 +0200462 * general. In case of failure the next port is tried until the whole port
463 * range is tried once. */
Philipp Maier87bd9be2017-08-22 16:35:41 +0200464static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200465{
466 int i;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200467 struct mgcp_port_range *range;
Philipp Maierb38fb892018-05-22 13:52:21 +0200468 unsigned int tries;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200469
Philipp Maier87bd9be2017-08-22 16:35:41 +0200470 OSMO_ASSERT(conn);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200471
Ericfbf78d12021-08-23 22:31:39 +0200472 range = &endp->trunk->cfg->net_ports;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200473
Eric55fdfc22021-08-13 00:14:18 +0200474 pthread_mutex_lock(&range->lock);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200475 /* attempt to find a port */
Philipp Maierb38fb892018-05-22 13:52:21 +0200476 tries = (range->range_end - range->range_start) / 2;
477 for (i = 0; i < tries; ++i) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200478 int rc;
479
480 if (range->last_port >= range->range_end)
481 range->last_port = range->range_start;
482
Philipp Maier87bd9be2017-08-22 16:35:41 +0200483 rc = mgcp_bind_net_rtp_port(endp, range->last_port, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200484
485 range->last_port += 2;
486 if (rc == 0) {
Eric55fdfc22021-08-13 00:14:18 +0200487 pthread_mutex_unlock(&range->lock);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200488 return 0;
489 }
490
491 }
Eric55fdfc22021-08-13 00:14:18 +0200492 pthread_mutex_unlock(&range->lock);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200493 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
494 "Allocating a RTP/RTCP port failed %u times.\n",
495 tries);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200496 return -1;
497}
498
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200499/*! Helper function for check_local_cx_options() to get a pointer of the next
500 * lco option identifier
501 * \param[in] lco string
502 * \returns pointer to the beginning of the LCO identifier, NULL on failure */
503char *get_lco_identifier(const char *options)
504{
505 char *ptr;
506 unsigned int count = 0;
507
508 /* Jump to the end of the lco identifier */
509 ptr = strstr(options, ":");
510 if (!ptr)
511 return NULL;
512
513 /* Walk backwards until the pointer points to the beginning of the
514 * lco identifier. We know that we stand at the beginning when we
515 * are either at the beginning of the memory or see a space or
516 * comma. (this is tolerant, it will accept a:10, b:11 as well as
517 * a:10,b:11) */
518 while (1) {
519 /* Endless loop protection */
520 if (count > 10000)
521 return NULL;
522 else if (ptr < options || *ptr == ' ' || *ptr == ',') {
523 ptr++;
524 break;
525 }
526 ptr--;
527 count++;
528 }
529
530 /* Check if we got any result */
531 if (*ptr == ':')
532 return NULL;
533
534 return ptr;
535}
536
537/*! Check the LCO option. This function checks for multiple appearence of LCO
538 * options, which is illegal
539 * \param[in] ctx talloc context
540 * \param[in] lco string
541 * \returns 0 on success, -1 on failure */
542int check_local_cx_options(void *ctx, const char *options)
543{
544 int i;
545 char *options_copy;
546 char *lco_identifier;
547 char *lco_identifier_end;
548 char *next_lco_identifier;
549
550 char **lco_seen;
551 unsigned int lco_seen_n = 0;
552
553 if (!options)
554 return -1;
555
556 lco_seen =
557 (char **)talloc_zero_size(ctx, strlen(options) * sizeof(char *));
558 options_copy = talloc_strdup(ctx, options);
559 lco_identifier = options_copy;
560
561 do {
562 /* Move the lco_identifier pointer to the beginning of the
563 * current lco option identifier */
564 lco_identifier = get_lco_identifier(lco_identifier);
565 if (!lco_identifier)
566 goto error;
567
568 /* Look ahead to the next LCO option early, since we
569 * will parse destructively */
570 next_lco_identifier = strstr(lco_identifier + 1, ",");
571
572 /* Pinch off the end of the lco field identifier name
573 * and see if we still got something, also check if
574 * there is some value after the colon. */
575 lco_identifier_end = strstr(lco_identifier, ":");
576 if (!lco_identifier_end)
577 goto error;
578 if (*(lco_identifier_end + 1) == ' '
579 || *(lco_identifier_end + 1) == ','
580 || *(lco_identifier_end + 1) == '\0')
581 goto error;
582 *lco_identifier_end = '\0';
583 if (strlen(lco_identifier) == 0)
584 goto error;
585
586 /* Check if we have already seen the current field identifier
587 * before. If yes, we must bail, an LCO must only appear once
588 * in the LCO string */
589 for (i = 0; i < lco_seen_n; i++) {
Pau Espin Pedrol7eb6f2c2019-06-26 13:00:52 +0200590 if (strcasecmp(lco_seen[i], lco_identifier) == 0)
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200591 goto error;
592 }
593 lco_seen[lco_seen_n] = lco_identifier;
594 lco_seen_n++;
595
596 /* The first identifier must always be found at the beginnning
597 * of the LCO string */
598 if (lco_seen[0] != options_copy)
599 goto error;
600
601 /* Go to the next lco option */
602 lco_identifier = next_lco_identifier;
603 } while (lco_identifier);
604
605 talloc_free(lco_seen);
606 talloc_free(options_copy);
607 return 0;
608error:
609 talloc_free(lco_seen);
610 talloc_free(options_copy);
611 return -1;
612}
613
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200614/* Set the LCO from a string (see RFC 3435).
Harald Welte1d1b98f2017-12-25 10:03:40 +0100615 * The string is stored in the 'string' field. A NULL string is handled exactly
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200616 * like an empty string, the 'string' field is never NULL after this function
617 * has been called. */
Philipp Maiera390d0b2018-01-31 17:30:19 +0100618static int set_local_cx_options(void *ctx, struct mgcp_lco *lco,
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200619 const char *options)
620{
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200621 char *lco_id;
Philipp Maier8dbc9ed2018-10-26 14:50:25 +0200622 char codec[17];
Pau Espin Pedrol19539862020-09-21 11:43:41 +0200623 char nt[17];
Pau Espin Pedrol83fd8a52019-06-26 12:55:26 +0200624 int len;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200625
Philipp Maier604410c2018-06-06 10:02:16 +0200626 if (!options)
627 return 0;
628 if (strlen(options) == 0)
629 return 0;
630
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200631 /* Make sure the encoding of the LCO is consistant before we proceed */
632 if (check_local_cx_options(ctx, options) != 0) {
633 LOGP(DLMGCP, LOGL_ERROR,
634 "local CX options: Internal inconsistency in Local Connection Options!\n");
635 return 524;
636 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200637
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200638 talloc_free(lco->string);
639 lco->string = talloc_strdup(ctx, options);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200640
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200641 lco_id = lco->string;
642 while ((lco_id = get_lco_identifier(lco_id))) {
643 switch (tolower(lco_id[0])) {
644 case 'p':
645 if (sscanf(lco_id + 1, ":%d-%d",
646 &lco->pkt_period_min, &lco->pkt_period_max) == 1)
647 lco->pkt_period_max = lco->pkt_period_min;
648 break;
649 case 'a':
Pau Espin Pedrol1dc2dce2020-09-21 11:25:15 +0200650 /* FIXME: LCO also supports the negotiation of more than one codec.
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200651 * (e.g. a:PCMU;G726-32) But this implementation only supports a single
652 * codec only. */
653 if (sscanf(lco_id + 1, ":%16[^,]", codec) == 1) {
654 talloc_free(lco->codec);
Pau Espin Pedrol83fd8a52019-06-26 12:55:26 +0200655 /* MGCP header is case insensive, and we'll need
656 codec in uppercase when using it later: */
657 len = strlen(codec);
658 lco->codec = talloc_size(ctx, len + 1);
659 osmo_str_toupper_buf(lco->codec, len + 1, codec);
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200660 }
661 break;
Pau Espin Pedrol19539862020-09-21 11:43:41 +0200662 case 'n':
663 if (lco_id[1] == 't' && sscanf(lco_id + 2, ":%16[^,]", nt) == 1)
664 break;
665 /* else: fall throught to print notice log */
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200666 default:
667 LOGP(DLMGCP, LOGL_NOTICE,
668 "LCO: unhandled option: '%c'/%d in \"%s\"\n",
669 *lco_id, *lco_id, lco->string);
670 break;
671 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200672
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200673 lco_id = strchr(lco_id, ',');
674 if (!lco_id)
675 break;
Philipp Maier604410c2018-06-06 10:02:16 +0200676 }
Philipp Maier87bd9be2017-08-22 16:35:41 +0200677
678 LOGP(DLMGCP, LOGL_DEBUG,
679 "local CX options: lco->pkt_period_max: %i, lco->codec: %s\n",
680 lco->pkt_period_max, lco->codec);
Philipp Maiera390d0b2018-01-31 17:30:19 +0100681
682 /* Check if the packetization fits the 20ms raster */
683 if (lco->pkt_period_min % 20 && lco->pkt_period_max % 20) {
684 LOGP(DLMGCP, LOGL_ERROR,
685 "local CX options: packetization interval is not a multiple of 20ms!\n");
686 return 535;
687 }
688
689 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200690}
691
692void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
693 struct mgcp_rtp_end *rtp)
694{
Philipp Maier14b27a82020-06-02 20:15:30 +0200695 struct mgcp_trunk *trunk = endp->trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200696
Philipp Maier14b27a82020-06-02 20:15:30 +0200697 int patch_ssrc = expect_ssrc_change && trunk->force_constant_ssrc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200698
Philipp Maier14b27a82020-06-02 20:15:30 +0200699 rtp->force_aligned_timing = trunk->force_aligned_timing;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200700 rtp->force_constant_ssrc = patch_ssrc ? 1 : 0;
Philipp Maier14b27a82020-06-02 20:15:30 +0200701 rtp->rfc5993_hr_convert = trunk->rfc5993_hr_convert;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200702
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200703 LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
704 "Configuring RTP endpoint: local port %d%s%s\n",
705 ntohs(rtp->rtp_port),
706 rtp->force_aligned_timing ? ", force constant timing" : "",
707 rtp->force_constant_ssrc ? ", force constant ssrc" : "");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200708}
709
Pau Espin Pedrol8358c4b2021-07-07 12:41:38 +0200710uint32_t mgcp_rtp_packet_duration(const struct mgcp_endpoint *endp,
711 const struct mgcp_rtp_end *rtp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200712{
713 int f = 0;
714
715 /* Get the number of frames per channel and packet */
716 if (rtp->frames_per_packet)
717 f = rtp->frames_per_packet;
Philipp Maierbc0346e2018-06-07 09:52:16 +0200718 else if (rtp->packet_duration_ms && rtp->codec->frame_duration_num) {
719 int den = 1000 * rtp->codec->frame_duration_num;
720 f = (rtp->packet_duration_ms * rtp->codec->frame_duration_den +
Philipp Maier87bd9be2017-08-22 16:35:41 +0200721 den / 2)
722 / den;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200723 }
724
Philipp Maierbc0346e2018-06-07 09:52:16 +0200725 return rtp->codec->rate * f * rtp->codec->frame_duration_num /
726 rtp->codec->frame_duration_den;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200727}
728
Pau Espin Pedrol9fb8ddf2019-05-08 15:35:36 +0200729/*! Initializes osmux socket if not yet initialized. Parses Osmux CID from MGCP line.
730 * \param[in] endp Endpoint willing to initialize osmux
731 * \param[in] line Line X-Osmux from MGCP header msg to parse
732 * \returns OSMUX CID, -1 for wildcard, -2 on parse error, -3 on osmux initalize error
733 */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200734static int mgcp_osmux_setup(struct mgcp_endpoint *endp, const char *line)
735{
Ericfbf78d12021-08-23 22:31:39 +0200736 if (!endp->trunk->cfg->osmux_init) {
737 if (osmux_init(OSMUX_ROLE_BSC, endp->trunk->cfg) < 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200738 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "Cannot init OSMUX\n");
Pau Espin Pedrol9fb8ddf2019-05-08 15:35:36 +0200739 return -3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200740 }
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200741 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "OSMUX socket has been set up\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200742 }
743
744 return mgcp_parse_osmux_cid(line);
745}
746
Philipp Maierbc0346e2018-06-07 09:52:16 +0200747/* Process codec information contained in CRCX/MDCX */
748static int handle_codec_info(struct mgcp_conn_rtp *conn,
Philipp Maier8dc35972021-07-14 11:20:16 +0200749 struct mgcp_request_data *rq, int have_sdp, bool crcx)
Philipp Maierbc0346e2018-06-07 09:52:16 +0200750{
Philipp Maier8dc35972021-07-14 11:20:16 +0200751 struct mgcp_endpoint *endp = rq->endp;
Philipp Maierbc0346e2018-06-07 09:52:16 +0200752 int rc;
753 char *cmd;
754
755 if (crcx)
756 cmd = "CRCX";
757 else
758 cmd = "MDCX";
759
760 /* Collect codec information */
761 if (have_sdp) {
762 /* If we have SDP, we ignore the local connection options and
763 * use only the SDP information. */
764 mgcp_codec_reset_all(conn);
Philipp Maier8dc35972021-07-14 11:20:16 +0200765 rc = mgcp_parse_sdp_data(endp, conn, rq->pdata);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200766 if (rc != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200767 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
768 "%s: sdp not parseable\n", cmd);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200769
770 /* See also RFC 3661: Protocol error */
771 return 510;
772 }
773 } else if (endp->local_options.codec) {
774 /* When no SDP is available, we use the codec information from
775 * the local connection options (if present) */
776 mgcp_codec_reset_all(conn);
Philipp Maier228e5912019-03-05 13:56:59 +0100777 rc = mgcp_codec_add(conn, PTYPE_UNDEFINED, endp->local_options.codec, NULL);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200778 if (rc != 0)
779 goto error;
780 }
781
782 /* Make sure we always set a sane default codec */
783 if (conn->end.codecs_assigned == 0) {
784 /* When SDP and/or LCO did not supply any codec information,
785 * than it makes sense to pick a sane default: (payload-type 0,
786 * PCMU), see also: OS#2658 */
787 mgcp_codec_reset_all(conn);
Philipp Maier228e5912019-03-05 13:56:59 +0100788 rc = mgcp_codec_add(conn, 0, NULL, NULL);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200789 if (rc != 0)
790 goto error;
791 }
792
793 /* Make codec decision */
794 if (mgcp_codec_decide(conn) != 0)
795 goto error;
796
797 return 0;
798
799error:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200800 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
801 "%s: codec negotiation failure\n", cmd);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200802
803 /* See also RFC 3661: Codec negotiation failure */
804 return 534;
805}
806
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200807static bool parse_x_osmo_ign(struct mgcp_endpoint *endp, char *line)
808{
809 char *saveptr = NULL;
810
Pau Espin Pedrol6049a632020-09-21 11:03:21 +0200811 if (strncasecmp(line, MGCP_X_OSMO_IGN_HEADER, strlen(MGCP_X_OSMO_IGN_HEADER)))
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200812 return false;
813 line += strlen(MGCP_X_OSMO_IGN_HEADER);
814
815 while (1) {
816 char *token = strtok_r(line, " ", &saveptr);
817 line = NULL;
818 if (!token)
819 break;
820
Pau Espin Pedrol6e26c702019-06-26 13:09:39 +0200821 if (!strcasecmp(token, "C"))
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200822 endp->x_osmo_ign |= MGCP_X_OSMO_IGN_CALLID;
823 else
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200824 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "received unknown X-Osmo-IGN item '%s'\n", token);
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200825 }
826
827 return true;
828}
829
Philipp Maier87bd9be2017-08-22 16:35:41 +0200830/* CRCX command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +0200831static struct msgb *handle_create_con(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200832{
Philipp Maier8dc35972021-07-14 11:20:16 +0200833 struct mgcp_parse_data *pdata = rq->pdata;
834 struct mgcp_trunk *trunk = rq->trunk;
835 struct mgcp_endpoint *endp = rq->endp;
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200836 struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_crcx_ctr_group;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200837 int error_code = 400;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200838 const char *local_options = NULL;
839 const char *callid = NULL;
840 const char *mode = NULL;
841 char *line;
Pau Espin Pedrol2b896172019-04-24 13:47:23 +0200842 int have_sdp = 0, osmux_cid = -2;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200843 struct mgcp_conn_rtp *conn = NULL;
Philipp Maierffd75e42017-11-22 11:44:50 +0100844 struct mgcp_conn *_conn = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200845 char conn_name[512];
Philipp Maiera390d0b2018-01-31 17:30:19 +0100846 int rc;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200847
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200848 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "CRCX: creating new connection ...\n");
Philipp Maier246233d2020-08-18 15:15:24 +0200849
Ericee6958c2021-09-14 18:36:53 +0200850 /* we must have a free ep */
851 if (!endp) {
852 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_AVAIL));
853 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "CRCX: no free endpoints available!\n");
854 return create_err_response(rq->trunk, NULL, 403, "CRCX", pdata->trans);
855 }
856
Philipp Maier8d6a1932020-06-18 12:19:31 +0200857 if (!mgcp_endp_avail(endp)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200858 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_AVAIL));
Philipp Maiera910a812020-08-18 15:13:33 +0200859 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
860 "CRCX: selected endpoint not available!\n");
Ericfbcf4a62021-09-09 18:02:31 +0200861 return create_err_response(rq->trunk, NULL, 501, "CRCX", pdata->trans);
Philipp Maier8d6a1932020-06-18 12:19:31 +0200862 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200863
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200864 /* parse CallID C: and LocalParameters L: */
Philipp Maier8dc35972021-07-14 11:20:16 +0200865 for_each_line(line, pdata->save) {
Philipp Maier036612b2021-07-19 17:47:49 +0200866 if (!mgcp_check_param(endp, trunk, line))
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200867 continue;
868
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +0200869 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200870 case 'L':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200871 local_options = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200872 break;
873 case 'C':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200874 callid = (const char *)line + 3;
875 break;
876 case 'I':
Philipp Maierffd75e42017-11-22 11:44:50 +0100877 /* It is illegal to send a connection identifier
878 * together with a CRCX, the MGW will assign the
879 * connection identifier by itself on CRCX */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200880 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BAD_ACTION));
Ericfbcf4a62021-09-09 18:02:31 +0200881 return create_err_response(rq->trunk, NULL, 523, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200882 break;
883 case 'M':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200884 mode = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200885 break;
886 case 'X':
Pau Espin Pedrolc1bf4692019-05-14 16:23:24 +0200887 if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200888 /* If osmux is disabled, just skip setting it up */
Ericfbf78d12021-08-23 22:31:39 +0200889 if (!rq->endp->trunk->cfg->osmux)
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200890 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200891 osmux_cid = mgcp_osmux_setup(endp, line);
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200892 break;
893 }
894
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200895 if (parse_x_osmo_ign(endp, line))
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200896 break;
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200897
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200898 /* Ignore unknown X-headers */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200899 break;
900 case '\0':
901 have_sdp = 1;
902 goto mgcp_header_done;
903 default:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200904 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
905 "CRCX: unhandled option: '%c'/%d\n", *line, *line);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200906 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_UNHANDLED_PARAM));
Ericfbcf4a62021-09-09 18:02:31 +0200907 return create_err_response(rq->trunk, NULL, 539, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200908 break;
909 }
910 }
911
912mgcp_header_done:
Philipp Maier87bd9be2017-08-22 16:35:41 +0200913 /* Check parameters */
914 if (!callid) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200915 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
916 "CRCX: insufficient parameters, missing callid\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200917 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_MISSING_CALLID));
Ericfbcf4a62021-09-09 18:02:31 +0200918 return create_err_response(endp, endp, 516, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200919 }
920
Philipp Maier87bd9be2017-08-22 16:35:41 +0200921 if (!mode) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200922 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
923 "CRCX: insufficient parameters, missing mode\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200924 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_MODE));
Ericfbcf4a62021-09-09 18:02:31 +0200925 return create_err_response(endp, endp, 517, "CRCX", pdata->trans);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200926 }
927
Philipp Maier87bd9be2017-08-22 16:35:41 +0200928 /* Check if we are able to accept the creation of another connection */
929 if (llist_count(&endp->conns) >= endp->type->max_conns) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200930 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
931 "CRCX: endpoint full, max. %i connections allowed!\n",
932 endp->type->max_conns);
Philipp Maier14b27a82020-06-02 20:15:30 +0200933 if (trunk->force_realloc) {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200934 /* There is no more room for a connection, make some
935 * room by blindly tossing the oldest of the two two
936 * connections */
937 mgcp_conn_free_oldest(endp);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200938 } else {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200939 /* There is no more room for a connection, leave
940 * everything as it is and return with an error */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200941 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_LIMIT_EXCEEDED));
Ericfbcf4a62021-09-09 18:02:31 +0200942 return create_err_response(endp, endp, 540, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200943 }
944 }
945
Philipp Maier87bd9be2017-08-22 16:35:41 +0200946 /* Check if this endpoint already serves a call, if so, check if the
947 * callids match up so that we are sure that this is our call */
948 if (endp->callid && mgcp_verify_call_id(endp, callid)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200949 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
950 "CRCX: already seized by other call (%s)\n",
951 endp->callid);
Philipp Maier14b27a82020-06-02 20:15:30 +0200952 if (trunk->force_realloc)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200953 /* This is not our call, toss everything by releasing
954 * the entire endpoint. (rude!) */
Philipp Maier1355d7e2018-02-01 14:30:06 +0100955 mgcp_endp_release(endp);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200956 else {
957 /* This is not our call, leave everything as it is and
958 * return with an error. */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200959 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_UNKNOWN_CALLID));
Ericfbcf4a62021-09-09 18:02:31 +0200960 return create_err_response(endp, endp, 400, "CRCX", pdata->trans);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200961 }
962 }
963
Philipp Maier889fe7f2020-07-06 17:44:12 +0200964 if (!endp->callid) {
965 /* Claim endpoint resources. This will also set the callid,
966 * creating additional connections will only be possible if
967 * the callid matches up (see above). */
968 rc = mgcp_endp_claim(endp, callid);
969 if (rc != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200970 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_CLAIM));
Ericfbcf4a62021-09-09 18:02:31 +0200971 return create_err_response(endp, endp, 502, "CRCX", pdata->trans);
Philipp Maier889fe7f2020-07-06 17:44:12 +0200972 }
973 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200974
Philipp Maierffd75e42017-11-22 11:44:50 +0100975 snprintf(conn_name, sizeof(conn_name), "%s", callid);
Philipp Maier14b27a82020-06-02 20:15:30 +0200976 _conn = mgcp_conn_alloc(trunk->endpoints, endp, MGCP_CONN_TYPE_RTP, conn_name);
Philipp Maierffd75e42017-11-22 11:44:50 +0100977 if (!_conn) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200978 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
979 "CRCX: unable to allocate RTP connection\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200980 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_ALLOC_CONN));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200981 goto error2;
982
Philipp Maier87bd9be2017-08-22 16:35:41 +0200983 }
Philipp Maier889fe7f2020-07-06 17:44:12 +0200984
Philipp Maierffd75e42017-11-22 11:44:50 +0100985 conn = mgcp_conn_get_rtp(endp, _conn->id);
986 OSMO_ASSERT(conn);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200987
988 if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
989 error_code = 517;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200990 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_MODE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200991 goto error2;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200992 }
993
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200994 /* Annotate Osmux circuit ID and set it to negotiating state until this
Philipp Maier87bd9be2017-08-22 16:35:41 +0200995 * is fully set up from the dummy load. */
996 conn->osmux.state = OSMUX_STATE_DISABLED;
Pau Espin Pedrol2b896172019-04-24 13:47:23 +0200997 if (osmux_cid >= -1) { /* -1 is wilcard, alloc next avail CID */
Pau Espin Pedrol14f8a082019-05-13 13:10:06 +0200998 conn->osmux.state = OSMUX_STATE_ACTIVATING;
Pau Espin Pedrol2b896172019-04-24 13:47:23 +0200999 if (conn_osmux_allocate_cid(conn, osmux_cid) == -1) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001000 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_OSMUX));
Pau Espin Pedrol2b896172019-04-24 13:47:23 +02001001 goto error2;
1002 }
Ericfbf78d12021-08-23 22:31:39 +02001003 } else if (endp->trunk->cfg->osmux == OSMUX_USAGE_ONLY) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001004 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
1005 "CRCX: osmux only and no osmux offered\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001006 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_OSMUX));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001007 goto error2;
1008 }
1009
Philipp Maierbc0346e2018-06-07 09:52:16 +02001010 /* Set local connection options, if present */
1011 if (local_options) {
Philipp Maier8dc35972021-07-14 11:20:16 +02001012 rc = set_local_cx_options(trunk->endpoints,
Philipp Maierbc0346e2018-06-07 09:52:16 +02001013 &endp->local_options, local_options);
1014 if (rc != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001015 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
1016 "CRCX: inavlid local connection options!\n");
Philipp Maierbc0346e2018-06-07 09:52:16 +02001017 error_code = rc;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001018 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS));
Philipp Maierbc0346e2018-06-07 09:52:16 +02001019 goto error2;
1020 }
1021 }
1022
1023 /* Handle codec information and decide for a suitable codec */
Philipp Maier8dc35972021-07-14 11:20:16 +02001024 rc = handle_codec_info(conn, rq, have_sdp, true);
Philipp Maierbc0346e2018-06-07 09:52:16 +02001025 mgcp_codec_summary(conn);
1026 if (rc) {
1027 error_code = rc;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001028 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_CODEC_NEGOTIATION));
Philipp Maierbc0346e2018-06-07 09:52:16 +02001029 goto error2;
1030 }
1031
Philipp Maier14b27a82020-06-02 20:15:30 +02001032 conn->end.fmtp_extra = talloc_strdup(trunk->endpoints,
1033 trunk->audio_fmtp_extra);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001034
Philipp Maier8dc35972021-07-14 11:20:16 +02001035 if (pdata->cfg->force_ptime) {
1036 conn->end.packet_duration_ms = pdata->cfg->force_ptime;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001037 conn->end.force_output_ptime = 1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001038 }
1039
Philipp Maier1cb1e382017-11-02 17:16:04 +01001040 mgcp_rtp_end_config(endp, 0, &conn->end);
1041
Philipp Maierc3cc6542018-02-02 12:58:42 +01001042 /* check connection mode setting */
1043 if (conn->conn->mode != MGCP_CONN_LOOPBACK
1044 && conn->conn->mode != MGCP_CONN_RECV_ONLY
1045 && conn->end.rtp_port == 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001046 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
1047 "CRCX: selected connection mode type requires an opposite end!\n");
Philipp Maierc3cc6542018-02-02 12:58:42 +01001048 error_code = 527;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001049 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC));
Philipp Maierc3cc6542018-02-02 12:58:42 +01001050 goto error2;
1051 }
1052
Pau Espin Pedrol71d42e72020-09-03 14:20:07 +02001053 /* Find a local address for conn based on policy and initial SDP remote
1054 information, then find a free port for it */
1055 mgcp_get_local_addr(conn->end.local_addr, conn);
Philipp Maier1cb1e382017-11-02 17:16:04 +01001056 if (allocate_port(endp, conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001057 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
Philipp Maier1cb1e382017-11-02 17:16:04 +01001058 goto error2;
1059 }
1060
Philipp Maier87bd9be2017-08-22 16:35:41 +02001061 if (setup_rtp_processing(endp, conn) != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001062 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
1063 "CRCX: could not start RTP processing!\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001064 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_START_RTP));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001065 goto error2;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001066 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001067
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001068 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
1069 "CRCX: Creating connection: port: %u\n", conn->end.local_port);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001070
Philipp Maiere726d4f2017-11-01 10:41:34 +01001071 /* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
Philipp Maier14b27a82020-06-02 20:15:30 +02001072 OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Pau Espin Pedrolca280a12021-07-06 18:15:35 +02001073 if (conn->conn->mode & MGCP_CONN_RECV_ONLY &&
1074 mgcp_rtp_end_remote_addr_available(&conn->end) &&
1075 trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001076 send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001077
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001078 LOGPCONN(_conn, DLMGCP, LOGL_NOTICE,
1079 "CRCX: connection successfully created\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001080 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_SUCCESS));
Philipp Maier889fe7f2020-07-06 17:44:12 +02001081 mgcp_endp_update(endp);
Philipp Maier41d59202021-07-20 15:49:00 +02001082
1083 /* NOTE: Only in the virtual trunk we allow dynamic endpoint names */
1084 bool add_epname = rq->wildcarded && trunk->trunk_type == MGCP_TRUNK_VIRTUAL;
1085 return create_response_with_sdp(endp, conn, "CRCX", pdata->trans, add_epname, true);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001086error2:
Philipp Maier1355d7e2018-02-01 14:30:06 +01001087 mgcp_endp_release(endp);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001088 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1089 "CRCX: unable to create connection\n");
Ericfbcf4a62021-09-09 18:02:31 +02001090 return create_err_response(endp, endp, error_code, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001091}
1092
Philipp Maier87bd9be2017-08-22 16:35:41 +02001093/* MDCX command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +02001094static struct msgb *handle_modify_con(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001095{
Philipp Maier8dc35972021-07-14 11:20:16 +02001096 struct mgcp_parse_data *pdata = rq->pdata;
1097 struct mgcp_trunk *trunk = rq->trunk;
1098 struct mgcp_endpoint *endp = rq->endp;
1099 struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_mdcx_ctr_group;
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001100 char new_local_addr[INET6_ADDRSTRLEN];
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001101 int error_code = 500;
1102 int silent = 0;
1103 int have_sdp = 0;
1104 char *line;
1105 const char *local_options = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001106 const char *mode = NULL;
1107 struct mgcp_conn_rtp *conn = NULL;
Philipp Maier01d24a32017-11-21 17:26:09 +01001108 const char *conn_id = NULL;
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001109 int osmux_cid = -2;
Philipp Maiera390d0b2018-01-31 17:30:19 +01001110 int rc;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001111
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001112 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "MDCX: modifying existing connection ...\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001113
Philipp Maier5656fbf2018-02-02 14:41:58 +01001114 /* Prohibit wildcarded requests */
Philipp Maier8dc35972021-07-14 11:20:16 +02001115 if (rq->wildcarded) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001116 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1117 "MDCX: wildcarded endpoint names not supported.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001118 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_WILDCARD));
Ericfbcf4a62021-09-09 18:02:31 +02001119 return create_err_response(rq->trunk, endp, 507, "MDCX", pdata->trans);
Philipp Maier5656fbf2018-02-02 14:41:58 +01001120 }
1121
Ericee6958c2021-09-14 18:36:53 +02001122 if (!endp || !mgcp_endp_avail(endp)) {
1123 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_AVAIL));
1124 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "MDCX: selected endpoint not available!\n");
1125 return create_err_response(rq->trunk, NULL, 501, "MDCX", pdata->trans);
1126 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001127 if (llist_count(&endp->conns) <= 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001128 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1129 "MDCX: endpoint is not holding a connection.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001130 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_CONN));
Ericfbcf4a62021-09-09 18:02:31 +02001131 return create_err_response(endp, endp, 400, "MDCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001132 }
1133
Philipp Maier8dc35972021-07-14 11:20:16 +02001134 for_each_line(line, pdata->save) {
Philipp Maier036612b2021-07-19 17:47:49 +02001135 if (!mgcp_check_param(endp, trunk, line))
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001136 continue;
1137
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001138 switch (toupper(line[0])) {
Philipp Maier87bd9be2017-08-22 16:35:41 +02001139 case 'C':
Harald Weltee35eeae2017-12-28 13:47:37 +01001140 if (mgcp_verify_call_id(endp, line + 3) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001141 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CALLID));
Harald Weltee35eeae2017-12-28 13:47:37 +01001142 error_code = 516;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001143 goto error3;
Harald Weltee35eeae2017-12-28 13:47:37 +01001144 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001145 break;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001146 case 'I':
Philipp Maier01d24a32017-11-21 17:26:09 +01001147 conn_id = (const char *)line + 3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001148 if ((error_code = mgcp_verify_ci(endp, conn_id))) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001149 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CONNID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001150 goto error3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001151 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001152 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001153 case 'L':
Philipp Maier87bd9be2017-08-22 16:35:41 +02001154 local_options = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001155 break;
1156 case 'M':
Philipp Maier87bd9be2017-08-22 16:35:41 +02001157 mode = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001158 break;
1159 case 'Z':
Pau Espin Pedrol9b508f62019-06-26 13:11:22 +02001160 silent = strcasecmp("noanswer", line + 3) == 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001161 break;
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001162 case 'X':
Pau Espin Pedrolc1bf4692019-05-14 16:23:24 +02001163 if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001164 /* If osmux is disabled, just skip setting it up */
Ericfbf78d12021-08-23 22:31:39 +02001165 if (!endp->trunk->cfg->osmux)
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001166 break;
1167 osmux_cid = mgcp_osmux_setup(endp, line);
1168 break;
1169 }
1170 /* Ignore unknown X-headers */
1171 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001172 case '\0':
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001173 have_sdp = 1;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001174 goto mgcp_header_done;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001175 break;
1176 default:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001177 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1178 "MDCX: Unhandled MGCP option: '%c'/%d\n",
1179 line[0], line[0]);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001180 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_UNHANDLED_PARAM));
Ericfbcf4a62021-09-09 18:02:31 +02001181 return create_err_response(rq->trunk, NULL, 539, "MDCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001182 break;
1183 }
1184 }
1185
Philipp Maier87bd9be2017-08-22 16:35:41 +02001186mgcp_header_done:
Philipp Maier01d24a32017-11-21 17:26:09 +01001187 if (!conn_id) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001188 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1189 "MDCX: insufficient parameters, missing ci (connectionIdentifier)\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001190 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_CONNID));
Ericfbcf4a62021-09-09 18:02:31 +02001191 return create_err_response(endp, endp, 515, "MDCX", pdata->trans);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001192 }
1193
1194 conn = mgcp_conn_get_rtp(endp, conn_id);
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001195 if (!conn) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001196 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_CONN_NOT_FOUND));
Ericfbcf4a62021-09-09 18:02:31 +02001197 return create_err_response(endp, endp, 400, "MDCX", pdata->trans);
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001198 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001199
Oliver Smithe36b7752019-01-22 16:31:36 +01001200 mgcp_conn_watchdog_kick(conn->conn);
1201
Philipp Maier87bd9be2017-08-22 16:35:41 +02001202 if (mode) {
1203 if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001204 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_MODE));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001205 error_code = 517;
1206 goto error3;
1207 }
1208 } else
Pau Espin Pedrol209eb9f2019-04-24 12:03:04 +02001209 conn->conn->mode = conn->conn->mode_orig;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001210
Philipp Maierbc0346e2018-06-07 09:52:16 +02001211 /* Set local connection options, if present */
1212 if (local_options) {
Philipp Maier8dc35972021-07-14 11:20:16 +02001213 rc = set_local_cx_options(trunk->endpoints,
Philipp Maierbc0346e2018-06-07 09:52:16 +02001214 &endp->local_options, local_options);
1215 if (rc != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001216 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1217 "MDCX: invalid local connection options!\n");
Philipp Maierbc0346e2018-06-07 09:52:16 +02001218 error_code = rc;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001219 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS));
Philipp Maierbc0346e2018-06-07 09:52:16 +02001220 goto error3;
1221 }
1222 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001223
Philipp Maierbc0346e2018-06-07 09:52:16 +02001224 /* Handle codec information and decide for a suitable codec */
Philipp Maier8dc35972021-07-14 11:20:16 +02001225 rc = handle_codec_info(conn, rq, have_sdp, false);
Philipp Maierbc0346e2018-06-07 09:52:16 +02001226 mgcp_codec_summary(conn);
1227 if (rc) {
Philipp Maieraf07f662018-02-02 11:34:02 +01001228 error_code = rc;
1229 goto error3;
Philipp Maiera390d0b2018-01-31 17:30:19 +01001230 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001231
Philipp Maierc3cc6542018-02-02 12:58:42 +01001232 /* check connection mode setting */
1233 if (conn->conn->mode != MGCP_CONN_LOOPBACK
1234 && conn->conn->mode != MGCP_CONN_RECV_ONLY
Pau Espin Pedrold6769ea2021-07-06 19:44:27 +02001235 && !mgcp_rtp_end_remote_addr_available(&conn->end)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001236 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1237 "MDCX: selected connection mode type requires an opposite end!\n");
Philipp Maierc3cc6542018-02-02 12:58:42 +01001238 error_code = 527;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001239 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC));
Philipp Maierc3cc6542018-02-02 12:58:42 +01001240 goto error3;
1241 }
1242
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001243 if (mgcp_conn_rtp_is_osmux(conn)) {
1244 OSMO_ASSERT(conn->osmux.cid_allocated);
1245 if (osmux_cid < -1) {
1246 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1247 "MDCX: Failed to parse Osmux CID!\n");
1248 goto error3;
1249 } else if (osmux_cid == -1) {
1250 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1251 "MDCX: wilcard in MDCX is not supported!\n");
1252 goto error3;
1253 } else if (osmux_cid != (int) conn->osmux.cid) {
1254 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1255 "MDCX: changing already allocated CID is not supported!\n");
1256 goto error3;
1257 }
1258 /* TODO: In the future (when we have recvCID!=sendCID), we need to
1259 tell Osmux code that osmux_cid is to be used as sendCID for
1260 that conn. */
1261 }
Philipp Maierbc0346e2018-06-07 09:52:16 +02001262
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001263 /* MDCX may have provided a new remote address, which means we may need
1264 to update our announced IP addr and re-bind our local end. This can
1265 happen for instance if MGW initially provided an IPv4 during CRCX
1266 ACK, and now MDCX tells us the remote has an IPv6 address. */
1267 mgcp_get_local_addr(new_local_addr, conn);
1268 if (strcmp(new_local_addr, conn->end.local_addr)) {
1269 osmo_strlcpy(conn->end.local_addr, new_local_addr, sizeof(conn->end.local_addr));
1270 mgcp_free_rtp_port(&conn->end);
1271 if (allocate_port(endp, conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001272 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001273 goto error3;
1274 }
1275 }
1276
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001277 if (setup_rtp_processing(endp, conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001278 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_START_RTP));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001279 goto error3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001280 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001281
Philipp Maier87bd9be2017-08-22 16:35:41 +02001282 mgcp_rtp_end_config(endp, 1, &conn->end);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001283
1284 /* modify */
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001285 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
1286 "MDCX: modified conn:%s\n", mgcp_conn_dump(conn->conn));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001287
Philipp Maiere726d4f2017-11-01 10:41:34 +01001288 /* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
Philipp Maier8dc35972021-07-14 11:20:16 +02001289 OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Pau Espin Pedrolca280a12021-07-06 18:15:35 +02001290 if (conn->conn->mode & MGCP_CONN_RECV_ONLY &&
1291 mgcp_rtp_end_remote_addr_available(&conn->end) &&
Philipp Maier8dc35972021-07-14 11:20:16 +02001292 trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001293 send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001294
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001295 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_SUCCESS));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001296 if (silent)
1297 goto out_silent;
1298
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001299 LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
1300 "MDCX: connection successfully modified\n");
Philipp Maier889fe7f2020-07-06 17:44:12 +02001301 mgcp_endp_update(endp);
Philipp Maier41d59202021-07-20 15:49:00 +02001302 return create_response_with_sdp(endp, conn, "MDCX", pdata->trans, false, false);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001303error3:
Ericfbcf4a62021-09-09 18:02:31 +02001304 return create_err_response(endp, endp, error_code, "MDCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001305
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001306out_silent:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001307 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "MDCX: silent exit\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001308 return NULL;
1309}
1310
Philipp Maier87bd9be2017-08-22 16:35:41 +02001311/* DLCX command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +02001312static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001313{
Philipp Maier8dc35972021-07-14 11:20:16 +02001314 struct mgcp_parse_data *pdata = rq->pdata;
1315 struct mgcp_trunk *trunk = rq->trunk;
1316 struct mgcp_endpoint *endp = rq->endp;
1317 struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_dlcx_ctr_group;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001318 int error_code = 400;
1319 int silent = 0;
1320 char *line;
1321 char stats[1048];
Philipp Maier01d24a32017-11-21 17:26:09 +01001322 const char *conn_id = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001323 struct mgcp_conn_rtp *conn = NULL;
Philipp Maierf486e742021-07-19 14:56:29 +02001324 unsigned int i;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001325
Philipp Maierf486e742021-07-19 14:56:29 +02001326 /* NOTE: In this handler we can not take it for granted that the endp
1327 * pointer will be populated, however a trunk is always guaranteed. */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001328
Philipp Maierf486e742021-07-19 14:56:29 +02001329 LOGPEPTR(endp, trunk, DLMGCP, LOGL_NOTICE, "DLCX: deleting connection(s) ...\n");
1330
1331 if (endp && !mgcp_endp_avail(endp)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001332 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_AVAIL));
Philipp Maiera910a812020-08-18 15:13:33 +02001333 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1334 "DLCX: selected endpoint not available!\n");
Ericfbcf4a62021-09-09 18:02:31 +02001335 return create_err_response(rq->trunk, NULL, 501, "DLCX", pdata->trans);
Philipp Maier8d6a1932020-06-18 12:19:31 +02001336 }
1337
Philipp Maierf486e742021-07-19 14:56:29 +02001338 if (endp && !rq->wildcarded && llist_empty(&endp->conns)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001339 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1340 "DLCX: endpoint is not holding a connection.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001341 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_NO_CONN));
Ericfbcf4a62021-09-09 18:02:31 +02001342 return create_err_response(endp, endp, 515, "DLCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001343 }
1344
Ericee6958c2021-09-14 18:36:53 +02001345 /* Handle wildcarded DLCX that refers to the whole trunk. This means
1346 * that we walk over all endpoints on the trunk in order to drop all
1347 * connections on the trunk. (see also RFC3435 Annex F.7) */
1348 if (rq->wildcarded) {
1349 int num_conns = 0;
1350 for (i = 0; i < trunk->number_endpoints; i++) {
1351 num_conns += llist_count(&trunk->endpoints[i]->conns);
1352 mgcp_endp_release(trunk->endpoints[i]);
1353 }
1354 rate_ctr_add(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS), num_conns);
1355 return create_ok_response(trunk, NULL, 200, "DLCX", pdata->trans);
1356 }
1357
Philipp Maier8dc35972021-07-14 11:20:16 +02001358 for_each_line(line, pdata->save) {
Philipp Maier036612b2021-07-19 17:47:49 +02001359 if (!mgcp_check_param(endp, trunk, line))
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001360 continue;
1361
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001362 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001363 case 'C':
Philipp Maierf486e742021-07-19 14:56:29 +02001364 /* If we have no endpoint, but a call id in the request,
1365 then this request cannot be handled */
1366 if (!endp) {
1367 LOGPTRUNK(trunk, DLMGCP, LOGL_NOTICE,
1368 "cannot handle requests with call-id (C) without endpoint -- abort!");
1369 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));
Ericfbcf4a62021-09-09 18:02:31 +02001370 return create_err_response(rq->trunk, NULL, 539, "DLCX", pdata->trans);
Philipp Maierf486e742021-07-19 14:56:29 +02001371 }
1372
Harald Weltee35eeae2017-12-28 13:47:37 +01001373 if (mgcp_verify_call_id(endp, line + 3) != 0) {
1374 error_code = 516;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001375 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CALLID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001376 goto error3;
Harald Weltee35eeae2017-12-28 13:47:37 +01001377 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001378 break;
1379 case 'I':
Philipp Maierf486e742021-07-19 14:56:29 +02001380 /* If we have no endpoint, but a connection id in the request,
1381 then this request cannot be handled */
1382 if (!endp) {
1383 LOGPTRUNK(trunk, DLMGCP, LOGL_NOTICE,
1384 "cannot handle requests with conn-id (I) without endpoint -- abort!");
1385 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));
Ericfbcf4a62021-09-09 18:02:31 +02001386 return create_err_response(rq->trunk, NULL, 539, "DLCX", pdata->trans);
Philipp Maierf486e742021-07-19 14:56:29 +02001387 }
1388
Philipp Maier01d24a32017-11-21 17:26:09 +01001389 conn_id = (const char *)line + 3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001390 if ((error_code = mgcp_verify_ci(endp, conn_id))) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001391 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CONNID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001392 goto error3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001393 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001394 break;
1395 case 'Z':
Pau Espin Pedrol9b508f62019-06-26 13:11:22 +02001396 silent = strcasecmp("noanswer", line + 3) == 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001397 break;
1398 default:
Philipp Maierf486e742021-07-19 14:56:29 +02001399 LOGPEPTR(endp, trunk, DLMGCP, LOGL_NOTICE, "DLCX: Unhandled MGCP option: '%c'/%d\n",
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001400 line[0], line[0]);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001401 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));
Ericfbcf4a62021-09-09 18:02:31 +02001402 return create_err_response(rq->trunk, NULL, 539, "DLCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001403 break;
1404 }
1405 }
1406
Philipp Maierce187052021-07-23 10:58:19 +02001407 /* The logic does not permit to go past this point without having the
1408 * the endp pointer populated. */
1409 OSMO_ASSERT(endp);
1410
Philipp Maierf4c0e372017-10-11 16:06:45 +02001411 /* When no connection id is supplied, we will interpret this as a
Philipp Maierf486e742021-07-19 14:56:29 +02001412 * wildcarded DLCX that refers to the selected endpoint. This means
1413 * that we drop all connections on that specific endpoint at once.
1414 * (See also RFC3435 Section F.7) */
Philipp Maier01d24a32017-11-21 17:26:09 +01001415 if (!conn_id) {
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001416 int num_conns = llist_count(&endp->conns);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001417 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1418 "DLCX: missing ci (connectionIdentifier), will remove all connections (%d total) at once\n",
1419 num_conns);
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001420
1421 if (num_conns > 0)
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001422 rate_ctr_add(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS), num_conns);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001423
Philipp Maier1355d7e2018-02-01 14:30:06 +01001424 mgcp_endp_release(endp);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001425
1426 /* Note: In this case we do not return any statistics,
1427 * as we assume that the client is not interested in
1428 * this case. */
Ericfbcf4a62021-09-09 18:02:31 +02001429 return create_ok_response(endp, endp, 200, "DLCX", pdata->trans);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001430 }
1431
Philipp Maierf4c0e372017-10-11 16:06:45 +02001432 /* Find the connection */
Philipp Maier87bd9be2017-08-22 16:35:41 +02001433 conn = mgcp_conn_get_rtp(endp, conn_id);
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001434 if (!conn) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001435 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CONNID));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001436 goto error3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001437 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001438 /* save the statistics of the current connection */
1439 mgcp_format_stats(stats, sizeof(stats), conn->conn);
1440
1441 /* delete connection */
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001442 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG, "DLCX: deleting conn:%s\n",
1443 mgcp_conn_dump(conn->conn));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001444 mgcp_conn_free(endp, conn_id);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001445 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1446 "DLCX: connection successfully deleted\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +02001447
1448 /* When all connections are closed, the endpoint will be released
1449 * in order to be ready to be used by another call. */
1450 if (llist_count(&endp->conns) <= 0) {
Philipp Maier1355d7e2018-02-01 14:30:06 +01001451 mgcp_endp_release(endp);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001452 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: endpoint released\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +02001453 }
1454
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001455 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001456 if (silent)
1457 goto out_silent;
Ericfbcf4a62021-09-09 18:02:31 +02001458 return create_ok_resp_with_param(endp, endp, 250, "DLCX", pdata->trans, stats);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001459
1460error3:
Ericfbcf4a62021-09-09 18:02:31 +02001461 return create_err_response(endp, endp, error_code, "DLCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001462
1463out_silent:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001464 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: silent exit\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001465 return NULL;
1466}
1467
Philipp Maier87bd9be2017-08-22 16:35:41 +02001468/* RSIP command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +02001469static struct msgb *handle_rsip(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001470{
Philipp Maier87bd9be2017-08-22 16:35:41 +02001471 /* TODO: Also implement the resetting of a specific endpoint
1472 * to make mgcp_send_reset_ep() work. Currently this will call
1473 * mgcp_rsip_cb() in mgw_main.c, which sets reset_endpoints=1
1474 * to make read_call_agent() reset all endpoints when called
1475 * next time. In order to selectively reset endpoints some
1476 * mechanism to distinguish which endpoint shall be resetted
1477 * is needed */
1478
1479 LOGP(DLMGCP, LOGL_NOTICE, "RSIP: resetting all endpoints ...\n");
1480
Philipp Maier8dc35972021-07-14 11:20:16 +02001481 if (rq->pdata->cfg->reset_cb)
1482 rq->pdata->cfg->reset_cb(rq->endp->trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001483 return NULL;
1484}
1485
1486static char extract_tone(const char *line)
1487{
1488 const char *str = strstr(line, "D/");
1489 if (!str)
1490 return CHAR_MAX;
1491
1492 return str[2];
1493}
1494
Philipp Maier87bd9be2017-08-22 16:35:41 +02001495/* This can request like DTMF detection and forward, fax detection... it
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001496 * can also request when the notification should be send and such. We don't
Philipp Maier87bd9be2017-08-22 16:35:41 +02001497 * do this right now. */
Philipp Maier8dc35972021-07-14 11:20:16 +02001498static struct msgb *handle_noti_req(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001499{
1500 int res = 0;
1501 char *line;
1502 char tone = CHAR_MAX;
1503
Philipp Maier87bd9be2017-08-22 16:35:41 +02001504 LOGP(DLMGCP, LOGL_NOTICE, "RQNT: processing request for notification ...\n");
1505
Philipp Maier8dc35972021-07-14 11:20:16 +02001506 for_each_line(line, rq->pdata->save) {
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001507 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001508 case 'S':
1509 tone = extract_tone(line);
1510 break;
1511 }
1512 }
1513
1514 /* we didn't see a signal request with a tone */
1515 if (tone == CHAR_MAX)
Ericfbcf4a62021-09-09 18:02:31 +02001516 return create_ok_response(rq->endp, rq->endp, 200, "RQNT", rq->pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001517
Philipp Maier8dc35972021-07-14 11:20:16 +02001518 if (rq->pdata->cfg->rqnt_cb)
1519 res = rq->pdata->cfg->rqnt_cb(rq->endp, tone);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001520
Ericfbcf4a62021-09-09 18:02:31 +02001521 return res == 0 ? create_ok_response(rq->endp, rq->endp, 200, "RQNT", rq->pdata->trans) :
1522 create_err_response(rq->endp, rq->endp, res, "RQNT", rq->pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001523}
1524
Philipp Maier87bd9be2017-08-22 16:35:41 +02001525/* Connection keepalive timer, will take care that dummy packets are send
Harald Welte1d1b98f2017-12-25 10:03:40 +01001526 * regularly, so that NAT connections stay open */
Philipp Maier14b27a82020-06-02 20:15:30 +02001527static void mgcp_keepalive_timer_cb(void *_trunk)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001528{
Philipp Maier14b27a82020-06-02 20:15:30 +02001529 struct mgcp_trunk *trunk = _trunk;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001530 struct mgcp_conn *conn;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001531 int i;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001532
Philipp Maiere726d4f2017-11-01 10:41:34 +01001533 LOGP(DLMGCP, LOGL_DEBUG, "triggered trunk %d keepalive timer\n",
Philipp Maier14b27a82020-06-02 20:15:30 +02001534 trunk->trunk_nr);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001535
Philipp Maiere726d4f2017-11-01 10:41:34 +01001536 /* Do not accept invalid configuration values
1537 * valid is MGCP_KEEPALIVE_NEVER, MGCP_KEEPALIVE_ONCE and
1538 * values greater 0 */
Philipp Maier14b27a82020-06-02 20:15:30 +02001539 OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Philipp Maiere726d4f2017-11-01 10:41:34 +01001540
1541 /* The dummy packet functionality has been disabled, we will exit
1542 * immediately, no further timer is scheduled, which means we will no
1543 * longer send dummy packets even when we did before */
Philipp Maier14b27a82020-06-02 20:15:30 +02001544 if (trunk->keepalive_interval == MGCP_KEEPALIVE_NEVER)
Philipp Maiere726d4f2017-11-01 10:41:34 +01001545 return;
1546
1547 /* In cases where only one dummy packet is sent, we do not need
1548 * the timer since the functions that handle the CRCX and MDCX are
1549 * triggering the sending of the dummy packet. So we behave like in
1550 * the MGCP_KEEPALIVE_NEVER case */
Philipp Maier14b27a82020-06-02 20:15:30 +02001551 if (trunk->keepalive_interval == MGCP_KEEPALIVE_ONCE)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001552 return;
1553
Philipp Maier87bd9be2017-08-22 16:35:41 +02001554 /* Send walk over all endpoints and send out dummy packets through
1555 * every connection present on each endpoint */
Philipp Maier4131a652021-07-07 14:04:34 +02001556 for (i = 0; i < trunk->number_endpoints; ++i) {
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001557 struct mgcp_endpoint *endp = trunk->endpoints[i];
Philipp Maier87bd9be2017-08-22 16:35:41 +02001558 llist_for_each_entry(conn, &endp->conns, entry) {
Pau Espin Pedrolca280a12021-07-06 18:15:35 +02001559 if (conn->type == MGCP_CONN_TYPE_RTP &&
1560 conn->mode == MGCP_CONN_RECV_ONLY &&
1561 mgcp_rtp_end_remote_addr_available(&conn->u.rtp.end))
Philipp Maier87bd9be2017-08-22 16:35:41 +02001562 send_dummy(endp, &conn->u.rtp);
1563 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001564 }
1565
Philipp Maiere726d4f2017-11-01 10:41:34 +01001566 /* Schedule the keepalive timer for the next round */
1567 LOGP(DLMGCP, LOGL_DEBUG, "rescheduling trunk %d keepalive timer\n",
Philipp Maier14b27a82020-06-02 20:15:30 +02001568 trunk->trunk_nr);
1569 osmo_timer_schedule(&trunk->keepalive_timer, trunk->keepalive_interval,
Philipp Maier87bd9be2017-08-22 16:35:41 +02001570 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001571}
1572
Philipp Maier14b27a82020-06-02 20:15:30 +02001573void mgcp_trunk_set_keepalive(struct mgcp_trunk *trunk, int interval)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001574{
Philipp Maier14b27a82020-06-02 20:15:30 +02001575 trunk->keepalive_interval = interval;
1576 osmo_timer_setup(&trunk->keepalive_timer, mgcp_keepalive_timer_cb, trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001577
1578 if (interval <= 0)
Philipp Maier14b27a82020-06-02 20:15:30 +02001579 osmo_timer_del(&trunk->keepalive_timer);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001580 else
Philipp Maier14b27a82020-06-02 20:15:30 +02001581 osmo_timer_schedule(&trunk->keepalive_timer,
1582 trunk->keepalive_interval, 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001583}
1584
Philipp Maier38533ba2021-07-29 17:38:34 +02001585/* Free config, this function is automatically called by talloc_free when the configuration is freed. */
1586static int config_free_talloc_destructor(struct mgcp_config *cfg)
1587{
1588 mgcp_ratectr_global_free(cfg);
1589 return 0;
1590}
1591
Philipp Maier87bd9be2017-08-22 16:35:41 +02001592/*! allocate configuration with default values.
1593 * (called once at startup by main function) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001594struct mgcp_config *mgcp_config_alloc(void)
1595{
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001596 /* FIXME: This is unrelated to the protocol, put this in some
1597 * appropiate place! */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001598 struct mgcp_config *cfg;
1599
1600 cfg = talloc_zero(NULL, struct mgcp_config);
1601 if (!cfg) {
1602 LOGP(DLMGCP, LOGL_FATAL, "Failed to allocate config.\n");
1603 return NULL;
1604 }
1605
Philipp Maier12943ea2018-01-17 15:40:25 +01001606 osmo_strlcpy(cfg->domain, "mgw", sizeof(cfg->domain));
1607
Eric55fdfc22021-08-13 00:14:18 +02001608 cfg->net_ports.lock = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001609 cfg->net_ports.range_start = RTP_PORT_DEFAULT_RANGE_START;
1610 cfg->net_ports.range_end = RTP_PORT_DEFAULT_RANGE_END;
1611 cfg->net_ports.last_port = cfg->net_ports.range_start;
1612
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001613 cfg->source_port = 2427;
Eric2764bdb2021-08-23 22:11:47 +02001614 osmo_strlcpy(cfg->source_addr, "0.0.0.0", sizeof(cfg->source_addr));
1615 osmo_strlcpy(cfg->osmux_addr, "0.0.0.0", sizeof(cfg->osmux_addr));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001616
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001617 cfg->rtp_processing_cb = &mgcp_rtp_processing_default;
1618 cfg->setup_rtp_processing_cb = &mgcp_setup_rtp_processing_default;
1619
1620 cfg->get_net_downlink_format_cb = &mgcp_get_net_downlink_format_default;
1621
Philipp Maierd19de2e2020-06-03 13:55:33 +02001622 INIT_LLIST_HEAD(&cfg->trunks);
1623
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001624 /* Allocate virtual trunk */
Philipp Maierd19de2e2020-06-03 13:55:33 +02001625 if (!mgcp_trunk_alloc(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID)) {
Harald Welte3ac604e2019-05-08 14:07:41 +02001626 talloc_free(cfg);
1627 return NULL;
1628 }
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001629
Philipp Maiera065e632021-07-09 13:22:42 +02001630 mgcp_ratectr_global_alloc(cfg);
Philipp Maier38533ba2021-07-29 17:38:34 +02001631 talloc_set_destructor(cfg, config_free_talloc_destructor);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001632
1633 return cfg;
1634}
1635
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001636static int send_agent(struct mgcp_config *cfg, const char *buf, int len)
1637{
1638 return write(cfg->gw_fd.bfd.fd, buf, len);
1639}
1640
Philipp Maier87bd9be2017-08-22 16:35:41 +02001641/*! Reset all endpoints by sending RSIP message to self.
1642 * (called by VTY)
1643 * \param[in] endp trunk endpoint
1644 * \param[in] endpoint number
1645 * \returns 0 on success, -1 on error */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001646int mgcp_send_reset_all(struct mgcp_config *cfg)
1647{
Philipp Maier12943ea2018-01-17 15:40:25 +01001648 char buf[MGCP_ENDPOINT_MAXLEN + 128];
1649 int len;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001650 int rc;
1651
Philipp Maier12943ea2018-01-17 15:40:25 +01001652 len = snprintf(buf, sizeof(buf),
1653 "RSIP 1 *@%s MGCP 1.0\r\n", cfg->domain);
1654 if (len < 0)
1655 return -1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001656
Philipp Maier12943ea2018-01-17 15:40:25 +01001657 rc = send_agent(cfg, buf, len);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001658 if (rc <= 0)
1659 return -1;
1660
1661 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001662}
1663
Philipp Maier87bd9be2017-08-22 16:35:41 +02001664/*! Reset a single endpoint by sending RSIP message to self.
1665 * (called by VTY)
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001666 * \param[in] endp to reset
Philipp Maier87bd9be2017-08-22 16:35:41 +02001667 * \returns 0 on success, -1 on error */
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001668int mgcp_send_reset_ep(struct mgcp_endpoint *endp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001669{
Philipp Maier12943ea2018-01-17 15:40:25 +01001670 char buf[MGCP_ENDPOINT_MAXLEN + 128];
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001671 int len;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001672 int rc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001673
1674 len = snprintf(buf, sizeof(buf),
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001675 "RSIP 39 %s MGCP 1.0\r\n", endp->name);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001676 if (len < 0)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001677 return -1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001678
Ericfbf78d12021-08-23 22:31:39 +02001679 rc = send_agent(endp->trunk->cfg, buf, len);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001680 if (rc <= 0)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001681 return -1;
1682
Philipp Maier87bd9be2017-08-22 16:35:41 +02001683 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001684}