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/rt_ioctl.c b/zebra/rt_ioctl.c
index a5d588c..404a7c6 100644
--- a/zebra/rt_ioctl.c
+++ b/zebra/rt_ioctl.c
@@ -169,7 +169,8 @@
   int sock;
   struct rtentry rtentry;
   struct sockaddr_in sin_dest, sin_mask, sin_gate;
-  struct nexthop *nexthop;
+  struct nexthop *nexthop, *tnexthop;
+  int recursing;
   int nexthop_num = 0;
   struct interface *ifp;
 
@@ -188,65 +189,49 @@
       SET_FLAG (rtentry.rt_flags, RTF_REJECT);
 
       if (cmd == SIOCADDRT)
-	for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
-	  SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
-
+	for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing))
+	  {
+            /* We shouldn't encounter recursive nexthops on discard routes,
+             * but it is probably better to handle that case correctly anyway.
+             */
+	    if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
+	      continue;
+	    SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
+	  }
       goto skip;
     }
 
   memset (&sin_gate, 0, sizeof (struct sockaddr_in));
 
   /* Make gateway. */
-  for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
+  for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing))
     {
+      if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
+        continue;
+
       if ((cmd == SIOCADDRT 
 	   && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
 	  || (cmd == SIOCDELRT
 	      && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)))
 	{
-	  if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
+	  if (nexthop->type == NEXTHOP_TYPE_IPV4 ||
+	      nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX)
 	    {
-	      if (nexthop->rtype == NEXTHOP_TYPE_IPV4 ||
-		  nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX)
-		{
-		  sin_gate.sin_family = AF_INET;
+	      sin_gate.sin_family = AF_INET;
 #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
-		  sin_gate.sin_len = sizeof (struct sockaddr_in);
+	      sin_gate.sin_len = sizeof (struct sockaddr_in);
 #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
-		  sin_gate.sin_addr = nexthop->rgate.ipv4;
-		  rtentry.rt_flags |= RTF_GATEWAY;
-		}
-	      if (nexthop->rtype == NEXTHOP_TYPE_IFINDEX
-		  || nexthop->rtype == NEXTHOP_TYPE_IFNAME)
-		{
-		  ifp = if_lookup_by_index (nexthop->rifindex);
-		  if (ifp)
-		    rtentry.rt_dev = ifp->name;
-		  else
-		    return -1;
-		}
+	      sin_gate.sin_addr = nexthop->gate.ipv4;
+	      rtentry.rt_flags |= RTF_GATEWAY;
 	    }
-	  else
+	  if (nexthop->type == NEXTHOP_TYPE_IFINDEX
+	      || nexthop->type == NEXTHOP_TYPE_IFNAME)
 	    {
-	      if (nexthop->type == NEXTHOP_TYPE_IPV4 ||
-		  nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX)
-		{
-		  sin_gate.sin_family = AF_INET;
-#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
-		  sin_gate.sin_len = sizeof (struct sockaddr_in);
-#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
-		  sin_gate.sin_addr = nexthop->gate.ipv4;
-		  rtentry.rt_flags |= RTF_GATEWAY;
-		}
-	      if (nexthop->type == NEXTHOP_TYPE_IFINDEX
-		  || nexthop->type == NEXTHOP_TYPE_IFNAME)
-		{
-		  ifp = if_lookup_by_index (nexthop->ifindex);
-		  if (ifp)
-		    rtentry.rt_dev = ifp->name;
-		  else
-		    return -1;
-		}
+	      ifp = if_lookup_by_index (nexthop->ifindex);
+	      if (ifp)
+	        rtentry.rt_dev = ifp->name;
+	      else
+	        return -1;
 	    }
 
 	  if (cmd == SIOCADDRT)
@@ -430,7 +415,8 @@
   int ret;
   int sock;
   struct in6_rtmsg rtm;
-  struct nexthop *nexthop;
+  struct nexthop *nexthop, *tnexthop;
+  int recursing;
   int nexthop_num = 0;
     
   memset (&rtm, 0, sizeof (struct in6_rtmsg));
@@ -456,48 +442,30 @@
   /* rtm.rtmsg_flags |= RTF_DYNAMIC; */
 
   /* Make gateway. */
-  for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
+  for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing))
     {
+      if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
+        continue;
+
       if ((cmd == SIOCADDRT 
 	   && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
 	  || (cmd == SIOCDELRT
 	      && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)))
 	{
-	  if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
+	  if (nexthop->type == NEXTHOP_TYPE_IPV6
+	      || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME
+	      || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)
 	    {
-	      if (nexthop->rtype == NEXTHOP_TYPE_IPV6
-		  || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME
-		  || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX)
-		{
-		  memcpy (&rtm.rtmsg_gateway, &nexthop->rgate.ipv6,
-			  sizeof (struct in6_addr));
-		}
-	      if (nexthop->rtype == NEXTHOP_TYPE_IFINDEX
-		  || nexthop->rtype == NEXTHOP_TYPE_IFNAME
-		  || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME
-		  || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX)
-		rtm.rtmsg_ifindex = nexthop->rifindex;
-	      else
-		rtm.rtmsg_ifindex = 0;
-	      
+	      memcpy (&rtm.rtmsg_gateway, &nexthop->gate.ipv6,
+		      sizeof (struct in6_addr));
 	    }
+	  if (nexthop->type == NEXTHOP_TYPE_IFINDEX
+	      || nexthop->type == NEXTHOP_TYPE_IFNAME
+	      || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME
+	      || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)
+	    rtm.rtmsg_ifindex = nexthop->ifindex;
 	  else
-	    {
-	      if (nexthop->type == NEXTHOP_TYPE_IPV6
-		  || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME
-		  || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)
-		{
-		  memcpy (&rtm.rtmsg_gateway, &nexthop->gate.ipv6,
-			  sizeof (struct in6_addr));
-		}
-	      if (nexthop->type == NEXTHOP_TYPE_IFINDEX
-		  || nexthop->type == NEXTHOP_TYPE_IFNAME
-		  || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME
-		  || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)
-		rtm.rtmsg_ifindex = nexthop->ifindex;
-	      else
-		rtm.rtmsg_ifindex = 0;
-	    }
+	    rtm.rtmsg_ifindex = 0;
 
 	  if (cmd == SIOCADDRT)
 	    SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);