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_netlink.c b/zebra/rt_netlink.c
index 86e02ef..b0ade05 100644
--- a/zebra/rt_netlink.c
+++ b/zebra/rt_netlink.c
@@ -1426,6 +1426,207 @@
   return 0;
 }
 
+/* This function takes a nexthop as argument and adds
+ * the appropriate netlink attributes to an existing
+ * netlink message.
+ *
+ * @param routedesc: Human readable description of route type
+ *                   (direct/recursive, single-/multipath)
+ * @param bytelen: Length of addresses in bytes.
+ * @param nexthop: Nexthop information
+ * @param nlmsg: nlmsghdr structure to fill in.
+ * @param req_size: The size allocated for the message.
+ */
+static void
+_netlink_route_build_singlepath(
+        const char *routedesc,
+        int bytelen,
+        struct nexthop *nexthop,
+        struct nlmsghdr *nlmsg,
+        size_t req_size)
+{
+  if (nexthop->type == NEXTHOP_TYPE_IPV4
+      || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX)
+    {
+      addattr_l (nlmsg, req_size, RTA_GATEWAY,
+                 &nexthop->gate.ipv4, bytelen);
+      if (nexthop->src.ipv4.s_addr)
+        addattr_l (nlmsg, req_size, RTA_PREFSRC,
+                   &nexthop->src.ipv4, bytelen);
+
+      if (IS_ZEBRA_DEBUG_KERNEL)
+        zlog_debug("netlink_route_multipath() (%s): "
+                   "nexthop via %s if %u",
+                   routedesc,
+                   inet_ntoa (nexthop->gate.ipv4),
+                   nexthop->ifindex);
+    }
+#ifdef HAVE_IPV6
+  if (nexthop->type == NEXTHOP_TYPE_IPV6
+      || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME
+      || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)
+    {
+      addattr_l (nlmsg, req_size, RTA_GATEWAY,
+                 &nexthop->gate.ipv6, bytelen);
+
+      if (IS_ZEBRA_DEBUG_KERNEL)
+        zlog_debug("netlink_route_multipath() (%s): "
+                   "nexthop via %s if %u",
+                   routedesc,
+                   inet6_ntoa (nexthop->gate.ipv6),
+                   nexthop->ifindex);
+    }
+#endif /* HAVE_IPV6 */
+  if (nexthop->type == NEXTHOP_TYPE_IFINDEX
+      || nexthop->type == NEXTHOP_TYPE_IFNAME
+      || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX)
+    {
+      addattr32 (nlmsg, req_size, RTA_OIF, nexthop->ifindex);
+
+      if (nexthop->src.ipv4.s_addr)
+        addattr_l (nlmsg, req_size, RTA_PREFSRC,
+                   &nexthop->src.ipv4, bytelen);
+
+      if (IS_ZEBRA_DEBUG_KERNEL)
+        zlog_debug("netlink_route_multipath() (%s): "
+                   "nexthop via if %u", routedesc, nexthop->ifindex);
+    }
+
+  if (nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX
+      || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME)
+    {
+      addattr32 (nlmsg, req_size, RTA_OIF, nexthop->ifindex);
+
+      if (IS_ZEBRA_DEBUG_KERNEL)
+        zlog_debug("netlink_route_multipath() (%s): "
+                   "nexthop via if %u", routedesc, nexthop->ifindex);
+    }
+}
+
+/* This function takes a nexthop as argument and
+ * appends to the given rtattr/rtnexthop pair the
+ * representation of the nexthop. If the nexthop
+ * defines a preferred source, the src parameter
+ * will be modified to point to that src, otherwise
+ * it will be kept unmodified.
+ *
+ * @param routedesc: Human readable description of route type
+ *                   (direct/recursive, single-/multipath)
+ * @param bytelen: Length of addresses in bytes.
+ * @param nexthop: Nexthop information
+ * @param rta: rtnetlink attribute structure
+ * @param rtnh: pointer to an rtnetlink nexthop structure
+ * @param src: pointer pointing to a location where
+ *             the prefsrc should be stored.
+ */
+static void
+_netlink_route_build_multipath(
+        const char *routedesc,
+        int bytelen,
+        struct nexthop *nexthop,
+        struct rtattr *rta,
+        struct rtnexthop *rtnh,
+        union g_addr **src
+        )
+{
+  rtnh->rtnh_len = sizeof (*rtnh);
+  rtnh->rtnh_flags = 0;
+  rtnh->rtnh_hops = 0;
+  rta->rta_len += rtnh->rtnh_len;
+
+  if (nexthop->type == NEXTHOP_TYPE_IPV4
+      || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX)
+    {
+      rta_addattr_l (rta, NL_PKT_BUF_SIZE, RTA_GATEWAY,
+                     &nexthop->gate.ipv4, bytelen);
+      rtnh->rtnh_len += sizeof (struct rtattr) + 4;
+
+      if (nexthop->src.ipv4.s_addr)
+        *src = &nexthop->src;
+
+      if (IS_ZEBRA_DEBUG_KERNEL)
+        zlog_debug("netlink_route_multipath() (%s): "
+                   "nexthop via %s if %u",
+                   routedesc,
+                   inet_ntoa (nexthop->gate.ipv4),
+                   nexthop->ifindex);
+    }
+#ifdef HAVE_IPV6
+  if (nexthop->type == NEXTHOP_TYPE_IPV6
+      || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME
+      || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)
+    {
+      rta_addattr_l (rta, NL_PKT_BUF_SIZE, RTA_GATEWAY,
+                     &nexthop->gate.ipv6, bytelen);
+
+      if (IS_ZEBRA_DEBUG_KERNEL)
+        zlog_debug("netlink_route_multipath() (%s): "
+                   "nexthop via %s if %u",
+                   routedesc,
+                   inet6_ntoa (nexthop->gate.ipv6),
+                   nexthop->ifindex);
+    }
+#endif /* HAVE_IPV6 */
+  /* ifindex */
+  if (nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX
+      || nexthop->type == NEXTHOP_TYPE_IFINDEX
+      || nexthop->type == NEXTHOP_TYPE_IFNAME)
+    {
+      rtnh->rtnh_ifindex = nexthop->ifindex;
+      if (nexthop->src.ipv4.s_addr)
+        *src = &nexthop->src;
+      if (IS_ZEBRA_DEBUG_KERNEL)
+        zlog_debug("netlink_route_multipath() (%s): "
+                   "nexthop via if %u", routedesc, nexthop->ifindex);
+    }
+  else if (nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME
+      || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)
+    {
+      rtnh->rtnh_ifindex = nexthop->ifindex;
+
+      if (IS_ZEBRA_DEBUG_KERNEL)
+        zlog_debug("netlink_route_multipath() (%s): "
+                   "nexthop via if %u", routedesc, nexthop->ifindex);
+    }
+  else
+    {
+      rtnh->rtnh_ifindex = 0;
+    }
+}
+
+/* Log debug information for netlink_route_multipath
+ * if debug logging is enabled.
+ *
+ * @param cmd: Netlink command which is to be processed
+ * @param p: Prefix for which the change is due
+ * @param nexthop: Nexthop which is currently processed
+ * @param routedesc: Semantic annotation for nexthop
+ *                     (recursive, multipath, etc.)
+ * @param family: Address family which the change concerns
+ */
+static void
+_netlink_route_debug(
+        int cmd,
+        struct prefix *p,
+        struct nexthop *nexthop,
+        const char *routedesc,
+        int family)
+{
+  if (IS_ZEBRA_DEBUG_KERNEL)
+    {
+      zlog_debug ("netlink_route_multipath() (%s): %s %s/%d type %s",
+         routedesc,
+         lookup (nlmsg_str, cmd),
+#ifdef HAVE_IPV6
+         (family == AF_INET) ? inet_ntoa (p->u.prefix4) :
+         inet6_ntoa (p->u.prefix6),
+#else
+         inet_ntoa (p->u.prefix4),
+#endif /* HAVE_IPV6 */
+         p->prefixlen, nexthop_type_to_str (nexthop->type));
+    }
+}
+
 /* Routing table change via netlink interface. */
 static int
 netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib,
