]> git.puffer.fish Git - mirror/frr.git/commitdiff
isisd: implement Remote LFA
authorRenato Westphal <renato@opensourcerouting.org>
Thu, 26 Nov 2020 02:39:09 +0000 (23:39 -0300)
committerRenato Westphal <renato@opensourcerouting.org>
Sat, 9 Jan 2021 01:22:11 +0000 (22:22 -0300)
Remote LFA (RFC 7490) is an extension to the base LFA mechanism
that uses dynamically determined tunnels to extend the IP-FRR
protection coverage.

RLFA is similar to TI-LFA in that it computes a post-convergence
SPT (with the protected interface pruned from the network topology)
and the P/Q spaces based on that SPT. There are a few differences
however:
* RLFAs can push at most one label, so the P/Q spaces need to
  intersect otherwise the destination can't be protected (the
  protection coverage is topology dependent).
* isisd needs to interface with ldpd to obtain the labels it needs to
  create a tunnel to the PQ node. That interaction needs to be done
  asynchronously to prevent blocking the daemon for too long. With
  TI-LFA all required labels are already available in the LSPDB.

RLFA and TI-LFA have more similarities than differences though,
and thanks to that both features share a lot of code.

Limitations:
* Only RLFA link protection is implemented. The algorithm used
  to find node-protecting RLFAs (RFC 8102) is too CPU intensive and
  doesn't always work. Most vendors implement RLFA link protection
  only.
* RFC 7490 says it should be a local matter whether the repair path
  selection policy favors LFA repairs over RLFA repairs. It might be
  desirable, for instance, to prefer RLFAs that satisfy the downstream
  condition over LFAs that don't. In this implementation, however,
  RLFAs are only computed for destinations that can't be protected
  by local LFAs.

Signed-off-by: Renato Westphal <renato@opensourcerouting.org>
16 files changed:
isisd/isis_circuit.h
isisd/isis_lfa.c
isisd/isis_lfa.h
isisd/isis_main.c
isisd/isis_memory.c
isisd/isis_memory.h
isisd/isis_nb_config.c
isisd/isis_route.c
isisd/isis_route.h
isisd/isis_spf.c
isisd/isis_spf.h
isisd/isis_spf_private.h
isisd/isis_zebra.c
isisd/isis_zebra.h
isisd/isisd.c
isisd/isisd.h

