]> git.proxmox.com Git - mirror_frr.git/blob - isisd/isis_spf.c
zebra: Unlock the route node when sending route notifications
[mirror_frr.git] / isisd / isis_spf.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * IS-IS Rout(e)ing protocol - isis_spf.c
4 * The SPT algorithm
5 *
6 * Copyright (C) 2001,2002 Sampo Saaristo
7 * Tampere University of Technology
8 * Institute of Communications Engineering
9 * Copyright (C) 2017 Christian Franke <chris@opensourcerouting.org>
10 */
11
12 #include <zebra.h>
13
14 #include "frrevent.h"
15 #include "linklist.h"
16 #include "vty.h"
17 #include "log.h"
18 #include "command.h"
19 #include "termtable.h"
20 #include "memory.h"
21 #include "prefix.h"
22 #include "filter.h"
23 #include "if.h"
24 #include "hash.h"
25 #include "table.h"
26 #include "spf_backoff.h"
27 #include "srcdest_table.h"
28 #include "vrf.h"
29 #include "lib/json.h"
30
31 #include "isis_errors.h"
32 #include "isis_constants.h"
33 #include "isis_common.h"
34 #include "isis_flags.h"
35 #include "isisd.h"
36 #include "isis_misc.h"
37 #include "isis_adjacency.h"
38 #include "isis_circuit.h"
39 #include "isis_pdu.h"
40 #include "isis_lsp.h"
41 #include "isis_dynhn.h"
42 #include "isis_spf.h"
43 #include "isis_route.h"
44 #include "isis_csm.h"
45 #include "isis_mt.h"
46 #include "isis_tlvs.h"
47 #include "isis_flex_algo.h"
48 #include "isis_zebra.h"
49 #include "fabricd.h"
50 #include "isis_spf_private.h"
51
52 DEFINE_MTYPE_STATIC(ISISD, ISIS_SPFTREE, "ISIS SPFtree");
53 DEFINE_MTYPE_STATIC(ISISD, ISIS_SPF_RUN, "ISIS SPF Run Info");
54 DEFINE_MTYPE_STATIC(ISISD, ISIS_SPF_ADJ, "ISIS SPF Adjacency");
55 DEFINE_MTYPE_STATIC(ISISD, ISIS_VERTEX, "ISIS vertex");
56 DEFINE_MTYPE_STATIC(ISISD, ISIS_VERTEX_ADJ, "ISIS SPF Vertex Adjacency");
57
58 static void spf_adj_list_parse_lsp(struct isis_spftree *spftree,
59 struct list *adj_list, struct isis_lsp *lsp,
60 const uint8_t *pseudo_nodeid,
61 uint32_t pseudo_metric);
62
63 /*
64 * supports the given af ?
65 */
66 static bool speaks(uint8_t *protocols, uint8_t count, int family)
67 {
68 for (uint8_t i = 0; i < count; i++) {
69 if (family == AF_INET && protocols[i] == NLPID_IP)
70 return true;
71 if (family == AF_INET6 && protocols[i] == NLPID_IPV6)
72 return true;
73 }
74 return false;
75 }
76
77 struct isis_spf_run {
78 struct isis_area *area;
79 int level;
80 };
81
82 /* 7.2.7 */
83 static void remove_excess_adjs(struct list *adjs)
84 {
85 struct listnode *node, *excess = NULL;
86 struct isis_vertex_adj *vadj, *candidate = NULL;
87 int comp;
88
89 for (ALL_LIST_ELEMENTS_RO(adjs, node, vadj)) {
90 struct isis_adjacency *adj, *candidate_adj;
91
92 adj = vadj->sadj->adj;
93 assert(adj);
94
95 if (excess == NULL)
96 excess = node;
97 candidate = listgetdata(excess);
98 candidate_adj = candidate->sadj->adj;
99
100 if (candidate_adj->sys_type < adj->sys_type) {
101 excess = node;
102 continue;
103 }
104 if (candidate_adj->sys_type > adj->sys_type)
105 continue;
106
107 comp = memcmp(candidate_adj->sysid, adj->sysid,
108 ISIS_SYS_ID_LEN);
109 if (comp > 0) {
110 excess = node;
111 continue;
112 }
113 if (comp < 0)
114 continue;
115
116 if (candidate_adj->circuit->idx > adj->circuit->idx) {
117 excess = node;
118 continue;
119 }
120
121 if (candidate_adj->circuit->idx < adj->circuit->idx)
122 continue;
123
124 comp = memcmp(candidate_adj->snpa, adj->snpa, ETH_ALEN);
125 if (comp > 0) {
126 excess = node;
127 continue;
128 }
129 }
130
131 list_delete_node(adjs, excess);
132
133 return;
134 }
135
136 const char *vtype2string(enum vertextype vtype)
137 {
138 switch (vtype) {
139 case VTYPE_PSEUDO_IS:
140 return "pseudo_IS";
141 case VTYPE_PSEUDO_TE_IS:
142 return "pseudo_TE-IS";
143 case VTYPE_NONPSEUDO_IS:
144 return "IS";
145 case VTYPE_NONPSEUDO_TE_IS:
146 return "TE-IS";
147 case VTYPE_ES:
148 return "ES";
149 case VTYPE_IPREACH_INTERNAL:
150 return "IP internal";
151 case VTYPE_IPREACH_EXTERNAL:
152 return "IP external";
153 case VTYPE_IPREACH_TE:
154 return "IP TE";
155 case VTYPE_IP6REACH_INTERNAL:
156 return "IP6 internal";
157 case VTYPE_IP6REACH_EXTERNAL:
158 return "IP6 external";
159 default:
160 return "UNKNOWN";
161 }
162 return NULL; /* Not reached */
163 }
164
165 const char *vid2string(const struct isis_vertex *vertex, char *buff, int size)
166 {
167 if (VTYPE_IS(vertex->type) || VTYPE_ES(vertex->type)) {
168 const char *hostname = print_sys_hostname(vertex->N.id);
169 strlcpy(buff, hostname, size);
170 return buff;
171 }
172
173 if (VTYPE_IP(vertex->type)) {
174 srcdest2str(&vertex->N.ip.p.dest, &vertex->N.ip.p.src, buff,
175 size);
176 return buff;
177 }
178
179 return "UNKNOWN";
180 }
181
182 static bool prefix_sid_cmp(const void *value1, const void *value2)
183 {
184 const struct isis_vertex *c1 = value1;
185 const struct isis_vertex *c2 = value2;
186
187 if (CHECK_FLAG(c1->N.ip.sr.sid.flags,
188 ISIS_PREFIX_SID_VALUE | ISIS_PREFIX_SID_LOCAL)
189 != CHECK_FLAG(c2->N.ip.sr.sid.flags,
190 ISIS_PREFIX_SID_VALUE | ISIS_PREFIX_SID_LOCAL))
191 return false;
192
193 return c1->N.ip.sr.sid.value == c2->N.ip.sr.sid.value;
194 }
195
196 static unsigned int prefix_sid_key_make(const void *value)
197 {
198 const struct isis_vertex *vertex = value;
199
200 return jhash_1word(vertex->N.ip.sr.sid.value, 0);
201 }
202
203 struct isis_vertex *isis_spf_prefix_sid_lookup(struct isis_spftree *spftree,
204 struct isis_prefix_sid *psid)
205 {
206 struct isis_vertex lookup = {};
207
208 lookup.N.ip.sr.sid = *psid;
209 return hash_lookup(spftree->prefix_sids, &lookup);
210 }
211
212 void isis_vertex_adj_free(void *arg)
213 {
214 struct isis_vertex_adj *vadj = arg;
215
216 XFREE(MTYPE_ISIS_VERTEX_ADJ, vadj);
217 }
218
219 static struct isis_vertex *isis_vertex_new(struct isis_spftree *spftree,
220 void *id,
221 enum vertextype vtype)
222 {
223 struct isis_vertex *vertex;
224
225 vertex = XCALLOC(MTYPE_ISIS_VERTEX, sizeof(struct isis_vertex));
226
227 isis_vertex_id_init(vertex, id, vtype);
228
229 vertex->Adj_N = list_new();
230 vertex->Adj_N->del = isis_vertex_adj_free;
231 vertex->parents = list_new();
232
233 if (CHECK_FLAG(spftree->flags, F_SPFTREE_HOPCOUNT_METRIC)) {
234 vertex->firsthops = hash_create(isis_vertex_queue_hash_key,
235 isis_vertex_queue_hash_cmp,
236 NULL);
237 }
238
239 return vertex;
240 }
241
242 void isis_vertex_del(struct isis_vertex *vertex)
243 {
244 list_delete(&vertex->Adj_N);
245 list_delete(&vertex->parents);
246 hash_clean_and_free(&vertex->firsthops, NULL);
247
248 memset(vertex, 0, sizeof(struct isis_vertex));
249 XFREE(MTYPE_ISIS_VERTEX, vertex);
250 }
251
252 struct isis_vertex_adj *
253 isis_vertex_adj_add(struct isis_spftree *spftree, struct isis_vertex *vertex,
254 struct list *vadj_list, struct isis_spf_adj *sadj,
255 struct isis_prefix_sid *psid, bool last_hop)
256 {
257 struct isis_vertex_adj *vadj;
258
259 vadj = XCALLOC(MTYPE_ISIS_VERTEX_ADJ, sizeof(*vadj));
260 vadj->sadj = sadj;
261 if (spftree->area->srdb.enabled && psid) {
262 if (vertex->N.ip.sr.present
263 && vertex->N.ip.sr.sid.value != psid->value)
264 zlog_warn(
265 "ISIS-SPF: ignoring different Prefix-SID for route %pFX",
266 &vertex->N.ip.p.dest);
267 else {
268 vadj->sr.sid = *psid;
269 vadj->sr.label = sr_prefix_out_label(
270 spftree->lspdb, vertex->N.ip.p.dest.family,
271 psid, sadj->id, last_hop);
272 if (vadj->sr.label != MPLS_INVALID_LABEL)
273 vadj->sr.present = true;
274 }
275 }
276 listnode_add(vadj_list, vadj);
277
278 return vadj;
279 }
280
281 static void isis_vertex_adj_del(struct isis_vertex *vertex,
282 struct isis_adjacency *adj)
283 {
284 struct isis_vertex_adj *vadj;
285 struct listnode *node, *nextnode;
286
287 if (!vertex)
288 return;
289
290 for (ALL_LIST_ELEMENTS(vertex->Adj_N, node, nextnode, vadj)) {
291 if (vadj->sadj->adj == adj) {
292 listnode_delete(vertex->Adj_N, vadj);
293 isis_vertex_adj_free(vadj);
294 }
295 }
296 return;
297 }
298
299 bool isis_vertex_adj_exists(const struct isis_spftree *spftree,
300 const struct isis_vertex *vertex,
301 const struct isis_spf_adj *sadj)
302 {
303 struct isis_vertex_adj *tmp;
304 struct listnode *node;
305
306 for (ALL_LIST_ELEMENTS_RO(vertex->Adj_N, node, tmp)) {
307 if (CHECK_FLAG(spftree->flags, F_SPFTREE_NO_ADJACENCIES)) {
308 if (memcmp(sadj->id, tmp->sadj->id, sizeof(sadj->id))
309 == 0)
310 return true;
311 } else {
312 if (sadj->adj == tmp->sadj->adj)
313 return true;
314 }
315 }
316
317 return false;
318 }
319
320 static void isis_spf_adj_free(void *arg)
321 {
322 struct isis_spf_adj *sadj = arg;
323
324 XFREE(MTYPE_ISIS_SPF_ADJ, sadj);
325 }
326
327 static void _isis_spftree_init(struct isis_spftree *tree)
328 {
329 isis_vertex_queue_init(&tree->tents, "IS-IS SPF tents", true);
330 isis_vertex_queue_init(&tree->paths, "IS-IS SPF paths", false);
331 tree->route_table = srcdest_table_init();
332 tree->route_table->cleanup = isis_route_node_cleanup;
333 tree->route_table->info = isis_route_table_info_alloc(tree->algorithm);
334 tree->route_table_backup = srcdest_table_init();
335 tree->route_table_backup->info =
336 isis_route_table_info_alloc(tree->algorithm);
337 tree->route_table_backup->cleanup = isis_route_node_cleanup;
338 tree->prefix_sids = hash_create(prefix_sid_key_make, prefix_sid_cmp,
339 "SR Prefix-SID Entries");
340 tree->sadj_list = list_new();
341 tree->sadj_list->del = isis_spf_adj_free;
342 isis_rlfa_list_init(tree);
343 tree->lfa.remote.pc_spftrees = list_new();
344 tree->lfa.remote.pc_spftrees->del = (void (*)(void *))isis_spftree_del;
345 if (tree->type == SPF_TYPE_RLFA || tree->type == SPF_TYPE_TI_LFA) {
346 isis_spf_node_list_init(&tree->lfa.p_space);
347 isis_spf_node_list_init(&tree->lfa.q_space);
348 }
349 }
350
351 struct isis_spftree *
352 isis_spftree_new(struct isis_area *area, struct lspdb_head *lspdb,
353 const uint8_t *sysid, int level, enum spf_tree_id tree_id,
354 enum spf_type type, uint8_t flags, uint8_t algorithm)
355 {
356 struct isis_spftree *tree;
357
358 tree = XCALLOC(MTYPE_ISIS_SPFTREE, sizeof(struct isis_spftree));
359
360 tree->area = area;
361 tree->lspdb = lspdb;
362 tree->last_run_timestamp = 0;
363 tree->last_run_monotime = 0;
364 tree->last_run_duration = 0;
365 tree->runcount = 0;
366 tree->type = type;
367 memcpy(tree->sysid, sysid, ISIS_SYS_ID_LEN);
368 tree->level = level;
369 tree->tree_id = tree_id;
370 tree->family = (tree->tree_id == SPFTREE_IPV4) ? AF_INET : AF_INET6;
371 tree->flags = flags;
372 tree->algorithm = algorithm;
373
374 _isis_spftree_init(tree);
375
376 return tree;
377 }
378
379 static void _isis_spftree_del(struct isis_spftree *spftree)
380 {
381 hash_clean_and_free(&spftree->prefix_sids, NULL);
382 isis_zebra_rlfa_unregister_all(spftree);
383 isis_rlfa_list_clear(spftree);
384 list_delete(&spftree->lfa.remote.pc_spftrees);
385 if (spftree->type == SPF_TYPE_RLFA
386 || spftree->type == SPF_TYPE_TI_LFA) {
387 isis_spf_node_list_clear(&spftree->lfa.q_space);
388 isis_spf_node_list_clear(&spftree->lfa.p_space);
389 }
390 isis_spf_node_list_clear(&spftree->adj_nodes);
391 list_delete(&spftree->sadj_list);
392 isis_vertex_queue_free(&spftree->tents);
393 isis_vertex_queue_free(&spftree->paths);
394 isis_route_table_info_free(spftree->route_table->info);
395 isis_route_table_info_free(spftree->route_table_backup->info);
396 route_table_finish(spftree->route_table);
397 route_table_finish(spftree->route_table_backup);
398 }
399
400 void isis_spftree_del(struct isis_spftree *spftree)
401 {
402 _isis_spftree_del(spftree);
403
404 spftree->route_table = NULL;
405
406 XFREE(MTYPE_ISIS_SPFTREE, spftree);
407 return;
408 }
409
410 #ifndef FABRICD
411 static void isis_spftree_clear(struct isis_spftree *spftree)
412 {
413 _isis_spftree_del(spftree);
414 _isis_spftree_init(spftree);
415 }
416 #endif /* ifndef FABRICD */
417
418 static void isis_spftree_adj_del(struct isis_spftree *spftree,
419 struct isis_adjacency *adj)
420 {
421 struct listnode *node;
422 struct isis_vertex *v;
423 if (!adj)
424 return;
425 assert(!isis_vertex_queue_count(&spftree->tents));
426 for (ALL_QUEUE_ELEMENTS_RO(&spftree->paths, node, v))
427 isis_vertex_adj_del(v, adj);
428 return;
429 }
430
431 void spftree_area_init(struct isis_area *area)
432 {
433 for (int tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++) {
434 for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) {
435 if (!(area->is_type & level))
436 continue;
437 if (area->spftree[tree][level - 1])
438 continue;
439
440 area->spftree[tree][level - 1] = isis_spftree_new(
441 area, &area->lspdb[level - 1],
442 area->isis->sysid, level, tree,
443 SPF_TYPE_FORWARD, 0, SR_ALGORITHM_SPF);
444 }
445 }
446 }
447
448 void spftree_area_del(struct isis_area *area)
449 {
450 for (int tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++) {
451 for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) {
452 if (!(area->is_type & level))
453 continue;
454 if (!area->spftree[tree][level - 1])
455 continue;
456
457 isis_spftree_del(area->spftree[tree][level - 1]);
458 }
459 }
460 }
461
462 static int spf_adj_state_change(struct isis_adjacency *adj)
463 {
464 struct isis_area *area = adj->circuit->area;
465
466 if (adj->adj_state == ISIS_ADJ_UP)
467 return 0;
468
469 /* Remove adjacency from all SPF trees. */
470 for (int tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++) {
471 for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) {
472 if (!(area->is_type & level))
473 continue;
474 if (!area->spftree[tree][level - 1])
475 continue;
476 isis_spftree_adj_del(area->spftree[tree][level - 1],
477 adj);
478 }
479 }
480
481 if (fabricd_spftree(area) != NULL)
482 isis_spftree_adj_del(fabricd_spftree(area), adj);
483
484 return 0;
485 }
486
487 /*
488 * Find the system LSP: returns the LSP in our LSP database
489 * associated with the given system ID.
490 */
491 struct isis_lsp *isis_root_system_lsp(struct lspdb_head *lspdb,
492 const uint8_t *sysid)
493 {
494 struct isis_lsp *lsp;
495 uint8_t lspid[ISIS_SYS_ID_LEN + 2];
496
497 memcpy(lspid, sysid, ISIS_SYS_ID_LEN);
498 LSP_PSEUDO_ID(lspid) = 0;
499 LSP_FRAGMENT(lspid) = 0;
500 lsp = lsp_search(lspdb, lspid);
501 if (lsp && lsp->hdr.rem_lifetime != 0)
502 return lsp;
503 return NULL;
504 }
505
506 /*
507 * Add this IS to the root of SPT
508 */
509 static struct isis_vertex *isis_spf_add_root(struct isis_spftree *spftree)
510 {
511 struct isis_vertex *vertex;
512 #ifdef EXTREME_DEBUG
513 char buff[VID2STR_BUFFER];
514 #endif /* EXTREME_DEBUG */
515
516 vertex = isis_vertex_new(spftree, spftree->sysid,
517 spftree->area->oldmetric
518 ? VTYPE_NONPSEUDO_IS
519 : VTYPE_NONPSEUDO_TE_IS);
520 isis_vertex_queue_append(&spftree->paths, vertex);
521
522 #ifdef EXTREME_DEBUG
523 if (IS_DEBUG_SPF_EVENTS)
524 zlog_debug(
525 "ISIS-SPF: A:%hhu added this IS %s %s depth %d dist %d to PATHS",
526 spftree->algorithm, vtype2string(vertex->type),
527 vid2string(vertex, buff, sizeof(buff)), vertex->depth,
528 vertex->d_N);
529 #endif /* EXTREME_DEBUG */
530
531 return vertex;
532 }
533
534 static void vertex_add_parent_firsthop(struct hash_bucket *bucket, void *arg)
535 {
536 struct isis_vertex *vertex = arg;
537 struct isis_vertex *hop = bucket->data;
538
539 (void)hash_get(vertex->firsthops, hop, hash_alloc_intern);
540 }
541
542 static void vertex_update_firsthops(struct isis_vertex *vertex,
543 struct isis_vertex *parent)
544 {
545 if (vertex->d_N <= 2)
546 (void)hash_get(vertex->firsthops, vertex, hash_alloc_intern);
547
548 if (vertex->d_N < 2 || !parent)
549 return;
550
551 hash_iterate(parent->firsthops, vertex_add_parent_firsthop, vertex);
552 }
553
554 /*
555 * Add a vertex to TENT sorted by cost and by vertextype on tie break situation
556 */
557 static struct isis_vertex *
558 isis_spf_add2tent(struct isis_spftree *spftree, enum vertextype vtype, void *id,
559 uint32_t cost, int depth, struct isis_spf_adj *sadj,
560 struct isis_prefix_sid *psid, struct isis_vertex *parent)
561 {
562 struct isis_vertex *vertex;
563 struct listnode *node;
564 bool last_hop;
565 char buff[VID2STR_BUFFER];
566
567 vertex = isis_find_vertex(&spftree->paths, id, vtype);
568 if (vertex != NULL) {
569 zlog_err(
570 "%s: vertex %s of type %s already in PATH; check for sysId collisions with established neighbors",
571 __func__, vid2string(vertex, buff, sizeof(buff)),
572 vtype2string(vertex->type));
573 return NULL;
574 }
575 vertex = isis_find_vertex(&spftree->tents, id, vtype);
576 if (vertex != NULL) {
577 zlog_err(
578 "%s: vertex %s of type %s already in TENT; check for sysId collisions with established neighbors",
579 __func__, vid2string(vertex, buff, sizeof(buff)),
580 vtype2string(vertex->type));
581 return NULL;
582 }
583
584 vertex = isis_vertex_new(spftree, id, vtype);
585 vertex->d_N = cost;
586 vertex->depth = depth;
587 if (VTYPE_IP(vtype) && spftree->area->srdb.enabled && psid) {
588 struct isis_area *area = spftree->area;
589 struct isis_vertex *vertex_psid;
590
591 /*
592 * Check if the Prefix-SID is already in use by another prefix.
593 */
594 vertex_psid = isis_spf_prefix_sid_lookup(spftree, psid);
595 if (vertex_psid
596 && !prefix_same(&vertex_psid->N.ip.p.dest,
597 &vertex->N.ip.p.dest)) {
598 flog_warn(
599 EC_ISIS_SID_COLLISION,
600 "ISIS-Sr (%s): collision detected, prefixes %pFX and %pFX share the same SID %s (%u)",
601 area->area_tag, &vertex->N.ip.p.dest,
602 &vertex_psid->N.ip.p.dest,
603 CHECK_FLAG(psid->flags, ISIS_PREFIX_SID_VALUE)
604 ? "label"
605 : "index",
606 psid->value);
607 psid = NULL;
608 } else {
609 bool local;
610
611 local = (vertex->depth == 1);
612 vertex->N.ip.sr.sid = *psid;
613 vertex->N.ip.sr.label =
614 sr_prefix_in_label(area, psid, local);
615 vertex->N.ip.sr.algorithm = psid->algorithm;
616
617 if (vertex->N.ip.sr.label != MPLS_INVALID_LABEL)
618 vertex->N.ip.sr.present = true;
619
620 #ifndef FABRICD
621 if (flex_algo_id_valid(spftree->algorithm) &&
622 !isis_flex_algo_elected_supported(
623 spftree->algorithm, spftree->area)) {
624 vertex->N.ip.sr.present = false;
625 vertex->N.ip.sr.label = MPLS_INVALID_LABEL;
626 }
627 #endif /* ifndef FABRICD */
628
629 (void)hash_get(spftree->prefix_sids, vertex,
630 hash_alloc_intern);
631 }
632 }
633
634 if (parent) {
635 listnode_add(vertex->parents, parent);
636 }
637
638 if (CHECK_FLAG(spftree->flags, F_SPFTREE_HOPCOUNT_METRIC))
639 vertex_update_firsthops(vertex, parent);
640
641 last_hop = (vertex->depth == 2);
642 if (parent && parent->Adj_N && listcount(parent->Adj_N) > 0) {
643 struct isis_vertex_adj *parent_vadj;
644
645 for (ALL_LIST_ELEMENTS_RO(parent->Adj_N, node, parent_vadj))
646 isis_vertex_adj_add(spftree, vertex, vertex->Adj_N,
647 parent_vadj->sadj, psid, last_hop);
648 } else if (sadj) {
649 isis_vertex_adj_add(spftree, vertex, vertex->Adj_N, sadj, psid,
650 last_hop);
651 }
652
653 #ifdef EXTREME_DEBUG
654 if (IS_DEBUG_SPF_EVENTS)
655 zlog_debug(
656 "ISIS-SPF: A:%hhu add to TENT %s %s %s depth %d dist %d adjcount %d",
657 spftree->algorithm, print_sys_hostname(vertex->N.id),
658 vtype2string(vertex->type),
659 vid2string(vertex, buff, sizeof(buff)), vertex->depth,
660 vertex->d_N, listcount(vertex->Adj_N));
661 #endif /* EXTREME_DEBUG */
662
663 isis_vertex_queue_insert(&spftree->tents, vertex);
664 return vertex;
665 }
666
667 static void isis_spf_add_local(struct isis_spftree *spftree,
668 enum vertextype vtype, void *id,
669 struct isis_spf_adj *sadj, uint32_t cost,
670 struct isis_prefix_sid *psid,
671 struct isis_vertex *parent)
672 {
673 struct isis_vertex *vertex;
674
675 vertex = isis_find_vertex(&spftree->tents, id, vtype);
676
677 if (vertex) {
678 /* C.2.5 c) */
679 if (vertex->d_N == cost) {
680 if (sadj) {
681 bool last_hop = (vertex->depth == 2);
682
683 isis_vertex_adj_add(spftree, vertex,
684 vertex->Adj_N, sadj, psid,
685 last_hop);
686 }
687 /* d) */
688 if (!CHECK_FLAG(spftree->flags,
689 F_SPFTREE_NO_ADJACENCIES)
690 && listcount(vertex->Adj_N) > ISIS_MAX_PATH_SPLITS)
691 remove_excess_adjs(vertex->Adj_N);
692 if (parent && (listnode_lookup(vertex->parents, parent)
693 == NULL))
694 listnode_add(vertex->parents, parent);
695 return;
696 } else if (vertex->d_N < cost) {
697 /* e) do nothing */
698 return;
699 } else { /* vertex->d_N > cost */
700 /* f) */
701 isis_vertex_queue_delete(&spftree->tents, vertex);
702 isis_vertex_del(vertex);
703 }
704 }
705
706 isis_spf_add2tent(spftree, vtype, id, cost, 1, sadj, psid, parent);
707 return;
708 }
709
710 static void process_N(struct isis_spftree *spftree, enum vertextype vtype,
711 void *id, uint32_t dist, uint16_t depth,
712 struct isis_prefix_sid *psid, struct isis_vertex *parent)
713 {
714 struct isis_vertex *vertex;
715 #ifdef EXTREME_DEBUG
716 char buff[VID2STR_BUFFER];
717 #endif
718
719 assert(spftree && parent);
720
721 if (CHECK_FLAG(spftree->flags, F_SPFTREE_HOPCOUNT_METRIC)
722 && !VTYPE_IS(vtype))
723 return;
724
725 struct prefix_pair p;
726 if (vtype >= VTYPE_IPREACH_INTERNAL) {
727 memcpy(&p, id, sizeof(p));
728 apply_mask(&p.dest);
729 apply_mask(&p.src);
730 id = &p;
731 }
732
733 /* RFC3787 section 5.1 */
734 if (spftree->area->newmetric == 1) {
735 if (dist > MAX_WIDE_PATH_METRIC)
736 return;
737 }
738 /* C.2.6 b) */
739 else if (spftree->area->oldmetric == 1) {
740 if (dist > MAX_NARROW_PATH_METRIC)
741 return;
742 }
743
744 /* c) */
745 vertex = isis_find_vertex(&spftree->paths, id, vtype);
746 if (vertex) {
747 #ifdef EXTREME_DEBUG
748 if (IS_DEBUG_SPF_EVENTS)
749 zlog_debug(
750 "ISIS-SPF: A:%hhu process_N %s %s %s dist %d already found from PATH",
751 spftree->algorithm,
752 print_sys_hostname(vertex->N.id),
753 vtype2string(vtype),
754 vid2string(vertex, buff, sizeof(buff)), dist);
755 #endif /* EXTREME_DEBUG */
756 assert(dist >= vertex->d_N);
757 return;
758 }
759
760 vertex = isis_find_vertex(&spftree->tents, id, vtype);
761 /* d) */
762 if (vertex) {
763 /* 1) */
764 #ifdef EXTREME_DEBUG
765 if (IS_DEBUG_SPF_EVENTS)
766 zlog_debug(
767 "ISIS-SPF: A:%hhu process_N %s %s %s dist %d parent %s adjcount %d",
768 spftree->algorithm,
769 print_sys_hostname(vertex->N.id),
770 vtype2string(vtype),
771 vid2string(vertex, buff, sizeof(buff)), dist,
772 (parent ? print_sys_hostname(parent->N.id)
773 : "null"),
774 (parent ? listcount(parent->Adj_N) : 0));
775 #endif /* EXTREME_DEBUG */
776 if (vertex->d_N == dist) {
777 struct listnode *node;
778 struct isis_vertex_adj *parent_vadj;
779 for (ALL_LIST_ELEMENTS_RO(parent->Adj_N, node,
780 parent_vadj))
781 if (!isis_vertex_adj_exists(
782 spftree, vertex,
783 parent_vadj->sadj)) {
784 bool last_hop = (vertex->depth == 2);
785
786 isis_vertex_adj_add(spftree, vertex,
787 vertex->Adj_N,
788 parent_vadj->sadj,
789 psid, last_hop);
790 }
791 if (CHECK_FLAG(spftree->flags,
792 F_SPFTREE_HOPCOUNT_METRIC))
793 vertex_update_firsthops(vertex, parent);
794 /* 2) */
795 if (!CHECK_FLAG(spftree->flags,
796 F_SPFTREE_NO_ADJACENCIES)
797 && listcount(vertex->Adj_N) > ISIS_MAX_PATH_SPLITS)
798 remove_excess_adjs(vertex->Adj_N);
799 if (listnode_lookup(vertex->parents, parent) == NULL)
800 listnode_add(vertex->parents, parent);
801 return;
802 } else if (vertex->d_N < dist) {
803 return;
804 /* 4) */
805 } else {
806 isis_vertex_queue_delete(&spftree->tents, vertex);
807 isis_vertex_del(vertex);
808 }
809 }
810
811 #ifdef EXTREME_DEBUG
812 if (IS_DEBUG_SPF_EVENTS)
813 zlog_debug(
814 "ISIS-SPF: A:%hhu process_N add2tent %s %s dist %d parent %s",
815 spftree->algorithm, print_sys_hostname(id),
816 vtype2string(vtype), dist,
817 (parent ? print_sys_hostname(parent->N.id) : "null"));
818 #endif /* EXTREME_DEBUG */
819
820 isis_spf_add2tent(spftree, vtype, id, dist, depth, NULL, psid, parent);
821 return;
822 }
823
824 /*
825 * C.2.6 Step 1
826 */
827 static int isis_spf_process_lsp(struct isis_spftree *spftree,
828 struct isis_lsp *lsp, uint32_t cost,
829 uint16_t depth, uint8_t *root_sysid,
830 struct isis_vertex *parent)
831 {
832 bool pseudo_lsp = LSP_PSEUDO_ID(lsp->hdr.lsp_id);
833 struct listnode *fragnode = NULL;
834 uint32_t dist;
835 enum vertextype vtype;
836 static const uint8_t null_sysid[ISIS_SYS_ID_LEN];
837 struct isis_mt_router_info *mt_router_info = NULL;
838 struct prefix_pair ip_info;
839 bool has_valid_psid;
840
841 if (isis_lfa_excise_node_check(spftree, lsp->hdr.lsp_id)) {
842 if (IS_DEBUG_LFA)
843 zlog_debug("ISIS-LFA: excising node %s",
844 print_sys_hostname(lsp->hdr.lsp_id));
845 return ISIS_OK;
846 }
847
848 if (!lsp->tlvs)
849 return ISIS_OK;
850
851 if (spftree->mtid != ISIS_MT_IPV4_UNICAST)
852 mt_router_info = isis_tlvs_lookup_mt_router_info(lsp->tlvs,
853 spftree->mtid);
854
855 if (!pseudo_lsp && (spftree->mtid == ISIS_MT_IPV4_UNICAST
856 && !speaks(lsp->tlvs->protocols_supported.protocols,
857 lsp->tlvs->protocols_supported.count,
858 spftree->family))
859 && !mt_router_info)
860 return ISIS_OK;
861
862 /* RFC3787 section 4 SHOULD ignore overload bit in pseudo LSPs */
863 bool no_overload = (pseudo_lsp
864 || (spftree->mtid == ISIS_MT_IPV4_UNICAST
865 && !ISIS_MASK_LSP_OL_BIT(lsp->hdr.lsp_bits))
866 || (mt_router_info && !mt_router_info->overload));
867
868 lspfragloop:
869 if (lsp->hdr.seqno == 0) {
870 zlog_warn("%s: lsp with 0 seq_num - ignore", __func__);
871 return ISIS_WARNING;
872 }
873
874 #ifdef EXTREME_DEBUG
875 if (IS_DEBUG_SPF_EVENTS)
876 zlog_debug("ISIS-SPF: A:%hhu process_lsp %s",
877 spftree->algorithm,
878 print_sys_hostname(lsp->hdr.lsp_id));
879 #endif /* EXTREME_DEBUG */
880
881 if (no_overload) {
882 if ((pseudo_lsp || spftree->mtid == ISIS_MT_IPV4_UNICAST)
883 && spftree->area->oldmetric) {
884 struct isis_oldstyle_reach *r;
885 for (r = (struct isis_oldstyle_reach *)
886 lsp->tlvs->oldstyle_reach.head;
887 r; r = r->next) {
888 if (fabricd)
889 continue;
890
891 /* C.2.6 a) */
892 /* Two way connectivity */
893 if (!LSP_PSEUDO_ID(r->id)
894 && !memcmp(r->id, root_sysid,
895 ISIS_SYS_ID_LEN))
896 continue;
897 if (!pseudo_lsp
898 && !memcmp(r->id, null_sysid,
899 ISIS_SYS_ID_LEN))
900 continue;
901 dist = cost + r->metric;
902 process_N(spftree,
903 LSP_PSEUDO_ID(r->id)
904 ? VTYPE_PSEUDO_IS
905 : VTYPE_NONPSEUDO_IS,
906 (void *)r->id, dist, depth + 1, NULL,
907 parent);
908 }
909 }
910
911 if (spftree->area->newmetric) {
912 struct isis_item_list *te_neighs = NULL;
913 if (pseudo_lsp || spftree->mtid == ISIS_MT_IPV4_UNICAST)
914 te_neighs = &lsp->tlvs->extended_reach;
915 else
916 te_neighs = isis_lookup_mt_items(
917 &lsp->tlvs->mt_reach, spftree->mtid);
918
919 struct isis_extended_reach *er;
920 for (er = te_neighs ? (struct isis_extended_reach *)
921 te_neighs->head
922 : NULL;
923 er; er = er->next) {
924 /* C.2.6 a) */
925 /* Two way connectivity */
926 if (!LSP_PSEUDO_ID(er->id)
927 && !memcmp(er->id, root_sysid,
928 ISIS_SYS_ID_LEN))
929 continue;
930 if (!pseudo_lsp
931 && !memcmp(er->id, null_sysid,
932 ISIS_SYS_ID_LEN))
933 continue;
934 #ifndef FABRICD
935
936 if (flex_algo_id_valid(spftree->algorithm) &&
937 (!sr_algorithm_participated(
938 lsp, spftree->algorithm) ||
939 isis_flex_algo_constraint_drop(spftree,
940 lsp, er)))
941 continue;
942 #endif /* ifndef FABRICD */
943
944 dist = cost
945 + (CHECK_FLAG(spftree->flags,
946 F_SPFTREE_HOPCOUNT_METRIC)
947 ? 1
948 : er->metric);
949 process_N(spftree,
950 LSP_PSEUDO_ID(er->id)
951 ? VTYPE_PSEUDO_TE_IS
952 : VTYPE_NONPSEUDO_TE_IS,
953 (void *)er->id, dist, depth + 1, NULL,
954 parent);
955 }
956 }
957 }
958
959 if (!fabricd && !pseudo_lsp && spftree->family == AF_INET
960 && spftree->mtid == ISIS_MT_IPV4_UNICAST
961 && spftree->area->oldmetric) {
962 struct isis_item_list *reachs[] = {
963 &lsp->tlvs->oldstyle_ip_reach,
964 &lsp->tlvs->oldstyle_ip_reach_ext};
965
966 for (unsigned int i = 0; i < array_size(reachs); i++) {
967 vtype = i ? VTYPE_IPREACH_EXTERNAL
968 : VTYPE_IPREACH_INTERNAL;
969
970 memset(&ip_info, 0, sizeof(ip_info));
971 ip_info.dest.family = AF_INET;
972
973 struct isis_oldstyle_ip_reach *r;
974 for (r = (struct isis_oldstyle_ip_reach *)reachs[i]
975 ->head;
976 r; r = r->next) {
977 dist = cost + r->metric;
978 ip_info.dest.u.prefix4 = r->prefix.prefix;
979 ip_info.dest.prefixlen = r->prefix.prefixlen;
980 process_N(spftree, vtype, &ip_info,
981 dist, depth + 1, NULL, parent);
982 }
983 }
984 }
985
986 /* we can skip all the rest if we're using metric style narrow */
987 if (!spftree->area->newmetric)
988 goto end;
989
990 if (!pseudo_lsp && spftree->family == AF_INET) {
991 struct isis_item_list *ipv4_reachs;
992 if (spftree->mtid == ISIS_MT_IPV4_UNICAST)
993 ipv4_reachs = &lsp->tlvs->extended_ip_reach;
994 else
995 ipv4_reachs = isis_lookup_mt_items(
996 &lsp->tlvs->mt_ip_reach, spftree->mtid);
997
998 memset(&ip_info, 0, sizeof(ip_info));
999 ip_info.dest.family = AF_INET;
1000
1001 struct isis_extended_ip_reach *r;
1002 for (r = ipv4_reachs
1003 ? (struct isis_extended_ip_reach *)
1004 ipv4_reachs->head
1005 : NULL;
1006 r; r = r->next) {
1007 dist = cost + r->metric;
1008 ip_info.dest.u.prefix4 = r->prefix.prefix;
1009 ip_info.dest.prefixlen = r->prefix.prefixlen;
1010
1011 /* Parse list of Prefix-SID subTLVs if SR is enabled */
1012 has_valid_psid = false;
1013 if (spftree->area->srdb.enabled && r->subtlvs) {
1014 for (struct isis_item *i =
1015 r->subtlvs->prefix_sids.head;
1016 i; i = i->next) {
1017 struct isis_prefix_sid *psid =
1018 (struct isis_prefix_sid *)i;
1019
1020 if (psid->algorithm !=
1021 spftree->algorithm)
1022 continue;
1023
1024 #ifndef FABRICD
1025 if (flex_algo_id_valid(
1026 spftree->algorithm) &&
1027 (!sr_algorithm_participated(
1028 lsp, spftree->algorithm) ||
1029 !isis_flex_algo_elected_supported(
1030 spftree->algorithm,
1031 spftree->area)))
1032 continue;
1033 #endif /* ifndef FABRICD */
1034
1035 has_valid_psid = true;
1036 process_N(spftree, VTYPE_IPREACH_TE,
1037 &ip_info, dist, depth + 1,
1038 psid, parent);
1039 /*
1040 * Stop the Prefix-SID iteration since
1041 * we only support the SPF algorithm for
1042 * now.
1043 */
1044 break;
1045 }
1046 }
1047 if (!has_valid_psid)
1048 process_N(spftree, VTYPE_IPREACH_TE, &ip_info,
1049 dist, depth + 1, NULL, parent);
1050 }
1051 }
1052
1053 if (!pseudo_lsp && spftree->family == AF_INET6) {
1054 struct isis_item_list *ipv6_reachs;
1055 if (spftree->mtid == ISIS_MT_IPV4_UNICAST)
1056 ipv6_reachs = &lsp->tlvs->ipv6_reach;
1057 else
1058 ipv6_reachs = isis_lookup_mt_items(
1059 &lsp->tlvs->mt_ipv6_reach, spftree->mtid);
1060
1061 struct isis_ipv6_reach *r;
1062 for (r = ipv6_reachs
1063 ? (struct isis_ipv6_reach *)ipv6_reachs->head
1064 : NULL;
1065 r; r = r->next) {
1066 dist = cost + r->metric;
1067 vtype = r->external ? VTYPE_IP6REACH_EXTERNAL
1068 : VTYPE_IP6REACH_INTERNAL;
1069 memset(&ip_info, 0, sizeof(ip_info));
1070 ip_info.dest.family = AF_INET6;
1071 ip_info.dest.u.prefix6 = r->prefix.prefix;
1072 ip_info.dest.prefixlen = r->prefix.prefixlen;
1073
1074 if (spftree->area->srdb.enabled && r->subtlvs &&
1075 r->subtlvs->source_prefix &&
1076 r->subtlvs->source_prefix->prefixlen) {
1077 if (spftree->tree_id != SPFTREE_DSTSRC) {
1078 char buff[VID2STR_BUFFER];
1079 zlog_warn("Ignoring dest-src route %s in non dest-src topology",
1080 srcdest2str(
1081 &ip_info.dest,
1082 r->subtlvs->source_prefix,
1083 buff, sizeof(buff)
1084 )
1085 );
1086 continue;
1087 }
1088 ip_info.src = *r->subtlvs->source_prefix;
1089 }
1090
1091 /* Parse list of Prefix-SID subTLVs */
1092 has_valid_psid = false;
1093 if (r->subtlvs) {
1094 for (struct isis_item *i =
1095 r->subtlvs->prefix_sids.head;
1096 i; i = i->next) {
1097 struct isis_prefix_sid *psid =
1098 (struct isis_prefix_sid *)i;
1099
1100 if (psid->algorithm !=
1101 spftree->algorithm)
1102 continue;
1103
1104 #ifndef FABRICD
1105 if (flex_algo_id_valid(
1106 spftree->algorithm) &&
1107 (!sr_algorithm_participated(
1108 lsp, spftree->algorithm) ||
1109 !isis_flex_algo_elected_supported(
1110 spftree->algorithm,
1111 spftree->area)))
1112 continue;
1113 #endif /* ifndef FABRICD */
1114
1115 has_valid_psid = true;
1116 process_N(spftree, vtype, &ip_info,
1117 dist, depth + 1, psid,
1118 parent);
1119 /*
1120 * Stop the Prefix-SID iteration since
1121 * we only support the SPF algorithm for
1122 * now.
1123 */
1124 break;
1125 }
1126 }
1127 if (!has_valid_psid)
1128 process_N(spftree, vtype, &ip_info, dist,
1129 depth + 1, NULL, parent);
1130 }
1131 }
1132
1133 end:
1134
1135 /* if attach bit set in LSP, attached-bit receive ignore is
1136 * not configured, we are a level-1 area and we have no other
1137 * level-2 | level1-2 areas then add a default route toward
1138 * this neighbor
1139 */
1140 if ((lsp->hdr.lsp_bits & LSPBIT_ATT) == LSPBIT_ATT
1141 && !spftree->area->attached_bit_rcv_ignore
1142 && (spftree->area->is_type & IS_LEVEL_1)
1143 && !isis_level2_adj_up(spftree->area)) {
1144 struct prefix_pair ip_info = { {0} };
1145 if (IS_DEBUG_RTE_EVENTS)
1146 zlog_debug("ISIS-Spf (%pLS): add default %s route",
1147 lsp->hdr.lsp_id,
1148 spftree->family == AF_INET ? "ipv4"
1149 : "ipv6");
1150
1151 if (spftree->family == AF_INET) {
1152 ip_info.dest.family = AF_INET;
1153 vtype = VTYPE_IPREACH_INTERNAL;
1154 } else {
1155 ip_info.dest.family = AF_INET6;
1156 vtype = VTYPE_IP6REACH_INTERNAL;
1157 }
1158 process_N(spftree, vtype, &ip_info, cost, depth + 1, NULL,
1159 parent);
1160 }
1161
1162 if (fragnode == NULL)
1163 fragnode = listhead(lsp->lspu.frags);
1164 else
1165 fragnode = listnextnode(fragnode);
1166
1167 if (fragnode) {
1168 lsp = listgetdata(fragnode);
1169 goto lspfragloop;
1170 }
1171
1172 return ISIS_OK;
1173 }
1174
1175 static struct isis_adjacency *adj_find(struct list *adj_list, const uint8_t *id,
1176 int level, uint16_t mtid, int family)
1177 {
1178 struct isis_adjacency *adj;
1179 struct listnode *node;
1180
1181 for (ALL_LIST_ELEMENTS_RO(adj_list, node, adj)) {
1182 if (!(adj->level & level))
1183 continue;
1184 if (memcmp(adj->sysid, id, ISIS_SYS_ID_LEN) != 0)
1185 continue;
1186 if (adj->adj_state != ISIS_ADJ_UP)
1187 continue;
1188 if (!adj_has_mt(adj, mtid))
1189 continue;
1190 if (mtid == ISIS_MT_IPV4_UNICAST
1191 && !speaks(adj->nlpids.nlpids, adj->nlpids.count, family))
1192 continue;
1193 return adj;
1194 }
1195
1196 return NULL;
1197 }
1198
1199 struct spf_preload_tent_ip_reach_args {
1200 struct isis_spftree *spftree;
1201 struct isis_vertex *parent;
1202 };
1203
1204 static int isis_spf_preload_tent_ip_reach_cb(const struct prefix *prefix,
1205 uint32_t metric, bool external,
1206 struct isis_subtlvs *subtlvs,
1207 void *arg)
1208 {
1209 struct spf_preload_tent_ip_reach_args *args = arg;
1210 struct isis_spftree *spftree = args->spftree;
1211 struct isis_vertex *parent = args->parent;
1212 struct prefix_pair ip_info;
1213 enum vertextype vtype;
1214 bool has_valid_psid = false;
1215
1216 if (external)
1217 return LSP_ITER_CONTINUE;
1218
1219 assert(spftree->family == prefix->family);
1220 memset(&ip_info, 0, sizeof(ip_info));
1221 prefix_copy(&ip_info.dest, prefix);
1222 apply_mask(&ip_info.dest);
1223
1224 if (prefix->family == AF_INET)
1225 vtype = VTYPE_IPREACH_INTERNAL;
1226 else
1227 vtype = VTYPE_IP6REACH_INTERNAL;
1228
1229 /* Parse list of Prefix-SID subTLVs if SR is enabled */
1230 if (spftree->area->srdb.enabled && subtlvs) {
1231 for (struct isis_item *i = subtlvs->prefix_sids.head; i;
1232 i = i->next) {
1233 struct isis_prefix_sid *psid =
1234 (struct isis_prefix_sid *)i;
1235
1236 if (psid->algorithm != spftree->algorithm)
1237 continue;
1238
1239 has_valid_psid = true;
1240 isis_spf_add_local(spftree, vtype, &ip_info, NULL, 0,
1241 psid, parent);
1242
1243 /*
1244 * Stop the Prefix-SID iteration since we only support
1245 * the SPF algorithm for now.
1246 */
1247 break;
1248 }
1249 }
1250 if (!has_valid_psid)
1251 isis_spf_add_local(spftree, vtype, &ip_info, NULL, 0, NULL,
1252 parent);
1253
1254 return LSP_ITER_CONTINUE;
1255 }
1256
1257 static void isis_spf_preload_tent(struct isis_spftree *spftree,
1258 uint8_t *root_sysid,
1259 struct isis_lsp *root_lsp,
1260 struct isis_vertex *parent)
1261 {
1262 struct spf_preload_tent_ip_reach_args ip_reach_args;
1263 struct isis_spf_adj *sadj;
1264 struct listnode *node;
1265
1266 if (!CHECK_FLAG(spftree->flags, F_SPFTREE_HOPCOUNT_METRIC)) {
1267 ip_reach_args.spftree = spftree;
1268 ip_reach_args.parent = parent;
1269 isis_lsp_iterate_ip_reach(
1270 root_lsp, spftree->family, spftree->mtid,
1271 isis_spf_preload_tent_ip_reach_cb, &ip_reach_args);
1272 }
1273
1274 /* Iterate over adjacencies. */
1275 for (ALL_LIST_ELEMENTS_RO(spftree->sadj_list, node, sadj)) {
1276 const uint8_t *adj_id;
1277 uint32_t metric;
1278
1279 if (CHECK_FLAG(sadj->flags, F_ISIS_SPF_ADJ_BROADCAST))
1280 adj_id = sadj->lan.desig_is_id;
1281 else
1282 adj_id = sadj->id;
1283
1284 if (isis_lfa_excise_adj_check(spftree, adj_id)) {
1285 if (IS_DEBUG_LFA)
1286 zlog_debug("ISIS-SPF: excising adjacency %pPN",
1287 sadj->id);
1288 continue;
1289 }
1290
1291 metric = CHECK_FLAG(spftree->flags, F_SPFTREE_HOPCOUNT_METRIC)
1292 ? 1
1293 : sadj->metric;
1294 if (!LSP_PSEUDO_ID(sadj->id)) {
1295 isis_spf_add_local(spftree,
1296 CHECK_FLAG(sadj->flags,
1297 F_ISIS_SPF_ADJ_OLDMETRIC)
1298 ? VTYPE_NONPSEUDO_IS
1299 : VTYPE_NONPSEUDO_TE_IS,
1300 sadj->id, sadj, metric, NULL,
1301 parent);
1302 } else if (sadj->lsp) {
1303 isis_spf_process_lsp(spftree, sadj->lsp, metric, 0,
1304 spftree->sysid, parent);
1305 }
1306 }
1307 }
1308
1309 struct spf_adj_find_reverse_metric_args {
1310 const uint8_t *id_self;
1311 uint32_t reverse_metric;
1312 };
1313
1314 static int spf_adj_find_reverse_metric_cb(const uint8_t *id, uint32_t metric,
1315 bool oldmetric,
1316 struct isis_ext_subtlvs *subtlvs,
1317 void *arg)
1318 {
1319 struct spf_adj_find_reverse_metric_args *args = arg;
1320
1321 if (memcmp(id, args->id_self, ISIS_SYS_ID_LEN))
1322 return LSP_ITER_CONTINUE;
1323
1324 args->reverse_metric = metric;
1325
1326 return LSP_ITER_STOP;
1327 }
1328
1329 /*
1330 * Change all SPF adjacencies to use the link cost in the direction from the
1331 * next hop back towards root in place of the link cost in the direction away
1332 * from root towards the next hop.
1333 */
1334 static void spf_adj_get_reverse_metrics(struct isis_spftree *spftree)
1335 {
1336 struct isis_spf_adj *sadj;
1337 struct listnode *node, *nnode;
1338
1339 for (ALL_LIST_ELEMENTS(spftree->sadj_list, node, nnode, sadj)) {
1340 uint8_t lspid[ISIS_SYS_ID_LEN + 2];
1341 struct isis_lsp *lsp_adj;
1342 const uint8_t *id_self;
1343 struct spf_adj_find_reverse_metric_args args;
1344
1345 /* Skip pseudonodes. */
1346 if (LSP_PSEUDO_ID(sadj->id))
1347 continue;
1348
1349 /* Find LSP of the corresponding adjacency. */
1350 memcpy(lspid, sadj->id, ISIS_SYS_ID_LEN);
1351 LSP_PSEUDO_ID(lspid) = 0;
1352 LSP_FRAGMENT(lspid) = 0;
1353 lsp_adj = lsp_search(spftree->lspdb, lspid);
1354 if (lsp_adj == NULL || lsp_adj->hdr.rem_lifetime == 0) {
1355 /* Delete one-way adjacency. */
1356 listnode_delete(spftree->sadj_list, sadj);
1357 isis_spf_adj_free(sadj);
1358 continue;
1359 }
1360
1361 /* Find root node in the LSP of the adjacent router. */
1362 if (CHECK_FLAG(sadj->flags, F_ISIS_SPF_ADJ_BROADCAST))
1363 id_self = sadj->lan.desig_is_id;
1364 else
1365 id_self = spftree->sysid;
1366 args.id_self = id_self;
1367 args.reverse_metric = UINT32_MAX;
1368 isis_lsp_iterate_is_reach(lsp_adj, spftree->mtid,
1369 spf_adj_find_reverse_metric_cb,
1370 &args);
1371 if (args.reverse_metric == UINT32_MAX) {
1372 /* Delete one-way adjacency. */
1373 listnode_delete(spftree->sadj_list, sadj);
1374 isis_spf_adj_free(sadj);
1375 continue;
1376 }
1377 sadj->metric = args.reverse_metric;
1378 }
1379 }
1380
1381 static void spf_adj_list_parse_tlv(struct isis_spftree *spftree,
1382 struct list *adj_list, const uint8_t *id,
1383 const uint8_t *desig_is_id,
1384 uint32_t pseudo_metric, uint32_t metric,
1385 bool oldmetric,
1386 struct isis_ext_subtlvs *subtlvs)
1387 {
1388 struct isis_spf_adj *sadj;
1389 uint8_t lspid[ISIS_SYS_ID_LEN + 2];
1390 struct isis_lsp *lsp;
1391 uint8_t flags = 0;
1392
1393 /* Skip self in the pseudonode. */
1394 if (desig_is_id && !memcmp(id, spftree->sysid, ISIS_SYS_ID_LEN))
1395 return;
1396
1397 /* Find LSP from the adjacency. */
1398 memcpy(lspid, id, ISIS_SYS_ID_LEN + 1);
1399 LSP_FRAGMENT(lspid) = 0;
1400 lsp = lsp_search(spftree->lspdb, lspid);
1401 if (lsp == NULL || lsp->hdr.rem_lifetime == 0) {
1402 zlog_warn("ISIS-SPF: No LSP found from root to L%d %pLS",
1403 spftree->level, lspid);
1404 return;
1405 }
1406
1407 sadj = XCALLOC(MTYPE_ISIS_SPF_ADJ, sizeof(*sadj));
1408 memcpy(sadj->id, id, sizeof(sadj->id));
1409 if (desig_is_id) {
1410 memcpy(sadj->lan.desig_is_id, desig_is_id,
1411 sizeof(sadj->lan.desig_is_id));
1412 SET_FLAG(flags, F_ISIS_SPF_ADJ_BROADCAST);
1413 sadj->metric = pseudo_metric;
1414 } else
1415 sadj->metric = metric;
1416 if (oldmetric)
1417 SET_FLAG(flags, F_ISIS_SPF_ADJ_OLDMETRIC);
1418 sadj->lsp = lsp;
1419 sadj->subtlvs = subtlvs;
1420 sadj->flags = flags;
1421
1422 if ((oldmetric && metric == ISIS_NARROW_METRIC_INFINITY)
1423 || (!oldmetric && metric == ISIS_WIDE_METRIC_INFINITY))
1424 SET_FLAG(flags, F_ISIS_SPF_ADJ_METRIC_INFINITY);
1425
1426 /* Set real adjacency. */
1427 if (!CHECK_FLAG(spftree->flags, F_SPFTREE_NO_ADJACENCIES)
1428 && !LSP_PSEUDO_ID(id)) {
1429 struct isis_adjacency *adj;
1430
1431 adj = adj_find(adj_list, id, spftree->level, spftree->mtid,
1432 spftree->family);
1433 if (!adj) {
1434 XFREE(MTYPE_ISIS_SPF_ADJ, sadj);
1435 return;
1436 }
1437
1438 listnode_delete(adj_list, adj);
1439 sadj->adj = adj;
1440 }
1441
1442 /* Add adjacency to the list. */
1443 listnode_add(spftree->sadj_list, sadj);
1444
1445 if (!LSP_PSEUDO_ID(id)) {
1446 struct isis_spf_node *node;
1447
1448 node = isis_spf_node_find(&spftree->adj_nodes, id);
1449 if (!node)
1450 node = isis_spf_node_new(&spftree->adj_nodes, id);
1451 if (node->best_metric == 0 || sadj->metric < node->best_metric)
1452 node->best_metric = sadj->metric;
1453 listnode_add(node->adjacencies, sadj);
1454 }
1455
1456 /* Parse pseudonode LSP too. */
1457 if (LSP_PSEUDO_ID(id))
1458 spf_adj_list_parse_lsp(spftree, adj_list, lsp, id, metric);
1459 }
1460
1461 static void spf_adj_list_parse_lsp(struct isis_spftree *spftree,
1462 struct list *adj_list, struct isis_lsp *lsp,
1463 const uint8_t *pseudo_nodeid,
1464 uint32_t pseudo_metric)
1465 {
1466 bool pseudo_lsp = LSP_PSEUDO_ID(lsp->hdr.lsp_id);
1467 struct isis_lsp *frag;
1468 struct listnode *node;
1469 struct isis_item *head;
1470 struct isis_item_list *te_neighs;
1471
1472 if (lsp->hdr.seqno == 0 || lsp->hdr.rem_lifetime == 0)
1473 return;
1474
1475 /* Parse LSP. */
1476 if (lsp->tlvs) {
1477 if (pseudo_lsp || spftree->mtid == ISIS_MT_IPV4_UNICAST) {
1478 head = lsp->tlvs->oldstyle_reach.head;
1479 for (struct isis_oldstyle_reach *reach =
1480 (struct isis_oldstyle_reach *)head;
1481 reach; reach = reach->next) {
1482 spf_adj_list_parse_tlv(
1483 spftree, adj_list, reach->id,
1484 pseudo_nodeid, pseudo_metric,
1485 reach->metric, true, NULL);
1486 }
1487 }
1488
1489 if (pseudo_lsp || spftree->mtid == ISIS_MT_IPV4_UNICAST)
1490 te_neighs = &lsp->tlvs->extended_reach;
1491 else
1492 te_neighs = isis_get_mt_items(&lsp->tlvs->mt_reach,
1493 spftree->mtid);
1494 if (te_neighs) {
1495 head = te_neighs->head;
1496 for (struct isis_extended_reach *reach =
1497 (struct isis_extended_reach *)head;
1498 reach; reach = reach->next) {
1499 #ifndef FABRICD
1500 /*
1501 * cutting out adjacency by flex-algo link
1502 * affinity attribute
1503 */
1504 if (flex_algo_id_valid(spftree->algorithm) &&
1505 (!sr_algorithm_participated(
1506 lsp, spftree->algorithm) ||
1507 isis_flex_algo_constraint_drop(
1508 spftree, lsp, reach)))
1509 continue;
1510 #endif /* ifndef FABRICD */
1511
1512 spf_adj_list_parse_tlv(
1513 spftree, adj_list, reach->id,
1514 pseudo_nodeid, pseudo_metric,
1515 reach->metric, false, reach->subtlvs);
1516 }
1517 }
1518 }
1519
1520 if (LSP_FRAGMENT(lsp->hdr.lsp_id))
1521 return;
1522
1523 /* Parse LSP fragments. */
1524 for (ALL_LIST_ELEMENTS_RO(lsp->lspu.frags, node, frag)) {
1525 if (!frag->tlvs)
1526 continue;
1527
1528 spf_adj_list_parse_lsp(spftree, adj_list, frag, pseudo_nodeid,
1529 pseudo_metric);
1530 }
1531 }
1532
1533 static void isis_spf_build_adj_list(struct isis_spftree *spftree,
1534 struct isis_lsp *lsp)
1535 {
1536 struct list *adj_list = NULL;
1537
1538 if (!CHECK_FLAG(spftree->flags, F_SPFTREE_NO_ADJACENCIES))
1539 adj_list = list_dup(spftree->area->adjacency_list);
1540
1541 spf_adj_list_parse_lsp(spftree, adj_list, lsp, NULL, 0);
1542
1543 if (!CHECK_FLAG(spftree->flags, F_SPFTREE_NO_ADJACENCIES))
1544 list_delete(&adj_list);
1545
1546 if (spftree->type == SPF_TYPE_REVERSE)
1547 spf_adj_get_reverse_metrics(spftree);
1548 }
1549
1550 /*
1551 * The parent(s) for vertex is set when added to TENT list
1552 * now we just put the child pointer(s) in place
1553 */
1554 static void add_to_paths(struct isis_spftree *spftree,
1555 struct isis_vertex *vertex)
1556 {
1557 #ifdef EXTREME_DEBUG
1558 char buff[VID2STR_BUFFER];
1559 #endif /* EXTREME_DEBUG */
1560
1561 if (isis_find_vertex(&spftree->paths, &vertex->N, vertex->type))
1562 return;
1563 isis_vertex_queue_append(&spftree->paths, vertex);
1564
1565 #ifdef EXTREME_DEBUG
1566 if (IS_DEBUG_SPF_EVENTS)
1567 zlog_debug(
1568 "ISIS-SPF: A:%hhu S:%p added %s %s %s depth %d dist %d to PATHS",
1569 spftree->algorithm, spftree,
1570 print_sys_hostname(vertex->N.id),
1571 vtype2string(vertex->type),
1572 vid2string(vertex, buff, sizeof(buff)), vertex->depth,
1573 vertex->d_N);
1574 #endif /* EXTREME_DEBUG */
1575 }
1576
1577 static void init_spt(struct isis_spftree *spftree, int mtid)
1578 {
1579 /* Clear data from previous run. */
1580 hash_clean(spftree->prefix_sids, NULL);
1581 isis_spf_node_list_clear(&spftree->adj_nodes);
1582 list_delete_all_node(spftree->sadj_list);
1583 isis_vertex_queue_clear(&spftree->tents);
1584 isis_vertex_queue_clear(&spftree->paths);
1585 isis_zebra_rlfa_unregister_all(spftree);
1586 isis_rlfa_list_clear(spftree);
1587 list_delete_all_node(spftree->lfa.remote.pc_spftrees);
1588 memset(&spftree->lfa.protection_counters, 0,
1589 sizeof(spftree->lfa.protection_counters));
1590
1591 spftree->mtid = mtid;
1592 }
1593
1594 static enum spf_prefix_priority
1595 spf_prefix_priority(struct isis_spftree *spftree, struct isis_vertex *vertex)
1596 {
1597 struct isis_area *area = spftree->area;
1598 struct prefix *prefix = &vertex->N.ip.p.dest;
1599
1600 for (int priority = SPF_PREFIX_PRIO_CRITICAL;
1601 priority <= SPF_PREFIX_PRIO_MEDIUM; priority++) {
1602 struct spf_prefix_priority_acl *ppa;
1603 enum filter_type ret = FILTER_PERMIT;
1604
1605 ppa = &area->spf_prefix_priorities[priority];
1606 switch (spftree->family) {
1607 case AF_INET:
1608 ret = access_list_apply(ppa->list_v4, prefix);
1609 break;
1610 case AF_INET6:
1611 ret = access_list_apply(ppa->list_v6, prefix);
1612 break;
1613 default:
1614 break;
1615 }
1616
1617 if (ret == FILTER_PERMIT)
1618 return priority;
1619 }
1620
1621 /* Assign medium priority to loopback prefixes by default. */
1622 if (is_host_route(prefix))
1623 return SPF_PREFIX_PRIO_MEDIUM;
1624
1625 return SPF_PREFIX_PRIO_LOW;
1626 }
1627
1628 static void spf_path_process(struct isis_spftree *spftree,
1629 struct isis_vertex *vertex)
1630 {
1631 struct isis_area *area = spftree->area;
1632 int level = spftree->level;
1633 char buff[VID2STR_BUFFER];
1634
1635 if (spftree->type == SPF_TYPE_TI_LFA && VTYPE_IS(vertex->type)
1636 && !CHECK_FLAG(spftree->flags, F_SPFTREE_NO_ADJACENCIES)) {
1637 if (listcount(vertex->Adj_N) > 0) {
1638 struct isis_adjacency *adj;
1639
1640 if (isis_tilfa_check(spftree, vertex) != 0)
1641 return;
1642
1643 adj = isis_adj_find(area, level, vertex->N.id);
1644 if (adj)
1645 sr_adj_sid_add_single(adj, spftree->family,
1646 true, vertex->Adj_N);
1647 } else if (IS_DEBUG_SPF_EVENTS)
1648 zlog_debug(
1649 "ISIS-SPF: no adjacencies, do not install backup Adj-SID for %s depth %d dist %d",
1650 vid2string(vertex, buff, sizeof(buff)),
1651 vertex->depth, vertex->d_N);
1652 }
1653
1654 if (VTYPE_IP(vertex->type)
1655 && !CHECK_FLAG(spftree->flags, F_SPFTREE_NO_ROUTES)) {
1656 enum spf_prefix_priority priority;
1657
1658 priority = spf_prefix_priority(spftree, vertex);
1659 vertex->N.ip.priority = priority;
1660 if (vertex->depth == 1 || listcount(vertex->Adj_N) > 0) {
1661 struct isis_spftree *pre_spftree;
1662 struct route_table *route_table = NULL;
1663 bool allow_ecmp = false;
1664
1665 switch (spftree->type) {
1666 case SPF_TYPE_RLFA:
1667 case SPF_TYPE_TI_LFA:
1668 if (priority
1669 > area->lfa_priority_limit[level - 1]) {
1670 if (IS_DEBUG_LFA)
1671 zlog_debug(
1672 "ISIS-LFA: skipping %s %s (low prefix priority)",
1673 vtype2string(
1674 vertex->type),
1675 vid2string(
1676 vertex, buff,
1677 sizeof(buff)));
1678 return;
1679 }
1680 break;
1681 case SPF_TYPE_FORWARD:
1682 case SPF_TYPE_REVERSE:
1683 break;
1684 }
1685
1686 switch (spftree->type) {
1687 case SPF_TYPE_RLFA:
1688 isis_rlfa_check(spftree, vertex);
1689 return;
1690 case SPF_TYPE_TI_LFA:
1691 if (isis_tilfa_check(spftree, vertex) != 0)
1692 return;
1693
1694 pre_spftree = spftree->lfa.old.spftree;
1695 route_table = pre_spftree->route_table_backup;
1696 allow_ecmp = area->lfa_load_sharing[level - 1];
1697 pre_spftree->lfa.protection_counters
1698 .tilfa[vertex->N.ip.priority] += 1;
1699 break;
1700 case SPF_TYPE_FORWARD:
1701 case SPF_TYPE_REVERSE:
1702 route_table = spftree->route_table;
1703 allow_ecmp = true;
1704
1705 /*
1706 * Update LFA protection counters (ignore local
1707 * routes).
1708 */
1709 if (vertex->depth > 1) {
1710 spftree->lfa.protection_counters
1711 .total[priority] += 1;
1712 if (listcount(vertex->Adj_N) > 1)
1713 spftree->lfa.protection_counters
1714 .ecmp[priority] += 1;
1715 }
1716 break;
1717 }
1718
1719 #ifdef EXTREME_DEBUG
1720 struct isis_route_info *ri =
1721 #endif /* EXTREME_DEBUG */
1722 isis_route_create(&vertex->N.ip.p.dest,
1723 &vertex->N.ip.p.src,
1724 vertex->d_N, vertex->depth,
1725 &vertex->N.ip.sr,
1726 vertex->Adj_N, allow_ecmp,
1727 area, route_table);
1728
1729 #ifdef EXTREME_DEBUG
1730 zlog_debug(
1731 "ISIS-SPF: A:%hhu create route pfx %pFX dist %d, sr.algo %d, table %p, rv %p",
1732 spftree->algorithm, &vertex->N.ip.p.dest,
1733 vertex->d_N, vertex->N.ip.sr.algorithm,
1734 route_table, ri);
1735 #endif /* EXTREME_DEBUG */
1736 } else if (IS_DEBUG_SPF_EVENTS)
1737 zlog_debug(
1738 "ISIS-SPF: no adjacencies, do not install route for %s depth %d dist %d",
1739 vid2string(vertex, buff, sizeof(buff)),
1740 vertex->depth, vertex->d_N);
1741 }
1742 }
1743
1744 static void isis_spf_loop(struct isis_spftree *spftree,
1745 uint8_t *root_sysid)
1746 {
1747 struct isis_vertex *vertex;
1748 struct isis_lsp *lsp;
1749 struct listnode *node;
1750
1751 while (isis_vertex_queue_count(&spftree->tents)) {
1752 vertex = isis_vertex_queue_pop(&spftree->tents);
1753
1754 #ifdef EXTREME_DEBUG
1755 zlog_debug(
1756 "ISIS-SPF: A:%hhu get TENT node %s %s depth %d dist %d to PATHS",
1757 spftree->algorithm, print_sys_hostname(vertex->N.id),
1758 vtype2string(vertex->type), vertex->depth, vertex->d_N);
1759 #endif /* EXTREME_DEBUG */
1760
1761 add_to_paths(spftree, vertex);
1762 if (!VTYPE_IS(vertex->type))
1763 continue;
1764
1765 lsp = lsp_for_vertex(spftree, vertex);
1766 if (!lsp) {
1767 zlog_warn("ISIS-SPF: No LSP found for %pPN",
1768 vertex->N.id);
1769 continue;
1770 }
1771
1772 isis_spf_process_lsp(spftree, lsp, vertex->d_N, vertex->depth,
1773 root_sysid, vertex);
1774 }
1775
1776 /* Generate routes once the SPT is formed. */
1777 for (ALL_QUEUE_ELEMENTS_RO(&spftree->paths, node, vertex)) {
1778 /* New-style TLVs take precedence over the old-style TLVs. */
1779 switch (vertex->type) {
1780 case VTYPE_IPREACH_INTERNAL:
1781 case VTYPE_IPREACH_EXTERNAL:
1782 if (isis_find_vertex(&spftree->paths, &vertex->N,
1783 VTYPE_IPREACH_TE))
1784 continue;
1785 break;
1786 case VTYPE_PSEUDO_IS:
1787 case VTYPE_PSEUDO_TE_IS:
1788 case VTYPE_NONPSEUDO_IS:
1789 case VTYPE_NONPSEUDO_TE_IS:
1790 case VTYPE_ES:
1791 case VTYPE_IPREACH_TE:
1792 case VTYPE_IP6REACH_INTERNAL:
1793 case VTYPE_IP6REACH_EXTERNAL:
1794 break;
1795 }
1796
1797 spf_path_process(spftree, vertex);
1798 }
1799 }
1800
1801 struct isis_spftree *isis_run_hopcount_spf(struct isis_area *area,
1802 uint8_t *sysid,
1803 struct isis_spftree *spftree)
1804 {
1805 if (!spftree)
1806 spftree = isis_spftree_new(
1807 area, &area->lspdb[IS_LEVEL_2 - 1], sysid, ISIS_LEVEL2,
1808 SPFTREE_IPV4, SPF_TYPE_FORWARD,
1809 F_SPFTREE_HOPCOUNT_METRIC, SR_ALGORITHM_SPF);
1810
1811 init_spt(spftree, ISIS_MT_IPV4_UNICAST);
1812 if (!memcmp(sysid, area->isis->sysid, ISIS_SYS_ID_LEN)) {
1813 struct isis_lsp *root_lsp;
1814 struct isis_vertex *root_vertex;
1815
1816 root_lsp = isis_root_system_lsp(spftree->lspdb, spftree->sysid);
1817 if (root_lsp) {
1818 /*
1819 * If we are running locally, initialize with
1820 * information from adjacencies
1821 */
1822 root_vertex = isis_spf_add_root(spftree);
1823
1824 isis_spf_preload_tent(spftree, sysid, root_lsp,
1825 root_vertex);
1826 }
1827 } else {
1828 isis_vertex_queue_insert(
1829 &spftree->tents,
1830 isis_vertex_new(spftree, sysid, VTYPE_NONPSEUDO_TE_IS));
1831 }
1832
1833 isis_spf_loop(spftree, sysid);
1834
1835 return spftree;
1836 }
1837
1838 void isis_run_spf(struct isis_spftree *spftree)
1839 {
1840 struct isis_lsp *root_lsp;
1841 struct isis_vertex *root_vertex;
1842 struct timeval time_start;
1843 struct timeval time_end;
1844 struct isis_mt_router_info *mt_router_info;
1845 uint16_t mtid = 0;
1846
1847 /* Get time that can't roll backwards. */
1848 monotime(&time_start);
1849
1850 root_lsp = isis_root_system_lsp(spftree->lspdb, spftree->sysid);
1851 if (root_lsp == NULL) {
1852 zlog_err("ISIS-SPF: could not find own l%d LSP!",
1853 spftree->level);
1854 return;
1855 }
1856
1857 /* Get Multi-Topology ID. */
1858 switch (spftree->tree_id) {
1859 case SPFTREE_IPV4:
1860 mtid = ISIS_MT_IPV4_UNICAST;
1861 break;
1862 case SPFTREE_IPV6:
1863 mt_router_info = isis_tlvs_lookup_mt_router_info(
1864 root_lsp->tlvs, ISIS_MT_IPV6_UNICAST);
1865 if (mt_router_info)
1866 mtid = ISIS_MT_IPV6_UNICAST;
1867 else
1868 mtid = ISIS_MT_IPV4_UNICAST;
1869 break;
1870 case SPFTREE_DSTSRC:
1871 mtid = ISIS_MT_IPV6_DSTSRC;
1872 break;
1873 case SPFTREE_COUNT:
1874 zlog_err(
1875 "%s should never be called with SPFTREE_COUNT as argument!",
1876 __func__);
1877 exit(1);
1878 }
1879
1880 #ifndef FABRICD
1881 /* If a node is configured to participate in a particular Flexible-
1882 * Algorithm, but there is no valid Flex-Algorithm definition available
1883 * for it, or the selected Flex-Algorithm definition includes
1884 * calculation-type, metric-type, constraint, flag, or Sub-TLV that is
1885 * not supported by the node, it MUST stop participating in such
1886 * Flexible-Algorithm.
1887 */
1888 if (flex_algo_id_valid(spftree->algorithm) &&
1889 !flex_algo_get_state(spftree->area->flex_algos,
1890 spftree->algorithm)) {
1891 if (!CHECK_FLAG(spftree->flags, F_SPFTREE_DISABLED)) {
1892 isis_spftree_clear(spftree);
1893 SET_FLAG(spftree->flags, F_SPFTREE_DISABLED);
1894 lsp_regenerate_schedule(spftree->area,
1895 spftree->area->is_type, 0);
1896 }
1897 goto out;
1898 }
1899 #endif /* ifndef FABRICD */
1900
1901 /*
1902 * C.2.5 Step 0
1903 */
1904 init_spt(spftree, mtid);
1905 /* a) */
1906 root_vertex = isis_spf_add_root(spftree);
1907 /* b) */
1908 isis_spf_build_adj_list(spftree, root_lsp);
1909 isis_spf_preload_tent(spftree, spftree->sysid, root_lsp, root_vertex);
1910
1911 /*
1912 * C.2.7 Step 2
1913 */
1914 if (!isis_vertex_queue_count(&spftree->tents)
1915 && (IS_DEBUG_SPF_EVENTS)) {
1916 zlog_warn("ISIS-SPF: TENT is empty SPF-root:%s",
1917 print_sys_hostname(spftree->sysid));
1918 }
1919
1920 isis_spf_loop(spftree, spftree->sysid);
1921
1922
1923 #ifndef FABRICD
1924 /* flex-algo */
1925 if (CHECK_FLAG(spftree->flags, F_SPFTREE_DISABLED)) {
1926 UNSET_FLAG(spftree->flags, F_SPFTREE_DISABLED);
1927 lsp_regenerate_schedule(spftree->area, spftree->area->is_type,
1928 0);
1929 }
1930
1931 out:
1932 #endif /* ifndef FABRICD */
1933 spftree->runcount++;
1934 spftree->last_run_timestamp = time(NULL);
1935 spftree->last_run_monotime = monotime(&time_end);
1936 spftree->last_run_duration =
1937 ((time_end.tv_sec - time_start.tv_sec) * 1000000)
1938 + (time_end.tv_usec - time_start.tv_usec);
1939 }
1940
1941 static void isis_run_spf_with_protection(struct isis_area *area,
1942 struct isis_spftree *spftree)
1943 {
1944 /* Run forward SPF locally. */
1945 memcpy(spftree->sysid, area->isis->sysid, ISIS_SYS_ID_LEN);
1946 isis_run_spf(spftree);
1947
1948 /* Run LFA protection if configured. */
1949 if (area->lfa_protected_links[spftree->level - 1] > 0
1950 || area->tilfa_protected_links[spftree->level - 1] > 0)
1951 isis_spf_run_lfa(area, spftree);
1952 }
1953
1954 void isis_spf_verify_routes(struct isis_area *area, struct isis_spftree **trees,
1955 int tree)
1956 {
1957 if (area->is_type == IS_LEVEL_1) {
1958 isis_route_verify_table(area, trees[0]->route_table,
1959 trees[0]->route_table_backup, tree);
1960 } else if (area->is_type == IS_LEVEL_2) {
1961 isis_route_verify_table(area, trees[1]->route_table,
1962 trees[1]->route_table_backup, tree);
1963 } else {
1964 isis_route_verify_merge(area, trees[0]->route_table,
1965 trees[0]->route_table_backup,
1966 trees[1]->route_table,
1967 trees[1]->route_table_backup, tree);
1968 }
1969 }
1970
1971 void isis_spf_invalidate_routes(struct isis_spftree *tree)
1972 {
1973 struct isis_route_table_info *backup_info;
1974
1975 isis_route_invalidate_table(tree->area, tree->route_table);
1976
1977 /* Delete backup routes. */
1978
1979 backup_info = tree->route_table_backup->info;
1980 route_table_finish(tree->route_table_backup);
1981 isis_route_table_info_free(backup_info);
1982 tree->route_table_backup = srcdest_table_init();
1983 tree->route_table_backup->info =
1984 isis_route_table_info_alloc(tree->algorithm);
1985 tree->route_table_backup->cleanup = isis_route_node_cleanup;
1986 }
1987
1988 void isis_spf_switchover_routes(struct isis_area *area,
1989 struct isis_spftree **trees, int family,
1990 union g_addr *nexthop_ip, ifindex_t ifindex,
1991 int level)
1992 {
1993 isis_route_switchover_nexthop(area, trees[level - 1]->route_table,
1994 family, nexthop_ip, ifindex);
1995 }
1996
1997 static void isis_run_spf_cb(struct event *thread)
1998 {
1999 struct isis_spf_run *run = EVENT_ARG(thread);
2000 struct isis_area *area = run->area;
2001 int level = run->level;
2002 int have_run = 0;
2003 struct listnode *node;
2004 struct isis_circuit *circuit;
2005 #ifndef FABRICD
2006 struct flex_algo *fa;
2007 struct isis_flex_algo_data *data;
2008 #endif /* ifndef FABRICD */
2009
2010 XFREE(MTYPE_ISIS_SPF_RUN, run);
2011
2012 if (!(area->is_type & level)) {
2013 if (IS_DEBUG_SPF_EVENTS)
2014 zlog_warn("ISIS-SPF (%s) area does not share level",
2015 area->area_tag);
2016 return;
2017 }
2018
2019 isis_area_delete_backup_adj_sids(area, level);
2020 isis_area_invalidate_routes(area, level);
2021
2022 if (IS_DEBUG_SPF_EVENTS)
2023 zlog_debug("ISIS-SPF (%s) L%d SPF needed, periodic SPF",
2024 area->area_tag, level);
2025
2026 if (area->ip_circuits) {
2027 isis_run_spf_with_protection(
2028 area, area->spftree[SPFTREE_IPV4][level - 1]);
2029 #ifndef FABRICD
2030 for (ALL_LIST_ELEMENTS_RO(area->flex_algos->flex_algos, node,
2031 fa)) {
2032 data = fa->data;
2033 isis_run_spf_with_protection(
2034 area, data->spftree[SPFTREE_IPV4][level - 1]);
2035 }
2036 #endif /* ifndef FABRICD */
2037 have_run = 1;
2038 }
2039 if (area->ipv6_circuits) {
2040 isis_run_spf_with_protection(
2041 area, area->spftree[SPFTREE_IPV6][level - 1]);
2042 #ifndef FABRICD
2043 for (ALL_LIST_ELEMENTS_RO(area->flex_algos->flex_algos, node,
2044 fa)) {
2045 data = fa->data;
2046 isis_run_spf_with_protection(
2047 area, data->spftree[SPFTREE_IPV6][level - 1]);
2048 }
2049 #endif /* ifndef FABRICD */
2050 have_run = 1;
2051 }
2052 if (area->ipv6_circuits && isis_area_ipv6_dstsrc_enabled(area)) {
2053 isis_run_spf_with_protection(
2054 area, area->spftree[SPFTREE_DSTSRC][level - 1]);
2055 have_run = 1;
2056 }
2057
2058 if (have_run)
2059 area->spf_run_count[level]++;
2060
2061 isis_area_verify_routes(area);
2062
2063 /* walk all circuits and reset any spf specific flags */
2064 for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit))
2065 UNSET_FLAG(circuit->flags, ISIS_CIRCUIT_FLAPPED_AFTER_SPF);
2066
2067 fabricd_run_spf(area);
2068 }
2069
2070 static struct isis_spf_run *isis_run_spf_arg(struct isis_area *area, int level)
2071 {
2072 struct isis_spf_run *run = XMALLOC(MTYPE_ISIS_SPF_RUN, sizeof(*run));
2073
2074 run->area = area;
2075 run->level = level;
2076
2077 return run;
2078 }
2079
2080 void isis_spf_timer_free(void *run)
2081 {
2082 XFREE(MTYPE_ISIS_SPF_RUN, run);
2083 }
2084
2085 int _isis_spf_schedule(struct isis_area *area, int level,
2086 const char *func, const char *file, int line)
2087 {
2088 struct isis_spftree *spftree;
2089 time_t now;
2090 long tree_diff, diff;
2091 int tree;
2092
2093 now = monotime(NULL);
2094 diff = 0;
2095 for (tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++) {
2096 spftree = area->spftree[tree][level - 1];
2097 tree_diff = difftime(now - spftree->last_run_monotime, 0);
2098 if (tree_diff != now && (diff == 0 || tree_diff < diff))
2099 diff = tree_diff;
2100 }
2101
2102 if (CHECK_FLAG(im->options, F_ISIS_UNIT_TEST))
2103 return 0;
2104
2105 assert(diff >= 0);
2106 assert(area->is_type & level);
2107
2108 if (IS_DEBUG_SPF_EVENTS) {
2109 zlog_debug(
2110 "ISIS-SPF (%s) L%d SPF schedule called, lastrun %ld sec ago Caller: %s %s:%d",
2111 area->area_tag, level, diff, func, file, line);
2112 }
2113
2114 EVENT_OFF(area->t_rlfa_rib_update);
2115 if (area->spf_delay_ietf[level - 1]) {
2116 /* Need to call schedule function also if spf delay is running
2117 * to
2118 * restart holdoff timer - compare
2119 * draft-ietf-rtgwg-backoff-algo-04 */
2120 long delay =
2121 spf_backoff_schedule(area->spf_delay_ietf[level - 1]);
2122 if (area->spf_timer[level - 1])
2123 return ISIS_OK;
2124
2125 event_add_timer_msec(master, isis_run_spf_cb,
2126 isis_run_spf_arg(area, level), delay,
2127 &area->spf_timer[level - 1]);
2128 return ISIS_OK;
2129 }
2130
2131 if (area->spf_timer[level - 1])
2132 return ISIS_OK;
2133
2134 /* wait configured min_spf_interval before doing the SPF */
2135 long timer;
2136 if (diff >= area->min_spf_interval[level - 1]
2137 || area->bfd_force_spf_refresh) {
2138 /*
2139 * Last run is more than min interval ago or BFD signalled a
2140 * 'down' message, schedule immediate run
2141 */
2142 timer = 0;
2143
2144 if (area->bfd_force_spf_refresh) {
2145 zlog_debug(
2146 "ISIS-SPF (%s) L%d SPF scheduled immediately due to BFD 'down' message",
2147 area->area_tag, level);
2148 area->bfd_force_spf_refresh = false;
2149 }
2150 } else {
2151 timer = area->min_spf_interval[level - 1] - diff;
2152 }
2153
2154 event_add_timer(master, isis_run_spf_cb, isis_run_spf_arg(area, level),
2155 timer, &area->spf_timer[level - 1]);
2156
2157 if (IS_DEBUG_SPF_EVENTS)
2158 zlog_debug("ISIS-SPF (%s) L%d SPF scheduled %ld sec from now",
2159 area->area_tag, level, timer);
2160
2161 return ISIS_OK;
2162 }
2163
2164 static void isis_print_paths(struct vty *vty, struct isis_vertex_queue *queue,
2165 uint8_t *root_sysid)
2166 {
2167 struct listnode *node;
2168 struct isis_vertex *vertex;
2169 char buff[VID2STR_BUFFER];
2170
2171 vty_out(vty,
2172 "Vertex Type Metric Next-Hop Interface Parent\n");
2173
2174 for (ALL_QUEUE_ELEMENTS_RO(queue, node, vertex)) {
2175 if (VTYPE_IS(vertex->type)
2176 && memcmp(vertex->N.id, root_sysid, ISIS_SYS_ID_LEN) == 0) {
2177 vty_out(vty, "%-20s %-12s %-6s",
2178 print_sys_hostname(root_sysid), "", "");
2179 vty_out(vty, "%-30s\n", "");
2180 continue;
2181 }
2182
2183 int rows = 0;
2184 struct listnode *anode = listhead(vertex->Adj_N);
2185 struct listnode *pnode = listhead(vertex->parents);
2186 struct isis_vertex_adj *vadj;
2187 struct isis_vertex *pvertex;
2188
2189 vty_out(vty, "%-20s %-12s %-6u ",
2190 vid2string(vertex, buff, sizeof(buff)),
2191 vtype2string(vertex->type), vertex->d_N);
2192 for (unsigned int i = 0;
2193 i < MAX(vertex->Adj_N ? listcount(vertex->Adj_N) : 0,
2194 vertex->parents ? listcount(vertex->parents) : 0);
2195 i++) {
2196 if (anode) {
2197 vadj = listgetdata(anode);
2198 anode = anode->next;
2199 } else {
2200 vadj = NULL;
2201 }
2202
2203 if (pnode) {
2204 pvertex = listgetdata(pnode);
2205 pnode = pnode->next;
2206 } else {
2207 pvertex = NULL;
2208 }
2209
2210 if (rows) {
2211 vty_out(vty, "\n");
2212 vty_out(vty, "%-20s %-12s %-6s ", "", "", "");
2213 }
2214
2215 if (vadj) {
2216 struct isis_spf_adj *sadj = vadj->sadj;
2217
2218 vty_out(vty, "%-20s %-9s ",
2219 print_sys_hostname(sadj->id),
2220 sadj->adj ? sadj->adj->circuit
2221 ->interface->name
2222 : "-");
2223 }
2224
2225 if (pvertex) {
2226 if (!vadj)
2227 vty_out(vty, "%-20s %-9s ", "", "");
2228
2229 vty_out(vty, "%s(%d)",
2230 vid2string(pvertex, buff, sizeof(buff)),
2231 pvertex->type);
2232 }
2233
2234 ++rows;
2235 }
2236 vty_out(vty, "\n");
2237 }
2238 }
2239
2240 void isis_print_spftree(struct vty *vty, struct isis_spftree *spftree)
2241 {
2242 const char *tree_id_text = NULL;
2243
2244 if (!spftree || !isis_vertex_queue_count(&spftree->paths))
2245 return;
2246
2247 switch (spftree->tree_id) {
2248 case SPFTREE_IPV4:
2249 tree_id_text = "that speak IP";
2250 break;
2251 case SPFTREE_IPV6:
2252 tree_id_text = "that speak IPv6";
2253 break;
2254 case SPFTREE_DSTSRC:
2255 tree_id_text = "that support IPv6 dst-src routing";
2256 break;
2257 case SPFTREE_COUNT:
2258 assert(!"isis_print_spftree shouldn't be called with SPFTREE_COUNT as type");
2259 return;
2260 }
2261
2262 vty_out(vty, "IS-IS paths to level-%d routers %s\n", spftree->level,
2263 tree_id_text);
2264 isis_print_paths(vty, &spftree->paths, spftree->sysid);
2265 vty_out(vty, "\n");
2266 }
2267
2268 static void show_isis_topology_common(struct vty *vty, int levels,
2269 struct isis *isis, uint8_t algo)
2270 {
2271 #ifndef FABRICD
2272 struct isis_flex_algo_data *fa_data;
2273 struct flex_algo *fa;
2274 #endif /* ifndef FABRICD */
2275 struct isis_spftree *spftree;
2276 struct listnode *node;
2277 struct isis_area *area;
2278
2279 if (!isis->area_list || isis->area_list->count == 0)
2280 return;
2281
2282 for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) {
2283 vty_out(vty,
2284 "Area %s:", area->area_tag ? area->area_tag : "null");
2285
2286 #ifndef FABRICD
2287 /*
2288 * The shapes of the flex algo spftree 2-dimensional array
2289 * and the area spftree 2-dimensional array are not guaranteed
2290 * to be identical.
2291 */
2292 fa = NULL;
2293 if (flex_algo_id_valid(algo)) {
2294 fa = flex_algo_lookup(area->flex_algos, algo);
2295 if (!fa)
2296 continue;
2297 fa_data = (struct isis_flex_algo_data *)fa->data;
2298 } else
2299 fa_data = NULL;
2300
2301 if (algo != SR_ALGORITHM_SPF)
2302 vty_out(vty, " Algorithm %hhu\n", algo);
2303 else
2304 #endif /* ifndef FABRICD */
2305 vty_out(vty, "\n");
2306
2307 for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) {
2308 if ((level & levels) == 0)
2309 continue;
2310
2311 if (area->ip_circuits > 0) {
2312 #ifndef FABRICD
2313 if (fa_data)
2314 spftree = fa_data->spftree[SPFTREE_IPV4]
2315 [level - 1];
2316 else
2317 #endif /* ifndef FABRICD */
2318 spftree = area->spftree[SPFTREE_IPV4]
2319 [level - 1];
2320
2321 isis_print_spftree(vty, spftree);
2322 }
2323 if (area->ipv6_circuits > 0) {
2324 #ifndef FABRICD
2325 if (fa_data)
2326 spftree = fa_data->spftree[SPFTREE_IPV6]
2327 [level - 1];
2328 else
2329 #endif /* ifndef FABRICD */
2330 spftree = area->spftree[SPFTREE_IPV6]
2331 [level - 1];
2332 isis_print_spftree(vty, spftree);
2333 }
2334 if (isis_area_ipv6_dstsrc_enabled(area)) {
2335 #ifndef FABRICD
2336 if (fa_data)
2337 spftree =
2338 fa_data->spftree[SPFTREE_DSTSRC]
2339 [level - 1];
2340 else
2341 #endif /* ifndef FABRICD */
2342 spftree = area->spftree[SPFTREE_DSTSRC]
2343 [level - 1];
2344 isis_print_spftree(vty, spftree);
2345 }
2346 }
2347
2348 if (fabricd_spftree(area)) {
2349 vty_out(vty,
2350 "IS-IS paths to level-2 routers with hop-by-hop metric\n");
2351 isis_print_paths(vty, &fabricd_spftree(area)->paths, isis->sysid);
2352 vty_out(vty, "\n");
2353 }
2354
2355 vty_out(vty, "\n");
2356 }
2357 }
2358
2359 DEFUN(show_isis_topology, show_isis_topology_cmd,
2360 "show " PROTO_NAME
2361 " [vrf <NAME|all>] topology"
2362 #ifndef FABRICD
2363 " [<level-1|level-2>]"
2364 " [algorithm (128-255)]"
2365 #endif /* ifndef FABRICD */
2366 ,
2367 SHOW_STR PROTO_HELP VRF_CMD_HELP_STR
2368 "All VRFs\n"
2369 "IS-IS paths to Intermediate Systems\n"
2370 #ifndef FABRICD
2371 "Paths to all level-1 routers in the area\n"
2372 "Paths to all level-2 routers in the domain\n"
2373 "Show Flex-algo routes\n"
2374 "Algorithm number\n"
2375 #endif /* ifndef FABRICD */
2376 )
2377 {
2378 int levels = ISIS_LEVELS;
2379 struct listnode *node;
2380 struct isis *isis = NULL;
2381 const char *vrf_name = VRF_DEFAULT_NAME;
2382 bool all_vrf = false;
2383 int idx_vrf = 0;
2384 uint8_t algorithm = SR_ALGORITHM_SPF;
2385 #ifndef FABRICD
2386 int idx = 0;
2387
2388 levels = ISIS_LEVEL1 | ISIS_LEVEL2;
2389 if (argv_find(argv, argc, "level-1", &idx))
2390 levels = ISIS_LEVEL1;
2391 if (argv_find(argv, argc, "level-2", &idx))
2392 levels = ISIS_LEVEL2;
2393 if (argv_find(argv, argc, "algorithm", &idx))
2394 algorithm = (uint8_t)strtoul(argv[idx + 1]->arg, NULL, 10);
2395 #endif /* ifndef FABRICD */
2396
2397 if (!im) {
2398 vty_out(vty, "IS-IS Routing Process not enabled\n");
2399 return CMD_SUCCESS;
2400 }
2401 ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
2402
2403 if (vrf_name) {
2404 if (all_vrf) {
2405 for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis))
2406 show_isis_topology_common(vty, levels, isis,
2407 algorithm);
2408 return CMD_SUCCESS;
2409 }
2410 isis = isis_lookup_by_vrfname(vrf_name);
2411 if (isis != NULL)
2412 show_isis_topology_common(vty, levels, isis, algorithm);
2413 }
2414
2415 return CMD_SUCCESS;
2416 }
2417
2418 #ifndef FABRICD
2419 static void show_isis_flex_algo_display_eag(struct vty *vty, char *buf,
2420 int indent,
2421 struct admin_group *admin_group)
2422 {
2423 if (admin_group_zero(admin_group))
2424 vty_out(vty, "not-set\n");
2425 else {
2426 vty_out(vty, "%s\n",
2427 admin_group_string(buf, ADMIN_GROUP_PRINT_MAX_SIZE,
2428 indent, admin_group));
2429 admin_group_print(buf, indent, admin_group);
2430 if (buf[0] != '\0')
2431 vty_out(vty, " Bit positions: %s\n", buf);
2432 }
2433 }
2434
2435 static void show_isis_flex_algo_common(struct vty *vty, struct isis *isis,
2436 uint8_t algorithm)
2437 {
2438 struct isis_router_cap_fad *router_fad;
2439 char buf[ADMIN_GROUP_PRINT_MAX_SIZE];
2440 struct admin_group *admin_group;
2441 struct isis_area *area;
2442 struct listnode *node;
2443 struct flex_algo *fa;
2444 int indent, algo;
2445 bool fad_identical, fad_supported;
2446
2447 if (!isis->area_list || isis->area_list->count == 0)
2448 return;
2449
2450 for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) {
2451 /*
2452 * The shapes of the flex algo spftree 2-dimensional array
2453 * and the area spftree 2-dimensional array are not guaranteed
2454 * to be identical.
2455 */
2456
2457 for (algo = 0; algo < SR_ALGORITHM_COUNT; algo++) {
2458 if (algorithm != SR_ALGORITHM_UNSET &&
2459 algorithm != algo)
2460 continue;
2461
2462 fa = flex_algo_lookup(area->flex_algos, algo);
2463 if (!fa)
2464 continue;
2465
2466 vty_out(vty, "Area %s:",
2467 area->area_tag ? area->area_tag : "null");
2468
2469 vty_out(vty, " Algorithm %d\n", algo);
2470 vty_out(vty, "\n");
2471
2472 vty_out(vty, " Enabled Data-Planes:");
2473 if (fa->dataplanes == 0) {
2474 vty_out(vty, " None\n\n");
2475 continue;
2476 }
2477 if (CHECK_FLAG(fa->dataplanes, FLEX_ALGO_SR_MPLS))
2478 vty_out(vty, " SR-MPLS");
2479 if (CHECK_FLAG(fa->dataplanes, FLEX_ALGO_SRV6))
2480 vty_out(vty, " SRv6");
2481 if (CHECK_FLAG(fa->dataplanes, FLEX_ALGO_IP))
2482 vty_out(vty, " IP");
2483 vty_out(vty, "\n\n");
2484
2485
2486 router_fad = isis_flex_algo_elected(algo, area);
2487 vty_out(vty,
2488 " Elected and running Flexible-Algorithm Definition:\n");
2489 if (router_fad)
2490 vty_out(vty, " Source: %pSY\n",
2491 router_fad->sysid);
2492 else
2493 vty_out(vty, " Source: Not found\n");
2494
2495 if (!router_fad) {
2496 vty_out(vty, "\n");
2497 continue;
2498 }
2499
2500 fad_identical =
2501 flex_algo_definition_cmp(fa, &router_fad->fad);
2502 fad_supported =
2503 isis_flex_algo_supported(&router_fad->fad);
2504 vty_out(vty, " Priority: %d\n",
2505 router_fad->fad.priority);
2506 vty_out(vty, " Equal to local: %s\n",
2507 fad_identical ? "yes" : "no");
2508 vty_out(vty, " Local state: %s\n",
2509 fad_supported
2510 ? "enabled"
2511 : "disabled (unsupported definition)");
2512 vty_out(vty, " Calculation type: ");
2513 if (router_fad->fad.calc_type == 0)
2514 vty_out(vty, "spf\n");
2515 else
2516 vty_out(vty, "%d\n", router_fad->fad.calc_type);
2517 vty_out(vty, " Metric type: %s\n",
2518 flex_algo_metric_type_print(
2519 buf, sizeof(buf),
2520 router_fad->fad.metric_type));
2521 vty_out(vty, " Prefix-metric: %s\n",
2522 CHECK_FLAG(router_fad->fad.flags, FAD_FLAG_M)
2523 ? "enabled"
2524 : "disabled");
2525 if (router_fad->fad.flags != 0 &&
2526 router_fad->fad.flags != FAD_FLAG_M)
2527 vty_out(vty, " Flags: 0x%x\n",
2528 router_fad->fad.flags);
2529 vty_out(vty, " Exclude SRLG: %s\n",
2530 router_fad->fad.exclude_srlg ? "enabled"
2531 : "disabled");
2532
2533 admin_group = &router_fad->fad.admin_group_exclude_any;
2534 indent = vty_out(vty, " Exclude-any admin-group: ");
2535 show_isis_flex_algo_display_eag(vty, buf, indent,
2536 admin_group);
2537
2538 admin_group = &router_fad->fad.admin_group_include_all;
2539 indent = vty_out(vty, " Include-all admin-group: ");
2540 show_isis_flex_algo_display_eag(vty, buf, indent,
2541 admin_group);
2542
2543 admin_group = &router_fad->fad.admin_group_include_any;
2544 indent = vty_out(vty, " Include-any admin-group: ");
2545 show_isis_flex_algo_display_eag(vty, buf, indent,
2546 admin_group);
2547
2548 if (router_fad->fad.unsupported_subtlv)
2549 vty_out(vty,
2550 " Unsupported sub-TLV: Present (see logs)");
2551
2552 vty_out(vty, "\n");
2553 }
2554 }
2555 }
2556
2557 DEFUN(show_isis_flex_algo, show_isis_flex_algo_cmd,
2558 "show " PROTO_NAME
2559 " [vrf <NAME|all>] flex-algo"
2560 " [(128-255)]",
2561 SHOW_STR PROTO_HELP VRF_CMD_HELP_STR
2562 "All VRFs\n"
2563 "IS-IS Flex-algo information\n"
2564 "Algorithm number\n")
2565 {
2566 struct isis *isis;
2567 struct listnode *node;
2568 const char *vrf_name = VRF_DEFAULT_NAME;
2569 bool all_vrf = false;
2570 int idx = 0;
2571 int idx_vrf = 0;
2572 uint8_t flex_algo;
2573
2574 if (!im) {
2575 vty_out(vty, "IS-IS Routing Process not enabled\n");
2576 return CMD_SUCCESS;
2577 }
2578
2579 if (argv_find(argv, argc, "flex-algo", &idx) && (idx + 1) < argc)
2580 flex_algo = (uint8_t)strtoul(argv[idx + 1]->arg, NULL, 10);
2581 else
2582 flex_algo = SR_ALGORITHM_UNSET;
2583
2584 ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
2585
2586 if (vrf_name) {
2587 if (all_vrf) {
2588 for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis))
2589 show_isis_flex_algo_common(vty, isis,
2590 flex_algo);
2591 return CMD_SUCCESS;
2592 }
2593 isis = isis_lookup_by_vrfname(vrf_name);
2594 if (isis != NULL)
2595 show_isis_flex_algo_common(vty, isis, flex_algo);
2596 }
2597
2598 return CMD_SUCCESS;
2599 }
2600 #endif /* ifndef FABRICD */
2601
2602 static void isis_print_route(struct ttable *tt, const struct prefix *prefix,
2603 struct isis_route_info *rinfo, bool prefix_sid,
2604 bool no_adjacencies, bool json)
2605 {
2606 struct isis_nexthop *nexthop;
2607 struct listnode *node;
2608 bool first = true;
2609 char buf_prefix[BUFSIZ];
2610
2611 (void)prefix2str(prefix, buf_prefix, sizeof(buf_prefix));
2612 for (int alg = 0; alg < SR_ALGORITHM_COUNT; alg++) {
2613 for (ALL_LIST_ELEMENTS_RO(rinfo->sr_algo[alg].nexthops, node,
2614 nexthop)) {
2615 struct interface *ifp;
2616 char buf_iface[BUFSIZ];
2617 char buf_nhop[BUFSIZ];
2618
2619 if (!no_adjacencies) {
2620 inet_ntop(nexthop->family, &nexthop->ip,
2621 buf_nhop, sizeof(buf_nhop));
2622 ifp = if_lookup_by_index(nexthop->ifindex,
2623 VRF_DEFAULT);
2624 if (ifp)
2625 strlcpy(buf_iface, ifp->name,
2626 sizeof(buf_iface));
2627 else
2628 snprintf(buf_iface, sizeof(buf_iface),
2629 "ifindex %u",
2630 nexthop->ifindex);
2631 } else {
2632 strlcpy(buf_nhop,
2633 print_sys_hostname(nexthop->sysid),
2634 sizeof(buf_nhop));
2635 strlcpy(buf_iface, "-", sizeof(buf_iface));
2636 }
2637
2638 if (prefix_sid) {
2639 char buf_sid[BUFSIZ] = {};
2640 char buf_lblop[BUFSIZ] = {};
2641
2642 if (rinfo->sr_algo[alg].present) {
2643 snprintf(buf_sid, sizeof(buf_sid), "%u",
2644 rinfo->sr_algo[alg].sid.value);
2645 sr_op2str(buf_lblop, sizeof(buf_lblop),
2646 rinfo->sr_algo[alg].label,
2647 nexthop->sr.label);
2648 } else if (alg == SR_ALGORITHM_SPF) {
2649 strlcpy(buf_sid, "-", sizeof(buf_sid));
2650 strlcpy(buf_lblop, "-",
2651 sizeof(buf_lblop));
2652 } else {
2653 continue;
2654 }
2655
2656 if (first || json) {
2657 ttable_add_row(tt,
2658 "%s|%u|%s|%s|%s|%s|%d",
2659 buf_prefix, rinfo->cost,
2660 buf_iface, buf_nhop,
2661 buf_sid, buf_lblop, alg);
2662 first = false;
2663 } else
2664 ttable_add_row(tt, "||%s|%s|%s|%s|%d",
2665 buf_iface, buf_nhop,
2666 buf_sid, buf_lblop, alg);
2667 } else {
2668 char buf_labels[BUFSIZ] = {};
2669
2670 if (nexthop->label_stack) {
2671 for (int i = 0;
2672 i <
2673 nexthop->label_stack->num_labels;
2674 i++) {
2675 char buf_label[BUFSIZ];
2676
2677 label2str(nexthop->label_stack
2678 ->label[i],
2679 0, buf_label,
2680 sizeof(buf_label));
2681 if (i != 0)
2682 strlcat(buf_labels, "/",
2683 sizeof(buf_labels));
2684 strlcat(buf_labels, buf_label,
2685 sizeof(buf_labels));
2686 }
2687 } else if (nexthop->sr.present)
2688 label2str(nexthop->sr.label, 0,
2689 buf_labels,
2690 sizeof(buf_labels));
2691 else
2692 strlcpy(buf_labels, "-",
2693 sizeof(buf_labels));
2694
2695 if (first || json) {
2696 ttable_add_row(tt, "%s|%u|%s|%s|%s",
2697 buf_prefix, rinfo->cost,
2698 buf_iface, buf_nhop,
2699 buf_labels);
2700 first = false;
2701 } else
2702 ttable_add_row(tt, "||%s|%s|%s",
2703 buf_iface, buf_nhop,
2704 buf_labels);
2705 }
2706 }
2707 }
2708
2709 if (list_isempty(rinfo->nexthops)) {
2710 if (prefix_sid) {
2711 char buf_sid[BUFSIZ] = {};
2712 char buf_lblop[BUFSIZ] = {};
2713
2714 if (rinfo->sr_algo[SR_ALGORITHM_SPF].present) {
2715 snprintf(buf_sid, sizeof(buf_sid), "%u",
2716 rinfo->sr_algo[SR_ALGORITHM_SPF]
2717 .sid.value);
2718 sr_op2str(
2719 buf_lblop, sizeof(buf_lblop),
2720 rinfo->sr_algo[SR_ALGORITHM_SPF].label,
2721 MPLS_LABEL_IMPLICIT_NULL);
2722 } else {
2723 strlcpy(buf_sid, "-", sizeof(buf_sid));
2724 strlcpy(buf_lblop, "-", sizeof(buf_lblop));
2725 }
2726
2727 ttable_add_row(tt, "%s|%u|%s|%s|%s|%s", buf_prefix,
2728 rinfo->cost, "-", "-", buf_sid,
2729 buf_lblop);
2730 } else
2731 ttable_add_row(tt, "%s|%u|%s|%s|%s", buf_prefix,
2732 rinfo->cost, "-", "-", "-");
2733 }
2734 }
2735
2736 void isis_print_routes(struct vty *vty, struct isis_spftree *spftree,
2737 struct json_object **json, bool prefix_sid, bool backup)
2738 {
2739 struct route_table *route_table;
2740 struct ttable *tt;
2741 struct route_node *rn;
2742 bool no_adjacencies = false;
2743 const char *tree_id_text = NULL;
2744
2745 if (!spftree)
2746 return;
2747
2748 switch (spftree->tree_id) {
2749 case SPFTREE_IPV4:
2750 tree_id_text = "IPv4";
2751 break;
2752 case SPFTREE_IPV6:
2753 tree_id_text = "IPv6";
2754 break;
2755 case SPFTREE_DSTSRC:
2756 tree_id_text = "IPv6 (dst-src routing)";
2757 break;
2758 case SPFTREE_COUNT:
2759 assert(!"isis_print_routes shouldn't be called with SPFTREE_COUNT as type");
2760 return;
2761 }
2762
2763 if (json == NULL)
2764 vty_out(vty, "IS-IS %s %s routing table:\n\n",
2765 circuit_t2string(spftree->level), tree_id_text);
2766
2767 /* Prepare table. */
2768 tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
2769 if (prefix_sid)
2770 ttable_add_row(
2771 tt,
2772 "Prefix|Metric|Interface|Nexthop|SID|Label Op.|Algo");
2773 else
2774 ttable_add_row(tt, "Prefix|Metric|Interface|Nexthop|Label(s)");
2775 tt->style.cell.rpad = 2;
2776 tt->style.corner = '+';
2777 ttable_restyle(tt);
2778 ttable_rowseps(tt, 0, BOTTOM, true, '-');
2779
2780 if (CHECK_FLAG(spftree->flags, F_SPFTREE_NO_ADJACENCIES))
2781 no_adjacencies = true;
2782
2783 route_table =
2784 (backup) ? spftree->route_table_backup : spftree->route_table;
2785 for (rn = route_top(route_table); rn; rn = route_next(rn)) {
2786 struct isis_route_info *rinfo;
2787
2788 rinfo = rn->info;
2789 if (!rinfo)
2790 continue;
2791
2792 isis_print_route(tt, &rn->p, rinfo, prefix_sid, no_adjacencies,
2793 json != NULL);
2794 }
2795
2796 /* Dump the generated table. */
2797 if (json == NULL && tt->nrows > 1) {
2798 char *table;
2799
2800 table = ttable_dump(tt, "\n");
2801 vty_out(vty, "%s\n", table);
2802 XFREE(MTYPE_TMP, table);
2803 } else if (json) {
2804 *json = ttable_json(tt, prefix_sid ? "sdssdsdd" : "sdsss");
2805 }
2806 ttable_del(tt);
2807 }
2808
2809 static void show_isis_route_common(struct vty *vty, int levels,
2810 struct isis *isis, bool prefix_sid,
2811 bool backup, uint8_t algo,
2812 json_object **json)
2813 {
2814 json_object *json_level = NULL, *jstr = NULL, *json_val;
2815 #ifndef FABRICD
2816 struct isis_flex_algo_data *fa_data;
2817 struct flex_algo *fa;
2818 #endif /* ifndef FABRICD */
2819 struct isis_spftree *spftree;
2820 struct listnode *node;
2821 struct isis_area *area;
2822 char key[8];
2823
2824 if (!isis->area_list || isis->area_list->count == 0)
2825 return;
2826
2827 if (json)
2828 *json = json_object_new_object();
2829
2830 for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) {
2831 #ifndef FABRICD
2832 /*
2833 * The shapes of the flex algo spftree 2-dimensional array
2834 * and the area spftree 2-dimensional array are not guaranteed
2835 * to be identical.
2836 */
2837 fa = NULL;
2838 if (flex_algo_id_valid(algo)) {
2839 fa = flex_algo_lookup(area->flex_algos, algo);
2840 if (!fa)
2841 continue;
2842 fa_data = (struct isis_flex_algo_data *)fa->data;
2843 } else {
2844 fa_data = NULL;
2845 }
2846 #endif /* ifndef FABRICD */
2847
2848 if (json) {
2849 jstr = json_object_new_string(
2850 area->area_tag ? area->area_tag : "null");
2851 json_object_object_add(*json, "area", jstr);
2852 } else {
2853 vty_out(vty, "Area %s:",
2854 area->area_tag ? area->area_tag : "null");
2855 #ifndef FABRICD
2856 if (algo != SR_ALGORITHM_SPF)
2857 vty_out(vty, " Algorithm %hhu\n", algo);
2858 else
2859 #endif /* ifndef FABRICD */
2860 vty_out(vty, "\n");
2861 }
2862
2863 for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) {
2864 if ((level & levels) == 0)
2865 continue;
2866
2867 if (json) {
2868 json_level = json_object_new_object();
2869 jstr = json_object_new_string(
2870 area->area_tag ? area->area_tag
2871 : "null");
2872 json_object_object_add(json_level, "area",
2873 jstr);
2874 }
2875
2876 if (area->ip_circuits > 0) {
2877 json_val = NULL;
2878 #ifndef FABRICD
2879 if (fa_data)
2880 spftree = fa_data->spftree[SPFTREE_IPV4]
2881 [level - 1];
2882 else
2883 #endif /* ifndef FABRICD */
2884 spftree = area->spftree[SPFTREE_IPV4]
2885 [level - 1];
2886
2887 if (!json)
2888 isis_print_spftree(vty, spftree);
2889
2890 isis_print_routes(vty, spftree,
2891 json ? &json_val : NULL,
2892 prefix_sid, backup);
2893 if (json && json_val) {
2894 json_object_object_add(
2895 json_level, "ipv4", json_val);
2896 }
2897 }
2898 if (area->ipv6_circuits > 0) {
2899 json_val = NULL;
2900 #ifndef FABRICD
2901 if (fa_data)
2902 spftree = fa_data->spftree[SPFTREE_IPV6]
2903 [level - 1];
2904 else
2905 #endif /* ifndef FABRICD */
2906 spftree = area->spftree[SPFTREE_IPV6]
2907 [level - 1];
2908
2909 if (!json)
2910 isis_print_spftree(vty, spftree);
2911
2912 isis_print_routes(vty, spftree,
2913 json ? &json_val : NULL,
2914 prefix_sid, backup);
2915 if (json && json_val) {
2916 json_object_object_add(
2917 json_level, "ipv6", json_val);
2918 }
2919 }
2920 if (isis_area_ipv6_dstsrc_enabled(area)) {
2921 json_val = NULL;
2922 #ifndef FABRICD
2923 if (fa_data)
2924 spftree =
2925 fa_data->spftree[SPFTREE_DSTSRC]
2926 [level - 1];
2927 else
2928 #endif /* ifndef FABRICD */
2929 spftree = area->spftree[SPFTREE_DSTSRC]
2930 [level - 1];
2931
2932 if (!json)
2933 isis_print_spftree(vty, spftree);
2934 isis_print_routes(vty, spftree,
2935 json ? &json_val : NULL,
2936 prefix_sid, backup);
2937 if (json && json_val) {
2938 json_object_object_add(json_level,
2939 "ipv6-dstsrc",
2940 json_val);
2941 }
2942 }
2943 if (json) {
2944 snprintf(key, sizeof(key), "level-%d", level);
2945 json_object_object_add(*json, key, json_level);
2946 }
2947 }
2948 }
2949 }
2950
2951 DEFUN(show_isis_route, show_isis_route_cmd,
2952 "show " PROTO_NAME
2953 " [vrf <NAME|all>] route"
2954 #ifndef FABRICD
2955 " [<level-1|level-2>]"
2956 #endif /* ifndef FABRICD */
2957 " [<prefix-sid|backup>]"
2958 #ifndef FABRICD
2959 " [algorithm (128-255)]"
2960 #endif /* ifndef FABRICD */
2961 " [json$uj]",
2962 SHOW_STR PROTO_HELP VRF_FULL_CMD_HELP_STR
2963 "IS-IS routing table\n"
2964 #ifndef FABRICD
2965 "level-1 routes\n"
2966 "level-2 routes\n"
2967 #endif /* ifndef FABRICD */
2968 "Show Prefix-SID information\n"
2969 "Show backup routes\n"
2970 #ifndef FABRICD
2971 "Show Flex-algo routes\n"
2972 "Algorithm number\n"
2973 #endif /* ifndef FABRICD */
2974 JSON_STR)
2975 {
2976 int levels;
2977 struct isis *isis;
2978 struct listnode *node;
2979 const char *vrf_name = VRF_DEFAULT_NAME;
2980 bool all_vrf = false;
2981 bool prefix_sid = false;
2982 bool backup = false;
2983 bool uj = use_json(argc, argv);
2984 int idx = 0;
2985 json_object *json = NULL, *json_vrf = NULL;
2986 uint8_t algorithm = SR_ALGORITHM_SPF;
2987
2988 if (argv_find(argv, argc, "level-1", &idx))
2989 levels = ISIS_LEVEL1;
2990 else if (argv_find(argv, argc, "level-2", &idx))
2991 levels = ISIS_LEVEL2;
2992 else
2993 levels = ISIS_LEVEL1 | ISIS_LEVEL2;
2994
2995 if (!im) {
2996 vty_out(vty, "IS-IS Routing Process not enabled\n");
2997 return CMD_SUCCESS;
2998 }
2999 ISIS_FIND_VRF_ARGS(argv, argc, idx, vrf_name, all_vrf);
3000
3001 if (argv_find(argv, argc, "prefix-sid", &idx))
3002 prefix_sid = true;
3003 if (argv_find(argv, argc, "backup", &idx))
3004 backup = true;
3005
3006 #ifndef FABRICD
3007 if (argv_find(argv, argc, "algorithm", &idx))
3008 algorithm = (uint8_t)strtoul(argv[idx + 1]->arg, NULL, 10);
3009 #endif /* ifndef FABRICD */
3010
3011 if (uj)
3012 json = json_object_new_array();
3013
3014 if (vrf_name) {
3015 if (all_vrf) {
3016 for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) {
3017 show_isis_route_common(
3018 vty, levels, isis, prefix_sid, backup,
3019 algorithm, uj ? &json_vrf : NULL);
3020 if (uj) {
3021 json_object_object_add(
3022 json_vrf, "vrf_id",
3023 json_object_new_int(
3024 isis->vrf_id));
3025 json_object_array_add(json, json_vrf);
3026 }
3027 }
3028 goto out;
3029 }
3030 isis = isis_lookup_by_vrfname(vrf_name);
3031 if (isis != NULL) {
3032 show_isis_route_common(vty, levels, isis, prefix_sid,
3033 backup, algorithm,
3034 uj ? &json_vrf : NULL);
3035 if (uj) {
3036 json_object_object_add(
3037 json_vrf, "vrf_id",
3038 json_object_new_int(isis->vrf_id));
3039 json_object_array_add(json, json_vrf);
3040 }
3041 }
3042 }
3043
3044 out:
3045 if (uj) {
3046 vty_out(vty, "%s\n",
3047 json_object_to_json_string_ext(
3048 json, JSON_C_TO_STRING_PRETTY));
3049 json_object_free(json);
3050 }
3051
3052 return CMD_SUCCESS;
3053 }
3054
3055 static void isis_print_frr_summary_line(struct ttable *tt,
3056 const char *protection,
3057 uint32_t counters[SPF_PREFIX_PRIO_MAX])
3058 {
3059 uint32_t critical, high, medium, low, total;
3060
3061 critical = counters[SPF_PREFIX_PRIO_CRITICAL];
3062 high = counters[SPF_PREFIX_PRIO_HIGH];
3063 medium = counters[SPF_PREFIX_PRIO_MEDIUM];
3064 low = counters[SPF_PREFIX_PRIO_LOW];
3065 total = critical + high + medium + low;
3066
3067 ttable_add_row(tt, "%s|%u|%u|%u|%u|%u", protection, critical, high,
3068 medium, low, total);
3069 }
3070
3071 static void
3072 isis_print_frr_summary_line_coverage(struct ttable *tt, const char *protection,
3073 double counters[SPF_PREFIX_PRIO_MAX],
3074 double total)
3075 {
3076 double critical, high, medium, low;
3077
3078 critical = counters[SPF_PREFIX_PRIO_CRITICAL] * 100;
3079 high = counters[SPF_PREFIX_PRIO_HIGH] * 100;
3080 medium = counters[SPF_PREFIX_PRIO_MEDIUM] * 100;
3081 low = counters[SPF_PREFIX_PRIO_LOW] * 100;
3082 total *= 100;
3083
3084 ttable_add_row(tt, "%s|%.2f%%|%.2f%%|%.2f%%|%.2f%%|%.2f%%", protection,
3085 critical, high, medium, low, total);
3086 }
3087
3088 static void isis_print_frr_summary(struct vty *vty,
3089 struct isis_spftree *spftree)
3090 {
3091 struct ttable *tt;
3092 char *table;
3093 const char *tree_id_text = NULL;
3094 uint32_t protectd[SPF_PREFIX_PRIO_MAX] = {0};
3095 uint32_t unprotected[SPF_PREFIX_PRIO_MAX] = {0};
3096 double coverage[SPF_PREFIX_PRIO_MAX] = {0};
3097 uint32_t protected_total = 0, grand_total = 0;
3098 double coverage_total;
3099
3100 if (!spftree)
3101 return;
3102
3103 switch (spftree->tree_id) {
3104 case SPFTREE_IPV4:
3105 tree_id_text = "IPv4";
3106 break;
3107 case SPFTREE_IPV6:
3108 tree_id_text = "IPv6";
3109 break;
3110 case SPFTREE_DSTSRC:
3111 tree_id_text = "IPv6 (dst-src routing)";
3112 break;
3113 case SPFTREE_COUNT:
3114 assert(!"isis_print_frr_summary shouldn't be called with SPFTREE_COUNT as type");
3115 return;
3116 }
3117
3118 vty_out(vty, " IS-IS %s %s Fast ReRoute summary:\n\n",
3119 circuit_t2string(spftree->level), tree_id_text);
3120
3121 /* Prepare table. */
3122 tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
3123 ttable_add_row(
3124 tt,
3125 "Protection \\ Priority|Critical|High |Medium |Low |Total");
3126 tt->style.cell.rpad = 2;
3127 tt->style.corner = '+';
3128 ttable_restyle(tt);
3129 ttable_rowseps(tt, 0, BOTTOM, true, '-');
3130
3131 /* Compute unprotected and coverage totals. */
3132 for (int priority = SPF_PREFIX_PRIO_CRITICAL;
3133 priority < SPF_PREFIX_PRIO_MAX; priority++) {
3134 uint32_t *lfa = spftree->lfa.protection_counters.lfa;
3135 uint32_t *rlfa = spftree->lfa.protection_counters.rlfa;
3136 uint32_t *tilfa = spftree->lfa.protection_counters.tilfa;
3137 uint32_t *ecmp = spftree->lfa.protection_counters.ecmp;
3138 uint32_t *total = spftree->lfa.protection_counters.total;
3139
3140 protectd[priority] = lfa[priority] + rlfa[priority]
3141 + tilfa[priority] + ecmp[priority];
3142 /* Safeguard to protect against possible inconsistencies. */
3143 if (protectd[priority] > total[priority])
3144 protectd[priority] = total[priority];
3145 unprotected[priority] = total[priority] - protectd[priority];
3146 protected_total += protectd[priority];
3147 grand_total += total[priority];
3148
3149 if (!total[priority])
3150 coverage[priority] = 0;
3151 else
3152 coverage[priority] =
3153 protectd[priority] / (double)total[priority];
3154 }
3155
3156 if (!grand_total)
3157 coverage_total = 0;
3158 else
3159 coverage_total = protected_total / (double)grand_total;
3160
3161 /* Add rows. */
3162 isis_print_frr_summary_line(tt, "Classic LFA",
3163 spftree->lfa.protection_counters.lfa);
3164 isis_print_frr_summary_line(tt, "Remote LFA",
3165 spftree->lfa.protection_counters.rlfa);
3166 isis_print_frr_summary_line(tt, "Topology Independent LFA",
3167 spftree->lfa.protection_counters.tilfa);
3168 isis_print_frr_summary_line(tt, "ECMP",
3169 spftree->lfa.protection_counters.ecmp);
3170 isis_print_frr_summary_line(tt, "Unprotected", unprotected);
3171 isis_print_frr_summary_line_coverage(tt, "Protection coverage",
3172 coverage, coverage_total);
3173
3174 /* Dump the generated table. */
3175 table = ttable_dump(tt, "\n");
3176 vty_out(vty, "%s\n", table);
3177 XFREE(MTYPE_TMP, table);
3178 ttable_del(tt);
3179 }
3180
3181 static void show_isis_frr_summary_common(struct vty *vty, int levels,
3182 struct isis *isis)
3183 {
3184 struct listnode *node;
3185 struct isis_area *area;
3186
3187 if (!isis->area_list || isis->area_list->count == 0)
3188 return;
3189
3190 for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) {
3191 vty_out(vty, "Area %s:\n",
3192 area->area_tag ? area->area_tag : "null");
3193
3194 for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) {
3195 if ((level & levels) == 0)
3196 continue;
3197
3198 if (area->ip_circuits > 0) {
3199 isis_print_frr_summary(
3200 vty,
3201 area->spftree[SPFTREE_IPV4][level - 1]);
3202 }
3203 if (area->ipv6_circuits > 0) {
3204 isis_print_frr_summary(
3205 vty,
3206 area->spftree[SPFTREE_IPV6][level - 1]);
3207 }
3208 if (isis_area_ipv6_dstsrc_enabled(area)) {
3209 isis_print_frr_summary(
3210 vty, area->spftree[SPFTREE_DSTSRC]
3211 [level - 1]);
3212 }
3213 }
3214 }
3215 }
3216
3217 DEFUN(show_isis_frr_summary, show_isis_frr_summary_cmd,
3218 "show " PROTO_NAME
3219 " [vrf <NAME|all>] fast-reroute summary"
3220 #ifndef FABRICD
3221 " [<level-1|level-2>]"
3222 #endif
3223 ,
3224 SHOW_STR PROTO_HELP VRF_FULL_CMD_HELP_STR
3225 "IS-IS FRR information\n"
3226 "FRR summary\n"
3227 #ifndef FABRICD
3228 "level-1 routes\n"
3229 "level-2 routes\n"
3230 #endif
3231 )
3232 {
3233 int levels;
3234 struct isis *isis;
3235 struct listnode *node;
3236 const char *vrf_name = VRF_DEFAULT_NAME;
3237 bool all_vrf = false;
3238 int idx = 0;
3239
3240 if (argv_find(argv, argc, "level-1", &idx))
3241 levels = ISIS_LEVEL1;
3242 else if (argv_find(argv, argc, "level-2", &idx))
3243 levels = ISIS_LEVEL2;
3244 else
3245 levels = ISIS_LEVEL1 | ISIS_LEVEL2;
3246
3247 if (!im) {
3248 vty_out(vty, "IS-IS Routing Process not enabled\n");
3249 return CMD_SUCCESS;
3250 }
3251 ISIS_FIND_VRF_ARGS(argv, argc, idx, vrf_name, all_vrf);
3252
3253 if (vrf_name) {
3254 if (all_vrf) {
3255 for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis))
3256 show_isis_frr_summary_common(vty, levels, isis);
3257 return CMD_SUCCESS;
3258 }
3259 isis = isis_lookup_by_vrfname(vrf_name);
3260 if (isis != NULL)
3261 show_isis_frr_summary_common(vty, levels, isis);
3262 }
3263
3264 return CMD_SUCCESS;
3265 }
3266
3267 void isis_spf_init(void)
3268 {
3269 #ifndef FABRICD
3270 install_element(VIEW_NODE, &show_isis_flex_algo_cmd);
3271 #endif /* ifndef FABRICD */
3272 install_element(VIEW_NODE, &show_isis_topology_cmd);
3273 install_element(VIEW_NODE, &show_isis_route_cmd);
3274 install_element(VIEW_NODE, &show_isis_frr_summary_cmd);
3275
3276 /* Register hook(s). */
3277 hook_register(isis_adj_state_change_hook, spf_adj_state_change);
3278 }
3279
3280 void isis_spf_print(struct isis_spftree *spftree, struct vty *vty)
3281 {
3282 uint64_t last_run_duration = spftree->last_run_duration;
3283
3284 vty_out(vty, " last run elapsed : ");
3285 vty_out_timestr(vty, spftree->last_run_timestamp);
3286 vty_out(vty, "\n");
3287
3288 vty_out(vty, " last run duration : %" PRIu64 " usec\n",
3289 last_run_duration);
3290
3291 vty_out(vty, " run count : %u\n", spftree->runcount);
3292 }
3293 void isis_spf_print_json(struct isis_spftree *spftree, struct json_object *json)
3294 {
3295 char uptime[MONOTIME_STRLEN];
3296 time_t cur;
3297 cur = time(NULL);
3298 cur -= spftree->last_run_timestamp;
3299 frrtime_to_interval(cur, uptime, sizeof(uptime));
3300 json_object_string_add(json, "last-run-elapsed", uptime);
3301 json_object_int_add(json, "last-run-duration-usec",
3302 spftree->last_run_duration);
3303 json_object_int_add(json, "last-run-count", spftree->runcount);
3304 }