From 6707c20b7d34a7e488826056615111e969a6dad2 Mon Sep 17 00:00:00 2001
From: Oleg Hahm <oleg@hobbykeller.org>
Date: Sun, 14 Feb 2016 14:49:03 +0100
Subject: [PATCH] netstats: initial import of IPv6 netstats

---
 Makefile.dep                                  |  4 +
 Makefile.pseudomodules                        |  2 +
 examples/gnrc_networking/Makefile             |  2 +
 sys/include/net/gnrc/ipv6/netif.h             | 16 ++++
 sys/include/net/netstats.h                    | 11 +++
 sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c   | 18 ++++
 .../ipv6/netif/gnrc_ipv6_netif.c              |  8 ++
 sys/shell/commands/sc_netif.c                 | 83 ++++++++++++++++---
 8 files changed, 132 insertions(+), 12 deletions(-)

diff --git a/Makefile.dep b/Makefile.dep
index aeeeeba1e0..1eb0b3870a 100644
--- a/Makefile.dep
+++ b/Makefile.dep
@@ -507,6 +507,10 @@ ifneq (,$(filter gnrc_netdev2,$(USEMODULE)))
   USEMODULE += netopt
 endif
 
+ifneq (,$(filter netstats_%, $(USEMODULE)))
+  USEMODULE += netstats
+endif
+
 ifneq (,$(filter pthread,$(USEMODULE)))
     USEMODULE += xtimer
     USEMODULE += timex
diff --git a/Makefile.pseudomodules b/Makefile.pseudomodules
index 2bc202a645..ddb8d76fcd 100644
--- a/Makefile.pseudomodules
+++ b/Makefile.pseudomodules
@@ -33,7 +33,9 @@ PSEUDOMODULES += lwip_udp
 PSEUDOMODULES += lwip_udplite
 PSEUDOMODULES += netdev_default
 PSEUDOMODULES += netif
+PSEUDOMODULES += netstats
 PSEUDOMODULES += netstats_l2
+PSEUDOMODULES += netstats_ipv6
 PSEUDOMODULES += newlib
 PSEUDOMODULES += newlib_nano
 PSEUDOMODULES += pktqueue
diff --git a/examples/gnrc_networking/Makefile b/examples/gnrc_networking/Makefile
index ce2441c12b..a40d111056 100644
--- a/examples/gnrc_networking/Makefile
+++ b/examples/gnrc_networking/Makefile
@@ -29,6 +29,8 @@ USEMODULE += gnrc_icmpv6_echo
 USEMODULE += shell
 USEMODULE += shell_commands
 USEMODULE += ps
+USEMODULE += netstats_l2
+USEMODULE += netstats_ipv6
 
 # Comment this out to disable code in RIOT that does safety checking
 # which is not needed in a production environment but helps in the
diff --git a/sys/include/net/gnrc/ipv6/netif.h b/sys/include/net/gnrc/ipv6/netif.h
index a1bcfa571b..4de109421e 100644
--- a/sys/include/net/gnrc/ipv6/netif.h
+++ b/sys/include/net/gnrc/ipv6/netif.h
@@ -30,6 +30,7 @@
 #include "mutex.h"
 #include "net/ipv6.h"
 #include "net/ipv6/addr.h"
+#include "net/netstats.h"
 #include "xtimer.h"
 
 #ifdef __cplusplus
@@ -344,6 +345,9 @@ typedef struct {
     xtimer_t rtr_adv_timer; /**< Timer for periodic router advertisements */
     msg_t rtr_adv_msg;      /**< msg_t for gnrc_ipv6_netif_t::rtr_adv_timer */
 #endif
+#ifdef MODULE_NETSTATS_IPV6
+    netstats_t stats;                       /**< transceiver's statistics */
+#endif
 } gnrc_ipv6_netif_t;
 
 /**
@@ -587,6 +591,18 @@ static inline bool gnrc_ipv6_netif_addr_is_non_unicast(const ipv6_addr_t *addr)
  */
 void gnrc_ipv6_netif_init_by_dev(void);
 
+/**
+ * @brief   Get sent and received statistics about IPv6 traffic on this interface.
+ *
+ * @note    This function is only available if compiled with module `netstats_ipv6`.
+ *
+ * @param[in] pid   The PID to the interface.
+ *
+ * @return  A @ref netstats_t pointer to the statistics.
+ * @return  NULL if no statistics are available.
+ */
+netstats_t *gnrc_ipv6_netif_get_stats(kernel_pid_t pid);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/sys/include/net/netstats.h b/sys/include/net/netstats.h
index 152bbc6e30..1bbb4ee32b 100644
--- a/sys/include/net/netstats.h
+++ b/sys/include/net/netstats.h
@@ -18,6 +18,8 @@
  * @author      Oliver Hahm <oliver.hahm@inria.fr>
  */
 
