blob: c92d83eed6a419294acdca5de79fcdc30521ffa2 [file] [log] [blame]
Harald Weltedda21ed2017-08-12 15:07:02 +02001/*
2 * (C) 2017 by Harald Welte <laforge@gnumonks.org>
3 * All Rights Reserved
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 */
19
20#include <string.h>
21#include <stdint.h>
22#include <inttypes.h>
23#include <netinet/in.h>
24#include <arpa/inet.h>
25
26#include <osmocom/core/talloc.h>
27#include <osmocom/core/utils.h>
28#include <osmocom/core/rate_ctr.h>
29#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
30
31#include <osmocom/vty/command.h>
32#include <osmocom/vty/vty.h>
33#include <osmocom/vty/misc.h>
34
35#include "../gtp/gtp.h"
36#include "../gtp/pdp.h"
37
38#include "ggsn.h"
39
40#define PREFIX_STR "Prefix (Network/Netmask)\n"
41#define IFCONFIG_STR "GGSN-based interface configuration\n"
42#define GGSN_STR "Gateway GPRS Support NODE (GGSN)\n"
43
44LLIST_HEAD(g_ggsn_list);
45
46enum ggsn_vty_node {
47 GGSN_NODE = _LAST_OSMOVTY_NODE + 1,
48 APN_NODE,
49};
50
51struct ggsn_ctx *ggsn_find(const char *name)
52{
53 struct ggsn_ctx *ggsn;
54
55 llist_for_each_entry(ggsn, &g_ggsn_list, list) {
56 if (!strcmp(ggsn->cfg.name, name))
57 return ggsn;
58 }
59 return NULL;
60}
61
62struct ggsn_ctx *ggsn_find_or_create(void *ctx, const char *name)
63{
64 struct ggsn_ctx *ggsn;
65
66 ggsn = ggsn_find(name);
67 if (ggsn)
68 return ggsn;
69
70 ggsn = talloc_zero(ctx, struct ggsn_ctx);
71 if (!ggsn)
72 return NULL;
73
74 ggsn->cfg.name = talloc_strdup(ggsn, name);
75 ggsn->cfg.state_dir = talloc_strdup(ggsn, "/tmp");
76 ggsn->cfg.shutdown = true;
77 INIT_LLIST_HEAD(&ggsn->apn_list);
78
79 llist_add_tail(&ggsn->list, &g_ggsn_list);
80 return ggsn;
81}
82
83struct apn_ctx *ggsn_find_apn(struct ggsn_ctx *ggsn, const char *name)
84{
85 struct apn_ctx *apn;
86
87 llist_for_each_entry(apn, &ggsn->apn_list, list) {
88 if (!strcmp(apn->cfg.name, name))
89 return apn;
90 }
91 return NULL;
92}
93
94struct apn_ctx *ggsn_find_or_create_apn(struct ggsn_ctx *ggsn, const char *name)
95{
96 struct apn_ctx *apn = ggsn_find_apn(ggsn, name);
97 if (apn)
98 return apn;
99
100 apn = talloc_zero(ggsn, struct apn_ctx);
101 if (!apn)
102 return NULL;
103 apn->ggsn = ggsn;
104 apn->cfg.name = talloc_strdup(apn, name);
105 apn->cfg.shutdown = true;
106 INIT_LLIST_HEAD(&apn->cfg.name_list);
107
108 llist_add_tail(&apn->list, &ggsn->apn_list);
109 return apn;
110}
111
112/* GGSN Node */
113
114static struct cmd_node ggsn_node = {
115 GGSN_NODE,
116 "%s(config-ggsn)# ",
117 1,
118};
119
120DEFUN(cfg_ggsn, cfg_ggsn_cmd,
121 "ggsn NAME",
122 "Configure the Gateway GPRS Support Node\n" "GGSN Name (has only local significance)\n")
123{
124 struct ggsn_ctx *ggsn;
125
126 ggsn = ggsn_find_or_create(tall_ggsn_ctx, argv[0]);
127 if (!ggsn)
128 return CMD_WARNING;
129
130 vty->node = GGSN_NODE;
131 vty->index = ggsn;
132 vty->index_sub = &ggsn->cfg.description;
133
134 return CMD_SUCCESS;
135}
136
137DEFUN(cfg_no_ggsn, cfg_no_ggsn_cmd,
138 "no ggsn NAME",
139 NO_STR "Remove the named Gateway GPRS Support Node\n"
140 "GGSN Name (has only local significance)\n")
141{
142 struct ggsn_ctx *ggsn;
143
144 ggsn = ggsn_find(argv[0]);
145 if (!ggsn) {
146 vty_out(vty, "%% No such GGSN '%s'%s", argv[0], VTY_NEWLINE);
147 return CMD_WARNING;
148 }
149
150 if (!ggsn->cfg.shutdown) {
151 vty_out(vty, "%% GGSN %s is still active, please shutdown first%s",
152 ggsn->cfg.name, VTY_NEWLINE);
153 return CMD_WARNING;
154 }
155
156 if (!llist_empty(&ggsn->apn_list)) {
157 vty_out(vty, "%% GGSN %s still has APNs configured, please remove first%s",
158 ggsn->cfg.name, VTY_NEWLINE);
159 return CMD_WARNING;
160 }
161
162 llist_del(&ggsn->list);
163 talloc_free(ggsn);
164
165 return CMD_SUCCESS;
166}
167
Harald Welte98146772017-09-05 17:41:20 +0200168DEFUN(cfg_ggsn_bind_ip, cfg_ggsn_bind_ip_cmd,
169 "gtp bind-ip A.B.C.D",
Harald Weltedda21ed2017-08-12 15:07:02 +0200170 "GTP Parameters\n"
171 "Set the IP address for the local GTP bind\n"
172 "IPv4 Address\n")
173{
174 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
175 size_t t;
176
177 ippool_aton(&ggsn->cfg.listen_addr, &t, argv[0], 0);
178
179 return CMD_SUCCESS;
180}
181
Harald Welte98146772017-09-05 17:41:20 +0200182DEFUN(cfg_ggsn_gtpc_ip, cfg_ggsn_gtpc_ip_cmd,
183 "gtp control-ip A.B.C.D",
184 "GTP Parameters\n"
185 "Set the IP address states as local IP in GTP-C messages\n"
186 "IPv4 Address\n")
187{
188 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
189 size_t t;
190
191 ippool_aton(&ggsn->cfg.gtpc_addr, &t, argv[0], 0);
192
193 return CMD_SUCCESS;
194}
195
196DEFUN(cfg_ggsn_gtpu_ip, cfg_ggsn_gtpu_ip_cmd,
197 "gtp user-ip A.B.C.D",
198 "GTP Parameters\n"
199 "Set the IP address states as local IP in GTP-U messages\n"
200 "IPv4 Address\n")
201{
202 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
203 size_t t;
204
205 ippool_aton(&ggsn->cfg.gtpu_addr, &t, argv[0], 0);
206
207 return CMD_SUCCESS;
208}
209
Harald Weltedda21ed2017-08-12 15:07:02 +0200210DEFUN(cfg_ggsn_state_dir, cfg_ggsn_state_dir_cmd,
211 "gtp state-dir PATH",
212 "GTP Parameters\n"
213 "Set the directory for the GTP State file\n"
214 "Local Directory\n")
215{
216 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
217
218 osmo_talloc_replace_string(ggsn, &ggsn->cfg.state_dir, argv[0]);
219
220 return CMD_SUCCESS;
221}
222
223DEFUN(cfg_ggsn_apn, cfg_ggsn_apn_cmd,
224 "apn NAME", "APN Configuration\n" "APN Name\n")
225{
226 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
227 struct apn_ctx *apn;
228
229 apn = ggsn_find_or_create_apn(ggsn, argv[0]);
230 if (!apn)
231 return CMD_WARNING;
232
233 vty->node = APN_NODE;
234 vty->index = apn;
235 vty->index_sub = &ggsn->cfg.description;
236
237 return CMD_SUCCESS;
238}
239
240DEFUN(cfg_ggsn_no_apn, cfg_ggsn_no_apn_cmd,
241 "no apn NAME",
242 NO_STR "Remove APN Configuration\n" "APN Name\n")
243{
244 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
245 struct apn_ctx *apn;
246
247 apn = ggsn_find_apn(ggsn, argv[0]);
248 if (!apn) {
249 vty_out(vty, "%% No such APN '%s'%s", argv[0], VTY_NEWLINE);
250 return CMD_WARNING;
251 }
252
253 if (!apn->cfg.shutdown) {
254 vty_out(vty, "%% APN %s still active, please shutdown first%s",
255 apn->cfg.name, VTY_NEWLINE);
256 return CMD_WARNING;
257 }
258
259 llist_del(&apn->list);
260 talloc_free(apn);
261
262 return CMD_SUCCESS;
263}
264
265DEFUN(cfg_ggsn_default_apn, cfg_ggsn_default_apn_cmd,
266 "default-apn NAME",
267 "Set a default-APN to be used if no other APN matches\n"
268 "APN Name\n")
269{
270 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
271 struct apn_ctx *apn;
272
273 apn = ggsn_find_apn(ggsn, argv[0]);
274 if (!apn) {
275 vty_out(vty, "%% No APN of name '%s' found%s", argv[0], VTY_NEWLINE);
276 return CMD_WARNING;
277 }
278
279 ggsn->cfg.default_apn = apn;
280 return CMD_SUCCESS;
281}
282
283DEFUN(cfg_ggsn_no_default_apn, cfg_ggsn_no_default_apn_cmd,
284 "no default-apn",
285 NO_STR "Remove default-APN to be used if no other APN matches\n")
286{
287 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
288 ggsn->cfg.default_apn = NULL;
289 return CMD_SUCCESS;
290}
291
292DEFUN(cfg_ggsn_shutdown, cfg_ggsn_shutdown_cmd,
293 "shutdown ggsn",
294 "Put the GGSN in administrative shut-down\n" GGSN_STR)
295{
296 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
297
298 if (!ggsn->cfg.shutdown) {
299 if (ggsn_stop(ggsn)) {
300 vty_out(vty, "%% Failed to shutdown GGSN%s", VTY_NEWLINE);
301 return CMD_WARNING;
302 }
303 ggsn->cfg.shutdown = true;
304 }
305
306 return CMD_SUCCESS;
307}
308
309DEFUN(cfg_ggsn_no_shutdown, cfg_ggsn_no_shutdown_cmd,
310 "no shutdown ggsn",
311 NO_STR GGSN_STR "Remove the GGSN from administrative shut-down\n")
312{
313 struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
314
315 if (ggsn->cfg.shutdown) {
316 if (ggsn_start(ggsn) < 0) {
317 vty_out(vty, "%% Failed to start GGSN, check log for details%s", VTY_NEWLINE);
318 return CMD_WARNING;
319 }
320 ggsn->cfg.shutdown = false;
321 }
322
323 return CMD_SUCCESS;
324}
325
326/* APN Node */
327
328static struct cmd_node apn_node = {
329 APN_NODE,
330 "%s(config-ggsn-apn)# ",
331 1,
332};
333
334static const struct value_string pdp_type_names[] = {
335 { APN_TYPE_IPv4, "v4" },
336 { APN_TYPE_IPv6, "v6" },
337 { APN_TYPE_IPv4v6, "v4v6" },
338 { 0, NULL }
339};
340
341static const struct value_string apn_gtpu_mode_names[] = {
342 { APN_GTPU_MODE_TUN, "tun" },
343 { APN_GTPU_MODE_KERNEL_GTP, "kernel-gtp" },
344 { 0, NULL }
345};
346
347
348#define V4V6V46_STRING "IPv4(-only) PDP Type\n" \
349 "IPv6(-only) PDP Type\n" \
350 "IPv4v6 (dual-stack) PDP Type\n"
351
352DEFUN(cfg_apn_type_support, cfg_apn_type_support_cmd,
353 "type-support (v4|v6|v4v6)",
354 "Enable support for PDP Type\n"
355 V4V6V46_STRING)
356{
357 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
358 uint32_t type = get_string_value(pdp_type_names, argv[0]);
359
360 apn->cfg.apn_type_mask |= type;
361 return CMD_SUCCESS;
362}
363
364DEFUN(cfg_apn_no_type_support, cfg_apn_no_type_support_cmd,
365 "no type-support (v4|v6|v4v6)",
366 NO_STR "Disable support for PDP Type\n"
367 V4V6V46_STRING)
368{
369 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
370 uint32_t type = get_string_value(pdp_type_names, argv[0]);
371
372 apn->cfg.apn_type_mask &= ~type;
373 return CMD_SUCCESS;
374}
375
376DEFUN(cfg_apn_gtpu_mode, cfg_apn_gtpu_mode_cmd,
377 "gtpu-mode (tun|kernel-gtp)",
378 "Set the Mode for this APN (tun or Linux Kernel GTP)\n"
379 "GTP-U in userspace using TUN device\n"
380 "GTP-U in kernel using Linux Kernel GTP\n")
381{
382 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
383
384 apn->cfg.gtpu_mode = get_string_value(apn_gtpu_mode_names, argv[0]);
385 return CMD_SUCCESS;
386}
387
388DEFUN(cfg_apn_tun_dev_name, cfg_apn_tun_dev_name_cmd,
389 "tun-device NAME",
390 "Configure tun device name\n"
391 "TUN device name")
392{
393 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
394 osmo_talloc_replace_string(apn, &apn->tun.cfg.dev_name, argv[0]);
395 return CMD_SUCCESS;
396}
397
398DEFUN(cfg_apn_ipup_script, cfg_apn_ipup_script_cmd,
399 "ipup-script PATH",
400 "Configure name/path of ip-up script\n"
401 "File/Path name of ip-up script\n")
402{
403 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
404 osmo_talloc_replace_string(apn, &apn->tun.cfg.ipup_script, argv[0]);
405 return CMD_SUCCESS;
406}
407
408DEFUN(cfg_apn_no_ipup_script, cfg_apn_no_ipup_script_cmd,
409 "no ipup-script",
410 NO_STR "Disable ip-up script\n")
411{
412 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
413 talloc_free(apn->tun.cfg.ipup_script);
414 apn->tun.cfg.ipup_script = NULL;
415 return CMD_SUCCESS;
416}
417
418DEFUN(cfg_apn_ipdown_script, cfg_apn_ipdown_script_cmd,
419 "ipdown-script PATH",
420 "Configure name/path of ip-down script\n"
421 "File/Path name of ip-down script\n")
422{
423 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
424 osmo_talloc_replace_string(apn, &apn->tun.cfg.ipdown_script, argv[0]);
425 return CMD_SUCCESS;
426}
427
428/* convert prefix from "A.B.C.D/M" notation to in46_prefix */
429static void str2prefix(struct in46_prefix *pfx, const char *in)
430{
431 size_t t;
432
433 ippool_aton(&pfx->addr, &t, in, 0);
434 pfx->prefixlen = t;
435}
436
437DEFUN(cfg_apn_no_ipdown_script, cfg_apn_no_ipdown_script_cmd,
438 "no ipdown-script",
439 NO_STR "Disable ip-down script\n")
440{
441 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
442 talloc_free(apn->tun.cfg.ipdown_script);
443 apn->tun.cfg.ipdown_script = NULL;
444 return CMD_SUCCESS;
445}
446
447DEFUN(cfg_apn_ip_prefix, cfg_apn_ip_prefix_cmd,
448 "ip prefix (static|dynamic) A.B.C.D/M",
449 IP_STR PREFIX_STR "IPv4 Adress/Prefix-Length\n")
450{
451 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
452 struct in46_prefix *pfx;
453
454 /* first update our parsed prefix */
455 if (!strcmp(argv[0], "static"))
456 pfx = &apn->v4.cfg.static_prefix;
457 else
458 pfx = &apn->v4.cfg.dynamic_prefix;
459 str2prefix(pfx, argv[1]);
460
461 return CMD_SUCCESS;
462}
463
464DEFUN(cfg_apn_ip_ifconfig, cfg_apn_ip_ifconfig_cmd,
465 "ip ifconfig A.B.C.D/M",
466 IP_STR IFCONFIG_STR "IPv4 Adress/Prefix-Length\n")
467{
468 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
469 str2prefix(&apn->v4.cfg.ifconfig_prefix, argv[0]);
470 return CMD_SUCCESS;
471}
472
473DEFUN(cfg_apn_no_ip_ifconfig, cfg_apn_no_ip_ifconfig_cmd,
474 "no ip ifconfig",
475 NO_STR IP_STR IFCONFIG_STR)
476{
477 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
478 memset(&apn->v4.cfg.ifconfig_prefix, 0, sizeof(apn->v4.cfg.ifconfig_prefix));
479 return CMD_SUCCESS;
480}
481
482DEFUN(cfg_apn_ipv6_prefix, cfg_apn_ipv6_prefix_cmd,
483 "ipv6 prefix (static|dynamic) X:X::X:X/M",
484 IP6_STR PREFIX_STR "IPv6 Address/Prefix-Length\n")
485{
486 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
487 struct in46_prefix *pfx;
488
489 if (!strcmp(argv[0], "static"))
490 pfx = &apn->v6.cfg.static_prefix;
491 else
492 pfx = &apn->v6.cfg.dynamic_prefix;
493 str2prefix(pfx, argv[1]);
494 return CMD_SUCCESS;
495}
496
497DEFUN(cfg_apn_ipv6_ifconfig, cfg_apn_ipv6_ifconfig_cmd,
498 "ipv6 ifconfig X:X::X:X/M",
499 IP6_STR IFCONFIG_STR "IPv6 Adress/Prefix-Length\n")
500{
501 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
502 str2prefix(&apn->v6.cfg.ifconfig_prefix, argv[0]);
503 return CMD_SUCCESS;
504}
505
506DEFUN(cfg_apn_no_ipv6_ifconfig, cfg_apn_no_ipv6_ifconfig_cmd,
507 "no ipv6 ifconfig",
508 NO_STR IP6_STR IFCONFIG_STR)
509{
510 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
511 memset(&apn->v6.cfg.ifconfig_prefix, 0, sizeof(apn->v6.cfg.ifconfig_prefix));
512 return CMD_SUCCESS;
513}
514
515#define DNS_STRINGS "Configure DNS Server\n" "primary/secondary DNS\n" "IP address of DNS Sever\n"
516
517DEFUN(cfg_apn_ip_dns, cfg_apn_ip_dns_cmd,
518 "ip dns <0-1> A.B.C.D",
519 IP_STR DNS_STRINGS)
520{
521 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
522 int idx = atoi(argv[0]);
523 size_t dummy;
524
525 ippool_aton(&apn->v4.cfg.dns[idx], &dummy, argv[1], 0);
526
527 return CMD_SUCCESS;
528}
529
530DEFUN(cfg_apn_ipv6_dns, cfg_apn_ipv6_dns_cmd,
531 "ipv6 dns <0-1> X:X::X:X",
532 IP6_STR DNS_STRINGS)
533{
534 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
535 int idx = atoi(argv[0]);
536 size_t dummy;
537
538 ippool_aton(&apn->v6.cfg.dns[idx], &dummy, argv[1], 0);
539
540 return CMD_SUCCESS;
541}
542
543DEFUN(cfg_apn_no_dns, cfg_apn_no_dns_cmd,
544 "no (ip|ipv6) dns <0-1>",
545 NO_STR IP_STR IP6_STR "Disable DNS Server\n" "primary/secondary DNS\n")
546{
547 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
548 struct in46_addr *a;
549 int idx = atoi(argv[1]);
550
551 if (!strcmp(argv[0], "ip"))
552 a = &apn->v4.cfg.dns[idx];
553 else
554 a = &apn->v6.cfg.dns[idx];
555
556 memset(a, 0, sizeof(*a));
557
558 return CMD_SUCCESS;
559}
560
561DEFUN(cfg_apn_shutdown, cfg_apn_shutdown_cmd,
562 "shutdown",
563 "Put the APN in administrative shut-down\n")
564{
565 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
566
567 if (!apn->cfg.shutdown) {
568 if (apn_stop(apn, false)) {
569 vty_out(vty, "%% Failed to Stop APN%s", VTY_NEWLINE);
570 return CMD_WARNING;
571 }
572 apn->cfg.shutdown = true;
573 }
574
575 return CMD_SUCCESS;
576}
577
578DEFUN(cfg_apn_no_shutdown, cfg_apn_no_shutdown_cmd,
579 "no shutdown",
580 NO_STR "Remove the APN from administrative shut-down\n")
581{
582 struct apn_ctx *apn = (struct apn_ctx *) vty->index;
583
584 if (apn->cfg.shutdown) {
585 if (apn_start(apn) < 0) {
586 vty_out(vty, "%% Failed to start APN, check log for details%s", VTY_NEWLINE);
587 return CMD_WARNING;
588 }
589 apn->cfg.shutdown = false;
590 }
591
592 return CMD_SUCCESS;
593}
594
595
596static void vty_dump_prefix(struct vty *vty, const char *pre, const struct in46_prefix *pfx)
597{
598 vty_out(vty, "%s %s%s", pre, in46p_ntoa(pfx), VTY_NEWLINE);
599}
600
601static void config_write_apn(struct vty *vty, struct apn_ctx *apn)
602{
603 unsigned int i;
604
605 vty_out(vty, " apn %s%s", apn->cfg.name, VTY_NEWLINE);
606 if (apn->cfg.description)
607 vty_out(vty, " description %s%s", apn->cfg.description, VTY_NEWLINE);
608 vty_out(vty, " gtpu-mode %s%s", get_value_string(apn_gtpu_mode_names, apn->cfg.gtpu_mode),
609 VTY_NEWLINE);
610 if (apn->tun.cfg.dev_name)
611 vty_out(vty, " tun-device %s%s", apn->tun.cfg.dev_name, VTY_NEWLINE);
612 if (apn->tun.cfg.ipup_script)
613 vty_out(vty, " ipup-script %s%s", apn->tun.cfg.ipup_script, VTY_NEWLINE);
614 if (apn->tun.cfg.ipdown_script)
615 vty_out(vty, " ipdown-script %s%s", apn->tun.cfg.ipdown_script, VTY_NEWLINE);
616
617 for (i = 0; i < 32; i++) {
618 if (!(apn->cfg.apn_type_mask & (1 << i)))
619 continue;
620 vty_out(vty, " type-support %s%s", get_value_string(pdp_type_names, (1 << i)),
621 VTY_NEWLINE);
622 }
623
624 /* IPv4 prefixes + DNS */
625 if (apn->v4.cfg.static_prefix.addr.len)
626 vty_dump_prefix(vty, " ip prefix static", &apn->v4.cfg.static_prefix);
627 if (apn->v4.cfg.dynamic_prefix.addr.len)
628 vty_dump_prefix(vty, " ip prefix dynamic", &apn->v4.cfg.dynamic_prefix);
629 for (i = 0; i < ARRAY_SIZE(apn->v4.cfg.dns); i++) {
630 if (!apn->v4.cfg.dns[i].len)
631 continue;
632 vty_out(vty, " ip dns %u %s%s", i, in46a_ntoa(&apn->v4.cfg.dns[i]), VTY_NEWLINE);
633 }
634 if (apn->v4.cfg.ifconfig_prefix.addr.len)
Harald Welteff438172017-09-24 22:48:46 +0800635 vty_dump_prefix(vty, " ip ifconfig", &apn->v4.cfg.ifconfig_prefix);
Harald Weltedda21ed2017-08-12 15:07:02 +0200636
637 /* IPv6 prefixes + DNS */
638 if (apn->v6.cfg.static_prefix.addr.len)
639 vty_dump_prefix(vty, " ipv6 prefix static", &apn->v6.cfg.static_prefix);
640 if (apn->v6.cfg.dynamic_prefix.addr.len)
641 vty_dump_prefix(vty, " ipv6 prefix dynamic", &apn->v6.cfg.dynamic_prefix);
642 for (i = 0; i < ARRAY_SIZE(apn->v6.cfg.dns); i++) {
643 if (!apn->v6.cfg.dns[i].len)
644 continue;
Harald Welte3ca419a2017-09-24 22:47:51 +0800645 vty_out(vty, " ipv6 dns %u %s%s", i, in46a_ntoa(&apn->v6.cfg.dns[i]), VTY_NEWLINE);
Harald Weltedda21ed2017-08-12 15:07:02 +0200646 }
647 if (apn->v6.cfg.ifconfig_prefix.addr.len)
Harald Welteff438172017-09-24 22:48:46 +0800648 vty_dump_prefix(vty, " ipv6 ifconfig", &apn->v6.cfg.ifconfig_prefix);
Harald Weltedda21ed2017-08-12 15:07:02 +0200649
650 /* must be last */
651 vty_out(vty, " %sshutdown%s", apn->cfg.shutdown ? "" : "no ", VTY_NEWLINE);
652}
653
654static int config_write_ggsn(struct vty *vty)
655{
656 struct ggsn_ctx *ggsn;
657
658 llist_for_each_entry(ggsn, &g_ggsn_list, list) {
659 struct apn_ctx *apn;
660 vty_out(vty, "ggsn %s%s", ggsn->cfg.name, VTY_NEWLINE);
661 if (ggsn->cfg.description)
662 vty_out(vty, " description %s%s", ggsn->cfg.description, VTY_NEWLINE);
663 vty_out(vty, " gtp state-dir %s%s", ggsn->cfg.state_dir, VTY_NEWLINE);
Harald Welte98146772017-09-05 17:41:20 +0200664 vty_out(vty, " gtp bind-ip %s%s", in46a_ntoa(&ggsn->cfg.listen_addr), VTY_NEWLINE);
665 if (ggsn->cfg.gtpc_addr.v4.s_addr)
666 vty_out(vty, " gtp control-ip %s%s", in46a_ntoa(&ggsn->cfg.gtpc_addr), VTY_NEWLINE);
667 if (ggsn->cfg.gtpu_addr.v4.s_addr)
668 vty_out(vty, " gtp user-ip %s%s", in46a_ntoa(&ggsn->cfg.gtpu_addr), VTY_NEWLINE);
Harald Weltedda21ed2017-08-12 15:07:02 +0200669 llist_for_each_entry(apn, &ggsn->apn_list, list)
670 config_write_apn(vty, apn);
671 if (ggsn->cfg.default_apn)
672 vty_out(vty, " default-apn %s%s", ggsn->cfg.default_apn->cfg.name, VTY_NEWLINE);
673 /* must be last */
674 vty_out(vty, " %sshutdown ggsn%s", ggsn->cfg.shutdown ? "" : "no ", VTY_NEWLINE);
675 }
676
677 return 0;
678}
679
680static const char *print_gsnaddr(const struct ul16_t *in)
681{
682 struct in46_addr in46;
683
684 in46.len = in->l;
685 OSMO_ASSERT(in->l <= sizeof(in46.v6));
686 memcpy(&in46.v6, in->v, in->l);
687
688 return in46a_ntoa(&in46);
689}
690
691static void show_one_pdp(struct vty *vty, struct pdp_t *pdp)
692{
693 struct in46_addr eua46;
694
695 vty_out(vty, "IMSI: %s, NSAPI: %u, MSISDN: %s%s", imsi_gtp2str(&pdp->imsi), pdp->nsapi,
696 osmo_hexdump_nospc(pdp->msisdn.v, pdp->msisdn.l), VTY_NEWLINE);
697
698 vty_out(vty, " Control: %s:%08x ", print_gsnaddr(&pdp->gsnlc), pdp->teic_own);
699 vty_out(vty, "<-> %s:%08x%s", print_gsnaddr(&pdp->gsnrc), pdp->teic_gn, VTY_NEWLINE);
700
701 vty_out(vty, " Data: %s:%08x ", print_gsnaddr(&pdp->gsnlu), pdp->teid_own);
702 vty_out(vty, "<-> %s:%08x%s", print_gsnaddr(&pdp->gsnru), pdp->teid_gn, VTY_NEWLINE);
703
704 in46a_from_eua(&pdp->eua, &eua46);
705 vty_out(vty, " End-User Address: %s%s", in46a_ntoa(&eua46), VTY_NEWLINE);
706}
707
708DEFUN(show_pdpctx_imsi, show_pdpctx_imsi_cmd,
709 "show pdp-context imsi IMSI [<0-15>]",
710 SHOW_STR "Display information on PDP Context\n"
711 "PDP contexts for given IMSI\n"
712 "PDP context for given NSAPI\n")
713{
714 uint64_t imsi = strtoull(argv[0], NULL, 10);
715 unsigned int nsapi;
716 struct pdp_t *pdp;
717 int num_found = 0;
718
719 if (argc > 1) {
720 nsapi = atoi(argv[1]);
721 if (pdp_getimsi(&pdp, imsi, nsapi)) {
722 show_one_pdp(vty, pdp);
723 num_found++;
724 }
725 } else {
726 for (nsapi = 0; nsapi < PDP_MAXNSAPI; nsapi++) {
727 if (pdp_getimsi(&pdp, imsi, nsapi))
728 continue;
729 show_one_pdp(vty, pdp);
730 num_found++;
731 }
732 }
733 if (num_found == 0) {
734 vty_out(vty, "%% No such PDP context found%s", VTY_NEWLINE);
735 return CMD_WARNING;
736 } else
737 return CMD_SUCCESS;
738}
739
740/* show all (active) PDP contexts within a pool */
741static void ippool_show_pdp_contexts(struct vty *vty, struct ippool_t *pool)
742{
743 unsigned int i;
744
745 if (!pool)
746 return;
747
748 for (i = 0; i < pool->listsize; i++) {
749 struct ippoolm_t *member = &pool->member[i];
750 if (member->inuse == 0)
751 continue;
752 show_one_pdp(vty, member->peer);
753 }
754}
755
756/* show all (active) PDP contexts within an APN */
757static void apn_show_pdp_contexts(struct vty *vty, struct apn_ctx *apn)
758{
759 ippool_show_pdp_contexts(vty, apn->v4.pool);
760 ippool_show_pdp_contexts(vty, apn->v6.pool);
761}
762
763DEFUN(show_pdpctx, show_pdpctx_cmd,
764 "show pdp-context ggsn NAME [apn APN]",
765 SHOW_STR "Show PDP Context Information\n"
766 GGSN_STR "GGSN Name\n") // FIXME
767{
768 struct ggsn_ctx *ggsn;
769 struct apn_ctx *apn;
770
771 ggsn = ggsn_find(argv[0]);
772 if (!ggsn) {
773 vty_out(vty, "%% No such GGSN '%s'%s", argv[0], VTY_NEWLINE);
774 return CMD_WARNING;
775 }
776 if (argc < 2) {
777 llist_for_each_entry(apn, &ggsn->apn_list, list)
778 apn_show_pdp_contexts(vty, apn);
779 } else {
780 apn = ggsn_find_apn(ggsn, argv[1]);
781 if (!apn) {
782 vty_out(vty, "%% No such APN '%s'%s", argv[1], VTY_NEWLINE);
783 return CMD_WARNING;
784 }
785 apn_show_pdp_contexts(vty, apn);
786 }
787
788 return CMD_SUCCESS;
789}
790
791static void show_apn(struct vty *vty, struct apn_ctx *apn)
792{
793 vty_out(vty, " APN: %s%s", apn->cfg.name, VTY_NEWLINE);
794 /* FIXME */
795}
796
797static void show_one_ggsn(struct vty *vty, struct ggsn_ctx *ggsn)
798{
799 struct apn_ctx *apn;
800 vty_out(vty, "GGSN %s: Bound to %s%s", ggsn->cfg.name, in46a_ntoa(&ggsn->cfg.listen_addr),
801 VTY_NEWLINE);
802 /* FIXME */
803
804 llist_for_each_entry(apn, &ggsn->apn_list, list)
805 show_apn(vty, apn);
806}
807
808DEFUN(show_ggsn, show_ggsn_cmd,
809 "show ggsn [NAME]",
810 SHOW_STR "Display information on the GGSN\n")
811{
812 struct ggsn_ctx *ggsn;
813
814 if (argc == 0) {
815 llist_for_each_entry(ggsn, &g_ggsn_list, list)
816 show_one_ggsn(vty, ggsn);
817 } else {
818 ggsn = ggsn_find(argv[0]);
819 if (!ggsn)
820 return CMD_WARNING;
821 show_one_ggsn(vty, ggsn);
822 }
823
824 return CMD_SUCCESS;
825}
826
827int ggsn_vty_init(void)
828{
829 install_element_ve(&show_pdpctx_cmd);
830 install_element_ve(&show_pdpctx_imsi_cmd);
831 install_element_ve(&show_ggsn_cmd);
832
833 install_element(CONFIG_NODE, &cfg_ggsn_cmd);
834 install_element(CONFIG_NODE, &cfg_no_ggsn_cmd);
835 install_node(&ggsn_node, config_write_ggsn);
836 vty_install_default(GGSN_NODE);
837 install_element(GGSN_NODE, &cfg_description_cmd);
838 install_element(GGSN_NODE, &cfg_no_description_cmd);
839 install_element(GGSN_NODE, &cfg_ggsn_shutdown_cmd);
840 install_element(GGSN_NODE, &cfg_ggsn_no_shutdown_cmd);
Harald Welte98146772017-09-05 17:41:20 +0200841 install_element(GGSN_NODE, &cfg_ggsn_bind_ip_cmd);
842 install_element(GGSN_NODE, &cfg_ggsn_gtpc_ip_cmd);
843 install_element(GGSN_NODE, &cfg_ggsn_gtpu_ip_cmd);
Harald Weltedda21ed2017-08-12 15:07:02 +0200844 install_element(GGSN_NODE, &cfg_ggsn_state_dir_cmd);
845 install_element(GGSN_NODE, &cfg_ggsn_apn_cmd);
846 install_element(GGSN_NODE, &cfg_ggsn_no_apn_cmd);
847 install_element(GGSN_NODE, &cfg_ggsn_default_apn_cmd);
848 install_element(GGSN_NODE, &cfg_ggsn_no_default_apn_cmd);
849
850 install_node(&apn_node, NULL);
851 vty_install_default(APN_NODE);
852 install_element(APN_NODE, &cfg_description_cmd);
853 install_element(APN_NODE, &cfg_no_description_cmd);
854 install_element(APN_NODE, &cfg_apn_shutdown_cmd);
855 install_element(APN_NODE, &cfg_apn_no_shutdown_cmd);
856 install_element(APN_NODE, &cfg_apn_gtpu_mode_cmd);
857 install_element(APN_NODE, &cfg_apn_type_support_cmd);
858 install_element(APN_NODE, &cfg_apn_no_type_support_cmd);
859 install_element(APN_NODE, &cfg_apn_tun_dev_name_cmd);
860 install_element(APN_NODE, &cfg_apn_ipup_script_cmd);
861 install_element(APN_NODE, &cfg_apn_no_ipup_script_cmd);
862 install_element(APN_NODE, &cfg_apn_ipdown_script_cmd);
863 install_element(APN_NODE, &cfg_apn_no_ipdown_script_cmd);
864 install_element(APN_NODE, &cfg_apn_ip_prefix_cmd);
865 install_element(APN_NODE, &cfg_apn_ipv6_prefix_cmd);
866 install_element(APN_NODE, &cfg_apn_ip_dns_cmd);
867 install_element(APN_NODE, &cfg_apn_ipv6_dns_cmd);
868 install_element(APN_NODE, &cfg_apn_no_dns_cmd);
869 install_element(APN_NODE, &cfg_apn_ip_ifconfig_cmd);
870 install_element(APN_NODE, &cfg_apn_no_ip_ifconfig_cmd);
871 install_element(APN_NODE, &cfg_apn_ipv6_ifconfig_cmd);
872 install_element(APN_NODE, &cfg_apn_no_ipv6_ifconfig_cmd);
873
874 return 0;
875}
876
877static int ggsn_vty_is_config_node(struct vty *vty, int node)
878{
879 switch (node) {
880 case GGSN_NODE:
881 case APN_NODE:
882 return 1;
883 default:
884 return 0;
885 }
886}
887
888static int ggsn_vty_go_parent(struct vty *vty)
889{
890 switch (vty->node) {
891 case GGSN_NODE:
892 vty->node = CONFIG_NODE;
893 vty->index = NULL;
894 vty->index_sub = NULL;
895 break;
896 case APN_NODE:
897 vty->node = GGSN_NODE;
898 {
899 struct apn_ctx *apn = vty->index;
900 vty->index = apn->ggsn;
901 vty->index_sub = &apn->ggsn->cfg.description;
902 }
903 break;
904 }
905
906 return vty->node;
907}
908
909static const char ggsn_copyright[] =
910 "Copyright (C) 2011-2017 Harald Welte <laforge@gnumonks.org>\r\n"
911 "Copyright (C) 2012-2017 Holger Hans Peter Freyther <holger@moiji-mobile.com>\r\n"
912 "Copyright (C) 2012-2017 sysmocom - s.f.m.c. GmbH\r\n"
913 "Copyright (C) 2002-2005 Mondru AB\r\n"
914 "License GPLv2: GNU GPL version 2 <http://gnu.org/licenses/gpl-2.0.html>\r\n"
915 "This is free software: you are free to change and redistribute it.\r\n"
916 "There is NO WARRANTY, to the extent permitted by law.\r\n";
917
918struct vty_app_info g_vty_info = {
Harald Welte632e8432017-09-05 18:12:14 +0200919 .name = "OsmoGGSN",
Harald Weltedda21ed2017-08-12 15:07:02 +0200920 .version = PACKAGE_VERSION,
921 .copyright = ggsn_copyright,
922 .go_parent_cb = ggsn_vty_go_parent,
923 .is_config_node = ggsn_vty_is_config_node,
924};