blob: ccd15e8425283f1835bb2032b7a779e9ff314bbd [file] [log] [blame]
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
2/* The protocol implementation */
3
4/*
Holger Hans Peter Freyther8d0be252012-11-29 12:54:22 +01005 * (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
6 * (C) 2009-2012 by On-Waves
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01007 * All Rights Reserved
8 *
9 * This program is free software; you can redistribute it and/or modify
Harald Welte9af6ddf2011-01-01 15:25:50 +010010 * 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
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +010012 * (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
Harald Welte9af6ddf2011-01-01 15:25:50 +010017 * GNU Affero General Public License for more details.
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +010018 *
Harald Welte9af6ddf2011-01-01 15:25:50 +010019 * 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/>.
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +010021 *
22 */
23
24#include <ctype.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <time.h>
29#include <limits.h>
30#include <unistd.h>
Jacob Erlbeck845d0052014-03-17 12:30:56 +010031#include <errno.h>
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +010032
Pablo Neira Ayuso136f4532011-03-22 16:47:59 +010033#include <osmocom/core/msgb.h>
34#include <osmocom/core/talloc.h>
35#include <osmocom/core/select.h>
Holger Hans Peter Freyther16b7f5c2011-02-28 22:06:47 +010036
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +010037#include <openbsc/mgcp.h>
Holger Hans Peter Freyther7bdc6372010-02-20 21:21:02 +010038#include <openbsc/mgcp_internal.h>
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +010039
Jacob Erlbecka01bd602013-11-29 13:43:43 +010040#define for_each_non_empty_line(line, save) \
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +010041 for (line = strtok_r(NULL, "\r\n", &save); line;\
Harald Welteae199722012-01-27 12:31:36 +010042 line = strtok_r(NULL, "\r\n", &save))
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +010043
Jacob Erlbeck2bee7f92013-11-29 13:43:44 +010044#define for_each_line(line, save) \
45 for (line = strline_r(NULL, &save); line;\
46 line = strline_r(NULL, &save))
47
48char *strline_r(char *str, char **saveptr)
49{
50 char *result;
51
52 if (str)
53 *saveptr = str;
54
55 result = *saveptr;
56
57 if (*saveptr != NULL) {
58 *saveptr = strpbrk(*saveptr, "\r\n");
59
60 if (*saveptr != NULL) {
61 char *eos = *saveptr;
62
63 if ((*saveptr)[0] == '\r' && (*saveptr)[1] == '\n')
64 (*saveptr)++;
65 (*saveptr)++;
66 if ((*saveptr)[0] == '\0')
67 *saveptr = NULL;
68
69 *eos = '\0';
70 }
71 }
72
73 return result;
74}
75
Jacob Erlbeck72c30902013-11-25 15:23:35 +010076/* Assume audio frame length of 20ms */
77#define DEFAULT_RTP_AUDIO_FRAME_DUR_NUM 20
78#define DEFAULT_RTP_AUDIO_FRAME_DUR_DEN 1000
Jacob Erlbeckf6ec0e92013-12-04 10:30:11 +010079#define DEFAULT_RTP_AUDIO_PACKET_DURATION_MS 20
Jacob Erlbeck72c30902013-11-25 15:23:35 +010080#define DEFAULT_RTP_AUDIO_DEFAULT_RATE 8000
Jacob Erlbeck845d0052014-03-17 12:30:56 +010081#define DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS 1
Jacob Erlbeck72c30902013-11-25 15:23:35 +010082
Jacob Erlbeck452c1832014-05-09 19:42:17 +020083#define PTYPE_UNDEFINED (-1)
84
Holger Hans Peter Freytherf138f912010-08-05 08:08:17 +080085static void mgcp_rtp_end_reset(struct mgcp_rtp_end *end);
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +010086
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +010087struct mgcp_parse_data {
88 struct mgcp_config *cfg;
89 struct mgcp_endpoint *endp;
90 char *trans;
91 char *save;
92 int found;
93};
94
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +010095struct mgcp_request {
96 char *name;
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +010097 struct msgb *(*handle_request) (struct mgcp_parse_data *data);
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +010098 char *debug_name;
99};
100
101#define MGCP_REQUEST(NAME, REQ, DEBUG_NAME) \
102 { .name = NAME, .handle_request = REQ, .debug_name = DEBUG_NAME },
103
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100104static struct msgb *handle_audit_endpoint(struct mgcp_parse_data *data);
105static struct msgb *handle_create_con(struct mgcp_parse_data *data);
106static struct msgb *handle_delete_con(struct mgcp_parse_data *data);
107static struct msgb *handle_modify_con(struct mgcp_parse_data *data);
108static struct msgb *handle_rsip(struct mgcp_parse_data *data);
109static struct msgb *handle_noti_req(struct mgcp_parse_data *data);
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100110
Holger Hans Peter Freyther3b5e3c42010-09-18 06:35:29 +0800111static void create_transcoder(struct mgcp_endpoint *endp);
112static void delete_transcoder(struct mgcp_endpoint *endp);
113
Jacob Erlbecka0d64ce2014-03-13 14:24:52 +0100114static void setup_rtp_processing(struct mgcp_endpoint *endp);
115
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100116static int mgcp_analyze_header(struct mgcp_parse_data *parse, char *data);
117
Holger Hans Peter Freyther1cc5ff82014-05-22 15:03:10 +0200118static int mgcp_check_param(const struct mgcp_endpoint *endp, const char *line)
119{
120 const size_t line_len = strlen(line);
121 if (line[0] != '\0' && line_len < 2) {
122 LOGP(DMGCP, LOGL_ERROR,
123 "Wrong MGCP option format: '%s' on 0x%x\n",
124 line, ENDPOINT_NUMBER(endp));
125 return 0;
126 }
127
128 return 1;
129}
130
Holger Hans Peter Freyther46340132010-08-06 08:26:54 +0800131static uint32_t generate_call_id(struct mgcp_config *cfg)
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100132{
133 int i;
134
135 /* use the call id */
Holger Hans Peter Freyther7bdc6372010-02-20 21:21:02 +0100136 ++cfg->last_call_id;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100137
138 /* handle wrap around */
Holger Hans Peter Freyther7bdc6372010-02-20 21:21:02 +0100139 if (cfg->last_call_id == CI_UNUSED)
140 ++cfg->last_call_id;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100141
142 /* callstack can only be of size number_of_endpoints */
143 /* verify that the call id is free, e.g. in case of overrun */
Holger Hans Peter Freyther88ad7722011-02-28 00:56:17 +0100144 for (i = 1; i < cfg->trunk.number_endpoints; ++i)
145 if (cfg->trunk.endpoints[i].ci == cfg->last_call_id)
Holger Hans Peter Freyther7bdc6372010-02-20 21:21:02 +0100146 return generate_call_id(cfg);
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100147
Holger Hans Peter Freyther7bdc6372010-02-20 21:21:02 +0100148 return cfg->last_call_id;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100149}
150
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100151/*
152 * array of function pointers for handling various
153 * messages. In the future this might be binary sorted
154 * for performance reasons.
155 */
156static const struct mgcp_request mgcp_requests [] = {
157 MGCP_REQUEST("AUEP", handle_audit_endpoint, "AuditEndpoint")
158 MGCP_REQUEST("CRCX", handle_create_con, "CreateConnection")
159 MGCP_REQUEST("DLCX", handle_delete_con, "DeleteConnection")
160 MGCP_REQUEST("MDCX", handle_modify_con, "ModifiyConnection")
Holger Hans Peter Freyther6e94d6d2011-01-25 23:33:54 +0100161 MGCP_REQUEST("RQNT", handle_noti_req, "NotificationRequest")
Holger Hans Peter Freyther9bdcc9c2010-03-31 06:39:35 +0200162
163 /* SPEC extension */
164 MGCP_REQUEST("RSIP", handle_rsip, "ReSetInProgress")
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100165};
166
Holger Hans Peter Freyther62e836c2010-02-03 11:03:45 +0100167static struct msgb *mgcp_msgb_alloc(void)
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100168{
Holger Hans Peter Freyther62e836c2010-02-03 11:03:45 +0100169 struct msgb *msg;
170 msg = msgb_alloc_headroom(4096, 128, "MGCP msg");
171 if (!msg)
172 LOGP(DMGCP, LOGL_ERROR, "Failed to msgb for MGCP data.\n");
173
174 return msg;
175}
176
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100177static struct msgb *do_retransmission(const struct mgcp_endpoint *endp)
178{
179 struct msgb *msg = mgcp_msgb_alloc();
180 if (!msg)
181 return NULL;
182
183 msg->l2h = msgb_put(msg, strlen(endp->last_response));
184 memcpy(msg->l2h, endp->last_response, msgb_l2len(msg));
185 return msg;
186}
187
Holger Hans Peter Freythera769dcb2012-12-07 15:14:27 +0100188static struct msgb *create_resp(struct mgcp_endpoint *endp, int code,
189 const char *txt, const char *msg,
Holger Hans Peter Freyther0bf15a82012-09-14 17:18:12 +0200190 const char *trans, const char *param,
191 const char *sdp)
Holger Hans Peter Freyther62e836c2010-02-03 11:03:45 +0100192{
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100193 int len;
Holger Hans Peter Freyther62e836c2010-02-03 11:03:45 +0100194 struct msgb *res;
195
196 res = mgcp_msgb_alloc();
197 if (!res)
198 return NULL;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100199
Holger Hans Peter Freyther0bf15a82012-09-14 17:18:12 +0200200 len = snprintf((char *) res->data, 2048, "%d %s%s%s\r\n%s",
201 code, trans, txt, param ? param : "", sdp ? sdp : "");
202 if (len < 0) {
203 LOGP(DMGCP, LOGL_ERROR, "Failed to sprintf MGCP response.\n");
204 msgb_free(res);
205 return NULL;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100206 }
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100207
Holger Hans Peter Freyther62e836c2010-02-03 11:03:45 +0100208 res->l2h = msgb_put(res, len);
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100209 LOGP(DMGCP, LOGL_DEBUG, "Generated response: code: %d for '%s'\n", code, res->l2h);
210
211 /*
212 * Remember the last transmission per endpoint.
213 */
214 if (endp) {
215 struct mgcp_trunk_config *tcfg = endp->tcfg;
216 talloc_free(endp->last_response);
217 talloc_free(endp->last_trans);
218 endp->last_trans = talloc_strdup(tcfg->endpoints, trans);
219 endp->last_response = talloc_strndup(tcfg->endpoints,
220 (const char *) res->l2h,
221 msgb_l2len(res));
222 }
223
Holger Hans Peter Freyther62e836c2010-02-03 11:03:45 +0100224 return res;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100225}
226
Holger Hans Peter Freythera769dcb2012-12-07 15:14:27 +0100227static struct msgb *create_ok_resp_with_param(struct mgcp_endpoint *endp,
228 int code, const char *msg,
Holger Hans Peter Freyther0bf15a82012-09-14 17:18:12 +0200229 const char *trans, const char *param)
230{
Holger Hans Peter Freythera769dcb2012-12-07 15:14:27 +0100231 return create_resp(endp, code, " OK", msg, trans, param, NULL);
Holger Hans Peter Freyther0bf15a82012-09-14 17:18:12 +0200232}
233
Holger Hans Peter Freythera769dcb2012-12-07 15:14:27 +0100234static struct msgb *create_ok_response(struct mgcp_endpoint *endp,
235 int code, const char *msg, const char *trans)
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100236{
Holger Hans Peter Freythera769dcb2012-12-07 15:14:27 +0100237 return create_ok_resp_with_param(endp, code, msg, trans, NULL);
Holger Hans Peter Freyther6adac172011-01-06 16:19:55 +0100238}
239
Holger Hans Peter Freythera769dcb2012-12-07 15:14:27 +0100240static struct msgb *create_err_response(struct mgcp_endpoint *endp,
241 int code, const char *msg, const char *trans)
Holger Hans Peter Freyther6adac172011-01-06 16:19:55 +0100242{
Holger Hans Peter Freythera769dcb2012-12-07 15:14:27 +0100243 return create_resp(endp, code, " FAIL", msg, trans, NULL, NULL);
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100244}
245
Jacob Erlbeck997e1e82014-03-14 18:01:20 +0100246static int write_response_sdp(struct mgcp_endpoint *endp,
247 char *sdp_record, size_t size, const char *addr)
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100248{
Jacob Erlbeck168ca002014-03-17 12:40:07 +0100249 const char *fmtp_extra;
250 const char *audio_name;
251 int payload_type;
Jacob Erlbeck0a1bc562013-12-10 13:09:37 +0100252 int len;
Jacob Erlbeck997e1e82014-03-14 18:01:20 +0100253 int nchars;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100254
Jacob Erlbeck168ca002014-03-17 12:40:07 +0100255 endp->cfg->get_net_downlink_format_cb(endp, &payload_type,
256 &audio_name, &fmtp_extra);
257
Jacob Erlbeck997e1e82014-03-14 18:01:20 +0100258 len = snprintf(sdp_record, size,
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100259 "v=0\r\n"
Holger Hans Peter Freytherc7e49d32011-07-20 20:43:15 +0200260 "o=- %u 23 IN IP4 %s\r\n"
Holger Hans Peter Freytherb1edf7b2014-11-20 23:17:32 +0100261 "s=-\r\n"
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100262 "c=IN IP4 %s\r\n"
Jacob Erlbeck997e1e82014-03-14 18:01:20 +0100263 "t=0 0\r\n",
264 endp->ci, addr, addr);
Jacob Erlbeck0a1bc562013-12-10 13:09:37 +0100265
Jacob Erlbeck997e1e82014-03-14 18:01:20 +0100266 if (len < 0 || len >= size)
Jacob Erlbeck0a1bc562013-12-10 13:09:37 +0100267 goto buffer_too_small;
268
Jacob Erlbeck997e1e82014-03-14 18:01:20 +0100269 if (payload_type >= 0) {
270 nchars = snprintf(sdp_record + len, size - len,
271 "m=audio %d RTP/AVP %d\r\n",
272 endp->net_end.local_port, payload_type);
273 if (nchars < 0 || nchars >= size - len)
274 goto buffer_too_small;
275
276 len += nchars;
277
Holger Hans Peter Freyther619b0142014-11-19 16:04:45 +0100278 if (audio_name && endp->tcfg->audio_send_name) {
Jacob Erlbeck997e1e82014-03-14 18:01:20 +0100279 nchars = snprintf(sdp_record + len, size - len,
280 "a=rtpmap:%d %s\r\n",
281 payload_type, audio_name);
282
283 if (nchars < 0 || nchars >= size - len)
284 goto buffer_too_small;
285
286 len += nchars;
287 }
288
289 if (fmtp_extra) {
290 nchars = snprintf(sdp_record + len, size - len,
Jacob Erlbeckcb04ca22014-06-12 12:42:38 +0200291 "%s\r\n", fmtp_extra);
Jacob Erlbeck997e1e82014-03-14 18:01:20 +0100292
293 if (nchars < 0 || nchars >= size - len)
294 goto buffer_too_small;
295
296 len += nchars;
297 }
298 }
Jacob Erlbeck0a1bc562013-12-10 13:09:37 +0100299 if (endp->bts_end.packet_duration_ms > 0 && endp->tcfg->audio_send_ptime) {
Jacob Erlbeck997e1e82014-03-14 18:01:20 +0100300 nchars = snprintf(sdp_record + len, size - len,
301 "a=ptime:%d\r\n",
302 endp->bts_end.packet_duration_ms);
303 if (nchars < 0 || nchars >= size - len)
Jacob Erlbeck0a1bc562013-12-10 13:09:37 +0100304 goto buffer_too_small;
305
306 len += nchars;
307 }
Jacob Erlbeck997e1e82014-03-14 18:01:20 +0100308
309 return len;
Jacob Erlbeck0a1bc562013-12-10 13:09:37 +0100310
311buffer_too_small:
Holger Hans Peter Freytherc6e32ad2015-01-02 11:25:54 +0100312 LOGP(DMGCP, LOGL_ERROR, "SDP buffer too small: %zu (needed %d)\n",
Jacob Erlbeck997e1e82014-03-14 18:01:20 +0100313 size, len);
314 return -1;
315}
316
317static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
318 const char *msg, const char *trans_id)
319{
320 const char *addr = endp->cfg->local_ip;
321 char sdp_record[4096];
322 int len;
323 int nchars;
Pablo Neira Ayusob769f3c2014-08-27 17:02:52 +0200324 char osmux_extension[strlen("\nX-Osmux: 255") + 1];
Jacob Erlbeck997e1e82014-03-14 18:01:20 +0100325
326 if (!addr)
327 addr = endp->cfg->source_addr;
328
Pablo Neira Ayusob769f3c2014-08-27 17:02:52 +0200329 if (endp->osmux.state == OSMUX_STATE_ACTIVATING)
330 sprintf(osmux_extension, "\nX-Osmux: %u", endp->osmux.cid);
331 else
332 osmux_extension[0] = '\0';
Jacob Erlbeck997e1e82014-03-14 18:01:20 +0100333
Pablo Neira Ayusob769f3c2014-08-27 17:02:52 +0200334 len = snprintf(sdp_record, sizeof(sdp_record),
335 "I: %u%s\n\n", endp->ci, osmux_extension);
Jacob Erlbeck997e1e82014-03-14 18:01:20 +0100336 if (len < 0)
337 return NULL;
338
339 nchars = write_response_sdp(endp, sdp_record + len,
340 sizeof(sdp_record) - len - 1, addr);
341 if (nchars < 0)
342 return NULL;
343
344 len += nchars;
345
346 sdp_record[sizeof(sdp_record) - 1] = '\0';
347
348 return create_resp(endp, 200, " OK", msg, trans_id, NULL, sdp_record);
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100349}
350
Holger Hans Peter Freyther6b0ba012014-07-08 13:01:56 +0200351static void send_dummy(struct mgcp_endpoint *endp)
352{
Pablo Neira Ayusob769f3c2014-08-27 17:02:52 +0200353 if (endp->osmux.state != OSMUX_STATE_DISABLED)
Holger Hans Peter Freyther6b0ba012014-07-08 13:01:56 +0200354 osmux_send_dummy(endp);
355 else
356 mgcp_send_dummy(endp);
357}
358
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100359/*
360 * handle incoming messages:
361 * - this can be a command (four letters, space, transaction id)
362 * - or a response (three numbers, space, transaction id)
363 */
Holger Hans Peter Freyther7bdc6372010-02-20 21:21:02 +0100364struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100365{
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100366 struct mgcp_parse_data pdata;
367 int i, code, handled = 0;
Holger Hans Peter Freyther62e836c2010-02-03 11:03:45 +0100368 struct msgb *resp = NULL;
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100369 char *data;
Jacob Erlbeck9107e2d2013-12-03 17:14:44 +0100370 unsigned char *tail = msg->l2h + msgb_l2len(msg); /* char after l2 data */
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100371
Holger Hans Peter Freytherf36a11a2010-03-31 13:26:46 +0200372 if (msgb_l2len(msg) < 4) {
Holger Hans Peter Freyther0d904e02012-12-07 16:46:19 +0100373 LOGP(DMGCP, LOGL_ERROR, "msg too short: %d\n", msg->len);
Holger Hans Peter Freyther62e836c2010-02-03 11:03:45 +0100374 return NULL;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100375 }
376
Jacob Erlbeck9107e2d2013-12-03 17:14:44 +0100377 /* Ensure that the msg->l2h is NUL terminated. */
378 if (tail[-1] == '\0')
379 /* nothing to do */;
380 else if (msgb_tailroom(msg) > 0)
381 tail[0] = '\0';
382 else if (tail[-1] == '\r' || tail[-1] == '\n')
383 tail[-1] = '\0';
384 else {
385 LOGP(DMGCP, LOGL_ERROR, "Cannot NUL terminate MGCP message: "
386 "Length: %d, Buffer size: %d\n",
387 msgb_l2len(msg), msg->data_len);
388 return NULL;
389 }
390
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100391 /* attempt to treat it as a response */
Holger Hans Peter Freytherf36a11a2010-03-31 13:26:46 +0200392 if (sscanf((const char *)&msg->l2h[0], "%3d %*s", &code) == 1) {
Holger Hans Peter Freyther63f2db22010-02-26 13:30:57 +0100393 LOGP(DMGCP, LOGL_DEBUG, "Response: Code: %d\n", code);
Holger Hans Peter Freyther29de3462012-12-07 16:07:46 +0100394 return NULL;
395 }
396
397 msg->l3h = &msg->l2h[4];
398
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100399
400 /*
401 * Check for a duplicate message and respond.
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100402 */
403 memset(&pdata, 0, sizeof(pdata));
404 pdata.cfg = cfg;
Jacob Erlbeck17711712013-11-29 13:43:46 +0100405 data = strline_r((char *) msg->l3h, &pdata.save);
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100406 pdata.found = mgcp_analyze_header(&pdata, data);
407 if (pdata.endp && pdata.trans
408 && pdata.endp->last_trans
409 && strcmp(pdata.endp->last_trans, pdata.trans) == 0) {
410 return do_retransmission(pdata.endp);
411 }
412
Holger Hans Peter Freyther29de3462012-12-07 16:07:46 +0100413 for (i = 0; i < ARRAY_SIZE(mgcp_requests); ++i) {
414 if (strncmp(mgcp_requests[i].name, (const char *) &msg->l2h[0], 4) == 0) {
415 handled = 1;
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100416 resp = mgcp_requests[i].handle_request(&pdata);
Holger Hans Peter Freyther29de3462012-12-07 16:07:46 +0100417 break;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100418 }
419 }
420
Holger Hans Peter Freyther29de3462012-12-07 16:07:46 +0100421 if (!handled)
422 LOGP(DMGCP, LOGL_NOTICE, "MSG with type: '%.4s' not handled\n", &msg->l2h[0]);
423
Holger Hans Peter Freyther62e836c2010-02-03 11:03:45 +0100424 return resp;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100425}
426
Holger Hans Peter Freyther9f239a22011-01-06 19:32:52 +0100427/**
428 * We have a null terminated string with the endpoint name here. We only
429 * support two kinds. Simple ones as seen on the BSC level and the ones
430 * seen on the trunk side.
431 */
432static struct mgcp_endpoint *find_e1_endpoint(struct mgcp_config *cfg,
433 const char *mgcp)
434{
435 char *rest = NULL;
Holger Hans Peter Freyther74e61112011-02-28 14:13:47 +0100436 struct mgcp_trunk_config *tcfg;
437 int trunk, endp;
Holger Hans Peter Freyther9f239a22011-01-06 19:32:52 +0100438
439 trunk = strtoul(mgcp + 6, &rest, 10);
Holger Hans Peter Freytherf43f2fc2011-01-07 11:30:21 +0100440 if (rest == NULL || rest[0] != '/' || trunk < 1) {
Holger Hans Peter Freyther9f239a22011-01-06 19:32:52 +0100441 LOGP(DMGCP, LOGL_ERROR, "Wrong trunk name '%s'\n", mgcp);
442 return NULL;
443 }
444
445 endp = strtoul(rest + 1, &rest, 10);
446 if (rest == NULL || rest[0] != '@') {
Holger Hans Peter Freytherf43f2fc2011-01-07 11:30:21 +0100447 LOGP(DMGCP, LOGL_ERROR, "Wrong endpoint name '%s'\n", mgcp);
Holger Hans Peter Freyther9f239a22011-01-06 19:32:52 +0100448 return NULL;
449 }
450
451 /* signalling is on timeslot 1 */
452 if (endp == 1)
453 return NULL;
454
Holger Hans Peter Freyther74e61112011-02-28 14:13:47 +0100455 tcfg = mgcp_trunk_num(cfg, trunk);
456 if (!tcfg) {
457 LOGP(DMGCP, LOGL_ERROR, "The trunk %d is not declared.\n", trunk);
458 return NULL;
459 }
460
461 if (!tcfg->endpoints) {
462 LOGP(DMGCP, LOGL_ERROR, "Endpoints of trunk %d not allocated.\n", trunk);
463 return NULL;
464 }
465
466 if (endp < 1 || endp >= tcfg->number_endpoints) {
Holger Hans Peter Freyther9f239a22011-01-06 19:32:52 +0100467 LOGP(DMGCP, LOGL_ERROR, "Failed to find endpoint '%s'\n", mgcp);
Holger Hans Peter Freyther45c21842011-01-07 11:36:54 +0100468 return NULL;
Holger Hans Peter Freyther9f239a22011-01-06 19:32:52 +0100469 }
470
Holger Hans Peter Freyther74e61112011-02-28 14:13:47 +0100471 return &tcfg->endpoints[endp];
Holger Hans Peter Freyther9f239a22011-01-06 19:32:52 +0100472}
473
Holger Hans Peter Freyther7bdc6372010-02-20 21:21:02 +0100474static struct mgcp_endpoint *find_endpoint(struct mgcp_config *cfg, const char *mgcp)
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100475{
476 char *endptr = NULL;
477 unsigned int gw = INT_MAX;
478
Holger Hans Peter Freyther9f239a22011-01-06 19:32:52 +0100479 if (strncmp(mgcp, "ds/e1", 5) == 0) {
480 return find_e1_endpoint(cfg, mgcp);
481 } else {
482 gw = strtoul(mgcp, &endptr, 16);
Holger Hans Peter Freyther88ad7722011-02-28 00:56:17 +0100483 if (gw > 0 && gw < cfg->trunk.number_endpoints && strcmp(endptr, "@mgw") == 0)
484 return &cfg->trunk.endpoints[gw];
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100485 }
486
Holger Hans Peter Freyther9f239a22011-01-06 19:32:52 +0100487 LOGP(DMGCP, LOGL_ERROR, "Not able to find endpoint: '%s'\n", mgcp);
488 return NULL;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100489}
490
Harald Welteae199722012-01-27 12:31:36 +0100491/**
492 * @returns 0 when the status line was complete and transaction_id and
493 * endp out parameters are set.
494 */
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100495static int mgcp_analyze_header(struct mgcp_parse_data *pdata, char *data)
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100496{
Harald Welteae199722012-01-27 12:31:36 +0100497 int i = 0;
Holger Hans Peter Freyther1eba7de2013-07-03 10:23:35 +0200498 char *elem, *save = NULL;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100499
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100500 pdata->trans = "000000";
Holger Hans Peter Freythera820c5f2010-02-26 13:32:55 +0100501
Harald Welteae199722012-01-27 12:31:36 +0100502 for (elem = strtok_r(data, " ", &save); elem;
503 elem = strtok_r(NULL, " ", &save)) {
504 switch (i) {
505 case 0:
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100506 pdata->trans = elem;
Harald Welteae199722012-01-27 12:31:36 +0100507 break;
508 case 1:
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100509 pdata->endp = find_endpoint(pdata->cfg, elem);
510 if (!pdata->endp) {
511 LOGP(DMGCP, LOGL_ERROR,
512 "Unable to find Endpoint `%s'\n", elem);
513 return -1;
Harald Welteae199722012-01-27 12:31:36 +0100514 }
515 break;
516 case 2:
517 if (strcmp("MGCP", elem)) {
518 LOGP(DMGCP, LOGL_ERROR,
519 "MGCP header parsing error\n");
520 return -1;
521 }
522 break;
523 case 3:
524 if (strcmp("1.0", elem)) {
525 LOGP(DMGCP, LOGL_ERROR, "MGCP version `%s' "
526 "not supported\n", elem);
527 return -1;
528 }
529 break;
530 }
531 i++;
532 }
533
534 if (i != 4) {
535 LOGP(DMGCP, LOGL_ERROR, "MGCP status line too short.\n");
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100536 pdata->trans = "000000";
537 pdata->endp = NULL;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100538 return -1;
539 }
540
Holger Hans Peter Freytherf2f15912010-04-01 03:27:04 +0200541 return 0;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100542}
543
544static int verify_call_id(const struct mgcp_endpoint *endp,
545 const char *callid)
546{
547 if (strcmp(endp->callid, callid) != 0) {
Holger Hans Peter Freyther2ada71d2010-02-03 09:13:10 +0100548 LOGP(DMGCP, LOGL_ERROR, "CallIDs does not match on 0x%x. '%s' != '%s'\n",
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100549 ENDPOINT_NUMBER(endp), endp->callid, callid);
550 return -1;
551 }
552
553 return 0;
554}
555
556static int verify_ci(const struct mgcp_endpoint *endp,
Holger Hans Peter Freyther46340132010-08-06 08:26:54 +0800557 const char *_ci)
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100558{
Holger Hans Peter Freyther46340132010-08-06 08:26:54 +0800559 uint32_t ci = strtoul(_ci, NULL, 10);
560
561 if (ci != endp->ci) {
562 LOGP(DMGCP, LOGL_ERROR, "ConnectionIdentifiers do not match on 0x%x. %u != %s\n",
563 ENDPOINT_NUMBER(endp), endp->ci, _ci);
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100564 return -1;
565 }
566
567 return 0;
568}
569
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100570static struct msgb *handle_audit_endpoint(struct mgcp_parse_data *p)
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100571{
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100572 if (p->found != 0)
573 return create_err_response(NULL, 500, "AUEP", p->trans);
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100574 else
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100575 return create_ok_response(p->endp, 200, "AUEP", p->trans);
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100576}
577
Jacob Erlbeck2645c9e2013-12-19 12:23:24 +0100578static int parse_conn_mode(const char *msg, struct mgcp_endpoint *endp)
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100579{
580 int ret = 0;
581 if (strcmp(msg, "recvonly") == 0)
Jacob Erlbeck2645c9e2013-12-19 12:23:24 +0100582 endp->conn_mode = MGCP_CONN_RECV_ONLY;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100583 else if (strcmp(msg, "sendrecv") == 0)
Jacob Erlbeck2645c9e2013-12-19 12:23:24 +0100584 endp->conn_mode = MGCP_CONN_RECV_SEND;
Holger Hans Peter Freythere02860a2010-09-18 20:21:44 +0800585 else if (strcmp(msg, "sendonly") == 0)
Jacob Erlbeck2645c9e2013-12-19 12:23:24 +0100586 endp->conn_mode = MGCP_CONN_SEND_ONLY;
Holger Hans Peter Freyther98a38772010-08-03 02:27:21 +0800587 else if (strcmp(msg, "loopback") == 0)
Jacob Erlbeck2645c9e2013-12-19 12:23:24 +0100588 endp->conn_mode = MGCP_CONN_LOOPBACK;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100589 else {
Holger Hans Peter Freyther2ada71d2010-02-03 09:13:10 +0100590 LOGP(DMGCP, LOGL_ERROR, "Unknown connection mode: '%s'\n", msg);
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100591 ret = -1;
592 }
593
Jacob Erlbecke35fd132014-01-16 16:50:40 +0100594 endp->net_end.output_enabled =
595 endp->conn_mode & MGCP_CONN_SEND_ONLY ? 1 : 0;
596 endp->bts_end.output_enabled =
597 endp->conn_mode & MGCP_CONN_RECV_ONLY ? 1 : 0;
Jacob Erlbeck2645c9e2013-12-19 12:23:24 +0100598
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100599 return ret;
600}
601
Holger Hans Peter Freythercac24382014-09-01 10:35:55 +0200602static int set_audio_info(void *ctx, struct mgcp_rtp_codec *codec,
Jacob Erlbeck845d0052014-03-17 12:30:56 +0100603 int payload_type, const char *audio_name)
604{
Holger Hans Peter Freythercac24382014-09-01 10:35:55 +0200605 int rate = codec->rate;
606 int channels = codec->channels;
Jacob Erlbeck845d0052014-03-17 12:30:56 +0100607 char audio_codec[64];
608
Holger Hans Peter Freythercac24382014-09-01 10:35:55 +0200609 talloc_free(codec->subtype_name);
610 codec->subtype_name = NULL;
611 talloc_free(codec->audio_name);
612 codec->audio_name = NULL;
Jacob Erlbeck845d0052014-03-17 12:30:56 +0100613
Jacob Erlbeck452c1832014-05-09 19:42:17 +0200614 if (payload_type != PTYPE_UNDEFINED)
Holger Hans Peter Freythercac24382014-09-01 10:35:55 +0200615 codec->payload_type = payload_type;
Jacob Erlbeck845d0052014-03-17 12:30:56 +0100616
617 if (!audio_name) {
618 switch (payload_type) {
619 case 3: audio_name = "GSM/8000/1"; break;
620 case 8: audio_name = "PCMA/8000/1"; break;
621 case 18: audio_name = "G729/8000/1"; break;
622 default:
623 /* Payload type is unknown, don't change rate and
624 * channels. */
625 /* TODO: return value? */
626 return 0;
627 }
628 }
629
630 if (sscanf(audio_name, "%63[^/]/%d/%d",
Jacob Erlbeck452c1832014-05-09 19:42:17 +0200631 audio_codec, &rate, &channels) < 1)
Jacob Erlbeck845d0052014-03-17 12:30:56 +0100632 return -EINVAL;
633
Holger Hans Peter Freythercac24382014-09-01 10:35:55 +0200634 codec->rate = rate;
635 codec->channels = channels;
636 codec->subtype_name = talloc_strdup(ctx, audio_codec);
637 codec->audio_name = talloc_strdup(ctx, audio_name);
Jacob Erlbeck42a833e2014-04-14 10:31:47 +0200638
639 if (!strcmp(audio_codec, "G729")) {
Holger Hans Peter Freythercac24382014-09-01 10:35:55 +0200640 codec->frame_duration_num = 10;
641 codec->frame_duration_den = 1000;
Jacob Erlbeck42a833e2014-04-14 10:31:47 +0200642 } else {
Holger Hans Peter Freythercac24382014-09-01 10:35:55 +0200643 codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
644 codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
Jacob Erlbeck42a833e2014-04-14 10:31:47 +0200645 }
646
Jacob Erlbeck452c1832014-05-09 19:42:17 +0200647 if (payload_type < 0) {
648 payload_type = 96;
649 if (rate == 8000 && channels == 1) {
650 if (!strcmp(audio_codec, "GSM"))
651 payload_type = 3;
652 else if (!strcmp(audio_codec, "PCMA"))
653 payload_type = 8;
654 else if (!strcmp(audio_codec, "G729"))
655 payload_type = 18;
656 }
657
Holger Hans Peter Freythercac24382014-09-01 10:35:55 +0200658 codec->payload_type = payload_type;
Jacob Erlbeck452c1832014-05-09 19:42:17 +0200659 }
660
Jacob Erlbeck845d0052014-03-17 12:30:56 +0100661 if (channels != 1)
662 LOGP(DMGCP, LOGL_NOTICE,
663 "Channels != 1 in SDP: '%s'\n", audio_name);
664
665 return 0;
666}
667
Holger Hans Peter Freytherf138f912010-08-05 08:08:17 +0800668static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_rtp_end *end,
Holger Hans Peter Freyther218f8562010-09-18 02:30:02 +0800669 struct mgcp_port_range *range,
670 int (*alloc)(struct mgcp_endpoint *endp, int port))
Holger Hans Peter Freytherf138f912010-08-05 08:08:17 +0800671{
672 int i;
673
674 if (range->mode == PORT_ALLOC_STATIC) {
Holger Hans Peter Freytherf138f912010-08-05 08:08:17 +0800675 end->local_alloc = PORT_ALLOC_STATIC;
676 return 0;
677 }
678
679 /* attempt to find a port */
680 for (i = 0; i < 200; ++i) {
681 int rc;
682
683 if (range->last_port >= range->range_end)
684 range->last_port = range->range_start;
685
Holger Hans Peter Freyther218f8562010-09-18 02:30:02 +0800686 rc = alloc(endp, range->last_port);
Holger Hans Peter Freytherf138f912010-08-05 08:08:17 +0800687
688 range->last_port += 2;
689 if (rc == 0) {
690 end->local_alloc = PORT_ALLOC_DYNAMIC;
691 return 0;
692 }
693
694 }
695
Holger Hans Peter Freyther218f8562010-09-18 02:30:02 +0800696 LOGP(DMGCP, LOGL_ERROR, "Allocating a RTP/RTCP port failed 200 times 0x%x.\n",
697 ENDPOINT_NUMBER(endp));
Holger Hans Peter Freytherf138f912010-08-05 08:08:17 +0800698 return -1;
699}
700
701static int allocate_ports(struct mgcp_endpoint *endp)
702{
Holger Hans Peter Freyther218f8562010-09-18 02:30:02 +0800703 if (allocate_port(endp, &endp->net_end, &endp->cfg->net_ports,
704 mgcp_bind_net_rtp_port) != 0)
Holger Hans Peter Freytherf138f912010-08-05 08:08:17 +0800705 return -1;
706
Holger Hans Peter Freyther218f8562010-09-18 02:30:02 +0800707 if (allocate_port(endp, &endp->bts_end, &endp->cfg->bts_ports,
708 mgcp_bind_bts_rtp_port) != 0) {
Holger Hans Peter Freytherf138f912010-08-05 08:08:17 +0800709 mgcp_rtp_end_reset(&endp->net_end);
710 return -1;
711 }
712
Holger Hans Peter Freyther69906872011-02-28 14:51:48 +0100713 if (endp->cfg->transcoder_ip && endp->tcfg->trunk_type == MGCP_TRUNK_VIRTUAL) {
Holger Hans Peter Freyther21262332010-11-01 20:53:31 +0100714 if (allocate_port(endp, &endp->trans_net,
Holger Hans Peter Freytherb54048f2010-11-01 19:57:50 +0100715 &endp->cfg->transcoder_ports,
Holger Hans Peter Freytherbd7b3c52010-11-01 21:04:54 +0100716 mgcp_bind_trans_net_rtp_port) != 0) {
Holger Hans Peter Freytherb54048f2010-11-01 19:57:50 +0100717 mgcp_rtp_end_reset(&endp->net_end);
718 mgcp_rtp_end_reset(&endp->bts_end);
719 return -1;
720 }
721
Holger Hans Peter Freytherbd7b3c52010-11-01 21:04:54 +0100722 if (allocate_port(endp, &endp->trans_bts,
723 &endp->cfg->transcoder_ports,
724 mgcp_bind_trans_bts_rtp_port) != 0) {
725 mgcp_rtp_end_reset(&endp->net_end);
726 mgcp_rtp_end_reset(&endp->bts_end);
727 mgcp_rtp_end_reset(&endp->trans_net);
728 return -1;
729 }
730
Holger Hans Peter Freytherb54048f2010-11-01 19:57:50 +0100731 /* remember that we have set up transcoding */
Pablo Neira Ayuso46bd4242013-07-08 05:09:46 +0200732 endp->type = MGCP_RTP_TRANSCODED;
Holger Hans Peter Freyther218f8562010-09-18 02:30:02 +0800733 }
734
Holger Hans Peter Freytherf138f912010-08-05 08:08:17 +0800735 return 0;
736}
737
Jacob Erlbeck17711712013-11-29 13:43:46 +0100738static int parse_sdp_data(struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p)
739{
740 char *line;
741 int found_media = 0;
Holger Hans Peter Freythere46bc272014-09-01 10:50:22 +0200742 /* TODO/XXX make it more generic */
Jacob Erlbeck24754f02013-12-10 13:09:37 +0100743 int audio_payload = -1;
Holger Hans Peter Freythere46bc272014-09-01 10:50:22 +0200744 int audio_payload_alt = -1;
Jacob Erlbeck17711712013-11-29 13:43:46 +0100745
746 for_each_line(line, p->save) {
747 switch (line[0]) {
Jacob Erlbeck17711712013-11-29 13:43:46 +0100748 case 'o':
749 case 's':
750 case 't':
751 case 'v':
752 /* skip these SDP attributes */
753 break;
Jacob Erlbeck24754f02013-12-10 13:09:37 +0100754 case 'a': {
755 int payload;
Jacob Erlbeck24754f02013-12-10 13:09:37 +0100756 int ptime, ptime2 = 0;
757 char audio_name[64];
Jacob Erlbeck24754f02013-12-10 13:09:37 +0100758
759 if (audio_payload == -1)
760 break;
761
Holger Hans Peter Freyther4f0381b2014-05-28 16:44:34 +0200762 if (sscanf(line, "a=rtpmap:%d %63s",
Jacob Erlbeck24754f02013-12-10 13:09:37 +0100763 &payload, audio_name) == 2) {
Holger Hans Peter Freythere46bc272014-09-01 10:50:22 +0200764 if (payload == audio_payload)
765 set_audio_info(p->cfg, &rtp->codec,
766 payload, audio_name);
767 else if (payload == audio_payload_alt)
768 set_audio_info(p->cfg, &rtp->alt_codec,
769 payload, audio_name);
Jacob Erlbeck24754f02013-12-10 13:09:37 +0100770 } else if (sscanf(line, "a=ptime:%d-%d",
771 &ptime, &ptime2) >= 1) {
772 if (ptime2 > 0 && ptime2 != ptime)
773 rtp->packet_duration_ms = 0;
774 else
775 rtp->packet_duration_ms = ptime;
776 } else if (sscanf(line, "a=maxptime:%d", &ptime2) == 1) {
Holger Hans Peter Freythere46bc272014-09-01 10:50:22 +0200777 /* TODO/XXX: Store this per codec and derive it on use */
Holger Hans Peter Freythercac24382014-09-01 10:35:55 +0200778 if (ptime2 * rtp->codec.frame_duration_den >
779 rtp->codec.frame_duration_num * 1500)
Jacob Erlbeck24754f02013-12-10 13:09:37 +0100780 /* more than 1 frame */
781 rtp->packet_duration_ms = 0;
782 }
783 break;
784 }
Jacob Erlbeck17711712013-11-29 13:43:46 +0100785 case 'm': {
Holger Hans Peter Freythere46bc272014-09-01 10:50:22 +0200786 int port, rc;
Jacob Erlbeck24754f02013-12-10 13:09:37 +0100787 audio_payload = -1;
Holger Hans Peter Freythere46bc272014-09-01 10:50:22 +0200788 audio_payload_alt = -1;
Jacob Erlbeck17711712013-11-29 13:43:46 +0100789
Holger Hans Peter Freythere46bc272014-09-01 10:50:22 +0200790 rc = sscanf(line, "m=audio %d RTP/AVP %d %d",
791 &port, &audio_payload, &audio_payload_alt);
792 if (rc >= 2) {
Jacob Erlbeck17711712013-11-29 13:43:46 +0100793 rtp->rtp_port = htons(port);
794 rtp->rtcp_port = htons(port + 1);
Jacob Erlbeck17711712013-11-29 13:43:46 +0100795 found_media = 1;
Holger Hans Peter Freythercac24382014-09-01 10:35:55 +0200796 set_audio_info(p->cfg, &rtp->codec, audio_payload, NULL);
Holger Hans Peter Freythere46bc272014-09-01 10:50:22 +0200797 if (rc == 3)
798 set_audio_info(p->cfg, &rtp->alt_codec,
799 audio_payload_alt, NULL);
Jacob Erlbeck17711712013-11-29 13:43:46 +0100800 }
801 break;
802 }
803 case 'c': {
804 char ipv4[16];
805
806 if (sscanf(line, "c=IN IP4 %15s", ipv4) == 1) {
807 inet_aton(ipv4, &rtp->addr);
808 }
809 break;
810 }
811 default:
812 if (p->endp)
813 LOGP(DMGCP, LOGL_NOTICE,
814 "Unhandled SDP option: '%c'/%d on 0x%x\n",
815 line[0], line[0], ENDPOINT_NUMBER(p->endp));
816 else
817 LOGP(DMGCP, LOGL_NOTICE,
818 "Unhandled SDP option: '%c'/%d\n",
819 line[0], line[0]);
820 break;
821 }
822 }
823
824 if (found_media)
825 LOGP(DMGCP, LOGL_NOTICE,
Jacob Erlbeck997e1e82014-03-14 18:01:20 +0100826 "Got media info via SDP: port %d, payload %d (%s), "
Jacob Erlbeck24754f02013-12-10 13:09:37 +0100827 "duration %d, addr %s\n",
Holger Hans Peter Freythercac24382014-09-01 10:35:55 +0200828 ntohs(rtp->rtp_port), rtp->codec.payload_type,
829 rtp->codec.subtype_name ? rtp->codec.subtype_name : "unknown",
Jacob Erlbeck24754f02013-12-10 13:09:37 +0100830 rtp->packet_duration_ms, inet_ntoa(rtp->addr));
Jacob Erlbeck17711712013-11-29 13:43:46 +0100831
832 return found_media;
833}
834
Jacob Erlbeck2c2ca4d2013-12-09 14:32:03 +0100835/* Set the LCO from a string (see RFC 3435).
836 * The string is stored in the 'string' field. A NULL string is handled excatly
837 * like an empty string, the 'string' field is never NULL after this function
838 * has been called. */
839static void set_local_cx_options(void *ctx, struct mgcp_lco *lco,
840 const char *options)
841{
Jacob Erlbeck452c1832014-05-09 19:42:17 +0200842 char *p_opt, *a_opt;
843 char codec[9];
Jacob Erlbeck2c2ca4d2013-12-09 14:32:03 +0100844
845 talloc_free(lco->string);
Jacob Erlbeck452c1832014-05-09 19:42:17 +0200846 talloc_free(lco->codec);
847 lco->codec = NULL;
Jacob Erlbeck2c2ca4d2013-12-09 14:32:03 +0100848 lco->pkt_period_min = lco->pkt_period_max = 0;
849 lco->string = talloc_strdup(ctx, options ? options : "");
850
851 p_opt = strstr(lco->string, "p:");
852 if (p_opt && sscanf(p_opt, "p:%d-%d",
853 &lco->pkt_period_min, &lco->pkt_period_max) == 1)
854 lco->pkt_period_max = lco->pkt_period_min;
Jacob Erlbeck452c1832014-05-09 19:42:17 +0200855
856 a_opt = strstr(lco->string, "a:");
857 if (a_opt && sscanf(a_opt, "a:%8[^,]", codec) == 1)
858 lco->codec = talloc_strdup(ctx, codec);
Jacob Erlbeck2c2ca4d2013-12-09 14:32:03 +0100859}
860
Jacob Erlbeckdb2d4312013-12-03 14:43:34 +0100861void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
862 struct mgcp_rtp_end *rtp)
863{
864 struct mgcp_trunk_config *tcfg = endp->tcfg;
865
Jacob Erlbecke2292f32013-12-03 15:13:12 +0100866 int patch_ssrc = expect_ssrc_change && tcfg->force_constant_ssrc;
867
Jacob Erlbeck4bbddc62013-12-18 12:54:51 +0100868 rtp->force_aligned_timing = tcfg->force_aligned_timing;
Jacob Erlbecke2292f32013-12-03 15:13:12 +0100869 rtp->force_constant_ssrc = patch_ssrc ? 1 : 0;
Jacob Erlbeckdb2d4312013-12-03 14:43:34 +0100870
871 LOGP(DMGCP, LOGL_DEBUG,
872 "Configuring RTP endpoint: local port %d%s%s\n",
873 ntohs(rtp->rtp_port),
Jacob Erlbeck4bbddc62013-12-18 12:54:51 +0100874 rtp->force_aligned_timing ? ", force constant timing" : "",
Jacob Erlbeckdb2d4312013-12-03 14:43:34 +0100875 rtp->force_constant_ssrc ? ", force constant ssrc" : "");
876}
877
Jacob Erlbeckf6ec0e92013-12-04 10:30:11 +0100878uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp,
879 struct mgcp_rtp_end *rtp)
880{
881 int f = 0;
882
883 /* Get the number of frames per channel and packet */
884 if (rtp->frames_per_packet)
885 f = rtp->frames_per_packet;
Holger Hans Peter Freythercac24382014-09-01 10:35:55 +0200886 else if (rtp->packet_duration_ms && rtp->codec.frame_duration_num) {
887 int den = 1000 * rtp->codec.frame_duration_num;
888 f = (rtp->packet_duration_ms * rtp->codec.frame_duration_den + den/2)
Jacob Erlbeckf6ec0e92013-12-04 10:30:11 +0100889 / den;
890 }
891
Holger Hans Peter Freythercac24382014-09-01 10:35:55 +0200892 return rtp->codec.rate * f * rtp->codec.frame_duration_num / rtp->codec.frame_duration_den;
Jacob Erlbeckf6ec0e92013-12-04 10:30:11 +0100893}
894
Pablo Neira Ayusob769f3c2014-08-27 17:02:52 +0200895static int mgcp_parse_osmux_cid(const char *line)
896{
Pablo Neira Ayuso3ba3cf82014-08-29 08:46:05 +0200897 int osmux_cid;
Pablo Neira Ayusob769f3c2014-08-27 17:02:52 +0200898
Pablo Neira Ayuso3ba3cf82014-08-29 08:46:05 +0200899 if (sscanf(line + 2, "Osmux: %u", &osmux_cid) != 1)
900 return -1;
901
Pablo Neira Ayusob769f3c2014-08-27 17:02:52 +0200902 if (osmux_cid > OSMUX_CID_MAX) {
903 LOGP(DMGCP, LOGL_ERROR, "Osmux ID too large: %u > %u\n",
904 osmux_cid, OSMUX_CID_MAX);
905 return -1;
906 }
907 LOGP(DMGCP, LOGL_DEBUG, "bsc-nat offered Osmux CID %u\n", osmux_cid);
908
909 return osmux_cid;
910}
911
912static int mgcp_osmux_setup(struct mgcp_endpoint *endp, const char *line)
Pablo Neira Ayuso8be171e2014-08-27 14:58:43 +0200913{
914 if (!endp->cfg->osmux_init) {
915 if (osmux_init(OSMUX_ROLE_BSC, endp->cfg) < 0) {
916 LOGP(DMGCP, LOGL_ERROR, "Cannot init OSMUX\n");
917 return -1;
918 }
919 LOGP(DMGCP, LOGL_NOTICE, "OSMUX socket has been set up\n");
920 }
921
Pablo Neira Ayusob769f3c2014-08-27 17:02:52 +0200922 return mgcp_parse_osmux_cid(line);
Pablo Neira Ayuso8be171e2014-08-27 14:58:43 +0200923}
924
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100925static struct msgb *handle_create_con(struct mgcp_parse_data *p)
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100926{
Holger Hans Peter Freyther88ad7722011-02-28 00:56:17 +0100927 struct mgcp_trunk_config *tcfg;
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100928 struct mgcp_endpoint *endp = p->endp;
Holger Hans Peter Freyther9e9392d2010-08-08 16:24:48 +0800929 int error_code = 400;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100930
Holger Hans Peter Freyther3488c3d2011-07-20 20:44:54 +0200931 const char *local_options = NULL;
932 const char *callid = NULL;
933 const char *mode = NULL;
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100934 char *line;
Pablo Neira Ayusob769f3c2014-08-27 17:02:52 +0200935 int have_sdp = 0, osmux_cid = -1;
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100936
937 if (p->found != 0)
938 return create_err_response(NULL, 510, "CRCX", p->trans);
Holger Hans Peter Freyther88ad7722011-02-28 00:56:17 +0100939
Holger Hans Peter Freyther3488c3d2011-07-20 20:44:54 +0200940 /* parse CallID C: and LocalParameters L: */
Jacob Erlbeck17711712013-11-29 13:43:46 +0100941 for_each_line(line, p->save) {
Holger Hans Peter Freyther1cc5ff82014-05-22 15:03:10 +0200942 if (!mgcp_check_param(endp, line))
943 continue;
944
Harald Welteae199722012-01-27 12:31:36 +0100945 switch (line[0]) {
946 case 'L':
947 local_options = (const char *) line + 3;
948 break;
949 case 'C':
950 callid = (const char *) line + 3;
951 break;
952 case 'M':
953 mode = (const char *) line + 3;
954 break;
Pablo Neira Ayusocab6e752014-02-05 18:56:17 +0100955 case 'X':
Pablo Neira Ayusob769f3c2014-08-27 17:02:52 +0200956 /* Osmux is not enabled in this bsc, ignore it so the
957 * bsc-nat knows that we don't want to use Osmux.
958 */
959 if (!p->endp->cfg->osmux)
960 break;
961
962 if (strncmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0)
963 osmux_cid = mgcp_osmux_setup(endp, line);
Pablo Neira Ayusocab6e752014-02-05 18:56:17 +0100964 break;
Jacob Erlbeck78a95012013-11-29 13:43:49 +0100965 case '\0':
966 have_sdp = 1;
967 goto mgcp_header_done;
Harald Welteae199722012-01-27 12:31:36 +0100968 default:
969 LOGP(DMGCP, LOGL_NOTICE, "Unhandled option: '%c'/%d on 0x%x\n",
970 *line, *line, ENDPOINT_NUMBER(endp));
971 break;
972 }
Holger Hans Peter Freyther3488c3d2011-07-20 20:44:54 +0200973 }
Harald Welteae199722012-01-27 12:31:36 +0100974
Jacob Erlbeck78a95012013-11-29 13:43:49 +0100975mgcp_header_done:
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100976 tcfg = p->endp->tcfg;
Holger Hans Peter Freyther3488c3d2011-07-20 20:44:54 +0200977
978 /* Check required data */
979 if (!callid || !mode) {
980 LOGP(DMGCP, LOGL_ERROR, "Missing callid and mode in CRCX on 0x%x\n",
981 ENDPOINT_NUMBER(endp));
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100982 return create_err_response(endp, 400, "CRCX", p->trans);
Holger Hans Peter Freyther3488c3d2011-07-20 20:44:54 +0200983 }
984
Holger Hans Peter Freyther39a97e22010-08-06 18:03:11 +0800985 if (endp->allocated) {
Holger Hans Peter Freyther88ad7722011-02-28 00:56:17 +0100986 if (tcfg->force_realloc) {
Holger Hans Peter Freyther408cc4a2010-04-07 10:51:27 +0200987 LOGP(DMGCP, LOGL_NOTICE, "Endpoint 0x%x already allocated. Forcing realloc.\n",
988 ENDPOINT_NUMBER(endp));
Holger Hans Peter Freythercb6ad702014-07-22 15:00:52 +0200989 mgcp_release_endp(endp);
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100990 if (p->cfg->realloc_cb)
991 p->cfg->realloc_cb(tcfg, ENDPOINT_NUMBER(endp));
Holger Hans Peter Freyther408cc4a2010-04-07 10:51:27 +0200992 } else {
993 LOGP(DMGCP, LOGL_ERROR, "Endpoint is already used. 0x%x\n",
994 ENDPOINT_NUMBER(endp));
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100995 return create_err_response(endp, 400, "CRCX", p->trans);
Holger Hans Peter Freyther408cc4a2010-04-07 10:51:27 +0200996 }
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100997 }
998
Holger Hans Peter Freyther3488c3d2011-07-20 20:44:54 +0200999 /* copy some parameters */
1000 endp->callid = talloc_strdup(tcfg->endpoints, callid);
1001
Jacob Erlbeck2c2ca4d2013-12-09 14:32:03 +01001002 set_local_cx_options(endp->tcfg->endpoints, &endp->local_options,
1003 local_options);
Holger Hans Peter Freyther3488c3d2011-07-20 20:44:54 +02001004
Jacob Erlbeck2645c9e2013-12-19 12:23:24 +01001005 if (parse_conn_mode(mode, endp) != 0) {
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001006 error_code = 517;
1007 goto error2;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001008 }
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001009
1010 /* initialize */
Holger Hans Peter Freythera17d7012010-08-05 01:34:51 +08001011 endp->net_end.rtp_port = endp->net_end.rtcp_port = endp->bts_end.rtp_port = endp->bts_end.rtcp_port = 0;
Jacob Erlbeckdb2d4312013-12-03 14:43:34 +01001012 mgcp_rtp_end_config(endp, 0, &endp->net_end);
1013 mgcp_rtp_end_config(endp, 0, &endp->bts_end);
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001014
1015 /* set to zero until we get the info */
Holger Hans Peter Freythera17d7012010-08-05 01:34:51 +08001016 memset(&endp->net_end.addr, 0, sizeof(endp->net_end.addr));
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001017
1018 /* bind to the port now */
Holger Hans Peter Freytherf138f912010-08-05 08:08:17 +08001019 if (allocate_ports(endp) != 0)
1020 goto error2;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001021
1022 /* assign a local call identifier or fail */
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001023 endp->ci = generate_call_id(p->cfg);
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001024 if (endp->ci == CI_UNUSED)
1025 goto error2;
1026
Pablo Neira Ayusob769f3c2014-08-27 17:02:52 +02001027 /* Annotate Osmux circuit ID and set it to activating state until this
1028 * is fully set up from the dummy load.
1029 */
1030 endp->osmux.state = OSMUX_STATE_DISABLED;
1031 if (osmux_cid >= 0) {
1032 endp->osmux.cid = osmux_cid;
1033 endp->osmux.state = OSMUX_STATE_ACTIVATING;
1034 }
1035
Holger Hans Peter Freyther39a97e22010-08-06 18:03:11 +08001036 endp->allocated = 1;
Jacob Erlbeck78a95012013-11-29 13:43:49 +01001037
1038 /* set up RTP media parameters */
Holger Hans Peter Freythercac24382014-09-01 10:35:55 +02001039 set_audio_info(p->cfg, &endp->bts_end.codec, tcfg->audio_payload, tcfg->audio_name);
Holger Hans Peter Freyther5ea1bc72012-09-03 00:07:39 +02001040 endp->bts_end.fmtp_extra = talloc_strdup(tcfg->endpoints,
1041 tcfg->audio_fmtp_extra);
Jacob Erlbeck42a833e2014-04-14 10:31:47 +02001042 if (have_sdp)
Jacob Erlbeck78a95012013-11-29 13:43:49 +01001043 parse_sdp_data(&endp->net_end, p);
Jacob Erlbeck452c1832014-05-09 19:42:17 +02001044 else if (endp->local_options.codec)
Holger Hans Peter Freythercac24382014-09-01 10:35:55 +02001045 set_audio_info(p->cfg, &endp->net_end.codec,
Jacob Erlbeck452c1832014-05-09 19:42:17 +02001046 PTYPE_UNDEFINED, endp->local_options.codec);
Jacob Erlbeck42a833e2014-04-14 10:31:47 +02001047
1048 if (p->cfg->bts_force_ptime) {
1049 endp->bts_end.packet_duration_ms = p->cfg->bts_force_ptime;
1050 endp->bts_end.force_output_ptime = 1;
Jacob Erlbecka0d64ce2014-03-13 14:24:52 +01001051 }
Holger Hans Peter Freytheref6bb252010-02-26 13:41:22 +01001052
Jacob Erlbeck42a833e2014-04-14 10:31:47 +02001053 setup_rtp_processing(endp);
1054
Holger Hans Peter Freytherfe86d3c2010-02-26 13:37:05 +01001055 /* policy CB */
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001056 if (p->cfg->policy_cb) {
1057 int rc;
1058 rc = p->cfg->policy_cb(tcfg, ENDPOINT_NUMBER(endp),
1059 MGCP_ENDP_CRCX, p->trans);
1060 switch (rc) {
Holger Hans Peter Freytherfe86d3c2010-02-26 13:37:05 +01001061 case MGCP_POLICY_REJECT:
1062 LOGP(DMGCP, LOGL_NOTICE, "CRCX rejected by policy on 0x%x\n",
1063 ENDPOINT_NUMBER(endp));
Holger Hans Peter Freythercb6ad702014-07-22 15:00:52 +02001064 mgcp_release_endp(endp);
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001065 return create_err_response(endp, 400, "CRCX", p->trans);
Holger Hans Peter Freytherfe86d3c2010-02-26 13:37:05 +01001066 break;
1067 case MGCP_POLICY_DEFER:
1068 /* stop processing */
Holger Hans Peter Freyther3b5e3c42010-09-18 06:35:29 +08001069 create_transcoder(endp);
Holger Hans Peter Freytherfe86d3c2010-02-26 13:37:05 +01001070 return NULL;
1071 break;
1072 case MGCP_POLICY_CONT:
1073 /* just continue */
1074 break;
1075 }
1076 }
1077
Holger Hans Peter Freythere378cb12010-08-06 20:22:23 +08001078 LOGP(DMGCP, LOGL_DEBUG, "Creating endpoint on: 0x%x CI: %u port: %u/%u\n",
Holger Hans Peter Freyther58ff2192010-08-05 03:08:35 +08001079 ENDPOINT_NUMBER(endp), endp->ci,
1080 endp->net_end.local_port, endp->bts_end.local_port);
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001081 if (p->cfg->change_cb)
1082 p->cfg->change_cb(tcfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_CRCX);
Holger Hans Peter Freyther77f7afe2010-02-03 09:54:43 +01001083
Pablo Neira Ayusocab6e752014-02-05 18:56:17 +01001084 if (endp->conn_mode & MGCP_CONN_RECV_ONLY && tcfg->keepalive_interval != 0) {
Holger Hans Peter Freyther6b0ba012014-07-08 13:01:56 +02001085 send_dummy(endp);
Pablo Neira Ayusocab6e752014-02-05 18:56:17 +01001086 }
Jacob Erlbeck075a9eb2013-12-19 18:53:07 +01001087
Holger Hans Peter Freyther3b5e3c42010-09-18 06:35:29 +08001088 create_transcoder(endp);
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001089 return create_response_with_sdp(endp, "CRCX", p->trans);
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001090error2:
Holger Hans Peter Freythercb6ad702014-07-22 15:00:52 +02001091 mgcp_release_endp(endp);
Holger Hans Peter Freyther2ada71d2010-02-03 09:13:10 +01001092 LOGP(DMGCP, LOGL_NOTICE, "Resource error on 0x%x\n", ENDPOINT_NUMBER(endp));
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001093 return create_err_response(endp, error_code, "CRCX", p->trans);
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001094}
1095
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001096static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001097{
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001098 struct mgcp_endpoint *endp = p->endp;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001099 int error_code = 500;
Pablo Neira Ayusob769f3c2014-08-27 17:02:52 +02001100 int silent = 0;
Jacob Erlbeck452c1832014-05-09 19:42:17 +02001101 int have_sdp = 0;
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001102 char *line;
Jacob Erlbeck2c2ca4d2013-12-09 14:32:03 +01001103 const char *local_options = NULL;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001104
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001105 if (p->found != 0)
1106 return create_err_response(NULL, 510, "MDCX", p->trans);
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001107
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001108 if (endp->ci == CI_UNUSED) {
1109 LOGP(DMGCP, LOGL_ERROR, "Endpoint is not "
1110 "holding a connection. 0x%x\n", ENDPOINT_NUMBER(endp));
1111 return create_err_response(endp, 400, "MDCX", p->trans);
1112 }
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001113
Jacob Erlbeck17711712013-11-29 13:43:46 +01001114 for_each_line(line, p->save) {
Holger Hans Peter Freyther1cc5ff82014-05-22 15:03:10 +02001115 if (!mgcp_check_param(endp, line))
1116 continue;
1117
Harald Welteae199722012-01-27 12:31:36 +01001118 switch (line[0]) {
1119 case 'C': {
1120 if (verify_call_id(endp, line + 3) != 0)
1121 goto error3;
1122 break;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001123 }
Harald Welteae199722012-01-27 12:31:36 +01001124 case 'I': {
1125 if (verify_ci(endp, line + 3) != 0)
1126 goto error3;
1127 break;
1128 }
1129 case 'L':
Jacob Erlbeck2c2ca4d2013-12-09 14:32:03 +01001130 local_options = (const char *) line + 3;
Harald Welteae199722012-01-27 12:31:36 +01001131 break;
1132 case 'M':
Jacob Erlbeck2645c9e2013-12-19 12:23:24 +01001133 if (parse_conn_mode(line + 3, endp) != 0) {
Harald Welteae199722012-01-27 12:31:36 +01001134 error_code = 517;
1135 goto error3;
1136 }
1137 endp->orig_mode = endp->conn_mode;
1138 break;
1139 case 'Z':
1140 silent = strcmp("noanswer", line + 3) == 0;
1141 break;
1142 case '\0':
1143 /* SDP file begins */
Jacob Erlbeck452c1832014-05-09 19:42:17 +02001144 have_sdp = 1;
Jacob Erlbeck17711712013-11-29 13:43:46 +01001145 parse_sdp_data(&endp->net_end, p);
1146 /* This will exhaust p->save, so the loop will
1147 * terminate next time.
1148 */
Harald Welteae199722012-01-27 12:31:36 +01001149 break;
Harald Welteae199722012-01-27 12:31:36 +01001150 default:
Jacob Erlbeck17711712013-11-29 13:43:46 +01001151 LOGP(DMGCP, LOGL_NOTICE, "Unhandled MGCP option: '%c'/%d on 0x%x\n",
Harald Welteae199722012-01-27 12:31:36 +01001152 line[0], line[0], ENDPOINT_NUMBER(endp));
1153 break;
1154 }
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001155 }
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001156
Jacob Erlbeck2c2ca4d2013-12-09 14:32:03 +01001157 set_local_cx_options(endp->tcfg->endpoints, &endp->local_options,
1158 local_options);
1159
Jacob Erlbeck452c1832014-05-09 19:42:17 +02001160 if (!have_sdp && endp->local_options.codec)
Holger Hans Peter Freythercac24382014-09-01 10:35:55 +02001161 set_audio_info(p->cfg, &endp->net_end.codec,
Jacob Erlbeck452c1832014-05-09 19:42:17 +02001162 PTYPE_UNDEFINED, endp->local_options.codec);
1163
Jacob Erlbecka0d64ce2014-03-13 14:24:52 +01001164 setup_rtp_processing(endp);
1165
Holger Hans Peter Freytherfe86d3c2010-02-26 13:37:05 +01001166 /* policy CB */
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001167 if (p->cfg->policy_cb) {
1168 int rc;
1169 rc = p->cfg->policy_cb(endp->tcfg, ENDPOINT_NUMBER(endp),
1170 MGCP_ENDP_MDCX, p->trans);
1171 switch (rc) {
Holger Hans Peter Freytherfe86d3c2010-02-26 13:37:05 +01001172 case MGCP_POLICY_REJECT:
1173 LOGP(DMGCP, LOGL_NOTICE, "MDCX rejected by policy on 0x%x\n",
1174 ENDPOINT_NUMBER(endp));
Holger Hans Peter Freythere3d16bb2010-04-22 11:58:13 +08001175 if (silent)
1176 goto out_silent;
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001177 return create_err_response(endp, 400, "MDCX", p->trans);
Holger Hans Peter Freytherfe86d3c2010-02-26 13:37:05 +01001178 break;
1179 case MGCP_POLICY_DEFER:
1180 /* stop processing */
1181 return NULL;
1182 break;
1183 case MGCP_POLICY_CONT:
1184 /* just continue */
1185 break;
1186 }
1187 }
1188
Jacob Erlbeckdb2d4312013-12-03 14:43:34 +01001189 mgcp_rtp_end_config(endp, 1, &endp->net_end);
1190 mgcp_rtp_end_config(endp, 1, &endp->bts_end);
1191
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001192 /* modify */
Holger Hans Peter Freythere378cb12010-08-06 20:22:23 +08001193 LOGP(DMGCP, LOGL_DEBUG, "Modified endpoint on: 0x%x Server: %s:%u\n",
Holger Hans Peter Freythera17d7012010-08-05 01:34:51 +08001194 ENDPOINT_NUMBER(endp), inet_ntoa(endp->net_end.addr), ntohs(endp->net_end.rtp_port));
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001195 if (p->cfg->change_cb)
1196 p->cfg->change_cb(endp->tcfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_MDCX);
Jacob Erlbeck075a9eb2013-12-19 18:53:07 +01001197
Jacob Erlbecke35fd132014-01-16 16:50:40 +01001198 if (endp->conn_mode & MGCP_CONN_RECV_ONLY &&
1199 endp->tcfg->keepalive_interval != 0)
Holger Hans Peter Freyther6b0ba012014-07-08 13:01:56 +02001200 send_dummy(endp);
Jacob Erlbeck075a9eb2013-12-19 18:53:07 +01001201
Holger Hans Peter Freythere3d16bb2010-04-22 11:58:13 +08001202 if (silent)
1203 goto out_silent;
1204
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001205 return create_response_with_sdp(endp, "MDCX", p->trans);
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001206
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001207error3:
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001208 return create_err_response(endp, error_code, "MDCX", p->trans);
Holger Hans Peter Freythere3d16bb2010-04-22 11:58:13 +08001209
1210
1211out_silent:
1212 return NULL;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001213}
1214
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001215static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001216{
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001217 struct mgcp_endpoint *endp = p->endp;
Holger Hans Peter Freyther9e9392d2010-08-08 16:24:48 +08001218 int error_code = 400;
Holger Hans Peter Freythere3d16bb2010-04-22 11:58:13 +08001219 int silent = 0;
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001220 char *line;
Holger Hans Peter Freyther0bf15a82012-09-14 17:18:12 +02001221 char stats[1048];
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001222
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001223 if (p->found != 0)
1224 return create_err_response(NULL, error_code, "DLCX", p->trans);
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001225
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001226 if (!p->endp->allocated) {
1227 LOGP(DMGCP, LOGL_ERROR, "Endpoint is not used. 0x%x\n",
1228 ENDPOINT_NUMBER(endp));
1229 return create_err_response(endp, 400, "DLCX", p->trans);
1230 }
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001231
Jacob Erlbeck17711712013-11-29 13:43:46 +01001232 for_each_line(line, p->save) {
Holger Hans Peter Freyther1cc5ff82014-05-22 15:03:10 +02001233 if (!mgcp_check_param(endp, line))
1234 continue;
1235
Harald Welteae199722012-01-27 12:31:36 +01001236 switch (line[0]) {
1237 case 'C':
1238 if (verify_call_id(endp, line + 3) != 0)
1239 goto error3;
1240 break;
1241 case 'I':
1242 if (verify_ci(endp, line + 3) != 0)
1243 goto error3;
1244 break;
1245 case 'Z':
1246 silent = strcmp("noanswer", line + 3) == 0;
1247 break;
1248 default:
1249 LOGP(DMGCP, LOGL_NOTICE, "Unhandled option: '%c'/%d on 0x%x\n",
1250 line[0], line[0], ENDPOINT_NUMBER(endp));
1251 break;
1252 }
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001253 }
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001254
Holger Hans Peter Freytherfe86d3c2010-02-26 13:37:05 +01001255 /* policy CB */
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001256 if (p->cfg->policy_cb) {
1257 int rc;
1258 rc = p->cfg->policy_cb(endp->tcfg, ENDPOINT_NUMBER(endp),
1259 MGCP_ENDP_DLCX, p->trans);
1260 switch (rc) {
Holger Hans Peter Freytherfe86d3c2010-02-26 13:37:05 +01001261 case MGCP_POLICY_REJECT:
1262 LOGP(DMGCP, LOGL_NOTICE, "DLCX rejected by policy on 0x%x\n",
1263 ENDPOINT_NUMBER(endp));
Holger Hans Peter Freythere3d16bb2010-04-22 11:58:13 +08001264 if (silent)
1265 goto out_silent;
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001266 return create_err_response(endp, 400, "DLCX", p->trans);
Holger Hans Peter Freytherfe86d3c2010-02-26 13:37:05 +01001267 break;
1268 case MGCP_POLICY_DEFER:
1269 /* stop processing */
Holger Hans Peter Freyther3b5e3c42010-09-18 06:35:29 +08001270 delete_transcoder(endp);
Holger Hans Peter Freytherfe86d3c2010-02-26 13:37:05 +01001271 return NULL;
1272 break;
1273 case MGCP_POLICY_CONT:
1274 /* just continue */
1275 break;
1276 }
1277 }
1278
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001279 /* free the connection */
Holger Hans Peter Freythere378cb12010-08-06 20:22:23 +08001280 LOGP(DMGCP, LOGL_DEBUG, "Deleted endpoint on: 0x%x Server: %s:%u\n",
Holger Hans Peter Freythera17d7012010-08-05 01:34:51 +08001281 ENDPOINT_NUMBER(endp), inet_ntoa(endp->net_end.addr), ntohs(endp->net_end.rtp_port));
Holger Hans Peter Freyther3b5e3c42010-09-18 06:35:29 +08001282
Holger Hans Peter Freyther0bf15a82012-09-14 17:18:12 +02001283 /* save the statistics of the current call */
1284 mgcp_format_stats(endp, stats, sizeof(stats));
1285
Holger Hans Peter Freyther3b5e3c42010-09-18 06:35:29 +08001286 delete_transcoder(endp);
Holger Hans Peter Freythercb6ad702014-07-22 15:00:52 +02001287 mgcp_release_endp(endp);
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001288 if (p->cfg->change_cb)
1289 p->cfg->change_cb(endp->tcfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_DLCX);
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001290
Holger Hans Peter Freythere3d16bb2010-04-22 11:58:13 +08001291 if (silent)
1292 goto out_silent;
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001293 return create_ok_resp_with_param(endp, 250, "DLCX", p->trans, stats);
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001294
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001295error3:
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001296 return create_err_response(endp, error_code, "DLCX", p->trans);
Holger Hans Peter Freythere3d16bb2010-04-22 11:58:13 +08001297
1298out_silent:
1299 return NULL;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001300}
1301
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001302static struct msgb *handle_rsip(struct mgcp_parse_data *p)
Holger Hans Peter Freyther9bdcc9c2010-03-31 06:39:35 +02001303{
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001304 if (p->found != 0) {
Holger Hans Peter Freyther74db7742011-06-10 00:04:55 +02001305 LOGP(DMGCP, LOGL_ERROR, "Failed to find the endpoint.\n");
1306 return NULL;
1307 }
1308
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001309 if (p->cfg->reset_cb)
1310 p->cfg->reset_cb(p->endp->tcfg);
Holger Hans Peter Freyther9bdcc9c2010-03-31 06:39:35 +02001311 return NULL;
1312}
1313
Holger Hans Peter Freyther8d0be252012-11-29 12:54:22 +01001314static char extract_tone(const char *line)
1315{
1316 const char *str = strstr(line, "D/");
1317 if (!str)
1318 return CHAR_MAX;
1319
1320 return str[2];
1321}
1322
Holger Hans Peter Freyther6e94d6d2011-01-25 23:33:54 +01001323/*
1324 * This can request like DTMF detection and forward, fax detection... it
1325 * can also request when the notification should be send and such. We don't
1326 * do this right now.
1327 */
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001328static struct msgb *handle_noti_req(struct mgcp_parse_data *p)
Holger Hans Peter Freyther6e94d6d2011-01-25 23:33:54 +01001329{
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001330 int res = 0;
1331 char *line;
Holger Hans Peter Freyther9f3835b2013-05-21 17:04:35 +02001332 char tone = CHAR_MAX;
Holger Hans Peter Freyther6e94d6d2011-01-25 23:33:54 +01001333
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001334 if (p->found != 0)
1335 return create_err_response(NULL, 400, "RQNT", p->trans);
Holger Hans Peter Freyther6e94d6d2011-01-25 23:33:54 +01001336
Jacob Erlbeck17711712013-11-29 13:43:46 +01001337 for_each_line(line, p->save) {
Holger Hans Peter Freyther8d0be252012-11-29 12:54:22 +01001338 switch (line[0]) {
1339 case 'S':
1340 tone = extract_tone(line);
1341 break;
1342 }
Holger Hans Peter Freyther6e94d6d2011-01-25 23:33:54 +01001343 }
Holger Hans Peter Freyther8d0be252012-11-29 12:54:22 +01001344
1345 /* we didn't see a signal request with a tone */
1346 if (tone == CHAR_MAX)
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001347 return create_ok_response(p->endp, 200, "RQNT", p->trans);
Holger Hans Peter Freyther8d0be252012-11-29 12:54:22 +01001348
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001349 if (p->cfg->rqnt_cb)
1350 res = p->cfg->rqnt_cb(p->endp, tone);
Holger Hans Peter Freyther8d0be252012-11-29 12:54:22 +01001351
1352 return res == 0 ?
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001353 create_ok_response(p->endp, 200, "RQNT", p->trans) :
1354 create_err_response(p->endp, res, "RQNT", p->trans);
Holger Hans Peter Freyther6e94d6d2011-01-25 23:33:54 +01001355}
1356
Jacob Erlbeck075a9eb2013-12-19 18:53:07 +01001357static void mgcp_keepalive_timer_cb(void *_tcfg)
1358{
1359 struct mgcp_trunk_config *tcfg = _tcfg;
1360 int i;
1361 LOGP(DMGCP, LOGL_DEBUG, "Triggered trunk %d keepalive timer.\n",
1362 tcfg->trunk_nr);
1363
1364 if (tcfg->keepalive_interval <= 0)
1365 return;
1366
1367 for (i = 1; i < tcfg->number_endpoints; ++i) {
1368 struct mgcp_endpoint *endp = &tcfg->endpoints[i];
1369 if (endp->conn_mode == MGCP_CONN_RECV_ONLY)
Holger Hans Peter Freyther6b0ba012014-07-08 13:01:56 +02001370 send_dummy(endp);
Jacob Erlbeck075a9eb2013-12-19 18:53:07 +01001371 }
1372
1373 LOGP(DMGCP, LOGL_DEBUG, "Rescheduling trunk %d keepalive timer.\n",
1374 tcfg->trunk_nr);
1375 osmo_timer_schedule(&tcfg->keepalive_timer, tcfg->keepalive_interval, 0);
1376}
1377
1378void mgcp_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval)
1379{
1380 tcfg->keepalive_interval = interval;
1381 tcfg->keepalive_timer.data = tcfg;
1382 tcfg->keepalive_timer.cb = mgcp_keepalive_timer_cb;
1383
1384 if (interval <= 0)
1385 osmo_timer_del(&tcfg->keepalive_timer);
1386 else
1387 osmo_timer_schedule(&tcfg->keepalive_timer,
1388 tcfg->keepalive_interval, 0);
1389}
1390
Holger Hans Peter Freyther7bdc6372010-02-20 21:21:02 +01001391struct mgcp_config *mgcp_config_alloc(void)
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001392{
Holger Hans Peter Freyther7bdc6372010-02-20 21:21:02 +01001393 struct mgcp_config *cfg;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001394
Holger Hans Peter Freyther7bdc6372010-02-20 21:21:02 +01001395 cfg = talloc_zero(NULL, struct mgcp_config);
1396 if (!cfg) {
1397 LOGP(DMGCP, LOGL_FATAL, "Failed to allocate config.\n");
1398 return NULL;
1399 }
1400
1401 cfg->source_port = 2427;
1402 cfg->source_addr = talloc_strdup(cfg, "0.0.0.0");
Holger Hans Peter Freyther88ad7722011-02-28 00:56:17 +01001403
Holger Hans Peter Freytherb98ba722010-09-19 04:21:39 +08001404 cfg->transcoder_remote_base = 4000;
Holger Hans Peter Freyther15e73892010-08-05 07:10:56 +08001405
1406 cfg->bts_ports.base_port = RTP_PORT_DEFAULT;
1407 cfg->net_ports.base_port = RTP_PORT_NET_DEFAULT;
Holger Hans Peter Freyther7bdc6372010-02-20 21:21:02 +01001408
Jacob Erlbecka0d64ce2014-03-13 14:24:52 +01001409 cfg->rtp_processing_cb = &mgcp_rtp_processing_default;
1410 cfg->setup_rtp_processing_cb = &mgcp_setup_rtp_processing_default;
1411
Jacob Erlbeck168ca002014-03-17 12:40:07 +01001412 cfg->get_net_downlink_format_cb = &mgcp_get_net_downlink_format_default;
1413
Holger Hans Peter Freyther88ad7722011-02-28 00:56:17 +01001414 /* default trunk handling */
1415 cfg->trunk.cfg = cfg;
1416 cfg->trunk.trunk_nr = 0;
1417 cfg->trunk.trunk_type = MGCP_TRUNK_VIRTUAL;
1418 cfg->trunk.audio_name = talloc_strdup(cfg, "AMR/8000");
1419 cfg->trunk.audio_payload = 126;
Jacob Erlbeck0a1bc562013-12-10 13:09:37 +01001420 cfg->trunk.audio_send_ptime = 1;
Holger Hans Peter Freyther619b0142014-11-19 16:04:45 +01001421 cfg->trunk.audio_send_name = 1;
Holger Hans Peter Freythera8090d52012-05-11 13:00:45 +02001422 cfg->trunk.omit_rtcp = 0;
Jacob Erlbeck075a9eb2013-12-19 18:53:07 +01001423 mgcp_trunk_set_keepalive(&cfg->trunk, MGCP_KEEPALIVE_ONCE);
Holger Hans Peter Freyther88ad7722011-02-28 00:56:17 +01001424
Holger Hans Peter Freyther0e939fe2011-02-28 12:11:02 +01001425 INIT_LLIST_HEAD(&cfg->trunks);
1426
Holger Hans Peter Freyther7bdc6372010-02-20 21:21:02 +01001427 return cfg;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001428}
1429
Holger Hans Peter Freyther0e939fe2011-02-28 12:11:02 +01001430struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int nr)
1431{
1432 struct mgcp_trunk_config *trunk;
1433
1434 trunk = talloc_zero(cfg, struct mgcp_trunk_config);
1435 if (!trunk) {
1436 LOGP(DMGCP, LOGL_ERROR, "Failed to allocate.\n");
1437 return NULL;
1438 }
1439
1440 trunk->cfg = cfg;
1441 trunk->trunk_type = MGCP_TRUNK_E1;
1442 trunk->trunk_nr = nr;
1443 trunk->audio_name = talloc_strdup(cfg, "AMR/8000");
1444 trunk->audio_payload = 126;
Jacob Erlbeck0a1bc562013-12-10 13:09:37 +01001445 trunk->audio_send_ptime = 1;
Holger Hans Peter Freyther619b0142014-11-19 16:04:45 +01001446 trunk->audio_send_name = 1;
Holger Hans Peter Freyther0e939fe2011-02-28 12:11:02 +01001447 trunk->number_endpoints = 33;
Holger Hans Peter Freythera8090d52012-05-11 13:00:45 +02001448 trunk->omit_rtcp = 0;
Jacob Erlbeck075a9eb2013-12-19 18:53:07 +01001449 mgcp_trunk_set_keepalive(trunk, MGCP_KEEPALIVE_ONCE);
Holger Hans Peter Freyther0e939fe2011-02-28 12:11:02 +01001450 llist_add_tail(&trunk->entry, &cfg->trunks);
1451 return trunk;
1452}
1453
1454struct mgcp_trunk_config *mgcp_trunk_num(struct mgcp_config *cfg, int index)
1455{
1456 struct mgcp_trunk_config *trunk;
1457
1458 llist_for_each_entry(trunk, &cfg->trunks, entry)
1459 if (trunk->trunk_nr == index)
1460 return trunk;
1461
1462 return NULL;
1463}
1464
Holger Hans Peter Freytherfa80d072014-09-01 10:40:19 +02001465static void mgcp_rtp_codec_reset(struct mgcp_rtp_codec *codec)
1466{
1467 codec->payload_type = -1;
1468 talloc_free(codec->subtype_name);
1469 codec->subtype_name = NULL;
1470 talloc_free(codec->audio_name);
1471 codec->audio_name = NULL;
1472 codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
1473 codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
1474 codec->rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE;
1475 codec->channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS;
1476}
1477
Holger Hans Peter Freythera17d7012010-08-05 01:34:51 +08001478static void mgcp_rtp_end_reset(struct mgcp_rtp_end *end)
1479{
Holger Hans Peter Freythere97155a2010-11-02 19:06:13 +01001480 if (end->local_alloc == PORT_ALLOC_DYNAMIC) {
Holger Hans Peter Freytherf138f912010-08-05 08:08:17 +08001481 mgcp_free_rtp_port(end);
Holger Hans Peter Freythere97155a2010-11-02 19:06:13 +01001482 end->local_port = 0;
1483 }
Holger Hans Peter Freytherf138f912010-08-05 08:08:17 +08001484
Holger Hans Peter Freytherc4921272010-08-05 03:37:22 +08001485 end->packets = 0;
Holger Hans Peter Freyther952f7522012-09-12 11:38:37 +02001486 end->octets = 0;
Jacob Erlbeck0970bab2013-12-19 12:13:32 +01001487 end->dropped_packets = 0;
Holger Hans Peter Freytherc4921272010-08-05 03:37:22 +08001488 memset(&end->addr, 0, sizeof(end->addr));
Holger Hans Peter Freythere97155a2010-11-02 19:06:13 +01001489 end->rtp_port = end->rtcp_port = 0;
Holger Hans Peter Freytherf138f912010-08-05 08:08:17 +08001490 end->local_alloc = -1;
Holger Hans Peter Freyther5ea1bc72012-09-03 00:07:39 +02001491 talloc_free(end->fmtp_extra);
1492 end->fmtp_extra = NULL;
Jacob Erlbecka0d64ce2014-03-13 14:24:52 +01001493 talloc_free(end->rtp_process_data);
1494 end->rtp_process_data = NULL;
Jacob Erlbeck72c30902013-11-25 15:23:35 +01001495
1496 /* Set default values */
Jacob Erlbeckf6ec0e92013-12-04 10:30:11 +01001497 end->frames_per_packet = 0; /* unknown */
1498 end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS;
Jacob Erlbeck8857c902014-01-16 16:50:39 +01001499 end->output_enabled = 0;
Holger Hans Peter Freytherfa80d072014-09-01 10:40:19 +02001500
1501 mgcp_rtp_codec_reset(&end->codec);
Holger Hans Peter Freythere46bc272014-09-01 10:50:22 +02001502 mgcp_rtp_codec_reset(&end->alt_codec);
Holger Hans Peter Freythera17d7012010-08-05 01:34:51 +08001503}
1504
Holger Hans Peter Freytherc4921272010-08-05 03:37:22 +08001505static void mgcp_rtp_end_init(struct mgcp_rtp_end *end)
1506{
1507 mgcp_rtp_end_reset(end);
1508 end->rtp.fd = -1;
1509 end->rtcp.fd = -1;
1510}
1511
Holger Hans Peter Freyther1f0c5b42011-02-28 14:37:03 +01001512int mgcp_endpoints_allocate(struct mgcp_trunk_config *tcfg)
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001513{
1514 int i;
1515
Holger Hans Peter Freyther7bdc6372010-02-20 21:21:02 +01001516 /* Initialize all endpoints */
Holger Hans Peter Freyther1f0c5b42011-02-28 14:37:03 +01001517 tcfg->endpoints = _talloc_zero_array(tcfg->cfg,
Holger Hans Peter Freyther7bdc6372010-02-20 21:21:02 +01001518 sizeof(struct mgcp_endpoint),
Holger Hans Peter Freyther88ad7722011-02-28 00:56:17 +01001519 tcfg->number_endpoints, "endpoints");
1520 if (!tcfg->endpoints)
Holger Hans Peter Freyther7bdc6372010-02-20 21:21:02 +01001521 return -1;
1522
Holger Hans Peter Freyther88ad7722011-02-28 00:56:17 +01001523 for (i = 0; i < tcfg->number_endpoints; ++i) {
1524 tcfg->endpoints[i].ci = CI_UNUSED;
Holger Hans Peter Freyther1f0c5b42011-02-28 14:37:03 +01001525 tcfg->endpoints[i].cfg = tcfg->cfg;
Holger Hans Peter Freyther88ad7722011-02-28 00:56:17 +01001526 tcfg->endpoints[i].tcfg = tcfg;
1527 mgcp_rtp_end_init(&tcfg->endpoints[i].net_end);
1528 mgcp_rtp_end_init(&tcfg->endpoints[i].bts_end);
1529 mgcp_rtp_end_init(&tcfg->endpoints[i].trans_net);
1530 mgcp_rtp_end_init(&tcfg->endpoints[i].trans_bts);
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001531 }
1532
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001533 return 0;
1534}
Holger Hans Peter Freyther154b9552010-02-26 13:27:51 +01001535
Holger Hans Peter Freythercb6ad702014-07-22 15:00:52 +02001536void mgcp_release_endp(struct mgcp_endpoint *endp)
Holger Hans Peter Freyther154b9552010-02-26 13:27:51 +01001537{
Holger Hans Peter Freythercb6ad702014-07-22 15:00:52 +02001538 LOGP(DMGCP, LOGL_DEBUG, "Releasing endpoint on: 0x%x\n", ENDPOINT_NUMBER(endp));
Holger Hans Peter Freyther89976e82010-08-03 15:01:43 +00001539 endp->ci = CI_UNUSED;
Holger Hans Peter Freyther39a97e22010-08-06 18:03:11 +08001540 endp->allocated = 0;
Holger Hans Peter Freyther154b9552010-02-26 13:27:51 +01001541
Holger Hans Peter Freyther01d315f2012-12-07 11:52:16 +01001542 talloc_free(endp->callid);
1543 endp->callid = NULL;
Holger Hans Peter Freyther154b9552010-02-26 13:27:51 +01001544
Jacob Erlbeck2c2ca4d2013-12-09 14:32:03 +01001545 talloc_free(endp->local_options.string);
1546 endp->local_options.string = NULL;
Jacob Erlbeck452c1832014-05-09 19:42:17 +02001547 talloc_free(endp->local_options.codec);
1548 endp->local_options.codec = NULL;
Holger Hans Peter Freyther154b9552010-02-26 13:27:51 +01001549
Holger Hans Peter Freythera17d7012010-08-05 01:34:51 +08001550 mgcp_rtp_end_reset(&endp->bts_end);
1551 mgcp_rtp_end_reset(&endp->net_end);
Holger Hans Peter Freyther21262332010-11-01 20:53:31 +01001552 mgcp_rtp_end_reset(&endp->trans_net);
Holger Hans Peter Freytherbd7b3c52010-11-01 21:04:54 +01001553 mgcp_rtp_end_reset(&endp->trans_bts);
Pablo Neira Ayuso46bd4242013-07-08 05:09:46 +02001554 endp->type = MGCP_RTP_DEFAULT;
Holger Hans Peter Freyther380b8712010-07-29 02:38:39 +08001555
Holger Hans Peter Freyther31868922010-08-03 11:59:04 +00001556 memset(&endp->net_state, 0, sizeof(endp->net_state));
1557 memset(&endp->bts_state, 0, sizeof(endp->bts_state));
Holger Hans Peter Freytherc597a4e2010-08-03 02:57:02 +08001558
1559 endp->conn_mode = endp->orig_mode = MGCP_CONN_NONE;
Holger Hans Peter Freyther260d6ed2010-08-06 01:12:21 +08001560
Pablo Neira Ayusob769f3c2014-08-27 17:02:52 +02001561 if (endp->osmux.state == OSMUX_STATE_ENABLED)
1562 osmux_disable_endpoint(endp);
1563
Holger Hans Peter Freyther260d6ed2010-08-06 01:12:21 +08001564 memset(&endp->taps, 0, sizeof(endp->taps));
Holger Hans Peter Freyther154b9552010-02-26 13:27:51 +01001565}
Holger Hans Peter Freyther3b5e3c42010-09-18 06:35:29 +08001566
Holger Hans Peter Freythercb6ad702014-07-22 15:00:52 +02001567void mgcp_initialize_endp(struct mgcp_endpoint *endp)
1568{
1569 return mgcp_release_endp(endp);
1570}
1571
Holger Hans Peter Freyther557b1ab2010-09-19 04:39:55 +08001572static int send_trans(struct mgcp_config *cfg, const char *buf, int len)
1573{
1574 struct sockaddr_in addr;
1575
1576 memset(&addr, 0, sizeof(addr));
1577 addr.sin_family = AF_INET;
1578 addr.sin_addr = cfg->transcoder_in;
1579 addr.sin_port = htons(2427);
1580 return sendto(cfg->gw_fd.bfd.fd, buf, len, 0,
1581 (struct sockaddr *) &addr, sizeof(addr));
1582}
1583
Holger Hans Peter Freyther3b5e3c42010-09-18 06:35:29 +08001584static void send_msg(struct mgcp_endpoint *endp, int endpoint, int port,
1585 const char *msg, const char *mode)
1586{
Holger Hans Peter Freyther3b5e3c42010-09-18 06:35:29 +08001587 char buf[2096];
1588 int len;
Jacob Erlbeck997e1e82014-03-14 18:01:20 +01001589 int nchars;
Holger Hans Peter Freyther3b5e3c42010-09-18 06:35:29 +08001590
1591 /* hardcoded to AMR right now, we do not know the real type at this point */
1592 len = snprintf(buf, sizeof(buf),
1593 "%s 42 %x@mgw MGCP 1.0\r\n"
1594 "C: 4256\r\n"
1595 "M: %s\r\n"
Jacob Erlbeck997e1e82014-03-14 18:01:20 +01001596 "\r\n",
1597 msg, endpoint, mode);
Holger Hans Peter Freyther3b5e3c42010-09-18 06:35:29 +08001598
1599 if (len < 0)
1600 return;
1601
Jacob Erlbeck997e1e82014-03-14 18:01:20 +01001602 nchars = write_response_sdp(endp, buf + len, sizeof(buf) + len - 1,
1603 endp->cfg->source_addr);
1604 if (nchars < 0)
1605 return;
1606
1607 len += nchars;
1608
Holger Hans Peter Freyther3b5e3c42010-09-18 06:35:29 +08001609 buf[sizeof(buf) - 1] = '\0';
1610
Holger Hans Peter Freyther557b1ab2010-09-19 04:39:55 +08001611 send_trans(endp->cfg, buf, len);
Holger Hans Peter Freyther3b5e3c42010-09-18 06:35:29 +08001612}
1613
1614static void send_dlcx(struct mgcp_endpoint *endp, int endpoint)
1615{
Holger Hans Peter Freyther3b5e3c42010-09-18 06:35:29 +08001616 char buf[2096];
1617 int len;
1618
1619 len = snprintf(buf, sizeof(buf),
1620 "DLCX 43 %x@mgw MGCP 1.0\r\n"
1621 "C: 4256\r\n"
1622 , endpoint);
1623
1624 if (len < 0)
1625 return;
1626
1627 buf[sizeof(buf) - 1] = '\0';
1628
Holger Hans Peter Freyther557b1ab2010-09-19 04:39:55 +08001629 send_trans(endp->cfg, buf, len);
Holger Hans Peter Freyther3b5e3c42010-09-18 06:35:29 +08001630}
1631
Harald Welte6a857052012-01-27 00:41:39 +01001632static int send_agent(struct mgcp_config *cfg, const char *buf, int len)
1633{
1634 return write(cfg->gw_fd.bfd.fd, buf, len);
1635}
1636
1637int mgcp_send_reset_all(struct mgcp_config *cfg)
1638{
1639 static const char mgcp_reset[] = {
1640 "RSIP 1 *@mgw MGCP 1.0\r\n"
1641 };
1642
1643 return send_agent(cfg, mgcp_reset, sizeof mgcp_reset -1);
1644}
1645
1646int mgcp_send_reset_ep(struct mgcp_endpoint *endp, int endpoint)
1647{
1648 char buf[128];
1649 int len;
1650
1651 len = snprintf(buf, sizeof(buf),
1652 "RSIP 39 %x@mgw MGCP 1.0\r\n"
1653 , endpoint);
1654 if (len < 0)
1655 return len;
1656
1657 buf[sizeof(buf) - 1] = '\0';
1658
1659 return send_agent(endp->cfg, buf, len);
1660}
1661
Jacob Erlbecka0d64ce2014-03-13 14:24:52 +01001662static void setup_rtp_processing(struct mgcp_endpoint *endp)
1663{
1664 struct mgcp_config *cfg = endp->cfg;
1665
1666 if (endp->type != MGCP_RTP_DEFAULT)
1667 return;
1668
1669 if (endp->conn_mode == MGCP_CONN_LOOPBACK)
1670 return;
1671
1672 if (endp->conn_mode & MGCP_CONN_SEND_ONLY)
1673 cfg->setup_rtp_processing_cb(endp, &endp->net_end, &endp->bts_end);
1674 else
1675 cfg->setup_rtp_processing_cb(endp, &endp->net_end, NULL);
1676
1677 if (endp->conn_mode & MGCP_CONN_RECV_ONLY)
1678 cfg->setup_rtp_processing_cb(endp, &endp->bts_end, &endp->net_end);
1679 else
1680 cfg->setup_rtp_processing_cb(endp, &endp->bts_end, NULL);
1681}
1682
Holger Hans Peter Freyther3b5e3c42010-09-18 06:35:29 +08001683static void create_transcoder(struct mgcp_endpoint *endp)
1684{
1685 int port;
1686 int in_endp = ENDPOINT_NUMBER(endp);
Holger Hans Peter Freytherbd7b3c52010-11-01 21:04:54 +01001687 int out_endp = endp_back_channel(in_endp);
Holger Hans Peter Freyther3b5e3c42010-09-18 06:35:29 +08001688
Pablo Neira Ayuso46bd4242013-07-08 05:09:46 +02001689 if (endp->type != MGCP_RTP_TRANSCODED)
Holger Hans Peter Freyther3b5e3c42010-09-18 06:35:29 +08001690 return;
1691
Holger Hans Peter Freyther8b19dee2010-11-01 22:38:25 +01001692 send_msg(endp, in_endp, endp->trans_bts.local_port, "CRCX", "sendrecv");
1693 send_msg(endp, in_endp, endp->trans_bts.local_port, "MDCX", "sendrecv");
Holger Hans Peter Freyther21262332010-11-01 20:53:31 +01001694 send_msg(endp, out_endp, endp->trans_net.local_port, "CRCX", "sendrecv");
1695 send_msg(endp, out_endp, endp->trans_net.local_port, "MDCX", "sendrecv");
Holger Hans Peter Freyther3b5e3c42010-09-18 06:35:29 +08001696
Holger Hans Peter Freytherbd7b3c52010-11-01 21:04:54 +01001697 port = rtp_calculate_port(in_endp, endp->cfg->transcoder_remote_base);
1698 endp->trans_bts.rtp_port = htons(port);
1699 endp->trans_bts.rtcp_port = htons(port + 1);
1700
Holger Hans Peter Freytherb98ba722010-09-19 04:21:39 +08001701 port = rtp_calculate_port(out_endp, endp->cfg->transcoder_remote_base);
Holger Hans Peter Freyther21262332010-11-01 20:53:31 +01001702 endp->trans_net.rtp_port = htons(port);
1703 endp->trans_net.rtcp_port = htons(port + 1);
Holger Hans Peter Freyther3b5e3c42010-09-18 06:35:29 +08001704}
1705
1706static void delete_transcoder(struct mgcp_endpoint *endp)
1707{
1708 int in_endp = ENDPOINT_NUMBER(endp);
Holger Hans Peter Freytherbd7b3c52010-11-01 21:04:54 +01001709 int out_endp = endp_back_channel(in_endp);
Holger Hans Peter Freyther3b5e3c42010-09-18 06:35:29 +08001710
Pablo Neira Ayuso46bd4242013-07-08 05:09:46 +02001711 if (endp->type != MGCP_RTP_TRANSCODED)
Holger Hans Peter Freyther3b5e3c42010-09-18 06:35:29 +08001712 return;
1713
1714 send_dlcx(endp, in_endp);
1715 send_dlcx(endp, out_endp);
1716}
Holger Hans Peter Freytherf2eedff2010-09-19 04:36:07 +08001717
1718int mgcp_reset_transcoder(struct mgcp_config *cfg)
1719{
Holger Hans Peter Freytherf2eedff2010-09-19 04:36:07 +08001720 if (!cfg->transcoder_ip)
Holger Hans Peter Freythercf1c8772010-09-24 04:41:28 +08001721 return 0;
Holger Hans Peter Freytherf2eedff2010-09-19 04:36:07 +08001722
1723 static const char mgcp_reset[] = {
1724 "RSIP 1 13@mgw MGCP 1.0\r\n"
1725 };
1726
Holger Hans Peter Freyther557b1ab2010-09-19 04:39:55 +08001727 return send_trans(cfg, mgcp_reset, sizeof mgcp_reset -1);
Holger Hans Peter Freytherf2eedff2010-09-19 04:36:07 +08001728}
Holger Hans Peter Freyther0bf15a82012-09-14 17:18:12 +02001729
1730void mgcp_format_stats(struct mgcp_endpoint *endp, char *msg, size_t size)
1731{
Holger Hans Peter Freythercb306a62012-10-24 21:22:47 +02001732 uint32_t expected, jitter;
Holger Hans Peter Freyther38e02c52012-10-22 18:09:35 +02001733 int ploss;
Jacob Erlbeck50079a12013-11-25 12:53:28 +01001734 int nchars;
Holger Hans Peter Freyther38e02c52012-10-22 18:09:35 +02001735 mgcp_state_calc_loss(&endp->net_state, &endp->net_end,
1736 &expected, &ploss);
Holger Hans Peter Freythercb306a62012-10-24 21:22:47 +02001737 jitter = mgcp_state_calc_jitter(&endp->net_state);
Holger Hans Peter Freyther38e02c52012-10-22 18:09:35 +02001738
Jacob Erlbeck50079a12013-11-25 12:53:28 +01001739 nchars = snprintf(msg, size,
1740 "\r\nP: PS=%u, OS=%u, PR=%u, OR=%u, PL=%d, JI=%u",
1741 endp->bts_end.packets, endp->bts_end.octets,
1742 endp->net_end.packets, endp->net_end.octets,
1743 ploss, jitter);
1744 if (nchars < 0 || nchars >= size)
1745 goto truncate;
1746
1747 msg += nchars;
1748 size -= nchars;
1749
1750 /* Error Counter */
Pablo Neira Ayusoee11bc02014-09-17 12:33:09 +02001751 nchars = snprintf(msg, size,
1752 "\r\nX-Osmo-CP: EC TIS=%u, TOS=%u, TIR=%u, TOR=%u",
1753 endp->net_state.in_stream.err_ts_counter,
1754 endp->net_state.out_stream.err_ts_counter,
1755 endp->bts_state.in_stream.err_ts_counter,
1756 endp->bts_state.out_stream.err_ts_counter);
1757 if (nchars < 0 || nchars >= size)
1758 goto truncate;
1759
1760 msg += nchars;
1761 size -= nchars;
1762
1763 if (endp->osmux.state == OSMUX_STATE_ENABLED) {
1764 snprintf(msg, size,
1765 "\r\nX-Osmux-ST: CR=%u, BR=%u",
1766 endp->osmux.stats.chunks,
1767 endp->osmux.stats.octets);
1768 }
Jacob Erlbeck50079a12013-11-25 12:53:28 +01001769truncate:
Holger Hans Peter Freyther0bf15a82012-09-14 17:18:12 +02001770 msg[size - 1] = '\0';
1771}
Holger Hans Peter Freyther462b7d72012-10-24 21:53:40 +02001772
1773int mgcp_parse_stats(struct msgb *msg, uint32_t *ps, uint32_t *os,
1774 uint32_t *pr, uint32_t *_or, int *loss, uint32_t *jitter)
1775{
1776 char *line, *save;
1777 int rc;
1778
1779 /* initialize with bad values */
1780 *ps = *os = *pr = *_or = *jitter = UINT_MAX;
1781 *loss = INT_MAX;
1782
1783
1784 line = strtok_r((char *) msg->l2h, "\r\n", &save);
1785 if (!line)
1786 return -1;
1787
1788 /* this can only parse the message that is created above... */
Jacob Erlbecka01bd602013-11-29 13:43:43 +01001789 for_each_non_empty_line(line, save) {
Holger Hans Peter Freyther462b7d72012-10-24 21:53:40 +02001790 switch (line[0]) {
1791 case 'P':
1792 rc = sscanf(line, "P: PS=%u, OS=%u, PR=%u, OR=%u, PL=%d, JI=%u",
1793 ps, os, pr, _or, loss, jitter);
1794 return rc == 6 ? 0 : -1;
1795 }
1796 }
1797
1798 return -1;
1799}