]> git.proxmox.com Git - mirror_frr.git/commitdiff
zebra: Move nexthop_active_XXX functions to zebra_nhg.c
authorStephen Worley <sworley@cumulusnetworks.com>
Mon, 13 May 2019 19:46:05 +0000 (12:46 -0700)
committerStephen Worley <sworley@cumulusnetworks.com>
Tue, 28 May 2019 21:41:38 +0000 (17:41 -0400)
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 <sharpd@cumulusnetworks.com>
zebra/subdir.am
zebra/zebra_nhg.c [new file with mode: 0644]
zebra/zebra_nhg.h [new file with mode: 0644]
zebra/zebra_rib.c

index 1e36d020a3235a1dc942f6be5d2ac617305871f1..25040a2717dd2a15e818a9d4fdf44db6a984965b 100644 (file)
@@ -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 (file)
index 0000000..f2a76d1
--- /dev/null
@@ -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 <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;
+}
+
diff --git a/zebra/zebra_nhg.h b/zebra/zebra_nhg.h
new file mode 100644 (file)
index 0000000..ff2351c
--- /dev/null
@@ -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
index 8f27316669af3724ecf50badec7ca217681ffed3..1878ac39c3dd7fff7ef0426ae7afe750bf6f76b2 100644 (file)
@@ -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.