/* * (C) 2017 by Harald Welte * 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 . * */ #include #include #include #include #include #include #include #include #include #include #include #include #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 \r\n" "Copyright (C) 2012-2017 Holger Hans Peter Freyther \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 \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, };