]> git.proxmox.com Git - mirror_frr.git/blame - isisd/isis_spf.c
fabricd: implement flooding optimization
[mirror_frr.git] / isisd / isis_spf.c
CommitLineData
eb5d44eb 1/*
2 * IS-IS Rout(e)ing protocol - isis_spf.c
3 * The SPT algorithm
4 *
5 * Copyright (C) 2001,2002 Sampo Saaristo
d62a17ae 6 * Tampere University of Technology
eb5d44eb 7 * Institute of Communications Engineering
1b49e4f0 8 * Copyright (C) 2017 Christian Franke <chris@opensourcerouting.org>
eb5d44eb 9 *
d62a17ae 10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public Licenseas published by the Free
12 * Software Foundation; either version 2 of the License, or (at your option)
eb5d44eb 13 * any later version.
14 *
d62a17ae 15 * This program is distributed in the hope that it will be useful,but WITHOUT
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
eb5d44eb 18 * more details.
896014f4
DL
19 *
20 * You should have received a copy of the GNU General Public License along
21 * with this program; see the file COPYING; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
eb5d44eb 23 */
24
eb5d44eb 25#include <zebra.h>
eb5d44eb 26
27#include "thread.h"
28#include "linklist.h"
29#include "vty.h"
30#include "log.h"
31#include "command.h"
32#include "memory.h"
33#include "prefix.h"
eb5d44eb 34#include "if.h"
35#include "table.h"
03f7e182 36#include "spf_backoff.h"
321c1bbb 37#include "srcdest_table.h"
eb5d44eb 38
39#include "isis_constants.h"
40#include "isis_common.h"
3f045a08 41#include "isis_flags.h"
eb5d44eb 42#include "dict.h"
43#include "isisd.h"
44#include "isis_misc.h"
45#include "isis_adjacency.h"
46#include "isis_circuit.h"
eb5d44eb 47#include "isis_pdu.h"
48#include "isis_lsp.h"
49#include "isis_dynhn.h"
50#include "isis_spf.h"
51#include "isis_route.h"
52#include "isis_csm.h"
2b67862c 53#include "isis_mt.h"
841791b6 54#include "isis_tlvs.h"
b30e837b 55#include "fabricd.h"
cbd8e49e 56#include "isis_spf_private.h"
eb5d44eb 57
1b49e4f0
CF
58DEFINE_MTYPE_STATIC(ISISD, ISIS_SPF_RUN, "ISIS SPF Run Info");
59
af8ac8f9
CF
60/*
61 * supports the given af ?
62 */
63static bool speaks(uint8_t *protocols, uint8_t count, int family)
64{
65 for (uint8_t i = 0; i < count; i++) {
66 if (family == AF_INET && protocols[i] == NLPID_IP)
67 return true;
68 if (family == AF_INET6 && protocols[i] == NLPID_IPV6)
69 return true;
70 }
71 return false;
72}
73
1b49e4f0 74struct isis_spf_run {
d62a17ae 75 struct isis_area *area;
76 int level;
1b49e4f0
CF
77};
78
eb5d44eb 79/* 7.2.7 */
d62a17ae 80static void remove_excess_adjs(struct list *adjs)
eb5d44eb 81{
d62a17ae 82 struct listnode *node, *excess = NULL;
83 struct isis_adjacency *adj, *candidate = NULL;
84 int comp;
85
86 for (ALL_LIST_ELEMENTS_RO(adjs, node, adj)) {
87 if (excess == NULL)
88 excess = node;
89 candidate = listgetdata(excess);
90
91 if (candidate->sys_type < adj->sys_type) {
92 excess = node;
93 continue;
94 }
95 if (candidate->sys_type > adj->sys_type)
96 continue;
f390d2c7 97
d62a17ae 98 comp = memcmp(candidate->sysid, adj->sysid, ISIS_SYS_ID_LEN);
99 if (comp > 0) {
100 excess = node;
101 continue;
102 }
103 if (comp < 0)
104 continue;
eb5d44eb 105
0849c75e 106 if (candidate->circuit->idx > adj->circuit->idx) {
d62a17ae 107 excess = node;
108 continue;
109 }
f390d2c7 110
0849c75e 111 if (candidate->circuit->idx < adj->circuit->idx)
d62a17ae 112 continue;
f390d2c7 113
d62a17ae 114 comp = memcmp(candidate->snpa, adj->snpa, ETH_ALEN);
115 if (comp > 0) {
116 excess = node;
117 continue;
118 }
f390d2c7 119 }
f390d2c7 120
d62a17ae 121 list_delete_node(adjs, excess);
eb5d44eb 122
d62a17ae 123 return;
eb5d44eb 124}
125
d62a17ae 126static const char *vtype2string(enum vertextype vtype)
eb5d44eb 127{
d62a17ae 128 switch (vtype) {
129 case VTYPE_PSEUDO_IS:
130 return "pseudo_IS";
131 break;
132 case VTYPE_PSEUDO_TE_IS:
133 return "pseudo_TE-IS";
134 break;
135 case VTYPE_NONPSEUDO_IS:
136 return "IS";
137 break;
138 case VTYPE_NONPSEUDO_TE_IS:
139 return "TE-IS";
140 break;
141 case VTYPE_ES:
142 return "ES";
143 break;
144 case VTYPE_IPREACH_INTERNAL:
145 return "IP internal";
146 break;
147 case VTYPE_IPREACH_EXTERNAL:
148 return "IP external";
149 break;
150 case VTYPE_IPREACH_TE:
151 return "IP TE";
152 break;
153 case VTYPE_IP6REACH_INTERNAL:
154 return "IP6 internal";
155 break;
156 case VTYPE_IP6REACH_EXTERNAL:
157 return "IP6 external";
158 break;
159 default:
160 return "UNKNOWN";
161 }
162 return NULL; /* Not reached */
eb5d44eb 163}
164
321c1bbb 165#define VID2STR_BUFFER SRCDEST2STR_BUFFER
d62a17ae 166static const char *vid2string(struct isis_vertex *vertex, char *buff, int size)
eb5d44eb 167{
d62a17ae 168 if (VTYPE_IS(vertex->type) || VTYPE_ES(vertex->type)) {
169 return print_sys_hostname(vertex->N.id);
170 }
171
172 if (VTYPE_IP(vertex->type)) {
321c1bbb
CF
173 srcdest2str(&vertex->N.ip.dest,
174 &vertex->N.ip.src,
175 buff, size);
d62a17ae 176 return buff;
177 }
178
179 return "UNKNOWN";
eb5d44eb 180}
181
686afe9f
CF
182static struct isis_vertex *isis_vertex_new(struct isis_spftree *spftree,
183 union isis_N *n,
ae9c9aba 184 enum vertextype vtype)
eb919f07
CF
185{
186 struct isis_vertex *vertex;
187
188 vertex = XCALLOC(MTYPE_ISIS_VERTEX, sizeof(struct isis_vertex));
189
ae9c9aba 190 isis_vertex_id_init(vertex, n, vtype);
d62a17ae 191
192 vertex->Adj_N = list_new();
193 vertex->parents = list_new();
d62a17ae 194
686afe9f
CF
195 if (spftree->hopcount_metric) {
196 vertex->firsthops = hash_create(isis_vertex_queue_hash_key,
197 isis_vertex_queue_hash_cmp,
198 NULL);
199 }
200
d62a17ae 201 return vertex;
eb5d44eb 202}
203
d62a17ae 204static void isis_vertex_adj_del(struct isis_vertex *vertex,
205 struct isis_adjacency *adj)
3f045a08 206{
d62a17ae 207 struct listnode *node, *nextnode;
208 if (!vertex)
209 return;
210 for (node = listhead(vertex->Adj_N); node; node = nextnode) {
211 nextnode = listnextnode(node);
212 if (listgetdata(node) == adj)
213 list_delete_node(vertex->Adj_N, node);
214 }
215 return;
3f045a08
JB
216}
217
d62a17ae 218struct isis_spftree *isis_spftree_new(struct isis_area *area)
3f045a08 219{
d62a17ae 220 struct isis_spftree *tree;
221
222 tree = XCALLOC(MTYPE_ISIS_SPFTREE, sizeof(struct isis_spftree));
d62a17ae 223
bded4060
CF
224 isis_vertex_queue_init(&tree->tents, "IS-IS SPF tents", true);
225 isis_vertex_queue_init(&tree->paths, "IS-IS SPF paths", false);
321c1bbb 226 tree->route_table = srcdest_table_init();
d62a17ae 227 tree->area = area;
228 tree->last_run_timestamp = 0;
3dca3c8c 229 tree->last_run_monotime = 0;
d62a17ae 230 tree->last_run_duration = 0;
231 tree->runcount = 0;
232 return tree;
3f045a08
JB
233}
234
d62a17ae 235void isis_spftree_del(struct isis_spftree *spftree)
eb5d44eb 236{
eb919f07
CF
237 isis_vertex_queue_free(&spftree->tents);
238 isis_vertex_queue_free(&spftree->paths);
3dace42d
CF
239 route_table_finish(spftree->route_table);
240 spftree->route_table = NULL;
eb5d44eb 241
3dace42d 242 XFREE(MTYPE_ISIS_SPFTREE, spftree);
d62a17ae 243 return;
eb5d44eb 244}
3f045a08 245
d62a17ae 246static void isis_spftree_adj_del(struct isis_spftree *spftree,
247 struct isis_adjacency *adj)
3f045a08 248{
d62a17ae 249 struct listnode *node;
eb919f07 250 struct isis_vertex *v;
d62a17ae 251 if (!adj)
252 return;
bded4060 253 assert(!isis_vertex_queue_count(&spftree->tents));
eb919f07
CF
254 for (ALL_QUEUE_ELEMENTS_RO(&spftree->paths, node, v))
255 isis_vertex_adj_del(v, adj);
d62a17ae 256 return;
3f045a08 257}
eb5d44eb 258
d62a17ae 259void spftree_area_init(struct isis_area *area)
eb5d44eb 260{
be985ba0
CF
261 for (int tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++) {
262 for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) {
263 if (!(area->is_type & level))
264 continue;
265 if (area->spftree[tree][level - 1])
266 continue;
d62a17ae 267
be985ba0
CF
268 area->spftree[tree][level - 1] = isis_spftree_new(area);
269 }
d62a17ae 270 }
eb5d44eb 271}
272
d62a17ae 273void spftree_area_del(struct isis_area *area)
eb5d44eb 274{
be985ba0
CF
275 for (int tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++) {
276 for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) {
277 if (!(area->is_type & level))
278 continue;
279 if (!area->spftree[tree][level - 1])
280 continue;
d62a17ae 281
be985ba0 282 isis_spftree_del(area->spftree[tree][level - 1]);
d62a17ae 283 }
284 }
3f045a08 285}
f390d2c7 286
d62a17ae 287void spftree_area_adj_del(struct isis_area *area, struct isis_adjacency *adj)
3f045a08 288{
be985ba0
CF
289 for (int tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++) {
290 for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) {
291 if (!(area->is_type & level))
292 continue;
293 if (!area->spftree[tree][level - 1])
294 continue;
295 isis_spftree_adj_del(area->spftree[tree][level - 1],
296 adj);
297 }
d62a17ae 298 }
b30e837b
CF
299
300 if (fabricd_spftree(area) != NULL)
301 isis_spftree_adj_del(fabricd_spftree(area), adj);
3f045a08
JB
302}
303
d62a17ae 304/*
305 * Find the system LSP: returns the LSP in our LSP database
3f045a08
JB
306 * associated with the given system ID.
307 */
d62a17ae 308static struct isis_lsp *isis_root_system_lsp(struct isis_area *area, int level,
d7c0a89a 309 uint8_t *sysid)
3f045a08 310{
d62a17ae 311 struct isis_lsp *lsp;
d7c0a89a 312 uint8_t lspid[ISIS_SYS_ID_LEN + 2];
d62a17ae 313
314 memcpy(lspid, sysid, ISIS_SYS_ID_LEN);
315 LSP_PSEUDO_ID(lspid) = 0;
316 LSP_FRAGMENT(lspid) = 0;
317 lsp = lsp_search(lspid, area->lspdb[level - 1]);
af8ac8f9 318 if (lsp && lsp->hdr.rem_lifetime != 0)
d62a17ae 319 return lsp;
320 return NULL;
eb5d44eb 321}
322
323/*
324 * Add this IS to the root of SPT
325 */
d62a17ae 326static struct isis_vertex *isis_spf_add_root(struct isis_spftree *spftree,
d7c0a89a 327 uint8_t *sysid)
eb5d44eb 328{
d62a17ae 329 struct isis_vertex *vertex;
330 struct isis_lsp *lsp;
eb5d44eb 331#ifdef EXTREME_DEBUG
321c1bbb 332 char buff[VID2STR_BUFFER];
eb5d44eb 333#endif /* EXTREME_DEBUG */
ae9c9aba 334 union isis_N n;
f390d2c7 335
ae9c9aba 336 memcpy(n.id, sysid, ISIS_SYS_ID_LEN);
337 LSP_PSEUDO_ID(n.id) = 0;
f390d2c7 338
d62a17ae 339 lsp = isis_root_system_lsp(spftree->area, spftree->level, sysid);
340 if (lsp == NULL)
341 zlog_warn("ISIS-Spf: could not find own l%d LSP!",
342 spftree->level);
eb5d44eb 343
686afe9f 344 vertex = isis_vertex_new(spftree, &n,
9d303b37
DL
345 spftree->area->oldmetric
346 ? VTYPE_NONPSEUDO_IS
347 : VTYPE_NONPSEUDO_TE_IS);
bded4060 348 isis_vertex_queue_append(&spftree->paths, vertex);
eb5d44eb 349
350#ifdef EXTREME_DEBUG
d62a17ae 351 zlog_debug("ISIS-Spf: added this IS %s %s depth %d dist %d to PATHS",
352 vtype2string(vertex->type),
353 vid2string(vertex, buff, sizeof(buff)), vertex->depth,
354 vertex->d_N);
eb5d44eb 355#endif /* EXTREME_DEBUG */
356
d62a17ae 357 return vertex;
eb5d44eb 358}
359
686afe9f
CF
360static void vertex_add_parent_firsthop(struct hash_backet *backet, void *arg)
361{
362 struct isis_vertex *vertex = arg;
363 struct isis_vertex *hop = backet->data;
364
365 hash_get(vertex->firsthops, hop, hash_alloc_intern);
366}
367
368static void vertex_update_firsthops(struct isis_vertex *vertex,
369 struct isis_vertex *parent)
370{
371 if (vertex->d_N <= 2)
372 hash_get(vertex->firsthops, vertex, hash_alloc_intern);
373
374 if (vertex->d_N < 2 || !parent)
375 return;
376
377 hash_iterate(parent->firsthops, vertex_add_parent_firsthop, vertex);
378}
379
eb5d44eb 380/*
381 * Add a vertex to TENT sorted by cost and by vertextype on tie break situation
382 */
d62a17ae 383static struct isis_vertex *isis_spf_add2tent(struct isis_spftree *spftree,
384 enum vertextype vtype, void *id,
385 uint32_t cost, int depth,
386 struct isis_adjacency *adj,
387 struct isis_vertex *parent)
eb5d44eb 388{
eb919f07 389 struct isis_vertex *vertex;
d62a17ae 390 struct listnode *node;
391 struct isis_adjacency *parent_adj;
f390d2c7 392#ifdef EXTREME_DEBUG
321c1bbb 393 char buff[VID2STR_BUFFER];
eb5d44eb 394#endif
395
eb919f07
CF
396 assert(isis_find_vertex(&spftree->paths, id, vtype) == NULL);
397 assert(isis_find_vertex(&spftree->tents, id, vtype) == NULL);
686afe9f 398 vertex = isis_vertex_new(spftree, id, vtype);
d62a17ae 399 vertex->d_N = cost;
400 vertex->depth = depth;
401
402 if (parent) {
403 listnode_add(vertex->parents, parent);
d62a17ae 404 }
405
686afe9f
CF
406 if (spftree->hopcount_metric)
407 vertex_update_firsthops(vertex, parent);
408
d62a17ae 409 if (parent && parent->Adj_N && listcount(parent->Adj_N) > 0) {
410 for (ALL_LIST_ELEMENTS_RO(parent->Adj_N, node, parent_adj))
411 listnode_add(vertex->Adj_N, parent_adj);
412 } else if (adj) {
413 listnode_add(vertex->Adj_N, adj);
414 }
3f045a08 415
f390d2c7 416#ifdef EXTREME_DEBUG
d62a17ae 417 zlog_debug(
418 "ISIS-Spf: add to TENT %s %s %s depth %d dist %d adjcount %d",
419 print_sys_hostname(vertex->N.id), vtype2string(vertex->type),
420 vid2string(vertex, buff, sizeof(buff)), vertex->depth,
421 vertex->d_N, listcount(vertex->Adj_N));
eb5d44eb 422#endif /* EXTREME_DEBUG */
3f045a08 423
eb919f07 424 isis_vertex_queue_insert(&spftree->tents, vertex);
d62a17ae 425 return vertex;
426}
427
428static void isis_spf_add_local(struct isis_spftree *spftree,
429 enum vertextype vtype, void *id,
430 struct isis_adjacency *adj, uint32_t cost,
431 struct isis_vertex *parent)
432{
433 struct isis_vertex *vertex;
434
eb919f07 435 vertex = isis_find_vertex(&spftree->tents, id, vtype);
d62a17ae 436
437 if (vertex) {
438 /* C.2.5 c) */
439 if (vertex->d_N == cost) {
440 if (adj)
441 listnode_add(vertex->Adj_N, adj);
442 /* d) */
443 if (listcount(vertex->Adj_N) > ISIS_MAX_PATH_SPLITS)
444 remove_excess_adjs(vertex->Adj_N);
9d303b37
DL
445 if (parent && (listnode_lookup(vertex->parents, parent)
446 == NULL))
d62a17ae 447 listnode_add(vertex->parents, parent);
d62a17ae 448 return;
449 } else if (vertex->d_N < cost) {
450 /* e) do nothing */
451 return;
452 } else { /* vertex->d_N > cost */
453 /* f) */
eb919f07 454 isis_vertex_queue_delete(&spftree->tents, vertex);
d62a17ae 455 isis_vertex_del(vertex);
456 }
f390d2c7 457 }
d62a17ae 458
459 isis_spf_add2tent(spftree, vtype, id, cost, 1, adj, parent);
460 return;
eb5d44eb 461}
462
d62a17ae 463static void process_N(struct isis_spftree *spftree, enum vertextype vtype,
464 void *id, uint32_t dist, uint16_t depth,
465 struct isis_vertex *parent)
eb5d44eb 466{
d62a17ae 467 struct isis_vertex *vertex;
eb5d44eb 468#ifdef EXTREME_DEBUG
321c1bbb 469 char buff[VID2STR_BUFFER];
eb5d44eb 470#endif
471
d62a17ae 472 assert(spftree && parent);
473
b30e837b
CF
474 if (spftree->hopcount_metric
475 && !VTYPE_IS(vtype))
476 return;
477
321c1bbb 478 struct prefix_pair p;
af8ac8f9 479 if (vtype >= VTYPE_IPREACH_INTERNAL) {
321c1bbb
CF
480 memcpy(&p, id, sizeof(p));
481 apply_mask(&p.dest);
482 apply_mask((struct prefix *)&p.src);
af8ac8f9
CF
483 id = &p;
484 }
485
d62a17ae 486 /* RFC3787 section 5.1 */
487 if (spftree->area->newmetric == 1) {
488 if (dist > MAX_WIDE_PATH_METRIC)
489 return;
490 }
491 /* C.2.6 b) */
492 else if (spftree->area->oldmetric == 1) {
493 if (dist > MAX_NARROW_PATH_METRIC)
494 return;
495 }
496
497 /* c) */
eb919f07 498 vertex = isis_find_vertex(&spftree->paths, id, vtype);
d62a17ae 499 if (vertex) {
eb5d44eb 500#ifdef EXTREME_DEBUG
d62a17ae 501 zlog_debug(
502 "ISIS-Spf: process_N %s %s %s dist %d already found from PATH",
503 print_sys_hostname(vertex->N.id), vtype2string(vtype),
504 vid2string(vertex, buff, sizeof(buff)), dist);
eb5d44eb 505#endif /* EXTREME_DEBUG */
d62a17ae 506 assert(dist >= vertex->d_N);
507 return;
508 }
509
eb919f07 510 vertex = isis_find_vertex(&spftree->tents, id, vtype);
d62a17ae 511 /* d) */
512 if (vertex) {
513/* 1) */
eb5d44eb 514#ifdef EXTREME_DEBUG
d62a17ae 515 zlog_debug(
516 "ISIS-Spf: process_N %s %s %s dist %d parent %s adjcount %d",
517 print_sys_hostname(vertex->N.id), vtype2string(vtype),
518 vid2string(vertex, buff, sizeof(buff)), dist,
519 (parent ? print_sys_hostname(parent->N.id) : "null"),
520 (parent ? listcount(parent->Adj_N) : 0));
eb5d44eb 521#endif /* EXTREME_DEBUG */
d62a17ae 522 if (vertex->d_N == dist) {
523 struct listnode *node;
524 struct isis_adjacency *parent_adj;
525 for (ALL_LIST_ELEMENTS_RO(parent->Adj_N, node,
526 parent_adj))
527 if (listnode_lookup(vertex->Adj_N, parent_adj)
528 == NULL)
529 listnode_add(vertex->Adj_N, parent_adj);
686afe9f
CF
530 if (spftree->hopcount_metric)
531 vertex_update_firsthops(vertex, parent);
d62a17ae 532 /* 2) */
533 if (listcount(vertex->Adj_N) > ISIS_MAX_PATH_SPLITS)
534 remove_excess_adjs(vertex->Adj_N);
535 if (listnode_lookup(vertex->parents, parent) == NULL)
536 listnode_add(vertex->parents, parent);
d62a17ae 537 return;
538 } else if (vertex->d_N < dist) {
539 return;
540 /* 4) */
541 } else {
eb919f07 542 isis_vertex_queue_delete(&spftree->tents, vertex);
d62a17ae 543 isis_vertex_del(vertex);
544 }
f390d2c7 545 }
f390d2c7 546
3f045a08 547#ifdef EXTREME_DEBUG
d62a17ae 548 zlog_debug("ISIS-Spf: process_N add2tent %s %s dist %d parent %s",
549 print_sys_hostname(id), vtype2string(vtype), dist,
550 (parent ? print_sys_hostname(parent->N.id) : "null"));
3f045a08
JB
551#endif /* EXTREME_DEBUG */
552
d62a17ae 553 isis_spf_add2tent(spftree, vtype, id, dist, depth, NULL, parent);
554 return;
eb5d44eb 555}
556
557/*
558 * C.2.6 Step 1
559 */
d62a17ae 560static int isis_spf_process_lsp(struct isis_spftree *spftree,
561 struct isis_lsp *lsp, uint32_t cost,
d7c0a89a 562 uint16_t depth, uint8_t *root_sysid,
d62a17ae 563 struct isis_vertex *parent)
eb5d44eb 564{
af8ac8f9
CF
565 bool pseudo_lsp = LSP_PSEUDO_ID(lsp->hdr.lsp_id);
566 struct listnode *fragnode = NULL;
d62a17ae 567 uint32_t dist;
d62a17ae 568 enum vertextype vtype;
d7c0a89a 569 static const uint8_t null_sysid[ISIS_SYS_ID_LEN];
af8ac8f9 570 struct isis_mt_router_info *mt_router_info = NULL;
321c1bbb 571 struct prefix_pair ip_info;
af8ac8f9
CF
572
573 if (!lsp->tlvs)
574 return ISIS_OK;
d62a17ae 575
576 if (spftree->mtid != ISIS_MT_IPV4_UNICAST)
af8ac8f9
CF
577 mt_router_info = isis_tlvs_lookup_mt_router_info(lsp->tlvs,
578 spftree->mtid);
d62a17ae 579
9d303b37 580 if (!pseudo_lsp && (spftree->mtid == ISIS_MT_IPV4_UNICAST
af8ac8f9
CF
581 && !speaks(lsp->tlvs->protocols_supported.protocols,
582 lsp->tlvs->protocols_supported.count,
583 spftree->family))
d62a17ae 584 && !mt_router_info)
585 return ISIS_OK;
eb5d44eb 586
39a275aa 587 /* RFC3787 section 4 SHOULD ignore overload bit in pseudo LSPs */
98c5bc15
CF
588 bool no_overload = (pseudo_lsp
589 || (spftree->mtid == ISIS_MT_IPV4_UNICAST
39a275aa 590 && !ISIS_MASK_LSP_OL_BIT(lsp->hdr.lsp_bits))
98c5bc15 591 || (mt_router_info && !mt_router_info->overload));
39a275aa 592
f390d2c7 593lspfragloop:
af8ac8f9 594 if (lsp->hdr.seqno == 0) {
d62a17ae 595 zlog_warn(
596 "isis_spf_process_lsp(): lsp with 0 seq_num - ignore");
597 return ISIS_WARNING;
598 }
f390d2c7 599
3f045a08 600#ifdef EXTREME_DEBUG
d62a17ae 601 zlog_debug("ISIS-Spf: process_lsp %s",
af8ac8f9 602 print_sys_hostname(lsp->hdr.lsp_id));
3f045a08
JB
603#endif /* EXTREME_DEBUG */
604
39a275aa 605 if (no_overload) {
d62a17ae 606 if (pseudo_lsp || spftree->mtid == ISIS_MT_IPV4_UNICAST) {
af8ac8f9
CF
607 struct isis_oldstyle_reach *r;
608 for (r = (struct isis_oldstyle_reach *)
609 lsp->tlvs->oldstyle_reach.head;
610 r; r = r->next) {
d62a17ae 611 /* C.2.6 a) */
612 /* Two way connectivity */
af8ac8f9 613 if (!memcmp(r->id, root_sysid, ISIS_SYS_ID_LEN))
d62a17ae 614 continue;
615 if (!pseudo_lsp
af8ac8f9 616 && !memcmp(r->id, null_sysid,
d62a17ae 617 ISIS_SYS_ID_LEN))
618 continue;
af8ac8f9 619 dist = cost + r->metric;
d62a17ae 620 process_N(spftree,
af8ac8f9 621 LSP_PSEUDO_ID(r->id)
d62a17ae 622 ? VTYPE_PSEUDO_IS
623 : VTYPE_NONPSEUDO_IS,
af8ac8f9
CF
624 (void *)r->id, dist, depth + 1,
625 parent);
d62a17ae 626 }
627 }
628
af8ac8f9
CF
629 struct isis_item_list *te_neighs = NULL;
630 if (pseudo_lsp || spftree->mtid == ISIS_MT_IPV4_UNICAST)
631 te_neighs = &lsp->tlvs->extended_reach;
632 else
633 te_neighs = isis_lookup_mt_items(&lsp->tlvs->mt_reach,
634 spftree->mtid);
635
636 struct isis_extended_reach *er;
637 for (er = te_neighs
638 ? (struct isis_extended_reach *)
639 te_neighs->head
640 : NULL;
641 er; er = er->next) {
642 if (!memcmp(er->id, root_sysid, ISIS_SYS_ID_LEN))
d62a17ae 643 continue;
644 if (!pseudo_lsp
af8ac8f9 645 && !memcmp(er->id, null_sysid, ISIS_SYS_ID_LEN))
d62a17ae 646 continue;
b30e837b 647 dist = cost + (spftree->hopcount_metric ? 1 : er->metric);
d62a17ae 648 process_N(spftree,
af8ac8f9
CF
649 LSP_PSEUDO_ID(er->id) ? VTYPE_PSEUDO_TE_IS
650 : VTYPE_NONPSEUDO_TE_IS,
651 (void *)er->id, dist, depth + 1, parent);
d62a17ae 652 }
f390d2c7 653 }
d62a17ae 654
655 if (!pseudo_lsp && spftree->family == AF_INET
656 && spftree->mtid == ISIS_MT_IPV4_UNICAST) {
af8ac8f9
CF
657 struct isis_item_list *reachs[] = {
658 &lsp->tlvs->oldstyle_ip_reach,
659 &lsp->tlvs->oldstyle_ip_reach_ext};
d62a17ae 660
d62a17ae 661 for (unsigned int i = 0; i < array_size(reachs); i++) {
af8ac8f9
CF
662 vtype = i ? VTYPE_IPREACH_EXTERNAL
663 : VTYPE_IPREACH_INTERNAL;
664
321c1bbb
CF
665 memset(&ip_info, 0, sizeof(ip_info));
666 ip_info.dest.family = AF_INET;
667
af8ac8f9
CF
668 struct isis_oldstyle_ip_reach *r;
669 for (r = (struct isis_oldstyle_ip_reach *)reachs[i]
670 ->head;
671 r; r = r->next) {
672 dist = cost + r->metric;
321c1bbb
CF
673 ip_info.dest.u.prefix4 = r->prefix.prefix;
674 ip_info.dest.prefixlen = r->prefix.prefixlen;
675 process_N(spftree, vtype, &ip_info,
af8ac8f9 676 dist, depth + 1, parent);
d62a17ae 677 }
678 }
f390d2c7 679 }
d62a17ae 680
681 if (!pseudo_lsp && spftree->family == AF_INET) {
af8ac8f9
CF
682 struct isis_item_list *ipv4_reachs;
683 if (spftree->mtid == ISIS_MT_IPV4_UNICAST)
684 ipv4_reachs = &lsp->tlvs->extended_ip_reach;
685 else
686 ipv4_reachs = isis_lookup_mt_items(
687 &lsp->tlvs->mt_ip_reach, spftree->mtid);
688
321c1bbb
CF
689 memset(&ip_info, 0, sizeof(ip_info));
690 ip_info.dest.family = AF_INET;
691
af8ac8f9
CF
692 struct isis_extended_ip_reach *r;
693 for (r = ipv4_reachs
694 ? (struct isis_extended_ip_reach *)
695 ipv4_reachs->head
696 : NULL;
697 r; r = r->next) {
698 dist = cost + r->metric;
321c1bbb
CF
699 ip_info.dest.u.prefix4 = r->prefix.prefix;
700 ip_info.dest.prefixlen = r->prefix.prefixlen;
701 process_N(spftree, VTYPE_IPREACH_TE, &ip_info,
d62a17ae 702 dist, depth + 1, parent);
f390d2c7 703 }
f390d2c7 704 }
d62a17ae 705
706 if (!pseudo_lsp && spftree->family == AF_INET6) {
af8ac8f9
CF
707 struct isis_item_list *ipv6_reachs;
708 if (spftree->mtid == ISIS_MT_IPV4_UNICAST)
709 ipv6_reachs = &lsp->tlvs->ipv6_reach;
710 else
711 ipv6_reachs = isis_lookup_mt_items(
712 &lsp->tlvs->mt_ipv6_reach, spftree->mtid);
713
714 struct isis_ipv6_reach *r;
715 for (r = ipv6_reachs
716 ? (struct isis_ipv6_reach *)ipv6_reachs->head
717 : NULL;
718 r; r = r->next) {
719 dist = cost + r->metric;
720 vtype = r->external ? VTYPE_IP6REACH_EXTERNAL
721 : VTYPE_IP6REACH_INTERNAL;
321c1bbb
CF
722 memset(&ip_info, 0, sizeof(ip_info));
723 ip_info.dest.family = AF_INET6;
724 ip_info.dest.u.prefix6 = r->prefix.prefix;
725 ip_info.dest.prefixlen = r->prefix.prefixlen;
726
727 if (r->subtlvs
728 && r->subtlvs->source_prefix
729 && r->subtlvs->source_prefix->prefixlen) {
730 if (spftree->tree_id != SPFTREE_DSTSRC) {
731 char buff[VID2STR_BUFFER];
732 zlog_warn("Ignoring dest-src route %s in non dest-src topology",
733 srcdest2str(
734 &ip_info.dest,
735 r->subtlvs->source_prefix,
736 buff, sizeof(buff)
737 )
738 );
739 continue;
740 }
741 ip_info.src = *r->subtlvs->source_prefix;
742 }
743 process_N(spftree, vtype, &ip_info, dist,
d62a17ae 744 depth + 1, parent);
745 }
f390d2c7 746 }
d62a17ae 747
748 if (fragnode == NULL)
749 fragnode = listhead(lsp->lspu.frags);
750 else
751 fragnode = listnextnode(fragnode);
752
753 if (fragnode) {
754 lsp = listgetdata(fragnode);
755 goto lspfragloop;
756 }
757
758 return ISIS_OK;
759}
760
761static int isis_spf_preload_tent(struct isis_spftree *spftree,
d7c0a89a
QY
762 uint8_t *root_sysid,
763 struct isis_vertex *parent)
d62a17ae 764{
765 struct isis_circuit *circuit;
766 struct listnode *cnode, *anode, *ipnode;
767 struct isis_adjacency *adj;
768 struct isis_lsp *lsp;
769 struct list *adj_list;
770 struct list *adjdb;
771 struct prefix_ipv4 *ipv4;
321c1bbb 772 struct prefix_pair ip_info;
d62a17ae 773 int retval = ISIS_OK;
d7c0a89a
QY
774 uint8_t lsp_id[ISIS_SYS_ID_LEN + 2];
775 static uint8_t null_lsp_id[ISIS_SYS_ID_LEN + 2];
d62a17ae 776 struct prefix_ipv6 *ipv6;
777 struct isis_circuit_mt_setting *circuit_mt;
778
779 for (ALL_LIST_ELEMENTS_RO(spftree->area->circuit_list, cnode,
780 circuit)) {
781 circuit_mt = circuit_lookup_mt_setting(circuit, spftree->mtid);
782 if (circuit_mt && !circuit_mt->enabled)
783 continue;
784 if (circuit->state != C_STATE_UP)
785 continue;
786 if (!(circuit->is_type & spftree->level))
787 continue;
788 if (spftree->family == AF_INET && !circuit->ip_router)
789 continue;
790 if (spftree->family == AF_INET6 && !circuit->ipv6_router)
791 continue;
792 /*
793 * Add IP(v6) addresses of this circuit
794 */
b30e837b 795 if (spftree->family == AF_INET && !spftree->hopcount_metric) {
321c1bbb
CF
796 memset(&ip_info, 0, sizeof(ip_info));
797 ip_info.dest.family = AF_INET;
d62a17ae 798 for (ALL_LIST_ELEMENTS_RO(circuit->ip_addrs, ipnode,
799 ipv4)) {
321c1bbb
CF
800 ip_info.dest.u.prefix4 = ipv4->prefix;
801 ip_info.dest.prefixlen = ipv4->prefixlen;
802 apply_mask(&ip_info.dest);
d62a17ae 803 isis_spf_add_local(spftree,
804 VTYPE_IPREACH_INTERNAL,
321c1bbb 805 &ip_info, NULL, 0, parent);
d62a17ae 806 }
807 }
b30e837b 808 if (spftree->family == AF_INET6 && !spftree->hopcount_metric) {
321c1bbb
CF
809 memset(&ip_info, 0, sizeof(ip_info));
810 ip_info.dest.family = AF_INET6;
d62a17ae 811 for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_non_link,
812 ipnode, ipv6)) {
321c1bbb
CF
813 ip_info.dest.u.prefix6 = ipv6->prefix;
814 ip_info.dest.prefixlen = ipv6->prefixlen;
815 apply_mask(&ip_info.dest);
d62a17ae 816 isis_spf_add_local(spftree,
817 VTYPE_IP6REACH_INTERNAL,
321c1bbb 818 &ip_info, NULL, 0, parent);
d62a17ae 819 }
820 }
821 if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
822 /*
823 * Add the adjacencies
824 */
825 adj_list = list_new();
826 adjdb = circuit->u.bc.adjdb[spftree->level - 1];
827 isis_adj_build_up_list(adjdb, adj_list);
828 if (listcount(adj_list) == 0) {
affe9e99 829 list_delete_and_null(&adj_list);
d62a17ae 830 if (isis->debugs & DEBUG_SPF_EVENTS)
831 zlog_debug(
832 "ISIS-Spf: no L%d adjacencies on circuit %s",
833 spftree->level,
834 circuit->interface->name);
835 continue;
836 }
837 for (ALL_LIST_ELEMENTS_RO(adj_list, anode, adj)) {
838 if (!adj_has_mt(adj, spftree->mtid))
839 continue;
840 if (spftree->mtid == ISIS_MT_IPV4_UNICAST
af8ac8f9
CF
841 && !speaks(adj->nlpids.nlpids,
842 adj->nlpids.count,
843 spftree->family))
d62a17ae 844 continue;
845 switch (adj->sys_type) {
846 case ISIS_SYSTYPE_ES:
847 memcpy(lsp_id, adj->sysid,
848 ISIS_SYS_ID_LEN);
849 LSP_PSEUDO_ID(lsp_id) = 0;
850 isis_spf_add_local(
851 spftree, VTYPE_ES, lsp_id, adj,
b30e837b 852 spftree->hopcount_metric ? 1 :
d62a17ae 853 circuit->te_metric
854 [spftree->level - 1],
855 parent);
856 break;
857 case ISIS_SYSTYPE_IS:
858 case ISIS_SYSTYPE_L1_IS:
859 case ISIS_SYSTYPE_L2_IS:
860 memcpy(lsp_id, adj->sysid,
861 ISIS_SYS_ID_LEN);
862 LSP_PSEUDO_ID(lsp_id) = 0;
863 LSP_FRAGMENT(lsp_id) = 0;
864 isis_spf_add_local(
865 spftree,
866 spftree->area->oldmetric
867 ? VTYPE_NONPSEUDO_IS
868 : VTYPE_NONPSEUDO_TE_IS,
869 lsp_id, adj,
b30e837b 870 spftree->hopcount_metric ? 1 :
d62a17ae 871 circuit->te_metric
872 [spftree->level - 1],
873 parent);
874 lsp = lsp_search(
875 lsp_id,
876 spftree->area
877 ->lspdb[spftree->level
878 - 1]);
879 if (lsp == NULL
af8ac8f9 880 || lsp->hdr.rem_lifetime == 0)
d62a17ae 881 zlog_warn(
882 "ISIS-Spf: No LSP %s found for IS adjacency "
883 "L%d on %s (ID %u)",
884 rawlspid_print(lsp_id),
885 spftree->level,
886 circuit->interface->name,
887 circuit->circuit_id);
888 break;
889 case ISIS_SYSTYPE_UNKNOWN:
890 default:
891 zlog_warn(
892 "isis_spf_preload_tent unknow adj type");
893 }
894 }
affe9e99 895 list_delete_and_null(&adj_list);
d62a17ae 896 /*
897 * Add the pseudonode
898 */
899 if (spftree->level == 1)
900 memcpy(lsp_id, circuit->u.bc.l1_desig_is,
901 ISIS_SYS_ID_LEN + 1);
902 else
903 memcpy(lsp_id, circuit->u.bc.l2_desig_is,
904 ISIS_SYS_ID_LEN + 1);
905 /* can happen during DR reboot */
906 if (memcmp(lsp_id, null_lsp_id, ISIS_SYS_ID_LEN + 1)
907 == 0) {
908 if (isis->debugs & DEBUG_SPF_EVENTS)
909 zlog_debug(
910 "ISIS-Spf: No L%d DR on %s (ID %d)",
911 spftree->level,
912 circuit->interface->name,
913 circuit->circuit_id);
914 continue;
915 }
916 adj = isis_adj_lookup(lsp_id, adjdb);
917 /* if no adj, we are the dis or error */
918 if (!adj && !circuit->u.bc.is_dr[spftree->level - 1]) {
919 zlog_warn(
920 "ISIS-Spf: No adjacency found from root "
921 "to L%d DR %s on %s (ID %d)",
922 spftree->level, rawlspid_print(lsp_id),
923 circuit->interface->name,
924 circuit->circuit_id);
925 continue;
926 }
927 lsp = lsp_search(
928 lsp_id,
929 spftree->area->lspdb[spftree->level - 1]);
af8ac8f9 930 if (lsp == NULL || lsp->hdr.rem_lifetime == 0) {
d62a17ae 931 zlog_warn(
932 "ISIS-Spf: No lsp (%p) found from root "
933 "to L%d DR %s on %s (ID %d)",
934 (void *)lsp, spftree->level,
935 rawlspid_print(lsp_id),
936 circuit->interface->name,
937 circuit->circuit_id);
938 continue;
939 }
b30e837b
CF
940 isis_spf_process_lsp(spftree, lsp,
941 spftree->hopcount_metric ?
942 1 : circuit->te_metric[spftree->level - 1],
943 0, root_sysid, parent);
d62a17ae 944 } else if (circuit->circ_type == CIRCUIT_T_P2P) {
945 adj = circuit->u.p2p.neighbor;
44b89511 946 if (!adj || adj->adj_state != ISIS_ADJ_UP)
d62a17ae 947 continue;
948 if (!adj_has_mt(adj, spftree->mtid))
949 continue;
950 switch (adj->sys_type) {
951 case ISIS_SYSTYPE_ES:
952 memcpy(lsp_id, adj->sysid, ISIS_SYS_ID_LEN);
953 LSP_PSEUDO_ID(lsp_id) = 0;
954 isis_spf_add_local(
955 spftree, VTYPE_ES, lsp_id, adj,
b30e837b 956 spftree->hopcount_metric ? 1 :
d62a17ae 957 circuit->te_metric[spftree->level - 1],
958 parent);
959 break;
960 case ISIS_SYSTYPE_IS:
961 case ISIS_SYSTYPE_L1_IS:
962 case ISIS_SYSTYPE_L2_IS:
963 memcpy(lsp_id, adj->sysid, ISIS_SYS_ID_LEN);
964 LSP_PSEUDO_ID(lsp_id) = 0;
965 LSP_FRAGMENT(lsp_id) = 0;
966 if (spftree->mtid != ISIS_MT_IPV4_UNICAST
af8ac8f9
CF
967 || speaks(adj->nlpids.nlpids,
968 adj->nlpids.count,
969 spftree->family))
d62a17ae 970 isis_spf_add_local(
971 spftree,
972 spftree->area->oldmetric
973 ? VTYPE_NONPSEUDO_IS
974 : VTYPE_NONPSEUDO_TE_IS,
975 lsp_id, adj,
b30e837b 976 spftree->hopcount_metric ? 1 :
d62a17ae 977 circuit->te_metric
978 [spftree->level - 1],
979 parent);
980 break;
981 case ISIS_SYSTYPE_UNKNOWN:
982 default:
983 zlog_warn(
984 "isis_spf_preload_tent unknown adj type");
985 break;
986 }
987 } else if (circuit->circ_type == CIRCUIT_T_LOOPBACK) {
988 continue;
989 } else {
990 zlog_warn("isis_spf_preload_tent unsupported media");
991 retval = ISIS_WARNING;
992 }
f390d2c7 993 }
eb5d44eb 994
d62a17ae 995 return retval;
eb5d44eb 996}
997
998/*
999 * The parent(s) for vertex is set when added to TENT list
1000 * now we just put the child pointer(s) in place
1001 */
d62a17ae 1002static void add_to_paths(struct isis_spftree *spftree,
1003 struct isis_vertex *vertex)
eb5d44eb 1004{
321c1bbb 1005 char buff[VID2STR_BUFFER];
3f045a08 1006
ae9c9aba 1007 if (isis_find_vertex(&spftree->paths, &vertex->N, vertex->type))
d62a17ae 1008 return;
bded4060 1009 isis_vertex_queue_append(&spftree->paths, vertex);
eb5d44eb 1010
f390d2c7 1011#ifdef EXTREME_DEBUG
d62a17ae 1012 zlog_debug("ISIS-Spf: added %s %s %s depth %d dist %d to PATHS",
1013 print_sys_hostname(vertex->N.id), vtype2string(vertex->type),
1014 vid2string(vertex, buff, sizeof(buff)), vertex->depth,
1015 vertex->d_N);
f390d2c7 1016#endif /* EXTREME_DEBUG */
3f045a08 1017
d62a17ae 1018 if (VTYPE_IP(vertex->type)) {
1019 if (listcount(vertex->Adj_N) > 0)
321c1bbb
CF
1020 isis_route_create(&vertex->N.ip.dest,
1021 &vertex->N.ip.src,
d62a17ae 1022 vertex->d_N, vertex->depth,
1023 vertex->Adj_N, spftree->area,
3dace42d 1024 spftree->route_table);
d62a17ae 1025 else if (isis->debugs & DEBUG_SPF_EVENTS)
1026 zlog_debug(
1027 "ISIS-Spf: no adjacencies do not install route for "
1028 "%s depth %d dist %d",
1029 vid2string(vertex, buff, sizeof(buff)),
1030 vertex->depth, vertex->d_N);
1031 }
1032
1033 return;
eb5d44eb 1034}
1035
d62a17ae 1036static void init_spt(struct isis_spftree *spftree, int mtid, int level,
b30e837b
CF
1037 int family, enum spf_tree_id tree_id,
1038 bool hopcount_metric)
eb5d44eb 1039{
eb919f07
CF
1040 isis_vertex_queue_clear(&spftree->tents);
1041 isis_vertex_queue_clear(&spftree->paths);
d62a17ae 1042
1043 spftree->mtid = mtid;
1044 spftree->level = level;
1045 spftree->family = family;
6d38d078 1046 spftree->tree_id = tree_id;
b30e837b
CF
1047 spftree->hopcount_metric = hopcount_metric;
1048}
1049
1050static void isis_spf_loop(struct isis_spftree *spftree,
1051 uint8_t *root_sysid)
1052{
1053 struct isis_vertex *vertex;
b30e837b
CF
1054 struct isis_lsp *lsp;
1055
1056 while (isis_vertex_queue_count(&spftree->tents)) {
1057 vertex = isis_vertex_queue_pop(&spftree->tents);
1058
1059#ifdef EXTREME_DEBUG
1060 zlog_debug(
1061 "ISIS-Spf: get TENT node %s %s depth %d dist %d to PATHS",
1062 print_sys_hostname(vertex->N.id),
1063 vtype2string(vertex->type), vertex->depth, vertex->d_N);
1064#endif /* EXTREME_DEBUG */
1065
1066 add_to_paths(spftree, vertex);
cbd8e49e
CF
1067 if (!VTYPE_IS(vertex->type))
1068 continue;
1069
1070 lsp = lsp_for_vertex(spftree, vertex);
1071 if (!lsp) {
1072 zlog_warn("ISIS-Spf: No LSP found for %s",
1073 rawlspid_print(vertex->N.id)); /* FIXME */
1074 continue;
b30e837b 1075 }
cbd8e49e
CF
1076
1077 isis_spf_process_lsp(spftree, lsp, vertex->d_N, vertex->depth,
1078 root_sysid, vertex);
b30e837b
CF
1079 }
1080}
1081
1082struct isis_spftree *isis_run_hopcount_spf(struct isis_area *area,
1083 uint8_t *sysid,
1084 struct isis_spftree *spftree)
1085{
1086 if (!spftree)
1087 spftree = isis_spftree_new(area);
1088
1089 init_spt(spftree, ISIS_MT_IPV4_UNICAST, ISIS_LEVEL2,
1090 AF_INET, SPFTREE_IPV4, true);
1091 if (!memcmp(sysid, isis->sysid, ISIS_SYS_ID_LEN)) {
1092 /* If we are running locally, initialize with information from adjacencies */
1093 struct isis_vertex *root = isis_spf_add_root(spftree, sysid);
1094 isis_spf_preload_tent(spftree, sysid, root);
1095 } else {
1096 isis_vertex_queue_insert(&spftree->tents, isis_vertex_new(
686afe9f
CF
1097 spftree, sysid,
1098 VTYPE_NONPSEUDO_TE_IS));
b30e837b
CF
1099 }
1100
1101 isis_spf_loop(spftree, sysid);
1102
1103 return spftree;
eb5d44eb 1104}
1105
6d38d078
CF
1106static int isis_run_spf(struct isis_area *area, int level,
1107 enum spf_tree_id tree_id,
d7c0a89a 1108 uint8_t *sysid, struct timeval *nowtv)
eb5d44eb 1109{
d62a17ae 1110 int retval = ISIS_OK;
d62a17ae 1111 struct isis_vertex *root_vertex;
6d38d078 1112 struct isis_spftree *spftree = area->spftree[tree_id][level - 1];
d62a17ae 1113 struct timeval time_now;
1114 unsigned long long start_time, end_time;
6d38d078 1115 uint16_t mtid = 0;
d62a17ae 1116
1117 /* Get time that can't roll backwards. */
a699dd69
RZ
1118 start_time = nowtv->tv_sec;
1119 start_time = (start_time * 1000000) + nowtv->tv_usec;
d62a17ae 1120
6d38d078
CF
1121 int family = -1;
1122 switch (tree_id) {
1123 case SPFTREE_IPV4:
1124 family = AF_INET;
1125 mtid = ISIS_MT_IPV4_UNICAST;
1126 break;
1127 case SPFTREE_IPV6:
1128 family = AF_INET6;
1129 mtid = isis_area_ipv6_topology(area);
1130 break;
321c1bbb
CF
1131 case SPFTREE_DSTSRC:
1132 family = AF_INET6;
1133 mtid = ISIS_MT_IPV6_DSTSRC;
1134 break;
6d38d078
CF
1135 case SPFTREE_COUNT:
1136 assert(!"isis_run_spf should never be called with SPFTREE_COUNT as argument!");
1137 return ISIS_WARNING;
1138 }
1139
d62a17ae 1140 assert(spftree);
1141 assert(sysid);
1142
d62a17ae 1143 /*
1144 * C.2.5 Step 0
1145 */
b30e837b 1146 init_spt(spftree, mtid, level, family, tree_id, false);
d62a17ae 1147 /* a) */
1148 root_vertex = isis_spf_add_root(spftree, sysid);
1149 /* b) */
1150 retval = isis_spf_preload_tent(spftree, sysid, root_vertex);
1151 if (retval != ISIS_OK) {
1152 zlog_warn("ISIS-Spf: failed to load TENT SPF-root:%s",
1153 print_sys_hostname(sysid));
1154 goto out;
1155 }
1156
1157 /*
1158 * C.2.7 Step 2
1159 */
ce837d81
CF
1160 if (!isis_vertex_queue_count(&spftree->tents)
1161 && (isis->debugs & DEBUG_SPF_EVENTS)) {
d62a17ae 1162 zlog_warn("ISIS-Spf: TENT is empty SPF-root:%s",
1163 print_sys_hostname(sysid));
d62a17ae 1164 }
1165
b30e837b 1166 isis_spf_loop(spftree, sysid);
13fb40ac 1167out:
d62a17ae 1168 spftree->runcount++;
1169 spftree->last_run_timestamp = time(NULL);
3dca3c8c 1170 spftree->last_run_monotime = monotime(&time_now);
d62a17ae 1171 end_time = time_now.tv_sec;
1172 end_time = (end_time * 1000000) + time_now.tv_usec;
1173 spftree->last_run_duration = end_time - start_time;
1174
1175 return retval;
eb5d44eb 1176}
1177
3dace42d
CF
1178void isis_spf_verify_routes(struct isis_area *area, struct isis_spftree **trees)
1179{
1180 if (area->is_type == IS_LEVEL_1) {
1181 isis_route_verify_table(area, trees[0]->route_table);
1182 } else if (area->is_type == IS_LEVEL_2) {
1183 isis_route_verify_table(area, trees[1]->route_table);
1184 } else {
1185 isis_route_verify_merge(area, trees[0]->route_table,
1186 trees[1]->route_table);
1187 }
1188}
1189
1190void isis_spf_invalidate_routes(struct isis_spftree *tree)
1191{
1192 isis_route_invalidate_table(tree->area, tree->route_table);
1193}
1194
d62a17ae 1195static int isis_run_spf_cb(struct thread *thread)
eb5d44eb 1196{
d62a17ae 1197 struct isis_spf_run *run = THREAD_ARG(thread);
1198 struct isis_area *area = run->area;
1199 int level = run->level;
1200 int retval = ISIS_OK;
1201
1202 XFREE(MTYPE_ISIS_SPF_RUN, run);
1203 area->spf_timer[level - 1] = NULL;
1204
1205 if (!(area->is_type & level)) {
1206 if (isis->debugs & DEBUG_SPF_EVENTS)
1207 zlog_warn("ISIS-SPF (%s) area does not share level",
1208 area->area_tag);
1209 return ISIS_WARNING;
1210 }
eb5d44eb 1211
3dace42d
CF
1212 isis_area_invalidate_routes(area, level);
1213
d62a17ae 1214 if (isis->debugs & DEBUG_SPF_EVENTS)
1215 zlog_debug("ISIS-Spf (%s) L%d SPF needed, periodic SPF",
1216 area->area_tag, level);
f390d2c7 1217
d62a17ae 1218 if (area->ip_circuits)
6d38d078 1219 retval = isis_run_spf(area, level, SPFTREE_IPV4, isis->sysid,
996c9314 1220 &thread->real);
d62a17ae 1221 if (area->ipv6_circuits)
6d38d078 1222 retval = isis_run_spf(area, level, SPFTREE_IPV6, isis->sysid,
996c9314 1223 &thread->real);
321c1bbb
CF
1224 if (area->ipv6_circuits
1225 && isis_area_ipv6_dstsrc_enabled(area))
1226 retval = isis_run_spf(area, level, SPFTREE_DSTSRC, isis->sysid,
1227 &thread->real);
eb5d44eb 1228
3dace42d
CF
1229 isis_area_verify_routes(area);
1230
1231 /* walk all circuits and reset any spf specific flags */
1232 struct listnode *node;
1233 struct isis_circuit *circuit;
1234 for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit))
1235 UNSET_FLAG(circuit->flags, ISIS_CIRCUIT_FLAPPED_AFTER_SPF);
1236
b30e837b
CF
1237 fabricd_run_spf(area);
1238
d62a17ae 1239 return retval;
eb5d44eb 1240}
1241
d62a17ae 1242static struct isis_spf_run *isis_run_spf_arg(struct isis_area *area, int level)
eb5d44eb 1243{
d62a17ae 1244 struct isis_spf_run *run = XMALLOC(MTYPE_ISIS_SPF_RUN, sizeof(*run));
1245
1246 run->area = area;
1247 run->level = level;
1248
1249 return run;
eb5d44eb 1250}
eb5d44eb 1251
d62a17ae 1252int isis_spf_schedule(struct isis_area *area, int level)
eb5d44eb 1253{
be985ba0 1254 struct isis_spftree *spftree = area->spftree[SPFTREE_IPV4][level - 1];
3dca3c8c
CF
1255 time_t now = monotime(NULL);
1256 int diff = now - spftree->last_run_monotime;
d62a17ae 1257
1258 assert(diff >= 0);
1259 assert(area->is_type & level);
1260
1261 if (isis->debugs & DEBUG_SPF_EVENTS)
1262 zlog_debug(
1263 "ISIS-Spf (%s) L%d SPF schedule called, lastrun %d sec ago",
1264 area->area_tag, level, diff);
1265
1266 if (area->spf_delay_ietf[level - 1]) {
1267 /* Need to call schedule function also if spf delay is running
1268 * to
1269 * restart holdoff timer - compare
1270 * draft-ietf-rtgwg-backoff-algo-04 */
1271 long delay =
1272 spf_backoff_schedule(area->spf_delay_ietf[level - 1]);
1273 if (area->spf_timer[level - 1])
1274 return ISIS_OK;
1275
1276 thread_add_timer_msec(master, isis_run_spf_cb,
1277 isis_run_spf_arg(area, level), delay,
1278 &area->spf_timer[level - 1]);
1279 return ISIS_OK;
3f045a08 1280 }
d62a17ae 1281
1282 if (area->spf_timer[level - 1])
1283 return ISIS_OK;
1284
1285 /* wait configured min_spf_interval before doing the SPF */
3dca3c8c 1286 long timer;
d62a17ae 1287 if (diff >= area->min_spf_interval[level - 1]) {
98c5bc15 1288 /* Last run is more than min interval ago, schedule immediate run */
3dca3c8c
CF
1289 timer = 0;
1290 } else {
1291 timer = area->min_spf_interval[level - 1] - diff;
f390d2c7 1292 }
3f045a08 1293
d62a17ae 1294 thread_add_timer(master, isis_run_spf_cb, isis_run_spf_arg(area, level),
3dca3c8c 1295 timer, &area->spf_timer[level - 1]);
d62a17ae 1296
1297 if (isis->debugs & DEBUG_SPF_EVENTS)
013b29d7
RZ
1298 zlog_debug("ISIS-Spf (%s) L%d SPF scheduled %ld sec from now",
1299 area->area_tag, level, timer);
d62a17ae 1300
1301 return ISIS_OK;
1302}
1303
eb919f07 1304static void isis_print_paths(struct vty *vty, struct isis_vertex_queue *queue,
d7c0a89a 1305 uint8_t *root_sysid)
d62a17ae 1306{
1307 struct listnode *node;
d62a17ae 1308 struct isis_vertex *vertex;
321c1bbb 1309 char buff[VID2STR_BUFFER];
d62a17ae 1310
1311 vty_out(vty,
1312 "Vertex Type Metric Next-Hop Interface Parent\n");
1313
eb919f07 1314 for (ALL_QUEUE_ELEMENTS_RO(queue, node, vertex)) {
d62a17ae 1315 if (memcmp(vertex->N.id, root_sysid, ISIS_SYS_ID_LEN) == 0) {
1316 vty_out(vty, "%-20s %-12s %-6s",
1317 print_sys_hostname(root_sysid), "", "");
af88c591
CF
1318 vty_out(vty, "%-30s\n", "");
1319 continue;
d62a17ae 1320 }
1321
af88c591
CF
1322 int rows = 0;
1323 struct listnode *anode = listhead(vertex->Adj_N);
1324 struct listnode *pnode = listhead(vertex->parents);
1325 struct isis_adjacency *adj;
1326 struct isis_vertex *pvertex;
1327
1328 vty_out(vty, "%-20s %-12s %-6u ",
1329 vid2string(vertex, buff, sizeof(buff)),
1330 vtype2string(vertex->type), vertex->d_N);
1230a82d
A
1331 for (unsigned int i = 0;
1332 i < MAX(vertex->Adj_N ? listcount(vertex->Adj_N) : 0,
1333 vertex->parents ? listcount(vertex->parents) : 0);
996c9314 1334 i++) {
af88c591
CF
1335 if (anode) {
1336 adj = listgetdata(anode);
1337 anode = anode->next;
1338 } else {
1339 adj = NULL;
1340 }
1341
1342 if (pnode) {
1343 pvertex = listgetdata(pnode);
1344 pnode = pnode->next;
1345 } else {
1346 pvertex = NULL;
1347 }
1348
1349 if (rows) {
1350 vty_out(vty, "\n");
1351 vty_out(vty, "%-20s %-12s %-6s ", "", "", "");
1352 }
1353
1354 if (adj) {
1355 vty_out(vty, "%-20s %-9s ",
1356 print_sys_hostname(adj->sysid),
1357 adj->circuit->interface->name);
1358 }
1359
1360 if (pvertex) {
1361 if (!adj)
1362 vty_out(vty, "%-20s %-9s ", "", "");
1363
d62a17ae 1364 vty_out(vty, "%s(%d)",
996c9314 1365 vid2string(pvertex, buff, sizeof(buff)),
d62a17ae 1366 pvertex->type);
d62a17ae 1367 }
d62a17ae 1368
af88c591
CF
1369 ++rows;
1370 }
d62a17ae 1371 vty_out(vty, "\n");
1372 }
eb5d44eb 1373}
1374
321c1bbb
CF
1375static void isis_print_spftree(struct vty *vty, int level,
1376 struct isis_area *area,
1377 enum spf_tree_id tree_id)
1378{
1379 const char *tree_id_text = NULL;
1380
1381 switch (tree_id) {
1382 case SPFTREE_IPV4:
1383 tree_id_text = "that speak IP";
1384 break;
1385 case SPFTREE_IPV6:
1386 tree_id_text = "that speak IPv6";
1387 break;
1388 case SPFTREE_DSTSRC:
1389 tree_id_text = "that support IPv6 dst-src routing";
1390 break;
1391 case SPFTREE_COUNT:
1392 assert(!"isis_print_spftree shouldn't be called with SPFTREE_COUNT as type");
1393 return;
1394 }
1395
1396 if (!area->spftree[tree_id][level - 1]
1397 || !isis_vertex_queue_count(
1398 &area->spftree[tree_id][level - 1]->paths))
1399 return;
1400
1401 vty_out(vty, "IS-IS paths to level-%d routers %s\n",
1402 level, tree_id_text);
1403 isis_print_paths(vty, &area->spftree[tree_id][level - 1]->paths,
1404 isis->sysid);
1405 vty_out(vty, "\n");
1406}
1407
eb5d44eb 1408DEFUN (show_isis_topology,
1409 show_isis_topology_cmd,
ef020087
CF
1410 "show " PROTO_NAME " topology"
1411#ifndef FABRICD
1412 " [<level-1|level-2>]"
1413#endif
1414 , SHOW_STR
7c0cbd0e 1415 PROTO_HELP
1b49e4f0 1416 "IS-IS paths to Intermediate Systems\n"
ef020087 1417#ifndef FABRICD
1b49e4f0 1418 "Paths to all level-1 routers in the area\n"
ef020087
CF
1419 "Paths to all level-2 routers in the domain\n"
1420#endif
1421 )
eb5d44eb 1422{
d62a17ae 1423 int levels;
1424 struct listnode *node;
1425 struct isis_area *area;
1426
1427 if (argc < 4)
1428 levels = ISIS_LEVEL1 | ISIS_LEVEL2;
1429 else if (strmatch(argv[3]->text, "level-1"))
1430 levels = ISIS_LEVEL1;
1431 else
1432 levels = ISIS_LEVEL2;
1433
1434 if (!isis->area_list || isis->area_list->count == 0)
1435 return CMD_SUCCESS;
1436
1437 for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) {
1438 vty_out(vty, "Area %s:\n",
1439 area->area_tag ? area->area_tag : "null");
1440
1441 for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) {
1442 if ((level & levels) == 0)
1443 continue;
1444
321c1bbb
CF
1445 if (area->ip_circuits > 0) {
1446 isis_print_spftree(vty, level, area,
1447 SPFTREE_IPV4);
d62a17ae 1448 }
321c1bbb
CF
1449 if (area->ipv6_circuits > 0) {
1450 isis_print_spftree(vty, level, area,
1451 SPFTREE_IPV6);
1452 }
1453 if (isis_area_ipv6_dstsrc_enabled(area)) {
1454 isis_print_spftree(vty, level, area,
1455 SPFTREE_DSTSRC);
d62a17ae 1456 }
1457 }
f390d2c7 1458
b30e837b
CF
1459 if (fabricd_spftree(area)) {
1460 vty_out(vty,
1461 "IS-IS paths to level-2 routers with hop-by-hop metric\n");
1462 isis_print_paths(vty, &fabricd_spftree(area)->paths, isis->sysid);
1463 vty_out(vty, "\n");
1464 }
1465
d62a17ae 1466 vty_out(vty, "\n");
f390d2c7 1467 }
3f045a08 1468
d62a17ae 1469 return CMD_SUCCESS;
f390d2c7 1470}
eb5d44eb 1471
d62a17ae 1472void isis_spf_cmds_init()
eb5d44eb 1473{
d62a17ae 1474 install_element(VIEW_NODE, &show_isis_topology_cmd);
eb5d44eb 1475}
02cd317e
CF
1476
1477void isis_spf_print(struct isis_spftree *spftree, struct vty *vty)
1478{
1479 vty_out(vty, " last run elapsed : ");
1480 vty_out_timestr(vty, spftree->last_run_timestamp);
1481 vty_out(vty, "\n");
1482
1483 vty_out(vty, " last run duration : %u usec\n",
d7c0a89a 1484 (uint32_t)spftree->last_run_duration);
02cd317e 1485
996c9314 1486 vty_out(vty, " run count : %u\n", spftree->runcount);
02cd317e 1487}