From ad28e79ac998ae5372b1ac4504ff946cbf6926a0 Mon Sep 17 00:00:00 2001 From: Stephen Worley Date: Mon, 13 May 2019 12:46:05 -0700 Subject: [PATCH] zebra: Move nexthop_active_XXX functions to zebra_nhg.c Since these functions are not really rib processing problems let's move them to zebra_nhg.c which is meant for processing of nexthop groups. Signed-off-by: Donald Sharp --- zebra/subdir.am | 2 + zebra/zebra_nhg.c | 511 ++++++++++++++++++++++++++++++++++++++++++++++ zebra/zebra_nhg.h | 29 +++ zebra/zebra_rib.c | 477 +------------------------------------------ 4 files changed, 543 insertions(+), 476 deletions(-) create mode 100644 zebra/zebra_nhg.c create mode 100644 zebra/zebra_nhg.h diff --git a/zebra/subdir.am b/zebra/subdir.am index 1e36d020a..25040a271 100644 --- a/zebra/subdir.am +++ b/zebra/subdir.am @@ -75,6 +75,7 @@ zebra_zebra_SOURCES = \ zebra/zebra_mpls_null.c \ zebra/zebra_mpls_vty.c \ zebra/zebra_mroute.c \ + zebra/zebra_nhg.c \ zebra/zebra_ns.c \ zebra/zebra_pbr.c \ zebra/zebra_ptm.c \ @@ -135,6 +136,7 @@ noinst_HEADERS += \ zebra/zebra_memory.h \ zebra/zebra_mpls.h \ zebra/zebra_mroute.h \ + zebra/zebra_nhg.h \ zebra/zebra_ns.h \ zebra/zebra_pbr.h \ zebra/zebra_ptm.h \ diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c new file mode 100644 index 000000000..f2a76d1c5 --- /dev/null +++ b/zebra/zebra_nhg.c @@ -0,0 +1,511 @@ +/* Zebra Nexthop Group Code. + * Copyright (C) 2019 Cumulus Networks, Inc. + * Donald Sharp + * Stephen Worley + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ +#include + +#include "lib/nexthop.h" +#include "lib/routemap.h" + +#include "zebra/connected.h" +#include "zebra/debug.h" +#include "zebra/zebra_router.h" +#include "zebra/zebra_nhg.h" +#include "zebra/zebra_rnh.h" +#include "zebra/zebra_routemap.h" +#include "zebra/rt.h" + +static void nexthop_set_resolved(afi_t afi, const struct nexthop *newhop, + struct nexthop *nexthop) +{ + struct nexthop *resolved_hop; + + resolved_hop = nexthop_new(); + SET_FLAG(resolved_hop->flags, NEXTHOP_FLAG_ACTIVE); + + resolved_hop->vrf_id = nexthop->vrf_id; + switch (newhop->type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + /* If the resolving route specifies a gateway, use it */ + 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; + } + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + 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; + } + break; + case NEXTHOP_TYPE_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. + */ + resolved_hop->flags |= NEXTHOP_FLAG_ONLINK; + if (afi == AFI_IP) { + resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX; + resolved_hop->gate.ipv4 = nexthop->gate.ipv4; + } else if (afi == AFI_IP6) { + resolved_hop->type = NEXTHOP_TYPE_IPV6_IFINDEX; + resolved_hop->gate.ipv6 = nexthop->gate.ipv6; + } + resolved_hop->ifindex = newhop->ifindex; + break; + case NEXTHOP_TYPE_BLACKHOLE: + resolved_hop->type = NEXTHOP_TYPE_BLACKHOLE; + resolved_hop->bh_type = nexthop->bh_type; + break; + } + + if (newhop->flags & NEXTHOP_FLAG_ONLINK) + resolved_hop->flags |= NEXTHOP_FLAG_ONLINK; + + /* Copy labels of the resolved route */ + if (newhop->nh_label) + nexthop_add_labels(resolved_hop, newhop->nh_label_type, + newhop->nh_label->num_labels, + &newhop->nh_label->label[0]); + + resolved_hop->rparent = nexthop; + nexthop_add(&nexthop->resolved, resolved_hop); +} + +/* + * Given a nexthop we need to properly recursively resolve + * the route. As such, do a table lookup to find and match + * if at all possible. Set the nexthop->ifindex as appropriate + */ +static int nexthop_active(afi_t afi, struct route_entry *re, + struct nexthop *nexthop, struct route_node *top) +{ + struct prefix p; + struct route_table *table; + struct route_node *rn; + struct route_entry *match = NULL; + int resolved; + struct nexthop *newhop; + struct interface *ifp; + rib_dest_t *dest; + + if ((nexthop->type == NEXTHOP_TYPE_IPV4) + || nexthop->type == NEXTHOP_TYPE_IPV6) + nexthop->ifindex = 0; + + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE); + nexthops_free(nexthop->resolved); + nexthop->resolved = NULL; + re->nexthop_mtu = 0; + + /* + * If the kernel has sent us a route, then + * by golly gee whiz it's a good route. + */ + if (re->type == ZEBRA_ROUTE_KERNEL || re->type == ZEBRA_ROUTE_SYSTEM) + return 1; + + /* + * 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. + * This check should suffice to handle IPv4 or IPv6 routes + * sourced from EVPN routes which are installed with the + * next hop as the remote VTEP IP. + */ + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) { + ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id); + if (!ifp) { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug( + "\t%s: Onlink and interface: %u[%u] does not exist", + __PRETTY_FUNCTION__, nexthop->ifindex, + nexthop->vrf_id); + return 0; + } + if (connected_is_unnumbered(ifp)) { + if (if_is_operative(ifp)) + return 1; + else { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug( + "\t%s: Onlink and interface %s is not operative", + __PRETTY_FUNCTION__, ifp->name); + return 0; + } + } + if (!if_is_operative(ifp)) { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug( + "\t%s: Interface %s is not unnumbered", + __PRETTY_FUNCTION__, ifp->name); + return 0; + } + } + + /* Make lookup prefix. */ + memset(&p, 0, sizeof(struct prefix)); + switch (afi) { + case AFI_IP: + p.family = AF_INET; + p.prefixlen = IPV4_MAX_PREFIXLEN; + p.u.prefix4 = nexthop->gate.ipv4; + break; + case AFI_IP6: + p.family = AF_INET6; + p.prefixlen = IPV6_MAX_PREFIXLEN; + p.u.prefix6 = nexthop->gate.ipv6; + break; + default: + assert(afi != AFI_IP && afi != AFI_IP6); + break; + } + /* Lookup table. */ + table = zebra_vrf_table(afi, SAFI_UNICAST, nexthop->vrf_id); + if (!table) { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug("\t%s: Table not found", + __PRETTY_FUNCTION__); + return 0; + } + + rn = route_node_match(table, (struct prefix *)&p); + while (rn) { + route_unlock_node(rn); + + /* Lookup should halt if we've matched against ourselves ('top', + * if specified) - i.e., we cannot have a nexthop NH1 is + * resolved by a route NH1. The exception is if the route is a + * host route. + */ + if (top && rn == top) + if (((afi == AFI_IP) && (rn->p.prefixlen != 32)) + || ((afi == AFI_IP6) && (rn->p.prefixlen != 128))) { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug( + "\t%s: Matched against ourself and prefix length is not max bit length", + __PRETTY_FUNCTION__); + return 0; + } + + /* Pick up selected route. */ + /* However, do not resolve over default route unless explicitly + * allowed. */ + if (is_default_prefix(&rn->p) + && !rnh_resolve_via_default(p.family)) { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug( + "\t:%s: Resolved against default route", + __PRETTY_FUNCTION__); + return 0; + } + + dest = rib_dest_from_rnode(rn); + if (dest && dest->selected_fib + && !CHECK_FLAG(dest->selected_fib->status, + ROUTE_ENTRY_REMOVED) + && dest->selected_fib->type != ZEBRA_ROUTE_TABLE) + match = dest->selected_fib; + + /* If there is no selected route or matched route is EGP, go up + tree. */ + if (!match) { + do { + rn = rn->parent; + } while (rn && rn->info == NULL); + if (rn) + route_lock_node(rn); + + continue; + } + + if (match->type == ZEBRA_ROUTE_CONNECT) { + /* Directly point connected route. */ + newhop = match->ng.nexthop; + if (newhop) { + if (nexthop->type == NEXTHOP_TYPE_IPV4 + || nexthop->type == NEXTHOP_TYPE_IPV6) + nexthop->ifindex = newhop->ifindex; + } + return 1; + } else if (CHECK_FLAG(re->flags, ZEBRA_FLAG_ALLOW_RECURSION)) { + resolved = 0; + for (ALL_NEXTHOPS(match->ng, newhop)) { + if (!CHECK_FLAG(match->status, + ROUTE_ENTRY_INSTALLED)) + continue; + if (CHECK_FLAG(newhop->flags, + NEXTHOP_FLAG_RECURSIVE)) + continue; + + SET_FLAG(nexthop->flags, + NEXTHOP_FLAG_RECURSIVE); + SET_FLAG(re->status, + ROUTE_ENTRY_NEXTHOPS_CHANGED); + nexthop_set_resolved(afi, newhop, nexthop); + resolved = 1; + } + if (resolved) + re->nexthop_mtu = match->mtu; + if (!resolved && IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug("\t%s: Recursion failed to find", + __PRETTY_FUNCTION__); + return resolved; + } else if (re->type == ZEBRA_ROUTE_STATIC) { + resolved = 0; + for (ALL_NEXTHOPS(match->ng, newhop)) { + if (!CHECK_FLAG(match->status, + ROUTE_ENTRY_INSTALLED)) + continue; + if (CHECK_FLAG(newhop->flags, + NEXTHOP_FLAG_RECURSIVE)) + continue; + + SET_FLAG(nexthop->flags, + NEXTHOP_FLAG_RECURSIVE); + nexthop_set_resolved(afi, newhop, nexthop); + resolved = 1; + } + if (resolved) + re->nexthop_mtu = match->mtu; + + if (!resolved && IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug( + "\t%s: Static route unable to resolve", + __PRETTY_FUNCTION__); + return resolved; + } else { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) { + zlog_debug( + "\t%s: Route Type %s has not turned on recursion", + __PRETTY_FUNCTION__, + zebra_route_string(re->type)); + if (re->type == ZEBRA_ROUTE_BGP + && !CHECK_FLAG(re->flags, ZEBRA_FLAG_IBGP)) + zlog_debug( + "\tEBGP: see \"disable-ebgp-connected-route-check\" or \"disable-connected-check\""); + } + return 0; + } + } + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug("\t%s: Nexthop did not lookup in table", + __PRETTY_FUNCTION__); + return 0; +} + +/* This function verifies reachability of one given nexthop, which can be + * numbered or unnumbered, IPv4 or IPv6. The result is unconditionally stored + * in nexthop->flags field. The nexthop->ifindex will be updated + * appropriately as well. An existing route map can turn + * (otherwise active) nexthop into inactive, but not vice versa. + * + * The return value is the final value of 'ACTIVE' flag. + */ +static unsigned nexthop_active_check(struct route_node *rn, + struct route_entry *re, + struct nexthop *nexthop) +{ + struct interface *ifp; + route_map_result_t ret = RMAP_MATCH; + int family; + char buf[SRCDEST2STR_BUFFER]; + const struct prefix *p, *src_p; + struct zebra_vrf *zvrf; + + srcdest_rnode_prefixes(rn, &p, &src_p); + + if (rn->p.family == AF_INET) + family = AFI_IP; + else if (rn->p.family == AF_INET6) + family = AFI_IP6; + else + family = 0; + switch (nexthop->type) { + case NEXTHOP_TYPE_IFINDEX: + ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id); + if (ifp && if_is_operative(ifp)) + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + else + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + break; + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + family = AFI_IP; + if (nexthop_active(AFI_IP, re, nexthop, rn)) + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + else + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + break; + case NEXTHOP_TYPE_IPV6: + family = AFI_IP6; + if (nexthop_active(AFI_IP6, re, nexthop, rn)) + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + else + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + break; + case NEXTHOP_TYPE_IPV6_IFINDEX: + /* RFC 5549, v4 prefix with v6 NH */ + if (rn->p.family != AF_INET) + family = AFI_IP6; + if (IN6_IS_ADDR_LINKLOCAL(&nexthop->gate.ipv6)) { + ifp = if_lookup_by_index(nexthop->ifindex, + nexthop->vrf_id); + if (ifp && if_is_operative(ifp)) + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + else + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + } else { + if (nexthop_active(AFI_IP6, re, nexthop, rn)) + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + else + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + } + break; + case NEXTHOP_TYPE_BLACKHOLE: + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + break; + default: + break; + } + if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug("\t%s: Unable to find a active nexthop", + __PRETTY_FUNCTION__); + return 0; + } + + /* XXX: What exactly do those checks do? Do we support + * e.g. IPv4 routes with IPv6 nexthops or vice versa? + */ + if (RIB_SYSTEM_ROUTE(re) || (family == AFI_IP && p->family != AF_INET) + || (family == AFI_IP6 && p->family != AF_INET6)) + return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + + /* The original code didn't determine the family correctly + * e.g. for NEXTHOP_TYPE_IFINDEX. Retrieve the correct afi + * from the rib_table_info in those cases. + * Possibly it may be better to use only the rib_table_info + * in every case. + */ + if (!family) { + rib_table_info_t *info; + + info = srcdest_rnode_table_info(rn); + family = info->afi; + } + + memset(&nexthop->rmap_src.ipv6, 0, sizeof(union g_addr)); + + zvrf = zebra_vrf_lookup_by_id(nexthop->vrf_id); + if (!zvrf) { + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug("\t%s: zvrf is NULL", __PRETTY_FUNCTION__); + return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + } + + /* It'll get set if required inside */ + ret = zebra_route_map_check(family, re->type, re->instance, p, nexthop, + zvrf, re->tag); + if (ret == RMAP_DENYMATCH) { + if (IS_ZEBRA_DEBUG_RIB) { + srcdest_rnode2str(rn, buf, sizeof(buf)); + zlog_debug( + "%u:%s: Filtering out with NH out %s due to route map", + re->vrf_id, buf, + ifindex2ifname(nexthop->ifindex, + nexthop->vrf_id)); + } + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + } + return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); +} + +/* + * Iterate over all nexthops of the given RIB entry and refresh their + * ACTIVE flag. re->nexthop_active_num is updated accordingly. If any + * nexthop is found to toggle the ACTIVE flag, the whole re structure + * is flagged with ROUTE_ENTRY_CHANGED. + * + * Return value is the new number of active nexthops. + */ +int nexthop_active_update(struct route_node *rn, struct route_entry *re) +{ + struct nexthop *nexthop; + union g_addr prev_src; + unsigned int prev_active, new_active; + ifindex_t prev_index; + + re->nexthop_active_num = 0; + UNSET_FLAG(re->status, ROUTE_ENTRY_CHANGED); + + for (nexthop = re->ng.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; + /* + * We need to respect the multipath_num here + * as that what we should be able to install from + * a multipath perpsective should not be a data plane + * decision point. + */ + new_active = nexthop_active_check(rn, re, nexthop); + if (new_active + && re->nexthop_active_num >= zrouter.multipath_num) { + UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); + new_active = 0; + } + if (new_active) + re->nexthop_active_num++; + /* Don't allow src setting on IPv6 addr for now */ + if (prev_active != new_active || 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) + || ((nexthop->type >= NEXTHOP_TYPE_IPV6 + && nexthop->type < NEXTHOP_TYPE_BLACKHOLE) + && !(IPV6_ADDR_SAME(&prev_src.ipv6, + &nexthop->rmap_src.ipv6))) + || CHECK_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED)) { + SET_FLAG(re->status, ROUTE_ENTRY_CHANGED); + SET_FLAG(re->status, ROUTE_ENTRY_NEXTHOPS_CHANGED); + } + } + + return re->nexthop_active_num; +} + diff --git a/zebra/zebra_nhg.h b/zebra/zebra_nhg.h new file mode 100644 index 000000000..ff2351c75 --- /dev/null +++ b/zebra/zebra_nhg.h @@ -0,0 +1,29 @@ +/* Zebra Nexthop Group header. + * Copyright (C) 2019 Cumulus Networks, Inc. + * Donald Sharp + * Stephen Worley + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ +#ifndef __ZEBRA_NHG_H__ +#define __ZEBRA_NHG_H__ + +#include "zebra/rib.h" + +extern int nexthop_active_update(struct route_node *rn, struct route_entry *re); +#endif diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 8f2731666..1878ac39c 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -54,6 +54,7 @@ #include "zebra/zebra_vxlan.h" #include "zebra/zapi_msg.h" #include "zebra/zebra_dplane.h" +#include "zebra/zebra_nhg.h" /* * Event, list, and mutex for delivery of dataplane results @@ -336,298 +337,6 @@ struct nexthop *route_entry_nexthop_blackhole_add(struct route_entry *re, return nexthop; } -static void nexthop_set_resolved(afi_t afi, const struct nexthop *newhop, - struct nexthop *nexthop) -{ - struct nexthop *resolved_hop; - - resolved_hop = nexthop_new(); - SET_FLAG(resolved_hop->flags, NEXTHOP_FLAG_ACTIVE); - - resolved_hop->vrf_id = nexthop->vrf_id; - switch (newhop->type) { - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - /* If the resolving route specifies a gateway, use it */ - 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; - } - break; - case NEXTHOP_TYPE_IPV6: - case NEXTHOP_TYPE_IPV6_IFINDEX: - 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; - } - break; - case NEXTHOP_TYPE_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. - */ - resolved_hop->flags |= NEXTHOP_FLAG_ONLINK; - if (afi == AFI_IP) { - resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX; - resolved_hop->gate.ipv4 = nexthop->gate.ipv4; - } else if (afi == AFI_IP6) { - resolved_hop->type = NEXTHOP_TYPE_IPV6_IFINDEX; - resolved_hop->gate.ipv6 = nexthop->gate.ipv6; - } - resolved_hop->ifindex = newhop->ifindex; - break; - case NEXTHOP_TYPE_BLACKHOLE: - resolved_hop->type = NEXTHOP_TYPE_BLACKHOLE; - resolved_hop->bh_type = nexthop->bh_type; - break; - } - - if (newhop->flags & NEXTHOP_FLAG_ONLINK) - resolved_hop->flags |= NEXTHOP_FLAG_ONLINK; - - /* Copy labels of the resolved route */ - if (newhop->nh_label) - nexthop_add_labels(resolved_hop, newhop->nh_label_type, - newhop->nh_label->num_labels, - &newhop->nh_label->label[0]); - - resolved_hop->rparent = nexthop; - nexthop_add(&nexthop->resolved, resolved_hop); -} - -/* - * Given a nexthop we need to properly recursively resolve - * the route. As such, do a table lookup to find and match - * if at all possible. Set the nexthop->ifindex as appropriate - */ -static int nexthop_active(afi_t afi, struct route_entry *re, - struct nexthop *nexthop, - struct route_node *top) -{ - struct prefix p; - struct route_table *table; - struct route_node *rn; - struct route_entry *match = NULL; - int resolved; - struct nexthop *newhop; - struct interface *ifp; - rib_dest_t *dest; - - if ((nexthop->type == NEXTHOP_TYPE_IPV4) - || nexthop->type == NEXTHOP_TYPE_IPV6) - nexthop->ifindex = 0; - - UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE); - nexthops_free(nexthop->resolved); - nexthop->resolved = NULL; - re->nexthop_mtu = 0; - - /* - * If the kernel has sent us a route, then - * by golly gee whiz it's a good route. - */ - if (re->type == ZEBRA_ROUTE_KERNEL || - re->type == ZEBRA_ROUTE_SYSTEM) - return 1; - - /* - * 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. - * This check should suffice to handle IPv4 or IPv6 routes - * sourced from EVPN routes which are installed with the - * next hop as the remote VTEP IP. - */ - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) { - ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id); - if (!ifp) { - if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug( - "\t%s: Onlink and interface: %u[%u] does not exist", - __PRETTY_FUNCTION__, nexthop->ifindex, - nexthop->vrf_id); - return 0; - } - if (connected_is_unnumbered(ifp)) { - if (if_is_operative(ifp)) - return 1; - else { - if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug( - "\t%s: Onlink and interface %s is not operative", - __PRETTY_FUNCTION__, ifp->name); - return 0; - } - } - if (!if_is_operative(ifp)) { - if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug( - "\t%s: Interface %s is not unnumbered", - __PRETTY_FUNCTION__, ifp->name); - return 0; - } - } - - /* Make lookup prefix. */ - memset(&p, 0, sizeof(struct prefix)); - switch (afi) { - case AFI_IP: - p.family = AF_INET; - p.prefixlen = IPV4_MAX_PREFIXLEN; - p.u.prefix4 = nexthop->gate.ipv4; - break; - case AFI_IP6: - p.family = AF_INET6; - p.prefixlen = IPV6_MAX_PREFIXLEN; - p.u.prefix6 = nexthop->gate.ipv6; - break; - default: - assert(afi != AFI_IP && afi != AFI_IP6); - break; - } - /* Lookup table. */ - table = zebra_vrf_table(afi, SAFI_UNICAST, nexthop->vrf_id); - if (!table) { - if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug("\t%s: Table not found", - __PRETTY_FUNCTION__); - return 0; - } - - rn = route_node_match(table, (struct prefix *)&p); - while (rn) { - route_unlock_node(rn); - - /* Lookup should halt if we've matched against ourselves ('top', - * if specified) - i.e., we cannot have a nexthop NH1 is - * resolved by a route NH1. The exception is if the route is a - * host route. - */ - if (top && rn == top) - if (((afi == AFI_IP) && (rn->p.prefixlen != 32)) - || ((afi == AFI_IP6) && (rn->p.prefixlen != 128))) { - if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug( - "\t%s: Matched against ourself and prefix length is not max bit length", - __PRETTY_FUNCTION__); - return 0; - } - - /* Pick up selected route. */ - /* However, do not resolve over default route unless explicitly - * allowed. */ - if (is_default_prefix(&rn->p) - && !rnh_resolve_via_default(p.family)) { - if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug( - "\t:%s: Resolved against default route", - __PRETTY_FUNCTION__); - return 0; - } - - dest = rib_dest_from_rnode(rn); - if (dest && dest->selected_fib - && !CHECK_FLAG(dest->selected_fib->status, - ROUTE_ENTRY_REMOVED) - && dest->selected_fib->type != ZEBRA_ROUTE_TABLE) - match = dest->selected_fib; - - /* If there is no selected route or matched route is EGP, go up - tree. */ - if (!match) { - do { - rn = rn->parent; - } while (rn && rn->info == NULL); - if (rn) - route_lock_node(rn); - - continue; - } - - if (match->type == ZEBRA_ROUTE_CONNECT) { - /* Directly point connected route. */ - newhop = match->ng.nexthop; - if (newhop) { - if (nexthop->type == NEXTHOP_TYPE_IPV4 - || nexthop->type == NEXTHOP_TYPE_IPV6) - nexthop->ifindex = newhop->ifindex; - } - return 1; - } else if (CHECK_FLAG(re->flags, ZEBRA_FLAG_ALLOW_RECURSION)) { - resolved = 0; - for (ALL_NEXTHOPS(match->ng, newhop)) { - if (!CHECK_FLAG(match->status, - ROUTE_ENTRY_INSTALLED)) - continue; - if (CHECK_FLAG(newhop->flags, - NEXTHOP_FLAG_RECURSIVE)) - continue; - - SET_FLAG(nexthop->flags, - NEXTHOP_FLAG_RECURSIVE); - SET_FLAG(re->status, - ROUTE_ENTRY_NEXTHOPS_CHANGED); - nexthop_set_resolved(afi, newhop, nexthop); - resolved = 1; - } - if (resolved) - re->nexthop_mtu = match->mtu; - if (!resolved && IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug("\t%s: Recursion failed to find", - __PRETTY_FUNCTION__); - return resolved; - } else if (re->type == ZEBRA_ROUTE_STATIC) { - resolved = 0; - for (ALL_NEXTHOPS(match->ng, newhop)) { - if (!CHECK_FLAG(match->status, - ROUTE_ENTRY_INSTALLED)) - continue; - if (CHECK_FLAG(newhop->flags, - NEXTHOP_FLAG_RECURSIVE)) - continue; - - SET_FLAG(nexthop->flags, - NEXTHOP_FLAG_RECURSIVE); - nexthop_set_resolved(afi, newhop, nexthop); - resolved = 1; - } - if (resolved) - re->nexthop_mtu = match->mtu; - - if (!resolved && IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug( - "\t%s: Static route unable to resolve", - __PRETTY_FUNCTION__); - return resolved; - } else { - if (IS_ZEBRA_DEBUG_RIB_DETAILED) { - zlog_debug("\t%s: Route Type %s has not turned on recursion", - __PRETTY_FUNCTION__, - zebra_route_string(re->type)); - if (re->type == ZEBRA_ROUTE_BGP && - !CHECK_FLAG(re->flags, ZEBRA_FLAG_IBGP)) - zlog_debug("\tEBGP: see \"disable-ebgp-connected-route-check\" or \"disable-connected-check\""); - } - return 0; - } - } - if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug("\t%s: Nexthop did not lookup in table", - __PRETTY_FUNCTION__); - return 0; -} - struct route_entry *rib_match(afi_t afi, safi_t safi, vrf_id_t vrf_id, union g_addr *addr, struct route_node **rn_out) { @@ -798,190 +507,6 @@ struct route_entry *rib_lookup_ipv4(struct prefix_ipv4 *p, vrf_id_t vrf_id) return NULL; } -/* This function verifies reachability of one given nexthop, which can be - * numbered or unnumbered, IPv4 or IPv6. The result is unconditionally stored - * in nexthop->flags field. The nexthop->ifindex will be updated - * appropriately as well. An existing route map can turn - * (otherwise active) nexthop into inactive, but not vice versa. - * - * The return value is the final value of 'ACTIVE' flag. - */ -static unsigned nexthop_active_check(struct route_node *rn, - struct route_entry *re, - struct nexthop *nexthop) -{ - struct interface *ifp; - route_map_result_t ret = RMAP_MATCH; - int family; - char buf[SRCDEST2STR_BUFFER]; - const struct prefix *p, *src_p; - struct zebra_vrf *zvrf; - - srcdest_rnode_prefixes(rn, &p, &src_p); - - if (rn->p.family == AF_INET) - family = AFI_IP; - else if (rn->p.family == AF_INET6) - family = AFI_IP6; - else - family = 0; - switch (nexthop->type) { - case NEXTHOP_TYPE_IFINDEX: - ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id); - if (ifp && if_is_operative(ifp)) - SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - else - UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - break; - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - family = AFI_IP; - if (nexthop_active(AFI_IP, re, nexthop, rn)) - SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - else - UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - break; - case NEXTHOP_TYPE_IPV6: - family = AFI_IP6; - if (nexthop_active(AFI_IP6, re, nexthop, rn)) - SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - else - UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - break; - case NEXTHOP_TYPE_IPV6_IFINDEX: - /* RFC 5549, v4 prefix with v6 NH */ - if (rn->p.family != AF_INET) - family = AFI_IP6; - if (IN6_IS_ADDR_LINKLOCAL(&nexthop->gate.ipv6)) { - ifp = if_lookup_by_index(nexthop->ifindex, - nexthop->vrf_id); - if (ifp && if_is_operative(ifp)) - SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - else - UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - } else { - if (nexthop_active(AFI_IP6, re, nexthop, rn)) - SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - else - UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - } - break; - case NEXTHOP_TYPE_BLACKHOLE: - SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - break; - default: - break; - } - if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) { - if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug("\t%s: Unable to find a active nexthop", - __PRETTY_FUNCTION__); - return 0; - } - - /* XXX: What exactly do those checks do? Do we support - * e.g. IPv4 routes with IPv6 nexthops or vice versa? - */ - if (RIB_SYSTEM_ROUTE(re) || (family == AFI_IP && p->family != AF_INET) - || (family == AFI_IP6 && p->family != AF_INET6)) - return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - - /* The original code didn't determine the family correctly - * e.g. for NEXTHOP_TYPE_IFINDEX. Retrieve the correct afi - * from the rib_table_info in those cases. - * Possibly it may be better to use only the rib_table_info - * in every case. - */ - if (!family) { - rib_table_info_t *info; - - info = srcdest_rnode_table_info(rn); - family = info->afi; - } - - memset(&nexthop->rmap_src.ipv6, 0, sizeof(union g_addr)); - - zvrf = zebra_vrf_lookup_by_id(nexthop->vrf_id); - if (!zvrf) { - if (IS_ZEBRA_DEBUG_RIB_DETAILED) - zlog_debug("\t%s: zvrf is NULL", __PRETTY_FUNCTION__); - return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - } - - /* It'll get set if required inside */ - ret = zebra_route_map_check(family, re->type, re->instance, p, - nexthop, zvrf, re->tag); - if (ret == RMAP_DENYMATCH) { - if (IS_ZEBRA_DEBUG_RIB) { - srcdest_rnode2str(rn, buf, sizeof(buf)); - zlog_debug( - "%u:%s: Filtering out with NH out %s due to route map", - re->vrf_id, buf, - ifindex2ifname(nexthop->ifindex, - nexthop->vrf_id)); - } - UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - } - return CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); -} - -/* - * Iterate over all nexthops of the given RIB entry and refresh their - * ACTIVE flag. re->nexthop_active_num is updated accordingly. If any - * nexthop is found to toggle the ACTIVE flag, the whole re structure - * is flagged with ROUTE_ENTRY_CHANGED. - * - * Return value is the new number of active nexthops. - */ -static int nexthop_active_update(struct route_node *rn, struct route_entry *re) -{ - struct nexthop *nexthop; - union g_addr prev_src; - unsigned int prev_active, new_active; - ifindex_t prev_index; - - re->nexthop_active_num = 0; - UNSET_FLAG(re->status, ROUTE_ENTRY_CHANGED); - - for (nexthop = re->ng.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; - /* - * We need to respect the multipath_num here - * as that what we should be able to install from - * a multipath perpsective should not be a data plane - * decision point. - */ - new_active = nexthop_active_check(rn, re, nexthop); - if (new_active - && re->nexthop_active_num >= zrouter.multipath_num) { - UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); - new_active = 0; - } - if (new_active) - re->nexthop_active_num++; - /* Don't allow src setting on IPv6 addr for now */ - if (prev_active != new_active || 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) - || ((nexthop->type >= NEXTHOP_TYPE_IPV6 - && nexthop->type < NEXTHOP_TYPE_BLACKHOLE) - && !(IPV6_ADDR_SAME(&prev_src.ipv6, - &nexthop->rmap_src.ipv6))) - || CHECK_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED)) { - SET_FLAG(re->status, ROUTE_ENTRY_CHANGED); - SET_FLAG(re->status, ROUTE_ENTRY_NEXTHOPS_CHANGED); - } - } - - return re->nexthop_active_num; -} - /* * Is this RIB labeled-unicast? It must be of type BGP and all paths * (nexthops) must have a label. -- 2.39.2