zebra: rework recursive route resolution

Change the datastructure for recursive routes. This brings the following
benefits:

By using struct nexthop also to store nexthops obtained by recursive
resolution, we can get rid of quite a bit of code duplication in the fib
management. (rt_netlink, rt_socket, ...)

With the new datastructure we can make use of all available paths when
recursive routes are resolved with multipath routes.

Signed-off-by: Christian Franke <chris@opensourcerouting.org>
Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
diff --git a/zebra/zebra_fpm_netlink.c b/zebra/zebra_fpm_netlink.c
index 67bcf0a..b5f2b76 100644
--- a/zebra/zebra_fpm_netlink.c
+++ b/zebra/zebra_fpm_netlink.c
@@ -152,7 +152,8 @@
  * Returns TRUE if a nexthop was added, FALSE otherwise.
  */
 static int
-netlink_route_info_add_nh (netlink_route_info_t *ri, struct nexthop *nexthop)
+netlink_route_info_add_nh (netlink_route_info_t *ri, struct nexthop *nexthop,
+			   int recursive)
 {
   netlink_nh_info_t nhi;
   union g_addr *src;
@@ -163,40 +164,7 @@
   if (ri->num_nhs >= (int) ZEBRA_NUM_OF (ri->nhs))
     return 0;
 
-  if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
-    {
-      nhi.recursive = 1;
-      nhi.type = nexthop->rtype;
-      nhi.if_index = nexthop->rifindex;
-
-      if (nexthop->rtype == NEXTHOP_TYPE_IPV4
-	  || nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX)
-	{
-	  nhi.gateway = &nexthop->rgate;
-	  if (nexthop->src.ipv4.s_addr)
-	    src = &nexthop->src;
-	}
-
-#ifdef HAVE_IPV6
-      if (nexthop->rtype == NEXTHOP_TYPE_IPV6
-	  || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX
-	  || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME)
-	{
-	  nhi.gateway = &nexthop->rgate;
-	}
-#endif /* HAVE_IPV6 */
-
-      if (nexthop->rtype == NEXTHOP_TYPE_IFINDEX
-	  || nexthop->rtype == NEXTHOP_TYPE_IFNAME)
-	{
-	  if (nexthop->src.ipv4.s_addr)
-	    src = &nexthop->src;
-	}
-
-      goto done;
-    }
-
-  nhi.recursive = 0;
+  nhi.recursive = recursive;
   nhi.type = nexthop->type;
   nhi.if_index = nexthop->ifindex;
 
@@ -224,11 +192,6 @@
 	src = &nexthop->src;
     }
 
-  /*
-   * Fall through...
-   */
-
- done:
   if (!nhi.gateway && nhi.if_index == 0)
     return 0;
 
@@ -272,7 +235,8 @@
 netlink_route_info_fill (netlink_route_info_t *ri, int cmd,
 			 rib_dest_t *dest, struct rib *rib)
 {
-  struct nexthop *nexthop = NULL;
+  struct nexthop *nexthop, *tnexthop;
+  int recursing;
   int discard;
 
   memset (ri, 0, sizeof (*ri));
@@ -321,35 +285,20 @@
       goto skip;
     }
 
-  /* Multipath case. */
-  if (rib->nexthop_active_num == 1 || MULTIPATH_NUM == 1)
+  for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing))
     {
-      for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
-        {
+      if (MULTIPATH_NUM != 0 && ri->num_nhs >= MULTIPATH_NUM)
+        break;
 
-          if ((cmd == RTM_NEWROUTE
-               && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
-              || (cmd == RTM_DELROUTE
-                  && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)))
-            {
-	      netlink_route_info_add_nh (ri, nexthop);
-              break;
-            }
-        }
-    }
-  else
-    {
-      for (nexthop = rib->nexthop;
-           nexthop && (MULTIPATH_NUM == 0 || ri->num_nhs < MULTIPATH_NUM);
-           nexthop = nexthop->next)
+      if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
+        continue;
+
+      if ((cmd == RTM_NEWROUTE
+           && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
+          || (cmd == RTM_DELROUTE
+              && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)))
         {
-          if ((cmd == RTM_NEWROUTE
-               && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
-              || (cmd == RTM_DELROUTE
-                  && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)))
-            {
-	      netlink_route_info_add_nh (ri, nexthop);
-            }
+          netlink_route_info_add_nh (ri, nexthop, recursing);
         }
     }