blob: f4cdd761f47f6add6f0a33ad750d69b5553f5df2 [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
100 /* true if the request requires an endpoint, false if only a trunk
101 * is sufficient. (corner cases, e.g. wildcarded DLCX) */
102 bool require_endp;
Philipp Maier33d97f72021-07-14 14:53:45 +0200103
104 /* a human readable name that describes the request */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200105 char *debug_name;
106};
107
Philipp Maier8dc35972021-07-14 11:20:16 +0200108static struct msgb *handle_audit_endpoint(struct mgcp_request_data *data);
109static struct msgb *handle_create_con(struct mgcp_request_data *data);
110static struct msgb *handle_delete_con(struct mgcp_request_data *data);
111static struct msgb *handle_modify_con(struct mgcp_request_data *data);
112static struct msgb *handle_rsip(struct mgcp_request_data *data);
113static struct msgb *handle_noti_req(struct mgcp_request_data *data);
Philipp Maier33d97f72021-07-14 14:53:45 +0200114static const struct mgcp_request mgcp_requests[] = {
115 { .name = "AUEP",
116 .handle_request = handle_audit_endpoint,
Philipp Maier8dc35972021-07-14 11:20:16 +0200117 .debug_name = "AuditEndpoint",
118 .require_endp = true },
Philipp Maier33d97f72021-07-14 14:53:45 +0200119 { .name = "CRCX",
120 .handle_request = handle_create_con,
Philipp Maier8dc35972021-07-14 11:20:16 +0200121 .debug_name = "CreateConnection",
122 .require_endp = true },
Philipp Maier33d97f72021-07-14 14:53:45 +0200123 { .name = "DLCX",
124 .handle_request = handle_delete_con,
Philipp Maier8dc35972021-07-14 11:20:16 +0200125 .debug_name = "DeleteConnection",
Philipp Maierf486e742021-07-19 14:56:29 +0200126 .require_endp = false },
Philipp Maier33d97f72021-07-14 14:53:45 +0200127 { .name = "MDCX",
128 .handle_request = handle_modify_con,
Philipp Maier8dc35972021-07-14 11:20:16 +0200129 .debug_name = "ModifiyConnection",
130 .require_endp = true },
Philipp Maier33d97f72021-07-14 14:53:45 +0200131 { .name = "RQNT",
132 .handle_request = handle_noti_req,
Philipp Maier8dc35972021-07-14 11:20:16 +0200133 .debug_name = "NotificationRequest",
134 .require_endp = true },
Philipp Maier33d97f72021-07-14 14:53:45 +0200135
136 /* SPEC extension */
137 { .name = "RSIP",
138 .handle_request = handle_rsip,
Philipp Maier8dc35972021-07-14 11:20:16 +0200139 .debug_name = "ReSetInProgress",
140 .require_endp = true },
Philipp Maier33d97f72021-07-14 14:53:45 +0200141};
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200142
Philipp Maier87bd9be2017-08-22 16:35:41 +0200143/* Initalize transcoder */
144static int setup_rtp_processing(struct mgcp_endpoint *endp,
145 struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200146{
Ericfbf78d12021-08-23 22:31:39 +0200147 struct mgcp_config *cfg = endp->trunk->cfg;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200148 struct mgcp_conn_rtp *conn_src = NULL;
149 struct mgcp_conn_rtp *conn_dst = conn;
150 struct mgcp_conn *_conn;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200151
Pau Espin Pedrolfa810e82019-05-06 18:54:10 +0200152 if (conn->type != MGCP_RTP_DEFAULT && !mgcp_conn_rtp_is_osmux(conn)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200153 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
154 "RTP-setup: Endpoint is not configured as RTP default, stopping here!\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200155 return 0;
156 }
157
Philipp Maier87bd9be2017-08-22 16:35:41 +0200158 if (conn->conn->mode == MGCP_CONN_LOOPBACK) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200159 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
160 "RTP-setup: Endpoint is in loopback mode, stopping here!\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +0200161 return 0;
162 }
163
164 /* Find the "sister" connection */
165 llist_for_each_entry(_conn, &endp->conns, entry) {
166 if (_conn->id != conn->conn->id) {
167 conn_src = &_conn->u.rtp;
168 break;
169 }
170 }
171
Philipp Maieracc10352018-07-19 18:07:57 +0200172 return cfg->setup_rtp_processing_cb(endp, conn_dst, conn_src);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200173}
174
Philipp Maier87bd9be2017-08-22 16:35:41 +0200175/* Helper function to allocate some memory for responses and retransmissions */
Ericfbcf4a62021-09-09 18:02:31 +0200176static struct msgb *mgcp_msgb_alloc(void *ctx)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200177{
178 struct msgb *msg;
Ericfbcf4a62021-09-09 18:02:31 +0200179 msg = msgb_alloc_headroom_c(ctx, 4096, 128, "MGCP msg");
180
181 if (!msg) {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200182 LOGP(DLMGCP, LOGL_ERROR, "Failed to msgb for MGCP data.\n");
Ericfbcf4a62021-09-09 18:02:31 +0200183 return NULL;
184 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200185
186 return msg;
187}
188
Philipp Maier87bd9be2017-08-22 16:35:41 +0200189/* Helper function for do_retransmission() and create_resp() */
Eric958f5e72021-08-03 23:00:17 +0200190static struct msgb *create_retransmission_response(const struct mgcp_endpoint *endp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200191{
Ericfbcf4a62021-09-09 18:02:31 +0200192 struct msgb *msg = mgcp_msgb_alloc(endp->trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200193 if (!msg)
194 return NULL;
195
196 msg->l2h = msgb_put(msg, strlen(endp->last_response));
197 memcpy(msg->l2h, endp->last_response, msgb_l2len(msg));
Philipp Maier87bd9be2017-08-22 16:35:41 +0200198 mgcp_disp_msg(msg->l2h, msgb_l2len(msg), "Retransmitted response");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200199 return msg;
200}
201
Ericfbcf4a62021-09-09 18:02:31 +0200202static struct msgb *create_resp(void *msgctx, struct mgcp_endpoint *endp, int code, const char *txt, const char *msg,
203 const char *trans, const char *param, const char *sdp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200204{
205 int len;
206 struct msgb *res;
207
Ericfbcf4a62021-09-09 18:02:31 +0200208 OSMO_ASSERT(msgctx != 0);
209 res = mgcp_msgb_alloc(msgctx);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200210 if (!res)
211 return NULL;
212
Philipp Maier87bd9be2017-08-22 16:35:41 +0200213 len = snprintf((char *)res->data, 2048, "%d %s%s%s\r\n%s",
214 code, trans, txt, param ? param : "", sdp ? sdp : "");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200215 if (len < 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200216 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "Failed to sprintf MGCP response.\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200217 msgb_free(res);
218 return NULL;
219 }
220
221 res->l2h = msgb_put(res, len);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200222 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "Generated response: code=%d\n", code);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200223 mgcp_disp_msg(res->l2h, msgb_l2len(res), "Generated response");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200224
225 /*
226 * Remember the last transmission per endpoint.
227 */
228 if (endp) {
Philipp Maier14b27a82020-06-02 20:15:30 +0200229 struct mgcp_trunk *trunk = endp->trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200230 talloc_free(endp->last_response);
231 talloc_free(endp->last_trans);
Philipp Maier14b27a82020-06-02 20:15:30 +0200232 endp->last_trans = talloc_strdup(trunk->endpoints, trans);
233 endp->last_response = talloc_strndup(trunk->endpoints,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200234 (const char *)res->l2h,
235 msgb_l2len(res));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200236 }
237
238 return res;
239}
240
Ericfbcf4a62021-09-09 18:02:31 +0200241static struct msgb *create_ok_resp_with_param(void *msgctx, struct mgcp_endpoint *endp, int code, const char *msg,
242 const char *trans, const char *param)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200243{
Ericfbcf4a62021-09-09 18:02:31 +0200244 return create_resp(msgctx, endp, code, " OK", msg, trans, param, NULL);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200245}
246
Ericfbcf4a62021-09-09 18:02:31 +0200247static struct msgb *create_ok_response(void *msgctx, struct mgcp_endpoint *endp, int code, const char *msg,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200248 const char *trans)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200249{
Ericfbcf4a62021-09-09 18:02:31 +0200250 return create_ok_resp_with_param(msgctx, endp, code, msg, trans, NULL);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200251}
252
Ericfbcf4a62021-09-09 18:02:31 +0200253static struct msgb *create_err_response(void *msgctx, struct mgcp_endpoint *endp, int code, const char *msg,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200254 const char *trans)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200255{
Ericfbcf4a62021-09-09 18:02:31 +0200256 return create_resp(msgctx, endp, code, " FAIL", msg, trans, NULL, NULL);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200257}
258
Philipp Maier87bd9be2017-08-22 16:35:41 +0200259/* Format MGCP response string (with SDP attached) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200260static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200261 struct mgcp_conn_rtp *conn,
262 const char *msg,
Philipp Maier55295f72018-01-15 14:00:28 +0100263 const char *trans_id,
Philipp Maier41d59202021-07-20 15:49:00 +0200264 bool add_epname,
265 bool add_conn_id)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200266{
Pau Espin Pedrol71d42e72020-09-03 14:20:07 +0200267 /* cfg->local_ip allows overwritting the announced IP address with
268 * regards to the one we actually bind to. Useful in behind-NAT
269 * scenarios.
270 * TODO: we may want to define another local_ip_osmux var to
271 * us for OSMUX connections. Perhaps adding a new internal API to get it
272 * based on conn type.
273 */
Ericfbf78d12021-08-23 22:31:39 +0200274 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 +0200275 struct msgb *sdp;
276 int rc;
277 struct msgb *result;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200278
Ericfbcf4a62021-09-09 18:02:31 +0200279 sdp = msgb_alloc_headroom_c(endp->trunk, 4096, 128, "sdp record");
Philipp Maier8970c492017-10-11 13:33:42 +0200280 if (!sdp)
281 return NULL;
282
Philipp Maier41d59202021-07-20 15:49:00 +0200283 /* Attach optional endpoint name */
284 if (add_epname) {
285 rc = msgb_printf(sdp, "Z: %s\r\n", endp->name);
286 if (rc < 0)
287 goto error;
288 }
289
290 /* Attach optional connection id */
291 if (add_conn_id) {
292 rc = msgb_printf(sdp, "I: %s\r\n", conn->conn->id);
Philipp Maier55295f72018-01-15 14:00:28 +0100293 if (rc < 0)
294 goto error;
295 }
296
Philipp Maier3cbfb8a2018-01-22 11:39:59 +0100297 /* Attach optional OSMUX parameters */
Pau Espin Pedrolc63f15a2019-05-10 16:52:08 +0200298 if (mgcp_conn_rtp_is_osmux(conn)) {
Pau Espin Pedrol5e8d7992019-04-24 19:56:43 +0200299 rc = msgb_printf(sdp, "X-Osmux: %u\r\n", conn->osmux.cid);
Philipp Maier3cbfb8a2018-01-22 11:39:59 +0100300 if (rc < 0)
301 goto error;
302 }
303
304 /* Attach line break to separate the parameters from the SDP block */
Philipp Maierc3cfae22018-01-22 12:03:03 +0100305 rc = msgb_printf(sdp, "\r\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200306
Philipp Maier8970c492017-10-11 13:33:42 +0200307 rc = mgcp_write_response_sdp(endp, conn, sdp, addr);
308 if (rc < 0)
309 goto error;
Ericfbcf4a62021-09-09 18:02:31 +0200310 result = create_resp(endp->trunk, endp, 200, " OK", msg, trans_id, NULL, (char *)sdp->data);
Philipp Maier8970c492017-10-11 13:33:42 +0200311 msgb_free(sdp);
312 return result;
313error:
314 msgb_free(sdp);
315 return NULL;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200316}
317
Philipp Maier87bd9be2017-08-22 16:35:41 +0200318/* Send out dummy packet to keep the connection open, if the connection is an
319 * osmux connection, send the dummy packet via OSMUX */
320static void send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200321{
Philipp Maier87bd9be2017-08-22 16:35:41 +0200322 if (conn->osmux.state != OSMUX_STATE_DISABLED)
323 osmux_send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200324 else
Philipp Maier87bd9be2017-08-22 16:35:41 +0200325 mgcp_send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200326}
327
Philipp Maier87bd9be2017-08-22 16:35:41 +0200328/* handle incoming messages:
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200329 * - this can be a command (four letters, space, transaction id)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200330 * - or a response (three numbers, space, transaction id) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200331struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
332{
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200333 struct rate_ctr_group *rate_ctrs = cfg->ratectr.mgcp_general_ctr_group;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200334 struct mgcp_parse_data pdata;
Philipp Maier8dc35972021-07-14 11:20:16 +0200335 struct mgcp_request_data rq;
Harald Weltee35eeae2017-12-28 13:47:37 +0100336 int rc, i, code, handled = 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200337 struct msgb *resp = NULL;
338 char *data;
339
Philipp Maier39889e42021-08-04 17:42:57 +0200340 debug_last_endpoint_name[0] = '\0';
341
Alexander Chemeris63866002020-05-05 17:18:40 +0300342 /* Count all messages, even incorect ones */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200343 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_TOTAL));
Alexander Chemeris63866002020-05-05 17:18:40 +0300344
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200345 if (msgb_l2len(msg) < 4) {
346 LOGP(DLMGCP, LOGL_ERROR, "msg too short: %d\n", msg->len);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200347 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200348 return NULL;
349 }
350
Alexander Chemeris63866002020-05-05 17:18:40 +0300351 if (mgcp_msg_terminate_nul(msg)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200352 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200353 return NULL;
Alexander Chemeris63866002020-05-05 17:18:40 +0300354 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200355
Philipp Maier87bd9be2017-08-22 16:35:41 +0200356 mgcp_disp_msg(msg->l2h, msgb_l2len(msg), "Received message");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200357
Philipp Maier87bd9be2017-08-22 16:35:41 +0200358 /* attempt to treat it as a response */
359 if (sscanf((const char *)&msg->l2h[0], "%3d %*s", &code) == 1) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200360 LOGP(DLMGCP, LOGL_DEBUG, "Response: Code: %d\n", code);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200361 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200362 return NULL;
363 }
364
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200365
Philipp Maier8dc35972021-07-14 11:20:16 +0200366 /* Parse message, extract endpoint name and transaction identifier and request name etc. */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200367 memset(&pdata, 0, sizeof(pdata));
Philipp Maier8dc35972021-07-14 11:20:16 +0200368 memset(&rq, 0, sizeof(rq));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200369 pdata.cfg = cfg;
Philipp Maier8dc35972021-07-14 11:20:16 +0200370 memcpy(rq.name, (const char *)&msg->l2h[0], sizeof(rq.name)-1);
371 msg->l3h = &msg->l2h[4];
Philipp Maier87bd9be2017-08-22 16:35:41 +0200372 data = mgcp_strline((char *)msg->l3h, &pdata.save);
Harald Weltee35eeae2017-12-28 13:47:37 +0100373 rc = mgcp_parse_header(&pdata, data);
Harald Weltee35eeae2017-12-28 13:47:37 +0100374 if (rc < 0) {
Philipp Maier8dc35972021-07-14 11:20:16 +0200375 LOGP(DLMGCP, LOGL_ERROR, "%s: failed to parse MCGP message\n", rq.name);
376 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_MSG_PARSE));
Ericfbcf4a62021-09-09 18:02:31 +0200377 return create_err_response(cfg, NULL, -rc, rq.name, "000000");
Harald Welteabbb6b92017-12-28 13:13:50 +0100378 }
379
Philipp Maier8dc35972021-07-14 11:20:16 +0200380 /* Locate endpoint and trunk, if no endpoint can be located try at least to identify the trunk. */
381 rq.pdata = &pdata;
382 rq.wildcarded = mgcp_endp_is_wildcarded(pdata.epname);
383 rq.endp = mgcp_endp_by_name(&rc, pdata.epname, pdata.cfg);
384 rq.mgcp_cause = rc;
385 if (!rq.endp) {
386 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_FAIL_NO_ENDPOINT));
387 if (rq.wildcarded) {
388 /* If we are unable to find the endpoint we still may be able to identify the trunk. Some
389 * request handlers will still be able to perform a useful action if the request refers to
390 * the whole trunk (wildcarded request). */
391 LOGP(DLMGCP, LOGL_NOTICE,
392 "%s: cannot find endpoint \"%s\", cause=%d -- trying to identify trunk...\n", rq.name,
393 pdata.epname, -rq.mgcp_cause);
394 rq.trunk = mgcp_trunk_by_name(pdata.cfg, pdata.epname);
395 if (!rq.trunk) {
396 LOGP(DLMGCP, LOGL_ERROR, "%s: failed to identify trunk for endpoint \"%s\" -- abort\n",
397 rq.name, pdata.epname);
Ericfbcf4a62021-09-09 18:02:31 +0200398 return create_err_response(cfg, NULL, -rq.mgcp_cause, rq.name, pdata.trans);
Philipp Maier8dc35972021-07-14 11:20:16 +0200399 }
400 } else {
401 /* If the endpoint name suggests that the request refers to a specific endpoint, then the
402 * request cannot be handled and we must stop early. */
403 LOGP(DLMGCP, LOGL_NOTICE,
404 "%s: cannot find endpoint \"%s\", cause=%d -- abort\n", rq.name,
405 pdata.epname, -rq.mgcp_cause);
Ericfbcf4a62021-09-09 18:02:31 +0200406 return create_err_response(cfg, NULL, -rq.mgcp_cause, rq.name, pdata.trans);
Philipp Maier8dc35972021-07-14 11:20:16 +0200407 }
408 } else {
Philipp Maier39889e42021-08-04 17:42:57 +0200409 osmo_strlcpy(debug_last_endpoint_name, rq.endp->name, sizeof(debug_last_endpoint_name));
Philipp Maier8dc35972021-07-14 11:20:16 +0200410 rq.trunk = rq.endp->trunk;
411 rq.mgcp_cause = 0;
412
413 /* Check if we have to retransmit a response from a previous transaction */
414 if (pdata.trans && rq.endp->last_trans && strcmp(rq.endp->last_trans, pdata.trans) == 0) {
415 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_RETRANSMITTED));
Eric958f5e72021-08-03 23:00:17 +0200416 return create_retransmission_response(rq.endp);
Philipp Maier8dc35972021-07-14 11:20:16 +0200417 }
418 }
419
420 /* Find an appropriate handler for the current request and execute it */
421 for (i = 0; i < ARRAY_SIZE(mgcp_requests); i++) {
422 if (strcmp(mgcp_requests[i].name, rq.name) == 0) {
423 /* Check if the request requires and endpoint, if yes, check if we have it, otherwise don't
424 * execute the request handler. */
425 if (mgcp_requests[i].require_endp && !rq.endp) {
426 LOGP(DLMGCP, LOGL_ERROR,
427 "%s: the request handler \"%s\" requires an endpoint resource for \"%s\", which is not available -- abort\n",
428 rq.name, mgcp_requests[i].debug_name, pdata.epname);
Ericfbcf4a62021-09-09 18:02:31 +0200429 return create_err_response(rq.trunk, NULL, -rq.mgcp_cause, rq.name, pdata.trans);
Philipp Maier8dc35972021-07-14 11:20:16 +0200430 }
431
432 /* Execute request handler */
433 if (rq.endp)
434 LOGP(DLMGCP, LOGL_INFO,
435 "%s: executing request handler \"%s\" for endpoint resource \"%s\"\n", rq.name,
436 mgcp_requests[i].debug_name, rq.endp->name);
437 else
438 LOGP(DLMGCP, LOGL_INFO,
439 "%s: executing request handler \"%s\" for trunk resource of endpoint \"%s\"\n",
440 rq.name, mgcp_requests[i].debug_name, pdata.epname);
441 resp = mgcp_requests[i].handle_request(&rq);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200442 handled = 1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200443 break;
444 }
445 }
446
Philipp Maier8dc35972021-07-14 11:20:16 +0200447 /* Check if the MGCP request was handled and increment rate counters accordingly. */
Alexander Chemeris63866002020-05-05 17:18:40 +0300448 if (handled) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200449 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_HANDLED));
Alexander Chemeris63866002020-05-05 17:18:40 +0300450 } else {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200451 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_GENERAL_RX_MSGS_UNHANDLED));
Philipp Maier8dc35972021-07-14 11:20:16 +0200452 LOGP(DLMGCP, LOGL_ERROR, "MSG with type: '%.4s' not handled\n", &msg->l2h[0]);
Alexander Chemeris63866002020-05-05 17:18:40 +0300453 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200454
455 return resp;
456}
457
Philipp Maier87bd9be2017-08-22 16:35:41 +0200458/* AUEP command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +0200459static struct msgb *handle_audit_endpoint(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200460{
Philipp Maier8dc35972021-07-14 11:20:16 +0200461 LOGPENDP(rq->endp, DLMGCP, LOGL_NOTICE, "AUEP: auditing endpoint ...\n");
Ericfbcf4a62021-09-09 18:02:31 +0200462 return create_ok_response(rq->trunk, rq->endp, 200, "AUEP", rq->pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200463}
464
Harald Welte1d1b98f2017-12-25 10:03:40 +0100465/* Try to find a free port by attempting to bind on it. Also handle the
Philipp Maier87bd9be2017-08-22 16:35:41 +0200466 * counter that points on the next free port. Since we have a pointer
Philipp Maierb38fb892018-05-22 13:52:21 +0200467 * to the next free port, binding should in work on the first attempt in
Pau Espin Pedrolfc806732019-04-23 00:18:43 +0200468 * general. In case of failure the next port is tried until the whole port
469 * range is tried once. */
Philipp Maier87bd9be2017-08-22 16:35:41 +0200470static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200471{
472 int i;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200473 struct mgcp_port_range *range;
Philipp Maierb38fb892018-05-22 13:52:21 +0200474 unsigned int tries;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200475
Philipp Maier87bd9be2017-08-22 16:35:41 +0200476 OSMO_ASSERT(conn);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200477
Ericfbf78d12021-08-23 22:31:39 +0200478 range = &endp->trunk->cfg->net_ports;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200479
Eric55fdfc22021-08-13 00:14:18 +0200480 pthread_mutex_lock(&range->lock);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200481 /* attempt to find a port */
Philipp Maierb38fb892018-05-22 13:52:21 +0200482 tries = (range->range_end - range->range_start) / 2;
483 for (i = 0; i < tries; ++i) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200484 int rc;
485
486 if (range->last_port >= range->range_end)
487 range->last_port = range->range_start;
488
Philipp Maier87bd9be2017-08-22 16:35:41 +0200489 rc = mgcp_bind_net_rtp_port(endp, range->last_port, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200490
491 range->last_port += 2;
492 if (rc == 0) {
Eric55fdfc22021-08-13 00:14:18 +0200493 pthread_mutex_unlock(&range->lock);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200494 return 0;
495 }
496
497 }
Eric55fdfc22021-08-13 00:14:18 +0200498 pthread_mutex_unlock(&range->lock);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200499 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
500 "Allocating a RTP/RTCP port failed %u times.\n",
501 tries);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200502 return -1;
503}
504
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200505/*! Helper function for check_local_cx_options() to get a pointer of the next
506 * lco option identifier
507 * \param[in] lco string
508 * \returns pointer to the beginning of the LCO identifier, NULL on failure */
509char *get_lco_identifier(const char *options)
510{
511 char *ptr;
512 unsigned int count = 0;
513
514 /* Jump to the end of the lco identifier */
515 ptr = strstr(options, ":");
516 if (!ptr)
517 return NULL;
518
519 /* Walk backwards until the pointer points to the beginning of the
520 * lco identifier. We know that we stand at the beginning when we
521 * are either at the beginning of the memory or see a space or
522 * comma. (this is tolerant, it will accept a:10, b:11 as well as
523 * a:10,b:11) */
524 while (1) {
525 /* Endless loop protection */
526 if (count > 10000)
527 return NULL;
528 else if (ptr < options || *ptr == ' ' || *ptr == ',') {
529 ptr++;
530 break;
531 }
532 ptr--;
533 count++;
534 }
535
536 /* Check if we got any result */
537 if (*ptr == ':')
538 return NULL;
539
540 return ptr;
541}
542
543/*! Check the LCO option. This function checks for multiple appearence of LCO
544 * options, which is illegal
545 * \param[in] ctx talloc context
546 * \param[in] lco string
547 * \returns 0 on success, -1 on failure */
548int check_local_cx_options(void *ctx, const char *options)
549{
550 int i;
551 char *options_copy;
552 char *lco_identifier;
553 char *lco_identifier_end;
554 char *next_lco_identifier;
555
556 char **lco_seen;
557 unsigned int lco_seen_n = 0;
558
559 if (!options)
560 return -1;
561
562 lco_seen =
563 (char **)talloc_zero_size(ctx, strlen(options) * sizeof(char *));
564 options_copy = talloc_strdup(ctx, options);
565 lco_identifier = options_copy;
566
567 do {
568 /* Move the lco_identifier pointer to the beginning of the
569 * current lco option identifier */
570 lco_identifier = get_lco_identifier(lco_identifier);
571 if (!lco_identifier)
572 goto error;
573
574 /* Look ahead to the next LCO option early, since we
575 * will parse destructively */
576 next_lco_identifier = strstr(lco_identifier + 1, ",");
577
578 /* Pinch off the end of the lco field identifier name
579 * and see if we still got something, also check if
580 * there is some value after the colon. */
581 lco_identifier_end = strstr(lco_identifier, ":");
582 if (!lco_identifier_end)
583 goto error;
584 if (*(lco_identifier_end + 1) == ' '
585 || *(lco_identifier_end + 1) == ','
586 || *(lco_identifier_end + 1) == '\0')
587 goto error;
588 *lco_identifier_end = '\0';
589 if (strlen(lco_identifier) == 0)
590 goto error;
591
592 /* Check if we have already seen the current field identifier
593 * before. If yes, we must bail, an LCO must only appear once
594 * in the LCO string */
595 for (i = 0; i < lco_seen_n; i++) {
Pau Espin Pedrol7eb6f2c2019-06-26 13:00:52 +0200596 if (strcasecmp(lco_seen[i], lco_identifier) == 0)
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200597 goto error;
598 }
599 lco_seen[lco_seen_n] = lco_identifier;
600 lco_seen_n++;
601
602 /* The first identifier must always be found at the beginnning
603 * of the LCO string */
604 if (lco_seen[0] != options_copy)
605 goto error;
606
607 /* Go to the next lco option */
608 lco_identifier = next_lco_identifier;
609 } while (lco_identifier);
610
611 talloc_free(lco_seen);
612 talloc_free(options_copy);
613 return 0;
614error:
615 talloc_free(lco_seen);
616 talloc_free(options_copy);
617 return -1;
618}
619
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200620/* Set the LCO from a string (see RFC 3435).
Harald Welte1d1b98f2017-12-25 10:03:40 +0100621 * The string is stored in the 'string' field. A NULL string is handled exactly
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200622 * like an empty string, the 'string' field is never NULL after this function
623 * has been called. */
Philipp Maiera390d0b2018-01-31 17:30:19 +0100624static int set_local_cx_options(void *ctx, struct mgcp_lco *lco,
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200625 const char *options)
626{
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200627 char *lco_id;
Philipp Maier8dbc9ed2018-10-26 14:50:25 +0200628 char codec[17];
Pau Espin Pedrol19539862020-09-21 11:43:41 +0200629 char nt[17];
Pau Espin Pedrol83fd8a52019-06-26 12:55:26 +0200630 int len;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200631
Philipp Maier604410c2018-06-06 10:02:16 +0200632 if (!options)
633 return 0;
634 if (strlen(options) == 0)
635 return 0;
636
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200637 /* Make sure the encoding of the LCO is consistant before we proceed */
638 if (check_local_cx_options(ctx, options) != 0) {
639 LOGP(DLMGCP, LOGL_ERROR,
640 "local CX options: Internal inconsistency in Local Connection Options!\n");
641 return 524;
642 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200643
Philipp Maier3d7b58d2018-06-06 09:35:31 +0200644 talloc_free(lco->string);
645 lco->string = talloc_strdup(ctx, options);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200646
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200647 lco_id = lco->string;
648 while ((lco_id = get_lco_identifier(lco_id))) {
649 switch (tolower(lco_id[0])) {
650 case 'p':
651 if (sscanf(lco_id + 1, ":%d-%d",
652 &lco->pkt_period_min, &lco->pkt_period_max) == 1)
653 lco->pkt_period_max = lco->pkt_period_min;
654 break;
655 case 'a':
Pau Espin Pedrol1dc2dce2020-09-21 11:25:15 +0200656 /* FIXME: LCO also supports the negotiation of more than one codec.
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200657 * (e.g. a:PCMU;G726-32) But this implementation only supports a single
658 * codec only. */
659 if (sscanf(lco_id + 1, ":%16[^,]", codec) == 1) {
660 talloc_free(lco->codec);
Pau Espin Pedrol83fd8a52019-06-26 12:55:26 +0200661 /* MGCP header is case insensive, and we'll need
662 codec in uppercase when using it later: */
663 len = strlen(codec);
664 lco->codec = talloc_size(ctx, len + 1);
665 osmo_str_toupper_buf(lco->codec, len + 1, codec);
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200666 }
667 break;
Pau Espin Pedrol19539862020-09-21 11:43:41 +0200668 case 'n':
669 if (lco_id[1] == 't' && sscanf(lco_id + 2, ":%16[^,]", nt) == 1)
670 break;
671 /* else: fall throught to print notice log */
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200672 default:
673 LOGP(DLMGCP, LOGL_NOTICE,
674 "LCO: unhandled option: '%c'/%d in \"%s\"\n",
675 *lco_id, *lco_id, lco->string);
676 break;
677 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200678
Pau Espin Pedrolfe9a1fe2019-06-25 16:59:15 +0200679 lco_id = strchr(lco_id, ',');
680 if (!lco_id)
681 break;
Philipp Maier604410c2018-06-06 10:02:16 +0200682 }
Philipp Maier87bd9be2017-08-22 16:35:41 +0200683
684 LOGP(DLMGCP, LOGL_DEBUG,
685 "local CX options: lco->pkt_period_max: %i, lco->codec: %s\n",
686 lco->pkt_period_max, lco->codec);
Philipp Maiera390d0b2018-01-31 17:30:19 +0100687
688 /* Check if the packetization fits the 20ms raster */
689 if (lco->pkt_period_min % 20 && lco->pkt_period_max % 20) {
690 LOGP(DLMGCP, LOGL_ERROR,
691 "local CX options: packetization interval is not a multiple of 20ms!\n");
692 return 535;
693 }
694
695 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200696}
697
698void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
699 struct mgcp_rtp_end *rtp)
700{
Philipp Maier14b27a82020-06-02 20:15:30 +0200701 struct mgcp_trunk *trunk = endp->trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200702
Philipp Maier14b27a82020-06-02 20:15:30 +0200703 int patch_ssrc = expect_ssrc_change && trunk->force_constant_ssrc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200704
Philipp Maier14b27a82020-06-02 20:15:30 +0200705 rtp->force_aligned_timing = trunk->force_aligned_timing;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200706 rtp->force_constant_ssrc = patch_ssrc ? 1 : 0;
Philipp Maier14b27a82020-06-02 20:15:30 +0200707 rtp->rfc5993_hr_convert = trunk->rfc5993_hr_convert;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200708
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200709 LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
710 "Configuring RTP endpoint: local port %d%s%s\n",
711 ntohs(rtp->rtp_port),
712 rtp->force_aligned_timing ? ", force constant timing" : "",
713 rtp->force_constant_ssrc ? ", force constant ssrc" : "");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200714}
715
Pau Espin Pedrol8358c4b2021-07-07 12:41:38 +0200716uint32_t mgcp_rtp_packet_duration(const struct mgcp_endpoint *endp,
717 const struct mgcp_rtp_end *rtp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200718{
719 int f = 0;
720
721 /* Get the number of frames per channel and packet */
722 if (rtp->frames_per_packet)
723 f = rtp->frames_per_packet;
Philipp Maierbc0346e2018-06-07 09:52:16 +0200724 else if (rtp->packet_duration_ms && rtp->codec->frame_duration_num) {
725 int den = 1000 * rtp->codec->frame_duration_num;
726 f = (rtp->packet_duration_ms * rtp->codec->frame_duration_den +
Philipp Maier87bd9be2017-08-22 16:35:41 +0200727 den / 2)
728 / den;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200729 }
730
Philipp Maierbc0346e2018-06-07 09:52:16 +0200731 return rtp->codec->rate * f * rtp->codec->frame_duration_num /
732 rtp->codec->frame_duration_den;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200733}
734
Pau Espin Pedrol9fb8ddf2019-05-08 15:35:36 +0200735/*! Initializes osmux socket if not yet initialized. Parses Osmux CID from MGCP line.
736 * \param[in] endp Endpoint willing to initialize osmux
737 * \param[in] line Line X-Osmux from MGCP header msg to parse
738 * \returns OSMUX CID, -1 for wildcard, -2 on parse error, -3 on osmux initalize error
739 */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200740static int mgcp_osmux_setup(struct mgcp_endpoint *endp, const char *line)
741{
Ericfbf78d12021-08-23 22:31:39 +0200742 if (!endp->trunk->cfg->osmux_init) {
743 if (osmux_init(OSMUX_ROLE_BSC, endp->trunk->cfg) < 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200744 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "Cannot init OSMUX\n");
Pau Espin Pedrol9fb8ddf2019-05-08 15:35:36 +0200745 return -3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200746 }
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200747 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "OSMUX socket has been set up\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200748 }
749
750 return mgcp_parse_osmux_cid(line);
751}
752
Philipp Maierbc0346e2018-06-07 09:52:16 +0200753/* Process codec information contained in CRCX/MDCX */
754static int handle_codec_info(struct mgcp_conn_rtp *conn,
Philipp Maier8dc35972021-07-14 11:20:16 +0200755 struct mgcp_request_data *rq, int have_sdp, bool crcx)
Philipp Maierbc0346e2018-06-07 09:52:16 +0200756{
Philipp Maier8dc35972021-07-14 11:20:16 +0200757 struct mgcp_endpoint *endp = rq->endp;
Philipp Maierbc0346e2018-06-07 09:52:16 +0200758 int rc;
759 char *cmd;
760
761 if (crcx)
762 cmd = "CRCX";
763 else
764 cmd = "MDCX";
765
766 /* Collect codec information */
767 if (have_sdp) {
768 /* If we have SDP, we ignore the local connection options and
769 * use only the SDP information. */
770 mgcp_codec_reset_all(conn);
Philipp Maier8dc35972021-07-14 11:20:16 +0200771 rc = mgcp_parse_sdp_data(endp, conn, rq->pdata);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200772 if (rc != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200773 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
774 "%s: sdp not parseable\n", cmd);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200775
776 /* See also RFC 3661: Protocol error */
777 return 510;
778 }
779 } else if (endp->local_options.codec) {
780 /* When no SDP is available, we use the codec information from
781 * the local connection options (if present) */
782 mgcp_codec_reset_all(conn);
Philipp Maier228e5912019-03-05 13:56:59 +0100783 rc = mgcp_codec_add(conn, PTYPE_UNDEFINED, endp->local_options.codec, NULL);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200784 if (rc != 0)
785 goto error;
786 }
787
788 /* Make sure we always set a sane default codec */
789 if (conn->end.codecs_assigned == 0) {
790 /* When SDP and/or LCO did not supply any codec information,
791 * than it makes sense to pick a sane default: (payload-type 0,
792 * PCMU), see also: OS#2658 */
793 mgcp_codec_reset_all(conn);
Philipp Maier228e5912019-03-05 13:56:59 +0100794 rc = mgcp_codec_add(conn, 0, NULL, NULL);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200795 if (rc != 0)
796 goto error;
797 }
798
799 /* Make codec decision */
800 if (mgcp_codec_decide(conn) != 0)
801 goto error;
802
803 return 0;
804
805error:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200806 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
807 "%s: codec negotiation failure\n", cmd);
Philipp Maierbc0346e2018-06-07 09:52:16 +0200808
809 /* See also RFC 3661: Codec negotiation failure */
810 return 534;
811}
812
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200813static bool parse_x_osmo_ign(struct mgcp_endpoint *endp, char *line)
814{
815 char *saveptr = NULL;
816
Pau Espin Pedrol6049a632020-09-21 11:03:21 +0200817 if (strncasecmp(line, MGCP_X_OSMO_IGN_HEADER, strlen(MGCP_X_OSMO_IGN_HEADER)))
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200818 return false;
819 line += strlen(MGCP_X_OSMO_IGN_HEADER);
820
821 while (1) {
822 char *token = strtok_r(line, " ", &saveptr);
823 line = NULL;
824 if (!token)
825 break;
826
Pau Espin Pedrol6e26c702019-06-26 13:09:39 +0200827 if (!strcasecmp(token, "C"))
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200828 endp->x_osmo_ign |= MGCP_X_OSMO_IGN_CALLID;
829 else
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200830 LOGPENDP(endp, DLMGCP, LOGL_ERROR, "received unknown X-Osmo-IGN item '%s'\n", token);
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200831 }
832
833 return true;
834}
835
Philipp Maier87bd9be2017-08-22 16:35:41 +0200836/* CRCX command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +0200837static struct msgb *handle_create_con(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200838{
Philipp Maier8dc35972021-07-14 11:20:16 +0200839 struct mgcp_parse_data *pdata = rq->pdata;
840 struct mgcp_trunk *trunk = rq->trunk;
841 struct mgcp_endpoint *endp = rq->endp;
Philipp Maierc66ab2c2020-06-02 20:55:34 +0200842 struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_crcx_ctr_group;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200843 int error_code = 400;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200844 const char *local_options = NULL;
845 const char *callid = NULL;
846 const char *mode = NULL;
847 char *line;
Pau Espin Pedrol2b896172019-04-24 13:47:23 +0200848 int have_sdp = 0, osmux_cid = -2;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200849 struct mgcp_conn_rtp *conn = NULL;
Philipp Maierffd75e42017-11-22 11:44:50 +0100850 struct mgcp_conn *_conn = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200851 char conn_name[512];
Philipp Maiera390d0b2018-01-31 17:30:19 +0100852 int rc;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200853
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200854 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "CRCX: creating new connection ...\n");
Philipp Maier246233d2020-08-18 15:15:24 +0200855
Philipp Maier8d6a1932020-06-18 12:19:31 +0200856 if (!mgcp_endp_avail(endp)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200857 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_AVAIL));
Philipp Maiera910a812020-08-18 15:13:33 +0200858 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
859 "CRCX: selected endpoint not available!\n");
Ericfbcf4a62021-09-09 18:02:31 +0200860 return create_err_response(rq->trunk, NULL, 501, "CRCX", pdata->trans);
Philipp Maier8d6a1932020-06-18 12:19:31 +0200861 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200862
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200863 /* parse CallID C: and LocalParameters L: */
Philipp Maier8dc35972021-07-14 11:20:16 +0200864 for_each_line(line, pdata->save) {
Philipp Maier036612b2021-07-19 17:47:49 +0200865 if (!mgcp_check_param(endp, trunk, line))
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200866 continue;
867
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +0200868 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200869 case 'L':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200870 local_options = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200871 break;
872 case 'C':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200873 callid = (const char *)line + 3;
874 break;
875 case 'I':
Philipp Maierffd75e42017-11-22 11:44:50 +0100876 /* It is illegal to send a connection identifier
877 * together with a CRCX, the MGW will assign the
878 * connection identifier by itself on CRCX */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200879 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BAD_ACTION));
Ericfbcf4a62021-09-09 18:02:31 +0200880 return create_err_response(rq->trunk, NULL, 523, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200881 break;
882 case 'M':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200883 mode = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200884 break;
885 case 'X':
Pau Espin Pedrolc1bf4692019-05-14 16:23:24 +0200886 if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200887 /* If osmux is disabled, just skip setting it up */
Ericfbf78d12021-08-23 22:31:39 +0200888 if (!rq->endp->trunk->cfg->osmux)
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200889 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200890 osmux_cid = mgcp_osmux_setup(endp, line);
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200891 break;
892 }
893
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200894 if (parse_x_osmo_ign(endp, line))
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200895 break;
Neels Hofmeyrf2388ea2018-08-26 23:36:53 +0200896
Neels Hofmeyre6d8e912018-08-23 16:36:48 +0200897 /* Ignore unknown X-headers */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200898 break;
899 case '\0':
900 have_sdp = 1;
901 goto mgcp_header_done;
902 default:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200903 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
904 "CRCX: unhandled option: '%c'/%d\n", *line, *line);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200905 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_UNHANDLED_PARAM));
Ericfbcf4a62021-09-09 18:02:31 +0200906 return create_err_response(rq->trunk, NULL, 539, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200907 break;
908 }
909 }
910
911mgcp_header_done:
Philipp Maier87bd9be2017-08-22 16:35:41 +0200912 /* Check parameters */
913 if (!callid) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200914 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
915 "CRCX: insufficient parameters, missing callid\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200916 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_MISSING_CALLID));
Ericfbcf4a62021-09-09 18:02:31 +0200917 return create_err_response(endp, endp, 516, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200918 }
919
Philipp Maier87bd9be2017-08-22 16:35:41 +0200920 if (!mode) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200921 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
922 "CRCX: insufficient parameters, missing mode\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200923 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_MODE));
Ericfbcf4a62021-09-09 18:02:31 +0200924 return create_err_response(endp, endp, 517, "CRCX", pdata->trans);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200925 }
926
Philipp Maier87bd9be2017-08-22 16:35:41 +0200927 /* Check if we are able to accept the creation of another connection */
928 if (llist_count(&endp->conns) >= endp->type->max_conns) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200929 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
930 "CRCX: endpoint full, max. %i connections allowed!\n",
931 endp->type->max_conns);
Philipp Maier14b27a82020-06-02 20:15:30 +0200932 if (trunk->force_realloc) {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200933 /* There is no more room for a connection, make some
934 * room by blindly tossing the oldest of the two two
935 * connections */
936 mgcp_conn_free_oldest(endp);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200937 } else {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200938 /* There is no more room for a connection, leave
939 * everything as it is and return with an error */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200940 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_LIMIT_EXCEEDED));
Ericfbcf4a62021-09-09 18:02:31 +0200941 return create_err_response(endp, endp, 540, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200942 }
943 }
944
Philipp Maier87bd9be2017-08-22 16:35:41 +0200945 /* Check if this endpoint already serves a call, if so, check if the
946 * callids match up so that we are sure that this is our call */
947 if (endp->callid && mgcp_verify_call_id(endp, callid)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200948 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
949 "CRCX: already seized by other call (%s)\n",
950 endp->callid);
Philipp Maier14b27a82020-06-02 20:15:30 +0200951 if (trunk->force_realloc)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200952 /* This is not our call, toss everything by releasing
953 * the entire endpoint. (rude!) */
Philipp Maier1355d7e2018-02-01 14:30:06 +0100954 mgcp_endp_release(endp);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200955 else {
956 /* This is not our call, leave everything as it is and
957 * return with an error. */
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200958 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_UNKNOWN_CALLID));
Ericfbcf4a62021-09-09 18:02:31 +0200959 return create_err_response(endp, endp, 400, "CRCX", pdata->trans);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200960 }
961 }
962
Philipp Maier889fe7f2020-07-06 17:44:12 +0200963 if (!endp->callid) {
964 /* Claim endpoint resources. This will also set the callid,
965 * creating additional connections will only be possible if
966 * the callid matches up (see above). */
967 rc = mgcp_endp_claim(endp, callid);
968 if (rc != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200969 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_CLAIM));
Ericfbcf4a62021-09-09 18:02:31 +0200970 return create_err_response(endp, endp, 502, "CRCX", pdata->trans);
Philipp Maier889fe7f2020-07-06 17:44:12 +0200971 }
972 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200973
Philipp Maierffd75e42017-11-22 11:44:50 +0100974 snprintf(conn_name, sizeof(conn_name), "%s", callid);
Philipp Maier14b27a82020-06-02 20:15:30 +0200975 _conn = mgcp_conn_alloc(trunk->endpoints, endp, MGCP_CONN_TYPE_RTP, conn_name);
Philipp Maierffd75e42017-11-22 11:44:50 +0100976 if (!_conn) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +0200977 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
978 "CRCX: unable to allocate RTP connection\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200979 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_ALLOC_CONN));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200980 goto error2;
981
Philipp Maier87bd9be2017-08-22 16:35:41 +0200982 }
Philipp Maier889fe7f2020-07-06 17:44:12 +0200983
Philipp Maierffd75e42017-11-22 11:44:50 +0100984 conn = mgcp_conn_get_rtp(endp, _conn->id);
985 OSMO_ASSERT(conn);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200986
987 if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
988 error_code = 517;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200989 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_MODE));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200990 goto error2;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200991 }
992
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200993 /* Annotate Osmux circuit ID and set it to negotiating state until this
Philipp Maier87bd9be2017-08-22 16:35:41 +0200994 * is fully set up from the dummy load. */
995 conn->osmux.state = OSMUX_STATE_DISABLED;
Pau Espin Pedrol2b896172019-04-24 13:47:23 +0200996 if (osmux_cid >= -1) { /* -1 is wilcard, alloc next avail CID */
Pau Espin Pedrol14f8a082019-05-13 13:10:06 +0200997 conn->osmux.state = OSMUX_STATE_ACTIVATING;
Pau Espin Pedrol2b896172019-04-24 13:47:23 +0200998 if (conn_osmux_allocate_cid(conn, osmux_cid) == -1) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +0200999 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_OSMUX));
Pau Espin Pedrol2b896172019-04-24 13:47:23 +02001000 goto error2;
1001 }
Ericfbf78d12021-08-23 22:31:39 +02001002 } else if (endp->trunk->cfg->osmux == OSMUX_USAGE_ONLY) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001003 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
1004 "CRCX: osmux only and no osmux offered\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001005 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_OSMUX));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001006 goto error2;
1007 }
1008
Philipp Maierbc0346e2018-06-07 09:52:16 +02001009 /* Set local connection options, if present */
1010 if (local_options) {
Philipp Maier8dc35972021-07-14 11:20:16 +02001011 rc = set_local_cx_options(trunk->endpoints,
Philipp Maierbc0346e2018-06-07 09:52:16 +02001012 &endp->local_options, local_options);
1013 if (rc != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001014 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
1015 "CRCX: inavlid local connection options!\n");
Philipp Maierbc0346e2018-06-07 09:52:16 +02001016 error_code = rc;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001017 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS));
Philipp Maierbc0346e2018-06-07 09:52:16 +02001018 goto error2;
1019 }
1020 }
1021
1022 /* Handle codec information and decide for a suitable codec */
Philipp Maier8dc35972021-07-14 11:20:16 +02001023 rc = handle_codec_info(conn, rq, have_sdp, true);
Philipp Maierbc0346e2018-06-07 09:52:16 +02001024 mgcp_codec_summary(conn);
1025 if (rc) {
1026 error_code = rc;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001027 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_CODEC_NEGOTIATION));
Philipp Maierbc0346e2018-06-07 09:52:16 +02001028 goto error2;
1029 }
1030
Philipp Maier14b27a82020-06-02 20:15:30 +02001031 conn->end.fmtp_extra = talloc_strdup(trunk->endpoints,
1032 trunk->audio_fmtp_extra);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001033
Philipp Maier8dc35972021-07-14 11:20:16 +02001034 if (pdata->cfg->force_ptime) {
1035 conn->end.packet_duration_ms = pdata->cfg->force_ptime;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001036 conn->end.force_output_ptime = 1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001037 }
1038
Philipp Maier1cb1e382017-11-02 17:16:04 +01001039 mgcp_rtp_end_config(endp, 0, &conn->end);
1040
Philipp Maierc3cc6542018-02-02 12:58:42 +01001041 /* check connection mode setting */
1042 if (conn->conn->mode != MGCP_CONN_LOOPBACK
1043 && conn->conn->mode != MGCP_CONN_RECV_ONLY
1044 && conn->end.rtp_port == 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001045 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
1046 "CRCX: selected connection mode type requires an opposite end!\n");
Philipp Maierc3cc6542018-02-02 12:58:42 +01001047 error_code = 527;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001048 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC));
Philipp Maierc3cc6542018-02-02 12:58:42 +01001049 goto error2;
1050 }
1051
Pau Espin Pedrol71d42e72020-09-03 14:20:07 +02001052 /* Find a local address for conn based on policy and initial SDP remote
1053 information, then find a free port for it */
1054 mgcp_get_local_addr(conn->end.local_addr, conn);
Philipp Maier1cb1e382017-11-02 17:16:04 +01001055 if (allocate_port(endp, conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001056 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
Philipp Maier1cb1e382017-11-02 17:16:04 +01001057 goto error2;
1058 }
1059
Philipp Maier87bd9be2017-08-22 16:35:41 +02001060 if (setup_rtp_processing(endp, conn) != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001061 LOGPCONN(_conn, DLMGCP, LOGL_ERROR,
1062 "CRCX: could not start RTP processing!\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001063 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_START_RTP));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001064 goto error2;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001065 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001066
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001067 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
1068 "CRCX: Creating connection: port: %u\n", conn->end.local_port);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001069
Philipp Maiere726d4f2017-11-01 10:41:34 +01001070 /* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
Philipp Maier14b27a82020-06-02 20:15:30 +02001071 OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Pau Espin Pedrolca280a12021-07-06 18:15:35 +02001072 if (conn->conn->mode & MGCP_CONN_RECV_ONLY &&
1073 mgcp_rtp_end_remote_addr_available(&conn->end) &&
1074 trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001075 send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001076
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001077 LOGPCONN(_conn, DLMGCP, LOGL_NOTICE,
1078 "CRCX: connection successfully created\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001079 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_SUCCESS));
Philipp Maier889fe7f2020-07-06 17:44:12 +02001080 mgcp_endp_update(endp);
Philipp Maier41d59202021-07-20 15:49:00 +02001081
1082 /* NOTE: Only in the virtual trunk we allow dynamic endpoint names */
1083 bool add_epname = rq->wildcarded && trunk->trunk_type == MGCP_TRUNK_VIRTUAL;
1084 return create_response_with_sdp(endp, conn, "CRCX", pdata->trans, add_epname, true);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001085error2:
Philipp Maier1355d7e2018-02-01 14:30:06 +01001086 mgcp_endp_release(endp);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001087 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1088 "CRCX: unable to create connection\n");
Ericfbcf4a62021-09-09 18:02:31 +02001089 return create_err_response(endp, endp, error_code, "CRCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001090}
1091
Philipp Maier87bd9be2017-08-22 16:35:41 +02001092/* MDCX command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +02001093static struct msgb *handle_modify_con(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001094{
Philipp Maier8dc35972021-07-14 11:20:16 +02001095 struct mgcp_parse_data *pdata = rq->pdata;
1096 struct mgcp_trunk *trunk = rq->trunk;
1097 struct mgcp_endpoint *endp = rq->endp;
1098 struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_mdcx_ctr_group;
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001099 char new_local_addr[INET6_ADDRSTRLEN];
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001100 int error_code = 500;
1101 int silent = 0;
1102 int have_sdp = 0;
1103 char *line;
1104 const char *local_options = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001105 const char *mode = NULL;
1106 struct mgcp_conn_rtp *conn = NULL;
Philipp Maier01d24a32017-11-21 17:26:09 +01001107 const char *conn_id = NULL;
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001108 int osmux_cid = -2;
Philipp Maiera390d0b2018-01-31 17:30:19 +01001109 int rc;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001110
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001111 LOGPENDP(endp, DLMGCP, LOGL_NOTICE, "MDCX: modifying existing connection ...\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001112
Philipp Maier8d6a1932020-06-18 12:19:31 +02001113 if (!mgcp_endp_avail(endp)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001114 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_AVAIL));
Philipp Maiera910a812020-08-18 15:13:33 +02001115 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1116 "MDCX: selected endpoint not available!\n");
Ericfbcf4a62021-09-09 18:02:31 +02001117 return create_err_response(endp, NULL, 501, "MDCX", pdata->trans);
Philipp Maier8d6a1932020-06-18 12:19:31 +02001118 }
1119
Philipp Maier5656fbf2018-02-02 14:41:58 +01001120 /* Prohibit wildcarded requests */
Philipp Maier8dc35972021-07-14 11:20:16 +02001121 if (rq->wildcarded) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001122 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1123 "MDCX: wildcarded endpoint names not supported.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001124 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_WILDCARD));
Ericfbcf4a62021-09-09 18:02:31 +02001125 return create_err_response(rq->trunk, endp, 507, "MDCX", pdata->trans);
Philipp Maier5656fbf2018-02-02 14:41:58 +01001126 }
1127
Philipp Maier87bd9be2017-08-22 16:35:41 +02001128 if (llist_count(&endp->conns) <= 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001129 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1130 "MDCX: endpoint is not holding a connection.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001131 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_CONN));
Ericfbcf4a62021-09-09 18:02:31 +02001132 return create_err_response(endp, endp, 400, "MDCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001133 }
1134
Philipp Maier8dc35972021-07-14 11:20:16 +02001135 for_each_line(line, pdata->save) {
Philipp Maier036612b2021-07-19 17:47:49 +02001136 if (!mgcp_check_param(endp, trunk, line))
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001137 continue;
1138
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001139 switch (toupper(line[0])) {
Philipp Maier87bd9be2017-08-22 16:35:41 +02001140 case 'C':
Harald Weltee35eeae2017-12-28 13:47:37 +01001141 if (mgcp_verify_call_id(endp, line + 3) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001142 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CALLID));
Harald Weltee35eeae2017-12-28 13:47:37 +01001143 error_code = 516;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001144 goto error3;
Harald Weltee35eeae2017-12-28 13:47:37 +01001145 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001146 break;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001147 case 'I':
Philipp Maier01d24a32017-11-21 17:26:09 +01001148 conn_id = (const char *)line + 3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001149 if ((error_code = mgcp_verify_ci(endp, conn_id))) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001150 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CONNID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001151 goto error3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001152 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001153 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001154 case 'L':
Philipp Maier87bd9be2017-08-22 16:35:41 +02001155 local_options = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001156 break;
1157 case 'M':
Philipp Maier87bd9be2017-08-22 16:35:41 +02001158 mode = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001159 break;
1160 case 'Z':
Pau Espin Pedrol9b508f62019-06-26 13:11:22 +02001161 silent = strcasecmp("noanswer", line + 3) == 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001162 break;
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001163 case 'X':
Pau Espin Pedrolc1bf4692019-05-14 16:23:24 +02001164 if (strncasecmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) {
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001165 /* If osmux is disabled, just skip setting it up */
Ericfbf78d12021-08-23 22:31:39 +02001166 if (!endp->trunk->cfg->osmux)
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001167 break;
1168 osmux_cid = mgcp_osmux_setup(endp, line);
1169 break;
1170 }
1171 /* Ignore unknown X-headers */
1172 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001173 case '\0':
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001174 have_sdp = 1;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001175 goto mgcp_header_done;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001176 break;
1177 default:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001178 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1179 "MDCX: Unhandled MGCP option: '%c'/%d\n",
1180 line[0], line[0]);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001181 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_UNHANDLED_PARAM));
Ericfbcf4a62021-09-09 18:02:31 +02001182 return create_err_response(rq->trunk, NULL, 539, "MDCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001183 break;
1184 }
1185 }
1186
Philipp Maier87bd9be2017-08-22 16:35:41 +02001187mgcp_header_done:
Philipp Maier01d24a32017-11-21 17:26:09 +01001188 if (!conn_id) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001189 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1190 "MDCX: insufficient parameters, missing ci (connectionIdentifier)\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001191 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_CONNID));
Ericfbcf4a62021-09-09 18:02:31 +02001192 return create_err_response(endp, endp, 515, "MDCX", pdata->trans);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001193 }
1194
1195 conn = mgcp_conn_get_rtp(endp, conn_id);
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001196 if (!conn) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001197 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_CONN_NOT_FOUND));
Ericfbcf4a62021-09-09 18:02:31 +02001198 return create_err_response(endp, endp, 400, "MDCX", pdata->trans);
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001199 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001200
Oliver Smithe36b7752019-01-22 16:31:36 +01001201 mgcp_conn_watchdog_kick(conn->conn);
1202
Philipp Maier87bd9be2017-08-22 16:35:41 +02001203 if (mode) {
1204 if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001205 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_MODE));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001206 error_code = 517;
1207 goto error3;
1208 }
1209 } else
Pau Espin Pedrol209eb9f2019-04-24 12:03:04 +02001210 conn->conn->mode = conn->conn->mode_orig;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001211
Philipp Maierbc0346e2018-06-07 09:52:16 +02001212 /* Set local connection options, if present */
1213 if (local_options) {
Philipp Maier8dc35972021-07-14 11:20:16 +02001214 rc = set_local_cx_options(trunk->endpoints,
Philipp Maierbc0346e2018-06-07 09:52:16 +02001215 &endp->local_options, local_options);
1216 if (rc != 0) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001217 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1218 "MDCX: invalid local connection options!\n");
Philipp Maierbc0346e2018-06-07 09:52:16 +02001219 error_code = rc;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001220 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS));
Philipp Maierbc0346e2018-06-07 09:52:16 +02001221 goto error3;
1222 }
1223 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001224
Philipp Maierbc0346e2018-06-07 09:52:16 +02001225 /* Handle codec information and decide for a suitable codec */
Philipp Maier8dc35972021-07-14 11:20:16 +02001226 rc = handle_codec_info(conn, rq, have_sdp, false);
Philipp Maierbc0346e2018-06-07 09:52:16 +02001227 mgcp_codec_summary(conn);
1228 if (rc) {
Philipp Maieraf07f662018-02-02 11:34:02 +01001229 error_code = rc;
1230 goto error3;
Philipp Maiera390d0b2018-01-31 17:30:19 +01001231 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001232
Philipp Maierc3cc6542018-02-02 12:58:42 +01001233 /* check connection mode setting */
1234 if (conn->conn->mode != MGCP_CONN_LOOPBACK
1235 && conn->conn->mode != MGCP_CONN_RECV_ONLY
Pau Espin Pedrold6769ea2021-07-06 19:44:27 +02001236 && !mgcp_rtp_end_remote_addr_available(&conn->end)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001237 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1238 "MDCX: selected connection mode type requires an opposite end!\n");
Philipp Maierc3cc6542018-02-02 12:58:42 +01001239 error_code = 527;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001240 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC));
Philipp Maierc3cc6542018-02-02 12:58:42 +01001241 goto error3;
1242 }
1243
Pau Espin Pedrol6be2c492019-05-08 15:22:59 +02001244 if (mgcp_conn_rtp_is_osmux(conn)) {
1245 OSMO_ASSERT(conn->osmux.cid_allocated);
1246 if (osmux_cid < -1) {
1247 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1248 "MDCX: Failed to parse Osmux CID!\n");
1249 goto error3;
1250 } else if (osmux_cid == -1) {
1251 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1252 "MDCX: wilcard in MDCX is not supported!\n");
1253 goto error3;
1254 } else if (osmux_cid != (int) conn->osmux.cid) {
1255 LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
1256 "MDCX: changing already allocated CID is not supported!\n");
1257 goto error3;
1258 }
1259 /* TODO: In the future (when we have recvCID!=sendCID), we need to
1260 tell Osmux code that osmux_cid is to be used as sendCID for
1261 that conn. */
1262 }
Philipp Maierbc0346e2018-06-07 09:52:16 +02001263
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001264 /* MDCX may have provided a new remote address, which means we may need
1265 to update our announced IP addr and re-bind our local end. This can
1266 happen for instance if MGW initially provided an IPv4 during CRCX
1267 ACK, and now MDCX tells us the remote has an IPv6 address. */
1268 mgcp_get_local_addr(new_local_addr, conn);
1269 if (strcmp(new_local_addr, conn->end.local_addr)) {
1270 osmo_strlcpy(conn->end.local_addr, new_local_addr, sizeof(conn->end.local_addr));
1271 mgcp_free_rtp_port(&conn->end);
1272 if (allocate_port(endp, conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001273 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_BIND_PORT));
Pau Espin Pedrolfbbe8f22020-09-03 14:49:56 +02001274 goto error3;
1275 }
1276 }
1277
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001278 if (setup_rtp_processing(endp, conn) != 0) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001279 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_FAIL_START_RTP));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001280 goto error3;
Stefan Sperlingaa823bf2018-10-29 14:51:41 +01001281 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001282
Philipp Maier87bd9be2017-08-22 16:35:41 +02001283 mgcp_rtp_end_config(endp, 1, &conn->end);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001284
1285 /* modify */
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001286 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
1287 "MDCX: modified conn:%s\n", mgcp_conn_dump(conn->conn));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001288
Philipp Maiere726d4f2017-11-01 10:41:34 +01001289 /* Send dummy packet, see also comments in mgcp_keepalive_timer_cb() */
Philipp Maier8dc35972021-07-14 11:20:16 +02001290 OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Pau Espin Pedrolca280a12021-07-06 18:15:35 +02001291 if (conn->conn->mode & MGCP_CONN_RECV_ONLY &&
1292 mgcp_rtp_end_remote_addr_available(&conn->end) &&
Philipp Maier8dc35972021-07-14 11:20:16 +02001293 trunk->keepalive_interval != MGCP_KEEPALIVE_NEVER)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001294 send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001295
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001296 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_MDCX_SUCCESS));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001297 if (silent)
1298 goto out_silent;
1299
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001300 LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
1301 "MDCX: connection successfully modified\n");
Philipp Maier889fe7f2020-07-06 17:44:12 +02001302 mgcp_endp_update(endp);
Philipp Maier41d59202021-07-20 15:49:00 +02001303 return create_response_with_sdp(endp, conn, "MDCX", pdata->trans, false, false);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001304error3:
Ericfbcf4a62021-09-09 18:02:31 +02001305 return create_err_response(endp, endp, error_code, "MDCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001306
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001307out_silent:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001308 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "MDCX: silent exit\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001309 return NULL;
1310}
1311
Philipp Maier87bd9be2017-08-22 16:35:41 +02001312/* DLCX command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +02001313static struct msgb *handle_delete_con(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001314{
Philipp Maier8dc35972021-07-14 11:20:16 +02001315 struct mgcp_parse_data *pdata = rq->pdata;
1316 struct mgcp_trunk *trunk = rq->trunk;
1317 struct mgcp_endpoint *endp = rq->endp;
1318 struct rate_ctr_group *rate_ctrs = trunk->ratectr.mgcp_dlcx_ctr_group;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001319 int error_code = 400;
1320 int silent = 0;
1321 char *line;
1322 char stats[1048];
Philipp Maier01d24a32017-11-21 17:26:09 +01001323 const char *conn_id = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001324 struct mgcp_conn_rtp *conn = NULL;
Philipp Maierf486e742021-07-19 14:56:29 +02001325 unsigned int i;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001326
Philipp Maierf486e742021-07-19 14:56:29 +02001327 /* NOTE: In this handler we can not take it for granted that the endp
1328 * pointer will be populated, however a trunk is always guaranteed. */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001329
Philipp Maierf486e742021-07-19 14:56:29 +02001330 LOGPEPTR(endp, trunk, DLMGCP, LOGL_NOTICE, "DLCX: deleting connection(s) ...\n");
1331
1332 if (endp && !mgcp_endp_avail(endp)) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001333 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_AVAIL));
Philipp Maiera910a812020-08-18 15:13:33 +02001334 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1335 "DLCX: selected endpoint not available!\n");
Ericfbcf4a62021-09-09 18:02:31 +02001336 return create_err_response(rq->trunk, NULL, 501, "DLCX", pdata->trans);
Philipp Maier8d6a1932020-06-18 12:19:31 +02001337 }
1338
Philipp Maierf486e742021-07-19 14:56:29 +02001339 if (endp && !rq->wildcarded && llist_empty(&endp->conns)) {
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001340 LOGPENDP(endp, DLMGCP, LOGL_ERROR,
1341 "DLCX: endpoint is not holding a connection.\n");
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001342 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_NO_CONN));
Ericfbcf4a62021-09-09 18:02:31 +02001343 return create_err_response(endp, endp, 515, "DLCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001344 }
1345
Philipp Maier8dc35972021-07-14 11:20:16 +02001346 for_each_line(line, pdata->save) {
Philipp Maier036612b2021-07-19 17:47:49 +02001347 if (!mgcp_check_param(endp, trunk, line))
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001348 continue;
1349
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001350 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001351 case 'C':
Philipp Maierf486e742021-07-19 14:56:29 +02001352 /* If we have no endpoint, but a call id in the request,
1353 then this request cannot be handled */
1354 if (!endp) {
1355 LOGPTRUNK(trunk, DLMGCP, LOGL_NOTICE,
1356 "cannot handle requests with call-id (C) without endpoint -- abort!");
1357 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));
Ericfbcf4a62021-09-09 18:02:31 +02001358 return create_err_response(rq->trunk, NULL, 539, "DLCX", pdata->trans);
Philipp Maierf486e742021-07-19 14:56:29 +02001359 }
1360
Harald Weltee35eeae2017-12-28 13:47:37 +01001361 if (mgcp_verify_call_id(endp, line + 3) != 0) {
1362 error_code = 516;
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001363 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CALLID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001364 goto error3;
Harald Weltee35eeae2017-12-28 13:47:37 +01001365 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001366 break;
1367 case 'I':
Philipp Maierf486e742021-07-19 14:56:29 +02001368 /* If we have no endpoint, but a connection id in the request,
1369 then this request cannot be handled */
1370 if (!endp) {
1371 LOGPTRUNK(trunk, DLMGCP, LOGL_NOTICE,
1372 "cannot handle requests with conn-id (I) without endpoint -- abort!");
1373 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));
Ericfbcf4a62021-09-09 18:02:31 +02001374 return create_err_response(rq->trunk, NULL, 539, "DLCX", pdata->trans);
Philipp Maierf486e742021-07-19 14:56:29 +02001375 }
1376
Philipp Maier01d24a32017-11-21 17:26:09 +01001377 conn_id = (const char *)line + 3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001378 if ((error_code = mgcp_verify_ci(endp, conn_id))) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001379 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CONNID));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001380 goto error3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001381 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001382 break;
1383 case 'Z':
Pau Espin Pedrol9b508f62019-06-26 13:11:22 +02001384 silent = strcasecmp("noanswer", line + 3) == 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001385 break;
1386 default:
Philipp Maierf486e742021-07-19 14:56:29 +02001387 LOGPEPTR(endp, trunk, DLMGCP, LOGL_NOTICE, "DLCX: Unhandled MGCP option: '%c'/%d\n",
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001388 line[0], line[0]);
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001389 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_UNHANDLED_PARAM));
Ericfbcf4a62021-09-09 18:02:31 +02001390 return create_err_response(rq->trunk, NULL, 539, "DLCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001391 break;
1392 }
1393 }
1394
Philipp Maierf486e742021-07-19 14:56:29 +02001395 /* Handle wildcarded DLCX that refers to the whole trunk. This means
1396 * that we walk over all endpoints on the trunk in order to drop all
1397 * connections on the trunk. (see also RFC3435 Annex F.7) */
1398 if (rq->wildcarded) {
1399 int num_conns = 0;
1400 for (i = 0; i < trunk->number_endpoints; i++) {
1401 num_conns += llist_count(&trunk->endpoints[i]->conns);
1402 mgcp_endp_release(trunk->endpoints[i]);
1403 }
1404 rate_ctr_add(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS), num_conns);
Ericfbcf4a62021-09-09 18:02:31 +02001405 return create_ok_response(trunk, NULL, 200, "DLCX", pdata->trans);
Philipp Maierf486e742021-07-19 14:56:29 +02001406 }
1407
Philipp Maierce187052021-07-23 10:58:19 +02001408 /* The logic does not permit to go past this point without having the
1409 * the endp pointer populated. */
1410 OSMO_ASSERT(endp);
1411
Philipp Maierf4c0e372017-10-11 16:06:45 +02001412 /* When no connection id is supplied, we will interpret this as a
Philipp Maierf486e742021-07-19 14:56:29 +02001413 * wildcarded DLCX that refers to the selected endpoint. This means
1414 * that we drop all connections on that specific endpoint at once.
1415 * (See also RFC3435 Section F.7) */
Philipp Maier01d24a32017-11-21 17:26:09 +01001416 if (!conn_id) {
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001417 int num_conns = llist_count(&endp->conns);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001418 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1419 "DLCX: missing ci (connectionIdentifier), will remove all connections (%d total) at once\n",
1420 num_conns);
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001421
1422 if (num_conns > 0)
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001423 rate_ctr_add(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS), num_conns);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001424
Philipp Maier1355d7e2018-02-01 14:30:06 +01001425 mgcp_endp_release(endp);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001426
1427 /* Note: In this case we do not return any statistics,
1428 * as we assume that the client is not interested in
1429 * this case. */
Ericfbcf4a62021-09-09 18:02:31 +02001430 return create_ok_response(endp, endp, 200, "DLCX", pdata->trans);
Philipp Maierf4c0e372017-10-11 16:06:45 +02001431 }
1432
Philipp Maierf4c0e372017-10-11 16:06:45 +02001433 /* Find the connection */
Philipp Maier87bd9be2017-08-22 16:35:41 +02001434 conn = mgcp_conn_get_rtp(endp, conn_id);
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001435 if (!conn) {
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001436 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_FAIL_INVALID_CONNID));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001437 goto error3;
Stefan Sperling8ab3fbb2018-10-30 14:57:25 +01001438 }
Philipp Maier87bd9be2017-08-22 16:35:41 +02001439 /* save the statistics of the current connection */
1440 mgcp_format_stats(stats, sizeof(stats), conn->conn);
1441
1442 /* delete connection */
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001443 LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG, "DLCX: deleting conn:%s\n",
1444 mgcp_conn_dump(conn->conn));
Philipp Maier87bd9be2017-08-22 16:35:41 +02001445 mgcp_conn_free(endp, conn_id);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001446 LOGPENDP(endp, DLMGCP, LOGL_NOTICE,
1447 "DLCX: connection successfully deleted\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +02001448
1449 /* When all connections are closed, the endpoint will be released
1450 * in order to be ready to be used by another call. */
1451 if (llist_count(&endp->conns) <= 0) {
Philipp Maier1355d7e2018-02-01 14:30:06 +01001452 mgcp_endp_release(endp);
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001453 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: endpoint released\n");
Philipp Maier87bd9be2017-08-22 16:35:41 +02001454 }
1455
Pau Espin Pedrol907744e2021-06-04 17:57:34 +02001456 rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_DLCX_SUCCESS));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001457 if (silent)
1458 goto out_silent;
Ericfbcf4a62021-09-09 18:02:31 +02001459 return create_ok_resp_with_param(endp, endp, 250, "DLCX", pdata->trans, stats);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001460
1461error3:
Ericfbcf4a62021-09-09 18:02:31 +02001462 return create_err_response(endp, endp, error_code, "DLCX", pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001463
1464out_silent:
Pau Espin Pedrol3239f622019-04-24 18:47:46 +02001465 LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "DLCX: silent exit\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001466 return NULL;
1467}
1468
Philipp Maier87bd9be2017-08-22 16:35:41 +02001469/* RSIP command handler, processes the received command */
Philipp Maier8dc35972021-07-14 11:20:16 +02001470static struct msgb *handle_rsip(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001471{
Philipp Maier87bd9be2017-08-22 16:35:41 +02001472 /* TODO: Also implement the resetting of a specific endpoint
1473 * to make mgcp_send_reset_ep() work. Currently this will call
1474 * mgcp_rsip_cb() in mgw_main.c, which sets reset_endpoints=1
1475 * to make read_call_agent() reset all endpoints when called
1476 * next time. In order to selectively reset endpoints some
1477 * mechanism to distinguish which endpoint shall be resetted
1478 * is needed */
1479
1480 LOGP(DLMGCP, LOGL_NOTICE, "RSIP: resetting all endpoints ...\n");
1481
Philipp Maier8dc35972021-07-14 11:20:16 +02001482 if (rq->pdata->cfg->reset_cb)
1483 rq->pdata->cfg->reset_cb(rq->endp->trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001484 return NULL;
1485}
1486
1487static char extract_tone(const char *line)
1488{
1489 const char *str = strstr(line, "D/");
1490 if (!str)
1491 return CHAR_MAX;
1492
1493 return str[2];
1494}
1495
Philipp Maier87bd9be2017-08-22 16:35:41 +02001496/* This can request like DTMF detection and forward, fax detection... it
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001497 * can also request when the notification should be send and such. We don't
Philipp Maier87bd9be2017-08-22 16:35:41 +02001498 * do this right now. */
Philipp Maier8dc35972021-07-14 11:20:16 +02001499static struct msgb *handle_noti_req(struct mgcp_request_data *rq)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001500{
1501 int res = 0;
1502 char *line;
1503 char tone = CHAR_MAX;
1504
Philipp Maier87bd9be2017-08-22 16:35:41 +02001505 LOGP(DLMGCP, LOGL_NOTICE, "RQNT: processing request for notification ...\n");
1506
Philipp Maier8dc35972021-07-14 11:20:16 +02001507 for_each_line(line, rq->pdata->save) {
Pau Espin Pedrol0c6c3c12019-06-25 17:18:12 +02001508 switch (toupper(line[0])) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001509 case 'S':
1510 tone = extract_tone(line);
1511 break;
1512 }
1513 }
1514
1515 /* we didn't see a signal request with a tone */
1516 if (tone == CHAR_MAX)
Ericfbcf4a62021-09-09 18:02:31 +02001517 return create_ok_response(rq->endp, rq->endp, 200, "RQNT", rq->pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001518
Philipp Maier8dc35972021-07-14 11:20:16 +02001519 if (rq->pdata->cfg->rqnt_cb)
1520 res = rq->pdata->cfg->rqnt_cb(rq->endp, tone);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001521
Ericfbcf4a62021-09-09 18:02:31 +02001522 return res == 0 ? create_ok_response(rq->endp, rq->endp, 200, "RQNT", rq->pdata->trans) :
1523 create_err_response(rq->endp, rq->endp, res, "RQNT", rq->pdata->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001524}
1525
Philipp Maier87bd9be2017-08-22 16:35:41 +02001526/* Connection keepalive timer, will take care that dummy packets are send
Harald Welte1d1b98f2017-12-25 10:03:40 +01001527 * regularly, so that NAT connections stay open */
Philipp Maier14b27a82020-06-02 20:15:30 +02001528static void mgcp_keepalive_timer_cb(void *_trunk)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001529{
Philipp Maier14b27a82020-06-02 20:15:30 +02001530 struct mgcp_trunk *trunk = _trunk;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001531 struct mgcp_conn *conn;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001532 int i;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001533
Philipp Maiere726d4f2017-11-01 10:41:34 +01001534 LOGP(DLMGCP, LOGL_DEBUG, "triggered trunk %d keepalive timer\n",
Philipp Maier14b27a82020-06-02 20:15:30 +02001535 trunk->trunk_nr);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001536
Philipp Maiere726d4f2017-11-01 10:41:34 +01001537 /* Do not accept invalid configuration values
1538 * valid is MGCP_KEEPALIVE_NEVER, MGCP_KEEPALIVE_ONCE and
1539 * values greater 0 */
Philipp Maier14b27a82020-06-02 20:15:30 +02001540 OSMO_ASSERT(trunk->keepalive_interval >= MGCP_KEEPALIVE_ONCE);
Philipp Maiere726d4f2017-11-01 10:41:34 +01001541
1542 /* The dummy packet functionality has been disabled, we will exit
1543 * immediately, no further timer is scheduled, which means we will no
1544 * longer send dummy packets even when we did before */
Philipp Maier14b27a82020-06-02 20:15:30 +02001545 if (trunk->keepalive_interval == MGCP_KEEPALIVE_NEVER)
Philipp Maiere726d4f2017-11-01 10:41:34 +01001546 return;
1547
1548 /* In cases where only one dummy packet is sent, we do not need
1549 * the timer since the functions that handle the CRCX and MDCX are
1550 * triggering the sending of the dummy packet. So we behave like in
1551 * the MGCP_KEEPALIVE_NEVER case */
Philipp Maier14b27a82020-06-02 20:15:30 +02001552 if (trunk->keepalive_interval == MGCP_KEEPALIVE_ONCE)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001553 return;
1554
Philipp Maier87bd9be2017-08-22 16:35:41 +02001555 /* Send walk over all endpoints and send out dummy packets through
1556 * every connection present on each endpoint */
Philipp Maier4131a652021-07-07 14:04:34 +02001557 for (i = 0; i < trunk->number_endpoints; ++i) {
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001558 struct mgcp_endpoint *endp = trunk->endpoints[i];
Philipp Maier87bd9be2017-08-22 16:35:41 +02001559 llist_for_each_entry(conn, &endp->conns, entry) {
Pau Espin Pedrolca280a12021-07-06 18:15:35 +02001560 if (conn->type == MGCP_CONN_TYPE_RTP &&
1561 conn->mode == MGCP_CONN_RECV_ONLY &&
1562 mgcp_rtp_end_remote_addr_available(&conn->u.rtp.end))
Philipp Maier87bd9be2017-08-22 16:35:41 +02001563 send_dummy(endp, &conn->u.rtp);
1564 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001565 }
1566
Philipp Maiere726d4f2017-11-01 10:41:34 +01001567 /* Schedule the keepalive timer for the next round */
1568 LOGP(DLMGCP, LOGL_DEBUG, "rescheduling trunk %d keepalive timer\n",
Philipp Maier14b27a82020-06-02 20:15:30 +02001569 trunk->trunk_nr);
1570 osmo_timer_schedule(&trunk->keepalive_timer, trunk->keepalive_interval,
Philipp Maier87bd9be2017-08-22 16:35:41 +02001571 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001572}
1573
Philipp Maier14b27a82020-06-02 20:15:30 +02001574void mgcp_trunk_set_keepalive(struct mgcp_trunk *trunk, int interval)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001575{
Philipp Maier14b27a82020-06-02 20:15:30 +02001576 trunk->keepalive_interval = interval;
1577 osmo_timer_setup(&trunk->keepalive_timer, mgcp_keepalive_timer_cb, trunk);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001578
1579 if (interval <= 0)
Philipp Maier14b27a82020-06-02 20:15:30 +02001580 osmo_timer_del(&trunk->keepalive_timer);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001581 else
Philipp Maier14b27a82020-06-02 20:15:30 +02001582 osmo_timer_schedule(&trunk->keepalive_timer,
1583 trunk->keepalive_interval, 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001584}
1585
Philipp Maier38533ba2021-07-29 17:38:34 +02001586/* Free config, this function is automatically called by talloc_free when the configuration is freed. */
1587static int config_free_talloc_destructor(struct mgcp_config *cfg)
1588{
1589 mgcp_ratectr_global_free(cfg);
1590 return 0;
1591}
1592
Philipp Maier87bd9be2017-08-22 16:35:41 +02001593/*! allocate configuration with default values.
1594 * (called once at startup by main function) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001595struct mgcp_config *mgcp_config_alloc(void)
1596{
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001597 /* FIXME: This is unrelated to the protocol, put this in some
1598 * appropiate place! */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001599 struct mgcp_config *cfg;
1600
1601 cfg = talloc_zero(NULL, struct mgcp_config);
1602 if (!cfg) {
1603 LOGP(DLMGCP, LOGL_FATAL, "Failed to allocate config.\n");
1604 return NULL;
1605 }
1606
Philipp Maier12943ea2018-01-17 15:40:25 +01001607 osmo_strlcpy(cfg->domain, "mgw", sizeof(cfg->domain));
1608
Eric55fdfc22021-08-13 00:14:18 +02001609 cfg->net_ports.lock = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001610 cfg->net_ports.range_start = RTP_PORT_DEFAULT_RANGE_START;
1611 cfg->net_ports.range_end = RTP_PORT_DEFAULT_RANGE_END;
1612 cfg->net_ports.last_port = cfg->net_ports.range_start;
1613
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001614 cfg->source_port = 2427;
Eric2764bdb2021-08-23 22:11:47 +02001615 osmo_strlcpy(cfg->source_addr, "0.0.0.0", sizeof(cfg->source_addr));
1616 osmo_strlcpy(cfg->osmux_addr, "0.0.0.0", sizeof(cfg->osmux_addr));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001617
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001618 cfg->rtp_processing_cb = &mgcp_rtp_processing_default;
1619 cfg->setup_rtp_processing_cb = &mgcp_setup_rtp_processing_default;
1620
1621 cfg->get_net_downlink_format_cb = &mgcp_get_net_downlink_format_default;
1622
Philipp Maierd19de2e2020-06-03 13:55:33 +02001623 INIT_LLIST_HEAD(&cfg->trunks);
1624
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001625 /* Allocate virtual trunk */
Philipp Maierd19de2e2020-06-03 13:55:33 +02001626 if (!mgcp_trunk_alloc(cfg, MGCP_TRUNK_VIRTUAL, MGCP_VIRT_TRUNK_ID)) {
Harald Welte3ac604e2019-05-08 14:07:41 +02001627 talloc_free(cfg);
1628 return NULL;
1629 }
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001630
Philipp Maiera065e632021-07-09 13:22:42 +02001631 mgcp_ratectr_global_alloc(cfg);
Philipp Maier38533ba2021-07-29 17:38:34 +02001632 talloc_set_destructor(cfg, config_free_talloc_destructor);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001633
1634 return cfg;
1635}
1636
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001637static int send_agent(struct mgcp_config *cfg, const char *buf, int len)
1638{
1639 return write(cfg->gw_fd.bfd.fd, buf, len);
1640}
1641
Philipp Maier87bd9be2017-08-22 16:35:41 +02001642/*! Reset all endpoints by sending RSIP message to self.
1643 * (called by VTY)
1644 * \param[in] endp trunk endpoint
1645 * \param[in] endpoint number
1646 * \returns 0 on success, -1 on error */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001647int mgcp_send_reset_all(struct mgcp_config *cfg)
1648{
Philipp Maier12943ea2018-01-17 15:40:25 +01001649 char buf[MGCP_ENDPOINT_MAXLEN + 128];
1650 int len;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001651 int rc;
1652
Philipp Maier12943ea2018-01-17 15:40:25 +01001653 len = snprintf(buf, sizeof(buf),
1654 "RSIP 1 *@%s MGCP 1.0\r\n", cfg->domain);
1655 if (len < 0)
1656 return -1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001657
Philipp Maier12943ea2018-01-17 15:40:25 +01001658 rc = send_agent(cfg, buf, len);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001659 if (rc <= 0)
1660 return -1;
1661
1662 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001663}
1664
Philipp Maier87bd9be2017-08-22 16:35:41 +02001665/*! Reset a single endpoint by sending RSIP message to self.
1666 * (called by VTY)
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001667 * \param[in] endp to reset
Philipp Maier87bd9be2017-08-22 16:35:41 +02001668 * \returns 0 on success, -1 on error */
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001669int mgcp_send_reset_ep(struct mgcp_endpoint *endp)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001670{
Philipp Maier12943ea2018-01-17 15:40:25 +01001671 char buf[MGCP_ENDPOINT_MAXLEN + 128];
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001672 int len;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001673 int rc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001674
1675 len = snprintf(buf, sizeof(buf),
Philipp Maierc66ab2c2020-06-02 20:55:34 +02001676 "RSIP 39 %s MGCP 1.0\r\n", endp->name);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001677 if (len < 0)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001678 return -1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001679
Ericfbf78d12021-08-23 22:31:39 +02001680 rc = send_agent(endp->trunk->cfg, buf, len);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001681 if (rc <= 0)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001682 return -1;
1683
Philipp Maier87bd9be2017-08-22 16:35:41 +02001684 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001685}