+#include <stdint.h>
+
 #ifndef NETSTATS_H
 #define NETSTATS_H
 
@@ -25,6 +27,15 @@
 extern "C" {
 #endif
 
+/**
+ * @name @ref net_netstats module names
+ * @{
+ */
+#define NETSTATS_LAYER2     (0x01)
+#define NETSTATS_IPV6       (0x02)
+#define NETSTATS_ALL        (0xFF)
+/** @} */
+
 /**
  * @brief       Global statistics struct
  */
diff --git a/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c b/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c
index 27cdd52569..397429c0dd 100644
--- a/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c
+++ b/sys/net/gnrc/network_layer/ipv6/gnrc_ipv6.c
@@ -373,6 +373,11 @@ static void _send_to_iface(kernel_pid_t iface, gnrc_pktsnip_t *pkt)
         gnrc_pktbuf_release(pkt);
         return;
     }
+#ifdef MODULE_NETSTATS_IPV6
+    if_entry->stats.tx_success++;
+    if_entry->stats.tx_bytes += gnrc_pkt_len(pkt->next);
+#endif
+
 #ifdef MODULE_GNRC_SIXLOWPAN
     if (if_entry->flags & GNRC_IPV6_NETIF_FLAGS_SIXLOWPAN) {
         DEBUG("ipv6: send to 6LoWPAN instead\n");
@@ -419,6 +424,9 @@ static void _send_unicast(kernel_pid_t iface, uint8_t *dst_l2addr,
 
     DEBUG("ipv6: send unicast over interface %" PRIkernel_pid "\n", iface);
     /* and send to interface */
+#ifdef MODULE_NETSTATS_IPV6
+    gnrc_ipv6_netif_get_stats(iface)->tx_unicast_count++;
+#endif
     _send_to_iface(iface, pkt);
 }
 
@@ -486,6 +494,9 @@ static inline void _send_multicast_over_iface(kernel_pid_t iface, gnrc_pktsnip_t
     DEBUG("ipv6: send multicast over interface %" PRIkernel_pid "\n", iface);
     /* mark as multicast */
     ((gnrc_netif_hdr_t *)pkt->data)->flags |= GNRC_NETIF_HDR_FLAGS_MULTICAST;
+#ifdef MODULE_NETSTATS_IPV6
+    gnrc_ipv6_netif_get_stats(iface)->tx_mcast_count++;
+#endif
     /* and send to interface */
     _send_to_iface(iface, pkt);
 }
@@ -788,6 +799,13 @@ static void _receive(gnrc_pktsnip_t *pkt)
 
     if (netif != NULL) {
         iface = ((gnrc_netif_hdr_t *)netif->data)->if_pid;
+
+#ifdef MODULE_NETSTATS_IPV6
+        assert(iface);
+        netstats_t *stats = gnrc_ipv6_netif_get_stats(iface);
+        stats->rx_count++;
+        stats->rx_bytes += (gnrc_pkt_len(pkt) - netif->size);
+#endif
     }
 
     first_ext = pkt;
diff --git a/sys/net/gnrc/network_layer/ipv6/netif/gnrc_ipv6_netif.c b/sys/net/gnrc/network_layer/ipv6/netif/gnrc_ipv6_netif.c
index 553423a7e4..c083b2946e 100644
--- a/sys/net/gnrc/network_layer/ipv6/netif/gnrc_ipv6_netif.c
+++ b/sys/net/gnrc/network_layer/ipv6/netif/gnrc_ipv6_netif.c
@@ -897,6 +897,14 @@ void gnrc_ipv6_netif_init_by_dev(void)
     }
 }
 
+#ifdef MODULE_NETSTATS_IPV6
+netstats_t *gnrc_ipv6_netif_get_stats(kernel_pid_t pid)
+{
+    gnrc_ipv6_netif_t *iface = gnrc_ipv6_netif_get(pid);
+    return &(iface->stats);
+}
+#endif
+
 /**
  * @}
  */
diff --git a/sys/shell/commands/sc_netif.c b/sys/shell/commands/sc_netif.c
index f6d760e49e..343ed0f4c0 100644
--- a/sys/shell/commands/sc_netif.c
+++ b/sys/shell/commands/sc_netif.c
@@ -73,23 +73,51 @@ static bool _is_iface(kernel_pid_t dev)
     return false;
 }
 
