blob: 6025088544ba558406a8f917b546c9b0e65e36ed [file] [log] [blame]
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +02001/* (C) 2015 by sysmocom s.f.m.c. GmbH
2 * All Rights Reserved
3 *
4 * Author: Neels Hofmeyr
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
15 *
16 * You should have received a copy of the GNU Affero General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21#include <string.h>
22
Neels Hofmeyrb6c2db52015-11-18 18:11:32 +010023#include <ares.h>
24#include <sys/socket.h>
25#include <netinet/in.h>
26#include <arpa/inet.h>
27
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +020028#include <osmocom/core/talloc.h>
29#include <osmocom/vty/command.h>
Neels Hofmeyr4b2cbda2015-11-20 03:16:19 +010030#include <osmocom/vty/misc.h>
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +020031
32#include <openbsc/vty.h>
33#include <openbsc/gtphub.h>
34
Neels Hofmeyrb6c2db52015-11-18 18:11:32 +010035/* TODO split GRX ares from sgsn into a separate struct and allow use without
36 * globals. */
37#include <openbsc/sgsn.h>
38extern struct sgsn_instance *sgsn;
39
Neels Hofmeyr4b2cbda2015-11-20 03:16:19 +010040static struct gtphub *g_hub = 0;
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +020041static struct gtphub_cfg *g_cfg = 0;
42
43static struct cmd_node gtphub_node = {
44 GTPHUB_NODE,
45 "%s(config-gtphub)# ",
46 1,
47};
48
49#define GTPH_DEFAULT_CONTROL_PORT 2123
50#define GTPH_DEFAULT_USER_PORT 2152
51
52static void write_addrs(struct vty *vty, const char *name,
53 struct gtphub_cfg_addr *c, struct gtphub_cfg_addr *u)
54{
55 if ((c->port == GTPH_DEFAULT_CONTROL_PORT)
56 && (u->port == GTPH_DEFAULT_USER_PORT)
57 && (strcmp(c->addr_str, u->addr_str) == 0)) {
58 /* Default port numbers and same IP address: write "short"
59 * variant. */
60 vty_out(vty, " %s %s%s",
61 name,
62 c->addr_str,
63 VTY_NEWLINE);
64 return;
65 }
66
67 vty_out(vty, " %s ctrl %s %d user %s %d%s",
68 name,
69 c->addr_str, (int)c->port,
70 u->addr_str, (int)u->port,
71 VTY_NEWLINE);
Neels Hofmeyrb6c2db52015-11-18 18:11:32 +010072
73 struct ares_addr_node *server;
74 for (server = sgsn->ares_servers; server; server = server->next)
75 vty_out(vty, " grx-dns-add %s%s", inet_ntoa(server->addr.addr4), VTY_NEWLINE);
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +020076}
77
78static int config_write_gtphub(struct vty *vty)
79{
80 vty_out(vty, "gtphub%s", VTY_NEWLINE);
81
82 write_addrs(vty, "bind-to-sgsns",
83 &g_cfg->to_sgsns[GTPH_PLANE_CTRL].bind,
84 &g_cfg->to_sgsns[GTPH_PLANE_USER].bind);
85
86 write_addrs(vty, "bind-to-ggsns",
87 &g_cfg->to_ggsns[GTPH_PLANE_CTRL].bind,
88 &g_cfg->to_ggsns[GTPH_PLANE_USER].bind);
89
90 if (g_cfg->ggsn_proxy[GTPH_PLANE_CTRL].addr_str) {
91 write_addrs(vty, "ggsn-proxy",
92 &g_cfg->ggsn_proxy[GTPH_PLANE_CTRL],
93 &g_cfg->ggsn_proxy[GTPH_PLANE_USER]);
94 }
95
96 return CMD_SUCCESS;
97}
98
99DEFUN(cfg_gtphub, cfg_gtphub_cmd,
100 "gtphub",
101 "Configure the GTP hub")
102{
103 vty->node = GTPHUB_NODE;
104 return CMD_SUCCESS;
105}
106
107#define BIND_ARGS "ctrl ADDR <0-65535> user ADDR <0-65535>"
108#define BIND_DOCS \
109 "Set GTP-C bind\n" \
110 "GTP-C local IP address (v4 or v6)\n" \
111 "GTP-C local port\n" \
112 "Set GTP-U bind\n" \
113 "GTP-U local IP address (v4 or v6)\n" \
114 "GTP-U local port\n"
115
116
117DEFUN(cfg_gtphub_bind_to_sgsns_short, cfg_gtphub_bind_to_sgsns_short_cmd,
118 "bind-to-sgsns ADDR",
119 "GTP Hub Parameters\n"
120 "Set the local bind address to listen for SGSNs, for both GTP-C and GTP-U\n"
121 "Local IP address (v4 or v6)\n"
122 )
123{
124 int i;
125 for (i = 0; i < GTPH_PLANE_N; i++)
126 g_cfg->to_sgsns[i].bind.addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
127 g_cfg->to_sgsns[GTPH_PLANE_CTRL].bind.port = GTPH_DEFAULT_CONTROL_PORT;
128 g_cfg->to_sgsns[GTPH_PLANE_USER].bind.port = GTPH_DEFAULT_USER_PORT;
129 return CMD_SUCCESS;
130}
131
132DEFUN(cfg_gtphub_bind_to_ggsns_short, cfg_gtphub_bind_to_ggsns_short_cmd,
133 "bind-to-ggsns ADDR",
134 "GTP Hub Parameters\n"
135 "Set the local bind address to listen for GGSNs, for both GTP-C and GTP-U\n"
136 "Local IP address (v4 or v6)\n"
137 )
138{
139 int i;
140 for (i = 0; i < GTPH_PLANE_N; i++)
141 g_cfg->to_ggsns[i].bind.addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
142 g_cfg->to_ggsns[GTPH_PLANE_CTRL].bind.port = GTPH_DEFAULT_CONTROL_PORT;
143 g_cfg->to_ggsns[GTPH_PLANE_USER].bind.port = GTPH_DEFAULT_USER_PORT;
144 return CMD_SUCCESS;
145}
146
147
148static int handle_binds(struct gtphub_cfg_bind *b, const char **argv)
149{
150 b[GTPH_PLANE_CTRL].bind.addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
151 b[GTPH_PLANE_CTRL].bind.port = atoi(argv[1]);
152 b[GTPH_PLANE_USER].bind.addr_str = talloc_strdup(tall_vty_ctx, argv[2]);
153 b[GTPH_PLANE_USER].bind.port = atoi(argv[3]);
154 return CMD_SUCCESS;
155}
156
157DEFUN(cfg_gtphub_bind_to_sgsns, cfg_gtphub_bind_to_sgsns_cmd,
158 "bind-to-sgsns " BIND_ARGS,
159 "GTP Hub Parameters\n"
160 "Set the local bind addresses and ports to listen for SGSNs\n"
161 BIND_DOCS
162 )
163{
164 return handle_binds(g_cfg->to_sgsns, argv);
165}
166
167DEFUN(cfg_gtphub_bind_to_ggsns, cfg_gtphub_bind_to_ggsns_cmd,
168 "bind-to-ggsns " BIND_ARGS,
169 "GTP Hub Parameters\n"
170 "Set the local bind addresses and ports to listen for GGSNs\n"
171 BIND_DOCS
172 )
173{
174 return handle_binds(g_cfg->to_ggsns, argv);
175}
176
177DEFUN(cfg_gtphub_ggsn_proxy_short, cfg_gtphub_ggsn_proxy_short_cmd,
178 "ggsn-proxy ADDR",
179 "GTP Hub Parameters\n"
180 "Redirect all GGSN bound traffic to default ports on this address (another gtphub)\n"
181 "Remote IP address (v4 or v6)\n"
182 )
183{
184 g_cfg->ggsn_proxy[GTPH_PLANE_CTRL].addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
185 g_cfg->ggsn_proxy[GTPH_PLANE_CTRL].port = GTPH_DEFAULT_CONTROL_PORT;
186 g_cfg->ggsn_proxy[GTPH_PLANE_USER].addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
187 g_cfg->ggsn_proxy[GTPH_PLANE_USER].port = GTPH_DEFAULT_USER_PORT;
188 return CMD_SUCCESS;
189}
190
191DEFUN(cfg_gtphub_ggsn_proxy, cfg_gtphub_ggsn_proxy_cmd,
192 "ggsn-proxy " BIND_ARGS,
193 "GTP Hub Parameters\n"
194 "Redirect all GGSN bound traffic to these addresses and ports (another gtphub)\n"
195 BIND_DOCS
196 )
197{
198 g_cfg->ggsn_proxy[GTPH_PLANE_CTRL].addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
199 g_cfg->ggsn_proxy[GTPH_PLANE_CTRL].port = atoi(argv[1]);
200 g_cfg->ggsn_proxy[GTPH_PLANE_USER].addr_str = talloc_strdup(tall_vty_ctx, argv[2]);
201 g_cfg->ggsn_proxy[GTPH_PLANE_USER].port = atoi(argv[3]);
202 return CMD_SUCCESS;
203}
204
205DEFUN(cfg_gtphub_sgsn_proxy_short, cfg_gtphub_sgsn_proxy_short_cmd,
206 "sgsn-proxy ADDR",
207 "GTP Hub Parameters\n"
208 "Redirect all SGSN bound traffic to default ports on this address (another gtphub)\n"
209 "Remote IP address (v4 or v6)\n"
210 )
211{
212 g_cfg->sgsn_proxy[GTPH_PLANE_CTRL].addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
213 g_cfg->sgsn_proxy[GTPH_PLANE_CTRL].port = GTPH_DEFAULT_CONTROL_PORT;
214 g_cfg->sgsn_proxy[GTPH_PLANE_USER].addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
215 g_cfg->sgsn_proxy[GTPH_PLANE_USER].port = GTPH_DEFAULT_USER_PORT;
216 return CMD_SUCCESS;
217}
218
219DEFUN(cfg_gtphub_sgsn_proxy, cfg_gtphub_sgsn_proxy_cmd,
220 "sgsn-proxy " BIND_ARGS,
221 "GTP Hub Parameters\n"
222 "Redirect all SGSN bound traffic to these addresses and ports (another gtphub)\n"
223 BIND_DOCS
224 )
225{
226 g_cfg->sgsn_proxy[GTPH_PLANE_CTRL].addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
227 g_cfg->sgsn_proxy[GTPH_PLANE_CTRL].port = atoi(argv[1]);
228 g_cfg->sgsn_proxy[GTPH_PLANE_USER].addr_str = talloc_strdup(tall_vty_ctx, argv[2]);
229 g_cfg->sgsn_proxy[GTPH_PLANE_USER].port = atoi(argv[3]);
230 return CMD_SUCCESS;
231}
232
Neels Hofmeyrb6c2db52015-11-18 18:11:32 +0100233
234/* Copied from sgsn_vty.h */
235DEFUN(cfg_grx_ggsn, cfg_grx_ggsn_cmd,
236 "grx-dns-add A.B.C.D",
237 "Add DNS server\nIPv4 address\n")
238{
239 struct ares_addr_node *node = talloc_zero(tall_bsc_ctx, struct ares_addr_node);
240 node->family = AF_INET;
241 inet_aton(argv[0], &node->addr.addr4);
242
243 node->next = sgsn->ares_servers;
244 sgsn->ares_servers = node;
245 return CMD_SUCCESS;
246}
247
248
Neels Hofmeyr4b2cbda2015-11-20 03:16:19 +0100249/*
250(show gtphub all, show gtphub stats, show gtphub teidmap,
251 show gtphub peers, ...)
252*/
253
254static void show_bind_stats_all(struct vty *vty)
255{
256 int plane_idx;
257 for (plane_idx = 0; plane_idx < GTPH_PLANE_N; plane_idx++) {
258 vty_out(vty, "- %s Plane:%s",
259 gtphub_plane_idx_names[plane_idx], VTY_NEWLINE);
260
261 struct gtphub_bind *b = &g_hub->to_ggsns[plane_idx];
262 vty_out(vty, " - to/from GGSNs: %s port %d%s",
263 gsn_addr_to_str(&b->local_addr), (int)b->local_port,
264 VTY_NEWLINE);
265 vty_out_rate_ctr_group(vty, " ", b->counters_io);
266
267 b = &g_hub->to_sgsns[plane_idx];
268 vty_out(vty, " - to/from SGSNs: %s port %d%s",
269 gsn_addr_to_str(&b->local_addr), (int)b->local_port,
270 VTY_NEWLINE);
271 vty_out_rate_ctr_group(vty, " ", b->counters_io);
272 }
273}
274
275/*
276static void show_peers_summary(struct vty *vty)
277{
278 int c
279 int plane_idx;
280}
281*/
282
283static void show_tei_maps_summary(struct vty *vty)
284{
285 time_t now = gtphub_now();
286
287 unsigned long long int count_all = 0; /* ...just joking. */
288
289 const int w = 36;
290 int max_expiry = g_hub->expire_tei_maps.expiry_in_seconds;
291 float seconds_per_step = ((float)max_expiry) / w;
292
293 /* Print TEI mapping expiry in an ASCII histogram, like:
294 TEI map summary
295 Legend: '_'=0 '.'<=1% ':'<=2% '|'<=10% '#'>10% (10.0 m/step)
296 CTRL: 30 mappings, valid for 360m[#__:.____|___.____:__.______________]1m
297 USER: 30 mappings, valid for 360m[#__:.____|___.____:__.______________]1m
298 4 TEI mappings in total, last expiry in 359.4 min
299 */
300 vty_out(vty,
301 "TEI map summary%s"
302 " Legend: '_'=0 '.'<=1%% ':'<=2%% '|'<=10%% '#'>10%% (%.1f m/step)%s",
303 VTY_NEWLINE,
304 seconds_per_step / 60.,
305 VTY_NEWLINE);
306
307 int last_expiry = 0;
308 int plane_idx;
309 for (plane_idx = 0; plane_idx < GTPH_PLANE_N; plane_idx++) {
310
311 unsigned int count = 0;
312
313 int histogram[w];
314 memset(histogram, 0, sizeof(histogram));
315
316 struct nr_mapping *m;
317 llist_for_each_entry(m, &g_hub->tei_map[plane_idx].mappings, entry) {
318 count ++;
319 int expiry = m->expiry_entry.expiry - now;
320 last_expiry = (last_expiry > expiry) ? last_expiry : expiry;
321
322 int hi = ((float)expiry) / seconds_per_step;
323 if (hi < 0)
324 hi = 0;
325 if (hi > (w - 1))
326 hi = w - 1;
327 histogram[hi] ++;
328 }
329
330 vty_out(vty,
331 " %4s: %u mappings, valid for %dm[",
332 gtphub_plane_idx_names[plane_idx],
333 count, max_expiry / 60);
334
335 int i;
336 for (i = w - 1; i >= 0; i--) {
337 char c;
338 int val = histogram[i];
339 int percent = 100. * val / count;
340 if (!val)
341 c = '_';
342 else if (percent <= 1)
343 c = '.';
344 else if (percent <= 2)
345 c = ':';
346 else if (percent <= 10)
347 c = '|';
348 else c = '#';
349 vty_out(vty, "%c", c);
350 }
351 vty_out(vty, "]1m%s", VTY_NEWLINE);
352
353 count_all += count;
354 }
355 vty_out(vty, " %llu TEI mappings in total, last expiry in %.1f min%s",
356 count_all,
357 ((float)last_expiry) / 60.,
358 VTY_NEWLINE);
359}
360
361static void show_tei_maps_all(struct vty *vty)
362{
363 time_t now = gtphub_now();
364
365 unsigned long long int count_all = 0; /* ...just joking. */
366
367 vty_out(vty, "All TEI mappings:%s", VTY_NEWLINE);
368 int plane_idx;
369 for (plane_idx = 0; plane_idx < GTPH_PLANE_N; plane_idx++) {
370 vty_out(vty,
371 "- %s Plane:%s"
372 " (timeout) replaced-TEI <--> original-TEI from-peer%s",
373 gtphub_plane_idx_names[plane_idx],
374 VTY_NEWLINE, VTY_NEWLINE);
375
376 unsigned int count = 0;
377 struct nr_mapping *m;
378 llist_for_each_entry(m, &g_hub->tei_map[plane_idx].mappings, entry) {
379 struct gtphub_peer_port *pp = m->origin;
380 vty_out(vty,
381 " (%4dm) %8x <--> %8x %s",
382 -(int)((m->expiry_entry.expiry - now) / 60),
383 (uint32_t)m->repl,
384 (uint32_t)m->orig,
385 gsn_addr_to_str(&pp->peer_addr->addr));
386 if (pp->port != gtphub_plane_idx_default_port[plane_idx])
387 vty_out(vty, " port %d", (int)pp->port);
388 vty_out(vty, VTY_NEWLINE);
389 count ++;
390 }
391 vty_out(vty, " (%u %s TEI mappings)%s", count,
392 gtphub_plane_idx_names[plane_idx], VTY_NEWLINE);
393 count_all += count;
394 }
395 vty_out(vty, "- %llu TEI mappings in total%s", count_all, VTY_NEWLINE);
396}
397
398DEFUN(show_gtphub_tei_summary, show_gtphub_tei_summary_cmd, "show gtphub tei summary",
399 SHOW_STR "TEI mappings summary")
400{
401 show_tei_maps_summary(vty);
402 return CMD_SUCCESS;
403}
404
405DEFUN(show_gtphub_tei_dump, show_gtphub_tei_dump_cmd, "show gtphub tei dump",
406 SHOW_STR "Dump all current TEI mappings")
407{
408 show_tei_maps_all(vty);
409 return CMD_SUCCESS;
410}
411
412DEFUN(show_gtphub, show_gtphub_cmd, "show gtphub all",
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +0200413 SHOW_STR "Display information about the GTP hub")
414{
Neels Hofmeyr4b2cbda2015-11-20 03:16:19 +0100415 show_bind_stats_all(vty);
416 show_tei_maps_summary(vty);
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +0200417 return CMD_SUCCESS;
418}
419
420
Neels Hofmeyr4b2cbda2015-11-20 03:16:19 +0100421int gtphub_vty_init(struct gtphub *global_hub, struct gtphub_cfg *global_cfg)
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +0200422{
Neels Hofmeyr4b2cbda2015-11-20 03:16:19 +0100423 g_hub = global_hub;
424 g_cfg = global_cfg;
425
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +0200426 install_element_ve(&show_gtphub_cmd);
Neels Hofmeyr4b2cbda2015-11-20 03:16:19 +0100427 install_element_ve(&show_gtphub_tei_summary_cmd);
428 install_element_ve(&show_gtphub_tei_dump_cmd);
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +0200429
430 install_element(CONFIG_NODE, &cfg_gtphub_cmd);
431 install_node(&gtphub_node, config_write_gtphub);
432 vty_install_default(GTPHUB_NODE);
433
434 install_element(GTPHUB_NODE, &cfg_gtphub_bind_to_sgsns_short_cmd);
435 install_element(GTPHUB_NODE, &cfg_gtphub_bind_to_sgsns_cmd);
436 install_element(GTPHUB_NODE, &cfg_gtphub_bind_to_ggsns_short_cmd);
437 install_element(GTPHUB_NODE, &cfg_gtphub_bind_to_ggsns_cmd);
438 install_element(GTPHUB_NODE, &cfg_gtphub_ggsn_proxy_short_cmd);
439 install_element(GTPHUB_NODE, &cfg_gtphub_ggsn_proxy_cmd);
440 install_element(GTPHUB_NODE, &cfg_gtphub_sgsn_proxy_short_cmd);
441 install_element(GTPHUB_NODE, &cfg_gtphub_sgsn_proxy_cmd);
Neels Hofmeyrb6c2db52015-11-18 18:11:32 +0100442 install_element(GTPHUB_NODE, &cfg_grx_ggsn_cmd);
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +0200443
444 return 0;
445}
446
447int gtphub_cfg_read(struct gtphub_cfg *cfg, const char *config_file)
448{
449 int rc;
450
Neels Hofmeyrc8a614d2015-09-24 17:32:30 +0200451 rc = vty_read_config_file(config_file, NULL);
452 if (rc < 0) {
453 fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
454 return rc;
455 }
456
457 return 0;
458}