zebra: read multipath routes and hw addr from netlink

  * zebra/rt_netlink.c:

    - Pick up the hardware address of an interface when we receive a
      netlink link change message. Extract code for parsing the
      link-layer hardware address into a new function so we can reuse
      it.

    - netlink_routing_table(): Update to handle multipath routes.

    - netlink_route_change(): Update to handle multipath routes. Fix
      problem where the metric was not being read out.

  * zebra/zebra_rib.[ch]: Extern nexthop_ipv4_ifindex_add() -- it is
    now called from the netlink code.

From: Josh Bailey <joshb@google.com>
Signed-off-by: Avneesh Sachdev <avneesh@opensourcerouting.org>
Signed-off-by: David Lamparter <equinox@diac24.net>
diff --git a/zebra/rib.h b/zebra/rib.h
index 887ed3c..b5c9e05 100644
--- a/zebra/rib.h
+++ b/zebra/rib.h
@@ -225,6 +225,10 @@
 extern struct nexthop *nexthop_blackhole_add (struct rib *);
 extern struct nexthop *nexthop_ipv4_add (struct rib *, struct in_addr *,
 					 struct in_addr *);
+extern struct nexthop *nexthop_ipv4_ifindex_add (struct rib *,
+                                                 struct in_addr *,
+                                                 struct in_addr *,
+                                                 unsigned int);
 extern void rib_lookup_and_dump (struct prefix_ipv4 *);
 extern void rib_lookup_and_pushup (struct prefix_ipv4 *);
 extern void rib_dump (const char *, const struct prefix_ipv4 *, const struct rib *);
diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c
index 7652f80..2cde50a 100644
--- a/zebra/rt_netlink.c
+++ b/zebra/rt_netlink.c
@@ -32,6 +32,7 @@
 #include "prefix.h"
 #include "connected.h"
 #include "table.h"
+#include "memory.h"
 #include "rib.h"
 #include "thread.h"
 #include "privs.h"
@@ -426,6 +427,37 @@
     }
 }
 
+/* Utility function to parse hardware link-layer address and update ifp */
+static void
+netlink_interface_update_hw_addr (struct rtattr **tb, struct interface *ifp)
+{
+  int i;
+
+  if (tb[IFLA_ADDRESS])
+    {
+      int hw_addr_len;
+
+      hw_addr_len = RTA_PAYLOAD (tb[IFLA_ADDRESS]);
+
+      if (hw_addr_len > INTERFACE_HWADDR_MAX)
+        zlog_warn ("Hardware address is too large: %d", hw_addr_len);
+      else
+        {
+          ifp->hw_addr_len = hw_addr_len;
+          memcpy (ifp->hw_addr, RTA_DATA (tb[IFLA_ADDRESS]), hw_addr_len);
+
+          for (i = 0; i < hw_addr_len; i++)
+            if (ifp->hw_addr[i] != 0)
+              break;
+
+          if (i == hw_addr_len)
+            ifp->hw_addr_len = 0;
+          else
+            ifp->hw_addr_len = hw_addr_len;
+        }
+    }
+}
+
 /* Called from interface_lookup_netlink().  This function is only used
    during bootstrap. */
 static int
@@ -436,7 +468,6 @@
   struct rtattr *tb[IFLA_MAX + 1];
   struct interface *ifp;
   char *name;
-  int i;
 
   ifi = NLMSG_DATA (h);
 
@@ -474,30 +505,7 @@
 
   /* Hardware type and address. */
   ifp->hw_type = ifi->ifi_type;
-
-  if (tb[IFLA_ADDRESS])
-    {
-      int hw_addr_len;
-
-      hw_addr_len = RTA_PAYLOAD (tb[IFLA_ADDRESS]);
-
-      if (hw_addr_len > INTERFACE_HWADDR_MAX)
-        zlog_warn ("Hardware address is too large: %d", hw_addr_len);
-      else
-        {
-          ifp->hw_addr_len = hw_addr_len;
-          memcpy (ifp->hw_addr, RTA_DATA (tb[IFLA_ADDRESS]), hw_addr_len);
-
-          for (i = 0; i < hw_addr_len; i++)
-            if (ifp->hw_addr[i] != 0)
-              break;
-
-          if (i == hw_addr_len)
-            ifp->hw_addr_len = 0;
-          else
-            ifp->hw_addr_len = hw_addr_len;
-        }
-    }
+  netlink_interface_update_hw_addr (tb, ifp);
 
   if_add_update (ifp);
 
@@ -709,7 +717,6 @@
   if (tb[RTA_PREFSRC])
     src = RTA_DATA (tb[RTA_PREFSRC]);
 
-  /* Multipath treatment is needed. */
   if (tb[RTA_GATEWAY])
     gate = RTA_DATA (tb[RTA_GATEWAY]);
 
@@ -723,7 +730,64 @@
       memcpy (&p.prefix, dest, 4);
       p.prefixlen = rtm->rtm_dst_len;
 
