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