blob: 964971b005ec3ccc1dbbea196042d283700fb722 [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>
31
Pablo Neira Ayuso136f4532011-03-22 16:47:59 +010032#include <osmocom/core/msgb.h>
33#include <osmocom/core/talloc.h>
34#include <osmocom/core/select.h>
Holger Hans Peter Freyther16b7f5c2011-02-28 22:06:47 +010035
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +010036#include <openbsc/mgcp.h>
Holger Hans Peter Freyther7bdc6372010-02-20 21:21:02 +010037#include <openbsc/mgcp_internal.h>
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +010038
Jacob Erlbecka01bd602013-11-29 13:43:43 +010039#define for_each_non_empty_line(line, save) \
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +010040 for (line = strtok_r(NULL, "\r\n", &save); line;\
Harald Welteae199722012-01-27 12:31:36 +010041 line = strtok_r(NULL, "\r\n", &save))
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +010042
Jacob Erlbeck2bee7f92013-11-29 13:43:44 +010043#define for_each_line(line, save) \
44 for (line = strline_r(NULL, &save); line;\
45 line = strline_r(NULL, &save))
46
47char *strline_r(char *str, char **saveptr)
48{
49 char *result;
50
51 if (str)
52 *saveptr = str;
53
54 result = *saveptr;
55
56 if (*saveptr != NULL) {
57 *saveptr = strpbrk(*saveptr, "\r\n");
58
59 if (*saveptr != NULL) {
60 char *eos = *saveptr;
61
62 if ((*saveptr)[0] == '\r' && (*saveptr)[1] == '\n')
63 (*saveptr)++;
64 (*saveptr)++;
65 if ((*saveptr)[0] == '\0')
66 *saveptr = NULL;
67
68 *eos = '\0';
69 }
70 }
71
72 return result;
73}
74
Jacob Erlbeck72c30902013-11-25 15:23:35 +010075/* Assume audio frame length of 20ms */
76#define DEFAULT_RTP_AUDIO_FRAME_DUR_NUM 20
77#define DEFAULT_RTP_AUDIO_FRAME_DUR_DEN 1000
Jacob Erlbeckf6ec0e92013-12-04 10:30:11 +010078#define DEFAULT_RTP_AUDIO_PACKET_DURATION_MS 20
Jacob Erlbeck72c30902013-11-25 15:23:35 +010079#define DEFAULT_RTP_AUDIO_DEFAULT_RATE 8000
80
Holger Hans Peter Freytherf138f912010-08-05 08:08:17 +080081static void mgcp_rtp_end_reset(struct mgcp_rtp_end *end);
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +010082
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +010083struct mgcp_parse_data {
84 struct mgcp_config *cfg;
85 struct mgcp_endpoint *endp;
86 char *trans;
87 char *save;
88 int found;
89};
90
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +010091struct mgcp_request {
92 char *name;
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +010093 struct msgb *(*handle_request) (struct mgcp_parse_data *data);
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +010094 char *debug_name;
95};
96
97#define MGCP_REQUEST(NAME, REQ, DEBUG_NAME) \
98 { .name = NAME, .handle_request = REQ, .debug_name = DEBUG_NAME },
99
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100100static struct msgb *handle_audit_endpoint(struct mgcp_parse_data *data);
101static struct msgb *handle_create_con(struct mgcp_parse_data *data);
102static struct msgb *handle_delete_con(struct mgcp_parse_data *data);
103static struct msgb *handle_modify_con(struct mgcp_parse_data *data);
104static struct msgb *handle_rsip(struct mgcp_parse_data *data);
105static struct msgb *handle_noti_req(struct mgcp_parse_data *data);
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100106
Holger Hans Peter Freyther3b5e3c42010-09-18 06:35:29 +0800107static void create_transcoder(struct mgcp_endpoint *endp);
108static void delete_transcoder(struct mgcp_endpoint *endp);
109
Jacob Erlbecka0d64ce2014-03-13 14:24:52 +0100110static void setup_rtp_processing(struct mgcp_endpoint *endp);
111
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100112static int mgcp_analyze_header(struct mgcp_parse_data *parse, char *data);
113
Holger Hans Peter Freyther1cc5ff82014-05-22 15:03:10 +0200114static int mgcp_check_param(const struct mgcp_endpoint *endp, const char *line)
115{
116 const size_t line_len = strlen(line);
117 if (line[0] != '\0' && line_len < 2) {
118 LOGP(DMGCP, LOGL_ERROR,
119 "Wrong MGCP option format: '%s' on 0x%x\n",
120 line, ENDPOINT_NUMBER(endp));
121 return 0;
122 }
123
124 return 1;
125}
126
Holger Hans Peter Freyther46340132010-08-06 08:26:54 +0800127static uint32_t generate_call_id(struct mgcp_config *cfg)
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100128{
129 int i;
130
131 /* use the call id */
Holger Hans Peter Freyther7bdc6372010-02-20 21:21:02 +0100132 ++cfg->last_call_id;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100133
134 /* handle wrap around */
Holger Hans Peter Freyther7bdc6372010-02-20 21:21:02 +0100135 if (cfg->last_call_id == CI_UNUSED)
136 ++cfg->last_call_id;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100137
138 /* callstack can only be of size number_of_endpoints */
139 /* verify that the call id is free, e.g. in case of overrun */
Holger Hans Peter Freyther88ad7722011-02-28 00:56:17 +0100140 for (i = 1; i < cfg->trunk.number_endpoints; ++i)
141 if (cfg->trunk.endpoints[i].ci == cfg->last_call_id)
Holger Hans Peter Freyther7bdc6372010-02-20 21:21:02 +0100142 return generate_call_id(cfg);
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100143
Holger Hans Peter Freyther7bdc6372010-02-20 21:21:02 +0100144 return cfg->last_call_id;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100145}
146
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100147/*
148 * array of function pointers for handling various
149 * messages. In the future this might be binary sorted
150 * for performance reasons.
151 */
152static const struct mgcp_request mgcp_requests [] = {
153 MGCP_REQUEST("AUEP", handle_audit_endpoint, "AuditEndpoint")
154 MGCP_REQUEST("CRCX", handle_create_con, "CreateConnection")
155 MGCP_REQUEST("DLCX", handle_delete_con, "DeleteConnection")
156 MGCP_REQUEST("MDCX", handle_modify_con, "ModifiyConnection")
Holger Hans Peter Freyther6e94d6d2011-01-25 23:33:54 +0100157 MGCP_REQUEST("RQNT", handle_noti_req, "NotificationRequest")
Holger Hans Peter Freyther9bdcc9c2010-03-31 06:39:35 +0200158
159 /* SPEC extension */
160 MGCP_REQUEST("RSIP", handle_rsip, "ReSetInProgress")
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100161};
162
Holger Hans Peter Freyther62e836c2010-02-03 11:03:45 +0100163static struct msgb *mgcp_msgb_alloc(void)
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100164{
Holger Hans Peter Freyther62e836c2010-02-03 11:03:45 +0100165 struct msgb *msg;
166 msg = msgb_alloc_headroom(4096, 128, "MGCP msg");
167 if (!msg)
168 LOGP(DMGCP, LOGL_ERROR, "Failed to msgb for MGCP data.\n");
169
170 return msg;
171}
172
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100173static struct msgb *do_retransmission(const struct mgcp_endpoint *endp)
174{
175 struct msgb *msg = mgcp_msgb_alloc();
176 if (!msg)
177 return NULL;
178
179 msg->l2h = msgb_put(msg, strlen(endp->last_response));
180 memcpy(msg->l2h, endp->last_response, msgb_l2len(msg));
181 return msg;
182}
183
Holger Hans Peter Freythera769dcb2012-12-07 15:14:27 +0100184static struct msgb *create_resp(struct mgcp_endpoint *endp, int code,
185 const char *txt, const char *msg,
Holger Hans Peter Freyther0bf15a82012-09-14 17:18:12 +0200186 const char *trans, const char *param,
187 const char *sdp)
Holger Hans Peter Freyther62e836c2010-02-03 11:03:45 +0100188{
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100189 int len;
Holger Hans Peter Freyther62e836c2010-02-03 11:03:45 +0100190 struct msgb *res;
191
192 res = mgcp_msgb_alloc();
193 if (!res)
194 return NULL;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100195
Holger Hans Peter Freyther0bf15a82012-09-14 17:18:12 +0200196 len = snprintf((char *) res->data, 2048, "%d %s%s%s\r\n%s",
197 code, trans, txt, param ? param : "", sdp ? sdp : "");
198 if (len < 0) {
199 LOGP(DMGCP, LOGL_ERROR, "Failed to sprintf MGCP response.\n");
200 msgb_free(res);
201 return NULL;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100202 }
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100203
Holger Hans Peter Freyther62e836c2010-02-03 11:03:45 +0100204 res->l2h = msgb_put(res, len);
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100205 LOGP(DMGCP, LOGL_DEBUG, "Generated response: code: %d for '%s'\n", code, res->l2h);
206
207 /*
208 * Remember the last transmission per endpoint.
209 */
210 if (endp) {
211 struct mgcp_trunk_config *tcfg = endp->tcfg;
212 talloc_free(endp->last_response);
213 talloc_free(endp->last_trans);
214 endp->last_trans = talloc_strdup(tcfg->endpoints, trans);
215 endp->last_response = talloc_strndup(tcfg->endpoints,
216 (const char *) res->l2h,
217 msgb_l2len(res));
218 }
219
Holger Hans Peter Freyther62e836c2010-02-03 11:03:45 +0100220 return res;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100221}
222
Holger Hans Peter Freythera769dcb2012-12-07 15:14:27 +0100223static struct msgb *create_ok_resp_with_param(struct mgcp_endpoint *endp,
224 int code, const char *msg,
Holger Hans Peter Freyther0bf15a82012-09-14 17:18:12 +0200225 const char *trans, const char *param)
226{
Holger Hans Peter Freythera769dcb2012-12-07 15:14:27 +0100227 return create_resp(endp, code, " OK", msg, trans, param, NULL);
Holger Hans Peter Freyther0bf15a82012-09-14 17:18:12 +0200228}
229
Holger Hans Peter Freythera769dcb2012-12-07 15:14:27 +0100230static struct msgb *create_ok_response(struct mgcp_endpoint *endp,
231 int code, const char *msg, const char *trans)
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100232{
Holger Hans Peter Freythera769dcb2012-12-07 15:14:27 +0100233 return create_ok_resp_with_param(endp, code, msg, trans, NULL);
Holger Hans Peter Freyther6adac172011-01-06 16:19:55 +0100234}
235
Holger Hans Peter Freythera769dcb2012-12-07 15:14:27 +0100236static struct msgb *create_err_response(struct mgcp_endpoint *endp,
237 int code, const char *msg, const char *trans)
Holger Hans Peter Freyther6adac172011-01-06 16:19:55 +0100238{
Holger Hans Peter Freythera769dcb2012-12-07 15:14:27 +0100239 return create_resp(endp, code, " FAIL", msg, trans, NULL, NULL);
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100240}
241
Holger Hans Peter Freyther8d188ed2010-02-21 01:44:08 +0100242static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
243 const char *msg, const char *trans_id)
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100244{
Holger Hans Peter Freyther7bdc6372010-02-20 21:21:02 +0100245 const char *addr = endp->cfg->local_ip;
Holger Hans Peter Freyther5ea1bc72012-09-03 00:07:39 +0200246 const char *fmtp_extra = endp->bts_end.fmtp_extra;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100247 char sdp_record[4096];
Jacob Erlbeck0a1bc562013-12-10 13:09:37 +0100248 int len;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100249
250 if (!addr)
Holger Hans Peter Freyther7bdc6372010-02-20 21:21:02 +0100251 addr = endp->cfg->source_addr;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100252
Jacob Erlbeck0a1bc562013-12-10 13:09:37 +0100253 len = snprintf(sdp_record, sizeof(sdp_record) - 1,
Pablo Neira Ayusocab6e752014-02-05 18:56:17 +0100254 "I: %u%s\n\n"
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100255 "v=0\r\n"
Holger Hans Peter Freytherc7e49d32011-07-20 20:43:15 +0200256 "o=- %u 23 IN IP4 %s\r\n"
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100257 "c=IN IP4 %s\r\n"
Holger Hans Peter Freytherc7e49d32011-07-20 20:43:15 +0200258 "t=0 0\r\n"
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100259 "m=audio %d RTP/AVP %d\r\n"
Holger Hans Peter Freyther5ea1bc72012-09-03 00:07:39 +0200260 "a=rtpmap:%d %s\r\n"
261 "%s%s",
Pablo Neira Ayusocab6e752014-02-05 18:56:17 +0100262 endp->ci, endp->cfg->osmux && endp->osmux ? "\nX-Osmux: On" : "",
263 endp->ci, addr, addr,
Holger Hans Peter Freytherc7e49d32011-07-20 20:43:15 +0200264 endp->net_end.local_port, endp->bts_end.payload_type,
Holger Hans Peter Freyther5ea1bc72012-09-03 00:07:39 +0200265 endp->bts_end.payload_type, endp->tcfg->audio_name,
266 fmtp_extra ? fmtp_extra : "", fmtp_extra ? "\r\n" : "");
Jacob Erlbeck0a1bc562013-12-10 13:09:37 +0100267
268 if (len < 0 || len >= sizeof(sdp_record))
269 goto buffer_too_small;
270
271 if (endp->bts_end.packet_duration_ms > 0 && endp->tcfg->audio_send_ptime) {
272 int nchars = snprintf(sdp_record + len, sizeof(sdp_record) - len,
273 "a=ptime:%d\r\n",
274 endp->bts_end.packet_duration_ms);
275 if (nchars < 0 || nchars >= sizeof(sdp_record) - len)
276 goto buffer_too_small;
277
278 len += nchars;
279 }
Holger Hans Peter Freythera769dcb2012-12-07 15:14:27 +0100280 return create_resp(endp, 200, " OK", msg, trans_id, NULL, sdp_record);
Jacob Erlbeck0a1bc562013-12-10 13:09:37 +0100281
282buffer_too_small:
283 LOGP(DMGCP, LOGL_ERROR, "SDP buffer too small: %d (needed %d)\n",
284 sizeof(sdp_record), len);
285 return NULL;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100286}
287
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100288/*
289 * handle incoming messages:
290 * - this can be a command (four letters, space, transaction id)
291 * - or a response (three numbers, space, transaction id)
292 */
Holger Hans Peter Freyther7bdc6372010-02-20 21:21:02 +0100293struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100294{
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100295 struct mgcp_parse_data pdata;
296 int i, code, handled = 0;
Holger Hans Peter Freyther62e836c2010-02-03 11:03:45 +0100297 struct msgb *resp = NULL;
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100298 char *data;
Jacob Erlbeck9107e2d2013-12-03 17:14:44 +0100299 unsigned char *tail = msg->l2h + msgb_l2len(msg); /* char after l2 data */
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100300
Holger Hans Peter Freytherf36a11a2010-03-31 13:26:46 +0200301 if (msgb_l2len(msg) < 4) {
Holger Hans Peter Freyther0d904e02012-12-07 16:46:19 +0100302 LOGP(DMGCP, LOGL_ERROR, "msg too short: %d\n", msg->len);
Holger Hans Peter Freyther62e836c2010-02-03 11:03:45 +0100303 return NULL;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100304 }
305
Jacob Erlbeck9107e2d2013-12-03 17:14:44 +0100306 /* Ensure that the msg->l2h is NUL terminated. */
307 if (tail[-1] == '\0')
308 /* nothing to do */;
309 else if (msgb_tailroom(msg) > 0)
310 tail[0] = '\0';
311 else if (tail[-1] == '\r' || tail[-1] == '\n')
312 tail[-1] = '\0';
313 else {
314 LOGP(DMGCP, LOGL_ERROR, "Cannot NUL terminate MGCP message: "
315 "Length: %d, Buffer size: %d\n",
316 msgb_l2len(msg), msg->data_len);
317 return NULL;
318 }
319
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100320 /* attempt to treat it as a response */
Holger Hans Peter Freytherf36a11a2010-03-31 13:26:46 +0200321 if (sscanf((const char *)&msg->l2h[0], "%3d %*s", &code) == 1) {
Holger Hans Peter Freyther63f2db22010-02-26 13:30:57 +0100322 LOGP(DMGCP, LOGL_DEBUG, "Response: Code: %d\n", code);
Holger Hans Peter Freyther29de3462012-12-07 16:07:46 +0100323 return NULL;
324 }
325
326 msg->l3h = &msg->l2h[4];
327
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100328
329 /*
330 * Check for a duplicate message and respond.
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100331 */
332 memset(&pdata, 0, sizeof(pdata));
333 pdata.cfg = cfg;
Jacob Erlbeck17711712013-11-29 13:43:46 +0100334 data = strline_r((char *) msg->l3h, &pdata.save);
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100335 pdata.found = mgcp_analyze_header(&pdata, data);
336 if (pdata.endp && pdata.trans
337 && pdata.endp->last_trans
338 && strcmp(pdata.endp->last_trans, pdata.trans) == 0) {
339 return do_retransmission(pdata.endp);
340 }
341
Holger Hans Peter Freyther29de3462012-12-07 16:07:46 +0100342 for (i = 0; i < ARRAY_SIZE(mgcp_requests); ++i) {
343 if (strncmp(mgcp_requests[i].name, (const char *) &msg->l2h[0], 4) == 0) {
344 handled = 1;
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100345 resp = mgcp_requests[i].handle_request(&pdata);
Holger Hans Peter Freyther29de3462012-12-07 16:07:46 +0100346 break;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100347 }
348 }
349
Holger Hans Peter Freyther29de3462012-12-07 16:07:46 +0100350 if (!handled)
351 LOGP(DMGCP, LOGL_NOTICE, "MSG with type: '%.4s' not handled\n", &msg->l2h[0]);
352
Holger Hans Peter Freyther62e836c2010-02-03 11:03:45 +0100353 return resp;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100354}
355
Holger Hans Peter Freyther9f239a22011-01-06 19:32:52 +0100356/**
357 * We have a null terminated string with the endpoint name here. We only
358 * support two kinds. Simple ones as seen on the BSC level and the ones
359 * seen on the trunk side.
360 */
361static struct mgcp_endpoint *find_e1_endpoint(struct mgcp_config *cfg,
362 const char *mgcp)
363{
364 char *rest = NULL;
Holger Hans Peter Freyther74e61112011-02-28 14:13:47 +0100365 struct mgcp_trunk_config *tcfg;
366 int trunk, endp;
Holger Hans Peter Freyther9f239a22011-01-06 19:32:52 +0100367
368 trunk = strtoul(mgcp + 6, &rest, 10);
Holger Hans Peter Freytherf43f2fc2011-01-07 11:30:21 +0100369 if (rest == NULL || rest[0] != '/' || trunk < 1) {
Holger Hans Peter Freyther9f239a22011-01-06 19:32:52 +0100370 LOGP(DMGCP, LOGL_ERROR, "Wrong trunk name '%s'\n", mgcp);
371 return NULL;
372 }
373
374 endp = strtoul(rest + 1, &rest, 10);
375 if (rest == NULL || rest[0] != '@') {
Holger Hans Peter Freytherf43f2fc2011-01-07 11:30:21 +0100376 LOGP(DMGCP, LOGL_ERROR, "Wrong endpoint name '%s'\n", mgcp);
Holger Hans Peter Freyther9f239a22011-01-06 19:32:52 +0100377 return NULL;
378 }
379
380 /* signalling is on timeslot 1 */
381 if (endp == 1)
382 return NULL;
383
Holger Hans Peter Freyther74e61112011-02-28 14:13:47 +0100384 tcfg = mgcp_trunk_num(cfg, trunk);
385 if (!tcfg) {
386 LOGP(DMGCP, LOGL_ERROR, "The trunk %d is not declared.\n", trunk);
387 return NULL;
388 }
389
390 if (!tcfg->endpoints) {
391 LOGP(DMGCP, LOGL_ERROR, "Endpoints of trunk %d not allocated.\n", trunk);
392 return NULL;
393 }
394
395 if (endp < 1 || endp >= tcfg->number_endpoints) {
Holger Hans Peter Freyther9f239a22011-01-06 19:32:52 +0100396 LOGP(DMGCP, LOGL_ERROR, "Failed to find endpoint '%s'\n", mgcp);
Holger Hans Peter Freyther45c21842011-01-07 11:36:54 +0100397 return NULL;
Holger Hans Peter Freyther9f239a22011-01-06 19:32:52 +0100398 }
399
Holger Hans Peter Freyther74e61112011-02-28 14:13:47 +0100400 return &tcfg->endpoints[endp];
Holger Hans Peter Freyther9f239a22011-01-06 19:32:52 +0100401}
402
Holger Hans Peter Freyther7bdc6372010-02-20 21:21:02 +0100403static struct mgcp_endpoint *find_endpoint(struct mgcp_config *cfg, const char *mgcp)
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100404{
405 char *endptr = NULL;
406 unsigned int gw = INT_MAX;
407
Holger Hans Peter Freyther9f239a22011-01-06 19:32:52 +0100408 if (strncmp(mgcp, "ds/e1", 5) == 0) {
409 return find_e1_endpoint(cfg, mgcp);
410 } else {
411 gw = strtoul(mgcp, &endptr, 16);
Holger Hans Peter Freyther88ad7722011-02-28 00:56:17 +0100412 if (gw > 0 && gw < cfg->trunk.number_endpoints && strcmp(endptr, "@mgw") == 0)
413 return &cfg->trunk.endpoints[gw];
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100414 }
415
Holger Hans Peter Freyther9f239a22011-01-06 19:32:52 +0100416 LOGP(DMGCP, LOGL_ERROR, "Not able to find endpoint: '%s'\n", mgcp);
417 return NULL;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100418}
419
Harald Welteae199722012-01-27 12:31:36 +0100420/**
421 * @returns 0 when the status line was complete and transaction_id and
422 * endp out parameters are set.
423 */
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100424static int mgcp_analyze_header(struct mgcp_parse_data *pdata, char *data)
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100425{
Harald Welteae199722012-01-27 12:31:36 +0100426 int i = 0;
Holger Hans Peter Freyther1eba7de2013-07-03 10:23:35 +0200427 char *elem, *save = NULL;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100428
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100429 pdata->trans = "000000";
Holger Hans Peter Freythera820c5f2010-02-26 13:32:55 +0100430
Harald Welteae199722012-01-27 12:31:36 +0100431 for (elem = strtok_r(data, " ", &save); elem;
432 elem = strtok_r(NULL, " ", &save)) {
433 switch (i) {
434 case 0:
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100435 pdata->trans = elem;
Harald Welteae199722012-01-27 12:31:36 +0100436 break;
437 case 1:
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100438 pdata->endp = find_endpoint(pdata->cfg, elem);
439 if (!pdata->endp) {
440 LOGP(DMGCP, LOGL_ERROR,
441 "Unable to find Endpoint `%s'\n", elem);
442 return -1;
Harald Welteae199722012-01-27 12:31:36 +0100443 }
444 break;
445 case 2:
446 if (strcmp("MGCP", elem)) {
447 LOGP(DMGCP, LOGL_ERROR,
448 "MGCP header parsing error\n");
449 return -1;
450 }
451 break;
452 case 3:
453 if (strcmp("1.0", elem)) {
454 LOGP(DMGCP, LOGL_ERROR, "MGCP version `%s' "
455 "not supported\n", elem);
456 return -1;
457 }
458 break;
459 }
460 i++;
461 }
462
463 if (i != 4) {
464 LOGP(DMGCP, LOGL_ERROR, "MGCP status line too short.\n");
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100465 pdata->trans = "000000";
466 pdata->endp = NULL;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100467 return -1;
468 }
469
Holger Hans Peter Freytherf2f15912010-04-01 03:27:04 +0200470 return 0;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100471}
472
473static int verify_call_id(const struct mgcp_endpoint *endp,
474 const char *callid)
475{
476 if (strcmp(endp->callid, callid) != 0) {
Holger Hans Peter Freyther2ada71d2010-02-03 09:13:10 +0100477 LOGP(DMGCP, LOGL_ERROR, "CallIDs does not match on 0x%x. '%s' != '%s'\n",
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100478 ENDPOINT_NUMBER(endp), endp->callid, callid);
479 return -1;
480 }
481
482 return 0;
483}
484
485static int verify_ci(const struct mgcp_endpoint *endp,
Holger Hans Peter Freyther46340132010-08-06 08:26:54 +0800486 const char *_ci)
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100487{
Holger Hans Peter Freyther46340132010-08-06 08:26:54 +0800488 uint32_t ci = strtoul(_ci, NULL, 10);
489
490 if (ci != endp->ci) {
491 LOGP(DMGCP, LOGL_ERROR, "ConnectionIdentifiers do not match on 0x%x. %u != %s\n",
492 ENDPOINT_NUMBER(endp), endp->ci, _ci);
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100493 return -1;
494 }
495
496 return 0;
497}
498
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100499static struct msgb *handle_audit_endpoint(struct mgcp_parse_data *p)
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100500{
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100501 if (p->found != 0)
502 return create_err_response(NULL, 500, "AUEP", p->trans);
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100503 else
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100504 return create_ok_response(p->endp, 200, "AUEP", p->trans);
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100505}
506
Jacob Erlbeck2645c9e2013-12-19 12:23:24 +0100507static int parse_conn_mode(const char *msg, struct mgcp_endpoint *endp)
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100508{
509 int ret = 0;
510 if (strcmp(msg, "recvonly") == 0)
Jacob Erlbeck2645c9e2013-12-19 12:23:24 +0100511 endp->conn_mode = MGCP_CONN_RECV_ONLY;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100512 else if (strcmp(msg, "sendrecv") == 0)
Jacob Erlbeck2645c9e2013-12-19 12:23:24 +0100513 endp->conn_mode = MGCP_CONN_RECV_SEND;
Holger Hans Peter Freythere02860a2010-09-18 20:21:44 +0800514 else if (strcmp(msg, "sendonly") == 0)
Jacob Erlbeck2645c9e2013-12-19 12:23:24 +0100515 endp->conn_mode = MGCP_CONN_SEND_ONLY;
Holger Hans Peter Freyther98a38772010-08-03 02:27:21 +0800516 else if (strcmp(msg, "loopback") == 0)
Jacob Erlbeck2645c9e2013-12-19 12:23:24 +0100517 endp->conn_mode = MGCP_CONN_LOOPBACK;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100518 else {
Holger Hans Peter Freyther2ada71d2010-02-03 09:13:10 +0100519 LOGP(DMGCP, LOGL_ERROR, "Unknown connection mode: '%s'\n", msg);
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100520 ret = -1;
521 }
522
Jacob Erlbecke35fd132014-01-16 16:50:40 +0100523 endp->net_end.output_enabled =
524 endp->conn_mode & MGCP_CONN_SEND_ONLY ? 1 : 0;
525 endp->bts_end.output_enabled =
526 endp->conn_mode & MGCP_CONN_RECV_ONLY ? 1 : 0;
Jacob Erlbeck2645c9e2013-12-19 12:23:24 +0100527
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100528 return ret;
529}
530
Holger Hans Peter Freytherf138f912010-08-05 08:08:17 +0800531static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_rtp_end *end,
Holger Hans Peter Freyther218f8562010-09-18 02:30:02 +0800532 struct mgcp_port_range *range,
533 int (*alloc)(struct mgcp_endpoint *endp, int port))
Holger Hans Peter Freytherf138f912010-08-05 08:08:17 +0800534{
535 int i;
536
537 if (range->mode == PORT_ALLOC_STATIC) {
Holger Hans Peter Freytherf138f912010-08-05 08:08:17 +0800538 end->local_alloc = PORT_ALLOC_STATIC;
539 return 0;
540 }
541
542 /* attempt to find a port */
543 for (i = 0; i < 200; ++i) {
544 int rc;
545
546 if (range->last_port >= range->range_end)
547 range->last_port = range->range_start;
548
Holger Hans Peter Freyther218f8562010-09-18 02:30:02 +0800549 rc = alloc(endp, range->last_port);
Holger Hans Peter Freytherf138f912010-08-05 08:08:17 +0800550
551 range->last_port += 2;
552 if (rc == 0) {
553 end->local_alloc = PORT_ALLOC_DYNAMIC;
554 return 0;
555 }
556
557 }
558
Holger Hans Peter Freyther218f8562010-09-18 02:30:02 +0800559 LOGP(DMGCP, LOGL_ERROR, "Allocating a RTP/RTCP port failed 200 times 0x%x.\n",
560 ENDPOINT_NUMBER(endp));
Holger Hans Peter Freytherf138f912010-08-05 08:08:17 +0800561 return -1;
562}
563
564static int allocate_ports(struct mgcp_endpoint *endp)
565{
Holger Hans Peter Freyther218f8562010-09-18 02:30:02 +0800566 if (allocate_port(endp, &endp->net_end, &endp->cfg->net_ports,
567 mgcp_bind_net_rtp_port) != 0)
Holger Hans Peter Freytherf138f912010-08-05 08:08:17 +0800568 return -1;
569
Holger Hans Peter Freyther218f8562010-09-18 02:30:02 +0800570 if (allocate_port(endp, &endp->bts_end, &endp->cfg->bts_ports,
571 mgcp_bind_bts_rtp_port) != 0) {
Holger Hans Peter Freytherf138f912010-08-05 08:08:17 +0800572 mgcp_rtp_end_reset(&endp->net_end);
573 return -1;
574 }
575
Holger Hans Peter Freyther69906872011-02-28 14:51:48 +0100576 if (endp->cfg->transcoder_ip && endp->tcfg->trunk_type == MGCP_TRUNK_VIRTUAL) {
Holger Hans Peter Freyther21262332010-11-01 20:53:31 +0100577 if (allocate_port(endp, &endp->trans_net,
Holger Hans Peter Freytherb54048f2010-11-01 19:57:50 +0100578 &endp->cfg->transcoder_ports,
Holger Hans Peter Freytherbd7b3c52010-11-01 21:04:54 +0100579 mgcp_bind_trans_net_rtp_port) != 0) {
Holger Hans Peter Freytherb54048f2010-11-01 19:57:50 +0100580 mgcp_rtp_end_reset(&endp->net_end);
581 mgcp_rtp_end_reset(&endp->bts_end);
582 return -1;
583 }
584
Holger Hans Peter Freytherbd7b3c52010-11-01 21:04:54 +0100585 if (allocate_port(endp, &endp->trans_bts,
586 &endp->cfg->transcoder_ports,
587 mgcp_bind_trans_bts_rtp_port) != 0) {
588 mgcp_rtp_end_reset(&endp->net_end);
589 mgcp_rtp_end_reset(&endp->bts_end);
590 mgcp_rtp_end_reset(&endp->trans_net);
591 return -1;
592 }
593
Holger Hans Peter Freytherb54048f2010-11-01 19:57:50 +0100594 /* remember that we have set up transcoding */
Pablo Neira Ayuso46bd4242013-07-08 05:09:46 +0200595 endp->type = MGCP_RTP_TRANSCODED;
Holger Hans Peter Freyther218f8562010-09-18 02:30:02 +0800596 }
597
Holger Hans Peter Freytherf138f912010-08-05 08:08:17 +0800598 return 0;
599}
600
Jacob Erlbeck17711712013-11-29 13:43:46 +0100601static int parse_sdp_data(struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p)
602{
603 char *line;
604 int found_media = 0;
Jacob Erlbeck24754f02013-12-10 13:09:37 +0100605 int audio_payload = -1;
Jacob Erlbeck17711712013-11-29 13:43:46 +0100606
607 for_each_line(line, p->save) {
608 switch (line[0]) {
Jacob Erlbeck17711712013-11-29 13:43:46 +0100609 case 'o':
610 case 's':
611 case 't':
612 case 'v':
613 /* skip these SDP attributes */
614 break;
Jacob Erlbeck24754f02013-12-10 13:09:37 +0100615 case 'a': {
616 int payload;
617 int rate;
618 int channels = 1;
619 int ptime, ptime2 = 0;
620 char audio_name[64];
621 char audio_codec[64];
622
623 if (audio_payload == -1)
624 break;
625
Holger Hans Peter Freyther4f0381b2014-05-28 16:44:34 +0200626 if (sscanf(line, "a=rtpmap:%d %63s",
Jacob Erlbeck24754f02013-12-10 13:09:37 +0100627 &payload, audio_name) == 2) {
628 if (payload != audio_payload)
629 break;
630
631 if (sscanf(audio_name, "%[^/]/%d/%d",
632 audio_codec, &rate, &channels) < 2)
633 break;
634
635 rtp->rate = rate;
636 if (channels != 1)
637 LOGP(DMGCP, LOGL_NOTICE,
638 "Channels != 1 in SDP: '%s' on 0x%x\n",
639 line, ENDPOINT_NUMBER(p->endp));
640 } else if (sscanf(line, "a=ptime:%d-%d",
641 &ptime, &ptime2) >= 1) {
642 if (ptime2 > 0 && ptime2 != ptime)
643 rtp->packet_duration_ms = 0;
644 else
645 rtp->packet_duration_ms = ptime;
646 } else if (sscanf(line, "a=maxptime:%d", &ptime2) == 1) {
647 if (ptime2 * rtp->frame_duration_den >
648 rtp->frame_duration_num * 1500)
649 /* more than 1 frame */
650 rtp->packet_duration_ms = 0;
651 }
652 break;
653 }
Jacob Erlbeck17711712013-11-29 13:43:46 +0100654 case 'm': {
655 int port;
Jacob Erlbeck24754f02013-12-10 13:09:37 +0100656 audio_payload = -1;
Jacob Erlbeck17711712013-11-29 13:43:46 +0100657
658 if (sscanf(line, "m=audio %d RTP/AVP %d",
Jacob Erlbeck24754f02013-12-10 13:09:37 +0100659 &port, &audio_payload) == 2) {
Jacob Erlbeck17711712013-11-29 13:43:46 +0100660 rtp->rtp_port = htons(port);
661 rtp->rtcp_port = htons(port + 1);
Jacob Erlbeck24754f02013-12-10 13:09:37 +0100662 rtp->payload_type = audio_payload;
Jacob Erlbeck17711712013-11-29 13:43:46 +0100663 found_media = 1;
664 }
665 break;
666 }
667 case 'c': {
668 char ipv4[16];
669
670 if (sscanf(line, "c=IN IP4 %15s", ipv4) == 1) {
671 inet_aton(ipv4, &rtp->addr);
672 }
673 break;
674 }
675 default:
676 if (p->endp)
677 LOGP(DMGCP, LOGL_NOTICE,
678 "Unhandled SDP option: '%c'/%d on 0x%x\n",
679 line[0], line[0], ENDPOINT_NUMBER(p->endp));
680 else
681 LOGP(DMGCP, LOGL_NOTICE,
682 "Unhandled SDP option: '%c'/%d\n",
683 line[0], line[0]);
684 break;
685 }
686 }
687
688 if (found_media)
689 LOGP(DMGCP, LOGL_NOTICE,
Jacob Erlbeck24754f02013-12-10 13:09:37 +0100690 "Got media info via SDP: port %d, payload %d, "
691 "duration %d, addr %s\n",
692 ntohs(rtp->rtp_port), rtp->payload_type,
693 rtp->packet_duration_ms, inet_ntoa(rtp->addr));
Jacob Erlbeck17711712013-11-29 13:43:46 +0100694
695 return found_media;
696}
697
Jacob Erlbeck2c2ca4d2013-12-09 14:32:03 +0100698/* Set the LCO from a string (see RFC 3435).
699 * The string is stored in the 'string' field. A NULL string is handled excatly
700 * like an empty string, the 'string' field is never NULL after this function
701 * has been called. */
702static void set_local_cx_options(void *ctx, struct mgcp_lco *lco,
703 const char *options)
704{
705 char *p_opt;
706
707 talloc_free(lco->string);
708 lco->pkt_period_min = lco->pkt_period_max = 0;
709 lco->string = talloc_strdup(ctx, options ? options : "");
710
711 p_opt = strstr(lco->string, "p:");
712 if (p_opt && sscanf(p_opt, "p:%d-%d",
713 &lco->pkt_period_min, &lco->pkt_period_max) == 1)
714 lco->pkt_period_max = lco->pkt_period_min;
715}
716
Jacob Erlbeckdb2d4312013-12-03 14:43:34 +0100717void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
718 struct mgcp_rtp_end *rtp)
719{
720 struct mgcp_trunk_config *tcfg = endp->tcfg;
721
Jacob Erlbecke2292f32013-12-03 15:13:12 +0100722 int patch_ssrc = expect_ssrc_change && tcfg->force_constant_ssrc;
723
Jacob Erlbeck4bbddc62013-12-18 12:54:51 +0100724 rtp->force_aligned_timing = tcfg->force_aligned_timing;
Jacob Erlbecke2292f32013-12-03 15:13:12 +0100725 rtp->force_constant_ssrc = patch_ssrc ? 1 : 0;
Jacob Erlbeckdb2d4312013-12-03 14:43:34 +0100726
727 LOGP(DMGCP, LOGL_DEBUG,
728 "Configuring RTP endpoint: local port %d%s%s\n",
729 ntohs(rtp->rtp_port),
Jacob Erlbeck4bbddc62013-12-18 12:54:51 +0100730 rtp->force_aligned_timing ? ", force constant timing" : "",
Jacob Erlbeckdb2d4312013-12-03 14:43:34 +0100731 rtp->force_constant_ssrc ? ", force constant ssrc" : "");
732}
733
Jacob Erlbeckf6ec0e92013-12-04 10:30:11 +0100734uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp,
735 struct mgcp_rtp_end *rtp)
736{
737 int f = 0;
738
739 /* Get the number of frames per channel and packet */
740 if (rtp->frames_per_packet)
741 f = rtp->frames_per_packet;
742 else if (rtp->packet_duration_ms && rtp->frame_duration_num) {
743 int den = 1000 * rtp->frame_duration_num;
744 f = (rtp->packet_duration_ms * rtp->frame_duration_den + den/2)
745 / den;
746 }
747
748 return rtp->rate * f * rtp->frame_duration_num / rtp->frame_duration_den;
749}
750
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100751static struct msgb *handle_create_con(struct mgcp_parse_data *p)
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100752{
Holger Hans Peter Freyther88ad7722011-02-28 00:56:17 +0100753 struct mgcp_trunk_config *tcfg;
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100754 struct mgcp_endpoint *endp = p->endp;
Holger Hans Peter Freyther9e9392d2010-08-08 16:24:48 +0800755 int error_code = 400;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100756
Holger Hans Peter Freyther3488c3d2011-07-20 20:44:54 +0200757 const char *local_options = NULL;
758 const char *callid = NULL;
759 const char *mode = NULL;
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100760 char *line;
Jacob Erlbeck78a95012013-11-29 13:43:49 +0100761 int have_sdp = 0;
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100762
763 if (p->found != 0)
764 return create_err_response(NULL, 510, "CRCX", p->trans);
Holger Hans Peter Freyther88ad7722011-02-28 00:56:17 +0100765
Holger Hans Peter Freyther3488c3d2011-07-20 20:44:54 +0200766 /* parse CallID C: and LocalParameters L: */
Jacob Erlbeck17711712013-11-29 13:43:46 +0100767 for_each_line(line, p->save) {
Holger Hans Peter Freyther1cc5ff82014-05-22 15:03:10 +0200768 if (!mgcp_check_param(endp, line))
769 continue;
770
Harald Welteae199722012-01-27 12:31:36 +0100771 switch (line[0]) {
772 case 'L':
773 local_options = (const char *) line + 3;
774 break;
775 case 'C':
776 callid = (const char *) line + 3;
777 break;
778 case 'M':
779 mode = (const char *) line + 3;
780 break;
Pablo Neira Ayusocab6e752014-02-05 18:56:17 +0100781 case 'X':
782 if (strcmp("Osmux: on", line + 2) == 0 &&
783 osmux_enable_endpoint(endp, OSMUX_ROLE_BSC) < 0) {
784 LOGP(DMGCP, LOGL_ERROR,
785 "Could not activate osmux in endpoint %d\n",
786 ENDPOINT_NUMBER(endp));
787 }
788 break;
Jacob Erlbeck78a95012013-11-29 13:43:49 +0100789 case '\0':
790 have_sdp = 1;
791 goto mgcp_header_done;
Harald Welteae199722012-01-27 12:31:36 +0100792 default:
793 LOGP(DMGCP, LOGL_NOTICE, "Unhandled option: '%c'/%d on 0x%x\n",
794 *line, *line, ENDPOINT_NUMBER(endp));
795 break;
796 }
Holger Hans Peter Freyther3488c3d2011-07-20 20:44:54 +0200797 }
Harald Welteae199722012-01-27 12:31:36 +0100798
Jacob Erlbeck78a95012013-11-29 13:43:49 +0100799mgcp_header_done:
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100800 tcfg = p->endp->tcfg;
Holger Hans Peter Freyther3488c3d2011-07-20 20:44:54 +0200801
802 /* Check required data */
803 if (!callid || !mode) {
804 LOGP(DMGCP, LOGL_ERROR, "Missing callid and mode in CRCX on 0x%x\n",
805 ENDPOINT_NUMBER(endp));
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100806 return create_err_response(endp, 400, "CRCX", p->trans);
Holger Hans Peter Freyther3488c3d2011-07-20 20:44:54 +0200807 }
808
Holger Hans Peter Freyther39a97e22010-08-06 18:03:11 +0800809 if (endp->allocated) {
Holger Hans Peter Freyther88ad7722011-02-28 00:56:17 +0100810 if (tcfg->force_realloc) {
Holger Hans Peter Freyther408cc4a2010-04-07 10:51:27 +0200811 LOGP(DMGCP, LOGL_NOTICE, "Endpoint 0x%x already allocated. Forcing realloc.\n",
812 ENDPOINT_NUMBER(endp));
Holger Hans Peter Freyther32d4e502010-04-24 21:02:48 +0800813 mgcp_free_endp(endp);
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100814 if (p->cfg->realloc_cb)
815 p->cfg->realloc_cb(tcfg, ENDPOINT_NUMBER(endp));
Holger Hans Peter Freyther408cc4a2010-04-07 10:51:27 +0200816 } else {
817 LOGP(DMGCP, LOGL_ERROR, "Endpoint is already used. 0x%x\n",
818 ENDPOINT_NUMBER(endp));
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100819 return create_err_response(endp, 400, "CRCX", p->trans);
Holger Hans Peter Freyther408cc4a2010-04-07 10:51:27 +0200820 }
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100821 }
822
Holger Hans Peter Freyther3488c3d2011-07-20 20:44:54 +0200823 /* copy some parameters */
824 endp->callid = talloc_strdup(tcfg->endpoints, callid);
825
Jacob Erlbeck2c2ca4d2013-12-09 14:32:03 +0100826 set_local_cx_options(endp->tcfg->endpoints, &endp->local_options,
827 local_options);
Holger Hans Peter Freyther3488c3d2011-07-20 20:44:54 +0200828
Jacob Erlbeck2645c9e2013-12-19 12:23:24 +0100829 if (parse_conn_mode(mode, endp) != 0) {
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100830 error_code = 517;
831 goto error2;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100832 }
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100833
834 /* initialize */
Holger Hans Peter Freythera17d7012010-08-05 01:34:51 +0800835 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 +0100836 mgcp_rtp_end_config(endp, 0, &endp->net_end);
837 mgcp_rtp_end_config(endp, 0, &endp->bts_end);
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100838
839 /* set to zero until we get the info */
Holger Hans Peter Freythera17d7012010-08-05 01:34:51 +0800840 memset(&endp->net_end.addr, 0, sizeof(endp->net_end.addr));
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100841
842 /* bind to the port now */
Holger Hans Peter Freytherf138f912010-08-05 08:08:17 +0800843 if (allocate_ports(endp) != 0)
844 goto error2;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100845
846 /* assign a local call identifier or fail */
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100847 endp->ci = generate_call_id(p->cfg);
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100848 if (endp->ci == CI_UNUSED)
849 goto error2;
850
Holger Hans Peter Freyther39a97e22010-08-06 18:03:11 +0800851 endp->allocated = 1;
Jacob Erlbeck78a95012013-11-29 13:43:49 +0100852
853 /* set up RTP media parameters */
Holger Hans Peter Freyther88ad7722011-02-28 00:56:17 +0100854 endp->bts_end.payload_type = tcfg->audio_payload;
Holger Hans Peter Freyther5ea1bc72012-09-03 00:07:39 +0200855 endp->bts_end.fmtp_extra = talloc_strdup(tcfg->endpoints,
856 tcfg->audio_fmtp_extra);
Jacob Erlbecka0d64ce2014-03-13 14:24:52 +0100857 if (have_sdp) {
Jacob Erlbeck78a95012013-11-29 13:43:49 +0100858 parse_sdp_data(&endp->net_end, p);
Jacob Erlbecka0d64ce2014-03-13 14:24:52 +0100859 setup_rtp_processing(endp);
860 }
Holger Hans Peter Freytheref6bb252010-02-26 13:41:22 +0100861
Holger Hans Peter Freytherfe86d3c2010-02-26 13:37:05 +0100862 /* policy CB */
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100863 if (p->cfg->policy_cb) {
864 int rc;
865 rc = p->cfg->policy_cb(tcfg, ENDPOINT_NUMBER(endp),
866 MGCP_ENDP_CRCX, p->trans);
867 switch (rc) {
Holger Hans Peter Freytherfe86d3c2010-02-26 13:37:05 +0100868 case MGCP_POLICY_REJECT:
869 LOGP(DMGCP, LOGL_NOTICE, "CRCX rejected by policy on 0x%x\n",
870 ENDPOINT_NUMBER(endp));
871 mgcp_free_endp(endp);
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100872 return create_err_response(endp, 400, "CRCX", p->trans);
Holger Hans Peter Freytherfe86d3c2010-02-26 13:37:05 +0100873 break;
874 case MGCP_POLICY_DEFER:
875 /* stop processing */
Holger Hans Peter Freyther3b5e3c42010-09-18 06:35:29 +0800876 create_transcoder(endp);
Holger Hans Peter Freytherfe86d3c2010-02-26 13:37:05 +0100877 return NULL;
878 break;
879 case MGCP_POLICY_CONT:
880 /* just continue */
881 break;
882 }
883 }
884
Holger Hans Peter Freythere378cb12010-08-06 20:22:23 +0800885 LOGP(DMGCP, LOGL_DEBUG, "Creating endpoint on: 0x%x CI: %u port: %u/%u\n",
Holger Hans Peter Freyther58ff2192010-08-05 03:08:35 +0800886 ENDPOINT_NUMBER(endp), endp->ci,
887 endp->net_end.local_port, endp->bts_end.local_port);
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100888 if (p->cfg->change_cb)
889 p->cfg->change_cb(tcfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_CRCX);
Holger Hans Peter Freyther77f7afe2010-02-03 09:54:43 +0100890
Pablo Neira Ayusocab6e752014-02-05 18:56:17 +0100891 if (endp->conn_mode & MGCP_CONN_RECV_ONLY && tcfg->keepalive_interval != 0) {
892 if (endp->osmux)
893 osmux_send_dummy(endp);
894 else
895 mgcp_send_dummy(endp);
896 }
Jacob Erlbeck075a9eb2013-12-19 18:53:07 +0100897
Holger Hans Peter Freyther3b5e3c42010-09-18 06:35:29 +0800898 create_transcoder(endp);
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100899 return create_response_with_sdp(endp, "CRCX", p->trans);
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100900error2:
Holger Hans Peter Freyther414bf402010-08-06 07:05:13 +0800901 mgcp_free_endp(endp);
Holger Hans Peter Freyther2ada71d2010-02-03 09:13:10 +0100902 LOGP(DMGCP, LOGL_NOTICE, "Resource error on 0x%x\n", ENDPOINT_NUMBER(endp));
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100903 return create_err_response(endp, error_code, "CRCX", p->trans);
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100904}
905
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100906static struct msgb *handle_modify_con(struct mgcp_parse_data *p)
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100907{
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100908 struct mgcp_endpoint *endp = p->endp;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100909 int error_code = 500;
Pablo Neira Ayusocab6e752014-02-05 18:56:17 +0100910 int silent = 0, osmux = 0;
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100911 char *line;
Jacob Erlbeck2c2ca4d2013-12-09 14:32:03 +0100912 const char *local_options = NULL;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100913
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100914 if (p->found != 0)
915 return create_err_response(NULL, 510, "MDCX", p->trans);
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100916
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100917 if (endp->ci == CI_UNUSED) {
918 LOGP(DMGCP, LOGL_ERROR, "Endpoint is not "
919 "holding a connection. 0x%x\n", ENDPOINT_NUMBER(endp));
920 return create_err_response(endp, 400, "MDCX", p->trans);
921 }
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100922
Jacob Erlbeck17711712013-11-29 13:43:46 +0100923 for_each_line(line, p->save) {
Holger Hans Peter Freyther1cc5ff82014-05-22 15:03:10 +0200924 if (!mgcp_check_param(endp, line))
925 continue;
926
Harald Welteae199722012-01-27 12:31:36 +0100927 switch (line[0]) {
928 case 'C': {
929 if (verify_call_id(endp, line + 3) != 0)
930 goto error3;
931 break;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100932 }
Harald Welteae199722012-01-27 12:31:36 +0100933 case 'I': {
934 if (verify_ci(endp, line + 3) != 0)
935 goto error3;
936 break;
937 }
938 case 'L':
Jacob Erlbeck2c2ca4d2013-12-09 14:32:03 +0100939 local_options = (const char *) line + 3;
Harald Welteae199722012-01-27 12:31:36 +0100940 break;
941 case 'M':
Jacob Erlbeck2645c9e2013-12-19 12:23:24 +0100942 if (parse_conn_mode(line + 3, endp) != 0) {
Harald Welteae199722012-01-27 12:31:36 +0100943 error_code = 517;
944 goto error3;
945 }
946 endp->orig_mode = endp->conn_mode;
947 break;
Pablo Neira Ayusocab6e752014-02-05 18:56:17 +0100948 case 'X':
949 if (strcmp("Osmux: on", line + 2) == 0)
950 osmux = 1;
951 break;
Harald Welteae199722012-01-27 12:31:36 +0100952 case 'Z':
953 silent = strcmp("noanswer", line + 3) == 0;
954 break;
955 case '\0':
956 /* SDP file begins */
Jacob Erlbeck17711712013-11-29 13:43:46 +0100957 parse_sdp_data(&endp->net_end, p);
958 /* This will exhaust p->save, so the loop will
959 * terminate next time.
960 */
Harald Welteae199722012-01-27 12:31:36 +0100961 break;
Harald Welteae199722012-01-27 12:31:36 +0100962 default:
Jacob Erlbeck17711712013-11-29 13:43:46 +0100963 LOGP(DMGCP, LOGL_NOTICE, "Unhandled MGCP option: '%c'/%d on 0x%x\n",
Harald Welteae199722012-01-27 12:31:36 +0100964 line[0], line[0], ENDPOINT_NUMBER(endp));
965 break;
966 }
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100967 }
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +0100968
Pablo Neira Ayusocab6e752014-02-05 18:56:17 +0100969 /* Re-enable Osmux if we receive a MDCX, we have to set up a new
970 * RTP flow: this generates a randomly allocated RTP SSRC and sequence
971 * number.
972 */
973 if (osmux) {
974 if (osmux_enable_endpoint(endp, OSMUX_ROLE_BSC) < 0) {
975 LOGP(DMGCP, LOGL_ERROR,
976 "Could not update osmux in endpoint %d\n",
977 ENDPOINT_NUMBER(endp));
978 }
979 LOGP(DMGCP, LOGL_NOTICE,
980 "Re-enabling osmux in endpoint %d, we got updated\n",
981 ENDPOINT_NUMBER(endp));
982 }
983
Jacob Erlbeck2c2ca4d2013-12-09 14:32:03 +0100984 set_local_cx_options(endp->tcfg->endpoints, &endp->local_options,
985 local_options);
986
Jacob Erlbecka0d64ce2014-03-13 14:24:52 +0100987 setup_rtp_processing(endp);
988
Holger Hans Peter Freytherfe86d3c2010-02-26 13:37:05 +0100989 /* policy CB */
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +0100990 if (p->cfg->policy_cb) {
991 int rc;
992 rc = p->cfg->policy_cb(endp->tcfg, ENDPOINT_NUMBER(endp),
993 MGCP_ENDP_MDCX, p->trans);
994 switch (rc) {
Holger Hans Peter Freytherfe86d3c2010-02-26 13:37:05 +0100995 case MGCP_POLICY_REJECT:
996 LOGP(DMGCP, LOGL_NOTICE, "MDCX rejected by policy on 0x%x\n",
997 ENDPOINT_NUMBER(endp));
Holger Hans Peter Freythere3d16bb2010-04-22 11:58:13 +0800998 if (silent)
999 goto out_silent;
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001000 return create_err_response(endp, 400, "MDCX", p->trans);
Holger Hans Peter Freytherfe86d3c2010-02-26 13:37:05 +01001001 break;
1002 case MGCP_POLICY_DEFER:
1003 /* stop processing */
1004 return NULL;
1005 break;
1006 case MGCP_POLICY_CONT:
1007 /* just continue */
1008 break;
1009 }
1010 }
1011
Jacob Erlbeckdb2d4312013-12-03 14:43:34 +01001012 mgcp_rtp_end_config(endp, 1, &endp->net_end);
1013 mgcp_rtp_end_config(endp, 1, &endp->bts_end);
1014
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001015 /* modify */
Holger Hans Peter Freythere378cb12010-08-06 20:22:23 +08001016 LOGP(DMGCP, LOGL_DEBUG, "Modified endpoint on: 0x%x Server: %s:%u\n",
Holger Hans Peter Freythera17d7012010-08-05 01:34:51 +08001017 ENDPOINT_NUMBER(endp), inet_ntoa(endp->net_end.addr), ntohs(endp->net_end.rtp_port));
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001018 if (p->cfg->change_cb)
1019 p->cfg->change_cb(endp->tcfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_MDCX);
Jacob Erlbeck075a9eb2013-12-19 18:53:07 +01001020
Jacob Erlbecke35fd132014-01-16 16:50:40 +01001021 if (endp->conn_mode & MGCP_CONN_RECV_ONLY &&
1022 endp->tcfg->keepalive_interval != 0)
Jacob Erlbeck075a9eb2013-12-19 18:53:07 +01001023 mgcp_send_dummy(endp);
1024
Holger Hans Peter Freythere3d16bb2010-04-22 11:58:13 +08001025 if (silent)
1026 goto out_silent;
1027
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001028 return create_response_with_sdp(endp, "MDCX", p->trans);
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001029
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001030error3:
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001031 return create_err_response(endp, error_code, "MDCX", p->trans);
Holger Hans Peter Freythere3d16bb2010-04-22 11:58:13 +08001032
1033
1034out_silent:
1035 return NULL;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001036}
1037
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001038static struct msgb *handle_delete_con(struct mgcp_parse_data *p)
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001039{
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001040 struct mgcp_endpoint *endp = p->endp;
Holger Hans Peter Freyther9e9392d2010-08-08 16:24:48 +08001041 int error_code = 400;
Holger Hans Peter Freythere3d16bb2010-04-22 11:58:13 +08001042 int silent = 0;
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001043 char *line;
Holger Hans Peter Freyther0bf15a82012-09-14 17:18:12 +02001044 char stats[1048];
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001045
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001046 if (p->found != 0)
1047 return create_err_response(NULL, error_code, "DLCX", p->trans);
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001048
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001049 if (!p->endp->allocated) {
1050 LOGP(DMGCP, LOGL_ERROR, "Endpoint is not used. 0x%x\n",
1051 ENDPOINT_NUMBER(endp));
1052 return create_err_response(endp, 400, "DLCX", p->trans);
1053 }
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001054
Jacob Erlbeck17711712013-11-29 13:43:46 +01001055 for_each_line(line, p->save) {
Holger Hans Peter Freyther1cc5ff82014-05-22 15:03:10 +02001056 if (!mgcp_check_param(endp, line))
1057 continue;
1058
Harald Welteae199722012-01-27 12:31:36 +01001059 switch (line[0]) {
1060 case 'C':
1061 if (verify_call_id(endp, line + 3) != 0)
1062 goto error3;
1063 break;
1064 case 'I':
1065 if (verify_ci(endp, line + 3) != 0)
1066 goto error3;
1067 break;
1068 case 'Z':
1069 silent = strcmp("noanswer", line + 3) == 0;
1070 break;
1071 default:
1072 LOGP(DMGCP, LOGL_NOTICE, "Unhandled option: '%c'/%d on 0x%x\n",
1073 line[0], line[0], ENDPOINT_NUMBER(endp));
1074 break;
1075 }
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001076 }
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001077
Holger Hans Peter Freytherfe86d3c2010-02-26 13:37:05 +01001078 /* policy CB */
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001079 if (p->cfg->policy_cb) {
1080 int rc;
1081 rc = p->cfg->policy_cb(endp->tcfg, ENDPOINT_NUMBER(endp),
1082 MGCP_ENDP_DLCX, p->trans);
1083 switch (rc) {
Holger Hans Peter Freytherfe86d3c2010-02-26 13:37:05 +01001084 case MGCP_POLICY_REJECT:
1085 LOGP(DMGCP, LOGL_NOTICE, "DLCX rejected by policy on 0x%x\n",
1086 ENDPOINT_NUMBER(endp));
Holger Hans Peter Freythere3d16bb2010-04-22 11:58:13 +08001087 if (silent)
1088 goto out_silent;
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001089 return create_err_response(endp, 400, "DLCX", p->trans);
Holger Hans Peter Freytherfe86d3c2010-02-26 13:37:05 +01001090 break;
1091 case MGCP_POLICY_DEFER:
1092 /* stop processing */
Holger Hans Peter Freyther3b5e3c42010-09-18 06:35:29 +08001093 delete_transcoder(endp);
Holger Hans Peter Freytherfe86d3c2010-02-26 13:37:05 +01001094 return NULL;
1095 break;
1096 case MGCP_POLICY_CONT:
1097 /* just continue */
1098 break;
1099 }
1100 }
1101
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001102 /* free the connection */
Holger Hans Peter Freythere378cb12010-08-06 20:22:23 +08001103 LOGP(DMGCP, LOGL_DEBUG, "Deleted endpoint on: 0x%x Server: %s:%u\n",
Holger Hans Peter Freythera17d7012010-08-05 01:34:51 +08001104 ENDPOINT_NUMBER(endp), inet_ntoa(endp->net_end.addr), ntohs(endp->net_end.rtp_port));
Holger Hans Peter Freyther3b5e3c42010-09-18 06:35:29 +08001105
Holger Hans Peter Freyther0bf15a82012-09-14 17:18:12 +02001106 /* save the statistics of the current call */
1107 mgcp_format_stats(endp, stats, sizeof(stats));
1108
Holger Hans Peter Freyther3b5e3c42010-09-18 06:35:29 +08001109 delete_transcoder(endp);
Holger Hans Peter Freyther154b9552010-02-26 13:27:51 +01001110 mgcp_free_endp(endp);
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001111 if (p->cfg->change_cb)
1112 p->cfg->change_cb(endp->tcfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_DLCX);
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001113
Holger Hans Peter Freythere3d16bb2010-04-22 11:58:13 +08001114 if (silent)
1115 goto out_silent;
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001116 return create_ok_resp_with_param(endp, 250, "DLCX", p->trans, stats);
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001117
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001118error3:
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001119 return create_err_response(endp, error_code, "DLCX", p->trans);
Holger Hans Peter Freythere3d16bb2010-04-22 11:58:13 +08001120
1121out_silent:
1122 return NULL;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001123}
1124
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001125static struct msgb *handle_rsip(struct mgcp_parse_data *p)
Holger Hans Peter Freyther9bdcc9c2010-03-31 06:39:35 +02001126{
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001127 if (p->found != 0) {
Holger Hans Peter Freyther74db7742011-06-10 00:04:55 +02001128 LOGP(DMGCP, LOGL_ERROR, "Failed to find the endpoint.\n");
1129 return NULL;
1130 }
1131
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001132 if (p->cfg->reset_cb)
1133 p->cfg->reset_cb(p->endp->tcfg);
Holger Hans Peter Freyther9bdcc9c2010-03-31 06:39:35 +02001134 return NULL;
1135}
1136
Holger Hans Peter Freyther8d0be252012-11-29 12:54:22 +01001137static char extract_tone(const char *line)
1138{
1139 const char *str = strstr(line, "D/");
1140 if (!str)
1141 return CHAR_MAX;
1142
1143 return str[2];
1144}
1145
Holger Hans Peter Freyther6e94d6d2011-01-25 23:33:54 +01001146/*
1147 * This can request like DTMF detection and forward, fax detection... it
1148 * can also request when the notification should be send and such. We don't
1149 * do this right now.
1150 */
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001151static struct msgb *handle_noti_req(struct mgcp_parse_data *p)
Holger Hans Peter Freyther6e94d6d2011-01-25 23:33:54 +01001152{
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001153 int res = 0;
1154 char *line;
Holger Hans Peter Freyther9f3835b2013-05-21 17:04:35 +02001155 char tone = CHAR_MAX;
Holger Hans Peter Freyther6e94d6d2011-01-25 23:33:54 +01001156
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001157 if (p->found != 0)
1158 return create_err_response(NULL, 400, "RQNT", p->trans);
Holger Hans Peter Freyther6e94d6d2011-01-25 23:33:54 +01001159
Jacob Erlbeck17711712013-11-29 13:43:46 +01001160 for_each_line(line, p->save) {
Holger Hans Peter Freyther8d0be252012-11-29 12:54:22 +01001161 switch (line[0]) {
1162 case 'S':
1163 tone = extract_tone(line);
1164 break;
1165 }
Holger Hans Peter Freyther6e94d6d2011-01-25 23:33:54 +01001166 }
Holger Hans Peter Freyther8d0be252012-11-29 12:54:22 +01001167
1168 /* we didn't see a signal request with a tone */
1169 if (tone == CHAR_MAX)
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001170 return create_ok_response(p->endp, 200, "RQNT", p->trans);
Holger Hans Peter Freyther8d0be252012-11-29 12:54:22 +01001171
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001172 if (p->cfg->rqnt_cb)
1173 res = p->cfg->rqnt_cb(p->endp, tone);
Holger Hans Peter Freyther8d0be252012-11-29 12:54:22 +01001174
1175 return res == 0 ?
Holger Hans Peter Freytherce553612012-12-07 15:04:07 +01001176 create_ok_response(p->endp, 200, "RQNT", p->trans) :
1177 create_err_response(p->endp, res, "RQNT", p->trans);
Holger Hans Peter Freyther6e94d6d2011-01-25 23:33:54 +01001178}
1179
Jacob Erlbeck075a9eb2013-12-19 18:53:07 +01001180static void mgcp_keepalive_timer_cb(void *_tcfg)
1181{
1182 struct mgcp_trunk_config *tcfg = _tcfg;
1183 int i;
1184 LOGP(DMGCP, LOGL_DEBUG, "Triggered trunk %d keepalive timer.\n",
1185 tcfg->trunk_nr);
1186
1187 if (tcfg->keepalive_interval <= 0)
1188 return;
1189
1190 for (i = 1; i < tcfg->number_endpoints; ++i) {
1191 struct mgcp_endpoint *endp = &tcfg->endpoints[i];
1192 if (endp->conn_mode == MGCP_CONN_RECV_ONLY)
1193 mgcp_send_dummy(endp);
1194 }
1195
1196 LOGP(DMGCP, LOGL_DEBUG, "Rescheduling trunk %d keepalive timer.\n",
1197 tcfg->trunk_nr);
1198 osmo_timer_schedule(&tcfg->keepalive_timer, tcfg->keepalive_interval, 0);
1199}
1200
1201void mgcp_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval)
1202{
1203 tcfg->keepalive_interval = interval;
1204 tcfg->keepalive_timer.data = tcfg;
1205 tcfg->keepalive_timer.cb = mgcp_keepalive_timer_cb;
1206
1207 if (interval <= 0)
1208 osmo_timer_del(&tcfg->keepalive_timer);
1209 else
1210 osmo_timer_schedule(&tcfg->keepalive_timer,
1211 tcfg->keepalive_interval, 0);
1212}
1213
Holger Hans Peter Freyther7bdc6372010-02-20 21:21:02 +01001214struct mgcp_config *mgcp_config_alloc(void)
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001215{
Holger Hans Peter Freyther7bdc6372010-02-20 21:21:02 +01001216 struct mgcp_config *cfg;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001217
Holger Hans Peter Freyther7bdc6372010-02-20 21:21:02 +01001218 cfg = talloc_zero(NULL, struct mgcp_config);
1219 if (!cfg) {
1220 LOGP(DMGCP, LOGL_FATAL, "Failed to allocate config.\n");
1221 return NULL;
1222 }
1223
1224 cfg->source_port = 2427;
1225 cfg->source_addr = talloc_strdup(cfg, "0.0.0.0");
Holger Hans Peter Freyther88ad7722011-02-28 00:56:17 +01001226
Holger Hans Peter Freytherb98ba722010-09-19 04:21:39 +08001227 cfg->transcoder_remote_base = 4000;
Holger Hans Peter Freyther15e73892010-08-05 07:10:56 +08001228
1229 cfg->bts_ports.base_port = RTP_PORT_DEFAULT;
1230 cfg->net_ports.base_port = RTP_PORT_NET_DEFAULT;
Holger Hans Peter Freyther7bdc6372010-02-20 21:21:02 +01001231
Jacob Erlbecka0d64ce2014-03-13 14:24:52 +01001232 cfg->rtp_processing_cb = &mgcp_rtp_processing_default;
1233 cfg->setup_rtp_processing_cb = &mgcp_setup_rtp_processing_default;
1234
Holger Hans Peter Freyther88ad7722011-02-28 00:56:17 +01001235 /* default trunk handling */
1236 cfg->trunk.cfg = cfg;
1237 cfg->trunk.trunk_nr = 0;
1238 cfg->trunk.trunk_type = MGCP_TRUNK_VIRTUAL;
1239 cfg->trunk.audio_name = talloc_strdup(cfg, "AMR/8000");
1240 cfg->trunk.audio_payload = 126;
Jacob Erlbeck0a1bc562013-12-10 13:09:37 +01001241 cfg->trunk.audio_send_ptime = 1;
Holger Hans Peter Freythera8090d52012-05-11 13:00:45 +02001242 cfg->trunk.omit_rtcp = 0;
Jacob Erlbeck075a9eb2013-12-19 18:53:07 +01001243 mgcp_trunk_set_keepalive(&cfg->trunk, MGCP_KEEPALIVE_ONCE);
Holger Hans Peter Freyther88ad7722011-02-28 00:56:17 +01001244
Holger Hans Peter Freyther0e939fe2011-02-28 12:11:02 +01001245 INIT_LLIST_HEAD(&cfg->trunks);
1246
Holger Hans Peter Freyther7bdc6372010-02-20 21:21:02 +01001247 return cfg;
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001248}
1249
Holger Hans Peter Freyther0e939fe2011-02-28 12:11:02 +01001250struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int nr)
1251{
1252 struct mgcp_trunk_config *trunk;
1253
1254 trunk = talloc_zero(cfg, struct mgcp_trunk_config);
1255 if (!trunk) {
1256 LOGP(DMGCP, LOGL_ERROR, "Failed to allocate.\n");
1257 return NULL;
1258 }
1259
1260 trunk->cfg = cfg;
1261 trunk->trunk_type = MGCP_TRUNK_E1;
1262 trunk->trunk_nr = nr;
1263 trunk->audio_name = talloc_strdup(cfg, "AMR/8000");
1264 trunk->audio_payload = 126;
Jacob Erlbeck0a1bc562013-12-10 13:09:37 +01001265 trunk->audio_send_ptime = 1;
Holger Hans Peter Freyther0e939fe2011-02-28 12:11:02 +01001266 trunk->number_endpoints = 33;
Holger Hans Peter Freythera8090d52012-05-11 13:00:45 +02001267 trunk->omit_rtcp = 0;
Jacob Erlbeck075a9eb2013-12-19 18:53:07 +01001268 mgcp_trunk_set_keepalive(trunk, MGCP_KEEPALIVE_ONCE);
Holger Hans Peter Freyther0e939fe2011-02-28 12:11:02 +01001269 llist_add_tail(&trunk->entry, &cfg->trunks);
1270 return trunk;
1271}
1272
1273struct mgcp_trunk_config *mgcp_trunk_num(struct mgcp_config *cfg, int index)
1274{
1275 struct mgcp_trunk_config *trunk;
1276
1277 llist_for_each_entry(trunk, &cfg->trunks, entry)
1278 if (trunk->trunk_nr == index)
1279 return trunk;
1280
1281 return NULL;
1282}
1283
Holger Hans Peter Freythera17d7012010-08-05 01:34:51 +08001284static void mgcp_rtp_end_reset(struct mgcp_rtp_end *end)
1285{
Holger Hans Peter Freythere97155a2010-11-02 19:06:13 +01001286 if (end->local_alloc == PORT_ALLOC_DYNAMIC) {
Holger Hans Peter Freytherf138f912010-08-05 08:08:17 +08001287 mgcp_free_rtp_port(end);
Holger Hans Peter Freythere97155a2010-11-02 19:06:13 +01001288 end->local_port = 0;
1289 }
Holger Hans Peter Freytherf138f912010-08-05 08:08:17 +08001290
Holger Hans Peter Freytherc4921272010-08-05 03:37:22 +08001291 end->packets = 0;
Holger Hans Peter Freyther952f7522012-09-12 11:38:37 +02001292 end->octets = 0;
Jacob Erlbeck0970bab2013-12-19 12:13:32 +01001293 end->dropped_packets = 0;
Holger Hans Peter Freytherc4921272010-08-05 03:37:22 +08001294 memset(&end->addr, 0, sizeof(end->addr));
Holger Hans Peter Freythere97155a2010-11-02 19:06:13 +01001295 end->rtp_port = end->rtcp_port = 0;
Holger Hans Peter Freythera17d7012010-08-05 01:34:51 +08001296 end->payload_type = -1;
Holger Hans Peter Freytherf138f912010-08-05 08:08:17 +08001297 end->local_alloc = -1;
Holger Hans Peter Freyther5ea1bc72012-09-03 00:07:39 +02001298 talloc_free(end->fmtp_extra);
1299 end->fmtp_extra = NULL;
Jacob Erlbecka0d64ce2014-03-13 14:24:52 +01001300 talloc_free(end->rtp_process_data);
1301 end->rtp_process_data = NULL;
Jacob Erlbeck72c30902013-11-25 15:23:35 +01001302
1303 /* Set default values */
1304 end->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
1305 end->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
Jacob Erlbeckf6ec0e92013-12-04 10:30:11 +01001306 end->frames_per_packet = 0; /* unknown */
1307 end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS;
1308 end->rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE;
Jacob Erlbeck8857c902014-01-16 16:50:39 +01001309 end->output_enabled = 0;
Holger Hans Peter Freythera17d7012010-08-05 01:34:51 +08001310}
1311
Holger Hans Peter Freytherc4921272010-08-05 03:37:22 +08001312static void mgcp_rtp_end_init(struct mgcp_rtp_end *end)
1313{
1314 mgcp_rtp_end_reset(end);
1315 end->rtp.fd = -1;
1316 end->rtcp.fd = -1;
1317}
1318
Holger Hans Peter Freyther1f0c5b42011-02-28 14:37:03 +01001319int mgcp_endpoints_allocate(struct mgcp_trunk_config *tcfg)
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001320{
1321 int i;
1322
Holger Hans Peter Freyther7bdc6372010-02-20 21:21:02 +01001323 /* Initialize all endpoints */
Holger Hans Peter Freyther1f0c5b42011-02-28 14:37:03 +01001324 tcfg->endpoints = _talloc_zero_array(tcfg->cfg,
Holger Hans Peter Freyther7bdc6372010-02-20 21:21:02 +01001325 sizeof(struct mgcp_endpoint),
Holger Hans Peter Freyther88ad7722011-02-28 00:56:17 +01001326 tcfg->number_endpoints, "endpoints");
1327 if (!tcfg->endpoints)
Holger Hans Peter Freyther7bdc6372010-02-20 21:21:02 +01001328 return -1;
1329
Holger Hans Peter Freyther88ad7722011-02-28 00:56:17 +01001330 for (i = 0; i < tcfg->number_endpoints; ++i) {
1331 tcfg->endpoints[i].ci = CI_UNUSED;
Holger Hans Peter Freyther1f0c5b42011-02-28 14:37:03 +01001332 tcfg->endpoints[i].cfg = tcfg->cfg;
Holger Hans Peter Freyther88ad7722011-02-28 00:56:17 +01001333 tcfg->endpoints[i].tcfg = tcfg;
1334 mgcp_rtp_end_init(&tcfg->endpoints[i].net_end);
1335 mgcp_rtp_end_init(&tcfg->endpoints[i].bts_end);
1336 mgcp_rtp_end_init(&tcfg->endpoints[i].trans_net);
1337 mgcp_rtp_end_init(&tcfg->endpoints[i].trans_bts);
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001338 }
1339
Holger Hans Peter Freythere0955022010-02-03 08:50:33 +01001340 return 0;
1341}
Holger Hans Peter Freyther154b9552010-02-26 13:27:51 +01001342
1343void mgcp_free_endp(struct mgcp_endpoint *endp)
1344{
Holger Hans Peter Freytherc77efdf2010-03-31 12:31:09 +02001345 LOGP(DMGCP, LOGL_DEBUG, "Deleting endpoint on: 0x%x\n", ENDPOINT_NUMBER(endp));
Holger Hans Peter Freyther89976e82010-08-03 15:01:43 +00001346 endp->ci = CI_UNUSED;
Holger Hans Peter Freyther39a97e22010-08-06 18:03:11 +08001347 endp->allocated = 0;
Holger Hans Peter Freyther154b9552010-02-26 13:27:51 +01001348
Holger Hans Peter Freyther01d315f2012-12-07 11:52:16 +01001349 talloc_free(endp->callid);
1350 endp->callid = NULL;
Holger Hans Peter Freyther154b9552010-02-26 13:27:51 +01001351
Jacob Erlbeck2c2ca4d2013-12-09 14:32:03 +01001352 talloc_free(endp->local_options.string);
1353 endp->local_options.string = NULL;
Holger Hans Peter Freyther154b9552010-02-26 13:27:51 +01001354
Holger Hans Peter Freythera17d7012010-08-05 01:34:51 +08001355 mgcp_rtp_end_reset(&endp->bts_end);
1356 mgcp_rtp_end_reset(&endp->net_end);
Holger Hans Peter Freyther21262332010-11-01 20:53:31 +01001357 mgcp_rtp_end_reset(&endp->trans_net);
Holger Hans Peter Freytherbd7b3c52010-11-01 21:04:54 +01001358 mgcp_rtp_end_reset(&endp->trans_bts);
Pablo Neira Ayuso46bd4242013-07-08 05:09:46 +02001359 endp->type = MGCP_RTP_DEFAULT;
Holger Hans Peter Freyther380b8712010-07-29 02:38:39 +08001360
Holger Hans Peter Freyther31868922010-08-03 11:59:04 +00001361 memset(&endp->net_state, 0, sizeof(endp->net_state));
1362 memset(&endp->bts_state, 0, sizeof(endp->bts_state));
Holger Hans Peter Freytherc597a4e2010-08-03 02:57:02 +08001363
1364 endp->conn_mode = endp->orig_mode = MGCP_CONN_NONE;
Holger Hans Peter Freyther260d6ed2010-08-06 01:12:21 +08001365
1366 memset(&endp->taps, 0, sizeof(endp->taps));
Holger Hans Peter Freyther154b9552010-02-26 13:27:51 +01001367}
Holger Hans Peter Freyther3b5e3c42010-09-18 06:35:29 +08001368
Holger Hans Peter Freyther557b1ab2010-09-19 04:39:55 +08001369static int send_trans(struct mgcp_config *cfg, const char *buf, int len)
1370{
1371 struct sockaddr_in addr;
1372
1373 memset(&addr, 0, sizeof(addr));
1374 addr.sin_family = AF_INET;
1375 addr.sin_addr = cfg->transcoder_in;
1376 addr.sin_port = htons(2427);
1377 return sendto(cfg->gw_fd.bfd.fd, buf, len, 0,
1378 (struct sockaddr *) &addr, sizeof(addr));
1379}
1380
Holger Hans Peter Freyther3b5e3c42010-09-18 06:35:29 +08001381static void send_msg(struct mgcp_endpoint *endp, int endpoint, int port,
1382 const char *msg, const char *mode)
1383{
Holger Hans Peter Freyther3b5e3c42010-09-18 06:35:29 +08001384 char buf[2096];
1385 int len;
1386
1387 /* hardcoded to AMR right now, we do not know the real type at this point */
1388 len = snprintf(buf, sizeof(buf),
1389 "%s 42 %x@mgw MGCP 1.0\r\n"
1390 "C: 4256\r\n"
1391 "M: %s\r\n"
1392 "\r\n"
1393 "c=IN IP4 %s\r\n"
1394 "m=audio %d RTP/AVP %d\r\n"
1395 "a=rtpmap:%d %s\r\n",
1396 msg, endpoint, mode, endp->cfg->source_addr,
Holger Hans Peter Freyther88ad7722011-02-28 00:56:17 +01001397 port, endp->tcfg->audio_payload,
1398 endp->tcfg->audio_payload, endp->tcfg->audio_name);
Holger Hans Peter Freyther3b5e3c42010-09-18 06:35:29 +08001399
1400 if (len < 0)
1401 return;
1402
1403 buf[sizeof(buf) - 1] = '\0';
1404
Holger Hans Peter Freyther557b1ab2010-09-19 04:39:55 +08001405 send_trans(endp->cfg, buf, len);
Holger Hans Peter Freyther3b5e3c42010-09-18 06:35:29 +08001406}
1407
1408static void send_dlcx(struct mgcp_endpoint *endp, int endpoint)
1409{
Holger Hans Peter Freyther3b5e3c42010-09-18 06:35:29 +08001410 char buf[2096];
1411 int len;
1412
1413 len = snprintf(buf, sizeof(buf),
1414 "DLCX 43 %x@mgw MGCP 1.0\r\n"
1415 "C: 4256\r\n"
1416 , endpoint);
1417
1418 if (len < 0)
1419 return;
1420
1421 buf[sizeof(buf) - 1] = '\0';
1422
Holger Hans Peter Freyther557b1ab2010-09-19 04:39:55 +08001423 send_trans(endp->cfg, buf, len);
Holger Hans Peter Freyther3b5e3c42010-09-18 06:35:29 +08001424}
1425
Harald Welte6a857052012-01-27 00:41:39 +01001426static int send_agent(struct mgcp_config *cfg, const char *buf, int len)
1427{
1428 return write(cfg->gw_fd.bfd.fd, buf, len);
1429}
1430
1431int mgcp_send_reset_all(struct mgcp_config *cfg)
1432{
1433 static const char mgcp_reset[] = {
1434 "RSIP 1 *@mgw MGCP 1.0\r\n"
1435 };
1436
1437 return send_agent(cfg, mgcp_reset, sizeof mgcp_reset -1);
1438}
1439
1440int mgcp_send_reset_ep(struct mgcp_endpoint *endp, int endpoint)
1441{
1442 char buf[128];
1443 int len;
1444
1445 len = snprintf(buf, sizeof(buf),
1446 "RSIP 39 %x@mgw MGCP 1.0\r\n"
1447 , endpoint);
1448 if (len < 0)
1449 return len;
1450
1451 buf[sizeof(buf) - 1] = '\0';
1452
1453 return send_agent(endp->cfg, buf, len);
1454}
1455
Jacob Erlbecka0d64ce2014-03-13 14:24:52 +01001456static void setup_rtp_processing(struct mgcp_endpoint *endp)
1457{
1458 struct mgcp_config *cfg = endp->cfg;
1459
1460 if (endp->type != MGCP_RTP_DEFAULT)
1461 return;
1462
1463 if (endp->conn_mode == MGCP_CONN_LOOPBACK)
1464 return;
1465
1466 if (endp->conn_mode & MGCP_CONN_SEND_ONLY)
1467 cfg->setup_rtp_processing_cb(endp, &endp->net_end, &endp->bts_end);
1468 else
1469 cfg->setup_rtp_processing_cb(endp, &endp->net_end, NULL);
1470
1471 if (endp->conn_mode & MGCP_CONN_RECV_ONLY)
1472 cfg->setup_rtp_processing_cb(endp, &endp->bts_end, &endp->net_end);
1473 else
1474 cfg->setup_rtp_processing_cb(endp, &endp->bts_end, NULL);
1475}
1476
Holger Hans Peter Freyther3b5e3c42010-09-18 06:35:29 +08001477static void create_transcoder(struct mgcp_endpoint *endp)
1478{
1479 int port;
1480 int in_endp = ENDPOINT_NUMBER(endp);
Holger Hans Peter Freytherbd7b3c52010-11-01 21:04:54 +01001481 int out_endp = endp_back_channel(in_endp);
Holger Hans Peter Freyther3b5e3c42010-09-18 06:35:29 +08001482
Pablo Neira Ayuso46bd4242013-07-08 05:09:46 +02001483 if (endp->type != MGCP_RTP_TRANSCODED)
Holger Hans Peter Freyther3b5e3c42010-09-18 06:35:29 +08001484 return;
1485
Holger Hans Peter Freyther8b19dee2010-11-01 22:38:25 +01001486 send_msg(endp, in_endp, endp->trans_bts.local_port, "CRCX", "sendrecv");
1487 send_msg(endp, in_endp, endp->trans_bts.local_port, "MDCX", "sendrecv");
Holger Hans Peter Freyther21262332010-11-01 20:53:31 +01001488 send_msg(endp, out_endp, endp->trans_net.local_port, "CRCX", "sendrecv");
1489 send_msg(endp, out_endp, endp->trans_net.local_port, "MDCX", "sendrecv");
Holger Hans Peter Freyther3b5e3c42010-09-18 06:35:29 +08001490
Holger Hans Peter Freytherbd7b3c52010-11-01 21:04:54 +01001491 port = rtp_calculate_port(in_endp, endp->cfg->transcoder_remote_base);
1492 endp->trans_bts.rtp_port = htons(port);
1493 endp->trans_bts.rtcp_port = htons(port + 1);
1494
Holger Hans Peter Freytherb98ba722010-09-19 04:21:39 +08001495 port = rtp_calculate_port(out_endp, endp->cfg->transcoder_remote_base);
Holger Hans Peter Freyther21262332010-11-01 20:53:31 +01001496 endp->trans_net.rtp_port = htons(port);
1497 endp->trans_net.rtcp_port = htons(port + 1);
Holger Hans Peter Freyther3b5e3c42010-09-18 06:35:29 +08001498}
1499
1500static void delete_transcoder(struct mgcp_endpoint *endp)
1501{
1502 int in_endp = ENDPOINT_NUMBER(endp);
Holger Hans Peter Freytherbd7b3c52010-11-01 21:04:54 +01001503 int out_endp = endp_back_channel(in_endp);
Holger Hans Peter Freyther3b5e3c42010-09-18 06:35:29 +08001504
Pablo Neira Ayuso46bd4242013-07-08 05:09:46 +02001505 if (endp->type != MGCP_RTP_TRANSCODED)
Holger Hans Peter Freyther3b5e3c42010-09-18 06:35:29 +08001506 return;
1507
1508 send_dlcx(endp, in_endp);
1509 send_dlcx(endp, out_endp);
1510}
Holger Hans Peter Freytherf2eedff2010-09-19 04:36:07 +08001511
1512int mgcp_reset_transcoder(struct mgcp_config *cfg)
1513{
Holger Hans Peter Freytherf2eedff2010-09-19 04:36:07 +08001514 if (!cfg->transcoder_ip)
Holger Hans Peter Freythercf1c8772010-09-24 04:41:28 +08001515 return 0;
Holger Hans Peter Freytherf2eedff2010-09-19 04:36:07 +08001516
1517 static const char mgcp_reset[] = {
1518 "RSIP 1 13@mgw MGCP 1.0\r\n"
1519 };
1520
Holger Hans Peter Freyther557b1ab2010-09-19 04:39:55 +08001521 return send_trans(cfg, mgcp_reset, sizeof mgcp_reset -1);
Holger Hans Peter Freytherf2eedff2010-09-19 04:36:07 +08001522}
Holger Hans Peter Freyther0bf15a82012-09-14 17:18:12 +02001523
1524void mgcp_format_stats(struct mgcp_endpoint *endp, char *msg, size_t size)
1525{
Holger Hans Peter Freythercb306a62012-10-24 21:22:47 +02001526 uint32_t expected, jitter;
Holger Hans Peter Freyther38e02c52012-10-22 18:09:35 +02001527 int ploss;
Jacob Erlbeck50079a12013-11-25 12:53:28 +01001528 int nchars;
Holger Hans Peter Freyther38e02c52012-10-22 18:09:35 +02001529 mgcp_state_calc_loss(&endp->net_state, &endp->net_end,
1530 &expected, &ploss);
Holger Hans Peter Freythercb306a62012-10-24 21:22:47 +02001531 jitter = mgcp_state_calc_jitter(&endp->net_state);
Holger Hans Peter Freyther38e02c52012-10-22 18:09:35 +02001532
Jacob Erlbeck50079a12013-11-25 12:53:28 +01001533 nchars = snprintf(msg, size,
1534 "\r\nP: PS=%u, OS=%u, PR=%u, OR=%u, PL=%d, JI=%u",
1535 endp->bts_end.packets, endp->bts_end.octets,
1536 endp->net_end.packets, endp->net_end.octets,
1537 ploss, jitter);
1538 if (nchars < 0 || nchars >= size)
1539 goto truncate;
1540
1541 msg += nchars;
1542 size -= nchars;
1543
1544 /* Error Counter */
1545 snprintf(msg, size,
1546 "\r\nX-Osmo-CP: EC TIS=%u, TOS=%u, TIR=%u, TOR=%u",
1547 endp->net_state.in_stream.err_ts_counter,
1548 endp->net_state.out_stream.err_ts_counter,
1549 endp->bts_state.in_stream.err_ts_counter,
1550 endp->bts_state.out_stream.err_ts_counter);
1551truncate:
Holger Hans Peter Freyther0bf15a82012-09-14 17:18:12 +02001552 msg[size - 1] = '\0';
1553}
Holger Hans Peter Freyther462b7d72012-10-24 21:53:40 +02001554
1555int mgcp_parse_stats(struct msgb *msg, uint32_t *ps, uint32_t *os,
1556 uint32_t *pr, uint32_t *_or, int *loss, uint32_t *jitter)
1557{
1558 char *line, *save;
1559 int rc;
1560
1561 /* initialize with bad values */
1562 *ps = *os = *pr = *_or = *jitter = UINT_MAX;
1563 *loss = INT_MAX;
1564
1565
1566 line = strtok_r((char *) msg->l2h, "\r\n", &save);
1567 if (!line)
1568 return -1;
1569
1570 /* this can only parse the message that is created above... */
Jacob Erlbecka01bd602013-11-29 13:43:43 +01001571 for_each_non_empty_line(line, save) {
Holger Hans Peter Freyther462b7d72012-10-24 21:53:40 +02001572 switch (line[0]) {
1573 case 'P':
1574 rc = sscanf(line, "P: PS=%u, OS=%u, PR=%u, OR=%u, PL=%d, JI=%u",
1575 ps, os, pr, _or, loss, jitter);
1576 return rc == 6 ? 0 : -1;
1577 }
1578 }
1579
1580 return -1;
1581}