-      rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, flags, &p, gate, src, index, table, metric, 0);
+      if (!tb[RTA_MULTIPATH])
+          rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, flags, &p, gate, src, index,
+                        table, metric, 0);
+      else
+        {
+          /* This is a multipath route */
+
+          struct rib *rib;
+          struct rtnexthop *rtnh =
+            (struct rtnexthop *) RTA_DATA (tb[RTA_MULTIPATH]);
+
+          len = RTA_PAYLOAD (tb[RTA_MULTIPATH]);
+
+          rib = XCALLOC (MTYPE_RIB, sizeof (struct rib));
+          rib->type = ZEBRA_ROUTE_KERNEL;
+          rib->distance = 0;
+          rib->flags = flags;
+          rib->metric = metric;
+          rib->table = table;
+          rib->nexthop_num = 0;
+          rib->uptime = time (NULL);
+
+          for (;;)
+            {
+              if (len < (int) sizeof (*rtnh) || rtnh->rtnh_len > len)
+                break;
+
+              rib->nexthop_num++;
+              index = rtnh->rtnh_ifindex;
+              gate = 0;
+              if (rtnh->rtnh_len > sizeof (*rtnh))
+                {
+                  memset (tb, 0, sizeof (tb));
+                  netlink_parse_rtattr (tb, RTA_MAX, RTNH_DATA (rtnh),
+                                        rtnh->rtnh_len - sizeof (*rtnh));
+                  if (tb[RTA_GATEWAY])
+                    gate = RTA_DATA (tb[RTA_GATEWAY]);
+                }
+
+              if (gate)
+                {
+                  if (index)
+                    nexthop_ipv4_ifindex_add (rib, gate, src, index);
+                  else
+                    nexthop_ipv4_add (rib, gate, src);
+                }
+              else
+                nexthop_ifindex_add (rib, index);
+
+              len -= NLMSG_ALIGN(rtnh->rtnh_len);
+              rtnh = RTNH_NEXT(rtnh);
+            }
+
+          if (rib->nexthop_num == 0)
+            XFREE (MTYPE_RIB, rib);
+          else
+            rib_add_ipv4_multipath (&p, rib);
+        }
     }
 #ifdef HAVE_IPV6
   if (rtm->rtm_family == AF_INET6)
@@ -768,6 +832,7 @@
 
   int index;
   int table;
+  int metric;
   void *dest;
   void *gate;
   void *src;
@@ -825,6 +890,7 @@
     }
 
   index = 0;
+  metric = 0;
   dest = NULL;
   gate = NULL;
   src = NULL;
@@ -843,6 +909,9 @@
   if (tb[RTA_PREFSRC])
     src = RTA_DATA (tb[RTA_PREFSRC]);
 
+  if (tb[RTA_PRIORITY])
+    metric = *(int *) RTA_DATA(tb[RTA_PRIORITY]);
+
   if (rtm->rtm_family == AF_INET)
     {
       struct prefix_ipv4 p;
@@ -861,7 +930,66 @@
         }
 
       if (h->nlmsg_type == RTM_NEWROUTE)
-        rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, src, index, table, 0, 0);
+        {
+          if (!tb[RTA_MULTIPATH])
+            rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, src, index, table,
+                          metric, 0);
+          else
+            {
+              /* This is a multipath route */
+
+              struct rib *rib;
+              struct rtnexthop *rtnh =
+                (struct rtnexthop *) RTA_DATA (tb[RTA_MULTIPATH]);
+
+              len = RTA_PAYLOAD (tb[RTA_MULTIPATH]);
+
+              rib = XCALLOC (MTYPE_RIB, sizeof (struct rib));
+              rib->type = ZEBRA_ROUTE_KERNEL;
+              rib->distance = 0;
+              rib->flags = 0;
+              rib->metric = metric;
+              rib->table = table;
+              rib->nexthop_num = 0;
+              rib->uptime = time (NULL);
+
+              for (;;)
+                {
+                  if (len < (int) sizeof (*rtnh) || rtnh->rtnh_len > len)
+                    break;
+
+                  rib->nexthop_num++;
+                  index = rtnh->rtnh_ifindex;
+                  gate = 0;
+                  if (rtnh->rtnh_len > sizeof (*rtnh))
+                    {
+                      memset (tb, 0, sizeof (tb));
+                      netlink_parse_rtattr (tb, RTA_MAX, RTNH_DATA (rtnh),
+                                            rtnh->rtnh_len - sizeof (*rtnh));
+                      if (tb[RTA_GATEWAY])
+                        gate = RTA_DATA (tb[RTA_GATEWAY]);
+                    }
+
+                  if (gate)
+                    {
+                      if (index)
+                        nexthop_ipv4_ifindex_add (rib, gate, src, index);
+                      else
+                        nexthop_ipv4_add (rib, gate, src);
+                    }
+                  else
+                    nexthop_ifindex_add (rib, index);
+
+                  len -= NLMSG_ALIGN(rtnh->rtnh_len);
+                  rtnh = RTNH_NEXT(rtnh);
+                }
+
+              if (rib->nexthop_num == 0)
+                XFREE (MTYPE_RIB, rib);
+              else
+                rib_add_ipv4_multipath (&p, rib);
+            }
+        }
       else
         rib_delete_ipv4 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, index, table);
     }
@@ -954,6 +1082,8 @@
           ifp->mtu6 = ifp->mtu = *(int *) RTA_DATA (tb[IFLA_MTU]);
           ifp->metric = 1;
 
+          netlink_interface_update_hw_addr (tb, ifp);
+
           /* If new link is added. */
           if_add_update (ifp);
         }
@@ -964,6 +1094,8 @@
           ifp->mtu6 = ifp->mtu = *(int *) RTA_DATA (tb[IFLA_MTU]);
           ifp->metric = 1;
 
+          netlink_interface_update_hw_addr (tb, ifp);
+
           if (if_is_operative (ifp))
             {
               ifp->flags = ifi->ifi_flags & 0x0000fffff;
diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c
index 12f3fa5..580b75b 100644
--- a/zebra/zebra_rib.c
+++ b/zebra/zebra_rib.c
@@ -225,7 +225,7 @@
   return nexthop;
 }
 
-static struct nexthop *
+struct nexthop *
 nexthop_ipv4_ifindex_add (struct rib *rib, struct in_addr *ipv4, 
                           struct in_addr *src, unsigned int ifindex)
 {