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
36 #include "spf_backoff.h"
37 #include "srcdest_table.h"
39 #include "isis_constants.h"
40 #include "isis_common.h"
41 #include "isis_flags.h"
43 #include "isis_misc.h"
44 #include "isis_adjacency.h"
45 #include "isis_circuit.h"
48 #include "isis_dynhn.h"
50 #include "isis_route.h"
53 #include "isis_tlvs.h"
55 #include "isis_spf_private.h"
57 DEFINE_MTYPE_STATIC(ISISD
, ISIS_SPF_RUN
, "ISIS SPF Run Info");
60 * supports the given af ?
62 static bool speaks(uint8_t *protocols
, uint8_t count
, int family
)
64 for (uint8_t i
= 0; i
< count
; i
++) {
65 if (family
== AF_INET
&& protocols
[i
] == NLPID_IP
)
67 if (family
== AF_INET6
&& protocols
[i
] == NLPID_IPV6
)
74 struct isis_area
*area
;
79 static void remove_excess_adjs(struct list
*adjs
)
81 struct listnode
*node
, *excess
= NULL
;
82 struct isis_adjacency
*adj
, *candidate
= NULL
;
85 for (ALL_LIST_ELEMENTS_RO(adjs
, node
, adj
)) {
88 candidate
= listgetdata(excess
);
90 if (candidate
->sys_type
< adj
->sys_type
) {
94 if (candidate
->sys_type
> adj
->sys_type
)
97 comp
= memcmp(candidate
->sysid
, adj
->sysid
, ISIS_SYS_ID_LEN
);
105 if (candidate
->circuit
->idx
> adj
->circuit
->idx
) {
110 if (candidate
->circuit
->idx
< adj
->circuit
->idx
)
113 comp
= memcmp(candidate
->snpa
, adj
->snpa
, ETH_ALEN
);
120 list_delete_node(adjs
, excess
);
125 static const char *vtype2string(enum vertextype vtype
)
128 case VTYPE_PSEUDO_IS
:
131 case VTYPE_PSEUDO_TE_IS
:
132 return "pseudo_TE-IS";
134 case VTYPE_NONPSEUDO_IS
:
137 case VTYPE_NONPSEUDO_TE_IS
:
143 case VTYPE_IPREACH_INTERNAL
:
144 return "IP internal";
146 case VTYPE_IPREACH_EXTERNAL
:
147 return "IP external";
149 case VTYPE_IPREACH_TE
:
152 case VTYPE_IP6REACH_INTERNAL
:
153 return "IP6 internal";
155 case VTYPE_IP6REACH_EXTERNAL
:
156 return "IP6 external";
161 return NULL
; /* Not reached */
164 const char *vid2string(struct isis_vertex
*vertex
, char *buff
, int size
)
166 if (VTYPE_IS(vertex
->type
) || VTYPE_ES(vertex
->type
)) {
167 return print_sys_hostname(vertex
->N
.id
);
170 if (VTYPE_IP(vertex
->type
)) {
171 srcdest2str(&vertex
->N
.ip
.dest
,
180 static struct isis_vertex
*isis_vertex_new(struct isis_spftree
*spftree
,
182 enum vertextype vtype
)
184 struct isis_vertex
*vertex
;
186 vertex
= XCALLOC(MTYPE_ISIS_VERTEX
, sizeof(struct isis_vertex
));
188 isis_vertex_id_init(vertex
, id
, vtype
);
190 vertex
->Adj_N
= list_new();
191 vertex
->parents
= list_new();
193 if (spftree
->hopcount_metric
) {
194 vertex
->firsthops
= hash_create(isis_vertex_queue_hash_key
,
195 isis_vertex_queue_hash_cmp
,
202 static void isis_vertex_adj_del(struct isis_vertex
*vertex
,
203 struct isis_adjacency
*adj
)
205 struct listnode
*node
, *nextnode
;
208 for (node
= listhead(vertex
->Adj_N
); node
; node
= nextnode
) {
209 nextnode
= listnextnode(node
);
210 if (listgetdata(node
) == adj
)
211 list_delete_node(vertex
->Adj_N
, node
);
216 struct isis_spftree
*isis_spftree_new(struct isis_area
*area
)
218 struct isis_spftree
*tree
;
220 tree
= XCALLOC(MTYPE_ISIS_SPFTREE
, sizeof(struct isis_spftree
));
222 isis_vertex_queue_init(&tree
->tents
, "IS-IS SPF tents", true);
223 isis_vertex_queue_init(&tree
->paths
, "IS-IS SPF paths", false);
224 tree
->route_table
= srcdest_table_init();
226 tree
->last_run_timestamp
= 0;
227 tree
->last_run_monotime
= 0;
228 tree
->last_run_duration
= 0;
233 void isis_spftree_del(struct isis_spftree
*spftree
)
235 isis_vertex_queue_free(&spftree
->tents
);
236 isis_vertex_queue_free(&spftree
->paths
);
237 route_table_finish(spftree
->route_table
);
238 spftree
->route_table
= NULL
;
240 XFREE(MTYPE_ISIS_SPFTREE
, spftree
);
244 static void isis_spftree_adj_del(struct isis_spftree
*spftree
,
245 struct isis_adjacency
*adj
)
247 struct listnode
*node
;
248 struct isis_vertex
*v
;
251 assert(!isis_vertex_queue_count(&spftree
->tents
));
252 for (ALL_QUEUE_ELEMENTS_RO(&spftree
->paths
, node
, v
))
253 isis_vertex_adj_del(v
, adj
);
257 void spftree_area_init(struct isis_area
*area
)
259 for (int tree
= SPFTREE_IPV4
; tree
< SPFTREE_COUNT
; tree
++) {
260 for (int level
= ISIS_LEVEL1
; level
<= ISIS_LEVEL2
; level
++) {
261 if (!(area
->is_type
& level
))
263 if (area
->spftree
[tree
][level
- 1])
266 area
->spftree
[tree
][level
- 1] = isis_spftree_new(area
);
271 void spftree_area_del(struct isis_area
*area
)
273 for (int tree
= SPFTREE_IPV4
; tree
< SPFTREE_COUNT
; tree
++) {
274 for (int level
= ISIS_LEVEL1
; level
<= ISIS_LEVEL2
; level
++) {
275 if (!(area
->is_type
& level
))
277 if (!area
->spftree
[tree
][level
- 1])
280 isis_spftree_del(area
->spftree
[tree
][level
- 1]);
285 void spftree_area_adj_del(struct isis_area
*area
, struct isis_adjacency
*adj
)
287 for (int tree
= SPFTREE_IPV4
; tree
< SPFTREE_COUNT
; tree
++) {
288 for (int level
= ISIS_LEVEL1
; level
<= ISIS_LEVEL2
; level
++) {
289 if (!(area
->is_type
& level
))
291 if (!area
->spftree
[tree
][level
- 1])
293 isis_spftree_adj_del(area
->spftree
[tree
][level
- 1],
298 if (fabricd_spftree(area
) != NULL
)
299 isis_spftree_adj_del(fabricd_spftree(area
), adj
);
303 * Find the system LSP: returns the LSP in our LSP database
304 * associated with the given system ID.
306 static struct isis_lsp
*isis_root_system_lsp(struct isis_area
*area
, int level
,
309 struct isis_lsp
*lsp
;
310 uint8_t lspid
[ISIS_SYS_ID_LEN
+ 2];
312 memcpy(lspid
, sysid
, ISIS_SYS_ID_LEN
);
313 LSP_PSEUDO_ID(lspid
) = 0;
314 LSP_FRAGMENT(lspid
) = 0;
315 lsp
= lsp_search(&area
->lspdb
[level
- 1], lspid
);
316 if (lsp
&& lsp
->hdr
.rem_lifetime
!= 0)
322 * Add this IS to the root of SPT
324 static struct isis_vertex
*isis_spf_add_root(struct isis_spftree
*spftree
,
327 struct isis_vertex
*vertex
;
328 struct isis_lsp
*lsp
;
330 char buff
[VID2STR_BUFFER
];
331 #endif /* EXTREME_DEBUG */
333 lsp
= isis_root_system_lsp(spftree
->area
, spftree
->level
, sysid
);
335 zlog_warn("ISIS-Spf: could not find own l%d LSP!",
338 vertex
= isis_vertex_new(spftree
, sysid
,
339 spftree
->area
->oldmetric
341 : VTYPE_NONPSEUDO_TE_IS
);
342 isis_vertex_queue_append(&spftree
->paths
, vertex
);
345 zlog_debug("ISIS-Spf: added this IS %s %s depth %d dist %d to PATHS",
346 vtype2string(vertex
->type
),
347 vid2string(vertex
, buff
, sizeof(buff
)), vertex
->depth
,
349 #endif /* EXTREME_DEBUG */
354 static void vertex_add_parent_firsthop(struct hash_bucket
*bucket
, void *arg
)
356 struct isis_vertex
*vertex
= arg
;
357 struct isis_vertex
*hop
= bucket
->data
;
359 hash_get(vertex
->firsthops
, hop
, hash_alloc_intern
);
362 static void vertex_update_firsthops(struct isis_vertex
*vertex
,
363 struct isis_vertex
*parent
)
365 if (vertex
->d_N
<= 2)
366 hash_get(vertex
->firsthops
, vertex
, hash_alloc_intern
);
368 if (vertex
->d_N
< 2 || !parent
)
371 hash_iterate(parent
->firsthops
, vertex_add_parent_firsthop
, vertex
);
375 * Add a vertex to TENT sorted by cost and by vertextype on tie break situation
377 static struct isis_vertex
*isis_spf_add2tent(struct isis_spftree
*spftree
,
378 enum vertextype vtype
, void *id
,
379 uint32_t cost
, int depth
,
380 struct isis_adjacency
*adj
,
381 struct isis_vertex
*parent
)
383 struct isis_vertex
*vertex
;
384 struct listnode
*node
;
385 struct isis_adjacency
*parent_adj
;
387 char buff
[VID2STR_BUFFER
];
390 assert(isis_find_vertex(&spftree
->paths
, id
, vtype
) == NULL
);
391 assert(isis_find_vertex(&spftree
->tents
, id
, vtype
) == NULL
);
392 vertex
= isis_vertex_new(spftree
, id
, vtype
);
394 vertex
->depth
= depth
;
397 listnode_add(vertex
->parents
, parent
);
400 if (spftree
->hopcount_metric
)
401 vertex_update_firsthops(vertex
, parent
);
403 if (parent
&& parent
->Adj_N
&& listcount(parent
->Adj_N
) > 0) {
404 for (ALL_LIST_ELEMENTS_RO(parent
->Adj_N
, node
, parent_adj
))
405 listnode_add(vertex
->Adj_N
, parent_adj
);
407 listnode_add(vertex
->Adj_N
, adj
);
412 "ISIS-Spf: add to TENT %s %s %s depth %d dist %d adjcount %d",
413 print_sys_hostname(vertex
->N
.id
), vtype2string(vertex
->type
),
414 vid2string(vertex
, buff
, sizeof(buff
)), vertex
->depth
,
415 vertex
->d_N
, listcount(vertex
->Adj_N
));
416 #endif /* EXTREME_DEBUG */
418 isis_vertex_queue_insert(&spftree
->tents
, vertex
);
422 static void isis_spf_add_local(struct isis_spftree
*spftree
,
423 enum vertextype vtype
, void *id
,
424 struct isis_adjacency
*adj
, uint32_t cost
,
425 struct isis_vertex
*parent
)
427 struct isis_vertex
*vertex
;
429 vertex
= isis_find_vertex(&spftree
->tents
, id
, vtype
);
433 if (vertex
->d_N
== cost
) {
435 listnode_add(vertex
->Adj_N
, adj
);
437 if (listcount(vertex
->Adj_N
) > ISIS_MAX_PATH_SPLITS
)
438 remove_excess_adjs(vertex
->Adj_N
);
439 if (parent
&& (listnode_lookup(vertex
->parents
, parent
)
441 listnode_add(vertex
->parents
, parent
);
443 } else if (vertex
->d_N
< cost
) {
446 } else { /* vertex->d_N > cost */
448 isis_vertex_queue_delete(&spftree
->tents
, vertex
);
449 isis_vertex_del(vertex
);
453 isis_spf_add2tent(spftree
, vtype
, id
, cost
, 1, adj
, parent
);
457 static void process_N(struct isis_spftree
*spftree
, enum vertextype vtype
,
458 void *id
, uint32_t dist
, uint16_t depth
,
459 struct isis_vertex
*parent
)
461 struct isis_vertex
*vertex
;
463 char buff
[VID2STR_BUFFER
];
466 assert(spftree
&& parent
);
468 if (spftree
->hopcount_metric
472 struct prefix_pair p
;
473 if (vtype
>= VTYPE_IPREACH_INTERNAL
) {
474 memcpy(&p
, id
, sizeof(p
));
476 apply_mask((struct prefix
*)&p
.src
);
480 /* RFC3787 section 5.1 */
481 if (spftree
->area
->newmetric
== 1) {
482 if (dist
> MAX_WIDE_PATH_METRIC
)
486 else if (spftree
->area
->oldmetric
== 1) {
487 if (dist
> MAX_NARROW_PATH_METRIC
)
492 vertex
= isis_find_vertex(&spftree
->paths
, id
, vtype
);
496 "ISIS-Spf: process_N %s %s %s dist %d already found from PATH",
497 print_sys_hostname(vertex
->N
.id
), vtype2string(vtype
),
498 vid2string(vertex
, buff
, sizeof(buff
)), dist
);
499 #endif /* EXTREME_DEBUG */
500 assert(dist
>= vertex
->d_N
);
504 vertex
= isis_find_vertex(&spftree
->tents
, id
, vtype
);
510 "ISIS-Spf: process_N %s %s %s dist %d parent %s adjcount %d",
511 print_sys_hostname(vertex
->N
.id
), vtype2string(vtype
),
512 vid2string(vertex
, buff
, sizeof(buff
)), dist
,
513 (parent
? print_sys_hostname(parent
->N
.id
) : "null"),
514 (parent
? listcount(parent
->Adj_N
) : 0));
515 #endif /* EXTREME_DEBUG */
516 if (vertex
->d_N
== dist
) {
517 struct listnode
*node
;
518 struct isis_adjacency
*parent_adj
;
519 for (ALL_LIST_ELEMENTS_RO(parent
->Adj_N
, node
,
521 if (listnode_lookup(vertex
->Adj_N
, parent_adj
)
523 listnode_add(vertex
->Adj_N
, parent_adj
);
524 if (spftree
->hopcount_metric
)
525 vertex_update_firsthops(vertex
, parent
);
527 if (listcount(vertex
->Adj_N
) > ISIS_MAX_PATH_SPLITS
)
528 remove_excess_adjs(vertex
->Adj_N
);
529 if (listnode_lookup(vertex
->parents
, parent
) == NULL
)
530 listnode_add(vertex
->parents
, parent
);
532 } else if (vertex
->d_N
< dist
) {
536 isis_vertex_queue_delete(&spftree
->tents
, vertex
);
537 isis_vertex_del(vertex
);
542 zlog_debug("ISIS-Spf: process_N add2tent %s %s dist %d parent %s",
543 print_sys_hostname(id
), vtype2string(vtype
), dist
,
544 (parent
? print_sys_hostname(parent
->N
.id
) : "null"));
545 #endif /* EXTREME_DEBUG */
547 isis_spf_add2tent(spftree
, vtype
, id
, dist
, depth
, NULL
, parent
);
554 static int isis_spf_process_lsp(struct isis_spftree
*spftree
,
555 struct isis_lsp
*lsp
, uint32_t cost
,
556 uint16_t depth
, uint8_t *root_sysid
,
557 struct isis_vertex
*parent
)
559 bool pseudo_lsp
= LSP_PSEUDO_ID(lsp
->hdr
.lsp_id
);
560 struct listnode
*fragnode
= NULL
;
562 enum vertextype vtype
;
563 static const uint8_t null_sysid
[ISIS_SYS_ID_LEN
];
564 struct isis_mt_router_info
*mt_router_info
= NULL
;
565 struct prefix_pair ip_info
;
570 if (spftree
->mtid
!= ISIS_MT_IPV4_UNICAST
)
571 mt_router_info
= isis_tlvs_lookup_mt_router_info(lsp
->tlvs
,
574 if (!pseudo_lsp
&& (spftree
->mtid
== ISIS_MT_IPV4_UNICAST
575 && !speaks(lsp
->tlvs
->protocols_supported
.protocols
,
576 lsp
->tlvs
->protocols_supported
.count
,
581 /* RFC3787 section 4 SHOULD ignore overload bit in pseudo LSPs */
582 bool no_overload
= (pseudo_lsp
583 || (spftree
->mtid
== ISIS_MT_IPV4_UNICAST
584 && !ISIS_MASK_LSP_OL_BIT(lsp
->hdr
.lsp_bits
))
585 || (mt_router_info
&& !mt_router_info
->overload
));
588 if (lsp
->hdr
.seqno
== 0) {
590 "isis_spf_process_lsp(): lsp with 0 seq_num - ignore");
595 zlog_debug("ISIS-Spf: process_lsp %s",
596 print_sys_hostname(lsp
->hdr
.lsp_id
));
597 #endif /* EXTREME_DEBUG */
600 if (pseudo_lsp
|| spftree
->mtid
== ISIS_MT_IPV4_UNICAST
) {
601 struct isis_oldstyle_reach
*r
;
602 for (r
= (struct isis_oldstyle_reach
*)
603 lsp
->tlvs
->oldstyle_reach
.head
;
609 /* Two way connectivity */
610 if (!memcmp(r
->id
, root_sysid
, ISIS_SYS_ID_LEN
))
613 && !memcmp(r
->id
, null_sysid
,
616 dist
= cost
+ r
->metric
;
620 : VTYPE_NONPSEUDO_IS
,
621 (void *)r
->id
, dist
, depth
+ 1,
626 struct isis_item_list
*te_neighs
= NULL
;
627 if (pseudo_lsp
|| spftree
->mtid
== ISIS_MT_IPV4_UNICAST
)
628 te_neighs
= &lsp
->tlvs
->extended_reach
;
630 te_neighs
= isis_lookup_mt_items(&lsp
->tlvs
->mt_reach
,
633 struct isis_extended_reach
*er
;
635 ? (struct isis_extended_reach
*)
639 if (!memcmp(er
->id
, root_sysid
, ISIS_SYS_ID_LEN
))
642 && !memcmp(er
->id
, null_sysid
, ISIS_SYS_ID_LEN
))
644 dist
= cost
+ (spftree
->hopcount_metric
? 1 : er
->metric
);
646 LSP_PSEUDO_ID(er
->id
) ? VTYPE_PSEUDO_TE_IS
647 : VTYPE_NONPSEUDO_TE_IS
,
648 (void *)er
->id
, dist
, depth
+ 1, parent
);
652 if (!fabricd
&& !pseudo_lsp
&& spftree
->family
== AF_INET
653 && spftree
->mtid
== ISIS_MT_IPV4_UNICAST
) {
654 struct isis_item_list
*reachs
[] = {
655 &lsp
->tlvs
->oldstyle_ip_reach
,
656 &lsp
->tlvs
->oldstyle_ip_reach_ext
};
658 for (unsigned int i
= 0; i
< array_size(reachs
); i
++) {
659 vtype
= i
? VTYPE_IPREACH_EXTERNAL
660 : VTYPE_IPREACH_INTERNAL
;
662 memset(&ip_info
, 0, sizeof(ip_info
));
663 ip_info
.dest
.family
= AF_INET
;
665 struct isis_oldstyle_ip_reach
*r
;
666 for (r
= (struct isis_oldstyle_ip_reach
*)reachs
[i
]
669 dist
= cost
+ r
->metric
;
670 ip_info
.dest
.u
.prefix4
= r
->prefix
.prefix
;
671 ip_info
.dest
.prefixlen
= r
->prefix
.prefixlen
;
672 process_N(spftree
, vtype
, &ip_info
,
673 dist
, depth
+ 1, parent
);
678 if (!pseudo_lsp
&& spftree
->family
== AF_INET
) {
679 struct isis_item_list
*ipv4_reachs
;
680 if (spftree
->mtid
== ISIS_MT_IPV4_UNICAST
)
681 ipv4_reachs
= &lsp
->tlvs
->extended_ip_reach
;
683 ipv4_reachs
= isis_lookup_mt_items(
684 &lsp
->tlvs
->mt_ip_reach
, spftree
->mtid
);
686 memset(&ip_info
, 0, sizeof(ip_info
));
687 ip_info
.dest
.family
= AF_INET
;
689 struct isis_extended_ip_reach
*r
;
691 ? (struct isis_extended_ip_reach
*)
695 dist
= cost
+ r
->metric
;
696 ip_info
.dest
.u
.prefix4
= r
->prefix
.prefix
;
697 ip_info
.dest
.prefixlen
= r
->prefix
.prefixlen
;
698 process_N(spftree
, VTYPE_IPREACH_TE
, &ip_info
,
699 dist
, depth
+ 1, parent
);
703 if (!pseudo_lsp
&& spftree
->family
== AF_INET6
) {
704 struct isis_item_list
*ipv6_reachs
;
705 if (spftree
->mtid
== ISIS_MT_IPV4_UNICAST
)
706 ipv6_reachs
= &lsp
->tlvs
->ipv6_reach
;
708 ipv6_reachs
= isis_lookup_mt_items(
709 &lsp
->tlvs
->mt_ipv6_reach
, spftree
->mtid
);
711 struct isis_ipv6_reach
*r
;
713 ? (struct isis_ipv6_reach
*)ipv6_reachs
->head
716 dist
= cost
+ r
->metric
;
717 vtype
= r
->external
? VTYPE_IP6REACH_EXTERNAL
718 : VTYPE_IP6REACH_INTERNAL
;
719 memset(&ip_info
, 0, sizeof(ip_info
));
720 ip_info
.dest
.family
= AF_INET6
;
721 ip_info
.dest
.u
.prefix6
= r
->prefix
.prefix
;
722 ip_info
.dest
.prefixlen
= r
->prefix
.prefixlen
;
725 && r
->subtlvs
->source_prefix
726 && r
->subtlvs
->source_prefix
->prefixlen
) {
727 if (spftree
->tree_id
!= SPFTREE_DSTSRC
) {
728 char buff
[VID2STR_BUFFER
];
729 zlog_warn("Ignoring dest-src route %s in non dest-src topology",
732 r
->subtlvs
->source_prefix
,
738 ip_info
.src
= *r
->subtlvs
->source_prefix
;
740 process_N(spftree
, vtype
, &ip_info
, dist
,
745 if (fragnode
== NULL
)
746 fragnode
= listhead(lsp
->lspu
.frags
);
748 fragnode
= listnextnode(fragnode
);
751 lsp
= listgetdata(fragnode
);
758 static int isis_spf_preload_tent(struct isis_spftree
*spftree
,
760 struct isis_vertex
*parent
)
762 struct isis_circuit
*circuit
;
763 struct listnode
*cnode
, *anode
, *ipnode
;
764 struct isis_adjacency
*adj
;
765 struct isis_lsp
*lsp
;
766 struct list
*adj_list
;
768 struct prefix_ipv4
*ipv4
;
769 struct prefix_pair ip_info
;
770 int retval
= ISIS_OK
;
771 uint8_t lsp_id
[ISIS_SYS_ID_LEN
+ 2];
772 static uint8_t null_lsp_id
[ISIS_SYS_ID_LEN
+ 2];
773 struct prefix_ipv6
*ipv6
;
774 struct isis_circuit_mt_setting
*circuit_mt
;
776 for (ALL_LIST_ELEMENTS_RO(spftree
->area
->circuit_list
, cnode
,
778 circuit_mt
= circuit_lookup_mt_setting(circuit
, spftree
->mtid
);
779 if (circuit_mt
&& !circuit_mt
->enabled
)
781 if (circuit
->state
!= C_STATE_UP
)
783 if (!(circuit
->is_type
& spftree
->level
))
785 if (spftree
->family
== AF_INET
&& !circuit
->ip_router
)
787 if (spftree
->family
== AF_INET6
&& !circuit
->ipv6_router
)
790 * Add IP(v6) addresses of this circuit
792 if (spftree
->family
== AF_INET
&& !spftree
->hopcount_metric
) {
793 memset(&ip_info
, 0, sizeof(ip_info
));
794 ip_info
.dest
.family
= AF_INET
;
795 for (ALL_LIST_ELEMENTS_RO(circuit
->ip_addrs
, ipnode
,
797 ip_info
.dest
.u
.prefix4
= ipv4
->prefix
;
798 ip_info
.dest
.prefixlen
= ipv4
->prefixlen
;
799 apply_mask(&ip_info
.dest
);
800 isis_spf_add_local(spftree
,
801 VTYPE_IPREACH_INTERNAL
,
802 &ip_info
, NULL
, 0, parent
);
805 if (spftree
->family
== AF_INET6
&& !spftree
->hopcount_metric
) {
806 memset(&ip_info
, 0, sizeof(ip_info
));
807 ip_info
.dest
.family
= AF_INET6
;
808 for (ALL_LIST_ELEMENTS_RO(circuit
->ipv6_non_link
,
810 ip_info
.dest
.u
.prefix6
= ipv6
->prefix
;
811 ip_info
.dest
.prefixlen
= ipv6
->prefixlen
;
812 apply_mask(&ip_info
.dest
);
813 isis_spf_add_local(spftree
,
814 VTYPE_IP6REACH_INTERNAL
,
815 &ip_info
, NULL
, 0, parent
);
818 if (circuit
->circ_type
== CIRCUIT_T_BROADCAST
) {
820 * Add the adjacencies
822 adj_list
= list_new();
823 adjdb
= circuit
->u
.bc
.adjdb
[spftree
->level
- 1];
824 isis_adj_build_up_list(adjdb
, adj_list
);
825 if (listcount(adj_list
) == 0) {
826 list_delete(&adj_list
);
827 if (isis
->debugs
& DEBUG_SPF_EVENTS
)
829 "ISIS-Spf: no L%d adjacencies on circuit %s",
831 circuit
->interface
->name
);
834 for (ALL_LIST_ELEMENTS_RO(adj_list
, anode
, adj
)) {
835 if (!adj_has_mt(adj
, spftree
->mtid
))
837 if (spftree
->mtid
== ISIS_MT_IPV4_UNICAST
838 && !speaks(adj
->nlpids
.nlpids
,
842 switch (adj
->sys_type
) {
843 case ISIS_SYSTYPE_ES
:
844 memcpy(lsp_id
, adj
->sysid
,
846 LSP_PSEUDO_ID(lsp_id
) = 0;
848 spftree
, VTYPE_ES
, lsp_id
, adj
,
849 spftree
->hopcount_metric
? 1 :
851 [spftree
->level
- 1],
854 case ISIS_SYSTYPE_IS
:
855 case ISIS_SYSTYPE_L1_IS
:
856 case ISIS_SYSTYPE_L2_IS
:
857 memcpy(lsp_id
, adj
->sysid
,
859 LSP_PSEUDO_ID(lsp_id
) = 0;
860 LSP_FRAGMENT(lsp_id
) = 0;
863 spftree
->area
->oldmetric
865 : VTYPE_NONPSEUDO_TE_IS
,
867 spftree
->hopcount_metric
? 1 :
869 [spftree
->level
- 1],
872 &spftree
->area
->lspdb
[spftree
->level
- 1],
875 || lsp
->hdr
.rem_lifetime
== 0)
877 "ISIS-Spf: No LSP %s found for IS adjacency "
879 rawlspid_print(lsp_id
),
881 circuit
->interface
->name
,
882 circuit
->circuit_id
);
884 case ISIS_SYSTYPE_UNKNOWN
:
887 "isis_spf_preload_tent unknown adj type");
890 list_delete(&adj_list
);
894 if (spftree
->level
== 1)
895 memcpy(lsp_id
, circuit
->u
.bc
.l1_desig_is
,
896 ISIS_SYS_ID_LEN
+ 1);
898 memcpy(lsp_id
, circuit
->u
.bc
.l2_desig_is
,
899 ISIS_SYS_ID_LEN
+ 1);
900 /* can happen during DR reboot */
901 if (memcmp(lsp_id
, null_lsp_id
, ISIS_SYS_ID_LEN
+ 1)
903 if (isis
->debugs
& DEBUG_SPF_EVENTS
)
905 "ISIS-Spf: No L%d DR on %s (ID %d)",
907 circuit
->interface
->name
,
908 circuit
->circuit_id
);
911 adj
= isis_adj_lookup(lsp_id
, adjdb
);
912 /* if no adj, we are the dis or error */
913 if (!adj
&& !circuit
->u
.bc
.is_dr
[spftree
->level
- 1]) {
915 "ISIS-Spf: No adjacency found from root "
916 "to L%d DR %s on %s (ID %d)",
917 spftree
->level
, rawlspid_print(lsp_id
),
918 circuit
->interface
->name
,
919 circuit
->circuit_id
);
923 &spftree
->area
->lspdb
[spftree
->level
- 1],
925 if (lsp
== NULL
|| lsp
->hdr
.rem_lifetime
== 0) {
927 "ISIS-Spf: No lsp (%p) found from root "
928 "to L%d DR %s on %s (ID %d)",
929 (void *)lsp
, spftree
->level
,
930 rawlspid_print(lsp_id
),
931 circuit
->interface
->name
,
932 circuit
->circuit_id
);
935 isis_spf_process_lsp(spftree
, lsp
,
936 spftree
->hopcount_metric
?
937 1 : circuit
->te_metric
[spftree
->level
- 1],
938 0, root_sysid
, parent
);
939 } else if (circuit
->circ_type
== CIRCUIT_T_P2P
) {
940 adj
= circuit
->u
.p2p
.neighbor
;
941 if (!adj
|| adj
->adj_state
!= ISIS_ADJ_UP
)
943 if (!adj_has_mt(adj
, spftree
->mtid
))
945 switch (adj
->sys_type
) {
946 case ISIS_SYSTYPE_ES
:
947 memcpy(lsp_id
, adj
->sysid
, ISIS_SYS_ID_LEN
);
948 LSP_PSEUDO_ID(lsp_id
) = 0;
950 spftree
, VTYPE_ES
, lsp_id
, adj
,
951 spftree
->hopcount_metric
? 1 :
952 circuit
->te_metric
[spftree
->level
- 1],
955 case ISIS_SYSTYPE_IS
:
956 case ISIS_SYSTYPE_L1_IS
:
957 case ISIS_SYSTYPE_L2_IS
:
958 memcpy(lsp_id
, adj
->sysid
, ISIS_SYS_ID_LEN
);
959 LSP_PSEUDO_ID(lsp_id
) = 0;
960 LSP_FRAGMENT(lsp_id
) = 0;
961 if (spftree
->mtid
!= ISIS_MT_IPV4_UNICAST
962 || speaks(adj
->nlpids
.nlpids
,
967 spftree
->area
->oldmetric
969 : VTYPE_NONPSEUDO_TE_IS
,
971 spftree
->hopcount_metric
? 1 :
973 [spftree
->level
- 1],
976 case ISIS_SYSTYPE_UNKNOWN
:
979 "isis_spf_preload_tent unknown adj type");
982 } else if (circuit
->circ_type
== CIRCUIT_T_LOOPBACK
) {
985 zlog_warn("isis_spf_preload_tent unsupported media");
986 retval
= ISIS_WARNING
;
994 * The parent(s) for vertex is set when added to TENT list
995 * now we just put the child pointer(s) in place
997 static void add_to_paths(struct isis_spftree
*spftree
,
998 struct isis_vertex
*vertex
)
1000 char buff
[VID2STR_BUFFER
];
1002 if (isis_find_vertex(&spftree
->paths
, &vertex
->N
, vertex
->type
))
1004 isis_vertex_queue_append(&spftree
->paths
, vertex
);
1006 #ifdef EXTREME_DEBUG
1007 zlog_debug("ISIS-Spf: added %s %s %s depth %d dist %d to PATHS",
1008 print_sys_hostname(vertex
->N
.id
), vtype2string(vertex
->type
),
1009 vid2string(vertex
, buff
, sizeof(buff
)), vertex
->depth
,
1011 #endif /* EXTREME_DEBUG */
1013 if (VTYPE_IP(vertex
->type
)) {
1014 if (listcount(vertex
->Adj_N
) > 0)
1015 isis_route_create(&vertex
->N
.ip
.dest
,
1017 vertex
->d_N
, vertex
->depth
,
1018 vertex
->Adj_N
, spftree
->area
,
1019 spftree
->route_table
);
1020 else if (isis
->debugs
& DEBUG_SPF_EVENTS
)
1022 "ISIS-Spf: no adjacencies do not install route for "
1023 "%s depth %d dist %d",
1024 vid2string(vertex
, buff
, sizeof(buff
)),
1025 vertex
->depth
, vertex
->d_N
);
1031 static void init_spt(struct isis_spftree
*spftree
, int mtid
, int level
,
1032 int family
, enum spf_tree_id tree_id
,
1033 bool hopcount_metric
)
1035 isis_vertex_queue_clear(&spftree
->tents
);
1036 isis_vertex_queue_clear(&spftree
->paths
);
1038 spftree
->mtid
= mtid
;
1039 spftree
->level
= level
;
1040 spftree
->family
= family
;
1041 spftree
->tree_id
= tree_id
;
1042 spftree
->hopcount_metric
= hopcount_metric
;
1045 static void isis_spf_loop(struct isis_spftree
*spftree
,
1046 uint8_t *root_sysid
)
1048 struct isis_vertex
*vertex
;
1049 struct isis_lsp
*lsp
;
1051 while (isis_vertex_queue_count(&spftree
->tents
)) {
1052 vertex
= isis_vertex_queue_pop(&spftree
->tents
);
1054 #ifdef EXTREME_DEBUG
1056 "ISIS-Spf: get TENT node %s %s depth %d dist %d to PATHS",
1057 print_sys_hostname(vertex
->N
.id
),
1058 vtype2string(vertex
->type
), vertex
->depth
, vertex
->d_N
);
1059 #endif /* EXTREME_DEBUG */
1061 add_to_paths(spftree
, vertex
);
1062 if (!VTYPE_IS(vertex
->type
))
1065 lsp
= lsp_for_vertex(spftree
, vertex
);
1067 zlog_warn("ISIS-Spf: No LSP found for %s",
1068 isis_format_id(vertex
->N
.id
,
1069 sizeof(vertex
->N
.id
)));
1073 isis_spf_process_lsp(spftree
, lsp
, vertex
->d_N
, vertex
->depth
,
1074 root_sysid
, vertex
);
1078 struct isis_spftree
*isis_run_hopcount_spf(struct isis_area
*area
,
1080 struct isis_spftree
*spftree
)
1083 spftree
= isis_spftree_new(area
);
1085 init_spt(spftree
, ISIS_MT_IPV4_UNICAST
, ISIS_LEVEL2
,
1086 AF_INET
, SPFTREE_IPV4
, true);
1087 if (!memcmp(sysid
, isis
->sysid
, ISIS_SYS_ID_LEN
)) {
1088 /* If we are running locally, initialize with information from adjacencies */
1089 struct isis_vertex
*root
= isis_spf_add_root(spftree
, sysid
);
1090 isis_spf_preload_tent(spftree
, sysid
, root
);
1092 isis_vertex_queue_insert(&spftree
->tents
, isis_vertex_new(
1094 VTYPE_NONPSEUDO_TE_IS
));
1097 isis_spf_loop(spftree
, sysid
);
1102 static int isis_run_spf(struct isis_area
*area
, int level
,
1103 enum spf_tree_id tree_id
,
1104 uint8_t *sysid
, struct timeval
*nowtv
)
1106 int retval
= ISIS_OK
;
1107 struct isis_vertex
*root_vertex
;
1108 struct isis_spftree
*spftree
= area
->spftree
[tree_id
][level
- 1];
1109 struct timeval time_now
;
1110 unsigned long long start_time
, end_time
;
1113 /* Get time that can't roll backwards. */
1114 start_time
= nowtv
->tv_sec
;
1115 start_time
= (start_time
* 1000000) + nowtv
->tv_usec
;
1121 mtid
= ISIS_MT_IPV4_UNICAST
;
1125 mtid
= isis_area_ipv6_topology(area
);
1127 case SPFTREE_DSTSRC
:
1129 mtid
= ISIS_MT_IPV6_DSTSRC
;
1132 assert(!"isis_run_spf should never be called with SPFTREE_COUNT as argument!");
1133 return ISIS_WARNING
;
1142 init_spt(spftree
, mtid
, level
, family
, tree_id
, false);
1144 root_vertex
= isis_spf_add_root(spftree
, sysid
);
1146 retval
= isis_spf_preload_tent(spftree
, sysid
, root_vertex
);
1147 if (retval
!= ISIS_OK
) {
1148 zlog_warn("ISIS-Spf: failed to load TENT SPF-root:%s",
1149 print_sys_hostname(sysid
));
1156 if (!isis_vertex_queue_count(&spftree
->tents
)
1157 && (isis
->debugs
& DEBUG_SPF_EVENTS
)) {
1158 zlog_warn("ISIS-Spf: TENT is empty SPF-root:%s",
1159 print_sys_hostname(sysid
));
1162 isis_spf_loop(spftree
, sysid
);
1164 spftree
->runcount
++;
1165 spftree
->last_run_timestamp
= time(NULL
);
1166 spftree
->last_run_monotime
= monotime(&time_now
);
1167 end_time
= time_now
.tv_sec
;
1168 end_time
= (end_time
* 1000000) + time_now
.tv_usec
;
1169 spftree
->last_run_duration
= end_time
- start_time
;
1174 void isis_spf_verify_routes(struct isis_area
*area
, struct isis_spftree
**trees
)
1176 if (area
->is_type
== IS_LEVEL_1
) {
1177 isis_route_verify_table(area
, trees
[0]->route_table
);
1178 } else if (area
->is_type
== IS_LEVEL_2
) {
1179 isis_route_verify_table(area
, trees
[1]->route_table
);
1181 isis_route_verify_merge(area
, trees
[0]->route_table
,
1182 trees
[1]->route_table
);
1186 void isis_spf_invalidate_routes(struct isis_spftree
*tree
)
1188 isis_route_invalidate_table(tree
->area
, tree
->route_table
);
1191 static int isis_run_spf_cb(struct thread
*thread
)
1193 struct isis_spf_run
*run
= THREAD_ARG(thread
);
1194 struct isis_area
*area
= run
->area
;
1195 int level
= run
->level
;
1196 int retval
= ISIS_OK
;
1198 XFREE(MTYPE_ISIS_SPF_RUN
, run
);
1199 area
->spf_timer
[level
- 1] = NULL
;
1201 if (!(area
->is_type
& level
)) {
1202 if (isis
->debugs
& DEBUG_SPF_EVENTS
)
1203 zlog_warn("ISIS-SPF (%s) area does not share level",
1205 return ISIS_WARNING
;
1208 isis_area_invalidate_routes(area
, level
);
1210 if (isis
->debugs
& DEBUG_SPF_EVENTS
)
1211 zlog_debug("ISIS-Spf (%s) L%d SPF needed, periodic SPF",
1212 area
->area_tag
, level
);
1214 if (area
->ip_circuits
)
1215 retval
= isis_run_spf(area
, level
, SPFTREE_IPV4
, isis
->sysid
,
1217 if (area
->ipv6_circuits
)
1218 retval
= isis_run_spf(area
, level
, SPFTREE_IPV6
, isis
->sysid
,
1220 if (area
->ipv6_circuits
1221 && isis_area_ipv6_dstsrc_enabled(area
))
1222 retval
= isis_run_spf(area
, level
, SPFTREE_DSTSRC
, isis
->sysid
,
1225 isis_area_verify_routes(area
);
1227 /* walk all circuits and reset any spf specific flags */
1228 struct listnode
*node
;
1229 struct isis_circuit
*circuit
;
1230 for (ALL_LIST_ELEMENTS_RO(area
->circuit_list
, node
, circuit
))
1231 UNSET_FLAG(circuit
->flags
, ISIS_CIRCUIT_FLAPPED_AFTER_SPF
);
1233 fabricd_run_spf(area
);
1238 static struct isis_spf_run
*isis_run_spf_arg(struct isis_area
*area
, int level
)
1240 struct isis_spf_run
*run
= XMALLOC(MTYPE_ISIS_SPF_RUN
, sizeof(*run
));
1248 int _isis_spf_schedule(struct isis_area
*area
, int level
,
1249 const char *func
, const char *file
, int line
)
1251 struct isis_spftree
*spftree
= area
->spftree
[SPFTREE_IPV4
][level
- 1];
1252 time_t now
= monotime(NULL
);
1253 int diff
= now
- spftree
->last_run_monotime
;
1256 assert(area
->is_type
& level
);
1258 if (isis
->debugs
& DEBUG_SPF_EVENTS
) {
1260 "ISIS-Spf (%s) L%d SPF schedule called, lastrun %d sec ago"
1261 " Caller: %s %s:%d",
1262 area
->area_tag
, level
, diff
, func
, file
, line
);
1265 if (area
->spf_delay_ietf
[level
- 1]) {
1266 /* Need to call schedule function also if spf delay is running
1268 * restart holdoff timer - compare
1269 * draft-ietf-rtgwg-backoff-algo-04 */
1271 spf_backoff_schedule(area
->spf_delay_ietf
[level
- 1]);
1272 if (area
->spf_timer
[level
- 1])
1275 thread_add_timer_msec(master
, isis_run_spf_cb
,
1276 isis_run_spf_arg(area
, level
), delay
,
1277 &area
->spf_timer
[level
- 1]);
1281 if (area
->spf_timer
[level
- 1])
1284 /* wait configured min_spf_interval before doing the SPF */
1286 if (diff
>= area
->min_spf_interval
[level
- 1]) {
1287 /* Last run is more than min interval ago, schedule immediate run */
1290 timer
= area
->min_spf_interval
[level
- 1] - diff
;
1293 thread_add_timer(master
, isis_run_spf_cb
, isis_run_spf_arg(area
, level
),
1294 timer
, &area
->spf_timer
[level
- 1]);
1296 if (isis
->debugs
& DEBUG_SPF_EVENTS
)
1297 zlog_debug("ISIS-Spf (%s) L%d SPF scheduled %ld sec from now",
1298 area
->area_tag
, level
, timer
);
1303 static void isis_print_paths(struct vty
*vty
, struct isis_vertex_queue
*queue
,
1304 uint8_t *root_sysid
)
1306 struct listnode
*node
;
1307 struct isis_vertex
*vertex
;
1308 char buff
[VID2STR_BUFFER
];
1311 "Vertex Type Metric Next-Hop Interface Parent\n");
1313 for (ALL_QUEUE_ELEMENTS_RO(queue
, node
, vertex
)) {
1314 if (memcmp(vertex
->N
.id
, root_sysid
, ISIS_SYS_ID_LEN
) == 0) {
1315 vty_out(vty
, "%-20s %-12s %-6s",
1316 print_sys_hostname(root_sysid
), "", "");
1317 vty_out(vty
, "%-30s\n", "");
1322 struct listnode
*anode
= listhead(vertex
->Adj_N
);
1323 struct listnode
*pnode
= listhead(vertex
->parents
);
1324 struct isis_adjacency
*adj
;
1325 struct isis_vertex
*pvertex
;
1327 vty_out(vty
, "%-20s %-12s %-6u ",
1328 vid2string(vertex
, buff
, sizeof(buff
)),
1329 vtype2string(vertex
->type
), vertex
->d_N
);
1330 for (unsigned int i
= 0;
1331 i
< MAX(vertex
->Adj_N
? listcount(vertex
->Adj_N
) : 0,
1332 vertex
->parents
? listcount(vertex
->parents
) : 0);
1335 adj
= listgetdata(anode
);
1336 anode
= anode
->next
;
1342 pvertex
= listgetdata(pnode
);
1343 pnode
= pnode
->next
;
1350 vty_out(vty
, "%-20s %-12s %-6s ", "", "", "");
1354 vty_out(vty
, "%-20s %-9s ",
1355 print_sys_hostname(adj
->sysid
),
1356 adj
->circuit
->interface
->name
);
1361 vty_out(vty
, "%-20s %-9s ", "", "");
1363 vty_out(vty
, "%s(%d)",
1364 vid2string(pvertex
, buff
, sizeof(buff
)),
1374 static void isis_print_spftree(struct vty
*vty
, int level
,
1375 struct isis_area
*area
,
1376 enum spf_tree_id tree_id
)
1378 const char *tree_id_text
= NULL
;
1382 tree_id_text
= "that speak IP";
1385 tree_id_text
= "that speak IPv6";
1387 case SPFTREE_DSTSRC
:
1388 tree_id_text
= "that support IPv6 dst-src routing";
1391 assert(!"isis_print_spftree shouldn't be called with SPFTREE_COUNT as type");
1395 if (!area
->spftree
[tree_id
][level
- 1]
1396 || !isis_vertex_queue_count(
1397 &area
->spftree
[tree_id
][level
- 1]->paths
))
1400 vty_out(vty
, "IS-IS paths to level-%d routers %s\n",
1401 level
, tree_id_text
);
1402 isis_print_paths(vty
, &area
->spftree
[tree_id
][level
- 1]->paths
,
1407 DEFUN (show_isis_topology
,
1408 show_isis_topology_cmd
,
1409 "show " PROTO_NAME
" topology"
1411 " [<level-1|level-2>]"
1415 "IS-IS paths to Intermediate Systems\n"
1417 "Paths to all level-1 routers in the area\n"
1418 "Paths to all level-2 routers in the domain\n"
1423 struct listnode
*node
;
1424 struct isis_area
*area
;
1427 levels
= ISIS_LEVEL1
| ISIS_LEVEL2
;
1428 else if (strmatch(argv
[3]->text
, "level-1"))
1429 levels
= ISIS_LEVEL1
;
1431 levels
= ISIS_LEVEL2
;
1433 if (!isis
->area_list
|| isis
->area_list
->count
== 0)
1436 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, node
, area
)) {
1437 vty_out(vty
, "Area %s:\n",
1438 area
->area_tag
? area
->area_tag
: "null");
1440 for (int level
= ISIS_LEVEL1
; level
<= ISIS_LEVELS
; level
++) {
1441 if ((level
& levels
) == 0)
1444 if (area
->ip_circuits
> 0) {
1445 isis_print_spftree(vty
, level
, area
,
1448 if (area
->ipv6_circuits
> 0) {
1449 isis_print_spftree(vty
, level
, area
,
1452 if (isis_area_ipv6_dstsrc_enabled(area
)) {
1453 isis_print_spftree(vty
, level
, area
,
1458 if (fabricd_spftree(area
)) {
1460 "IS-IS paths to level-2 routers with hop-by-hop metric\n");
1461 isis_print_paths(vty
, &fabricd_spftree(area
)->paths
, isis
->sysid
);
1471 void isis_spf_cmds_init(void)
1473 install_element(VIEW_NODE
, &show_isis_topology_cmd
);
1476 void isis_spf_print(struct isis_spftree
*spftree
, struct vty
*vty
)
1478 vty_out(vty
, " last run elapsed : ");
1479 vty_out_timestr(vty
, spftree
->last_run_timestamp
);
1482 vty_out(vty
, " last run duration : %u usec\n",
1483 (uint32_t)spftree
->last_run_duration
);
1485 vty_out(vty
, " run count : %u\n", spftree
->runcount
);