614 lines
18 KiB
C
614 lines
18 KiB
C
/* (C) 2015 by sysmocom s.f.m.c. GmbH
|
|
* All Rights Reserved
|
|
*
|
|
* Author: Neels Hofmeyr
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as published by
|
|
* the Free Software Foundation; either version 3 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 Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <inttypes.h>
|
|
|
|
#include <ares.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
|
|
#include <osmocom/core/talloc.h>
|
|
#include <osmocom/vty/command.h>
|
|
#include <osmocom/vty/misc.h>
|
|
|
|
#include <openbsc/vty.h>
|
|
#include <openbsc/gtphub.h>
|
|
|
|
/* TODO split GRX ares from sgsn into a separate struct and allow use without
|
|
* globals. */
|
|
#include <openbsc/sgsn.h>
|
|
extern struct sgsn_instance *sgsn;
|
|
|
|
static struct gtphub *g_hub = 0;
|
|
static struct gtphub_cfg *g_cfg = 0;
|
|
|
|
static struct cmd_node gtphub_node = {
|
|
GTPHUB_NODE,
|
|
"%s(config-gtphub)# ",
|
|
1,
|
|
};
|
|
|
|
#define GTPH_DEFAULT_CONTROL_PORT 2123
|
|
#define GTPH_DEFAULT_USER_PORT 2152
|
|
|
|
static void write_addrs(struct vty *vty, const char *name,
|
|
struct gtphub_cfg_addr *c, struct gtphub_cfg_addr *u)
|
|
{
|
|
if ((c->port == GTPH_DEFAULT_CONTROL_PORT)
|
|
&& (u->port == GTPH_DEFAULT_USER_PORT)
|
|
&& (strcmp(c->addr_str, u->addr_str) == 0)) {
|
|
/* Default port numbers and same IP address: write "short"
|
|
* variant. */
|
|
vty_out(vty, " %s %s%s",
|
|
name,
|
|
c->addr_str,
|
|
VTY_NEWLINE);
|
|
return;
|
|
}
|
|
|
|
vty_out(vty, " %s ctrl %s %d user %s %d%s",
|
|
name,
|
|
c->addr_str, (int)c->port,
|
|
u->addr_str, (int)u->port,
|
|
VTY_NEWLINE);
|
|
|
|
struct ares_addr_node *server;
|
|
for (server = sgsn->ares_servers; server; server = server->next)
|
|
vty_out(vty, " grx-dns-add %s%s", inet_ntoa(server->addr.addr4), VTY_NEWLINE);
|
|
}
|
|
|
|
static int config_write_gtphub(struct vty *vty)
|
|
{
|
|
vty_out(vty, "gtphub%s", VTY_NEWLINE);
|
|
|
|
write_addrs(vty, "bind-to-sgsns",
|
|
&g_cfg->to_gsns[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].bind,
|
|
&g_cfg->to_gsns[GTPH_SIDE_SGSN][GTPH_PLANE_USER].bind);
|
|
|
|
write_addrs(vty, "bind-to-ggsns",
|
|
&g_cfg->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].bind,
|
|
&g_cfg->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_USER].bind);
|
|
|
|
if (g_cfg->sgsn_use_sender) {
|
|
vty_out(vty, "sgsn-use-sender%s", VTY_NEWLINE);
|
|
}
|
|
|
|
if (g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].addr_str) {
|
|
write_addrs(vty, "sgsn-proxy",
|
|
&g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL],
|
|
&g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_USER]);
|
|
}
|
|
|
|
if (g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].addr_str) {
|
|
write_addrs(vty, "ggsn-proxy",
|
|
&g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL],
|
|
&g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_USER]);
|
|
}
|
|
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
DEFUN(cfg_gtphub, cfg_gtphub_cmd,
|
|
"gtphub",
|
|
"Configure the GTP hub\n")
|
|
{
|
|
vty->node = GTPHUB_NODE;
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
#define BIND_ARGS "ctrl ADDR <0-65535> user ADDR <0-65535>"
|
|
#define BIND_DOCS \
|
|
"Set GTP-C bind\n" \
|
|
"GTP-C local IP address (v4 or v6)\n" \
|
|
"GTP-C local port\n" \
|
|
"Set GTP-U bind\n" \
|
|
"GTP-U local IP address (v4 or v6)\n" \
|
|
"GTP-U local port\n"
|
|
|
|
|
|
DEFUN(cfg_gtphub_bind_to_sgsns_short, cfg_gtphub_bind_to_sgsns_short_cmd,
|
|
"bind-to-sgsns ADDR",
|
|
"GTP Hub Parameters\n"
|
|
"Set the local bind address to listen for SGSNs, for both GTP-C and GTP-U\n"
|
|
"Local IP address (v4 or v6)\n"
|
|
)
|
|
{
|
|
int i;
|
|
for_each_plane(i)
|
|
g_cfg->to_gsns[GTPH_SIDE_SGSN][i].bind.addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
|
|
g_cfg->to_gsns[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].bind.port = GTPH_DEFAULT_CONTROL_PORT;
|
|
g_cfg->to_gsns[GTPH_SIDE_SGSN][GTPH_PLANE_USER].bind.port = GTPH_DEFAULT_USER_PORT;
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
DEFUN(cfg_gtphub_bind_to_ggsns_short, cfg_gtphub_bind_to_ggsns_short_cmd,
|
|
"bind-to-ggsns ADDR",
|
|
"GTP Hub Parameters\n"
|
|
"Set the local bind address to listen for GGSNs, for both GTP-C and GTP-U\n"
|
|
"Local IP address (v4 or v6)\n"
|
|
)
|
|
{
|
|
int i;
|
|
for_each_plane(i)
|
|
g_cfg->to_gsns[GTPH_SIDE_GGSN][i].bind.addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
|
|
g_cfg->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].bind.port = GTPH_DEFAULT_CONTROL_PORT;
|
|
g_cfg->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_USER].bind.port = GTPH_DEFAULT_USER_PORT;
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
|
|
static int handle_binds(struct gtphub_cfg_bind *b, const char **argv)
|
|
{
|
|
b[GTPH_PLANE_CTRL].bind.addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
|
|
b[GTPH_PLANE_CTRL].bind.port = atoi(argv[1]);
|
|
b[GTPH_PLANE_USER].bind.addr_str = talloc_strdup(tall_vty_ctx, argv[2]);
|
|
b[GTPH_PLANE_USER].bind.port = atoi(argv[3]);
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
DEFUN(cfg_gtphub_bind_to_sgsns, cfg_gtphub_bind_to_sgsns_cmd,
|
|
"bind-to-sgsns " BIND_ARGS,
|
|
"GTP Hub Parameters\n"
|
|
"Set the local bind addresses and ports to listen for SGSNs\n"
|
|
BIND_DOCS
|
|
)
|
|
{
|
|
return handle_binds(g_cfg->to_gsns[GTPH_SIDE_SGSN], argv);
|
|
}
|
|
|
|
DEFUN(cfg_gtphub_bind_to_ggsns, cfg_gtphub_bind_to_ggsns_cmd,
|
|
"bind-to-ggsns " BIND_ARGS,
|
|
"GTP Hub Parameters\n"
|
|
"Set the local bind addresses and ports to listen for GGSNs\n"
|
|
BIND_DOCS
|
|
)
|
|
{
|
|
return handle_binds(g_cfg->to_gsns[GTPH_SIDE_GGSN], argv);
|
|
}
|
|
|
|
DEFUN(cfg_gtphub_ggsn_proxy_short, cfg_gtphub_ggsn_proxy_short_cmd,
|
|
"ggsn-proxy ADDR",
|
|
"GTP Hub Parameters\n"
|
|
"Redirect all GGSN bound traffic to default ports on this address (another gtphub)\n"
|
|
"Remote IP address (v4 or v6)\n"
|
|
)
|
|
{
|
|
g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
|
|
g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].port = GTPH_DEFAULT_CONTROL_PORT;
|
|
g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_USER].addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
|
|
g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_USER].port = GTPH_DEFAULT_USER_PORT;
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
DEFUN(cfg_gtphub_ggsn_proxy, cfg_gtphub_ggsn_proxy_cmd,
|
|
"ggsn-proxy " BIND_ARGS,
|
|
"GTP Hub Parameters\n"
|
|
"Redirect all GGSN bound traffic to these addresses and ports (another gtphub)\n"
|
|
BIND_DOCS
|
|
)
|
|
{
|
|
g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
|
|
g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].port = atoi(argv[1]);
|
|
g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_USER].addr_str = talloc_strdup(tall_vty_ctx, argv[2]);
|
|
g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_USER].port = atoi(argv[3]);
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
DEFUN(cfg_gtphub_sgsn_proxy_short, cfg_gtphub_sgsn_proxy_short_cmd,
|
|
"sgsn-proxy ADDR",
|
|
"GTP Hub Parameters\n"
|
|
"Redirect all SGSN bound traffic to default ports on this address (another gtphub)\n"
|
|
"Remote IP address (v4 or v6)\n"
|
|
)
|
|
{
|
|
g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
|
|
g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].port = GTPH_DEFAULT_CONTROL_PORT;
|
|
g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_USER].addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
|
|
g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_USER].port = GTPH_DEFAULT_USER_PORT;
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
DEFUN(cfg_gtphub_sgsn_proxy, cfg_gtphub_sgsn_proxy_cmd,
|
|
"sgsn-proxy " BIND_ARGS,
|
|
"GTP Hub Parameters\n"
|
|
"Redirect all SGSN bound traffic to these addresses and ports (another gtphub)\n"
|
|
BIND_DOCS
|
|
)
|
|
{
|
|
g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
|
|
g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].port = atoi(argv[1]);
|
|
g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_USER].addr_str = talloc_strdup(tall_vty_ctx, argv[2]);
|
|
g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_USER].port = atoi(argv[3]);
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
|
|
#define SGSN_USE_SENDER_STR \
|
|
"Ignore SGSN's Address IEs, use sender address and port (useful over NAT)\n"
|
|
|
|
DEFUN(cfg_gtphub_sgsn_use_sender,
|
|
cfg_gtphub_sgsn_use_sender_cmd,
|
|
"sgsn-use-sender",
|
|
SGSN_USE_SENDER_STR)
|
|
{
|
|
g_cfg->sgsn_use_sender = 1;
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
DEFUN(cfg_gtphub_no_sgsn_use_sender,
|
|
cfg_gtphub_no_sgsn_use_sender_cmd,
|
|
"no sgsn-use-sender",
|
|
NO_STR SGSN_USE_SENDER_STR)
|
|
{
|
|
g_cfg->sgsn_use_sender = 0;
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
|
|
/* Copied from sgsn_vty.h */
|
|
DEFUN(cfg_grx_ggsn, cfg_grx_ggsn_cmd,
|
|
"grx-dns-add A.B.C.D",
|
|
"Add DNS server\nIPv4 address\n")
|
|
{
|
|
struct ares_addr_node *node = talloc_zero(tall_bsc_ctx, struct ares_addr_node);
|
|
node->family = AF_INET;
|
|
inet_aton(argv[0], &node->addr.addr4);
|
|
|
|
node->next = sgsn->ares_servers;
|
|
sgsn->ares_servers = node;
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
|
|
static void show_bind_stats_all(struct vty *vty)
|
|
{
|
|
int plane_idx;
|
|
for_each_plane(plane_idx) {
|
|
vty_out(vty, "- %s Plane:%s",
|
|
gtphub_plane_idx_names[plane_idx], VTY_NEWLINE);
|
|
|
|
int side_idx;
|
|
for_each_side(side_idx) {
|
|
struct gtphub_bind *b = &g_hub->to_gsns[side_idx][plane_idx];
|
|
vty_out(vty, " - local addr to/from %ss: %s port %d%s",
|
|
gtphub_side_idx_names[side_idx],
|
|
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_tunnel_stats(struct vty *vty, struct gtphub_tunnel *tun)
|
|
{
|
|
int plane_idx;
|
|
for_each_plane(plane_idx) {
|
|
vty_out(vty, "- %s Plane:%s",
|
|
gtphub_plane_idx_names[plane_idx], VTY_NEWLINE);
|
|
|
|
int side_idx;
|
|
for_each_side(side_idx) {
|
|
struct gtphub_tunnel_endpoint *te = &tun->endpoint[side_idx][plane_idx];
|
|
vty_out(vty, " - to/from %s:%s",
|
|
gtphub_side_idx_names[side_idx],
|
|
VTY_NEWLINE);
|
|
vty_out_rate_ctr_group(vty, " ", te->counters_io);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void show_peer_summary(struct vty *vty, const char *prefix,
|
|
int side_idx, int plane_idx,
|
|
struct gtphub_peer *p, int with_io_stats)
|
|
{
|
|
struct gtphub_peer_addr *pa;
|
|
int p2l = strlen(prefix) + 4 + 1;
|
|
char prefix2[p2l];
|
|
memset(prefix2, ' ', p2l - 1);
|
|
prefix2[p2l - 1] = '\0';
|
|
|
|
if (with_io_stats) {
|
|
llist_for_each_entry(pa, &p->addresses, entry) {
|
|
vty_out(vty, "%s- %s %s %s%s", prefix,
|
|
gtphub_side_idx_names[side_idx],
|
|
gtphub_plane_idx_names[plane_idx],
|
|
gsn_addr_to_str(&pa->addr),
|
|
VTY_NEWLINE);
|
|
|
|
|
|
struct gtphub_peer_port *pp;
|
|
llist_for_each_entry(pp, &pa->ports, entry) {
|
|
vty_out(vty, "%s Port %" PRIu16 "%s", prefix, pp->port, VTY_NEWLINE);
|
|
vty_out_rate_ctr_group(vty, prefix2, pp->counters_io);
|
|
}
|
|
}
|
|
} else {
|
|
llist_for_each_entry(pa, &p->addresses, entry) {
|
|
vty_out(vty, "%s- %s %s %s", prefix,
|
|
gtphub_side_idx_names[side_idx],
|
|
gtphub_plane_idx_names[plane_idx],
|
|
gsn_addr_to_str(&pa->addr));
|
|
struct gtphub_peer_port *pp;
|
|
llist_for_each_entry(pp, &pa->ports, entry) {
|
|
vty_out(vty, ":%" PRIu16, pp->port);
|
|
}
|
|
vty_out(vty, VTY_NEWLINE);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void show_peers_summary(struct vty *vty)
|
|
{
|
|
int side_idx;
|
|
int plane_idx;
|
|
|
|
int count[GTPH_SIDE_N][GTPH_PLANE_N] = {{0}};
|
|
|
|
for_each_side(side_idx) {
|
|
for_each_plane(plane_idx) {
|
|
struct gtphub_peer *p;
|
|
llist_for_each_entry(p, &g_hub->to_gsns[side_idx][plane_idx].peers, entry) {
|
|
count[side_idx][plane_idx] ++;
|
|
}
|
|
}
|
|
}
|
|
|
|
vty_out(vty, "Peers Count:%s", VTY_NEWLINE);
|
|
for_each_side_and_plane(side_idx, plane_idx) {
|
|
vty_out(vty, " %s %s peers: %d%s",
|
|
gtphub_side_idx_names[side_idx],
|
|
gtphub_plane_idx_names[plane_idx],
|
|
count[side_idx][plane_idx],
|
|
VTY_NEWLINE);
|
|
}
|
|
}
|
|
|
|
static void show_peers_all(struct vty *vty, int with_io_stats)
|
|
{
|
|
int side_idx;
|
|
int plane_idx;
|
|
|
|
int count[GTPH_SIDE_N][GTPH_PLANE_N] = {{0}};
|
|
|
|
vty_out(vty, "All Peers%s%s",
|
|
with_io_stats? " with I/O stats" : "",
|
|
VTY_NEWLINE);
|
|
for_each_side(side_idx) {
|
|
vty_out(vty, "- %s%s", gtphub_side_idx_names[side_idx], VTY_NEWLINE);
|
|
for_each_plane(plane_idx) {
|
|
struct gtphub_peer *p;
|
|
llist_for_each_entry(p, &g_hub->to_gsns[side_idx][plane_idx].peers, entry) {
|
|
count[side_idx][plane_idx] ++;
|
|
show_peer_summary(vty, " ", side_idx, plane_idx, p, with_io_stats);
|
|
}
|
|
}
|
|
}
|
|
for_each_side_and_plane(side_idx, plane_idx) {
|
|
vty_out(vty, "%s %s peers: %d%s",
|
|
gtphub_side_idx_names[side_idx],
|
|
gtphub_plane_idx_names[plane_idx],
|
|
count[side_idx][plane_idx],
|
|
VTY_NEWLINE);
|
|
}
|
|
}
|
|
|
|
|
|
static void show_tunnels_summary(struct vty *vty)
|
|
{
|
|
time_t now = gtphub_now();
|
|
|
|
const int w = 36;
|
|
int max_expiry = g_hub->expire_slowly.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,
|
|
"Tunnels summary%s"
|
|
" Legend: ' '=0 '.'<=1%% ':'<=2%% '|'<=10%% '#'>10%% (%.1f m/step)%s",
|
|
VTY_NEWLINE,
|
|
seconds_per_step / 60.,
|
|
VTY_NEWLINE);
|
|
|
|
int last_expiry = 0;
|
|
|
|
unsigned int count = 0;
|
|
|
|
int histogram[w];
|
|
memset(histogram, 0, sizeof(histogram));
|
|
|
|
struct gtphub_tunnel *t;
|
|
llist_for_each_entry(t, &g_hub->tunnels, entry) {
|
|
count ++;
|
|
int expiry = t->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,
|
|
" %u tunnels, valid for %dm[",
|
|
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);
|
|
|
|
vty_out(vty, " last expiry in %.1f min%s",
|
|
((float)last_expiry) / 60.,
|
|
VTY_NEWLINE);
|
|
}
|
|
|
|
static void show_tunnels_all(struct vty *vty, int with_io_stats)
|
|
{
|
|
time_t now = gtphub_now();
|
|
|
|
vty_out(vty, "All tunnels%s:%s"
|
|
"Legend: TEI=<hex>: SGSN <-> GGSN (expiry in minutes), with each:%s"
|
|
" <IP-Ctrl>[/<IP-User>] (TEI C=<TEI-Ctrl-hex> U=<TEI-User-hex>)%s",
|
|
with_io_stats? "with I/O stats" : "",
|
|
VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
|
|
|
|
unsigned int count = 0;
|
|
unsigned int incomplete = 0;
|
|
struct gtphub_tunnel *tun;
|
|
llist_for_each_entry(tun, &g_hub->tunnels, entry) {
|
|
vty_out(vty,
|
|
"%s (expiry in %dm)%s",
|
|
gtphub_tunnel_str(tun),
|
|
(int)((tun->expiry_entry.expiry - now) / 60),
|
|
VTY_NEWLINE);
|
|
count ++;
|
|
if (!gtphub_tunnel_complete(tun))
|
|
incomplete ++;
|
|
if (with_io_stats)
|
|
show_tunnel_stats(vty, tun);
|
|
}
|
|
vty_out(vty, "Total: %u tunnels (of which %u incomplete)%s",
|
|
count, incomplete, VTY_NEWLINE);
|
|
}
|
|
|
|
#define SHOW_GTPHUB_STRS SHOW_STR "Show info on running GTP hub\n"
|
|
#define SHOW_GTPHUB_PEERS_STRS SHOW_GTPHUB_STRS "Active peers\n"
|
|
#define SHOW_GTPHUB_TUNS_STRS SHOW_GTPHUB_STRS "Active tunnels\n"
|
|
|
|
DEFUN(show_gtphub_peers_summary, show_gtphub_peers_summary_cmd, "show gtphub peers summary",
|
|
SHOW_GTPHUB_PEERS_STRS "Summary of all peers\n")
|
|
{
|
|
show_peers_summary(vty);
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
DEFUN(show_gtphub_peers_list, show_gtphub_peers_list_cmd, "show gtphub peers list",
|
|
SHOW_GTPHUB_PEERS_STRS "List all peers\n")
|
|
{
|
|
show_peers_all(vty, 0);
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
DEFUN(show_gtphub_peers_stats, show_gtphub_peers_stats_cmd, "show gtphub peers stats",
|
|
SHOW_GTPHUB_PEERS_STRS "List all peers with I/O stats\n")
|
|
{
|
|
show_peers_all(vty, 1);
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
DEFUN(show_gtphub_tunnels_summary, show_gtphub_tunnels_summary_cmd, "show gtphub tunnels summary",
|
|
SHOW_GTPHUB_TUNS_STRS "Summary of all tunnels\n")
|
|
{
|
|
show_tunnels_summary(vty);
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
DEFUN(show_gtphub_tunnels_list, show_gtphub_tunnels_list_cmd, "show gtphub tunnels list",
|
|
SHOW_GTPHUB_TUNS_STRS "List all tunnels\n")
|
|
{
|
|
show_tunnels_all(vty, 0);
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
DEFUN(show_gtphub_tunnels_stats, show_gtphub_tunnels_stats_cmd, "show gtphub tunnels stats",
|
|
SHOW_GTPHUB_TUNS_STRS "List all tunnels with I/O stats\n")
|
|
{
|
|
show_tunnels_all(vty, 1);
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
DEFUN(show_gtphub, show_gtphub_cmd, "show gtphub all",
|
|
SHOW_GTPHUB_STRS "Summarize everything about the GTP hub\n")
|
|
{
|
|
show_bind_stats_all(vty);
|
|
show_peers_summary(vty);
|
|
show_tunnels_summary(vty);
|
|
return CMD_SUCCESS;
|
|
}
|
|
|
|
|
|
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_peers_summary_cmd);
|
|
install_element_ve(&show_gtphub_peers_list_cmd);
|
|
install_element_ve(&show_gtphub_peers_stats_cmd);
|
|
install_element_ve(&show_gtphub_tunnels_summary_cmd);
|
|
install_element_ve(&show_gtphub_tunnels_list_cmd);
|
|
install_element_ve(&show_gtphub_tunnels_stats_cmd);
|
|
|
|
install_element(CONFIG_NODE, &cfg_gtphub_cmd);
|
|
install_node(>phub_node, config_write_gtphub);
|
|
vty_install_default(GTPHUB_NODE);
|
|
|
|
install_element(GTPHUB_NODE, &cfg_gtphub_bind_to_sgsns_short_cmd);
|
|
install_element(GTPHUB_NODE, &cfg_gtphub_bind_to_sgsns_cmd);
|
|
install_element(GTPHUB_NODE, &cfg_gtphub_bind_to_ggsns_short_cmd);
|
|
install_element(GTPHUB_NODE, &cfg_gtphub_bind_to_ggsns_cmd);
|
|
install_element(GTPHUB_NODE, &cfg_gtphub_ggsn_proxy_short_cmd);
|
|
install_element(GTPHUB_NODE, &cfg_gtphub_ggsn_proxy_cmd);
|
|
install_element(GTPHUB_NODE, &cfg_gtphub_sgsn_proxy_short_cmd);
|
|
install_element(GTPHUB_NODE, &cfg_gtphub_sgsn_proxy_cmd);
|
|
install_element(GTPHUB_NODE, &cfg_gtphub_sgsn_use_sender_cmd);
|
|
install_element(GTPHUB_NODE, &cfg_gtphub_no_sgsn_use_sender_cmd);
|
|
install_element(GTPHUB_NODE, &cfg_grx_ggsn_cmd);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int gtphub_cfg_read(struct gtphub_cfg *cfg, const char *config_file)
|
|
{
|
|
int rc;
|
|
|
|
rc = vty_read_config_file(config_file, NULL);
|
|
if (rc < 0) {
|
|
fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
|
|
return rc;
|
|
}
|
|
|
|
return 0;
|
|
}
|