@@ -1433,9 +1634,11 @@
 {
   int bytelen;
   struct sockaddr_nl snl;
-  struct nexthop *nexthop = NULL;
-  int nexthop_num = 0;
+  struct nexthop *nexthop = NULL, *tnexthop;
+  int recursing;
+  int nexthop_num;
   int discard;
+  const char *routedesc;
 
   struct
   {
@@ -1485,159 +1688,52 @@
   if (discard)
     {
       if (cmd == RTM_NEWROUTE)
-        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;
     }
 
-  /* Multipath case. */
-  if (rib->nexthop_active_num == 1 || MULTIPATH_NUM == 1)
+  /* Count overall nexthops so we can decide whether to use singlepath
+   * or multipath case. */
+  nexthop_num = 0;
+  for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing))
     {
-      for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
+      if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
+        continue;
+      if (cmd == RTM_NEWROUTE && !CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE))
+        continue;
+      if (cmd == RTM_DELROUTE && !CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))
+        continue;
+
+      nexthop_num++;
+    }
+
+  /* Singlepath case. */
+  if (nexthop_num == 1 || MULTIPATH_NUM == 1)
+    {
+      nexthop_num = 0;
+      for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing))
         {
+          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)))
             {
+              routedesc = recursing ? "recursive, 1 hop" : "single hop";
 
-              if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
-                {
-                  if (IS_ZEBRA_DEBUG_KERNEL)
-                    {
-                      zlog_debug
-                        ("netlink_route_multipath() (recursive, 1 hop): "
-                         "%s %s/%d, type %s", lookup (nlmsg_str, cmd),
-#ifdef HAVE_IPV6
-			 (family == AF_INET) ? inet_ntoa (p->u.prefix4) :
-			 inet6_ntoa (p->u.prefix6),
-#else
-			 inet_ntoa (p->u.prefix4),
-#endif /* HAVE_IPV6 */
-			 
-			 p->prefixlen, nexthop_type_to_str (nexthop->rtype));
-                    }
-
-                  if (nexthop->rtype == NEXTHOP_TYPE_IPV4
-                      || nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX)
-		    {
-		      addattr_l (&req.n, sizeof req, RTA_GATEWAY,
-				 &nexthop->rgate.ipv4, bytelen);
-                      if (nexthop->src.ipv4.s_addr)
-		          addattr_l(&req.n, sizeof req, RTA_PREFSRC,
-				     &nexthop->src.ipv4, bytelen);
-		      if (IS_ZEBRA_DEBUG_KERNEL)
-			zlog_debug("netlink_route_multipath() (recursive, "
-				   "1 hop): nexthop via %s if %u",
-				   inet_ntoa (nexthop->rgate.ipv4),
-				   nexthop->rifindex);
-		    }
-#ifdef HAVE_IPV6
-                  if (nexthop->rtype == NEXTHOP_TYPE_IPV6
-                      || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX
-                      || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME)
-		    {
-		      addattr_l (&req.n, sizeof req, RTA_GATEWAY,
-				 &nexthop->rgate.ipv6, bytelen);
-
-		      if (IS_ZEBRA_DEBUG_KERNEL)
-			zlog_debug("netlink_route_multipath() (recursive, "
-				   "1 hop): nexthop via %s if %u",
-				   inet6_ntoa (nexthop->rgate.ipv6),
-				   nexthop->rifindex);
-		    }
-#endif /* HAVE_IPV6 */
-                  if (nexthop->rtype == NEXTHOP_TYPE_IFINDEX
-                      || nexthop->rtype == NEXTHOP_TYPE_IFNAME
-                      || nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX
-                      || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX
-                      || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME)
-		    {
-		      addattr32 (&req.n, sizeof req, RTA_OIF,
-				 nexthop->rifindex);
-                      if ((nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX
-                           || nexthop->rtype == NEXTHOP_TYPE_IFINDEX)
-                          && nexthop->src.ipv4.s_addr)
-                        addattr_l (&req.n, sizeof req, RTA_PREFSRC,
-				 &nexthop->src.ipv4, bytelen);
-
-		      if (IS_ZEBRA_DEBUG_KERNEL)
-			zlog_debug("netlink_route_multipath() (recursive, "
-				   "1 hop): nexthop via if %u",
-				   nexthop->rifindex);
-		    }
-                }
-              else
-                {
-                  if (IS_ZEBRA_DEBUG_KERNEL)
-                    {
-                      zlog_debug
-                        ("netlink_route_multipath() (single hop): "
-                         "%s %s/%d, type %s", lookup (nlmsg_str, cmd),
-#ifdef HAVE_IPV6
-			 (family == AF_INET) ? inet_ntoa (p->u.prefix4) :
-			 inet6_ntoa (p->u.prefix6),
-#else
-			 inet_ntoa (p->u.prefix4),
-#endif /* HAVE_IPV6 */
-			 p->prefixlen, nexthop_type_to_str (nexthop->type));
-                    }
-
-                  if (nexthop->type == NEXTHOP_TYPE_IPV4
-                      || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX)
-		    {
-		      addattr_l (&req.n, sizeof req, RTA_GATEWAY,
-				 &nexthop->gate.ipv4, bytelen);
-		      if (nexthop->src.ipv4.s_addr)
-                        addattr_l (&req.n, sizeof req, RTA_PREFSRC,
-				 &nexthop->src.ipv4, bytelen);
-
-		      if (IS_ZEBRA_DEBUG_KERNEL)
-			zlog_debug("netlink_route_multipath() (single hop): "
-				   "nexthop via %s if %u",
-				   inet_ntoa (nexthop->gate.ipv4),
-				   nexthop->ifindex);
-		    }
-#ifdef HAVE_IPV6
-                  if (nexthop->type == NEXTHOP_TYPE_IPV6
-                      || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME
-                      || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)
-		    {
-		      addattr_l (&req.n, sizeof req, RTA_GATEWAY,
-				 &nexthop->gate.ipv6, bytelen);
-
-		      if (IS_ZEBRA_DEBUG_KERNEL)
-			zlog_debug("netlink_route_multipath() (single hop): "
-				   "nexthop via %s if %u",
-				   inet6_ntoa (nexthop->gate.ipv6),
-				   nexthop->ifindex);
-		    }
-#endif /* HAVE_IPV6 */
-                  if (nexthop->type == NEXTHOP_TYPE_IFINDEX
-                      || nexthop->type == NEXTHOP_TYPE_IFNAME
-                      || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX)
-		    {
-		      addattr32 (&req.n, sizeof req, RTA_OIF, nexthop->ifindex);
-
-		      if (nexthop->src.ipv4.s_addr)
-                        addattr_l (&req.n, sizeof req, RTA_PREFSRC,
-				 &nexthop->src.ipv4, bytelen);
-
-		      if (IS_ZEBRA_DEBUG_KERNEL)
-			zlog_debug("netlink_route_multipath() (single hop): "
-				   "nexthop via if %u", nexthop->ifindex);
-		    }
-                  else if (nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX
-                      || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME)
-		    {
-		      addattr32 (&req.n, sizeof req, RTA_OIF, nexthop->ifindex);
-
-		      if (IS_ZEBRA_DEBUG_KERNEL)
-			zlog_debug("netlink_route_multipath() (single hop): "
-				   "nexthop via if %u", nexthop->ifindex);
-		    }
-                }
+              _netlink_route_debug(cmd, p, nexthop, routedesc, family);
+              _netlink_route_build_singlepath(routedesc, bytelen,
+                                              nexthop, &req.n, sizeof req);
 
               if (cmd == RTM_NEWROUTE)
                 SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
@@ -1659,168 +1755,26 @@
       rtnh = RTA_DATA (rta);
 
       nexthop_num = 0;
-      for (nexthop = rib->nexthop;
-           nexthop && (MULTIPATH_NUM == 0 || nexthop_num < MULTIPATH_NUM);
-           nexthop = nexthop->next)
+      for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing))
         {
+          if (MULTIPATH_NUM != 0 && nexthop_num >= MULTIPATH_NUM)
+            break;
+
+          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)))
             {
+              routedesc = recursing ? "recursive, multihop" : "multihop";
               nexthop_num++;
 
-              rtnh->rtnh_len = sizeof (*rtnh);
-              rtnh->rtnh_flags = 0;
-              rtnh->rtnh_hops = 0;
-              rta->rta_len += rtnh->rtnh_len;
-
-              if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
-                {
-                  if (IS_ZEBRA_DEBUG_KERNEL)
-                    {
-                      zlog_debug ("netlink_route_multipath() "
-                         "(recursive, multihop): %s %s/%d type %s",
-			 lookup (nlmsg_str, cmd),
-#ifdef HAVE_IPV6
-			 (family == AF_INET) ? inet_ntoa (p->u.prefix4) :
-			 inet6_ntoa (p->u.prefix6),
-#else
-			 inet_ntoa (p->u.prefix4),
-#endif /* HAVE_IPV6 */
-			 p->prefixlen, nexthop_type_to_str (nexthop->rtype));
-                    }
-                  if (nexthop->rtype == NEXTHOP_TYPE_IPV4
-                      || nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX)
-                    {
-                      rta_addattr_l (rta, NL_PKT_BUF_SIZE, RTA_GATEWAY,
-                                     &nexthop->rgate.ipv4, bytelen);
-                      rtnh->rtnh_len += sizeof (struct rtattr) + 4;
-
-		      if (nexthop->src.ipv4.s_addr)
-                        src = &nexthop->src;
-
-		      if (IS_ZEBRA_DEBUG_KERNEL)
-			zlog_debug("netlink_route_multipath() (recursive, "
-				   "multihop): nexthop via %s if %u",
-				   inet_ntoa (nexthop->rgate.ipv4),
-				   nexthop->rifindex);
-                    }
-#ifdef HAVE_IPV6
-                  if (nexthop->rtype == NEXTHOP_TYPE_IPV6
-                      || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME
-                      || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX)
-		    {
-		      rta_addattr_l (rta, NL_PKT_BUF_SIZE, RTA_GATEWAY,
-				     &nexthop->rgate.ipv6, bytelen);
-
-		      if (IS_ZEBRA_DEBUG_KERNEL)
-			zlog_debug("netlink_route_multipath() (recursive, "
-				   "multihop): nexthop via %s if %u",
-				   inet6_ntoa (nexthop->rgate.ipv6),
-				   nexthop->rifindex);
-		    }
-#endif /* HAVE_IPV6 */
-                  /* ifindex */
-                  if (nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX
-		      || nexthop->rtype == NEXTHOP_TYPE_IFINDEX
-                      || nexthop->rtype == NEXTHOP_TYPE_IFNAME)
-		    {
-		      rtnh->rtnh_ifindex = nexthop->rifindex;
-                      if (nexthop->src.ipv4.s_addr)
-                        src = &nexthop->src;
-
-		      if (IS_ZEBRA_DEBUG_KERNEL)
-			zlog_debug("netlink_route_multipath() (recursive, "
-				   "multihop): nexthop via if %u",
-				   nexthop->rifindex);
-		    }
-		  else if (nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX
-                      || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME)
-		    {
-		      rtnh->rtnh_ifindex = nexthop->rifindex;
-
-		      if (IS_ZEBRA_DEBUG_KERNEL)
-			zlog_debug("netlink_route_multipath() (recursive, "
-				   "multihop): nexthop via if %u",
-				   nexthop->rifindex);
-		    }
-                  else
-		    {
-		      rtnh->rtnh_ifindex = 0;
-		    }
-                }
-              else
-                {
-                  if (IS_ZEBRA_DEBUG_KERNEL)
-                    {
-                      zlog_debug ("netlink_route_multipath() (multihop): "
-                         "%s %s/%d, type %s", lookup (nlmsg_str, cmd),
-#ifdef HAVE_IPV6
-			 (family == AF_INET) ? inet_ntoa (p->u.prefix4) :
-			 inet6_ntoa (p->u.prefix6),
-#else
-			 inet_ntoa (p->u.prefix4),
-#endif /* HAVE_IPV6 */
-			 p->prefixlen, nexthop_type_to_str (nexthop->type));
-                    }
-                  if (nexthop->type == NEXTHOP_TYPE_IPV4
-                      || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX)
-                    {
-		      rta_addattr_l (rta, NL_PKT_BUF_SIZE, RTA_GATEWAY,
-				     &nexthop->gate.ipv4, bytelen);
-		      rtnh->rtnh_len += sizeof (struct rtattr) + 4;
-
-		      if (nexthop->src.ipv4.s_addr)
-                        src = &nexthop->src;
-
-                      if (IS_ZEBRA_DEBUG_KERNEL)
-			zlog_debug("netlink_route_multipath() (multihop): "
-				   "nexthop via %s if %u",
-				   inet_ntoa (nexthop->gate.ipv4),
-				   nexthop->ifindex);
-                    }
-#ifdef HAVE_IPV6
-                  if (nexthop->type == NEXTHOP_TYPE_IPV6
-                      || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME
-                      || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)
-		    { 
-		      rta_addattr_l (rta, NL_PKT_BUF_SIZE, RTA_GATEWAY,
-				     &nexthop->gate.ipv6, bytelen);
-
-		      if (IS_ZEBRA_DEBUG_KERNEL)
-			zlog_debug("netlink_route_multipath() (multihop): "
-				   "nexthop via %s if %u",
-				   inet6_ntoa (nexthop->gate.ipv6),
-				   nexthop->ifindex);
-		    }
-#endif /* HAVE_IPV6 */
-                  /* ifindex */
-                  if (nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX
-		      || nexthop->type == NEXTHOP_TYPE_IFINDEX
-                      || nexthop->type == NEXTHOP_TYPE_IFNAME)
-                    {
-		      rtnh->rtnh_ifindex = nexthop->ifindex;
-		      if (nexthop->src.ipv4.s_addr)
-			src = &nexthop->src;
-		      if (IS_ZEBRA_DEBUG_KERNEL)
-			zlog_debug("netlink_route_multipath() (multihop): "
-				   "nexthop via if %u", nexthop->ifindex);
-		    }
-                  else if (nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME
-                      || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)
-		    {
-		      rtnh->rtnh_ifindex = nexthop->ifindex;
-
-		      if (IS_ZEBRA_DEBUG_KERNEL)
-			zlog_debug("netlink_route_multipath() (multihop): "
-				   "nexthop via if %u", nexthop->ifindex);
-		    }
-                  else
-		    {
-		      rtnh->rtnh_ifindex = 0;
-		    }
-                }
+              _netlink_route_debug(cmd, p, nexthop,
+                                   routedesc, family);
+              _netlink_route_build_multipath(routedesc, bytelen,
+                                             nexthop, rta, rtnh, &src);
               rtnh = RTNH_NEXT (rtnh);
 
               if (cmd == RTM_NEWROUTE)