ospfd: Tighten up the connected check for redistribution
[jleu-quagga.git] / isisd / isis_spf.c
blob5d7e9da4255525e08048663fcbef8e26ca7330d6
1 /*
2 * IS-IS Rout(e)ing protocol - isis_spf.c
3 * The SPT algorithm
5 * Copyright (C) 2001,2002 Sampo Saaristo
6 * Tampere University of Technology
7 * Institute of Communications Engineering
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.
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.
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.
24 #include <zebra.h>
26 #include "thread.h"
27 #include "linklist.h"
28 #include "vty.h"
29 #include "log.h"
30 #include "command.h"
31 #include "memory.h"
32 #include "prefix.h"
33 #include "hash.h"
34 #include "if.h"
35 #include "table.h"
37 #include "isis_constants.h"
38 #include "isis_common.h"
39 #include "dict.h"
40 #include "isisd.h"
41 #include "isis_misc.h"
42 #include "isis_adjacency.h"
43 #include "isis_circuit.h"
44 #include "isis_tlv.h"
45 #include "isis_pdu.h"
46 #include "isis_lsp.h"
47 #include "isis_dynhn.h"
48 #include "isis_spf.h"
49 #include "isis_route.h"
50 #include "isis_csm.h"
52 extern struct isis *isis;
53 extern struct thread_master *master;
54 extern struct host host;
56 int isis_run_spf_l1 (struct thread *thread);
57 int isis_run_spf_l2 (struct thread *thread);
59 /* 7.2.7 */
60 static void
61 remove_excess_adjs (struct list *adjs)
63 struct listnode *node, *excess = NULL;
64 struct isis_adjacency *adj, *candidate = NULL;
65 int comp;
67 for (ALL_LIST_ELEMENTS_RO (adjs, node, adj))
69 if (excess == NULL)
70 excess = node;
71 candidate = listgetdata (excess);
73 if (candidate->sys_type < adj->sys_type)
75 excess = node;
76 candidate = adj;
77 continue;
79 if (candidate->sys_type > adj->sys_type)
80 continue;
82 comp = memcmp (candidate->sysid, adj->sysid, ISIS_SYS_ID_LEN);
83 if (comp > 0)
85 excess = node;
86 candidate = adj;
87 continue;
89 if (comp < 0)
90 continue;
92 if (candidate->circuit->circuit_id > adj->circuit->circuit_id)
94 excess = node;
95 candidate = adj;
96 continue;
99 if (candidate->circuit->circuit_id < adj->circuit->circuit_id)
100 continue;
102 comp = memcmp (candidate->snpa, adj->snpa, ETH_ALEN);
103 if (comp > 0)
105 excess = node;
106 candidate = adj;
107 continue;
111 list_delete_node (adjs, excess);
113 return;
116 #ifdef EXTREME_DEBUG
117 static const char *
118 vtype2string (enum vertextype vtype)
120 switch (vtype)
122 case VTYPE_PSEUDO_IS:
123 return "pseudo_IS";
124 break;
125 case VTYPE_PSEUDO_TE_IS:
126 return "pseudo_TE-IS";
127 break;
128 case VTYPE_NONPSEUDO_IS:
129 return "IS";
130 break;
131 case VTYPE_NONPSEUDO_TE_IS:
132 return "TE-IS";
133 break;
134 case VTYPE_ES:
135 return "ES";
136 break;
137 case VTYPE_IPREACH_INTERNAL:
138 return "IP internal";
139 break;
140 case VTYPE_IPREACH_EXTERNAL:
141 return "IP external";
142 break;
143 case VTYPE_IPREACH_TE:
144 return "IP TE";
145 break;
146 #ifdef HAVE_IPV6
147 case VTYPE_IP6REACH_INTERNAL:
148 return "IP6 internal";
149 break;
150 case VTYPE_IP6REACH_EXTERNAL:
151 return "IP6 external";
152 break;
153 #endif /* HAVE_IPV6 */
154 default:
155 return "UNKNOWN";
157 return NULL; /* Not reached */
160 static const char *
161 vid2string (struct isis_vertex *vertex, u_char * buff)
163 switch (vertex->type)
165 case VTYPE_PSEUDO_IS:
166 case VTYPE_PSEUDO_TE_IS:
167 return rawlspid_print (vertex->N.id);
168 break;
169 case VTYPE_NONPSEUDO_IS:
170 case VTYPE_NONPSEUDO_TE_IS:
171 case VTYPE_ES:
172 return sysid_print (vertex->N.id);
173 break;
174 case VTYPE_IPREACH_INTERNAL:
175 case VTYPE_IPREACH_EXTERNAL:
176 case VTYPE_IPREACH_TE:
177 #ifdef HAVE_IPV6
178 case VTYPE_IP6REACH_INTERNAL:
179 case VTYPE_IP6REACH_EXTERNAL:
180 #endif /* HAVE_IPV6 */
181 prefix2str ((struct prefix *) &vertex->N.prefix, (char *) buff, BUFSIZ);
182 break;
183 default:
184 return "UNKNOWN";
187 return (char *) buff;
189 #endif /* EXTREME_DEBUG */
191 static struct isis_spftree *
192 isis_spftree_new ()
194 struct isis_spftree *tree;
196 tree = XCALLOC (MTYPE_ISIS_SPFTREE, sizeof (struct isis_spftree));
197 if (tree == NULL)
199 zlog_err ("ISIS-Spf: isis_spftree_new Out of memory!");
200 return NULL;
203 tree->tents = list_new ();
204 tree->paths = list_new ();
205 return tree;
208 static void
209 isis_vertex_del (struct isis_vertex *vertex)
211 list_delete (vertex->Adj_N);
213 XFREE (MTYPE_ISIS_VERTEX, vertex);
215 return;
218 #if 0 /* HT: Not used yet. */
219 static void
220 isis_spftree_del (struct isis_spftree *spftree)
222 spftree->tents->del = (void (*)(void *)) isis_vertex_del;
223 list_delete (spftree->tents);
225 spftree->paths->del = (void (*)(void *)) isis_vertex_del;
226 list_delete (spftree->paths);
228 XFREE (MTYPE_ISIS_SPFTREE, spftree);
230 return;
232 #endif
234 void
235 spftree_area_init (struct isis_area *area)
237 if ((area->is_type & IS_LEVEL_1) && area->spftree[0] == NULL)
239 area->spftree[0] = isis_spftree_new ();
240 #ifdef HAVE_IPV6
241 area->spftree6[0] = isis_spftree_new ();
242 #endif
244 /* thread_add_timer (master, isis_run_spf_l1, area,
245 isis_jitter (PERIODIC_SPF_INTERVAL, 10)); */
248 if ((area->is_type & IS_LEVEL_2) && area->spftree[1] == NULL)
250 area->spftree[1] = isis_spftree_new ();
251 #ifdef HAVE_IPV6
252 area->spftree6[1] = isis_spftree_new ();
253 #endif
254 /* thread_add_timer (master, isis_run_spf_l2, area,
255 isis_jitter (PERIODIC_SPF_INTERVAL, 10)); */
258 return;
261 static struct isis_vertex *
262 isis_vertex_new (void *id, enum vertextype vtype)
264 struct isis_vertex *vertex;
266 vertex = XCALLOC (MTYPE_ISIS_VERTEX, sizeof (struct isis_vertex));
267 if (vertex == NULL)
269 zlog_err ("isis_vertex_new Out of memory!");
270 return NULL;
273 vertex->type = vtype;
274 switch (vtype)
276 case VTYPE_ES:
277 case VTYPE_NONPSEUDO_IS:
278 case VTYPE_NONPSEUDO_TE_IS:
279 memcpy (vertex->N.id, (u_char *) id, ISIS_SYS_ID_LEN);
280 break;
281 case VTYPE_PSEUDO_IS:
282 case VTYPE_PSEUDO_TE_IS:
283 memcpy (vertex->N.id, (u_char *) id, ISIS_SYS_ID_LEN + 1);
284 break;
285 case VTYPE_IPREACH_INTERNAL:
286 case VTYPE_IPREACH_EXTERNAL:
287 case VTYPE_IPREACH_TE:
288 #ifdef HAVE_IPV6
289 case VTYPE_IP6REACH_INTERNAL:
290 case VTYPE_IP6REACH_EXTERNAL:
291 #endif /* HAVE_IPV6 */
292 memcpy (&vertex->N.prefix, (struct prefix *) id,
293 sizeof (struct prefix));
294 break;
295 default:
296 zlog_err ("WTF!");
299 vertex->Adj_N = list_new ();
301 return vertex;
305 * Add this IS to the root of SPT
307 static void
308 isis_spf_add_self (struct isis_spftree *spftree, struct isis_area *area,
309 int level)
311 struct isis_vertex *vertex;
312 struct isis_lsp *lsp;
313 u_char lspid[ISIS_SYS_ID_LEN + 2];
314 #ifdef EXTREME_DEBUG
315 u_char buff[BUFSIZ];
316 #endif /* EXTREME_DEBUG */
317 memcpy (lspid, isis->sysid, ISIS_SYS_ID_LEN);
318 LSP_PSEUDO_ID (lspid) = 0;
319 LSP_FRAGMENT (lspid) = 0;
321 lsp = lsp_search (lspid, area->lspdb[level - 1]);
323 if (lsp == NULL)
324 zlog_warn ("ISIS-Spf: could not find own l%d LSP!", level);
326 if (!area->oldmetric)
327 vertex = isis_vertex_new (isis->sysid, VTYPE_NONPSEUDO_TE_IS);
328 else
329 vertex = isis_vertex_new (isis->sysid, VTYPE_NONPSEUDO_IS);
331 vertex->lsp = lsp;
333 listnode_add (spftree->paths, vertex);
335 #ifdef EXTREME_DEBUG
336 zlog_debug ("ISIS-Spf: added this IS %s %s depth %d dist %d to PATHS",
337 vtype2string (vertex->type), vid2string (vertex, buff),
338 vertex->depth, vertex->d_N);
339 #endif /* EXTREME_DEBUG */
341 return;
344 static struct isis_vertex *
345 isis_find_vertex (struct list *list, void *id, enum vertextype vtype)
347 struct listnode *node;
348 struct isis_vertex *vertex;
349 struct prefix *p1, *p2;
351 for (ALL_LIST_ELEMENTS_RO (list, node, vertex))
353 if (vertex->type != vtype)
354 continue;
355 switch (vtype)
357 case VTYPE_ES:
358 case VTYPE_NONPSEUDO_IS:
359 case VTYPE_NONPSEUDO_TE_IS:
360 if (memcmp ((u_char *) id, vertex->N.id, ISIS_SYS_ID_LEN) == 0)
361 return vertex;
362 break;
363 case VTYPE_PSEUDO_IS:
364 case VTYPE_PSEUDO_TE_IS:
365 if (memcmp ((u_char *) id, vertex->N.id, ISIS_SYS_ID_LEN + 1) == 0)
366 return vertex;
367 break;
368 case VTYPE_IPREACH_INTERNAL:
369 case VTYPE_IPREACH_EXTERNAL:
370 case VTYPE_IPREACH_TE:
371 #ifdef HAVE_IPV6
372 case VTYPE_IP6REACH_INTERNAL:
373 case VTYPE_IP6REACH_EXTERNAL:
374 #endif /* HAVE_IPV6 */
375 p1 = (struct prefix *) id;
376 p2 = (struct prefix *) &vertex->N.id;
377 if (p1->family == p2->family && p1->prefixlen == p2->prefixlen &&
378 memcmp (&p1->u.prefix, &p2->u.prefix,
379 PSIZE (p1->prefixlen)) == 0)
380 return vertex;
381 break;
385 return NULL;
389 * Add a vertex to TENT sorted by cost and by vertextype on tie break situation
391 static struct isis_vertex *
392 isis_spf_add2tent (struct isis_spftree *spftree, enum vertextype vtype,
393 void *id, struct isis_adjacency *adj, u_int32_t cost,
394 int depth, int family)
396 struct isis_vertex *vertex, *v;
397 struct listnode *node;
398 #ifdef EXTREME_DEBUG
399 u_char buff[BUFSIZ];
400 #endif
402 vertex = isis_vertex_new (id, vtype);
403 vertex->d_N = cost;
404 vertex->depth = depth;
406 if (adj)
407 listnode_add (vertex->Adj_N, adj);
408 #ifdef EXTREME_DEBUG
409 zlog_debug ("ISIS-Spf: add to TENT %s %s depth %d dist %d",
410 vtype2string (vertex->type), vid2string (vertex, buff),
411 vertex->depth, vertex->d_N);
412 #endif /* EXTREME_DEBUG */
413 listnode_add (spftree->tents, vertex);
414 if (list_isempty (spftree->tents))
416 listnode_add (spftree->tents, vertex);
417 return vertex;
420 /* XXX: This cant use the standard ALL_LIST_ELEMENT macro */
421 for (node = listhead (spftree->tents); node; node = listnextnode (node))
423 v = listgetdata (node);
424 if (v->d_N > vertex->d_N)
426 list_add_node_prev (spftree->tents, node, vertex);
427 break;
429 else if (v->d_N == vertex->d_N)
431 /* Tie break, add according to type */
432 while (v && v->d_N == vertex->d_N && v->type > vertex->type)
434 if (v->type > vertex->type)
436 break;
438 /* XXX: this seems dubious, node is the loop iterator */
439 node = listnextnode (node);
440 (node) ? (v = listgetdata (node)) : (v = NULL);
442 list_add_node_prev (spftree->tents, node, vertex);
443 break;
445 else if (node->next == NULL)
447 list_add_node_next (spftree->tents, node, vertex);
448 break;
451 return vertex;
454 static struct isis_vertex *
455 isis_spf_add_local (struct isis_spftree *spftree, enum vertextype vtype,
456 void *id, struct isis_adjacency *adj, u_int32_t cost,
457 int family)
459 struct isis_vertex *vertex;
461 vertex = isis_find_vertex (spftree->tents, id, vtype);
463 if (vertex)
465 /* C.2.5 c) */
466 if (vertex->d_N == cost)
468 if (adj)
469 listnode_add (vertex->Adj_N, adj);
470 /* d) */
471 if (listcount (vertex->Adj_N) > ISIS_MAX_PATH_SPLITS)
472 remove_excess_adjs (vertex->Adj_N);
474 /* f) */
475 else if (vertex->d_N > cost)
477 listnode_delete (spftree->tents, vertex);
478 goto add2tent;
480 /* e) do nothing */
481 return vertex;
484 add2tent:
485 return isis_spf_add2tent (spftree, vtype, id, adj, cost, 1, family);
488 static void
489 process_N (struct isis_spftree *spftree, enum vertextype vtype, void *id,
490 u_int16_t dist, u_int16_t depth, struct isis_adjacency *adj,
491 int family)
493 struct isis_vertex *vertex;
494 #ifdef EXTREME_DEBUG
495 u_char buff[255];
496 #endif
498 /* C.2.6 b) */
499 if (dist > MAX_PATH_METRIC)
500 return;
501 /* c) */
502 vertex = isis_find_vertex (spftree->paths, id, vtype);
503 if (vertex)
505 #ifdef EXTREME_DEBUG
506 zlog_debug ("ISIS-Spf: process_N %s %s dist %d already found from PATH",
507 vtype2string (vtype), vid2string (vertex, buff), dist);
508 #endif /* EXTREME_DEBUG */
509 assert (dist >= vertex->d_N);
510 return;
513 vertex = isis_find_vertex (spftree->tents, id, vtype);
514 /* d) */
515 if (vertex)
517 /* 1) */
518 #ifdef EXTREME_DEBUG
519 zlog_debug ("ISIS-Spf: process_N %s %s dist %d",
520 vtype2string (vtype), vid2string (vertex, buff), dist);
521 #endif /* EXTREME_DEBUG */
522 if (vertex->d_N == dist)
524 if (adj)
525 listnode_add (vertex->Adj_N, adj);
526 /* 2) */
527 if (listcount (vertex->Adj_N) > ISIS_MAX_PATH_SPLITS)
528 remove_excess_adjs (vertex->Adj_N);
529 /* 3) */
530 return;
532 else if (vertex->d_N < dist)
534 return;
535 /* 4) */
537 else
539 listnode_delete (spftree->tents, vertex);
543 isis_spf_add2tent (spftree, vtype, id, adj, dist, depth, family);
544 return;
548 * C.2.6 Step 1
550 static int
551 isis_spf_process_lsp (struct isis_spftree *spftree, struct isis_lsp *lsp,
552 uint32_t cost, uint16_t depth, int family)
554 struct listnode *node, *fragnode = NULL;
555 u_int16_t dist;
556 struct is_neigh *is_neigh;
557 struct te_is_neigh *te_is_neigh;
558 struct ipv4_reachability *ipreach;
559 struct te_ipv4_reachability *te_ipv4_reach;
560 enum vertextype vtype;
561 struct prefix prefix;
562 #ifdef HAVE_IPV6
563 struct ipv6_reachability *ip6reach;
564 #endif /* HAVE_IPV6 */
567 if (!lsp->adj)
568 return ISIS_WARNING;
569 if (lsp->tlv_data.nlpids == NULL || !speaks (lsp->tlv_data.nlpids, family))
570 return ISIS_OK;
572 lspfragloop:
573 if (lsp->lsp_header->seq_num == 0)
575 zlog_warn ("isis_spf_process_lsp(): lsp with 0 seq_num"
576 " - do not process");
577 return ISIS_WARNING;
580 if (!ISIS_MASK_LSP_OL_BIT (lsp->lsp_header->lsp_bits))
582 if (lsp->tlv_data.is_neighs)
584 for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.is_neighs, node, is_neigh))
586 /* C.2.6 a) */
587 /* Two way connectivity */
588 if (!memcmp (is_neigh->neigh_id, isis->sysid, ISIS_SYS_ID_LEN))
589 continue;
590 dist = cost + is_neigh->metrics.metric_default;
591 vtype = LSP_PSEUDO_ID (is_neigh->neigh_id) ? VTYPE_PSEUDO_IS
592 : VTYPE_NONPSEUDO_IS;
593 process_N (spftree, vtype, (void *) is_neigh->neigh_id, dist,
594 depth + 1, lsp->adj, family);
597 if (lsp->tlv_data.te_is_neighs)
599 for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.te_is_neighs, node,
600 te_is_neigh))
602 uint32_t metric;
603 if (!memcmp (te_is_neigh->neigh_id, isis->sysid, ISIS_SYS_ID_LEN))
604 continue;
605 memcpy (&metric, te_is_neigh->te_metric, 3);
606 dist = cost + ntohl (metric << 8);
607 vtype = LSP_PSEUDO_ID (te_is_neigh->neigh_id) ? VTYPE_PSEUDO_TE_IS
608 : VTYPE_NONPSEUDO_TE_IS;
609 process_N (spftree, vtype, (void *) te_is_neigh->neigh_id, dist,
610 depth + 1, lsp->adj, family);
613 if (family == AF_INET && lsp->tlv_data.ipv4_int_reachs)
615 prefix.family = AF_INET;
616 for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.ipv4_int_reachs,
617 node, ipreach))
619 dist = cost + ipreach->metrics.metric_default;
620 vtype = VTYPE_IPREACH_INTERNAL;
621 prefix.u.prefix4 = ipreach->prefix;
622 prefix.prefixlen = ip_masklen (ipreach->mask);
623 process_N (spftree, vtype, (void *) &prefix, dist, depth + 1,
624 lsp->adj, family);
628 if (family == AF_INET && lsp->tlv_data.ipv4_ext_reachs)
630 prefix.family = AF_INET;
631 for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.ipv4_ext_reachs,
632 node, ipreach))
634 dist = cost + ipreach->metrics.metric_default;
635 vtype = VTYPE_IPREACH_EXTERNAL;
636 prefix.u.prefix4 = ipreach->prefix;
637 prefix.prefixlen = ip_masklen (ipreach->mask);
638 process_N (spftree, vtype, (void *) &prefix, dist, depth + 1,
639 lsp->adj, family);
642 if (family == AF_INET && lsp->tlv_data.te_ipv4_reachs)
644 prefix.family = AF_INET;
645 for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.te_ipv4_reachs,
646 node, te_ipv4_reach))
648 dist = cost + ntohl (te_ipv4_reach->te_metric);
649 vtype = VTYPE_IPREACH_TE;
650 prefix.u.prefix4 = newprefix2inaddr (&te_ipv4_reach->prefix_start,
651 te_ipv4_reach->control);
652 prefix.prefixlen = (te_ipv4_reach->control & 0x3F);
653 process_N (spftree, vtype, (void *) &prefix, dist, depth + 1,
654 lsp->adj, family);
657 #ifdef HAVE_IPV6
658 if (family == AF_INET6 && lsp->tlv_data.ipv6_reachs)
660 prefix.family = AF_INET6;
661 for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.ipv6_reachs,
662 node, ip6reach))
664 dist = cost + ip6reach->metric;
665 vtype = (ip6reach->control_info & CTRL_INFO_DISTRIBUTION) ?
666 VTYPE_IP6REACH_EXTERNAL : VTYPE_IP6REACH_INTERNAL;
667 prefix.prefixlen = ip6reach->prefix_len;
668 memcpy (&prefix.u.prefix6.s6_addr, ip6reach->prefix,
669 PSIZE (ip6reach->prefix_len));
670 process_N (spftree, vtype, (void *) &prefix, dist, depth + 1,
671 lsp->adj, family);
674 #endif /* HAVE_IPV6 */
677 if (fragnode == NULL)
678 fragnode = listhead (lsp->lspu.frags);
679 else
680 fragnode = listnextnode (fragnode);
682 if (fragnode)
684 lsp = listgetdata (fragnode);
685 goto lspfragloop;
688 return ISIS_OK;
691 static int
692 isis_spf_process_pseudo_lsp (struct isis_spftree *spftree,
693 struct isis_lsp *lsp, uint16_t cost,
694 uint16_t depth, int family)
696 struct listnode *node, *fragnode = NULL;
697 struct is_neigh *is_neigh;
698 struct te_is_neigh *te_is_neigh;
699 enum vertextype vtype;
701 pseudofragloop:
703 if (lsp->lsp_header->seq_num == 0)
705 zlog_warn ("isis_spf_process_pseudo_lsp(): lsp with 0 seq_num"
706 " - do not process");
707 return ISIS_WARNING;
710 if (lsp->tlv_data.is_neighs)
711 for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.is_neighs, node, is_neigh))
713 vtype = LSP_PSEUDO_ID (is_neigh->neigh_id) ? VTYPE_PSEUDO_IS
714 : VTYPE_NONPSEUDO_IS;
715 /* Two way connectivity */
716 if (!memcmp (is_neigh->neigh_id, isis->sysid, ISIS_SYS_ID_LEN))
717 continue;
718 if (isis_find_vertex
719 (spftree->tents, (void *) is_neigh->neigh_id, vtype) == NULL
720 && isis_find_vertex (spftree->paths, (void *) is_neigh->neigh_id,
721 vtype) == NULL)
723 /* C.2.5 i) */
724 isis_spf_add2tent (spftree, vtype, is_neigh->neigh_id, lsp->adj,
725 cost, depth, family);
728 if (lsp->tlv_data.te_is_neighs)
729 for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.te_is_neighs, node, te_is_neigh))
731 vtype = LSP_PSEUDO_ID (te_is_neigh->neigh_id) ? VTYPE_PSEUDO_TE_IS
732 : VTYPE_NONPSEUDO_TE_IS;
733 /* Two way connectivity */
734 if (!memcmp (te_is_neigh->neigh_id, isis->sysid, ISIS_SYS_ID_LEN))
735 continue;
736 if (isis_find_vertex
737 (spftree->tents, (void *) te_is_neigh->neigh_id, vtype) == NULL
738 && isis_find_vertex (spftree->paths, (void *) te_is_neigh->neigh_id,
739 vtype) == NULL)
741 /* C.2.5 i) */
742 isis_spf_add2tent (spftree, vtype, te_is_neigh->neigh_id, lsp->adj,
743 cost, depth, family);
747 if (fragnode == NULL)
748 fragnode = listhead (lsp->lspu.frags);
749 else
750 fragnode = listnextnode (fragnode);
752 if (fragnode)
754 lsp = listgetdata (fragnode);
755 goto pseudofragloop;
758 return ISIS_OK;
761 static int
762 isis_spf_preload_tent (struct isis_spftree *spftree,
763 struct isis_area *area, int level, int family)
765 struct isis_vertex *vertex;
766 struct isis_circuit *circuit;
767 struct listnode *cnode, *anode, *ipnode;
768 struct isis_adjacency *adj;
769 struct isis_lsp *lsp;
770 struct list *adj_list;
771 struct list *adjdb;
772 struct prefix_ipv4 *ipv4;
773 struct prefix prefix;
774 int retval = ISIS_OK;
775 u_char lsp_id[ISIS_SYS_ID_LEN + 2];
776 #ifdef HAVE_IPV6
777 struct prefix_ipv6 *ipv6;
778 #endif /* HAVE_IPV6 */
780 for (ALL_LIST_ELEMENTS_RO (area->circuit_list, cnode, circuit))
782 if (circuit->state != C_STATE_UP)
783 continue;
784 if (!(circuit->circuit_is_type & level))
785 continue;
786 if (family == AF_INET && !circuit->ip_router)
787 continue;
788 #ifdef HAVE_IPV6
789 if (family == AF_INET6 && !circuit->ipv6_router)
790 continue;
791 #endif /* HAVE_IPV6 */
793 * Add IP(v6) addresses of this circuit
795 if (family == AF_INET)
797 prefix.family = AF_INET;
798 for (ALL_LIST_ELEMENTS_RO (circuit->ip_addrs, ipnode, ipv4))
800 prefix.u.prefix4 = ipv4->prefix;
801 prefix.prefixlen = ipv4->prefixlen;
802 isis_spf_add_local (spftree, VTYPE_IPREACH_INTERNAL, &prefix,
803 NULL, 0, family);
806 #ifdef HAVE_IPV6
807 if (family == AF_INET6)
809 prefix.family = AF_INET6;
810 for (ALL_LIST_ELEMENTS_RO (circuit->ipv6_non_link, ipnode, ipv6))
812 prefix.prefixlen = ipv6->prefixlen;
813 prefix.u.prefix6 = ipv6->prefix;
814 isis_spf_add_local (spftree, VTYPE_IP6REACH_INTERNAL,
815 &prefix, NULL, 0, family);
818 #endif /* HAVE_IPV6 */
819 if (circuit->circ_type == CIRCUIT_T_BROADCAST)
822 * Add the adjacencies
824 adj_list = list_new ();
825 adjdb = circuit->u.bc.adjdb[level - 1];
826 isis_adj_build_up_list (adjdb, adj_list);
827 if (listcount (adj_list) == 0)
829 list_delete (adj_list);
830 if (isis->debugs & DEBUG_SPF_EVENTS)
831 zlog_debug ("ISIS-Spf: no L%d adjacencies on circuit %s",
832 level, circuit->interface->name);
833 continue;
835 anode = listhead (adj_list);
836 while (anode)
838 adj = listgetdata (anode);
839 if (!speaks (&adj->nlpids, family))
841 anode = listnextnode (anode);
842 continue;
844 switch (adj->sys_type)
846 case ISIS_SYSTYPE_ES:
847 isis_spf_add_local (spftree, VTYPE_ES, adj->sysid, adj,
848 circuit->te_metric[level - 1], family);
849 break;
850 case ISIS_SYSTYPE_IS:
851 case ISIS_SYSTYPE_L1_IS:
852 case ISIS_SYSTYPE_L2_IS:
853 vertex =
854 isis_spf_add_local (spftree, VTYPE_NONPSEUDO_IS,
855 adj->sysid, adj,
856 circuit->te_metric[level - 1], family);
857 memcpy (lsp_id, adj->sysid, ISIS_SYS_ID_LEN);
858 LSP_PSEUDO_ID (lsp_id) = 0;
859 LSP_FRAGMENT (lsp_id) = 0;
860 lsp = lsp_search (lsp_id, area->lspdb[level - 1]);
861 if (!lsp)
862 zlog_warn ("No lsp found for IS adjacency");
863 /* else {
864 isis_spf_process_lsp (spftree, lsp, vertex->d_N, 1, family);
865 } */
866 break;
867 case ISIS_SYSTYPE_UNKNOWN:
868 default:
869 zlog_warn ("isis_spf_preload_tent unknow adj type");
871 anode = listnextnode (anode);
873 list_delete (adj_list);
875 * Add the pseudonode
877 if (level == 1)
878 memcpy (lsp_id, circuit->u.bc.l1_desig_is, ISIS_SYS_ID_LEN + 1);
879 else
880 memcpy (lsp_id, circuit->u.bc.l2_desig_is, ISIS_SYS_ID_LEN + 1);
881 lsp = lsp_search (lsp_id, area->lspdb[level - 1]);
882 adj = isis_adj_lookup (lsp_id, adjdb);
883 /* if no adj, we are the dis or error */
884 if (!adj && !circuit->u.bc.is_dr[level - 1])
886 zlog_warn ("ISIS-Spf: No adjacency found for DR");
888 if (lsp == NULL || lsp->lsp_header->rem_lifetime == 0)
890 zlog_warn ("ISIS-Spf: No lsp found for DR");
892 else
894 isis_spf_process_pseudo_lsp (spftree, lsp,
895 circuit->te_metric[level - 1], 0, family);
899 else if (circuit->circ_type == CIRCUIT_T_P2P)
901 adj = circuit->u.p2p.neighbor;
902 if (!adj)
903 continue;
904 switch (adj->sys_type)
906 case ISIS_SYSTYPE_ES:
907 isis_spf_add_local (spftree, VTYPE_ES, adj->sysid, adj,
908 circuit->te_metric[level - 1], family);
909 break;
910 case ISIS_SYSTYPE_IS:
911 case ISIS_SYSTYPE_L1_IS:
912 case ISIS_SYSTYPE_L2_IS:
913 if (speaks (&adj->nlpids, family))
914 isis_spf_add_local (spftree, VTYPE_NONPSEUDO_IS, adj->sysid,
915 adj, circuit->te_metric[level - 1],
916 family);
917 break;
918 case ISIS_SYSTYPE_UNKNOWN:
919 default:
920 zlog_warn ("isis_spf_preload_tent unknow adj type");
921 break;
924 else
926 zlog_warn ("isis_spf_preload_tent unsupported media");
927 retval = ISIS_WARNING;
932 return retval;
936 * The parent(s) for vertex is set when added to TENT list
937 * now we just put the child pointer(s) in place
939 static void
940 add_to_paths (struct isis_spftree *spftree, struct isis_vertex *vertex,
941 struct isis_area *area, int level)
943 #ifdef EXTREME_DEBUG
944 u_char buff[BUFSIZ];
945 #endif /* EXTREME_DEBUG */
946 listnode_add (spftree->paths, vertex);
948 #ifdef EXTREME_DEBUG
949 zlog_debug ("ISIS-Spf: added %s %s depth %d dist %d to PATHS",
950 vtype2string (vertex->type), vid2string (vertex, buff),
951 vertex->depth, vertex->d_N);
952 #endif /* EXTREME_DEBUG */
953 if (vertex->type > VTYPE_ES)
955 if (listcount (vertex->Adj_N) > 0)
956 isis_route_create ((struct prefix *) &vertex->N.prefix, vertex->d_N,
957 vertex->depth, vertex->Adj_N, area, level);
958 else if (isis->debugs & DEBUG_SPF_EVENTS)
959 zlog_debug ("ISIS-Spf: no adjacencies do not install route");
962 return;
965 static void
966 init_spt (struct isis_spftree *spftree)
968 spftree->tents->del = spftree->paths->del = (void (*)(void *)) isis_vertex_del;
969 list_delete_all_node (spftree->tents);
970 list_delete_all_node (spftree->paths);
971 spftree->tents->del = spftree->paths->del = NULL;
973 return;
976 static int
977 isis_run_spf (struct isis_area *area, int level, int family)
979 int retval = ISIS_OK;
980 struct listnode *node;
981 struct isis_vertex *vertex;
982 struct isis_spftree *spftree = NULL;
983 u_char lsp_id[ISIS_SYS_ID_LEN + 2];
984 struct isis_lsp *lsp;
985 struct route_table *table = NULL;
986 struct route_node *rode;
987 struct isis_route_info *rinfo;
989 if (family == AF_INET)
990 spftree = area->spftree[level - 1];
991 #ifdef HAVE_IPV6
992 else if (family == AF_INET6)
993 spftree = area->spftree6[level - 1];
994 #endif
996 assert (spftree);
998 /* Make all routes in current route table inactive. */
999 if (family == AF_INET)
1000 table = area->route_table[level - 1];
1001 #ifdef HAVE_IPV6
1002 else if (family == AF_INET6)
1003 table = area->route_table6[level - 1];
1004 #endif
1006 for (rode = route_top (table); rode; rode = route_next (rode))
1008 if (rode->info == NULL)
1009 continue;
1010 rinfo = rode->info;
1012 UNSET_FLAG (rinfo->flag, ISIS_ROUTE_FLAG_ACTIVE);
1016 * C.2.5 Step 0
1018 init_spt (spftree);
1019 /* a) */
1020 isis_spf_add_self (spftree, area, level);
1021 /* b) */
1022 retval = isis_spf_preload_tent (spftree, area, level, family);
1025 * C.2.7 Step 2
1027 if (listcount (spftree->tents) == 0)
1029 zlog_warn ("ISIS-Spf: TENT is empty");
1030 goto out;
1033 while (listcount (spftree->tents) > 0)
1035 node = listhead (spftree->tents);
1036 vertex = listgetdata (node);
1037 /* Remove from tent list */
1038 list_delete_node (spftree->tents, node);
1039 if (isis_find_vertex (spftree->paths, vertex->N.id, vertex->type))
1040 continue;
1041 add_to_paths (spftree, vertex, area, level);
1042 if (vertex->type == VTYPE_PSEUDO_IS ||
1043 vertex->type == VTYPE_NONPSEUDO_IS)
1045 memcpy (lsp_id, vertex->N.id, ISIS_SYS_ID_LEN + 1);
1046 LSP_FRAGMENT (lsp_id) = 0;
1047 lsp = lsp_search (lsp_id, area->lspdb[level - 1]);
1048 if (lsp)
1050 if (LSP_PSEUDO_ID (lsp_id))
1052 isis_spf_process_pseudo_lsp (spftree, lsp, vertex->d_N,
1053 vertex->depth, family);
1056 else
1058 isis_spf_process_lsp (spftree, lsp, vertex->d_N,
1059 vertex->depth, family);
1062 else
1064 zlog_warn ("ISIS-Spf: No LSP found for %s",
1065 rawlspid_print (lsp_id));
1070 out:
1071 thread_add_event (master, isis_route_validate, area, 0);
1072 spftree->lastrun = time (NULL);
1073 spftree->pending = 0;
1075 return retval;
1079 isis_run_spf_l1 (struct thread *thread)
1081 struct isis_area *area;
1082 int retval = ISIS_OK;
1084 area = THREAD_ARG (thread);
1085 assert (area);
1087 area->spftree[0]->t_spf = NULL;
1089 if (!(area->is_type & IS_LEVEL_1))
1091 if (isis->debugs & DEBUG_SPF_EVENTS)
1092 zlog_warn ("ISIS-SPF (%s) area does not share level",
1093 area->area_tag);
1094 return ISIS_WARNING;
1097 if (isis->debugs & DEBUG_SPF_EVENTS)
1098 zlog_debug ("ISIS-Spf (%s) L1 SPF needed, periodic SPF", area->area_tag);
1100 if (area->ip_circuits)
1101 retval = isis_run_spf (area, 1, AF_INET);
1103 THREAD_TIMER_ON (master, area->spftree[0]->t_spf, isis_run_spf_l1, area,
1104 isis_jitter (PERIODIC_SPF_INTERVAL, 10));
1106 return retval;
1110 isis_run_spf_l2 (struct thread *thread)
1112 struct isis_area *area;
1113 int retval = ISIS_OK;
1115 area = THREAD_ARG (thread);
1116 assert (area);
1118 area->spftree[1]->t_spf = NULL;
1120 if (!(area->is_type & IS_LEVEL_2))
1122 if (isis->debugs & DEBUG_SPF_EVENTS)
1123 zlog_warn ("ISIS-SPF (%s) area does not share level", area->area_tag);
1124 return ISIS_WARNING;
1127 if (isis->debugs & DEBUG_SPF_EVENTS)
1128 zlog_debug ("ISIS-Spf (%s) L2 SPF needed, periodic SPF", area->area_tag);
1130 if (area->ip_circuits)
1131 retval = isis_run_spf (area, 2, AF_INET);
1133 THREAD_TIMER_ON (master, area->spftree[1]->t_spf, isis_run_spf_l2, area,
1134 isis_jitter (PERIODIC_SPF_INTERVAL, 10));
1136 return retval;
1140 isis_spf_schedule (struct isis_area *area, int level)
1142 int retval = ISIS_OK;
1143 struct isis_spftree *spftree = area->spftree[level - 1];
1144 time_t diff, now = time (NULL);
1146 if (spftree->pending)
1147 return retval;
1149 diff = now - spftree->lastrun;
1151 /* FIXME: let's wait a minute before doing the SPF */
1152 if (now - isis->uptime < 60 || isis->uptime == 0)
1154 if (level == 1)
1155 THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_l1, area, 60);
1156 else
1157 THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_l2, area, 60);
1159 spftree->pending = 1;
1160 return retval;
1163 THREAD_TIMER_OFF (spftree->t_spf);
1165 if (diff < MINIMUM_SPF_INTERVAL)
1167 if (level == 1)
1168 THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_l1, area,
1169 MINIMUM_SPF_INTERVAL - diff);
1170 else
1171 THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_l2, area,
1172 MINIMUM_SPF_INTERVAL - diff);
1174 spftree->pending = 1;
1176 else
1178 spftree->pending = 0;
1179 retval = isis_run_spf (area, level, AF_INET);
1180 if (level == 1)
1181 THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_l1, area,
1182 isis_jitter (PERIODIC_SPF_INTERVAL, 10));
1183 else
1184 THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_l2, area,
1185 isis_jitter (PERIODIC_SPF_INTERVAL, 10));
1188 return retval;
1191 #ifdef HAVE_IPV6
1192 static int
1193 isis_run_spf6_l1 (struct thread *thread)
1195 struct isis_area *area;
1196 int retval = ISIS_OK;
1198 area = THREAD_ARG (thread);
1199 assert (area);
1201 area->spftree6[0]->t_spf = NULL;
1203 if (!(area->is_type & IS_LEVEL_1))
1205 if (isis->debugs & DEBUG_SPF_EVENTS)
1206 zlog_warn ("ISIS-SPF (%s) area does not share level", area->area_tag);
1207 return ISIS_WARNING;
1210 if (isis->debugs & DEBUG_SPF_EVENTS)
1211 zlog_debug ("ISIS-Spf (%s) L1 SPF needed, periodic SPF", area->area_tag);
1213 if (area->ipv6_circuits)
1214 retval = isis_run_spf (area, 1, AF_INET6);
1216 THREAD_TIMER_ON (master, area->spftree6[0]->t_spf, isis_run_spf6_l1, area,
1217 isis_jitter (PERIODIC_SPF_INTERVAL, 10));
1219 return retval;
1222 static int
1223 isis_run_spf6_l2 (struct thread *thread)
1225 struct isis_area *area;
1226 int retval = ISIS_OK;
1228 area = THREAD_ARG (thread);
1229 assert (area);
1231 area->spftree6[1]->t_spf = NULL;
1233 if (!(area->is_type & IS_LEVEL_2))
1235 if (isis->debugs & DEBUG_SPF_EVENTS)
1236 zlog_warn ("ISIS-SPF (%s) area does not share level", area->area_tag);
1237 return ISIS_WARNING;
1240 if (isis->debugs & DEBUG_SPF_EVENTS)
1241 zlog_debug ("ISIS-Spf (%s) L2 SPF needed, periodic SPF.", area->area_tag);
1243 if (area->ipv6_circuits)
1244 retval = isis_run_spf (area, 2, AF_INET6);
1246 THREAD_TIMER_ON (master, area->spftree6[1]->t_spf, isis_run_spf6_l2, area,
1247 isis_jitter (PERIODIC_SPF_INTERVAL, 10));
1249 return retval;
1253 isis_spf_schedule6 (struct isis_area *area, int level)
1255 int retval = ISIS_OK;
1256 struct isis_spftree *spftree = area->spftree6[level - 1];
1257 time_t diff, now = time (NULL);
1259 if (spftree->pending)
1260 return retval;
1262 diff = now - spftree->lastrun;
1264 /* FIXME: let's wait a minute before doing the SPF */
1265 if (now - isis->uptime < 60 || isis->uptime == 0)
1267 if (level == 1)
1268 THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf6_l1, area, 60);
1269 else
1270 THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf6_l2, area, 60);
1272 spftree->pending = 1;
1273 return retval;
1276 THREAD_TIMER_OFF (spftree->t_spf);
1278 if (diff < MINIMUM_SPF_INTERVAL)
1280 if (level == 1)
1281 THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf6_l1, area,
1282 MINIMUM_SPF_INTERVAL - diff);
1283 else
1284 THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf6_l2, area,
1285 MINIMUM_SPF_INTERVAL - diff);
1287 spftree->pending = 1;
1289 else
1291 spftree->pending = 0;
1292 retval = isis_run_spf (area, level, AF_INET6);
1294 if (level == 1)
1295 THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf6_l1, area,
1296 isis_jitter (PERIODIC_SPF_INTERVAL, 10));
1297 else
1298 THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf6_l2, area,
1299 isis_jitter (PERIODIC_SPF_INTERVAL, 10));
1302 return retval;
1304 #endif
1306 static void
1307 isis_print_paths (struct vty *vty, struct list *paths)
1309 struct listnode *node;
1310 struct isis_vertex *vertex;
1311 struct isis_dynhn *dyn, *nh_dyn = NULL;
1312 struct isis_adjacency *adj;
1313 #if 0
1314 u_char buff[255];
1315 #endif /* 0 */
1317 vty_out (vty, "System Id Metric Next-Hop"
1318 " Interface SNPA%s", VTY_NEWLINE);
1320 for (ALL_LIST_ELEMENTS_RO (paths, node, vertex))
1322 if (vertex->type != VTYPE_NONPSEUDO_IS)
1323 continue;
1324 if (memcmp (vertex->N.id, isis->sysid, ISIS_SYS_ID_LEN) == 0)
1326 vty_out (vty, "%s --%s", host.name?host.name:"",
1327 VTY_NEWLINE);
1329 else
1331 dyn = dynhn_find_by_id ((u_char *) vertex->N.id);
1332 adj = listgetdata (listhead (vertex->Adj_N));
1333 if (adj)
1335 nh_dyn = dynhn_find_by_id (adj->sysid);
1336 vty_out (vty, "%-20s %-10u %-20s %-11s %-5s%s",
1337 (dyn != NULL) ? dyn->name.name :
1338 (const u_char *)rawlspid_print ((u_char *) vertex->N.id),
1339 vertex->d_N, (nh_dyn != NULL) ? nh_dyn->name.name :
1340 (const u_char *)rawlspid_print (adj->sysid),
1341 adj->circuit->interface->name,
1342 snpa_print (adj->snpa), VTY_NEWLINE);
1344 else
1346 vty_out (vty, "%s %u %s", dyn ? dyn->name.name :
1347 (const u_char *) rawlspid_print (vertex->N.id),
1348 vertex->d_N, VTY_NEWLINE);
1351 #if 0
1352 vty_out (vty, "%s %s %u %s", vtype2string (vertex->type),
1353 vid2string (vertex, buff), vertex->d_N, VTY_NEWLINE);
1354 #endif
1358 DEFUN (show_isis_topology,
1359 show_isis_topology_cmd,
1360 "show isis topology",
1361 SHOW_STR
1362 "IS-IS information\n"
1363 "IS-IS paths to Intermediate Systems\n")
1365 struct listnode *node;
1366 struct isis_area *area;
1367 int level;
1369 if (!isis->area_list || isis->area_list->count == 0)
1370 return CMD_SUCCESS;
1372 for (ALL_LIST_ELEMENTS_RO (isis->area_list, node, area))
1374 vty_out (vty, "Area %s:%s", area->area_tag ? area->area_tag : "null",
1375 VTY_NEWLINE);
1377 for (level = 0; level < ISIS_LEVELS; level++)
1379 if (area->ip_circuits > 0 && area->spftree[level]
1380 && area->spftree[level]->paths->count > 0)
1382 vty_out (vty, "IS-IS paths to level-%d routers that speak IP%s",
1383 level + 1, VTY_NEWLINE);
1384 isis_print_paths (vty, area->spftree[level]->paths);
1386 #ifdef HAVE_IPV6
1387 if (area->ipv6_circuits > 0 && area->spftree6[level]
1388 && area->spftree6[level]->paths->count > 0)
1390 vty_out (vty,
1391 "IS-IS paths to level-%d routers that speak IPv6%s",
1392 level + 1, VTY_NEWLINE);
1393 isis_print_paths (vty, area->spftree6[level]->paths);
1395 #endif /* HAVE_IPV6 */
1399 return CMD_SUCCESS;
1402 DEFUN (show_isis_topology_l1,
1403 show_isis_topology_l1_cmd,
1404 "show isis topology level-1",
1405 SHOW_STR
1406 "IS-IS information\n"
1407 "IS-IS paths to Intermediate Systems\n"
1408 "Paths to all level-1 routers in the area\n")
1410 struct listnode *node;
1411 struct isis_area *area;
1413 if (!isis->area_list || isis->area_list->count == 0)
1414 return CMD_SUCCESS;
1416 for (ALL_LIST_ELEMENTS_RO (isis->area_list, node, area))
1418 vty_out (vty, "Area %s:%s", area->area_tag ? area->area_tag : "null",
1419 VTY_NEWLINE);
1421 if (area->ip_circuits > 0 && area->spftree[0]
1422 && area->spftree[0]->paths->count > 0)
1424 vty_out (vty, "IS-IS paths to level-1 routers that speak IP%s",
1425 VTY_NEWLINE);
1426 isis_print_paths (vty, area->spftree[0]->paths);
1428 #ifdef HAVE_IPV6
1429 if (area->ipv6_circuits > 0 && area->spftree6[0]
1430 && area->spftree6[0]->paths->count > 0)
1432 vty_out (vty, "IS-IS paths to level-1 routers that speak IPv6%s",
1433 VTY_NEWLINE);
1434 isis_print_paths (vty, area->spftree6[0]->paths);
1436 #endif /* HAVE_IPV6 */
1439 return CMD_SUCCESS;
1442 DEFUN (show_isis_topology_l2,
1443 show_isis_topology_l2_cmd,
1444 "show isis topology level-2",
1445 SHOW_STR
1446 "IS-IS information\n"
1447 "IS-IS paths to Intermediate Systems\n"
1448 "Paths to all level-2 routers in the domain\n")
1450 struct listnode *node;
1451 struct isis_area *area;
1453 if (!isis->area_list || isis->area_list->count == 0)
1454 return CMD_SUCCESS;
1456 for (ALL_LIST_ELEMENTS_RO (isis->area_list, node, area))
1458 vty_out (vty, "Area %s:%s", area->area_tag ? area->area_tag : "null",
1459 VTY_NEWLINE);
1461 if (area->ip_circuits > 0 && area->spftree[1]
1462 && area->spftree[1]->paths->count > 0)
1464 vty_out (vty, "IS-IS paths to level-2 routers that speak IP%s",
1465 VTY_NEWLINE);
1466 isis_print_paths (vty, area->spftree[1]->paths);
1468 #ifdef HAVE_IPV6
1469 if (area->ipv6_circuits > 0 && area->spftree6[1]
1470 && area->spftree6[1]->paths->count > 0)
1472 vty_out (vty, "IS-IS paths to level-2 routers that speak IPv6%s",
1473 VTY_NEWLINE);
1474 isis_print_paths (vty, area->spftree6[1]->paths);
1476 #endif /* HAVE_IPV6 */
1479 return CMD_SUCCESS;
1482 void
1483 isis_spf_cmds_init ()
1485 install_element (VIEW_NODE, &show_isis_topology_cmd);
1486 install_element (VIEW_NODE, &show_isis_topology_l1_cmd);
1487 install_element (VIEW_NODE, &show_isis_topology_l2_cmd);
1489 install_element (ENABLE_NODE, &show_isis_topology_cmd);
1490 install_element (ENABLE_NODE, &show_isis_topology_l1_cmd);
1491 install_element (ENABLE_NODE, &show_isis_topology_l2_cmd);