]> git.puffer.fish Git - matthieu/frr.git/commitdiff
isisd: add support for classic LFA (RFC 5286)
authorRenato Westphal <renato@opensourcerouting.org>
Sat, 7 Nov 2020 00:15:39 +0000 (21:15 -0300)
committerRenato Westphal <renato@opensourcerouting.org>
Tue, 24 Nov 2020 23:15:52 +0000 (20:15 -0300)
Signed-off-by: Renato Westphal <renato@opensourcerouting.org>
16 files changed:
doc/user/isisd.rst
isisd/isis_circuit.c
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/isisd.c
isisd/isisd.h

index 0bdc8121aa1682f55c31a2060e78cbc0030ac14a..a470f4e809073296f9af92799f4afdfb426f413b 100644 (file)
@@ -175,6 +175,35 @@ ISIS Timer
 
    Set minimum interval between consecutive SPF calculations in seconds.
 
+.. _isis-fast-reroute:
+
+ISIS Fast-Reroute
+=================
+
+.. index:: spf prefix-priority [critical | high | medium] WORD
+.. clicmd:: spf prefix-priority [critical | high | medium] WORD
+
+.. index:: spf prefix-priority [critical | high | medium] WORD
+.. clicmd:: no spf prefix-priority [critical | high | medium] [WORD]
+
+   Assign a priority to the prefixes that match the specified access-list.
+
+.. index:: fast-reroute priority-limit [critical | high | medium] [level-1 | level-2]
+.. clicmd:: [no] fast-reroute priority-limit [critical | high | medium] [level-1 | level-2]
+
+   Limit LFA backup computation up to the specified prefix priority.
+
+.. index:: fast-reroute lfa tiebreaker [downstream | lowest-backup-metric | node-protecting] index (1-255) [level-1 | level-2]
+.. clicmd:: [no] fast-reroute lfa tiebreaker [downstream | lowest-backup-metric | node-protecting] index (1-255) [level-1 | level-2]
+
+   Configure a tie-breaker for multiple LFA backups. Lower indexes are processed
+   first.
+
+.. index:: fast-reroute load-sharing disable [level-1 | level-2]
+.. clicmd:: [no] fast-reroute load-sharing disable [level-1 | level-2]
+
+   Disable load sharing across multiple LFA backups.
+
 .. _isis-region:
 
 ISIS region
@@ -356,6 +385,16 @@ ISIS interface
    Enable or disable :rfc:`5303` Three-Way Handshake for P2P adjacencies.
    Three-Way Handshake is enabled by default.
 
+.. index:: isis fast-reroute lfa [level-1 | level-2]
+.. clicmd:: [no] isis fast-reroute lfa [level-1 | level-2]
+
+   Enable per-prefix LFA fast reroute link protection.
+
+.. index:: isis fast-reroute lfa [level-1 | level-2] exclude interface IFNAME
+.. clicmd:: [no] isis fast-reroute lfa [level-1 | level-2] exclude interface IFNAME
+
+   Exclude an interface from the LFA backup nexthop computation.
+
 .. index:: isis fast-reroute ti-lfa [level-1|level-2] [node-protection]
 .. clicmd:: [no] isis fast-reroute ti-lfa [level-1|level-2] [node-protection]
 
index e3c70264f8089a1e6b544291cfe7fb00cff0a46e..2580a7c43a1d5062592b800d82806d15da277678 100644 (file)
@@ -140,6 +140,8 @@ struct isis_circuit *isis_circuit_new(struct isis *isis)
 #endif /* ifndef FABRICD */
 
        circuit_mt_init(circuit);
+       isis_lfa_excluded_ifaces_init(circuit, ISIS_LEVEL1);
+       isis_lfa_excluded_ifaces_init(circuit, ISIS_LEVEL2);
 
        QOBJ_REG(circuit, isis_circuit);
 
@@ -156,6 +158,8 @@ void isis_circuit_del(struct isis_circuit *circuit)
        isis_circuit_if_unbind(circuit, circuit->interface);
 
        circuit_mt_finish(circuit);
+       isis_lfa_excluded_ifaces_clear(circuit, ISIS_LEVEL1);
+       isis_lfa_excluded_ifaces_clear(circuit, ISIS_LEVEL2);
 
        /* and lastly the circuit itself */
        XFREE(MTYPE_ISIS_CIRCUIT, circuit);
