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_vty.c b/zebra/zebra_vty.c
index a672d42..e1da7df 100644
--- a/zebra/zebra_vty.c
+++ b/zebra/zebra_vty.c
@@ -533,7 +533,8 @@
 vty_show_ip_route_detail (struct vty *vty, struct route_node *rn)
 {
   struct rib *rib;
-  struct nexthop *nexthop;
+  struct nexthop *nexthop, *tnexthop;
+  int recursing;
 
   RNODE_FOREACH_RIB (rn, rib)
     {
@@ -582,12 +583,13 @@
 	  vty_out (vty, " ago%s", VTY_NEWLINE);
 	}
 
-      for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
+      for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing))
 	{
           char addrstr[32];
 
-	  vty_out (vty, "  %c",
-		   CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) ? '*' : ' ');
+	  vty_out (vty, "  %c%s",
+		   CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) ? '*' : ' ',
+		   recursing ? "  " : "");
 
 	  switch (nexthop->type)
 	    {
@@ -614,28 +616,8 @@
 	    vty_out (vty, " inactive");
 
 	  if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
-	    {
-	      vty_out (vty, " (recursive");
-		
-	      switch (nexthop->rtype)
-		{
-		case NEXTHOP_TYPE_IPV4:
-		case NEXTHOP_TYPE_IPV4_IFINDEX:
-		  vty_out (vty, " via %s", inet_ntoa (nexthop->rgate.ipv4));
-		  if (nexthop->rifindex)
-		    vty_out (vty, ", %s", ifindex2ifname (nexthop->rifindex));
-		  vty_out (vty, ")");
+	    vty_out (vty, " (recursive)");
 
-		  break;
-		case NEXTHOP_TYPE_IFINDEX:
-		case NEXTHOP_TYPE_IFNAME:
-		  vty_out (vty, " is directly connected, %s)",
-			   ifindex2ifname (nexthop->rifindex));
-		  break;
-		default:
-		  break;
-		}
-	    }
 	  switch (nexthop->type)
             {
             case NEXTHOP_TYPE_IPV4:
@@ -672,12 +654,13 @@
 static void
 vty_show_ip_route (struct vty *vty, struct route_node *rn, struct rib *rib)
 {
-  struct nexthop *nexthop;
+  struct nexthop *nexthop, *tnexthop;
+  int recursing;
   int len = 0;
   char buf[BUFSIZ];
 
   /* Nexthop information. */
-  for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
+  for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing))
     {
       if (nexthop == rib->nexthop)
 	{
@@ -701,7 +684,7 @@
 	vty_out (vty, "  %c%*c",
 		 CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)
 		 ? '*' : ' ',
-		 len - 3, ' ');
+		 len - 3 + (2 * recursing), ' ');
 
       switch (nexthop->type)
 	{
@@ -728,27 +711,8 @@
 	vty_out (vty, " inactive");
 
       if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
-	{
-	  vty_out (vty, " (recursive");
-		
-	  switch (nexthop->rtype)
-	    {
-	    case NEXTHOP_TYPE_IPV4:
-	    case NEXTHOP_TYPE_IPV4_IFINDEX:
-	      vty_out (vty, " via %s", inet_ntoa (nexthop->rgate.ipv4));
-	      if (nexthop->rifindex)
-		vty_out (vty, ", %s", ifindex2ifname (nexthop->rifindex));
-	      vty_out (vty, ")");
-	      break;
-	    case NEXTHOP_TYPE_IFINDEX:
-	    case NEXTHOP_TYPE_IFNAME:
-	      vty_out (vty, " is directly connected, %s)",
-		       ifindex2ifname (nexthop->rifindex));
-	      break;
-	    default:
-	      break;
-	    }
-	}
+	vty_out (vty, " (recursive)");
+
       switch (nexthop->type)
         {
           case NEXTHOP_TYPE_IPV4:
@@ -1058,7 +1022,8 @@
         {
 	  rib_cnt[ZEBRA_ROUTE_TOTAL]++;
 	  rib_cnt[rib->type]++;
-	  if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) 
+	  if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)
+	      || nexthop_has_fib_child(nexthop))
 	    {
 	      fib_cnt[ZEBRA_ROUTE_TOTAL]++;
 	      fib_cnt[rib->type]++;
@@ -1067,7 +1032,8 @@
 	      CHECK_FLAG (rib->flags, ZEBRA_FLAG_IBGP)) 
 	    {
 	      rib_cnt[ZEBRA_ROUTE_IBGP]++;
-	      if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) 
+	      if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)
+		  || nexthop_has_fib_child(nexthop))
 		fib_cnt[ZEBRA_ROUTE_IBGP]++;
 	    }
 	}