index e736d8fb1fb00980f55c842253b6e3898c91e973..9a8982dc06ef58231c34be178b747bf1c0b6ee1f 100644 (file)
@@ -142,6 +142,8 @@ struct isis_circuit {
        struct bfd_info *bfd_info;
        struct ldp_sync_info *ldp_sync_info;
        bool lfa_protection[ISIS_LEVELS];
+       bool rlfa_protection[ISIS_LEVELS];
+       uint32_t rlfa_max_metric[ISIS_LEVELS];
        struct hash *lfa_excluded_ifaces[ISIS_LEVELS];
        bool tilfa_protection[ISIS_LEVELS];
        bool tilfa_node_protection[ISIS_LEVELS];
index fc6b435b62763ad2ebca0812b4916c52c4acd2c7..2da4600a21225601c7c1caacc8185b7c7eae11c2 100644 (file)
@@ -25,6 +25,8 @@
 #include "vrf.h"
 #include "table.h"
 #include "srcdest_table.h"
+#include "plist.h"
+#include "zclient.h"
 
 #include "isis_common.h"
 #include "isisd.h"
 #include "isis_mt.h"
 #include "isis_tlvs.h"
 #include "isis_spf_private.h"
-#include "isisd/isis_errors.h"
+#include "isis_zebra.h"
+#include "isis_errors.h"
 
 DEFINE_MTYPE_STATIC(ISISD, ISIS_SPF_NODE, "ISIS SPF Node");
 DEFINE_MTYPE_STATIC(ISISD, ISIS_LFA_TIEBREAKER, "ISIS LFA Tiebreaker");
 DEFINE_MTYPE_STATIC(ISISD, ISIS_LFA_EXCL_IFACE, "ISIS LFA Excluded Interface");
+DEFINE_MTYPE_STATIC(ISISD, ISIS_RLFA, "ISIS Remote LFA");
 
 static inline int isis_spf_node_compare(const struct isis_spf_node *a,
                                        const struct isis_spf_node *b)
@@ -316,7 +320,7 @@ bool isis_lfa_excise_adj_check(const struct isis_spftree *spftree,
 {
        const struct lfa_protected_resource *resource;
 
-       if (spftree->type != SPF_TYPE_TI_LFA)
+       if (spftree->type != SPF_TYPE_RLFA && spftree->type != SPF_TYPE_TI_LFA)
                return false;
 
        /*
@@ -832,14 +836,14 @@ spf_vertex_check_is_affected(const struct isis_vertex *vertex,
        return false;
 }
 
-/* Check if a given TI-LFA post-convergence SPF vertex needs protection. */
-static bool tilfa_check_needs_protection(const struct isis_spftree *spftree_pc,
-                                        const struct isis_vertex *vertex)
+/* Check if a given RLFA/TI-LFA post-convergence SPF vertex needs protection. */
+static bool lfa_check_needs_protection(const struct isis_spftree *spftree_pc,
+                                      const struct isis_vertex *vertex)
 {
        struct isis_vertex *vertex_old;
 
-       /* Only local adjacencies need Adj-SID protection. */
-       if (VTYPE_IS(vertex->type)
+       /* Only local adjacencies need TI-LFA Adj-SID protection. */
+       if (spftree_pc->type == SPF_TYPE_TI_LFA && VTYPE_IS(vertex->type)
            && !isis_adj_find(spftree_pc->area, spftree_pc->level,
                              vertex->N.id))
                return false;
@@ -849,6 +853,10 @@ static bool tilfa_check_needs_protection(const struct isis_spftree *spftree_pc,
        if (!vertex_old)
                return false;
 
+       /* Skip vertex if it's already protected by local LFA. */
+       if (CHECK_FLAG(vertex_old->flags, F_ISIS_VERTEX_LFA_PROTECTED))
+               return false;
+
        return spf_vertex_check_is_affected(
                vertex_old, spftree_pc->sysid,
                &spftree_pc->lfa.protected_resource);
@@ -877,7 +885,7 @@ int isis_tilfa_check(struct isis_spftree *spftree_pc,
        if (IS_DEBUG_LFA)
                vid2string(vertex, buf, sizeof(buf));
 
-       if (!tilfa_check_needs_protection(spftree_pc, vertex)) {
+       if (!lfa_check_needs_protection(spftree_pc, vertex)) {
                if (IS_DEBUG_LFA)
                        zlog_debug(
                                "ISIS-LFA: %s %s unaffected by %s",
@@ -1166,7 +1174,7 @@ struct isis_spftree *isis_tilfa_compute(struct isis_area *area,
        struct isis_spf_node *adj_node;
 
        if (IS_DEBUG_LFA)
-               zlog_debug("ISIS-LFA: computing the P/Q spaces w.r.t. %s",
+               zlog_debug("ISIS-LFA: computing TI-LFAs for %s",
                           lfa_protected_resource2str(resource));
 
        /* Populate list of nodes affected by link failure. */
@@ -1238,6 +1246,497 @@ int isis_spf_run_neighbors(struct isis_spftree *spftree)
        return 0;
 }
 
+/* Find Router ID of PQ node. */
+static struct in_addr *rlfa_pq_node_rtr_id(struct isis_spftree *spftree,
+                                          const struct isis_vertex *vertex_pq)
+{
+       struct isis_lsp *lsp;
+
+       lsp = isis_root_system_lsp(spftree->lspdb, vertex_pq->N.id);
+       if (!lsp)
+               return NULL;
+
+       if (lsp->tlvs->router_cap->router_id.s_addr == INADDR_ANY)
+               return NULL;
+
+       return &lsp->tlvs->router_cap->router_id;
+}
+
+/* Find PQ node by intersecting the P/Q spaces. This is a recursive function. */
+static const struct in_addr *
+rlfa_find_pq_node(struct isis_spftree *spftree_pc,
+                 struct isis_vertex *vertex_dest,
+                 const struct isis_vertex *vertex,
+                 const struct isis_vertex *vertex_child)
+{
+       struct isis_area *area = spftree_pc->area;
+       int level = spftree_pc->level;
+       struct isis_vertex *pvertex;
+       struct listnode *node;
+       bool is_pnode, is_qnode;
+
+       if (!vertex_child)
+               goto parents;
+       if (vertex->type != VTYPE_NONPSEUDO_IS
+           && vertex->type != VTYPE_NONPSEUDO_TE_IS)
+               goto parents;
+       if (!VTYPE_IS(vertex_child->type))
+               vertex_child = NULL;
+
+       /* Check if node is part of the extended P-space and/or Q-space. */
+       is_pnode = lfa_ext_p_space_check(spftree_pc, vertex_dest, vertex);
+       is_qnode = lfa_q_space_check(spftree_pc, vertex);
+
+       if (is_pnode && is_qnode) {
+               const struct in_addr *rtr_id_pq;
+               uint32_t max_metric;
+               struct prefix_list *plist = NULL;
+
+               rtr_id_pq = rlfa_pq_node_rtr_id(spftree_pc, vertex);
+               if (!rtr_id_pq) {
+                       if (IS_DEBUG_LFA) {
+                               char buf[VID2STR_BUFFER];
+
+                               vid2string(vertex, buf, sizeof(buf));
+                               zlog_debug(
+                                       "ISIS-LFA: tentative PQ node (%s %s) doesn't have a router-ID",
+                                       vtype2string(vertex->type), buf);
+                       }
+                       goto parents;
+               }
+
+               max_metric = spftree_pc->lfa.remote.max_metric;
+               if (max_metric && vertex->d_N > max_metric) {
+                       if (IS_DEBUG_LFA)
+                               zlog_debug(
+                                       "ISIS-LFA: skipping PQ node %pI4 (maximum metric)",
+                                       rtr_id_pq);
+                       goto parents;
+               }
+
+               plist = area->rlfa_plist[level - 1];
+               if (plist) {
+                       struct prefix p;
+
+                       p.family = AF_INET;
+                       p.prefixlen = IPV4_MAX_BITLEN;
+                       p.u.prefix4 = *rtr_id_pq;
+                       if (prefix_list_apply(plist, &p) == PREFIX_DENY) {
+                               if (IS_DEBUG_LFA)
+                                       zlog_debug(
+                                               "ISIS-LFA: PQ node %pI4 filtered by prefix-list",
+                                               rtr_id_pq);
+                               goto parents;
+                       }
+               }
+
+               if (IS_DEBUG_LFA)
+                       zlog_debug("ISIS-LFA: found PQ node: %pI4", rtr_id_pq);
+
+               return rtr_id_pq;
+       }
+
+parents:
+       for (ALL_LIST_ELEMENTS_RO(vertex->parents, node, pvertex)) {
+               const struct in_addr *rtr_id_pq;
+
+               rtr_id_pq = rlfa_find_pq_node(spftree_pc, vertex_dest, pvertex,
+                                             vertex);
+               if (rtr_id_pq)
+                       return rtr_id_pq;
+       }
+
+       return NULL;
+}
+
+int rlfa_cmp(const struct rlfa *a, const struct rlfa *b)
+{
+       return prefix_cmp(&a->prefix, &b->prefix);
+}
+
+static struct rlfa *rlfa_add(struct isis_spftree *spftree,
+                            struct isis_vertex *vertex,
+                            struct in_addr pq_address)
+{
+       struct rlfa *rlfa;
+
+       assert(VTYPE_IP(vertex->type));
+       rlfa = XCALLOC(MTYPE_ISIS_RLFA, sizeof(*rlfa));
+       rlfa->prefix = vertex->N.ip.p.dest;
+       rlfa->vertex = vertex;
+       rlfa->pq_address = pq_address;
+       rlfa_tree_add(&spftree->lfa.remote.rlfas, rlfa);
+
+       return rlfa;
+}
+
+static void rlfa_delete(struct isis_spftree *spftree, struct rlfa *rlfa)
+{
+       rlfa_tree_del(&spftree->lfa.remote.rlfas, rlfa);
+       XFREE(MTYPE_ISIS_RLFA, rlfa);
+}
+
+static struct rlfa *rlfa_lookup(struct isis_spftree *spftree,
+                               union prefixconstptr pu)
+{
+       struct rlfa s = {};
+
+       s.prefix = *pu.p;
+       return rlfa_tree_find(&spftree->lfa.remote.rlfas, &s);
+}
+
+static int isis_area_verify_routes_cb(struct thread *thread)
+{
+       struct isis_area *area = THREAD_ARG(thread);
+
+       if (IS_DEBUG_LFA)
+               zlog_debug("ISIS-LFA: updating RLFAs in the RIB");
+
+       isis_area_verify_routes(area);
+
+       return 0;
+}
+
+static mpls_label_t rlfa_nexthop_label(struct isis_spftree *spftree,
+                                      struct isis_vertex_adj *vadj,
+                                      struct zapi_rlfa_response *response)
+{
+       struct isis_spf_adj *sadj = vadj->sadj;
+       struct isis_adjacency *adj = sadj->adj;
+
+       /*
+        * Special case to make unit tests work (use implicit-null labels
+        * instead of artifical ones).
+        */
+       if (CHECK_FLAG(spftree->flags, F_SPFTREE_NO_ADJACENCIES))
+               return MPLS_LABEL_IMPLICIT_NULL;
+
+       for (unsigned int i = 0; i < response->nexthop_num; i++) {
+               switch (response->nexthops[i].family) {
+               case AF_INET:
+                       for (unsigned int j = 0; j < adj->ipv4_address_count;
+                            j++) {
+                               struct in_addr addr = adj->ipv4_addresses[j];
+
+                               if (!IPV4_ADDR_SAME(
+                                           &addr,
+                                           &response->nexthops[i].gate.ipv4))
+                                       continue;
+
+                               return response->nexthops[i].label;
+                       }
+                       break;
+               case AF_INET6:
+                       for (unsigned int j = 0; j < adj->ipv6_address_count;
+                            j++) {
+                               struct in6_addr addr = adj->ipv6_addresses[j];
+
+                               if (!IPV6_ADDR_SAME(
+                                           &addr,
+                                           &response->nexthops[i].gate.ipv6))
+                                       continue;
+
+                               return response->nexthops[i].label;
+                       }
+                       break;
+
+               default:
+                       break;
+               }
+       }
+
+       return MPLS_INVALID_LABEL;
+}
+
+int isis_rlfa_activate(struct isis_spftree *spftree, struct rlfa *rlfa,
+                      struct zapi_rlfa_response *response)
+{
+       struct isis_area *area = spftree->area;
+       struct isis_vertex *vertex = rlfa->vertex;
+       struct isis_vertex_adj *vadj;
+       struct listnode *node;
+
+       for (ALL_LIST_ELEMENTS_RO(vertex->Adj_N, node, vadj)) {
+               mpls_label_t ldp_label;
+               struct mpls_label_stack *label_stack;
+               size_t num_labels = 0;
+               size_t i = 0;
+
+               ldp_label = rlfa_nexthop_label(spftree, vadj, response);
+               if (ldp_label == MPLS_INVALID_LABEL) {
+                       if (IS_DEBUG_LFA)
+                               zlog_debug(
+                                       "ISIS-LFA: failed to activate RLFA: missing LDP label to reach PQ node through %s",
+                                       sysid_print(vadj->sadj->id));
+                       return -1;
+               }
+
+               if (ldp_label != MPLS_LABEL_IMPLICIT_NULL)
+                       num_labels++;
+               if (response->pq_label != MPLS_LABEL_IMPLICIT_NULL)
+                       num_labels++;
+               if (vadj->sr.present
+                   && vadj->sr.label != MPLS_LABEL_IMPLICIT_NULL)
+                       num_labels++;
+
+               /* Allocate label stack. */
+               label_stack =
+                       XCALLOC(MTYPE_ISIS_NEXTHOP_LABELS,
+                               sizeof(struct mpls_label_stack)
+                                       + num_labels * sizeof(mpls_label_t));
+               label_stack->num_labels = num_labels;
+
+               /* Push label allocated by the nexthop (outer label). */
+               if (ldp_label != MPLS_LABEL_IMPLICIT_NULL)
+                       label_stack->label[i++] = ldp_label;
+               /* Push label allocated by the PQ node (inner label). */
+               if (response->pq_label != MPLS_LABEL_IMPLICIT_NULL)
+                       label_stack->label[i++] = response->pq_label;
+               /* Preserve the original Prefix-SID label when it's present. */
+               if (vadj->sr.present
+                   && vadj->sr.label != MPLS_LABEL_IMPLICIT_NULL)
+                       label_stack->label[i++] = vadj->sr.label;
+
+               vadj->label_stack = label_stack;
+       }
+
+       isis_route_create(&vertex->N.ip.p.dest, &vertex->N.ip.p.src,
+                         vertex->d_N, vertex->depth, &vertex->N.ip.sr,
+                         vertex->Adj_N, true, area,
+                         spftree->route_table_backup);
+       spftree->lfa.protection_counters.rlfa[vertex->N.ip.priority] += 1;
+
+       thread_cancel(&area->t_rlfa_rib_update);
+       thread_add_timer(master, isis_area_verify_routes_cb, area, 2,
+                        &area->t_rlfa_rib_update);
+
+       return 0;
+}
+
+void isis_rlfa_deactivate(struct isis_spftree *spftree, struct rlfa *rlfa)
+{
+       struct isis_area *area = spftree->area;
+       struct isis_vertex *vertex = rlfa->vertex;
+       struct route_node *rn;
+
+       rn = route_node_lookup(spftree->route_table_backup, &rlfa->prefix);
+       if (!rn)
+               return;
+       isis_route_delete(area, rn, spftree->route_table_backup);
+       spftree->lfa.protection_counters.rlfa[vertex->N.ip.priority] -= 1;
+
+       thread_cancel(&area->t_rlfa_rib_update);
+       thread_add_timer(master, isis_area_verify_routes_cb, area, 2,
+                        &area->t_rlfa_rib_update);
+}
+
+void isis_rlfa_list_init(struct isis_spftree *spftree)
+{
+       rlfa_tree_init(&spftree->lfa.remote.rlfas);
+}
+
+void isis_rlfa_list_clear(struct isis_spftree *spftree)
+{
+       while (rlfa_tree_count(&spftree->lfa.remote.rlfas) > 0) {
+               struct rlfa *rlfa;
+
+               rlfa = rlfa_tree_first(&spftree->lfa.remote.rlfas);
+               isis_rlfa_deactivate(spftree, rlfa);
+               rlfa_delete(spftree, rlfa);
+       }
+}
+
+void isis_rlfa_process_ldp_response(struct zapi_rlfa_response *response)
+{
+       struct isis *isis;
+       struct isis_area *area;
+       struct isis_spftree *spftree;
+       struct rlfa *rlfa;
+       enum spf_tree_id tree_id;
+       uint32_t spf_run_id;
+       int level;
+
+       if (response->igp.protocol != ZEBRA_ROUTE_ISIS)
+               return;
+
+       isis = isis_lookup_by_vrfid(response->igp.vrf_id);
+       if (!isis)
+               return;
+
+       area = isis_area_lookup(response->igp.isis.area_tag,
+                               response->igp.vrf_id);
+       if (!area)
+               return;
+
+       tree_id = response->igp.isis.spf.tree_id;
+       if (tree_id != SPFTREE_IPV4 && tree_id != SPFTREE_IPV6) {
+               zlog_warn("ISIS-LFA: invalid SPF tree ID received from LDP");
+               return;
+       }
+
+       level = response->igp.isis.spf.level;
+       if (level != ISIS_LEVEL1 && level != ISIS_LEVEL2) {
+               zlog_warn("ISIS-LFA: invalid IS-IS level received from LDP");
+               return;
+       }
+
+       spf_run_id = response->igp.isis.spf.run_id;
+       spftree = area->spftree[tree_id][level - 1];
+       if (spftree->runcount != spf_run_id)
+               /* Outdated RLFA, ignore... */
+               return;
+
+       rlfa = rlfa_lookup(spftree, &response->destination);
+       if (!rlfa) {
+               zlog_warn(
+                       "ISIS-LFA: couldn't find Remote-LFA %pFX received from LDP",
+                       &response->destination);
+               return;
+       }
+
+       if (response->pq_label != MPLS_INVALID_LABEL) {
+               if (IS_DEBUG_LFA)
+                       zlog_debug(
+                               "ISIS-LFA: activating/updating RLFA for %pFX",
+                               &rlfa->prefix);
+
+               if (isis_rlfa_activate(spftree, rlfa, response) != 0)
+                       isis_rlfa_deactivate(spftree, rlfa);
+       } else {
+               if (IS_DEBUG_LFA)
+                       zlog_debug("ISIS-LFA: deactivating RLFA for %pFX",
+                                  &rlfa->prefix);
+
+               isis_rlfa_deactivate(spftree, rlfa);
+       }
+}
+
+void isis_ldp_rlfa_handle_client_close(struct zapi_client_close_info *info)
+{
+       struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT);
+       struct isis_area *area;
+       struct listnode *node;
+
+       if (!isis)
+               return;
+
+       /* Check if the LDP main client session closed */
+       if (info->proto != ZEBRA_ROUTE_LDP || info->session_id == 0)
+               return;
+
+       if (IS_DEBUG_LFA)
+               zlog_debug("ISIS-LFA: LDP is down, deactivating all RLFAs");
+
+       for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) {
+               for (int tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++) {
+                       for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS;
+                            level++) {
+                               struct isis_spftree *spftree;
+
+                               spftree = area->spftree[tree][level - 1];
+                               isis_rlfa_list_clear(spftree);
+                       }
+               }
+       }
+}
+
+/**
+ * Check if the given SPF vertex needs protection and, if so, attempt to
+ * compute a Remote LFA for it.
+ *
+ * @param spftree_pc   The post-convergence SPF tree
+ * @param vertex       IS-IS SPF vertex to check
+ */
+void isis_rlfa_check(struct isis_spftree *spftree_pc,
+                    struct isis_vertex *vertex)
+{
+       struct isis_spftree *spftree_old = spftree_pc->lfa.old.spftree;
+       struct rlfa *rlfa;
+       const struct in_addr *rtr_id_pq;
+       char buf[VID2STR_BUFFER];
+
+       if (!lfa_check_needs_protection(spftree_pc, vertex)) {
+               if (IS_DEBUG_LFA)
+                       zlog_debug(
+                               "ISIS-LFA: %s %s unaffected by %s",
+                               vtype2string(vertex->type),
+                               vid2string(vertex, buf, sizeof(buf)),
+                               lfa_protected_resource2str(
+                                       &spftree_pc->lfa.protected_resource));
+
+               return;
+       }
+
+       if (IS_DEBUG_LFA)
+               zlog_debug(
+                       "ISIS-LFA: computing repair path(s) of %s %s w.r.t %s",
+                       vtype2string(vertex->type),
+                       vid2string(vertex, buf, sizeof(buf)),
+                       lfa_protected_resource2str(
+                               &spftree_pc->lfa.protected_resource));
+
+       /* Find PQ node. */
+       rtr_id_pq = rlfa_find_pq_node(spftree_pc, vertex, vertex, NULL);
+       if (!rtr_id_pq) {
+               if (IS_DEBUG_LFA)
+                       zlog_debug("ISIS-LFA: no acceptable PQ node found");
+               return;
+       }
+
+       /* Store valid RLFA and store LDP label for the PQ node. */
+       rlfa = rlfa_add(spftree_old, vertex, *rtr_id_pq);
+
+       /* Register RLFA with LDP. */
+       if (isis_zebra_rlfa_register(spftree_old, rlfa) != 0)
+               rlfa_delete(spftree_old, rlfa);
+}
+
+/**
+ * Compute the Remote LFA backup paths for a given protected interface.
+ *
+ * @param area           IS-IS area
+ * @param spftree        IS-IS SPF tree
+ * @param spftree_reverse IS-IS Reverse SPF tree
+ * @param max_metric     Remote LFA maximum metric
+ * @param resource       Protected resource
+ *
+ * @return               Pointer to the post-convergence SPF tree
+ */
+struct isis_spftree *isis_rlfa_compute(struct isis_area *area,
+                                      struct isis_spftree *spftree,
+                                      struct isis_spftree *spftree_reverse,
+                                      uint32_t max_metric,
+                                      struct lfa_protected_resource *resource)
+{
+       struct isis_spftree *spftree_pc;
+
+       if (IS_DEBUG_LFA)
+               zlog_debug("ISIS-LFA: computing remote LFAs for %s",
+                          lfa_protected_resource2str(resource));
+
+       /* Create post-convergence SPF tree. */
+       spftree_pc = isis_spftree_new(area, spftree->lspdb, spftree->sysid,
+                                     spftree->level, spftree->tree_id,
+                                     SPF_TYPE_RLFA, spftree->flags);
+       spftree_pc->lfa.old.spftree = spftree;
+       spftree_pc->lfa.old.spftree_reverse = spftree_reverse;
+       spftree_pc->lfa.remote.max_metric = max_metric;
+       spftree_pc->lfa.protected_resource = *resource;
+
+       /* Compute the extended P-space and Q-space. */
+       lfa_calc_pq_spaces(spftree_pc, resource);
+
+       if (IS_DEBUG_LFA)
+               zlog_debug(
+                       "ISIS-LFA: computing the post convergence SPT w.r.t. %s",
+                       lfa_protected_resource2str(resource));
+
+       /* Re-run SPF in the local node to find the post-convergence paths. */
+       isis_run_spf(spftree_pc);
+
+       return spftree_pc;
+}
+
 /* Calculate the distance from the root node to the given IP destination. */
 static int lfa_calc_dist_destination(struct isis_spftree *spftree,
                                     const struct isis_vertex *vertex_N,
@@ -1451,8 +1950,7 @@ static bool clfa_node_protecting_check(struct isis_spftree *spftree,
 }
 
 static struct list *
-isis_lfa_tiebreakers(struct isis_area *area, struct isis_circuit *circuit,
-                    struct isis_spftree *spftree,
+isis_lfa_tiebreakers(struct isis_area *area, struct isis_spftree *spftree,
                     struct lfa_protected_resource *resource,
                     struct isis_vertex *vertex,
                     struct isis_spf_adj *sadj_primary, struct list *lfa_list)
@@ -1572,6 +2070,10 @@ void isis_lfa_compute(struct isis_area *area, struct isis_circuit *circuit,
 
        resource->type = LFA_LINK_PROTECTION;
 
+       if (IS_DEBUG_LFA)
+               zlog_debug("ISIS-LFA: computing local LFAs for %s",
+                          lfa_protected_resource2str(resource));
+
        for (ALL_QUEUE_ELEMENTS_RO(&spftree->paths, vnode, vertex)) {
                struct list *lfa_list;
                struct list *filtered_lfa_list;
@@ -1591,7 +2093,8 @@ void isis_lfa_compute(struct isis_area *area, struct isis_circuit *circuit,
                                                  resource)) {
                        if (IS_DEBUG_LFA)
                                zlog_debug(
-                                       "ISIS-LFA: route unaffected by %s",
+                                       "ISIS-LFA: %s %s unaffected by %s",
+                                       vtype2string(vertex->type), buf,
                                        lfa_protected_resource2str(resource));
                        continue;
                }
@@ -1697,15 +2200,18 @@ void isis_lfa_compute(struct isis_area *area, struct isis_circuit *circuit,
 
                if (list_isempty(lfa_list)) {
                        if (IS_DEBUG_LFA)
-                               zlog_debug("ISIS-LFA: no valid LFAs found");
+                               zlog_debug(
+                                       "ISIS-LFA: no valid local LFAs found");
                        list_delete(&lfa_list);
                        continue;
                }
 
+               SET_FLAG(vertex->flags, F_ISIS_VERTEX_LFA_PROTECTED);
+
                /* Check tie-breakers. */
                filtered_lfa_list =
-                       isis_lfa_tiebreakers(area, circuit, spftree, resource,
-                                            vertex, sadj_primary, lfa_list);
+                       isis_lfa_tiebreakers(area, spftree, resource, vertex,
+                                            sadj_primary, lfa_list);
 
                /* Create backup route using the best LFAs. */
                allow_ecmp = area->lfa_load_sharing[level - 1];
@@ -1746,7 +2252,7 @@ static void isis_spf_run_tilfa(struct isis_area *area,
 }
 
 /**
- * Run the LFA/TI-LFA algorithms for all protected interfaces.
+ * Run the LFA/RLFA/TI-LFA algorithms for all protected interfaces.
  *
  * @param area         IS-IS area
  * @param spftree      IS-IS SPF tree
@@ -1756,13 +2262,11 @@ void isis_spf_run_lfa(struct isis_area *area, struct isis_spftree *spftree)
        struct isis_spftree *spftree_reverse = NULL;
        struct isis_circuit *circuit;
        struct listnode *node;
-       bool tilfa_configured;
        int level = spftree->level;
 
-       tilfa_configured = (area->tilfa_protected_links[level - 1] > 0);
-
        /* Run reverse SPF locally. */
-       if (tilfa_configured)
+       if (area->rlfa_protected_links[level - 1] > 0
+           || area->tilfa_protected_links[level - 1] > 0)
                spftree_reverse = isis_spf_reverse_run(spftree);
 
        /* Run forward SPF on all adjacent routers. */
@@ -1808,15 +2312,32 @@ void isis_spf_run_lfa(struct isis_area *area, struct isis_spftree *spftree)
                        continue;
                }
 
-               if (circuit->lfa_protection[level - 1])
+               if (circuit->lfa_protection[level - 1]) {
+                       /* Run local LFA. */
                        isis_lfa_compute(area, circuit, spftree, &resource);
-               else if (circuit->tilfa_protection[level - 1]) {
+
+                       if (circuit->rlfa_protection[level - 1]) {
+                               struct isis_spftree *spftree_pc;
+                               uint32_t max_metric;
+
+                               /* Run remote LFA. */
+                               assert(spftree_reverse);
+                               max_metric =
+                                       circuit->rlfa_max_metric[level - 1];
+                               spftree_pc = isis_rlfa_compute(
+                                       area, spftree, spftree_reverse,
+                                       max_metric, &resource);
+                               listnode_add(spftree->lfa.remote.pc_spftrees,
+                                            spftree_pc);
+                       }
+               } else if (circuit->tilfa_protection[level - 1]) {
+                       /* Run TI-LFA. */
                        assert(spftree_reverse);
                        isis_spf_run_tilfa(area, circuit, spftree,
                                           spftree_reverse, &resource);
                }
        }
 
-       if (tilfa_configured)
+       if (spftree_reverse)
                isis_spftree_del(spftree_reverse);
 }
index f09fc663a45a4d2e34e684ab391ed79ff51843ae..65891cae44313e81ed0f56fe1651facb6ca62d55 100644 (file)
 #define _FRR_ISIS_LFA_H
 
 #include "lib/typesafe.h"
+#include "lib/zclient.h"
 
 PREDECL_RBTREE_UNIQ(lfa_tiebreaker_tree)
+PREDECL_RBTREE_UNIQ(rlfa_tree)
 
 enum lfa_tiebreaker_type {
        LFA_TIEBREAKER_DOWNSTREAM = 0,
@@ -41,6 +43,15 @@ int lfa_tiebreaker_cmp(const struct lfa_tiebreaker *a,
 DECLARE_RBTREE_UNIQ(lfa_tiebreaker_tree, struct lfa_tiebreaker, entry,
                    lfa_tiebreaker_cmp)
 
+struct rlfa {
+       struct rlfa_tree_item entry;
+       struct prefix prefix;
+       struct isis_vertex *vertex;
+       struct in_addr pq_address;
+};
+int rlfa_cmp(const struct rlfa *a, const struct rlfa *b);
+DECLARE_RBTREE_UNIQ(rlfa_tree, struct rlfa, entry, rlfa_cmp)
+
 enum isis_tilfa_sid_type {
        TILFA_SID_PREFIX = 1,
        TILFA_SID_ADJ,
@@ -145,6 +156,19 @@ bool isis_lfa_excise_node_check(const struct isis_spftree *spftree,
                                const uint8_t *id);
 struct isis_spftree *isis_spf_reverse_run(const struct isis_spftree *spftree);
 int isis_spf_run_neighbors(struct isis_spftree *spftree);
+int isis_rlfa_activate(struct isis_spftree *spftree, struct rlfa *rlfa,
+                      struct zapi_rlfa_response *response);
+void isis_rlfa_deactivate(struct isis_spftree *spftree, struct rlfa *rlfa);
+void isis_rlfa_list_init(struct isis_spftree *spftree);
+void isis_rlfa_list_clear(struct isis_spftree *spftree);
+void isis_rlfa_process_ldp_response(struct zapi_rlfa_response *response);
+void isis_ldp_rlfa_handle_client_close(struct zapi_client_close_info *info);
+void isis_rlfa_check(struct isis_spftree *spftree, struct isis_vertex *vertex);
+struct isis_spftree *isis_rlfa_compute(struct isis_area *area,
+                                      struct isis_spftree *spftree,
+                                      struct isis_spftree *spftree_reverse,
+                                      uint32_t max_metric,
+                                      struct lfa_protected_resource *resource);
 void isis_lfa_compute(struct isis_area *area, struct isis_circuit *circuit,
                      struct isis_spftree *spftree,
                      struct lfa_protected_resource *resource);
index 4576a4a95c9a8f63aabbb4695a96b7d8d017d246..1b04f4f7a0938112ac0e08ee361fdb4f81001c32 100644 (file)
@@ -246,6 +246,8 @@ int main(int argc, char **argv, char **envp)
        access_list_delete_hook(isis_filter_update);
        isis_vrf_init();
        prefix_list_init();
+       prefix_list_add_hook(isis_prefix_list_update);
+       prefix_list_delete_hook(isis_prefix_list_update);
        isis_init();
        isis_circuit_init();
 #ifdef FABRICD
index b63a82f4048e2c3b6299b60a82823f088aae42a8..f716e060cd59828c0185148a54e23e96ef5391cc 100644 (file)
@@ -46,3 +46,4 @@ DEFINE_MTYPE(ISISD, ISIS_EXT_ROUTE, "ISIS redistributed route")
 DEFINE_MTYPE(ISISD, ISIS_EXT_INFO, "ISIS redistributed route info")
 DEFINE_MTYPE(ISISD, ISIS_MPLS_TE, "ISIS MPLS_TE parameters")
 DEFINE_MTYPE(ISISD, ISIS_ACL_NAME, "ISIS access-list name")
+DEFINE_MTYPE(ISISD, ISIS_PLIST_NAME, "ISIS prefix-list name")
index 3ef1c5bf0bbd23b4f92783284df59979b590d71c..5bcd2a3983528b98e7ae2af8b187fc9103ab5574 100644 (file)
@@ -45,5 +45,6 @@ DECLARE_MTYPE(ISIS_EXT_ROUTE)
 DECLARE_MTYPE(ISIS_EXT_INFO)
 DECLARE_MTYPE(ISIS_MPLS_TE)
 DECLARE_MTYPE(ISIS_ACL_NAME)
+DECLARE_MTYPE(ISIS_PLIST_NAME)
 
 #endif /* _QUAGGA_ISIS_MEMORY_H */
index e3222e23c9c81a567bf54c4ab682d651aaef7ad6..3ec23df43d1afa5d45e3b2594a32d23cf333bf34 100644 (file)
@@ -28,6 +28,7 @@
 #include "log.h"
 #include "bfd.h"
 #include "filter.h"
+#include "plist.h"
 #include "spf_backoff.h"
 #include "lib_errors.h"
 #include "vrf.h"
@@ -1548,14 +1549,18 @@ int isis_instance_fast_reroute_level_1_lfa_tiebreaker_type_modify(
 int isis_instance_fast_reroute_level_1_remote_lfa_prefix_list_modify(
        struct nb_cb_modify_args *args)
 {
-       switch (args->event) {
-       case NB_EV_VALIDATE:
-       case NB_EV_PREPARE:
-       case NB_EV_ABORT:
-       case NB_EV_APPLY:
-               /* TODO: implement me. */
-               break;
-       }
+       struct isis_area *area;
+       const char *plist_name;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       area = nb_running_get_entry(args->dnode, NULL, true);
+       plist_name = yang_dnode_get_string(args->dnode, NULL);
+
+       area->rlfa_plist_name[0] = XSTRDUP(MTYPE_ISIS_PLIST_NAME, plist_name);
+       area->rlfa_plist[0] = prefix_list_lookup(AFI_IP, plist_name);
+       lsp_regenerate_schedule(area, area->is_type, 0);
 
        return NB_OK;
 }
@@ -1563,14 +1568,16 @@ int isis_instance_fast_reroute_level_1_remote_lfa_prefix_list_modify(
 int isis_instance_fast_reroute_level_1_remote_lfa_prefix_list_destroy(
        struct nb_cb_destroy_args *args)
 {
-       switch (args->event) {
-       case NB_EV_VALIDATE:
-       case NB_EV_PREPARE:
-       case NB_EV_ABORT:
-       case NB_EV_APPLY:
-               /* TODO: implement me. */
-               break;
-       }
+       struct isis_area *area;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       area = nb_running_get_entry(args->dnode, NULL, true);
+
+       XFREE(MTYPE_ISIS_PLIST_NAME, area->rlfa_plist_name[0]);
+       area->rlfa_plist[0] = NULL;
+       lsp_regenerate_schedule(area, area->is_type, 0);
 
        return NB_OK;
 }
@@ -1691,14 +1698,18 @@ int isis_instance_fast_reroute_level_2_lfa_tiebreaker_type_modify(
 int isis_instance_fast_reroute_level_2_remote_lfa_prefix_list_modify(
        struct nb_cb_modify_args *args)
 {
-       switch (args->event) {
-       case NB_EV_VALIDATE:
-       case NB_EV_PREPARE:
-       case NB_EV_ABORT:
-       case NB_EV_APPLY:
-               /* TODO: implement me. */
-               break;
-       }
+       struct isis_area *area;
+       const char *plist_name;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       area = nb_running_get_entry(args->dnode, NULL, true);
+       plist_name = yang_dnode_get_string(args->dnode, NULL);
+
+       area->rlfa_plist_name[1] = XSTRDUP(MTYPE_ISIS_PLIST_NAME, plist_name);
+       area->rlfa_plist[1] = prefix_list_lookup(AFI_IP, plist_name);
+       lsp_regenerate_schedule(area, area->is_type, 0);
 
        return NB_OK;
 }
@@ -1706,14 +1717,16 @@ int isis_instance_fast_reroute_level_2_remote_lfa_prefix_list_modify(
 int isis_instance_fast_reroute_level_2_remote_lfa_prefix_list_destroy(
        struct nb_cb_destroy_args *args)
 {
-       switch (args->event) {
-       case NB_EV_VALIDATE:
-       case NB_EV_PREPARE:
-       case NB_EV_ABORT:
-       case NB_EV_APPLY:
-               /* TODO: implement me. */
-               break;
-       }
+       struct isis_area *area;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       area = nb_running_get_entry(args->dnode, NULL, true);
+
+       XFREE(MTYPE_ISIS_PLIST_NAME, area->rlfa_plist_name[1]);
+       area->rlfa_plist[1] = NULL;
+       lsp_regenerate_schedule(area, area->is_type, 0);
 
        return NB_OK;
 }
@@ -3510,15 +3523,24 @@ int lib_interface_isis_fast_reroute_level_1_lfa_exclude_interface_destroy(
 int lib_interface_isis_fast_reroute_level_1_remote_lfa_enable_modify(
        struct nb_cb_modify_args *args)
 {
-       switch (args->event) {
-       case NB_EV_VALIDATE:
-       case NB_EV_PREPARE:
-       case NB_EV_ABORT:
-       case NB_EV_APPLY:
-               /* TODO: implement me. */
-               break;
+       struct isis_area *area;
+       struct isis_circuit *circuit;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       circuit = nb_running_get_entry(args->dnode, NULL, true);
+       circuit->rlfa_protection[0] = yang_dnode_get_bool(args->dnode, NULL);
+       if (circuit->rlfa_protection[0])
+               circuit->area->rlfa_protected_links[0]++;
+       else {
+               assert(circuit->area->rlfa_protected_links[0] > 0);
+               circuit->area->rlfa_protected_links[0]--;
        }
 
+       area = circuit->area;
+       lsp_regenerate_schedule(area, area->is_type, 0);
+
        return NB_OK;
 }
 
@@ -3529,14 +3551,17 @@ int lib_interface_isis_fast_reroute_level_1_remote_lfa_enable_modify(
 int lib_interface_isis_fast_reroute_level_1_remote_lfa_maximum_metric_modify(
        struct nb_cb_modify_args *args)
 {
-       switch (args->event) {
-       case NB_EV_VALIDATE:
-       case NB_EV_PREPARE:
-       case NB_EV_ABORT:
-       case NB_EV_APPLY:
-               /* TODO: implement me. */
-               break;
-       }
+       struct isis_area *area;
+       struct isis_circuit *circuit;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       circuit = nb_running_get_entry(args->dnode, NULL, true);
+       circuit->rlfa_max_metric[0] = yang_dnode_get_uint32(args->dnode, NULL);
+
+       area = circuit->area;
+       lsp_regenerate_schedule(area, area->is_type, 0);
 
        return NB_OK;
 }
@@ -3544,19 +3569,21 @@ int lib_interface_isis_fast_reroute_level_1_remote_lfa_maximum_metric_modify(
 int lib_interface_isis_fast_reroute_level_1_remote_lfa_maximum_metric_destroy(
        struct nb_cb_destroy_args *args)
 {
-       switch (args->event) {
-       case NB_EV_VALIDATE:
-       case NB_EV_PREPARE:
-       case NB_EV_ABORT:
-       case NB_EV_APPLY:
-               /* TODO: implement me. */
-               break;
-       }
+       struct isis_area *area;
+       struct isis_circuit *circuit;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       circuit = nb_running_get_entry(args->dnode, NULL, true);
+       circuit->rlfa_max_metric[0] = 0;
+
+       area = circuit->area;
+       lsp_regenerate_schedule(area, area->is_type, 0);
 
        return NB_OK;
 }
 
-
 /*
  * XPath:
  * /frr-interface:lib/interface/frr-isisd:isis/fast-reroute/level-1/ti-lfa/enable
@@ -3687,15 +3714,24 @@ int lib_interface_isis_fast_reroute_level_2_lfa_exclude_interface_destroy(
 int lib_interface_isis_fast_reroute_level_2_remote_lfa_enable_modify(
        struct nb_cb_modify_args *args)
 {
-       switch (args->event) {
-       case NB_EV_VALIDATE:
-       case NB_EV_PREPARE:
-       case NB_EV_ABORT:
-       case NB_EV_APPLY:
-               /* TODO: implement me. */
-               break;
+       struct isis_area *area;
+       struct isis_circuit *circuit;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       circuit = nb_running_get_entry(args->dnode, NULL, true);
+       circuit->rlfa_protection[1] = yang_dnode_get_bool(args->dnode, NULL);
+       if (circuit->rlfa_protection[1])
+               circuit->area->rlfa_protected_links[1]++;
+       else {
+               assert(circuit->area->rlfa_protected_links[1] > 0);
+               circuit->area->rlfa_protected_links[1]--;
        }
 
+       area = circuit->area;
+       lsp_regenerate_schedule(area, area->is_type, 0);
+
        return NB_OK;
 }
 
@@ -3706,14 +3742,17 @@ int lib_interface_isis_fast_reroute_level_2_remote_lfa_enable_modify(
 int lib_interface_isis_fast_reroute_level_2_remote_lfa_maximum_metric_modify(
        struct nb_cb_modify_args *args)
 {
-       switch (args->event) {
-       case NB_EV_VALIDATE:
-       case NB_EV_PREPARE:
-       case NB_EV_ABORT:
-       case NB_EV_APPLY:
-               /* TODO: implement me. */
-               break;
-       }
+       struct isis_area *area;
+       struct isis_circuit *circuit;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       circuit = nb_running_get_entry(args->dnode, NULL, true);
+       circuit->rlfa_max_metric[1] = yang_dnode_get_uint32(args->dnode, NULL);
+
+       area = circuit->area;
+       lsp_regenerate_schedule(area, area->is_type, 0);
 
        return NB_OK;
 }
@@ -3721,14 +3760,17 @@ int lib_interface_isis_fast_reroute_level_2_remote_lfa_maximum_metric_modify(
 int lib_interface_isis_fast_reroute_level_2_remote_lfa_maximum_metric_destroy(
        struct nb_cb_destroy_args *args)
 {
-       switch (args->event) {
-       case NB_EV_VALIDATE:
-       case NB_EV_PREPARE:
-       case NB_EV_ABORT:
-       case NB_EV_APPLY:
-               /* TODO: implement me. */
-               break;
-       }
+       struct isis_area *area;
+       struct isis_circuit *circuit;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       circuit = nb_running_get_entry(args->dnode, NULL, true);
+       circuit->rlfa_max_metric[1] = 0;
+
+       area = circuit->area;
+       lsp_regenerate_schedule(area, area->is_type, 0);
 
        return NB_OK;
 }
index d32f219e98b2d740d19d5f9fa3f12a061c70f0f6..e1baf351f49952a22bad9fb68624c59a91e4784b 100644 (file)
@@ -279,6 +279,22 @@ static bool isis_sr_psid_info_same(struct isis_sr_psid_info *new,
        return true;
 }
 
+static bool isis_label_stack_same(struct mpls_label_stack *new,
+                                 struct mpls_label_stack *old)
+{
+       if (!new && !old)
+               return true;
+       if (!new || !old)
+               return false;
+       if (new->num_labels != old->num_labels)
+               return false;
+       if (memcmp(&new->label, &old->label,
+                  sizeof(mpls_label_t) * new->num_labels))
+               return false;
+
+       return true;
+}
+
 static int isis_route_info_same(struct isis_route_info *new,
                                struct isis_route_info *old, char *buf,
                                size_t buf_size)
@@ -327,6 +343,12 @@ static int isis_route_info_same(struct isis_route_info *new,
                                snprintf(buf, buf_size, "nhop SR label");
                        return 0;
                }
+               if (!isis_label_stack_same(new_nh->label_stack,
+                                          old_nh->label_stack)) {
+                       if (buf)
+                               snprintf(buf, buf_size, "nhop label stack");
+                       return 0;
+               }
        }
 
        /* only the resync flag needs to be checked */
@@ -400,8 +422,8 @@ isis_route_create(struct prefix *prefix, struct prefix_ipv6 *src_p,
        return route_info;
 }
 
-static void isis_route_delete(struct isis_area *area, struct route_node *rode,
-                             struct route_table *table)
+void isis_route_delete(struct isis_area *area, struct route_node *rode,
+                      struct route_table *table)
 {
        struct isis_route_info *rinfo;
        char buff[SRCDEST2STR_BUFFER];
@@ -466,9 +488,6 @@ static void isis_route_update(struct isis_area *area, struct prefix *prefix,
                SET_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED);
                UNSET_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_RESYNC);
        } else {
-               if (!CHECK_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED))
-                       return;
-
                /* Uninstall Prefix-SID label. */
                if (route_info->sr.present)
                        isis_zebra_prefix_sid_uninstall(
@@ -516,6 +535,10 @@ static void _isis_route_verify_table(struct isis_area *area,
                                rinfo->backup = rnode_bck->info;
                                UNSET_FLAG(rinfo->flag,
                                           ISIS_ROUTE_FLAG_ZEBRA_SYNCED);
+                       } else if (rinfo->backup) {
+                               rinfo->backup = NULL;
+                               UNSET_FLAG(rinfo->flag,
+                                          ISIS_ROUTE_FLAG_ZEBRA_SYNCED);
                        }
                }
 
@@ -629,6 +652,10 @@ void isis_route_verify_merge(struct isis_area *area,
                                rinfo->backup = rnode_bck->info;
                                UNSET_FLAG(rinfo->flag,
                                           ISIS_ROUTE_FLAG_ZEBRA_SYNCED);
+                       } else if (rinfo->backup) {
+                               rinfo->backup = NULL;
+                               UNSET_FLAG(rinfo->flag,
+                                          ISIS_ROUTE_FLAG_ZEBRA_SYNCED);
                        }
 
                        mrnode = srcdest_rnode_get(merge, prefix, src_p);
index 0d4f8849590fd08a5a95d0b2e626640481030a6f..d6763ec76c10e267ac3494aed32930eeae7b9e31 100644 (file)
@@ -63,6 +63,8 @@ isis_route_create(struct prefix *prefix, struct prefix_ipv6 *src_p,
                  uint32_t cost, uint32_t depth, struct isis_sr_psid_info *sr,
                  struct list *adjacencies, bool allow_ecmp,
                  struct isis_area *area, struct route_table *table);
+void isis_route_delete(struct isis_area *area, struct route_node *rode,
+                      struct route_table *table);
 
 /* Walk the given table and install new routes to zebra and remove old ones.
  * route status is tracked using ISIS_ROUTE_FLAG_ACTIVE */
index 57b1d66c226b28624d2106b06010b1fcdb5322ca..30a94c189035c621cfb15ce3a613ee26a9d10ed7 100644 (file)
@@ -56,6 +56,7 @@
 #include "isis_csm.h"
 #include "isis_mt.h"
 #include "isis_tlvs.h"
+#include "isis_zebra.h"
 #include "fabricd.h"
 #include "isis_spf_private.h"
 
@@ -354,7 +355,10 @@ struct isis_spftree *isis_spftree_new(struct isis_area *area,
        tree->tree_id = tree_id;
        tree->family = (tree->tree_id == SPFTREE_IPV4) ? AF_INET : AF_INET6;
        tree->flags = flags;
-       if (tree->type == SPF_TYPE_TI_LFA) {
+       isis_rlfa_list_init(tree);
+       tree->lfa.remote.pc_spftrees = list_new();
+       tree->lfa.remote.pc_spftrees->del = (void (*)(void *))isis_spftree_del;
+       if (tree->type == SPF_TYPE_RLFA || tree->type == SPF_TYPE_TI_LFA) {
                isis_spf_node_list_init(&tree->lfa.p_space);
                isis_spf_node_list_init(&tree->lfa.q_space);
        }
@@ -366,7 +370,11 @@ void isis_spftree_del(struct isis_spftree *spftree)
 {
        hash_clean(spftree->prefix_sids, NULL);
        hash_free(spftree->prefix_sids);
-       if (spftree->type == SPF_TYPE_TI_LFA) {
+       isis_zebra_rlfa_unregister_all(spftree);
+       isis_rlfa_list_clear(spftree);
+       list_delete(&spftree->lfa.remote.pc_spftrees);
+       if (spftree->type == SPF_TYPE_RLFA
+           || spftree->type == SPF_TYPE_TI_LFA) {
                isis_spf_node_list_clear(&spftree->lfa.q_space);
                isis_spf_node_list_clear(&spftree->lfa.p_space);
        }
@@ -1429,6 +1437,9 @@ static void init_spt(struct isis_spftree *spftree, int mtid)
        list_delete_all_node(spftree->sadj_list);
        isis_vertex_queue_clear(&spftree->tents);
        isis_vertex_queue_clear(&spftree->paths);
+       isis_zebra_rlfa_unregister_all(spftree);
+       isis_rlfa_list_clear(spftree);
+       list_delete_all_node(spftree->lfa.remote.pc_spftrees);
        memset(&spftree->lfa.protection_counters, 0,
               sizeof(spftree->lfa.protection_counters));
 
@@ -1502,12 +1513,13 @@ static void spf_path_process(struct isis_spftree *spftree,
                priority = spf_prefix_priority(spftree, vertex);
                vertex->N.ip.priority = priority;
                if (vertex->depth == 1 || listcount(vertex->Adj_N) > 0) {
+                       struct isis_spftree *pre_spftree;
                        struct route_table *route_table;
                        bool allow_ecmp;
 
-                       if (spftree->type == SPF_TYPE_TI_LFA) {
-                               struct isis_spftree *pre_spftree;
-
+                       switch (spftree->type) {
+                       case SPF_TYPE_RLFA:
+                       case SPF_TYPE_TI_LFA:
                                if (priority
                                    > area->lfa_priority_limit[level - 1]) {
                                        if (IS_DEBUG_LFA)
@@ -1520,7 +1532,16 @@ static void spf_path_process(struct isis_spftree *spftree,
                                                                sizeof(buff)));
                                        return;
                                }
+                               break;
+                       default:
+                               break;
+                       }
 
+                       switch (spftree->type) {
+                       case SPF_TYPE_RLFA:
+                               isis_rlfa_check(spftree, vertex);
+                               return;
+                       case SPF_TYPE_TI_LFA:
                                if (isis_tilfa_check(spftree, vertex) != 0)
                                        return;
 
@@ -1529,7 +1550,8 @@ static void spf_path_process(struct isis_spftree *spftree,
                                allow_ecmp = area->lfa_load_sharing[level - 1];
                                pre_spftree->lfa.protection_counters
                                        .tilfa[vertex->N.ip.priority] += 1;
-                       } else {
+                               break;
+                       default:
                                route_table = spftree->route_table;
                                allow_ecmp = true;
 
@@ -1544,6 +1566,7 @@ static void spf_path_process(struct isis_spftree *spftree,
                                                spftree->lfa.protection_counters
                                                        .ecmp[priority] += 1;
                                }
+                               break;
                        }
 
                        isis_route_create(
@@ -1834,6 +1857,7 @@ int _isis_spf_schedule(struct isis_area *area, int level,
                        area->area_tag, level, diff, func, file, line);
        }
 
+       thread_cancel(&area->t_rlfa_rib_update);
        if (area->spf_delay_ietf[level - 1]) {
                /* Need to call schedule function also if spf delay is running
                 * to
index 5b6fcdaf6875ebb55630d8600a2b41ee21959d06..5b3aa593799bf347d609dd471bf96d82fc681f73 100644 (file)
@@ -31,6 +31,7 @@ struct isis_spftree;
 enum spf_type {
        SPF_TYPE_FORWARD = 1,
        SPF_TYPE_REVERSE,
+       SPF_TYPE_RLFA,
        SPF_TYPE_TI_LFA,
 };
 
index b7f326ca864692cf8477f2d5d09b73a25ebb3aa0..79dfa3e164be09412598d9bb86a1423d096f7267 100644 (file)
@@ -76,7 +76,9 @@ struct isis_vertex {
        struct list *parents;  /* list of parents for ECMP */
        struct hash *firsthops; /* first two hops to neighbor */
        uint64_t insert_counter;
+       uint8_t flags;
 };
+#define F_ISIS_VERTEX_LFA_PROTECTED    0x01
 
 /* Vertex Queue and associated functions */
 
@@ -349,6 +351,21 @@ struct isis_spftree {
                struct isis_spf_nodes p_space;
                struct isis_spf_nodes q_space;
 
+               /* Remote LFA related information. */
+               struct {
+                       /* List of RLFAs eligible to be installed. */
+                       struct rlfa_tree_head rlfas;
+
+                       /*
+                        * RLFA post-convergence SPTs (needed to activate RLFAs
+                        * once label information is received from LDP).
+                        */
+                       struct list *pc_spftrees;
+
+                       /* RLFA maximum metric (or zero if absent). */
+                       uint32_t max_metric;
+               } remote;
+
                /* Protection counters. */
                struct {
                        uint32_t lfa[SPF_PREFIX_PRIO_MAX];
index f08737c2c1a0b5b7fcae74add9435be4fc26b624..703532234ab55a0ffc1a7da93347e1a484b144d9 100644 (file)
@@ -47,6 +47,8 @@
 #include "isisd/isis_circuit.h"
 #include "isisd/isis_csm.h"
 #include "isisd/isis_lsp.h"
+#include "isisd/isis_spf.h"
+#include "isisd/isis_spf_private.h"
 #include "isisd/isis_route.h"
 #include "isisd/isis_zebra.h"
 #include "isisd/isis_adjacency.h"
@@ -540,6 +542,72 @@ void isis_zebra_redistribute_unset(afi_t afi, int type)
                                     type, 0, VRF_DEFAULT);
 }
 
+/**
+ * Register RLFA with LDP.
+ */
+int isis_zebra_rlfa_register(struct isis_spftree *spftree, struct rlfa *rlfa)
+{
+       struct isis_area *area = spftree->area;
+       struct zapi_rlfa_request zr = {};
+       int ret;
+
+       if (!zclient)
+               return 0;
+
+       zr.igp.vrf_id = area->isis->vrf_id;
+       zr.igp.protocol = ZEBRA_ROUTE_ISIS;
+       strlcpy(zr.igp.isis.area_tag, area->area_tag,
+               sizeof(zr.igp.isis.area_tag));
+       zr.igp.isis.spf.tree_id = spftree->tree_id;
+       zr.igp.isis.spf.level = spftree->level;
+       zr.igp.isis.spf.run_id = spftree->runcount;
+       zr.destination = rlfa->prefix;
+       zr.pq_address = rlfa->pq_address;
+
+       zlog_debug("ISIS-LFA: registering RLFA %pFX@%pI4 with LDP",
+                  &rlfa->prefix, &rlfa->pq_address);
+
+       ret = zclient_send_opaque_unicast(zclient, LDP_RLFA_REGISTER,
+                                         ZEBRA_ROUTE_LDP, 0, 0,
+                                         (const uint8_t *)&zr, sizeof(zr));
+       if (ret == ZCLIENT_SEND_FAILURE) {
+               zlog_warn("ISIS-LFA: failed to register RLFA with LDP");
+               return -1;
+       }
+
+       return 0;
+}
+
+/**
+ * Unregister all RLFAs from the given SPF tree with LDP.
+ */
+void isis_zebra_rlfa_unregister_all(struct isis_spftree *spftree)
+{
+       struct isis_area *area = spftree->area;
+       struct zapi_rlfa_igp igp = {};
+       int ret;
+
+       if (!zclient || spftree->type != SPF_TYPE_FORWARD
+           || CHECK_FLAG(spftree->flags, F_SPFTREE_NO_ADJACENCIES))
+               return;
+
+       if (IS_DEBUG_LFA)
+               zlog_debug("ISIS-LFA: unregistering all RLFAs with LDP");
+
+       igp.vrf_id = area->isis->vrf_id;
+       igp.protocol = ZEBRA_ROUTE_ISIS;
+       strlcpy(igp.isis.area_tag, area->area_tag, sizeof(igp.isis.area_tag));
+       igp.isis.spf.tree_id = spftree->tree_id;
+       igp.isis.spf.level = spftree->level;
+       igp.isis.spf.run_id = spftree->runcount;
+
+       ret = zclient_send_opaque_unicast(zclient, LDP_RLFA_UNREGISTER_ALL,
+                                         ZEBRA_ROUTE_LDP, 0, 0,
+                                         (const uint8_t *)&igp, sizeof(igp));
+       if (ret == ZCLIENT_SEND_FAILURE)
+               zlog_warn("ISIS-LFA: failed to unregister RLFA with LDP");
+}
+
 /* Label Manager Functions */
 
 /**
@@ -659,6 +727,7 @@ void isis_zebra_vrf_register(struct isis *isis)
 static void isis_zebra_connected(struct zclient *zclient)
 {
        zclient_send_reg_requests(zclient, VRF_DEFAULT);
+       zclient_register_opaque(zclient, LDP_RLFA_LABELS);
 }
 
 /*
@@ -670,6 +739,7 @@ static int isis_opaque_msg_handler(ZAPI_CALLBACK_ARGS)
        struct zapi_opaque_msg info;
        struct ldp_igp_sync_if_state state;
        struct ldp_igp_sync_announce announce;
+       struct zapi_rlfa_response rlfa;
        int ret = 0;
 
        s = zclient->ibuf;
@@ -685,6 +755,10 @@ static int isis_opaque_msg_handler(ZAPI_CALLBACK_ARGS)
                STREAM_GET(&announce, s, sizeof(announce));
                ret = isis_ldp_sync_announce_update(announce);
                break;
+       case LDP_RLFA_LABELS:
+               STREAM_GET(&rlfa, s, sizeof(rlfa));
+               isis_rlfa_process_ldp_response(&rlfa);
+               break;
        default:
                break;
        }
@@ -704,6 +778,7 @@ static int isis_zebra_client_close_notify(ZAPI_CALLBACK_ARGS)
                return -1;
 
        isis_ldp_sync_handle_client_close(&info);
+       isis_ldp_rlfa_handle_client_close(&info);
 
        return ret;
 }
@@ -742,6 +817,7 @@ void isis_zebra_init(struct thread_master *master, int instance)
 
 void isis_zebra_stop(void)
 {
+       zclient_unregister_opaque(zclient, LDP_RLFA_LABELS);
        zclient_stop(zclient_sync);
        zclient_free(zclient_sync);
        zclient_stop(zclient);
index c5c52a6bc6bd262deca9944cd3108d0e86bef968..b44ec4f085484708d00ff670c29ee507237efd9a 100644 (file)
@@ -59,6 +59,8 @@ void isis_zebra_send_adjacency_sid(int cmd, const struct sr_adjacency *sra);
 int isis_distribute_list_update(int routetype);
 void isis_zebra_redistribute_set(afi_t afi, int type);
 void isis_zebra_redistribute_unset(afi_t afi, int type);
+int isis_zebra_rlfa_register(struct isis_spftree *spftree, struct rlfa *rlfa);
+void isis_zebra_rlfa_unregister_all(struct isis_spftree *spftree);
 bool isis_zebra_label_manager_ready(void);
 int isis_zebra_label_manager_connect(void);
 int isis_zebra_request_label_range(uint32_t base, uint32_t chunk_size);
index 827e022bafd063ac547a6c2ee08d687449ef9ab0..eabebab4e000c3bc2b4bf5611ce88a6b987bc0b8 100644 (file)
@@ -32,6 +32,7 @@
 #include "if.h"
 #include "hash.h"
 #include "filter.h"
+#include "plist.h"
 #include "stream.h"
 #include "prefix.h"
 #include "table.h"
@@ -485,6 +486,7 @@ void isis_area_destroy(struct isis_area *area)
        thread_cancel(&area->t_tick);
        thread_cancel(&area->t_lsp_refresh[0]);
        thread_cancel(&area->t_lsp_refresh[1]);
+       thread_cancel(&area->t_rlfa_rib_update);
 
        thread_cancel_event(master, area);
 
@@ -649,6 +651,34 @@ void isis_filter_update(struct access_list *access)
        }
 }
 
+void isis_prefix_list_update(struct prefix_list *plist)
+{
+       struct isis *isis;
+       struct isis_area *area;
+       struct listnode *node, *anode;
+
+       for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) {
+               for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, area)) {
+                       for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS;
+                            level++) {
+                               const char *plist_name =
+                                       prefix_list_name(plist);
+
+                               if (!area->rlfa_plist_name[level - 1])
+                                       continue;
+
+                               if (!strmatch(area->rlfa_plist_name[level - 1],
+                                             plist_name))
+                                       continue;
+
+                               area->rlfa_plist[level - 1] =
+                                       prefix_list_lookup(AFI_IP, plist_name);
+                               lsp_regenerate_schedule(area, area->is_type, 0);
+                       }
+               }
+       }
+}
+
 #ifdef FABRICD
 static void area_set_mt_enabled(struct isis_area *area, uint16_t mtid,
                                bool enabled)
index 4618d14af3db0580189fdf1eb7df8f9ffa5ac06c..9b903eed48098be08b8d638fbfa855fee46420c6 100644 (file)
@@ -131,6 +131,7 @@ struct isis_area {
        struct thread *t_tick; /* LSP walker */
        struct thread *t_lsp_refresh[ISIS_LEVELS];
        struct timeval last_lsp_refresh_event[ISIS_LEVELS];
+       struct thread *t_rlfa_rib_update;
        /* t_lsp_refresh is used in two ways:
         * a) regular refresh of LSPs
         * b) (possibly throttled) updates to LSPs
@@ -197,6 +198,9 @@ struct isis_area {
        size_t lfa_load_sharing[ISIS_LEVELS];
        enum spf_prefix_priority lfa_priority_limit[ISIS_LEVELS];
        struct lfa_tiebreaker_tree_head lfa_tiebreakers[ISIS_LEVELS];
+       char *rlfa_plist_name[ISIS_LEVELS];
+       struct prefix_list *rlfa_plist[ISIS_LEVELS];
+       size_t rlfa_protected_links[ISIS_LEVELS];
        size_t tilfa_protected_links[ISIS_LEVELS];
        /* Counters */
        uint32_t circuit_state_changes;
@@ -240,6 +244,7 @@ struct isis_area *isis_area_lookup_by_vrf(const char *area_tag,
 int isis_area_get(struct vty *vty, const char *area_tag);
 void isis_area_destroy(struct isis_area *area);
 void isis_filter_update(struct access_list *access);
+void isis_prefix_list_update(struct prefix_list *plist);
 void print_debug(struct vty *, int, int);
 struct isis_lsp *lsp_for_arg(struct lspdb_head *head, const char *argv,
                             struct isis *isis);