index b4b03bf6b902271c997d85adc77a78751aa4a797..e736d8fb1fb00980f55c842253b6e3898c91e973 100644 (file)
@@ -141,6 +141,8 @@ struct isis_circuit {
        bool disable_threeway_adj;
        struct bfd_info *bfd_info;
        struct ldp_sync_info *ldp_sync_info;
+       bool lfa_protection[ISIS_LEVELS];
+       struct hash *lfa_excluded_ifaces[ISIS_LEVELS];
        bool tilfa_protection[ISIS_LEVELS];
        bool tilfa_node_protection[ISIS_LEVELS];
        /*
index 13822a9170004a85160f632a93e2efa8a367e8bf..7d516dc933e193d98a585fa49cd76d49f39216eb 100644 (file)
@@ -40,6 +40,8 @@
 #include "isisd/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");
 
 static inline int isis_spf_node_compare(const struct isis_spf_node *a,
                                        const struct isis_spf_node *b)
@@ -119,6 +121,185 @@ struct isis_spf_node *isis_spf_node_find(const struct isis_spf_nodes *nodes,
        return RB_FIND(isis_spf_nodes, nodes, &node);
 }
 
+/**
+ * LFA tiebreaker RB-tree comparison function.
+ *
+ * @param a    First LFA tiebreaker
+ * @param b    Second LFA tiebreaker
+ *
+ * @return     -1 (a < b), 0 (a == b) or +1 (a > b)
+ */
+int lfa_tiebreaker_cmp(const struct lfa_tiebreaker *a,
+                      const struct lfa_tiebreaker *b)
+{
+       if (a->index < b->index)
+               return -1;
+       if (a->index > b->index)
+               return 1;
+
+       return a->type - b->type;
+}
+
+/**
+ * Initialize list of LFA tie-breakers.
+ *
+ * @param area         IS-IS area
+ * @param level                IS-IS level
+ */
+void isis_lfa_tiebreakers_init(struct isis_area *area, int level)
+{
+       lfa_tiebreaker_tree_init(&area->lfa_tiebreakers[level - 1]);
+}
+
+/**
+ * Clear list of LFA tie-breakers, releasing all allocated memory.
+ *
+ * @param area         IS-IS area
+ * @param level                IS-IS level
+ */
+void isis_lfa_tiebreakers_clear(struct isis_area *area, int level)
+{
+       while (lfa_tiebreaker_tree_count(&area->lfa_tiebreakers[level - 1])
+              > 0) {
+               struct lfa_tiebreaker *tie_b;
+
+               tie_b = lfa_tiebreaker_tree_first(
+                       &area->lfa_tiebreakers[level - 1]);
+               isis_lfa_tiebreaker_delete(area, level, tie_b);
+       }
+}
+
+/**
+ * Add new LFA tie-breaker to list of LFA tie-breakers.
+ *
+ * @param area         IS-IS area
+ * @param level                IS-IS level
+ * @param index                LFA tie-breaker index
+ * @param type         LFA tie-breaker type
+ *
+ * @return             Pointer to new LFA tie-breaker structure.
+ */
+struct lfa_tiebreaker *isis_lfa_tiebreaker_add(struct isis_area *area,
+                                              int level, uint8_t index,
+                                              enum lfa_tiebreaker_type type)
+{
+       struct lfa_tiebreaker *tie_b;
+
+       tie_b = XCALLOC(MTYPE_ISIS_LFA_TIEBREAKER, sizeof(*tie_b));
+       tie_b->index = index;
+       tie_b->type = type;
+       tie_b->area = area;
+       lfa_tiebreaker_tree_add(&area->lfa_tiebreakers[level - 1], tie_b);
+
+       return tie_b;
+}
+
+/**
+ * Remove LFA tie-breaker from list of LFA tie-breakers.
+ *
+ * @param area         IS-IS area
+ * @param level                IS-IS level
+ * @param tie_b                Pointer to LFA tie-breaker structure
+ */
+void isis_lfa_tiebreaker_delete(struct isis_area *area, int level,
+                               struct lfa_tiebreaker *tie_b)
+{
+       lfa_tiebreaker_tree_del(&area->lfa_tiebreakers[level - 1], tie_b);
+       XFREE(MTYPE_ISIS_LFA_TIEBREAKER, tie_b);
+}
+
+static bool lfa_excl_interface_hash_cmp(const void *value1, const void *value2)
+{
+       return strmatch(value1, value2);
+}
+
+static unsigned int lfa_excl_interface_hash_make(const void *value)
+{
+       return string_hash_make(value);
+}
+
+static void *lfa_excl_interface_hash_alloc(void *p)
+{
+       return XSTRDUP(MTYPE_ISIS_LFA_EXCL_IFACE, p);
+}
+
+static void lfa_excl_interface_hash_free(void *arg)
+{
+       XFREE(MTYPE_ISIS_LFA_EXCL_IFACE, arg);
+}
+
+/**
+ * Initialize hash table of LFA excluded interfaces.
+ *
+ * @param circuit      IS-IS interface
+ * @param level                IS-IS level
+ */
+void isis_lfa_excluded_ifaces_init(struct isis_circuit *circuit, int level)
+{
+       circuit->lfa_excluded_ifaces[level - 1] = hash_create(
+               lfa_excl_interface_hash_make, lfa_excl_interface_hash_cmp,
+               "LFA Excluded Interfaces");
+}
+
+/**
+ * Clear hash table of LFA excluded interfaces, releasing all allocated memory.
+ *
+ * @param nodes                List of SPF nodes
+ */
+void isis_lfa_excluded_ifaces_clear(struct isis_circuit *circuit, int level)
+{
+       hash_clean(circuit->lfa_excluded_ifaces[level - 1],
+                  lfa_excl_interface_hash_free);
+}
+
+/**
+ * Add new interface to hash table of excluded interfaces.
+ *
+ * @param circuit      IS-IS interface
+ * @param level                IS-IS level
+ * @param ifname       Excluded interface name
+ */
+void isis_lfa_excluded_iface_add(struct isis_circuit *circuit, int level,
+                                const char *ifname)
+{
+       hash_get(circuit->lfa_excluded_ifaces[level - 1], (char *)ifname,
+                lfa_excl_interface_hash_alloc);
+}
+
+/**
+ * Remove interface from hash table of excluded interfaces.
+ *
+ * @param circuit      IS-IS interface
+ * @param level                IS-IS level
+ * @param ifname       Excluded interface name
+ */
+void isis_lfa_excluded_iface_delete(struct isis_circuit *circuit, int level,
+                                   const char *ifname)
+{
+       char *found;
+
+       found = hash_lookup(circuit->lfa_excluded_ifaces[level - 1],
+                           (char *)ifname);
+       if (found) {
+               hash_release(circuit->lfa_excluded_ifaces[level - 1], found);
+               lfa_excl_interface_hash_free(found);
+       }
+}
+
+/**
+ * Lookup excluded interface.
+ *
+ * @param circuit      IS-IS interface
+ * @param level                IS-IS level
+ * @param ifname       Excluded interface name
+ */
+bool isis_lfa_excluded_iface_check(struct isis_circuit *circuit, int level,
+                                  const char *ifname)
+{
+       return hash_lookup(circuit->lfa_excluded_ifaces[level - 1],
+                          (char *)ifname);
+}
+
 /**
  * Check if a given IS-IS adjacency needs to be excised when computing the SPF
  * post-convergence tree.
@@ -533,8 +714,9 @@ static int tilfa_build_repair_list(struct isis_spftree *spftree_pc,
                listnode_add_head(repair_list, &sid_pnode);
 
                /* Apply repair list. */
-               if (listcount(repair_list)
-                   > spftree_pc->area->srdb.config.msd) {
+               if (spftree_pc->area->srdb.config.msd
+                   && listcount(repair_list)
+                              > spftree_pc->area->srdb.config.msd) {
                        zlog_warn(
                                "ISIS-LFA: list of repair segments exceeds locally configured MSD (%u > %u)",
                                listcount(repair_list),
@@ -618,36 +800,25 @@ spf_adj_check_is_affected(const struct isis_spf_adj *sadj,
        return false;
 }
 
-/* Check if the given SPF vertex needs LFA protection. */
-static bool lfa_check_needs_protection(const struct isis_spftree *spftree_pc,
-                                      const struct isis_vertex *vertex)
+/* Check if the given vertex is affected by a given local failure. */
+static bool
+spf_vertex_check_is_affected(const struct isis_vertex *vertex,
+                            const uint8_t *root_sysid,
+                            const struct lfa_protected_resource *resource)
 {
-       struct isis_vertex *vertex_old;
+       struct isis_vertex_adj *vadj;
        struct listnode *node;
        size_t affected_nhs = 0;
-       struct isis_vertex_adj *vadj;
 
        /* Local routes don't need protection. */
        if (VTYPE_IP(vertex->type) && vertex->depth == 1)
                return false;
 
-       /* Only local adjacencies need Adj-SID protection. */
-       if (VTYPE_IS(vertex->type)
-           && !isis_adj_find(spftree_pc->area, spftree_pc->level,
-                             vertex->N.id))
-               return false;
-
-       vertex_old = isis_find_vertex(&spftree_pc->lfa.old.spftree->paths,
-                                     &vertex->N, vertex->type);
-       if (!vertex_old)
-               return false;
-
-       for (ALL_LIST_ELEMENTS_RO(vertex_old->Adj_N, node, vadj)) {
+       for (ALL_LIST_ELEMENTS_RO(vertex->Adj_N, node, vadj)) {
                struct isis_spf_adj *sadj = vadj->sadj;
 
-               if (spf_adj_check_is_affected(
-                           sadj, &spftree_pc->lfa.protected_resource,
-                           spftree_pc->sysid, false))
+               if (spf_adj_check_is_affected(sadj, resource, root_sysid,
+                                             false))
                        affected_nhs++;
        }
 
@@ -655,12 +826,34 @@ static bool lfa_check_needs_protection(const struct isis_spftree *spftree_pc,
         * No need to compute backup paths for ECMP routes, except if all
         * primary nexthops share the same broadcast interface.
         */
-       if (listcount(vertex_old->Adj_N) == affected_nhs)
+       if (listcount(vertex->Adj_N) == affected_nhs)
                return true;
 
        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)
+{
+       struct isis_vertex *vertex_old;
+
+       /* Only local adjacencies need Adj-SID protection. */
+       if (VTYPE_IS(vertex->type)
+           && !isis_adj_find(spftree_pc->area, spftree_pc->level,
+                             vertex->N.id))
+               return false;
+
+       vertex_old = isis_find_vertex(&spftree_pc->lfa.old.spftree->paths,
+                                     &vertex->N, vertex->type);
+       if (!vertex_old)
+               return false;
+
+       return spf_vertex_check_is_affected(
+               vertex_old, spftree_pc->sysid,
+               &spftree_pc->lfa.protected_resource);
+}
+
 /**
  * Check if the given SPF vertex needs protection and, if so, compute and
  * install the corresponding repair paths.
@@ -670,7 +863,8 @@ static bool lfa_check_needs_protection(const struct isis_spftree *spftree_pc,
  *
  * @return             0 if the vertex needs to be protected, -1 otherwise
  */
-int isis_lfa_check(struct isis_spftree *spftree_pc, struct isis_vertex *vertex)
+int isis_tilfa_check(struct isis_spftree *spftree_pc,
+                    struct isis_vertex *vertex)
 {
        struct isis_spf_nodes used_pnodes;
        char buf[VID2STR_BUFFER];
@@ -683,7 +877,7 @@ int isis_lfa_check(struct isis_spftree *spftree_pc, struct isis_vertex *vertex)
        if (IS_DEBUG_LFA)
                vid2string(vertex, buf, sizeof(buf));
 
-       if (!lfa_check_needs_protection(spftree_pc, vertex)) {
+       if (!tilfa_check_needs_protection(spftree_pc, vertex)) {
                if (IS_DEBUG_LFA)
                        zlog_debug(
                                "ISIS-LFA: %s %s unaffected by %s",
@@ -1044,20 +1238,530 @@ int isis_spf_run_neighbors(struct isis_spftree *spftree)
        return 0;
 }
 
+/* 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,
+                                    uint32_t *distance)
+{
+       struct isis_vertex *vertex, *vertex_best = NULL;
+
+       switch (spftree->family) {
+       case AF_INET:
+               for (int vtype = VTYPE_IPREACH_INTERNAL;
+                    vtype <= VTYPE_IPREACH_TE; vtype++) {
+                       vertex = isis_find_vertex(
+                               &spftree->paths, &vertex_N->N.ip.p.dest, vtype);
+                       if (!vertex)
+                               continue;
+
+                       /* Pick vertex with the best metric. */
+                       if (!vertex_best || vertex_best->d_N > vertex->d_N)
+                               vertex_best = vertex;
+               }
+               break;
+       case AF_INET6:
+               for (int vtype = VTYPE_IP6REACH_INTERNAL;
+                    vtype <= VTYPE_IP6REACH_EXTERNAL; vtype++) {
+                       vertex = isis_find_vertex(
+                               &spftree->paths, &vertex_N->N.ip.p.dest, vtype);
+                       if (!vertex)
+                               continue;
+
+                       /* Pick vertex with the best metric. */
+                       if (!vertex_best || vertex_best->d_N > vertex->d_N)
+                               vertex_best = vertex;
+               }
+               break;
+       default:
+               break;
+       }
+
+       if (!vertex_best)
+               return -1;
+
+       assert(VTYPE_IP(vertex_best->type));
+       vertex_best = listnode_head(vertex_best->parents);
+       *distance = vertex_best->d_N;
+
+       return 0;
+}
+
+/* Calculate the distance from the root node to the given node. */
+static int lfa_calc_dist_node(struct isis_spftree *spftree,
+                             const uint8_t *sysid, uint32_t *distance)
+{
+       struct isis_vertex *vertex, *vertex_best = NULL;
+
+       for (int vtype = VTYPE_PSEUDO_IS; vtype <= VTYPE_NONPSEUDO_TE_IS;
+            vtype++) {
+               vertex = isis_find_vertex(&spftree->paths, sysid, vtype);
+               if (!vertex)
+                       continue;
+
+               /* Pick vertex with the best metric. */
+               if (!vertex_best || vertex_best->d_N > vertex->d_N)
+                       vertex_best = vertex;
+       }
+
+       if (!vertex_best)
+               return -1;
+
+       *distance = vertex_best->d_N;
+
+       return 0;
+}
+
+/*
+ * Check loop-free criterion (RFC 5286's inequality 1):
+ * - Dist_opt(N, D) < Dist_opt(N, S) + Dist_opt(S, D)
+ */
+static bool clfa_loop_free_check(struct isis_spftree *spftree,
+                                struct isis_vertex *vertex_S_D,
+                                struct isis_spf_adj *sadj_primary,
+                                struct isis_spf_adj *sadj_N,
+                                uint32_t *lfa_metric)
+{
+       struct isis_spf_node *node_N;
+       uint32_t dist_N_D;
+       uint32_t dist_N_S;
+       uint32_t dist_S_D;
+
+       node_N = isis_spf_node_find(&spftree->adj_nodes, sadj_N->id);
+       assert(node_N);
+
+       /* Distance from N to D. */
+       if (lfa_calc_dist_destination(node_N->lfa.spftree, vertex_S_D,
+                                     &dist_N_D)
+           != 0)
+               return false;
+
+       /* Distance from N to S (or PN). */
+       if (CHECK_FLAG(sadj_primary->flags, F_ISIS_SPF_ADJ_BROADCAST)) {
+               static uint8_t pn_sysid[ISIS_SYS_ID_LEN + 1];
+
+               memcpy(pn_sysid, sadj_primary->id, ISIS_SYS_ID_LEN + 1);
+               if (lfa_calc_dist_node(node_N->lfa.spftree, pn_sysid, &dist_N_S)
+                   != 0)
+                       return false;
+       } else {
+               static uint8_t root_sysid[ISIS_SYS_ID_LEN + 1];
+
+               memcpy(root_sysid, spftree->sysid, ISIS_SYS_ID_LEN);
+               LSP_PSEUDO_ID(root_sysid) = 0;
+               if (lfa_calc_dist_node(node_N->lfa.spftree, root_sysid,
+                                      &dist_N_S)
+                   != 0)
+                       return false;
+       }
+
+       /* Distance from S (or PN) to D. */
+       vertex_S_D = listnode_head(vertex_S_D->parents);
+       dist_S_D = vertex_S_D->d_N;
+       if (CHECK_FLAG(sadj_primary->flags, F_ISIS_SPF_ADJ_BROADCAST))
+               dist_S_D -= sadj_primary->metric;
+
+       if (IS_DEBUG_LFA)
+               zlog_debug("ISIS-LFA: loop-free check: %u < %u + %u", dist_N_D,
+                          dist_N_S, dist_S_D);
+
+       if (dist_N_D < (dist_N_S + dist_S_D)) {
+               *lfa_metric = sadj_N->metric + dist_N_D;
+               return true;
+       }
+
+       return false;
+}
+
+/*
+ * Check loop-free criterion (RFC 5286's inequality 2):
+ * - Distance_opt(N, D) < Distance_opt(S, D)
+ */
+static bool clfa_downstream_check(struct isis_spftree *spftree,
+                                 struct isis_vertex *vertex_S_D,
+                                 struct isis_spf_adj *sadj_N)
+{
+       struct isis_spf_node *node_N;
+       uint32_t dist_N_D;
+       uint32_t dist_S_D;
+
+       node_N = isis_spf_node_find(&spftree->adj_nodes, sadj_N->id);
+       assert(node_N);
+
+       /* Distance from N to D. */
+       if (lfa_calc_dist_destination(node_N->lfa.spftree, vertex_S_D,
+                                     &dist_N_D)
+           != 0)
+               return false;
+
+       /* Distance from S (or PN) to D. */
+       vertex_S_D = listnode_head(vertex_S_D->parents);
+       dist_S_D = vertex_S_D->d_N;
+
+       if (IS_DEBUG_LFA)
+               zlog_debug("ISIS-LFA: downstream check: %u < %u", dist_N_D,
+                          dist_S_D);
+
+       if (dist_N_D < dist_S_D)
+               return true;
+
+       return false;
+}
+
+/*
+ * Check loop-free criterion (RFC 5286's inequality 3):
+ * - Dist_opt(N, D) < Dist_opt(N, E) + Dist_opt(E, D)
+ */
+static bool clfa_node_protecting_check(struct isis_spftree *spftree,
+                                      struct isis_vertex *vertex_S_D,
+                                      struct isis_spf_adj *sadj_N,
+                                      struct isis_spf_adj *sadj_E)
+{
+       struct isis_spf_node *node_N, *node_E;
+       uint32_t dist_N_D;
+       uint32_t dist_N_E;
+       uint32_t dist_E_D;
+
+       node_N = isis_spf_node_find(&spftree->adj_nodes, sadj_N->id);
+       assert(node_N);
+       node_E = isis_spf_node_find(&spftree->adj_nodes, sadj_E->id);
+       assert(node_E);
+
+       /* Distance from N to D. */
+       if (lfa_calc_dist_destination(node_N->lfa.spftree, vertex_S_D,
+                                     &dist_N_D)
+           != 0)
+               return false;
+
+       /* Distance from N to E. */
+       if (lfa_calc_dist_node(node_N->lfa.spftree, node_E->sysid, &dist_N_E)
+           != 0)
+               return false;
+
+       /* Distance from E to D. */
+       if (lfa_calc_dist_destination(node_E->lfa.spftree, vertex_S_D,
+                                     &dist_E_D)
+           != 0)
+               return false;
+
+       if (IS_DEBUG_LFA)
+               zlog_debug("ISIS-LFA: node protecting check: %u < %u + %u",
+                          dist_N_D, dist_N_E, dist_E_D);
+
+       return (dist_N_D < (dist_N_E + dist_E_D));
+}
+
+static struct list *
+isis_lfa_tiebreakers(struct isis_area *area, struct isis_circuit *circuit,
+                    struct isis_spftree *spftree,
+                    struct lfa_protected_resource *resource,
+                    struct isis_vertex *vertex,
+                    struct isis_spf_adj *sadj_primary, struct list *lfa_list)
+{
+       struct lfa_tiebreaker *tie_b;
+       int level = spftree->level;
+       struct list *filtered_lfa_list;
+       struct list *tent_lfa_list;
+
+       filtered_lfa_list = list_dup(lfa_list);
+       filtered_lfa_list->del = NULL;
+
+       if (listcount(filtered_lfa_list) == 1)
+               return filtered_lfa_list;
+
+       /* Check tiebreakers in ascending order by index. */
+       frr_each (lfa_tiebreaker_tree, &area->lfa_tiebreakers[level - 1],
+                 tie_b) {
+               struct isis_vertex_adj *lfa;
+               struct listnode *node, *nnode;
+               uint32_t best_metric = UINT32_MAX;
+
+               tent_lfa_list = list_dup(filtered_lfa_list);
+
+               switch (tie_b->type) {
+               case LFA_TIEBREAKER_DOWNSTREAM:
+                       for (ALL_LIST_ELEMENTS(tent_lfa_list, node, nnode,
+                                              lfa)) {
+                               if (clfa_downstream_check(spftree, vertex,
+                                                         lfa->sadj))
+                                       continue;
+
+                               if (IS_DEBUG_LFA)
+                                       zlog_debug(
+                                               "ISIS-LFA: LFA %s doesn't satisfy the downstream condition",
+                                               print_sys_hostname(
+                                                       lfa->sadj->id));
+                               listnode_delete(tent_lfa_list, lfa);
+                       }
+                       break;
+               case LFA_TIEBREAKER_LOWEST_METRIC:
+                       /* Find the best metric first. */
+                       for (ALL_LIST_ELEMENTS_RO(tent_lfa_list, node, lfa)) {
+                               if (lfa->lfa_metric < best_metric)
+                                       best_metric = lfa->lfa_metric;
+                       }
+
+                       /* Remove LFAs that don't have the best metric. */
+                       for (ALL_LIST_ELEMENTS(tent_lfa_list, node, nnode,
+                                              lfa)) {
+                               if (lfa->lfa_metric == best_metric)
+                                       continue;
+
+                               if (IS_DEBUG_LFA)
+                                       zlog_debug(
+                                               "ISIS-LFA: LFA %s doesn't have the lowest cost metric",
+                                               print_sys_hostname(
+                                                       lfa->sadj->id));
+                               listnode_delete(tent_lfa_list, lfa);
+                       }
+                       break;
+               case LFA_TIEBREAKER_NODE_PROTECTING:
+                       for (ALL_LIST_ELEMENTS(tent_lfa_list, node, nnode,
+                                              lfa)) {
+                               if (clfa_node_protecting_check(spftree, vertex,
+                                                              lfa->sadj,
+                                                              sadj_primary))
+                                       continue;
+
+                               if (IS_DEBUG_LFA)
+                                       zlog_debug(
+                                               "ISIS-LFA: LFA %s doesn't provide node protection",
+                                               print_sys_hostname(
+                                                       lfa->sadj->id));
+                               listnode_delete(tent_lfa_list, lfa);
+                       }
+                       break;
+               }
+
+               /*
+                * Decide what to do next based on the number of remaining LFAs.
+                */
+               switch (listcount(tent_lfa_list)) {
+               case 0:
+                       /*
+                        * Ignore this tie-breaker since it excluded all LFAs.
+                        * Move on to the next one (if any).
+                        */
+                       list_delete(&tent_lfa_list);
+                       break;
+               case 1:
+                       /* Finish tie-breaking once we get a single LFA. */
+                       list_delete(&filtered_lfa_list);
+                       filtered_lfa_list = tent_lfa_list;
+                       return filtered_lfa_list;
+               default:
+                       /*
+                        * We still have two or more LFAs. Move on to the next
+                        * tie-breaker (if any).
+                        */
+                       list_delete(&filtered_lfa_list);
+                       filtered_lfa_list = tent_lfa_list;
+                       break;
+               }
+       }
+
+       return filtered_lfa_list;
+}
+
+void isis_lfa_compute(struct isis_area *area, struct isis_circuit *circuit,
+                     struct isis_spftree *spftree,
+                     struct lfa_protected_resource *resource)
+{
+       struct isis_vertex *vertex;
+       struct listnode *vnode, *snode;
+       int level = spftree->level;
+
+       resource->type = LFA_LINK_PROTECTION;
+
+       for (ALL_QUEUE_ELEMENTS_RO(&spftree->paths, vnode, vertex)) {
+               struct list *lfa_list;
+               struct list *filtered_lfa_list;
+               struct isis_spf_adj *sadj_N;
+               struct isis_vertex_adj *vadj_primary;
+               struct isis_spf_adj *sadj_primary;
+               bool allow_ecmp;
+               uint32_t best_metric = UINT32_MAX;
+               char buf[VID2STR_BUFFER];
+
+               if (!VTYPE_IP(vertex->type))
+                       continue;
+
+               vid2string(vertex, buf, sizeof(buf));
+
+               if (!spf_vertex_check_is_affected(vertex, spftree->sysid,
+                                                 resource)) {
+                       if (IS_DEBUG_LFA)
+                               zlog_debug(
+                                       "ISIS-LFA: route unaffected by %s",
+                                       lfa_protected_resource2str(resource));
+                       continue;
+               }
+
+               if (IS_DEBUG_LFA)
+                       zlog_debug("ISIS-LFA: checking %s %s w.r.t %s",
+                                  vtype2string(vertex->type), buf,
+                                  lfa_protected_resource2str(resource));
+
+               if (vertex->N.ip.priority
+                   > area->lfa_priority_limit[level - 1]) {
+                       if (IS_DEBUG_LFA)
+                               zlog_debug(
+                                       "ISIS-LFA: skipping computing LFAs due to low prefix priority");
+                       continue;
+               }
+
+               vadj_primary = listnode_head(vertex->Adj_N);
+               sadj_primary = vadj_primary->sadj;
+
+               /*
+                * Loop over list of SPF adjacencies and compute a list of
+                * preliminary LFAs.
+                */
+               lfa_list = list_new();
+               lfa_list->del = isis_vertex_adj_free;
+               for (ALL_LIST_ELEMENTS_RO(spftree->sadj_list, snode, sadj_N)) {
+                       uint32_t lfa_metric;
+                       struct isis_vertex_adj *lfa;
+                       struct isis_prefix_sid *psid = NULL;
+                       bool last_hop = false;
+
+                       /* Skip pseudonodes. */
+                       if (LSP_PSEUDO_ID(sadj_N->id))
+                               continue;
+
+                       /*
+                        * Skip nexthops that are along a link whose cost is
+                        * infinite.
+                        */
+                       if (CHECK_FLAG(sadj_N->flags,
+                                      F_ISIS_SPF_ADJ_METRIC_INFINITY))
+                               continue;
+
+                       /* Skip nexthops that have the overload bit set. */
+                       if (spftree->mtid != ISIS_MT_IPV4_UNICAST) {
+                               struct isis_mt_router_info *mt_router_info;
+
+                               mt_router_info =
+                                       isis_tlvs_lookup_mt_router_info(
+                                               sadj_N->lsp->tlvs,
+                                               spftree->mtid);
+                               if (mt_router_info && mt_router_info->overload)
+                                       continue;
+                       } else if (ISIS_MASK_LSP_OL_BIT(
+                                          sadj_N->lsp->hdr.lsp_bits))
+                               continue;
+
+                       /* Skip primary nexthop. */
+                       if (spf_adj_check_is_affected(sadj_N, resource, NULL,
+                                                     false))
+                               continue;
+
+                       /* Skip excluded interfaces as per the configuration. */
+                       if (circuit
+                           && isis_lfa_excluded_iface_check(
+                                      circuit, level,
+                                      sadj_N->adj->circuit->interface->name))
+                               continue;
+
+                       if (IS_DEBUG_LFA)
+                               zlog_debug(
+                                       "ISIS-LFA: checking candidate LFA %s",
+                                       print_sys_hostname(sadj_N->id));
+
+                       /* Check loop-free criterion. */
+                       if (!clfa_loop_free_check(spftree, vertex, sadj_primary,
+                                                 sadj_N, &lfa_metric)) {
+                               if (IS_DEBUG_LFA)
+                                       zlog_debug(
+                                               "ISIS-LFA: LFA condition not met for %s",
+                                               print_sys_hostname(sadj_N->id));
+                               continue;
+                       }
+
+                       if (lfa_metric < best_metric)
+                               best_metric = lfa_metric;
+
+                       if (IS_DEBUG_LFA)
+                               zlog_debug(
+                                       "ISIS-LFA: %s is a valid loop-free alternate",
+                                       print_sys_hostname(sadj_N->id));
+
+                       if (vertex->N.ip.sr.present) {
+                               psid = &vertex->N.ip.sr.sid;
+                               if (lfa_metric == sadj_N->metric)
+                                       last_hop = true;
+                       }
+                       lfa = isis_vertex_adj_add(spftree, vertex, lfa_list,
+                                                 sadj_N, psid, last_hop);
+                       lfa->lfa_metric = lfa_metric;
+               }
+
+               if (list_isempty(lfa_list)) {
+                       if (IS_DEBUG_LFA)
+                               zlog_debug("ISIS-LFA: no valid LFAs found");
+                       list_delete(&lfa_list);
+                       continue;
+               }
+
+               /* Check tie-breakers. */
+               filtered_lfa_list =
+                       isis_lfa_tiebreakers(area, circuit, spftree, resource,
+                                            vertex, sadj_primary, lfa_list);
+
+               /* Create backup route using the best LFAs. */
+               allow_ecmp = area->lfa_load_sharing[level - 1];
+               isis_route_create(&vertex->N.ip.p.dest, &vertex->N.ip.p.src,
+                                 best_metric, vertex->depth, &vertex->N.ip.sr,
+                                 filtered_lfa_list, allow_ecmp, area,
+                                 spftree->route_table_backup);
+
+               list_delete(&filtered_lfa_list);
+               list_delete(&lfa_list);
+       }
+}
+
+static void isis_spf_run_tilfa(struct isis_area *area,
+                              struct isis_circuit *circuit,
+                              struct isis_spftree *spftree,
+                              struct isis_spftree *spftree_reverse,
+                              struct lfa_protected_resource *resource)
+{
+       struct isis_spftree *spftree_pc_link;
+       struct isis_spftree *spftree_pc_node;
+
+       /* Compute node protecting repair paths first (if necessary). */
+       if (circuit->tilfa_node_protection[spftree->level - 1]) {
+               resource->type = LFA_NODE_PROTECTION;
+               spftree_pc_node = isis_tilfa_compute(area, spftree,
+                                                    spftree_reverse, resource);
+               isis_spftree_del(spftree_pc_node);
+       }
+
+       /* Compute link protecting repair paths. */
+       resource->type = LFA_LINK_PROTECTION;
+       spftree_pc_link =
+               isis_tilfa_compute(area, spftree, spftree_reverse, resource);
+       isis_spftree_del(spftree_pc_link);
+}
+
 /**
- * Run the TI-LFA algorithm for all proctected interfaces.
+ * Run the LFA/TI-LFA algorithms for all protected interfaces.
  *
  * @param area         IS-IS area
  * @param spftree      IS-IS SPF tree
  */
 void isis_spf_run_lfa(struct isis_area *area, struct isis_spftree *spftree)
 {
-       struct isis_spftree *spftree_reverse;
+       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. */
-       spftree_reverse = isis_spf_reverse_run(spftree);
+       if (tilfa_configured)
+               spftree_reverse = isis_spf_reverse_run(spftree);
 
        /* Run forward SPF on all adjacent routers. */
        isis_spf_run_neighbors(spftree);
@@ -1066,20 +1770,19 @@ void isis_spf_run_lfa(struct isis_area *area, struct isis_spftree *spftree)
        for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) {
                struct lfa_protected_resource resource = {};
                struct isis_adjacency *adj;
-               struct isis_spftree *spftree_pc_link;
-               struct isis_spftree *spftree_pc_node;
                static uint8_t null_sysid[ISIS_SYS_ID_LEN + 1];
 
-               if (!(circuit->is_type & spftree->level))
+               if (!(circuit->is_type & level))
                        continue;
 
-               if (!circuit->tilfa_protection[spftree->level - 1])
+               if (!circuit->lfa_protection[level - 1]
+                   && !circuit->tilfa_protection[level - 1])
                        continue;
 
                /* Fill in the protected resource. */
                switch (circuit->circ_type) {
                case CIRCUIT_T_BROADCAST:
-                       if (spftree->level == 1)
+                       if (level == ISIS_LEVEL1)
                                memcpy(resource.adjacency,
                                       circuit->u.bc.l1_desig_is,
                                       ISIS_SYS_ID_LEN + 1);
@@ -1103,20 +1806,15 @@ void isis_spf_run_lfa(struct isis_area *area, struct isis_spftree *spftree)
                        continue;
                }
 
-               /* Compute node protecting repair paths first (if necessary). */
-               if (circuit->tilfa_node_protection[spftree->level - 1]) {
-                       resource.type = LFA_NODE_PROTECTION;
-                       spftree_pc_node = isis_tilfa_compute(
-                               area, spftree, spftree_reverse, &resource);
-                       isis_spftree_del(spftree_pc_node);
+               if (circuit->lfa_protection[level - 1])
+                       isis_lfa_compute(area, circuit, spftree, &resource);
+               else if (circuit->tilfa_protection[level - 1]) {
+                       assert(spftree_reverse);
+                       isis_spf_run_tilfa(area, circuit, spftree,
+                                          spftree_reverse, &resource);
                }
-
-               /* Compute link protecting repair paths. */
-               resource.type = LFA_LINK_PROTECTION;
-               spftree_pc_link = isis_tilfa_compute(
-                       area, spftree, spftree_reverse, &resource);
-               isis_spftree_del(spftree_pc_link);
        }
 
-       isis_spftree_del(spftree_reverse);
+       if (tilfa_configured)
+               isis_spftree_del(spftree_reverse);
 }
index 835618760c0f637bd975d730d964579ade830b62..f09fc663a45a4d2e34e684ab391ed79ff51843ae 100644 (file)
 #ifndef _FRR_ISIS_LFA_H
 #define _FRR_ISIS_LFA_H
 
+#include "lib/typesafe.h"
+
+PREDECL_RBTREE_UNIQ(lfa_tiebreaker_tree)
+
+enum lfa_tiebreaker_type {
+       LFA_TIEBREAKER_DOWNSTREAM = 0,
+       LFA_TIEBREAKER_LOWEST_METRIC,
+       LFA_TIEBREAKER_NODE_PROTECTING,
+};
+
+struct lfa_tiebreaker {
+       struct lfa_tiebreaker_tree_item entry;
+       uint8_t index;
+       enum lfa_tiebreaker_type type;
+       struct isis_area *area;
+};
+int lfa_tiebreaker_cmp(const struct lfa_tiebreaker *a,
+                      const struct lfa_tiebreaker *b);
+DECLARE_RBTREE_UNIQ(lfa_tiebreaker_tree, struct lfa_tiebreaker, entry,
+                   lfa_tiebreaker_cmp)
+
 enum isis_tilfa_sid_type {
        TILFA_SID_PREFIX = 1,
        TILFA_SID_ADJ,
@@ -37,6 +58,20 @@ struct isis_tilfa_sid {
        } value;
 };
 
+enum spf_prefix_priority {
+       SPF_PREFIX_PRIO_CRITICAL = 0,
+       SPF_PREFIX_PRIO_HIGH,
+       SPF_PREFIX_PRIO_MEDIUM,
+       SPF_PREFIX_PRIO_LOW,
+       SPF_PREFIX_PRIO_MAX,
+};
+
+struct spf_prefix_priority_acl {
+       char *name;
+       struct access_list *list_v4;
+       struct access_list *list_v6;
+};
+
 RB_HEAD(isis_spf_nodes, isis_spf_node);
 RB_PROTOTYPE(isis_spf_nodes, isis_spf_node, entry, isis_spf_node_compare)
 struct isis_spf_node {
@@ -89,14 +124,32 @@ struct isis_spf_node *isis_spf_node_new(struct isis_spf_nodes *nodes,
                                        const uint8_t *sysid);
 struct isis_spf_node *isis_spf_node_find(const struct isis_spf_nodes *nodes,
                                         const uint8_t *sysid);
+void isis_lfa_tiebreakers_init(struct isis_area *area, int level);
+void isis_lfa_tiebreakers_clear(struct isis_area *area, int level);
+struct lfa_tiebreaker *isis_lfa_tiebreaker_add(struct isis_area *area,
+                                              int level, uint8_t index,
+                                              enum lfa_tiebreaker_type type);
+void isis_lfa_tiebreaker_delete(struct isis_area *area, int level,
+                               struct lfa_tiebreaker *tie_b);
+void isis_lfa_excluded_ifaces_init(struct isis_circuit *circuit, int level);
+void isis_lfa_excluded_ifaces_clear(struct isis_circuit *circuit, int level);
+void isis_lfa_excluded_iface_add(struct isis_circuit *circuit, int level,
+                                const char *ifname);
+void isis_lfa_excluded_iface_delete(struct isis_circuit *circuit, int level,
+                                   const char *ifname);
+bool isis_lfa_excluded_iface_check(struct isis_circuit *circuit, int level,
+                                  const char *ifname);
 bool isis_lfa_excise_adj_check(const struct isis_spftree *spftree,
                               const uint8_t *id);
 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);
+void isis_lfa_compute(struct isis_area *area, struct isis_circuit *circuit,
+                     struct isis_spftree *spftree,
+                     struct lfa_protected_resource *resource);
 void isis_spf_run_lfa(struct isis_area *area, struct isis_spftree *spftree);
-int isis_lfa_check(struct isis_spftree *spftree, struct isis_vertex *vertex);
+int isis_tilfa_check(struct isis_spftree *spftree, struct isis_vertex *vertex);
 struct isis_spftree *
 isis_tilfa_compute(struct isis_area *area, struct isis_spftree *spftree,
                   struct isis_spftree *spftree_reverse,
index 22df3ff37e4d31f90f36287e8eed1cdf23d0c88a..4576a4a95c9a8f63aabbb4695a96b7d8d017d246 100644 (file)
@@ -242,6 +242,8 @@ int main(int argc, char **argv, char **envp)
         */
        isis_error_init();
        access_list_init();
+       access_list_add_hook(isis_filter_update);
+       access_list_delete_hook(isis_filter_update);
        isis_vrf_init();
        prefix_list_init();
        isis_init();
index a64decc14fbeb7d1c0d458b2d4e5e0c9c821fdf3..b63a82f4048e2c3b6299b60a82823f088aae42a8 100644 (file)
@@ -45,3 +45,4 @@ DEFINE_MTYPE(ISISD, ISIS_DICT_NODE, "ISIS dictionary node")
 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")
index 6b63b3ccb8cbad1e210ccfd5cb0c93d45c44d6d1..3ef1c5bf0bbd23b4f92783284df59979b590d71c 100644 (file)
@@ -44,5 +44,6 @@ DECLARE_MTYPE(ISIS_DICT_NODE)
 DECLARE_MTYPE(ISIS_EXT_ROUTE)
 DECLARE_MTYPE(ISIS_EXT_INFO)
 DECLARE_MTYPE(ISIS_MPLS_TE)
+DECLARE_MTYPE(ISIS_ACL_NAME)
 
 #endif /* _QUAGGA_ISIS_MEMORY_H */
index 595053fd223a9e75c1b6e15b2f1c90049f20c235..c12ee44a90a202077e74f2d758f0d9969099ff92 100644 (file)
@@ -27,6 +27,7 @@
 #include "linklist.h"
 #include "log.h"
 #include "bfd.h"
+#include "filter.h"
 #include "spf_backoff.h"
 #include "lib_errors.h"
 #include "vrf.h"
@@ -632,14 +633,22 @@ int isis_instance_spf_minimum_interval_level_2_modify(
 int isis_instance_spf_prefix_priorities_critical_access_list_name_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 *acl_name;
+       struct spf_prefix_priority_acl *ppa;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       area = nb_running_get_entry(args->dnode, NULL, true);
+       acl_name = yang_dnode_get_string(args->dnode, NULL);
+
+       ppa = &area->spf_prefix_priorities[SPF_PREFIX_PRIO_CRITICAL];
+       XFREE(MTYPE_ISIS_ACL_NAME, ppa->name);
+       ppa->name = XSTRDUP(MTYPE_ISIS_ACL_NAME, acl_name);
+       ppa->list_v4 = access_list_lookup(AFI_IP, acl_name);
+       ppa->list_v6 = access_list_lookup(AFI_IP6, acl_name);
+       lsp_regenerate_schedule(area, area->is_type, 0);
 
        return NB_OK;
 }
@@ -647,14 +656,19 @@ int isis_instance_spf_prefix_priorities_critical_access_list_name_modify(
 int isis_instance_spf_prefix_priorities_critical_access_list_name_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 spf_prefix_priority_acl *ppa;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       area = nb_running_get_entry(args->dnode, NULL, true);
+
+       ppa = &area->spf_prefix_priorities[SPF_PREFIX_PRIO_CRITICAL];
+       XFREE(MTYPE_ISIS_ACL_NAME, ppa->name);
+       ppa->list_v4 = NULL;
+       ppa->list_v6 = NULL;
+       lsp_regenerate_schedule(area, area->is_type, 0);
 
        return NB_OK;
 }
@@ -665,14 +679,22 @@ int isis_instance_spf_prefix_priorities_critical_access_list_name_destroy(
 int isis_instance_spf_prefix_priorities_high_access_list_name_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 *acl_name;
+       struct spf_prefix_priority_acl *ppa;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       area = nb_running_get_entry(args->dnode, NULL, true);
+       acl_name = yang_dnode_get_string(args->dnode, NULL);
+
+       ppa = &area->spf_prefix_priorities[SPF_PREFIX_PRIO_HIGH];
+       XFREE(MTYPE_ISIS_ACL_NAME, ppa->name);
+       ppa->name = XSTRDUP(MTYPE_ISIS_ACL_NAME, acl_name);
+       ppa->list_v4 = access_list_lookup(AFI_IP, acl_name);
+       ppa->list_v6 = access_list_lookup(AFI_IP6, acl_name);
+       lsp_regenerate_schedule(area, area->is_type, 0);
 
        return NB_OK;
 }
@@ -680,14 +702,19 @@ int isis_instance_spf_prefix_priorities_high_access_list_name_modify(
 int isis_instance_spf_prefix_priorities_high_access_list_name_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 spf_prefix_priority_acl *ppa;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       area = nb_running_get_entry(args->dnode, NULL, true);
+
+       ppa = &area->spf_prefix_priorities[SPF_PREFIX_PRIO_HIGH];
+       XFREE(MTYPE_ISIS_ACL_NAME, ppa->name);
+       ppa->list_v4 = NULL;
+       ppa->list_v6 = NULL;
+       lsp_regenerate_schedule(area, area->is_type, 0);
 
        return NB_OK;
 }
@@ -698,14 +725,22 @@ int isis_instance_spf_prefix_priorities_high_access_list_name_destroy(
 int isis_instance_spf_prefix_priorities_medium_access_list_name_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 *acl_name;
+       struct spf_prefix_priority_acl *ppa;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       area = nb_running_get_entry(args->dnode, NULL, true);
+       acl_name = yang_dnode_get_string(args->dnode, NULL);
+
+       ppa = &area->spf_prefix_priorities[SPF_PREFIX_PRIO_MEDIUM];
+       XFREE(MTYPE_ISIS_ACL_NAME, ppa->name);
+       ppa->name = XSTRDUP(MTYPE_ISIS_ACL_NAME, acl_name);
+       ppa->list_v4 = access_list_lookup(AFI_IP, acl_name);
+       ppa->list_v6 = access_list_lookup(AFI_IP6, acl_name);
+       lsp_regenerate_schedule(area, area->is_type, 0);
 
        return NB_OK;
 }
@@ -713,14 +748,19 @@ int isis_instance_spf_prefix_priorities_medium_access_list_name_modify(
 int isis_instance_spf_prefix_priorities_medium_access_list_name_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 spf_prefix_priority_acl *ppa;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       area = nb_running_get_entry(args->dnode, NULL, true);
+
+       ppa = &area->spf_prefix_priorities[SPF_PREFIX_PRIO_MEDIUM];
+       XFREE(MTYPE_ISIS_ACL_NAME, ppa->name);
+       ppa->list_v4 = NULL;
+       ppa->list_v6 = NULL;
+       lsp_regenerate_schedule(area, area->is_type, 0);
 
        return NB_OK;
 }
@@ -1395,14 +1435,14 @@ int isis_instance_multi_topology_ipv6_dstsrc_overload_modify(
 int isis_instance_fast_reroute_level_1_lfa_load_sharing_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;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       area = nb_running_get_entry(args->dnode, NULL, true);
+       area->lfa_load_sharing[0] = yang_dnode_get_bool(args->dnode, NULL);
+       lsp_regenerate_schedule(area, area->is_type, 0);
 
        return NB_OK;
 }
@@ -1413,14 +1453,14 @@ int isis_instance_fast_reroute_level_1_lfa_load_sharing_modify(
 int isis_instance_fast_reroute_level_1_lfa_priority_limit_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;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       area = nb_running_get_entry(args->dnode, NULL, true);
+       area->lfa_priority_limit[0] = yang_dnode_get_enum(args->dnode, NULL);
+       lsp_regenerate_schedule(area, area->is_type, 0);
 
        return NB_OK;
 }
@@ -1428,14 +1468,14 @@ int isis_instance_fast_reroute_level_1_lfa_priority_limit_modify(
 int isis_instance_fast_reroute_level_1_lfa_priority_limit_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);
+       area->lfa_priority_limit[0] = SPF_PREFIX_PRIO_LOW;
+       lsp_regenerate_schedule(area, area->is_type, 0);
 
        return NB_OK;
 }
@@ -1446,14 +1486,21 @@ int isis_instance_fast_reroute_level_1_lfa_priority_limit_destroy(
 int isis_instance_fast_reroute_level_1_lfa_tiebreaker_create(
        struct nb_cb_create_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;
+       uint8_t index;
+       enum lfa_tiebreaker_type type;
+       struct lfa_tiebreaker *tie_b;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       area = nb_running_get_entry(args->dnode, NULL, true);
+       index = yang_dnode_get_uint8(args->dnode, "./index");
+       type = yang_dnode_get_enum(args->dnode, "./type");
+
+       tie_b = isis_lfa_tiebreaker_add(area, ISIS_LEVEL1, index, type);
+       nb_running_set_entry(args->dnode, tie_b);
+       lsp_regenerate_schedule(area, area->is_type, 0);
 
        return NB_OK;
 }
@@ -1461,14 +1508,16 @@ int isis_instance_fast_reroute_level_1_lfa_tiebreaker_create(
 int isis_instance_fast_reroute_level_1_lfa_tiebreaker_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 lfa_tiebreaker *tie_b;
+       struct isis_area *area;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       tie_b = nb_running_unset_entry(args->dnode);
+       area = tie_b->area;
+       isis_lfa_tiebreaker_delete(area, ISIS_LEVEL1, tie_b);
+       lsp_regenerate_schedule(area, area->is_type, 0);
 
        return NB_OK;
 }
@@ -1479,14 +1528,16 @@ int isis_instance_fast_reroute_level_1_lfa_tiebreaker_destroy(
 int isis_instance_fast_reroute_level_1_lfa_tiebreaker_type_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 lfa_tiebreaker *tie_b;
+       struct isis_area *area;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       tie_b = nb_running_get_entry(args->dnode, NULL, true);
+       area = tie_b->area;
+       tie_b->type = yang_dnode_get_enum(args->dnode, NULL);
+       lsp_regenerate_schedule(area, area->is_type, 0);
 
        return NB_OK;
 }
@@ -1497,14 +1548,13 @@ int isis_instance_fast_reroute_level_1_lfa_tiebreaker_type_modify(
 int isis_instance_fast_reroute_level_2_lfa_load_sharing_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;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       area = nb_running_get_entry(args->dnode, NULL, true);
+       area->lfa_load_sharing[1] = yang_dnode_get_bool(args->dnode, NULL);
 
        return NB_OK;
 }
@@ -1515,14 +1565,13 @@ int isis_instance_fast_reroute_level_2_lfa_load_sharing_modify(
 int isis_instance_fast_reroute_level_2_lfa_priority_limit_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;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       area = nb_running_get_entry(args->dnode, NULL, true);
+       area->lfa_priority_limit[1] = yang_dnode_get_enum(args->dnode, NULL);
 
        return NB_OK;
 }
@@ -1530,14 +1579,13 @@ int isis_instance_fast_reroute_level_2_lfa_priority_limit_modify(
 int isis_instance_fast_reroute_level_2_lfa_priority_limit_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);
+       area->lfa_priority_limit[1] = SPF_PREFIX_PRIO_LOW;
 
        return NB_OK;
 }
@@ -1548,14 +1596,21 @@ int isis_instance_fast_reroute_level_2_lfa_priority_limit_destroy(
 int isis_instance_fast_reroute_level_2_lfa_tiebreaker_create(
        struct nb_cb_create_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;
+       uint8_t index;
+       enum lfa_tiebreaker_type type;
+       struct lfa_tiebreaker *tie_b;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       area = nb_running_get_entry(args->dnode, NULL, true);
+       index = yang_dnode_get_uint8(args->dnode, "./index");
+       type = yang_dnode_get_enum(args->dnode, "./type");
+
+       tie_b = isis_lfa_tiebreaker_add(area, ISIS_LEVEL2, index, type);
+       nb_running_set_entry(args->dnode, tie_b);
+       lsp_regenerate_schedule(area, area->is_type, 0);
 
        return NB_OK;
 }
@@ -1563,14 +1618,16 @@ int isis_instance_fast_reroute_level_2_lfa_tiebreaker_create(
 int isis_instance_fast_reroute_level_2_lfa_tiebreaker_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 lfa_tiebreaker *tie_b;
+       struct isis_area *area;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       tie_b = nb_running_unset_entry(args->dnode);
+       area = tie_b->area;
+       isis_lfa_tiebreaker_delete(area, ISIS_LEVEL2, tie_b);
+       lsp_regenerate_schedule(area, area->is_type, 0);
 
        return NB_OK;
 }
@@ -1581,14 +1638,16 @@ int isis_instance_fast_reroute_level_2_lfa_tiebreaker_destroy(
 int isis_instance_fast_reroute_level_2_lfa_tiebreaker_type_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 lfa_tiebreaker *tie_b;
+       struct isis_area *area;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       tie_b = nb_running_get_entry(args->dnode, NULL, true);
+       area = tie_b->area;
+       tie_b->type = yang_dnode_get_enum(args->dnode, NULL);
+       lsp_regenerate_schedule(area, area->is_type, 0);
 
        return NB_OK;
 }
@@ -3314,15 +3373,24 @@ int lib_interface_isis_mpls_holddown_destroy(struct nb_cb_destroy_args *args)
 int lib_interface_isis_fast_reroute_level_1_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->lfa_protection[0] = yang_dnode_get_bool(args->dnode, NULL);
+       if (circuit->lfa_protection[0])
+               circuit->area->lfa_protected_links[0]++;
+       else {
+               assert(circuit->area->lfa_protected_links[0] > 0);
+               circuit->area->lfa_protected_links[0]--;
        }
 
+       area = circuit->area;
+       lsp_regenerate_schedule(area, area->is_type, 0);
+
        return NB_OK;
 }
 
@@ -3333,14 +3401,19 @@ int lib_interface_isis_fast_reroute_level_1_lfa_enable_modify(
 int lib_interface_isis_fast_reroute_level_1_lfa_exclude_interface_create(
        struct nb_cb_create_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;
+       const char *exclude_ifname;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       circuit = nb_running_get_entry(args->dnode, NULL, true);
+       exclude_ifname = yang_dnode_get_string(args->dnode, NULL);
+
+       isis_lfa_excluded_iface_add(circuit, ISIS_LEVEL1, exclude_ifname);
+       area = circuit->area;
+       lsp_regenerate_schedule(area, area->is_type, 0);
 
        return NB_OK;
 }
@@ -3348,14 +3421,19 @@ int lib_interface_isis_fast_reroute_level_1_lfa_exclude_interface_create(
 int lib_interface_isis_fast_reroute_level_1_lfa_exclude_interface_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;
+       const char *exclude_ifname;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       circuit = nb_running_get_entry(args->dnode, NULL, true);
+       exclude_ifname = yang_dnode_get_string(args->dnode, NULL);
+
+       isis_lfa_excluded_iface_delete(circuit, ISIS_LEVEL1, exclude_ifname);
+       area = circuit->area;
+       lsp_regenerate_schedule(area, area->is_type, 0);
 
        return NB_OK;
 }
@@ -3376,10 +3454,10 @@ int lib_interface_isis_fast_reroute_level_1_ti_lfa_enable_modify(
        circuit = nb_running_get_entry(args->dnode, NULL, true);
        circuit->tilfa_protection[0] = yang_dnode_get_bool(args->dnode, NULL);
        if (circuit->tilfa_protection[0])
-               circuit->area->lfa_protected_links[0]++;
+               circuit->area->tilfa_protected_links[0]++;
        else {
-               assert(circuit->area->lfa_protected_links[0] > 0);
-               circuit->area->lfa_protected_links[0]--;
+               assert(circuit->area->tilfa_protected_links[0] > 0);
+               circuit->area->tilfa_protected_links[0]--;
        }
 
        area = circuit->area;
@@ -3418,15 +3496,24 @@ int lib_interface_isis_fast_reroute_level_1_ti_lfa_node_protection_modify(
 int lib_interface_isis_fast_reroute_level_2_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->lfa_protection[1] = yang_dnode_get_bool(args->dnode, NULL);
+       if (circuit->lfa_protection[1])
+               circuit->area->lfa_protected_links[1]++;
+       else {
+               assert(circuit->area->lfa_protected_links[1] > 0);
+               circuit->area->lfa_protected_links[1]--;
        }
 
+       area = circuit->area;
+       lsp_regenerate_schedule(area, area->is_type, 0);
+
        return NB_OK;
 }
 
@@ -3437,14 +3524,19 @@ int lib_interface_isis_fast_reroute_level_2_lfa_enable_modify(
 int lib_interface_isis_fast_reroute_level_2_lfa_exclude_interface_create(
        struct nb_cb_create_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;
+       const char *exclude_ifname;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       circuit = nb_running_get_entry(args->dnode, NULL, true);
+       exclude_ifname = yang_dnode_get_string(args->dnode, NULL);
+
+       isis_lfa_excluded_iface_add(circuit, ISIS_LEVEL2, exclude_ifname);
+       area = circuit->area;
+       lsp_regenerate_schedule(area, area->is_type, 0);
 
        return NB_OK;
 }
@@ -3452,14 +3544,19 @@ int lib_interface_isis_fast_reroute_level_2_lfa_exclude_interface_create(
 int lib_interface_isis_fast_reroute_level_2_lfa_exclude_interface_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;
+       const char *exclude_ifname;
+
+       if (args->event != NB_EV_APPLY)
+               return NB_OK;
+
+       circuit = nb_running_get_entry(args->dnode, NULL, true);
+       exclude_ifname = yang_dnode_get_string(args->dnode, NULL);
+
+       isis_lfa_excluded_iface_delete(circuit, ISIS_LEVEL2, exclude_ifname);
+       area = circuit->area;
+       lsp_regenerate_schedule(area, area->is_type, 0);
 
        return NB_OK;
 }
@@ -3480,10 +3577,10 @@ int lib_interface_isis_fast_reroute_level_2_ti_lfa_enable_modify(
        circuit = nb_running_get_entry(args->dnode, NULL, true);
        circuit->tilfa_protection[1] = yang_dnode_get_bool(args->dnode, NULL);
        if (circuit->tilfa_protection[1])
-               circuit->area->lfa_protected_links[1]++;
+               circuit->area->tilfa_protected_links[1]++;
        else {
-               assert(circuit->area->lfa_protected_links[1] > 0);
-               circuit->area->lfa_protected_links[1]--;
+               assert(circuit->area->tilfa_protected_links[1] > 0);
+               circuit->area->tilfa_protected_links[1]--;
        }
 
        area = circuit->area;
index d664a6f8962545c55eb25ed384b57d8137140c44..d32f219e98b2d740d19d5f9fa3f12a061c70f0f6 100644 (file)
@@ -183,7 +183,7 @@ static void isis_route_add_dummy_nexthops(struct isis_route_info *rinfo,
 static struct isis_route_info *
 isis_route_info_new(struct prefix *prefix, struct prefix_ipv6 *src_p,
                    uint32_t cost, uint32_t depth, struct isis_sr_psid_info *sr,
-                   struct list *adjacencies)
+                   struct list *adjacencies, bool allow_ecmp)
 {
        struct isis_route_info *rinfo;
        struct isis_vertex_adj *vadj;
@@ -205,6 +205,8 @@ isis_route_info_new(struct prefix *prefix, struct prefix_ipv6 *src_p,
                if (CHECK_FLAG(im->options, F_ISIS_UNIT_TEST)) {
                        isis_route_add_dummy_nexthops(rinfo, sadj->id, sr,
                                                      label_stack);
+                       if (!allow_ecmp)
+                               break;
                        continue;
                }
 
@@ -233,6 +235,8 @@ isis_route_info_new(struct prefix *prefix, struct prefix_ipv6 *src_p,
                }
                adjinfo2nexthop(prefix->family, rinfo->nexthops, adj, sr,
                                label_stack);
+               if (!allow_ecmp)
+                       break;
        }
 
        rinfo->cost = cost;
@@ -339,8 +343,8 @@ static int isis_route_info_same(struct isis_route_info *new,
 struct isis_route_info *
 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, struct isis_area *area,
-                 struct route_table *table)
+                 struct list *adjacencies, bool allow_ecmp,
+                 struct isis_area *area, struct route_table *table)
 {
        struct route_node *route_node;
        struct isis_route_info *rinfo_new, *rinfo_old, *route_info = NULL;
@@ -350,7 +354,7 @@ isis_route_create(struct prefix *prefix, struct prefix_ipv6 *src_p,
                return NULL;
 
        rinfo_new = isis_route_info_new(prefix, src_p, cost, depth, sr,
-                                       adjacencies);
+                                       adjacencies, allow_ecmp);
        route_node = srcdest_rnode_get(table, prefix, src_p);
 
        rinfo_old = route_node->info;
index b5e4aed6cccbe6df6e968d5dc8a8ceeee4df2445..0d4f8849590fd08a5a95d0b2e626640481030a6f 100644 (file)
@@ -61,8 +61,8 @@ void adjinfo2nexthop(int family, struct list *nexthops,
 struct isis_route_info *
 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, struct isis_area *area,
-                 struct route_table *table);
+                 struct list *adjacencies, bool allow_ecmp,
+                 struct isis_area *area, 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 a94ebbc014a889635863cabccd6069cb71d6e73c..947db9e92c1645e4da697eda2f555e265503d923 100644 (file)
@@ -32,6 +32,7 @@
 #include "termtable.h"
 #include "memory.h"
 #include "prefix.h"
+#include "filter.h"
 #include "if.h"
 #include "hash.h"
 #include "table.h"
@@ -216,7 +217,7 @@ struct isis_vertex *isis_spf_prefix_sid_lookup(struct isis_spftree *spftree,
        return hash_lookup(spftree->prefix_sids, &lookup);
 }
 
-static void isis_vertex_adj_free(void *arg)
+void isis_vertex_adj_free(void *arg)
 {
        struct isis_vertex_adj *vadj = arg;
 
@@ -246,10 +247,10 @@ static struct isis_vertex *isis_vertex_new(struct isis_spftree *spftree,
        return vertex;
 }
 
-static struct isis_vertex_adj *isis_vertex_adj_add(struct isis_spftree *spftree,
-                                                  struct isis_vertex *vertex,
-                                                  struct isis_spf_adj *sadj,
-                                                  struct isis_prefix_sid *psid)
+struct isis_vertex_adj *
+isis_vertex_adj_add(struct isis_spftree *spftree, struct isis_vertex *vertex,
+                   struct list *vadj_list, struct isis_spf_adj *sadj,
+                   struct isis_prefix_sid *psid, bool last_hop)
 {
        struct isis_vertex_adj *vadj;
 
@@ -262,9 +263,6 @@ static struct isis_vertex_adj *isis_vertex_adj_add(struct isis_spftree *spftree,
                                "ISIS-SPF: ignoring different Prefix-SID for route %pFX",
                                &vertex->N.ip.p.dest);
                else {
-                       bool last_hop;
-
-                       last_hop = (vertex->depth == 2);
                        vadj->sr.sid = *psid;
                        vadj->sr.label = sr_prefix_out_label(
                                spftree->lspdb, vertex->N.ip.p.dest.family,
@@ -273,7 +271,7 @@ static struct isis_vertex_adj *isis_vertex_adj_add(struct isis_spftree *spftree,
                                vadj->sr.present = true;
                }
        }
-       listnode_add(vertex->Adj_N, vadj);
+       listnode_add(vadj_list, vadj);
 
        return vadj;
 }
@@ -528,6 +526,7 @@ isis_spf_add2tent(struct isis_spftree *spftree, enum vertextype vtype, void *id,
 {
        struct isis_vertex *vertex;
        struct listnode *node;
+       bool last_hop;
        char buff[VID2STR_BUFFER];
 
        vertex = isis_find_vertex(&spftree->paths, id, vtype);
@@ -593,14 +592,16 @@ isis_spf_add2tent(struct isis_spftree *spftree, enum vertextype vtype, void *id,
        if (CHECK_FLAG(spftree->flags, F_SPFTREE_HOPCOUNT_METRIC))
                vertex_update_firsthops(vertex, parent);
 
+       last_hop = (vertex->depth == 2);
        if (parent && parent->Adj_N && listcount(parent->Adj_N) > 0) {
                struct isis_vertex_adj *parent_vadj;
 
                for (ALL_LIST_ELEMENTS_RO(parent->Adj_N, node, parent_vadj))
-                       isis_vertex_adj_add(spftree, vertex, parent_vadj->sadj,
-                                           psid);
+                       isis_vertex_adj_add(spftree, vertex, vertex->Adj_N,
+                                           parent_vadj->sadj, psid, last_hop);
        } else if (sadj) {
-               isis_vertex_adj_add(spftree, vertex, sadj, psid);
+               isis_vertex_adj_add(spftree, vertex, vertex->Adj_N, sadj, psid,
+                                   last_hop);
        }
 
 #ifdef EXTREME_DEBUG
@@ -628,9 +629,13 @@ static void isis_spf_add_local(struct isis_spftree *spftree,
        if (vertex) {
                /* C.2.5   c) */
                if (vertex->d_N == cost) {
-                       if (sadj)
-                               isis_vertex_adj_add(spftree, vertex, sadj,
-                                                   psid);
+                       if (sadj) {
+                               bool last_hop = (vertex->depth == 2);
+
+                               isis_vertex_adj_add(spftree, vertex,
+                                                   vertex->Adj_N, sadj, psid,
+                                                   last_hop);
+                       }
                        /*       d) */
                        if (!CHECK_FLAG(spftree->flags,
                                        F_SPFTREE_NO_ADJACENCIES)
@@ -718,11 +723,16 @@ static void process_N(struct isis_spftree *spftree, enum vertextype vtype,
                        struct isis_vertex_adj *parent_vadj;
                        for (ALL_LIST_ELEMENTS_RO(parent->Adj_N, node,
                                                  parent_vadj))
-                               if (!isis_vertex_adj_exists(spftree, vertex,
-                                                           parent_vadj->sadj))
+                               if (!isis_vertex_adj_exists(
+                                           spftree, vertex,
+                                           parent_vadj->sadj)) {
+                                       bool last_hop = (vertex->depth == 2);
+
                                        isis_vertex_adj_add(spftree, vertex,
+                                                           vertex->Adj_N,
                                                            parent_vadj->sadj,
-                                                           psid);
+                                                           psid, last_hop);
+                               }
                        if (CHECK_FLAG(spftree->flags,
                                       F_SPFTREE_HOPCOUNT_METRIC))
                                vertex_update_firsthops(vertex, parent);
@@ -1276,6 +1286,10 @@ static void spf_adj_list_parse_tlv(struct isis_spftree *spftree,
        sadj->subtlvs = subtlvs;
        sadj->flags = flags;
 
+       if ((oldmetric && metric == ISIS_NARROW_METRIC_INFINITY)
+           || (!oldmetric && metric == ISIS_WIDE_METRIC_INFINITY))
+               SET_FLAG(flags, F_ISIS_SPF_ADJ_METRIC_INFINITY);
+
        /* Set real adjacency. */
        if (!CHECK_FLAG(spftree->flags, F_SPFTREE_NO_ADJACENCIES)
            && !LSP_PSEUDO_ID(id)) {
@@ -1419,28 +1433,59 @@ static void init_spt(struct isis_spftree *spftree, int mtid)
        spftree->mtid = mtid;
 }
 
+static enum spf_prefix_priority
+spf_prefix_priority(struct isis_spftree *spftree, struct isis_vertex *vertex)
+{
+       struct isis_area *area = spftree->area;
+       struct prefix *prefix = &vertex->N.ip.p.dest;
+
+       for (int priority = SPF_PREFIX_PRIO_CRITICAL;
+            priority <= SPF_PREFIX_PRIO_MEDIUM; priority++) {
+               struct spf_prefix_priority_acl *ppa;
+               enum filter_type ret = FILTER_PERMIT;
+
+               ppa = &area->spf_prefix_priorities[priority];
+               switch (spftree->family) {
+               case AF_INET:
+                       ret = access_list_apply(ppa->list_v4, prefix);
+                       break;
+               case AF_INET6:
+                       ret = access_list_apply(ppa->list_v6, prefix);
+                       break;
+               default:
+                       break;
+               }
+
+               if (ret == FILTER_PERMIT)
+                       return priority;
+       }
+
+       /* Assign medium priority to loopback prefixes by default. */
+       if (is_host_route(prefix))
+               return SPF_PREFIX_PRIO_MEDIUM;
+
+       return SPF_PREFIX_PRIO_LOW;
+}
+
 static void spf_path_process(struct isis_spftree *spftree,
                             struct isis_vertex *vertex)
 {
        struct isis_area *area = spftree->area;
+       int level = spftree->level;
        char buff[VID2STR_BUFFER];
 
-       if (VTYPE_IS(vertex->type)
+       if (spftree->type == SPF_TYPE_TI_LFA && VTYPE_IS(vertex->type)
            && !CHECK_FLAG(spftree->flags, F_SPFTREE_NO_ADJACENCIES)) {
                if (listcount(vertex->Adj_N) > 0) {
-                       if (spftree->type == SPF_TYPE_TI_LFA) {
-                               struct isis_adjacency *adj;
+                       struct isis_adjacency *adj;
 
-                               if (isis_lfa_check(spftree, vertex) != 0)
-                                       return;
+                       if (isis_tilfa_check(spftree, vertex) != 0)
+                               return;
 
-                               adj = isis_adj_find(area, spftree->level,
-                                                   vertex->N.id);
-                               if (adj)
-                                       sr_adj_sid_add_single(
-                                               adj, spftree->family, true,
-                                               vertex->Adj_N);
-                       }
+                       adj = isis_adj_find(area, level, vertex->N.id);
+                       if (adj)
+                               sr_adj_sid_add_single(adj, spftree->family,
+                                                     true, vertex->Adj_N);
                } else if (IS_DEBUG_SPF_EVENTS)
                        zlog_debug(
                                "ISIS-SPF: no adjacencies, do not install backup Adj-SID for %s depth %d dist %d",
@@ -1450,21 +1495,45 @@ static void spf_path_process(struct isis_spftree *spftree,
 
        if (VTYPE_IP(vertex->type)
            && !CHECK_FLAG(spftree->flags, F_SPFTREE_NO_ROUTES)) {
+               enum spf_prefix_priority priority;
+
+               priority = spf_prefix_priority(spftree, vertex);
+               vertex->N.ip.priority = priority;
                if (vertex->depth == 1 || listcount(vertex->Adj_N) > 0) {
                        struct route_table *route_table;
+                       bool allow_ecmp;
 
                        if (spftree->type == SPF_TYPE_TI_LFA) {
-                               if (isis_lfa_check(spftree, vertex) != 0)
+                               struct isis_spftree *pre_spftree;
+
+                               if (priority
+                                   > area->lfa_priority_limit[level - 1]) {
+                                       if (IS_DEBUG_LFA)
+                                               zlog_debug(
+                                                       "ISIS-LFA: skipping %s %s (low prefix priority)",
+                                                       vtype2string(
+                                                               vertex->type),
+                                                       vid2string(
+                                                               vertex, buff,
+                                                               sizeof(buff)));
                                        return;
-                               route_table = spftree->lfa.old.spftree
-                                                     ->route_table_backup;
-                       } else
+                               }
+
+                               if (isis_tilfa_check(spftree, vertex) != 0)
+                                       return;
+
+                               pre_spftree = spftree->lfa.old.spftree;
+                               route_table = pre_spftree->route_table_backup;
+                               allow_ecmp = area->lfa_load_sharing[level - 1];
+                       } else {
                                route_table = spftree->route_table;
+                               allow_ecmp = true;
+                       }
 
-                       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, area, route_table);
+                       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, allow_ecmp, area, route_table);
                } else if (IS_DEBUG_SPF_EVENTS)
                        zlog_debug(
                                "ISIS-SPF: no adjacencies, do not install route for %s depth %d dist %d",
@@ -1638,7 +1707,8 @@ static void isis_run_spf_with_protection(struct isis_area *area,
        isis_run_spf(spftree);
 
        /* Run LFA protection if configured. */
-       if (area->lfa_protected_links[spftree->level - 1] > 0)
+       if (area->lfa_protected_links[spftree->level - 1] > 0
+           || area->tilfa_protected_links[spftree->level - 1] > 0)
                isis_spf_run_lfa(area, spftree);
 }
 
index e11495df485a25f0b6ea583e90787a388f614b70..5b6fcdaf6875ebb55630d8600a2b41ee21959d06 100644 (file)
@@ -46,6 +46,7 @@ struct isis_spf_adj {
        uint8_t flags;
 #define F_ISIS_SPF_ADJ_BROADCAST 0x01
 #define F_ISIS_SPF_ADJ_OLDMETRIC 0x02
+#define F_ISIS_SPF_ADJ_METRIC_INFINITY 0x04
 };
 
 struct isis_spftree *isis_spftree_new(struct isis_area *area,
index 9cb1a39b827253152eb2a49eacd1c070df87788d..f128b4b585995c6b4e0deff9a78aff5ee139e67a 100644 (file)
@@ -54,6 +54,7 @@ struct isis_vertex_adj {
        struct isis_spf_adj *sadj;
        struct isis_sr_psid_info sr;
        struct mpls_label_stack *label_stack;
+       uint32_t lfa_metric;
 };
 
 /*
@@ -66,6 +67,7 @@ struct isis_vertex {
                struct {
                        struct prefix_pair p;
                        struct isis_sr_psid_info sr;
+                       enum spf_prefix_priority priority;
                } ip;
        } N;
        uint32_t d_N;     /* d(N) Distance from this IS      */
@@ -193,6 +195,11 @@ static void isis_vertex_del(struct isis_vertex *vertex)
 bool isis_vertex_adj_exists(const struct isis_spftree *spftree,
                            const struct isis_vertex *vertex,
                            const struct isis_spf_adj *sadj);
+void isis_vertex_adj_free(void *arg);
+struct isis_vertex_adj *
+isis_vertex_adj_add(struct isis_spftree *spftree, struct isis_vertex *vertex,
+                   struct list *vadj_list, struct isis_spf_adj *sadj,
+                   struct isis_prefix_sid *psid, bool last_hop);
 
 __attribute__((__unused__))
 static void isis_vertex_queue_clear(struct isis_vertex_queue *queue)
index 42edd1d5416057479981aa97015bca4c2200a12b..827e022bafd063ac547a6c2ee08d687449ef9ab0 100644 (file)
@@ -31,6 +31,7 @@
 #include "linklist.h"
 #include "if.h"
 #include "hash.h"
+#include "filter.h"
 #include "stream.h"
 #include "prefix.h"
 #include "table.h"
@@ -310,6 +311,10 @@ struct isis_area *isis_area_create(const char *area_tag, const char *vrf_name)
        area->lsp_frag_threshold = 90; /* not currently configurable */
        area->lsp_mtu =
                yang_get_default_uint16("/frr-isisd:isis/instance/lsp/mtu");
+       area->lfa_load_sharing[0] = yang_get_default_bool(
+               "/frr-isisd:isis/instance/fast-reroute/level-1/lfa/load-sharing");
+       area->lfa_load_sharing[1] = yang_get_default_bool(
+               "/frr-isisd:isis/instance/fast-reroute/level-2/lfa/load-sharing");
 #else
        area->max_lsp_lifetime[0] = DEFAULT_LSP_LIFETIME;    /* 1200 */
        area->max_lsp_lifetime[1] = DEFAULT_LSP_LIFETIME;    /* 1200 */
@@ -324,7 +329,13 @@ struct isis_area *isis_area_create(const char *area_tag, const char *vrf_name)
        area->newmetric = 1;
        area->lsp_frag_threshold = 90;
        area->lsp_mtu = DEFAULT_LSP_MTU;
+       area->lfa_load_sharing[0] = true;
+       area->lfa_load_sharing[1] = true;
 #endif /* ifndef FABRICD */
+       area->lfa_priority_limit[0] = SPF_PREFIX_PRIO_LOW;
+       area->lfa_priority_limit[1] = SPF_PREFIX_PRIO_LOW;
+       isis_lfa_tiebreakers_init(area, ISIS_LEVEL1);
+       isis_lfa_tiebreakers_init(area, ISIS_LEVEL2);
 
        area_mt_init(area);
 
@@ -461,6 +472,16 @@ void isis_area_destroy(struct isis_area *area)
        }
        area->area_addrs = NULL;
 
+       for (int i = SPF_PREFIX_PRIO_CRITICAL; i <= SPF_PREFIX_PRIO_MEDIUM;
+            i++) {
+               struct spf_prefix_priority_acl *ppa;
+
+               ppa = &area->spf_prefix_priorities[i];
+               XFREE(MTYPE_ISIS_ACL_NAME, ppa->name);
+       }
+       isis_lfa_tiebreakers_clear(area, ISIS_LEVEL1);
+       isis_lfa_tiebreakers_clear(area, ISIS_LEVEL2);
+
        thread_cancel(&area->t_tick);
        thread_cancel(&area->t_lsp_refresh[0]);
        thread_cancel(&area->t_lsp_refresh[1]);
@@ -605,6 +626,29 @@ void isis_terminate()
                isis_finish(isis);
 }
 
+void isis_filter_update(struct access_list *access)
+{
+       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 i = SPF_PREFIX_PRIO_CRITICAL;
+                            i <= SPF_PREFIX_PRIO_MEDIUM; i++) {
+                               struct spf_prefix_priority_acl *ppa;
+
+                               ppa = &area->spf_prefix_priorities[i];
+                               ppa->list_v4 =
+                                       access_list_lookup(AFI_IP, ppa->name);
+                               ppa->list_v6 =
+                                       access_list_lookup(AFI_IP6, ppa->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 0690d3e10c42d0065f3ea107c2d578a17c76eb7c..4618d14af3db0580189fdf1eb7df8f9ffa5ac06c 100644 (file)
@@ -33,6 +33,7 @@
 #include "isisd/isis_sr.h"
 #include "isis_flags.h"
 #include "isis_lsp.h"
+#include "isis_lfa.h"
 #include "isis_memory.h"
 #include "qobj.h"
 #include "ldp_sync.h"
@@ -188,8 +189,15 @@ struct isis_area {
        struct isis_sr_db srdb;
        int ipv6_circuits;
        bool purge_originator;
+       /* SPF prefix priorities. */
+       struct spf_prefix_priority_acl
+               spf_prefix_priorities[SPF_PREFIX_PRIO_MAX];
        /* Fast Re-Route information. */
        size_t lfa_protected_links[ISIS_LEVELS];
+       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];
+       size_t tilfa_protected_links[ISIS_LEVELS];
        /* Counters */
        uint32_t circuit_state_changes;
        struct isis_redist redist_settings[REDIST_PROTOCOL_COUNT]
@@ -231,6 +239,7 @@ struct isis_area *isis_area_lookup_by_vrf(const char *area_tag,
                                          const char *vrf_name);
 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 print_debug(struct vty *, int, int);
 struct isis_lsp *lsp_for_arg(struct lspdb_head *head, const char *argv,
                             struct isis *isis);