1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2020 NetDEF, Inc.
14 #include "srcdest_table.h"
18 #include "isis_common.h"
20 #include "isis_misc.h"
21 #include "isis_adjacency.h"
22 #include "isis_circuit.h"
25 #include "isis_route.h"
27 #include "isis_tlvs.h"
28 #include "isis_spf_private.h"
29 #include "isis_zebra.h"
30 #include "isis_errors.h"
32 DEFINE_MTYPE_STATIC(ISISD
, ISIS_SPF_NODE
, "ISIS SPF Node");
33 DEFINE_MTYPE_STATIC(ISISD
, ISIS_LFA_TIEBREAKER
, "ISIS LFA Tiebreaker");
34 DEFINE_MTYPE_STATIC(ISISD
, ISIS_LFA_EXCL_IFACE
, "ISIS LFA Excluded Interface");
35 DEFINE_MTYPE_STATIC(ISISD
, ISIS_RLFA
, "ISIS Remote LFA");
36 DEFINE_MTYPE(ISISD
, ISIS_NEXTHOP_LABELS
, "ISIS nexthop MPLS labels");
38 static inline int isis_spf_node_compare(const struct isis_spf_node
*a
,
39 const struct isis_spf_node
*b
)
41 return memcmp(a
->sysid
, b
->sysid
, sizeof(a
->sysid
));
43 RB_GENERATE(isis_spf_nodes
, isis_spf_node
, entry
, isis_spf_node_compare
)
46 * Initialize list of SPF nodes.
48 * @param nodes List of SPF nodes
50 void isis_spf_node_list_init(struct isis_spf_nodes
*nodes
)
52 RB_INIT(isis_spf_nodes
, nodes
);
56 * Clear list of SPF nodes, releasing all allocated memory.
58 * @param nodes List of SPF nodes
60 void isis_spf_node_list_clear(struct isis_spf_nodes
*nodes
)
62 while (!RB_EMPTY(isis_spf_nodes
, nodes
)) {
63 struct isis_spf_node
*node
= RB_ROOT(isis_spf_nodes
, nodes
);
65 if (node
->adjacencies
)
66 list_delete(&node
->adjacencies
);
67 if (node
->lfa
.spftree
)
68 isis_spftree_del(node
->lfa
.spftree
);
69 if (node
->lfa
.spftree_reverse
)
70 isis_spftree_del(node
->lfa
.spftree_reverse
);
71 isis_spf_node_list_clear(&node
->lfa
.p_space
);
72 RB_REMOVE(isis_spf_nodes
, nodes
, node
);
73 XFREE(MTYPE_ISIS_SPF_NODE
, node
);
78 * Add new node to list of SPF nodes.
80 * @param nodes List of SPF nodes
81 * @param sysid Node System ID
83 * @return Pointer to new IS-IS SPF node structure.
85 struct isis_spf_node
*isis_spf_node_new(struct isis_spf_nodes
*nodes
,
88 struct isis_spf_node
*node
;
90 node
= XCALLOC(MTYPE_ISIS_SPF_NODE
, sizeof(*node
));
91 memcpy(node
->sysid
, sysid
, sizeof(node
->sysid
));
92 node
->adjacencies
= list_new();
93 isis_spf_node_list_init(&node
->lfa
.p_space
);
94 RB_INSERT(isis_spf_nodes
, nodes
, node
);
100 * Lookup SPF node by its System ID on the given list.
102 * @param nodes List of SPF nodes
103 * @param sysid Node System ID
105 * @return Pointer to SPF node if found, NULL otherwise
107 struct isis_spf_node
*isis_spf_node_find(const struct isis_spf_nodes
*nodes
,
108 const uint8_t *sysid
)
110 struct isis_spf_node node
= {};
112 memcpy(node
.sysid
, sysid
, sizeof(node
.sysid
));
113 return RB_FIND(isis_spf_nodes
, nodes
, &node
);
117 * LFA tiebreaker RB-tree comparison function.
119 * @param a First LFA tiebreaker
120 * @param b Second LFA tiebreaker
122 * @return -1 (a < b), 0 (a == b) or +1 (a > b)
124 int lfa_tiebreaker_cmp(const struct lfa_tiebreaker
*a
,
125 const struct lfa_tiebreaker
*b
)
127 if (a
->index
< b
->index
)
129 if (a
->index
> b
->index
)
132 return a
->type
- b
->type
;
136 * Initialize list of LFA tie-breakers.
138 * @param area IS-IS area
139 * @param level IS-IS level
141 void isis_lfa_tiebreakers_init(struct isis_area
*area
, int level
)
143 lfa_tiebreaker_tree_init(&area
->lfa_tiebreakers
[level
- 1]);
147 * Clear list of LFA tie-breakers, releasing all allocated memory.
149 * @param area IS-IS area
150 * @param level IS-IS level
152 void isis_lfa_tiebreakers_clear(struct isis_area
*area
, int level
)
154 while (lfa_tiebreaker_tree_count(&area
->lfa_tiebreakers
[level
- 1])
156 struct lfa_tiebreaker
*tie_b
;
158 tie_b
= lfa_tiebreaker_tree_first(
159 &area
->lfa_tiebreakers
[level
- 1]);
160 isis_lfa_tiebreaker_delete(area
, level
, tie_b
);
165 * Add new LFA tie-breaker to list of LFA tie-breakers.
167 * @param area IS-IS area
168 * @param level IS-IS level
169 * @param index LFA tie-breaker index
170 * @param type LFA tie-breaker type
172 * @return Pointer to new LFA tie-breaker structure.
174 struct lfa_tiebreaker
*isis_lfa_tiebreaker_add(struct isis_area
*area
,
175 int level
, uint8_t index
,
176 enum lfa_tiebreaker_type type
)
178 struct lfa_tiebreaker
*tie_b
;
180 tie_b
= XCALLOC(MTYPE_ISIS_LFA_TIEBREAKER
, sizeof(*tie_b
));
181 tie_b
->index
= index
;
184 lfa_tiebreaker_tree_add(&area
->lfa_tiebreakers
[level
- 1], tie_b
);
190 * Remove LFA tie-breaker from list of LFA tie-breakers.
192 * @param area IS-IS area
193 * @param level IS-IS level
194 * @param tie_b Pointer to LFA tie-breaker structure
196 void isis_lfa_tiebreaker_delete(struct isis_area
*area
, int level
,
197 struct lfa_tiebreaker
*tie_b
)
199 lfa_tiebreaker_tree_del(&area
->lfa_tiebreakers
[level
- 1], tie_b
);
200 XFREE(MTYPE_ISIS_LFA_TIEBREAKER
, tie_b
);
203 static bool lfa_excl_interface_hash_cmp(const void *value1
, const void *value2
)
205 return strmatch(value1
, value2
);
208 static unsigned int lfa_excl_interface_hash_make(const void *value
)
210 return string_hash_make(value
);
213 static void *lfa_excl_interface_hash_alloc(void *p
)
215 return XSTRDUP(MTYPE_ISIS_LFA_EXCL_IFACE
, p
);
218 static void lfa_excl_interface_hash_free(void *arg
)
220 XFREE(MTYPE_ISIS_LFA_EXCL_IFACE
, arg
);
224 * Initialize hash table of LFA excluded interfaces.
226 * @param circuit IS-IS interface
227 * @param level IS-IS level
229 void isis_lfa_excluded_ifaces_init(struct isis_circuit
*circuit
, int level
)
231 circuit
->lfa_excluded_ifaces
[level
- 1] = hash_create(
232 lfa_excl_interface_hash_make
, lfa_excl_interface_hash_cmp
,
233 "LFA Excluded Interfaces");
237 * Clear hash table of LFA excluded interfaces, releasing all allocated memory.
239 * @param nodes List of SPF nodes
241 void isis_lfa_excluded_ifaces_clear(struct isis_circuit
*circuit
, int level
)
243 hash_clean(circuit
->lfa_excluded_ifaces
[level
- 1],
244 lfa_excl_interface_hash_free
);
248 * Add new interface to hash table of excluded interfaces.
250 * @param circuit IS-IS interface
251 * @param level IS-IS level
252 * @param ifname Excluded interface name
254 void isis_lfa_excluded_iface_add(struct isis_circuit
*circuit
, int level
,
257 (void)hash_get(circuit
->lfa_excluded_ifaces
[level
- 1], (char *)ifname
,
258 lfa_excl_interface_hash_alloc
);
262 * Remove interface from hash table of excluded interfaces.
264 * @param circuit IS-IS interface
265 * @param level IS-IS level
266 * @param ifname Excluded interface name
268 void isis_lfa_excluded_iface_delete(struct isis_circuit
*circuit
, int level
,
273 found
= hash_lookup(circuit
->lfa_excluded_ifaces
[level
- 1],
276 hash_release(circuit
->lfa_excluded_ifaces
[level
- 1], found
);
277 lfa_excl_interface_hash_free(found
);
282 * Lookup excluded interface.
284 * @param circuit IS-IS interface
285 * @param level IS-IS level
286 * @param ifname Excluded interface name
288 bool isis_lfa_excluded_iface_check(struct isis_circuit
*circuit
, int level
,
291 return hash_lookup(circuit
->lfa_excluded_ifaces
[level
- 1],
296 * Check if a given IS-IS adjacency needs to be excised when computing the SPF
297 * post-convergence tree.
299 * @param spftree IS-IS SPF tree
300 * @param id Adjacency System ID (or LAN ID of the designated router
301 * for broadcast interfaces)
303 * @return true if the adjacency needs to be excised, false
306 bool isis_lfa_excise_adj_check(const struct isis_spftree
*spftree
,
309 const struct lfa_protected_resource
*resource
;
311 if (spftree
->type
!= SPF_TYPE_RLFA
&& spftree
->type
!= SPF_TYPE_TI_LFA
)
315 * Adjacencies formed over the failed interface should be excised both
316 * when using link and node protection.
318 resource
= &spftree
->lfa
.protected_resource
;
319 if (!memcmp(resource
->adjacency
, id
, ISIS_SYS_ID_LEN
+ 1))
326 * Check if a given IS-IS node needs to be excised when computing the SPF
327 * post-convergence tree.
329 * @param spftree IS-IS SPF tree
330 * @param id Node System ID
332 * @return true if the node needs to be excised, false otherwise
334 bool isis_lfa_excise_node_check(const struct isis_spftree
*spftree
,
337 const struct lfa_protected_resource
*resource
;
339 if (spftree
->type
!= SPF_TYPE_TI_LFA
)
343 * When using node protection, nodes reachable over the failed interface
346 resource
= &spftree
->lfa
.protected_resource
;
347 if (resource
->type
== LFA_LINK_PROTECTION
)
350 if (isis_spf_node_find(&resource
->nodes
, id
))
356 struct tilfa_find_pnode_prefix_sid_args
{
361 static int tilfa_find_pnode_prefix_sid_cb(const struct prefix
*prefix
,
362 uint32_t metric
, bool external
,
363 struct isis_subtlvs
*subtlvs
,
366 struct tilfa_find_pnode_prefix_sid_args
*args
= arg
;
367 struct isis_prefix_sid
*psid
;
369 if (!subtlvs
|| subtlvs
->prefix_sids
.count
== 0)
370 return LSP_ITER_CONTINUE
;
372 for (psid
= (struct isis_prefix_sid
*)subtlvs
->prefix_sids
.head
; psid
;
374 /* Require the node flag to be set. */
375 if (!CHECK_FLAG(psid
->flags
, ISIS_PREFIX_SID_NODE
))
377 if (psid
->algorithm
!= args
->algorithm
)
379 args
->sid_index
= psid
->value
;
380 return LSP_ITER_STOP
;
382 return LSP_ITER_CONTINUE
;
385 /* Find Prefix-SID associated to a System ID. */
386 static uint32_t tilfa_find_pnode_prefix_sid(struct isis_spftree
*spftree
,
387 const uint8_t *sysid
)
389 struct isis_lsp
*lsp
;
390 struct tilfa_find_pnode_prefix_sid_args args
;
392 lsp
= isis_root_system_lsp(spftree
->lspdb
, sysid
);
396 args
.algorithm
= spftree
->algorithm
;
398 args
.sid_index
= UINT32_MAX
;
399 isis_lsp_iterate_ip_reach(lsp
, spftree
->family
, spftree
->mtid
,
400 tilfa_find_pnode_prefix_sid_cb
, &args
);
402 return args
.sid_index
;
405 struct tilfa_find_qnode_adj_sid_args
{
406 const uint8_t *qnode_sysid
;
410 static int tilfa_find_qnode_adj_sid_cb(const uint8_t *id
, uint32_t metric
,
412 struct isis_ext_subtlvs
*subtlvs
,
415 struct tilfa_find_qnode_adj_sid_args
*args
= arg
;
416 struct isis_adj_sid
*adj_sid
;
418 if (memcmp(id
, args
->qnode_sysid
, ISIS_SYS_ID_LEN
))
419 return LSP_ITER_CONTINUE
;
420 if (!subtlvs
|| subtlvs
->adj_sid
.count
== 0)
421 return LSP_ITER_CONTINUE
;
423 adj_sid
= (struct isis_adj_sid
*)subtlvs
->adj_sid
.head
;
424 args
->label
= adj_sid
->sid
;
426 return LSP_ITER_STOP
;
429 /* Find Adj-SID associated to a pair of System IDs. */
430 static mpls_label_t
tilfa_find_qnode_adj_sid(struct isis_spftree
*spftree
,
431 const uint8_t *source_sysid
,
432 const uint8_t *qnode_sysid
)
434 struct isis_lsp
*lsp
;
435 struct tilfa_find_qnode_adj_sid_args args
;
437 lsp
= isis_root_system_lsp(spftree
->lspdb
, source_sysid
);
439 return MPLS_INVALID_LABEL
;
441 args
.qnode_sysid
= qnode_sysid
;
442 args
.label
= MPLS_INVALID_LABEL
;
443 isis_lsp_iterate_is_reach(lsp
, spftree
->mtid
,
444 tilfa_find_qnode_adj_sid_cb
, &args
);
450 * Compute the MPLS label stack associated to a TI-LFA repair list. This
451 * needs to be computed separately for each adjacency since different
452 * neighbors can have different SRGBs.
454 static struct mpls_label_stack
*
455 tilfa_compute_label_stack(struct lspdb_head
*lspdb
,
456 const struct isis_spf_adj
*sadj
,
457 const struct list
*repair_list
)
459 struct mpls_label_stack
*label_stack
;
460 struct isis_tilfa_sid
*sid
;
461 struct listnode
*node
;
464 /* Allocate label stack. */
465 label_stack
= XCALLOC(MTYPE_ISIS_NEXTHOP_LABELS
,
466 sizeof(struct mpls_label_stack
)
467 + listcount(repair_list
)
468 * sizeof(mpls_label_t
));
469 label_stack
->num_labels
= listcount(repair_list
);
471 for (ALL_LIST_ELEMENTS_RO(repair_list
, node
, sid
)) {
472 const uint8_t *target_node
;
473 struct isis_sr_block
*srgb
;
477 case TILFA_SID_PREFIX
:
478 if (sid
->value
.index
.remote
)
479 target_node
= sid
->value
.index
.remote_sysid
;
481 target_node
= sadj
->id
;
482 srgb
= isis_sr_find_srgb(lspdb
, target_node
);
484 zlog_warn("%s: SRGB not found for node %s",
486 print_sys_hostname(target_node
));
490 /* Check if the SID index falls inside the SRGB. */
491 if (sid
->value
.index
.value
>= srgb
->range_size
) {
493 EC_ISIS_SID_OVERFLOW
,
494 "%s: SID index %u falls outside remote SRGB range",
495 __func__
, sid
->value
.index
.value
);
500 * Prefix-SID: map SID index to label value within the
503 label
= srgb
->lower_bound
+ sid
->value
.index
.value
;
506 /* Adj-SID: absolute label value can be used directly */
507 label
= sid
->value
.label
;
510 flog_err(EC_LIB_DEVELOPMENT
,
511 "%s: unknown TI-LFA SID type [%u]", __func__
,
515 label_stack
->label
[i
++] = label
;
521 XFREE(MTYPE_ISIS_NEXTHOP_LABELS
, label_stack
);
525 static int tilfa_repair_list_apply(struct isis_spftree
*spftree
,
526 struct isis_vertex
*vertex_dest
,
527 const struct isis_vertex
*vertex_pnode
,
528 const struct list
*repair_list
)
530 struct isis_vertex_adj
*vadj
;
531 struct listnode
*node
;
533 for (ALL_LIST_ELEMENTS_RO(vertex_dest
->Adj_N
, node
, vadj
)) {
534 struct isis_spf_adj
*sadj
= vadj
->sadj
;
535 struct mpls_label_stack
*label_stack
;
538 * Don't try to apply the repair list if one was already applied
539 * before (can't have ECMP past the P-node).
541 if (vadj
->label_stack
)
544 if (!isis_vertex_adj_exists(spftree
, vertex_pnode
, sadj
))
547 label_stack
= tilfa_compute_label_stack(spftree
->lspdb
, sadj
,
550 char buf
[VID2STR_BUFFER
];
552 vid2string(vertex_dest
, buf
, sizeof(buf
));
554 "%s: %s %s adjacency %s: failed to compute label stack",
555 __func__
, vtype2string(vertex_dest
->type
), buf
,
556 print_sys_hostname(sadj
->id
));
560 vadj
->label_stack
= label_stack
;
567 * Check if a node belongs to the extended P-space corresponding to a given
570 static bool lfa_ext_p_space_check(const struct isis_spftree
*spftree_pc
,
571 const struct isis_vertex
*vertex_dest
,
572 const struct isis_vertex
*vertex
)
574 struct isis_spftree
*spftree_old
= spftree_pc
->lfa
.old
.spftree
;
575 struct isis_vertex_adj
*vadj
;
576 struct listnode
*node
;
578 /* Check the local P-space first. */
579 if (isis_spf_node_find(&spftree_pc
->lfa
.p_space
, vertex
->N
.id
))
583 * Check the P-space of the adjacent routers used to reach the
586 for (ALL_LIST_ELEMENTS_RO(vertex_dest
->Adj_N
, node
, vadj
)) {
587 struct isis_spf_adj
*sadj
= vadj
->sadj
;
588 struct isis_spf_node
*adj_node
;
591 isis_spf_node_find(&spftree_old
->adj_nodes
, sadj
->id
);
595 if (isis_spf_node_find(&adj_node
->lfa
.p_space
, vertex
->N
.id
))
602 /* Check if a node belongs to the Q-space. */
603 static bool lfa_q_space_check(const struct isis_spftree
*spftree_pc
,
604 const struct isis_vertex
*vertex
)
606 return isis_spf_node_find(&spftree_pc
->lfa
.q_space
, vertex
->N
.id
);
609 /* This is a recursive function. */
610 static int tilfa_build_repair_list(struct isis_spftree
*spftree_pc
,
611 struct isis_vertex
*vertex_dest
,
612 const struct isis_vertex
*vertex
,
613 const struct isis_vertex
*vertex_child
,
614 struct isis_spf_nodes
*used_pnodes
,
615 struct list
*repair_list
)
617 struct isis_vertex
*pvertex
;
618 struct listnode
*node
;
619 bool is_pnode
, is_qnode
;
620 char buf
[VID2STR_BUFFER
];
621 struct isis_tilfa_sid sid_dest
= {}, sid_qnode
= {}, sid_pnode
= {};
623 mpls_label_t label_qnode
;
626 vid2string(vertex
, buf
, sizeof(buf
));
627 zlog_debug("ISIS-LFA: vertex %s %s", vtype2string(vertex
->type
),
631 /* Push original Prefix-SID label when necessary. */
632 if (VTYPE_IP(vertex
->type
) && vertex
->N
.ip
.sr
.present
) {
633 pvertex
= listnode_head(vertex
->parents
);
636 sid_index
= vertex
->N
.ip
.sr
.sid
.value
;
639 "ISIS-LFA: pushing Prefix-SID to %pFX (index %u)",
640 &vertex
->N
.ip
.p
.dest
, sid_index
);
641 sid_dest
.type
= TILFA_SID_PREFIX
;
642 sid_dest
.value
.index
.value
= sid_index
;
643 sid_dest
.value
.index
.remote
= true;
644 memcpy(sid_dest
.value
.index
.remote_sysid
, pvertex
->N
.id
,
645 sizeof(sid_dest
.value
.index
.remote_sysid
));
646 listnode_add_head(repair_list
, &sid_dest
);
651 if (vertex
->type
!= VTYPE_NONPSEUDO_IS
652 && vertex
->type
!= VTYPE_NONPSEUDO_TE_IS
)
654 if (!VTYPE_IS(vertex_child
->type
))
657 /* Check if node is part of the extended P-space and/or Q-space. */
658 is_pnode
= lfa_ext_p_space_check(spftree_pc
, vertex_dest
, vertex
);
659 is_qnode
= lfa_q_space_check(spftree_pc
, vertex
);
661 /* Push Adj-SID label when necessary. */
663 || spftree_pc
->lfa
.protected_resource
.type
== LFA_NODE_PROTECTION
)
666 * If vertex is the penultimate hop router, then pushing an
667 * Adj-SID towards the final hop means that the No-PHP flag of
668 * the original Prefix-SID must be honored. We do that by
669 * removing the previously added Prefix-SID from the repair list
670 * when those conditions are met.
672 if (vertex
->depth
== (vertex_dest
->depth
- 2)
673 && VTYPE_IP(vertex_dest
->type
)
674 && vertex_dest
->N
.ip
.sr
.present
675 && !CHECK_FLAG(vertex_dest
->N
.ip
.sr
.sid
.flags
,
676 ISIS_PREFIX_SID_NO_PHP
)) {
677 list_delete_all_node(repair_list
);
680 label_qnode
= tilfa_find_qnode_adj_sid(spftree_pc
, vertex
->N
.id
,
682 if (label_qnode
== MPLS_INVALID_LABEL
) {
683 zlog_warn("ISIS-LFA: failed to find %s->%s Adj-SID",
684 print_sys_hostname(vertex
->N
.id
),
685 print_sys_hostname(vertex_child
->N
.id
));
690 "ISIS-LFA: pushing %s->%s Adj-SID (label %u)",
691 print_sys_hostname(vertex
->N
.id
),
692 print_sys_hostname(vertex_child
->N
.id
),
694 sid_qnode
.type
= TILFA_SID_ADJ
;
695 sid_qnode
.value
.label
= label_qnode
;
696 listnode_add_head(repair_list
, &sid_qnode
);
699 /* Push Prefix-SID label when necessary. */
701 /* The same P-node can't be used more than once. */
702 if (isis_spf_node_find(used_pnodes
, vertex
->N
.id
)) {
705 "ISIS-LFA: skipping already used P-node");
708 isis_spf_node_new(used_pnodes
, vertex
->N
.id
);
713 "ISIS-LFA: destination is within Ext-P-Space");
718 tilfa_find_pnode_prefix_sid(spftree_pc
, vertex
->N
.id
);
719 if (sid_index
== UINT32_MAX
) {
721 "ISIS-LFA: failed to find Prefix-SID corresponding to PQ-node %s",
722 print_sys_hostname(vertex
->N
.id
));
728 "ISIS-LFA: pushing Node-SID to %s (index %u)",
729 print_sys_hostname(vertex
->N
.id
), sid_index
);
730 sid_pnode
.type
= TILFA_SID_PREFIX
;
731 sid_pnode
.value
.index
.value
= sid_index
;
732 listnode_add_head(repair_list
, &sid_pnode
);
734 /* Apply repair list. */
735 if (spftree_pc
->area
->srdb
.config
.msd
736 && listcount(repair_list
)
737 > spftree_pc
->area
->srdb
.config
.msd
) {
739 "ISIS-LFA: list of repair segments exceeds locally configured MSD (%u > %u)",
740 listcount(repair_list
),
741 spftree_pc
->area
->srdb
.config
.msd
);
744 if (tilfa_repair_list_apply(spftree_pc
, vertex_dest
, vertex
,
752 for (ALL_LIST_ELEMENTS_RO(vertex
->parents
, node
, pvertex
)) {
753 struct list
*repair_list_parent
;
757 ecmp
= (listcount(vertex
->parents
) > 1) ? true : false;
758 repair_list_parent
= ecmp
? list_dup(repair_list
) : repair_list
;
759 ret
= tilfa_build_repair_list(spftree_pc
, vertex_dest
, pvertex
,
763 list_delete(&repair_list_parent
);
771 static const char *lfa_protection_type2str(enum lfa_protection_type type
)
774 case LFA_LINK_PROTECTION
:
775 return "link protection";
776 case LFA_NODE_PROTECTION
:
777 return "node protection";
779 return "unknown protection type";
784 lfa_protected_resource2str(const struct lfa_protected_resource
*resource
)
786 const uint8_t *fail_id
;
787 static char buffer
[128];
789 fail_id
= resource
->adjacency
;
790 snprintf(buffer
, sizeof(buffer
), "%s.%u's failure (%s)",
791 print_sys_hostname(fail_id
), LSP_PSEUDO_ID(fail_id
),
792 lfa_protection_type2str(resource
->type
));
798 spf_adj_check_is_affected(const struct isis_spf_adj
*sadj
,
799 const struct lfa_protected_resource
*resource
,
800 const uint8_t *root_sysid
, bool reverse
)
802 if (!!CHECK_FLAG(sadj
->flags
, F_ISIS_SPF_ADJ_BROADCAST
)
803 != !!LSP_PSEUDO_ID(resource
->adjacency
))
806 if (CHECK_FLAG(sadj
->flags
, F_ISIS_SPF_ADJ_BROADCAST
)) {
807 if (!memcmp(sadj
->lan
.desig_is_id
, resource
->adjacency
,
808 ISIS_SYS_ID_LEN
+ 1))
812 && !memcmp(sadj
->id
, resource
->adjacency
, ISIS_SYS_ID_LEN
))
814 if (reverse
&& !memcmp(sadj
->id
, root_sysid
, ISIS_SYS_ID_LEN
))
821 /* Check if the given vertex is affected by a given local failure. */
823 spf_vertex_check_is_affected(const struct isis_vertex
*vertex
,
824 const uint8_t *root_sysid
,
825 const struct lfa_protected_resource
*resource
)
827 struct isis_vertex_adj
*vadj
;
828 struct listnode
*node
;
829 size_t affected_nhs
= 0;
831 /* Local routes don't need protection. */
832 if (VTYPE_IP(vertex
->type
) && vertex
->depth
== 1)
835 for (ALL_LIST_ELEMENTS_RO(vertex
->Adj_N
, node
, vadj
)) {
836 struct isis_spf_adj
*sadj
= vadj
->sadj
;
838 if (spf_adj_check_is_affected(sadj
, resource
, root_sysid
,
844 * No need to compute backup paths for ECMP routes, except if all
845 * primary nexthops share the same broadcast interface.
847 if (listcount(vertex
->Adj_N
) == affected_nhs
)
853 /* Check if a given RLFA/TI-LFA post-convergence SPF vertex needs protection. */
854 static bool lfa_check_needs_protection(const struct isis_spftree
*spftree_pc
,
855 const struct isis_vertex
*vertex
)
857 struct isis_vertex
*vertex_old
;
859 /* Only local adjacencies need TI-LFA Adj-SID protection. */
860 if (spftree_pc
->type
== SPF_TYPE_TI_LFA
&& VTYPE_IS(vertex
->type
)
861 && !isis_adj_find(spftree_pc
->area
, spftree_pc
->level
,
865 vertex_old
= isis_find_vertex(&spftree_pc
->lfa
.old
.spftree
->paths
,
866 &vertex
->N
, vertex
->type
);
870 /* Skip vertex if it's already protected by local LFA. */
871 if (CHECK_FLAG(vertex_old
->flags
, F_ISIS_VERTEX_LFA_PROTECTED
))
874 return spf_vertex_check_is_affected(
875 vertex_old
, spftree_pc
->sysid
,
876 &spftree_pc
->lfa
.protected_resource
);
880 * Check if the given SPF vertex needs protection and, if so, compute and
881 * install the corresponding repair paths.
883 * @param spftree_pc The post-convergence SPF tree
884 * @param vertex IS-IS SPF vertex to check
886 * @return 0 if the vertex needs to be protected, -1 otherwise
888 int isis_tilfa_check(struct isis_spftree
*spftree_pc
,
889 struct isis_vertex
*vertex
)
891 struct isis_spf_nodes used_pnodes
;
892 char buf
[VID2STR_BUFFER
];
893 struct list
*repair_list
;
896 if (!spftree_pc
->area
->srdb
.enabled
)
899 if (!lfa_check_needs_protection(spftree_pc
, vertex
)) {
902 "ISIS-LFA: %s %s unaffected by %s",
903 vtype2string(vertex
->type
),
904 vid2string(vertex
, buf
, sizeof(buf
)),
905 lfa_protected_resource2str(
906 &spftree_pc
->lfa
.protected_resource
));
912 * Check if the route/adjacency was already covered by node protection.
914 if (VTYPE_IS(vertex
->type
)) {
915 struct isis_adjacency
*adj
;
917 adj
= isis_adj_find(spftree_pc
->area
, spftree_pc
->level
,
920 && isis_sr_adj_sid_find(adj
, spftree_pc
->family
,
921 ISIS_SR_LAN_BACKUP
)) {
924 "ISIS-LFA: %s %s already covered by node protection",
925 vtype2string(vertex
->type
),
926 vid2string(vertex
, buf
, sizeof(buf
)));
931 if (VTYPE_IP(vertex
->type
)) {
932 struct route_table
*route_table
;
934 route_table
= spftree_pc
->lfa
.old
.spftree
->route_table_backup
;
935 if (route_node_lookup(route_table
, &vertex
->N
.ip
.p
.dest
)) {
938 "ISIS-LFA: %s %s already covered by node protection",
939 vtype2string(vertex
->type
),
940 vid2string(vertex
, buf
, sizeof(buf
)));
948 "ISIS-LFA: computing repair path(s) of %s %s w.r.t %s",
949 vtype2string(vertex
->type
),
950 vid2string(vertex
, buf
, sizeof(buf
)),
951 lfa_protected_resource2str(
952 &spftree_pc
->lfa
.protected_resource
));
954 /* Create base repair list. */
955 repair_list
= list_new();
957 isis_spf_node_list_init(&used_pnodes
);
958 ret
= tilfa_build_repair_list(spftree_pc
, vertex
, vertex
, NULL
,
959 &used_pnodes
, repair_list
);
960 isis_spf_node_list_clear(&used_pnodes
);
961 list_delete(&repair_list
);
964 "ISIS-LFA: failed to compute repair path(s) of %s %s w.r.t %s",
965 vtype2string(vertex
->type
),
966 vid2string(vertex
, buf
, sizeof(buf
)),
967 lfa_protected_resource2str(
968 &spftree_pc
->lfa
.protected_resource
));
974 spf_adj_node_is_affected(struct isis_spf_node
*adj_node
,
975 const struct lfa_protected_resource
*resource
,
976 const uint8_t *root_sysid
)
978 struct isis_spf_adj
*sadj
;
979 struct listnode
*node
;
981 for (ALL_LIST_ELEMENTS_RO(adj_node
->adjacencies
, node
, sadj
)) {
982 if (sadj
->metric
!= adj_node
->best_metric
)
984 if (spf_adj_check_is_affected(sadj
, resource
, root_sysid
,
992 static bool vertex_is_affected(struct isis_spftree
*spftree_root
,
993 const struct isis_spf_nodes
*adj_nodes
,
994 bool p_space
, const struct isis_vertex
*vertex
,
995 const struct lfa_protected_resource
*resource
)
997 struct isis_vertex
*pvertex
;
998 struct listnode
*node
, *vnode
;
1000 for (ALL_LIST_ELEMENTS_RO(vertex
->parents
, node
, pvertex
)) {
1001 struct isis_spftree
*spftree_parent
;
1002 struct isis_vertex
*vertex_child
;
1003 struct isis_vertex_adj
*vadj
;
1004 bool reverse
= false;
1006 if (p_space
&& resource
->type
== LFA_NODE_PROTECTION
) {
1007 if (isis_spf_node_find(&resource
->nodes
, vertex
->N
.id
))
1012 /* Check if either the vertex or its parent is the root node. */
1013 if (memcmp(vertex
->N
.id
, spftree_root
->sysid
, ISIS_SYS_ID_LEN
)
1014 && memcmp(pvertex
->N
.id
, spftree_root
->sysid
,
1018 /* Get SPT of the parent vertex. */
1019 if (!memcmp(pvertex
->N
.id
, spftree_root
->sysid
,
1021 spftree_parent
= spftree_root
;
1023 struct isis_spf_node
*adj_node
;
1025 adj_node
= isis_spf_node_find(adj_nodes
, pvertex
->N
.id
);
1027 spftree_parent
= adj_node
->lfa
.spftree
;
1028 assert(spftree_parent
);
1032 /* Get paths pvertex uses to reach vertex. */
1033 vertex_child
= isis_find_vertex(&spftree_parent
->paths
,
1034 &vertex
->N
, vertex
->type
);
1038 /* Check if any of these paths use the protected resource. */
1039 for (ALL_LIST_ELEMENTS_RO(vertex_child
->Adj_N
, vnode
, vadj
))
1040 if (spf_adj_check_is_affected(vadj
->sadj
, resource
,
1041 spftree_root
->sysid
,
1046 if (vertex_is_affected(spftree_root
, adj_nodes
, p_space
,
1054 /* Calculate set of nodes reachable without using the protected interface. */
1055 static void lfa_calc_reach_nodes(struct isis_spftree
*spftree
,
1056 struct isis_spftree
*spftree_root
,
1057 const struct isis_spf_nodes
*adj_nodes
,
1059 const struct lfa_protected_resource
*resource
,
1060 struct isis_spf_nodes
*nodes
)
1062 struct isis_vertex
*vertex
;
1063 struct listnode
*node
;
1065 for (ALL_QUEUE_ELEMENTS_RO(&spftree
->paths
, node
, vertex
)) {
1066 char buf
[VID2STR_BUFFER
];
1068 if (!VTYPE_IS(vertex
->type
))
1071 /* Skip root node. */
1072 if (!memcmp(vertex
->N
.id
, spftree_root
->sysid
, ISIS_SYS_ID_LEN
))
1075 /* Don't add the same node twice. */
1076 if (isis_spf_node_find(nodes
, vertex
->N
.id
))
1079 if (!vertex_is_affected(spftree_root
, adj_nodes
, p_space
,
1080 vertex
, resource
)) {
1083 "ISIS-LFA: adding %s",
1084 vid2string(vertex
, buf
, sizeof(buf
)));
1086 isis_spf_node_new(nodes
, vertex
->N
.id
);
1092 * Helper function used to create an SPF tree structure and run reverse SPF on
1095 * @param spftree IS-IS SPF tree
1097 * @return Pointer to new SPF tree structure.
1099 struct isis_spftree
*isis_spf_reverse_run(const struct isis_spftree
*spftree
)
1101 struct isis_spftree
*spftree_reverse
;
1103 spftree_reverse
= isis_spftree_new(
1104 spftree
->area
, spftree
->lspdb
, spftree
->sysid
, spftree
->level
,
1105 spftree
->tree_id
, SPF_TYPE_REVERSE
,
1106 F_SPFTREE_NO_ADJACENCIES
| F_SPFTREE_NO_ROUTES
,
1107 spftree
->algorithm
);
1108 isis_run_spf(spftree_reverse
);
1110 return spftree_reverse
;
1114 * Calculate the Extended P-space and Q-space associated to a given link
1117 static void lfa_calc_pq_spaces(struct isis_spftree
*spftree_pc
,
1118 const struct lfa_protected_resource
*resource
)
1120 struct isis_spftree
*spftree
;
1121 struct isis_spftree
*spftree_reverse
;
1122 struct isis_spf_nodes
*adj_nodes
;
1123 struct isis_spf_node
*adj_node
;
1125 /* Obtain pre-failure SPTs and list of adjacent nodes. */
1126 spftree
= spftree_pc
->lfa
.old
.spftree
;
1127 spftree_reverse
= spftree_pc
->lfa
.old
.spftree_reverse
;
1128 adj_nodes
= &spftree
->adj_nodes
;
1131 zlog_debug("ISIS-LFA: computing P-space (self)");
1132 lfa_calc_reach_nodes(spftree
, spftree
, adj_nodes
, true, resource
,
1133 &spftree_pc
->lfa
.p_space
);
1135 RB_FOREACH (adj_node
, isis_spf_nodes
, adj_nodes
) {
1136 if (spf_adj_node_is_affected(adj_node
, resource
,
1139 zlog_debug("ISIS-LFA: computing Q-space (%s)",
1140 print_sys_hostname(adj_node
->sysid
));
1143 * Compute the reverse SPF in the behalf of the node
1144 * adjacent to the failure, if we haven't done that
1147 if (!adj_node
->lfa
.spftree_reverse
)
1148 adj_node
->lfa
.spftree_reverse
=
1149 isis_spf_reverse_run(
1150 adj_node
->lfa
.spftree
);
1152 lfa_calc_reach_nodes(adj_node
->lfa
.spftree_reverse
,
1153 spftree_reverse
, adj_nodes
, false,
1155 &spftree_pc
->lfa
.q_space
);
1158 zlog_debug("ISIS-LFA: computing P-space (%s)",
1159 print_sys_hostname(adj_node
->sysid
));
1160 lfa_calc_reach_nodes(adj_node
->lfa
.spftree
, spftree
,
1161 adj_nodes
, true, resource
,
1162 &adj_node
->lfa
.p_space
);
1168 * Compute the TI-LFA backup paths for a given protected interface.
1170 * @param area IS-IS area
1171 * @param spftree IS-IS SPF tree
1172 * @param spftree_reverse IS-IS Reverse SPF tree
1173 * @param resource Protected resource
1175 * @return Pointer to the post-convergence SPF tree
1177 struct isis_spftree
*isis_tilfa_compute(struct isis_area
*area
,
1178 struct isis_spftree
*spftree
,
1179 struct isis_spftree
*spftree_reverse
,
1180 struct lfa_protected_resource
*resource
)
1182 struct isis_spftree
*spftree_pc
;
1183 struct isis_spf_node
*adj_node
;
1186 zlog_debug("ISIS-LFA: computing TI-LFAs for %s",
1187 lfa_protected_resource2str(resource
));
1189 /* Populate list of nodes affected by link failure. */
1190 if (resource
->type
== LFA_NODE_PROTECTION
) {
1191 isis_spf_node_list_init(&resource
->nodes
);
1192 RB_FOREACH (adj_node
, isis_spf_nodes
, &spftree
->adj_nodes
) {
1193 if (spf_adj_node_is_affected(adj_node
, resource
,
1195 isis_spf_node_new(&resource
->nodes
,
1200 /* Create post-convergence SPF tree. */
1201 spftree_pc
= isis_spftree_new(area
, spftree
->lspdb
, spftree
->sysid
,
1202 spftree
->level
, spftree
->tree_id
,
1203 SPF_TYPE_TI_LFA
, spftree
->flags
,
1204 spftree
->algorithm
);
1205 spftree_pc
->lfa
.old
.spftree
= spftree
;
1206 spftree_pc
->lfa
.old
.spftree_reverse
= spftree_reverse
;
1207 spftree_pc
->lfa
.protected_resource
= *resource
;
1209 /* Compute the extended P-space and Q-space. */
1210 lfa_calc_pq_spaces(spftree_pc
, resource
);
1214 "ISIS-LFA: computing the post convergence SPT w.r.t. %s",
1215 lfa_protected_resource2str(resource
));
1217 /* Re-run SPF in the local node to find the post-convergence paths. */
1218 isis_run_spf(spftree_pc
);
1220 /* Clear list of nodes affeted by link failure. */
1221 if (resource
->type
== LFA_NODE_PROTECTION
)
1222 isis_spf_node_list_clear(&resource
->nodes
);
1228 * Run forward SPF on all adjacent routers.
1230 * @param spftree IS-IS SPF tree
1232 * @return 0 on success, -1 otherwise
1234 int isis_spf_run_neighbors(struct isis_spftree
*spftree
)
1236 struct isis_lsp
*lsp
;
1237 struct isis_spf_node
*adj_node
;
1239 lsp
= isis_root_system_lsp(spftree
->lspdb
, spftree
->sysid
);
1243 RB_FOREACH (adj_node
, isis_spf_nodes
, &spftree
->adj_nodes
) {
1245 zlog_debug("ISIS-LFA: running SPF on neighbor %s",
1246 print_sys_hostname(adj_node
->sysid
));
1248 /* Compute the SPT on behalf of the neighbor. */
1249 adj_node
->lfa
.spftree
= isis_spftree_new(
1250 spftree
->area
, spftree
->lspdb
, adj_node
->sysid
,
1251 spftree
->level
, spftree
->tree_id
, SPF_TYPE_FORWARD
,
1252 F_SPFTREE_NO_ADJACENCIES
| F_SPFTREE_NO_ROUTES
,
1253 spftree
->algorithm
);
1254 isis_run_spf(adj_node
->lfa
.spftree
);
1260 /* Find Router ID of PQ node. */
1261 static struct in_addr
*rlfa_pq_node_rtr_id(struct isis_spftree
*spftree
,
1262 const struct isis_vertex
*vertex_pq
)
1264 struct isis_lsp
*lsp
;
1266 lsp
= isis_root_system_lsp(spftree
->lspdb
, vertex_pq
->N
.id
);
1270 if (lsp
->tlvs
->router_cap
->router_id
.s_addr
== INADDR_ANY
)
1273 return &lsp
->tlvs
->router_cap
->router_id
;
1276 /* Find PQ node by intersecting the P/Q spaces. This is a recursive function. */
1277 static const struct in_addr
*
1278 rlfa_find_pq_node(struct isis_spftree
*spftree_pc
,
1279 struct isis_vertex
*vertex_dest
,
1280 const struct isis_vertex
*vertex
,
1281 const struct isis_vertex
*vertex_child
)
1283 struct isis_area
*area
= spftree_pc
->area
;
1284 int level
= spftree_pc
->level
;
1285 struct isis_vertex
*pvertex
;
1286 struct listnode
*node
;
1287 bool is_pnode
, is_qnode
;
1291 if (vertex
->type
!= VTYPE_NONPSEUDO_IS
1292 && vertex
->type
!= VTYPE_NONPSEUDO_TE_IS
)
1294 if (!VTYPE_IS(vertex_child
->type
))
1295 vertex_child
= NULL
;
1297 /* Check if node is part of the extended P-space and/or Q-space. */
1298 is_pnode
= lfa_ext_p_space_check(spftree_pc
, vertex_dest
, vertex
);
1299 is_qnode
= lfa_q_space_check(spftree_pc
, vertex
);
1301 if (is_pnode
&& is_qnode
) {
1302 const struct in_addr
*rtr_id_pq
;
1303 uint32_t max_metric
;
1304 struct prefix_list
*plist
= NULL
;
1306 rtr_id_pq
= rlfa_pq_node_rtr_id(spftree_pc
, vertex
);
1309 char buf
[VID2STR_BUFFER
];
1311 vid2string(vertex
, buf
, sizeof(buf
));
1313 "ISIS-LFA: tentative PQ node (%s %s) doesn't have a router-ID",
1314 vtype2string(vertex
->type
), buf
);
1319 max_metric
= spftree_pc
->lfa
.remote
.max_metric
;
1320 if (max_metric
&& vertex
->d_N
> max_metric
) {
1323 "ISIS-LFA: skipping PQ node %pI4 (maximum metric)",
1328 plist
= area
->rlfa_plist
[level
- 1];
1333 p
.prefixlen
= IPV4_MAX_BITLEN
;
1334 p
.u
.prefix4
= *rtr_id_pq
;
1335 if (prefix_list_apply(plist
, &p
) == PREFIX_DENY
) {
1338 "ISIS-LFA: PQ node %pI4 filtered by prefix-list",
1345 zlog_debug("ISIS-LFA: found PQ node: %pI4", rtr_id_pq
);
1351 for (ALL_LIST_ELEMENTS_RO(vertex
->parents
, node
, pvertex
)) {
1352 const struct in_addr
*rtr_id_pq
;
1354 rtr_id_pq
= rlfa_find_pq_node(spftree_pc
, vertex_dest
, pvertex
,
1363 int rlfa_cmp(const struct rlfa
*a
, const struct rlfa
*b
)
1365 return prefix_cmp(&a
->prefix
, &b
->prefix
);
1368 static struct rlfa
*rlfa_add(struct isis_spftree
*spftree
,
1369 struct isis_vertex
*vertex
,
1370 struct in_addr pq_address
)
1374 assert(VTYPE_IP(vertex
->type
));
1375 rlfa
= XCALLOC(MTYPE_ISIS_RLFA
, sizeof(*rlfa
));
1376 rlfa
->prefix
= vertex
->N
.ip
.p
.dest
;
1377 rlfa
->vertex
= vertex
;
1378 rlfa
->pq_address
= pq_address
;
1379 rlfa_tree_add(&spftree
->lfa
.remote
.rlfas
, rlfa
);
1384 static void rlfa_delete(struct isis_spftree
*spftree
, struct rlfa
*rlfa
)
1386 rlfa_tree_del(&spftree
->lfa
.remote
.rlfas
, rlfa
);
1387 XFREE(MTYPE_ISIS_RLFA
, rlfa
);
1390 static struct rlfa
*rlfa_lookup(struct isis_spftree
*spftree
,
1391 union prefixconstptr pu
)
1396 return rlfa_tree_find(&spftree
->lfa
.remote
.rlfas
, &s
);
1399 static void isis_area_verify_routes_cb(struct event
*thread
)
1401 struct isis_area
*area
= EVENT_ARG(thread
);
1404 zlog_debug("ISIS-LFA: updating RLFAs in the RIB");
1406 isis_area_verify_routes(area
);
1409 static mpls_label_t
rlfa_nexthop_label(struct isis_spftree
*spftree
,
1410 struct isis_vertex_adj
*vadj
,
1411 struct zapi_rlfa_response
*response
)
1413 struct isis_spf_adj
*sadj
= vadj
->sadj
;
1414 struct isis_adjacency
*adj
= sadj
->adj
;
1417 * Special case to make unit tests work (use implicit-null labels
1418 * instead of artifical ones).
1420 if (CHECK_FLAG(spftree
->flags
, F_SPFTREE_NO_ADJACENCIES
))
1421 return MPLS_LABEL_IMPLICIT_NULL
;
1423 for (unsigned int i
= 0; i
< response
->nexthop_num
; i
++) {
1424 switch (response
->nexthops
[i
].family
) {
1426 for (unsigned int j
= 0; j
< adj
->ipv4_address_count
;
1428 struct in_addr addr
= adj
->ipv4_addresses
[j
];
1430 if (!IPV4_ADDR_SAME(
1432 &response
->nexthops
[i
].gate
.ipv4
))
1435 return response
->nexthops
[i
].label
;
1439 for (unsigned int j
= 0; j
< adj
->ll_ipv6_count
; j
++) {
1440 struct in6_addr addr
= adj
->ll_ipv6_addrs
[j
];
1442 if (!IPV6_ADDR_SAME(
1444 &response
->nexthops
[i
].gate
.ipv6
))
1447 return response
->nexthops
[i
].label
;
1456 return MPLS_INVALID_LABEL
;
1459 int isis_rlfa_activate(struct isis_spftree
*spftree
, struct rlfa
*rlfa
,
1460 struct zapi_rlfa_response
*response
)
1462 struct isis_area
*area
= spftree
->area
;
1463 struct isis_vertex
*vertex
= rlfa
->vertex
;
1464 struct isis_vertex_adj
*vadj
;
1465 struct listnode
*node
;
1467 for (ALL_LIST_ELEMENTS_RO(vertex
->Adj_N
, node
, vadj
)) {
1468 mpls_label_t ldp_label
;
1469 struct mpls_label_stack
*label_stack
;
1470 size_t num_labels
= 0;
1473 ldp_label
= rlfa_nexthop_label(spftree
, vadj
, response
);
1474 if (ldp_label
== MPLS_INVALID_LABEL
) {
1477 "ISIS-LFA: failed to activate RLFA: missing LDP label to reach PQ node through %pSY",
1482 if (ldp_label
!= MPLS_LABEL_IMPLICIT_NULL
)
1484 if (response
->pq_label
!= MPLS_LABEL_IMPLICIT_NULL
)
1486 if (vadj
->sr
.present
1487 && vadj
->sr
.label
!= MPLS_LABEL_IMPLICIT_NULL
)
1490 /* Allocate label stack. */
1492 XCALLOC(MTYPE_ISIS_NEXTHOP_LABELS
,
1493 sizeof(struct mpls_label_stack
)
1494 + num_labels
* sizeof(mpls_label_t
));
1495 label_stack
->num_labels
= num_labels
;
1497 /* Push label allocated by the nexthop (outer label). */
1498 if (ldp_label
!= MPLS_LABEL_IMPLICIT_NULL
)
1499 label_stack
->label
[i
++] = ldp_label
;
1500 /* Push label allocated by the PQ node (inner label). */
1501 if (response
->pq_label
!= MPLS_LABEL_IMPLICIT_NULL
)
1502 label_stack
->label
[i
++] = response
->pq_label
;
1503 /* Preserve the original Prefix-SID label when it's present. */
1504 if (vadj
->sr
.present
1505 && vadj
->sr
.label
!= MPLS_LABEL_IMPLICIT_NULL
)
1506 label_stack
->label
[i
++] = vadj
->sr
.label
;
1508 vadj
->label_stack
= label_stack
;
1511 isis_route_create(&vertex
->N
.ip
.p
.dest
, &vertex
->N
.ip
.p
.src
,
1512 vertex
->d_N
, vertex
->depth
, &vertex
->N
.ip
.sr
,
1513 vertex
->Adj_N
, true, area
,
1514 spftree
->route_table_backup
);
1515 spftree
->lfa
.protection_counters
.rlfa
[vertex
->N
.ip
.priority
] += 1;
1517 EVENT_OFF(area
->t_rlfa_rib_update
);
1518 event_add_timer(master
, isis_area_verify_routes_cb
, area
, 2,
1519 &area
->t_rlfa_rib_update
);
1524 void isis_rlfa_deactivate(struct isis_spftree
*spftree
, struct rlfa
*rlfa
)
1526 struct isis_area
*area
= spftree
->area
;
1527 struct isis_vertex
*vertex
= rlfa
->vertex
;
1528 struct route_node
*rn
;
1530 rn
= route_node_lookup(spftree
->route_table_backup
, &rlfa
->prefix
);
1533 isis_route_delete(area
, rn
, spftree
->route_table_backup
);
1534 spftree
->lfa
.protection_counters
.rlfa
[vertex
->N
.ip
.priority
] -= 1;
1536 EVENT_OFF(area
->t_rlfa_rib_update
);
1537 event_add_timer(master
, isis_area_verify_routes_cb
, area
, 2,
1538 &area
->t_rlfa_rib_update
);
1541 void isis_rlfa_list_init(struct isis_spftree
*spftree
)
1543 rlfa_tree_init(&spftree
->lfa
.remote
.rlfas
);
1546 void isis_rlfa_list_clear(struct isis_spftree
*spftree
)
1548 while (rlfa_tree_count(&spftree
->lfa
.remote
.rlfas
) > 0) {
1551 rlfa
= rlfa_tree_first(&spftree
->lfa
.remote
.rlfas
);
1552 isis_rlfa_deactivate(spftree
, rlfa
);
1553 rlfa_delete(spftree
, rlfa
);
1557 void isis_rlfa_process_ldp_response(struct zapi_rlfa_response
*response
)
1560 struct isis_area
*area
;
1561 struct isis_spftree
*spftree
;
1563 enum spf_tree_id tree_id
;
1564 uint32_t spf_run_id
;
1567 if (response
->igp
.protocol
!= ZEBRA_ROUTE_ISIS
)
1570 isis
= isis_lookup_by_vrfid(response
->igp
.vrf_id
);
1574 area
= isis_area_lookup(response
->igp
.isis
.area_tag
,
1575 response
->igp
.vrf_id
);
1579 tree_id
= response
->igp
.isis
.spf
.tree_id
;
1580 if (tree_id
!= SPFTREE_IPV4
&& tree_id
!= SPFTREE_IPV6
) {
1581 zlog_warn("ISIS-LFA: invalid SPF tree ID received from LDP");
1585 level
= response
->igp
.isis
.spf
.level
;
1586 if (level
!= ISIS_LEVEL1
&& level
!= ISIS_LEVEL2
) {
1587 zlog_warn("ISIS-LFA: invalid IS-IS level received from LDP");
1591 spf_run_id
= response
->igp
.isis
.spf
.run_id
;
1592 spftree
= area
->spftree
[tree_id
][level
- 1];
1593 if (spftree
->runcount
!= spf_run_id
)
1594 /* Outdated RLFA, ignore... */
1597 rlfa
= rlfa_lookup(spftree
, &response
->destination
);
1600 "ISIS-LFA: couldn't find Remote-LFA %pFX received from LDP",
1601 &response
->destination
);
1605 if (response
->pq_label
!= MPLS_INVALID_LABEL
) {
1608 "ISIS-LFA: activating/updating RLFA for %pFX",
1611 if (isis_rlfa_activate(spftree
, rlfa
, response
) != 0)
1612 isis_rlfa_deactivate(spftree
, rlfa
);
1615 zlog_debug("ISIS-LFA: deactivating RLFA for %pFX",
1618 isis_rlfa_deactivate(spftree
, rlfa
);
1622 void isis_ldp_rlfa_handle_client_close(struct zapi_client_close_info
*info
)
1624 struct isis
*isis
= isis_lookup_by_vrfid(VRF_DEFAULT
);
1625 struct isis_area
*area
;
1626 struct listnode
*node
;
1631 /* Check if the LDP main client session closed */
1632 if (info
->proto
!= ZEBRA_ROUTE_LDP
|| info
->session_id
== 0)
1636 zlog_debug("ISIS-LFA: LDP is down, deactivating all RLFAs");
1638 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, node
, area
)) {
1639 for (int tree
= SPFTREE_IPV4
; tree
< SPFTREE_COUNT
; tree
++) {
1640 for (int level
= ISIS_LEVEL1
; level
<= ISIS_LEVELS
;
1642 struct isis_spftree
*spftree
;
1644 if (!(area
->is_type
& level
))
1646 if (!area
->spftree
[tree
][level
- 1])
1649 spftree
= area
->spftree
[tree
][level
- 1];
1650 isis_rlfa_list_clear(spftree
);
1657 * Check if the given SPF vertex needs protection and, if so, attempt to
1658 * compute a Remote LFA for it.
1660 * @param spftree_pc The post-convergence SPF tree
1661 * @param vertex IS-IS SPF vertex to check
1663 void isis_rlfa_check(struct isis_spftree
*spftree_pc
,
1664 struct isis_vertex
*vertex
)
1666 struct isis_spftree
*spftree_old
= spftree_pc
->lfa
.old
.spftree
;
1668 const struct in_addr
*rtr_id_pq
;
1669 char buf
[VID2STR_BUFFER
];
1671 if (!lfa_check_needs_protection(spftree_pc
, vertex
)) {
1674 "ISIS-LFA: %s %s unaffected by %s",
1675 vtype2string(vertex
->type
),
1676 vid2string(vertex
, buf
, sizeof(buf
)),
1677 lfa_protected_resource2str(
1678 &spftree_pc
->lfa
.protected_resource
));
1685 "ISIS-LFA: computing repair path(s) of %s %s w.r.t %s",
1686 vtype2string(vertex
->type
),
1687 vid2string(vertex
, buf
, sizeof(buf
)),
1688 lfa_protected_resource2str(
1689 &spftree_pc
->lfa
.protected_resource
));
1692 rtr_id_pq
= rlfa_find_pq_node(spftree_pc
, vertex
, vertex
, NULL
);
1695 zlog_debug("ISIS-LFA: no acceptable PQ node found");
1699 /* Store valid RLFA and store LDP label for the PQ node. */
1700 rlfa
= rlfa_add(spftree_old
, vertex
, *rtr_id_pq
);
1702 /* Register RLFA with LDP. */
1703 if (isis_zebra_rlfa_register(spftree_old
, rlfa
) != 0)
1704 rlfa_delete(spftree_old
, rlfa
);
1708 * Compute the Remote LFA backup paths for a given protected interface.
1710 * @param area IS-IS area
1711 * @param spftree IS-IS SPF tree
1712 * @param spftree_reverse IS-IS Reverse SPF tree
1713 * @param max_metric Remote LFA maximum metric
1714 * @param resource Protected resource
1716 * @return Pointer to the post-convergence SPF tree
1718 struct isis_spftree
*isis_rlfa_compute(struct isis_area
*area
,
1719 struct isis_spftree
*spftree
,
1720 struct isis_spftree
*spftree_reverse
,
1721 uint32_t max_metric
,
1722 struct lfa_protected_resource
*resource
)
1724 struct isis_spftree
*spftree_pc
;
1727 zlog_debug("ISIS-LFA: computing remote LFAs for %s",
1728 lfa_protected_resource2str(resource
));
1730 /* Create post-convergence SPF tree. */
1731 spftree_pc
= isis_spftree_new(area
, spftree
->lspdb
, spftree
->sysid
,
1732 spftree
->level
, spftree
->tree_id
,
1733 SPF_TYPE_RLFA
, spftree
->flags
,
1734 spftree
->algorithm
);
1735 spftree_pc
->lfa
.old
.spftree
= spftree
;
1736 spftree_pc
->lfa
.old
.spftree_reverse
= spftree_reverse
;
1737 spftree_pc
->lfa
.remote
.max_metric
= max_metric
;
1738 spftree_pc
->lfa
.protected_resource
= *resource
;
1740 /* Compute the extended P-space and Q-space. */
1741 lfa_calc_pq_spaces(spftree_pc
, resource
);
1745 "ISIS-LFA: computing the post convergence SPT w.r.t. %s",
1746 lfa_protected_resource2str(resource
));
1748 /* Re-run SPF in the local node to find the post-convergence paths. */
1749 isis_run_spf(spftree_pc
);
1754 /* Calculate the distance from the root node to the given IP destination. */
1755 static int lfa_calc_dist_destination(struct isis_spftree
*spftree
,
1756 const struct isis_vertex
*vertex_N
,
1759 struct isis_vertex
*vertex
, *vertex_best
= NULL
;
1761 switch (spftree
->family
) {
1763 for (int vtype
= VTYPE_IPREACH_INTERNAL
;
1764 vtype
<= VTYPE_IPREACH_TE
; vtype
++) {
1765 vertex
= isis_find_vertex(
1766 &spftree
->paths
, &vertex_N
->N
.ip
.p
.dest
, vtype
);
1770 /* Pick vertex with the best metric. */
1771 if (!vertex_best
|| vertex_best
->d_N
> vertex
->d_N
)
1772 vertex_best
= vertex
;
1776 for (int vtype
= VTYPE_IP6REACH_INTERNAL
;
1777 vtype
<= VTYPE_IP6REACH_EXTERNAL
; vtype
++) {
1778 vertex
= isis_find_vertex(
1779 &spftree
->paths
, &vertex_N
->N
.ip
.p
.dest
, vtype
);
1783 /* Pick vertex with the best metric. */
1784 if (!vertex_best
|| vertex_best
->d_N
> vertex
->d_N
)
1785 vertex_best
= vertex
;
1795 assert(VTYPE_IP(vertex_best
->type
));
1796 vertex_best
= listnode_head(vertex_best
->parents
);
1797 *distance
= vertex_best
->d_N
;
1802 /* Calculate the distance from the root node to the given node. */
1803 static int lfa_calc_dist_node(struct isis_spftree
*spftree
,
1804 const uint8_t *sysid
, uint32_t *distance
)
1806 struct isis_vertex
*vertex
, *vertex_best
= NULL
;
1808 for (int vtype
= VTYPE_PSEUDO_IS
; vtype
<= VTYPE_NONPSEUDO_TE_IS
;
1810 vertex
= isis_find_vertex(&spftree
->paths
, sysid
, vtype
);
1814 /* Pick vertex with the best metric. */
1815 if (!vertex_best
|| vertex_best
->d_N
> vertex
->d_N
)
1816 vertex_best
= vertex
;
1822 *distance
= vertex_best
->d_N
;
1828 * Check loop-free criterion (RFC 5286's inequality 1):
1829 * - Dist_opt(N, D) < Dist_opt(N, S) + Dist_opt(S, D)
1831 static bool clfa_loop_free_check(struct isis_spftree
*spftree
,
1832 struct isis_vertex
*vertex_S_D
,
1833 struct isis_spf_adj
*sadj_primary
,
1834 struct isis_spf_adj
*sadj_N
,
1835 uint32_t *path_metric
)
1837 struct isis_spf_node
*node_N
;
1842 node_N
= isis_spf_node_find(&spftree
->adj_nodes
, sadj_N
->id
);
1845 /* Distance from N to D. */
1846 if (lfa_calc_dist_destination(node_N
->lfa
.spftree
, vertex_S_D
,
1851 /* Distance from N to S (or PN). */
1852 if (CHECK_FLAG(sadj_primary
->flags
, F_ISIS_SPF_ADJ_BROADCAST
)) {
1853 static uint8_t pn_sysid
[ISIS_SYS_ID_LEN
+ 1];
1855 memcpy(pn_sysid
, sadj_primary
->id
, ISIS_SYS_ID_LEN
+ 1);
1856 if (lfa_calc_dist_node(node_N
->lfa
.spftree
, pn_sysid
, &dist_N_S
)
1860 static uint8_t root_sysid
[ISIS_SYS_ID_LEN
+ 1];
1862 memcpy(root_sysid
, spftree
->sysid
, ISIS_SYS_ID_LEN
);
1863 LSP_PSEUDO_ID(root_sysid
) = 0;
1864 if (lfa_calc_dist_node(node_N
->lfa
.spftree
, root_sysid
,
1870 /* Distance from S (or PN) to D. */
1871 vertex_S_D
= listnode_head(vertex_S_D
->parents
);
1872 dist_S_D
= vertex_S_D
->d_N
;
1873 if (CHECK_FLAG(sadj_primary
->flags
, F_ISIS_SPF_ADJ_BROADCAST
))
1874 dist_S_D
-= sadj_primary
->metric
;
1877 zlog_debug("ISIS-LFA: loop-free check: %u < %u + %u", dist_N_D
,
1878 dist_N_S
, dist_S_D
);
1880 if (dist_N_D
< (dist_N_S
+ dist_S_D
)) {
1881 *path_metric
= sadj_N
->metric
+ dist_N_D
;
1889 * Check loop-free criterion (RFC 5286's inequality 2):
1890 * - Distance_opt(N, D) < Distance_opt(S, D)
1892 static bool clfa_downstream_check(struct isis_spftree
*spftree
,
1893 struct isis_vertex
*vertex_S_D
,
1894 struct isis_spf_adj
*sadj_N
)
1896 struct isis_spf_node
*node_N
;
1900 node_N
= isis_spf_node_find(&spftree
->adj_nodes
, sadj_N
->id
);
1903 /* Distance from N to D. */
1904 if (lfa_calc_dist_destination(node_N
->lfa
.spftree
, vertex_S_D
,
1909 /* Distance from S (or PN) to D. */
1910 vertex_S_D
= listnode_head(vertex_S_D
->parents
);
1911 dist_S_D
= vertex_S_D
->d_N
;
1914 zlog_debug("ISIS-LFA: downstream check: %u < %u", dist_N_D
,
1917 if (dist_N_D
< dist_S_D
)
1924 * Check loop-free criterion (RFC 5286's inequality 3):
1925 * - Dist_opt(N, D) < Dist_opt(N, E) + Dist_opt(E, D)
1927 static bool clfa_node_protecting_check(struct isis_spftree
*spftree
,
1928 struct isis_vertex
*vertex_S_D
,
1929 struct isis_spf_adj
*sadj_N
,
1930 struct isis_spf_adj
*sadj_E
)
1932 struct isis_spf_node
*node_N
, *node_E
;
1937 node_N
= isis_spf_node_find(&spftree
->adj_nodes
, sadj_N
->id
);
1939 node_E
= isis_spf_node_find(&spftree
->adj_nodes
, sadj_E
->id
);
1942 /* Distance from N to D. */
1943 if (lfa_calc_dist_destination(node_N
->lfa
.spftree
, vertex_S_D
,
1948 /* Distance from N to E. */
1949 if (lfa_calc_dist_node(node_N
->lfa
.spftree
, node_E
->sysid
, &dist_N_E
)
1953 /* Distance from E to D. */
1954 if (lfa_calc_dist_destination(node_E
->lfa
.spftree
, vertex_S_D
,
1960 zlog_debug("ISIS-LFA: node protecting check: %u < %u + %u",
1961 dist_N_D
, dist_N_E
, dist_E_D
);
1963 return (dist_N_D
< (dist_N_E
+ dist_E_D
));
1966 static struct list
*
1967 isis_lfa_tiebreakers(struct isis_area
*area
, struct isis_spftree
*spftree
,
1968 struct lfa_protected_resource
*resource
,
1969 struct isis_vertex
*vertex
,
1970 struct isis_spf_adj
*sadj_primary
, struct list
*lfa_list
)
1972 struct lfa_tiebreaker
*tie_b
;
1973 int level
= spftree
->level
;
1974 struct list
*filtered_lfa_list
;
1975 struct list
*tent_lfa_list
;
1977 filtered_lfa_list
= list_dup(lfa_list
);
1978 filtered_lfa_list
->del
= NULL
;
1980 if (listcount(filtered_lfa_list
) == 1)
1981 return filtered_lfa_list
;
1983 /* Check tiebreakers in ascending order by index. */
1984 frr_each (lfa_tiebreaker_tree
, &area
->lfa_tiebreakers
[level
- 1],
1986 struct isis_vertex_adj
*lfa
;
1987 struct listnode
*node
, *nnode
;
1988 uint32_t best_metric
= UINT32_MAX
;
1990 tent_lfa_list
= list_dup(filtered_lfa_list
);
1992 switch (tie_b
->type
) {
1993 case LFA_TIEBREAKER_DOWNSTREAM
:
1994 for (ALL_LIST_ELEMENTS(tent_lfa_list
, node
, nnode
,
1996 if (clfa_downstream_check(spftree
, vertex
,
2002 "ISIS-LFA: LFA %s doesn't satisfy the downstream condition",
2005 listnode_delete(tent_lfa_list
, lfa
);
2008 case LFA_TIEBREAKER_LOWEST_METRIC
:
2009 /* Find the best metric first. */
2010 for (ALL_LIST_ELEMENTS_RO(tent_lfa_list
, node
, lfa
)) {
2011 if (lfa
->lfa_metric
< best_metric
)
2012 best_metric
= lfa
->lfa_metric
;
2015 /* Remove LFAs that don't have the best metric. */
2016 for (ALL_LIST_ELEMENTS(tent_lfa_list
, node
, nnode
,
2018 if (lfa
->lfa_metric
== best_metric
)
2023 "ISIS-LFA: LFA %s doesn't have the lowest cost metric",
2026 listnode_delete(tent_lfa_list
, lfa
);
2029 case LFA_TIEBREAKER_NODE_PROTECTING
:
2030 for (ALL_LIST_ELEMENTS(tent_lfa_list
, node
, nnode
,
2032 if (clfa_node_protecting_check(spftree
, vertex
,
2039 "ISIS-LFA: LFA %s doesn't provide node protection",
2042 listnode_delete(tent_lfa_list
, lfa
);
2048 * Decide what to do next based on the number of remaining LFAs.
2050 switch (listcount(tent_lfa_list
)) {
2053 * Ignore this tie-breaker since it excluded all LFAs.
2054 * Move on to the next one (if any).
2056 list_delete(&tent_lfa_list
);
2059 /* Finish tie-breaking once we get a single LFA. */
2060 list_delete(&filtered_lfa_list
);
2061 filtered_lfa_list
= tent_lfa_list
;
2062 return filtered_lfa_list
;
2065 * We still have two or more LFAs. Move on to the next
2066 * tie-breaker (if any).
2068 list_delete(&filtered_lfa_list
);
2069 filtered_lfa_list
= tent_lfa_list
;
2074 return filtered_lfa_list
;
2077 void isis_lfa_compute(struct isis_area
*area
, struct isis_circuit
*circuit
,
2078 struct isis_spftree
*spftree
,
2079 struct lfa_protected_resource
*resource
)
2081 struct isis_vertex
*vertex
, *parent_vertex
;
2082 struct listnode
*vnode
, *snode
;
2083 int level
= spftree
->level
;
2085 resource
->type
= LFA_LINK_PROTECTION
;
2088 zlog_debug("ISIS-LFA: computing local LFAs for %s",
2089 lfa_protected_resource2str(resource
));
2091 for (ALL_QUEUE_ELEMENTS_RO(&spftree
->paths
, vnode
, vertex
)) {
2092 struct list
*lfa_list
;
2093 struct list
*filtered_lfa_list
;
2094 struct isis_spf_adj
*sadj_N
;
2095 struct isis_vertex_adj
*vadj_primary
;
2096 struct isis_spf_adj
*sadj_primary
;
2098 uint32_t prefix_metric
, best_metric
= UINT32_MAX
;
2099 char buf
[VID2STR_BUFFER
];
2101 if (!VTYPE_IP(vertex
->type
))
2104 vid2string(vertex
, buf
, sizeof(buf
));
2106 if (!spf_vertex_check_is_affected(vertex
, spftree
->sysid
,
2110 "ISIS-LFA: %s %s unaffected by %s",
2111 vtype2string(vertex
->type
), buf
,
2112 lfa_protected_resource2str(resource
));
2117 zlog_debug("ISIS-LFA: checking %s %s w.r.t %s",
2118 vtype2string(vertex
->type
), buf
,
2119 lfa_protected_resource2str(resource
));
2121 if (vertex
->N
.ip
.priority
2122 > area
->lfa_priority_limit
[level
- 1]) {
2125 "ISIS-LFA: skipping computing LFAs due to low prefix priority");
2129 vadj_primary
= listnode_head(vertex
->Adj_N
);
2130 sadj_primary
= vadj_primary
->sadj
;
2132 parent_vertex
= listnode_head(vertex
->parents
);
2133 prefix_metric
= vertex
->d_N
- parent_vertex
->d_N
;
2136 * Loop over list of SPF adjacencies and compute a list of
2139 lfa_list
= list_new();
2140 lfa_list
->del
= isis_vertex_adj_free
;
2141 for (ALL_LIST_ELEMENTS_RO(spftree
->sadj_list
, snode
, sadj_N
)) {
2142 uint32_t lfa_metric
, path_metric
;
2143 struct isis_vertex_adj
*lfa
;
2144 struct isis_prefix_sid
*psid
= NULL
;
2145 bool last_hop
= false;
2147 /* Skip pseudonodes. */
2148 if (LSP_PSEUDO_ID(sadj_N
->id
))
2152 * Skip nexthops that are along a link whose cost is
2155 if (CHECK_FLAG(sadj_N
->flags
,
2156 F_ISIS_SPF_ADJ_METRIC_INFINITY
))
2159 /* Skip nexthops that have the overload bit set. */
2160 if (spftree
->mtid
!= ISIS_MT_IPV4_UNICAST
) {
2161 struct isis_mt_router_info
*mt_router_info
;
2164 isis_tlvs_lookup_mt_router_info(
2167 if (mt_router_info
&& mt_router_info
->overload
)
2169 } else if (ISIS_MASK_LSP_OL_BIT(
2170 sadj_N
->lsp
->hdr
.lsp_bits
))
2173 /* Skip primary nexthop. */
2174 if (spf_adj_check_is_affected(sadj_N
, resource
, NULL
,
2178 /* Skip excluded interfaces as per the configuration. */
2180 && isis_lfa_excluded_iface_check(
2182 sadj_N
->adj
->circuit
->interface
->name
))
2187 "ISIS-LFA: checking candidate LFA %s",
2188 print_sys_hostname(sadj_N
->id
));
2190 /* Check loop-free criterion. */
2191 if (!clfa_loop_free_check(spftree
, vertex
, sadj_primary
,
2192 sadj_N
, &path_metric
)) {
2195 "ISIS-LFA: LFA condition not met for %s",
2196 print_sys_hostname(sadj_N
->id
));
2200 lfa_metric
= path_metric
+ prefix_metric
;
2201 if (lfa_metric
< best_metric
)
2202 best_metric
= lfa_metric
;
2206 "ISIS-LFA: %s is a valid loop-free alternate",
2207 print_sys_hostname(sadj_N
->id
));
2209 if (vertex
->N
.ip
.sr
.present
) {
2210 psid
= &vertex
->N
.ip
.sr
.sid
;
2211 if (path_metric
== sadj_N
->metric
)
2214 lfa
= isis_vertex_adj_add(spftree
, vertex
, lfa_list
,
2215 sadj_N
, psid
, last_hop
);
2216 lfa
->lfa_metric
= lfa_metric
;
2219 if (list_isempty(lfa_list
)) {
2222 "ISIS-LFA: no valid local LFAs found");
2223 list_delete(&lfa_list
);
2227 SET_FLAG(vertex
->flags
, F_ISIS_VERTEX_LFA_PROTECTED
);
2229 /* Check tie-breakers. */
2231 isis_lfa_tiebreakers(area
, spftree
, resource
, vertex
,
2232 sadj_primary
, lfa_list
);
2234 /* Create backup route using the best LFAs. */
2235 allow_ecmp
= area
->lfa_load_sharing
[level
- 1];
2236 isis_route_create(&vertex
->N
.ip
.p
.dest
, &vertex
->N
.ip
.p
.src
,
2237 best_metric
, vertex
->depth
, &vertex
->N
.ip
.sr
,
2238 filtered_lfa_list
, allow_ecmp
, area
,
2239 spftree
->route_table_backup
);
2240 spftree
->lfa
.protection_counters
.lfa
[vertex
->N
.ip
.priority
] +=
2243 list_delete(&filtered_lfa_list
);
2244 list_delete(&lfa_list
);
2248 static void isis_spf_run_tilfa(struct isis_area
*area
,
2249 struct isis_circuit
*circuit
,
2250 struct isis_spftree
*spftree
,
2251 struct isis_spftree
*spftree_reverse
,
2252 struct lfa_protected_resource
*resource
)
2254 struct isis_spftree
*spftree_pc_link
;
2255 struct isis_spftree
*spftree_pc_node
;
2257 /* Compute node protecting repair paths first (if necessary). */
2258 if (circuit
->tilfa_node_protection
[spftree
->level
- 1]) {
2259 resource
->type
= LFA_NODE_PROTECTION
;
2260 spftree_pc_node
= isis_tilfa_compute(area
, spftree
,
2261 spftree_reverse
, resource
);
2262 isis_spftree_del(spftree_pc_node
);
2264 /* don't do link protection unless link-fallback is configured
2266 if (!circuit
->tilfa_link_fallback
[spftree
->level
- 1])
2270 /* Compute link protecting repair paths. */
2271 resource
->type
= LFA_LINK_PROTECTION
;
2273 isis_tilfa_compute(area
, spftree
, spftree_reverse
, resource
);
2274 isis_spftree_del(spftree_pc_link
);
2278 * Run the LFA/RLFA/TI-LFA algorithms for all protected interfaces.
2280 * @param area IS-IS area
2281 * @param spftree IS-IS SPF tree
2283 void isis_spf_run_lfa(struct isis_area
*area
, struct isis_spftree
*spftree
)
2285 struct isis_spftree
*spftree_reverse
= NULL
;
2286 struct isis_circuit
*circuit
;
2287 struct listnode
*node
;
2288 int level
= spftree
->level
;
2290 /* Run reverse SPF locally. */
2291 if (area
->rlfa_protected_links
[level
- 1] > 0
2292 || area
->tilfa_protected_links
[level
- 1] > 0)
2293 spftree_reverse
= isis_spf_reverse_run(spftree
);
2295 /* Run forward SPF on all adjacent routers. */
2296 isis_spf_run_neighbors(spftree
);
2298 /* Check which interfaces are protected. */
2299 for (ALL_LIST_ELEMENTS_RO(area
->circuit_list
, node
, circuit
)) {
2300 struct lfa_protected_resource resource
= {};
2301 struct isis_adjacency
*adj
;
2302 static uint8_t null_sysid
[ISIS_SYS_ID_LEN
+ 1];
2304 if (!(circuit
->is_type
& level
))
2307 if (!circuit
->lfa_protection
[level
- 1]
2308 && !circuit
->tilfa_protection
[level
- 1])
2311 /* Fill in the protected resource. */
2312 switch (circuit
->circ_type
) {
2313 case CIRCUIT_T_BROADCAST
:
2314 if (level
== ISIS_LEVEL1
)
2315 memcpy(resource
.adjacency
,
2316 circuit
->u
.bc
.l1_desig_is
,
2317 ISIS_SYS_ID_LEN
+ 1);
2319 memcpy(resource
.adjacency
,
2320 circuit
->u
.bc
.l2_desig_is
,
2321 ISIS_SYS_ID_LEN
+ 1);
2322 /* Do nothing if no DR was elected yet. */
2323 if (!memcmp(resource
.adjacency
, null_sysid
,
2324 ISIS_SYS_ID_LEN
+ 1))
2328 adj
= circuit
->u
.p2p
.neighbor
;
2331 memcpy(resource
.adjacency
, adj
->sysid
, ISIS_SYS_ID_LEN
);
2332 LSP_PSEUDO_ID(resource
.adjacency
) = 0;
2338 if (circuit
->lfa_protection
[level
- 1]) {
2339 /* Run local LFA. */
2340 isis_lfa_compute(area
, circuit
, spftree
, &resource
);
2342 if (circuit
->rlfa_protection
[level
- 1]) {
2343 struct isis_spftree
*spftree_pc
;
2344 uint32_t max_metric
;
2346 /* Run remote LFA. */
2347 assert(spftree_reverse
);
2349 circuit
->rlfa_max_metric
[level
- 1];
2350 spftree_pc
= isis_rlfa_compute(
2351 area
, spftree
, spftree_reverse
,
2352 max_metric
, &resource
);
2353 listnode_add(spftree
->lfa
.remote
.pc_spftrees
,
2356 } else if (circuit
->tilfa_protection
[level
- 1]) {
2358 assert(spftree_reverse
);
2359 isis_spf_run_tilfa(area
, circuit
, spftree
,
2360 spftree_reverse
, &resource
);
2364 if (spftree_reverse
)
2365 isis_spftree_del(spftree_reverse
);