ospf6d: add SNMP support for ospfv3NbrTable
diff --git a/ospf6d/ospf6_neighbor.c b/ospf6d/ospf6_neighbor.c
index f6c3aea..ab157ca 100644
--- a/ospf6d/ospf6_neighbor.c
+++ b/ospf6d/ospf6_neighbor.c
@@ -89,6 +89,7 @@
             buf, oi->interface->name);
   on->ospf6_if = oi;
   on->state = OSPF6_NEIGHBOR_DOWN;
+  on->state_change = 0;
   quagga_gettime (QUAGGA_CLK_MONOTONIC, &on->last_changed);
   on->router_id = router_id;
 
@@ -154,6 +155,7 @@
   if (prev_state == next_state)
     return;
 
+  on->state_change++;
   quagga_gettime (QUAGGA_CLK_MONOTONIC, &on->last_changed);
 
   /* log */
diff --git a/ospf6d/ospf6_neighbor.h b/ospf6d/ospf6_neighbor.h
index b3bd173..5f46c6f 100644
--- a/ospf6d/ospf6_neighbor.h
+++ b/ospf6d/ospf6_neighbor.h
@@ -46,6 +46,7 @@
   u_char state;
 
   /* timestamp of last changing state */
+  u_int32_t state_change;
   struct timeval last_changed;
 
   /* Neighbor Router ID */
diff --git a/ospf6d/ospf6_snmp.c b/ospf6d/ospf6_snmp.c
index 95b4fc0..5e4ca0f 100644
--- a/ospf6d/ospf6_snmp.c
+++ b/ospf6d/ospf6_snmp.c
@@ -225,6 +225,8 @@
 				int, size_t *, WriteMethod **);
 static u_char *ospfv3AreaLsdbEntry (struct variable *, oid *, size_t *,
 				    int, size_t *, WriteMethod **);
+static u_char *ospfv3NbrEntry (struct variable *, oid *, size_t *,
+			       int, size_t *, WriteMethod **);
 
 struct variable ospfv3_variables[] =
 {
@@ -324,6 +326,31 @@
   {OSPFv3AREALSDBTYPEKNOWN,     INTEGER,   RONLY,  ospfv3AreaLsdbEntry,
    4, {1, 4, 1, 9}},
 
+  /* OSPFv3 neighbors */
+  {OSPFv3NBRADDRESSTYPE,        INTEGER,   RONLY,  ospfv3NbrEntry,
+   4, {1, 9, 1, 4}},
+  {OSPFv3NBRADDRESS,            STRING,    RONLY,  ospfv3NbrEntry,
+   4, {1, 9, 1, 5}},
+  {OSPFv3NBROPTIONS,            INTEGER,   RONLY,  ospfv3NbrEntry,
+   4, {1, 9, 1, 6}},
+  {OSPFv3NBRPRIORITY,           INTEGER,   RONLY,  ospfv3NbrEntry,
+   4, {1, 9, 1, 7}},
+  {OSPFv3NBRSTATE,              INTEGER,   RONLY,  ospfv3NbrEntry,
+   4, {1, 9, 1, 8}},
+  {OSPFv3NBREVENTS,             COUNTER,   RONLY,  ospfv3NbrEntry,
+   4, {1, 9, 1, 9}},
+  {OSPFv3NBRLSRETRANSQLEN,        GAUGE,   RONLY,  ospfv3NbrEntry,
+   4, {1, 9, 1, 10}},
+  {OSPFv3NBRHELLOSUPPRESSED,    INTEGER,   RONLY,  ospfv3NbrEntry,
+   4, {1, 9, 1, 11}},
+  {OSPFv3NBRIFID,               INTEGER,   RONLY,  ospfv3NbrEntry,
+   4, {1, 9, 1, 12}},
+  {OSPFv3NBRRESTARTHELPERSTATUS, INTEGER,  RONLY,  ospfv3NbrEntry,
+   4, {1, 9, 1, 13}},
+  {OSPFv3NBRRESTARTHELPERAGE,  UNSIGNED,   RONLY,  ospfv3NbrEntry,
+   4, {1, 9, 1, 14}},
+  {OSPFv3NBRRESTARTHELPEREXITREASON, INTEGER, RONLY, ospfv3NbrEntry,
+   4, {1, 9, 1, 15}},
 };
 
 static u_char *
@@ -588,6 +615,143 @@
   return NULL;
 }
 
