diff options
Diffstat (limited to 'ggsn/ggsn_vty.c')
-rw-r--r-- | ggsn/ggsn_vty.c | 846 |
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, +}; |