diff options
-rw-r--r-- | doc/user/isisd.rst | 39 | ||||
-rw-r--r-- | isisd/isis_circuit.c | 4 | ||||
-rw-r--r-- | isisd/isis_circuit.h | 2 | ||||
-rw-r--r-- | isisd/isis_lfa.c | 790 | ||||
-rw-r--r-- | isisd/isis_lfa.h | 55 | ||||
-rw-r--r-- | isisd/isis_main.c | 2 | ||||
-rw-r--r-- | isisd/isis_memory.c | 1 | ||||
-rw-r--r-- | isisd/isis_memory.h | 1 | ||||
-rw-r--r-- | isisd/isis_nb_config.c | 489 | ||||
-rw-r--r-- | isisd/isis_route.c | 12 | ||||
-rw-r--r-- | isisd/isis_route.h | 4 | ||||
-rw-r--r-- | isisd/isis_spf.c | 148 | ||||
-rw-r--r-- | isisd/isis_spf.h | 1 | ||||
-rw-r--r-- | isisd/isis_spf_private.h | 7 | ||||
-rw-r--r-- | isisd/isisd.c | 44 | ||||
-rw-r--r-- | isisd/isisd.h | 9 |
16 files changed, 1320 insertions, 288 deletions
diff --git a/doc/user/isisd.rst b/doc/user/isisd.rst index 0bdc8121a..a470f4e80 100644 --- a/doc/user/isisd.rst +++ b/doc/user/isisd.rst @@ -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] diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c index e3c70264f..2580a7c43 100644 --- a/isisd/isis_circuit.c +++ b/isisd/isis_circuit.c @@ -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); diff --git a/isisd/isis_circuit.h b/isisd/isis_circuit.h index b4b03bf6b..e736d8fb1 100644 --- a/isisd/isis_circuit.h +++ b/isisd/isis_circuit.h @@ -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]; /* diff --git a/isisd/isis_lfa.c b/isisd/isis_lfa.c index 13822a917..7d516dc93 100644 --- a/isisd/isis_lfa.c +++ b/isisd/isis_lfa.c @@ -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) @@ -120,6 +122,185 @@ struct isis_spf_node *isis_spf_node_find(const struct isis_spf_nodes *nodes, } /** + * 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); } diff --git a/isisd/isis_lfa.h b/isisd/isis_lfa.h index 835618760..f09fc663a 100644 --- a/isisd/isis_lfa.h +++ b/isisd/isis_lfa.h @@ -20,6 +20,27 @@ #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, diff --git a/isisd/isis_main.c b/isisd/isis_main.c index 22df3ff37..4576a4a95 100644 --- a/isisd/isis_main.c +++ b/isisd/isis_main.c @@ -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(); diff --git a/isisd/isis_memory.c b/isisd/isis_memory.c index a64decc14..b63a82f40 100644 --- a/isisd/isis_memory.c +++ b/isisd/isis_memory.c @@ -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") diff --git a/isisd/isis_memory.h b/isisd/isis_memory.h index 6b63b3ccb..3ef1c5bf0 100644 --- a/isisd/isis_memory.h +++ b/isisd/isis_memory.h @@ -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 */ diff --git a/isisd/isis_nb_config.c b/isisd/isis_nb_config.c index 595053fd2..c12ee44a9 100644 --- a/isisd/isis_nb_config.c +++ b/isisd/isis_nb_config.c @@ -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; diff --git a/isisd/isis_route.c b/isisd/isis_route.c index d664a6f89..d32f219e9 100644 --- a/isisd/isis_route.c +++ b/isisd/isis_route.c @@ -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; diff --git a/isisd/isis_route.h b/isisd/isis_route.h index b5e4aed6c..0d4f88495 100644 --- a/isisd/isis_route.h +++ b/isisd/isis_route.h @@ -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 */ diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c index a94ebbc01..947db9e92 100644 --- a/isisd/isis_spf.c +++ b/isisd/isis_spf.c @@ -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); } diff --git a/isisd/isis_spf.h b/isisd/isis_spf.h index e11495df4..5b6fcdaf6 100644 --- a/isisd/isis_spf.h +++ b/isisd/isis_spf.h @@ -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, diff --git a/isisd/isis_spf_private.h b/isisd/isis_spf_private.h index 9cb1a39b8..f128b4b58 100644 --- a/isisd/isis_spf_private.h +++ b/isisd/isis_spf_private.h @@ -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) diff --git a/isisd/isisd.c b/isisd/isisd.c index 42edd1d54..827e022ba 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -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) diff --git a/isisd/isisd.h b/isisd/isisd.h index 0690d3e10..4618d14af 100644 --- a/isisd/isisd.h +++ b/isisd/isisd.h @@ -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); |