@@ -1550,7 +1516,8 @@
 vty_show_ipv6_route_detail (struct vty *vty, struct route_node *rn)
 {
   struct rib *rib;
-  struct nexthop *nexthop;
+  struct nexthop *nexthop, *tnexthop;
+  int recursing;
   char buf[BUFSIZ];
 
   RNODE_FOREACH_RIB (rn, rib)
@@ -1601,10 +1568,11 @@
 	  vty_out (vty, " ago%s", VTY_NEWLINE);
 	}
 
-      for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
+      for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing))
 	{
-	  vty_out (vty, "  %c",
-		   CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) ? '*' : ' ');
+	  vty_out (vty, "  %c%s",
+		   CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) ? '*' : ' ',
+		   recursing ? "  " : "");
 
 	  switch (nexthop->type)
 	    {
@@ -1633,29 +1601,8 @@
 	    vty_out (vty, " inactive");
 
 	  if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
-	    {
-	      vty_out (vty, " (recursive");
-		
-	      switch (nexthop->rtype)
-		{
-		case NEXTHOP_TYPE_IPV6:
-		case NEXTHOP_TYPE_IPV6_IFINDEX:
-		case NEXTHOP_TYPE_IPV6_IFNAME:
-		  vty_out (vty, " via %s)",
-			   inet_ntop (AF_INET6, &nexthop->rgate.ipv6,
-				      buf, BUFSIZ));
-		  if (nexthop->rifindex)
-		    vty_out (vty, ", %s", ifindex2ifname (nexthop->rifindex));
-		  break;
-		case NEXTHOP_TYPE_IFINDEX:
-		case NEXTHOP_TYPE_IFNAME:
-		  vty_out (vty, " is directly connected, %s)",
-			   ifindex2ifname (nexthop->rifindex));
-		  break;
-		default:
-		  break;
-		}
-	    }
+	    vty_out (vty, " (recursive)");
+
 	  vty_out (vty, "%s", VTY_NEWLINE);
 	}
       vty_out (vty, "%s", VTY_NEWLINE);
@@ -1666,12 +1613,13 @@
 vty_show_ipv6_route (struct vty *vty, struct route_node *rn,
 		     struct rib *rib)
 {
-  struct nexthop *nexthop;
+  struct nexthop *nexthop, *tnexthop;
+  int recursing;
   int len = 0;
   char buf[BUFSIZ];
 
   /* Nexthop information. */
-  for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
+  for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing))
     {
       if (nexthop == rib->nexthop)
 	{
@@ -1695,7 +1643,7 @@
 	vty_out (vty, "  %c%*c",
 		 CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)
 		 ? '*' : ' ',
-		 len - 3, ' ');
+		 len - 3 + (2 * recursing), ' ');
 
       switch (nexthop->type)
 	{
@@ -1724,29 +1672,7 @@
 	vty_out (vty, " inactive");
 
       if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
-	{
-	  vty_out (vty, " (recursive");
-		
-	  switch (nexthop->rtype)
-	    {
-	    case NEXTHOP_TYPE_IPV6:
-	    case NEXTHOP_TYPE_IPV6_IFINDEX:
-	    case NEXTHOP_TYPE_IPV6_IFNAME:
-	      vty_out (vty, " via %s)",
-		       inet_ntop (AF_INET6, &nexthop->rgate.ipv6,
-				  buf, BUFSIZ));
-	      if (nexthop->rifindex)
-		vty_out (vty, ", %s", ifindex2ifname (nexthop->rifindex));
-	      break;
-	    case NEXTHOP_TYPE_IFINDEX:
-	    case NEXTHOP_TYPE_IFNAME:
-	      vty_out (vty, " is directly connected, %s)",
-		       ifindex2ifname (nexthop->rifindex));
-	      break;
-	    default:
-	      break;
-	    }
-	}
+	vty_out (vty, " (recursive)");
 
       if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_BLACKHOLE))
        vty_out (vty, ", bh");