gtphub: add more detailed I/O rate counters.

Count bytes and packets per peer port, as well es per tunnel enpoint, which
adds two more levels of detail.

Sponsored-by: On-Waves ehi
diff --git a/openbsc/include/openbsc/gtphub.h b/openbsc/include/openbsc/gtphub.h
index 1bff681..97cfc60 100644
--- a/openbsc/include/openbsc/gtphub.h
+++ b/openbsc/include/openbsc/gtphub.h
@@ -391,12 +391,16 @@
 	unsigned int ref_count; /* references from other peers' seq_maps */
 	struct osmo_sockaddr sa; /* a "cache" for (peer_addr->addr, port) */
 	int last_restart_count; /* 0..255 = valid, all else means unknown */
+
+	struct rate_ctr_group *counters_io;
 };
 
 struct gtphub_tunnel_endpoint {
 	struct gtphub_peer_port *peer;
 	uint32_t tei_orig; /* from/to peer */
 	uint32_t tei_repl; /* from/to the other tunnel endpoint */
+
+	struct rate_ctr_group *counters_io;
 };
 
 struct gtphub_tunnel {
diff --git a/openbsc/src/gprs/gtphub.c b/openbsc/src/gprs/gtphub.c
index eb463d7..e2f3a54 100644
--- a/openbsc/src/gprs/gtphub.c
+++ b/openbsc/src/gprs/gtphub.c
@@ -783,6 +783,7 @@
 {
 	OSMO_ASSERT(pp->ref_count == 0);
 	llist_del(&pp->entry);
+	rate_ctr_group_free(pp->counters_io);
 	talloc_free(pp);
 }
 
@@ -1056,9 +1057,12 @@
 	int side_idx;
 	int plane_idx;
 	for_each_side_and_plane(side_idx, plane_idx) {
+		struct gtphub_tunnel_endpoint *te = &tun->endpoint[side_idx][plane_idx];
+
 		/* clear ref count */
-		gtphub_tunnel_endpoint_set_peer(&tun->endpoint[side_idx][plane_idx],
-						NULL);
+		gtphub_tunnel_endpoint_set_peer(te, NULL);
+
+		rate_ctr_group_free(te->counters_io);
 	}
 
 	talloc_free(tun);
@@ -1073,6 +1077,15 @@
 	INIT_LLIST_HEAD(&tun->entry);
 	expiring_item_init(&tun->expiry_entry);
 
+	int side_idx, plane_idx;
+	for_each_side_and_plane(side_idx, plane_idx) {
+		struct gtphub_tunnel_endpoint *te = &tun->endpoint[side_idx][plane_idx];
+		te->counters_io = rate_ctr_group_alloc(osmo_gtphub_ctx,
+						       &gtphub_ctrg_io_desc,
+						       0);
+		OSMO_ASSERT(te->counters_io);
+	}
+
 	tun->expiry_entry.del_cb = gtphub_tunnel_del_cb;
 	return tun;
 }
@@ -2162,6 +2175,10 @@
 		return -1;
 	}
 
+	rate_ctr_add(&from_peer->counters_io->ctr[GTPH_CTR_BYTES_IN],
+		     received);
+	rate_ctr_inc(&from_peer->counters_io->ctr[GTPH_CTR_PKTS_IN]);
+
 	LOG(LOGL_DEBUG, "from %s peer: %s\n", gtphub_side_idx_names[side_idx],
 	    gtphub_port_str(from_peer));
 
@@ -2174,6 +2191,13 @@
 		return -1;
 	}
 
+	if (p.tun) {
+		struct gtphub_tunnel_endpoint *te = &p.tun->endpoint[p.side_idx][p.plane_idx];
+		rate_ctr_add(&te->counters_io->ctr[GTPH_CTR_BYTES_IN],
+			     received);
+		rate_ctr_inc(&te->counters_io->ctr[GTPH_CTR_PKTS_IN]);
+	}
+
 	if ((!to_peer) && (side_idx == GTPH_SIDE_SGSN)) {
 		if (gtphub_resolve_ggsn(hub, &p, &to_peer) < 0)
 			return -1;
@@ -2216,7 +2240,19 @@
 		rate_ctr_inc(&to_bind->counters_io->ctr[GTPH_CTR_PKTS_OUT]);
 		rate_ctr_add(&to_bind->counters_io->ctr[GTPH_CTR_BYTES_OUT],
 			     received);
+
+		rate_ctr_inc(&to_peer->counters_io->ctr[GTPH_CTR_PKTS_OUT]);
+		rate_ctr_add(&to_peer->counters_io->ctr[GTPH_CTR_BYTES_OUT],
+			     received);
 	}
