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