blob: 03e68169482816252008b4a57193bfcccb88b50c [file] [log] [blame]
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +08001/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
2/* The protocol implementation */
3
4/*
5 * (C) 2009-2010 by Holger Hans Peter Freyther <zecke@selfish.org>
6 * (C) 2009-2010 by On-Waves
7 * All Rights Reserved
8 *
Holger Hans Peter Freytherde56c222011-01-16 17:45:14 +01009 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU Affero General Public License as published by
11 * the Free Software Foundation, either version 3 of the License, or
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080012 * (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
Holger Hans Peter Freytherde56c222011-01-16 17:45:14 +010017 * GNU Affero General Public License for more details.
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080018 *
Holger Hans Peter Freytherde56c222011-01-16 17:45:14 +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 Freyther97f66e22010-07-28 03:32:52 +080021 *
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
Holger Hans Peter Freythercbf7d182010-07-31 05:25:35 +080032#include <cellmgr_debug.h>
33
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080034#include <mgcp/mgcp.h>
35#include <mgcp/mgcp_internal.h>
36
Holger Hans Peter Freythercbf7d182010-07-31 05:25:35 +080037#include <osmocore/msgb.h>
38#include <osmocore/talloc.h>
39#include <osmocore/select.h>
40#include <osmocore/utils.h>
41
42
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080043/**
44 * Macro for tokenizing MGCP messages and SDP in one go.
45 *
46 */
47#define MSG_TOKENIZE_START \
48 line_start = 0; \
49 for (i = 0; i < msgb_l3len(msg); ++i) { \
50 /* we have a line end */ \
51 if (msg->l3h[i] == '\n') { \
52 /* skip the first line */ \
53 if (line_start == 0) { \
54 line_start = i + 1; \
55 continue; \
56 } \
57 \
58 /* check if we have a proper param */ \
59 if (i - line_start == 1 && msg->l3h[line_start] == '\r') { \
60 } else if (i - line_start > 2 \
61 && islower(msg->l3h[line_start]) \
62 && msg->l3h[line_start + 1] == '=') { \
63 } else if (i - line_start < 3 \
64 || msg->l3h[line_start + 1] != ':' \
65 || msg->l3h[line_start + 2] != ' ') \
66 goto error; \
67 \
68 msg->l3h[i] = '\0'; \
69 if (msg->l3h[i-1] == '\r') \
70 msg->l3h[i-1] = '\0';
71
72#define MSG_TOKENIZE_END \
73 line_start = i + 1; \
74 } \
75 }
76
77
78struct mgcp_request {
79 char *name;
80 struct msgb *(*handle_request) (struct mgcp_config *cfg, struct msgb *msg);
81 char *debug_name;
82};
83
84#define MGCP_REQUEST(NAME, REQ, DEBUG_NAME) \
85 { .name = NAME, .handle_request = REQ, .debug_name = DEBUG_NAME },
86
87static struct msgb *handle_audit_endpoint(struct mgcp_config *cfg, struct msgb *msg);
88static struct msgb *handle_create_con(struct mgcp_config *cfg, struct msgb *msg);
89static struct msgb *handle_delete_con(struct mgcp_config *cfg, struct msgb *msg);
90static struct msgb *handle_modify_con(struct mgcp_config *cfg, struct msgb *msg);
91static struct msgb *handle_rsip(struct mgcp_config *cfg, struct msgb *msg);
92
Holger Hans Peter Freyther5b084012010-08-08 07:51:51 +080093static uint32_t generate_call_id(struct mgcp_config *cfg)
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080094{
95 int i;
96
97 /* use the call id */
98 ++cfg->last_call_id;
99
100 /* handle wrap around */
101 if (cfg->last_call_id == CI_UNUSED)
102 ++cfg->last_call_id;
103
104 /* callstack can only be of size number_of_endpoints */
105 /* verify that the call id is free, e.g. in case of overrun */
106 for (i = 1; i < cfg->number_endpoints; ++i)
107 if (cfg->endpoints[i].ci == cfg->last_call_id)
108 return generate_call_id(cfg);
109
110 return cfg->last_call_id;
111}
112
113/*
114 * array of function pointers for handling various
115 * messages. In the future this might be binary sorted
116 * for performance reasons.
117 */
118static const struct mgcp_request mgcp_requests [] = {
119 MGCP_REQUEST("AUEP", handle_audit_endpoint, "AuditEndpoint")
120 MGCP_REQUEST("CRCX", handle_create_con, "CreateConnection")
121 MGCP_REQUEST("DLCX", handle_delete_con, "DeleteConnection")
122 MGCP_REQUEST("MDCX", handle_modify_con, "ModifiyConnection")
123
124 /* SPEC extension */
125 MGCP_REQUEST("RSIP", handle_rsip, "ReSetInProgress")
126};
127
128static struct msgb *mgcp_msgb_alloc(void)
129{
130 struct msgb *msg;
131 msg = msgb_alloc_headroom(4096, 128, "MGCP msg");
132 if (!msg)
133 LOGP(DMGCP, LOGL_ERROR, "Failed to msgb for MGCP data.\n");
134
135 return msg;
136}
137
Holger Hans Peter Freyther7e0936e2011-01-06 19:48:55 +0100138struct msgb *mgcp_create_response_with_data(int code, const char *txt,
139 const char *msg, const char *trans,
140 const char *data)
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800141{
142 int len;
143 struct msgb *res;
144
145 res = mgcp_msgb_alloc();
146 if (!res)
147 return NULL;
148
149 if (data) {
Holger Hans Peter Freyther7e0936e2011-01-06 19:48:55 +0100150 len = snprintf((char *) res->data, 2048, "%d %s%s\r\n%s", code, trans, txt, data);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800151 } else {
Holger Hans Peter Freyther7e0936e2011-01-06 19:48:55 +0100152 len = snprintf((char *) res->data, 2048, "%d %s%s\r\n", code, trans, txt);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800153 }
154
155 res->l2h = msgb_put(res, len);
156 LOGP(DMGCP, LOGL_DEBUG, "Sending response: code: %d for '%s'\n", code, res->l2h);
157 return res;
158}
159
Holger Hans Peter Freyther7e0936e2011-01-06 19:48:55 +0100160static struct msgb *create_ok_response(int code, const char *msg, const char *trans)
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800161{
Holger Hans Peter Freyther7e0936e2011-01-06 19:48:55 +0100162 return mgcp_create_response_with_data(code, " OK", msg, trans, NULL);
163}
164
165static struct msgb *create_err_response(int code, const char *msg, const char *trans)
166{
167 return mgcp_create_response_with_data(code, " FAIL", msg, trans, NULL);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800168}
169
170static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
171 const char *msg, const char *trans_id)
172{
173 const char *addr = endp->cfg->local_ip;
174 char sdp_record[4096];
175
176 if (!addr)
177 addr = endp->cfg->source_addr;
178
179 snprintf(sdp_record, sizeof(sdp_record) - 1,
Holger Hans Peter Freyther5b084012010-08-08 07:51:51 +0800180 "I: %u\n\n"
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800181 "v=0\r\n"
182 "c=IN IP4 %s\r\n"
183 "m=audio %d RTP/AVP %d\r\n"
184 "a=rtpmap:%d %s\r\n",
185 endp->ci, addr, endp->rtp_port,
186 endp->bts_payload_type, endp->bts_payload_type,
187 endp->cfg->audio_name);
Holger Hans Peter Freyther7e0936e2011-01-06 19:48:55 +0100188 return mgcp_create_response_with_data(200, " OK", msg, trans_id, sdp_record);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800189}
190
191/*
192 * handle incoming messages:
193 * - this can be a command (four letters, space, transaction id)
194 * - or a response (three numbers, space, transaction id)
195 */
196struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
197{
198 int code;
199 struct msgb *resp = NULL;
200
201 if (msgb_l2len(msg) < 4) {
202 LOGP(DMGCP, LOGL_ERROR, "mgs too short: %d\n", msg->len);
203 return NULL;
204 }
205
206 /* attempt to treat it as a response */
207 if (sscanf((const char *)&msg->l2h[0], "%3d %*s", &code) == 1) {
208 LOGP(DMGCP, LOGL_DEBUG, "Response: Code: %d\n", code);
209 } else {
210 int i, handled = 0;
211 msg->l3h = &msg->l2h[4];
212 for (i = 0; i < ARRAY_SIZE(mgcp_requests); ++i)
213 if (strncmp(mgcp_requests[i].name, (const char *) &msg->l2h[0], 4) == 0) {
214 handled = 1;
215 resp = mgcp_requests[i].handle_request(cfg, msg);
216 break;
217 }
218 if (!handled) {
219 LOGP(DMGCP, LOGL_NOTICE, "MSG with type: '%.4s' not handled\n", &msg->l2h[0]);
220 }
221 }
222
223 return resp;
224}
225
226/* string tokenizer for the poor */
227static int find_msg_pointers(struct msgb *msg, struct mgcp_msg_ptr *ptrs, int ptrs_length)
228{
229 int i, found = 0;
230
231 int whitespace = 1;
232 for (i = 0; i < msgb_l3len(msg) && ptrs_length > 0; ++i) {
233 /* if we have a space we found an end */
234 if (msg->l3h[i] == ' ' || msg->l3h[i] == '\r' || msg->l3h[i] == '\n') {
235 if (!whitespace) {
236 ++found;
237 whitespace = 1;
238 ptrs->length = i - ptrs->start - 1;
239 ++ptrs;
240 --ptrs_length;
241 } else {
242 /* skip any number of whitespace */
243 }
244
245 /* line end... stop */
246 if (msg->l3h[i] == '\r' || msg->l3h[i] == '\n')
247 break;
248 } else if (msg->l3h[i] == '\r' || msg->l3h[i] == '\n') {
249 /* line end, be done */
250 break;
251 } else if (whitespace) {
252 whitespace = 0;
253 ptrs->start = i;
254 }
255 }
256
257 if (ptrs_length == 0)
258 return -1;
259 return found;
260}
261
Holger Hans Peter Freyther7e0936e2011-01-06 19:48:55 +0100262/**
263 * We have a null terminated string with the endpoint name here. We only
264 * support two kinds. Simple ones as seen on the BSC level and the ones
265 * seen on the trunk side.
266 */
267static struct mgcp_endpoint *find_e1_endpoint(struct mgcp_config *cfg,
268 const char *mgcp)
269{
270 char *rest = NULL;
271 int trunk, endp, mgcp_endp;
272
273 trunk = strtoul(mgcp + 6, &rest, 10);
Holger Hans Peter Freytherea853042011-01-07 11:38:00 +0100274 if (rest == NULL || rest[0] != '/' || trunk < 1) {
Holger Hans Peter Freyther7e0936e2011-01-06 19:48:55 +0100275 LOGP(DMGCP, LOGL_ERROR, "Wrong trunk name '%s'\n", mgcp);
276 return NULL;
277 }
278
279 endp = strtoul(rest + 1, &rest, 10);
280 if (rest == NULL || rest[0] != '@') {
Holger Hans Peter Freytherea853042011-01-07 11:38:00 +0100281 LOGP(DMGCP, LOGL_ERROR, "Wrong endpoint name '%s'\n", mgcp);
Holger Hans Peter Freyther7e0936e2011-01-06 19:48:55 +0100282 return NULL;
283 }
284
285 /* signalling is on timeslot 1 */
286 if (endp == 1)
287 return NULL;
288
Holger Hans Peter Freytherea853042011-01-07 11:38:00 +0100289 mgcp_endp = mgcp_timeslot_to_endpoint(trunk - 1, endp);
Holger Hans Peter Freyther7e0936e2011-01-06 19:48:55 +0100290 if (mgcp_endp < 1 || mgcp_endp >= cfg->number_endpoints) {
291 LOGP(DMGCP, LOGL_ERROR, "Failed to find endpoint '%s'\n", mgcp);
Holger Hans Peter Freytherea853042011-01-07 11:38:00 +0100292 return NULL;
Holger Hans Peter Freyther7e0936e2011-01-06 19:48:55 +0100293 }
294
295 return &cfg->endpoints[mgcp_endp];
296}
297
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800298static struct mgcp_endpoint *find_endpoint(struct mgcp_config *cfg, const char *mgcp)
299{
300 char *endptr = NULL;
301 unsigned int gw = INT_MAX;
302
Holger Hans Peter Freyther7e0936e2011-01-06 19:48:55 +0100303 if (strncmp(mgcp, "ds/e1", 5) == 0) {
304 return find_e1_endpoint(cfg, mgcp);
305 } else {
306 gw = strtoul(mgcp, &endptr, 16);
307 if (gw > 0 && gw < cfg->number_endpoints && strcmp(endptr, "@mgw") == 0)
308 return &cfg->endpoints[gw];
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800309 }
310
Holger Hans Peter Freyther7e0936e2011-01-06 19:48:55 +0100311 LOGP(DMGCP, LOGL_ERROR, "Not able to find endpoint: '%s'\n", mgcp);
312 return NULL;
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800313}
314
315int mgcp_analyze_header(struct mgcp_config *cfg, struct msgb *msg,
316 struct mgcp_msg_ptr *ptr, int size,
317 const char **transaction_id, struct mgcp_endpoint **endp)
318{
319 int found;
320
321 *transaction_id = "000000";
322
323 if (size < 3) {
324 LOGP(DMGCP, LOGL_ERROR, "Not enough space in ptr\n");
325 return -1;
326 }
327
328 found = find_msg_pointers(msg, ptr, size);
329
330 if (found <= 3) {
331 LOGP(DMGCP, LOGL_ERROR, "Gateway: Not enough params. Found: %d\n", found);
332 return -1;
333 }
334
335 /*
336 * replace the space with \0. the main method gurantess that
337 * we still have + 1 for null termination
338 */
339 msg->l3h[ptr[3].start + ptr[3].length + 1] = '\0';
340 msg->l3h[ptr[2].start + ptr[2].length + 1] = '\0';
341 msg->l3h[ptr[1].start + ptr[1].length + 1] = '\0';
342 msg->l3h[ptr[0].start + ptr[0].length + 1] = '\0';
343
344 if (strncmp("1.0", (const char *)&msg->l3h[ptr[3].start], 3) != 0
345 || strncmp("MGCP", (const char *)&msg->l3h[ptr[2].start], 4) != 0) {
346 LOGP(DMGCP, LOGL_ERROR, "Wrong MGCP version. Not handling: '%s' '%s'\n",
347 (const char *)&msg->l3h[ptr[3].start],
348 (const char *)&msg->l3h[ptr[2].start]);
349 return -1;
350 }
351
352 *transaction_id = (const char *)&msg->l3h[ptr[0].start];
353 if (endp) {
354 *endp = find_endpoint(cfg, (const char *)&msg->l3h[ptr[1].start]);
355 return *endp == NULL;
356 }
357 return 0;
358}
359
360static int verify_call_id(const struct mgcp_endpoint *endp,
361 const char *callid)
362{
363 if (strcmp(endp->callid, callid) != 0) {
364 LOGP(DMGCP, LOGL_ERROR, "CallIDs does not match on 0x%x. '%s' != '%s'\n",
365 ENDPOINT_NUMBER(endp), endp->callid, callid);
366 return -1;
367 }
368
369 return 0;
370}
371
372static int verify_ci(const struct mgcp_endpoint *endp,
Holger Hans Peter Freyther5b084012010-08-08 07:51:51 +0800373 const char *_ci)
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800374{
Holger Hans Peter Freyther5b084012010-08-08 07:51:51 +0800375 uint32_t ci = strtoul(_ci, NULL, 10);
376
377 if (ci != endp->ci) {
378 LOGP(DMGCP, LOGL_ERROR, "ConnectionIdentifiers do not match on 0x%x. %u != %s\n",
379 ENDPOINT_NUMBER(endp), endp->ci, _ci);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800380 return -1;
381 }
382
383 return 0;
384}
385
386static struct msgb *handle_audit_endpoint(struct mgcp_config *cfg, struct msgb *msg)
387{
388 struct mgcp_msg_ptr data_ptrs[6];
Holger Hans Peter Freyther7e0936e2011-01-06 19:48:55 +0100389 int found;
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800390 const char *trans_id;
391 struct mgcp_endpoint *endp;
392
393 found = mgcp_analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
394 if (found != 0)
Holger Hans Peter Freyther7e0936e2011-01-06 19:48:55 +0100395 return create_err_response(500, "AUEP", trans_id);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800396 else
Holger Hans Peter Freyther7e0936e2011-01-06 19:48:55 +0100397 return create_ok_response(200, "AUEP", trans_id);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800398}
399
400static int parse_conn_mode(const char* msg, int *conn_mode)
401{
402 int ret = 0;
403 if (strcmp(msg, "recvonly") == 0)
404 *conn_mode = MGCP_CONN_RECV_ONLY;
405 else if (strcmp(msg, "sendrecv") == 0)
406 *conn_mode = MGCP_CONN_RECV_SEND;
Holger Hans Peter Freytherb988caa2010-09-18 20:23:05 +0800407 else if (strcmp(msg, "sendonly") == 0)
408 *conn_mode = MGCP_CONN_SEND_ONLY;
Holger Hans Peter Freytherd86208d2010-08-04 06:11:27 +0800409 else if (strcmp(msg, "loopback") == 0)
410 *conn_mode = MGCP_CONN_LOOPBACK;
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800411 else {
412 LOGP(DMGCP, LOGL_ERROR, "Unknown connection mode: '%s'\n", msg);
413 ret = -1;
414 }
415
416 return ret;
417}
418
419static struct msgb *handle_create_con(struct mgcp_config *cfg, struct msgb *msg)
420{
421 struct mgcp_msg_ptr data_ptrs[6];
422 int found, i, line_start;
423 const char *trans_id;
424 struct mgcp_endpoint *endp;
425 int error_code = 500;
426 int port;
427
428 found = mgcp_analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
429 if (found != 0)
Holger Hans Peter Freyther7e0936e2011-01-06 19:48:55 +0100430 return create_err_response(510, "CRCX", trans_id);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800431
432 if (endp->ci != CI_UNUSED) {
433 if (cfg->force_realloc) {
434 LOGP(DMGCP, LOGL_NOTICE, "Endpoint 0x%x already allocated. Forcing realloc.\n",
435 ENDPOINT_NUMBER(endp));
Holger Hans Peter Freytherd86208d2010-08-04 06:11:27 +0800436 mgcp_free_endp(endp);
Holger Hans Peter Freythere40bc382010-09-18 03:11:00 +0800437 if (cfg->realloc_cb)
438 cfg->realloc_cb(cfg, ENDPOINT_NUMBER(endp));
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800439 } else {
440 LOGP(DMGCP, LOGL_ERROR, "Endpoint is already used. 0x%x\n",
441 ENDPOINT_NUMBER(endp));
Holger Hans Peter Freyther7e0936e2011-01-06 19:48:55 +0100442 return create_err_response(400, "CRCX", trans_id);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800443 }
444 }
445
446 /* parse CallID C: and LocalParameters L: */
447 MSG_TOKENIZE_START
448 switch (msg->l3h[line_start]) {
449 case 'L':
450 endp->local_options = talloc_strdup(cfg->endpoints,
451 (const char *)&msg->l3h[line_start + 3]);
452 break;
453 case 'C':
454 endp->callid = talloc_strdup(cfg->endpoints,
455 (const char *)&msg->l3h[line_start + 3]);
456 break;
457 case 'M':
458 if (parse_conn_mode((const char *)&msg->l3h[line_start + 3],
459 &endp->conn_mode) != 0) {
460 error_code = 517;
461 goto error2;
462 }
Holger Hans Peter Freytherd86208d2010-08-04 06:11:27 +0800463
464 endp->orig_mode = endp->conn_mode;
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800465 break;
466 default:
467 LOGP(DMGCP, LOGL_NOTICE, "Unhandled option: '%c'/%d on 0x%x\n",
468 msg->l3h[line_start], msg->l3h[line_start],
469 ENDPOINT_NUMBER(endp));
470 break;
471 }
472 MSG_TOKENIZE_END
473
474 /* initialize */
475 endp->net_rtp = endp->net_rtcp = endp->bts_rtp = endp->bts_rtcp = 0;
476
477 /* set to zero until we get the info */
478 memset(&endp->remote, 0, sizeof(endp->remote));
479
480 /* bind to the port now */
481 port = rtp_calculate_port(ENDPOINT_NUMBER(endp), cfg->rtp_base_port);
482 if (cfg->early_bind)
483 endp->rtp_port = port;
484 else if (mgcp_bind_rtp_port(endp, port) != 0)
485 goto error2;
486
487 /* assign a local call identifier or fail */
488 endp->ci = generate_call_id(cfg);
489 if (endp->ci == CI_UNUSED)
490 goto error2;
491
492 endp->bts_payload_type = cfg->audio_payload;
493
494 /* policy CB */
495 if (cfg->policy_cb) {
496 switch (cfg->policy_cb(cfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_CRCX, trans_id)) {
497 case MGCP_POLICY_REJECT:
498 LOGP(DMGCP, LOGL_NOTICE, "CRCX rejected by policy on 0x%x\n",
499 ENDPOINT_NUMBER(endp));
500 mgcp_free_endp(endp);
Holger Hans Peter Freyther7e0936e2011-01-06 19:48:55 +0100501 return create_err_response(400, "CRCX", trans_id);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800502 break;
503 case MGCP_POLICY_DEFER:
504 /* stop processing */
505 return NULL;
506 break;
507 case MGCP_POLICY_CONT:
508 /* just continue */
509 break;
510 }
511 }
512
513 LOGP(DMGCP, LOGL_NOTICE, "Creating endpoint on: 0x%x CI: %u port: %u\n",
514 ENDPOINT_NUMBER(endp), endp->ci, endp->rtp_port);
515 if (cfg->change_cb)
516 cfg->change_cb(cfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_CRCX, endp->rtp_port);
517
518 return create_response_with_sdp(endp, "CRCX", trans_id);
519error:
520 LOGP(DMGCP, LOGL_ERROR, "Malformed line: %s on 0x%x with: line_start: %d %d\n",
521 hexdump(msg->l3h, msgb_l3len(msg)),
522 ENDPOINT_NUMBER(endp), line_start, i);
Holger Hans Peter Freyther7e0936e2011-01-06 19:48:55 +0100523 return create_err_response(error_code, "CRCX", trans_id);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800524
525error2:
526 LOGP(DMGCP, LOGL_NOTICE, "Resource error on 0x%x\n", ENDPOINT_NUMBER(endp));
Holger Hans Peter Freyther7e0936e2011-01-06 19:48:55 +0100527 return create_err_response(error_code, "CRCX", trans_id);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800528}
529
530static struct msgb *handle_modify_con(struct mgcp_config *cfg, struct msgb *msg)
531{
532 struct mgcp_msg_ptr data_ptrs[6];
533 int found, i, line_start;
534 const char *trans_id;
535 struct mgcp_endpoint *endp;
536 int error_code = 500;
537 int silent = 0;
538
539 found = mgcp_analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
540 if (found != 0)
Holger Hans Peter Freyther7e0936e2011-01-06 19:48:55 +0100541 return create_err_response(510, "MDCX", trans_id);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800542
543 if (endp->ci == CI_UNUSED) {
544 LOGP(DMGCP, LOGL_ERROR, "Endpoint is not holding a connection. 0x%x\n", ENDPOINT_NUMBER(endp));
Holger Hans Peter Freyther7e0936e2011-01-06 19:48:55 +0100545 return create_err_response(400, "MDCX", trans_id);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800546 }
547
548 MSG_TOKENIZE_START
549 switch (msg->l3h[line_start]) {
550 case 'C': {
551 if (verify_call_id(endp, (const char *)&msg->l3h[line_start + 3]) != 0)
552 goto error3;
553 break;
554 }
555 case 'I': {
556 if (verify_ci(endp, (const char *)&msg->l3h[line_start + 3]) != 0)
557 goto error3;
558 break;
559 }
560 case 'L':
561 /* skip */
562 break;
563 case 'M':
564 if (parse_conn_mode((const char *)&msg->l3h[line_start + 3],
565 &endp->conn_mode) != 0) {
566 error_code = 517;
567 goto error3;
568 }
Holger Hans Peter Freytherd86208d2010-08-04 06:11:27 +0800569 endp->orig_mode = endp->conn_mode;
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800570 break;
571 case 'Z':
572 silent = strcmp("noanswer", (const char *)&msg->l3h[line_start + 3]) == 0;
573 break;
574 case '\0':
575 /* SDP file begins */
576 break;
577 case 'a':
578 case 'o':
579 case 's':
580 case 't':
581 case 'v':
582 /* skip these SDP attributes */
583 break;
584 case 'm': {
585 int port;
586 int payload;
587 const char *param = (const char *)&msg->l3h[line_start];
588
589 if (sscanf(param, "m=audio %d RTP/AVP %d", &port, &payload) == 2) {
590 endp->net_rtp = htons(port);
591 endp->net_rtcp = htons(port + 1);
592 endp->net_payload_type = payload;
593 }
594 break;
595 }
596 case 'c': {
597 char ipv4[16];
598 const char *param = (const char *)&msg->l3h[line_start];
599
600 if (sscanf(param, "c=IN IP4 %15s", ipv4) == 1) {
601 inet_aton(ipv4, &endp->remote);
602 }
603 break;
604 }
605 default:
606 LOGP(DMGCP, LOGL_NOTICE, "Unhandled option: '%c'/%d on 0x%x\n",
607 msg->l3h[line_start], msg->l3h[line_start],
608 ENDPOINT_NUMBER(endp));
609 break;
610 }
611 MSG_TOKENIZE_END
612
613 /* policy CB */
614 if (cfg->policy_cb) {
615 switch (cfg->policy_cb(cfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_MDCX, trans_id)) {
616 case MGCP_POLICY_REJECT:
617 LOGP(DMGCP, LOGL_NOTICE, "MDCX rejected by policy on 0x%x\n",
618 ENDPOINT_NUMBER(endp));
619 if (silent)
620 goto out_silent;
Holger Hans Peter Freyther7e0936e2011-01-06 19:48:55 +0100621 return create_err_response(400, "MDCX", trans_id);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800622 break;
623 case MGCP_POLICY_DEFER:
624 /* stop processing */
625 return NULL;
626 break;
627 case MGCP_POLICY_CONT:
628 /* just continue */
629 break;
630 }
631 }
632
633 /* modify */
634 LOGP(DMGCP, LOGL_NOTICE, "Modified endpoint on: 0x%x Server: %s:%u\n",
635 ENDPOINT_NUMBER(endp), inet_ntoa(endp->remote), ntohs(endp->net_rtp));
636 if (cfg->change_cb)
637 cfg->change_cb(cfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_MDCX, endp->rtp_port);
638 if (silent)
639 goto out_silent;
640
641 return create_response_with_sdp(endp, "MDCX", trans_id);
642
643error:
644 LOGP(DMGCP, LOGL_ERROR, "Malformed line: %s on 0x%x with: line_start: %d %d %d\n",
645 hexdump(msg->l3h, msgb_l3len(msg)),
646 ENDPOINT_NUMBER(endp), line_start, i, msg->l3h[line_start]);
Holger Hans Peter Freyther7e0936e2011-01-06 19:48:55 +0100647 return create_err_response(error_code, "MDCX", trans_id);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800648
649error3:
Holger Hans Peter Freyther7e0936e2011-01-06 19:48:55 +0100650 return create_err_response(error_code, "MDCX", trans_id);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800651
652
653out_silent:
654 return NULL;
655}
656
657static struct msgb *handle_delete_con(struct mgcp_config *cfg, struct msgb *msg)
658{
659 struct mgcp_msg_ptr data_ptrs[6];
660 int found, i, line_start;
661 const char *trans_id;
662 struct mgcp_endpoint *endp;
663 int error_code = 500;
664 int silent = 0;
665
666 found = mgcp_analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
667 if (found != 0)
Holger Hans Peter Freyther7e0936e2011-01-06 19:48:55 +0100668 return create_err_response(error_code, "DLCX", trans_id);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800669
670 if (endp->ci == CI_UNUSED) {
671 LOGP(DMGCP, LOGL_ERROR, "Endpoint is not used. 0x%x\n", ENDPOINT_NUMBER(endp));
Holger Hans Peter Freyther7e0936e2011-01-06 19:48:55 +0100672 return create_err_response(400, "DLCX", trans_id);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800673 }
674
675 MSG_TOKENIZE_START
676 switch (msg->l3h[line_start]) {
677 case 'C': {
678 if (verify_call_id(endp, (const char *)&msg->l3h[line_start + 3]) != 0)
679 goto error3;
680 break;
681 }
682 case 'I': {
683 if (verify_ci(endp, (const char *)&msg->l3h[line_start + 3]) != 0)
684 goto error3;
685 break;
686 case 'Z':
687 silent = strcmp("noanswer", (const char *)&msg->l3h[line_start + 3]) == 0;
688 break;
689 }
690 default:
691 LOGP(DMGCP, LOGL_NOTICE, "Unhandled option: '%c'/%d on 0x%x\n",
692 msg->l3h[line_start], msg->l3h[line_start],
693 ENDPOINT_NUMBER(endp));
694 break;
695 }
696 MSG_TOKENIZE_END
697
698 /* policy CB */
699 if (cfg->policy_cb) {
700 switch (cfg->policy_cb(cfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_DLCX, trans_id)) {
701 case MGCP_POLICY_REJECT:
702 LOGP(DMGCP, LOGL_NOTICE, "DLCX rejected by policy on 0x%x\n",
703 ENDPOINT_NUMBER(endp));
704 if (silent)
705 goto out_silent;
Holger Hans Peter Freyther7e0936e2011-01-06 19:48:55 +0100706 return create_err_response(400, "DLCX", trans_id);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800707 break;
708 case MGCP_POLICY_DEFER:
709 /* stop processing */
710 return NULL;
711 break;
712 case MGCP_POLICY_CONT:
713 /* just continue */
714 break;
715 }
716 }
717
718 /* free the connection */
719 LOGP(DMGCP, LOGL_NOTICE, "Deleted endpoint on: 0x%x Server: %s:%u\n",
720 ENDPOINT_NUMBER(endp), inet_ntoa(endp->remote), ntohs(endp->net_rtp));
721 mgcp_free_endp(endp);
722 if (cfg->change_cb)
723 cfg->change_cb(cfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_DLCX, endp->rtp_port);
724
725 if (silent)
726 goto out_silent;
Holger Hans Peter Freyther7e0936e2011-01-06 19:48:55 +0100727 return create_ok_response(250, "DLCX", trans_id);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800728
729error:
730 LOGP(DMGCP, LOGL_ERROR, "Malformed line: %s on 0x%x with: line_start: %d %d\n",
731 hexdump(msg->l3h, msgb_l3len(msg)),
732 ENDPOINT_NUMBER(endp), line_start, i);
Holger Hans Peter Freyther7e0936e2011-01-06 19:48:55 +0100733 return create_err_response(error_code, "DLCX", trans_id);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800734
735error3:
Holger Hans Peter Freyther7e0936e2011-01-06 19:48:55 +0100736 return create_err_response(error_code, "DLCX", trans_id);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800737
738out_silent:
739 return NULL;
740}
741
742static struct msgb *handle_rsip(struct mgcp_config *cfg, struct msgb *msg)
743{
744 if (cfg->reset_cb)
745 cfg->reset_cb(cfg);
746 return NULL;
747}
748
749struct mgcp_config *mgcp_config_alloc(void)
750{
751 struct mgcp_config *cfg;
752
753 cfg = talloc_zero(NULL, struct mgcp_config);
754 if (!cfg) {
755 LOGP(DMGCP, LOGL_FATAL, "Failed to allocate config.\n");
756 return NULL;
757 }
758
759 cfg->source_port = 2427;
760 cfg->source_addr = talloc_strdup(cfg, "0.0.0.0");
761 cfg->audio_name = talloc_strdup(cfg, "GSM-EFR/8000");
762 cfg->audio_payload = 97;
763 cfg->rtp_base_port = RTP_PORT_DEFAULT;
764
765 return cfg;
766}
767
768int mgcp_endpoints_allocate(struct mgcp_config *cfg)
769{
770 int i;
771
772 /* Initialize all endpoints */
773 cfg->endpoints = _talloc_zero_array(cfg,
774 sizeof(struct mgcp_endpoint),
775 cfg->number_endpoints, "endpoints");
776 if (!cfg->endpoints)
777 return -1;
778
779 for (i = 0; i < cfg->number_endpoints; ++i) {
780 cfg->endpoints[i].local_rtp.fd = -1;
781 cfg->endpoints[i].local_rtcp.fd = -1;
782 cfg->endpoints[i].ci = CI_UNUSED;
783 cfg->endpoints[i].cfg = cfg;
784 cfg->endpoints[i].net_payload_type = -1;
785 cfg->endpoints[i].bts_payload_type = -1;
786 }
787
788 return 0;
789}
790
791void mgcp_free_endp(struct mgcp_endpoint *endp)
792{
793 LOGP(DMGCP, LOGL_DEBUG, "Deleting endpoint on: 0x%x\n", ENDPOINT_NUMBER(endp));
Holger Hans Peter Freytherd86208d2010-08-04 06:11:27 +0800794 endp->ci = CI_UNUSED;
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800795
796 if (endp->callid) {
797 talloc_free(endp->callid);
798 endp->callid = NULL;
799 }
800
801 if (endp->local_options) {
802 talloc_free(endp->local_options);
803 endp->local_options = NULL;
804 }
805
806 if (!endp->cfg->early_bind) {
807 bsc_unregister_fd(&endp->local_rtp);
808 bsc_unregister_fd(&endp->local_rtcp);
809 }
810
811 endp->net_rtp = endp->net_rtcp = endp->bts_rtp = endp->bts_rtcp = 0;
812 endp->net_payload_type = endp->bts_payload_type = -1;
813 endp->in_bts = endp->in_remote = 0;
814 memset(&endp->remote, 0, sizeof(endp->remote));
815 memset(&endp->bts, 0, sizeof(endp->bts));
Holger Hans Peter Freytherd86208d2010-08-04 06:11:27 +0800816
817 memset(&endp->net_state, 0, sizeof(endp->net_state));
818 memset(&endp->bts_state, 0, sizeof(endp->bts_state));
819
820 endp->conn_mode = endp->orig_mode = MGCP_CONN_NONE;
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800821}