+
+	if (p.tun) {
+		struct gtphub_tunnel_endpoint *te = &p.tun->endpoint[other_side_idx(p.side_idx)][p.plane_idx];
+		rate_ctr_inc(&te->counters_io->ctr[GTPH_CTR_PKTS_OUT]);
+		rate_ctr_add(&te->counters_io->ctr[GTPH_CTR_BYTES_OUT],
+			     received);
+	}
+
 	LOG(LOGL_DEBUG, "%s Forward to %s: %d bytes to %s\n",
 	    (side_idx == GTPH_SIDE_SGSN)? "-->" : "<--",
 	    gtphub_side_idx_names[other_side_idx(side_idx)],
@@ -2614,6 +2650,9 @@
 		return NULL;
 	}
 
+	pp->counters_io = rate_ctr_group_alloc(osmo_gtphub_ctx,
+					       &gtphub_ctrg_io_desc, 0);
+
 	llist_add(&pp->entry, &a->ports);
 
 	LOG(LOGL_DEBUG, "New peer port: %s port %d\n",
diff --git a/openbsc/src/gprs/gtphub_vty.c b/openbsc/src/gprs/gtphub_vty.c
index 0eb9261..5b9fc79 100644
--- a/openbsc/src/gprs/gtphub_vty.c
+++ b/openbsc/src/gprs/gtphub_vty.c
@@ -278,11 +278,6 @@
 }
 
 
-/*
-(show gtphub all, show gtphub stats, show gtphub teidmap,
- show gtphub peers, ...)
-*/
-
 static void show_bind_stats_all(struct vty *vty)
 {
 	int plane_idx;
@@ -302,6 +297,24 @@
 	}
 }
 
+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_peers_summary(struct vty *vty)
 {
@@ -380,29 +393,33 @@
 		VTY_NEWLINE);
 }
 
-static void show_tunnels_all(struct vty *vty)
+static void show_tunnels_all(struct vty *vty, int with_io_stats)
 {
 	time_t now = gtphub_now();
 
-	vty_out(vty, "All tunnels:%s"
+	vty_out(vty, "All tunnels%s:%s"
 		"Legend: (expiry in minutes) SGSN <-> GGSN, with each:%s"
 		"        <IP-Ctrl>[/<IP-User>] (<TEI-Ctrl>=<mapped>/<TEI-User>=<mapped>)%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 *t;
-	llist_for_each_entry(t, &g_hub->tunnels, entry) {
+	struct gtphub_tunnel *tun;
+	llist_for_each_entry(tun, &g_hub->tunnels, entry) {
 		vty_out(vty,
 			"(%4dm) %s%s",
-			(int)((t->expiry_entry.expiry - now) / 60),
-			gtphub_tunnel_str(t),
+			(int)((tun->expiry_entry.expiry - now) / 60),
+			gtphub_tunnel_str(tun),
 			VTY_NEWLINE);
 		count ++;
-		if (!gtphub_tunnel_complete(t))
+		if (!gtphub_tunnel_complete(tun))
 			incomplete ++;
+		if (with_io_stats)
+			show_tunnel_stats(vty, tun);
 	}
-	vty_out(vty, "Total: %u tunnels%s", count, VTY_NEWLINE);
+	vty_out(vty, "Total: %u tunnels (of which %u incomplete)%s",
+		count, incomplete, VTY_NEWLINE);
 }
 
 DEFUN(show_gtphub_tunnels_summary, show_gtphub_tunnels_summary_cmd, "show gtphub tunnels summary",
@@ -415,7 +432,14 @@
 DEFUN(show_gtphub_tunnels_list, show_gtphub_tunnels_list_cmd, "show gtphub tunnels list",
       SHOW_STR "List all tunnels")
 {
-	show_tunnels_all(vty);
+	show_tunnels_all(vty, 0);
+	return CMD_SUCCESS;
+}
+
+DEFUN(show_gtphub_tunnels_stats, show_gtphub_tunnels_stats_cmd, "show gtphub tunnels stats",
+      SHOW_STR "List all tunnels with I/O stats")
+{
+	show_tunnels_all(vty, 1);
 	return CMD_SUCCESS;
 }
 
@@ -436,6 +460,7 @@
 	install_element_ve(&show_gtphub_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(&gtphub_node, config_write_gtphub);