-#ifdef MODULE_NETSTATS_L2
-static int _netif_stats(kernel_pid_t dev, bool reset)
+#if defined(MODULE_NETSTATS)
+const char *_netstats_module_to_str(uint8_t module)
+{
+    switch (module) {
+        case NETSTATS_LAYER2:
+            return "Layer 2";
+        case NETSTATS_IPV6:
+            return "IPv6";
+        case NETSTATS_ALL:
+            return "all";
+        default:
+            return "Unknown";
+    }
+}
+
+static int _netif_stats(kernel_pid_t dev, unsigned module, bool reset)
 {
     netstats_t *stats;
     int res = -ENOTSUP;
-    res = gnrc_netapi_get(dev, NETOPT_STATS, 0, &stats, sizeof(&stats));
+
+    if (module == NETSTATS_LAYER2) {
+        res = gnrc_netapi_get(dev, NETOPT_STATS, 0, &stats, sizeof(&stats));
+    }
+#ifdef MODULE_NETSTATS_IPV6
+    else if (module == NETSTATS_IPV6) {
+        stats = gnrc_ipv6_netif_get_stats(dev);
+        if (stats != NULL) {
+            res = 1;
+        }
+    }
+#endif
+
     if (res < 0) {
         puts("           Protocol or device doesn't provide statistics.");
     }
     else if (reset) {
         memset(stats, 0, sizeof(netstats_t));
-        puts("Reset statistics!");
+        printf("Reset statistics for module %s!\n", _netstats_module_to_str(module));
     }
     else {
-        printf("           RX packets %u  bytes %u\n"
-               "           TX packets %u (Multicast: %u)  bytes %u\n"
-               "           TX succeeded %u errors %u\n",
+        printf("           Statistics for %s\n"
+               "            RX packets %u  bytes %u\n"
+               "            TX packets %u (Multicast: %u)  bytes %u\n"
+               "            TX succeeded %u errors %u\n",
+               _netstats_module_to_str(module),
                (unsigned) stats->rx_count,
                (unsigned) stats->rx_bytes,
                (unsigned) (stats->tx_unicast_count + stats->tx_mcast_count),
@@ -97,6 +125,7 @@ static int _netif_stats(kernel_pid_t dev, bool reset)
                (unsigned) stats->tx_bytes,
                (unsigned) stats->tx_success,
                (unsigned) stats->tx_failed);
+        res = 0;
     }
     return res;
 }
@@ -155,7 +184,8 @@ static void _del_usage(char *cmd_name)
 
 static void _stats_usage(char *cmd_name)
 {
-    printf("usage: %s <if_id> stats [reset]\n", cmd_name);
+    printf("usage: %s <if_id> stats [l2|ipv6] [reset]\n", cmd_name);
+    puts("       reset can be only used if the module is specified.");
 }
 
 static void _print_netopt(netopt_t opt)
@@ -460,7 +490,10 @@ static void _netif_list(kernel_pid_t dev)
 
 #ifdef MODULE_NETSTATS_L2
     puts("");
-    _netif_stats(dev, false);
+    _netif_stats(dev, NETSTATS_LAYER2, false);
+#endif
+#ifdef MODULE_NETSTATS_IPV6
+    _netif_stats(dev, NETSTATS_IPV6, false);
 #endif
     puts("");
 }
@@ -1106,13 +1139,39 @@ int _netif_config(int argc, char **argv)
 
                 return _netif_mtu((kernel_pid_t)dev, argv[3]);
             }
-#ifdef MODULE_NETSTATS_L2
+#ifdef MODULE_NETSTATS
             else if (strcmp(argv[2], "stats") == 0) {
+                uint8_t module;
                 bool reset = false;
-                if ((argc > 3) && (strncmp(argv[3], "reset", 5) == 0)) {
+
+                /* check for requested module */
+                if ((argc == 3) || (strcmp(argv[3], "all") == 0)) {
+                    module = NETSTATS_ALL;
+                }
+                else if (strcmp(argv[3], "l2") == 0) {
+                    module = NETSTATS_LAYER2;
+                }
+                else if (strcmp(argv[3], "ipv6") == 0) {
+                    module = NETSTATS_IPV6;
+                }
+                else {
+                    printf("Module %s doesn't exist or does not provide statistics.\n", argv[3]);
+
+                    return 0;
+                }
+
+                /* check if reset flag was given */
+                if ((argc > 4) && (strncmp(argv[4], "reset", 5) == 0)) {
                     reset = true;
                 }
-                return _netif_stats((kernel_pid_t)dev, reset);
+                if (module & NETSTATS_LAYER2) {
+                    _netif_stats((kernel_pid_t) dev, NETSTATS_LAYER2, reset);
+                }
+                if (module & NETSTATS_IPV6) {
+                    _netif_stats((kernel_pid_t) dev, NETSTATS_IPV6, reset);
+                }
+
+                return 1;
             }
 #endif
 #ifdef MODULE_GNRC_IPV6_NETIF
-- 
GitLab