blob: 227fc78b73b76f7e828260d61741965d2136b53f [file] [log] [blame]
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02001/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
2/* The main method to drive it as a standalone process */
3
4/*
5 * (C) 2009-2011 by Holger Hans Peter Freyther <zecke@selfish.org>
6 * (C) 2009-2011 by On-Waves
Harald Weltea896f9c2017-11-17 15:08:41 +01007 * (C) 2017 by sysmocom - s.f.m.c. GmbH, Author: Philipp Maier
Neels Hofmeyrf83ec562017-09-07 19:18:40 +02008 * All Rights Reserved
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU Affero General Public License as published by
12 * the Free Software Foundation; either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Affero General Public License for more details.
19 *
20 * You should have received a copy of the GNU Affero General Public License
21 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 *
23 */
24
25#include <ctype.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include <time.h>
30#include <limits.h>
31#include <unistd.h>
32#include <errno.h>
33
34#include <sys/socket.h>
35
Philipp Maier87bd9be2017-08-22 16:35:41 +020036#include <osmocom/mgcp/mgcp.h>
37#include <osmocom/mgcp/mgcp_internal.h>
38#include <osmocom/mgcp/vty.h>
Philipp Maierc3413882017-10-27 12:26:54 +020039#include <osmocom/mgcp/debug.h>
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020040
41#include <osmocom/core/application.h>
42#include <osmocom/core/msgb.h>
43#include <osmocom/core/talloc.h>
44#include <osmocom/core/select.h>
45#include <osmocom/core/stats.h>
46#include <osmocom/core/rate_ctr.h>
47#include <osmocom/core/logging.h>
48
49#include <osmocom/vty/telnet_interface.h>
50#include <osmocom/vty/logging.h>
51#include <osmocom/vty/ports.h>
52#include <osmocom/vty/command.h>
53#include <osmocom/vty/stats.h>
54
55#include "../../bscconfig.h"
56
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020057#define _GNU_SOURCE
58#include <getopt.h>
59
Philipp Maier87bd9be2017-08-22 16:35:41 +020060/* FIXME: Make use of the rtp proxy code */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020061
62static struct mgcp_config *cfg;
63static struct mgcp_trunk_config *reset_trunk;
64static int reset_endpoints = 0;
65static int daemonize = 0;
66
Harald Welte839fc952017-11-17 20:52:44 +010067const char *osmomgw_copyright =
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020068 "Copyright (C) 2009-2010 Holger Freyther and On-Waves\r\n"
Philipp Maier87bd9be2017-08-22 16:35:41 +020069 "Copyright (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>\r\n"
Harald Welte839fc952017-11-17 20:52:44 +010070 "Contributions by Pablo Neira Ayuso, Jacob Erlbeck, Neels Hofmeyr\r\n"
71 "Philipp Maier\r\n\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020072 "License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
73 "This is free software: you are free to change and redistribute it.\r\n"
74 "There is NO WARRANTY, to the extent permitted by law.\r\n";
75
Harald Welted164b052017-11-17 15:10:52 +010076static char *config_file = "osmo-mgw.cfg";
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020077
78/* used by msgb and mgcp */
79void *tall_bsc_ctx = NULL;
80
81static void print_help()
82{
83 printf("Some useful help...\n");
84 printf(" -h --help is printing this text.\n");
85 printf(" -c --config-file filename The config file to use.\n");
86 printf(" -s --disable-color\n");
87 printf(" -D --daemonize Fork the process into a background daemon\n");
88 printf(" -V --version Print the version number\n");
89}
90
91static void handle_options(int argc, char **argv)
92{
93 while (1) {
94 int option_index = 0, c;
95 static struct option long_options[] = {
96 {"help", 0, 0, 'h'},
97 {"config-file", 1, 0, 'c'},
98 {"daemonize", 0, 0, 'D'},
99 {"version", 0, 0, 'V'},
100 {"disable-color", 0, 0, 's'},
101 {0, 0, 0, 0},
102 };
103
104 c = getopt_long(argc, argv, "hc:VD", long_options, &option_index);
105
106 if (c == -1)
107 break;
108
109 switch(c) {
110 case 'h':
111 print_help();
112 exit(0);
113 break;
114 case 'c':
115 config_file = talloc_strdup(tall_bsc_ctx, optarg);
116 break;
117 case 's':
118 log_set_use_color(osmo_stderr_target, 0);
119 break;
120 case 'V':
121 print_version(1);
122 exit(0);
123 break;
124 case 'D':
125 daemonize = 1;
126 break;
127 default:
128 /* ignore */
129 break;
130 };
131 }
132}
133
Philipp Maier87bd9be2017-08-22 16:35:41 +0200134/* Callback function to be called when the RSIP ("Reset in Progress") mgcp
135 * command is received */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200136static int mgcp_rsip_cb(struct mgcp_trunk_config *tcfg)
137{
Philipp Maier87bd9be2017-08-22 16:35:41 +0200138 /* Set flag so that, when read_call_agent() is called next time
139 * the reset can progress */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200140 reset_endpoints = 1;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200141
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200142 reset_trunk = tcfg;
143
144 return 0;
145}
146
147static int read_call_agent(struct osmo_fd *fd, unsigned int what)
148{
149 struct sockaddr_in addr;
150 socklen_t slen = sizeof(addr);
151 struct msgb *msg;
152 struct msgb *resp;
153 int i;
154
155 msg = (struct msgb *) fd->data;
156
157 /* read one less so we can use it as a \0 */
158 int rc = recvfrom(cfg->gw_fd.bfd.fd, msg->data, msg->data_len - 1, 0,
159 (struct sockaddr *) &addr, &slen);
160 if (rc < 0) {
161 perror("Gateway failed to read");
162 return -1;
163 } else if (slen > sizeof(addr)) {
164 fprintf(stderr, "Gateway received message from outerspace: %zu %zu\n",
165 (size_t) slen, sizeof(addr));
166 return -1;
167 }
168
169 /* handle message now */
170 msg->l2h = msgb_put(msg, rc);
171 resp = mgcp_handle_message(cfg, msg);
172 msgb_reset(msg);
173
174 if (resp) {
175 sendto(cfg->gw_fd.bfd.fd, resp->l2h, msgb_l2len(resp), 0, (struct sockaddr *) &addr, sizeof(addr));
176 msgb_free(resp);
177 }
178
Philipp Maier87bd9be2017-08-22 16:35:41 +0200179 /* reset endpoints */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200180 if (reset_endpoints) {
181 LOGP(DLMGCP, LOGL_NOTICE,
182 "Asked to reset endpoints: %d/%d\n",
183 reset_trunk->trunk_nr, reset_trunk->trunk_type);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200184
185 /* reset flag */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200186 reset_endpoints = 0;
187
Philipp Maier87bd9be2017-08-22 16:35:41 +0200188 /* Walk over all endpoints and trigger a release, this will release all
189 * endpoints, possible open connections are forcefully dropped */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200190 for (i = 1; i < reset_trunk->number_endpoints; ++i)
191 mgcp_release_endp(&reset_trunk->endpoints[i]);
192 }
193
194 return 0;
195}
196
197int mgcp_vty_is_config_node(struct vty *vty, int node)
198{
Harald Welte9bf7c532017-11-17 14:14:31 +0100199 switch (node) {
200 case CONFIG_NODE:
201 return 0;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200202
Harald Welte9bf7c532017-11-17 14:14:31 +0100203 default:
204 return 1;
205 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200206}
207
208int mgcp_vty_go_parent(struct vty *vty)
209{
Harald Welte9bf7c532017-11-17 14:14:31 +0100210 switch (vty->node) {
211 case TRUNK_NODE:
212 vty->node = MGCP_NODE;
213 vty->index = NULL;
214 break;
215 case MGCP_NODE:
216 default:
217 if (mgcp_vty_is_config_node(vty, vty->node))
218 vty->node = CONFIG_NODE;
219 else
220 vty->node = ENABLE_NODE;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200221
Harald Welte9bf7c532017-11-17 14:14:31 +0100222 vty->index = NULL;
223 }
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200224
Harald Welte9bf7c532017-11-17 14:14:31 +0100225 return vty->node;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200226}
227
228
229static struct vty_app_info vty_info = {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200230 .name = "OsmoMGW",
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200231 .version = PACKAGE_VERSION,
232 .go_parent_cb = mgcp_vty_go_parent,
233 .is_config_node = mgcp_vty_is_config_node,
234};
235
236static const struct log_info_cat log_categories[] = {
237 /* DLMGCP is provided by the MGCP library */
Philipp Maierc3413882017-10-27 12:26:54 +0200238 [DRTP] = {
239 .name = "DRTP",
240 .description = "RTP stream handling",
241 .color = "\033[1;30m",
242 .enabled = 1,.loglevel = LOGL_NOTICE,
243 },
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200244};
245
246const struct log_info log_info = {
Harald Welte9bf7c532017-11-17 14:14:31 +0100247 .cat = log_categories,
248 .num_cat = ARRAY_SIZE(log_categories),
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200249};
250
251int main(int argc, char **argv)
252{
253 struct sockaddr_in addr;
254 int on = 1, rc;
255
256 tall_bsc_ctx = talloc_named_const(NULL, 1, "mgcp-callagent");
257 msgb_talloc_ctx_init(tall_bsc_ctx, 0);
258
259 osmo_init_ignore_signals();
260 osmo_init_logging(&log_info);
261
262 cfg = mgcp_config_alloc();
263 if (!cfg)
264 return -1;
265
Harald Welte839fc952017-11-17 20:52:44 +0100266 vty_info.copyright = osmomgw_copyright;
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200267 vty_init(&vty_info);
268 logging_vty_add_cmds(NULL);
269 osmo_stats_vty_add_cmds(&log_info);
270 mgcp_vty_init();
271
272 handle_options(argc, argv);
273
274 rate_ctr_init(tall_bsc_ctx);
275 osmo_stats_init(tall_bsc_ctx);
276
277 rc = mgcp_parse_config(config_file, cfg, MGCP_BSC);
278 if (rc < 0)
279 return rc;
280
281 /* start telnet after reading config for vty_get_bind_addr() */
282 rc = telnet_init_dynif(tall_bsc_ctx, NULL,
Philipp Maier9a3543a2017-11-14 15:23:40 +0100283 vty_get_bind_addr(), OSMO_VTY_PORT_MGW);
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200284 if (rc < 0)
285 return rc;
286
Philipp Maier87bd9be2017-08-22 16:35:41 +0200287 /* Set the reset callback function. This functions is called when the
288 * mgcp-command "RSIP" (Reset in Progress) is received */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200289 cfg->reset_cb = mgcp_rsip_cb;
290
Harald Welte9bf7c532017-11-17 14:14:31 +0100291 /* we need to bind a socket */
292 if (rc == 0) {
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200293 cfg->gw_fd.bfd.when = BSC_FD_READ;
294 cfg->gw_fd.bfd.cb = read_call_agent;
295 cfg->gw_fd.bfd.fd = socket(AF_INET, SOCK_DGRAM, 0);
296 if (cfg->gw_fd.bfd.fd < 0) {
297 perror("Gateway failed to listen");
298 return -1;
299 }
300
301 setsockopt(cfg->gw_fd.bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
302
303 memset(&addr, 0, sizeof(addr));
304 addr.sin_family = AF_INET;
305 addr.sin_port = htons(cfg->source_port);
306 inet_aton(cfg->source_addr, &addr.sin_addr);
307
308 if (bind(cfg->gw_fd.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
309 perror("Gateway failed to bind");
310 return -1;
311 }
312
313 cfg->gw_fd.bfd.data = msgb_alloc(4096, "mgcp-msg");
314 if (!cfg->gw_fd.bfd.data) {
315 fprintf(stderr, "Gateway memory error.\n");
316 return -1;
317 }
318
319 if (cfg->call_agent_addr) {
320 addr.sin_port = htons(2727);
321 inet_aton(cfg->call_agent_addr, &addr.sin_addr);
322 if (connect(cfg->gw_fd.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
323 LOGP(DLMGCP, LOGL_ERROR, "Failed to connect to: '%s'. errno: %d\n",
324 cfg->call_agent_addr, errno);
325 close(cfg->gw_fd.bfd.fd);
326 cfg->gw_fd.bfd.fd = -1;
327 return -1;
328 }
329 }
330
331 if (osmo_fd_register(&cfg->gw_fd.bfd) != 0) {
332 LOGP(DLMGCP, LOGL_FATAL, "Failed to register the fd\n");
333 return -1;
334 }
335
336 LOGP(DLMGCP, LOGL_NOTICE, "Configured for MGCP.\n");
337 }
338
339 /* initialisation */
340 srand(time(NULL));
341
342 if (daemonize) {
343 rc = osmo_daemonize();
344 if (rc < 0) {
345 perror("Error during daemonize");
346 exit(1);
347 }
348 }
349
350 /* main loop */
351 while (1) {
352 osmo_select_main(0);
353 }
354
355
356 return 0;
357}