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