1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * IS-IS Rout(e)ing protocol - isis_spf.c
6 * Copyright (C) 2001,2002 Sampo Saaristo
7 * Tampere University of Technology
8 * Institute of Communications Engineering
9 * Copyright (C) 2017 Christian Franke <chris@opensourcerouting.org>
19 #include "termtable.h"
26 #include "spf_backoff.h"
27 #include "srcdest_table.h"
30 #include "isis_errors.h"
31 #include "isis_constants.h"
32 #include "isis_common.h"
33 #include "isis_flags.h"
35 #include "isis_misc.h"
36 #include "isis_adjacency.h"
37 #include "isis_circuit.h"
40 #include "isis_dynhn.h"
42 #include "isis_route.h"
45 #include "isis_tlvs.h"
46 #include "isis_zebra.h"
48 #include "isis_spf_private.h"
50 DEFINE_MTYPE_STATIC(ISISD
, ISIS_SPFTREE
, "ISIS SPFtree");
51 DEFINE_MTYPE_STATIC(ISISD
, ISIS_SPF_RUN
, "ISIS SPF Run Info");
52 DEFINE_MTYPE_STATIC(ISISD
, ISIS_SPF_ADJ
, "ISIS SPF Adjacency");
53 DEFINE_MTYPE_STATIC(ISISD
, ISIS_VERTEX
, "ISIS vertex");
54 DEFINE_MTYPE_STATIC(ISISD
, ISIS_VERTEX_ADJ
, "ISIS SPF Vertex Adjacency");
56 static void spf_adj_list_parse_lsp(struct isis_spftree
*spftree
,
57 struct list
*adj_list
, struct isis_lsp
*lsp
,
58 const uint8_t *pseudo_nodeid
,
59 uint32_t pseudo_metric
);
62 * supports the given af ?
64 static bool speaks(uint8_t *protocols
, uint8_t count
, int family
)
66 for (uint8_t i
= 0; i
< count
; i
++) {
67 if (family
== AF_INET
&& protocols
[i
] == NLPID_IP
)
69 if (family
== AF_INET6
&& protocols
[i
] == NLPID_IPV6
)
76 struct isis_area
*area
;
81 static void remove_excess_adjs(struct list
*adjs
)
83 struct listnode
*node
, *excess
= NULL
;
84 struct isis_vertex_adj
*vadj
, *candidate
= NULL
;
87 for (ALL_LIST_ELEMENTS_RO(adjs
, node
, vadj
)) {
88 struct isis_adjacency
*adj
, *candidate_adj
;
90 adj
= vadj
->sadj
->adj
;
95 candidate
= listgetdata(excess
);
96 candidate_adj
= candidate
->sadj
->adj
;
98 if (candidate_adj
->sys_type
< adj
->sys_type
) {
102 if (candidate_adj
->sys_type
> adj
->sys_type
)
105 comp
= memcmp(candidate_adj
->sysid
, adj
->sysid
,
114 if (candidate_adj
->circuit
->idx
> adj
->circuit
->idx
) {
119 if (candidate_adj
->circuit
->idx
< adj
->circuit
->idx
)
122 comp
= memcmp(candidate_adj
->snpa
, adj
->snpa
, ETH_ALEN
);
129 list_delete_node(adjs
, excess
);
134 const char *vtype2string(enum vertextype vtype
)
137 case VTYPE_PSEUDO_IS
:
139 case VTYPE_PSEUDO_TE_IS
:
140 return "pseudo_TE-IS";
141 case VTYPE_NONPSEUDO_IS
:
143 case VTYPE_NONPSEUDO_TE_IS
:
147 case VTYPE_IPREACH_INTERNAL
:
148 return "IP internal";
149 case VTYPE_IPREACH_EXTERNAL
:
150 return "IP external";
151 case VTYPE_IPREACH_TE
:
153 case VTYPE_IP6REACH_INTERNAL
:
154 return "IP6 internal";
155 case VTYPE_IP6REACH_EXTERNAL
:
156 return "IP6 external";
160 return NULL
; /* Not reached */
163 const char *vid2string(const struct isis_vertex
*vertex
, char *buff
, int size
)
165 if (VTYPE_IS(vertex
->type
) || VTYPE_ES(vertex
->type
)) {
166 const char *hostname
= print_sys_hostname(vertex
->N
.id
);
167 strlcpy(buff
, hostname
, size
);
171 if (VTYPE_IP(vertex
->type
)) {
172 srcdest2str(&vertex
->N
.ip
.p
.dest
, &vertex
->N
.ip
.p
.src
, buff
,
180 static bool prefix_sid_cmp(const void *value1
, const void *value2
)
182 const struct isis_vertex
*c1
= value1
;
183 const struct isis_vertex
*c2
= value2
;
185 if (CHECK_FLAG(c1
->N
.ip
.sr
.sid
.flags
,
186 ISIS_PREFIX_SID_VALUE
| ISIS_PREFIX_SID_LOCAL
)
187 != CHECK_FLAG(c2
->N
.ip
.sr
.sid
.flags
,
188 ISIS_PREFIX_SID_VALUE
| ISIS_PREFIX_SID_LOCAL
))
191 return c1
->N
.ip
.sr
.sid
.value
== c2
->N
.ip
.sr
.sid
.value
;
194 static unsigned int prefix_sid_key_make(const void *value
)
196 const struct isis_vertex
*vertex
= value
;
198 return jhash_1word(vertex
->N
.ip
.sr
.sid
.value
, 0);
201 struct isis_vertex
*isis_spf_prefix_sid_lookup(struct isis_spftree
*spftree
,
202 struct isis_prefix_sid
*psid
)
204 struct isis_vertex lookup
= {};
206 lookup
.N
.ip
.sr
.sid
= *psid
;
207 return hash_lookup(spftree
->prefix_sids
, &lookup
);
210 void isis_vertex_adj_free(void *arg
)
212 struct isis_vertex_adj
*vadj
= arg
;
214 XFREE(MTYPE_ISIS_VERTEX_ADJ
, vadj
);
217 static struct isis_vertex
*isis_vertex_new(struct isis_spftree
*spftree
,
219 enum vertextype vtype
)
221 struct isis_vertex
*vertex
;
223 vertex
= XCALLOC(MTYPE_ISIS_VERTEX
, sizeof(struct isis_vertex
));
225 isis_vertex_id_init(vertex
, id
, vtype
);
227 vertex
->Adj_N
= list_new();
228 vertex
->Adj_N
->del
= isis_vertex_adj_free
;
229 vertex
->parents
= list_new();
231 if (CHECK_FLAG(spftree
->flags
, F_SPFTREE_HOPCOUNT_METRIC
)) {
232 vertex
->firsthops
= hash_create(isis_vertex_queue_hash_key
,
233 isis_vertex_queue_hash_cmp
,
240 void isis_vertex_del(struct isis_vertex
*vertex
)
242 list_delete(&vertex
->Adj_N
);
243 list_delete(&vertex
->parents
);
244 hash_clean_and_free(&vertex
->firsthops
, NULL
);
246 memset(vertex
, 0, sizeof(struct isis_vertex
));
247 XFREE(MTYPE_ISIS_VERTEX
, vertex
);
250 struct isis_vertex_adj
*
251 isis_vertex_adj_add(struct isis_spftree
*spftree
, struct isis_vertex
*vertex
,
252 struct list
*vadj_list
, struct isis_spf_adj
*sadj
,
253 struct isis_prefix_sid
*psid
, bool last_hop
)
255 struct isis_vertex_adj
*vadj
;
257 vadj
= XCALLOC(MTYPE_ISIS_VERTEX_ADJ
, sizeof(*vadj
));
259 if (spftree
->area
->srdb
.enabled
&& psid
) {
260 if (vertex
->N
.ip
.sr
.present
261 && vertex
->N
.ip
.sr
.sid
.value
!= psid
->value
)
263 "ISIS-SPF: ignoring different Prefix-SID for route %pFX",
264 &vertex
->N
.ip
.p
.dest
);
266 vadj
->sr
.sid
= *psid
;
267 vadj
->sr
.label
= sr_prefix_out_label(
268 spftree
->lspdb
, vertex
->N
.ip
.p
.dest
.family
,
269 psid
, sadj
->id
, last_hop
);
270 if (vadj
->sr
.label
!= MPLS_INVALID_LABEL
)
271 vadj
->sr
.present
= true;
274 listnode_add(vadj_list
, vadj
);
279 static void isis_vertex_adj_del(struct isis_vertex
*vertex
,
280 struct isis_adjacency
*adj
)
282 struct isis_vertex_adj
*vadj
;
283 struct listnode
*node
, *nextnode
;
288 for (ALL_LIST_ELEMENTS(vertex
->Adj_N
, node
, nextnode
, vadj
)) {
289 if (vadj
->sadj
->adj
== adj
) {
290 listnode_delete(vertex
->Adj_N
, vadj
);
291 isis_vertex_adj_free(vadj
);
297 bool isis_vertex_adj_exists(const struct isis_spftree
*spftree
,
298 const struct isis_vertex
*vertex
,
299 const struct isis_spf_adj
*sadj
)
301 struct isis_vertex_adj
*tmp
;
302 struct listnode
*node
;
304 for (ALL_LIST_ELEMENTS_RO(vertex
->Adj_N
, node
, tmp
)) {
305 if (CHECK_FLAG(spftree
->flags
, F_SPFTREE_NO_ADJACENCIES
)) {
306 if (memcmp(sadj
->id
, tmp
->sadj
->id
, sizeof(sadj
->id
))
310 if (sadj
->adj
== tmp
->sadj
->adj
)
318 static void isis_spf_adj_free(void *arg
)
320 struct isis_spf_adj
*sadj
= arg
;
322 XFREE(MTYPE_ISIS_SPF_ADJ
, sadj
);
325 struct isis_spftree
*
326 isis_spftree_new(struct isis_area
*area
, struct lspdb_head
*lspdb
,
327 const uint8_t *sysid
, int level
, enum spf_tree_id tree_id
,
328 enum spf_type type
, uint8_t flags
, uint8_t algorithm
)
330 struct isis_spftree
*tree
;
332 tree
= XCALLOC(MTYPE_ISIS_SPFTREE
, sizeof(struct isis_spftree
));
334 isis_vertex_queue_init(&tree
->tents
, "IS-IS SPF tents", true);
335 isis_vertex_queue_init(&tree
->paths
, "IS-IS SPF paths", false);
336 tree
->route_table
= srcdest_table_init();
337 tree
->route_table
->cleanup
= isis_route_node_cleanup
;
338 tree
->route_table
->info
= isis_route_table_info_alloc(algorithm
);
339 tree
->route_table_backup
= srcdest_table_init();
340 tree
->route_table_backup
->info
= isis_route_table_info_alloc(algorithm
);
341 tree
->route_table_backup
->cleanup
= isis_route_node_cleanup
;
344 tree
->prefix_sids
= hash_create(prefix_sid_key_make
, prefix_sid_cmp
,
345 "SR Prefix-SID Entries");
346 tree
->sadj_list
= list_new();
347 tree
->sadj_list
->del
= isis_spf_adj_free
;
348 tree
->last_run_timestamp
= 0;
349 tree
->last_run_monotime
= 0;
350 tree
->last_run_duration
= 0;
353 memcpy(tree
->sysid
, sysid
, ISIS_SYS_ID_LEN
);
355 tree
->tree_id
= tree_id
;
356 tree
->family
= (tree
->tree_id
== SPFTREE_IPV4
) ? AF_INET
: AF_INET6
;
358 isis_rlfa_list_init(tree
);
359 tree
->lfa
.remote
.pc_spftrees
= list_new();
360 tree
->lfa
.remote
.pc_spftrees
->del
= (void (*)(void *))isis_spftree_del
;
361 if (tree
->type
== SPF_TYPE_RLFA
|| tree
->type
== SPF_TYPE_TI_LFA
) {
362 isis_spf_node_list_init(&tree
->lfa
.p_space
);
363 isis_spf_node_list_init(&tree
->lfa
.q_space
);
365 tree
->algorithm
= algorithm
;
370 void isis_spftree_del(struct isis_spftree
*spftree
)
372 hash_clean_and_free(&spftree
->prefix_sids
, NULL
);
373 isis_zebra_rlfa_unregister_all(spftree
);
374 isis_rlfa_list_clear(spftree
);
375 list_delete(&spftree
->lfa
.remote
.pc_spftrees
);
376 if (spftree
->type
== SPF_TYPE_RLFA
377 || spftree
->type
== SPF_TYPE_TI_LFA
) {
378 isis_spf_node_list_clear(&spftree
->lfa
.q_space
);
379 isis_spf_node_list_clear(&spftree
->lfa
.p_space
);
381 isis_spf_node_list_clear(&spftree
->adj_nodes
);
382 list_delete(&spftree
->sadj_list
);
383 isis_vertex_queue_free(&spftree
->tents
);
384 isis_vertex_queue_free(&spftree
->paths
);
385 isis_route_table_info_free(spftree
->route_table
->info
);
386 isis_route_table_info_free(spftree
->route_table_backup
->info
);
387 route_table_finish(spftree
->route_table
);
388 route_table_finish(spftree
->route_table_backup
);
389 spftree
->route_table
= NULL
;
391 XFREE(MTYPE_ISIS_SPFTREE
, spftree
);
395 static void isis_spftree_adj_del(struct isis_spftree
*spftree
,
396 struct isis_adjacency
*adj
)
398 struct listnode
*node
;
399 struct isis_vertex
*v
;
402 assert(!isis_vertex_queue_count(&spftree
->tents
));
403 for (ALL_QUEUE_ELEMENTS_RO(&spftree
->paths
, node
, v
))
404 isis_vertex_adj_del(v
, adj
);
408 void spftree_area_init(struct isis_area
*area
)
410 for (int tree
= SPFTREE_IPV4
; tree
< SPFTREE_COUNT
; tree
++) {
411 for (int level
= ISIS_LEVEL1
; level
<= ISIS_LEVEL2
; level
++) {
412 if (!(area
->is_type
& level
))
414 if (area
->spftree
[tree
][level
- 1])
417 area
->spftree
[tree
][level
- 1] = isis_spftree_new(
418 area
, &area
->lspdb
[level
- 1],
419 area
->isis
->sysid
, level
, tree
,
420 SPF_TYPE_FORWARD
, 0, SR_ALGORITHM_SPF
);
425 void spftree_area_del(struct isis_area
*area
)
427 for (int tree
= SPFTREE_IPV4
; tree
< SPFTREE_COUNT
; tree
++) {
428 for (int level
= ISIS_LEVEL1
; level
<= ISIS_LEVEL2
; level
++) {
429 if (!(area
->is_type
& level
))
431 if (!area
->spftree
[tree
][level
- 1])
434 isis_spftree_del(area
->spftree
[tree
][level
- 1]);
439 static int spf_adj_state_change(struct isis_adjacency
*adj
)
441 struct isis_area
*area
= adj
->circuit
->area
;
443 if (adj
->adj_state
== ISIS_ADJ_UP
)
446 /* Remove adjacency from all SPF trees. */
447 for (int tree
= SPFTREE_IPV4
; tree
< SPFTREE_COUNT
; tree
++) {
448 for (int level
= ISIS_LEVEL1
; level
<= ISIS_LEVEL2
; level
++) {
449 if (!(area
->is_type
& level
))
451 if (!area
->spftree
[tree
][level
- 1])
453 isis_spftree_adj_del(area
->spftree
[tree
][level
- 1],
458 if (fabricd_spftree(area
) != NULL
)
459 isis_spftree_adj_del(fabricd_spftree(area
), adj
);
465 * Find the system LSP: returns the LSP in our LSP database
466 * associated with the given system ID.
468 struct isis_lsp
*isis_root_system_lsp(struct lspdb_head
*lspdb
,
469 const uint8_t *sysid
)
471 struct isis_lsp
*lsp
;
472 uint8_t lspid
[ISIS_SYS_ID_LEN
+ 2];
474 memcpy(lspid
, sysid
, ISIS_SYS_ID_LEN
);
475 LSP_PSEUDO_ID(lspid
) = 0;
476 LSP_FRAGMENT(lspid
) = 0;
477 lsp
= lsp_search(lspdb
, lspid
);
478 if (lsp
&& lsp
->hdr
.rem_lifetime
!= 0)
484 * Add this IS to the root of SPT
486 static struct isis_vertex
*isis_spf_add_root(struct isis_spftree
*spftree
)
488 struct isis_vertex
*vertex
;
490 char buff
[VID2STR_BUFFER
];
491 #endif /* EXTREME_DEBUG */
493 vertex
= isis_vertex_new(spftree
, spftree
->sysid
,
494 spftree
->area
->oldmetric
496 : VTYPE_NONPSEUDO_TE_IS
);
497 isis_vertex_queue_append(&spftree
->paths
, vertex
);
500 if (IS_DEBUG_SPF_EVENTS
)
502 "ISIS-SPF: added this IS %s %s depth %d dist %d to PATHS",
503 vtype2string(vertex
->type
),
504 vid2string(vertex
, buff
, sizeof(buff
)), vertex
->depth
,
506 #endif /* EXTREME_DEBUG */
511 static void vertex_add_parent_firsthop(struct hash_bucket
*bucket
, void *arg
)
513 struct isis_vertex
*vertex
= arg
;
514 struct isis_vertex
*hop
= bucket
->data
;
516 (void)hash_get(vertex
->firsthops
, hop
, hash_alloc_intern
);
519 static void vertex_update_firsthops(struct isis_vertex
*vertex
,
520 struct isis_vertex
*parent
)
522 if (vertex
->d_N
<= 2)
523 (void)hash_get(vertex
->firsthops
, vertex
, hash_alloc_intern
);
525 if (vertex
->d_N
< 2 || !parent
)
528 hash_iterate(parent
->firsthops
, vertex_add_parent_firsthop
, vertex
);
532 * Add a vertex to TENT sorted by cost and by vertextype on tie break situation
534 static struct isis_vertex
*
535 isis_spf_add2tent(struct isis_spftree
*spftree
, enum vertextype vtype
, void *id
,
536 uint32_t cost
, int depth
, struct isis_spf_adj
*sadj
,
537 struct isis_prefix_sid
*psid
, struct isis_vertex
*parent
)
539 struct isis_vertex
*vertex
;
540 struct listnode
*node
;
542 char buff
[VID2STR_BUFFER
];
544 vertex
= isis_find_vertex(&spftree
->paths
, id
, vtype
);
545 if (vertex
!= NULL
) {
547 "%s: vertex %s of type %s already in PATH; check for sysId collisions with established neighbors",
548 __func__
, vid2string(vertex
, buff
, sizeof(buff
)),
549 vtype2string(vertex
->type
));
552 vertex
= isis_find_vertex(&spftree
->tents
, id
, vtype
);
553 if (vertex
!= NULL
) {
555 "%s: vertex %s of type %s already in TENT; check for sysId collisions with established neighbors",
556 __func__
, vid2string(vertex
, buff
, sizeof(buff
)),
557 vtype2string(vertex
->type
));
561 vertex
= isis_vertex_new(spftree
, id
, vtype
);
563 vertex
->depth
= depth
;
564 if (VTYPE_IP(vtype
) && spftree
->area
->srdb
.enabled
&& psid
) {
565 struct isis_area
*area
= spftree
->area
;
566 struct isis_vertex
*vertex_psid
;
569 * Check if the Prefix-SID is already in use by another prefix.
571 vertex_psid
= isis_spf_prefix_sid_lookup(spftree
, psid
);
573 && !prefix_same(&vertex_psid
->N
.ip
.p
.dest
,
574 &vertex
->N
.ip
.p
.dest
)) {
576 EC_ISIS_SID_COLLISION
,
577 "ISIS-Sr (%s): collision detected, prefixes %pFX and %pFX share the same SID %s (%u)",
578 area
->area_tag
, &vertex
->N
.ip
.p
.dest
,
579 &vertex_psid
->N
.ip
.p
.dest
,
580 CHECK_FLAG(psid
->flags
, ISIS_PREFIX_SID_VALUE
)
588 local
= (vertex
->depth
== 1);
589 vertex
->N
.ip
.sr
.sid
= *psid
;
590 vertex
->N
.ip
.sr
.label
=
591 sr_prefix_in_label(area
, psid
, local
);
592 vertex
->N
.ip
.sr
.algorithm
= psid
->algorithm
;
594 if (vertex
->N
.ip
.sr
.label
!= MPLS_INVALID_LABEL
)
595 vertex
->N
.ip
.sr
.present
= true;
597 (void)hash_get(spftree
->prefix_sids
, vertex
,
603 listnode_add(vertex
->parents
, parent
);
606 if (CHECK_FLAG(spftree
->flags
, F_SPFTREE_HOPCOUNT_METRIC
))
607 vertex_update_firsthops(vertex
, parent
);
609 last_hop
= (vertex
->depth
== 2);
610 if (parent
&& parent
->Adj_N
&& listcount(parent
->Adj_N
) > 0) {
611 struct isis_vertex_adj
*parent_vadj
;
613 for (ALL_LIST_ELEMENTS_RO(parent
->Adj_N
, node
, parent_vadj
))
614 isis_vertex_adj_add(spftree
, vertex
, vertex
->Adj_N
,
615 parent_vadj
->sadj
, psid
, last_hop
);
617 isis_vertex_adj_add(spftree
, vertex
, vertex
->Adj_N
, sadj
, psid
,
622 if (IS_DEBUG_SPF_EVENTS
)
624 "ISIS-SPF: add to TENT %s %s %s depth %d dist %d adjcount %d",
625 print_sys_hostname(vertex
->N
.id
),
626 vtype2string(vertex
->type
),
627 vid2string(vertex
, buff
, sizeof(buff
)), vertex
->depth
,
628 vertex
->d_N
, listcount(vertex
->Adj_N
));
629 #endif /* EXTREME_DEBUG */
631 isis_vertex_queue_insert(&spftree
->tents
, vertex
);
635 static void isis_spf_add_local(struct isis_spftree
*spftree
,
636 enum vertextype vtype
, void *id
,
637 struct isis_spf_adj
*sadj
, uint32_t cost
,
638 struct isis_prefix_sid
*psid
,
639 struct isis_vertex
*parent
)
641 struct isis_vertex
*vertex
;
643 vertex
= isis_find_vertex(&spftree
->tents
, id
, vtype
);
647 if (vertex
->d_N
== cost
) {
649 bool last_hop
= (vertex
->depth
== 2);
651 isis_vertex_adj_add(spftree
, vertex
,
652 vertex
->Adj_N
, sadj
, psid
,
656 if (!CHECK_FLAG(spftree
->flags
,
657 F_SPFTREE_NO_ADJACENCIES
)
658 && listcount(vertex
->Adj_N
) > ISIS_MAX_PATH_SPLITS
)
659 remove_excess_adjs(vertex
->Adj_N
);
660 if (parent
&& (listnode_lookup(vertex
->parents
, parent
)
662 listnode_add(vertex
->parents
, parent
);
664 } else if (vertex
->d_N
< cost
) {
667 } else { /* vertex->d_N > cost */
669 isis_vertex_queue_delete(&spftree
->tents
, vertex
);
670 isis_vertex_del(vertex
);
674 isis_spf_add2tent(spftree
, vtype
, id
, cost
, 1, sadj
, psid
, parent
);
678 static void process_N(struct isis_spftree
*spftree
, enum vertextype vtype
,
679 void *id
, uint32_t dist
, uint16_t depth
,
680 struct isis_prefix_sid
*psid
, struct isis_vertex
*parent
)
682 struct isis_vertex
*vertex
;
684 char buff
[VID2STR_BUFFER
];
687 assert(spftree
&& parent
);
689 if (CHECK_FLAG(spftree
->flags
, F_SPFTREE_HOPCOUNT_METRIC
)
693 struct prefix_pair p
;
694 if (vtype
>= VTYPE_IPREACH_INTERNAL
) {
695 memcpy(&p
, id
, sizeof(p
));
701 /* RFC3787 section 5.1 */
702 if (spftree
->area
->newmetric
== 1) {
703 if (dist
> MAX_WIDE_PATH_METRIC
)
707 else if (spftree
->area
->oldmetric
== 1) {
708 if (dist
> MAX_NARROW_PATH_METRIC
)
713 vertex
= isis_find_vertex(&spftree
->paths
, id
, vtype
);
716 if (IS_DEBUG_SPF_EVENTS
)
718 "ISIS-SPF: process_N %s %s %s dist %d already found from PATH",
719 print_sys_hostname(vertex
->N
.id
),
721 vid2string(vertex
, buff
, sizeof(buff
)), dist
);
722 #endif /* EXTREME_DEBUG */
723 assert(dist
>= vertex
->d_N
);
727 vertex
= isis_find_vertex(&spftree
->tents
, id
, vtype
);
732 if (IS_DEBUG_SPF_EVENTS
)
734 "ISIS-SPF: process_N %s %s %s dist %d parent %s adjcount %d",
735 print_sys_hostname(vertex
->N
.id
),
737 vid2string(vertex
, buff
, sizeof(buff
)), dist
,
738 (parent
? print_sys_hostname(parent
->N
.id
)
740 (parent
? listcount(parent
->Adj_N
) : 0));
741 #endif /* EXTREME_DEBUG */
742 if (vertex
->d_N
== dist
) {
743 struct listnode
*node
;
744 struct isis_vertex_adj
*parent_vadj
;
745 for (ALL_LIST_ELEMENTS_RO(parent
->Adj_N
, node
,
747 if (!isis_vertex_adj_exists(
749 parent_vadj
->sadj
)) {
750 bool last_hop
= (vertex
->depth
== 2);
752 isis_vertex_adj_add(spftree
, vertex
,
757 if (CHECK_FLAG(spftree
->flags
,
758 F_SPFTREE_HOPCOUNT_METRIC
))
759 vertex_update_firsthops(vertex
, parent
);
761 if (!CHECK_FLAG(spftree
->flags
,
762 F_SPFTREE_NO_ADJACENCIES
)
763 && listcount(vertex
->Adj_N
) > ISIS_MAX_PATH_SPLITS
)
764 remove_excess_adjs(vertex
->Adj_N
);
765 if (listnode_lookup(vertex
->parents
, parent
) == NULL
)
766 listnode_add(vertex
->parents
, parent
);
768 } else if (vertex
->d_N
< dist
) {
772 isis_vertex_queue_delete(&spftree
->tents
, vertex
);
773 isis_vertex_del(vertex
);
778 if (IS_DEBUG_SPF_EVENTS
)
780 "ISIS-SPF: process_N add2tent %s %s dist %d parent %s",
781 print_sys_hostname(id
), vtype2string(vtype
), dist
,
782 (parent
? print_sys_hostname(parent
->N
.id
) : "null"));
783 #endif /* EXTREME_DEBUG */
785 isis_spf_add2tent(spftree
, vtype
, id
, dist
, depth
, NULL
, psid
, parent
);
792 static int isis_spf_process_lsp(struct isis_spftree
*spftree
,
793 struct isis_lsp
*lsp
, uint32_t cost
,
794 uint16_t depth
, uint8_t *root_sysid
,
795 struct isis_vertex
*parent
)
797 bool pseudo_lsp
= LSP_PSEUDO_ID(lsp
->hdr
.lsp_id
);
798 struct listnode
*fragnode
= NULL
;
800 enum vertextype vtype
;
801 static const uint8_t null_sysid
[ISIS_SYS_ID_LEN
];
802 struct isis_mt_router_info
*mt_router_info
= NULL
;
803 struct prefix_pair ip_info
;
806 if (isis_lfa_excise_node_check(spftree
, lsp
->hdr
.lsp_id
)) {
808 zlog_debug("ISIS-LFA: excising node %s",
809 print_sys_hostname(lsp
->hdr
.lsp_id
));
816 if (spftree
->mtid
!= ISIS_MT_IPV4_UNICAST
)
817 mt_router_info
= isis_tlvs_lookup_mt_router_info(lsp
->tlvs
,
820 if (!pseudo_lsp
&& (spftree
->mtid
== ISIS_MT_IPV4_UNICAST
821 && !speaks(lsp
->tlvs
->protocols_supported
.protocols
,
822 lsp
->tlvs
->protocols_supported
.count
,
827 /* RFC3787 section 4 SHOULD ignore overload bit in pseudo LSPs */
828 bool no_overload
= (pseudo_lsp
829 || (spftree
->mtid
== ISIS_MT_IPV4_UNICAST
830 && !ISIS_MASK_LSP_OL_BIT(lsp
->hdr
.lsp_bits
))
831 || (mt_router_info
&& !mt_router_info
->overload
));
834 if (lsp
->hdr
.seqno
== 0) {
835 zlog_warn("%s: lsp with 0 seq_num - ignore", __func__
);
840 if (IS_DEBUG_SPF_EVENTS
)
841 zlog_debug("ISIS-SPF: process_lsp %s",
842 print_sys_hostname(lsp
->hdr
.lsp_id
));
843 #endif /* EXTREME_DEBUG */
846 if ((pseudo_lsp
|| spftree
->mtid
== ISIS_MT_IPV4_UNICAST
)
847 && spftree
->area
->oldmetric
) {
848 struct isis_oldstyle_reach
*r
;
849 for (r
= (struct isis_oldstyle_reach
*)
850 lsp
->tlvs
->oldstyle_reach
.head
;
856 /* Two way connectivity */
857 if (!LSP_PSEUDO_ID(r
->id
)
858 && !memcmp(r
->id
, root_sysid
,
862 && !memcmp(r
->id
, null_sysid
,
865 dist
= cost
+ r
->metric
;
869 : VTYPE_NONPSEUDO_IS
,
870 (void *)r
->id
, dist
, depth
+ 1, NULL
,
875 if (spftree
->area
->newmetric
) {
876 struct isis_item_list
*te_neighs
= NULL
;
877 if (pseudo_lsp
|| spftree
->mtid
== ISIS_MT_IPV4_UNICAST
)
878 te_neighs
= &lsp
->tlvs
->extended_reach
;
880 te_neighs
= isis_lookup_mt_items(
881 &lsp
->tlvs
->mt_reach
, spftree
->mtid
);
883 struct isis_extended_reach
*er
;
884 for (er
= te_neighs
? (struct isis_extended_reach
*)
889 /* Two way connectivity */
890 if (!LSP_PSEUDO_ID(er
->id
)
891 && !memcmp(er
->id
, root_sysid
,
895 && !memcmp(er
->id
, null_sysid
,
899 + (CHECK_FLAG(spftree
->flags
,
900 F_SPFTREE_HOPCOUNT_METRIC
)
904 LSP_PSEUDO_ID(er
->id
)
906 : VTYPE_NONPSEUDO_TE_IS
,
907 (void *)er
->id
, dist
, depth
+ 1, NULL
,
913 if (!fabricd
&& !pseudo_lsp
&& spftree
->family
== AF_INET
914 && spftree
->mtid
== ISIS_MT_IPV4_UNICAST
915 && spftree
->area
->oldmetric
) {
916 struct isis_item_list
*reachs
[] = {
917 &lsp
->tlvs
->oldstyle_ip_reach
,
918 &lsp
->tlvs
->oldstyle_ip_reach_ext
};
920 for (unsigned int i
= 0; i
< array_size(reachs
); i
++) {
921 vtype
= i
? VTYPE_IPREACH_EXTERNAL
922 : VTYPE_IPREACH_INTERNAL
;
924 memset(&ip_info
, 0, sizeof(ip_info
));
925 ip_info
.dest
.family
= AF_INET
;
927 struct isis_oldstyle_ip_reach
*r
;
928 for (r
= (struct isis_oldstyle_ip_reach
*)reachs
[i
]
931 dist
= cost
+ r
->metric
;
932 ip_info
.dest
.u
.prefix4
= r
->prefix
.prefix
;
933 ip_info
.dest
.prefixlen
= r
->prefix
.prefixlen
;
934 process_N(spftree
, vtype
, &ip_info
,
935 dist
, depth
+ 1, NULL
, parent
);
940 /* we can skip all the rest if we're using metric style narrow */
941 if (!spftree
->area
->newmetric
)
944 if (!pseudo_lsp
&& spftree
->family
== AF_INET
) {
945 struct isis_item_list
*ipv4_reachs
;
946 if (spftree
->mtid
== ISIS_MT_IPV4_UNICAST
)
947 ipv4_reachs
= &lsp
->tlvs
->extended_ip_reach
;
949 ipv4_reachs
= isis_lookup_mt_items(
950 &lsp
->tlvs
->mt_ip_reach
, spftree
->mtid
);
952 memset(&ip_info
, 0, sizeof(ip_info
));
953 ip_info
.dest
.family
= AF_INET
;
955 struct isis_extended_ip_reach
*r
;
957 ? (struct isis_extended_ip_reach
*)
961 dist
= cost
+ r
->metric
;
962 ip_info
.dest
.u
.prefix4
= r
->prefix
.prefix
;
963 ip_info
.dest
.prefixlen
= r
->prefix
.prefixlen
;
965 /* Parse list of Prefix-SID subTLVs if SR is enabled */
966 has_valid_psid
= false;
967 if (spftree
->area
->srdb
.enabled
&& r
->subtlvs
) {
968 for (struct isis_item
*i
=
969 r
->subtlvs
->prefix_sids
.head
;
971 struct isis_prefix_sid
*psid
=
972 (struct isis_prefix_sid
*)i
;
974 if (psid
->algorithm
!=
978 has_valid_psid
= true;
979 process_N(spftree
, VTYPE_IPREACH_TE
,
980 &ip_info
, dist
, depth
+ 1,
983 * Stop the Prefix-SID iteration since
984 * we only support the SPF algorithm for
991 process_N(spftree
, VTYPE_IPREACH_TE
, &ip_info
,
992 dist
, depth
+ 1, NULL
, parent
);
996 if (!pseudo_lsp
&& spftree
->family
== AF_INET6
) {
997 struct isis_item_list
*ipv6_reachs
;
998 if (spftree
->mtid
== ISIS_MT_IPV4_UNICAST
)
999 ipv6_reachs
= &lsp
->tlvs
->ipv6_reach
;
1001 ipv6_reachs
= isis_lookup_mt_items(
1002 &lsp
->tlvs
->mt_ipv6_reach
, spftree
->mtid
);
1004 struct isis_ipv6_reach
*r
;
1005 for (r
= ipv6_reachs
1006 ? (struct isis_ipv6_reach
*)ipv6_reachs
->head
1009 dist
= cost
+ r
->metric
;
1010 vtype
= r
->external
? VTYPE_IP6REACH_EXTERNAL
1011 : VTYPE_IP6REACH_INTERNAL
;
1012 memset(&ip_info
, 0, sizeof(ip_info
));
1013 ip_info
.dest
.family
= AF_INET6
;
1014 ip_info
.dest
.u
.prefix6
= r
->prefix
.prefix
;
1015 ip_info
.dest
.prefixlen
= r
->prefix
.prefixlen
;
1017 if (spftree
->area
->srdb
.enabled
&& r
->subtlvs
&&
1018 r
->subtlvs
->source_prefix
&&
1019 r
->subtlvs
->source_prefix
->prefixlen
) {
1020 if (spftree
->tree_id
!= SPFTREE_DSTSRC
) {
1021 char buff
[VID2STR_BUFFER
];
1022 zlog_warn("Ignoring dest-src route %s in non dest-src topology",
1025 r
->subtlvs
->source_prefix
,
1031 ip_info
.src
= *r
->subtlvs
->source_prefix
;
1034 /* Parse list of Prefix-SID subTLVs */
1035 has_valid_psid
= false;
1037 for (struct isis_item
*i
=
1038 r
->subtlvs
->prefix_sids
.head
;
1040 struct isis_prefix_sid
*psid
=
1041 (struct isis_prefix_sid
*)i
;
1043 if (psid
->algorithm
!=
1047 has_valid_psid
= true;
1048 process_N(spftree
, vtype
, &ip_info
,
1049 dist
, depth
+ 1, psid
,
1052 * Stop the Prefix-SID iteration since
1053 * we only support the SPF algorithm for
1059 if (!has_valid_psid
)
1060 process_N(spftree
, vtype
, &ip_info
, dist
,
1061 depth
+ 1, NULL
, parent
);
1067 /* if attach bit set in LSP, attached-bit receive ignore is
1068 * not configured, we are a level-1 area and we have no other
1069 * level-2 | level1-2 areas then add a default route toward
1072 if ((lsp
->hdr
.lsp_bits
& LSPBIT_ATT
) == LSPBIT_ATT
1073 && !spftree
->area
->attached_bit_rcv_ignore
1074 && (spftree
->area
->is_type
& IS_LEVEL_1
)
1075 && !isis_level2_adj_up(spftree
->area
)) {
1076 struct prefix_pair ip_info
= { {0} };
1077 if (IS_DEBUG_RTE_EVENTS
)
1078 zlog_debug("ISIS-Spf (%pLS): add default %s route",
1080 spftree
->family
== AF_INET
? "ipv4"
1083 if (spftree
->family
== AF_INET
) {
1084 ip_info
.dest
.family
= AF_INET
;
1085 vtype
= VTYPE_IPREACH_INTERNAL
;
1087 ip_info
.dest
.family
= AF_INET6
;
1088 vtype
= VTYPE_IP6REACH_INTERNAL
;
1090 process_N(spftree
, vtype
, &ip_info
, cost
, depth
+ 1, NULL
,
1094 if (fragnode
== NULL
)
1095 fragnode
= listhead(lsp
->lspu
.frags
);
1097 fragnode
= listnextnode(fragnode
);
1100 lsp
= listgetdata(fragnode
);
1107 static struct isis_adjacency
*adj_find(struct list
*adj_list
, const uint8_t *id
,
1108 int level
, uint16_t mtid
, int family
)
1110 struct isis_adjacency
*adj
;
1111 struct listnode
*node
;
1113 for (ALL_LIST_ELEMENTS_RO(adj_list
, node
, adj
)) {
1114 if (!(adj
->level
& level
))
1116 if (memcmp(adj
->sysid
, id
, ISIS_SYS_ID_LEN
) != 0)
1118 if (adj
->adj_state
!= ISIS_ADJ_UP
)
1120 if (!adj_has_mt(adj
, mtid
))
1122 if (mtid
== ISIS_MT_IPV4_UNICAST
1123 && !speaks(adj
->nlpids
.nlpids
, adj
->nlpids
.count
, family
))
1131 struct spf_preload_tent_ip_reach_args
{
1132 struct isis_spftree
*spftree
;
1133 struct isis_vertex
*parent
;
1136 static int isis_spf_preload_tent_ip_reach_cb(const struct prefix
*prefix
,
1137 uint32_t metric
, bool external
,
1138 struct isis_subtlvs
*subtlvs
,
1141 struct spf_preload_tent_ip_reach_args
*args
= arg
;
1142 struct isis_spftree
*spftree
= args
->spftree
;
1143 struct isis_vertex
*parent
= args
->parent
;
1144 struct prefix_pair ip_info
;
1145 enum vertextype vtype
;
1146 bool has_valid_psid
= false;
1149 return LSP_ITER_CONTINUE
;
1151 assert(spftree
->family
== prefix
->family
);
1152 memset(&ip_info
, 0, sizeof(ip_info
));
1153 prefix_copy(&ip_info
.dest
, prefix
);
1154 apply_mask(&ip_info
.dest
);
1156 if (prefix
->family
== AF_INET
)
1157 vtype
= VTYPE_IPREACH_INTERNAL
;
1159 vtype
= VTYPE_IP6REACH_INTERNAL
;
1161 /* Parse list of Prefix-SID subTLVs if SR is enabled */
1162 if (spftree
->area
->srdb
.enabled
&& subtlvs
) {
1163 for (struct isis_item
*i
= subtlvs
->prefix_sids
.head
; i
;
1165 struct isis_prefix_sid
*psid
=
1166 (struct isis_prefix_sid
*)i
;
1168 if (psid
->algorithm
!= spftree
->algorithm
)
1171 has_valid_psid
= true;
1172 isis_spf_add_local(spftree
, vtype
, &ip_info
, NULL
, 0,
1176 * Stop the Prefix-SID iteration since we only support
1177 * the SPF algorithm for now.
1182 if (!has_valid_psid
)
1183 isis_spf_add_local(spftree
, vtype
, &ip_info
, NULL
, 0, NULL
,
1186 return LSP_ITER_CONTINUE
;
1189 static void isis_spf_preload_tent(struct isis_spftree
*spftree
,
1190 uint8_t *root_sysid
,
1191 struct isis_lsp
*root_lsp
,
1192 struct isis_vertex
*parent
)
1194 struct spf_preload_tent_ip_reach_args ip_reach_args
;
1195 struct isis_spf_adj
*sadj
;
1196 struct listnode
*node
;
1198 if (!CHECK_FLAG(spftree
->flags
, F_SPFTREE_HOPCOUNT_METRIC
)) {
1199 ip_reach_args
.spftree
= spftree
;
1200 ip_reach_args
.parent
= parent
;
1201 isis_lsp_iterate_ip_reach(
1202 root_lsp
, spftree
->family
, spftree
->mtid
,
1203 isis_spf_preload_tent_ip_reach_cb
, &ip_reach_args
);
1206 /* Iterate over adjacencies. */
1207 for (ALL_LIST_ELEMENTS_RO(spftree
->sadj_list
, node
, sadj
)) {
1208 const uint8_t *adj_id
;
1211 if (CHECK_FLAG(sadj
->flags
, F_ISIS_SPF_ADJ_BROADCAST
))
1212 adj_id
= sadj
->lan
.desig_is_id
;
1216 if (isis_lfa_excise_adj_check(spftree
, adj_id
)) {
1218 zlog_debug("ISIS-SPF: excising adjacency %pPN",
1223 metric
= CHECK_FLAG(spftree
->flags
, F_SPFTREE_HOPCOUNT_METRIC
)
1226 if (!LSP_PSEUDO_ID(sadj
->id
)) {
1227 isis_spf_add_local(spftree
,
1228 CHECK_FLAG(sadj
->flags
,
1229 F_ISIS_SPF_ADJ_OLDMETRIC
)
1230 ? VTYPE_NONPSEUDO_IS
1231 : VTYPE_NONPSEUDO_TE_IS
,
1232 sadj
->id
, sadj
, metric
, NULL
,
1234 } else if (sadj
->lsp
) {
1235 isis_spf_process_lsp(spftree
, sadj
->lsp
, metric
, 0,
1236 spftree
->sysid
, parent
);
1241 struct spf_adj_find_reverse_metric_args
{
1242 const uint8_t *id_self
;
1243 uint32_t reverse_metric
;
1246 static int spf_adj_find_reverse_metric_cb(const uint8_t *id
, uint32_t metric
,
1248 struct isis_ext_subtlvs
*subtlvs
,
1251 struct spf_adj_find_reverse_metric_args
*args
= arg
;
1253 if (memcmp(id
, args
->id_self
, ISIS_SYS_ID_LEN
))
1254 return LSP_ITER_CONTINUE
;
1256 args
->reverse_metric
= metric
;
1258 return LSP_ITER_STOP
;
1262 * Change all SPF adjacencies to use the link cost in the direction from the
1263 * next hop back towards root in place of the link cost in the direction away
1264 * from root towards the next hop.
1266 static void spf_adj_get_reverse_metrics(struct isis_spftree
*spftree
)
1268 struct isis_spf_adj
*sadj
;
1269 struct listnode
*node
, *nnode
;
1271 for (ALL_LIST_ELEMENTS(spftree
->sadj_list
, node
, nnode
, sadj
)) {
1272 uint8_t lspid
[ISIS_SYS_ID_LEN
+ 2];
1273 struct isis_lsp
*lsp_adj
;
1274 const uint8_t *id_self
;
1275 struct spf_adj_find_reverse_metric_args args
;
1277 /* Skip pseudonodes. */
1278 if (LSP_PSEUDO_ID(sadj
->id
))
1281 /* Find LSP of the corresponding adjacency. */
1282 memcpy(lspid
, sadj
->id
, ISIS_SYS_ID_LEN
);
1283 LSP_PSEUDO_ID(lspid
) = 0;
1284 LSP_FRAGMENT(lspid
) = 0;
1285 lsp_adj
= lsp_search(spftree
->lspdb
, lspid
);
1286 if (lsp_adj
== NULL
|| lsp_adj
->hdr
.rem_lifetime
== 0) {
1287 /* Delete one-way adjacency. */
1288 listnode_delete(spftree
->sadj_list
, sadj
);
1289 isis_spf_adj_free(sadj
);
1293 /* Find root node in the LSP of the adjacent router. */
1294 if (CHECK_FLAG(sadj
->flags
, F_ISIS_SPF_ADJ_BROADCAST
))
1295 id_self
= sadj
->lan
.desig_is_id
;
1297 id_self
= spftree
->sysid
;
1298 args
.id_self
= id_self
;
1299 args
.reverse_metric
= UINT32_MAX
;
1300 isis_lsp_iterate_is_reach(lsp_adj
, spftree
->mtid
,
1301 spf_adj_find_reverse_metric_cb
,
1303 if (args
.reverse_metric
== UINT32_MAX
) {
1304 /* Delete one-way adjacency. */
1305 listnode_delete(spftree
->sadj_list
, sadj
);
1306 isis_spf_adj_free(sadj
);
1309 sadj
->metric
= args
.reverse_metric
;
1313 static void spf_adj_list_parse_tlv(struct isis_spftree
*spftree
,
1314 struct list
*adj_list
, const uint8_t *id
,
1315 const uint8_t *desig_is_id
,
1316 uint32_t pseudo_metric
, uint32_t metric
,
1318 struct isis_ext_subtlvs
*subtlvs
)
1320 struct isis_spf_adj
*sadj
;
1321 uint8_t lspid
[ISIS_SYS_ID_LEN
+ 2];
1322 struct isis_lsp
*lsp
;
1325 /* Skip self in the pseudonode. */
1326 if (desig_is_id
&& !memcmp(id
, spftree
->sysid
, ISIS_SYS_ID_LEN
))
1329 /* Find LSP from the adjacency. */
1330 memcpy(lspid
, id
, ISIS_SYS_ID_LEN
+ 1);
1331 LSP_FRAGMENT(lspid
) = 0;
1332 lsp
= lsp_search(spftree
->lspdb
, lspid
);
1333 if (lsp
== NULL
|| lsp
->hdr
.rem_lifetime
== 0) {
1334 zlog_warn("ISIS-SPF: No LSP found from root to L%d %pLS",
1335 spftree
->level
, lspid
);
1339 sadj
= XCALLOC(MTYPE_ISIS_SPF_ADJ
, sizeof(*sadj
));
1340 memcpy(sadj
->id
, id
, sizeof(sadj
->id
));
1342 memcpy(sadj
->lan
.desig_is_id
, desig_is_id
,
1343 sizeof(sadj
->lan
.desig_is_id
));
1344 SET_FLAG(flags
, F_ISIS_SPF_ADJ_BROADCAST
);
1345 sadj
->metric
= pseudo_metric
;
1347 sadj
->metric
= metric
;
1349 SET_FLAG(flags
, F_ISIS_SPF_ADJ_OLDMETRIC
);
1351 sadj
->subtlvs
= subtlvs
;
1352 sadj
->flags
= flags
;
1354 if ((oldmetric
&& metric
== ISIS_NARROW_METRIC_INFINITY
)
1355 || (!oldmetric
&& metric
== ISIS_WIDE_METRIC_INFINITY
))
1356 SET_FLAG(flags
, F_ISIS_SPF_ADJ_METRIC_INFINITY
);
1358 /* Set real adjacency. */
1359 if (!CHECK_FLAG(spftree
->flags
, F_SPFTREE_NO_ADJACENCIES
)
1360 && !LSP_PSEUDO_ID(id
)) {
1361 struct isis_adjacency
*adj
;
1363 adj
= adj_find(adj_list
, id
, spftree
->level
, spftree
->mtid
,
1366 XFREE(MTYPE_ISIS_SPF_ADJ
, sadj
);
1370 listnode_delete(adj_list
, adj
);
1374 /* Add adjacency to the list. */
1375 listnode_add(spftree
->sadj_list
, sadj
);
1377 if (!LSP_PSEUDO_ID(id
)) {
1378 struct isis_spf_node
*node
;
1380 node
= isis_spf_node_find(&spftree
->adj_nodes
, id
);
1382 node
= isis_spf_node_new(&spftree
->adj_nodes
, id
);
1383 if (node
->best_metric
== 0 || sadj
->metric
< node
->best_metric
)
1384 node
->best_metric
= sadj
->metric
;
1385 listnode_add(node
->adjacencies
, sadj
);
1388 /* Parse pseudonode LSP too. */
1389 if (LSP_PSEUDO_ID(id
))
1390 spf_adj_list_parse_lsp(spftree
, adj_list
, lsp
, id
, metric
);
1393 static void spf_adj_list_parse_lsp(struct isis_spftree
*spftree
,
1394 struct list
*adj_list
, struct isis_lsp
*lsp
,
1395 const uint8_t *pseudo_nodeid
,
1396 uint32_t pseudo_metric
)
1398 bool pseudo_lsp
= LSP_PSEUDO_ID(lsp
->hdr
.lsp_id
);
1399 struct isis_lsp
*frag
;
1400 struct listnode
*node
;
1401 struct isis_item
*head
;
1402 struct isis_item_list
*te_neighs
;
1404 if (lsp
->hdr
.seqno
== 0 || lsp
->hdr
.rem_lifetime
== 0)
1409 if (pseudo_lsp
|| spftree
->mtid
== ISIS_MT_IPV4_UNICAST
) {
1410 head
= lsp
->tlvs
->oldstyle_reach
.head
;
1411 for (struct isis_oldstyle_reach
*reach
=
1412 (struct isis_oldstyle_reach
*)head
;
1413 reach
; reach
= reach
->next
) {
1414 spf_adj_list_parse_tlv(
1415 spftree
, adj_list
, reach
->id
,
1416 pseudo_nodeid
, pseudo_metric
,
1417 reach
->metric
, true, NULL
);
1421 if (pseudo_lsp
|| spftree
->mtid
== ISIS_MT_IPV4_UNICAST
)
1422 te_neighs
= &lsp
->tlvs
->extended_reach
;
1424 te_neighs
= isis_get_mt_items(&lsp
->tlvs
->mt_reach
,
1427 head
= te_neighs
->head
;
1428 for (struct isis_extended_reach
*reach
=
1429 (struct isis_extended_reach
*)head
;
1430 reach
; reach
= reach
->next
) {
1431 spf_adj_list_parse_tlv(
1432 spftree
, adj_list
, reach
->id
,
1433 pseudo_nodeid
, pseudo_metric
,
1434 reach
->metric
, false, reach
->subtlvs
);
1439 if (LSP_FRAGMENT(lsp
->hdr
.lsp_id
))
1442 /* Parse LSP fragments. */
1443 for (ALL_LIST_ELEMENTS_RO(lsp
->lspu
.frags
, node
, frag
)) {
1447 spf_adj_list_parse_lsp(spftree
, adj_list
, frag
, pseudo_nodeid
,
1452 static void isis_spf_build_adj_list(struct isis_spftree
*spftree
,
1453 struct isis_lsp
*lsp
)
1455 struct list
*adj_list
= NULL
;
1457 if (!CHECK_FLAG(spftree
->flags
, F_SPFTREE_NO_ADJACENCIES
))
1458 adj_list
= list_dup(spftree
->area
->adjacency_list
);
1460 spf_adj_list_parse_lsp(spftree
, adj_list
, lsp
, NULL
, 0);
1462 if (!CHECK_FLAG(spftree
->flags
, F_SPFTREE_NO_ADJACENCIES
))
1463 list_delete(&adj_list
);
1465 if (spftree
->type
== SPF_TYPE_REVERSE
)
1466 spf_adj_get_reverse_metrics(spftree
);
1470 * The parent(s) for vertex is set when added to TENT list
1471 * now we just put the child pointer(s) in place
1473 static void add_to_paths(struct isis_spftree
*spftree
,
1474 struct isis_vertex
*vertex
)
1476 #ifdef EXTREME_DEBUG
1477 char buff
[VID2STR_BUFFER
];
1478 #endif /* EXTREME_DEBUG */
1480 if (isis_find_vertex(&spftree
->paths
, &vertex
->N
, vertex
->type
))
1482 isis_vertex_queue_append(&spftree
->paths
, vertex
);
1484 #ifdef EXTREME_DEBUG
1485 if (IS_DEBUG_SPF_EVENTS
)
1486 zlog_debug("ISIS-SPF: added %s %s %s depth %d dist %d to PATHS",
1487 print_sys_hostname(vertex
->N
.id
),
1488 vtype2string(vertex
->type
),
1489 vid2string(vertex
, buff
, sizeof(buff
)),
1490 vertex
->depth
, vertex
->d_N
);
1491 #endif /* EXTREME_DEBUG */
1494 static void init_spt(struct isis_spftree
*spftree
, int mtid
)
1496 /* Clear data from previous run. */
1497 hash_clean(spftree
->prefix_sids
, NULL
);
1498 isis_spf_node_list_clear(&spftree
->adj_nodes
);
1499 list_delete_all_node(spftree
->sadj_list
);
1500 isis_vertex_queue_clear(&spftree
->tents
);
1501 isis_vertex_queue_clear(&spftree
->paths
);
1502 isis_zebra_rlfa_unregister_all(spftree
);
1503 isis_rlfa_list_clear(spftree
);
1504 list_delete_all_node(spftree
->lfa
.remote
.pc_spftrees
);
1505 memset(&spftree
->lfa
.protection_counters
, 0,
1506 sizeof(spftree
->lfa
.protection_counters
));
1508 spftree
->mtid
= mtid
;
1511 static enum spf_prefix_priority
1512 spf_prefix_priority(struct isis_spftree
*spftree
, struct isis_vertex
*vertex
)
1514 struct isis_area
*area
= spftree
->area
;
1515 struct prefix
*prefix
= &vertex
->N
.ip
.p
.dest
;
1517 for (int priority
= SPF_PREFIX_PRIO_CRITICAL
;
1518 priority
<= SPF_PREFIX_PRIO_MEDIUM
; priority
++) {
1519 struct spf_prefix_priority_acl
*ppa
;
1520 enum filter_type ret
= FILTER_PERMIT
;
1522 ppa
= &area
->spf_prefix_priorities
[priority
];
1523 switch (spftree
->family
) {
1525 ret
= access_list_apply(ppa
->list_v4
, prefix
);
1528 ret
= access_list_apply(ppa
->list_v6
, prefix
);
1534 if (ret
== FILTER_PERMIT
)
1538 /* Assign medium priority to loopback prefixes by default. */
1539 if (is_host_route(prefix
))
1540 return SPF_PREFIX_PRIO_MEDIUM
;
1542 return SPF_PREFIX_PRIO_LOW
;
1545 static void spf_path_process(struct isis_spftree
*spftree
,
1546 struct isis_vertex
*vertex
)
1548 struct isis_area
*area
= spftree
->area
;
1549 int level
= spftree
->level
;
1550 char buff
[VID2STR_BUFFER
];
1552 if (spftree
->type
== SPF_TYPE_TI_LFA
&& VTYPE_IS(vertex
->type
)
1553 && !CHECK_FLAG(spftree
->flags
, F_SPFTREE_NO_ADJACENCIES
)) {
1554 if (listcount(vertex
->Adj_N
) > 0) {
1555 struct isis_adjacency
*adj
;
1557 if (isis_tilfa_check(spftree
, vertex
) != 0)
1560 adj
= isis_adj_find(area
, level
, vertex
->N
.id
);
1562 sr_adj_sid_add_single(adj
, spftree
->family
,
1563 true, vertex
->Adj_N
);
1564 } else if (IS_DEBUG_SPF_EVENTS
)
1566 "ISIS-SPF: no adjacencies, do not install backup Adj-SID for %s depth %d dist %d",
1567 vid2string(vertex
, buff
, sizeof(buff
)),
1568 vertex
->depth
, vertex
->d_N
);
1571 if (VTYPE_IP(vertex
->type
)
1572 && !CHECK_FLAG(spftree
->flags
, F_SPFTREE_NO_ROUTES
)) {
1573 enum spf_prefix_priority priority
;
1575 priority
= spf_prefix_priority(spftree
, vertex
);
1576 vertex
->N
.ip
.priority
= priority
;
1577 if (vertex
->depth
== 1 || listcount(vertex
->Adj_N
) > 0) {
1578 struct isis_spftree
*pre_spftree
;
1579 struct route_table
*route_table
= NULL
;
1580 bool allow_ecmp
= false;
1582 switch (spftree
->type
) {
1584 case SPF_TYPE_TI_LFA
:
1586 > area
->lfa_priority_limit
[level
- 1]) {
1589 "ISIS-LFA: skipping %s %s (low prefix priority)",
1598 case SPF_TYPE_FORWARD
:
1599 case SPF_TYPE_REVERSE
:
1603 switch (spftree
->type
) {
1605 isis_rlfa_check(spftree
, vertex
);
1607 case SPF_TYPE_TI_LFA
:
1608 if (isis_tilfa_check(spftree
, vertex
) != 0)
1611 pre_spftree
= spftree
->lfa
.old
.spftree
;
1612 route_table
= pre_spftree
->route_table_backup
;
1613 allow_ecmp
= area
->lfa_load_sharing
[level
- 1];
1614 pre_spftree
->lfa
.protection_counters
1615 .tilfa
[vertex
->N
.ip
.priority
] += 1;
1617 case SPF_TYPE_FORWARD
:
1618 case SPF_TYPE_REVERSE
:
1619 route_table
= spftree
->route_table
;
1623 * Update LFA protection counters (ignore local
1626 if (vertex
->depth
> 1) {
1627 spftree
->lfa
.protection_counters
1628 .total
[priority
] += 1;
1629 if (listcount(vertex
->Adj_N
) > 1)
1630 spftree
->lfa
.protection_counters
1631 .ecmp
[priority
] += 1;
1637 &vertex
->N
.ip
.p
.dest
, &vertex
->N
.ip
.p
.src
,
1638 vertex
->d_N
, vertex
->depth
, &vertex
->N
.ip
.sr
,
1639 vertex
->Adj_N
, allow_ecmp
, area
, route_table
);
1640 } else if (IS_DEBUG_SPF_EVENTS
)
1642 "ISIS-SPF: no adjacencies, do not install route for %s depth %d dist %d",
1643 vid2string(vertex
, buff
, sizeof(buff
)),
1644 vertex
->depth
, vertex
->d_N
);
1648 static void isis_spf_loop(struct isis_spftree
*spftree
,
1649 uint8_t *root_sysid
)
1651 struct isis_vertex
*vertex
;
1652 struct isis_lsp
*lsp
;
1653 struct listnode
*node
;
1655 while (isis_vertex_queue_count(&spftree
->tents
)) {
1656 vertex
= isis_vertex_queue_pop(&spftree
->tents
);
1658 #ifdef EXTREME_DEBUG
1659 if (IS_DEBUG_SPF_EVENTS
)
1661 "ISIS-SPF: get TENT node %s %s depth %d dist %d to PATHS",
1662 print_sys_hostname(vertex
->N
.id
),
1663 vtype2string(vertex
->type
), vertex
->depth
,
1665 #endif /* EXTREME_DEBUG */
1667 add_to_paths(spftree
, vertex
);
1668 if (!VTYPE_IS(vertex
->type
))
1671 lsp
= lsp_for_vertex(spftree
, vertex
);
1673 zlog_warn("ISIS-SPF: No LSP found for %pPN",
1678 isis_spf_process_lsp(spftree
, lsp
, vertex
->d_N
, vertex
->depth
,
1679 root_sysid
, vertex
);
1682 /* Generate routes once the SPT is formed. */
1683 for (ALL_QUEUE_ELEMENTS_RO(&spftree
->paths
, node
, vertex
)) {
1684 /* New-style TLVs take precedence over the old-style TLVs. */
1685 switch (vertex
->type
) {
1686 case VTYPE_IPREACH_INTERNAL
:
1687 case VTYPE_IPREACH_EXTERNAL
:
1688 if (isis_find_vertex(&spftree
->paths
, &vertex
->N
,
1692 case VTYPE_PSEUDO_IS
:
1693 case VTYPE_PSEUDO_TE_IS
:
1694 case VTYPE_NONPSEUDO_IS
:
1695 case VTYPE_NONPSEUDO_TE_IS
:
1697 case VTYPE_IPREACH_TE
:
1698 case VTYPE_IP6REACH_INTERNAL
:
1699 case VTYPE_IP6REACH_EXTERNAL
:
1703 spf_path_process(spftree
, vertex
);
1707 struct isis_spftree
*isis_run_hopcount_spf(struct isis_area
*area
,
1709 struct isis_spftree
*spftree
)
1712 spftree
= isis_spftree_new(
1713 area
, &area
->lspdb
[IS_LEVEL_2
- 1], sysid
, ISIS_LEVEL2
,
1714 SPFTREE_IPV4
, SPF_TYPE_FORWARD
,
1715 F_SPFTREE_HOPCOUNT_METRIC
, SR_ALGORITHM_SPF
);
1717 init_spt(spftree
, ISIS_MT_IPV4_UNICAST
);
1718 if (!memcmp(sysid
, area
->isis
->sysid
, ISIS_SYS_ID_LEN
)) {
1719 struct isis_lsp
*root_lsp
;
1720 struct isis_vertex
*root_vertex
;
1722 root_lsp
= isis_root_system_lsp(spftree
->lspdb
, spftree
->sysid
);
1725 * If we are running locally, initialize with
1726 * information from adjacencies
1728 root_vertex
= isis_spf_add_root(spftree
);
1730 isis_spf_preload_tent(spftree
, sysid
, root_lsp
,
1734 isis_vertex_queue_insert(
1736 isis_vertex_new(spftree
, sysid
, VTYPE_NONPSEUDO_TE_IS
));
1739 isis_spf_loop(spftree
, sysid
);
1744 void isis_run_spf(struct isis_spftree
*spftree
)
1746 struct isis_lsp
*root_lsp
;
1747 struct isis_vertex
*root_vertex
;
1748 struct timeval time_start
;
1749 struct timeval time_end
;
1750 struct isis_mt_router_info
*mt_router_info
;
1753 /* Get time that can't roll backwards. */
1754 monotime(&time_start
);
1756 root_lsp
= isis_root_system_lsp(spftree
->lspdb
, spftree
->sysid
);
1757 if (root_lsp
== NULL
) {
1758 zlog_err("ISIS-SPF: could not find own l%d LSP!",
1763 /* Get Multi-Topology ID. */
1764 switch (spftree
->tree_id
) {
1766 mtid
= ISIS_MT_IPV4_UNICAST
;
1769 mt_router_info
= isis_tlvs_lookup_mt_router_info(
1770 root_lsp
->tlvs
, ISIS_MT_IPV6_UNICAST
);
1772 mtid
= ISIS_MT_IPV6_UNICAST
;
1774 mtid
= ISIS_MT_IPV4_UNICAST
;
1776 case SPFTREE_DSTSRC
:
1777 mtid
= ISIS_MT_IPV6_DSTSRC
;
1781 "%s should never be called with SPFTREE_COUNT as argument!",
1789 init_spt(spftree
, mtid
);
1791 root_vertex
= isis_spf_add_root(spftree
);
1793 isis_spf_build_adj_list(spftree
, root_lsp
);
1794 isis_spf_preload_tent(spftree
, spftree
->sysid
, root_lsp
, root_vertex
);
1799 if (!isis_vertex_queue_count(&spftree
->tents
)
1800 && (IS_DEBUG_SPF_EVENTS
)) {
1801 zlog_warn("ISIS-SPF: TENT is empty SPF-root:%s",
1802 print_sys_hostname(spftree
->sysid
));
1805 isis_spf_loop(spftree
, spftree
->sysid
);
1806 spftree
->runcount
++;
1807 spftree
->last_run_timestamp
= time(NULL
);
1808 spftree
->last_run_monotime
= monotime(&time_end
);
1809 spftree
->last_run_duration
=
1810 ((time_end
.tv_sec
- time_start
.tv_sec
) * 1000000)
1811 + (time_end
.tv_usec
- time_start
.tv_usec
);
1814 static void isis_run_spf_with_protection(struct isis_area
*area
,
1815 struct isis_spftree
*spftree
)
1817 /* Run forward SPF locally. */
1818 memcpy(spftree
->sysid
, area
->isis
->sysid
, ISIS_SYS_ID_LEN
);
1819 isis_run_spf(spftree
);
1821 /* Run LFA protection if configured. */
1822 if (area
->lfa_protected_links
[spftree
->level
- 1] > 0
1823 || area
->tilfa_protected_links
[spftree
->level
- 1] > 0)
1824 isis_spf_run_lfa(area
, spftree
);
1827 void isis_spf_verify_routes(struct isis_area
*area
, struct isis_spftree
**trees
)
1829 if (area
->is_type
== IS_LEVEL_1
) {
1830 isis_route_verify_table(area
, trees
[0]->route_table
,
1831 trees
[0]->route_table_backup
);
1832 } else if (area
->is_type
== IS_LEVEL_2
) {
1833 isis_route_verify_table(area
, trees
[1]->route_table
,
1834 trees
[1]->route_table_backup
);
1836 isis_route_verify_merge(area
, trees
[0]->route_table
,
1837 trees
[0]->route_table_backup
,
1838 trees
[1]->route_table
,
1839 trees
[1]->route_table_backup
);
1843 void isis_spf_invalidate_routes(struct isis_spftree
*tree
)
1845 struct isis_route_table_info
*backup_info
;
1847 isis_route_invalidate_table(tree
->area
, tree
->route_table
);
1849 /* Delete backup routes. */
1851 backup_info
= tree
->route_table_backup
->info
;
1852 route_table_finish(tree
->route_table_backup
);
1853 isis_route_table_info_free(backup_info
);
1854 tree
->route_table_backup
= srcdest_table_init();
1855 tree
->route_table_backup
->info
=
1856 isis_route_table_info_alloc(tree
->algorithm
);
1857 tree
->route_table_backup
->cleanup
= isis_route_node_cleanup
;
1860 void isis_spf_switchover_routes(struct isis_area
*area
,
1861 struct isis_spftree
**trees
, int family
,
1862 union g_addr
*nexthop_ip
, ifindex_t ifindex
,
1865 isis_route_switchover_nexthop(area
, trees
[level
- 1]->route_table
,
1866 family
, nexthop_ip
, ifindex
);
1869 static void isis_run_spf_cb(struct event
*thread
)
1871 struct isis_spf_run
*run
= EVENT_ARG(thread
);
1872 struct isis_area
*area
= run
->area
;
1873 int level
= run
->level
;
1876 XFREE(MTYPE_ISIS_SPF_RUN
, run
);
1878 if (!(area
->is_type
& level
)) {
1879 if (IS_DEBUG_SPF_EVENTS
)
1880 zlog_warn("ISIS-SPF (%s) area does not share level",
1885 isis_area_delete_backup_adj_sids(area
, level
);
1886 isis_area_invalidate_routes(area
, level
);
1888 if (IS_DEBUG_SPF_EVENTS
)
1889 zlog_debug("ISIS-SPF (%s) L%d SPF needed, periodic SPF",
1890 area
->area_tag
, level
);
1892 if (area
->ip_circuits
) {
1893 isis_run_spf_with_protection(
1894 area
, area
->spftree
[SPFTREE_IPV4
][level
- 1]);
1897 if (area
->ipv6_circuits
) {
1898 isis_run_spf_with_protection(
1899 area
, area
->spftree
[SPFTREE_IPV6
][level
- 1]);
1902 if (area
->ipv6_circuits
&& isis_area_ipv6_dstsrc_enabled(area
)) {
1903 isis_run_spf_with_protection(
1904 area
, area
->spftree
[SPFTREE_DSTSRC
][level
- 1]);
1909 area
->spf_run_count
[level
]++;
1911 isis_area_verify_routes(area
);
1913 /* walk all circuits and reset any spf specific flags */
1914 struct listnode
*node
;
1915 struct isis_circuit
*circuit
;
1916 for (ALL_LIST_ELEMENTS_RO(area
->circuit_list
, node
, circuit
))
1917 UNSET_FLAG(circuit
->flags
, ISIS_CIRCUIT_FLAPPED_AFTER_SPF
);
1919 fabricd_run_spf(area
);
1922 static struct isis_spf_run
*isis_run_spf_arg(struct isis_area
*area
, int level
)
1924 struct isis_spf_run
*run
= XMALLOC(MTYPE_ISIS_SPF_RUN
, sizeof(*run
));
1932 void isis_spf_timer_free(void *run
)
1934 XFREE(MTYPE_ISIS_SPF_RUN
, run
);
1937 int _isis_spf_schedule(struct isis_area
*area
, int level
,
1938 const char *func
, const char *file
, int line
)
1940 struct isis_spftree
*spftree
;
1942 long tree_diff
, diff
;
1945 now
= monotime(NULL
);
1947 for (tree
= SPFTREE_IPV4
; tree
< SPFTREE_COUNT
; tree
++) {
1948 spftree
= area
->spftree
[tree
][level
- 1];
1949 tree_diff
= difftime(now
- spftree
->last_run_monotime
, 0);
1950 if (tree_diff
!= now
&& (diff
== 0 || tree_diff
< diff
))
1954 if (CHECK_FLAG(im
->options
, F_ISIS_UNIT_TEST
))
1958 assert(area
->is_type
& level
);
1960 if (IS_DEBUG_SPF_EVENTS
) {
1962 "ISIS-SPF (%s) L%d SPF schedule called, lastrun %ld sec ago Caller: %s %s:%d",
1963 area
->area_tag
, level
, diff
, func
, file
, line
);
1966 EVENT_OFF(area
->t_rlfa_rib_update
);
1967 if (area
->spf_delay_ietf
[level
- 1]) {
1968 /* Need to call schedule function also if spf delay is running
1970 * restart holdoff timer - compare
1971 * draft-ietf-rtgwg-backoff-algo-04 */
1973 spf_backoff_schedule(area
->spf_delay_ietf
[level
- 1]);
1974 if (area
->spf_timer
[level
- 1])
1977 event_add_timer_msec(master
, isis_run_spf_cb
,
1978 isis_run_spf_arg(area
, level
), delay
,
1979 &area
->spf_timer
[level
- 1]);
1983 if (area
->spf_timer
[level
- 1])
1986 /* wait configured min_spf_interval before doing the SPF */
1988 if (diff
>= area
->min_spf_interval
[level
- 1]
1989 || area
->bfd_force_spf_refresh
) {
1991 * Last run is more than min interval ago or BFD signalled a
1992 * 'down' message, schedule immediate run
1996 if (area
->bfd_force_spf_refresh
) {
1998 "ISIS-SPF (%s) L%d SPF scheduled immediately due to BFD 'down' message",
1999 area
->area_tag
, level
);
2000 area
->bfd_force_spf_refresh
= false;
2003 timer
= area
->min_spf_interval
[level
- 1] - diff
;
2006 event_add_timer(master
, isis_run_spf_cb
, isis_run_spf_arg(area
, level
),
2007 timer
, &area
->spf_timer
[level
- 1]);
2009 if (IS_DEBUG_SPF_EVENTS
)
2010 zlog_debug("ISIS-SPF (%s) L%d SPF scheduled %ld sec from now",
2011 area
->area_tag
, level
, timer
);
2016 static void isis_print_paths(struct vty
*vty
, struct isis_vertex_queue
*queue
,
2017 uint8_t *root_sysid
)
2019 struct listnode
*node
;
2020 struct isis_vertex
*vertex
;
2021 char buff
[VID2STR_BUFFER
];
2024 "Vertex Type Metric Next-Hop Interface Parent\n");
2026 for (ALL_QUEUE_ELEMENTS_RO(queue
, node
, vertex
)) {
2027 if (VTYPE_IS(vertex
->type
)
2028 && memcmp(vertex
->N
.id
, root_sysid
, ISIS_SYS_ID_LEN
) == 0) {
2029 vty_out(vty
, "%-20s %-12s %-6s",
2030 print_sys_hostname(root_sysid
), "", "");
2031 vty_out(vty
, "%-30s\n", "");
2036 struct listnode
*anode
= listhead(vertex
->Adj_N
);
2037 struct listnode
*pnode
= listhead(vertex
->parents
);
2038 struct isis_vertex_adj
*vadj
;
2039 struct isis_vertex
*pvertex
;
2041 vty_out(vty
, "%-20s %-12s %-6u ",
2042 vid2string(vertex
, buff
, sizeof(buff
)),
2043 vtype2string(vertex
->type
), vertex
->d_N
);
2044 for (unsigned int i
= 0;
2045 i
< MAX(vertex
->Adj_N
? listcount(vertex
->Adj_N
) : 0,
2046 vertex
->parents
? listcount(vertex
->parents
) : 0);
2049 vadj
= listgetdata(anode
);
2050 anode
= anode
->next
;
2056 pvertex
= listgetdata(pnode
);
2057 pnode
= pnode
->next
;
2064 vty_out(vty
, "%-20s %-12s %-6s ", "", "", "");
2068 struct isis_spf_adj
*sadj
= vadj
->sadj
;
2070 vty_out(vty
, "%-20s %-9s ",
2071 print_sys_hostname(sadj
->id
),
2072 sadj
->adj
? sadj
->adj
->circuit
2079 vty_out(vty
, "%-20s %-9s ", "", "");
2081 vty_out(vty
, "%s(%d)",
2082 vid2string(pvertex
, buff
, sizeof(buff
)),
2092 void isis_print_spftree(struct vty
*vty
, struct isis_spftree
*spftree
)
2094 const char *tree_id_text
= NULL
;
2096 if (!spftree
|| !isis_vertex_queue_count(&spftree
->paths
))
2099 switch (spftree
->tree_id
) {
2101 tree_id_text
= "that speak IP";
2104 tree_id_text
= "that speak IPv6";
2106 case SPFTREE_DSTSRC
:
2107 tree_id_text
= "that support IPv6 dst-src routing";
2110 assert(!"isis_print_spftree shouldn't be called with SPFTREE_COUNT as type");
2114 vty_out(vty
, "IS-IS paths to level-%d routers %s\n", spftree
->level
,
2116 isis_print_paths(vty
, &spftree
->paths
, spftree
->sysid
);
2120 static void show_isis_topology_common(struct vty
*vty
, int levels
,
2123 struct listnode
*node
;
2124 struct isis_area
*area
;
2126 if (!isis
->area_list
|| isis
->area_list
->count
== 0)
2129 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, node
, area
)) {
2130 vty_out(vty
, "Area %s:\n",
2131 area
->area_tag
? area
->area_tag
: "null");
2133 for (int level
= ISIS_LEVEL1
; level
<= ISIS_LEVELS
; level
++) {
2134 if ((level
& levels
) == 0)
2137 if (area
->ip_circuits
> 0) {
2140 area
->spftree
[SPFTREE_IPV4
][level
- 1]);
2142 if (area
->ipv6_circuits
> 0) {
2145 area
->spftree
[SPFTREE_IPV6
][level
- 1]);
2147 if (isis_area_ipv6_dstsrc_enabled(area
)) {
2148 isis_print_spftree(vty
,
2149 area
->spftree
[SPFTREE_DSTSRC
]
2154 if (fabricd_spftree(area
)) {
2156 "IS-IS paths to level-2 routers with hop-by-hop metric\n");
2157 isis_print_paths(vty
, &fabricd_spftree(area
)->paths
, isis
->sysid
);
2165 DEFUN(show_isis_topology
, show_isis_topology_cmd
,
2167 " [vrf <NAME|all>] topology"
2169 " [<level-1|level-2>]"
2172 SHOW_STR PROTO_HELP VRF_CMD_HELP_STR
2174 "IS-IS paths to Intermediate Systems\n"
2176 "Paths to all level-1 routers in the area\n"
2177 "Paths to all level-2 routers in the domain\n"
2181 int levels
= ISIS_LEVELS
;
2182 struct listnode
*node
;
2183 struct isis
*isis
= NULL
;
2185 const char *vrf_name
= VRF_DEFAULT_NAME
;
2186 bool all_vrf
= false;
2189 if (argv_find(argv
, argc
, "topology", &idx
)) {
2191 levels
= ISIS_LEVEL1
| ISIS_LEVEL2
;
2192 else if (strmatch(argv
[idx
+ 1]->arg
, "level-1"))
2193 levels
= ISIS_LEVEL1
;
2195 levels
= ISIS_LEVEL2
;
2199 vty_out(vty
, "IS-IS Routing Process not enabled\n");
2202 ISIS_FIND_VRF_ARGS(argv
, argc
, idx_vrf
, vrf_name
, all_vrf
);
2206 for (ALL_LIST_ELEMENTS_RO(im
->isis
, node
, isis
))
2207 show_isis_topology_common(vty
, levels
, isis
);
2210 isis
= isis_lookup_by_vrfname(vrf_name
);
2212 show_isis_topology_common(vty
, levels
, isis
);
2218 static void isis_print_route(struct ttable
*tt
, const struct prefix
*prefix
,
2219 struct isis_route_info
*rinfo
, bool prefix_sid
,
2220 bool no_adjacencies
)
2222 struct isis_nexthop
*nexthop
;
2223 struct listnode
*node
;
2225 char buf_prefix
[BUFSIZ
];
2227 (void)prefix2str(prefix
, buf_prefix
, sizeof(buf_prefix
));
2228 for (ALL_LIST_ELEMENTS_RO(rinfo
->nexthops
, node
, nexthop
)) {
2229 struct interface
*ifp
;
2230 char buf_iface
[BUFSIZ
];
2231 char buf_nhop
[BUFSIZ
];
2233 if (!no_adjacencies
) {
2234 inet_ntop(nexthop
->family
, &nexthop
->ip
, buf_nhop
,
2236 ifp
= if_lookup_by_index(nexthop
->ifindex
, VRF_DEFAULT
);
2238 strlcpy(buf_iface
, ifp
->name
,
2241 snprintf(buf_iface
, sizeof(buf_iface
),
2242 "ifindex %u", nexthop
->ifindex
);
2244 strlcpy(buf_nhop
, print_sys_hostname(nexthop
->sysid
),
2246 strlcpy(buf_iface
, "-", sizeof(buf_iface
));
2250 char buf_sid
[BUFSIZ
] = {};
2251 char buf_lblop
[BUFSIZ
] = {};
2253 if (nexthop
->sr
.present
) {
2254 snprintf(buf_sid
, sizeof(buf_sid
), "%u",
2255 nexthop
->sr
.sid
.value
);
2257 buf_lblop
, sizeof(buf_lblop
),
2258 rinfo
->sr_algo
[SR_ALGORITHM_SPF
].label
,
2261 strlcpy(buf_sid
, "-", sizeof(buf_sid
));
2262 strlcpy(buf_lblop
, "-", sizeof(buf_lblop
));
2266 ttable_add_row(tt
, "%s|%u|%s|%s|%s|%s",
2267 buf_prefix
, rinfo
->cost
,
2268 buf_iface
, buf_nhop
, buf_sid
,
2272 ttable_add_row(tt
, "||%s|%s|%s|%s", buf_iface
,
2273 buf_nhop
, buf_sid
, buf_lblop
);
2275 char buf_labels
[BUFSIZ
] = {};
2277 if (nexthop
->label_stack
) {
2279 i
< nexthop
->label_stack
->num_labels
;
2281 char buf_label
[BUFSIZ
];
2284 nexthop
->label_stack
->label
[i
],
2288 strlcat(buf_labels
, "/",
2289 sizeof(buf_labels
));
2290 strlcat(buf_labels
, buf_label
,
2291 sizeof(buf_labels
));
2293 } else if (nexthop
->sr
.present
)
2294 label2str(nexthop
->sr
.label
, 0, buf_labels
,
2295 sizeof(buf_labels
));
2297 strlcpy(buf_labels
, "-", sizeof(buf_labels
));
2300 ttable_add_row(tt
, "%s|%u|%s|%s|%s", buf_prefix
,
2301 rinfo
->cost
, buf_iface
, buf_nhop
,
2305 ttable_add_row(tt
, "||%s|%s|%s", buf_iface
,
2306 buf_nhop
, buf_labels
);
2309 if (list_isempty(rinfo
->nexthops
)) {
2311 char buf_sid
[BUFSIZ
] = {};
2312 char buf_lblop
[BUFSIZ
] = {};
2314 if (rinfo
->sr_algo
[SR_ALGORITHM_SPF
].present
) {
2315 snprintf(buf_sid
, sizeof(buf_sid
), "%u",
2316 rinfo
->sr_algo
[SR_ALGORITHM_SPF
]
2319 buf_lblop
, sizeof(buf_lblop
),
2320 rinfo
->sr_algo
[SR_ALGORITHM_SPF
].label
,
2321 MPLS_LABEL_IMPLICIT_NULL
);
2323 strlcpy(buf_sid
, "-", sizeof(buf_sid
));
2324 strlcpy(buf_lblop
, "-", sizeof(buf_lblop
));
2327 ttable_add_row(tt
, "%s|%u|%s|%s|%s|%s", buf_prefix
,
2328 rinfo
->cost
, "-", "-", buf_sid
,
2331 ttable_add_row(tt
, "%s|%u|%s|%s|%s", buf_prefix
,
2332 rinfo
->cost
, "-", "-", "-");
2336 void isis_print_routes(struct vty
*vty
, struct isis_spftree
*spftree
,
2337 bool prefix_sid
, bool backup
)
2339 struct route_table
*route_table
;
2341 struct route_node
*rn
;
2342 bool no_adjacencies
= false;
2343 const char *tree_id_text
= NULL
;
2348 switch (spftree
->tree_id
) {
2350 tree_id_text
= "IPv4";
2353 tree_id_text
= "IPv6";
2355 case SPFTREE_DSTSRC
:
2356 tree_id_text
= "IPv6 (dst-src routing)";
2359 assert(!"isis_print_routes shouldn't be called with SPFTREE_COUNT as type");
2363 vty_out(vty
, "IS-IS %s %s routing table:\n\n",
2364 circuit_t2string(spftree
->level
), tree_id_text
);
2366 /* Prepare table. */
2367 tt
= ttable_new(&ttable_styles
[TTSTYLE_BLANK
]);
2369 ttable_add_row(tt
, "Prefix|Metric|Interface|Nexthop|SID|Label Op.");
2371 ttable_add_row(tt
, "Prefix|Metric|Interface|Nexthop|Label(s)");
2372 tt
->style
.cell
.rpad
= 2;
2373 tt
->style
.corner
= '+';
2375 ttable_rowseps(tt
, 0, BOTTOM
, true, '-');
2377 if (CHECK_FLAG(spftree
->flags
, F_SPFTREE_NO_ADJACENCIES
))
2378 no_adjacencies
= true;
2381 (backup
) ? spftree
->route_table_backup
: spftree
->route_table
;
2382 for (rn
= route_top(route_table
); rn
; rn
= route_next(rn
)) {
2383 struct isis_route_info
*rinfo
;
2389 isis_print_route(tt
, &rn
->p
, rinfo
, prefix_sid
, no_adjacencies
);
2392 /* Dump the generated table. */
2393 if (tt
->nrows
> 1) {
2396 table
= ttable_dump(tt
, "\n");
2397 vty_out(vty
, "%s\n", table
);
2398 XFREE(MTYPE_TMP
, table
);
2403 static void show_isis_route_common(struct vty
*vty
, int levels
,
2404 struct isis
*isis
, bool prefix_sid
,
2407 struct listnode
*node
;
2408 struct isis_area
*area
;
2410 if (!isis
->area_list
|| isis
->area_list
->count
== 0)
2413 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, node
, area
)) {
2414 vty_out(vty
, "Area %s:\n",
2415 area
->area_tag
? area
->area_tag
: "null");
2417 for (int level
= ISIS_LEVEL1
; level
<= ISIS_LEVELS
; level
++) {
2418 if ((level
& levels
) == 0)
2421 if (area
->ip_circuits
> 0) {
2424 area
->spftree
[SPFTREE_IPV4
][level
- 1],
2425 prefix_sid
, backup
);
2427 if (area
->ipv6_circuits
> 0) {
2430 area
->spftree
[SPFTREE_IPV6
][level
- 1],
2431 prefix_sid
, backup
);
2433 if (isis_area_ipv6_dstsrc_enabled(area
)) {
2434 isis_print_routes(vty
,
2435 area
->spftree
[SPFTREE_DSTSRC
]
2437 prefix_sid
, backup
);
2443 DEFUN(show_isis_route
, show_isis_route_cmd
,
2445 " [vrf <NAME|all>] route"
2447 " [<level-1|level-2>]"
2449 " [<prefix-sid|backup>]",
2450 SHOW_STR PROTO_HELP VRF_FULL_CMD_HELP_STR
2451 "IS-IS routing table\n"
2456 "Show Prefix-SID information\n"
2457 "Show backup routes\n")
2461 struct listnode
*node
;
2462 const char *vrf_name
= VRF_DEFAULT_NAME
;
2463 bool all_vrf
= false;
2464 bool prefix_sid
= false;
2465 bool backup
= false;
2468 if (argv_find(argv
, argc
, "level-1", &idx
))
2469 levels
= ISIS_LEVEL1
;
2470 else if (argv_find(argv
, argc
, "level-2", &idx
))
2471 levels
= ISIS_LEVEL2
;
2473 levels
= ISIS_LEVEL1
| ISIS_LEVEL2
;
2476 vty_out(vty
, "IS-IS Routing Process not enabled\n");
2479 ISIS_FIND_VRF_ARGS(argv
, argc
, idx
, vrf_name
, all_vrf
);
2481 if (argv_find(argv
, argc
, "prefix-sid", &idx
))
2483 if (argv_find(argv
, argc
, "backup", &idx
))
2488 for (ALL_LIST_ELEMENTS_RO(im
->isis
, node
, isis
))
2489 show_isis_route_common(vty
, levels
, isis
,
2490 prefix_sid
, backup
);
2493 isis
= isis_lookup_by_vrfname(vrf_name
);
2495 show_isis_route_common(vty
, levels
, isis
, prefix_sid
,
2502 static void isis_print_frr_summary_line(struct ttable
*tt
,
2503 const char *protection
,
2504 uint32_t counters
[SPF_PREFIX_PRIO_MAX
])
2506 uint32_t critical
, high
, medium
, low
, total
;
2508 critical
= counters
[SPF_PREFIX_PRIO_CRITICAL
];
2509 high
= counters
[SPF_PREFIX_PRIO_HIGH
];
2510 medium
= counters
[SPF_PREFIX_PRIO_MEDIUM
];
2511 low
= counters
[SPF_PREFIX_PRIO_LOW
];
2512 total
= critical
+ high
+ medium
+ low
;
2514 ttable_add_row(tt
, "%s|%u|%u|%u|%u|%u", protection
, critical
, high
,
2515 medium
, low
, total
);
2519 isis_print_frr_summary_line_coverage(struct ttable
*tt
, const char *protection
,
2520 double counters
[SPF_PREFIX_PRIO_MAX
],
2523 double critical
, high
, medium
, low
;
2525 critical
= counters
[SPF_PREFIX_PRIO_CRITICAL
] * 100;
2526 high
= counters
[SPF_PREFIX_PRIO_HIGH
] * 100;
2527 medium
= counters
[SPF_PREFIX_PRIO_MEDIUM
] * 100;
2528 low
= counters
[SPF_PREFIX_PRIO_LOW
] * 100;
2531 ttable_add_row(tt
, "%s|%.2f%%|%.2f%%|%.2f%%|%.2f%%|%.2f%%", protection
,
2532 critical
, high
, medium
, low
, total
);
2535 static void isis_print_frr_summary(struct vty
*vty
,
2536 struct isis_spftree
*spftree
)
2540 const char *tree_id_text
= NULL
;
2541 uint32_t protectd
[SPF_PREFIX_PRIO_MAX
] = {0};
2542 uint32_t unprotected
[SPF_PREFIX_PRIO_MAX
] = {0};
2543 double coverage
[SPF_PREFIX_PRIO_MAX
] = {0};
2544 uint32_t protected_total
= 0, grand_total
= 0;
2545 double coverage_total
;
2550 switch (spftree
->tree_id
) {
2552 tree_id_text
= "IPv4";
2555 tree_id_text
= "IPv6";
2557 case SPFTREE_DSTSRC
:
2558 tree_id_text
= "IPv6 (dst-src routing)";
2561 assert(!"isis_print_frr_summary shouldn't be called with SPFTREE_COUNT as type");
2565 vty_out(vty
, " IS-IS %s %s Fast ReRoute summary:\n\n",
2566 circuit_t2string(spftree
->level
), tree_id_text
);
2568 /* Prepare table. */
2569 tt
= ttable_new(&ttable_styles
[TTSTYLE_BLANK
]);
2572 "Protection \\ Priority|Critical|High |Medium |Low |Total");
2573 tt
->style
.cell
.rpad
= 2;
2574 tt
->style
.corner
= '+';
2576 ttable_rowseps(tt
, 0, BOTTOM
, true, '-');
2578 /* Compute unprotected and coverage totals. */
2579 for (int priority
= SPF_PREFIX_PRIO_CRITICAL
;
2580 priority
< SPF_PREFIX_PRIO_MAX
; priority
++) {
2581 uint32_t *lfa
= spftree
->lfa
.protection_counters
.lfa
;
2582 uint32_t *rlfa
= spftree
->lfa
.protection_counters
.rlfa
;
2583 uint32_t *tilfa
= spftree
->lfa
.protection_counters
.tilfa
;
2584 uint32_t *ecmp
= spftree
->lfa
.protection_counters
.ecmp
;
2585 uint32_t *total
= spftree
->lfa
.protection_counters
.total
;
2587 protectd
[priority
] = lfa
[priority
] + rlfa
[priority
]
2588 + tilfa
[priority
] + ecmp
[priority
];
2589 /* Safeguard to protect against possible inconsistencies. */
2590 if (protectd
[priority
] > total
[priority
])
2591 protectd
[priority
] = total
[priority
];
2592 unprotected
[priority
] = total
[priority
] - protectd
[priority
];
2593 protected_total
+= protectd
[priority
];
2594 grand_total
+= total
[priority
];
2596 if (!total
[priority
])
2597 coverage
[priority
] = 0;
2599 coverage
[priority
] =
2600 protectd
[priority
] / (double)total
[priority
];
2606 coverage_total
= protected_total
/ (double)grand_total
;
2609 isis_print_frr_summary_line(tt
, "Classic LFA",
2610 spftree
->lfa
.protection_counters
.lfa
);
2611 isis_print_frr_summary_line(tt
, "Remote LFA",
2612 spftree
->lfa
.protection_counters
.rlfa
);
2613 isis_print_frr_summary_line(tt
, "Topology Independent LFA",
2614 spftree
->lfa
.protection_counters
.tilfa
);
2615 isis_print_frr_summary_line(tt
, "ECMP",
2616 spftree
->lfa
.protection_counters
.ecmp
);
2617 isis_print_frr_summary_line(tt
, "Unprotected", unprotected
);
2618 isis_print_frr_summary_line_coverage(tt
, "Protection coverage",
2619 coverage
, coverage_total
);
2621 /* Dump the generated table. */
2622 table
= ttable_dump(tt
, "\n");
2623 vty_out(vty
, "%s\n", table
);
2624 XFREE(MTYPE_TMP
, table
);
2628 static void show_isis_frr_summary_common(struct vty
*vty
, int levels
,
2631 struct listnode
*node
;
2632 struct isis_area
*area
;
2634 if (!isis
->area_list
|| isis
->area_list
->count
== 0)
2637 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, node
, area
)) {
2638 vty_out(vty
, "Area %s:\n",
2639 area
->area_tag
? area
->area_tag
: "null");
2641 for (int level
= ISIS_LEVEL1
; level
<= ISIS_LEVELS
; level
++) {
2642 if ((level
& levels
) == 0)
2645 if (area
->ip_circuits
> 0) {
2646 isis_print_frr_summary(
2648 area
->spftree
[SPFTREE_IPV4
][level
- 1]);
2650 if (area
->ipv6_circuits
> 0) {
2651 isis_print_frr_summary(
2653 area
->spftree
[SPFTREE_IPV6
][level
- 1]);
2655 if (isis_area_ipv6_dstsrc_enabled(area
)) {
2656 isis_print_frr_summary(
2657 vty
, area
->spftree
[SPFTREE_DSTSRC
]
2664 DEFUN(show_isis_frr_summary
, show_isis_frr_summary_cmd
,
2666 " [vrf <NAME|all>] fast-reroute summary"
2668 " [<level-1|level-2>]"
2671 SHOW_STR PROTO_HELP VRF_FULL_CMD_HELP_STR
2672 "IS-IS FRR information\n"
2682 struct listnode
*node
;
2683 const char *vrf_name
= VRF_DEFAULT_NAME
;
2684 bool all_vrf
= false;
2687 if (argv_find(argv
, argc
, "level-1", &idx
))
2688 levels
= ISIS_LEVEL1
;
2689 else if (argv_find(argv
, argc
, "level-2", &idx
))
2690 levels
= ISIS_LEVEL2
;
2692 levels
= ISIS_LEVEL1
| ISIS_LEVEL2
;
2695 vty_out(vty
, "IS-IS Routing Process not enabled\n");
2698 ISIS_FIND_VRF_ARGS(argv
, argc
, idx
, vrf_name
, all_vrf
);
2702 for (ALL_LIST_ELEMENTS_RO(im
->isis
, node
, isis
))
2703 show_isis_frr_summary_common(vty
, levels
, isis
);
2706 isis
= isis_lookup_by_vrfname(vrf_name
);
2708 show_isis_frr_summary_common(vty
, levels
, isis
);
2714 void isis_spf_init(void)
2716 install_element(VIEW_NODE
, &show_isis_topology_cmd
);
2717 install_element(VIEW_NODE
, &show_isis_route_cmd
);
2718 install_element(VIEW_NODE
, &show_isis_frr_summary_cmd
);
2720 /* Register hook(s). */
2721 hook_register(isis_adj_state_change_hook
, spf_adj_state_change
);
2724 void isis_spf_print(struct isis_spftree
*spftree
, struct vty
*vty
)
2726 uint64_t last_run_duration
= spftree
->last_run_duration
;
2728 vty_out(vty
, " last run elapsed : ");
2729 vty_out_timestr(vty
, spftree
->last_run_timestamp
);
2732 vty_out(vty
, " last run duration : %" PRIu64
" usec\n",
2735 vty_out(vty
, " run count : %u\n", spftree
->runcount
);
2737 void isis_spf_print_json(struct isis_spftree
*spftree
, struct json_object
*json
)
2739 char uptime
[MONOTIME_STRLEN
];
2742 cur
-= spftree
->last_run_timestamp
;
2743 frrtime_to_interval(cur
, uptime
, sizeof(uptime
));
2744 json_object_string_add(json
, "last-run-elapsed", uptime
);
2745 json_object_int_add(json
, "last-run-duration-usec",
2746 spftree
->last_run_duration
);
2747 json_object_int_add(json
, "last-run-count", spftree
->runcount
);