zebra: count iface up/down events and keep last time of their occurrence

It is quite useful to be able to assert whether specific interfaces have
flapped or also to verify that specific interfaces have not flapped.

By having counters for those events and storing the last time of their
occurrence, this is made possible.

Signed-off-by: Christian Franke <chris@opensourcerouting.org>
Tested-by: NetDEF CI System <cisystem@netdef.org>
diff --git a/zebra/interface.c b/zebra/interface.c
index b22186d..14dc589 100644
--- a/zebra/interface.c
+++ b/zebra/interface.c
@@ -33,6 +33,7 @@
 #include "log.h"
 #include "zclient.h"
 #include "vrf.h"
+#include "command.h"
 
 #include "zebra/interface.h"
 #include "zebra/rtadv.h"
@@ -361,6 +362,35 @@
     }
 }
 
+static void if_count_up(struct zebra_if *zif)
+{
+  event_counter_inc(&zif->up_events);
+}
+
+static void if_count_down(struct zebra_if *zif)
+{
+  event_counter_inc(&zif->down_events);
+}
+
+void
+if_startup_count_up (void)
+{
+  vrf_iter_t iter;
+  struct interface *ifp;
+  struct zebra_if *zif;
+  struct listnode *node;
+
+  for (iter = vrf_first(); iter != VRF_ITER_INVALID; iter = vrf_next(iter))
+    {
+      for (ALL_LIST_ELEMENTS_RO (vrf_iter2iflist(iter), node, ifp))
+        {
+          zif = ifp->info;
+          if (!zif->up_events.count && if_is_operative(ifp))
+            if_count_up(zif);
+        }
+    }
+}
+
 /* Handle interface addition */
 void
 if_add_update (struct interface *ifp)
@@ -402,6 +432,17 @@
 	zlog_debug ("interface %s vrf %u index %d is added.",
 		    ifp->name, ifp->vrf_id, ifp->ifindex);
     }
+
+  if (host_config_get())
+    {
+      /* If configuration and therefore link-detect have already been
+       * loaded, count an initial up event when new interfaces are added
+       * in up state.
+       * If configuration has not been loaded yet, this is handled by
+       * if_startup_count_up which is called after reading the config. */
+      if (!if_data->up_events.count && if_is_operative(ifp))
+        if_count_up(if_data);
+    }
 }
 
 /* Handle an interface delete event */
@@ -537,6 +578,8 @@
   struct connected *ifc;
   struct prefix *p;
 
+  if_count_up(ifp->info);
+
   /* Notify the protocol daemons. */
   zebra_interface_up_update (ifp);
 
@@ -569,6 +612,11 @@
   struct listnode *next;
   struct connected *ifc;
   struct prefix *p;
+  struct zebra_if *zif;
+
+  zif = ifp->info;
+  if (zif->up_events.count)
+    if_count_down(zif);
 
   /* Notify to the protocol daemons. */
   zebra_interface_down_update (ifp);
@@ -728,6 +776,11 @@
     vty_out (vty, "down%s", VTY_NEWLINE);
   }
 
+  vty_out (vty, "  Link ups:   %s%s",
+           event_counter_format(&zebra_if->up_events), VTY_NEWLINE);
+  vty_out (vty, "  Link downs: %s%s",
+           event_counter_format(&zebra_if->down_events), VTY_NEWLINE);
+
   vty_out (vty, "  vrf: %u%s", ifp->vrf_id, VTY_NEWLINE);
 
   if (ifp->desc)