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)