+static int
+if_icmp_func (struct interface *ifp1, struct interface *ifp2)
+{
+  return (ifp1->ifindex - ifp2->ifindex);
+}
+
+static u_char *
+ospfv3NbrEntry (struct variable *v, oid *name, size_t *length,
+		int exact, size_t *var_len, WriteMethod **write_method)
+{
+  unsigned int ifindex, instid, rtrid;
+  struct ospf6_interface *oi = NULL;
+  struct ospf6_neighbor  *on = NULL;
+  struct interface      *iif;
+  struct listnode *i, *j;
+  struct list *ifslist;
+  oid *offset;
+  int offsetlen, len;
+
+  if (smux_header_table (v, name, length, exact, var_len, write_method)
+      == MATCH_FAILED)
+    return NULL;
+
+  ifindex = instid = rtrid = 0;
+
+  /* Check OSPFv3 instance. */
+  if (ospf6 == NULL)
+    return NULL;
+
+  /* Get variable length. */
+  offset = name + v->namelen;
+  offsetlen = *length - v->namelen;
+
+  if (exact && offsetlen != 3)
+    return NULL;
+
+  /* Parse if index */
+  len = (offsetlen < 1 ? 0 : 1);
+  if (len)
+    ifindex = *offset;
+  offset += len;
+  offsetlen -= len;
+
+  /* Parse instance ID */
+  len = (offsetlen < 1 ? 0 : 1);
+  if (len)
+    instid = *offset;
+  offset += len;
+  offsetlen -= len;
+
+  /* Parse router ID */
+  len = (offsetlen < 1 ? 0 : 1);
+  if (len)
+    rtrid = htonl (*offset);
+  offset += len;
+  offsetlen -= len;
+
+  if (exact)
+    {
+      oi = ospf6_interface_lookup_by_ifindex (ifindex);
+      on = ospf6_neighbor_lookup (rtrid, oi);
+      if (oi->instance_id != instid) return NULL;
+    }
+  else
+    {
+      /* We build a sorted list of interfaces */
+      ifslist = list_new ();
+      if (!ifslist) return NULL;
+      ifslist->cmp = (int (*)(void *, void *))if_icmp_func;
+      for (ALL_LIST_ELEMENTS_RO (iflist, i, iif))
+	listnode_add_sort (ifslist, iif);
+
+      for (ALL_LIST_ELEMENTS_RO (ifslist, i, iif))
+        {
+          if (!iif->ifindex) continue;
+          oi = ospf6_interface_lookup_by_ifindex (iif->ifindex);
+          if (!oi) continue;
+          for (ALL_LIST_ELEMENTS_RO (oi->neighbor_list, j, on)) {
+            if (iif->ifindex > ifindex ||
+                (iif->ifindex == ifindex &&
+                 (oi->instance_id > instid ||
+                  (oi->instance_id == instid &&
+                   ntohl (on->router_id) > ntohl (rtrid)))))
+              break;
+          }
+          if (on) break;
+          oi = on = NULL;
+        }
+
+      list_delete_all_node (ifslist);
+    }
+
+  if (!oi || !on) return NULL;
+
+  /* Add Index (IfIndex, IfInstId, RtrId) */
+  *length = v->namelen + 3;
+  offset = name + v->namelen;
+  *offset = oi->interface->ifindex;
+  offset++;
+  *offset = oi->instance_id;
+  offset++;
+  *offset = ntohl (on->router_id);
+  offset++;
+
+  /* Return the current value of the variable */
+  switch (v->magic)
+    {
+    case OSPFv3NBRADDRESSTYPE:
+      return SNMP_INTEGER (2);	/* IPv6 only */
+    case OSPFv3NBRADDRESS:
+      *var_len = sizeof (struct in6_addr);
+      return (u_char *) &on->linklocal_addr;
+    case OSPFv3NBROPTIONS:
+      return SNMP_INTEGER (on->options[2]);
+    case OSPFv3NBRPRIORITY:
+      return SNMP_INTEGER (on->priority);
+    case OSPFv3NBRSTATE:
+      return SNMP_INTEGER (on->state);
+    case OSPFv3NBREVENTS:
+      return SNMP_INTEGER (on->state_change);
+    case OSPFv3NBRLSRETRANSQLEN:
+      return SNMP_INTEGER (on->retrans_list->count);
+    case OSPFv3NBRHELLOSUPPRESSED:
+      return SNMP_INTEGER (SNMP_FALSE);
+    case OSPFv3NBRIFID:
+      return SNMP_INTEGER (on->ifindex);
+    case OSPFv3NBRRESTARTHELPERSTATUS:
+    case OSPFv3NBRRESTARTHELPERAGE:
+    case OSPFv3NBRRESTARTHELPEREXITREASON:
+      /* Not implemented. Only works if all the last ones are not
+	 implemented! */
+      return NULL;
+    }
+
+  return NULL;
+}
+
 
 /* Register OSPFv3-MIB. */
 void