]>
git.proxmox.com Git - mirror_frr.git/blob - isisd/isis_spf.c
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"
39 #include "isis_constants.h"
40 #include "isis_common.h"
41 #include "isis_flags.h"
44 #include "isis_misc.h"
45 #include "isis_adjacency.h"
46 #include "isis_circuit.h"
50 #include "isis_dynhn.h"
52 #include "isis_route.h"
56 DEFINE_MTYPE_STATIC(ISISD
, ISIS_SPF_RUN
, "ISIS SPF Run Info");
59 struct isis_area
*area
;
64 static void remove_excess_adjs(struct list
*adjs
)
66 struct listnode
*node
, *excess
= NULL
;
67 struct isis_adjacency
*adj
, *candidate
= NULL
;
70 for (ALL_LIST_ELEMENTS_RO(adjs
, node
, adj
)) {
73 candidate
= listgetdata(excess
);
75 if (candidate
->sys_type
< adj
->sys_type
) {
79 if (candidate
->sys_type
> adj
->sys_type
)
82 comp
= memcmp(candidate
->sysid
, adj
->sysid
, ISIS_SYS_ID_LEN
);
90 if (candidate
->circuit
->circuit_id
> adj
->circuit
->circuit_id
) {
95 if (candidate
->circuit
->circuit_id
< adj
->circuit
->circuit_id
)
98 comp
= memcmp(candidate
->snpa
, adj
->snpa
, ETH_ALEN
);
105 list_delete_node(adjs
, excess
);
110 static const char *vtype2string(enum vertextype vtype
)
113 case VTYPE_PSEUDO_IS
:
116 case VTYPE_PSEUDO_TE_IS
:
117 return "pseudo_TE-IS";
119 case VTYPE_NONPSEUDO_IS
:
122 case VTYPE_NONPSEUDO_TE_IS
:
128 case VTYPE_IPREACH_INTERNAL
:
129 return "IP internal";
131 case VTYPE_IPREACH_EXTERNAL
:
132 return "IP external";
134 case VTYPE_IPREACH_TE
:
137 case VTYPE_IP6REACH_INTERNAL
:
138 return "IP6 internal";
140 case VTYPE_IP6REACH_EXTERNAL
:
141 return "IP6 external";
146 return NULL
; /* Not reached */
149 static const char *vid2string(struct isis_vertex
*vertex
, char *buff
, int size
)
151 if (VTYPE_IS(vertex
->type
) || VTYPE_ES(vertex
->type
)) {
152 return print_sys_hostname(vertex
->N
.id
);
155 if (VTYPE_IP(vertex
->type
)) {
156 prefix2str((struct prefix
*)&vertex
->N
.prefix
, buff
, size
);
163 static struct isis_vertex
*isis_vertex_new(void *id
, enum vertextype vtype
)
165 struct isis_vertex
*vertex
;
167 vertex
= XCALLOC(MTYPE_ISIS_VERTEX
, sizeof(struct isis_vertex
));
169 vertex
->type
= vtype
;
171 if (VTYPE_IS(vtype
) || VTYPE_ES(vtype
)) {
172 memcpy(vertex
->N
.id
, (u_char
*)id
, ISIS_SYS_ID_LEN
+ 1);
173 } else if (VTYPE_IP(vtype
)) {
174 memcpy(&vertex
->N
.prefix
, (struct prefix
*)id
,
175 sizeof(struct prefix
));
180 vertex
->Adj_N
= list_new();
181 vertex
->parents
= list_new();
182 vertex
->children
= list_new();
187 static void isis_vertex_del(struct isis_vertex
*vertex
)
189 list_delete(vertex
->Adj_N
);
190 vertex
->Adj_N
= NULL
;
191 list_delete(vertex
->parents
);
192 vertex
->parents
= NULL
;
193 list_delete(vertex
->children
);
194 vertex
->children
= NULL
;
196 memset(vertex
, 0, sizeof(struct isis_vertex
));
197 XFREE(MTYPE_ISIS_VERTEX
, vertex
);
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 zlog_err("ISIS-Spf: isis_spftree_new Out of memory!");
226 tree
->tents
= list_new();
227 tree
->paths
= list_new();
229 tree
->last_run_timestamp
= 0;
230 tree
->last_run_duration
= 0;
235 void isis_spftree_del(struct isis_spftree
*spftree
)
238 spftree
->tents
->del
= (void (*)(void *))isis_vertex_del
;
239 list_delete(spftree
->tents
);
240 spftree
->tents
= NULL
;
242 spftree
->paths
->del
= (void (*)(void *))isis_vertex_del
;
243 list_delete(spftree
->paths
);
244 spftree
->paths
= NULL
;
246 XFREE(MTYPE_ISIS_SPFTREE
, spftree
);
251 static void isis_spftree_adj_del(struct isis_spftree
*spftree
,
252 struct isis_adjacency
*adj
)
254 struct listnode
*node
;
257 for (node
= listhead(spftree
->tents
); node
; node
= listnextnode(node
))
258 isis_vertex_adj_del(listgetdata(node
), adj
);
259 for (node
= listhead(spftree
->paths
); node
; node
= listnextnode(node
))
260 isis_vertex_adj_del(listgetdata(node
), adj
);
264 void spftree_area_init(struct isis_area
*area
)
266 if (area
->is_type
& IS_LEVEL_1
) {
267 if (area
->spftree
[0] == NULL
)
268 area
->spftree
[0] = isis_spftree_new(area
);
269 if (area
->spftree6
[0] == NULL
)
270 area
->spftree6
[0] = isis_spftree_new(area
);
273 if (area
->is_type
& IS_LEVEL_2
) {
274 if (area
->spftree
[1] == NULL
)
275 area
->spftree
[1] = isis_spftree_new(area
);
276 if (area
->spftree6
[1] == NULL
)
277 area
->spftree6
[1] = isis_spftree_new(area
);
283 void spftree_area_del(struct isis_area
*area
)
285 if (area
->is_type
& IS_LEVEL_1
) {
286 if (area
->spftree
[0] != NULL
) {
287 isis_spftree_del(area
->spftree
[0]);
288 area
->spftree
[0] = NULL
;
290 if (area
->spftree6
[0]) {
291 isis_spftree_del(area
->spftree6
[0]);
292 area
->spftree6
[0] = NULL
;
296 if (area
->is_type
& IS_LEVEL_2
) {
297 if (area
->spftree
[1] != NULL
) {
298 isis_spftree_del(area
->spftree
[1]);
299 area
->spftree
[1] = NULL
;
301 if (area
->spftree6
[1] != NULL
) {
302 isis_spftree_del(area
->spftree6
[1]);
303 area
->spftree6
[1] = NULL
;
310 void spftree_area_adj_del(struct isis_area
*area
, struct isis_adjacency
*adj
)
312 if (area
->is_type
& IS_LEVEL_1
) {
313 if (area
->spftree
[0] != NULL
)
314 isis_spftree_adj_del(area
->spftree
[0], adj
);
315 if (area
->spftree6
[0] != NULL
)
316 isis_spftree_adj_del(area
->spftree6
[0], adj
);
319 if (area
->is_type
& IS_LEVEL_2
) {
320 if (area
->spftree
[1] != NULL
)
321 isis_spftree_adj_del(area
->spftree
[1], adj
);
322 if (area
->spftree6
[1] != NULL
)
323 isis_spftree_adj_del(area
->spftree6
[1], adj
);
330 * Find the system LSP: returns the LSP in our LSP database
331 * associated with the given system ID.
333 static struct isis_lsp
*isis_root_system_lsp(struct isis_area
*area
, int level
,
336 struct isis_lsp
*lsp
;
337 u_char lspid
[ISIS_SYS_ID_LEN
+ 2];
339 memcpy(lspid
, sysid
, ISIS_SYS_ID_LEN
);
340 LSP_PSEUDO_ID(lspid
) = 0;
341 LSP_FRAGMENT(lspid
) = 0;
342 lsp
= lsp_search(lspid
, area
->lspdb
[level
- 1]);
343 if (lsp
&& lsp
->lsp_header
->rem_lifetime
!= 0)
349 * Add this IS to the root of SPT
351 static struct isis_vertex
*isis_spf_add_root(struct isis_spftree
*spftree
,
354 struct isis_vertex
*vertex
;
355 struct isis_lsp
*lsp
;
357 char buff
[PREFIX2STR_BUFFER
];
358 #endif /* EXTREME_DEBUG */
359 u_char id
[ISIS_SYS_ID_LEN
+ 1];
361 memcpy(id
, sysid
, ISIS_SYS_ID_LEN
);
362 LSP_PSEUDO_ID(id
) = 0;
364 lsp
= isis_root_system_lsp(spftree
->area
, spftree
->level
, sysid
);
366 zlog_warn("ISIS-Spf: could not find own l%d LSP!",
369 vertex
= isis_vertex_new(id
,
370 spftree
->area
->oldmetric
372 : VTYPE_NONPSEUDO_TE_IS
);
373 listnode_add(spftree
->paths
, vertex
);
376 zlog_debug("ISIS-Spf: added this IS %s %s depth %d dist %d to PATHS",
377 vtype2string(vertex
->type
),
378 vid2string(vertex
, buff
, sizeof(buff
)), vertex
->depth
,
380 #endif /* EXTREME_DEBUG */
385 static struct isis_vertex
*isis_find_vertex(struct list
*list
, void *id
,
386 enum vertextype vtype
)
388 struct listnode
*node
;
389 struct isis_vertex
*vertex
;
390 struct prefix
*p1
, *p2
;
392 for (ALL_LIST_ELEMENTS_RO(list
, node
, vertex
)) {
393 if (vertex
->type
!= vtype
)
395 if (VTYPE_IS(vertex
->type
) || VTYPE_ES(vertex
->type
)) {
396 if (memcmp((u_char
*)id
, vertex
->N
.id
,
401 if (VTYPE_IP(vertex
->type
)) {
402 p1
= (struct prefix
*)id
;
403 p2
= (struct prefix
*)&vertex
->N
.id
;
404 if (p1
->family
== p2
->family
405 && p1
->prefixlen
== p2
->prefixlen
406 && !memcmp(&p1
->u
.prefix
, &p2
->u
.prefix
,
407 PSIZE(p1
->prefixlen
))) {
417 * Compares vertizes for sorting in the TENT list. Returns true
418 * if candidate should be considered before current, false otherwise.
420 static bool tent_cmp(struct isis_vertex
*current
, struct isis_vertex
*candidate
)
422 if (current
->d_N
> candidate
->d_N
)
425 if (current
->d_N
== candidate
->d_N
&& current
->type
> candidate
->type
)
432 * Add a vertex to TENT sorted by cost and by vertextype on tie break situation
434 static struct isis_vertex
*isis_spf_add2tent(struct isis_spftree
*spftree
,
435 enum vertextype vtype
, void *id
,
436 uint32_t cost
, int depth
,
437 struct isis_adjacency
*adj
,
438 struct isis_vertex
*parent
)
440 struct isis_vertex
*vertex
, *v
;
441 struct listnode
*node
;
442 struct isis_adjacency
*parent_adj
;
444 char buff
[PREFIX2STR_BUFFER
];
447 assert(isis_find_vertex(spftree
->paths
, id
, vtype
) == NULL
);
448 assert(isis_find_vertex(spftree
->tents
, id
, vtype
) == NULL
);
449 vertex
= isis_vertex_new(id
, vtype
);
451 vertex
->depth
= depth
;
454 listnode_add(vertex
->parents
, parent
);
455 if (listnode_lookup(parent
->children
, vertex
) == NULL
)
456 listnode_add(parent
->children
, vertex
);
459 if (parent
&& parent
->Adj_N
&& listcount(parent
->Adj_N
) > 0) {
460 for (ALL_LIST_ELEMENTS_RO(parent
->Adj_N
, node
, parent_adj
))
461 listnode_add(vertex
->Adj_N
, parent_adj
);
463 listnode_add(vertex
->Adj_N
, adj
);
468 "ISIS-Spf: add to TENT %s %s %s depth %d dist %d adjcount %d",
469 print_sys_hostname(vertex
->N
.id
), vtype2string(vertex
->type
),
470 vid2string(vertex
, buff
, sizeof(buff
)), vertex
->depth
,
471 vertex
->d_N
, listcount(vertex
->Adj_N
));
472 #endif /* EXTREME_DEBUG */
474 if (list_isempty(spftree
->tents
)) {
475 listnode_add(spftree
->tents
, vertex
);
479 /* XXX: This cant use the standard ALL_LIST_ELEMENTS macro */
480 for (node
= listhead(spftree
->tents
); node
; node
= listnextnode(node
)) {
481 v
= listgetdata(node
);
482 if (tent_cmp(v
, vertex
)) {
483 listnode_add_before(spftree
->tents
, node
, vertex
);
489 listnode_add(spftree
->tents
, vertex
);
494 static void isis_spf_add_local(struct isis_spftree
*spftree
,
495 enum vertextype vtype
, void *id
,
496 struct isis_adjacency
*adj
, uint32_t cost
,
497 struct isis_vertex
*parent
)
499 struct isis_vertex
*vertex
;
501 vertex
= isis_find_vertex(spftree
->tents
, id
, vtype
);
505 if (vertex
->d_N
== cost
) {
507 listnode_add(vertex
->Adj_N
, adj
);
509 if (listcount(vertex
->Adj_N
) > ISIS_MAX_PATH_SPLITS
)
510 remove_excess_adjs(vertex
->Adj_N
);
511 if (parent
&& (listnode_lookup(vertex
->parents
, parent
)
513 listnode_add(vertex
->parents
, parent
);
514 if (parent
&& (listnode_lookup(parent
->children
, vertex
)
516 listnode_add(parent
->children
, vertex
);
518 } else if (vertex
->d_N
< cost
) {
521 } else { /* vertex->d_N > cost */
523 struct listnode
*pnode
, *pnextnode
;
524 struct isis_vertex
*pvertex
;
525 listnode_delete(spftree
->tents
, vertex
);
526 assert(listcount(vertex
->children
) == 0);
527 for (ALL_LIST_ELEMENTS(vertex
->parents
, pnode
,
529 listnode_delete(pvertex
->children
, vertex
);
530 isis_vertex_del(vertex
);
534 isis_spf_add2tent(spftree
, vtype
, id
, cost
, 1, adj
, parent
);
538 static void process_N(struct isis_spftree
*spftree
, enum vertextype vtype
,
539 void *id
, uint32_t dist
, uint16_t depth
,
540 struct isis_vertex
*parent
)
542 struct isis_vertex
*vertex
;
544 char buff
[PREFIX2STR_BUFFER
];
547 assert(spftree
&& parent
);
549 /* RFC3787 section 5.1 */
550 if (spftree
->area
->newmetric
== 1) {
551 if (dist
> MAX_WIDE_PATH_METRIC
)
555 else if (spftree
->area
->oldmetric
== 1) {
556 if (dist
> MAX_NARROW_PATH_METRIC
)
561 vertex
= isis_find_vertex(spftree
->paths
, id
, vtype
);
565 "ISIS-Spf: process_N %s %s %s dist %d already found from PATH",
566 print_sys_hostname(vertex
->N
.id
), vtype2string(vtype
),
567 vid2string(vertex
, buff
, sizeof(buff
)), dist
);
568 #endif /* EXTREME_DEBUG */
569 assert(dist
>= vertex
->d_N
);
573 vertex
= isis_find_vertex(spftree
->tents
, id
, vtype
);
579 "ISIS-Spf: process_N %s %s %s dist %d parent %s adjcount %d",
580 print_sys_hostname(vertex
->N
.id
), vtype2string(vtype
),
581 vid2string(vertex
, buff
, sizeof(buff
)), dist
,
582 (parent
? print_sys_hostname(parent
->N
.id
) : "null"),
583 (parent
? listcount(parent
->Adj_N
) : 0));
584 #endif /* EXTREME_DEBUG */
585 if (vertex
->d_N
== dist
) {
586 struct listnode
*node
;
587 struct isis_adjacency
*parent_adj
;
588 for (ALL_LIST_ELEMENTS_RO(parent
->Adj_N
, node
,
590 if (listnode_lookup(vertex
->Adj_N
, parent_adj
)
592 listnode_add(vertex
->Adj_N
, parent_adj
);
594 if (listcount(vertex
->Adj_N
) > ISIS_MAX_PATH_SPLITS
)
595 remove_excess_adjs(vertex
->Adj_N
);
596 if (listnode_lookup(vertex
->parents
, parent
) == NULL
)
597 listnode_add(vertex
->parents
, parent
);
598 if (listnode_lookup(parent
->children
, vertex
) == NULL
)
599 listnode_add(parent
->children
, vertex
);
602 } else if (vertex
->d_N
< dist
) {
606 struct listnode
*pnode
, *pnextnode
;
607 struct isis_vertex
*pvertex
;
608 listnode_delete(spftree
->tents
, vertex
);
609 assert(listcount(vertex
->children
) == 0);
610 for (ALL_LIST_ELEMENTS(vertex
->parents
, pnode
,
612 listnode_delete(pvertex
->children
, vertex
);
613 isis_vertex_del(vertex
);
618 zlog_debug("ISIS-Spf: process_N add2tent %s %s dist %d parent %s",
619 print_sys_hostname(id
), vtype2string(vtype
), dist
,
620 (parent
? print_sys_hostname(parent
->N
.id
) : "null"));
621 #endif /* EXTREME_DEBUG */
623 isis_spf_add2tent(spftree
, vtype
, id
, dist
, depth
, NULL
, parent
);
630 static int isis_spf_process_lsp(struct isis_spftree
*spftree
,
631 struct isis_lsp
*lsp
, uint32_t cost
,
632 uint16_t depth
, u_char
*root_sysid
,
633 struct isis_vertex
*parent
)
635 bool pseudo_lsp
= LSP_PSEUDO_ID(lsp
->lsp_header
->lsp_id
);
636 struct listnode
*node
, *fragnode
= NULL
;
638 struct is_neigh
*is_neigh
;
639 struct te_is_neigh
*te_is_neigh
;
640 struct ipv4_reachability
*ipreach
;
641 struct te_ipv4_reachability
*te_ipv4_reach
;
642 enum vertextype vtype
;
643 struct prefix prefix
;
644 struct ipv6_reachability
*ip6reach
;
645 static const u_char null_sysid
[ISIS_SYS_ID_LEN
];
646 struct mt_router_info
*mt_router_info
= NULL
;
648 if (spftree
->mtid
!= ISIS_MT_IPV4_UNICAST
)
649 mt_router_info
= tlvs_lookup_mt_router_info(&lsp
->tlv_data
,
652 if (!pseudo_lsp
&& (spftree
->mtid
== ISIS_MT_IPV4_UNICAST
653 && !speaks(lsp
->tlv_data
.nlpids
, spftree
->family
))
658 if (lsp
->lsp_header
->seq_num
== 0) {
660 "isis_spf_process_lsp(): lsp with 0 seq_num - ignore");
665 zlog_debug("ISIS-Spf: process_lsp %s",
666 print_sys_hostname(lsp
->lsp_header
->lsp_id
));
667 #endif /* EXTREME_DEBUG */
669 /* RFC3787 section 4 SHOULD ignore overload bit in pseudo LSPs */
670 if (pseudo_lsp
|| (spftree
->mtid
== ISIS_MT_IPV4_UNICAST
671 && !ISIS_MASK_LSP_OL_BIT(lsp
->lsp_header
->lsp_bits
))
672 || (mt_router_info
&& !mt_router_info
->overload
))
675 if (pseudo_lsp
|| spftree
->mtid
== ISIS_MT_IPV4_UNICAST
) {
676 for (ALL_LIST_ELEMENTS_RO(lsp
->tlv_data
.is_neighs
, node
,
679 /* Two way connectivity */
680 if (!memcmp(is_neigh
->neigh_id
, root_sysid
,
684 && !memcmp(is_neigh
->neigh_id
, null_sysid
,
687 dist
= cost
+ is_neigh
->metrics
.metric_default
;
689 LSP_PSEUDO_ID(is_neigh
->neigh_id
)
691 : VTYPE_NONPSEUDO_IS
,
692 (void *)is_neigh
->neigh_id
, dist
,
697 struct list
*te_is_neighs
= NULL
;
698 if (pseudo_lsp
|| spftree
->mtid
== ISIS_MT_IPV4_UNICAST
) {
699 te_is_neighs
= lsp
->tlv_data
.te_is_neighs
;
701 struct tlv_mt_neighbors
*mt_neighbors
;
702 mt_neighbors
= tlvs_lookup_mt_neighbors(&lsp
->tlv_data
,
705 te_is_neighs
= mt_neighbors
->list
;
707 for (ALL_LIST_ELEMENTS_RO(te_is_neighs
, node
, te_is_neigh
)) {
708 if (!memcmp(te_is_neigh
->neigh_id
, root_sysid
,
712 && !memcmp(te_is_neigh
->neigh_id
, null_sysid
,
715 dist
= cost
+ GET_TE_METRIC(te_is_neigh
);
717 LSP_PSEUDO_ID(te_is_neigh
->neigh_id
)
719 : VTYPE_NONPSEUDO_TE_IS
,
720 (void *)te_is_neigh
->neigh_id
, dist
,
725 if (!pseudo_lsp
&& spftree
->family
== AF_INET
726 && spftree
->mtid
== ISIS_MT_IPV4_UNICAST
) {
727 struct list
*reachs
[] = {lsp
->tlv_data
.ipv4_int_reachs
,
728 lsp
->tlv_data
.ipv4_ext_reachs
};
730 prefix
.family
= AF_INET
;
731 for (unsigned int i
= 0; i
< array_size(reachs
); i
++) {
732 vtype
= (reachs
[i
] == lsp
->tlv_data
.ipv4_int_reachs
)
733 ? VTYPE_IPREACH_INTERNAL
734 : VTYPE_IPREACH_EXTERNAL
;
735 for (ALL_LIST_ELEMENTS_RO(reachs
[i
], node
, ipreach
)) {
736 dist
= cost
+ ipreach
->metrics
.metric_default
;
737 prefix
.u
.prefix4
= ipreach
->prefix
;
738 prefix
.prefixlen
= ip_masklen(ipreach
->mask
);
740 process_N(spftree
, vtype
, (void *)&prefix
, dist
,
746 if (!pseudo_lsp
&& spftree
->family
== AF_INET
) {
747 struct list
*ipv4reachs
= NULL
;
749 if (spftree
->mtid
== ISIS_MT_IPV4_UNICAST
) {
750 ipv4reachs
= lsp
->tlv_data
.te_ipv4_reachs
;
752 struct tlv_mt_ipv4_reachs
*mt_reachs
;
753 mt_reachs
= tlvs_lookup_mt_ipv4_reachs(&lsp
->tlv_data
,
756 ipv4reachs
= mt_reachs
->list
;
759 prefix
.family
= AF_INET
;
760 for (ALL_LIST_ELEMENTS_RO(ipv4reachs
, node
, te_ipv4_reach
)) {
761 assert((te_ipv4_reach
->control
& 0x3F)
764 dist
= cost
+ ntohl(te_ipv4_reach
->te_metric
);
766 newprefix2inaddr(&te_ipv4_reach
->prefix_start
,
767 te_ipv4_reach
->control
);
768 prefix
.prefixlen
= (te_ipv4_reach
->control
& 0x3F);
770 process_N(spftree
, VTYPE_IPREACH_TE
, (void *)&prefix
,
771 dist
, depth
+ 1, parent
);
775 if (!pseudo_lsp
&& spftree
->family
== AF_INET6
) {
776 struct list
*ipv6reachs
= NULL
;
778 if (spftree
->mtid
== ISIS_MT_IPV4_UNICAST
) {
779 ipv6reachs
= lsp
->tlv_data
.ipv6_reachs
;
781 struct tlv_mt_ipv6_reachs
*mt_reachs
;
782 mt_reachs
= tlvs_lookup_mt_ipv6_reachs(&lsp
->tlv_data
,
785 ipv6reachs
= mt_reachs
->list
;
788 prefix
.family
= AF_INET6
;
789 for (ALL_LIST_ELEMENTS_RO(ipv6reachs
, node
, ip6reach
)) {
790 assert(ip6reach
->prefix_len
<= IPV6_MAX_BITLEN
);
792 dist
= cost
+ ntohl(ip6reach
->metric
);
793 vtype
= (ip6reach
->control_info
794 & CTRL_INFO_DISTRIBUTION
)
795 ? VTYPE_IP6REACH_EXTERNAL
796 : VTYPE_IP6REACH_INTERNAL
;
797 prefix
.prefixlen
= ip6reach
->prefix_len
;
798 memcpy(&prefix
.u
.prefix6
.s6_addr
, ip6reach
->prefix
,
799 PSIZE(ip6reach
->prefix_len
));
801 process_N(spftree
, vtype
, (void *)&prefix
, dist
,
806 if (fragnode
== NULL
)
807 fragnode
= listhead(lsp
->lspu
.frags
);
809 fragnode
= listnextnode(fragnode
);
812 lsp
= listgetdata(fragnode
);
819 static int isis_spf_preload_tent(struct isis_spftree
*spftree
,
820 u_char
*root_sysid
, struct isis_vertex
*parent
)
822 struct isis_circuit
*circuit
;
823 struct listnode
*cnode
, *anode
, *ipnode
;
824 struct isis_adjacency
*adj
;
825 struct isis_lsp
*lsp
;
826 struct list
*adj_list
;
828 struct prefix_ipv4
*ipv4
;
829 struct prefix prefix
;
830 int retval
= ISIS_OK
;
831 u_char lsp_id
[ISIS_SYS_ID_LEN
+ 2];
832 static u_char null_lsp_id
[ISIS_SYS_ID_LEN
+ 2];
833 struct prefix_ipv6
*ipv6
;
834 struct isis_circuit_mt_setting
*circuit_mt
;
836 for (ALL_LIST_ELEMENTS_RO(spftree
->area
->circuit_list
, cnode
,
838 circuit_mt
= circuit_lookup_mt_setting(circuit
, spftree
->mtid
);
839 if (circuit_mt
&& !circuit_mt
->enabled
)
841 if (circuit
->state
!= C_STATE_UP
)
843 if (!(circuit
->is_type
& spftree
->level
))
845 if (spftree
->family
== AF_INET
&& !circuit
->ip_router
)
847 if (spftree
->family
== AF_INET6
&& !circuit
->ipv6_router
)
850 * Add IP(v6) addresses of this circuit
852 if (spftree
->family
== AF_INET
) {
853 prefix
.family
= AF_INET
;
854 for (ALL_LIST_ELEMENTS_RO(circuit
->ip_addrs
, ipnode
,
856 prefix
.u
.prefix4
= ipv4
->prefix
;
857 prefix
.prefixlen
= ipv4
->prefixlen
;
859 isis_spf_add_local(spftree
,
860 VTYPE_IPREACH_INTERNAL
,
861 &prefix
, NULL
, 0, parent
);
864 if (spftree
->family
== AF_INET6
) {
865 prefix
.family
= AF_INET6
;
866 for (ALL_LIST_ELEMENTS_RO(circuit
->ipv6_non_link
,
868 prefix
.prefixlen
= ipv6
->prefixlen
;
869 prefix
.u
.prefix6
= ipv6
->prefix
;
871 isis_spf_add_local(spftree
,
872 VTYPE_IP6REACH_INTERNAL
,
873 &prefix
, NULL
, 0, parent
);
876 if (circuit
->circ_type
== CIRCUIT_T_BROADCAST
) {
878 * Add the adjacencies
880 adj_list
= list_new();
881 adjdb
= circuit
->u
.bc
.adjdb
[spftree
->level
- 1];
882 isis_adj_build_up_list(adjdb
, adj_list
);
883 if (listcount(adj_list
) == 0) {
884 list_delete(adj_list
);
885 if (isis
->debugs
& DEBUG_SPF_EVENTS
)
887 "ISIS-Spf: no L%d adjacencies on circuit %s",
889 circuit
->interface
->name
);
892 for (ALL_LIST_ELEMENTS_RO(adj_list
, anode
, adj
)) {
893 if (!adj_has_mt(adj
, spftree
->mtid
))
895 if (spftree
->mtid
== ISIS_MT_IPV4_UNICAST
896 && !speaks(&adj
->nlpids
, spftree
->family
))
898 switch (adj
->sys_type
) {
899 case ISIS_SYSTYPE_ES
:
900 memcpy(lsp_id
, adj
->sysid
,
902 LSP_PSEUDO_ID(lsp_id
) = 0;
904 spftree
, VTYPE_ES
, lsp_id
, adj
,
906 [spftree
->level
- 1],
909 case ISIS_SYSTYPE_IS
:
910 case ISIS_SYSTYPE_L1_IS
:
911 case ISIS_SYSTYPE_L2_IS
:
912 memcpy(lsp_id
, adj
->sysid
,
914 LSP_PSEUDO_ID(lsp_id
) = 0;
915 LSP_FRAGMENT(lsp_id
) = 0;
918 spftree
->area
->oldmetric
920 : VTYPE_NONPSEUDO_TE_IS
,
923 [spftree
->level
- 1],
928 ->lspdb
[spftree
->level
931 || lsp
->lsp_header
->rem_lifetime
934 "ISIS-Spf: No LSP %s found for IS adjacency "
936 rawlspid_print(lsp_id
),
938 circuit
->interface
->name
,
939 circuit
->circuit_id
);
941 case ISIS_SYSTYPE_UNKNOWN
:
944 "isis_spf_preload_tent unknow adj type");
947 list_delete(adj_list
);
951 if (spftree
->level
== 1)
952 memcpy(lsp_id
, circuit
->u
.bc
.l1_desig_is
,
953 ISIS_SYS_ID_LEN
+ 1);
955 memcpy(lsp_id
, circuit
->u
.bc
.l2_desig_is
,
956 ISIS_SYS_ID_LEN
+ 1);
957 /* can happen during DR reboot */
958 if (memcmp(lsp_id
, null_lsp_id
, ISIS_SYS_ID_LEN
+ 1)
960 if (isis
->debugs
& DEBUG_SPF_EVENTS
)
962 "ISIS-Spf: No L%d DR on %s (ID %d)",
964 circuit
->interface
->name
,
965 circuit
->circuit_id
);
968 adj
= isis_adj_lookup(lsp_id
, adjdb
);
969 /* if no adj, we are the dis or error */
970 if (!adj
&& !circuit
->u
.bc
.is_dr
[spftree
->level
- 1]) {
972 "ISIS-Spf: No adjacency found from root "
973 "to L%d DR %s on %s (ID %d)",
974 spftree
->level
, rawlspid_print(lsp_id
),
975 circuit
->interface
->name
,
976 circuit
->circuit_id
);
981 spftree
->area
->lspdb
[spftree
->level
- 1]);
982 if (lsp
== NULL
|| lsp
->lsp_header
->rem_lifetime
== 0) {
984 "ISIS-Spf: No lsp (%p) found from root "
985 "to L%d DR %s on %s (ID %d)",
986 (void *)lsp
, spftree
->level
,
987 rawlspid_print(lsp_id
),
988 circuit
->interface
->name
,
989 circuit
->circuit_id
);
992 isis_spf_process_lsp(
994 circuit
->te_metric
[spftree
->level
- 1], 0,
996 } else if (circuit
->circ_type
== CIRCUIT_T_P2P
) {
997 adj
= circuit
->u
.p2p
.neighbor
;
1000 if (!adj_has_mt(adj
, spftree
->mtid
))
1002 switch (adj
->sys_type
) {
1003 case ISIS_SYSTYPE_ES
:
1004 memcpy(lsp_id
, adj
->sysid
, ISIS_SYS_ID_LEN
);
1005 LSP_PSEUDO_ID(lsp_id
) = 0;
1007 spftree
, VTYPE_ES
, lsp_id
, adj
,
1008 circuit
->te_metric
[spftree
->level
- 1],
1011 case ISIS_SYSTYPE_IS
:
1012 case ISIS_SYSTYPE_L1_IS
:
1013 case ISIS_SYSTYPE_L2_IS
:
1014 memcpy(lsp_id
, adj
->sysid
, ISIS_SYS_ID_LEN
);
1015 LSP_PSEUDO_ID(lsp_id
) = 0;
1016 LSP_FRAGMENT(lsp_id
) = 0;
1017 if (spftree
->mtid
!= ISIS_MT_IPV4_UNICAST
1018 || speaks(&adj
->nlpids
, spftree
->family
))
1021 spftree
->area
->oldmetric
1022 ? VTYPE_NONPSEUDO_IS
1023 : VTYPE_NONPSEUDO_TE_IS
,
1026 [spftree
->level
- 1],
1029 case ISIS_SYSTYPE_UNKNOWN
:
1032 "isis_spf_preload_tent unknown adj type");
1035 } else if (circuit
->circ_type
== CIRCUIT_T_LOOPBACK
) {
1038 zlog_warn("isis_spf_preload_tent unsupported media");
1039 retval
= ISIS_WARNING
;
1047 * The parent(s) for vertex is set when added to TENT list
1048 * now we just put the child pointer(s) in place
1050 static void add_to_paths(struct isis_spftree
*spftree
,
1051 struct isis_vertex
*vertex
)
1053 char buff
[PREFIX2STR_BUFFER
];
1055 if (isis_find_vertex(spftree
->paths
, vertex
->N
.id
, vertex
->type
))
1057 listnode_add(spftree
->paths
, vertex
);
1059 #ifdef EXTREME_DEBUG
1060 zlog_debug("ISIS-Spf: added %s %s %s depth %d dist %d to PATHS",
1061 print_sys_hostname(vertex
->N
.id
), vtype2string(vertex
->type
),
1062 vid2string(vertex
, buff
, sizeof(buff
)), vertex
->depth
,
1064 #endif /* EXTREME_DEBUG */
1066 if (VTYPE_IP(vertex
->type
)) {
1067 if (listcount(vertex
->Adj_N
) > 0)
1068 isis_route_create((struct prefix
*)&vertex
->N
.prefix
,
1069 vertex
->d_N
, vertex
->depth
,
1070 vertex
->Adj_N
, spftree
->area
,
1072 else if (isis
->debugs
& DEBUG_SPF_EVENTS
)
1074 "ISIS-Spf: no adjacencies do not install route for "
1075 "%s depth %d dist %d",
1076 vid2string(vertex
, buff
, sizeof(buff
)),
1077 vertex
->depth
, vertex
->d_N
);
1083 static void init_spt(struct isis_spftree
*spftree
, int mtid
, int level
,
1086 spftree
->tents
->del
= spftree
->paths
->del
=
1087 (void (*)(void *))isis_vertex_del
;
1088 list_delete_all_node(spftree
->tents
);
1089 list_delete_all_node(spftree
->paths
);
1090 spftree
->tents
->del
= spftree
->paths
->del
= NULL
;
1092 spftree
->mtid
= mtid
;
1093 spftree
->level
= level
;
1094 spftree
->family
= family
;
1098 static int isis_run_spf(struct isis_area
*area
, int level
, int family
,
1101 int retval
= ISIS_OK
;
1102 struct listnode
*node
;
1103 struct isis_vertex
*vertex
;
1104 struct isis_vertex
*root_vertex
;
1105 struct isis_spftree
*spftree
= NULL
;
1106 u_char lsp_id
[ISIS_SYS_ID_LEN
+ 2];
1107 struct isis_lsp
*lsp
;
1108 struct route_table
*table
= NULL
;
1109 struct timeval time_now
;
1110 unsigned long long start_time
, end_time
;
1113 /* Get time that can't roll backwards. */
1114 monotime(&time_now
);
1115 start_time
= time_now
.tv_sec
;
1116 start_time
= (start_time
* 1000000) + time_now
.tv_usec
;
1118 if (family
== AF_INET
)
1119 spftree
= area
->spftree
[level
- 1];
1120 else if (family
== AF_INET6
)
1121 spftree
= area
->spftree6
[level
- 1];
1125 /* Make all routes in current route table inactive. */
1126 if (family
== AF_INET
)
1127 table
= area
->route_table
[level
- 1];
1128 else if (family
== AF_INET6
)
1129 table
= area
->route_table6
[level
- 1];
1131 isis_route_invalidate_table(area
, table
);
1133 /* We only support ipv4-unicast and ipv6-unicast as topologies for now
1135 if (family
== AF_INET6
)
1136 mtid
= isis_area_ipv6_topology(area
);
1138 mtid
= ISIS_MT_IPV4_UNICAST
;
1143 init_spt(spftree
, mtid
, level
, family
);
1145 root_vertex
= isis_spf_add_root(spftree
, sysid
);
1147 retval
= isis_spf_preload_tent(spftree
, sysid
, root_vertex
);
1148 if (retval
!= ISIS_OK
) {
1149 zlog_warn("ISIS-Spf: failed to load TENT SPF-root:%s",
1150 print_sys_hostname(sysid
));
1157 if (listcount(spftree
->tents
) == 0) {
1158 zlog_warn("ISIS-Spf: TENT is empty SPF-root:%s",
1159 print_sys_hostname(sysid
));
1163 while (listcount(spftree
->tents
) > 0) {
1164 node
= listhead(spftree
->tents
);
1165 vertex
= listgetdata(node
);
1167 #ifdef EXTREME_DEBUG
1169 "ISIS-Spf: get TENT node %s %s depth %d dist %d to PATHS",
1170 print_sys_hostname(vertex
->N
.id
),
1171 vtype2string(vertex
->type
), vertex
->depth
, vertex
->d_N
);
1172 #endif /* EXTREME_DEBUG */
1174 /* Remove from tent list and add to paths list */
1175 list_delete_node(spftree
->tents
, node
);
1176 add_to_paths(spftree
, vertex
);
1177 if (VTYPE_IS(vertex
->type
)) {
1178 memcpy(lsp_id
, vertex
->N
.id
, ISIS_SYS_ID_LEN
+ 1);
1179 LSP_FRAGMENT(lsp_id
) = 0;
1180 lsp
= lsp_search(lsp_id
, area
->lspdb
[level
- 1]);
1181 if (lsp
&& lsp
->lsp_header
->rem_lifetime
!= 0) {
1182 isis_spf_process_lsp(spftree
, lsp
, vertex
->d_N
,
1183 vertex
->depth
, sysid
,
1186 zlog_warn("ISIS-Spf: No LSP found for %s",
1187 rawlspid_print(lsp_id
));
1193 isis_route_validate(area
);
1194 spftree
->runcount
++;
1195 spftree
->last_run_timestamp
= time(NULL
);
1196 monotime(&time_now
);
1197 end_time
= time_now
.tv_sec
;
1198 end_time
= (end_time
* 1000000) + time_now
.tv_usec
;
1199 spftree
->last_run_duration
= end_time
- start_time
;
1204 static int isis_run_spf_cb(struct thread
*thread
)
1206 struct isis_spf_run
*run
= THREAD_ARG(thread
);
1207 struct isis_area
*area
= run
->area
;
1208 int level
= run
->level
;
1209 int retval
= ISIS_OK
;
1211 XFREE(MTYPE_ISIS_SPF_RUN
, run
);
1212 area
->spf_timer
[level
- 1] = NULL
;
1214 if (!(area
->is_type
& level
)) {
1215 if (isis
->debugs
& DEBUG_SPF_EVENTS
)
1216 zlog_warn("ISIS-SPF (%s) area does not share level",
1218 return ISIS_WARNING
;
1221 if (isis
->debugs
& DEBUG_SPF_EVENTS
)
1222 zlog_debug("ISIS-Spf (%s) L%d SPF needed, periodic SPF",
1223 area
->area_tag
, level
);
1225 if (area
->ip_circuits
)
1226 retval
= isis_run_spf(area
, level
, AF_INET
, isis
->sysid
);
1227 if (area
->ipv6_circuits
)
1228 retval
= isis_run_spf(area
, level
, AF_INET6
, isis
->sysid
);
1233 static struct isis_spf_run
*isis_run_spf_arg(struct isis_area
*area
, int level
)
1235 struct isis_spf_run
*run
= XMALLOC(MTYPE_ISIS_SPF_RUN
, sizeof(*run
));
1243 int isis_spf_schedule(struct isis_area
*area
, int level
)
1245 struct isis_spftree
*spftree
= area
->spftree
[level
- 1];
1246 time_t now
= time(NULL
);
1247 int diff
= now
- spftree
->last_run_timestamp
;
1250 assert(area
->is_type
& level
);
1252 if (isis
->debugs
& DEBUG_SPF_EVENTS
)
1254 "ISIS-Spf (%s) L%d SPF schedule called, lastrun %d sec ago",
1255 area
->area_tag
, level
, diff
);
1257 if (area
->spf_delay_ietf
[level
- 1]) {
1258 /* Need to call schedule function also if spf delay is running
1260 * restart holdoff timer - compare
1261 * draft-ietf-rtgwg-backoff-algo-04 */
1263 spf_backoff_schedule(area
->spf_delay_ietf
[level
- 1]);
1264 if (area
->spf_timer
[level
- 1])
1267 thread_add_timer_msec(master
, isis_run_spf_cb
,
1268 isis_run_spf_arg(area
, level
), delay
,
1269 &area
->spf_timer
[level
- 1]);
1273 if (area
->spf_timer
[level
- 1])
1276 /* wait configured min_spf_interval before doing the SPF */
1277 if (diff
>= area
->min_spf_interval
[level
- 1]) {
1278 int retval
= ISIS_OK
;
1280 if (area
->ip_circuits
)
1282 isis_run_spf(area
, level
, AF_INET
, isis
->sysid
);
1283 if (area
->ipv6_circuits
)
1284 retval
= isis_run_spf(area
, level
, AF_INET6
,
1290 thread_add_timer(master
, isis_run_spf_cb
, isis_run_spf_arg(area
, level
),
1291 area
->min_spf_interval
[level
- 1] - diff
,
1292 &area
->spf_timer
[level
- 1]);
1294 if (isis
->debugs
& DEBUG_SPF_EVENTS
)
1295 zlog_debug("ISIS-Spf (%s) L%d SPF scheduled %d sec from now",
1296 area
->area_tag
, level
,
1297 area
->min_spf_interval
[level
- 1] - diff
);
1302 static void isis_print_paths(struct vty
*vty
, struct list
*paths
,
1305 struct listnode
*node
;
1306 struct listnode
*anode
;
1307 struct isis_vertex
*vertex
;
1308 struct isis_adjacency
*adj
;
1309 char buff
[PREFIX2STR_BUFFER
];
1312 "Vertex Type Metric Next-Hop Interface Parent\n");
1314 for (ALL_LIST_ELEMENTS_RO(paths
, node
, vertex
)) {
1315 if (memcmp(vertex
->N
.id
, root_sysid
, ISIS_SYS_ID_LEN
) == 0) {
1316 vty_out(vty
, "%-20s %-12s %-6s",
1317 print_sys_hostname(root_sysid
), "", "");
1318 vty_out(vty
, "%-30s", "");
1321 vty_out(vty
, "%-20s %-12s %-6u ",
1322 vid2string(vertex
, buff
, sizeof(buff
)),
1323 vtype2string(vertex
->type
), vertex
->d_N
);
1324 for (ALL_LIST_ELEMENTS_RO(vertex
->Adj_N
, anode
, adj
)) {
1329 "%-20s %-12s %-6s ", "",
1332 vty_out(vty
, "%-20s %-9s ",
1333 print_sys_hostname(adj
->sysid
),
1334 adj
->circuit
->interface
->name
);
1339 vty_out(vty
, "%-30s ", "");
1342 /* Print list of parents for the ECMP DAG */
1343 if (listcount(vertex
->parents
) > 0) {
1344 struct listnode
*pnode
;
1345 struct isis_vertex
*pvertex
;
1347 for (ALL_LIST_ELEMENTS_RO(vertex
->parents
, pnode
,
1351 vty_out(vty
, "%-72s", "");
1353 vty_out(vty
, "%s(%d)",
1354 vid2string(pvertex
, buff
, sizeof(buff
)),
1359 vty_out(vty
, " NULL ");
1366 DEFUN (show_isis_topology
,
1367 show_isis_topology_cmd
,
1368 "show isis topology [<level-1|level-2>]",
1370 "IS-IS information\n"
1371 "IS-IS paths to Intermediate Systems\n"
1372 "Paths to all level-1 routers in the area\n"
1373 "Paths to all level-2 routers in the domain\n")
1376 struct listnode
*node
;
1377 struct isis_area
*area
;
1380 levels
= ISIS_LEVEL1
| ISIS_LEVEL2
;
1381 else if (strmatch(argv
[3]->text
, "level-1"))
1382 levels
= ISIS_LEVEL1
;
1384 levels
= ISIS_LEVEL2
;
1386 if (!isis
->area_list
|| isis
->area_list
->count
== 0)
1389 for (ALL_LIST_ELEMENTS_RO(isis
->area_list
, node
, area
)) {
1390 vty_out(vty
, "Area %s:\n",
1391 area
->area_tag
? area
->area_tag
: "null");
1393 for (int level
= ISIS_LEVEL1
; level
<= ISIS_LEVELS
; level
++) {
1394 if ((level
& levels
) == 0)
1397 if (area
->ip_circuits
> 0 && area
->spftree
[level
- 1]
1398 && area
->spftree
[level
- 1]->paths
->count
> 0) {
1400 "IS-IS paths to level-%d routers that speak IP\n",
1403 vty
, area
->spftree
[level
- 1]->paths
,
1407 if (area
->ipv6_circuits
> 0 && area
->spftree6
[level
- 1]
1408 && area
->spftree6
[level
- 1]->paths
->count
> 0) {
1410 "IS-IS paths to level-%d routers that speak IPv6\n",
1413 vty
, area
->spftree6
[level
- 1]->paths
,
1425 void isis_spf_cmds_init()
1427 install_element(VIEW_NODE
, &show_isis_topology_cmd
);