blob: ab54e62dfc5a371c6caaae04b26b5c9e2fd3c401 [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
7 * All Rights Reserved
8 *
9 * 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
12 * (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
17 * GNU Affero General Public License for more details.
18 *
19 * 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/>.
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#include <errno.h>
32
33#include <sys/socket.h>
34
Philipp Maier87bd9be2017-08-22 16:35:41 +020035#include <osmocom/mgcp/mgcp.h>
36#include <osmocom/mgcp/mgcp_internal.h>
37#include <osmocom/mgcp/vty.h>
Philipp Maierc3413882017-10-27 12:26:54 +020038#include <osmocom/mgcp/debug.h>
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020039
40#include <osmocom/core/application.h>
41#include <osmocom/core/msgb.h>
42#include <osmocom/core/talloc.h>
43#include <osmocom/core/select.h>
44#include <osmocom/core/stats.h>
45#include <osmocom/core/rate_ctr.h>
46#include <osmocom/core/logging.h>
47
48#include <osmocom/vty/telnet_interface.h>
49#include <osmocom/vty/logging.h>
50#include <osmocom/vty/ports.h>
51#include <osmocom/vty/command.h>
52#include <osmocom/vty/stats.h>
53
54#include "../../bscconfig.h"
55
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020056#define _GNU_SOURCE
57#include <getopt.h>
58
Philipp Maier87bd9be2017-08-22 16:35:41 +020059/* FIXME: Make use of the rtp proxy code */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020060
61static struct mgcp_config *cfg;
62static struct mgcp_trunk_config *reset_trunk;
63static int reset_endpoints = 0;
64static int daemonize = 0;
65
66const char *openbsc_copyright =
67 "Copyright (C) 2009-2010 Holger Freyther and On-Waves\r\n"
Philipp Maier87bd9be2017-08-22 16:35:41 +020068 "Copyright (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>\r\n"
Neels Hofmeyrf83ec562017-09-07 19:18:40 +020069 "Contributions by Daniel Willmann, Jan Lübbe, Stefan Schmidt\r\n"
70 "Dieter Spaar, Andreas Eversberg, Harald Welte\r\n\r\n"
71 "License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
72 "This is free software: you are free to change and redistribute it.\r\n"
73 "There is NO WARRANTY, to the extent permitted by law.\r\n";
74
75static char *config_file = "mgcp.cfg";
76
77/* used by msgb and mgcp */
78void *tall_bsc_ctx = NULL;
79
80static void print_help()
81{
82 printf("Some useful help...\n");
83 printf(" -h --help is printing this text.\n");
84 printf(" -c --config-file filename The config file to use.\n");
85 printf(" -s --disable-color\n");
86 printf(" -D --daemonize Fork the process into a background daemon\n");
87 printf(" -V --version Print the version number\n");
88}
89
90static void handle_options(int argc, char **argv)
91{
92 while (1) {
93 int option_index = 0, c;
94 static struct option long_options[] = {
95 {"help", 0, 0, 'h'},
96 {"config-file", 1, 0, 'c'},
97 {"daemonize", 0, 0, 'D'},
98 {"version", 0, 0, 'V'},
99 {"disable-color", 0, 0, 's'},
100 {0, 0, 0, 0},
101 };
102
103 c = getopt_long(argc, argv, "hc:VD", long_options, &option_index);
104
105 if (c == -1)
106 break;
107
108 switch(c) {
109 case 'h':
110 print_help();
111 exit(0);
112 break;
113 case 'c':
114 config_file = talloc_strdup(tall_bsc_ctx, optarg);
115 break;
116 case 's':
117 log_set_use_color(osmo_stderr_target, 0);
118 break;
119 case 'V':
120 print_version(1);
121 exit(0);
122 break;
123 case 'D':
124 daemonize = 1;
125 break;
126 default:
127 /* ignore */
128 break;
129 };
130 }
131}
132
Philipp Maier87bd9be2017-08-22 16:35:41 +0200133/* Callback function to be called when the RSIP ("Reset in Progress") mgcp
134 * command is received */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200135static int mgcp_rsip_cb(struct mgcp_trunk_config *tcfg)
136{
Philipp Maier87bd9be2017-08-22 16:35:41 +0200137 /* Set flag so that, when read_call_agent() is called next time
138 * the reset can progress */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200139 reset_endpoints = 1;
Philipp Maier87bd9be2017-08-22 16:35:41 +0200140
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200141 reset_trunk = tcfg;
142
143 return 0;
144}
145
146static int read_call_agent(struct osmo_fd *fd, unsigned int what)
147{
148 struct sockaddr_in addr;
149 socklen_t slen = sizeof(addr);
150 struct msgb *msg;
151 struct msgb *resp;
152 int i;
153
154 msg = (struct msgb *) fd->data;
155
156 /* read one less so we can use it as a \0 */
157 int rc = recvfrom(cfg->gw_fd.bfd.fd, msg->data, msg->data_len - 1, 0,
158 (struct sockaddr *) &addr, &slen);
159 if (rc < 0) {
160 perror("Gateway failed to read");
161 return -1;
162 } else if (slen > sizeof(addr)) {
163 fprintf(stderr, "Gateway received message from outerspace: %zu %zu\n",
164 (size_t) slen, sizeof(addr));
165 return -1;
166 }
167
168 /* handle message now */
169 msg->l2h = msgb_put(msg, rc);
170 resp = mgcp_handle_message(cfg, msg);
171 msgb_reset(msg);
172
173 if (resp) {
174 sendto(cfg->gw_fd.bfd.fd, resp->l2h, msgb_l2len(resp), 0, (struct sockaddr *) &addr, sizeof(addr));
175 msgb_free(resp);
176 }
177
Philipp Maier87bd9be2017-08-22 16:35:41 +0200178 /* reset endpoints */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200179 if (reset_endpoints) {
180 LOGP(DLMGCP, LOGL_NOTICE,
181 "Asked to reset endpoints: %d/%d\n",
182 reset_trunk->trunk_nr, reset_trunk->trunk_type);
Philipp Maier87bd9be2017-08-22 16:35:41 +0200183
184 /* reset flag */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200185 reset_endpoints = 0;
186
Philipp Maier87bd9be2017-08-22 16:35:41 +0200187 /* Walk over all endpoints and trigger a release, this will release all
188 * endpoints, possible open connections are forcefully dropped */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200189 for (i = 1; i < reset_trunk->number_endpoints; ++i)
190 mgcp_release_endp(&reset_trunk->endpoints[i]);
191 }
192
193 return 0;
194}
195
196int mgcp_vty_is_config_node(struct vty *vty, int node)
197{
198 switch (node) {
199 case CONFIG_NODE:
200 return 0;
201
202 default:
203 return 1;
204 }
205}
206
207int mgcp_vty_go_parent(struct vty *vty)
208{
209 switch (vty->node) {
210 case TRUNK_NODE:
211 vty->node = MGCP_NODE;
212 vty->index = NULL;
213 break;
214 case MGCP_NODE:
215 default:
216 if (mgcp_vty_is_config_node(vty, vty->node))
217 vty->node = CONFIG_NODE;
218 else
219 vty->node = ENABLE_NODE;
220
221 vty->index = NULL;
222 }
223
224 return vty->node;
225}
226
227
228static struct vty_app_info vty_info = {
Philipp Maier87bd9be2017-08-22 16:35:41 +0200229 .name = "OsmoMGW",
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200230 .version = PACKAGE_VERSION,
231 .go_parent_cb = mgcp_vty_go_parent,
232 .is_config_node = mgcp_vty_is_config_node,
233};
234
235static const struct log_info_cat log_categories[] = {
236 /* DLMGCP is provided by the MGCP library */
Philipp Maierc3413882017-10-27 12:26:54 +0200237 [DRTP] = {
238 .name = "DRTP",
239 .description = "RTP stream handling",
240 .color = "\033[1;30m",
241 .enabled = 1,.loglevel = LOGL_NOTICE,
242 },
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200243};
244
245const struct log_info log_info = {
246 .cat = log_categories,
247 .num_cat = ARRAY_SIZE(log_categories),
248};
249
250int main(int argc, char **argv)
251{
252 struct sockaddr_in addr;
253 int on = 1, rc;
254
255 tall_bsc_ctx = talloc_named_const(NULL, 1, "mgcp-callagent");
256 msgb_talloc_ctx_init(tall_bsc_ctx, 0);
257
258 osmo_init_ignore_signals();
259 osmo_init_logging(&log_info);
260
261 cfg = mgcp_config_alloc();
262 if (!cfg)
263 return -1;
264
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200265 vty_info.copyright = openbsc_copyright;
266 vty_init(&vty_info);
267 logging_vty_add_cmds(NULL);
268 osmo_stats_vty_add_cmds(&log_info);
269 mgcp_vty_init();
270
271 handle_options(argc, argv);
272
273 rate_ctr_init(tall_bsc_ctx);
274 osmo_stats_init(tall_bsc_ctx);
275
276 rc = mgcp_parse_config(config_file, cfg, MGCP_BSC);
277 if (rc < 0)
278 return rc;
279
280 /* start telnet after reading config for vty_get_bind_addr() */
281 rc = telnet_init_dynif(tall_bsc_ctx, NULL,
282 vty_get_bind_addr(), OSMO_VTY_PORT_BSC_MGCP);
283 if (rc < 0)
284 return rc;
285
Philipp Maier87bd9be2017-08-22 16:35:41 +0200286 /* Set the reset callback function. This functions is called when the
287 * mgcp-command "RSIP" (Reset in Progress) is received */
Neels Hofmeyrf83ec562017-09-07 19:18:40 +0200288 cfg->reset_cb = mgcp_rsip_cb;
289
290 /* we need to bind a socket */
291 if (rc == 0) {
292 cfg->gw_fd.bfd.when = BSC_FD_READ;
293 cfg->gw_fd.bfd.cb = read_call_agent;
294 cfg->gw_fd.bfd.fd = socket(AF_INET, SOCK_DGRAM, 0);
295 if (cfg->gw_fd.bfd.fd < 0) {
296 perror("Gateway failed to listen");
297 return -1;
298 }
299
300 setsockopt(cfg->gw_fd.bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
301
302 memset(&addr, 0, sizeof(addr));
303 addr.sin_family = AF_INET;
304 addr.sin_port = htons(cfg->source_port);
305 inet_aton(cfg->source_addr, &addr.sin_addr);
306
307 if (bind(cfg->gw_fd.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
308 perror("Gateway failed to bind");
309 return -1;
310 }
311
312 cfg->gw_fd.bfd.data = msgb_alloc(4096, "mgcp-msg");
313 if (!cfg->gw_fd.bfd.data) {
314 fprintf(stderr, "Gateway memory error.\n");
315 return -1;
316 }
317
318 if (cfg->call_agent_addr) {
319 addr.sin_port = htons(2727);
320 inet_aton(cfg->call_agent_addr, &addr.sin_addr);
321 if (connect(cfg->gw_fd.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
322 LOGP(DLMGCP, LOGL_ERROR, "Failed to connect to: '%s'. errno: %d\n",
323 cfg->call_agent_addr, errno);
324 close(cfg->gw_fd.bfd.fd);
325 cfg->gw_fd.bfd.fd = -1;
326 return -1;
327 }
328 }
329
330 if (osmo_fd_register(&cfg->gw_fd.bfd) != 0) {
331 LOGP(DLMGCP, LOGL_FATAL, "Failed to register the fd\n");
332 return -1;
333 }
334
335 LOGP(DLMGCP, LOGL_NOTICE, "Configured for MGCP.\n");
336 }
337
338 /* initialisation */
339 srand(time(NULL));
340
341 if (daemonize) {
342 rc = osmo_daemonize();
343 if (rc < 0) {
344 perror("Error during daemonize");
345 exit(1);
346 }
347 }
348
349 /* main loop */
350 while (1) {
351 osmo_select_main(0);
352 }
353
354
355 return 0;
356}