blob: ac25894f85c936991a3e6a67ea9f628215c9a154 [file] [log] [blame]
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
2/* The protocol implementation */
3
4/*
5 * (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
6 * (C) 2009-2012 by On-Waves
7 * All Rights Reserved
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU Affero General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Affero General Public License for more details.
18 *
19 * You should have received a copy of the GNU Affero General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 *
22 */
23
24#include <ctype.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <time.h>
28#include <limits.h>
29#include <unistd.h>
30#include <errno.h>
31
32#include <osmocom/core/msgb.h>
33#include <osmocom/core/talloc.h>
34#include <osmocom/core/select.h>
35
Philipp Maier87bd9be2017-08-22 16:35:41 +020036#include <osmocom/mgcp/mgcp.h>
37#include <osmocom/mgcp/mgcp_internal.h>
38#include <osmocom/mgcp/mgcp_stat.h>
39#include <osmocom/mgcp/mgcp_msg.h>
40#include <osmocom/mgcp/mgcp_ep.h>
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020041
42struct mgcp_request {
43 char *name;
Philipp Maier87bd9be2017-08-22 16:35:41 +020044 struct msgb *(*handle_request) (struct mgcp_parse_data * data);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020045 char *debug_name;
46};
47
48#define MGCP_REQUEST(NAME, REQ, DEBUG_NAME) \
49 { .name = NAME, .handle_request = REQ, .debug_name = DEBUG_NAME },
50
51static struct msgb *handle_audit_endpoint(struct mgcp_parse_data *data);
52static struct msgb *handle_create_con(struct mgcp_parse_data *data);
53static struct msgb *handle_delete_con(struct mgcp_parse_data *data);
54static struct msgb *handle_modify_con(struct mgcp_parse_data *data);
55static struct msgb *handle_rsip(struct mgcp_parse_data *data);
56static struct msgb *handle_noti_req(struct mgcp_parse_data *data);
57
Philipp Maier87bd9be2017-08-22 16:35:41 +020058/* Initalize transcoder */
59static int setup_rtp_processing(struct mgcp_endpoint *endp,
60 struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020061{
Philipp Maier87bd9be2017-08-22 16:35:41 +020062 struct mgcp_config *cfg = endp->cfg;
63 struct mgcp_conn_rtp *conn_src = NULL;
64 struct mgcp_conn_rtp *conn_dst = conn;
65 struct mgcp_conn *_conn;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020066
Philipp Maier87bd9be2017-08-22 16:35:41 +020067 if (conn->type != MGCP_RTP_DEFAULT) {
68 LOGP(DLMGCP, LOGL_NOTICE,
69 "endpoint:%x RTP-setup: Endpoint is not configured as RTP default, stopping here!\n",
70 ENDPOINT_NUMBER(endp));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020071 return 0;
72 }
73
Philipp Maier87bd9be2017-08-22 16:35:41 +020074 if (conn->conn->mode == MGCP_CONN_LOOPBACK) {
75 LOGP(DLMGCP, LOGL_NOTICE,
76 "endpoint:%x RTP-setup: Endpoint is in loopback mode, stopping here!\n",
77 ENDPOINT_NUMBER(endp));
78 return 0;
79 }
80
81 /* Find the "sister" connection */
82 llist_for_each_entry(_conn, &endp->conns, entry) {
83 if (_conn->id != conn->conn->id) {
84 conn_src = &_conn->u.rtp;
85 break;
86 }
87 }
88
89 return cfg->setup_rtp_processing_cb(endp, &conn_dst->end,
90 &conn_src->end);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020091}
92
Philipp Maier87bd9be2017-08-22 16:35:41 +020093/* array of function pointers for handling various
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020094 * messages. In the future this might be binary sorted
Philipp Maier87bd9be2017-08-22 16:35:41 +020095 * for performance reasons. */
96static const struct mgcp_request mgcp_requests[] = {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020097 MGCP_REQUEST("AUEP", handle_audit_endpoint, "AuditEndpoint")
Philipp Maier87bd9be2017-08-22 16:35:41 +020098 MGCP_REQUEST("CRCX", handle_create_con, "CreateConnection")
99 MGCP_REQUEST("DLCX", handle_delete_con, "DeleteConnection")
100 MGCP_REQUEST("MDCX", handle_modify_con, "ModifiyConnection")
101 MGCP_REQUEST("RQNT", handle_noti_req, "NotificationRequest")
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200102
Philipp Maier87bd9be2017-08-22 16:35:41 +0200103 /* SPEC extension */
104 MGCP_REQUEST("RSIP", handle_rsip, "ReSetInProgress")
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200105};
106
Philipp Maier87bd9be2017-08-22 16:35:41 +0200107/* Helper function to allocate some memory for responses and retransmissions */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200108static struct msgb *mgcp_msgb_alloc(void)
109{
110 struct msgb *msg;
111 msg = msgb_alloc_headroom(4096, 128, "MGCP msg");
112 if (!msg)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200113 LOGP(DLMGCP, LOGL_ERROR, "Failed to msgb for MGCP data.\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200114
115 return msg;
116}
117
Philipp Maier87bd9be2017-08-22 16:35:41 +0200118/* Helper function for do_retransmission() and create_resp() */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200119static struct msgb *do_retransmission(const struct mgcp_endpoint *endp)
120{
121 struct msgb *msg = mgcp_msgb_alloc();
122 if (!msg)
123 return NULL;
124
125 msg->l2h = msgb_put(msg, strlen(endp->last_response));
126 memcpy(msg->l2h, endp->last_response, msgb_l2len(msg));
Philipp Maier87bd9be2017-08-22 16:35:41 +0200127 mgcp_disp_msg(msg->l2h, msgb_l2len(msg), "Retransmitted response");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200128 return msg;
129}
130
131static struct msgb *create_resp(struct mgcp_endpoint *endp, int code,
132 const char *txt, const char *msg,
133 const char *trans, const char *param,
134 const char *sdp)
135{
136 int len;
137 struct msgb *res;
138
139 res = mgcp_msgb_alloc();
140 if (!res)
141 return NULL;
142
Philipp Maier87bd9be2017-08-22 16:35:41 +0200143 len = snprintf((char *)res->data, 2048, "%d %s%s%s\r\n%s",
144 code, trans, txt, param ? param : "", sdp ? sdp : "");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200145 if (len < 0) {
146 LOGP(DLMGCP, LOGL_ERROR, "Failed to sprintf MGCP response.\n");
147 msgb_free(res);
148 return NULL;
149 }
150
151 res->l2h = msgb_put(res, len);
152 LOGP(DLMGCP, LOGL_DEBUG, "Generated response: code=%d\n", code);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200153 mgcp_disp_msg(res->l2h, msgb_l2len(res), "Generated response");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200154
155 /*
156 * Remember the last transmission per endpoint.
157 */
158 if (endp) {
159 struct mgcp_trunk_config *tcfg = endp->tcfg;
160 talloc_free(endp->last_response);
161 talloc_free(endp->last_trans);
162 endp->last_trans = talloc_strdup(tcfg->endpoints, trans);
163 endp->last_response = talloc_strndup(tcfg->endpoints,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200164 (const char *)res->l2h,
165 msgb_l2len(res));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200166 }
167
168 return res;
169}
170
171static struct msgb *create_ok_resp_with_param(struct mgcp_endpoint *endp,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200172 int code, const char *msg,
173 const char *trans,
174 const char *param)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200175{
176 return create_resp(endp, code, " OK", msg, trans, param, NULL);
177}
178
179static struct msgb *create_ok_response(struct mgcp_endpoint *endp,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200180 int code, const char *msg,
181 const char *trans)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200182{
183 return create_ok_resp_with_param(endp, code, msg, trans, NULL);
184}
185
186static struct msgb *create_err_response(struct mgcp_endpoint *endp,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200187 int code, const char *msg,
188 const char *trans)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200189{
190 return create_resp(endp, code, " FAIL", msg, trans, NULL, NULL);
191}
192
193static int write_response_sdp(struct mgcp_endpoint *endp,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200194 struct mgcp_conn_rtp *conn,
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200195 char *sdp_record, size_t size, const char *addr)
196{
197 const char *fmtp_extra;
198 const char *audio_name;
199 int payload_type;
200 int len;
201 int nchars;
202
Philipp Maier87bd9be2017-08-22 16:35:41 +0200203 if (!conn)
204 return -1;
205
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200206 endp->cfg->get_net_downlink_format_cb(endp, &payload_type,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200207 &audio_name, &fmtp_extra, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200208
209 len = snprintf(sdp_record, size,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200210 "v=0\r\n"
211 "o=- %u 23 IN IP4 %s\r\n"
212 "s=-\r\n"
213 "c=IN IP4 %s\r\n"
214 "t=0 0\r\n", conn->conn->id, addr, addr);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200215
216 if (len < 0 || len >= size)
217 goto buffer_too_small;
218
219 if (payload_type >= 0) {
220 nchars = snprintf(sdp_record + len, size - len,
221 "m=audio %d RTP/AVP %d\r\n",
Philipp Maier87bd9be2017-08-22 16:35:41 +0200222 conn->end.local_port, payload_type);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200223 if (nchars < 0 || nchars >= size - len)
224 goto buffer_too_small;
225
226 len += nchars;
227
228 if (audio_name && endp->tcfg->audio_send_name) {
229 nchars = snprintf(sdp_record + len, size - len,
230 "a=rtpmap:%d %s\r\n",
231 payload_type, audio_name);
232
233 if (nchars < 0 || nchars >= size - len)
234 goto buffer_too_small;
235
236 len += nchars;
237 }
238
239 if (fmtp_extra) {
240 nchars = snprintf(sdp_record + len, size - len,
241 "%s\r\n", fmtp_extra);
242
243 if (nchars < 0 || nchars >= size - len)
244 goto buffer_too_small;
245
246 len += nchars;
247 }
248 }
Philipp Maier87bd9be2017-08-22 16:35:41 +0200249 if (conn->end.packet_duration_ms > 0 && endp->tcfg->audio_send_ptime) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200250 nchars = snprintf(sdp_record + len, size - len,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200251 "a=ptime:%u\r\n",
252 conn->end.packet_duration_ms);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200253 if (nchars < 0 || nchars >= size - len)
254 goto buffer_too_small;
255
256 len += nchars;
257 }
258
259 return len;
260
261buffer_too_small:
262 LOGP(DLMGCP, LOGL_ERROR, "SDP buffer too small: %zu (needed %d)\n",
263 size, len);
264 return -1;
265}
266
Philipp Maier87bd9be2017-08-22 16:35:41 +0200267/* Format MGCP response string (with SDP attached) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200268static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200269 struct mgcp_conn_rtp *conn,
270 const char *msg,
271 const char *trans_id)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200272{
273 const char *addr = endp->cfg->local_ip;
274 char sdp_record[4096];
275 int len;
276 int nchars;
277 char osmux_extension[strlen("\nX-Osmux: 255") + 1];
278
279 if (!addr)
280 addr = mgcp_net_src_addr(endp);
281
Philipp Maier87bd9be2017-08-22 16:35:41 +0200282 if (conn->osmux.state == OSMUX_STATE_NEGOTIATING) {
283 sprintf(osmux_extension, "\nX-Osmux: %u", conn->osmux.cid);
284 conn->osmux.state = OSMUX_STATE_ACTIVATING;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200285 } else {
286 osmux_extension[0] = '\0';
287 }
288
289 len = snprintf(sdp_record, sizeof(sdp_record),
Philipp Maier87bd9be2017-08-22 16:35:41 +0200290 "I: %u%s\n\n", conn->conn->id, osmux_extension);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200291 if (len < 0)
292 return NULL;
293
Philipp Maier87bd9be2017-08-22 16:35:41 +0200294 nchars = write_response_sdp(endp, conn, sdp_record + len,
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200295 sizeof(sdp_record) - len - 1, addr);
296 if (nchars < 0)
297 return NULL;
298
299 len += nchars;
300
301 sdp_record[sizeof(sdp_record) - 1] = '\0';
302
303 return create_resp(endp, 200, " OK", msg, trans_id, NULL, sdp_record);
304}
305
Philipp Maier87bd9be2017-08-22 16:35:41 +0200306/* Send out dummy packet to keep the connection open, if the connection is an
307 * osmux connection, send the dummy packet via OSMUX */
308static void send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200309{
Philipp Maier87bd9be2017-08-22 16:35:41 +0200310 if (conn->osmux.state != OSMUX_STATE_DISABLED)
311 osmux_send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200312 else
Philipp Maier87bd9be2017-08-22 16:35:41 +0200313 mgcp_send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200314}
315
Philipp Maier87bd9be2017-08-22 16:35:41 +0200316/* handle incoming messages:
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200317 * - this can be a command (four letters, space, transaction id)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200318 * - or a response (three numbers, space, transaction id) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200319struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
320{
321 struct mgcp_parse_data pdata;
322 int i, code, handled = 0;
323 struct msgb *resp = NULL;
324 char *data;
325
326 if (msgb_l2len(msg) < 4) {
327 LOGP(DLMGCP, LOGL_ERROR, "msg too short: %d\n", msg->len);
328 return NULL;
329 }
330
331 if (mgcp_msg_terminate_nul(msg))
332 return NULL;
333
Philipp Maier87bd9be2017-08-22 16:35:41 +0200334 mgcp_disp_msg(msg->l2h, msgb_l2len(msg), "Received message");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200335
Philipp Maier87bd9be2017-08-22 16:35:41 +0200336 /* attempt to treat it as a response */
337 if (sscanf((const char *)&msg->l2h[0], "%3d %*s", &code) == 1) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200338 LOGP(DLMGCP, LOGL_DEBUG, "Response: Code: %d\n", code);
339 return NULL;
340 }
341
342 msg->l3h = &msg->l2h[4];
343
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200344 /*
345 * Check for a duplicate message and respond.
346 */
347 memset(&pdata, 0, sizeof(pdata));
348 pdata.cfg = cfg;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200349 data = mgcp_strline((char *)msg->l3h, &pdata.save);
350 pdata.found = mgcp_parse_header(&pdata, data);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200351 if (pdata.endp && pdata.trans
Philipp Maier87bd9be2017-08-22 16:35:41 +0200352 && pdata.endp->last_trans
353 && strcmp(pdata.endp->last_trans, pdata.trans) == 0) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200354 return do_retransmission(pdata.endp);
355 }
356
357 for (i = 0; i < ARRAY_SIZE(mgcp_requests); ++i) {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200358 if (strncmp
359 (mgcp_requests[i].name, (const char *)&msg->l2h[0],
360 4) == 0) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200361 handled = 1;
362 resp = mgcp_requests[i].handle_request(&pdata);
363 break;
364 }
365 }
366
367 if (!handled)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200368 LOGP(DLMGCP, LOGL_NOTICE, "MSG with type: '%.4s' not handled\n",
369 &msg->l2h[0]);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200370
371 return resp;
372}
373
Philipp Maier87bd9be2017-08-22 16:35:41 +0200374/* AUEP command handler, processes the received command */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200375static struct msgb *handle_audit_endpoint(struct mgcp_parse_data *p)
376{
Philipp Maier87bd9be2017-08-22 16:35:41 +0200377 LOGP(DLMGCP, LOGL_NOTICE, "AUEP: auditing endpoint ...\n");
378
379 if (p->found != 0) {
380 LOGP(DLMGCP, LOGL_ERROR,
381 "AUEP: failed to find the endpoint.\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200382 return create_err_response(NULL, 500, "AUEP", p->trans);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200383 } else
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200384 return create_ok_response(p->endp, 200, "AUEP", p->trans);
385}
386
Philipp Maier87bd9be2017-08-22 16:35:41 +0200387/* Try to find a free port by attemting to bind on it. Also handle the
388 * counter that points on the next free port. Since we have a pointer
389 * to the next free port, binding should work on the first attemt,
390 * neverless, try at least the next 200 ports before giving up */
391static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200392{
393 int i;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200394 struct mgcp_rtp_end *end;
395 struct mgcp_port_range *range;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200396
Philipp Maier87bd9be2017-08-22 16:35:41 +0200397 OSMO_ASSERT(conn);
398 end = &conn->end;
399 OSMO_ASSERT(end);
400
401 range = &endp->cfg->net_ports;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200402
403 /* attempt to find a port */
404 for (i = 0; i < 200; ++i) {
405 int rc;
406
407 if (range->last_port >= range->range_end)
408 range->last_port = range->range_start;
409
Philipp Maier87bd9be2017-08-22 16:35:41 +0200410 rc = mgcp_bind_net_rtp_port(endp, range->last_port, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200411
412 range->last_port += 2;
413 if (rc == 0) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200414 return 0;
415 }
416
417 }
418
Philipp Maier87bd9be2017-08-22 16:35:41 +0200419 LOGP(DLMGCP, LOGL_ERROR,
420 "Allocating a RTP/RTCP port failed 200 times 0x%x.\n",
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200421 ENDPOINT_NUMBER(endp));
422 return -1;
423}
424
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200425/* Set the LCO from a string (see RFC 3435).
Philipp Maier87bd9be2017-08-22 16:35:41 +0200426 * The string is stored in the 'string' field. A NULL string is handled excatlyy
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200427 * like an empty string, the 'string' field is never NULL after this function
428 * has been called. */
429static void set_local_cx_options(void *ctx, struct mgcp_lco *lco,
430 const char *options)
431{
432 char *p_opt, *a_opt;
433 char codec[9];
434
435 talloc_free(lco->string);
436 talloc_free(lco->codec);
437 lco->codec = NULL;
438 lco->pkt_period_min = lco->pkt_period_max = 0;
439 lco->string = talloc_strdup(ctx, options ? options : "");
440
441 p_opt = strstr(lco->string, "p:");
442 if (p_opt && sscanf(p_opt, "p:%d-%d",
443 &lco->pkt_period_min, &lco->pkt_period_max) == 1)
444 lco->pkt_period_max = lco->pkt_period_min;
445
446 a_opt = strstr(lco->string, "a:");
447 if (a_opt && sscanf(a_opt, "a:%8[^,]", codec) == 1)
448 lco->codec = talloc_strdup(ctx, codec);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200449
450 LOGP(DLMGCP, LOGL_DEBUG,
451 "local CX options: lco->pkt_period_max: %i, lco->codec: %s\n",
452 lco->pkt_period_max, lco->codec);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200453}
454
455void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
456 struct mgcp_rtp_end *rtp)
457{
458 struct mgcp_trunk_config *tcfg = endp->tcfg;
459
460 int patch_ssrc = expect_ssrc_change && tcfg->force_constant_ssrc;
461
462 rtp->force_aligned_timing = tcfg->force_aligned_timing;
463 rtp->force_constant_ssrc = patch_ssrc ? 1 : 0;
464
465 LOGP(DLMGCP, LOGL_DEBUG,
466 "Configuring RTP endpoint: local port %d%s%s\n",
467 ntohs(rtp->rtp_port),
468 rtp->force_aligned_timing ? ", force constant timing" : "",
469 rtp->force_constant_ssrc ? ", force constant ssrc" : "");
470}
471
472uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp,
473 struct mgcp_rtp_end *rtp)
474{
475 int f = 0;
476
477 /* Get the number of frames per channel and packet */
478 if (rtp->frames_per_packet)
479 f = rtp->frames_per_packet;
480 else if (rtp->packet_duration_ms && rtp->codec.frame_duration_num) {
481 int den = 1000 * rtp->codec.frame_duration_num;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200482 f = (rtp->packet_duration_ms * rtp->codec.frame_duration_den +
483 den / 2)
484 / den;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200485 }
486
Philipp Maier87bd9be2017-08-22 16:35:41 +0200487 return rtp->codec.rate * f * rtp->codec.frame_duration_num /
488 rtp->codec.frame_duration_den;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200489}
490
491static int mgcp_osmux_setup(struct mgcp_endpoint *endp, const char *line)
492{
493 if (!endp->cfg->osmux_init) {
494 if (osmux_init(OSMUX_ROLE_BSC, endp->cfg) < 0) {
495 LOGP(DLMGCP, LOGL_ERROR, "Cannot init OSMUX\n");
496 return -1;
497 }
498 LOGP(DLMGCP, LOGL_NOTICE, "OSMUX socket has been set up\n");
499 }
500
501 return mgcp_parse_osmux_cid(line);
502}
503
Philipp Maier87bd9be2017-08-22 16:35:41 +0200504/* CRCX command handler, processes the received command */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200505static struct msgb *handle_create_con(struct mgcp_parse_data *p)
506{
507 struct mgcp_trunk_config *tcfg;
508 struct mgcp_endpoint *endp = p->endp;
509 int error_code = 400;
510
511 const char *local_options = NULL;
512 const char *callid = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200513 const char *ci = NULL;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200514 const char *mode = NULL;
515 char *line;
516 int have_sdp = 0, osmux_cid = -1;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200517 struct mgcp_conn_rtp *conn = NULL;
518 uint32_t conn_id;
519 char conn_name[512];
520
521 LOGP(DLMGCP, LOGL_NOTICE, "CRCX: creating new connection ...\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200522
523 if (p->found != 0)
524 return create_err_response(NULL, 510, "CRCX", p->trans);
525
526 /* parse CallID C: and LocalParameters L: */
527 for_each_line(line, p->save) {
528 if (!mgcp_check_param(endp, line))
529 continue;
530
531 switch (line[0]) {
532 case 'L':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200533 local_options = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200534 break;
535 case 'C':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200536 callid = (const char *)line + 3;
537 break;
538 case 'I':
539 ci = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200540 break;
541 case 'M':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200542 mode = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200543 break;
544 case 'X':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200545 /* If osmoux is disabled, just skip setting it up */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200546 if (!p->endp->cfg->osmux)
547 break;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200548 if (strncmp("Osmux: ", line + 2, strlen("Osmux: ")) ==
549 0)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200550 osmux_cid = mgcp_osmux_setup(endp, line);
551 break;
552 case '\0':
553 have_sdp = 1;
554 goto mgcp_header_done;
555 default:
Philipp Maier87bd9be2017-08-22 16:35:41 +0200556 LOGP(DLMGCP, LOGL_NOTICE,
557 "CRCX: endpoint:%x unhandled option: '%c'/%d\n",
558 ENDPOINT_NUMBER(endp), *line, *line);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200559 break;
560 }
561 }
562
563mgcp_header_done:
564 tcfg = p->endp->tcfg;
565
Philipp Maier87bd9be2017-08-22 16:35:41 +0200566 /* Check parameters */
567 if (!callid) {
568 LOGP(DLMGCP, LOGL_ERROR,
569 "CRCX: endpoint:%x insufficient parameters, missing callid\n",
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200570 ENDPOINT_NUMBER(endp));
571 return create_err_response(endp, 400, "CRCX", p->trans);
572 }
573
Philipp Maier87bd9be2017-08-22 16:35:41 +0200574 if (!mode) {
575 LOGP(DLMGCP, LOGL_ERROR,
576 "CRCX: endpoint:%x insufficient parameters, missing mode\n",
577 ENDPOINT_NUMBER(endp));
578 return create_err_response(endp, 400, "CRCX", p->trans);
579 }
580
581 if (!ci) {
582 LOGP(DLMGCP, LOGL_ERROR,
583 "CRCX: endpoint:%x insufficient parameters, missing connection id\n",
584 ENDPOINT_NUMBER(endp));
585 return create_err_response(endp, 400, "CRCX", p->trans);
586 }
587
588 /* Check if we are able to accept the creation of another connection */
589 if (llist_count(&endp->conns) >= endp->type->max_conns) {
590 LOGP(DLMGCP, LOGL_ERROR,
591 "CRCX: endpoint:%x endpoint full, max. %i connections allowed!\n",
592 endp->type->max_conns, ENDPOINT_NUMBER(endp));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200593 if (tcfg->force_realloc) {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200594 /* There is no more room for a connection, make some
595 * room by blindly tossing the oldest of the two two
596 * connections */
597 mgcp_conn_free_oldest(endp);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200598 } else {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200599 /* There is no more room for a connection, leave
600 * everything as it is and return with an error */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200601 return create_err_response(endp, 400, "CRCX", p->trans);
602 }
603 }
604
Philipp Maier87bd9be2017-08-22 16:35:41 +0200605 /* Check if this endpoint already serves a call, if so, check if the
606 * callids match up so that we are sure that this is our call */
607 if (endp->callid && mgcp_verify_call_id(endp, callid)) {
608 LOGP(DLMGCP, LOGL_ERROR,
609 "CRCX: endpoint:%x allready seized by other call (%s)\n",
610 ENDPOINT_NUMBER(endp), endp->callid);
611 if (tcfg->force_realloc)
612 /* This is not our call, toss everything by releasing
613 * the entire endpoint. (rude!) */
614 mgcp_release_endp(endp);
615 else {
616 /* This is not our call, leave everything as it is and
617 * return with an error. */
618 return create_err_response(endp, 400, "CRCX", p->trans);
619 }
620 }
621
622 /* Set the callid, creation of another connection will only be possible
623 * when the callid matches up. (Connections are distinuished by their
624 * connection ids) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200625 endp->callid = talloc_strdup(tcfg->endpoints, callid);
626
Philipp Maier87bd9be2017-08-22 16:35:41 +0200627 /* Extract audio codec information */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200628 set_local_cx_options(endp->tcfg->endpoints, &endp->local_options,
629 local_options);
630
Philipp Maier87bd9be2017-08-22 16:35:41 +0200631 if (mgcp_parse_ci(&conn_id, ci)) {
632 LOGP(DLMGCP, LOGL_ERROR,
633 "CRCX: endpoint:%x insufficient parameters, missing ci (connectionIdentifier)\n",
634 ENDPOINT_NUMBER(endp));
635 return create_err_response(endp, 400, "CRCX", p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200636 }
637
Philipp Maier87bd9be2017-08-22 16:35:41 +0200638 /* Only accept another connection when the connection ID is different. */
639 if (mgcp_conn_get_rtp(endp, conn_id)) {
640 LOGP(DLMGCP, LOGL_ERROR,
641 "CRCX: endpoint:%x there is already a connection with id %u present!\n",
642 conn_id, ENDPOINT_NUMBER(endp));
643 if (tcfg->force_realloc) {
644 /* Ignore the existing connection by just freeing it */
645 mgcp_conn_free(endp, conn_id);
646 } else {
647 /* There is already a connection with that ID present,
648 * leave everything as it is and return with an error. */
649 return create_err_response(endp, 400, "CRCX", p->trans);
650 }
651 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200652
Philipp Maier87bd9be2017-08-22 16:35:41 +0200653 snprintf(conn_name, sizeof(conn_name), "%s-%u", callid, conn_id);
654 mgcp_conn_alloc(NULL, endp, conn_id, MGCP_CONN_TYPE_RTP,
655 conn_name);
656 conn = mgcp_conn_get_rtp(endp, conn_id);
657 if (!conn) {
658 LOGP(DLMGCP, LOGL_ERROR,
659 "CRCX: endpoint:%x unable to allocate RTP connection\n",
660 ENDPOINT_NUMBER(endp));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200661 goto error2;
662
Philipp Maier87bd9be2017-08-22 16:35:41 +0200663 }
664
665 if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
666 error_code = 517;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200667 goto error2;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200668 }
669
670 mgcp_rtp_end_config(endp, 0, &conn->end);
671
672 if (allocate_port(endp, conn) != 0) {
673 goto error2;
674 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200675
676 /* Annotate Osmux circuit ID and set it to negotiating state until this
Philipp Maier87bd9be2017-08-22 16:35:41 +0200677 * is fully set up from the dummy load. */
678 conn->osmux.state = OSMUX_STATE_DISABLED;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200679 if (osmux_cid >= 0) {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200680 conn->osmux.cid = osmux_cid;
681 conn->osmux.state = OSMUX_STATE_NEGOTIATING;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200682 } else if (endp->cfg->osmux == OSMUX_USAGE_ONLY) {
683 LOGP(DLMGCP, LOGL_ERROR,
Philipp Maier87bd9be2017-08-22 16:35:41 +0200684 "CRCX: endpoint:%x osmux only and no osmux offered\n",
685 ENDPOINT_NUMBER(endp));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200686 goto error2;
687 }
688
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200689 /* set up RTP media parameters */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200690 if (have_sdp)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200691 mgcp_parse_sdp_data(endp, &conn->end, p);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200692 else if (endp->local_options.codec)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200693 mgcp_set_audio_info(p->cfg, &conn->end.codec,
694 PTYPE_UNDEFINED, endp->local_options.codec);
695 conn->end.fmtp_extra = talloc_strdup(tcfg->endpoints,
696 tcfg->audio_fmtp_extra);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200697
Philipp Maier87bd9be2017-08-22 16:35:41 +0200698 if (p->cfg->force_ptime) {
699 conn->end.packet_duration_ms = p->cfg->force_ptime;
700 conn->end.force_output_ptime = 1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200701 }
702
Philipp Maier87bd9be2017-08-22 16:35:41 +0200703 if (setup_rtp_processing(endp, conn) != 0) {
704 LOGP(DLMGCP, LOGL_ERROR,
705 "CRCX: endpoint:%x could not start RTP processing!\n",
706 ENDPOINT_NUMBER(endp));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200707 goto error2;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200708 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200709
710 /* policy CB */
711 if (p->cfg->policy_cb) {
712 int rc;
713 rc = p->cfg->policy_cb(tcfg, ENDPOINT_NUMBER(endp),
Philipp Maier87bd9be2017-08-22 16:35:41 +0200714 MGCP_ENDP_CRCX, p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200715 switch (rc) {
716 case MGCP_POLICY_REJECT:
Philipp Maier87bd9be2017-08-22 16:35:41 +0200717 LOGP(DLMGCP, LOGL_NOTICE,
718 "CRCX: endpoint:%x CRCX rejected by policy\n",
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200719 ENDPOINT_NUMBER(endp));
720 mgcp_release_endp(endp);
721 return create_err_response(endp, 400, "CRCX", p->trans);
722 break;
723 case MGCP_POLICY_DEFER:
724 /* stop processing */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200725 return NULL;
726 break;
727 case MGCP_POLICY_CONT:
728 /* just continue */
729 break;
730 }
731 }
732
Philipp Maier87bd9be2017-08-22 16:35:41 +0200733 LOGP(DLMGCP, LOGL_DEBUG,
734 "CRCX: endpoint:%x Creating connection: CI: %u port: %u\n",
735 ENDPOINT_NUMBER(endp), conn->conn->id, conn->end.local_port);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200736 if (p->cfg->change_cb)
737 p->cfg->change_cb(tcfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_CRCX);
738
Philipp Maier87bd9be2017-08-22 16:35:41 +0200739 if (conn->conn->mode & MGCP_CONN_RECV_ONLY
740 && tcfg->keepalive_interval != 0) {
741 send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200742 }
743
Philipp Maier87bd9be2017-08-22 16:35:41 +0200744 LOGP(DLMGCP, LOGL_NOTICE,
745 "CRCX: endpoint:%x connection successfully created\n",
746 ENDPOINT_NUMBER(endp));
747 return create_response_with_sdp(endp, conn, "CRCX", p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200748error2:
749 mgcp_release_endp(endp);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200750 LOGP(DLMGCP, LOGL_NOTICE,
751 "CRCX: endpoint:%x unable to create connection resource error\n",
752 ENDPOINT_NUMBER(endp));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200753 return create_err_response(endp, error_code, "CRCX", p->trans);
754}
755
Philipp Maier87bd9be2017-08-22 16:35:41 +0200756/* MDCX command handler, processes the received command */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200757static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
758{
759 struct mgcp_endpoint *endp = p->endp;
760 int error_code = 500;
761 int silent = 0;
762 int have_sdp = 0;
763 char *line;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200764 const char *ci = NULL;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200765 const char *local_options = NULL;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200766 const char *mode = NULL;
767 struct mgcp_conn_rtp *conn = NULL;
768 uint32_t conn_id;
769
770 LOGP(DLMGCP, LOGL_NOTICE, "MDCX: modifying existing connection ...\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200771
772 if (p->found != 0)
773 return create_err_response(NULL, 510, "MDCX", p->trans);
774
Philipp Maier87bd9be2017-08-22 16:35:41 +0200775 if (llist_count(&endp->conns) <= 0) {
776 LOGP(DLMGCP, LOGL_ERROR,
777 "MDCX: endpoint:%x endpoint is not holding a connection.\n",
778 ENDPOINT_NUMBER(endp));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200779 return create_err_response(endp, 400, "MDCX", p->trans);
780 }
781
782 for_each_line(line, p->save) {
783 if (!mgcp_check_param(endp, line))
784 continue;
785
786 switch (line[0]) {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200787 case 'C':
788 if (mgcp_verify_call_id(endp, line + 3) != 0)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200789 goto error3;
790 break;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200791 case 'I':
792 ci = (const char *)line + 3;
793 if (mgcp_verify_ci(endp, ci) != 0)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200794 goto error3;
795 break;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200796 case 'L':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200797 local_options = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200798 break;
799 case 'M':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200800 mode = (const char *)line + 3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200801 break;
802 case 'Z':
803 silent = strcmp("noanswer", line + 3) == 0;
804 break;
805 case '\0':
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200806 have_sdp = 1;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200807 goto mgcp_header_done;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200808 break;
809 default:
Philipp Maier87bd9be2017-08-22 16:35:41 +0200810 LOGP(DLMGCP, LOGL_NOTICE,
811 "MDCX: endpoint:%x Unhandled MGCP option: '%c'/%d\n",
812 ENDPOINT_NUMBER(endp), line[0], line[0]);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200813 break;
814 }
815 }
816
Philipp Maier87bd9be2017-08-22 16:35:41 +0200817mgcp_header_done:
818 if (mgcp_parse_ci(&conn_id, ci)) {
819 LOGP(DLMGCP, LOGL_ERROR,
820 "MDCX: endpoint:%x insufficient parameters, missing ci (connectionIdentifier)\n",
821 ENDPOINT_NUMBER(endp));
822 return create_err_response(endp, 400, "MDCX", p->trans);
823 }
824
825 conn = mgcp_conn_get_rtp(endp, conn_id);
826 if (!conn)
827 return create_err_response(endp, 400, "MDCX", p->trans);
828
829 if (mode) {
830 if (mgcp_parse_conn_mode(mode, endp, conn->conn) != 0) {
831 error_code = 517;
832 goto error3;
833 }
834 } else
835 conn->conn->mode = conn->conn->mode_orig;
836
837 if (have_sdp)
838 mgcp_parse_sdp_data(endp, &conn->end, p);
839
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200840 set_local_cx_options(endp->tcfg->endpoints, &endp->local_options,
841 local_options);
842
843 if (!have_sdp && endp->local_options.codec)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200844 mgcp_set_audio_info(p->cfg, &conn->end.codec,
845 PTYPE_UNDEFINED, endp->local_options.codec);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200846
Philipp Maier87bd9be2017-08-22 16:35:41 +0200847 if (setup_rtp_processing(endp, conn) != 0)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200848 goto error3;
849
Philipp Maier87bd9be2017-08-22 16:35:41 +0200850
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200851 /* policy CB */
852 if (p->cfg->policy_cb) {
853 int rc;
854 rc = p->cfg->policy_cb(endp->tcfg, ENDPOINT_NUMBER(endp),
Philipp Maier87bd9be2017-08-22 16:35:41 +0200855 MGCP_ENDP_MDCX, p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200856 switch (rc) {
857 case MGCP_POLICY_REJECT:
Philipp Maier87bd9be2017-08-22 16:35:41 +0200858 LOGP(DLMGCP, LOGL_NOTICE,
859 "MDCX: endpoint:%x rejected by policy\n",
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200860 ENDPOINT_NUMBER(endp));
861 if (silent)
862 goto out_silent;
863 return create_err_response(endp, 400, "MDCX", p->trans);
864 break;
865 case MGCP_POLICY_DEFER:
866 /* stop processing */
Philipp Maier87bd9be2017-08-22 16:35:41 +0200867 LOGP(DLMGCP, LOGL_DEBUG,
868 "MDCX: endpoint:%x defered by policy\n",
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200869 ENDPOINT_NUMBER(endp));
870 return NULL;
871 break;
872 case MGCP_POLICY_CONT:
873 /* just continue */
874 break;
875 }
876 }
877
Philipp Maier87bd9be2017-08-22 16:35:41 +0200878 mgcp_rtp_end_config(endp, 1, &conn->end);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200879
880 /* modify */
Philipp Maier87bd9be2017-08-22 16:35:41 +0200881 LOGP(DLMGCP, LOGL_DEBUG,
882 "MDCX: endpoint:%x modified conn:%s\n",
883 ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn->conn));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200884 if (p->cfg->change_cb)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200885 p->cfg->change_cb(endp->tcfg, ENDPOINT_NUMBER(endp),
886 MGCP_ENDP_MDCX);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200887
Philipp Maier87bd9be2017-08-22 16:35:41 +0200888 if (conn->conn->mode & MGCP_CONN_RECV_ONLY &&
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200889 endp->tcfg->keepalive_interval != 0)
Philipp Maier87bd9be2017-08-22 16:35:41 +0200890 send_dummy(endp, conn);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200891
892 if (silent)
893 goto out_silent;
894
Philipp Maier87bd9be2017-08-22 16:35:41 +0200895 LOGP(DLMGCP, LOGL_NOTICE,
896 "MDCX: endpoint:%x connection successfully modified\n",
897 ENDPOINT_NUMBER(endp));
898 return create_response_with_sdp(endp, conn, "MDCX", p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200899error3:
900 return create_err_response(endp, error_code, "MDCX", p->trans);
901
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200902out_silent:
Philipp Maier87bd9be2017-08-22 16:35:41 +0200903 LOGP(DLMGCP, LOGL_DEBUG, "MDCX: endpoint:%x silent exit\n",
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200904 ENDPOINT_NUMBER(endp));
905 return NULL;
906}
907
Philipp Maier87bd9be2017-08-22 16:35:41 +0200908/* DLCX command handler, processes the received command */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200909static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
910{
911 struct mgcp_endpoint *endp = p->endp;
912 int error_code = 400;
913 int silent = 0;
914 char *line;
915 char stats[1048];
Philipp Maier87bd9be2017-08-22 16:35:41 +0200916 const char *ci = NULL;
917 struct mgcp_conn_rtp *conn = NULL;
918 uint32_t conn_id;
919
920 LOGP(DLMGCP, LOGL_NOTICE,
921 "DLCX: endpoint:%x deleting connection ...\n",
922 ENDPOINT_NUMBER(endp));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200923
924 if (p->found != 0)
925 return create_err_response(NULL, error_code, "DLCX", p->trans);
926
Philipp Maier87bd9be2017-08-22 16:35:41 +0200927 if (llist_count(&endp->conns) <= 0) {
928 LOGP(DLMGCP, LOGL_ERROR,
929 "DLCX: endpoint:%x endpoint is not holding a connection.\n",
930 ENDPOINT_NUMBER(endp));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200931 return create_err_response(endp, 400, "DLCX", p->trans);
932 }
933
934 for_each_line(line, p->save) {
935 if (!mgcp_check_param(endp, line))
936 continue;
937
938 switch (line[0]) {
939 case 'C':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200940 if (mgcp_verify_call_id(endp, line + 3) != 0)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200941 goto error3;
942 break;
943 case 'I':
Philipp Maier87bd9be2017-08-22 16:35:41 +0200944 ci = (const char *)line + 3;
945 if (mgcp_verify_ci(endp, ci) != 0)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200946 goto error3;
947 break;
948 case 'Z':
949 silent = strcmp("noanswer", line + 3) == 0;
950 break;
951 default:
Philipp Maier87bd9be2017-08-22 16:35:41 +0200952 LOGP(DLMGCP, LOGL_NOTICE,
953 "DLCX: endpoint:%x Unhandled MGCP option: '%c'/%d\n",
954 ENDPOINT_NUMBER(endp), line[0], line[0]);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200955 break;
956 }
957 }
958
959 /* policy CB */
960 if (p->cfg->policy_cb) {
961 int rc;
962 rc = p->cfg->policy_cb(endp->tcfg, ENDPOINT_NUMBER(endp),
Philipp Maier87bd9be2017-08-22 16:35:41 +0200963 MGCP_ENDP_DLCX, p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200964 switch (rc) {
965 case MGCP_POLICY_REJECT:
Philipp Maier87bd9be2017-08-22 16:35:41 +0200966 LOGP(DLMGCP, LOGL_NOTICE,
967 "DLCX: endpoint:%x rejected by policy\n",
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200968 ENDPOINT_NUMBER(endp));
969 if (silent)
970 goto out_silent;
971 return create_err_response(endp, 400, "DLCX", p->trans);
972 break;
973 case MGCP_POLICY_DEFER:
974 /* stop processing */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200975 return NULL;
976 break;
977 case MGCP_POLICY_CONT:
978 /* just continue */
979 break;
980 }
981 }
982
Philipp Maier87bd9be2017-08-22 16:35:41 +0200983 /* find the connection */
984 if (mgcp_parse_ci(&conn_id, ci)) {
985 LOGP(DLMGCP, LOGL_ERROR,
986 "DLCX: endpoint:%x insufficient parameters, missing ci (connectionIdentifier)\n",
987 ENDPOINT_NUMBER(endp));
988 return create_err_response(endp, 400, "DLCX", p->trans);
989 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200990
Philipp Maier87bd9be2017-08-22 16:35:41 +0200991 conn = mgcp_conn_get_rtp(endp, conn_id);
992 if (!conn)
993 goto error3;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200994
Philipp Maier87bd9be2017-08-22 16:35:41 +0200995 /* save the statistics of the current connection */
996 mgcp_format_stats(stats, sizeof(stats), conn->conn);
997
998 /* delete connection */
999 LOGP(DLMGCP, LOGL_DEBUG, "DLCX: endpoint:%x deleting conn:%s\n",
1000 ENDPOINT_NUMBER(endp), mgcp_conn_dump(conn->conn));
1001 mgcp_conn_free(endp, conn_id);
1002 LOGP(DLMGCP, LOGL_NOTICE,
1003 "DLCX: endpoint:%x connection successfully deleted\n",
1004 ENDPOINT_NUMBER(endp));
1005
1006 /* When all connections are closed, the endpoint will be released
1007 * in order to be ready to be used by another call. */
1008 if (llist_count(&endp->conns) <= 0) {
1009 mgcp_release_endp(endp);
1010 LOGP(DLMGCP, LOGL_DEBUG,
1011 "DLCX: endpoint:%x endpoint released\n",
1012 ENDPOINT_NUMBER(endp));
1013 }
1014
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001015 if (p->cfg->change_cb)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001016 p->cfg->change_cb(endp->tcfg, ENDPOINT_NUMBER(endp),
1017 MGCP_ENDP_DLCX);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001018
1019 if (silent)
1020 goto out_silent;
1021 return create_ok_resp_with_param(endp, 250, "DLCX", p->trans, stats);
1022
1023error3:
1024 return create_err_response(endp, error_code, "DLCX", p->trans);
1025
1026out_silent:
Philipp Maier87bd9be2017-08-22 16:35:41 +02001027 LOGP(DLMGCP, LOGL_DEBUG, "DLCX: endpoint:%x silent exit\n",
1028 ENDPOINT_NUMBER(endp));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001029 return NULL;
1030}
1031
Philipp Maier87bd9be2017-08-22 16:35:41 +02001032/* RSIP command handler, processes the received command */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001033static struct msgb *handle_rsip(struct mgcp_parse_data *p)
1034{
Philipp Maier87bd9be2017-08-22 16:35:41 +02001035 /* TODO: Also implement the resetting of a specific endpoint
1036 * to make mgcp_send_reset_ep() work. Currently this will call
1037 * mgcp_rsip_cb() in mgw_main.c, which sets reset_endpoints=1
1038 * to make read_call_agent() reset all endpoints when called
1039 * next time. In order to selectively reset endpoints some
1040 * mechanism to distinguish which endpoint shall be resetted
1041 * is needed */
1042
1043 LOGP(DLMGCP, LOGL_NOTICE, "RSIP: resetting all endpoints ...\n");
1044
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001045 if (p->found != 0) {
Philipp Maier87bd9be2017-08-22 16:35:41 +02001046 LOGP(DLMGCP, LOGL_ERROR,
1047 "RSIP: failed to find the endpoint.\n");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001048 return NULL;
1049 }
1050
1051 if (p->cfg->reset_cb)
1052 p->cfg->reset_cb(p->endp->tcfg);
1053 return NULL;
1054}
1055
1056static char extract_tone(const char *line)
1057{
1058 const char *str = strstr(line, "D/");
1059 if (!str)
1060 return CHAR_MAX;
1061
1062 return str[2];
1063}
1064
Philipp Maier87bd9be2017-08-22 16:35:41 +02001065/* This can request like DTMF detection and forward, fax detection... it
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001066 * can also request when the notification should be send and such. We don't
Philipp Maier87bd9be2017-08-22 16:35:41 +02001067 * do this right now. */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001068static struct msgb *handle_noti_req(struct mgcp_parse_data *p)
1069{
1070 int res = 0;
1071 char *line;
1072 char tone = CHAR_MAX;
1073
Philipp Maier87bd9be2017-08-22 16:35:41 +02001074 LOGP(DLMGCP, LOGL_NOTICE, "RQNT: processing request for notification ...\n");
1075
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001076 if (p->found != 0)
1077 return create_err_response(NULL, 400, "RQNT", p->trans);
1078
1079 for_each_line(line, p->save) {
1080 switch (line[0]) {
1081 case 'S':
1082 tone = extract_tone(line);
1083 break;
1084 }
1085 }
1086
1087 /* we didn't see a signal request with a tone */
1088 if (tone == CHAR_MAX)
1089 return create_ok_response(p->endp, 200, "RQNT", p->trans);
1090
1091 if (p->cfg->rqnt_cb)
1092 res = p->cfg->rqnt_cb(p->endp, tone);
1093
1094 return res == 0 ?
Philipp Maier87bd9be2017-08-22 16:35:41 +02001095 create_ok_response(p->endp, 200, "RQNT", p->trans) :
1096 create_err_response(p->endp, res, "RQNT", p->trans);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001097}
1098
Philipp Maier87bd9be2017-08-22 16:35:41 +02001099/* Connection keepalive timer, will take care that dummy packets are send
1100 * regulary, so that NAT connections stay open */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001101static void mgcp_keepalive_timer_cb(void *_tcfg)
1102{
1103 struct mgcp_trunk_config *tcfg = _tcfg;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001104 struct mgcp_conn *conn;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001105 int i;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001106
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001107 LOGP(DLMGCP, LOGL_DEBUG, "Triggered trunk %d keepalive timer.\n",
1108 tcfg->trunk_nr);
1109
1110 if (tcfg->keepalive_interval <= 0)
1111 return;
1112
Philipp Maier87bd9be2017-08-22 16:35:41 +02001113 /* Send walk over all endpoints and send out dummy packets through
1114 * every connection present on each endpoint */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001115 for (i = 1; i < tcfg->number_endpoints; ++i) {
1116 struct mgcp_endpoint *endp = &tcfg->endpoints[i];
Philipp Maier87bd9be2017-08-22 16:35:41 +02001117 llist_for_each_entry(conn, &endp->conns, entry) {
1118 if (conn->mode == MGCP_CONN_RECV_ONLY)
1119 send_dummy(endp, &conn->u.rtp);
1120 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001121 }
1122
1123 LOGP(DLMGCP, LOGL_DEBUG, "Rescheduling trunk %d keepalive timer.\n",
1124 tcfg->trunk_nr);
Philipp Maier87bd9be2017-08-22 16:35:41 +02001125 osmo_timer_schedule(&tcfg->keepalive_timer, tcfg->keepalive_interval,
1126 0);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001127}
1128
1129void mgcp_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval)
1130{
1131 tcfg->keepalive_interval = interval;
1132 osmo_timer_setup(&tcfg->keepalive_timer, mgcp_keepalive_timer_cb, tcfg);
1133
1134 if (interval <= 0)
1135 osmo_timer_del(&tcfg->keepalive_timer);
1136 else
1137 osmo_timer_schedule(&tcfg->keepalive_timer,
1138 tcfg->keepalive_interval, 0);
1139}
1140
Philipp Maier87bd9be2017-08-22 16:35:41 +02001141/*! allocate configuration with default values.
1142 * (called once at startup by main function) */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001143struct mgcp_config *mgcp_config_alloc(void)
1144{
1145 struct mgcp_config *cfg;
1146
1147 cfg = talloc_zero(NULL, struct mgcp_config);
1148 if (!cfg) {
1149 LOGP(DLMGCP, LOGL_FATAL, "Failed to allocate config.\n");
1150 return NULL;
1151 }
1152
Philipp Maier87bd9be2017-08-22 16:35:41 +02001153 cfg->net_ports.range_start = RTP_PORT_DEFAULT_RANGE_START;
1154 cfg->net_ports.range_end = RTP_PORT_DEFAULT_RANGE_END;
1155 cfg->net_ports.last_port = cfg->net_ports.range_start;
1156
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001157 cfg->source_port = 2427;
1158 cfg->source_addr = talloc_strdup(cfg, "0.0.0.0");
1159 cfg->osmux_addr = talloc_strdup(cfg, "0.0.0.0");
1160
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001161 cfg->rtp_processing_cb = &mgcp_rtp_processing_default;
1162 cfg->setup_rtp_processing_cb = &mgcp_setup_rtp_processing_default;
1163
1164 cfg->get_net_downlink_format_cb = &mgcp_get_net_downlink_format_default;
1165
1166 /* default trunk handling */
1167 cfg->trunk.cfg = cfg;
1168 cfg->trunk.trunk_nr = 0;
1169 cfg->trunk.trunk_type = MGCP_TRUNK_VIRTUAL;
1170 cfg->trunk.audio_name = talloc_strdup(cfg, "AMR/8000");
1171 cfg->trunk.audio_payload = 126;
1172 cfg->trunk.audio_send_ptime = 1;
1173 cfg->trunk.audio_send_name = 1;
1174 cfg->trunk.omit_rtcp = 0;
1175 mgcp_trunk_set_keepalive(&cfg->trunk, MGCP_KEEPALIVE_ONCE);
1176
1177 INIT_LLIST_HEAD(&cfg->trunks);
1178
1179 return cfg;
1180}
1181
Philipp Maier87bd9be2017-08-22 16:35:41 +02001182/*! allocate configuration with default values.
1183 * (called once at startup by VTY)
1184 * \param[in] cfg mgcp configuration
1185 * \param[in] nr trunk number
1186 * \returns pointer to allocated trunk configuration */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001187struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int nr)
1188{
1189 struct mgcp_trunk_config *trunk;
1190
1191 trunk = talloc_zero(cfg, struct mgcp_trunk_config);
1192 if (!trunk) {
1193 LOGP(DLMGCP, LOGL_ERROR, "Failed to allocate.\n");
1194 return NULL;
1195 }
1196
1197 trunk->cfg = cfg;
1198 trunk->trunk_type = MGCP_TRUNK_E1;
1199 trunk->trunk_nr = nr;
1200 trunk->audio_name = talloc_strdup(cfg, "AMR/8000");
1201 trunk->audio_payload = 126;
1202 trunk->audio_send_ptime = 1;
1203 trunk->audio_send_name = 1;
1204 trunk->number_endpoints = 33;
1205 trunk->omit_rtcp = 0;
1206 mgcp_trunk_set_keepalive(trunk, MGCP_KEEPALIVE_ONCE);
1207 llist_add_tail(&trunk->entry, &cfg->trunks);
1208 return trunk;
1209}
1210
Philipp Maier87bd9be2017-08-22 16:35:41 +02001211/*! get trunk configuration by trunk number (index).
1212 * \param[in] cfg mgcp configuration
1213 * \param[in] index trunk number
1214 * \returns pointer to trunk configuration, NULL on error */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001215struct mgcp_trunk_config *mgcp_trunk_num(struct mgcp_config *cfg, int index)
1216{
1217 struct mgcp_trunk_config *trunk;
1218
1219 llist_for_each_entry(trunk, &cfg->trunks, entry)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001220 if (trunk->trunk_nr == index)
1221 return trunk;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001222
1223 return NULL;
1224}
1225
Philipp Maier87bd9be2017-08-22 16:35:41 +02001226/*! allocate endpoints and set default values.
1227 * (called once at startup by VTY)
1228 * \param[in] tcfg trunk configuration
1229 * \returns 0 on success, -1 on failure */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001230int mgcp_endpoints_allocate(struct mgcp_trunk_config *tcfg)
1231{
1232 int i;
1233
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001234 tcfg->endpoints = _talloc_zero_array(tcfg->cfg,
Philipp Maier87bd9be2017-08-22 16:35:41 +02001235 sizeof(struct mgcp_endpoint),
1236 tcfg->number_endpoints,
1237 "endpoints");
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001238 if (!tcfg->endpoints)
1239 return -1;
1240
1241 for (i = 0; i < tcfg->number_endpoints; ++i) {
Philipp Maier87bd9be2017-08-22 16:35:41 +02001242 INIT_LLIST_HEAD(&tcfg->endpoints[i].conns);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001243 tcfg->endpoints[i].cfg = tcfg->cfg;
1244 tcfg->endpoints[i].tcfg = tcfg;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001245
1246 /* NOTE: Currently all endpoints are of type RTP, this will
1247 * change when new variations are implemented */
1248 tcfg->endpoints[i].type = &ep_typeset.rtp;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001249 }
1250
1251 return 0;
1252}
1253
Philipp Maier87bd9be2017-08-22 16:35:41 +02001254/*! relase endpoint, all open connections are closed.
1255 * \param[in] endp endpoint to release */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001256void mgcp_release_endp(struct mgcp_endpoint *endp)
1257{
Philipp Maier87bd9be2017-08-22 16:35:41 +02001258 LOGP(DLMGCP, LOGL_DEBUG, "Releasing endpoint:%x\n",
1259 ENDPOINT_NUMBER(endp));
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001260
Philipp Maier87bd9be2017-08-22 16:35:41 +02001261 /* Normally this function should only be called wehen
1262 * all connections have been removed already. In case
1263 * that there are still connections open (e.g. when
1264 * RSIP is executed), free them all at once. */
1265 mgcp_conn_free_all(endp);
1266
1267 /* Reset endpoint parameters and states */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001268 talloc_free(endp->callid);
1269 endp->callid = NULL;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001270 talloc_free(endp->local_options.string);
1271 endp->local_options.string = NULL;
1272 talloc_free(endp->local_options.codec);
1273 endp->local_options.codec = NULL;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001274}
1275
1276static int send_agent(struct mgcp_config *cfg, const char *buf, int len)
1277{
1278 return write(cfg->gw_fd.bfd.fd, buf, len);
1279}
1280
Philipp Maier87bd9be2017-08-22 16:35:41 +02001281/*! Reset all endpoints by sending RSIP message to self.
1282 * (called by VTY)
1283 * \param[in] endp trunk endpoint
1284 * \param[in] endpoint number
1285 * \returns 0 on success, -1 on error */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001286int mgcp_send_reset_all(struct mgcp_config *cfg)
1287{
Philipp Maier87bd9be2017-08-22 16:35:41 +02001288 int rc;
1289
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001290 static const char mgcp_reset[] = {
Philipp Maier87bd9be2017-08-22 16:35:41 +02001291 "RSIP 1 *@mgw MGCP 1.0\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001292 };
1293
Philipp Maier87bd9be2017-08-22 16:35:41 +02001294 rc = send_agent(cfg, mgcp_reset, sizeof mgcp_reset - 1);
1295 if (rc <= 0)
1296 return -1;
1297
1298 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001299}
1300
Philipp Maier87bd9be2017-08-22 16:35:41 +02001301/*! Reset a single endpoint by sending RSIP message to self.
1302 * (called by VTY)
1303 * \param[in] endp trunk endpoint
1304 * \param[in] endpoint number
1305 * \returns 0 on success, -1 on error */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001306int mgcp_send_reset_ep(struct mgcp_endpoint *endp, int endpoint)
1307{
1308 char buf[128];
1309 int len;
Philipp Maier87bd9be2017-08-22 16:35:41 +02001310 int rc;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001311
1312 len = snprintf(buf, sizeof(buf),
Philipp Maier87bd9be2017-08-22 16:35:41 +02001313 "RSIP 39 %x@mgw MGCP 1.0\r\n", endpoint);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001314 if (len < 0)
Philipp Maier87bd9be2017-08-22 16:35:41 +02001315 return -1;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001316
1317 buf[sizeof(buf) - 1] = '\0';
1318
Philipp Maier87bd9be2017-08-22 16:35:41 +02001319 rc = send_agent(endp->cfg, buf, len);
1320 if (rc <= 0)
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001321 return -1;
1322
Philipp Maier87bd9be2017-08-22 16:35:41 +02001323 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001324}