blob: 8b59b31cb805199085c534c8d174e72ed277a54b [file] [log] [blame]
Holger Hans Peter Freyther9a26b7c2009-10-09 07:08:11 +02001/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
2
3/*
4 * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
5 * (C) 2009 by on-waves.com
6 * All Rights Reserved
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 *
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
32#include <sys/socket.h>
33#include <arpa/inet.h>
34
35#include <openbsc/debug.h>
36#include <openbsc/msgb.h>
37#include <openbsc/talloc.h>
38#include <openbsc/gsm_data.h>
39#include <openbsc/select.h>
40#include <openbsc/mgcp.h>
41
42#include <vty/command.h>
43#include <vty/vty.h>
44
45/* this is here for the vty... it will never be called */
46void subscr_put() { abort(); }
47void vty_event() { }
48
49#define _GNU_SOURCE
50#include <getopt.h>
51
52#warning "Make use of the rtp proxy code"
53
54static int source_port = 2427;
55static const char *local_ip = NULL;
56static const char *source_addr = "0.0.0.0";
57static struct bsc_fd bfd;
58static const unsigned int number_endpoints = 32 + 1;
59static const char *bts_ip = NULL;
60static struct in_addr bts_in;
61static int first_request = 1;
62static const char *audio_name = "GSM-EFR/8000";
63static int audio_payload = 97;
64static int early_bind = 0;
65
66static char *config_file = "mgcp.cfg";
67
68/* used by msgb and mgcp */
69void *tall_bsc_ctx = NULL;
70
71enum mgcp_connection_mode {
72 MGCP_CONN_NONE = 0,
73 MGCP_CONN_RECV_ONLY = 1,
74 MGCP_CONN_SEND_ONLY = 2,
75 MGCP_CONN_RECV_SEND = MGCP_CONN_RECV_ONLY | MGCP_CONN_SEND_ONLY,
76};
77
78#define CI_UNUSED 0
79static unsigned int last_call_id = 0;
80
81struct mgcp_endpoint {
82 int ci;
83 char *callid;
84 char *local_options;
85 int conn_mode;
86
87 /* the local rtp port */
88 int rtp_port;
89
90 /*
91 * RTP mangling:
92 * - we get RTP and RTCP to us and need to forward to the BTS
93 * - we get RTP and RTCP from the BTS and forward to the network
94 */
95 struct bsc_fd local_rtp;
96 struct bsc_fd local_rtcp;
97
98 struct in_addr remote;
99
100 /* in network byte order */
101 int rtp, rtcp;
102 int bts_rtp, bts_rtcp;
103};
104
105static struct mgcp_endpoint *endpoints = NULL;
106#define ENDPOINT_NUMBER(endp) abs(endp - endpoints)
107
108/**
109 * Macro for tokenizing MGCP messages and SDP in one go.
110 *
111 */
112#define MSG_TOKENIZE_START \
113 line_start = 0; \
114 for (i = 0; i < msgb_l3len(msg); ++i) { \
115 /* we have a line end */ \
116 if (msg->l3h[i] == '\n') { \
117 /* skip the first line */ \
118 if (line_start == 0) { \
119 line_start = i + 1; \
120 continue; \
121 } \
122 \
123 /* check if we have a proper param */ \
124 if (i - line_start == 1 && msg->l3h[line_start] == '\r') { \
125 } else if (i - line_start > 2 \
126 && islower(msg->l3h[line_start]) \
127 && msg->l3h[line_start + 1] == '=') { \
128 } else if (i - line_start < 3 \
129 || msg->l3h[line_start + 1] != ':' \
130 || msg->l3h[line_start + 2] != ' ') \
131 goto error; \
132 \
133 msg->l3h[i] = '\0'; \
134 if (msg->l3h[i-1] == '\r') \
135 msg->l3h[i-1] = '\0';
136
137#define MSG_TOKENIZE_END \
138 line_start = i + 1; \
139 } \
140 }
141
142
143struct mgcp_msg_ptr {
144 unsigned int start;
145 unsigned int length;
146};
147
148struct mgcp_request {
149 char *name;
150 void (*handle_request) (struct msgb *msg, struct sockaddr_in *source);
151 char *debug_name;
152};
153
154#define MGCP_REQUEST(NAME, REQ, DEBUG_NAME) \
155 { .name = NAME, .handle_request = REQ, .debug_name = DEBUG_NAME },
156
157static void handle_audit_endpoint(struct msgb *msg, struct sockaddr_in *source);
158static void handle_create_con(struct msgb *msg, struct sockaddr_in *source);
159static void handle_delete_con(struct msgb *msg, struct sockaddr_in *source);
160static void handle_modify_con(struct msgb *msg, struct sockaddr_in *source);
161
162static int generate_call_id()
163{
164 int i;
165
166 /* use the call id */
167 ++last_call_id;
168
169 /* handle wrap around */
170 if (last_call_id == CI_UNUSED)
171 ++last_call_id;
172
173 /* callstack can only be of size number_of_endpoints */
174 /* verify that the call id is free, e.g. in case of overrun */
175 for (i = 1; i < number_endpoints; ++i)
176 if (endpoints[i].ci == last_call_id)
177 return generate_call_id();
178
179 return last_call_id;
180}
181
182/* FIXIME/TODO: need to have a list of pending transactions and check that */
183static unsigned int generate_transaction_id()
184{
185 return abs(rand());
186}
187
188static int _send(int fd, struct in_addr *addr, int port, char *buf, int len)
189{
190 struct sockaddr_in out;
191 out.sin_family = AF_INET;
192 out.sin_port = port;
193 memcpy(&out.sin_addr, addr, sizeof(*addr));
194
195 return sendto(fd, buf, len, 0, (struct sockaddr *)&out, sizeof(out));
196}
197
198/*
199 * There is data coming. We will have to figure out if it
200 * came from the BTS or the MediaGateway of the MSC. On top
201 * of that we need to figure out if it was RTP or RTCP.
202 *
203 * Currently we do not communicate with the BSC so we have
204 * no idea where the BTS is listening for RTP and need to
205 * do the classic routing trick. Wait for the first packet
206 * from the BTS and then go ahead.
207 */
208static int rtp_data_cb(struct bsc_fd *fd, unsigned int what)
209{
210 char buf[4096];
211 struct sockaddr_in addr;
212 socklen_t slen = sizeof(addr);
213 struct mgcp_endpoint *endp;
214 int rc, is_remote;
215
216 endp = (struct mgcp_endpoint *) fd->data;
217
218 rc = recvfrom(fd->fd, &buf, sizeof(buf), 0,
219 (struct sockaddr *) &addr, &slen);
220 if (rc < 0) {
221 DEBUGP(DMGCP, "Failed to receive message on: 0x%x\n",
222 ENDPOINT_NUMBER(endp));
223 return -1;
224 }
225
226 /*
227 * Figure out where to forward it to. This code assumes that we
228 * have received the Connection Modify and know who is a legitimate
229 * partner. According to the spec we could attempt to forward even
230 * after the Create Connection but we will not as we are not really
231 * able to tell if this is legitimate.
232 */
233 #warning "Slight spec violation. With connection mode recvonly we should attempt to forward."
234 is_remote = memcmp(&addr.sin_addr, &endp->remote, sizeof(addr.sin_addr)) == 0;
235 if (is_remote) {
236 if (endp->rtp == addr.sin_port) {
237 return _send(fd->fd, &bts_in, endp->bts_rtp, buf, rc);
238 } else if (endp->rtcp == addr.sin_port) {
239 return _send(fd->fd, &bts_in, endp->bts_rtcp, buf, rc);
240 } else {
241 DEBUGP(DMGCP, "Unknown remote port. Not able to forward on 0x%x port: %d\n",
242 ENDPOINT_NUMBER(endp), ntohs(addr.sin_port));
243 }
244
245 return -1;
246 }
247
248 /* We have no idea who called us, maybe it is the BTS. */
249 if (endp->bts_rtp == 0) {
250 /* it was the BTS... */
251 if (memcmp(&addr.sin_addr, &bts_in, sizeof(bts_in)) == 0) {
252 if (fd == &endp->local_rtp) {
253 endp->bts_rtp = addr.sin_port;
254 endp->bts_rtcp = htons(ntohs(addr.sin_port) + 1);
255 } else {
256 endp->bts_rtp = htons(ntohs(addr.sin_port) - 1);
257 endp->bts_rtcp = addr.sin_port;
258 }
259
260 DEBUGP(DMGCP, "Found BTS for endpoint: 0x%x on port: %d\n",
261 ENDPOINT_NUMBER(endp), ntohs(endp->bts_rtp));
262 }
263 }
264
265 if (endp->bts_rtp == 0 || endp->conn_mode == MGCP_CONN_RECV_ONLY) {
266 DEBUGP(DMGCP, "Not forwarding data from possible BTS conn: %d on 0x%x\n",
267 endp->conn_mode, ENDPOINT_NUMBER(endp));
268 return -1;
269 }
270
271 if (fd == &endp->local_rtp) {
272 return _send(fd->fd, &endp->remote, endp->rtp, buf, rc);
273 } else {
274 return _send(fd->fd, &endp->remote, endp->rtcp, buf, rc);
275 }
276}
277
278static int create_bind(struct bsc_fd *fd, int port)
279{
280 struct sockaddr_in addr;
281 int on = 1;
282
283 fd->fd = socket(AF_INET, SOCK_DGRAM, 0);
284 if (fd->fd < 0)
285 return -1;
286
287 setsockopt(fd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
288 memset(&addr, 0, sizeof(addr));
289 addr.sin_family = AF_INET;
290 addr.sin_port = htons(port);
291 inet_aton(source_addr, &addr.sin_addr);
292
293 if (bind(fd->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0)
294 return -1;
295
296 return 0;
297}
298
299static int bind_rtp(struct mgcp_endpoint *endp)
300{
301 /* set to zero until we get the info */
302 memset(&endp->remote, 0, sizeof(endp->remote));
303 endp->bts_rtp = endp->bts_rtcp = 0;
304 endp->rtp = endp->rtcp = 0;
305
306 if (create_bind(&endp->local_rtp, endp->rtp_port) != 0) {
307 DEBUGP(DMGCP, "Failed to create RTP port: %d on 0x%x\n",
308 endp->rtp_port, ENDPOINT_NUMBER(endp));
309 goto cleanup0;
310 }
311
312 if (create_bind(&endp->local_rtcp, endp->rtp_port + 1) != 0) {
313 DEBUGP(DMGCP, "Failed to create RTCP port: %d on 0x%x\n",
314 endp->rtp_port + 1, ENDPOINT_NUMBER(endp));
315 goto cleanup1;
316 }
317
318 endp->local_rtp.cb = rtp_data_cb;
319 endp->local_rtp.data = endp;
320 endp->local_rtp.when = BSC_FD_READ;
321 if (bsc_register_fd(&endp->local_rtp) != 0) {
322 DEBUGP(DMGCP, "Failed to register RTP port %d on 0x%x\n",
323 endp->rtp_port, ENDPOINT_NUMBER(endp));
324 goto cleanup2;
325 }
326
327 endp->local_rtcp.cb = rtp_data_cb;
328 endp->local_rtcp.data = endp;
329 endp->local_rtcp.when = BSC_FD_READ;
330 if (bsc_register_fd(&endp->local_rtcp) != 0) {
331 DEBUGP(DMGCP, "Failed to register RTCP port %d on 0x%x\n",
332 endp->rtp_port + 1, ENDPOINT_NUMBER(endp));
333 goto cleanup3;
334 }
335
336 return 0;
337
338cleanup3:
339 bsc_unregister_fd(&endp->local_rtp);
340cleanup2:
341 close(endp->local_rtcp.fd);
342 endp->local_rtcp.fd = -1;
343cleanup1:
344 close(endp->local_rtp.fd);
345 endp->local_rtp.fd = -1;
346cleanup0:
347 return -1;
348}
349
350/*
351 * array of function pointers for handling various
352 * messages. In the future this might be binary sorted
353 * for performance reasons.
354 */
355static const struct mgcp_request mgcp_requests [] = {
356 MGCP_REQUEST("AUEP", handle_audit_endpoint, "AuditEndpoint")
357 MGCP_REQUEST("CRCX", handle_create_con, "CreateConnection")
358 MGCP_REQUEST("DLCX", handle_delete_con, "DeleteConnection")
359 MGCP_REQUEST("MDCX", handle_modify_con, "ModifiyConnection")
360};
361
362static void send_response_with_data(int code, const char *msg, const char *trans,
363 const char *data, struct sockaddr_in *source)
364{
365 char buf[4096];
366 int len;
367
368 if (data) {
369 len = snprintf(buf, sizeof(buf), "%d %s\n%s", code, trans, data);
370 } else {
371 len = snprintf(buf, sizeof(buf), "%d %s\n", code, trans);
372 }
373 DEBUGP(DMGCP, "Sending response: code: %d for '%s'\n", code, msg);
374
375 sendto(bfd.fd, buf, len, 0, (struct sockaddr *)source, sizeof(*source));
376}
377
378static void send_response(int code, const char *msg, const char *trans, struct sockaddr_in *source)
379{
380 send_response_with_data(code, msg, trans, NULL, source);
381}
382
383static void send_with_sdp(struct mgcp_endpoint *endp, const char *msg, const char *trans_id, struct sockaddr_in *source)
384{
385 const char *addr = local_ip;
386 char sdp_record[4096];
387
388 if (!addr)
389 addr = source_addr;
390
391 snprintf(sdp_record, sizeof(sdp_record) - 1,
392 "I: %d\n\n"
393 "v=0\r\n"
394 "c=IN IP4 %s\r\n"
395 "m=audio %d RTP/AVP %d\r\n"
396 "a=rtpmap:%d %s\r\n",
397 endp->ci, addr, endp->rtp_port,
398 audio_payload, audio_payload, audio_name);
399 return send_response_with_data(200, msg, trans_id, sdp_record, source);
400}
401
402/* send a static record */
403static void send_rsip(struct sockaddr_in *source)
404{
405 char reset[4096];
406 int len, rc;
407
408 len = snprintf(reset, sizeof(reset) - 1,
409 "RSIP %u *@mgw MGCP 1.0\n"
410 "RM: restart\n", generate_transaction_id());
411 rc = sendto(bfd.fd, reset, len, 0, (struct sockaddr *) source, sizeof(*source));
412 if (rc < 0) {
413 DEBUGP(DMGCP, "Failed to send RSIP: %d\n", rc);
414 }
415}
416
417/*
418 * handle incoming messages:
419 * - this can be a command (four letters, space, transaction id)
420 * - or a response (three numbers, space, transaction id)
421 */
422static void handle_message(struct msgb *msg, struct sockaddr_in *source)
423{
424 int code;
425
426 if (msg->len < 4) {
427 DEBUGP(DMGCP, "mgs too short: %d\n", msg->len);
428 return;
429 }
430
431 /* attempt to treat it as a response */
432 if (sscanf((const char *)&msg->data[0], "%3d %*s", &code) == 1) {
433 DEBUGP(DMGCP, "Response: Code: %d\n", code);
434 } else {
435 int i, handled = 0;
436 msg->l3h = &msg->l2h[4];
437 for (i = 0; i < ARRAY_SIZE(mgcp_requests); ++i)
438 if (strncmp(mgcp_requests[i].name, (const char *) &msg->data[0], 4) == 0) {
439 handled = 1;
440 mgcp_requests[i].handle_request(msg, source);
441 }
442 if (!handled) {
443 DEBUGP(DMGCP, "MSG with type: '%.4s' not handled\n", &msg->data[0]);
444 }
445 }
446}
447
448/* string tokenizer for the poor */
449static int find_msg_pointers(struct msgb *msg, struct mgcp_msg_ptr *ptrs, int ptrs_length)
450{
451 int i, found = 0;
452
453 int whitespace = 1;
454 for (i = 0; i < msgb_l3len(msg) && ptrs_length > 0; ++i) {
455 /* if we have a space we found an end */
456 if (msg->l3h[i] == ' ' || msg->l3h[i] == '\r' || msg->l3h[i] == '\n') {
457 if (!whitespace) {
458 ++found;
459 whitespace = 1;
460 ptrs->length = i - ptrs->start - 1;
461 ++ptrs;
462 --ptrs_length;
463 } else {
464 /* skip any number of whitespace */
465 }
466
467 /* line end... stop */
468 if (msg->l3h[i] == '\r' || msg->l3h[i] == '\n')
469 break;
470 } else if (msg->l3h[i] == '\r' || msg->l3h[i] == '\n') {
471 /* line end, be done */
472 break;
473 } else if (whitespace) {
474 whitespace = 0;
475 ptrs->start = i;
476 }
477 }
478
479 if (ptrs_length == 0)
480 return -1;
481 return found;
482}
483
484static struct mgcp_endpoint *find_endpoint(const char *mgcp)
485{
486 char *endptr = NULL;
487 unsigned int gw = INT_MAX;
488
489 gw = strtoul(mgcp, &endptr, 16);
490 if (gw == 0 || gw >= number_endpoints || strcmp(endptr, "@mgw") != 0) {
491 DEBUGP(DMGCP, "Not able to find endpoint: '%s'\n", mgcp);
492 return NULL;
493 }
494
495 return &endpoints[gw];
496}
497
498static int analyze_header(struct msgb *msg, struct mgcp_msg_ptr *ptr, int size,
499 const char **transaction_id, struct mgcp_endpoint **endp)
500{
501 int found;
502
503 if (size < 3) {
504 DEBUGP(DMGCP, "Not enough space in ptr\n");
505 return -1;
506 }
507
508 found = find_msg_pointers(msg, ptr, size);
509
510 if (found < 3) {
511 DEBUGP(DMGCP, "Gateway: Not enough params. Found: %d\n", found);
512 return -1;
513 }
514
515 /*
516 * replace the space with \0. the main method gurantess that
517 * we still have + 1 for null termination
518 */
519 msg->l3h[ptr[3].start + ptr[3].length + 1] = '\0';
520 msg->l3h[ptr[2].start + ptr[2].length + 1] = '\0';
521 msg->l3h[ptr[1].start + ptr[1].length + 1] = '\0';
522 msg->l3h[ptr[0].start + ptr[0].length + 1] = '\0';
523
524 if (strncmp("1.0", (const char *)&msg->l3h[ptr[3].start], 3) != 0
525 || strncmp("MGCP", (const char *)&msg->l3h[ptr[2].start], 4) != 0) {
526 DEBUGP(DMGCP, "Wrong MGCP version. Not handling: '%s' '%s'\n",
527 (const char *)&msg->l3h[ptr[3].start],
528 (const char *)&msg->l3h[ptr[2].start]);
529 return -1;
530 }
531
532 *transaction_id = (const char *)&msg->l3h[ptr[0].start];
533 *endp = find_endpoint((const char *)&msg->l3h[ptr[1].start]);
534 return *endp == NULL;
535}
536
537static int verify_call_id(const struct mgcp_endpoint *endp,
538 const char *callid)
539{
540 if (strcmp(endp->callid, callid) != 0) {
541 DEBUGP(DMGCP, "CallIDs does not match on 0x%x. '%s' != '%s'\n",
542 ENDPOINT_NUMBER(endp), endp->callid, callid);
543 return -1;
544 }
545
546 return 0;
547}
548
549static int verify_ci(const struct mgcp_endpoint *endp,
550 const char *ci)
551{
552 if (atoi(ci) != endp->ci) {
553 DEBUGP(DMGCP, "ConnectionIdentifiers do not match on 0x%x. %d != %s\n",
554 ENDPOINT_NUMBER(endp), endp->ci, ci);
555 return -1;
556 }
557
558 return 0;
559}
560
561static void handle_audit_endpoint(struct msgb *msg, struct sockaddr_in *source)
562{
563 struct mgcp_msg_ptr data_ptrs[6];
564 int found, response;
565 const char *trans_id;
566 struct mgcp_endpoint *endp;
567
568 found = analyze_header(msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
569 if (found != 0)
570 response = 500;
571 else
572 response = 200;
573
574 return send_response(response, "AUEP", trans_id, source);
575}
576
577static int parse_conn_mode(const char* msg, int *conn_mode)
578{
579 int ret = 0;
580 if (strcmp(msg, "recvonly") == 0)
581 *conn_mode = MGCP_CONN_RECV_ONLY;
582 else if (strcmp(msg, "sendrecv") == 0)
583 *conn_mode = MGCP_CONN_RECV_SEND;
584 else {
585 DEBUGP(DMGCP, "Unknown connection mode: '%s'\n", msg);
586 ret = -1;
587 }
588
589 return ret;
590}
591
592static void handle_create_con(struct msgb *msg, struct sockaddr_in *source)
593{
594 struct mgcp_msg_ptr data_ptrs[6];
595 int found, i, line_start;
596 const char *trans_id;
597 struct mgcp_endpoint *endp;
598 int error_code = 500;
599
600 found = analyze_header(msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
601 if (found != 0)
602 return send_response(500, "CRCX", trans_id, source);
603
604 if (endp->ci != CI_UNUSED) {
605 DEBUGP(DMGCP, "Endpoint is already used. 0x%x\n", ENDPOINT_NUMBER(endp));
606 return send_response(500, "CRCX", trans_id, source);
607 }
608
609 /* parse CallID C: and LocalParameters L: */
610 MSG_TOKENIZE_START
611 switch (msg->l3h[line_start]) {
612 case 'L':
613 endp->local_options = talloc_strdup(endpoints,
614 (const char *)&msg->l3h[line_start + 3]);
615 break;
616 case 'C':
617 endp->callid = talloc_strdup(endpoints,
618 (const char *)&msg->l3h[line_start + 3]);
619 break;
620 case 'M':
621 if (parse_conn_mode((const char *)&msg->l3h[line_start + 3],
622 &endp->conn_mode) != 0) {
623 error_code = 517;
624 goto error2;
625 }
626 break;
627 default:
628 DEBUGP(DMGCP, "Unhandled option: '%c'/%d on 0x%x\n",
629 msg->l3h[line_start], msg->l3h[line_start],
630 ENDPOINT_NUMBER(endp));
631 break;
632 }
633 MSG_TOKENIZE_END
634
635
636 /* bind to the port now */
637 endp->rtp_port = rtp_calculate_port(ENDPOINT_NUMBER(endp), rtp_base_port);
638 if (!early_bind && bind_rtp(endp) != 0)
639 goto error2;
640
641 /* assign a local call identifier or fail */
642 endp->ci = generate_call_id();
643 if (endp->ci == CI_UNUSED)
644 goto error2;
645
646 DEBUGP(DMGCP, "Creating endpoint on: 0x%x CI: %u port: %u\n",
647 ENDPOINT_NUMBER(endp), endp->ci, endp->rtp_port);
648 return send_with_sdp(endp, "CRCX", trans_id, source);
649error:
650 DEBUGP(DMGCP, "Malformed line: %s on 0x%x with: line_start: %d %d\n",
651 hexdump(msg->l3h, msgb_l3len(msg)),
652 ENDPOINT_NUMBER(endp), line_start, i);
653 return send_response(error_code, "CRCX", trans_id, source);
654
655error2:
656 DEBUGP(DMGCP, "Resource error on 0x%x\n", ENDPOINT_NUMBER(endp));
657 return send_response(error_code, "CRCX", trans_id, source);
658}
659
660static void handle_modify_con(struct msgb *msg, struct sockaddr_in *source)
661{
662 struct mgcp_msg_ptr data_ptrs[6];
663 int found, i, line_start;
664 const char *trans_id;
665 struct mgcp_endpoint *endp;
666 int error_code = 500;
667
668 found = analyze_header(msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
669 if (found != 0)
670 return send_response(error_code, "MDCX", trans_id, source);
671
672 if (endp->ci == CI_UNUSED) {
673 DEBUGP(DMGCP, "Endpoint is not holding a connection. 0x%x\n", ENDPOINT_NUMBER(endp));
674 return send_response(error_code, "MDCX", trans_id, source);
675 }
676
677 MSG_TOKENIZE_START
678 switch (msg->l3h[line_start]) {
679 case 'C': {
680 if (verify_call_id(endp, (const char *)&msg->l3h[line_start + 3]) != 0)
681 goto error3;
682 break;
683 }
684 case 'I': {
685 if (verify_ci(endp, (const char *)&msg->l3h[line_start + 3]) != 0)
686 goto error3;
687 break;
688 }
689 case 'L':
690 /* skip */
691 break;
692 case 'M':
693 if (parse_conn_mode((const char *)&msg->l3h[line_start + 3],
694 &endp->conn_mode) != 0) {
695 error_code = 517;
696 goto error3;
697 }
698 break;
699 case '\0':
700 /* SDP file begins */
701 break;
702 case 'a':
703 case 'o':
704 case 's':
705 case 't':
706 case 'v':
707 /* skip these SDP attributes */
708 break;
709 case 'm': {
710 int port;
711 const char *param = (const char *)&msg->l3h[line_start];
712
713 if (sscanf(param, "m=audio %d RTP/AVP %*d", &port) == 1) {
714 endp->rtp = htons(port);
715 endp->rtcp = htons(port + 1);
716 }
717 break;
718 }
719 case 'c': {
720 char ipv4[16];
721 const char *param = (const char *)&msg->l3h[line_start];
722
723 if (sscanf(param, "c=IN IP4 %15s", ipv4) == 1) {
724 inet_aton(ipv4, &endp->remote);
725 }
726 break;
727 }
728 default:
729 DEBUGP(DMGCP, "Unhandled option: '%c'/%d on 0x%x\n",
730 msg->l3h[line_start], msg->l3h[line_start],
731 ENDPOINT_NUMBER(endp));
732 break;
733 }
734 MSG_TOKENIZE_END
735
736 /* modify */
737 DEBUGP(DMGCP, "Modified endpoint on: 0x%x Server: %s:%u\n",
738 ENDPOINT_NUMBER(endp), inet_ntoa(endp->remote), endp->rtp);
739 return send_with_sdp(endp, "MDCX", trans_id, source);
740
741error:
742 DEBUGP(DMGCP, "Malformed line: %s on 0x%x with: line_start: %d %d %d\n",
743 hexdump(msg->l3h, msgb_l3len(msg)),
744 ENDPOINT_NUMBER(endp), line_start, i, msg->l3h[line_start]);
745 return send_response(error_code, "MDCX", trans_id, source);
746
747error3:
748 return send_response(error_code, "MDCX", trans_id, source);
749}
750
751static void handle_delete_con(struct msgb *msg, struct sockaddr_in *source)
752{
753 struct mgcp_msg_ptr data_ptrs[6];
754 int found, i, line_start;
755 const char *trans_id;
756 struct mgcp_endpoint *endp;
757 int error_code = 500;
758
759 found = analyze_header(msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
760 if (found != 0)
761 return send_response(error_code, "DLCX", trans_id, source);
762
763 if (endp->ci == CI_UNUSED) {
764 DEBUGP(DMGCP, "Endpoint is not used. 0x%x\n", ENDPOINT_NUMBER(endp));
765 return send_response(error_code, "DLCX", trans_id, source);
766 }
767
768 MSG_TOKENIZE_START
769 switch (msg->l3h[line_start]) {
770 case 'C': {
771 if (verify_call_id(endp, (const char *)&msg->l3h[line_start + 3]) != 0)
772 goto error3;
773 break;
774 }
775 case 'I': {
776 if (verify_ci(endp, (const char *)&msg->l3h[line_start + 3]) != 0)
777 goto error3;
778 break;
779 }
780 default:
781 DEBUGP(DMGCP, "Unhandled option: '%c'/%d on 0x%x\n",
782 msg->l3h[line_start], msg->l3h[line_start],
783 ENDPOINT_NUMBER(endp));
784 break;
785 }
786 MSG_TOKENIZE_END
787
788
789 /* free the connection */
790 DEBUGP(DMGCP, "Deleting endpoint on: 0x%x\n", ENDPOINT_NUMBER(endp));
791 endp->ci= CI_UNUSED;
792 talloc_free(endp->callid);
793 talloc_free(endp->local_options);
794
795 if (!early_bind) {
796 bsc_unregister_fd(&endp->local_rtp);
797 bsc_unregister_fd(&endp->local_rtcp);
798 }
799
800 return send_response(250, "DLCX", trans_id, source);
801
802error:
803 DEBUGP(DMGCP, "Malformed line: %s on 0x%x with: line_start: %d %d\n",
804 hexdump(msg->l3h, msgb_l3len(msg)),
805 ENDPOINT_NUMBER(endp), line_start, i);
806 return send_response(error_code, "DLCX", trans_id, source);
807
808error3:
809 return send_response(error_code, "DLCX", trans_id, source);
810}
811
812static void print_help()
813{
814 printf("Some useful help...\n");
815 printf(" -h --help is printing this text.\n");
816 printf(" -c --config-file filename The config file to use.\n");
817}
818
819static void handle_options(int argc, char** argv)
820{
821 while (1) {
822 int option_index = 0, c;
823 static struct option long_options[] = {
824 {"help", 0, 0, 'h'},
825 {"config-file", 1, 0, 'c'},
826 {0, 0, 0, 0},
827 };
828
829 c = getopt_long(argc, argv, "hc:", long_options, &option_index);
830
831 if (c == -1)
832 break;
833
834 switch(c) {
835 case 'h':
836 print_help();
837 exit(0);
838 break;
839 case 'c':
840 config_file = talloc_strdup(tall_bsc_ctx, optarg);
841 break;
842 default:
843 /* ignore */
844 break;
845 };
846 }
847}
848
849static int read_call_agent(struct bsc_fd *fd, unsigned int what)
850{
851 struct sockaddr_in addr;
852 socklen_t slen = sizeof(addr);
853 struct msgb *msg;
854
855 msg = (struct msgb *) fd->data;
856
857 /* read one less so we can use it as a \0 */
858 int rc = recvfrom(bfd.fd, msg->data, msg->data_len - 1, 0,
859 (struct sockaddr *) &addr, &slen);
860 if (rc < 0) {
861 perror("Gateway failed to read");
862 return -1;
863 } else if (slen > sizeof(addr)) {
864 fprintf(stderr, "Gateway received message from outerspace: %d %d\n",
865 slen, sizeof(addr));
866 return -1;
867 }
868
869 if (first_request) {
870 first_request = 0;
871 send_rsip(&addr);
872 return 0;
873 }
874
875 /* handle message now */
876 msg->l2h = msgb_put(msg, rc);
877 handle_message(msg, &addr);
878 msgb_reset(msg);
879 return 0;
880}
881
882/*
883 * vty code for mgcp below
884 */
885struct cmd_node mgcp_node = {
886 MGCP_NODE,
887 "%s(mgcp)#",
888 1,
889};
890
891static int config_write_mgcp(struct vty *vty)
892{
893 vty_out(vty, "mgcp%s", VTY_NEWLINE);
894 if (local_ip)
895 vty_out(vty, " local ip %s%s", local_ip, VTY_NEWLINE);
896 vty_out(vty, " bts ip %s%s", bts_ip, VTY_NEWLINE);
897 vty_out(vty, " bind ip %s%s", source_addr, VTY_NEWLINE);
898 vty_out(vty, " bind port %u%s", source_port, VTY_NEWLINE);
899 vty_out(vty, " rtp base %u%s", rtp_base_port, VTY_NEWLINE);
900
901 return CMD_SUCCESS;
902}
903
904DEFUN(cfg_mgcp,
905 cfg_mgcp_cmd,
906 "mgcp",
907 "Configure the MGCP")
908{
909 vty->node = MGCP_NODE;
910 return CMD_SUCCESS;
911}
912
913DEFUN(cfg_mgcp_local_ip,
914 cfg_mgcp_local_ip_cmd,
915 "local ip IP",
916 "Set the IP to be used in SDP records")
917{
918 local_ip = talloc_strdup(tall_bsc_ctx, argv[0]);
919 return CMD_SUCCESS;
920}
921
922DEFUN(cfg_mgcp_bts_ip,
923 cfg_mgcp_bts_ip_cmd,
924 "bts ip IP",
925 "Set the IP of the BTS for RTP forwarding")
926{
927 bts_ip = talloc_strdup(tall_bsc_ctx, argv[0]);
928 inet_aton(bts_ip, &bts_in);
929 return CMD_SUCCESS;
930}
931
932DEFUN(cfg_mgcp_bind_ip,
933 cfg_mgcp_bind_ip_cmd,
934 "bind ip IP",
935 "Bind the MGCP to this local addr")
936{
937 source_addr = talloc_strdup(tall_bsc_ctx, argv[0]);
938 return CMD_SUCCESS;
939}
940
941DEFUN(cfg_mgcp_bind_port,
942 cfg_mgcp_bind_port_cmd,
943 "bind port <0-65534>",
944 "Bind the MGCP to this port")
945{
946 unsigned int port = atoi(argv[0]);
947 if (port > 65534) {
948 vty_out(vty, "%% wrong bind port '%s'%s", argv[0], VTY_NEWLINE);
949 return CMD_WARNING;
950 }
951
952 source_port = port;
953 return CMD_SUCCESS;
954}
955
956DEFUN(cfg_mgcp_bind_early,
957 cfg_mgcp_bind_early_cmd,
958 "bind early (0|1)",
959 "Bind all RTP ports early")
960{
961 unsigned int bind = atoi(argv[0]);
962 if (bind != 0 && bind != 1) {
963 vty_out(vty, "%% param must be 0 or 1.%s", VTY_NEWLINE);
964 return CMD_WARNING;
965 }
966
967 early_bind = bind == 1;
968 return CMD_SUCCESS;
969}
970
971DEFUN(cfg_mgcp_rtp_base_port,
972 cfg_mgcp_rtp_base_port_cmd,
973 "rtp base <0-65534>",
974 "Base port to use")
975{
976 unsigned int port = atoi(argv[0]);
977 if (port > 65534) {
978 vty_out(vty, "%% wrong base port '%s'%s", argv[0], VTY_NEWLINE);
979 return CMD_WARNING;
980 }
981
982 rtp_base_port = port;
983 return CMD_SUCCESS;
984}
985
986DEFUN(cfg_mgcp_sdp_payload_number,
987 cfg_mgcp_sdp_payload_number_cmd,
988 "sdp audio payload number <1-255>",
989 "Set the audio codec to use")
990{
991 unsigned int payload = atoi(argv[0]);
992 if (payload > 255) {
993 vty_out(vty, "%% wrong payload number '%s'%s", argv[0], VTY_NEWLINE);
994 return CMD_WARNING;
995 }
996
997 audio_payload = payload;
998 return CMD_SUCCESS;
999}
1000
1001DEFUN(cfg_mgcp_sdp_payload_name,
1002 cfg_mgcp_sdp_payload_name_cmd,
1003 "sdp audio payload name NAME",
1004 "Set the audio name to use")
1005{
1006 audio_name = talloc_strdup(tall_bsc_ctx, argv[0]);
1007 return CMD_SUCCESS;
1008}
1009
1010static void mgcp_vty_init()
1011{
1012 cmd_init(1);
1013 vty_init();
1014
1015 install_element(CONFIG_NODE, &cfg_mgcp_cmd);
1016 install_node(&mgcp_node, config_write_mgcp);
1017 install_default(MGCP_NODE);
1018 install_element(MGCP_NODE, &cfg_mgcp_local_ip_cmd);
1019 install_element(MGCP_NODE, &cfg_mgcp_bts_ip_cmd);
1020 install_element(MGCP_NODE, &cfg_mgcp_bind_ip_cmd);
1021 install_element(MGCP_NODE, &cfg_mgcp_bind_port_cmd);
1022 install_element(MGCP_NODE, &cfg_mgcp_bind_early_cmd);
1023 install_element(MGCP_NODE, &cfg_mgcp_rtp_base_port_cmd);
1024 install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_number_cmd);
1025 install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_name_cmd);
1026}
1027
1028int main(int argc, char** argv)
1029{
1030 struct sockaddr_in addr;
1031 int on = 1, i, rc;
1032
1033 tall_bsc_ctx = talloc_named_const(NULL, 1, "mgcp-callagent");
1034 handle_options(argc, argv);
1035
1036 mgcp_vty_init();
1037 rc = vty_read_config_file(config_file);
1038 if (rc < 0) {
1039 fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
1040 return rc;
1041 }
1042
1043
1044 if (!bts_ip) {
1045 fprintf(stderr, "Need to specify the BTS ip address for RTP handling.\n");
1046 return -1;
1047 }
1048
1049 endpoints = _talloc_zero_array(tall_bsc_ctx,
1050 sizeof(struct mgcp_endpoint),
1051 number_endpoints, "endpoints");
1052 if (!endpoints) {
1053 fprintf(stderr, "Failed to allocate endpoints: %d. Quitting.\n", number_endpoints);
1054 return -1;
1055 }
1056
1057 /* Initialize all endpoints */
1058 for (i = 0; i < number_endpoints; ++i) {
1059 endpoints[i].local_rtp.fd = -1;
1060 endpoints[i].local_rtcp.fd = -1;
1061 endpoints[i].ci = CI_UNUSED;
1062 }
1063
1064 /* initialize the socket */
1065 bfd.when = BSC_FD_READ;
1066 bfd.cb = read_call_agent;
1067 bfd.fd = socket(AF_INET, SOCK_DGRAM, 0);
1068 if (bfd.fd < 0) {
1069 perror("Gateway failed to listen");
1070 return -1;
1071 }
1072
1073 setsockopt(bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
1074
1075 memset(&addr, 0, sizeof(addr));
1076 addr.sin_family = AF_INET;
1077 addr.sin_port = htons(source_port);
1078 inet_aton(source_addr, &addr.sin_addr);
1079
1080 if (bind(bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
1081 perror("Gateway failed to bind");
1082 return -1;
1083 }
1084
1085 bfd.data = msgb_alloc(4096, "mgcp-msg");
1086 if (!bfd.data) {
1087 fprintf(stderr, "Gateway memory error.\n");
1088 return -1;
1089 }
1090
1091
1092 if (bsc_register_fd(&bfd) != 0) {
1093 DEBUGP(DMGCP, "Failed to register the fd\n");
1094 return -1;
1095 }
1096
1097 /* initialisation */
1098 srand(time(NULL));
1099
1100 /* early bind */
1101 if (early_bind) {
1102 for (i = 1; i < number_endpoints; ++i) {
1103 struct mgcp_endpoint *endp = &endpoints[i];
1104 endp->rtp_port = rtp_calculate_port(ENDPOINT_NUMBER(endp), rtp_base_port);
1105 if (bind_rtp(endp) != 0)
1106 return -1;
1107 }
1108 }
1109
1110 /* main loop */
1111 while (1) {
1112 bsc_select_main(0);
1113 }
1114
1115
1116 return 0;
1117}