blob: d0e9101b852431a66a8b8b2fd079fc56c83015d2 [file] [log] [blame]
paul718e3742002-12-13 20:15:29 +00001/*
hasso508e53e2004-05-18 18:57:06 +00002 * Copyright (C) 2003 Yasuhiro Ohara
paul718e3742002-12-13 20:15:29 +00003 *
4 * This file is part of GNU Zebra.
5 *
6 * GNU Zebra is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2, or (at your option) any
9 * later version.
10 *
11 * GNU Zebra is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with GNU Zebra; see the file COPYING. If not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
20 */
hasso508e53e2004-05-18 18:57:06 +000021
paul718e3742002-12-13 20:15:29 +000022/* Shortest Path First calculation for OSPFv3 */
23
hasso508e53e2004-05-18 18:57:06 +000024#include <zebra.h>
paul718e3742002-12-13 20:15:29 +000025
hasso508e53e2004-05-18 18:57:06 +000026#include "log.h"
27#include "memory.h"
28#include "command.h"
29#include "vty.h"
paul718e3742002-12-13 20:15:29 +000030#include "prefix.h"
hasso508e53e2004-05-18 18:57:06 +000031#include "pqueue.h"
32#include "linklist.h"
33#include "thread.h"
paul718e3742002-12-13 20:15:29 +000034
paul718e3742002-12-13 20:15:29 +000035#include "ospf6_lsa.h"
36#include "ospf6_lsdb.h"
37#include "ospf6_route.h"
paul718e3742002-12-13 20:15:29 +000038#include "ospf6_area.h"
hasso508e53e2004-05-18 18:57:06 +000039#include "ospf6_spf.h"
40#include "ospf6_intra.h"
41#include "ospf6_interface.h"
hasso049207c2004-08-04 20:02:13 +000042#include "ospf6d.h"
Paul Jakma7aa9dce2014-09-19 14:42:23 +010043#include "ospf6_abr.h"
paul718e3742002-12-13 20:15:29 +000044
hasso508e53e2004-05-18 18:57:06 +000045unsigned char conf_debug_ospf6_spf = 0;
paul718e3742002-12-13 20:15:29 +000046
Paul Jakma6ac29a52008-08-15 13:45:30 +010047static int
hasso508e53e2004-05-18 18:57:06 +000048ospf6_vertex_cmp (void *a, void *b)
paul718e3742002-12-13 20:15:29 +000049{
hasso508e53e2004-05-18 18:57:06 +000050 struct ospf6_vertex *va = (struct ospf6_vertex *) a;
51 struct ospf6_vertex *vb = (struct ospf6_vertex *) b;
52
53 /* ascending order */
Dmitrij Tejblum403138e2011-01-13 18:25:40 +030054 if (va->cost != vb->cost)
55 return (va->cost - vb->cost);
56 return (va->hops - vb->hops);
paul718e3742002-12-13 20:15:29 +000057}
58
Paul Jakma6ac29a52008-08-15 13:45:30 +010059static int
hasso508e53e2004-05-18 18:57:06 +000060ospf6_vertex_id_cmp (void *a, void *b)
paul718e3742002-12-13 20:15:29 +000061{
hasso508e53e2004-05-18 18:57:06 +000062 struct ospf6_vertex *va = (struct ospf6_vertex *) a;
63 struct ospf6_vertex *vb = (struct ospf6_vertex *) b;
64 int ret = 0;
paul718e3742002-12-13 20:15:29 +000065
hasso508e53e2004-05-18 18:57:06 +000066 ret = ntohl (ospf6_linkstate_prefix_adv_router (&va->vertex_id)) -
67 ntohl (ospf6_linkstate_prefix_adv_router (&vb->vertex_id));
68 if (ret)
69 return ret;
paul718e3742002-12-13 20:15:29 +000070
hasso508e53e2004-05-18 18:57:06 +000071 ret = ntohl (ospf6_linkstate_prefix_id (&va->vertex_id)) -
72 ntohl (ospf6_linkstate_prefix_id (&vb->vertex_id));
paul718e3742002-12-13 20:15:29 +000073 return ret;
74}
75
Paul Jakma6ac29a52008-08-15 13:45:30 +010076static struct ospf6_vertex *
hasso508e53e2004-05-18 18:57:06 +000077ospf6_vertex_create (struct ospf6_lsa *lsa)
78{
79 struct ospf6_vertex *v;
80 int i;
81
82 v = (struct ospf6_vertex *)
83 XMALLOC (MTYPE_OSPF6_VERTEX, sizeof (struct ospf6_vertex));
84
85 /* type */
86 if (ntohs (lsa->header->type) == OSPF6_LSTYPE_ROUTER)
87 v->type = OSPF6_VERTEX_TYPE_ROUTER;
88 else if (ntohs (lsa->header->type) == OSPF6_LSTYPE_NETWORK)
89 v->type = OSPF6_VERTEX_TYPE_NETWORK;
90 else
91 assert (0);
92
93 /* vertex_id */
94 ospf6_linkstate_prefix (lsa->header->adv_router, lsa->header->id,
95 &v->vertex_id);
96
97 /* name */
98 ospf6_linkstate_prefix2str (&v->vertex_id, v->name, sizeof (v->name));
99
100 /* Associated LSA */
101 v->lsa = lsa;
102
103 /* capability bits + options */
104 v->capability = *(u_char *)(OSPF6_LSA_HEADER_END (lsa->header));
105 v->options[0] = *(u_char *)(OSPF6_LSA_HEADER_END (lsa->header) + 1);
106 v->options[1] = *(u_char *)(OSPF6_LSA_HEADER_END (lsa->header) + 2);
107 v->options[2] = *(u_char *)(OSPF6_LSA_HEADER_END (lsa->header) + 3);
108
109 for (i = 0; i < OSPF6_MULTI_PATH_LIMIT; i++)
110 ospf6_nexthop_clear (&v->nexthop[i]);
111
112 v->parent = NULL;
113 v->child_list = list_new ();
114 v->child_list->cmp = ospf6_vertex_id_cmp;
115
116 return v;
117}
118
Paul Jakma6ac29a52008-08-15 13:45:30 +0100119static void
hasso508e53e2004-05-18 18:57:06 +0000120ospf6_vertex_delete (struct ospf6_vertex *v)
paul718e3742002-12-13 20:15:29 +0000121{
hasso508e53e2004-05-18 18:57:06 +0000122 list_delete (v->child_list);
paul718e3742002-12-13 20:15:29 +0000123 XFREE (MTYPE_OSPF6_VERTEX, v);
124}
125
Paul Jakma6ac29a52008-08-15 13:45:30 +0100126static struct ospf6_lsa *
hasso508e53e2004-05-18 18:57:06 +0000127ospf6_lsdesc_lsa (caddr_t lsdesc, struct ospf6_vertex *v)
paul718e3742002-12-13 20:15:29 +0000128{
paul718e3742002-12-13 20:15:29 +0000129 struct ospf6_lsa *lsa;
hasso508e53e2004-05-18 18:57:06 +0000130 u_int16_t type = 0;
131 u_int32_t id = 0, adv_router = 0;
paul718e3742002-12-13 20:15:29 +0000132
hasso508e53e2004-05-18 18:57:06 +0000133 if (VERTEX_IS_TYPE (NETWORK, v))
paul718e3742002-12-13 20:15:29 +0000134 {
hasso508e53e2004-05-18 18:57:06 +0000135 type = htons (OSPF6_LSTYPE_ROUTER);
136 id = htonl (0);
137 adv_router = NETWORK_LSDESC_GET_NBR_ROUTERID (lsdesc);
paul718e3742002-12-13 20:15:29 +0000138 }
paul718e3742002-12-13 20:15:29 +0000139 else
paul718e3742002-12-13 20:15:29 +0000140 {
hasso508e53e2004-05-18 18:57:06 +0000141 if (ROUTER_LSDESC_IS_TYPE (POINTTOPOINT, lsdesc))
paul718e3742002-12-13 20:15:29 +0000142 {
hasso508e53e2004-05-18 18:57:06 +0000143 type = htons (OSPF6_LSTYPE_ROUTER);
144 id = htonl (0);
145 adv_router = ROUTER_LSDESC_GET_NBR_ROUTERID (lsdesc);
146 }
147 else if (ROUTER_LSDESC_IS_TYPE (TRANSIT_NETWORK, lsdesc))
148 {
149 type = htons (OSPF6_LSTYPE_NETWORK);
150 id = htonl (ROUTER_LSDESC_GET_NBR_IFID (lsdesc));
151 adv_router = ROUTER_LSDESC_GET_NBR_ROUTERID (lsdesc);
paul718e3742002-12-13 20:15:29 +0000152 }
153 }
154
hasso508e53e2004-05-18 18:57:06 +0000155 lsa = ospf6_lsdb_lookup (type, id, adv_router, v->area->lsdb);
paul718e3742002-12-13 20:15:29 +0000156
hasso3b687352004-08-19 06:56:53 +0000157 if (IS_OSPF6_DEBUG_SPF (PROCESS))
paul718e3742002-12-13 20:15:29 +0000158 {
hasso508e53e2004-05-18 18:57:06 +0000159 char ibuf[16], abuf[16];
160 inet_ntop (AF_INET, &id, ibuf, sizeof (ibuf));
161 inet_ntop (AF_INET, &adv_router, abuf, sizeof (abuf));
162 if (lsa)
hassoc6487d62004-12-24 06:00:11 +0000163 zlog_debug (" Link to: %s", lsa->name);
hasso508e53e2004-05-18 18:57:06 +0000164 else
hassoc6487d62004-12-24 06:00:11 +0000165 zlog_debug (" Link to: [%s Id:%s Adv:%s] No LSA",
166 ospf6_lstype_name (type), ibuf, abuf);
paul718e3742002-12-13 20:15:29 +0000167 }
168
hasso508e53e2004-05-18 18:57:06 +0000169 return lsa;
170}
171
Paul Jakma6ac29a52008-08-15 13:45:30 +0100172static char *
hasso508e53e2004-05-18 18:57:06 +0000173ospf6_lsdesc_backlink (struct ospf6_lsa *lsa,
174 caddr_t lsdesc, struct ospf6_vertex *v)
175{
176 caddr_t backlink, found = NULL;
177 int size;
178
179 size = (OSPF6_LSA_IS_TYPE (ROUTER, lsa) ?
180 sizeof (struct ospf6_router_lsdesc) :
181 sizeof (struct ospf6_network_lsdesc));
182 for (backlink = OSPF6_LSA_HEADER_END (lsa->header) + 4;
183 backlink + size <= OSPF6_LSA_END (lsa->header); backlink += size)
184 {
185 assert (! (OSPF6_LSA_IS_TYPE (NETWORK, lsa) &&
186 VERTEX_IS_TYPE (NETWORK, v)));
187
188 if (OSPF6_LSA_IS_TYPE (NETWORK, lsa) &&
189 NETWORK_LSDESC_GET_NBR_ROUTERID (backlink)
190 == v->lsa->header->adv_router)
191 found = backlink;
192 else if (VERTEX_IS_TYPE (NETWORK, v) &&
193 ROUTER_LSDESC_IS_TYPE (TRANSIT_NETWORK, backlink) &&
194 ROUTER_LSDESC_GET_NBR_ROUTERID (backlink)
195 == v->lsa->header->adv_router &&
196 ROUTER_LSDESC_GET_NBR_IFID (backlink)
197 == ntohl (v->lsa->header->id))
198 found = backlink;
199 else
200 {
201 if (! ROUTER_LSDESC_IS_TYPE (POINTTOPOINT, backlink) ||
202 ! ROUTER_LSDESC_IS_TYPE (POINTTOPOINT, lsdesc))
203 continue;
204 if (ROUTER_LSDESC_GET_NBR_IFID (backlink) !=
205 ROUTER_LSDESC_GET_IFID (lsdesc) ||
206 ROUTER_LSDESC_GET_NBR_IFID (lsdesc) !=
207 ROUTER_LSDESC_GET_IFID (backlink))
208 continue;
209 if (ROUTER_LSDESC_GET_NBR_ROUTERID (backlink) !=
210 v->lsa->header->adv_router ||
211 ROUTER_LSDESC_GET_NBR_ROUTERID (lsdesc) !=
212 lsa->header->adv_router)
213 continue;
214 found = backlink;
215 }
216 }
217
hasso3b687352004-08-19 06:56:53 +0000218 if (IS_OSPF6_DEBUG_SPF (PROCESS))
hassoc6487d62004-12-24 06:00:11 +0000219 zlog_debug (" Backlink %s", (found ? "OK" : "FAIL"));
hasso508e53e2004-05-18 18:57:06 +0000220
221 return found;
222}
223
Paul Jakma6ac29a52008-08-15 13:45:30 +0100224static void
hasso508e53e2004-05-18 18:57:06 +0000225ospf6_nexthop_calc (struct ospf6_vertex *w, struct ospf6_vertex *v,
226 caddr_t lsdesc)
227{
228 int i, ifindex;
229 struct ospf6_interface *oi;
230 u_int16_t type;
231 u_int32_t adv_router;
232 struct ospf6_lsa *lsa;
233 struct ospf6_link_lsa *link_lsa;
234 char buf[64];
235
236 assert (VERTEX_IS_TYPE (ROUTER, w));
237 ifindex = (VERTEX_IS_TYPE (NETWORK, v) ? v->nexthop[0].ifindex :
238 ROUTER_LSDESC_GET_IFID (lsdesc));
239 oi = ospf6_interface_lookup_by_ifindex (ifindex);
240 if (oi == NULL)
241 {
hasso3b687352004-08-19 06:56:53 +0000242 if (IS_OSPF6_DEBUG_SPF (PROCESS))
hassoc6487d62004-12-24 06:00:11 +0000243 zlog_debug ("Can't find interface in SPF: ifindex %d", ifindex);
hasso508e53e2004-05-18 18:57:06 +0000244 return;
245 }
246
247 type = htons (OSPF6_LSTYPE_LINK);
248 adv_router = (VERTEX_IS_TYPE (NETWORK, v) ?
249 NETWORK_LSDESC_GET_NBR_ROUTERID (lsdesc) :
250 ROUTER_LSDESC_GET_NBR_ROUTERID (lsdesc));
251
252 i = 0;
253 for (lsa = ospf6_lsdb_type_router_head (type, adv_router, oi->lsdb); lsa;
254 lsa = ospf6_lsdb_type_router_next (type, adv_router, lsa))
255 {
256 if (VERTEX_IS_TYPE (ROUTER, v) &&
257 htonl (ROUTER_LSDESC_GET_NBR_IFID (lsdesc)) != lsa->header->id)
258 continue;
259
260 link_lsa = (struct ospf6_link_lsa *) OSPF6_LSA_HEADER_END (lsa->header);
hasso3b687352004-08-19 06:56:53 +0000261 if (IS_OSPF6_DEBUG_SPF (PROCESS))
hasso508e53e2004-05-18 18:57:06 +0000262 {
263 inet_ntop (AF_INET6, &link_lsa->linklocal_addr, buf, sizeof (buf));
hassoc6487d62004-12-24 06:00:11 +0000264 zlog_debug (" nexthop %s from %s", buf, lsa->name);
hasso508e53e2004-05-18 18:57:06 +0000265 }
266
267 if (i < OSPF6_MULTI_PATH_LIMIT)
268 {
269 memcpy (&w->nexthop[i].address, &link_lsa->linklocal_addr,
270 sizeof (struct in6_addr));
271 w->nexthop[i].ifindex = ifindex;
272 i++;
273 }
274 }
275
hasso3b687352004-08-19 06:56:53 +0000276 if (i == 0 && IS_OSPF6_DEBUG_SPF (PROCESS))
hassoc6487d62004-12-24 06:00:11 +0000277 zlog_debug ("No nexthop for %s found", w->name);
hasso508e53e2004-05-18 18:57:06 +0000278}
279
Paul Jakma6ac29a52008-08-15 13:45:30 +0100280static int
hasso508e53e2004-05-18 18:57:06 +0000281ospf6_spf_install (struct ospf6_vertex *v,
282 struct ospf6_route_table *result_table)
283{
284 struct ospf6_route *route;
285 int i, j;
Denis Ovsienko87362ce2011-08-27 22:19:34 +0400286 struct ospf6_vertex *prev;
hasso508e53e2004-05-18 18:57:06 +0000287
hasso3b687352004-08-19 06:56:53 +0000288 if (IS_OSPF6_DEBUG_SPF (PROCESS))
hassoc6487d62004-12-24 06:00:11 +0000289 zlog_debug ("SPF install %s hops %d cost %d",
290 v->name, v->hops, v->cost);
hasso508e53e2004-05-18 18:57:06 +0000291
292 route = ospf6_route_lookup (&v->vertex_id, result_table);
293 if (route && route->path.cost < v->cost)
294 {
hasso3b687352004-08-19 06:56:53 +0000295 if (IS_OSPF6_DEBUG_SPF (PROCESS))
hassoc6487d62004-12-24 06:00:11 +0000296 zlog_debug (" already installed with lower cost (%d), ignore",
297 route->path.cost);
hasso508e53e2004-05-18 18:57:06 +0000298 ospf6_vertex_delete (v);
299 return -1;
300 }
301 else if (route && route->path.cost == v->cost)
302 {
hasso3b687352004-08-19 06:56:53 +0000303 if (IS_OSPF6_DEBUG_SPF (PROCESS))
hassoc6487d62004-12-24 06:00:11 +0000304 zlog_debug (" another path found, merge");
hasso508e53e2004-05-18 18:57:06 +0000305
306 for (i = 0; ospf6_nexthop_is_set (&v->nexthop[i]) &&
307 i < OSPF6_MULTI_PATH_LIMIT; i++)
308 {
309 for (j = 0; j < OSPF6_MULTI_PATH_LIMIT; j++)
310 {
311 if (ospf6_nexthop_is_set (&route->nexthop[j]))
312 {
313 if (ospf6_nexthop_is_same (&route->nexthop[j],
314 &v->nexthop[i]))
315 break;
316 else
317 continue;
318 }
319 ospf6_nexthop_copy (&route->nexthop[j], &v->nexthop[i]);
320 break;
321 }
322 }
323
324 prev = (struct ospf6_vertex *) route->route_option;
Dmitrij Tejblum403138e2011-01-13 18:25:40 +0300325 assert (prev->hops <= v->hops);
326 ospf6_vertex_delete (v);
hasso508e53e2004-05-18 18:57:06 +0000327
328 return -1;
329 }
330
331 /* There should be no case where candidate being installed (variable
332 "v") is closer than the one in the SPF tree (variable "route").
hasso6452df02004-08-15 05:52:07 +0000333 In the case something has gone wrong with the behavior of
hasso508e53e2004-05-18 18:57:06 +0000334 Priority-Queue. */
hasso6452df02004-08-15 05:52:07 +0000335
336 /* the case where the route exists already is handled and returned
337 up to here. */
hasso508e53e2004-05-18 18:57:06 +0000338 assert (route == NULL);
339
340 route = ospf6_route_create ();
341 memcpy (&route->prefix, &v->vertex_id, sizeof (struct prefix));
342 route->type = OSPF6_DEST_TYPE_LINKSTATE;
343 route->path.type = OSPF6_PATH_TYPE_INTRA;
344 route->path.origin.type = v->lsa->header->type;
345 route->path.origin.id = v->lsa->header->id;
346 route->path.origin.adv_router = v->lsa->header->adv_router;
347 route->path.metric_type = 1;
348 route->path.cost = v->cost;
349 route->path.cost_e2 = v->hops;
350 route->path.router_bits = v->capability;
351 route->path.options[0] = v->options[0];
352 route->path.options[1] = v->options[1];
353 route->path.options[2] = v->options[2];
354
355 for (i = 0; ospf6_nexthop_is_set (&v->nexthop[i]) &&
356 i < OSPF6_MULTI_PATH_LIMIT; i++)
357 ospf6_nexthop_copy (&route->nexthop[i], &v->nexthop[i]);
358
359 if (v->parent)
360 listnode_add_sort (v->parent->child_list, v);
361 route->route_option = v;
362
363 ospf6_route_add (route, result_table);
paul718e3742002-12-13 20:15:29 +0000364 return 0;
365}
366
hasso508e53e2004-05-18 18:57:06 +0000367void
368ospf6_spf_table_finish (struct ospf6_route_table *result_table)
369{
370 struct ospf6_route *route;
371 struct ospf6_vertex *v;
372 for (route = ospf6_route_head (result_table); route;
373 route = ospf6_route_next (route))
374 {
375 v = (struct ospf6_vertex *) route->route_option;
376 ospf6_vertex_delete (v);
377 ospf6_route_remove (route, result_table);
378 }
379}
380
Dinesh Dutta0edf672013-08-26 03:40:23 +0000381static const char *ospf6_spf_reason_str[] =
382 {
383 "R+",
384 "R-",
385 "N+",
386 "N-",
387 "L+",
388 "L-",
389 "R*",
390 "N*",
391 };
392
393void ospf6_spf_reason_string (unsigned int reason, char *buf, int size)
394{
Paul Jakma7aa9dce2014-09-19 14:42:23 +0100395 size_t bit;
Dinesh Dutta0edf672013-08-26 03:40:23 +0000396 int len = 0;
397
398 if (!buf)
399 return;
400
401 for (bit = 0; bit <= (sizeof(ospf6_spf_reason_str) / sizeof(char *)); bit++)
402 {
403 if ((reason & (1 << bit)) && (len < size))
404 {
405 len += snprintf((buf + len), (size - len), "%s%s",
406 (len > 0) ? ", " : "", ospf6_spf_reason_str[bit]);
407 }
408 }
409}
410
hasso6452df02004-08-15 05:52:07 +0000411/* RFC2328 16.1. Calculating the shortest-path tree for an area */
412/* RFC2740 3.8.1. Calculating the shortest path tree for an area */
hasso508e53e2004-05-18 18:57:06 +0000413void
414ospf6_spf_calculation (u_int32_t router_id,
415 struct ospf6_route_table *result_table,
416 struct ospf6_area *oa)
417{
418 struct pqueue *candidate_list;
419 struct ospf6_vertex *root, *v, *w;
420 int i;
421 int size;
422 caddr_t lsdesc;
423 struct ospf6_lsa *lsa;
424
Tom Goffb48cebb2011-12-14 14:11:29 +0400425 ospf6_spf_table_finish (result_table);
426
hasso508e53e2004-05-18 18:57:06 +0000427 /* Install the calculating router itself as the root of the SPF tree */
428 /* construct root vertex */
429 lsa = ospf6_lsdb_lookup (htons (OSPF6_LSTYPE_ROUTER), htonl (0),
430 router_id, oa->lsdb);
431 if (lsa == NULL)
432 return;
Tom Goff1d192342010-11-10 13:02:38 -0800433
434 /* initialize */
435 candidate_list = pqueue_create ();
436 candidate_list->cmp = ospf6_vertex_cmp;
437
hasso508e53e2004-05-18 18:57:06 +0000438 root = ospf6_vertex_create (lsa);
439 root->area = oa;
440 root->cost = 0;
441 root->hops = 0;
hasso6452df02004-08-15 05:52:07 +0000442 root->nexthop[0].ifindex = 0; /* loopbak I/F is better ... */
hasso508e53e2004-05-18 18:57:06 +0000443 inet_pton (AF_INET6, "::1", &root->nexthop[0].address);
444
445 /* Actually insert root to the candidate-list as the only candidate */
446 pqueue_enqueue (root, candidate_list);
447
448 /* Iterate until candidate-list becomes empty */
449 while (candidate_list->size)
450 {
451 /* get closest candidate from priority queue */
452 v = pqueue_dequeue (candidate_list);
453
hasso6452df02004-08-15 05:52:07 +0000454 /* installing may result in merging or rejecting of the vertex */
hasso508e53e2004-05-18 18:57:06 +0000455 if (ospf6_spf_install (v, result_table) < 0)
456 continue;
457
Dinesh Duttf41b4a02013-08-24 08:00:37 +0000458 /* Skip overloaded routers */
459 if ((OSPF6_LSA_IS_TYPE (ROUTER, v->lsa) &&
460 ospf6_router_is_stub_router (v->lsa)))
461 continue;
462
hasso508e53e2004-05-18 18:57:06 +0000463 /* For each LS description in the just-added vertex V's LSA */
464 size = (VERTEX_IS_TYPE (ROUTER, v) ?
465 sizeof (struct ospf6_router_lsdesc) :
466 sizeof (struct ospf6_network_lsdesc));
467 for (lsdesc = OSPF6_LSA_HEADER_END (v->lsa->header) + 4;
468 lsdesc + size <= OSPF6_LSA_END (v->lsa->header); lsdesc += size)
469 {
470 lsa = ospf6_lsdesc_lsa (lsdesc, v);
471 if (lsa == NULL)
472 continue;
473
474 if (! ospf6_lsdesc_backlink (lsa, lsdesc, v))
475 continue;
476
477 w = ospf6_vertex_create (lsa);
478 w->area = oa;
479 w->parent = v;
480 if (VERTEX_IS_TYPE (ROUTER, v))
481 {
482 w->cost = v->cost + ROUTER_LSDESC_GET_METRIC (lsdesc);
483 w->hops = v->hops + (VERTEX_IS_TYPE (NETWORK, w) ? 0 : 1);
484 }
485 else /* NETWORK */
486 {
487 w->cost = v->cost;
488 w->hops = v->hops + 1;
489 }
490
491 /* nexthop calculation */
492 if (w->hops == 0)
493 w->nexthop[0].ifindex = ROUTER_LSDESC_GET_IFID (lsdesc);
494 else if (w->hops == 1 && v->hops == 0)
495 ospf6_nexthop_calc (w, v, lsdesc);
496 else
497 {
498 for (i = 0; ospf6_nexthop_is_set (&v->nexthop[i]) &&
499 i < OSPF6_MULTI_PATH_LIMIT; i++)
500 ospf6_nexthop_copy (&w->nexthop[i], &v->nexthop[i]);
501 }
502
503 /* add new candidate to the candidate_list */
hasso3b687352004-08-19 06:56:53 +0000504 if (IS_OSPF6_DEBUG_SPF (PROCESS))
hassoc6487d62004-12-24 06:00:11 +0000505 zlog_debug (" New candidate: %s hops %d cost %d",
506 w->name, w->hops, w->cost);
hasso508e53e2004-05-18 18:57:06 +0000507 pqueue_enqueue (w, candidate_list);
508 }
509 }
510
511 pqueue_delete (candidate_list);
Vincent Bernatea86e402012-06-04 10:29:49 +0200512
513 oa->spf_calculation++;
hasso508e53e2004-05-18 18:57:06 +0000514}
515
Paul Jakma6ac29a52008-08-15 13:45:30 +0100516static void
hasso2680aa22004-11-25 20:54:46 +0000517ospf6_spf_log_database (struct ospf6_area *oa)
518{
519 char *p, *end, buffer[256];
520 struct listnode *node;
521 struct ospf6_interface *oi;
522
523 p = buffer;
524 end = buffer + sizeof (buffer);
525
526 snprintf (p, end - p, "SPF on DB (#LSAs):");
527 p = (buffer + strlen (buffer) < end ? buffer + strlen (buffer) : end);
528 snprintf (p, end - p, " Area %s: %d", oa->name, oa->lsdb->count);
529 p = (buffer + strlen (buffer) < end ? buffer + strlen (buffer) : end);
530
paul1eb8ef22005-04-07 07:30:20 +0000531 for (ALL_LIST_ELEMENTS_RO (oa->if_list, node, oi))
hasso2680aa22004-11-25 20:54:46 +0000532 {
hasso2680aa22004-11-25 20:54:46 +0000533 snprintf (p, end - p, " I/F %s: %d",
534 oi->interface->name, oi->lsdb->count);
535 p = (buffer + strlen (buffer) < end ? buffer + strlen (buffer) : end);
536 }
537
hassoc6487d62004-12-24 06:00:11 +0000538 zlog_debug ("%s", buffer);
hasso2680aa22004-11-25 20:54:46 +0000539}
540
Paul Jakma6ac29a52008-08-15 13:45:30 +0100541static int
paul718e3742002-12-13 20:15:29 +0000542ospf6_spf_calculation_thread (struct thread *t)
543{
hasso508e53e2004-05-18 18:57:06 +0000544 struct ospf6_area *oa;
Dinesh Dutt3810e062013-08-24 07:54:09 +0000545 struct ospf6 *ospf6;
hasso508e53e2004-05-18 18:57:06 +0000546 struct timeval start, end, runtime;
Dinesh Dutt3810e062013-08-24 07:54:09 +0000547 struct listnode *node;
548 struct ospf6_route *route;
Dinesh Dutta0edf672013-08-26 03:40:23 +0000549 int areas_processed = 0;
550 char rbuf[32];
paul718e3742002-12-13 20:15:29 +0000551
Dinesh Dutt3810e062013-08-24 07:54:09 +0000552 ospf6 = (struct ospf6 *)THREAD_ARG (t);
553 ospf6->t_spf_calc = NULL;
paul718e3742002-12-13 20:15:29 +0000554
555 /* execute SPF calculation */
Takashi Sogabe86f72dc2009-06-22 13:07:02 +0900556 quagga_gettime (QUAGGA_CLK_MONOTONIC, &start);
Dinesh Dutt3810e062013-08-24 07:54:09 +0000557
558 for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, oa))
559 {
560
561 if (oa == ospf6->backbone)
562 continue;
563
564 if (IS_OSPF6_DEBUG_SPF (PROCESS))
565 zlog_debug ("SPF calculation for Area %s", oa->name);
566 if (IS_OSPF6_DEBUG_SPF (DATABASE))
567 ospf6_spf_log_database (oa);
568
569 ospf6_spf_calculation (ospf6->router_id, oa->spf_table, oa);
570 ospf6_intra_route_calculation (oa);
571 ospf6_intra_brouter_calculation (oa);
Dinesh Dutta0edf672013-08-26 03:40:23 +0000572
573 areas_processed++;
Dinesh Dutt3810e062013-08-24 07:54:09 +0000574 }
575
576 if (ospf6->backbone)
577 {
578 if (IS_OSPF6_DEBUG_SPF (PROCESS))
579 zlog_debug ("SPF calculation for Backbone area %s",
580 ospf6->backbone->name);
581 if (IS_OSPF6_DEBUG_SPF (DATABASE))
582 ospf6_spf_log_database(ospf6->backbone);
583
584 ospf6_spf_calculation(ospf6->router_id, ospf6->backbone->spf_table,
585 ospf6->backbone);
586 ospf6_intra_route_calculation(ospf6->backbone);
587 ospf6_intra_brouter_calculation(ospf6->backbone);
Dinesh Dutta0edf672013-08-26 03:40:23 +0000588 areas_processed++;
Dinesh Dutt3810e062013-08-24 07:54:09 +0000589 }
590
591 /* Redo summaries if required */
592 for (route = ospf6_route_head (ospf6->route_table); route;
593 route = ospf6_route_next (route))
594 ospf6_abr_originate_summary(route);
595
Takashi Sogabe86f72dc2009-06-22 13:07:02 +0900596 quagga_gettime (QUAGGA_CLK_MONOTONIC, &end);
hasso2680aa22004-11-25 20:54:46 +0000597 timersub (&end, &start, &runtime);
paul718e3742002-12-13 20:15:29 +0000598
Dinesh Dutt3810e062013-08-24 07:54:09 +0000599 ospf6->ts_spf_duration = runtime;
600
Dinesh Dutta0edf672013-08-26 03:40:23 +0000601 ospf6_spf_reason_string(ospf6->spf_reason, rbuf, sizeof(rbuf));
602
hasso3b687352004-08-19 06:56:53 +0000603 if (IS_OSPF6_DEBUG_SPF (PROCESS) || IS_OSPF6_DEBUG_SPF (TIME))
hassoc6487d62004-12-24 06:00:11 +0000604 zlog_debug ("SPF runtime: %ld sec %ld usec",
605 runtime.tv_sec, runtime.tv_usec);
paul718e3742002-12-13 20:15:29 +0000606
Dinesh Dutta0edf672013-08-26 03:40:23 +0000607 zlog_info("SPF processing: # Areas: %d, SPF runtime: %ld sec %ld usec, "
608 "Reason: %s\n", areas_processed, runtime.tv_sec, runtime.tv_usec,
609 rbuf);
610 ospf6->last_spf_reason = ospf6->spf_reason;
611 ospf6_reset_spf_reason(ospf6);
paul718e3742002-12-13 20:15:29 +0000612 return 0;
613}
614
Dinesh Dutt3810e062013-08-24 07:54:09 +0000615/* Add schedule for SPF calculation. To avoid frequenst SPF calc, we
616 set timer for SPF calc. */
paul718e3742002-12-13 20:15:29 +0000617void
Dinesh Dutta0edf672013-08-26 03:40:23 +0000618ospf6_spf_schedule (struct ospf6 *ospf6, unsigned int reason)
paul718e3742002-12-13 20:15:29 +0000619{
Dinesh Dutt3810e062013-08-24 07:54:09 +0000620 unsigned long delay, elapsed, ht;
621 struct timeval now, result;
622
Dinesh Dutta0edf672013-08-26 03:40:23 +0000623 ospf6_set_spf_reason(ospf6, reason);
624
Dinesh Dutt3810e062013-08-24 07:54:09 +0000625 if (IS_OSPF6_DEBUG_SPF(PROCESS) || IS_OSPF6_DEBUG_SPF (TIME))
Dinesh Dutta0edf672013-08-26 03:40:23 +0000626 {
627 char rbuf[32];
628 ospf6_spf_reason_string(reason, rbuf, sizeof(rbuf));
629 zlog_debug ("SPF: calculation timer scheduled (reason %s)", rbuf);
630 }
Dinesh Dutt3810e062013-08-24 07:54:09 +0000631
632 /* OSPF instance does not exist. */
633 if (ospf6 == NULL)
paul718e3742002-12-13 20:15:29 +0000634 return;
Dinesh Dutt3810e062013-08-24 07:54:09 +0000635
636 /* SPF calculation timer is already scheduled. */
637 if (ospf6->t_spf_calc)
638 {
639 if (IS_OSPF6_DEBUG_SPF(PROCESS) || IS_OSPF6_DEBUG_SPF (TIME))
640 zlog_debug ("SPF: calculation timer is already scheduled: %p",
641 ospf6->t_spf_calc);
642 return;
643 }
644
645 /* XXX Monotic timers: we only care about relative time here. */
646 now = recent_relative_time ();
647 timersub (&now, &ospf6->ts_spf, &result);
648
649 elapsed = (result.tv_sec * 1000) + (result.tv_usec / 1000);
650 ht = ospf6->spf_holdtime * ospf6->spf_hold_multiplier;
651
652 if (ht > ospf6->spf_max_holdtime)
653 ht = ospf6->spf_max_holdtime;
654
655 /* Get SPF calculation delay time. */
656 if (elapsed < ht)
657 {
658 /* Got an event within the hold time of last SPF. We need to
659 * increase the hold_multiplier, if it's not already at/past
660 * maximum value, and wasn't already increased..
661 */
662 if (ht < ospf6->spf_max_holdtime)
663 ospf6->spf_hold_multiplier++;
664
665 /* always honour the SPF initial delay */
666 if ( (ht - elapsed) < ospf6->spf_delay)
667 delay = ospf6->spf_delay;
668 else
669 delay = ht - elapsed;
670 }
671 else
672 {
673 /* Event is past required hold-time of last SPF */
674 delay = ospf6->spf_delay;
675 ospf6->spf_hold_multiplier = 1;
676 }
677
678 if (IS_OSPF6_DEBUG_SPF(PROCESS) || IS_OSPF6_DEBUG_SPF (TIME))
679 zlog_debug ("SPF: calculation timer delay = %ld", delay);
680
681 zlog_info ("SPF: Scheduled in %ld msec", delay);
682
683 ospf6->t_spf_calc =
684 thread_add_timer_msec (master, ospf6_spf_calculation_thread, ospf6, delay);
paul718e3742002-12-13 20:15:29 +0000685}
686
687void
paul0c083ee2004-10-10 12:54:58 +0000688ospf6_spf_display_subtree (struct vty *vty, const char *prefix, int rest,
hasso508e53e2004-05-18 18:57:06 +0000689 struct ospf6_vertex *v)
paul718e3742002-12-13 20:15:29 +0000690{
paul1eb8ef22005-04-07 07:30:20 +0000691 struct listnode *node, *nnode;
hasso508e53e2004-05-18 18:57:06 +0000692 struct ospf6_vertex *c;
693 char *next_prefix;
694 int len;
paul718e3742002-12-13 20:15:29 +0000695 int restnum;
paul718e3742002-12-13 20:15:29 +0000696
hasso508e53e2004-05-18 18:57:06 +0000697 /* "prefix" is the space prefix of the display line */
hasso049207c2004-08-04 20:02:13 +0000698 vty_out (vty, "%s+-%s [%d]%s", prefix, v->name, v->cost, VNL);
paul718e3742002-12-13 20:15:29 +0000699
hasso508e53e2004-05-18 18:57:06 +0000700 len = strlen (prefix) + 4;
701 next_prefix = (char *) malloc (len);
702 if (next_prefix == NULL)
paul718e3742002-12-13 20:15:29 +0000703 {
hasso049207c2004-08-04 20:02:13 +0000704 vty_out (vty, "malloc failed%s", VNL);
paul718e3742002-12-13 20:15:29 +0000705 return;
706 }
hasso508e53e2004-05-18 18:57:06 +0000707 snprintf (next_prefix, len, "%s%s", prefix, (rest ? "| " : " "));
paul718e3742002-12-13 20:15:29 +0000708
hasso508e53e2004-05-18 18:57:06 +0000709 restnum = listcount (v->child_list);
paul1eb8ef22005-04-07 07:30:20 +0000710 for (ALL_LIST_ELEMENTS (v->child_list, node, nnode, c))
paul718e3742002-12-13 20:15:29 +0000711 {
hasso508e53e2004-05-18 18:57:06 +0000712 restnum--;
713 ospf6_spf_display_subtree (vty, next_prefix, restnum, c);
paul718e3742002-12-13 20:15:29 +0000714 }
715
hasso508e53e2004-05-18 18:57:06 +0000716 free (next_prefix);
paul718e3742002-12-13 20:15:29 +0000717}
718
hasso3b687352004-08-19 06:56:53 +0000719DEFUN (debug_ospf6_spf_process,
720 debug_ospf6_spf_process_cmd,
721 "debug ospf6 spf process",
hasso508e53e2004-05-18 18:57:06 +0000722 DEBUG_STR
paul718e3742002-12-13 20:15:29 +0000723 OSPF6_STR
hasso508e53e2004-05-18 18:57:06 +0000724 "Debug SPF Calculation\n"
hasso3b687352004-08-19 06:56:53 +0000725 "Debug Detailed SPF Process\n"
hasso508e53e2004-05-18 18:57:06 +0000726 )
paul718e3742002-12-13 20:15:29 +0000727{
hasso508e53e2004-05-18 18:57:06 +0000728 unsigned char level = 0;
hasso3b687352004-08-19 06:56:53 +0000729 level = OSPF6_DEBUG_SPF_PROCESS;
hasso508e53e2004-05-18 18:57:06 +0000730 OSPF6_DEBUG_SPF_ON (level);
paul718e3742002-12-13 20:15:29 +0000731 return CMD_SUCCESS;
732}
733
hasso3b687352004-08-19 06:56:53 +0000734DEFUN (debug_ospf6_spf_time,
735 debug_ospf6_spf_time_cmd,
736 "debug ospf6 spf time",
hasso508e53e2004-05-18 18:57:06 +0000737 DEBUG_STR
paul718e3742002-12-13 20:15:29 +0000738 OSPF6_STR
hasso508e53e2004-05-18 18:57:06 +0000739 "Debug SPF Calculation\n"
hasso3b687352004-08-19 06:56:53 +0000740 "Measure time taken by SPF Calculation\n"
hasso508e53e2004-05-18 18:57:06 +0000741 )
paul718e3742002-12-13 20:15:29 +0000742{
hasso508e53e2004-05-18 18:57:06 +0000743 unsigned char level = 0;
hasso3b687352004-08-19 06:56:53 +0000744 level = OSPF6_DEBUG_SPF_TIME;
hasso508e53e2004-05-18 18:57:06 +0000745 OSPF6_DEBUG_SPF_ON (level);
746 return CMD_SUCCESS;
paul718e3742002-12-13 20:15:29 +0000747}
748
hasso2680aa22004-11-25 20:54:46 +0000749DEFUN (debug_ospf6_spf_database,
750 debug_ospf6_spf_database_cmd,
751 "debug ospf6 spf database",
752 DEBUG_STR
753 OSPF6_STR
754 "Debug SPF Calculation\n"
755 "Log number of LSAs at SPF Calculation time\n"
756 )
757{
758 unsigned char level = 0;
759 level = OSPF6_DEBUG_SPF_DATABASE;
760 OSPF6_DEBUG_SPF_ON (level);
761 return CMD_SUCCESS;
762}
763
hasso3b687352004-08-19 06:56:53 +0000764DEFUN (no_debug_ospf6_spf_process,
765 no_debug_ospf6_spf_process_cmd,
766 "no debug ospf6 spf process",
hasso508e53e2004-05-18 18:57:06 +0000767 NO_STR
768 DEBUG_STR
paul718e3742002-12-13 20:15:29 +0000769 OSPF6_STR
hasso508e53e2004-05-18 18:57:06 +0000770 "Quit Debugging SPF Calculation\n"
hasso3b687352004-08-19 06:56:53 +0000771 "Quit Debugging Detailed SPF Process\n"
hasso508e53e2004-05-18 18:57:06 +0000772 )
773{
774 unsigned char level = 0;
hasso3b687352004-08-19 06:56:53 +0000775 level = OSPF6_DEBUG_SPF_PROCESS;
hasso508e53e2004-05-18 18:57:06 +0000776 OSPF6_DEBUG_SPF_OFF (level);
777 return CMD_SUCCESS;
778}
paul718e3742002-12-13 20:15:29 +0000779
hasso3b687352004-08-19 06:56:53 +0000780DEFUN (no_debug_ospf6_spf_time,
781 no_debug_ospf6_spf_time_cmd,
782 "no debug ospf6 spf time",
hasso508e53e2004-05-18 18:57:06 +0000783 NO_STR
784 DEBUG_STR
paul718e3742002-12-13 20:15:29 +0000785 OSPF6_STR
hasso508e53e2004-05-18 18:57:06 +0000786 "Quit Debugging SPF Calculation\n"
hasso3b687352004-08-19 06:56:53 +0000787 "Quit Measuring time taken by SPF Calculation\n"
hasso508e53e2004-05-18 18:57:06 +0000788 )
789{
790 unsigned char level = 0;
hasso3b687352004-08-19 06:56:53 +0000791 level = OSPF6_DEBUG_SPF_TIME;
hasso508e53e2004-05-18 18:57:06 +0000792 OSPF6_DEBUG_SPF_OFF (level);
793 return CMD_SUCCESS;
794}
795
hasso2680aa22004-11-25 20:54:46 +0000796DEFUN (no_debug_ospf6_spf_database,
797 no_debug_ospf6_spf_database_cmd,
798 "no debug ospf6 spf database",
799 NO_STR
800 DEBUG_STR
801 OSPF6_STR
802 "Debug SPF Calculation\n"
803 "Quit Logging number of LSAs at SPF Calculation time\n"
804 )
805{
806 unsigned char level = 0;
807 level = OSPF6_DEBUG_SPF_DATABASE;
808 OSPF6_DEBUG_SPF_OFF (level);
809 return CMD_SUCCESS;
810}
811
Dinesh Dutt3810e062013-08-24 07:54:09 +0000812static int
813ospf6_timers_spf_set (struct vty *vty, unsigned int delay,
814 unsigned int hold,
815 unsigned int max)
816{
817 struct ospf6 *ospf = vty->index;
818
819 ospf->spf_delay = delay;
820 ospf->spf_holdtime = hold;
821 ospf->spf_max_holdtime = max;
822
823 return CMD_SUCCESS;
824}
825
826DEFUN (ospf6_timers_throttle_spf,
827 ospf6_timers_throttle_spf_cmd,
828 "timers throttle spf <0-600000> <0-600000> <0-600000>",
829 "Adjust routing timers\n"
830 "Throttling adaptive timer\n"
831 "OSPF6 SPF timers\n"
832 "Delay (msec) from first change received till SPF calculation\n"
833 "Initial hold time (msec) between consecutive SPF calculations\n"
834 "Maximum hold time (msec)\n")
835{
836 unsigned int delay, hold, max;
837
838 if (argc != 3)
839 {
840 vty_out (vty, "Insufficient arguments%s", VTY_NEWLINE);
841 return CMD_WARNING;
842 }
843
844 VTY_GET_INTEGER_RANGE ("SPF delay timer", delay, argv[0], 0, 600000);
845 VTY_GET_INTEGER_RANGE ("SPF hold timer", hold, argv[1], 0, 600000);
846 VTY_GET_INTEGER_RANGE ("SPF max-hold timer", max, argv[2], 0, 600000);
847
848 return ospf6_timers_spf_set (vty, delay, hold, max);
849}
850
851DEFUN (no_ospf6_timers_throttle_spf,
852 no_ospf6_timers_throttle_spf_cmd,
853 "no timers throttle spf",
854 NO_STR
855 "Adjust routing timers\n"
856 "Throttling adaptive timer\n"
857 "OSPF6 SPF timers\n")
858{
859 return ospf6_timers_spf_set (vty,
860 OSPF_SPF_DELAY_DEFAULT,
861 OSPF_SPF_HOLDTIME_DEFAULT,
862 OSPF_SPF_MAX_HOLDTIME_DEFAULT);
863}
864
hasso508e53e2004-05-18 18:57:06 +0000865int
866config_write_ospf6_debug_spf (struct vty *vty)
867{
hasso3b687352004-08-19 06:56:53 +0000868 if (IS_OSPF6_DEBUG_SPF (PROCESS))
869 vty_out (vty, "debug ospf6 spf process%s", VNL);
870 if (IS_OSPF6_DEBUG_SPF (TIME))
871 vty_out (vty, "debug ospf6 spf time%s", VNL);
hasso2680aa22004-11-25 20:54:46 +0000872 if (IS_OSPF6_DEBUG_SPF (DATABASE))
873 vty_out (vty, "debug ospf6 spf database%s", VNL);
hasso508e53e2004-05-18 18:57:06 +0000874 return 0;
875}
876
877void
Dinesh Dutt3810e062013-08-24 07:54:09 +0000878ospf6_spf_config_write (struct vty *vty)
879{
880
881 if (ospf6->spf_delay != OSPF_SPF_DELAY_DEFAULT ||
882 ospf6->spf_holdtime != OSPF_SPF_HOLDTIME_DEFAULT ||
883 ospf6->spf_max_holdtime != OSPF_SPF_MAX_HOLDTIME_DEFAULT)
884 vty_out (vty, " timers throttle spf %d %d %d%s",
885 ospf6->spf_delay, ospf6->spf_holdtime,
886 ospf6->spf_max_holdtime, VTY_NEWLINE);
887
888}
889
890void
Paul Jakma6ac29a52008-08-15 13:45:30 +0100891install_element_ospf6_debug_spf (void)
hasso508e53e2004-05-18 18:57:06 +0000892{
hasso3b687352004-08-19 06:56:53 +0000893 install_element (ENABLE_NODE, &debug_ospf6_spf_process_cmd);
894 install_element (ENABLE_NODE, &debug_ospf6_spf_time_cmd);
hasso2680aa22004-11-25 20:54:46 +0000895 install_element (ENABLE_NODE, &debug_ospf6_spf_database_cmd);
hasso3b687352004-08-19 06:56:53 +0000896 install_element (ENABLE_NODE, &no_debug_ospf6_spf_process_cmd);
897 install_element (ENABLE_NODE, &no_debug_ospf6_spf_time_cmd);
hasso2680aa22004-11-25 20:54:46 +0000898 install_element (ENABLE_NODE, &no_debug_ospf6_spf_database_cmd);
hasso3b687352004-08-19 06:56:53 +0000899 install_element (CONFIG_NODE, &debug_ospf6_spf_process_cmd);
900 install_element (CONFIG_NODE, &debug_ospf6_spf_time_cmd);
hasso2680aa22004-11-25 20:54:46 +0000901 install_element (CONFIG_NODE, &debug_ospf6_spf_database_cmd);
hasso3b687352004-08-19 06:56:53 +0000902 install_element (CONFIG_NODE, &no_debug_ospf6_spf_process_cmd);
903 install_element (CONFIG_NODE, &no_debug_ospf6_spf_time_cmd);
hasso2680aa22004-11-25 20:54:46 +0000904 install_element (CONFIG_NODE, &no_debug_ospf6_spf_database_cmd);
hasso508e53e2004-05-18 18:57:06 +0000905}
paul718e3742002-12-13 20:15:29 +0000906
907void
Paul Jakma6ac29a52008-08-15 13:45:30 +0100908ospf6_spf_init (void)
paul718e3742002-12-13 20:15:29 +0000909{
Dinesh Dutt3810e062013-08-24 07:54:09 +0000910 install_element (OSPF6_NODE, &ospf6_timers_throttle_spf_cmd);
911 install_element (OSPF6_NODE, &no_ospf6_timers_throttle_spf_cmd);
paul718e3742002-12-13 20:15:29 +0000912}