From 4b2cbdab3e29dfbd8c4b5f9d44a9a63e7553253d Mon Sep 17 00:00:00 2001 From: Neels Hofmeyr Date: Fri, 20 Nov 2015 03:16:19 +0100 Subject: [PATCH] gtphub: first vty show commands. Start adding VTY commands to show rate counters / statistics / cache dumps. Sponsored-by: On-Waves ehi --- openbsc/include/openbsc/gtphub.h | 3 +- openbsc/src/gprs/gtphub.c | 19 +++- openbsc/src/gprs/gtphub_main.c | 26 ++--- openbsc/src/gprs/gtphub_vty.c | 179 ++++++++++++++++++++++++++++++- 4 files changed, 205 insertions(+), 22 deletions(-) diff --git a/openbsc/include/openbsc/gtphub.h b/openbsc/include/openbsc/gtphub.h index 0b85d82b3..cf1bfe894 100644 --- a/openbsc/include/openbsc/gtphub.h +++ b/openbsc/include/openbsc/gtphub.h @@ -373,6 +373,7 @@ struct gtphub_peer_port { struct gtphub_bind { struct gsn_addr local_addr; + uint16_t local_port; struct osmo_fd ofd; /* list of struct gtphub_peer */ @@ -428,7 +429,7 @@ struct gtp_packet_desc; /* api */ -int gtphub_vty_init(void); +int gtphub_vty_init(struct gtphub *global_hub, struct gtphub_cfg *global_cfg); int gtphub_cfg_read(struct gtphub_cfg *cfg, const char *config_file); /* Initialize and start gtphub: bind to ports, run expiry timers. */ diff --git a/openbsc/src/gprs/gtphub.c b/openbsc/src/gprs/gtphub.c index 45599cc34..570ebc549 100644 --- a/openbsc/src/gprs/gtphub.c +++ b/openbsc/src/gprs/gtphub.c @@ -101,13 +101,13 @@ enum gtphub_counters_io { static const struct rate_ctr_desc gtphub_counters_io_desc[] = { { "packets.in", "Packets ( In)" }, { "packets.out", "Packets (Out)" }, - { "bytes.in", "Packets ( In)" }, - { "bytes.out", "Packets (Out)" }, + { "bytes.in", "Bytes ( In)" }, + { "bytes.out", "Bytes (Out)" }, }; static const struct rate_ctr_group_desc gtphub_ctrg_io_desc = { .group_name_prefix = "gtphub.bind", - .group_description = "Local address I/O statistics", + .group_description = "I/O Statistics", .num_ctr = ARRAY_SIZE(gtphub_counters_io_desc), .ctr_desc = gtphub_counters_io_desc, .class_id = OSMO_STATS_CLASS_GLOBAL, @@ -833,6 +833,7 @@ static int gtphub_bind_start(struct gtphub_bind *b, return -1; if (gtphub_sock_init(&b->ofd, &cfg->bind, cb, cb_data, ofd_id) != 0) return -1; + b->local_port = cfg->bind.port; return 0; } @@ -1419,6 +1420,12 @@ int gtphub_from_ggsns_handle_buf(struct gtphub *hub, /* It was an echo. Nothing left to do. */ osmo_sockaddr_copy(to_addr, from_addr); *to_ofd = &from_bind->ofd; + + rate_ctr_inc(&from_bind->counters_io->ctr[GTPH_CTR_PKTS_OUT]); + rate_ctr_add(&from_bind->counters_io->ctr[GTPH_CTR_BYTES_OUT], + reply_len); + LOG(LOGL_DEBUG, "--> Echo response to GGSN: %d bytes to %s\n", + (int)reply_len, osmo_sockaddr_to_str(to_addr)); return reply_len; } if (reply_len < 0) @@ -1578,6 +1585,12 @@ int gtphub_from_sgsns_handle_buf(struct gtphub *hub, /* It was an echo. Nothing left to do. */ osmo_sockaddr_copy(to_addr, from_addr); *to_ofd = &from_bind->ofd; + + rate_ctr_inc(&from_bind->counters_io->ctr[GTPH_CTR_PKTS_OUT]); + rate_ctr_add(&from_bind->counters_io->ctr[GTPH_CTR_BYTES_OUT], + reply_len); + LOG(LOGL_DEBUG, "<-- Echo response to SGSN: %d bytes to %s\n", + (int)reply_len, osmo_sockaddr_to_str(to_addr)); return reply_len; } if (reply_len < 0) diff --git a/openbsc/src/gprs/gtphub_main.c b/openbsc/src/gprs/gtphub_main.c index e747c77d5..06260bf93 100644 --- a/openbsc/src/gprs/gtphub_main.c +++ b/openbsc/src/gprs/gtphub_main.c @@ -217,6 +217,18 @@ int main(int argc, char **argv) { int rc; + struct cmdline_cfg _ccfg; + struct cmdline_cfg *ccfg = &_ccfg; + memset(ccfg, '\0', sizeof(*ccfg)); + ccfg->config_file = "./gtphub.conf"; + + struct gtphub_cfg _cfg; + struct gtphub_cfg *cfg = &_cfg; + memset(cfg, '\0', sizeof(*cfg)); + + struct gtphub _hub; + struct gtphub *hub = &_hub; + osmo_gtphub_ctx = talloc_named_const(NULL, 0, "osmo_gtphub"); signal(SIGINT, &signal_handler); @@ -230,25 +242,13 @@ int main(int argc, char **argv) vty_info.copyright = gtphub_copyright; vty_init(&vty_info); logging_vty_add_cmds(>phub_log_info); - gtphub_vty_init(); + gtphub_vty_init(hub, cfg); rate_ctr_init(osmo_gtphub_ctx); rc = telnet_init(osmo_gtphub_ctx, 0, OSMO_VTY_PORT_GTPHUB); if (rc < 0) exit(1); - struct cmdline_cfg _ccfg; - struct cmdline_cfg *ccfg = &_ccfg; - memset(ccfg, '\0', sizeof(*ccfg)); - ccfg->config_file = "./gtphub.conf"; - - struct gtphub_cfg _cfg; - struct gtphub_cfg *cfg = &_cfg; - memset(cfg, '\0', sizeof(*cfg)); - - struct gtphub _hub; - struct gtphub *hub = &_hub; - handle_options(ccfg, argc, argv); rc = gtphub_cfg_read(cfg, ccfg->config_file); diff --git a/openbsc/src/gprs/gtphub_vty.c b/openbsc/src/gprs/gtphub_vty.c index 29a731600..602508854 100644 --- a/openbsc/src/gprs/gtphub_vty.c +++ b/openbsc/src/gprs/gtphub_vty.c @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -36,6 +37,7 @@ #include extern struct sgsn_instance *sgsn; +static struct gtphub *g_hub = 0; static struct gtphub_cfg *g_cfg = 0; static struct cmd_node gtphub_node = { @@ -244,17 +246,186 @@ DEFUN(cfg_grx_ggsn, cfg_grx_ggsn_cmd, } -DEFUN(show_gtphub, show_gtphub_cmd, "show gtphub", +/* +(show gtphub all, show gtphub stats, show gtphub teidmap, + show gtphub peers, ...) +*/ + +static void show_bind_stats_all(struct vty *vty) +{ + int plane_idx; + for (plane_idx = 0; plane_idx < GTPH_PLANE_N; plane_idx++) { + vty_out(vty, "- %s Plane:%s", + gtphub_plane_idx_names[plane_idx], VTY_NEWLINE); + + struct gtphub_bind *b = &g_hub->to_ggsns[plane_idx]; + vty_out(vty, " - to/from GGSNs: %s port %d%s", + gsn_addr_to_str(&b->local_addr), (int)b->local_port, + VTY_NEWLINE); + vty_out_rate_ctr_group(vty, " ", b->counters_io); + + b = &g_hub->to_sgsns[plane_idx]; + vty_out(vty, " - to/from SGSNs: %s port %d%s", + gsn_addr_to_str(&b->local_addr), (int)b->local_port, + VTY_NEWLINE); + vty_out_rate_ctr_group(vty, " ", b->counters_io); + } +} + +/* +static void show_peers_summary(struct vty *vty) +{ + int c + int plane_idx; +} +*/ + +static void show_tei_maps_summary(struct vty *vty) +{ + time_t now = gtphub_now(); + + unsigned long long int count_all = 0; /* ...just joking. */ + + const int w = 36; + int max_expiry = g_hub->expire_tei_maps.expiry_in_seconds; + float seconds_per_step = ((float)max_expiry) / w; + + /* Print TEI mapping expiry in an ASCII histogram, like: + TEI map summary + Legend: '_'=0 '.'<=1% ':'<=2% '|'<=10% '#'>10% (10.0 m/step) + CTRL: 30 mappings, valid for 360m[#__:.____|___.____:__.______________]1m + USER: 30 mappings, valid for 360m[#__:.____|___.____:__.______________]1m + 4 TEI mappings in total, last expiry in 359.4 min + */ + vty_out(vty, + "TEI map summary%s" + " Legend: '_'=0 '.'<=1%% ':'<=2%% '|'<=10%% '#'>10%% (%.1f m/step)%s", + VTY_NEWLINE, + seconds_per_step / 60., + VTY_NEWLINE); + + int last_expiry = 0; + int plane_idx; + for (plane_idx = 0; plane_idx < GTPH_PLANE_N; plane_idx++) { + + unsigned int count = 0; + + int histogram[w]; + memset(histogram, 0, sizeof(histogram)); + + struct nr_mapping *m; + llist_for_each_entry(m, &g_hub->tei_map[plane_idx].mappings, entry) { + count ++; + int expiry = m->expiry_entry.expiry - now; + last_expiry = (last_expiry > expiry) ? last_expiry : expiry; + + int hi = ((float)expiry) / seconds_per_step; + if (hi < 0) + hi = 0; + if (hi > (w - 1)) + hi = w - 1; + histogram[hi] ++; + } + + vty_out(vty, + " %4s: %u mappings, valid for %dm[", + gtphub_plane_idx_names[plane_idx], + count, max_expiry / 60); + + int i; + for (i = w - 1; i >= 0; i--) { + char c; + int val = histogram[i]; + int percent = 100. * val / count; + if (!val) + c = '_'; + else if (percent <= 1) + c = '.'; + else if (percent <= 2) + c = ':'; + else if (percent <= 10) + c = '|'; + else c = '#'; + vty_out(vty, "%c", c); + } + vty_out(vty, "]1m%s", VTY_NEWLINE); + + count_all += count; + } + vty_out(vty, " %llu TEI mappings in total, last expiry in %.1f min%s", + count_all, + ((float)last_expiry) / 60., + VTY_NEWLINE); +} + +static void show_tei_maps_all(struct vty *vty) +{ + time_t now = gtphub_now(); + + unsigned long long int count_all = 0; /* ...just joking. */ + + vty_out(vty, "All TEI mappings:%s", VTY_NEWLINE); + int plane_idx; + for (plane_idx = 0; plane_idx < GTPH_PLANE_N; plane_idx++) { + vty_out(vty, + "- %s Plane:%s" + " (timeout) replaced-TEI <--> original-TEI from-peer%s", + gtphub_plane_idx_names[plane_idx], + VTY_NEWLINE, VTY_NEWLINE); + + unsigned int count = 0; + struct nr_mapping *m; + llist_for_each_entry(m, &g_hub->tei_map[plane_idx].mappings, entry) { + struct gtphub_peer_port *pp = m->origin; + vty_out(vty, + " (%4dm) %8x <--> %8x %s", + -(int)((m->expiry_entry.expiry - now) / 60), + (uint32_t)m->repl, + (uint32_t)m->orig, + gsn_addr_to_str(&pp->peer_addr->addr)); + if (pp->port != gtphub_plane_idx_default_port[plane_idx]) + vty_out(vty, " port %d", (int)pp->port); + vty_out(vty, VTY_NEWLINE); + count ++; + } + vty_out(vty, " (%u %s TEI mappings)%s", count, + gtphub_plane_idx_names[plane_idx], VTY_NEWLINE); + count_all += count; + } + vty_out(vty, "- %llu TEI mappings in total%s", count_all, VTY_NEWLINE); +} + +DEFUN(show_gtphub_tei_summary, show_gtphub_tei_summary_cmd, "show gtphub tei summary", + SHOW_STR "TEI mappings summary") +{ + show_tei_maps_summary(vty); + return CMD_SUCCESS; +} + +DEFUN(show_gtphub_tei_dump, show_gtphub_tei_dump_cmd, "show gtphub tei dump", + SHOW_STR "Dump all current TEI mappings") +{ + show_tei_maps_all(vty); + return CMD_SUCCESS; +} + +DEFUN(show_gtphub, show_gtphub_cmd, "show gtphub all", SHOW_STR "Display information about the GTP hub") { - vty_out(vty, "gtphub has nothing to say yet%s", VTY_NEWLINE); + show_bind_stats_all(vty); + show_tei_maps_summary(vty); return CMD_SUCCESS; } -int gtphub_vty_init(void) +int gtphub_vty_init(struct gtphub *global_hub, struct gtphub_cfg *global_cfg) { + g_hub = global_hub; + g_cfg = global_cfg; + install_element_ve(&show_gtphub_cmd); + install_element_ve(&show_gtphub_tei_summary_cmd); + install_element_ve(&show_gtphub_tei_dump_cmd); install_element(CONFIG_NODE, &cfg_gtphub_cmd); install_node(>phub_node, config_write_gtphub); @@ -277,8 +448,6 @@ int gtphub_cfg_read(struct gtphub_cfg *cfg, const char *config_file) { int rc; - g_cfg = cfg; - rc = vty_read_config_file(config_file, NULL); if (rc < 0) { fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);