aboutsummaryrefslogtreecommitdiffstats
path: root/ggsn/ggsn_vty.c
diff options
context:
space:
mode:
Diffstat (limited to 'ggsn/ggsn_vty.c')
-rw-r--r--ggsn/ggsn_vty.c846
1 files changed, 846 insertions, 0 deletions
diff --git a/ggsn/ggsn_vty.c b/ggsn/ggsn_vty.c
new file mode 100644
index 0000000..4f05bd7
--- /dev/null
+++ b/ggsn/ggsn_vty.c
@@ -0,0 +1,846 @@
+/*
+ * (C) 2017 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/rate_ctr.h>
+#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
+
+#include <osmocom/vty/command.h>
+#include <osmocom/vty/vty.h>
+#include <osmocom/vty/misc.h>
+
+#include "../gtp/gtp.h"
+#include "../gtp/pdp.h"
+
+#include "ggsn.h"
+
+#define PREFIX_STR "Prefix (Network/Netmask)\n"
+#define IFCONFIG_STR "GGSN-based interface configuration\n"
+#define GGSN_STR "Gateway GPRS Support NODE (GGSN)\n"
+
+LLIST_HEAD(g_ggsn_list);
+
+enum ggsn_vty_node {
+ GGSN_NODE = _LAST_OSMOVTY_NODE + 1,
+ APN_NODE,
+};
+
+struct ggsn_ctx *ggsn_find(const char *name)
+{
+ struct ggsn_ctx *ggsn;
+
+ llist_for_each_entry(ggsn, &g_ggsn_list, list) {
+ if (!strcmp(ggsn->cfg.name, name))
+ return ggsn;
+ }
+ return NULL;
+}
+
+struct ggsn_ctx *ggsn_find_or_create(void *ctx, const char *name)
+{
+ struct ggsn_ctx *ggsn;
+
+ ggsn = ggsn_find(name);
+ if (ggsn)
+ return ggsn;
+
+ ggsn = talloc_zero(ctx, struct ggsn_ctx);
+ if (!ggsn)
+ return NULL;
+
+ ggsn->cfg.name = talloc_strdup(ggsn, name);
+ ggsn->cfg.state_dir = talloc_strdup(ggsn, "/tmp");
+ ggsn->cfg.shutdown = true;
+ INIT_LLIST_HEAD(&ggsn->apn_list);
+
+ llist_add_tail(&ggsn->list, &g_ggsn_list);
+ return ggsn;
+}
+
+struct apn_ctx *ggsn_find_apn(struct ggsn_ctx *ggsn, const char *name)
+{
+ struct apn_ctx *apn;
+
+ llist_for_each_entry(apn, &ggsn->apn_list, list) {
+ if (!strcmp(apn->cfg.name, name))
+ return apn;
+ }
+ return NULL;
+}
+
+struct apn_ctx *ggsn_find_or_create_apn(struct ggsn_ctx *ggsn, const char *name)
+{
+ struct apn_ctx *apn = ggsn_find_apn(ggsn, name);
+ if (apn)
+ return apn;
+
+ apn = talloc_zero(ggsn, struct apn_ctx);
+ if (!apn)
+ return NULL;
+ apn->ggsn = ggsn;
+ apn->cfg.name = talloc_strdup(apn, name);
+ apn->cfg.shutdown = true;
+ INIT_LLIST_HEAD(&apn->cfg.name_list);
+
+ llist_add_tail(&apn->list, &ggsn->apn_list);
+ return apn;
+}
+
+/* GGSN Node */
+
+static struct cmd_node ggsn_node = {
+ GGSN_NODE,
+ "%s(config-ggsn)# ",
+ 1,
+};
+
+DEFUN(cfg_ggsn, cfg_ggsn_cmd,
+ "ggsn NAME",
+ "Configure the Gateway GPRS Support Node\n" "GGSN Name (has only local significance)\n")
+{
+ struct ggsn_ctx *ggsn;
+
+ ggsn = ggsn_find_or_create(tall_ggsn_ctx, argv[0]);
+ if (!ggsn)
+ return CMD_WARNING;
+
+ vty->node = GGSN_NODE;
+ vty->index = ggsn;
+ vty->index_sub = &ggsn->cfg.description;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_ggsn, cfg_no_ggsn_cmd,
+ "no ggsn NAME",
+ NO_STR "Remove the named Gateway GPRS Support Node\n"
+ "GGSN Name (has only local significance)\n")
+{
+ struct ggsn_ctx *ggsn;
+
+ ggsn = ggsn_find(argv[0]);
+ if (!ggsn) {
+ vty_out(vty, "%% No such GGSN '%s'%s", argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (!ggsn->cfg.shutdown) {
+ vty_out(vty, "%% GGSN %s is still active, please shutdown first%s",
+ ggsn->cfg.name, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (!llist_empty(&ggsn->apn_list)) {
+ vty_out(vty, "%% GGSN %s still has APNs configured, please remove first%s",
+ ggsn->cfg.name, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ llist_del(&ggsn->list);
+ talloc_free(ggsn);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ggsn_local_ip, cfg_ggsn_local_ip_cmd,
+ "gtp local-ip A.B.C.D",
+ "GTP Parameters\n"
+ "Set the IP address for the local GTP bind\n"
+ "IPv4 Address\n")
+{
+ struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
+ size_t t;
+
+ ippool_aton(&ggsn->cfg.listen_addr, &t, argv[0], 0);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ggsn_state_dir, cfg_ggsn_state_dir_cmd,
+ "gtp state-dir PATH",
+ "GTP Parameters\n"
+ "Set the directory for the GTP State file\n"
+ "Local Directory\n")
+{
+ struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
+
+ osmo_talloc_replace_string(ggsn, &ggsn->cfg.state_dir, argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ggsn_apn, cfg_ggsn_apn_cmd,
+ "apn NAME", "APN Configuration\n" "APN Name\n")
+{
+ struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
+ struct apn_ctx *apn;
+
+ apn = ggsn_find_or_create_apn(ggsn, argv[0]);
+ if (!apn)
+ return CMD_WARNING;
+
+ vty->node = APN_NODE;
+ vty->index = apn;
+ vty->index_sub = &ggsn->cfg.description;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ggsn_no_apn, cfg_ggsn_no_apn_cmd,
+ "no apn NAME",
+ NO_STR "Remove APN Configuration\n" "APN Name\n")
+{
+ struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
+ struct apn_ctx *apn;
+
+ apn = ggsn_find_apn(ggsn, argv[0]);
+ if (!apn) {
+ vty_out(vty, "%% No such APN '%s'%s", argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (!apn->cfg.shutdown) {
+ vty_out(vty, "%% APN %s still active, please shutdown first%s",
+ apn->cfg.name, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ llist_del(&apn->list);
+ talloc_free(apn);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ggsn_default_apn, cfg_ggsn_default_apn_cmd,
+ "default-apn NAME",
+ "Set a default-APN to be used if no other APN matches\n"
+ "APN Name\n")
+{
+ struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
+ struct apn_ctx *apn;
+
+ apn = ggsn_find_apn(ggsn, argv[0]);
+ if (!apn) {
+ vty_out(vty, "%% No APN of name '%s' found%s", argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ ggsn->cfg.default_apn = apn;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ggsn_no_default_apn, cfg_ggsn_no_default_apn_cmd,
+ "no default-apn",
+ NO_STR "Remove default-APN to be used if no other APN matches\n")
+{
+ struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
+ ggsn->cfg.default_apn = NULL;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ggsn_shutdown, cfg_ggsn_shutdown_cmd,
+ "shutdown ggsn",
+ "Put the GGSN in administrative shut-down\n" GGSN_STR)
+{
+ struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
+
+ if (!ggsn->cfg.shutdown) {
+ if (ggsn_stop(ggsn)) {
+ vty_out(vty, "%% Failed to shutdown GGSN%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ ggsn->cfg.shutdown = true;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ggsn_no_shutdown, cfg_ggsn_no_shutdown_cmd,
+ "no shutdown ggsn",
+ NO_STR GGSN_STR "Remove the GGSN from administrative shut-down\n")
+{
+ struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
+
+ if (ggsn->cfg.shutdown) {
+ if (ggsn_start(ggsn) < 0) {
+ vty_out(vty, "%% Failed to start GGSN, check log for details%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ ggsn->cfg.shutdown = false;
+ }
+
+ return CMD_SUCCESS;
+}
+
+/* APN Node */
+
+static struct cmd_node apn_node = {
+ APN_NODE,
+ "%s(config-ggsn-apn)# ",
+ 1,
+};
+
+static const struct value_string pdp_type_names[] = {
+ { APN_TYPE_IPv4, "v4" },
+ { APN_TYPE_IPv6, "v6" },
+ { APN_TYPE_IPv4v6, "v4v6" },
+ { 0, NULL }
+};
+
+static const struct value_string apn_gtpu_mode_names[] = {
+ { APN_GTPU_MODE_TUN, "tun" },
+ { APN_GTPU_MODE_KERNEL_GTP, "kernel-gtp" },
+ { 0, NULL }
+};
+
+
+#define V4V6V46_STRING "IPv4(-only) PDP Type\n" \
+ "IPv6(-only) PDP Type\n" \
+ "IPv4v6 (dual-stack) PDP Type\n"
+
+DEFUN(cfg_apn_type_support, cfg_apn_type_support_cmd,
+ "type-support (v4|v6|v4v6)",
+ "Enable support for PDP Type\n"
+ V4V6V46_STRING)
+{
+ struct apn_ctx *apn = (struct apn_ctx *) vty->index;
+ uint32_t type = get_string_value(pdp_type_names, argv[0]);
+
+ apn->cfg.apn_type_mask |= type;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_apn_no_type_support, cfg_apn_no_type_support_cmd,
+ "no type-support (v4|v6|v4v6)",
+ NO_STR "Disable support for PDP Type\n"
+ V4V6V46_STRING)
+{
+ struct apn_ctx *apn = (struct apn_ctx *) vty->index;
+ uint32_t type = get_string_value(pdp_type_names, argv[0]);
+
+ apn->cfg.apn_type_mask &= ~type;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_apn_gtpu_mode, cfg_apn_gtpu_mode_cmd,
+ "gtpu-mode (tun|kernel-gtp)",
+ "Set the Mode for this APN (tun or Linux Kernel GTP)\n"
+ "GTP-U in userspace using TUN device\n"
+ "GTP-U in kernel using Linux Kernel GTP\n")
+{
+ struct apn_ctx *apn = (struct apn_ctx *) vty->index;
+
+ apn->cfg.gtpu_mode = get_string_value(apn_gtpu_mode_names, argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_apn_tun_dev_name, cfg_apn_tun_dev_name_cmd,
+ "tun-device NAME",
+ "Configure tun device name\n"
+ "TUN device name")
+{
+ struct apn_ctx *apn = (struct apn_ctx *) vty->index;
+ osmo_talloc_replace_string(apn, &apn->tun.cfg.dev_name, argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_apn_ipup_script, cfg_apn_ipup_script_cmd,
+ "ipup-script PATH",
+ "Configure name/path of ip-up script\n"
+ "File/Path name of ip-up script\n")
+{
+ struct apn_ctx *apn = (struct apn_ctx *) vty->index;
+ osmo_talloc_replace_string(apn, &apn->tun.cfg.ipup_script, argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_apn_no_ipup_script, cfg_apn_no_ipup_script_cmd,
+ "no ipup-script",
+ NO_STR "Disable ip-up script\n")
+{
+ struct apn_ctx *apn = (struct apn_ctx *) vty->index;
+ talloc_free(apn->tun.cfg.ipup_script);
+ apn->tun.cfg.ipup_script = NULL;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_apn_ipdown_script, cfg_apn_ipdown_script_cmd,
+ "ipdown-script PATH",
+ "Configure name/path of ip-down script\n"
+ "File/Path name of ip-down script\n")
+{
+ struct apn_ctx *apn = (struct apn_ctx *) vty->index;
+ osmo_talloc_replace_string(apn, &apn->tun.cfg.ipdown_script, argv[0]);
+ return CMD_SUCCESS;
+}
+
+/* convert prefix from "A.B.C.D/M" notation to in46_prefix */
+static void str2prefix(struct in46_prefix *pfx, const char *in)
+{
+ size_t t;
+
+ ippool_aton(&pfx->addr, &t, in, 0);
+ pfx->prefixlen = t;
+}
+
+DEFUN(cfg_apn_no_ipdown_script, cfg_apn_no_ipdown_script_cmd,
+ "no ipdown-script",
+ NO_STR "Disable ip-down script\n")
+{
+ struct apn_ctx *apn = (struct apn_ctx *) vty->index;
+ talloc_free(apn->tun.cfg.ipdown_script);
+ apn->tun.cfg.ipdown_script = NULL;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_apn_ip_prefix, cfg_apn_ip_prefix_cmd,
+ "ip prefix (static|dynamic) A.B.C.D/M",
+ IP_STR PREFIX_STR "IPv4 Adress/Prefix-Length\n")
+{
+ struct apn_ctx *apn = (struct apn_ctx *) vty->index;
+ struct in46_prefix *pfx;
+
+ /* first update our parsed prefix */
+ if (!strcmp(argv[0], "static"))
+ pfx = &apn->v4.cfg.static_prefix;
+ else
+ pfx = &apn->v4.cfg.dynamic_prefix;
+ str2prefix(pfx, argv[1]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_apn_ip_ifconfig, cfg_apn_ip_ifconfig_cmd,
+ "ip ifconfig A.B.C.D/M",
+ IP_STR IFCONFIG_STR "IPv4 Adress/Prefix-Length\n")
+{
+ struct apn_ctx *apn = (struct apn_ctx *) vty->index;
+ str2prefix(&apn->v4.cfg.ifconfig_prefix, argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_apn_no_ip_ifconfig, cfg_apn_no_ip_ifconfig_cmd,
+ "no ip ifconfig",
+ NO_STR IP_STR IFCONFIG_STR)
+{
+ struct apn_ctx *apn = (struct apn_ctx *) vty->index;
+ memset(&apn->v4.cfg.ifconfig_prefix, 0, sizeof(apn->v4.cfg.ifconfig_prefix));
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_apn_ipv6_prefix, cfg_apn_ipv6_prefix_cmd,
+ "ipv6 prefix (static|dynamic) X:X::X:X/M",
+ IP6_STR PREFIX_STR "IPv6 Address/Prefix-Length\n")
+{
+ struct apn_ctx *apn = (struct apn_ctx *) vty->index;
+ struct in46_prefix *pfx;
+
+ if (!strcmp(argv[0], "static"))
+ pfx = &apn->v6.cfg.static_prefix;
+ else
+ pfx = &apn->v6.cfg.dynamic_prefix;
+ str2prefix(pfx, argv[1]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_apn_ipv6_ifconfig, cfg_apn_ipv6_ifconfig_cmd,
+ "ipv6 ifconfig X:X::X:X/M",
+ IP6_STR IFCONFIG_STR "IPv6 Adress/Prefix-Length\n")
+{
+ struct apn_ctx *apn = (struct apn_ctx *) vty->index;
+ str2prefix(&apn->v6.cfg.ifconfig_prefix, argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_apn_no_ipv6_ifconfig, cfg_apn_no_ipv6_ifconfig_cmd,
+ "no ipv6 ifconfig",
+ NO_STR IP6_STR IFCONFIG_STR)
+{
+ struct apn_ctx *apn = (struct apn_ctx *) vty->index;
+ memset(&apn->v6.cfg.ifconfig_prefix, 0, sizeof(apn->v6.cfg.ifconfig_prefix));
+ return CMD_SUCCESS;
+}
+
+#define DNS_STRINGS "Configure DNS Server\n" "primary/secondary DNS\n" "IP address of DNS Sever\n"
+
+DEFUN(cfg_apn_ip_dns, cfg_apn_ip_dns_cmd,
+ "ip dns <0-1> A.B.C.D",
+ IP_STR DNS_STRINGS)
+{
+ struct apn_ctx *apn = (struct apn_ctx *) vty->index;
+ int idx = atoi(argv[0]);
+ size_t dummy;
+
+ ippool_aton(&apn->v4.cfg.dns[idx], &dummy, argv[1], 0);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_apn_ipv6_dns, cfg_apn_ipv6_dns_cmd,
+ "ipv6 dns <0-1> X:X::X:X",
+ IP6_STR DNS_STRINGS)
+{
+ struct apn_ctx *apn = (struct apn_ctx *) vty->index;
+ int idx = atoi(argv[0]);
+ size_t dummy;
+
+ ippool_aton(&apn->v6.cfg.dns[idx], &dummy, argv[1], 0);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_apn_no_dns, cfg_apn_no_dns_cmd,
+ "no (ip|ipv6) dns <0-1>",
+ NO_STR IP_STR IP6_STR "Disable DNS Server\n" "primary/secondary DNS\n")
+{
+ struct apn_ctx *apn = (struct apn_ctx *) vty->index;
+ struct in46_addr *a;
+ int idx = atoi(argv[1]);
+
+ if (!strcmp(argv[0], "ip"))
+ a = &apn->v4.cfg.dns[idx];
+ else
+ a = &apn->v6.cfg.dns[idx];
+
+ memset(a, 0, sizeof(*a));
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_apn_shutdown, cfg_apn_shutdown_cmd,
+ "shutdown",
+ "Put the APN in administrative shut-down\n")
+{
+ struct apn_ctx *apn = (struct apn_ctx *) vty->index;
+
+ if (!apn->cfg.shutdown) {
+ if (apn_stop(apn, false)) {
+ vty_out(vty, "%% Failed to Stop APN%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ apn->cfg.shutdown = true;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_apn_no_shutdown, cfg_apn_no_shutdown_cmd,
+ "no shutdown",
+ NO_STR "Remove the APN from administrative shut-down\n")
+{
+ struct apn_ctx *apn = (struct apn_ctx *) vty->index;
+
+ if (apn->cfg.shutdown) {
+ if (apn_start(apn) < 0) {
+ vty_out(vty, "%% Failed to start APN, check log for details%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ apn->cfg.shutdown = false;
+ }
+
+ return CMD_SUCCESS;
+}
+
+
+static void vty_dump_prefix(struct vty *vty, const char *pre, const struct in46_prefix *pfx)
+{
+ vty_out(vty, "%s %s%s", pre, in46p_ntoa(pfx), VTY_NEWLINE);
+}
+
+static void config_write_apn(struct vty *vty, struct apn_ctx *apn)
+{
+ unsigned int i;
+
+ vty_out(vty, " apn %s%s", apn->cfg.name, VTY_NEWLINE);
+ if (apn->cfg.description)
+ vty_out(vty, " description %s%s", apn->cfg.description, VTY_NEWLINE);
+ vty_out(vty, " gtpu-mode %s%s", get_value_string(apn_gtpu_mode_names, apn->cfg.gtpu_mode),
+ VTY_NEWLINE);
+ if (apn->tun.cfg.dev_name)
+ vty_out(vty, " tun-device %s%s", apn->tun.cfg.dev_name, VTY_NEWLINE);
+ if (apn->tun.cfg.ipup_script)
+ vty_out(vty, " ipup-script %s%s", apn->tun.cfg.ipup_script, VTY_NEWLINE);
+ if (apn->tun.cfg.ipdown_script)
+ vty_out(vty, " ipdown-script %s%s", apn->tun.cfg.ipdown_script, VTY_NEWLINE);
+
+ for (i = 0; i < 32; i++) {
+ if (!(apn->cfg.apn_type_mask & (1 << i)))
+ continue;
+ vty_out(vty, " type-support %s%s", get_value_string(pdp_type_names, (1 << i)),
+ VTY_NEWLINE);
+ }
+
+ /* IPv4 prefixes + DNS */
+ if (apn->v4.cfg.static_prefix.addr.len)
+ vty_dump_prefix(vty, " ip prefix static", &apn->v4.cfg.static_prefix);
+ if (apn->v4.cfg.dynamic_prefix.addr.len)
+ vty_dump_prefix(vty, " ip prefix dynamic", &apn->v4.cfg.dynamic_prefix);
+ for (i = 0; i < ARRAY_SIZE(apn->v4.cfg.dns); i++) {
+ if (!apn->v4.cfg.dns[i].len)
+ continue;
+ vty_out(vty, " ip dns %u %s%s", i, in46a_ntoa(&apn->v4.cfg.dns[i]), VTY_NEWLINE);
+ }
+ if (apn->v4.cfg.ifconfig_prefix.addr.len)
+ vty_dump_prefix(vty, " ip ifconfig ", &apn->v4.cfg.ifconfig_prefix);
+
+ /* IPv6 prefixes + DNS */
+ if (apn->v6.cfg.static_prefix.addr.len)
+ vty_dump_prefix(vty, " ipv6 prefix static", &apn->v6.cfg.static_prefix);
+ if (apn->v6.cfg.dynamic_prefix.addr.len)
+ vty_dump_prefix(vty, " ipv6 prefix dynamic", &apn->v6.cfg.dynamic_prefix);
+ for (i = 0; i < ARRAY_SIZE(apn->v6.cfg.dns); i++) {
+ if (!apn->v6.cfg.dns[i].len)
+ continue;
+ vty_out(vty, " ip dns %u %s%s", i, in46a_ntoa(&apn->v6.cfg.dns[i]), VTY_NEWLINE);
+ }
+ if (apn->v6.cfg.ifconfig_prefix.addr.len)
+ vty_dump_prefix(vty, " ipv6 ifconfig ", &apn->v6.cfg.ifconfig_prefix);
+
+ /* must be last */
+ vty_out(vty, " %sshutdown%s", apn->cfg.shutdown ? "" : "no ", VTY_NEWLINE);
+}
+
+static int config_write_ggsn(struct vty *vty)
+{
+ struct ggsn_ctx *ggsn;
+
+ llist_for_each_entry(ggsn, &g_ggsn_list, list) {
+ struct apn_ctx *apn;
+ vty_out(vty, "ggsn %s%s", ggsn->cfg.name, VTY_NEWLINE);
+ if (ggsn->cfg.description)
+ vty_out(vty, " description %s%s", ggsn->cfg.description, VTY_NEWLINE);
+ vty_out(vty, " gtp state-dir %s%s", ggsn->cfg.state_dir, VTY_NEWLINE);
+ vty_out(vty, " gtp local-ip %s%s", in46a_ntoa(&ggsn->cfg.listen_addr), VTY_NEWLINE);
+ llist_for_each_entry(apn, &ggsn->apn_list, list)
+ config_write_apn(vty, apn);
+ if (ggsn->cfg.default_apn)
+ vty_out(vty, " default-apn %s%s", ggsn->cfg.default_apn->cfg.name, VTY_NEWLINE);
+ /* must be last */
+ vty_out(vty, " %sshutdown ggsn%s", ggsn->cfg.shutdown ? "" : "no ", VTY_NEWLINE);
+ }
+
+ return 0;
+}
+
+static const char *print_gsnaddr(const struct ul16_t *in)
+{
+ struct in46_addr in46;
+
+ in46.len = in->l;
+ OSMO_ASSERT(in->l <= sizeof(in46.v6));
+ memcpy(&in46.v6, in->v, in->l);
+
+ return in46a_ntoa(&in46);
+}
+
+static void show_one_pdp(struct vty *vty, struct pdp_t *pdp)
+{
+ struct in46_addr eua46;
+
+ vty_out(vty, "IMSI: %"PRIu64", NSAPI: %u, MSISDN: %s%s", pdp->imsi, pdp->nsapi,
+ osmo_hexdump_nospc(pdp->msisdn.v, pdp->msisdn.l), VTY_NEWLINE);
+
+ vty_out(vty, " Control: %s:%04x ", print_gsnaddr(&pdp->gsnlc), pdp->teic_own);
+ vty_out(vty, "<-> %s:%04x%s", print_gsnaddr(&pdp->gsnlu), pdp->teid_own, VTY_NEWLINE);
+
+ vty_out(vty, " Data: %s:%04x ", print_gsnaddr(&pdp->gsnrc), pdp->teic_gn);
+ vty_out(vty, "<-> %s:%04x%s", print_gsnaddr(&pdp->gsnru), pdp->teid_gn, VTY_NEWLINE);
+
+ in46a_from_eua(&pdp->eua, &eua46);
+ vty_out(vty, " End-User Address: %s\n", in46a_ntoa(&eua46));
+}
+
+DEFUN(show_pdpctx_imsi, show_pdpctx_imsi_cmd,
+ "show pdp-context imsi IMSI [<0-15>]",
+ SHOW_STR "Display information on PDP Context\n"
+ "PDP contexts for given IMSI\n"
+ "PDP context for given NSAPI\n")
+{
+ uint64_t imsi = strtoull(argv[0], NULL, 10);
+ unsigned int nsapi;
+ struct pdp_t *pdp;
+ int num_found = 0;
+
+ if (argc > 1) {
+ nsapi = atoi(argv[1]);
+ if (pdp_getimsi(&pdp, imsi, nsapi)) {
+ show_one_pdp(vty, pdp);
+ num_found++;
+ }
+ } else {
+ for (nsapi = 0; nsapi < PDP_MAXNSAPI; nsapi++) {
+ if (!pdp_getimsi(&pdp, imsi, nsapi))
+ continue;
+ show_one_pdp(vty, pdp);
+ num_found++;
+ }
+ }
+ if (num_found == 0) {
+ vty_out(vty, "%% No such PDP context found%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ } else
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_apn_all, show_apn_all_cmd,
+ "show apn all",
+ SHOW_STR "Display information on APN\n" "Show all APNs\n")
+{
+ return CMD_SUCCESS;
+}
+
+static void show_apn(struct vty *vty, struct apn_ctx *apn)
+{
+ vty_out(vty, " APN: %s%s", apn->cfg.name, VTY_NEWLINE);
+ /* FIXME */
+}
+
+static void show_one_ggsn(struct vty *vty, struct ggsn_ctx *ggsn)
+{
+ struct apn_ctx *apn;
+ vty_out(vty, "GGSN %s: Bound to %s%s\n", ggsn->cfg.name, in46a_ntoa(&ggsn->cfg.listen_addr),
+ VTY_NEWLINE);
+ /* FIXME */
+
+ llist_for_each_entry(apn, &ggsn->apn_list, list)
+ show_apn(vty, apn);
+}
+
+DEFUN(show_ggsn, show_ggsn_cmd,
+ "show ggsn [NAME]",
+ SHOW_STR "Display information on the GGSN\n")
+{
+ struct ggsn_ctx *ggsn;
+
+ if (argc == 0) {
+ llist_for_each_entry(ggsn, &g_ggsn_list, list)
+ show_one_ggsn(vty, ggsn);
+ } else {
+ ggsn = ggsn_find(argv[0]);
+ if (!ggsn)
+ return CMD_WARNING;
+ show_one_ggsn(vty, ggsn);
+ }
+
+ return CMD_SUCCESS;
+}
+
+int ggsn_vty_init(void)
+{
+ install_element_ve(&show_pdpctx_imsi_cmd);
+ install_element_ve(&show_apn_all_cmd);
+ install_element_ve(&show_ggsn_cmd);
+
+ install_element(CONFIG_NODE, &cfg_ggsn_cmd);
+ install_element(CONFIG_NODE, &cfg_no_ggsn_cmd);
+ install_node(&ggsn_node, config_write_ggsn);
+ vty_install_default(GGSN_NODE);
+ install_element(GGSN_NODE, &cfg_description_cmd);
+ install_element(GGSN_NODE, &cfg_no_description_cmd);
+ install_element(GGSN_NODE, &cfg_ggsn_shutdown_cmd);
+ install_element(GGSN_NODE, &cfg_ggsn_no_shutdown_cmd);
+ install_element(GGSN_NODE, &cfg_ggsn_local_ip_cmd);
+ install_element(GGSN_NODE, &cfg_ggsn_state_dir_cmd);
+ install_element(GGSN_NODE, &cfg_ggsn_apn_cmd);
+ install_element(GGSN_NODE, &cfg_ggsn_no_apn_cmd);
+ install_element(GGSN_NODE, &cfg_ggsn_default_apn_cmd);
+ install_element(GGSN_NODE, &cfg_ggsn_no_default_apn_cmd);
+
+ install_node(&apn_node, NULL);
+ vty_install_default(APN_NODE);
+ install_element(APN_NODE, &cfg_description_cmd);
+ install_element(APN_NODE, &cfg_no_description_cmd);
+ install_element(APN_NODE, &cfg_apn_shutdown_cmd);
+ install_element(APN_NODE, &cfg_apn_no_shutdown_cmd);
+ install_element(APN_NODE, &cfg_apn_gtpu_mode_cmd);
+ install_element(APN_NODE, &cfg_apn_type_support_cmd);
+ install_element(APN_NODE, &cfg_apn_no_type_support_cmd);
+ install_element(APN_NODE, &cfg_apn_tun_dev_name_cmd);
+ install_element(APN_NODE, &cfg_apn_ipup_script_cmd);
+ install_element(APN_NODE, &cfg_apn_no_ipup_script_cmd);
+ install_element(APN_NODE, &cfg_apn_ipdown_script_cmd);
+ install_element(APN_NODE, &cfg_apn_no_ipdown_script_cmd);
+ install_element(APN_NODE, &cfg_apn_ip_prefix_cmd);
+ install_element(APN_NODE, &cfg_apn_ipv6_prefix_cmd);
+ install_element(APN_NODE, &cfg_apn_ip_dns_cmd);
+ install_element(APN_NODE, &cfg_apn_ipv6_dns_cmd);
+ install_element(APN_NODE, &cfg_apn_no_dns_cmd);
+ install_element(APN_NODE, &cfg_apn_ip_ifconfig_cmd);
+ install_element(APN_NODE, &cfg_apn_no_ip_ifconfig_cmd);
+ install_element(APN_NODE, &cfg_apn_ipv6_ifconfig_cmd);
+ install_element(APN_NODE, &cfg_apn_no_ipv6_ifconfig_cmd);
+
+ return 0;
+}
+
+static int ggsn_vty_is_config_node(struct vty *vty, int node)
+{
+ switch (node) {
+ case GGSN_NODE:
+ case APN_NODE:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int ggsn_vty_go_parent(struct vty *vty)
+{
+ switch (vty->node) {
+ case GGSN_NODE:
+ vty->node = CONFIG_NODE;
+ vty->index = NULL;
+ vty->index_sub = NULL;
+ break;
+ case APN_NODE:
+ vty->node = GGSN_NODE;
+ {
+ struct apn_ctx *apn = vty->index;
+ vty->index = apn->ggsn;
+ vty->index_sub = &apn->ggsn->cfg.description;
+ }
+ break;
+ }
+
+ return vty->node;
+}
+
+static const char ggsn_copyright[] =
+ "Copyright (C) 2011-2017 Harald Welte <laforge@gnumonks.org>\r\n"
+ "Copyright (C) 2012-2017 Holger Hans Peter Freyther <holger@moiji-mobile.com>\r\n"
+ "Copyright (C) 2012-2017 sysmocom - s.f.m.c. GmbH\r\n"
+ "Copyright (C) 2002-2005 Mondru AB\r\n"
+ "License GPLv2: GNU GPL version 2 <http://gnu.org/licenses/gpl-2.0.html>\r\n"
+ "This is free software: you are free to change and redistribute it.\r\n"
+ "There is NO WARRANTY, to the extent permitted by law.\r\n";
+
+struct vty_app_info g_vty_info = {
+ .name = "OpenGGSN",
+ .version = PACKAGE_VERSION,
+ .copyright = ggsn_copyright,
+ .go_parent_cb = ggsn_vty_go_parent,
+ .is_config_node = ggsn_vty_is_config_node,
+};