]> git.proxmox.com Git - mirror_frr.git/blob - isisd/isis_spf.c
No warnings here any more.
[mirror_frr.git] / isisd / isis_spf.c
1 /*
2 * IS-IS Rout(e)ing protocol - isis_spf.c
3 * The SPT algorithm
4 *
5 * Copyright (C) 2001,2002 Sampo Saaristo
6 * Tampere University of Technology
7 * Institute of Communications Engineering
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public Licenseas published by the Free
11 * Software Foundation; either version 2 of the License, or (at your option)
12 * any later version.
13 *
14 * This program is distributed in the hope that it will be useful,but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17 * more details.
18
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 */
23
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <zebra.h>
27
28 #include "thread.h"
29 #include "linklist.h"
30 #include "vty.h"
31 #include "log.h"
32 #include "command.h"
33 #include "memory.h"
34 #include "prefix.h"
35 #include "hash.h"
36 #include "if.h"
37 #include "table.h"
38
39 #include "isis_constants.h"
40 #include "isis_common.h"
41 #include "dict.h"
42 #include "isisd.h"
43 #include "isis_misc.h"
44 #include "isis_adjacency.h"
45 #include "isis_circuit.h"
46 #include "isis_tlv.h"
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"
53
54 extern struct isis *isis;
55 extern struct thread_master *master;
56 extern struct host host;
57
58 int isis_run_spf_l1 (struct thread *thread);
59 int isis_run_spf_l2 (struct thread *thread);
60
61 /* performace issue ???? */
62 void
63 union_adjlist (struct list *target, struct list *source)
64 {
65 struct isis_adjacency *adj, *adj2;
66 struct listnode *node, *node2;
67
68 zlog_info ("Union adjlist!");
69 for (node = listhead (source); node; nextnode (node))
70 {
71 adj = getdata (node);
72
73 /* lookup adjacency in the source list */
74 for (node2 = listhead (target); node2; nextnode (node2))
75 {
76 adj2 = getdata (node2);
77 if (adj == adj2)
78 break;
79 }
80
81 if (!node2)
82 listnode_add (target, adj);
83 }
84 }
85
86 /* 7.2.7 */
87 void
88 remove_excess_adjs (struct list *adjs)
89 {
90 struct listnode *node, *excess = NULL;
91 struct isis_adjacency *adj, *candidate = NULL;
92 int comp;
93
94 for (node = listhead (adjs); node; nextnode (node))
95 {
96 if (excess == NULL)
97 excess = node;
98 candidate = getdata (excess);
99 adj = getdata (node);
100 if (candidate->sys_type < adj->sys_type)
101 {
102 excess = node;
103 candidate = adj;
104 continue;
105 }
106 if (candidate->sys_type > adj->sys_type)
107 continue;
108
109 comp = memcmp (candidate->sysid, adj->sysid, ISIS_SYS_ID_LEN);
110 if (comp > 0)
111 {
112 excess = node;
113 candidate = adj;
114 continue;
115 }
116 if (comp < 0)
117 continue;
118
119 if (candidate->circuit->circuit_id > adj->circuit->circuit_id)
120 {
121 excess = node;
122 candidate = adj;
123 continue;
124 }
125
126 if (candidate->circuit->circuit_id < adj->circuit->circuit_id)
127 continue;
128
129 comp = memcmp (candidate->snpa, adj->snpa, ETH_ALEN);
130 if (comp > 0)
131 {
132 excess = node;
133 candidate = adj;
134 continue;
135 }
136 }
137
138 list_delete_node (adjs, excess);
139
140 return;
141 }
142
143 const char *
144 vtype2string (enum vertextype vtype)
145 {
146 switch (vtype)
147 {
148 case VTYPE_PSEUDO_IS:
149 return "pseudo_IS";
150 break;
151 case VTYPE_NONPSEUDO_IS:
152 return "IS";
153 break;
154 case VTYPE_ES:
155 return "ES";
156 break;
157 case VTYPE_IPREACH_INTERNAL:
158 return "IP internal";
159 break;
160 case VTYPE_IPREACH_EXTERNAL:
161 return "IP external";
162 break;
163 #ifdef HAVE_IPV6
164 case VTYPE_IP6REACH_INTERNAL:
165 return "IP6 internal";
166 break;
167 case VTYPE_IP6REACH_EXTERNAL:
168 return "IP6 external";
169 break;
170 #endif /* HAVE_IPV6 */
171 default:
172 return "UNKNOWN";
173 }
174 return NULL; /* Not reached */
175 }
176
177 const char *
178 vid2string (struct isis_vertex *vertex, u_char * buff)
179 {
180 switch (vertex->type)
181 {
182 case VTYPE_PSEUDO_IS:
183 return rawlspid_print (vertex->N.id);
184 break;
185 case VTYPE_NONPSEUDO_IS:
186 case VTYPE_ES:
187 return sysid_print (vertex->N.id);
188 break;
189 case VTYPE_IPREACH_INTERNAL:
190 case VTYPE_IPREACH_EXTERNAL:
191 #ifdef HAVE_IPV6
192 case VTYPE_IP6REACH_INTERNAL:
193 case VTYPE_IP6REACH_EXTERNAL:
194 #endif /* HAVE_IPV6 */
195 prefix2str ((struct prefix *) &vertex->N.prefix, (char *) buff, BUFSIZ);
196 break;
197 default:
198 return "UNKNOWN";
199 }
200
201 return (char *) buff;
202 }
203
204 struct isis_spftree *
205 isis_spftree_new ()
206 {
207 struct isis_spftree *tree;
208
209 tree = XMALLOC (MTYPE_ISIS_SPFTREE, sizeof (struct isis_spftree));
210 if (tree == NULL)
211 {
212 zlog_err ("ISIS-Spf: isis_spftree_new Out of memory!");
213 return NULL;
214 }
215 memset (tree, 0, sizeof (struct isis_spftree));
216
217 tree->tents = list_new ();
218 tree->paths = list_new ();
219 return tree;
220 }
221
222 void
223 isis_vertex_del (struct isis_vertex *vertex)
224 {
225 list_delete (vertex->Adj_N);
226
227 XFREE (MTYPE_ISIS_VERTEX, vertex);
228
229 return;
230 }
231
232 void
233 isis_spftree_del (struct isis_spftree *spftree)
234 {
235 spftree->tents->del = (void (*)(void *)) isis_vertex_del;
236 list_delete (spftree->tents);
237
238 spftree->paths->del = (void (*)(void *)) isis_vertex_del;
239 list_delete (spftree->paths);
240
241 XFREE (MTYPE_ISIS_SPFTREE, spftree);
242
243 return;
244 }
245
246 void
247 spftree_area_init (struct isis_area *area)
248 {
249 if ((area->is_type & IS_LEVEL_1) && area->spftree[0] == NULL)
250 {
251 area->spftree[0] = isis_spftree_new ();
252 #ifdef HAVE_IPV6
253 area->spftree6[0] = isis_spftree_new ();
254 #endif
255
256 /* thread_add_timer (master, isis_run_spf_l1, area,
257 isis_jitter (PERIODIC_SPF_INTERVAL, 10)); */
258 }
259
260 if ((area->is_type & IS_LEVEL_2) && area->spftree[1] == NULL)
261 {
262 area->spftree[1] = isis_spftree_new ();
263 #ifdef HAVE_IPV6
264 area->spftree6[1] = isis_spftree_new ();
265 #endif
266 /* thread_add_timer (master, isis_run_spf_l2, area,
267 isis_jitter (PERIODIC_SPF_INTERVAL, 10)); */
268 }
269
270 return;
271 }
272
273 struct isis_vertex *
274 isis_vertex_new (void *id, enum vertextype vtype)
275 {
276 struct isis_vertex *vertex;
277
278 vertex = XMALLOC (MTYPE_ISIS_VERTEX, sizeof (struct isis_vertex));
279 if (vertex == NULL)
280 {
281 zlog_err ("isis_vertex_new Out of memory!");
282 return NULL;
283 }
284
285 memset (vertex, 0, sizeof (struct isis_vertex));
286 vertex->type = vtype;
287 switch (vtype)
288 {
289 case VTYPE_ES:
290 case VTYPE_NONPSEUDO_IS:
291 memcpy (vertex->N.id, (u_char *) id, ISIS_SYS_ID_LEN);
292 break;
293 case VTYPE_PSEUDO_IS:
294 memcpy (vertex->N.id, (u_char *) id, ISIS_SYS_ID_LEN + 1);
295 break;
296 case VTYPE_IPREACH_INTERNAL:
297 case VTYPE_IPREACH_EXTERNAL:
298 #ifdef HAVE_IPV6
299 case VTYPE_IP6REACH_INTERNAL:
300 case VTYPE_IP6REACH_EXTERNAL:
301 #endif /* HAVE_IPV6 */
302 memcpy (&vertex->N.prefix, (struct prefix *) id,
303 sizeof (struct prefix));
304 break;
305 default:
306 zlog_err ("WTF!");
307 }
308
309 vertex->Adj_N = list_new ();
310
311 return vertex;
312 }
313
314 /*
315 * Add this IS to the root of SPT
316 */
317 void
318 isis_spf_add_self (struct isis_spftree *spftree, struct isis_area *area,
319 int level)
320 {
321 struct isis_vertex *vertex;
322 struct isis_lsp *lsp;
323 u_char lspid[ISIS_SYS_ID_LEN + 2];
324 #ifdef EXTREME_DEBUG
325 u_char buff[BUFSIZ];
326 #endif /* EXTREME_DEBUG */
327 memcpy (lspid, isis->sysid, ISIS_SYS_ID_LEN);
328 LSP_PSEUDO_ID (lspid) = 0;
329 LSP_FRAGMENT (lspid) = 0;
330
331 lsp = lsp_search (lspid, area->lspdb[level - 1]);
332
333 if (lsp == NULL)
334 zlog_warn ("ISIS-Spf: could not find own l%d LSP!", level);
335
336 vertex = isis_vertex_new (isis->sysid, VTYPE_NONPSEUDO_IS);
337 vertex->lsp = lsp;
338
339 listnode_add (spftree->paths, vertex);
340
341 #ifdef EXTREME_DEBUG
342 zlog_info ("ISIS-Spf: added this IS %s %s depth %d dist %d to PATHS",
343 vtype2string (vertex->type), vid2string (vertex, buff),
344 vertex->depth, vertex->d_N);
345 #endif /* EXTREME_DEBUG */
346
347 return;
348 }
349
350 struct isis_vertex *
351 isis_find_vertex (struct list *list, void *id, enum vertextype vtype)
352 {
353 struct listnode *node;
354 struct isis_vertex *vertex;
355 struct prefix *p1, *p2;
356
357 for (node = listhead (list); node; nextnode (node))
358 {
359 vertex = getdata (node);
360 if (vertex->type != vtype)
361 continue;
362 switch (vtype)
363 {
364 case VTYPE_ES:
365 case VTYPE_NONPSEUDO_IS:
366 if (memcmp ((u_char *) id, vertex->N.id, ISIS_SYS_ID_LEN) == 0)
367 return vertex;
368 break;
369 case VTYPE_PSEUDO_IS:
370 if (memcmp ((u_char *) id, vertex->N.id, ISIS_SYS_ID_LEN + 1) == 0)
371 return vertex;
372 break;
373 case VTYPE_IPREACH_INTERNAL:
374 case VTYPE_IPREACH_EXTERNAL:
375 #ifdef HAVE_IPV6
376 case VTYPE_IP6REACH_INTERNAL:
377 case VTYPE_IP6REACH_EXTERNAL:
378 #endif /* HAVE_IPV6 */
379 p1 = (struct prefix *) id;
380 p2 = (struct prefix *) &vertex->N.id;
381 if (p1->family == p2->family && p1->prefixlen == p2->prefixlen &&
382 memcmp (&p1->u.prefix, &p2->u.prefix,
383 PSIZE (p1->prefixlen)) == 0)
384 return vertex;
385 break;
386 }
387 }
388
389 return NULL;
390 }
391
392 /*
393 * Add a vertex to TENT sorted by cost and by vertextype on tie break situation
394 */
395 struct isis_vertex *
396 isis_spf_add2tent (struct isis_spftree *spftree, enum vertextype vtype,
397 void *id, struct isis_adjacency *adj, u_int16_t cost,
398 int depth, int family)
399 {
400 struct isis_vertex *vertex, *v;
401 struct listnode *node;
402 #ifdef EXTREME_DEBUG
403 u_char buff[BUFSIZ];
404 #endif
405
406 vertex = isis_vertex_new (id, vtype);
407 vertex->d_N = cost;
408 vertex->depth = depth;
409
410 if (adj)
411 listnode_add (vertex->Adj_N, adj);
412 #ifdef EXTREME_DEBUG
413 zlog_info ("ISIS-Spf: add to TENT %s %s depth %d dist %d",
414 vtype2string (vertex->type), vid2string (vertex, buff),
415 vertex->depth, vertex->d_N);
416 #endif /* EXTREME_DEBUG */
417 listnode_add (spftree->tents, vertex);
418 if (list_isempty (spftree->tents))
419 {
420 listnode_add (spftree->tents, vertex);
421 return vertex;
422 }
423 for (node = listhead (spftree->tents); node; nextnode (node))
424 {
425 v = getdata (node);
426 if (v->d_N > vertex->d_N)
427 {
428 list_add_node_prev (spftree->tents, node, vertex);
429 break;
430 }
431 else if (v->d_N == vertex->d_N)
432 {
433 /* Tie break, add according to type */
434 while (v && v->d_N == vertex->d_N && v->type > vertex->type)
435 {
436 if (v->type > vertex->type)
437 {
438 break;
439 }
440 nextnode (node);
441 (node) ? (v = getdata (node)) : (v = NULL);
442 }
443 list_add_node_prev (spftree->tents, node, vertex);
444 break;
445 }
446 else if (node->next == NULL)
447 {
448 list_add_node_next (spftree->tents, node, vertex);
449 break;
450 }
451 }
452 return vertex;
453 }
454
455 struct isis_vertex *
456 isis_spf_add_local (struct isis_spftree *spftree, enum vertextype vtype,
457 void *id, struct isis_adjacency *adj, u_int16_t cost,
458 int family)
459 {
460 struct isis_vertex *vertex;
461
462 vertex = isis_find_vertex (spftree->tents, id, vtype);
463
464 if (vertex)
465 {
466 /* C.2.5 c) */
467 if (vertex->d_N == cost)
468 {
469 if (adj)
470 listnode_add (vertex->Adj_N, adj);
471 /* d) */
472 if (listcount (vertex->Adj_N) > ISIS_MAX_PATH_SPLITS)
473 remove_excess_adjs (vertex->Adj_N);
474 }
475 /* f) */
476 else if (vertex->d_N > cost)
477 {
478 listnode_delete (spftree->tents, vertex);
479 goto add2tent;
480 }
481 /* e) do nothing */
482 return vertex;
483 }
484
485 add2tent:
486 return isis_spf_add2tent (spftree, vtype, id, adj, cost, 1, family);
487 }
488
489 void
490 process_N (struct isis_spftree *spftree, enum vertextype vtype, void *id,
491 u_int16_t dist, u_int16_t depth, struct isis_adjacency *adj,
492 int family)
493 {
494 struct isis_vertex *vertex;
495 #ifdef EXTREME_DEBUG
496 u_char buff[255];
497 #endif
498
499 /* C.2.6 b) */
500 if (dist > MAX_PATH_METRIC)
501 return;
502 /* c) */
503 vertex = isis_find_vertex (spftree->paths, id, vtype);
504 if (vertex)
505 {
506 #ifdef EXTREME_DEBUG
507 zlog_info ("ISIS-Spf: process_N %s %s dist %d already found from PATH",
508 vtype2string (vtype), vid2string (vertex, buff), dist);
509 #endif /* EXTREME_DEBUG */
510 assert (dist >= vertex->d_N);
511 return;
512 }
513
514 vertex = isis_find_vertex (spftree->tents, id, vtype);
515 /* d) */
516 if (vertex)
517 {
518 /* 1) */
519 #ifdef EXTREME_DEBUG
520 zlog_info ("ISIS-Spf: process_N %s %s dist %d",
521 vtype2string (vtype), vid2string (vertex, buff), dist);
522 #endif /* EXTREME_DEBUG */
523 if (vertex->d_N == dist)
524 {
525 if (adj)
526 listnode_add (vertex->Adj_N, adj);
527 /* 2) */
528 if (listcount (vertex->Adj_N) > ISIS_MAX_PATH_SPLITS)
529 remove_excess_adjs (vertex->Adj_N);
530 /* 3) */
531 return;
532 }
533 else if (vertex->d_N < dist)
534 {
535 return;
536 /* 4) */
537 }
538 else
539 {
540 listnode_delete (spftree->tents, vertex);
541 }
542 }
543
544 isis_spf_add2tent (spftree, vtype, id, adj, dist, depth, family);
545 return;
546 }
547
548 /*
549 * C.2.6 Step 1
550 */
551 int
552 isis_spf_process_lsp (struct isis_spftree *spftree, struct isis_lsp *lsp,
553 uint16_t cost, uint16_t depth, int family)
554 {
555 struct listnode *node, *fragnode = NULL;
556 u_int16_t dist;
557 struct is_neigh *is_neigh;
558 struct ipv4_reachability *ipreach;
559 enum vertextype vtype;
560 struct prefix prefix;
561 #ifdef HAVE_IPV6
562 struct ipv6_reachability *ip6reach;
563 #endif /* HAVE_IPV6 */
564
565
566 if (!lsp->adj)
567 return ISIS_WARNING;
568 if (lsp->tlv_data.nlpids == NULL || !speaks (lsp->tlv_data.nlpids, family))
569 return ISIS_OK;
570
571 lspfragloop:
572 if (lsp->lsp_header->seq_num == 0)
573 {
574 zlog_warn ("isis_spf_process_lsp(): lsp with 0 seq_num"
575 " - do not process");
576 return ISIS_WARNING;
577 }
578
579 if (!ISIS_MASK_LSP_OL_BIT (lsp->lsp_header->lsp_bits))
580 {
581 if (lsp->tlv_data.is_neighs)
582 {
583 for (node = listhead (lsp->tlv_data.is_neighs); node;
584 nextnode (node))
585 {
586 is_neigh = getdata (node);
587 /* C.2.6 a) */
588 /* Two way connectivity */
589 if (!memcmp (is_neigh->neigh_id, isis->sysid, ISIS_SYS_ID_LEN))
590 continue;
591 dist = cost + is_neigh->metrics.metric_default;
592 vtype = LSP_PSEUDO_ID (is_neigh->neigh_id) ? VTYPE_PSEUDO_IS
593 : VTYPE_NONPSEUDO_IS;
594 process_N (spftree, vtype, (void *) is_neigh->neigh_id, dist,
595 depth + 1, lsp->adj, family);
596 }
597 }
598 if (family == AF_INET && lsp->tlv_data.ipv4_int_reachs)
599 {
600 prefix.family = AF_INET;
601 for (node = listhead (lsp->tlv_data.ipv4_int_reachs); node;
602 nextnode (node))
603 {
604 ipreach = getdata (node);
605 dist = cost + ipreach->metrics.metric_default;
606 vtype = VTYPE_IPREACH_INTERNAL;
607 prefix.u.prefix4 = ipreach->prefix;
608 prefix.prefixlen = ip_masklen (ipreach->mask);
609 process_N (spftree, vtype, (void *) &prefix, dist, depth + 1,
610 lsp->adj, family);
611 }
612 }
613
614 if (family == AF_INET && lsp->tlv_data.ipv4_ext_reachs)
615 {
616 prefix.family = AF_INET;
617 for (node = listhead (lsp->tlv_data.ipv4_ext_reachs); node;
618 nextnode (node))
619 {
620 ipreach = getdata (node);
621 dist = cost + ipreach->metrics.metric_default;
622 vtype = VTYPE_IPREACH_EXTERNAL;
623 prefix.u.prefix4 = ipreach->prefix;
624 prefix.prefixlen = ip_masklen (ipreach->mask);
625 process_N (spftree, vtype, (void *) &prefix, dist, depth + 1,
626 lsp->adj, family);
627 }
628 }
629 #ifdef HAVE_IPV6
630 if (family == AF_INET6 && lsp->tlv_data.ipv6_reachs)
631 {
632 prefix.family = AF_INET6;
633 for (node = listhead (lsp->tlv_data.ipv6_reachs); node;
634 nextnode (node))
635 {
636 ip6reach = getdata (node);
637 dist = cost + ip6reach->metric;
638 vtype = (ip6reach->control_info & CTRL_INFO_DISTRIBUTION) ?
639 VTYPE_IP6REACH_EXTERNAL : VTYPE_IP6REACH_INTERNAL;
640 prefix.prefixlen = ip6reach->prefix_len;
641 memcpy (&prefix.u.prefix6.s6_addr, ip6reach->prefix,
642 PSIZE (ip6reach->prefix_len));
643 process_N (spftree, vtype, (void *) &prefix, dist, depth + 1,
644 lsp->adj, family);
645 }
646 }
647 #endif /* HAVE_IPV6 */
648 }
649
650 if (fragnode == NULL)
651 fragnode = listhead (lsp->lspu.frags);
652 else
653 nextnode (fragnode);
654
655 if (fragnode)
656 {
657 lsp = getdata (fragnode);
658 goto lspfragloop;
659 }
660
661 return ISIS_OK;
662 }
663
664 int
665 isis_spf_process_pseudo_lsp (struct isis_spftree *spftree,
666 struct isis_lsp *lsp, uint16_t cost,
667 uint16_t depth, int family)
668 {
669 struct listnode *node, *fragnode = NULL;
670 struct is_neigh *is_neigh;
671 enum vertextype vtype;
672
673 pseudofragloop:
674
675 if (lsp->lsp_header->seq_num == 0)
676 {
677 zlog_warn ("isis_spf_process_pseudo_lsp(): lsp with 0 seq_num"
678 " - do not process");
679 return ISIS_WARNING;
680 }
681
682 for (node = (lsp->tlv_data.is_neighs ?
683 listhead (lsp->tlv_data.is_neighs) : NULL);
684 node; nextnode (node))
685 {
686 is_neigh = getdata (node);
687 vtype = LSP_PSEUDO_ID (is_neigh->neigh_id) ? VTYPE_PSEUDO_IS
688 : VTYPE_NONPSEUDO_IS;
689 /* Two way connectivity */
690 if (!memcmp (is_neigh->neigh_id, isis->sysid, ISIS_SYS_ID_LEN))
691 continue;
692 if (isis_find_vertex
693 (spftree->tents, (void *) is_neigh->neigh_id, vtype) == NULL
694 && isis_find_vertex (spftree->paths, (void *) is_neigh->neigh_id,
695 vtype) == NULL)
696 {
697 /* C.2.5 i) */
698 isis_spf_add2tent (spftree, vtype, is_neigh->neigh_id, lsp->adj,
699 cost, depth, family);
700 }
701 }
702
703 if (fragnode == NULL)
704 fragnode = listhead (lsp->lspu.frags);
705 else
706 nextnode (fragnode);
707
708 if (fragnode)
709 {
710 lsp = getdata (fragnode);
711 goto pseudofragloop;
712 }
713
714 return ISIS_OK;
715 }
716
717 int
718 isis_spf_preload_tent (struct isis_spftree *spftree,
719 struct isis_area *area, int level, int family)
720 {
721 struct isis_vertex *vertex;
722 struct isis_circuit *circuit;
723 struct listnode *cnode, *anode, *ipnode;
724 struct isis_adjacency *adj;
725 struct isis_lsp *lsp;
726 struct list *adj_list;
727 struct list *adjdb;
728 struct prefix_ipv4 *ipv4;
729 struct prefix prefix;
730 int retval = ISIS_OK;
731 u_char lsp_id[ISIS_SYS_ID_LEN + 2];
732 #ifdef HAVE_IPV6
733 struct prefix_ipv6 *ipv6;
734 #endif /* HAVE_IPV6 */
735
736 for (cnode = listhead (area->circuit_list); cnode; nextnode (cnode))
737 {
738 circuit = getdata (cnode);
739 if (circuit->state != C_STATE_UP)
740 continue;
741 if (!(circuit->circuit_is_type & level))
742 continue;
743 if (family == AF_INET && !circuit->ip_router)
744 continue;
745 #ifdef HAVE_IPV6
746 if (family == AF_INET6 && !circuit->ipv6_router)
747 continue;
748 #endif /* HAVE_IPV6 */
749 /*
750 * Add IP(v6) addresses of this circuit
751 */
752 if (family == AF_INET)
753 {
754 prefix.family = AF_INET;
755 for (ipnode =
756 (circuit->ip_addrs ? listhead (circuit->ip_addrs) : NULL);
757 ipnode; nextnode (ipnode))
758 {
759 ipv4 = getdata (ipnode);
760 prefix.u.prefix4 = ipv4->prefix;
761 prefix.prefixlen = ipv4->prefixlen;
762 isis_spf_add_local (spftree, VTYPE_IPREACH_INTERNAL, &prefix,
763 NULL, 0, family);
764 }
765 }
766 #ifdef HAVE_IPV6
767 if (family == AF_INET6)
768 {
769 prefix.family = AF_INET6;
770 for (ipnode = (circuit->ipv6_non_link ? listhead
771 (circuit->ipv6_non_link) : NULL); ipnode;
772 nextnode (ipnode))
773 {
774 ipv6 = getdata (ipnode);
775 prefix.prefixlen = ipv6->prefixlen;
776 prefix.u.prefix6 = ipv6->prefix;
777 isis_spf_add_local (spftree, VTYPE_IP6REACH_INTERNAL,
778 &prefix, NULL, 0, family);
779 }
780 }
781 #endif /* HAVE_IPV6 */
782 if (circuit->circ_type == CIRCUIT_T_BROADCAST)
783 {
784 /*
785 * Add the adjacencies
786 */
787 adj_list = list_new ();
788 adjdb = circuit->u.bc.adjdb[level - 1];
789 isis_adj_build_up_list (adjdb, adj_list);
790 if (listcount (adj_list) == 0)
791 {
792 list_delete (adj_list);
793 zlog_warn ("ISIS-Spf: no L%d adjacencies on circuit %s",
794 level, circuit->interface->name);
795 continue;
796 }
797 anode = listhead (adj_list);
798 while (anode)
799 {
800 adj = getdata (anode);
801 if (!speaks (&adj->nlpids, family))
802 {
803 nextnode (anode);
804 continue;
805 }
806 switch (adj->sys_type)
807 {
808 case ISIS_SYSTYPE_ES:
809 isis_spf_add_local (spftree, VTYPE_ES, adj->sysid, adj,
810 circuit->metrics[level -
811 1].metric_default,
812 family);
813 break;
814 case ISIS_SYSTYPE_IS:
815 case ISIS_SYSTYPE_L1_IS:
816 case ISIS_SYSTYPE_L2_IS:
817 vertex =
818 isis_spf_add_local (spftree, VTYPE_NONPSEUDO_IS,
819 adj->sysid, adj,
820 circuit->metrics[level -
821 1].metric_default,
822 family);
823 memcpy (lsp_id, adj->sysid, ISIS_SYS_ID_LEN);
824 LSP_PSEUDO_ID (lsp_id) = 0;
825 LSP_FRAGMENT (lsp_id) = 0;
826 lsp = lsp_search (lsp_id, area->lspdb[level - 1]);
827 if (!lsp)
828 zlog_warn ("No lsp found for IS adjacency");
829 /* else {
830 isis_spf_process_lsp (spftree, lsp, vertex->d_N, 1, family);
831 } */
832 break;
833 case ISIS_SYSTYPE_UNKNOWN:
834 default:
835 zlog_warn ("isis_spf_preload_tent unknow adj type");
836 }
837 nextnode (anode);
838 }
839 list_delete (adj_list);
840 /*
841 * Add the pseudonode
842 */
843 if (level == 1)
844 memcpy (lsp_id, circuit->u.bc.l1_desig_is, ISIS_SYS_ID_LEN + 1);
845 else
846 memcpy (lsp_id, circuit->u.bc.l2_desig_is, ISIS_SYS_ID_LEN + 1);
847 lsp = lsp_search (lsp_id, area->lspdb[level - 1]);
848 adj = isis_adj_lookup (lsp_id, adjdb);
849 /* if no adj, we are the dis or error */
850 if (!adj && !circuit->u.bc.is_dr[level - 1])
851 {
852 zlog_warn ("ISIS-Spf: No adjacency found for DR");
853 }
854 if (lsp == NULL || lsp->lsp_header->rem_lifetime == 0)
855 {
856 zlog_warn ("ISIS-Spf: No lsp found for DR");
857 }
858 else
859 {
860 isis_spf_process_pseudo_lsp
861 (spftree, lsp, circuit->metrics[level - 1].metric_default, 0,
862 family);
863
864 }
865 }
866 else if (circuit->circ_type == CIRCUIT_T_P2P)
867 {
868 adj = circuit->u.p2p.neighbor;
869 if (!adj)
870 continue;
871 switch (adj->sys_type)
872 {
873 case ISIS_SYSTYPE_ES:
874 isis_spf_add_local (spftree, VTYPE_ES, adj->sysid, adj,
875 circuit->metrics[level - 1].metric_default,
876 family);
877 break;
878 case ISIS_SYSTYPE_IS:
879 case ISIS_SYSTYPE_L1_IS:
880 case ISIS_SYSTYPE_L2_IS:
881 if (speaks (&adj->nlpids, family))
882 isis_spf_add_local (spftree, VTYPE_NONPSEUDO_IS, adj->sysid,
883 adj,
884 circuit->metrics[level -
885 1].metric_default,
886 family);
887 break;
888 case ISIS_SYSTYPE_UNKNOWN:
889 default:
890 zlog_warn ("isis_spf_preload_tent unknow adj type");
891 break;
892 }
893 }
894 else
895 {
896 zlog_warn ("isis_spf_preload_tent unsupported media");
897 retval = ISIS_WARNING;
898 }
899
900 }
901
902 return retval;
903 }
904
905 /*
906 * The parent(s) for vertex is set when added to TENT list
907 * now we just put the child pointer(s) in place
908 */
909 void
910 add_to_paths (struct isis_spftree *spftree, struct isis_vertex *vertex,
911 struct isis_area *area)
912 {
913 #ifdef EXTREME_DEBUG
914 u_char buff[BUFSIZ];
915 #endif /* EXTREME_DEBUG */
916 listnode_add (spftree->paths, vertex);
917
918 #ifdef EXTREME_DEBUG
919 zlog_info ("ISIS-Spf: added %s %s depth %d dist %d to PATHS",
920 vtype2string (vertex->type), vid2string (vertex, buff),
921 vertex->depth, vertex->d_N);
922 #endif /* EXTREME_DEBUG */
923 if (vertex->type > VTYPE_ES)
924 {
925 if (listcount (vertex->Adj_N) > 0)
926 isis_route_create ((struct prefix *) &vertex->N.prefix,
927 vertex->d_N, vertex->depth, vertex->Adj_N, area);
928 else if (isis->debugs & DEBUG_SPF_EVENTS)
929 zlog_info ("ISIS-Spf: no adjacencies do not install route");
930 }
931
932 return;
933 }
934
935 void
936 init_spt (struct isis_spftree *spftree)
937 {
938 spftree->tents->del = spftree->paths->del = (void (*)(void *)) isis_vertex_del;
939 list_delete_all_node (spftree->tents);
940 list_delete_all_node (spftree->paths);
941 spftree->tents->del = spftree->paths->del = NULL;
942
943 return;
944 }
945
946 int
947 isis_run_spf (struct isis_area *area, int level, int family)
948 {
949 int retval = ISIS_OK;
950 struct listnode *node;
951 struct isis_vertex *vertex;
952 struct isis_spftree *spftree = NULL;
953 u_char lsp_id[ISIS_SYS_ID_LEN + 2];
954 struct isis_lsp *lsp;
955
956 if (family == AF_INET)
957 spftree = area->spftree[level - 1];
958 #ifdef HAVE_IPV6
959 else if (family == AF_INET6)
960 spftree = area->spftree6[level - 1];
961 #endif
962
963 assert (spftree);
964
965 /*
966 * C.2.5 Step 0
967 */
968 init_spt (spftree);
969 /* a) */
970 isis_spf_add_self (spftree, area, level);
971 /* b) */
972 retval = isis_spf_preload_tent (spftree, area, level, family);
973
974 /*
975 * C.2.7 Step 2
976 */
977 if (listcount (spftree->tents) == 0)
978 {
979 zlog_warn ("ISIS-Spf: TENT is empty");
980 spftree->lastrun = time (NULL);
981 return retval;
982 }
983
984 while (listcount (spftree->tents) > 0)
985 {
986 node = listhead (spftree->tents);
987 vertex = getdata (node);
988 /* Remove from tent list */
989 list_delete_node (spftree->tents, node);
990 if (isis_find_vertex (spftree->paths, vertex->N.id, vertex->type))
991 continue;
992 add_to_paths (spftree, vertex, area);
993 if (vertex->type == VTYPE_PSEUDO_IS ||
994 vertex->type == VTYPE_NONPSEUDO_IS)
995 {
996 memcpy (lsp_id, vertex->N.id, ISIS_SYS_ID_LEN + 1);
997 LSP_FRAGMENT (lsp_id) = 0;
998 lsp = lsp_search (lsp_id, area->lspdb[level - 1]);
999 if (lsp)
1000 {
1001 if (LSP_PSEUDO_ID (lsp_id))
1002 {
1003 isis_spf_process_pseudo_lsp (spftree, lsp, vertex->d_N,
1004 vertex->depth, family);
1005
1006 }
1007 else
1008 {
1009 isis_spf_process_lsp (spftree, lsp, vertex->d_N,
1010 vertex->depth, family);
1011 }
1012 }
1013 else
1014 {
1015 zlog_warn ("ISIS-Spf: No LSP found for %s",
1016 rawlspid_print (lsp_id));
1017 }
1018 }
1019 }
1020
1021 thread_add_event (master, isis_route_validate, area, 0);
1022 spftree->lastrun = time (NULL);
1023 spftree->pending = 0;
1024
1025 return retval;
1026 }
1027
1028 int
1029 isis_run_spf_l1 (struct thread *thread)
1030 {
1031 struct isis_area *area;
1032 int retval = ISIS_OK;
1033
1034 area = THREAD_ARG (thread);
1035 assert (area);
1036
1037 area->spftree[0]->t_spf = NULL;
1038
1039 if (!(area->is_type & IS_LEVEL_1))
1040 {
1041 if (isis->debugs & DEBUG_SPF_EVENTS)
1042 zlog_warn ("ISIS-SPF (%s) area does not share level",
1043 area->area_tag);
1044 return ISIS_WARNING;
1045 }
1046
1047 if (isis->debugs & DEBUG_SPF_EVENTS)
1048 zlog_info ("ISIS-Spf (%s) L1 SPF needed, periodic SPF", area->area_tag);
1049
1050 if (area->ip_circuits)
1051 retval = isis_run_spf (area, 1, AF_INET);
1052
1053 THREAD_TIMER_ON (master, area->spftree[0]->t_spf, isis_run_spf_l1, area,
1054 isis_jitter (PERIODIC_SPF_INTERVAL, 10));
1055
1056 return retval;
1057 }
1058
1059 int
1060 isis_run_spf_l2 (struct thread *thread)
1061 {
1062 struct isis_area *area;
1063 int retval = ISIS_OK;
1064
1065 area = THREAD_ARG (thread);
1066 assert (area);
1067
1068 area->spftree[1]->t_spf = NULL;
1069
1070 if (!(area->is_type & IS_LEVEL_2))
1071 {
1072 if (isis->debugs & DEBUG_SPF_EVENTS)
1073 zlog_warn ("ISIS-SPF (%s) area does not share level", area->area_tag);
1074 return ISIS_WARNING;
1075 }
1076
1077 if (isis->debugs & DEBUG_SPF_EVENTS)
1078 zlog_info ("ISIS-Spf (%s) L2 SPF needed, periodic SPF", area->area_tag);
1079
1080 if (area->ip_circuits)
1081 retval = isis_run_spf (area, 2, AF_INET);
1082
1083 THREAD_TIMER_ON (master, area->spftree[1]->t_spf, isis_run_spf_l2, area,
1084 isis_jitter (PERIODIC_SPF_INTERVAL, 10));
1085
1086 return retval;
1087 }
1088
1089 int
1090 isis_spf_schedule (struct isis_area *area, int level)
1091 {
1092 int retval = ISIS_OK;
1093 struct isis_spftree *spftree = area->spftree[level - 1];
1094 time_t diff, now = time (NULL);
1095
1096 if (spftree->pending)
1097 return retval;
1098
1099 diff = now - spftree->lastrun;
1100
1101 /* FIXME: let's wait a minute before doing the SPF */
1102 if (now - isis->uptime < 60 || isis->uptime == 0)
1103 {
1104 if (level == 1)
1105 THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_l1, area, 60);
1106 else
1107 THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_l2, area, 60);
1108
1109 spftree->pending = 1;
1110 return retval;
1111 }
1112
1113 THREAD_TIMER_OFF (spftree->t_spf);
1114
1115 if (diff < MINIMUM_SPF_INTERVAL)
1116 {
1117 if (level == 1)
1118 THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_l1, area,
1119 MINIMUM_SPF_INTERVAL - diff);
1120 else
1121 THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_l2, area,
1122 MINIMUM_SPF_INTERVAL - diff);
1123
1124 spftree->pending = 1;
1125 }
1126 else
1127 {
1128 spftree->pending = 0;
1129 retval = isis_run_spf (area, level, AF_INET);
1130 if (level == 1)
1131 THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_l1, area,
1132 isis_jitter (PERIODIC_SPF_INTERVAL, 10));
1133 else
1134 THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_l2, area,
1135 isis_jitter (PERIODIC_SPF_INTERVAL, 10));
1136 }
1137
1138 return retval;
1139 }
1140
1141 #ifdef HAVE_IPV6
1142 int
1143 isis_run_spf6_l1 (struct thread *thread)
1144 {
1145 struct isis_area *area;
1146 int retval = ISIS_OK;
1147
1148 area = THREAD_ARG (thread);
1149 assert (area);
1150
1151 area->spftree6[0]->t_spf = NULL;
1152
1153 if (!(area->is_type & IS_LEVEL_1))
1154 {
1155 if (isis->debugs & DEBUG_SPF_EVENTS)
1156 zlog_warn ("ISIS-SPF (%s) area does not share level", area->area_tag);
1157 return ISIS_WARNING;
1158 }
1159
1160 if (isis->debugs & DEBUG_SPF_EVENTS)
1161 zlog_info ("ISIS-Spf (%s) L1 SPF needed, periodic SPF", area->area_tag);
1162
1163 if (area->ipv6_circuits)
1164 retval = isis_run_spf (area, 1, AF_INET6);
1165
1166 THREAD_TIMER_ON (master, area->spftree6[0]->t_spf, isis_run_spf6_l1, area,
1167 isis_jitter (PERIODIC_SPF_INTERVAL, 10));
1168
1169 return retval;
1170 }
1171
1172 int
1173 isis_run_spf6_l2 (struct thread *thread)
1174 {
1175 struct isis_area *area;
1176 int retval = ISIS_OK;
1177
1178 area = THREAD_ARG (thread);
1179 assert (area);
1180
1181 area->spftree6[1]->t_spf = NULL;
1182
1183 if (!(area->is_type & IS_LEVEL_2))
1184 {
1185 if (isis->debugs & DEBUG_SPF_EVENTS)
1186 zlog_warn ("ISIS-SPF (%s) area does not share level", area->area_tag);
1187 return ISIS_WARNING;
1188 }
1189
1190 if (isis->debugs & DEBUG_SPF_EVENTS)
1191 zlog_info ("ISIS-Spf (%s) L2 SPF needed, periodic SPF", area->area_tag);
1192
1193 if (area->ipv6_circuits)
1194 retval = isis_run_spf (area, 2, AF_INET6);
1195
1196 THREAD_TIMER_ON (master, area->spftree6[1]->t_spf, isis_run_spf6_l2, area,
1197 isis_jitter (PERIODIC_SPF_INTERVAL, 10));
1198
1199 return retval;
1200 }
1201
1202 int
1203 isis_spf_schedule6 (struct isis_area *area, int level)
1204 {
1205 int retval = ISIS_OK;
1206 struct isis_spftree *spftree = area->spftree6[level - 1];
1207 time_t diff, now = time (NULL);
1208
1209 if (spftree->pending)
1210 return retval;
1211
1212 diff = now - spftree->lastrun;
1213
1214 /* FIXME: let's wait a minute before doing the SPF */
1215 if (now - isis->uptime < 60 || isis->uptime == 0)
1216 {
1217 if (level == 1)
1218 THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf6_l1, area, 60);
1219 else
1220 THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf6_l2, area, 60);
1221
1222 spftree->pending = 1;
1223 return retval;
1224 }
1225
1226 THREAD_TIMER_OFF (spftree->t_spf);
1227
1228 if (diff < MINIMUM_SPF_INTERVAL)
1229 {
1230 if (level == 1)
1231 THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf6_l1, area,
1232 MINIMUM_SPF_INTERVAL - diff);
1233 else
1234 THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf6_l2, area,
1235 MINIMUM_SPF_INTERVAL - diff);
1236
1237 spftree->pending = 1;
1238 }
1239 else
1240 {
1241 spftree->pending = 0;
1242 retval = isis_run_spf (area, level, AF_INET6);
1243
1244 if (level == 1)
1245 THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf6_l1, area,
1246 isis_jitter (PERIODIC_SPF_INTERVAL, 10));
1247 else
1248 THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf6_l2, area,
1249 isis_jitter (PERIODIC_SPF_INTERVAL, 10));
1250 }
1251
1252 return retval;
1253 }
1254 #endif
1255
1256 void
1257 isis_print_paths (struct vty *vty, struct list *paths)
1258 {
1259 struct listnode *node, *anode;
1260 struct isis_vertex *vertex;
1261 struct isis_dynhn *dyn, *nh_dyn = NULL;
1262 struct isis_adjacency *adj;
1263 #ifdef EXTREME_DEBUG
1264 u_char buff[255];
1265 #endif
1266
1267 vty_out (vty, "System Id Metric Next-Hop"
1268 " Interface SNPA%s", VTY_NEWLINE);
1269 for (node = listhead (paths); node; nextnode (node))
1270 {
1271 vertex = getdata (node);
1272 if (vertex->type != VTYPE_NONPSEUDO_IS)
1273 continue;
1274 if (memcmp (vertex->N.id, isis->sysid, ISIS_SYS_ID_LEN) == 0)
1275 {
1276 vty_out (vty, "%s --%s", host.name, VTY_NEWLINE);
1277 }
1278 else
1279 {
1280 dyn = dynhn_find_by_id ((u_char *) vertex->N.id);
1281 anode = listhead (vertex->Adj_N);
1282 adj = getdata (anode);
1283 if (adj)
1284 {
1285 nh_dyn = dynhn_find_by_id (adj->sysid);
1286 vty_out (vty, "%-20s %-10u %-20s %-11s %-5s%s",
1287 (dyn != NULL) ? dyn->name.name :
1288 (u_char *) rawlspid_print ((u_char *) vertex->N.id),
1289 vertex->d_N, (nh_dyn != NULL) ? nh_dyn->name.name :
1290 (u_char *) rawlspid_print (adj->sysid),
1291 adj->circuit->interface->name,
1292 snpa_print (adj->snpa), VTY_NEWLINE);
1293 }
1294 else
1295 {
1296 vty_out (vty, "%s %u %s", dyn ? dyn->name.name :
1297 (u_char *) rawlspid_print (vertex->N.id),
1298 vertex->d_N, VTY_NEWLINE);
1299 }
1300 }
1301 #if 0
1302 vty_out (vty, "%s %s %u %s", vtype2string (vertex->type),
1303 vid2string (vertex, buff), vertex->d_N, VTY_NEWLINE);
1304 #endif
1305 }
1306 }
1307
1308 DEFUN (show_isis_topology,
1309 show_isis_topology_cmd,
1310 "show isis topology",
1311 SHOW_STR
1312 "IS-IS information\n"
1313 "IS-IS paths to Intermediate Systems\n")
1314 {
1315 struct listnode *node;
1316 struct isis_area *area;
1317 int level;
1318
1319 if (!isis->area_list || isis->area_list->count == 0)
1320 return CMD_SUCCESS;
1321
1322 for (node = listhead (isis->area_list); node; nextnode (node))
1323 {
1324 area = getdata (node);
1325
1326 vty_out (vty, "Area %s:%s", area->area_tag ? area->area_tag : "null",
1327 VTY_NEWLINE);
1328
1329 for (level = 0; level < ISIS_LEVELS; level++)
1330 {
1331 if (area->ip_circuits > 0 && area->spftree[level]
1332 && area->spftree[level]->paths->count > 0)
1333 {
1334 vty_out (vty, "IS-IS paths to level-%d routers that speak IP%s",
1335 level + 1, VTY_NEWLINE);
1336 isis_print_paths (vty, area->spftree[level]->paths);
1337 }
1338 #ifdef HAVE_IPV6
1339 if (area->ipv6_circuits > 0 && area->spftree6[level]
1340 && area->spftree6[level]->paths->count > 0)
1341 {
1342 vty_out (vty,
1343 "IS-IS paths to level-%d routers that speak IPv6%s",
1344 level + 1, VTY_NEWLINE);
1345 isis_print_paths (vty, area->spftree6[level]->paths);
1346 }
1347 #endif /* HAVE_IPV6 */
1348 }
1349 }
1350
1351 return CMD_SUCCESS;
1352 }
1353
1354 DEFUN (show_isis_topology_l1,
1355 show_isis_topology_l1_cmd,
1356 "show isis topology level-1",
1357 SHOW_STR
1358 "IS-IS information\n"
1359 "IS-IS paths to Intermediate Systems\n"
1360 "Paths to all level-1 routers in the area\n")
1361 {
1362 struct listnode *node;
1363 struct isis_area *area;
1364
1365 if (!isis->area_list || isis->area_list->count == 0)
1366 return CMD_SUCCESS;
1367
1368 for (node = listhead (isis->area_list); node; nextnode (node))
1369 {
1370 area = getdata (node);
1371
1372 vty_out (vty, "Area %s:%s", area->area_tag ? area->area_tag : "null",
1373 VTY_NEWLINE);
1374
1375 if (area->ip_circuits > 0 && area->spftree[0]
1376 && area->spftree[0]->paths->count > 0)
1377 {
1378 vty_out (vty, "IS-IS paths to level-1 routers that speak IP%s",
1379 VTY_NEWLINE);
1380 isis_print_paths (vty, area->spftree[0]->paths);
1381 }
1382 #ifdef HAVE_IPV6
1383 if (area->ipv6_circuits > 0 && area->spftree6[0]
1384 && area->spftree6[0]->paths->count > 0)
1385 {
1386 vty_out (vty, "IS-IS paths to level-1 routers that speak IPv6%s",
1387 VTY_NEWLINE);
1388 isis_print_paths (vty, area->spftree6[0]->paths);
1389 }
1390 #endif /* HAVE_IPV6 */
1391 }
1392
1393 return CMD_SUCCESS;
1394 }
1395
1396 DEFUN (show_isis_topology_l2,
1397 show_isis_topology_l2_cmd,
1398 "show isis topology level-2",
1399 SHOW_STR
1400 "IS-IS information\n"
1401 "IS-IS paths to Intermediate Systems\n"
1402 "Paths to all level-2 routers in the domain\n")
1403 {
1404 struct listnode *node;
1405 struct isis_area *area;
1406
1407 if (!isis->area_list || isis->area_list->count == 0)
1408 return CMD_SUCCESS;
1409
1410 for (node = listhead (isis->area_list); node; nextnode (node))
1411 {
1412 area = getdata (node);
1413
1414 vty_out (vty, "Area %s:%s", area->area_tag ? area->area_tag : "null",
1415 VTY_NEWLINE);
1416
1417 if (area->ip_circuits > 0 && area->spftree[1]
1418 && area->spftree[1]->paths->count > 0)
1419 {
1420 vty_out (vty, "IS-IS paths to level-2 routers that speak IP%s",
1421 VTY_NEWLINE);
1422 isis_print_paths (vty, area->spftree[1]->paths);
1423 }
1424 #ifdef HAVE_IPV6
1425 if (area->ipv6_circuits > 0 && area->spftree6[1]
1426 && area->spftree6[1]->paths->count > 0)
1427 {
1428 vty_out (vty, "IS-IS paths to level-2 routers that speak IPv6%s",
1429 VTY_NEWLINE);
1430 isis_print_paths (vty, area->spftree6[1]->paths);
1431 }
1432 #endif /* HAVE_IPV6 */
1433 }
1434
1435 return CMD_SUCCESS;
1436 }
1437
1438 void
1439 isis_spf_cmds_init ()
1440 {
1441 install_element (VIEW_NODE, &show_isis_topology_cmd);
1442 install_element (VIEW_NODE, &show_isis_topology_l1_cmd);
1443 install_element (VIEW_NODE, &show_isis_topology_l2_cmd);
1444
1445 install_element (ENABLE_NODE, &show_isis_topology_cmd);
1446 install_element (ENABLE_NODE, &show_isis_topology_l1_cmd);
1447 install_element (ENABLE_NODE, &show_isis_topology_l2_cmd);
1448 }