2 * IS-IS Rout(e)ing protocol - isis_spf.c
5 * Copyright (C) 2001,2002 Sampo Saaristo
6 * Tampere University of Technology
7 * Institute of Communications Engineering
8 * Copyright (C) 2017 Christian Franke <chris@opensourcerouting.org>
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public Licenseas published by the Free
12 * Software Foundation; either version 2 of the License, or (at your option)
15 * This program is distributed in the hope that it will be useful,but WITHOUT
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
20 * You should have received a copy of the GNU General Public License along
21 * with this program; see the file COPYING; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
37 #include "spf_backoff.h"
40 #include "srcdest_table.h"
42 #include "isis_constants.h"
43 #include "isis_common.h"
44 #include "isis_flags.h"
47 #include "isis_misc.h"
48 #include "isis_adjacency.h"
49 #include "isis_circuit.h"
52 #include "isis_dynhn.h"
54 #include "isis_route.h"
57 #include "isis_tlvs.h"
59 DEFINE_MTYPE_STATIC(ISISD
, ISIS_SPF_RUN
, "ISIS SPF Run Info");
65 VTYPE_NONPSEUDO_TE_IS
,
67 VTYPE_IPREACH_INTERNAL
,
68 VTYPE_IPREACH_EXTERNAL
,
70 VTYPE_IP6REACH_INTERNAL
,
71 VTYPE_IP6REACH_EXTERNAL
74 #define VTYPE_IS(t) ((t) >= VTYPE_PSEUDO_IS && (t) <= VTYPE_NONPSEUDO_TE_IS)
75 #define VTYPE_ES(t) ((t) == VTYPE_ES)
76 #define VTYPE_IP(t) ((t) >= VTYPE_IPREACH_INTERNAL && (t) <= VTYPE_IP6REACH_EXTERNAL)
80 struct prefix_ipv6 src
;
84 * Triple <N, d(N), {Adj(N)}>
87 uint8_t id
[ISIS_SYS_ID_LEN
+ 1];
88 struct prefix_pair ip
;
93 uint32_t d_N
; /* d(N) Distance from this IS */
94 uint16_t depth
; /* The depth in the imaginary tree */
95 struct list
*Adj_N
; /* {Adj(N)} next hop or neighbor list */
96 struct list
*parents
; /* list of parents for ECMP */
97 uint64_t insert_counter
;
100 /* Vertex Queue and associated functions */
102 struct isis_vertex_queue
{
104 struct skiplist
*slist
;
108 uint64_t insert_counter
;
111 static unsigned isis_vertex_queue_hash_key(void *vp
)
113 struct isis_vertex
*vertex
= vp
;
115 if (VTYPE_IP(vertex
->type
)) {
118 key
= prefix_hash_key(&vertex
->N
.ip
.dest
);
119 key
= jhash_1word(prefix_hash_key(&vertex
->N
.ip
.src
), key
);
123 return jhash(vertex
->N
.id
, ISIS_SYS_ID_LEN
+ 1, 0x55aa5a5a);
126 static int isis_vertex_queue_hash_cmp(const void *a
, const void *b
)
128 const struct isis_vertex
*va
= a
, *vb
= b
;
130 if (va
->type
!= vb
->type
)
133 if (VTYPE_IP(va
->type
)) {
134 if (prefix_cmp(&va
->N
.ip
.dest
, &vb
->N
.ip
.dest
))
137 return prefix_cmp((struct prefix
*)&va
->N
.ip
.src
,
138 (struct prefix
*)&vb
->N
.ip
.src
) == 0;
141 return memcmp(va
->N
.id
, vb
->N
.id
, ISIS_SYS_ID_LEN
+ 1) == 0;
145 * Compares vertizes for sorting in the TENT list. Returns true
146 * if candidate should be considered before current, false otherwise.
148 static int isis_vertex_queue_tent_cmp(void *a
, void *b
)
150 struct isis_vertex
*va
= a
;
151 struct isis_vertex
*vb
= b
;
153 if (va
->d_N
< vb
->d_N
)
156 if (va
->d_N
> vb
->d_N
)
159 if (va
->type
< vb
->type
)
162 if (va
->type
> vb
->type
)
165 if (va
->insert_counter
< vb
->insert_counter
)
168 if (va
->insert_counter
> vb
->insert_counter
)
174 static struct skiplist
*isis_vertex_queue_skiplist(void)
176 return skiplist_new(0, isis_vertex_queue_tent_cmp
, NULL
);
179 static void isis_vertex_queue_init(struct isis_vertex_queue
*queue
,
180 const char *name
, bool ordered
)
183 queue
->insert_counter
= 1;
184 queue
->l
.slist
= isis_vertex_queue_skiplist();
186 queue
->insert_counter
= 0;
187 queue
->l
.list
= list_new();
189 queue
->hash
= hash_create(isis_vertex_queue_hash_key
,
190 isis_vertex_queue_hash_cmp
, name
);
193 static void isis_vertex_del(struct isis_vertex
*vertex
);
195 static void isis_vertex_queue_clear(struct isis_vertex_queue
*queue
)
197 hash_clean(queue
->hash
, NULL
);
199 if (queue
->insert_counter
) {
200 struct isis_vertex
*vertex
;
201 while (0 == skiplist_first(queue
->l
.slist
, NULL
,
203 isis_vertex_del(vertex
);
204 skiplist_delete_first(queue
->l
.slist
);
206 queue
->insert_counter
= 1;
208 queue
->l
.list
->del
= (void (*)(void *))isis_vertex_del
;
209 list_delete_all_node(queue
->l
.list
);
210 queue
->l
.list
->del
= NULL
;
214 static void isis_vertex_queue_free(struct isis_vertex_queue
*queue
)
216 isis_vertex_queue_clear(queue
);
218 hash_free(queue
->hash
);
221 if (queue
->insert_counter
) {
222 skiplist_free(queue
->l
.slist
);
223 queue
->l
.slist
= NULL
;
225 list_delete_and_null(&queue
->l
.list
);
228 static unsigned int isis_vertex_queue_count(struct isis_vertex_queue
*queue
)
230 return hashcount(queue
->hash
);
233 static void isis_vertex_queue_append(struct isis_vertex_queue
*queue
,
234 struct isis_vertex
*vertex
)
236 assert(!queue
->insert_counter
);
238 listnode_add(queue
->l
.list
, vertex
);
240 struct isis_vertex
*inserted
;
242 inserted
= hash_get(queue
->hash
, vertex
, hash_alloc_intern
);
243 assert(inserted
== vertex
);
246 static void isis_vertex_queue_insert(struct isis_vertex_queue
*queue
,
247 struct isis_vertex
*vertex
)
249 assert(queue
->insert_counter
);
250 vertex
->insert_counter
= queue
->insert_counter
++;
251 assert(queue
->insert_counter
!= (uint64_t)-1);
253 skiplist_insert(queue
->l
.slist
, vertex
, vertex
);
255 struct isis_vertex
*inserted
;
256 inserted
= hash_get(queue
->hash
, vertex
, hash_alloc_intern
);
257 assert(inserted
== vertex
);
260 static struct isis_vertex
*
261 isis_vertex_queue_pop(struct isis_vertex_queue
*queue
)
263 assert(queue
->insert_counter
);
265 struct isis_vertex
*rv
;
267 if (skiplist_first(queue
->l
.slist
, NULL
, (void **)&rv
))
270 skiplist_delete_first(queue
->l
.slist
);
271 hash_release(queue
->hash
, rv
);
276 static void isis_vertex_queue_delete(struct isis_vertex_queue
*queue
,
277 struct isis_vertex
*vertex
)
279 assert(queue
->insert_counter
);
281 skiplist_delete(queue
->l
.slist
, vertex
, vertex
);
282 hash_release(queue
->hash
, vertex
);
285 #define ALL_QUEUE_ELEMENTS_RO(queue, node, data) \
286 ALL_LIST_ELEMENTS_RO((queue)->l.list, node, data)
289 /* End of vertex queue definitions */
291 struct isis_spftree
{
292 struct isis_vertex_queue paths
; /* the SPT */
293 struct isis_vertex_queue tents
; /* TENT */
294 struct route_table
*route_table
;
295 struct isis_area
*area
; /* back pointer to area */
296 unsigned int runcount
; /* number of runs since uptime */
297 time_t last_run_timestamp
; /* last run timestamp as wall time for display */
298 time_t last_run_monotime
; /* last run as monotime for scheduling */
299 time_t last_run_duration
; /* last run duration in msec */
304 enum spf_tree_id tree_id
;
309 * supports the given af ?
311 static bool speaks(uint8_t *protocols
, uint8_t count
, int family
)
313 for (uint8_t i
= 0; i
< count
; i
++) {
314 if (family
== AF_INET
&& protocols
[i
] == NLPID_IP
)
316 if (family
== AF_INET6
&& protocols
[i
] == NLPID_IPV6
)
322 struct isis_spf_run
{
323 struct isis_area
*area
;
328 static void remove_excess_adjs(struct list
*adjs
)
330 struct listnode
*node
, *excess
= NULL
;
331 struct isis_adjacency
*adj
, *candidate
= NULL
;
334 for (ALL_LIST_ELEMENTS_RO(adjs
, node
, adj
)) {
337 candidate
= listgetdata(excess
);
339 if (candidate
->sys_type
< adj
->sys_type
) {
343 if (candidate
->sys_type
> adj
->sys_type
)
346 comp
= memcmp(candidate
->sysid
, adj
->sysid
, ISIS_SYS_ID_LEN
);
354 if (candidate
->circuit
->idx
> adj
->circuit
->idx
) {
359 if (candidate
->circuit
->idx
< adj
->circuit
->idx
)
362 comp
= memcmp(candidate
->snpa
, adj
->snpa
, ETH_ALEN
);
369 list_delete_node(adjs
, excess
);
374 static const char *vtype2string(enum vertextype vtype
)
377 case VTYPE_PSEUDO_IS
:
380 case VTYPE_PSEUDO_TE_IS
:
381 return "pseudo_TE-IS";
383 case VTYPE_NONPSEUDO_IS
:
386 case VTYPE_NONPSEUDO_TE_IS
:
392 case VTYPE_IPREACH_INTERNAL
:
393 return "IP internal";
395 case VTYPE_IPREACH_EXTERNAL
:
396 return "IP external";
398 case VTYPE_IPREACH_TE
:
401 case VTYPE_IP6REACH_INTERNAL
:
402 return "IP6 internal";
404 case VTYPE_IP6REACH_EXTERNAL
:
405 return "IP6 external";
410 return NULL
; /* Not reached */
413 #define VID2STR_BUFFER SRCDEST2STR_BUFFER
414 static const char *vid2string(struct isis_vertex
*vertex
, char *buff
, int size
)
416 if (VTYPE_IS(vertex
->type
) || VTYPE_ES(vertex
->type
)) {
417 return print_sys_hostname(vertex
->N
.id
);
420 if (VTYPE_IP(vertex
->type
)) {
421 srcdest2str(&vertex
->N
.ip
.dest
,
430 static void isis_vertex_id_init(struct isis_vertex
*vertex
, union isis_N
*n
,
431 enum vertextype vtype
)
433 vertex
->type
= vtype
;
435 if (VTYPE_IS(vtype
) || VTYPE_ES(vtype
)) {
436 memcpy(vertex
->N
.id
, n
->id
, ISIS_SYS_ID_LEN
+ 1);
437 } else if (VTYPE_IP(vtype
)) {
438 memcpy(&vertex
->N
.ip
, &n
->ip
, sizeof(n
->ip
));
444 static struct isis_vertex
*isis_vertex_new(union isis_N
*n
,
445 enum vertextype vtype
)
447 struct isis_vertex
*vertex
;
449 vertex
= XCALLOC(MTYPE_ISIS_VERTEX
, sizeof(struct isis_vertex
));
451 isis_vertex_id_init(vertex
, n
, vtype
);
453 vertex
->Adj_N
= list_new();
454 vertex
->parents
= list_new();
459 static void isis_vertex_del(struct isis_vertex
*vertex
)
461 list_delete_and_null(&vertex
->Adj_N
);
462 list_delete_and_null(&vertex
->parents
);
464 memset(vertex
, 0, sizeof(struct isis_vertex
));
465 XFREE(MTYPE_ISIS_VERTEX
, vertex
);
470 static void isis_vertex_adj_del(struct isis_vertex
*vertex
,
471 struct isis_adjacency
*adj
)
473 struct listnode
*node
, *nextnode
;
476 for (node
= listhead(vertex
->Adj_N
); node
; node
= nextnode
) {
477 nextnode
= listnextnode(node
);
478 if (listgetdata(node
) == adj
)
479 list_delete_node(vertex
->Adj_N
, node
);
484 struct isis_spftree
*isis_spftree_new(struct isis_area
*area
)
486 struct isis_spftree
*tree
;
488 tree
= XCALLOC(MTYPE_ISIS_SPFTREE
, sizeof(struct isis_spftree
));
490 zlog_err("ISIS-Spf: isis_spftree_new Out of memory!");
494 isis_vertex_queue_init(&tree
->tents
, "IS-IS SPF tents", true);
495 isis_vertex_queue_init(&tree
->paths
, "IS-IS SPF paths", false);
496 tree
->route_table
= srcdest_table_init();
498 tree
->last_run_timestamp
= 0;
499 tree
->last_run_monotime
= 0;
500 tree
->last_run_duration
= 0;
505 void isis_spftree_del(struct isis_spftree
*spftree
)
507 isis_vertex_queue_free(&spftree
->tents
);
508 isis_vertex_queue_free(&spftree
->paths
);
509 route_table_finish(spftree
->route_table
);
510 spftree
->route_table
= NULL
;
512 XFREE(MTYPE_ISIS_SPFTREE
, spftree
);
516 static void isis_spftree_adj_del(struct isis_spftree
*spftree
,
517 struct isis_adjacency
*adj
)
519 struct listnode
*node
;
520 struct isis_vertex
*v
;
523 assert(!isis_vertex_queue_count(&spftree
->tents
));
524 for (ALL_QUEUE_ELEMENTS_RO(&spftree
->paths
, node
, v
))
525 isis_vertex_adj_del(v
, adj
);
529 void spftree_area_init(struct isis_area
*area
)
531 for (int tree
= SPFTREE_IPV4
; tree
< SPFTREE_COUNT
; tree
++) {
532 for (int level
= ISIS_LEVEL1
; level
<= ISIS_LEVEL2
; level
++) {
533 if (!(area
->is_type
& level
))
535 if (area
->spftree
[tree
][level
- 1])
538 area
->spftree
[tree
][level
- 1] = isis_spftree_new(area
);
543 void spftree_area_del(struct isis_area
*area
)
545 for (int tree
= SPFTREE_IPV4
; tree
< SPFTREE_COUNT
; tree
++) {
546 for (int level
= ISIS_LEVEL1
; level
<= ISIS_LEVEL2
; level
++) {
547 if (!(area
->is_type
& level
))
549 if (!area
->spftree
[tree
][level
- 1])
552 isis_spftree_del(area
->spftree
[tree
][level
- 1]);
557 void spftree_area_adj_del(struct isis_area
*area
, struct isis_adjacency
*adj
)
559 for (int tree
= SPFTREE_IPV4
; tree
< SPFTREE_COUNT
; tree
++) {
560 for (int level
= ISIS_LEVEL1
; level
<= ISIS_LEVEL2
; level
++) {
561 if (!(area
->is_type
& level
))
563 if (!area
->spftree
[tree
][level
- 1])
565 isis_spftree_adj_del(area
->spftree
[tree
][level
- 1],
572 * Find the system LSP: returns the LSP in our LSP database
573 * associated with the given system ID.
575 static struct isis_lsp
*isis_root_system_lsp(struct isis_area
*area
, int level
,
578 struct isis_lsp
*lsp
;
579 uint8_t lspid
[ISIS_SYS_ID_LEN
+ 2];
581 memcpy(lspid
, sysid
, ISIS_SYS_ID_LEN
);
582 LSP_PSEUDO_ID(lspid
) = 0;
583 LSP_FRAGMENT(lspid
) = 0;
584 lsp
= lsp_search(lspid
, area
->lspdb
[level
- 1]);
585 if (lsp
&& lsp
->hdr
.rem_lifetime
!= 0)
591 * Add this IS to the root of SPT
593 static struct isis_vertex
*isis_spf_add_root(struct isis_spftree
*spftree
,
596 struct isis_vertex
*vertex
;
597 struct isis_lsp
*lsp
;
599 char buff
[VID2STR_BUFFER
];
600 #endif /* EXTREME_DEBUG */
603 memcpy(n
.id
, sysid
, ISIS_SYS_ID_LEN
);
604 LSP_PSEUDO_ID(n
.id
) = 0;
606 lsp
= isis_root_system_lsp(spftree
->area
, spftree
->level
, sysid
);
608 zlog_warn("ISIS-Spf: could not find own l%d LSP!",
611 vertex
= isis_vertex_new(&n
,
612 spftree
->area
->oldmetric
614 : VTYPE_NONPSEUDO_TE_IS
);
615 isis_vertex_queue_append(&spftree
->paths
, vertex
);
618 zlog_debug("ISIS-Spf: added this IS %s %s depth %d dist %d to PATHS",
619 vtype2string(vertex
->type
),
620 vid2string(vertex
, buff
, sizeof(buff
)), vertex
->depth
,
622 #endif /* EXTREME_DEBUG */
627 static struct isis_vertex
*isis_find_vertex(struct isis_vertex_queue
*queue
,
629 enum vertextype vtype
)
631 struct isis_vertex querier
;
633 isis_vertex_id_init(&querier
, n
, vtype
);
634 return hash_lookup(queue
->hash
, &querier
);
638 * Add a vertex to TENT sorted by cost and by vertextype on tie break situation
640 static struct isis_vertex
*isis_spf_add2tent(struct isis_spftree
*spftree
,
641 enum vertextype vtype
, void *id
,
642 uint32_t cost
, int depth
,
643 struct isis_adjacency
*adj
,
644 struct isis_vertex
*parent
)
646 struct isis_vertex
*vertex
;
647 struct listnode
*node
;
648 struct isis_adjacency
*parent_adj
;
650 char buff
[VID2STR_BUFFER
];
653 assert(isis_find_vertex(&spftree
->paths
, id
, vtype
) == NULL
);
654 assert(isis_find_vertex(&spftree
->tents
, id
, vtype
) == NULL
);
655 vertex
= isis_vertex_new(id
, vtype
);
657 vertex
->depth
= depth
;
660 listnode_add(vertex
->parents
, parent
);
663 if (parent
&& parent
->Adj_N
&& listcount(parent
->Adj_N
) > 0) {
664 for (ALL_LIST_ELEMENTS_RO(parent
->Adj_N
, node
, parent_adj
))
665 listnode_add(vertex
->Adj_N
, parent_adj
);
667 listnode_add(vertex
->Adj_N
, adj
);
672 "ISIS-Spf: add to TENT %s %s %s depth %d dist %d adjcount %d",
673 print_sys_hostname(vertex
->N
.id
), vtype2string(vertex
->type
),
674 vid2string(vertex
, buff
, sizeof(buff
)), vertex
->depth
,
675 vertex
->d_N
, listcount(vertex
->Adj_N
));
676 #endif /* EXTREME_DEBUG */
678 isis_vertex_queue_insert(&spftree
->tents
, vertex
);
682 static void isis_spf_add_local(struct isis_spftree
*spftree
,
683 enum vertextype vtype
, void *id
,
684 struct isis_adjacency
*adj
, uint32_t cost
,
685 struct isis_vertex
*parent
)
687 struct isis_vertex
*vertex
;
689 vertex
= isis_find_vertex(&spftree
->tents
, id
, vtype
);
693 if (vertex
->d_N
== cost
) {
695 listnode_add(vertex
->Adj_N
, adj
);
697 if (listcount(vertex
->Adj_N
) > ISIS_MAX_PATH_SPLITS
)
698 remove_excess_adjs(vertex
->Adj_N
);
699 if (parent
&& (listnode_lookup(vertex
->parents
, parent
)
701 listnode_add(vertex
->parents
, parent
);
703 } else if (vertex
->d_N
< cost
) {
706 } else { /* vertex->d_N > cost */
708 isis_vertex_queue_delete(&spftree
->tents
, vertex
);
709 isis_vertex_del(vertex
);
713 isis_spf_add2tent(spftree
, vtype
, id
, cost
, 1, adj
, parent
);
717 static void process_N(struct isis_spftree
*spftree
, enum vertextype vtype
,
718 void *id
, uint32_t dist
, uint16_t depth
,
719 struct isis_vertex
*parent
)
721 struct isis_vertex
*vertex
;
723 char buff
[VID2STR_BUFFER
];
726 assert(spftree
&& parent
);
728 struct prefix_pair p
;
729 if (vtype
>= VTYPE_IPREACH_INTERNAL
) {
730 memcpy(&p
, id
, sizeof(p
));
732 apply_mask((struct prefix
*)&p
.src
);
736 /* RFC3787 section 5.1 */
737 if (spftree
->area
->newmetric
== 1) {
738 if (dist
> MAX_WIDE_PATH_METRIC
)
742 else if (spftree
->area
->oldmetric
== 1) {
743 if (dist
> MAX_NARROW_PATH_METRIC
)
748 vertex
= isis_find_vertex(&spftree
->paths
, id
, vtype
);
752 "ISIS-Spf: process_N %s %s %s dist %d already found from PATH",
753 print_sys_hostname(vertex
->N
.id
), vtype2string(vtype
),
754 vid2string(vertex
, buff
, sizeof(buff
)), dist
);
755 #endif /* EXTREME_DEBUG */
756 assert(dist
>= vertex
->d_N
);
760 vertex
= isis_find_vertex(&spftree
->tents
, id
, vtype
);
766 "ISIS-Spf: process_N %s %s %s dist %d parent %s adjcount %d",
767 print_sys_hostname(vertex
->N
.id
), vtype2string(vtype
),
768 vid2string(vertex
, buff
, sizeof(buff
)), dist
,
769 (parent
? print_sys_hostname(parent
->N
.id
) : "null"),
770 (parent
? listcount(parent
->Adj_N
) : 0));
771 #endif /* EXTREME_DEBUG */
772 if (vertex
->d_N
== dist
) {
773 struct listnode
*node
;
774 struct isis_adjacency
*parent_adj
;
775 for (ALL_LIST_ELEMENTS_RO(parent
->Adj_N
, node
,
777 if (listnode_lookup(vertex
->Adj_N
, parent_adj
)
779 listnode_add(vertex
->Adj_N
, parent_adj
);
781 if (listcount(vertex
->Adj_N
) > ISIS_MAX_PATH_SPLITS
)
782 remove_excess_adjs(vertex
->Adj_N
);
783 if (listnode_lookup(vertex
->parents
, parent
) == NULL
)
784 listnode_add(vertex
->parents
, parent
);
786 } else if (vertex
->d_N
< dist
) {
790 isis_vertex_queue_delete(&spftree
->tents
, vertex
);
791 isis_vertex_del(vertex
);
796 zlog_debug("ISIS-Spf: process_N add2tent %s %s dist %d parent %s",
797 print_sys_hostname(id
), vtype2string(vtype
), dist
,
798 (parent
? print_sys_hostname(parent
->N
.id
) : "null"));
799 #endif /* EXTREME_DEBUG */
801 isis_spf_add2tent(spftree
, vtype
, id
, dist
, depth
, NULL
, parent
);
808 static int isis_spf_process_lsp(struct isis_spftree
*spftree
,
809 struct isis_lsp
*lsp
, uint32_t cost
,
810 uint16_t depth
, uint8_t *root_sysid
,
811 struct isis_vertex
*parent
)
813 bool pseudo_lsp
= LSP_PSEUDO_ID(lsp
->hdr
.lsp_id
);
814 struct listnode
*fragnode
= NULL
;
816 enum vertextype vtype
;
817 static const uint8_t null_sysid
[ISIS_SYS_ID_LEN
];
818 struct isis_mt_router_info
*mt_router_info
= NULL
;
819 struct prefix_pair ip_info
;
824 if (spftree
->mtid
!= ISIS_MT_IPV4_UNICAST
)
825 mt_router_info
= isis_tlvs_lookup_mt_router_info(lsp
->tlvs
,
828 if (!pseudo_lsp
&& (spftree
->mtid
== ISIS_MT_IPV4_UNICAST
829 && !speaks(lsp
->tlvs
->protocols_supported
.protocols
,
830 lsp
->tlvs
->protocols_supported
.count
,
835 /* RFC3787 section 4 SHOULD ignore overload bit in pseudo LSPs */
836 bool no_overload
= (pseudo_lsp
837 || (spftree
->mtid
== ISIS_MT_IPV4_UNICAST
838 && !ISIS_MASK_LSP_OL_BIT(lsp
->hdr
.lsp_bits
))
839 || (mt_router_info
&& !mt_router_info
->overload
));
842 if (lsp
->hdr
.seqno
== 0) {
844 "isis_spf_process_lsp(): lsp with 0 seq_num - ignore");
849 zlog_debug("ISIS-Spf: process_lsp %s",
850 print_sys_hostname(lsp
->hdr
.lsp_id
));
851 #endif /* EXTREME_DEBUG */
854 if (pseudo_lsp
|| spftree
->mtid
== ISIS_MT_IPV4_UNICAST
) {
855 struct isis_oldstyle_reach
*r
;
856 for (r
= (struct isis_oldstyle_reach
*)
857 lsp
->tlvs
->oldstyle_reach
.head
;
860 /* Two way connectivity */
861 if (!memcmp(r
->id
, root_sysid
, ISIS_SYS_ID_LEN
))
864 && !memcmp(r
->id
, null_sysid
,
867 dist
= cost
+ r
->metric
;
871 : VTYPE_NONPSEUDO_IS
,
872 (void *)r
->id
, dist
, depth
+ 1,
877 struct isis_item_list
*te_neighs
= NULL
;
878 if (pseudo_lsp
|| spftree
->mtid
== ISIS_MT_IPV4_UNICAST
)
879 te_neighs
= &lsp
->tlvs
->extended_reach
;
881 te_neighs
= isis_lookup_mt_items(&lsp
->tlvs
->mt_reach
,
884 struct isis_extended_reach
*er
;
886 ? (struct isis_extended_reach
*)
890 if (!memcmp(er
->id
, root_sysid
, ISIS_SYS_ID_LEN
))
893 && !memcmp(er
->id
, null_sysid
, ISIS_SYS_ID_LEN
))
895 dist
= cost
+ er
->metric
;
897 LSP_PSEUDO_ID(er
->id
) ? VTYPE_PSEUDO_TE_IS
898 : VTYPE_NONPSEUDO_TE_IS
,
899 (void *)er
->id
, dist
, depth
+ 1, parent
);
903 if (!pseudo_lsp
&& spftree
->family
== AF_INET
904 && spftree
->mtid
== ISIS_MT_IPV4_UNICAST
) {
905 struct isis_item_list
*reachs
[] = {
906 &lsp
->tlvs
->oldstyle_ip_reach
,
907 &lsp
->tlvs
->oldstyle_ip_reach_ext
};
909 for (unsigned int i
= 0; i
< array_size(reachs
); i
++) {
910 vtype
= i
? VTYPE_IPREACH_EXTERNAL
911 : VTYPE_IPREACH_INTERNAL
;
913 memset(&ip_info
, 0, sizeof(ip_info
));
914 ip_info
.dest
.family
= AF_INET
;
916 struct isis_oldstyle_ip_reach
*r
;
917 for (r
= (struct isis_oldstyle_ip_reach
*)reachs
[i
]
920 dist
= cost
+ r
->metric
;
921 ip_info
.dest
.u
.prefix4
= r
->prefix
.prefix
;
922 ip_info
.dest
.prefixlen
= r
->prefix
.prefixlen
;
923 process_N(spftree
, vtype
, &ip_info
,
924 dist
, depth
+ 1, parent
);
929 if (!pseudo_lsp
&& spftree
->family
== AF_INET
) {
930 struct isis_item_list
*ipv4_reachs
;
931 if (spftree
->mtid
== ISIS_MT_IPV4_UNICAST
)
932 ipv4_reachs
= &lsp
->tlvs
->extended_ip_reach
;
934 ipv4_reachs
= isis_lookup_mt_items(
935 &lsp
->tlvs
->mt_ip_reach
, spftree
->mtid
);
937 memset(&ip_info
, 0, sizeof(ip_info
));
938 ip_info
.dest
.family
= AF_INET
;
940 struct isis_extended_ip_reach
*r
;
942 ? (struct isis_extended_ip_reach
*)
946 dist
= cost
+ r
->metric
;
947 ip_info
.dest
.u
.prefix4
= r
->prefix
.prefix
;
948 ip_info
.dest
.prefixlen
= r
->prefix
.prefixlen
;
949 process_N(spftree
, VTYPE_IPREACH_TE
, &ip_info
,
950 dist
, depth
+ 1, parent
);
954 if (!pseudo_lsp
&& spftree
->family
== AF_INET6
) {
955 struct isis_item_list
*ipv6_reachs
;
956 if (spftree
->mtid
== ISIS_MT_IPV4_UNICAST
)
957 ipv6_reachs
= &lsp
->tlvs
->ipv6_reach
;
959 ipv6_reachs
= isis_lookup_mt_items(
960 &lsp
->tlvs
->mt_ipv6_reach
, spftree
->mtid
);
962 struct isis_ipv6_reach
*r
;
964 ? (struct isis_ipv6_reach
*)ipv6_reachs
->head
967 dist
= cost
+ r
->metric
;
968 vtype
= r
->external
? VTYPE_IP6REACH_EXTERNAL
969 : VTYPE_IP6REACH_INTERNAL
;
970 memset(&ip_info
, 0, sizeof(ip_info
));
971 ip_info
.dest
.family
= AF_INET6
;
972 ip_info
.dest
.u
.prefix6
= r
->prefix
.prefix
;
973 ip_info
.dest
.prefixlen
= r
->prefix
.prefixlen
;
976 && r
->subtlvs
->source_prefix
977 && r
->subtlvs
->source_prefix
->prefixlen
) {
978 if (spftree
->tree_id
!= SPFTREE_DSTSRC
) {
979 char buff
[VID2STR_BUFFER
];
980 zlog_warn("Ignoring dest-src route %s in non dest-src topology",
983 r
->subtlvs
->source_prefix
,
989 ip_info
.src
= *r
->subtlvs
->source_prefix
;
991 process_N(spftree
, vtype
, &ip_info
, dist
,
996 if (fragnode
== NULL
)
997 fragnode
= listhead(lsp
->lspu
.frags
);
999 fragnode
= listnextnode(fragnode
);
1002 lsp
= listgetdata(fragnode
);
1009 static int isis_spf_preload_tent(struct isis_spftree
*spftree
,
1010 uint8_t *root_sysid
,
1011 struct isis_vertex
*parent
)
1013 struct isis_circuit
*circuit
;
1014 struct listnode
*cnode
, *anode
, *ipnode
;
1015 struct isis_adjacency
*adj
;
1016 struct isis_lsp
*lsp
;
1017 struct list
*adj_list
;
1019 struct prefix_ipv4
*ipv4
;
1020 struct prefix_pair ip_info
;
1021 int retval
= ISIS_OK
;
1022 uint8_t lsp_id
[ISIS_SYS_ID_LEN
+ 2];
1023 static uint8_t null_lsp_id
[ISIS_SYS_ID_LEN
+ 2];
1024 struct prefix_ipv6
*ipv6
;
1025 struct isis_circuit_mt_setting
*circuit_mt
;
1027 for (ALL_LIST_ELEMENTS_RO(spftree
->area
->circuit_list
, cnode
,
1029 circuit_mt
= circuit_lookup_mt_setting(circuit
, spftree
->mtid
);
1030 if (circuit_mt
&& !circuit_mt
->enabled
)
1032 if (circuit
->state
!= C_STATE_UP
)
1034 if (!(circuit
->is_type
& spftree
->level
))
1036 if (spftree
->family
== AF_INET
&& !circuit
->ip_router
)
1038 if (spftree
->family
== AF_INET6
&& !circuit
->ipv6_router
)
1041 * Add IP(v6) addresses of this circuit
1043 if (spftree
->family
== AF_INET
) {
1044 memset(&ip_info
, 0, sizeof(ip_info
));
1045 ip_info
.dest
.family
= AF_INET
;
1046 for (ALL_LIST_ELEMENTS_RO(circuit
->ip_addrs
, ipnode
,
1048 ip_info
.dest
.u
.prefix4
= ipv4
->prefix
;
1049 ip_info
.dest
.prefixlen
= ipv4
->prefixlen
;
1050 apply_mask(&ip_info
.dest
);
1051 isis_spf_add_local(spftree
,
1052 VTYPE_IPREACH_INTERNAL
,
1053 &ip_info
, NULL
, 0, parent
);
1056 if (spftree
->family
== AF_INET6
) {
1057 memset(&ip_info
, 0, sizeof(ip_info
));
1058 ip_info
.dest
.family
= AF_INET6
;
1059 for (ALL_LIST_ELEMENTS_RO(circuit
->ipv6_non_link
,
1061 ip_info
.dest
.u
.prefix6
= ipv6
->prefix
;
1062 ip_info
.dest
.prefixlen
= ipv6
->prefixlen
;
1063 apply_mask(&ip_info
.dest
);
1064 isis_spf_add_local(spftree
,
1065 VTYPE_IP6REACH_INTERNAL
,
1066 &ip_info
, NULL
, 0, parent
);
1069 if (circuit
->circ_type
== CIRCUIT_T_BROADCAST
) {
1071 * Add the adjacencies
1073 adj_list
= list_new();
1074 adjdb
= circuit
->u
.bc
.adjdb
[spftree
->level
- 1];
1075 isis_adj_build_up_list(adjdb
, adj_list
);
1076 if (listcount(adj_list
) == 0) {
1077 list_delete_and_null(&adj_list
);
1078 if (isis
->debugs
& DEBUG_SPF_EVENTS
)
1080 "ISIS-Spf: no L%d adjacencies on circuit %s",
1082 circuit
->interface
->name
);
1085 for (ALL_LIST_ELEMENTS_RO(adj_list
, anode
, adj
)) {
1086 if (!adj_has_mt(adj
, spftree
->mtid
))
1088 if (spftree
->mtid
== ISIS_MT_IPV4_UNICAST
1089 && !speaks(adj
->nlpids
.nlpids
,
1093 switch (adj
->sys_type
) {
1094 case ISIS_SYSTYPE_ES
:
1095 memcpy(lsp_id
, adj
->sysid
,
1097 LSP_PSEUDO_ID(lsp_id
) = 0;
1099 spftree
, VTYPE_ES
, lsp_id
, adj
,
1101 [spftree
->level
- 1],
1104 case ISIS_SYSTYPE_IS
:
1105 case ISIS_SYSTYPE_L1_IS
:
1106 case ISIS_SYSTYPE_L2_IS
:
1107 memcpy(lsp_id
, adj
->sysid
,
1109 LSP_PSEUDO_ID(lsp_id
) = 0;
1110 LSP_FRAGMENT(lsp_id
) = 0;
1113 spftree
->area
->oldmetric
1114 ? VTYPE_NONPSEUDO_IS
1115 : VTYPE_NONPSEUDO_TE_IS
,
1118 [spftree
->level
- 1],
1123 ->lspdb
[spftree
->level
1126 || lsp
->hdr
.rem_lifetime
== 0)
1128 "ISIS-Spf: No LSP %s found for IS adjacency "
1129 "L%d on %s (ID %u)",
1130 rawlspid_print(lsp_id
),
1132 circuit
->interface
->name
,
1133 circuit
->circuit_id
);
1135 case ISIS_SYSTYPE_UNKNOWN
:
1138 "isis_spf_preload_tent unknow adj type");
1141 list_delete_and_null(&adj_list
);
1143 * Add the pseudonode
1145 if (spftree
->level
== 1)
1146 memcpy(lsp_id
, circuit
->u
.bc
.l1_desig_is
,
1147 ISIS_SYS_ID_LEN
+ 1);
1149 memcpy(lsp_id
, circuit
->u
.bc
.l2_desig_is
,
1150 ISIS_SYS_ID_LEN
+ 1);
1151 /* can happen during DR reboot */
1152 if (memcmp(lsp_id
, null_lsp_id
, ISIS_SYS_ID_LEN
+ 1)
1154 if (isis
->debugs
& DEBUG_SPF_EVENTS
)
1156 "ISIS-Spf: No L%d DR on %s (ID %d)",
1158 circuit
->interface
->name
,
1159 circuit
->circuit_id
);
1162 adj
= isis_adj_lookup(lsp_id
, adjdb
);
1163 /* if no adj, we are the dis or error */
1164 if (!adj
&& !circuit
->u
.bc
.is_dr
[spftree
->level
- 1]) {
1166 "ISIS-Spf: No adjacency found from root "
1167 "to L%d DR %s on %s (ID %d)",
1168 spftree
->level
, rawlspid_print(lsp_id
),
1169 circuit
->interface
->name
,
1170 circuit
->circuit_id
);
1175 spftree
->area
->lspdb
[spftree
->level
- 1]);
1176 if (lsp
== NULL
|| lsp
->hdr
.rem_lifetime
== 0) {
1178 "ISIS-Spf: No lsp (%p) found from root "
1179 "to L%d DR %s on %s (ID %d)",
1180 (void *)lsp
, spftree
->level
,
1181 rawlspid_print(lsp_id
),
1182 circuit
->interface
->name
,
1183 circuit
->circuit_id
);
1186 isis_spf_process_lsp(
1188 circuit
->te_metric
[spftree
->level
- 1], 0,
1189 root_sysid
, parent
);
1190 } else if (circuit
->circ_type
== CIRCUIT_T_P2P
) {
1191 adj
= circuit
->u
.p2p
.neighbor
;
1192 if (!adj
|| adj
->adj_state
!= ISIS_ADJ_UP
)
1194 if (!adj_has_mt(adj
, spftree
->mtid
))
1196 switch (adj
->sys_type
) {
1197 case ISIS_SYSTYPE_ES
:
1198 memcpy(lsp_id
, adj
->sysid
, ISIS_SYS_ID_LEN
);
1199 LSP_PSEUDO_ID(lsp_id
) = 0;
1201 spftree
, VTYPE_ES
, lsp_id
, adj
,
1202 circuit
->te_metric
[spftree
->level
- 1],
1205 case ISIS_SYSTYPE_IS
:
1206 case ISIS_SYSTYPE_L1_IS
:
1207 case ISIS_SYSTYPE_L2_IS
:
1208 memcpy(lsp_id
, adj
->sysid
, ISIS_SYS_ID_LEN
);
1209 LSP_PSEUDO_ID(lsp_id
) = 0;
1210 LSP_FRAGMENT(lsp_id
) = 0;
1211 if (spftree
->mtid
!= ISIS_MT_IPV4_UNICAST
1212 || speaks(adj
->nlpids
.nlpids
,
1217 spftree
->area
->oldmetric
1218 ? VTYPE_NONPSEUDO_IS
1219 : VTYPE_NONPSEUDO_TE_IS
,
1222 [spftree
->level
- 1],
1225 case ISIS_SYSTYPE_UNKNOWN
:
1228 "isis_spf_preload_tent unknown adj type");
1231 } else if (circuit
->circ_type
== CIRCUIT_T_LOOPBACK
) {
1234 zlog_warn("isis_spf_preload_tent unsupported media");
1235 retval
= ISIS_WARNING
;
1243 * The parent(s) for vertex is set when added to TENT list
1244 * now we just put the child pointer(s) in place
1246 static void add_to_paths(struct isis_spftree
*spftree
,
1247 struct isis_vertex
*vertex
)
1249 char buff
[VID2STR_BUFFER
];
1251 if (isis_find_vertex(&spftree
->paths
, &vertex
->N
, vertex
->type
))
1253 isis_vertex_queue_append(&spftree
->paths
, vertex
);
1255 #ifdef EXTREME_DEBUG
1256 zlog_debug("ISIS-Spf: added %s %s %s depth %d dist %d to PATHS",
1257 print_sys_hostname(vertex
->N
.id
), vtype2string(vertex
->type
),
1258 vid2string(vertex
, buff
, sizeof(buff
)), vertex
->depth
,
1260 #endif /* EXTREME_DEBUG */
1262 if (VTYPE_IP(vertex
->type
)) {
1263 if (listcount(vertex
->Adj_N
) > 0)
1264 isis_route_create(&vertex
->N
.ip
.dest
,
1266 vertex
->d_N
, vertex
->depth
,
1267 vertex
->Adj_N
, spftree
->area
,
1268 spftree
->route_table
);
1269 else if (isis
->debugs
& DEBUG_SPF_EVENTS
)
1271 "ISIS-Spf: no adjacencies do not install route for "
1272 "%s depth %d dist %d",
1273 vid2string(vertex
, buff
, sizeof(buff
)),
1274 vertex
->depth
, vertex
->d_N
);
1280 static void init_spt(struct isis_spftree
*spftree
, int mtid
, int level
,
1281 int family
, enum spf_tree_id tree_id
)
1283 isis_vertex_queue_clear(&spftree
->tents
);
1284 isis_vertex_queue_clear(&spftree
->paths
);
1286 spftree
->mtid
= mtid
;
1287 spftree
->level
= level
;
1288 spftree
->family
= family
;
1289 spftree
->tree_id
= tree_id
;
1293 static int isis_run_spf(struct isis_area
*area
, int level
,
1294 enum spf_tree_id tree_id
,
1295 uint8_t *sysid
, struct timeval
*nowtv
)
1297 int retval
= ISIS_OK
;
1298 struct isis_vertex
*vertex
;
1299 struct isis_vertex
*root_vertex
;
1300 struct isis_spftree
*spftree
= area
->spftree
[tree_id
][level
- 1];
1301 uint8_t lsp_id
[ISIS_SYS_ID_LEN
+ 2];
1302 struct isis_lsp
*lsp
;
1303 struct timeval time_now
;
1304 unsigned long long start_time
, end_time
;
1307 /* Get time that can't roll backwards. */
1308 start_time
= nowtv
->tv_sec
;
1309 start_time
= (start_time
* 1000000) + nowtv
->tv_usec
;
1315 mtid
= ISIS_MT_IPV4_UNICAST
;
1319 mtid
= isis_area_ipv6_topology(area
);
1321 case SPFTREE_DSTSRC
:
1323 mtid
= ISIS_MT_IPV6_DSTSRC
;
1326 assert(!"isis_run_spf should never be called with SPFTREE_COUNT as argument!");
1327 return ISIS_WARNING
;
1336 init_spt(spftree
, mtid
, level
, family
, tree_id
);
1338 root_vertex
= isis_spf_add_root(spftree
, sysid
);
1340 retval
= isis_spf_preload_tent(spftree
, sysid
, root_vertex
);
1341 if (retval
!= ISIS_OK
) {
1342 zlog_warn("ISIS-Spf: failed to load TENT SPF-root:%s",
1343 print_sys_hostname(sysid
));
1350 if (!isis_vertex_queue_count(&spftree
->tents
)
1351 && (isis
->debugs
& DEBUG_SPF_EVENTS
)) {
1352 zlog_warn("ISIS-Spf: TENT is empty SPF-root:%s",
1353 print_sys_hostname(sysid
));
1356 while (isis_vertex_queue_count(&spftree
->tents
)) {
1357 vertex
= isis_vertex_queue_pop(&spftree
->tents
);
1359 #ifdef EXTREME_DEBUG
1361 "ISIS-Spf: get TENT node %s %s depth %d dist %d to PATHS",
1362 print_sys_hostname(vertex
->N
.id
),
1363 vtype2string(vertex
->type
), vertex
->depth
, vertex
->d_N
);
1364 #endif /* EXTREME_DEBUG */
1366 add_to_paths(spftree
, vertex
);
1367 if (VTYPE_IS(vertex
->type
)) {
1368 memcpy(lsp_id
, vertex
->N
.id
, ISIS_SYS_ID_LEN
+ 1);
1369 LSP_FRAGMENT(lsp_id
) = 0;
1370 lsp
= lsp_search(lsp_id
, area
->lspdb
[level
- 1]);
1371 if (lsp
&& lsp
->hdr
.rem_lifetime
!= 0) {
1372 isis_spf_process_lsp(spftree
, lsp
, vertex
->d_N
,
1373 vertex
->depth
, sysid
,
1376 zlog_warn("ISIS-Spf: No LSP found for %s",
1377 rawlspid_print(lsp_id
));
1383 spftree
->runcount
++;
1384 spftree
->last_run_timestamp
= time(NULL
);
1385 spftree
->last_run_monotime
= monotime(&time_now
);
1386 end_time
= time_now
.tv_sec
;
1387 end_time
= (end_time
* 1000000) + time_now
.tv_usec
;
1388 spftree
->last_run_duration
= end_time
- start_time
;
1393 void isis_spf_verify_routes(struct isis_area
*area
, struct isis_spftree
**trees
)
1395 if (area
->is_type
== IS_LEVEL_1
) {
1396 isis_route_verify_table(area
, trees
[0]->route_table
);
1397 } else if (area
->is_type
== IS_LEVEL_2
) {
1398 isis_route_verify_table(area
, trees
[1]->route_table
);
1400 isis_route_verify_merge(area
, trees
[0]->route_table
,
1401 trees
[1]->route_table
);
1405 void isis_spf_invalidate_routes(struct isis_spftree
*tree
)
1407 isis_route_invalidate_table(tree
->area
, tree
->route_table
);
1410 static int isis_run_spf_cb(struct thread
*thread
)
1412 struct isis_spf_run
*run
= THREAD_ARG(thread
);
1413 struct isis_area
*area
= run
->area
;
1414 int level
= run
->level
;
1415 int retval
= ISIS_OK
;
1417 XFREE(MTYPE_ISIS_SPF_RUN
, run
);
1418 area
->spf_timer
[level
- 1] = NULL
;
1420 if (!(area
->is_type
& level
)) {
1421 if (isis
->debugs
& DEBUG_SPF_EVENTS
)
1422 zlog_warn("ISIS-SPF (%s) area does not share level",
1424 return ISIS_WARNING
;
1427 isis_area_invalidate_routes(area
, level
);
1429 if (isis
->debugs
& DEBUG_SPF_EVENTS
)
1430 zlog_debug("ISIS-Spf (%s) L%d SPF needed, periodic SPF",
1431 area
->area_tag
, level
);
1433 if (area
->ip_circuits
)
1434 retval
= isis_run_spf(area
, level
, SPFTREE_IPV4
, isis
->sysid
,
1436 if (area
->ipv6_circuits
)
1437 retval
= isis_run_spf(area
, level
, SPFTREE_IPV6
, isis
->sysid
,
1439 if (area
->ipv6_circuits
1440 && isis_area_ipv6_dstsrc_enabled(area
))
1441 retval
= isis_run_spf(area
, level
, SPFTREE_DSTSRC
, isis
->sysid
,
1444 isis_area_verify_routes(area
);
1446 /* walk all circuits and reset any spf specific flags */
1447 struct listnode
*node
;
1448 struct isis_circuit
*circuit
;
1449 for (ALL_LIST_ELEMENTS_RO(area
->circuit_list
, node
, circuit
))
1450 UNSET_FLAG(circuit
->flags
, ISIS_CIRCUIT_FLAPPED_AFTER_SPF
);
1455 static struct isis_spf_run
*isis_run_spf_arg(struct isis_area
*area
, int level
)
1457 struct isis_spf_run
*run
= XMALLOC(MTYPE_ISIS_SPF_RUN
, sizeof(*run
));
1465 int isis_spf_schedule(struct isis_area
*area
, int level
)
1467 struct isis_spftree
*spftree
= area
->spftree
[SPFTREE_IPV4
][level
- 1];
1468 time_t now
= monotime(NULL
);
1469 int diff
= now
- spftree
->last_run_monotime
;
1472 assert(area
->is_type
& level
);
1474 if (isis
->debugs
& DEBUG_SPF_EVENTS
)
1476 "ISIS-Spf (%s) L%d SPF schedule called, lastrun %d sec ago",
1477 area
->area_tag
, level
, diff
);
1479 if (area
->spf_delay_ietf
[level
- 1]) {
1480 /* Need to call schedule function also if spf delay is running
1482 * restart holdoff timer - compare
1483 * draft-ietf-rtgwg-backoff-algo-04 */
1485 spf_backoff_schedule(area
->spf_delay_ietf
[level
- 1]);
1486 if (area
->spf_timer
[level
- 1])
1489 thread_add_timer_msec(master
, isis_run_spf_cb
,
1490 isis_run_spf_arg(area
, level
), delay
,
1491 &area
->spf_timer
[level
- 1]);
1495 if (area
->spf_timer
[level
- 1])
1498 /* wait configured min_spf_interval before doing the SPF */
1500 if (diff
>= area
->min_spf_interval
[level
- 1]) {
1501 /* Last run is more than min interval ago, schedule immediate run */
1504 timer
= area
->min_spf_interval
[level
- 1] - diff
;
1507 thread_add_timer(master
, isis_run_spf_cb
, isis_run_spf_arg(area
, level
),
1508 timer
, &area
->spf_timer
[level
- 1]);
1510 if (isis
->debugs
& DEBUG_SPF_EVENTS
)
1511 zlog_debug("ISIS-Spf (%s) L%d SPF scheduled %ld sec from now",
1512 area
->area_tag
, level
, timer
);
1517 static void isis_print_paths(struct vty
*vty
, struct isis_vertex_queue
*queue
,
1518 uint8_t *root_sysid
)
1520 struct listnode
*node
;
1521 struct isis_vertex
*vertex
;
1522 char buff
[VID2STR_BUFFER
];
1525 "Vertex Type Metric Next-Hop Interface Parent\n");
1527 for (ALL_QUEUE_ELEMENTS_RO(queue
, node
, vertex
)) {
1528 if (memcmp(vertex
->N
.id
, root_sysid
, ISIS_SYS_ID_LEN
) == 0) {
1529 vty_out(vty
, "%-20s %-12s %-6s",
1530 print_sys_hostname(root_sysid
), "", "");
1531 vty_out(vty
, "%-30s\n", "");
1536 struct listnode
*anode
= listhead(vertex
->Adj_N
);
1537 struct listnode
*pnode
= listhead(vertex
->parents
);
1538 struct isis_adjacency
*adj
;
1539 struct isis_vertex
*pvertex
;
1541 vty_out(vty
, "%-20s %-12s %-6u ",
1542 vid2string(vertex
, buff
, sizeof(buff
)),
1543 vtype2string(vertex
->type
), vertex
->d_N
);
1544 for (unsigned int i
= 0;
1545 i
< MAX(vertex
->Adj_N
? listcount(vertex
->Adj_N
) : 0,
1546 vertex
->parents
? listcount(vertex
->parents
) : 0);
1549 adj
= listgetdata(anode
);
1550 anode
= anode
->next
;
1556 pvertex
= listgetdata(pnode
);
1557 pnode
= pnode
->next
;
1564 vty_out(vty
, "%-20s %-12s %-6s ", "", "", "");
1568 vty_out(vty
, "%-20s %-9s ",
1569 print_sys_hostname(adj
->sysid
),
1570 adj
->circuit
->interface
->name
);
1575 vty_out(vty
, "%-20s %-9s ", "", "");
1577 vty_out(vty
, "%s(%d)",
1578 vid2string(pvertex
, buff
, sizeof(buff
)),
1588 static void isis_print_spftree(struct vty
*vty
, int level
,
1589 struct isis_area
*area
,
1590 enum spf_tree_id tree_id
)
1592 const char *tree_id_text
= NULL
;
1596 tree_id_text
= "that speak IP";
1599 tree_id_text
= "that speak IPv6";
1601 case SPFTREE_DSTSRC
:
1602 tree_id_text
= "that support IPv6 dst-src routing";
1605 assert(!"isis_print_spftree shouldn't be called with SPFTREE_COUNT as type");
1609 if (!area
->spftree
[tree_id
][level
- 1]
1610 || !isis_vertex_queue_count(
1611 &area
->spftree
[tree_id
][level
- 1]->paths
))
1614 vty_out(vty
, "IS-IS paths to level-%d routers %s\n",
1615 level
, tree_id_text
);
1616 isis_print_paths(vty
, &area
->spftree
[tree_id
][level
- 1]->paths
,
1621 DEFUN (show_isis_topology
,
1622 show_isis_topology_cmd
,
1623 "show isis topology [<level-1|level-2>]",
1625 "IS-IS information\n"
1626 "IS-IS paths to Intermediate Systems\n"
1627 "Paths to all level-1 routers in the area\n"
1628 "Paths to all level-2 routers in the domain\n")
1631 struct listnode
*node
;
1632 struct isis_area
*area
;
1635 levels
= ISIS_LEVEL1
| ISIS_LEVEL2
;
1636 else if (strmatch(argv
[3]->text
, "level-1"))
1637 levels
= ISIS_LEVEL1
;
1639 levels
= ISIS_LEVEL2
;
1641 if (!isis
->area_list
|| isis
->area_list
->count
== 0)
1644 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, node
, area
)) {
1645 vty_out(vty
, "Area %s:\n",
1646 area
->area_tag
? area
->area_tag
: "null");
1648 for (int level
= ISIS_LEVEL1
; level
<= ISIS_LEVELS
; level
++) {
1649 if ((level
& levels
) == 0)
1652 if (area
->ip_circuits
> 0) {
1653 isis_print_spftree(vty
, level
, area
,
1656 if (area
->ipv6_circuits
> 0) {
1657 isis_print_spftree(vty
, level
, area
,
1660 if (isis_area_ipv6_dstsrc_enabled(area
)) {
1661 isis_print_spftree(vty
, level
, area
,
1672 void isis_spf_cmds_init()
1674 install_element(VIEW_NODE
, &show_isis_topology_cmd
);
1677 void isis_spf_print(struct isis_spftree
*spftree
, struct vty
*vty
)
1679 vty_out(vty
, " last run elapsed : ");
1680 vty_out_timestr(vty
, spftree
->last_run_timestamp
);
1683 vty_out(vty
, " last run duration : %u usec\n",
1684 (uint32_t)spftree
->last_run_duration
);
1686 vty_out(vty
, " run count : %u\n", spftree
->runcount
);