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 \
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 \
--- /dev/null
+/* 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 <zebra.h>
+
+#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;
+}
+
--- /dev/null
+/* 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
#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
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)
{
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.