diff options
Diffstat (limited to 'zebra/zebra_rib.c')
-rw-r--r-- | zebra/zebra_rib.c | 669 |
1 files changed, 470 insertions, 199 deletions
diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 1650dabf..53691c76 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -35,13 +35,16 @@ #include "prefix.h" #include "routemap.h" #include "vrf.h" +#include "nexthop.h" +#include "zebra/connected.h" #include "zebra/rib.h" #include "zebra/rt.h" #include "zebra/zserv.h" #include "zebra/redistribute.h" #include "zebra/debug.h" #include "zebra/zebra_fpm.h" +#include "zebra/zebra_rnh.h" /* Default rtm_table for all clients */ extern struct zebra_t zebrad; @@ -110,57 +113,38 @@ _rnode_zlog(const char *_func, struct route_node *rn, int priority, #define rnode_info(node, ...) \ _rnode_zlog(__func__, node, LOG_INFO, __VA_ARGS__) -/* - * nexthop_type_to_str - */ -const char * -nexthop_type_to_str (enum nexthop_types_t nh_type) -{ - static const char *desc[] = { - "none", - "Directly connected", - "Interface route", - "IPv4 nexthop", - "IPv4 nexthop with ifindex", - "IPv4 nexthop with ifname", - "IPv6 nexthop", - "IPv6 nexthop with ifindex", - "IPv6 nexthop with ifname", - "Null0 nexthop", - }; - - if (nh_type >= ZEBRA_NUM_OF (desc)) - return "<Invalid nh type>"; - - return desc[nh_type]; -} - -/* Add nexthop to the end of a nexthop list. */ -static void -_nexthop_add (struct nexthop **target, struct nexthop *nexthop) +/* Add nexthop to the end of a rib node's nexthop list */ +void +rib_nexthop_add (struct rib *rib, struct nexthop *nexthop) { - struct nexthop *last; - - for (last = *target; last && last->next; last = last->next) - ; - if (last) - last->next = nexthop; - else - *target = nexthop; - nexthop->prev = last; + nexthop_add(&rib->nexthop, nexthop); + rib->nexthop_num++; } -/* Add nexthop to the end of a rib node's nexthop list */ -static void -nexthop_add (struct rib *rib, struct nexthop *nexthop) +/** + * copy_nexthop - copy a nexthop to the rib structure. + */ +void +rib_copy_nexthops (struct rib *rib, struct nexthop *nh) { - _nexthop_add(&rib->nexthop, nexthop); - rib->nexthop_num++; + struct nexthop *nexthop; + + nexthop = nexthop_new(); + nexthop->flags = nh->flags; + nexthop->type = nh->type; + nexthop->ifindex = nh->ifindex; + if (nh->ifname) + nexthop->ifname = XSTRDUP(0, nh->ifname); + memcpy(&(nexthop->gate), &(nh->gate), sizeof(union g_addr)); + memcpy(&(nexthop->src), &(nh->src), sizeof(union g_addr)); + rib_nexthop_add(rib, nexthop); + if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_RECURSIVE)) + copy_nexthops(&nexthop->resolved, nh->resolved); } /* Delete specified nexthop from the list. */ static void -nexthop_delete (struct rib *rib, struct nexthop *nexthop) +rib_nexthop_delete (struct rib *rib, struct nexthop *nexthop) { if (nexthop->next) nexthop->next->prev = nexthop->prev; @@ -171,150 +155,130 @@ nexthop_delete (struct rib *rib, struct nexthop *nexthop) rib->nexthop_num--; } -static void nexthops_free(struct nexthop *nexthop); - -/* Free nexthop. */ -static void -nexthop_free (struct nexthop *nexthop) -{ - if (nexthop->ifname) - XFREE (0, nexthop->ifname); - if (nexthop->resolved) - nexthops_free(nexthop->resolved); - XFREE (MTYPE_NEXTHOP, nexthop); -} - -/* Frees a list of nexthops */ -static void -nexthops_free (struct nexthop *nexthop) -{ - struct nexthop *nh, *next; - - for (nh = nexthop; nh; nh = next) - { - next = nh->next; - nexthop_free (nh); - } -} - struct nexthop * -nexthop_ifindex_add (struct rib *rib, ifindex_t ifindex) +rib_nexthop_ifindex_add (struct rib *rib, ifindex_t ifindex) { struct nexthop *nexthop; - nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop)); + nexthop = nexthop_new (); nexthop->type = NEXTHOP_TYPE_IFINDEX; nexthop->ifindex = ifindex; - nexthop_add (rib, nexthop); + rib_nexthop_add (rib, nexthop); return nexthop; } struct nexthop * -nexthop_ifname_add (struct rib *rib, char *ifname) +rib_nexthop_ifname_add (struct rib *rib, char *ifname) { struct nexthop *nexthop; - nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop)); + nexthop = nexthop_new (); nexthop->type = NEXTHOP_TYPE_IFNAME; nexthop->ifname = XSTRDUP (0, ifname); - nexthop_add (rib, nexthop); + rib_nexthop_add (rib, nexthop); return nexthop; } struct nexthop * -nexthop_ipv4_add (struct rib *rib, struct in_addr *ipv4, struct in_addr *src) +rib_nexthop_ipv4_add (struct rib *rib, struct in_addr *ipv4, struct in_addr *src) { struct nexthop *nexthop; - nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop)); + nexthop = nexthop_new (); nexthop->type = NEXTHOP_TYPE_IPV4; nexthop->gate.ipv4 = *ipv4; if (src) nexthop->src.ipv4 = *src; - nexthop_add (rib, nexthop); + rib_nexthop_add (rib, nexthop); return nexthop; } struct nexthop * -nexthop_ipv4_ifindex_add (struct rib *rib, struct in_addr *ipv4, - struct in_addr *src, ifindex_t ifindex) +rib_nexthop_ipv4_ifindex_add (struct rib *rib, struct in_addr *ipv4, + struct in_addr *src, ifindex_t ifindex) { struct nexthop *nexthop; - nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop)); + nexthop = nexthop_new (); nexthop->type = NEXTHOP_TYPE_IPV4_IFINDEX; nexthop->gate.ipv4 = *ipv4; if (src) nexthop->src.ipv4 = *src; nexthop->ifindex = ifindex; - nexthop_add (rib, nexthop); + rib_nexthop_add (rib, nexthop); return nexthop; } struct nexthop * -nexthop_ipv6_add (struct rib *rib, struct in6_addr *ipv6) +rib_nexthop_ipv6_add (struct rib *rib, struct in6_addr *ipv6) { struct nexthop *nexthop; - nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop)); + nexthop = nexthop_new (); nexthop->type = NEXTHOP_TYPE_IPV6; nexthop->gate.ipv6 = *ipv6; - nexthop_add (rib, nexthop); + rib_nexthop_add (rib, nexthop); return nexthop; } static struct nexthop * -nexthop_ipv6_ifname_add (struct rib *rib, struct in6_addr *ipv6, - char *ifname) +rib_nexthop_ipv6_ifname_add (struct rib *rib, struct in6_addr *ipv6, + char *ifname) { struct nexthop *nexthop; - nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop)); + nexthop = nexthop_new (); nexthop->type = NEXTHOP_TYPE_IPV6_IFNAME; nexthop->gate.ipv6 = *ipv6; nexthop->ifname = XSTRDUP (0, ifname); - nexthop_add (rib, nexthop); + rib_nexthop_add (rib, nexthop); return nexthop; } -static struct nexthop * -nexthop_ipv6_ifindex_add (struct rib *rib, struct in6_addr *ipv6, - ifindex_t ifindex) +struct nexthop * +rib_nexthop_ipv6_ifindex_add (struct rib *rib, struct in6_addr *ipv6, + ifindex_t ifindex) { struct nexthop *nexthop; + struct interface *ifp; - nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop)); + nexthop = nexthop_new (); nexthop->type = NEXTHOP_TYPE_IPV6_IFINDEX; nexthop->gate.ipv6 = *ipv6; nexthop->ifindex = ifindex; + ifp = if_lookup_by_index (nexthop->ifindex); + if (connected_is_unnumbered(ifp)) + { + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK); + } - nexthop_add (rib, nexthop); + rib_nexthop_add (rib, nexthop); return nexthop; } struct nexthop * -nexthop_blackhole_add (struct rib *rib) +rib_nexthop_blackhole_add (struct rib *rib) { struct nexthop *nexthop; - nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop)); + nexthop = nexthop_new (); nexthop->type = NEXTHOP_TYPE_BLACKHOLE; SET_FLAG (rib->flags, ZEBRA_FLAG_BLACKHOLE); - nexthop_add (rib, nexthop); + rib_nexthop_add (rib, nexthop); return nexthop; } @@ -348,8 +312,9 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set, struct route_node *rn; struct rib *match; int resolved; - struct nexthop *newhop; + struct nexthop *newhop, *tnewhop; struct nexthop *resolved_hop; + int recursing = 0; if (nexthop->type == NEXTHOP_TYPE_IPV4) nexthop->ifindex = 0; @@ -357,11 +322,18 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set, if (set) { UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE); + zebra_deregister_rnh_static_nexthops (nexthop->resolved, top); nexthops_free(nexthop->resolved); nexthop->resolved = NULL; rib->nexthop_mtu = 0; } + /* Skip nexthops that have been filtered out due to route-map */ + /* The nexthops are specific to this route and so the same */ + /* nexthop for a different route may not have this flag set */ + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FILTERED)) + return 0; + /* Make lookup prefix. */ memset (&p, 0, sizeof (struct prefix_ipv4)); p.family = AF_INET; @@ -393,8 +365,7 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set, /* If there is no selected route or matched route is EGP, go up tree. */ - if (! match - || match->type == ZEBRA_ROUTE_BGP) + if (! match) { do { rn = rn->parent; @@ -429,6 +400,7 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set, if (set) { SET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE); + SET_FLAG(rib->status, RIB_ENTRY_NEXTHOPS_CHANGED); resolved_hop = XCALLOC(MTYPE_NEXTHOP, sizeof (struct nexthop)); SET_FLAG (resolved_hop->flags, NEXTHOP_FLAG_ACTIVE); @@ -455,7 +427,7 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set, resolved_hop->ifindex = newhop->ifindex; } - _nexthop_add(&nexthop->resolved, resolved_hop); + nexthop_add(&nexthop->resolved, resolved_hop); } resolved = 1; } @@ -463,6 +435,57 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set, rib->nexthop_mtu = match->mtu; return resolved; } + else if (rib->type == ZEBRA_ROUTE_STATIC) + { + resolved = 0; + for (ALL_NEXTHOPS_RO(match->nexthop, newhop, tnewhop, recursing)) + if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB)) + { + if (set) + { + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE); + + resolved_hop = XCALLOC(MTYPE_NEXTHOP, sizeof (struct nexthop)); + SET_FLAG (resolved_hop->flags, NEXTHOP_FLAG_ACTIVE); + /* If the resolving route specifies a gateway, use it */ + if (newhop->type == NEXTHOP_TYPE_IPV4 + || newhop->type == NEXTHOP_TYPE_IPV4_IFINDEX + || newhop->type == NEXTHOP_TYPE_IPV4_IFNAME) + { + resolved_hop->type = newhop->type; + resolved_hop->gate.ipv4 = newhop->gate.ipv4; + + if (newhop->ifindex) + { + resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX; + resolved_hop->ifindex = newhop->ifindex; + } + } + + /* If the resolving route is an interface route, + * it means the gateway we are looking up is connected + * to that interface. (The actual network is _not_ onlink). + * Therefore, the resolved route should have the original + * gateway as nexthop as it is directly connected. + * + * On Linux, we have to set the onlink netlink flag because + * otherwise, the kernel won't accept the route. + */ + if (newhop->type == NEXTHOP_TYPE_IFINDEX + || newhop->type == NEXTHOP_TYPE_IFNAME) + { + resolved_hop->flags |= NEXTHOP_FLAG_ONLINK; + resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX; + resolved_hop->gate.ipv4 = nexthop->gate.ipv4; + resolved_hop->ifindex = newhop->ifindex; + } + + nexthop_add(&nexthop->resolved, resolved_hop); + } + resolved = 1; + } + return resolved; + } else { return 0; @@ -483,8 +506,10 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set, struct route_node *rn; struct rib *match; int resolved; - struct nexthop *newhop; + struct nexthop *newhop, *tnewhop; + int recursing = 0; struct nexthop *resolved_hop; + struct interface *ifp; if (nexthop->type == NEXTHOP_TYPE_IPV6) nexthop->ifindex = 0; @@ -492,10 +517,36 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set, if (set) { UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE); + zebra_deregister_rnh_static_nexthops (nexthop->resolved, top); nexthops_free(nexthop->resolved); nexthop->resolved = NULL; } + /* Skip nexthops that have been filtered out due to route-map */ + /* The nexthops are specific to this route and so the same */ + /* nexthop for a different route may not have this flag set */ + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FILTERED)) + return 0; + + /* + * Check to see if we should trust the passed in information + * for UNNUMBERED interfaces as that we won't find the GW + * address in the routing table. + */ + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) + { + ifp = if_lookup_by_index (nexthop->ifindex); + if (ifp && connected_is_unnumbered(ifp)) + { + if (if_is_operative(ifp)) + return 1; + else + return 0; + } + else + return 0; + } + /* Make lookup prefix. */ memset (&p, 0, sizeof (struct prefix_ipv6)); p.family = AF_INET6; @@ -527,8 +578,7 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set, /* If there is no selected route or matched route is EGP, go up tree. */ - if (! match - || match->type == ZEBRA_ROUTE_BGP) + if (! match) { do { rn = rn->parent; @@ -564,6 +614,7 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set, if (set) { SET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE); + SET_FLAG(rib->status, RIB_ENTRY_NEXTHOPS_CHANGED); resolved_hop = XCALLOC(MTYPE_NEXTHOP, sizeof (struct nexthop)); SET_FLAG (resolved_hop->flags, NEXTHOP_FLAG_ACTIVE); @@ -592,7 +643,50 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set, resolved_hop->ifindex = newhop->ifindex; } - _nexthop_add(&nexthop->resolved, resolved_hop); + nexthop_add(&nexthop->resolved, resolved_hop); + } + resolved = 1; + } + return resolved; + } + else if (rib->type == ZEBRA_ROUTE_STATIC) + { + resolved = 0; + for (ALL_NEXTHOPS_RO(match->nexthop, newhop, tnewhop, recursing)) + if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB)) + { + if (set) + { + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE); + + resolved_hop = XCALLOC(MTYPE_NEXTHOP, sizeof (struct nexthop)); + SET_FLAG (resolved_hop->flags, NEXTHOP_FLAG_ACTIVE); + /* See nexthop_active_ipv4 for a description how the + * resolved nexthop is constructed. */ + if (newhop->type == NEXTHOP_TYPE_IPV6 + || newhop->type == NEXTHOP_TYPE_IPV6_IFINDEX + || newhop->type == NEXTHOP_TYPE_IPV6_IFNAME) + { + resolved_hop->type = newhop->type; + resolved_hop->gate.ipv6 = newhop->gate.ipv6; + + if (newhop->ifindex) + { + resolved_hop->type = NEXTHOP_TYPE_IPV6_IFINDEX; + resolved_hop->ifindex = newhop->ifindex; + } + } + + if (newhop->type == NEXTHOP_TYPE_IFINDEX + || newhop->type == NEXTHOP_TYPE_IFNAME) + { + resolved_hop->flags |= NEXTHOP_FLAG_ONLINK; + resolved_hop->type = NEXTHOP_TYPE_IPV6_IFINDEX; + resolved_hop->gate.ipv6 = nexthop->gate.ipv6; + resolved_hop->ifindex = newhop->ifindex; + } + + nexthop_add(&nexthop->resolved, resolved_hop); } resolved = 1; } @@ -608,7 +702,7 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set, } struct rib * -rib_match_ipv4_safi (struct in_addr addr, safi_t safi, int skip_bgp, +rib_match_ipv4_safi (struct in_addr addr, safi_t safi, struct route_node **rn_out, vrf_id_t vrf_id) { struct route_table *table; @@ -639,7 +733,7 @@ rib_match_ipv4_safi (struct in_addr addr, safi_t safi, int skip_bgp, /* If there is no selected route or matched route is EGP, go up tree. */ - if (!match || (skip_bgp && (match->type == ZEBRA_ROUTE_BGP))) + if (! match) { do { rn = rn->parent; @@ -676,29 +770,22 @@ rib_match_ipv4_multicast (struct in_addr addr, struct route_node **rn_out, { struct rib *rib = NULL, *mrib = NULL, *urib = NULL; struct route_node *m_rn = NULL, *u_rn = NULL; - int skip_bgp = 0; /* bool */ switch (ipv4_multicast_mode) { case MCAST_MRIB_ONLY: - return rib_match_ipv4_safi (addr, SAFI_MULTICAST, skip_bgp, rn_out, - vrf_id); + return rib_match_ipv4_safi (addr, SAFI_MULTICAST, rn_out, vrf_id); case MCAST_URIB_ONLY: - return rib_match_ipv4_safi (addr, SAFI_UNICAST, skip_bgp, rn_out, - vrf_id); + return rib_match_ipv4_safi (addr, SAFI_UNICAST, rn_out, vrf_id); case MCAST_NO_CONFIG: case MCAST_MIX_MRIB_FIRST: - rib = mrib = rib_match_ipv4_safi (addr, SAFI_MULTICAST, skip_bgp, &m_rn, - vrf_id); + rib = mrib = rib_match_ipv4_safi (addr, SAFI_MULTICAST, &m_rn, vrf_id); if (!mrib) - rib = urib = rib_match_ipv4_safi (addr, SAFI_UNICAST, skip_bgp, &u_rn, - vrf_id); + rib = urib = rib_match_ipv4_safi (addr, SAFI_UNICAST, &u_rn, vrf_id); break; case MCAST_MIX_DISTANCE: - mrib = rib_match_ipv4_safi (addr, SAFI_MULTICAST, skip_bgp, &m_rn, - vrf_id); - urib = rib_match_ipv4_safi (addr, SAFI_UNICAST, skip_bgp, &u_rn, - vrf_id); + mrib = rib_match_ipv4_safi (addr, SAFI_MULTICAST, &m_rn, vrf_id); + urib = rib_match_ipv4_safi (addr, SAFI_UNICAST, &u_rn, vrf_id); if (mrib && urib) rib = urib->distance < mrib->distance ? urib : mrib; else if (mrib) @@ -707,10 +794,8 @@ rib_match_ipv4_multicast (struct in_addr addr, struct route_node **rn_out, rib = urib; break; case MCAST_MIX_PFXLEN: - mrib = rib_match_ipv4_safi (addr, SAFI_MULTICAST, skip_bgp, &m_rn, - vrf_id); - urib = rib_match_ipv4_safi (addr, SAFI_UNICAST, skip_bgp, &u_rn, - vrf_id); + mrib = rib_match_ipv4_safi (addr, SAFI_MULTICAST, &m_rn, vrf_id); + urib = rib_match_ipv4_safi (addr, SAFI_UNICAST, &u_rn, vrf_id); if (mrib && urib) rib = u_rn->p.prefixlen > m_rn->p.prefixlen ? urib : mrib; else if (mrib) @@ -782,7 +867,7 @@ rib_lookup_ipv4 (struct prefix_ipv4 *p, vrf_id_t vrf_id) break; } - if (! match || match->type == ZEBRA_ROUTE_BGP) + if (! match) return NULL; if (match->type == ZEBRA_ROUTE_CONNECT) @@ -910,8 +995,7 @@ rib_match_ipv6 (struct in6_addr *addr, vrf_id_t vrf_id) /* If there is no selected route or matched route is EGP, go up tree. */ - if (! match - || match->type == ZEBRA_ROUTE_BGP) + if (! match) { do { rn = rn->parent; @@ -956,9 +1040,8 @@ nexthop_active_check (struct route_node *rn, struct rib *rib, rib_table_info_t *info = rn->table->info; struct interface *ifp; route_map_result_t ret = RMAP_MATCH; - extern char *proto_rm[AFI_MAX][ZEBRA_ROUTE_MAX+1]; - struct route_map *rmap; int family; + char buf[INET6_ADDRSTRLEN+1]; family = 0; switch (nexthop->type) @@ -1045,19 +1128,21 @@ nexthop_active_check (struct route_node *rn, struct rib *rib, if (!family) family = info->afi; - rmap = 0; - if (rib->type >= 0 && rib->type < ZEBRA_ROUTE_MAX && - proto_rm[family][rib->type]) - rmap = route_map_lookup_by_name (proto_rm[family][rib->type]); - if (!rmap && proto_rm[family][ZEBRA_ROUTE_MAX]) - rmap = route_map_lookup_by_name (proto_rm[family][ZEBRA_ROUTE_MAX]); - if (rmap) { - struct nexthop_vrfid nh_vrf = {nexthop, rib->vrf_id}; - ret = route_map_apply(rmap, &rn->p, RMAP_ZEBRA, &nh_vrf); - } + memset(&nexthop->rmap_src.ipv6, 0, sizeof(union g_addr)); + /* It'll get set if required inside */ + ret = zebra_route_map_check(family, rib->type, &rn->p, nexthop, rib->vrf_id); if (ret == RMAP_DENYMATCH) - UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); + { + if (IS_ZEBRA_DEBUG_RIB) + { + inet_ntop (rn->p.family, &rn->p.u.prefix, buf, sizeof (buf)); + zlog_debug("%s: Filtering out %s with NH out %s due to route map", + __FUNCTION__, buf, nexthop->ifname); + } + UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); + } + return CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); } @@ -1074,22 +1159,35 @@ static int nexthop_active_update (struct route_node *rn, struct rib *rib, int set) { struct nexthop *nexthop; - unsigned int prev_active, new_active; + union g_addr prev_src; + unsigned int prev_active, new_active, old_num_nh; ifindex_t prev_index; - + + old_num_nh = rib->nexthop_active_num; + rib->nexthop_active_num = 0; - UNSET_FLAG (rib->status, RIB_ENTRY_CHANGED); + UNSET_FLAG (rib->status, RIB_ENTRY_NEXTHOPS_CHANGED); for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) { + /* No protocol daemon provides src and so we're skipping tracking it */ + prev_src = nexthop->rmap_src; prev_active = CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); prev_index = nexthop->ifindex; if ((new_active = nexthop_active_check (rn, rib, nexthop, set))) rib->nexthop_active_num++; + /* Don't allow src setting on IPv6 addr for now */ if (prev_active != new_active || - prev_index != nexthop->ifindex) - SET_FLAG (rib->status, RIB_ENTRY_CHANGED); + prev_index != nexthop->ifindex || + ((nexthop->type >= NEXTHOP_TYPE_IFINDEX && + nexthop->type < NEXTHOP_TYPE_IPV6) && + prev_src.ipv4.s_addr != nexthop->rmap_src.ipv4.s_addr)) + SET_FLAG (rib->status, RIB_ENTRY_NEXTHOPS_CHANGED); } + + if (old_num_nh != rib->nexthop_active_num) + SET_FLAG (rib->status, RIB_ENTRY_NEXTHOPS_CHANGED); + return rib->nexthop_active_num; } @@ -1275,6 +1373,8 @@ rib_process (struct route_node *rn) RNODE_FOREACH_RIB (rn, rib) { + UNSET_FLAG(rib->status, RIB_ENTRY_NEXTHOPS_CHANGED); + /* Currently installed rib. */ if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) { @@ -1292,7 +1392,18 @@ rib_process (struct route_node *rn) continue; /* Skip unreachable nexthop. */ - if (! nexthop_active_update (rn, rib, 0)) + /* This first call to nexthop_active_update is merely to determine if + * there's any change to nexthops associated with this RIB entry. Now, + * rib_process() can be invoked due to an external event such as link + * down or due to next-hop-tracking evaluation. In the latter case, + * a decision has already been made that the NHs have changed. So, no + * need to invoke a potentially expensive call again. Further, since + * the change might be in a recursive NH which is not caught in + * the nexthop_active_update() code. Thus, we might miss changes to + * recursive NHs. + */ + if (!CHECK_FLAG(rib->status, RIB_ENTRY_NEXTHOPS_CHANGED) && + ! nexthop_active_update (rn, rib, 0)) continue; /* Infinit distance. */ @@ -1327,7 +1438,7 @@ rib_process (struct route_node *rn) /* Update kernel if FIB entry has changed */ if (old_fib != new_fib - || (new_fib && CHECK_FLAG (new_fib->status, RIB_ENTRY_CHANGED))) + || (new_fib && CHECK_FLAG (new_fib->status, RIB_ENTRY_NEXTHOPS_CHANGED))) { if (old_fib && old_fib != new_fib) { @@ -1365,7 +1476,7 @@ rib_process (struct route_node *rn) /* Redistribute SELECTED entry */ if (old_selected != new_selected - || (new_selected && CHECK_FLAG (new_selected->status, RIB_ENTRY_CHANGED))) + || (new_selected && CHECK_FLAG (new_selected->status, RIB_ENTRY_NEXTHOPS_CHANGED))) { if (old_selected) { @@ -1436,6 +1547,18 @@ process_subq (struct list * subq, u_char qindex) return 1; } +/* + * All meta queues have been processed. Trigger next-hop evaluation. + */ +static void +meta_queue_process_complete (struct work_queue *dummy) +{ + zebra_evaluate_rnh_table(0, AF_INET, 0); +#ifdef HAVE_IPV6 + zebra_evaluate_rnh_table(0, AF_INET6, 0); +#endif /* HAVE_IPV6 */ +} + /* Dispatch the meta queue by picking, processing and unlocking the next RN from * a non-empty sub-queue with lowest priority. wq is equal to zebra->ribq and data * is pointed to the meta queue structure. @@ -1507,7 +1630,7 @@ rib_meta_queue_add (struct meta_queue *mq, struct route_node *rn) } /* Add route_node to work queue and schedule processing */ -static void +void rib_queue_add (struct zebra_t *zebra, struct route_node *rn) { assert (zebra && rn); @@ -1588,6 +1711,7 @@ rib_queue_init (struct zebra_t *zebra) /* fill in the work queue spec */ zebra->ribq->spec.workfunc = &meta_queue_process; zebra->ribq->spec.errorfunc = NULL; + zebra->ribq->spec.completion_func = &meta_queue_process_complete; /* XXX: TODO: These should be runtime configurable via vty */ zebra->ribq->spec.max_retries = 3; zebra->ribq->spec.hold = rib_process_hold_time; @@ -1720,7 +1844,8 @@ rib_unlink (struct route_node *rn, struct rib *rib) } /* free RIB and nexthops */ - nexthops_free(rib->nexthop); + zebra_deregister_rnh_static_nexthops (rib->nexthop, rn); + nexthops_free (rib->nexthop); XFREE (MTYPE_RIB, rib); } @@ -1811,12 +1936,12 @@ rib_add_ipv4 (int type, int flags, struct prefix_ipv4 *p, if (gate) { if (ifindex) - nexthop_ipv4_ifindex_add (rib, gate, src, ifindex); + rib_nexthop_ipv4_ifindex_add (rib, gate, src, ifindex); else - nexthop_ipv4_add (rib, gate, src); + rib_nexthop_ipv4_add (rib, gate, src); } else - nexthop_ifindex_add (rib, ifindex); + rib_nexthop_ifindex_add (rib, ifindex); /* If this route is kernel route, set FIB flag to the route. */ if (type == ZEBRA_ROUTE_KERNEL || type == ZEBRA_ROUTE_CONNECT) @@ -2009,6 +2134,7 @@ rib_add_ipv4_multipath (struct prefix_ipv4 *p, struct rib *rib, safi_t safi) struct route_node *rn; struct rib *same; struct nexthop *nexthop; + int ret = 0; /* Lookup table. */ table = zebra_vrf_table (AFI_IP, safi, rib->vrf_id); @@ -2051,6 +2177,7 @@ rib_add_ipv4_multipath (struct prefix_ipv4 *p, struct rib *rib, safi_t safi) /* Link new rib to node.*/ rib_addnode (rn, rib); + ret = 1; if (IS_ZEBRA_DEBUG_RIB) { zlog_debug ("%s: called rib_addnode (%p, %p) on new RIB entry", @@ -2068,10 +2195,11 @@ rib_add_ipv4_multipath (struct prefix_ipv4 *p, struct rib *rib, safi_t safi) rib_dump (p, same); } rib_delnode (rn, same); + ret = -1; } route_unlock_node (rn); - return 0; + return ret; } /* XXX factor with rib_delete_ipv6 */ @@ -2178,14 +2306,19 @@ rib_delete_ipv4 (int type, int flags, struct prefix_ipv4 *p, kernel. */ if (! same) { - if (fib && type == ZEBRA_ROUTE_KERNEL) - { - /* Unset flags. */ - for (nexthop = fib->nexthop; nexthop; nexthop = nexthop->next) - UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); - - UNSET_FLAG (fib->status, RIB_ENTRY_SELECTED_FIB); - } + if (fib && type == ZEBRA_ROUTE_KERNEL && + CHECK_FLAG(flags, ZEBRA_FLAG_SELFROUTE)) + { + if (IS_ZEBRA_DEBUG_KERNEL) + { + zlog_debug ("Zebra route %s/%d was deleted by others from kernel", + inet_ntop (AF_INET, &p->prefix, buf1, INET_ADDRSTRLEN), + p->prefixlen); + } + /* This means someone else, other than Zebra, has deleted + * a Zebra router from the kernel. We will add it back */ + rib_update_kernel(rn, NULL, fib); + } else { if (IS_ZEBRA_DEBUG_KERNEL) @@ -2222,6 +2355,7 @@ static_install_route (afi_t afi, safi_t safi, struct prefix *p, struct static_ro struct rib *rib; struct route_node *rn; struct route_table *table; + struct prefix nh_p; /* Lookup table. */ table = zebra_vrf_table (afi, safi, si->vrf_id); @@ -2241,28 +2375,40 @@ static_install_route (afi_t afi, safi_t safi, struct prefix *p, struct static_ro if (rib) { + /* if tag value changed , update old value in RIB */ + if (rib->tag != si->tag) + rib->tag = si->tag; + /* Same distance static route is there. Update it with new nexthop. */ route_unlock_node (rn); switch (si->type) { case STATIC_IPV4_GATEWAY: - nexthop_ipv4_add (rib, &si->addr.ipv4, NULL); + rib_nexthop_ipv4_add (rib, &si->addr.ipv4, NULL); + nh_p.family = AF_INET; + nh_p.prefixlen = IPV4_MAX_BITLEN; + nh_p.u.prefix4 = si->addr.ipv4; + zebra_register_rnh_static_nh(&nh_p, rn); break; case STATIC_IPV4_IFNAME: - nexthop_ifname_add (rib, si->ifname); + rib_nexthop_ifname_add (rib, si->ifname); break; case STATIC_IPV4_BLACKHOLE: - nexthop_blackhole_add (rib); + rib_nexthop_blackhole_add (rib); break; case STATIC_IPV6_GATEWAY: - nexthop_ipv6_add (rib, &si->addr.ipv6); + rib_nexthop_ipv6_add (rib, &si->addr.ipv6); + nh_p.family = AF_INET6; + nh_p.prefixlen = IPV6_MAX_BITLEN; + nh_p.u.prefix6 = si->addr.ipv6; + zebra_register_rnh_static_nh(&nh_p, rn); break; case STATIC_IPV6_IFNAME: - nexthop_ifname_add (rib, si->ifname); + rib_nexthop_ifname_add (rib, si->ifname); break; case STATIC_IPV6_GATEWAY_IFNAME: - nexthop_ipv6_ifname_add (rib, &si->addr.ipv6, si->ifname); + rib_nexthop_ipv6_ifname_add (rib, &si->addr.ipv6, si->ifname); break; } rib_queue_add (&zebrad, rn); @@ -2278,26 +2424,35 @@ static_install_route (afi_t afi, safi_t safi, struct prefix *p, struct static_ro rib->vrf_id = si->vrf_id; rib->table = zebrad.rtm_table_default; rib->nexthop_num = 0; + rib->tag = si->tag; switch (si->type) { case STATIC_IPV4_GATEWAY: - nexthop_ipv4_add (rib, &si->addr.ipv4, NULL); + rib_nexthop_ipv4_add (rib, &si->addr.ipv4, NULL); + nh_p.family = AF_INET; + nh_p.prefixlen = IPV4_MAX_BITLEN; + nh_p.u.prefix4 = si->addr.ipv4; + zebra_register_rnh_static_nh(&nh_p, rn); break; case STATIC_IPV4_IFNAME: - nexthop_ifname_add (rib, si->ifname); + rib_nexthop_ifname_add (rib, si->ifname); break; case STATIC_IPV4_BLACKHOLE: - nexthop_blackhole_add (rib); + rib_nexthop_blackhole_add (rib); break; case STATIC_IPV6_GATEWAY: - nexthop_ipv6_add (rib, &si->addr.ipv6); + rib_nexthop_ipv6_add (rib, &si->addr.ipv6); + nh_p.family = AF_INET6; + nh_p.prefixlen = IPV6_MAX_BITLEN; + nh_p.u.prefix6 = si->addr.ipv6; + zebra_register_rnh_static_nh(&nh_p, rn); break; case STATIC_IPV6_IFNAME: - nexthop_ifname_add (rib, si->ifname); + rib_nexthop_ifname_add (rib, si->ifname); break; case STATIC_IPV6_GATEWAY_IFNAME: - nexthop_ipv6_ifname_add (rib, &si->addr.ipv6, si->ifname); + rib_nexthop_ipv6_ifname_add (rib, &si->addr.ipv6, si->ifname); break; } @@ -2347,6 +2502,7 @@ static_uninstall_route (afi_t afi, safi_t safi, struct prefix *p, struct static_ struct rib *rib; struct nexthop *nexthop; struct route_table *table; + struct prefix nh_p; /* Lookup table. */ table = zebra_vrf_table (afi, safi, si->vrf_id); @@ -2363,7 +2519,8 @@ static_uninstall_route (afi_t afi, safi_t safi, struct prefix *p, struct static_ if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) continue; - if (rib->type == ZEBRA_ROUTE_STATIC && rib->distance == si->distance) + if (rib->type == ZEBRA_ROUTE_STATIC && rib->distance == si->distance && + rib->tag == si->tag) break; } @@ -2392,7 +2549,21 @@ static_uninstall_route (afi_t afi, safi_t safi, struct prefix *p, struct static_ { if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) rib_uninstall (rn, rib); - nexthop_delete (rib, nexthop); + + if (afi == AF_INET) + { + nh_p.family = AF_INET; + nh_p.prefixlen = IPV4_MAX_BITLEN; + nh_p.u.prefix4 = nexthop->gate.ipv4; + } + else + { + nh_p.family = AF_INET6; + nh_p.prefixlen = IPV6_MAX_BITLEN; + nh_p.u.prefix6 = nexthop->gate.ipv6; + } + rib_nexthop_delete (rib, nexthop); + zebra_deregister_rnh_static_nh(&nh_p, rn); nexthop_free (nexthop); rib_queue_add (&zebrad, rn); } @@ -2402,7 +2573,7 @@ static_uninstall_route (afi_t afi, safi_t safi, struct prefix *p, struct static_ int static_add_ipv4_safi (safi_t safi, struct prefix *p, struct in_addr *gate, - const char *ifname, u_char flags, u_char distance, + const char *ifname, u_char flags, u_short tag, u_char distance, vrf_id_t vrf_id) { u_char type = 0; @@ -2435,7 +2606,8 @@ static_add_ipv4_safi (safi_t safi, struct prefix *p, struct in_addr *gate, && (! gate || IPV4_ADDR_SAME (gate, &si->addr.ipv4)) && (! ifname || strcmp (ifname, si->ifname) == 0)) { - if (distance == si->distance) + if (distance == si->distance && + tag == si->tag) { route_unlock_node (rn); return 0; @@ -2445,15 +2617,16 @@ static_add_ipv4_safi (safi_t safi, struct prefix *p, struct in_addr *gate, } } - /* Distance changed. */ + /* Distance or tag changed. */ if (update) - static_delete_ipv4_safi (safi, p, gate, ifname, update->distance, vrf_id); + static_delete_ipv4_safi (safi, p, gate, ifname, update->tag, update->distance, vrf_id); /* Make new static route structure. */ si = XCALLOC (MTYPE_STATIC_ROUTE, sizeof (struct static_route)); si->type = type; si->distance = distance; + si->tag = tag; si->flags = flags; si->vrf_id = vrf_id; @@ -2497,7 +2670,7 @@ static_add_ipv4_safi (safi_t safi, struct prefix *p, struct in_addr *gate, int static_delete_ipv4_safi (safi_t safi, struct prefix *p, struct in_addr *gate, - const char *ifname, u_char distance, vrf_id_t vrf_id) + const char *ifname, u_short tag, u_char distance, vrf_id_t vrf_id) { u_char type = 0; struct route_node *rn; @@ -2526,7 +2699,8 @@ static_delete_ipv4_safi (safi_t safi, struct prefix *p, struct in_addr *gate, for (si = rn->info; si; si = si->next) if (type == si->type && (! gate || IPV4_ADDR_SAME (gate, &si->addr.ipv4)) - && (! ifname || strcmp (ifname, si->ifname) == 0)) + && (! ifname || strcmp (ifname, si->ifname) == 0) + && (! tag || (tag == si->tag))) break; /* Can't find static route. */ @@ -2628,12 +2802,12 @@ rib_add_ipv6 (int type, int flags, struct prefix_ipv6 *p, if (gate) { if (ifindex) - nexthop_ipv6_ifindex_add (rib, gate, ifindex); + rib_nexthop_ipv6_ifindex_add (rib, gate, ifindex); else - nexthop_ipv6_add (rib, gate); + rib_nexthop_ipv6_add (rib, gate); } else - nexthop_ifindex_add (rib, ifindex); + rib_nexthop_ifindex_add (rib, ifindex); /* If this route is kernel route, set FIB flag to the route. */ if (type == ZEBRA_ROUTE_KERNEL || type == ZEBRA_ROUTE_CONNECT) @@ -2665,6 +2839,86 @@ 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) +{ + struct route_table *table; + struct route_node *rn; + struct rib *same = NULL; + struct nexthop *nexthop; + int ret = 0; + + if (!rib) + return 0; /* why are we getting called with NULL rib */ + + /* Lookup table. */ + table = zebra_vrf_table (AFI_IP6, safi, rib->vrf_id); + + 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; + } + } + + /* 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, @@ -2757,14 +3011,19 @@ rib_delete_ipv6 (int type, int flags, struct prefix_ipv6 *p, kernel. */ if (! same) { - if (fib && type == ZEBRA_ROUTE_KERNEL) - { - /* Unset flags. */ - for (nexthop = fib->nexthop; nexthop; nexthop = nexthop->next) - UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); - - UNSET_FLAG (fib->status, RIB_ENTRY_SELECTED_FIB); - } + if (fib && type == ZEBRA_ROUTE_KERNEL && + CHECK_FLAG(flags, ZEBRA_FLAG_SELFROUTE)) + { + if (IS_ZEBRA_DEBUG_KERNEL) + { + zlog_debug ("Zebra route %s/%d was deleted by others from kernel", + inet_ntop (AF_INET, &p->prefix, buf1, INET_ADDRSTRLEN), + p->prefixlen); + } + /* This means someone else, other than Zebra, has deleted a Zebra + * route from the kernel. We will add it back */ + rib_update_kernel(rn, NULL, fib); + } else { if (IS_ZEBRA_DEBUG_KERNEL) @@ -2794,12 +3053,11 @@ rib_delete_ipv6 (int type, int flags, struct prefix_ipv6 *p, return 0; } - /* Add static route into static route configuration. */ int static_add_ipv6 (struct prefix *p, u_char type, struct in6_addr *gate, - const char *ifname, u_char flags, u_char distance, - vrf_id_t vrf_id) + const char *ifname, u_char flags, u_short tag, + u_char distance, vrf_id_t vrf_id) { struct route_node *rn; struct static_route *si; @@ -2827,6 +3085,7 @@ static_add_ipv6 (struct prefix *p, u_char type, struct in6_addr *gate, for (si = rn->info; si; si = si->next) { if (type == si->type + && tag == si->tag && (! gate || IPV6_ADDR_SAME (gate, &si->addr.ipv6)) && (! ifname || strcmp (ifname, si->ifname) == 0)) { @@ -2841,13 +3100,14 @@ static_add_ipv6 (struct prefix *p, u_char type, struct in6_addr *gate, } if (update) - static_delete_ipv6(p, type, gate, ifname, si->distance, vrf_id); + static_delete_ipv6(p, type, gate, ifname, tag, si->distance, vrf_id); /* Make new static route structure. */ si = XCALLOC (MTYPE_STATIC_ROUTE, sizeof (struct static_route)); si->type = type; si->distance = distance; + si->tag = tag; si->flags = flags; si->vrf_id = vrf_id; @@ -2894,7 +3154,7 @@ static_add_ipv6 (struct prefix *p, u_char type, struct in6_addr *gate, /* Delete static route from static route configuration. */ int static_delete_ipv6 (struct prefix *p, u_char type, struct in6_addr *gate, - const char *ifname, u_char distance, vrf_id_t vrf_id) + const char *ifname, u_short tag, u_char distance, vrf_id_t vrf_id) { struct route_node *rn; struct static_route *si; @@ -2915,7 +3175,8 @@ static_delete_ipv6 (struct prefix *p, u_char type, struct in6_addr *gate, if (distance == si->distance && type == si->type && (! gate || IPV6_ADDR_SAME (gate, &si->addr.ipv6)) - && (! ifname || strcmp (ifname, si->ifname) == 0)) + && (! ifname || strcmp (ifname, si->ifname) == 0) + && (! tag || (tag == si->tag))) break; /* Can't find static route. */ @@ -3237,6 +3498,13 @@ rib_tables_iter_next (rib_tables_iter_t *iter) return table; } +/* Lookup VRF by identifier. */ +struct zebra_vrf * +zebra_vrf_lookup (vrf_id_t vrf_id) +{ + return vrf_info_lookup (vrf_id); +} + /* * Create a routing table for the specific AFI/SAFI in the given VRF. */ @@ -3279,6 +3547,9 @@ zebra_vrf_alloc (vrf_id_t vrf_id) zvrf->stable[AFI_IP][SAFI_MULTICAST] = route_table_init (); zvrf->stable[AFI_IP6][SAFI_MULTICAST] = route_table_init (); + zvrf->rnh_table[AFI_IP] = route_table_init(); + zvrf->rnh_table[AFI_IP6] = route_table_init(); + /* Set VRF ID */ zvrf->vrf_id = vrf_id; |