From 41fc27146af720a40b5c3bbf82dd4ce6f884f372 Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Tue, 19 May 2015 17:24:43 -0700 Subject: [PATCH] This patch enables support for multipath for IPV6. The nexthop information from the protocols have ifindices and nexthop addresses in two different structures. This patch combines them to ensure that the correct APIs can be called. Also, given that IPV6 Linux implementation does not support the rta_XXX APIs for multipath, the communication with the kernel is in terms of a single nh/ifindex pair. --- zebra/rib.h | 12 ++++++ zebra/rt_netlink.c | 2 +- zebra/zebra_rib.c | 96 ++++++++++++++++++++++++++++++++++++++++++-- zebra/zserv.c | 99 +++++++++++++++++++++++++++++++++------------- 4 files changed, 178 insertions(+), 31 deletions(-) diff --git a/zebra/rib.h b/zebra/rib.h index d3a83c68a0..ad726c6cc9 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -398,6 +398,14 @@ extern int rib_lookup_ipv4_route (struct prefix_ipv4 *, union sockunion *); #ifdef HAVE_IPV6 extern struct nexthop *nexthop_ipv6_add (struct rib *, struct in6_addr *); +extern struct nexthop *nexthop_ipv6_ifindex_add (struct rib *rib, + struct in6_addr *ipv6, unsigned int ifindex); +extern struct nexthop *nexthop_ipv6_ifname_add (struct rib *rib, + struct in6_addr *ipv6, + char *ifname); +extern int +rib_bogus_ipv6 (int type, struct prefix_ipv6 *p, + struct in6_addr *gate, unsigned int ifindex, int table); #endif /* HAVE_IPV6 */ extern struct vrf *vrf_lookup (u_int32_t); @@ -458,6 +466,10 @@ static_add_ipv6 (struct prefix *p, u_char type, struct in6_addr *gate, const char *ifname, u_char flags, u_char distance, u_int32_t vrf_id); +extern int +rib_add_ipv6_multipath (struct prefix_ipv6 *, struct rib *, safi_t, + unsigned long); + extern int static_delete_ipv6 (struct prefix *p, u_char type, struct in6_addr *gate, const char *ifname, u_char distance, u_int32_t vrf_id); diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 95a82fd2e4..c242480f03 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -1580,7 +1580,7 @@ _netlink_route_build_multipath( { rta_addattr_l (rta, NL_PKT_BUF_SIZE, RTA_GATEWAY, &nexthop->gate.ipv6, bytelen); - + rtnh->rtnh_len += sizeof (struct rtattr) + bytelen; if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug("netlink_route_multipath() (%s): " "nexthop via %s if %u", diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 93edc23d72..02116b3d29 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -341,7 +341,7 @@ nexthop_ipv6_add (struct rib *rib, struct in6_addr *ipv6) return nexthop; } -static struct nexthop * +struct nexthop * nexthop_ipv6_ifname_add (struct rib *rib, struct in6_addr *ipv6, char *ifname) { @@ -357,7 +357,7 @@ nexthop_ipv6_ifname_add (struct rib *rib, struct in6_addr *ipv6, return nexthop; } -static struct nexthop * +struct nexthop * nexthop_ipv6_ifindex_add (struct rib *rib, struct in6_addr *ipv6, unsigned int ifindex) { @@ -2598,7 +2598,7 @@ static_delete_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname, #ifdef HAVE_IPV6 -static int +int rib_bogus_ipv6 (int type, struct prefix_ipv6 *p, struct in6_addr *gate, unsigned int ifindex, int table) { @@ -2727,6 +2727,96 @@ rib_add_ipv6 (int type, int flags, struct prefix_ipv6 *p, return 0; } +int +rib_add_ipv6_multipath (struct prefix_ipv6 *p, struct rib *rib, safi_t safi, + unsigned long ifindex) +{ + struct route_table *table; + struct route_node *rn; + struct rib *same = NULL; + struct nexthop *nexthop; + int ret = 0; + int table_id = 0; + + if (rib) + table_id = rib->table; + else + return 0; /* why are we getting called with NULL rib */ + + /* Lookup table. */ + table = vrf_table (AFI_IP6, safi, 0); + + if (! table) + return 0; + + /* Make sure mask is applied. */ + apply_mask_ipv6 (p); + + /* Set default distance by route type. */ + if (rib->distance == 0) + { + rib->distance = route_info[rib->type].distance; + + /* iBGP distance is 200. */ + if (rib->type == ZEBRA_ROUTE_BGP + && CHECK_FLAG (rib->flags, ZEBRA_FLAG_IBGP)) + rib->distance = 200; + } + + /* Lookup route node.*/ + rn = route_node_get (table, (struct prefix *) p); + + /* If same type of route are installed, treat it as a implicit + withdraw. */ + RNODE_FOREACH_RIB (rn, same) { + if (CHECK_FLAG (same->status, RIB_ENTRY_REMOVED)) { + continue; + } + if (same->type != rib->type) { + continue; + } + + if (same->table != rib->table) { + continue; + } + if (same->type != ZEBRA_ROUTE_CONNECT) { + break; + } + else if ((nexthop = same->nexthop) && + nexthop->type == NEXTHOP_TYPE_IFINDEX && + nexthop->ifindex == ifindex) { + same->refcnt++; + return 0; + } + } + + /* If this route is kernel route, set FIB flag to the route. */ + if (rib->type == ZEBRA_ROUTE_KERNEL || rib->type == ZEBRA_ROUTE_CONNECT) { + for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) { + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); + } + } + + /* Link new rib to node.*/ + rib_addnode (rn, rib); + ret = 1; + /* Free implicit route.*/ + if (same) + { + if (IS_ZEBRA_DEBUG_RIB) + { + zlog_debug ("%s: calling rib_delnode (%p, %p) on existing RIB entry", + __func__, rn, same); + rib_dump ((struct prefix *)p, same); + } + rib_delnode (rn, same); + ret = -1; + } + + route_unlock_node (rn); + return ret; +} + /* XXX factor with rib_delete_ipv6 */ int rib_delete_ipv6 (int type, int flags, struct prefix_ipv6 *p, diff --git a/zebra/zserv.c b/zebra/zserv.c index ca17c2c6de..b245b7f7bc 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -940,34 +940,52 @@ zread_ipv6_add (struct zserv *client, u_short length) { int i; struct stream *s; - struct zapi_ipv6 api; struct in6_addr nexthop; + struct rib *rib; + u_char message; + u_char nexthop_num; + u_char nexthop_type; unsigned long ifindex; struct prefix_ipv6 p; - + u_char ifname_len; + safi_t safi; + static struct in6_addr nexthops[MULTIPATH_NUM]; + static unsigned int ifindices[MULTIPATH_NUM]; + + /* Get input stream. */ s = client->ibuf; + ifindex = 0; memset (&nexthop, 0, sizeof (struct in6_addr)); + /* Allocate new rib. */ + rib = XCALLOC (MTYPE_RIB, sizeof (struct rib)); + /* Type, flags, message. */ - api.type = stream_getc (s); - api.flags = stream_getc (s); - api.message = stream_getc (s); - api.safi = stream_getw (s); + rib->type = stream_getc (s); + rib->flags = stream_getc (s); + message = stream_getc (s); + safi = stream_getw (s); + rib->uptime = time (NULL); - /* IPv4 prefix. */ + /* IPv6 prefix. */ memset (&p, 0, sizeof (struct prefix_ipv6)); p.family = AF_INET6; p.prefixlen = stream_getc (s); stream_get (&p.prefix, s, PSIZE (p.prefixlen)); - /* Nexthop, ifindex, distance, metric. */ - if (CHECK_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP)) + /* We need to give nh-addr, nh-ifindex with the same next-hop object + * to the rib to ensure that IPv6 multipathing works; need to coalesce + * these. Clients should send the same number of paired set of + * next-hop-addr/next-hop-ifindices. */ + if (CHECK_FLAG (message, ZAPI_MESSAGE_NEXTHOP)) { - u_char nexthop_type; + int nh_count = 0; + int if_count = 0; + int max_nh_if = 0; - api.nexthop_num = stream_getc (s); - for (i = 0; i < api.nexthop_num; i++) + nexthop_num = stream_getc (s); + for (i = 0; i < nexthop_num; i++) { nexthop_type = stream_getc (s); @@ -975,30 +993,57 @@ zread_ipv6_add (struct zserv *client, u_short length) { case ZEBRA_NEXTHOP_IPV6: stream_get (&nexthop, s, 16); + if (nh_count < MULTIPATH_NUM) { + nexthops[nh_count++] = nexthop; + } break; case ZEBRA_NEXTHOP_IFINDEX: - ifindex = stream_getl (s); + if (if_count < MULTIPATH_NUM) { + ifindices[if_count++] = stream_getl (s); + } break; } } + + max_nh_if = (nh_count > if_count) ? nh_count : if_count; + for (i = 0; i < max_nh_if; i++) + { + if ((i < nh_count) && !IN6_IS_ADDR_UNSPECIFIED (&nexthops[i])) { + if ((i < if_count) && ifindices[i]) { + if (rib_bogus_ipv6 (rib->type, &p, &nexthops[i], ifindices[i], 0)) { + continue; + } + nexthop_ipv6_ifindex_add (rib, &nexthops[i], ifindices[i]); + } + else { + if (rib_bogus_ipv6 (rib->type, &p, &nexthops[i], 0, 0)) { + continue; + } + nexthop_ipv6_add (rib, &nexthops[i]); + } + } + else { + if ((i < if_count) && ifindices[i]) { + if (rib_bogus_ipv6 (rib->type, &p, NULL, ifindices[i], 0)) { + continue; + } + nexthop_ifindex_add (rib, ifindices[i]); + } + } + } } - if (CHECK_FLAG (api.message, ZAPI_MESSAGE_DISTANCE)) - api.distance = stream_getc (s); - else - api.distance = 0; + /* Distance. */ + if (CHECK_FLAG (message, ZAPI_MESSAGE_DISTANCE)) + rib->distance = stream_getc (s); - if (CHECK_FLAG (api.message, ZAPI_MESSAGE_METRIC)) - api.metric = stream_getl (s); - else - api.metric = 0; + /* Metric. */ + if (CHECK_FLAG (message, ZAPI_MESSAGE_METRIC)) + rib->metric = stream_getl (s); - if (IN6_IS_ADDR_UNSPECIFIED (&nexthop)) - rib_add_ipv6 (api.type, api.flags, &p, NULL, ifindex, zebrad.rtm_table_default, api.metric, - api.distance, api.safi); - else - rib_add_ipv6 (api.type, api.flags, &p, &nexthop, ifindex, zebrad.rtm_table_default, api.metric, - api.distance, api.safi); + /* Table */ + rib->table=zebrad.rtm_table_default; + rib_add_ipv6_multipath (&p, rib, safi, ifindex); return 0; } -- 2.39.5