blob: 3ef5485fdedc28fa4b91da60b89f2e7d1448d7a3 [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"
paul718e3742002-12-13 20:15:29 +000043
hasso508e53e2004-05-18 18:57:06 +000044unsigned char conf_debug_ospf6_spf = 0;
paul718e3742002-12-13 20:15:29 +000045
Paul Jakma6ac29a52008-08-15 13:45:30 +010046static int
hasso508e53e2004-05-18 18:57:06 +000047ospf6_vertex_cmp (void *a, void *b)
paul718e3742002-12-13 20:15:29 +000048{
hasso508e53e2004-05-18 18:57:06 +000049 struct ospf6_vertex *va = (struct ospf6_vertex *) a;
50 struct ospf6_vertex *vb = (struct ospf6_vertex *) b;
51
52 /* ascending order */
Dmitrij Tejblum403138e2011-01-13 18:25:40 +030053 if (va->cost != vb->cost)
54 return (va->cost - vb->cost);
55 return (va->hops - vb->hops);
paul718e3742002-12-13 20:15:29 +000056}
57
Paul Jakma6ac29a52008-08-15 13:45:30 +010058static int
hasso508e53e2004-05-18 18:57:06 +000059ospf6_vertex_id_cmp (void *a, void *b)
paul718e3742002-12-13 20:15:29 +000060{
hasso508e53e2004-05-18 18:57:06 +000061 struct ospf6_vertex *va = (struct ospf6_vertex *) a;
62 struct ospf6_vertex *vb = (struct ospf6_vertex *) b;
63 int ret = 0;
paul718e3742002-12-13 20:15:29 +000064
hasso508e53e2004-05-18 18:57:06 +000065 ret = ntohl (ospf6_linkstate_prefix_adv_router (&va->vertex_id)) -
66 ntohl (ospf6_linkstate_prefix_adv_router (&vb->vertex_id));
67 if (ret)
68 return ret;
paul718e3742002-12-13 20:15:29 +000069
hasso508e53e2004-05-18 18:57:06 +000070 ret = ntohl (ospf6_linkstate_prefix_id (&va->vertex_id)) -
71 ntohl (ospf6_linkstate_prefix_id (&vb->vertex_id));
paul718e3742002-12-13 20:15:29 +000072 return ret;
73}
74
Paul Jakma6ac29a52008-08-15 13:45:30 +010075static struct ospf6_vertex *
hasso508e53e2004-05-18 18:57:06 +000076ospf6_vertex_create (struct ospf6_lsa *lsa)
77{
78 struct ospf6_vertex *v;
79 int i;
80
81 v = (struct ospf6_vertex *)
82 XMALLOC (MTYPE_OSPF6_VERTEX, sizeof (struct ospf6_vertex));
83
84 /* type */
85 if (ntohs (lsa->header->type) == OSPF6_LSTYPE_ROUTER)
86 v->type = OSPF6_VERTEX_TYPE_ROUTER;
87 else if (ntohs (lsa->header->type) == OSPF6_LSTYPE_NETWORK)
88 v->type = OSPF6_VERTEX_TYPE_NETWORK;
89 else
90 assert (0);
91
92 /* vertex_id */
93 ospf6_linkstate_prefix (lsa->header->adv_router, lsa->header->id,
94 &v->vertex_id);
95
96 /* name */
97 ospf6_linkstate_prefix2str (&v->vertex_id, v->name, sizeof (v->name));
98
99 /* Associated LSA */
100 v->lsa = lsa;
101
102 /* capability bits + options */
103 v->capability = *(u_char *)(OSPF6_LSA_HEADER_END (lsa->header));
104 v->options[0] = *(u_char *)(OSPF6_LSA_HEADER_END (lsa->header) + 1);
105 v->options[1] = *(u_char *)(OSPF6_LSA_HEADER_END (lsa->header) + 2);
106 v->options[2] = *(u_char *)(OSPF6_LSA_HEADER_END (lsa->header) + 3);
107
108 for (i = 0; i < OSPF6_MULTI_PATH_LIMIT; i++)
109 ospf6_nexthop_clear (&v->nexthop[i]);
110
111 v->parent = NULL;
112 v->child_list = list_new ();
113 v->child_list->cmp = ospf6_vertex_id_cmp;
114
115 return v;
116}
117
Paul Jakma6ac29a52008-08-15 13:45:30 +0100118static void
hasso508e53e2004-05-18 18:57:06 +0000119ospf6_vertex_delete (struct ospf6_vertex *v)
paul718e3742002-12-13 20:15:29 +0000120{
hasso508e53e2004-05-18 18:57:06 +0000121 list_delete (v->child_list);
paul718e3742002-12-13 20:15:29 +0000122 XFREE (MTYPE_OSPF6_VERTEX, v);
123}
124
Paul Jakma6ac29a52008-08-15 13:45:30 +0100125static struct ospf6_lsa *
hasso508e53e2004-05-18 18:57:06 +0000126ospf6_lsdesc_lsa (caddr_t lsdesc, struct ospf6_vertex *v)
paul718e3742002-12-13 20:15:29 +0000127{
paul718e3742002-12-13 20:15:29 +0000128 struct ospf6_lsa *lsa;
hasso508e53e2004-05-18 18:57:06 +0000129 u_int16_t type = 0;
130 u_int32_t id = 0, adv_router = 0;
paul718e3742002-12-13 20:15:29 +0000131
hasso508e53e2004-05-18 18:57:06 +0000132 if (VERTEX_IS_TYPE (NETWORK, v))
paul718e3742002-12-13 20:15:29 +0000133 {
hasso508e53e2004-05-18 18:57:06 +0000134 type = htons (OSPF6_LSTYPE_ROUTER);
135 id = htonl (0);
136 adv_router = NETWORK_LSDESC_GET_NBR_ROUTERID (lsdesc);
paul718e3742002-12-13 20:15:29 +0000137 }
paul718e3742002-12-13 20:15:29 +0000138 else
paul718e3742002-12-13 20:15:29 +0000139 {
hasso508e53e2004-05-18 18:57:06 +0000140 if (ROUTER_LSDESC_IS_TYPE (POINTTOPOINT, lsdesc))
paul718e3742002-12-13 20:15:29 +0000141 {
hasso508e53e2004-05-18 18:57:06 +0000142 type = htons (OSPF6_LSTYPE_ROUTER);
143 id = htonl (0);
144 adv_router = ROUTER_LSDESC_GET_NBR_ROUTERID (lsdesc);
145 }
146 else if (ROUTER_LSDESC_IS_TYPE (TRANSIT_NETWORK, lsdesc))
147 {
148 type = htons (OSPF6_LSTYPE_NETWORK);
149 id = htonl (ROUTER_LSDESC_GET_NBR_IFID (lsdesc));
150 adv_router = ROUTER_LSDESC_GET_NBR_ROUTERID (lsdesc);
paul718e3742002-12-13 20:15:29 +0000151 }
152 }
153
hasso508e53e2004-05-18 18:57:06 +0000154 lsa = ospf6_lsdb_lookup (type, id, adv_router, v->area->lsdb);
paul718e3742002-12-13 20:15:29 +0000155
hasso3b687352004-08-19 06:56:53 +0000156 if (IS_OSPF6_DEBUG_SPF (PROCESS))
paul718e3742002-12-13 20:15:29 +0000157 {
hasso508e53e2004-05-18 18:57:06 +0000158 char ibuf[16], abuf[16];
159 inet_ntop (AF_INET, &id, ibuf, sizeof (ibuf));
160 inet_ntop (AF_INET, &adv_router, abuf, sizeof (abuf));
161 if (lsa)
hassoc6487d62004-12-24 06:00:11 +0000162 zlog_debug (" Link to: %s", lsa->name);
hasso508e53e2004-05-18 18:57:06 +0000163 else
hassoc6487d62004-12-24 06:00:11 +0000164 zlog_debug (" Link to: [%s Id:%s Adv:%s] No LSA",
165 ospf6_lstype_name (type), ibuf, abuf);
paul718e3742002-12-13 20:15:29 +0000166 }
167
hasso508e53e2004-05-18 18:57:06 +0000168 return lsa;
169}
170
Paul Jakma6ac29a52008-08-15 13:45:30 +0100171static char *
hasso508e53e2004-05-18 18:57:06 +0000172ospf6_lsdesc_backlink (struct ospf6_lsa *lsa,
173 caddr_t lsdesc, struct ospf6_vertex *v)
174{
175 caddr_t backlink, found = NULL;
176 int size;
177
178 size = (OSPF6_LSA_IS_TYPE (ROUTER, lsa) ?
179 sizeof (struct ospf6_router_lsdesc) :
180 sizeof (struct ospf6_network_lsdesc));
181 for (backlink = OSPF6_LSA_HEADER_END (lsa->header) + 4;
182 backlink + size <= OSPF6_LSA_END (lsa->header); backlink += size)
183 {
184 assert (! (OSPF6_LSA_IS_TYPE (NETWORK, lsa) &&
185 VERTEX_IS_TYPE (NETWORK, v)));
186
187 if (OSPF6_LSA_IS_TYPE (NETWORK, lsa) &&
188 NETWORK_LSDESC_GET_NBR_ROUTERID (backlink)
189 == v->lsa->header->adv_router)
190 found = backlink;
191 else if (VERTEX_IS_TYPE (NETWORK, v) &&
192 ROUTER_LSDESC_IS_TYPE (TRANSIT_NETWORK, backlink) &&
193 ROUTER_LSDESC_GET_NBR_ROUTERID (backlink)
194 == v->lsa->header->adv_router &&
195 ROUTER_LSDESC_GET_NBR_IFID (backlink)
196 == ntohl (v->lsa->header->id))
197 found = backlink;
198 else
199 {
200 if (! ROUTER_LSDESC_IS_TYPE (POINTTOPOINT, backlink) ||
201 ! ROUTER_LSDESC_IS_TYPE (POINTTOPOINT, lsdesc))
202 continue;
203 if (ROUTER_LSDESC_GET_NBR_IFID (backlink) !=
204 ROUTER_LSDESC_GET_IFID (lsdesc) ||
205 ROUTER_LSDESC_GET_NBR_IFID (lsdesc) !=
206 ROUTER_LSDESC_GET_IFID (backlink))
207 continue;
208 if (ROUTER_LSDESC_GET_NBR_ROUTERID (backlink) !=
209 v->lsa->header->adv_router ||
210 ROUTER_LSDESC_GET_NBR_ROUTERID (lsdesc) !=
211 lsa->header->adv_router)
212 continue;
213 found = backlink;
214 }
215 }
216
hasso3b687352004-08-19 06:56:53 +0000217 if (IS_OSPF6_DEBUG_SPF (PROCESS))
hassoc6487d62004-12-24 06:00:11 +0000218 zlog_debug (" Backlink %s", (found ? "OK" : "FAIL"));
hasso508e53e2004-05-18 18:57:06 +0000219
220 return found;
221}
222
Paul Jakma6ac29a52008-08-15 13:45:30 +0100223static void
hasso508e53e2004-05-18 18:57:06 +0000224ospf6_nexthop_calc (struct ospf6_vertex *w, struct ospf6_vertex *v,
225 caddr_t lsdesc)
226{
227 int i, ifindex;
228 struct ospf6_interface *oi;
229 u_int16_t type;
230 u_int32_t adv_router;
231 struct ospf6_lsa *lsa;
232 struct ospf6_link_lsa *link_lsa;
233 char buf[64];
234
235 assert (VERTEX_IS_TYPE (ROUTER, w));
236 ifindex = (VERTEX_IS_TYPE (NETWORK, v) ? v->nexthop[0].ifindex :
237 ROUTER_LSDESC_GET_IFID (lsdesc));
238 oi = ospf6_interface_lookup_by_ifindex (ifindex);
239 if (oi == NULL)
240 {
hasso3b687352004-08-19 06:56:53 +0000241 if (IS_OSPF6_DEBUG_SPF (PROCESS))
hassoc6487d62004-12-24 06:00:11 +0000242 zlog_debug ("Can't find interface in SPF: ifindex %d", ifindex);
hasso508e53e2004-05-18 18:57:06 +0000243 return;
244 }
245
246 type = htons (OSPF6_LSTYPE_LINK);
247 adv_router = (VERTEX_IS_TYPE (NETWORK, v) ?
248 NETWORK_LSDESC_GET_NBR_ROUTERID (lsdesc) :
249 ROUTER_LSDESC_GET_NBR_ROUTERID (lsdesc));
250
251 i = 0;
252 for (lsa = ospf6_lsdb_type_router_head (type, adv_router, oi->lsdb); lsa;
253 lsa = ospf6_lsdb_type_router_next (type, adv_router, lsa))
254 {
255 if (VERTEX_IS_TYPE (ROUTER, v) &&
256 htonl (ROUTER_LSDESC_GET_NBR_IFID (lsdesc)) != lsa->header->id)
257 continue;
258
259 link_lsa = (struct ospf6_link_lsa *) OSPF6_LSA_HEADER_END (lsa->header);
hasso3b687352004-08-19 06:56:53 +0000260 if (IS_OSPF6_DEBUG_SPF (PROCESS))
hasso508e53e2004-05-18 18:57:06 +0000261 {
262 inet_ntop (AF_INET6, &link_lsa->linklocal_addr, buf, sizeof (buf));
hassoc6487d62004-12-24 06:00:11 +0000263 zlog_debug (" nexthop %s from %s", buf, lsa->name);
hasso508e53e2004-05-18 18:57:06 +0000264 }
265
266 if (i < OSPF6_MULTI_PATH_LIMIT)
267 {
268 memcpy (&w->nexthop[i].address, &link_lsa->linklocal_addr,
269 sizeof (struct in6_addr));
270 w->nexthop[i].ifindex = ifindex;
271 i++;
272 }
273 }
274
hasso3b687352004-08-19 06:56:53 +0000275 if (i == 0 && IS_OSPF6_DEBUG_SPF (PROCESS))
hassoc6487d62004-12-24 06:00:11 +0000276 zlog_debug ("No nexthop for %s found", w->name);
hasso508e53e2004-05-18 18:57:06 +0000277}
278
Paul Jakma6ac29a52008-08-15 13:45:30 +0100279static int
hasso508e53e2004-05-18 18:57:06 +0000280ospf6_spf_install (struct ospf6_vertex *v,
281 struct ospf6_route_table *result_table)
282{
283 struct ospf6_route *route;
284 int i, j;
Denis Ovsienko87362ce2011-08-27 22:19:34 +0400285 struct ospf6_vertex *prev;
hasso508e53e2004-05-18 18:57:06 +0000286
hasso3b687352004-08-19 06:56:53 +0000287 if (IS_OSPF6_DEBUG_SPF (PROCESS))
hassoc6487d62004-12-24 06:00:11 +0000288 zlog_debug ("SPF install %s hops %d cost %d",
289 v->name, v->hops, v->cost);
hasso508e53e2004-05-18 18:57:06 +0000290
291 route = ospf6_route_lookup (&v->vertex_id, result_table);
292 if (route && route->path.cost < v->cost)
293 {
hasso3b687352004-08-19 06:56:53 +0000294 if (IS_OSPF6_DEBUG_SPF (PROCESS))
hassoc6487d62004-12-24 06:00:11 +0000295 zlog_debug (" already installed with lower cost (%d), ignore",
296 route->path.cost);
hasso508e53e2004-05-18 18:57:06 +0000297 ospf6_vertex_delete (v);
298 return -1;
299 }
300 else if (route && route->path.cost == v->cost)
301 {
hasso3b687352004-08-19 06:56:53 +0000302 if (IS_OSPF6_DEBUG_SPF (PROCESS))
hassoc6487d62004-12-24 06:00:11 +0000303 zlog_debug (" another path found, merge");
hasso508e53e2004-05-18 18:57:06 +0000304
305 for (i = 0; ospf6_nexthop_is_set (&v->nexthop[i]) &&
306 i < OSPF6_MULTI_PATH_LIMIT; i++)
307 {
308 for (j = 0; j < OSPF6_MULTI_PATH_LIMIT; j++)
309 {
310 if (ospf6_nexthop_is_set (&route->nexthop[j]))
311 {
312 if (ospf6_nexthop_is_same (&route->nexthop[j],
313 &v->nexthop[i]))
314 break;
315 else
316 continue;
317 }
318 ospf6_nexthop_copy (&route->nexthop[j], &v->nexthop[i]);
319 break;
320 }
321 }
322
323 prev = (struct ospf6_vertex *) route->route_option;
Dmitrij Tejblum403138e2011-01-13 18:25:40 +0300324 assert (prev->hops <= v->hops);
325 ospf6_vertex_delete (v);
hasso508e53e2004-05-18 18:57:06 +0000326
327 return -1;
328 }
329
330 /* There should be no case where candidate being installed (variable
331 "v") is closer than the one in the SPF tree (variable "route").
hasso6452df02004-08-15 05:52:07 +0000332 In the case something has gone wrong with the behavior of
hasso508e53e2004-05-18 18:57:06 +0000333 Priority-Queue. */
hasso6452df02004-08-15 05:52:07 +0000334
335 /* the case where the route exists already is handled and returned
336 up to here. */
hasso508e53e2004-05-18 18:57:06 +0000337 assert (route == NULL);
338
339 route = ospf6_route_create ();
340 memcpy (&route->prefix, &v->vertex_id, sizeof (struct prefix));
341 route->type = OSPF6_DEST_TYPE_LINKSTATE;
342 route->path.type = OSPF6_PATH_TYPE_INTRA;
343 route->path.origin.type = v->lsa->header->type;
344 route->path.origin.id = v->lsa->header->id;
345 route->path.origin.adv_router = v->lsa->header->adv_router;
346 route->path.metric_type = 1;
347 route->path.cost = v->cost;
348 route->path.cost_e2 = v->hops;
349 route->path.router_bits = v->capability;
350 route->path.options[0] = v->options[0];
351 route->path.options[1] = v->options[1];
352 route->path.options[2] = v->options[2];
353
354 for (i = 0; ospf6_nexthop_is_set (&v->nexthop[i]) &&
355 i < OSPF6_MULTI_PATH_LIMIT; i++)
356 ospf6_nexthop_copy (&route->nexthop[i], &v->nexthop[i]);
357
358 if (v->parent)
359 listnode_add_sort (v->parent->child_list, v);
360 route->route_option = v;
361
362 ospf6_route_add (route, result_table);
paul718e3742002-12-13 20:15:29 +0000363 return 0;
364}
365
hasso508e53e2004-05-18 18:57:06 +0000366void
367ospf6_spf_table_finish (struct ospf6_route_table *result_table)
368{
369 struct ospf6_route *route;
370 struct ospf6_vertex *v;
371 for (route = ospf6_route_head (result_table); route;
372 route = ospf6_route_next (route))
373 {
374 v = (struct ospf6_vertex *) route->route_option;
375 ospf6_vertex_delete (v);
376 ospf6_route_remove (route, result_table);
377 }
378}
379
Dinesh Dutta0edf672013-08-26 03:40:23 +0000380static const char *ospf6_spf_reason_str[] =
381 {
382 "R+",
383 "R-",
384 "N+",
385 "N-",
386 "L+",
387 "L-",
388 "R*",
389 "N*",
390 };
391
392void ospf6_spf_reason_string (unsigned int reason, char *buf, int size)
393{
394 int bit;
395 int len = 0;
396
397 if (!buf)
398 return;
399
400 for (bit = 0; bit <= (sizeof(ospf6_spf_reason_str) / sizeof(char *)); bit++)
401 {
402 if ((reason & (1 << bit)) && (len < size))
403 {
404 len += snprintf((buf + len), (size - len), "%s%s",
405 (len > 0) ? ", " : "", ospf6_spf_reason_str[bit]);
406 }
407 }
408}
409
hasso6452df02004-08-15 05:52:07 +0000410/* RFC2328 16.1. Calculating the shortest-path tree for an area */
411/* RFC2740 3.8.1. Calculating the shortest path tree for an area */
hasso508e53e2004-05-18 18:57:06 +0000412void
413ospf6_spf_calculation (u_int32_t router_id,
414 struct ospf6_route_table *result_table,
415 struct ospf6_area *oa)
416{
417 struct pqueue *candidate_list;
418 struct ospf6_vertex *root, *v, *w;
419 int i;
420 int size;
421 caddr_t lsdesc;
422 struct ospf6_lsa *lsa;
423
Tom Goffb48cebb2011-12-14 14:11:29 +0400424 ospf6_spf_table_finish (result_table);
425
hasso508e53e2004-05-18 18:57:06 +0000426 /* Install the calculating router itself as the root of the SPF tree */
427 /* construct root vertex */
428 lsa = ospf6_lsdb_lookup (htons (OSPF6_LSTYPE_ROUTER), htonl (0),
429 router_id, oa->lsdb);
430 if (lsa == NULL)
431 return;
Tom Goff1d192342010-11-10 13:02:38 -0800432
433 /* initialize */
434 candidate_list = pqueue_create ();
435 candidate_list->cmp = ospf6_vertex_cmp;
436
hasso508e53e2004-05-18 18:57:06 +0000437 root = ospf6_vertex_create (lsa);
438 root->area = oa;
439 root->cost = 0;
440 root->hops = 0;
hasso6452df02004-08-15 05:52:07 +0000441 root->nexthop[0].ifindex = 0; /* loopbak I/F is better ... */
hasso508e53e2004-05-18 18:57:06 +0000442 inet_pton (AF_INET6, "::1", &root->nexthop[0].address);
443
444 /* Actually insert root to the candidate-list as the only candidate */
445 pqueue_enqueue (root, candidate_list);
446
447 /* Iterate until candidate-list becomes empty */
448 while (candidate_list->size)
449 {
450 /* get closest candidate from priority queue */
451 v = pqueue_dequeue (candidate_list);
452
hasso6452df02004-08-15 05:52:07 +0000453 /* installing may result in merging or rejecting of the vertex */
hasso508e53e2004-05-18 18:57:06 +0000454 if (ospf6_spf_install (v, result_table) < 0)
455 continue;
456
Dinesh Duttf41b4a02013-08-24 08:00:37 +0000457 /* Skip overloaded routers */
458 if ((OSPF6_LSA_IS_TYPE (ROUTER, v->lsa) &&
459 ospf6_router_is_stub_router (v->lsa)))
460 continue;
461
hasso508e53e2004-05-18 18:57:06 +0000462 /* For each LS description in the just-added vertex V's LSA */
463 size = (VERTEX_IS_TYPE (ROUTER, v) ?
464 sizeof (struct ospf6_router_lsdesc) :
465 sizeof (struct ospf6_network_lsdesc));
466 for (lsdesc = OSPF6_LSA_HEADER_END (v->lsa->header) + 4;
467 lsdesc + size <= OSPF6_LSA_END (v->lsa->header); lsdesc += size)
468 {
469 lsa = ospf6_lsdesc_lsa (lsdesc, v);
470 if (lsa == NULL)
471 continue;
472
473 if (! ospf6_lsdesc_backlink (lsa, lsdesc, v))
474 continue;
475
476 w = ospf6_vertex_create (lsa);
477 w->area = oa;
478 w->parent = v;
479 if (VERTEX_IS_TYPE (ROUTER, v))
480 {
481 w->cost = v->cost + ROUTER_LSDESC_GET_METRIC (lsdesc);
482 w->hops = v->hops + (VERTEX_IS_TYPE (NETWORK, w) ? 0 : 1);
483 }
484 else /* NETWORK */
485 {
486 w->cost = v->cost;
487 w->hops = v->hops + 1;
488 }
489
490 /* nexthop calculation */
491 if (w->hops == 0)
492 w->nexthop[0].ifindex = ROUTER_LSDESC_GET_IFID (lsdesc);
493 else if (w->hops == 1 && v->hops == 0)
494 ospf6_nexthop_calc (w, v, lsdesc);
495 else
496 {
497 for (i = 0; ospf6_nexthop_is_set (&v->nexthop[i]) &&
498 i < OSPF6_MULTI_PATH_LIMIT; i++)
499 ospf6_nexthop_copy (&w->nexthop[i], &v->nexthop[i]);
500 }
501
502 /* add new candidate to the candidate_list */
hasso3b687352004-08-19 06:56:53 +0000503 if (IS_OSPF6_DEBUG_SPF (PROCESS))
hassoc6487d62004-12-24 06:00:11 +0000504 zlog_debug (" New candidate: %s hops %d cost %d",
505 w->name, w->hops, w->cost);
hasso508e53e2004-05-18 18:57:06 +0000506 pqueue_enqueue (w, candidate_list);
507 }
508 }
509
510 pqueue_delete (candidate_list);
Vincent Bernatea86e402012-06-04 10:29:49 +0200511
512 oa->spf_calculation++;
hasso508e53e2004-05-18 18:57:06 +0000513}
514
Paul Jakma6ac29a52008-08-15 13:45:30 +0100515static void
hasso2680aa22004-11-25 20:54:46 +0000516ospf6_spf_log_database (struct ospf6_area *oa)
517{
518 char *p, *end, buffer[256];
519 struct listnode *node;
520 struct ospf6_interface *oi;
521
522 p = buffer;
523 end = buffer + sizeof (buffer);
524
525 snprintf (p, end - p, "SPF on DB (#LSAs):");
526 p = (buffer + strlen (buffer) < end ? buffer + strlen (buffer) : end);
527 snprintf (p, end - p, " Area %s: %d", oa->name, oa->lsdb->count);
528 p = (buffer + strlen (buffer) < end ? buffer + strlen (buffer) : end);
529
paul1eb8ef22005-04-07 07:30:20 +0000530 for (ALL_LIST_ELEMENTS_RO (oa->if_list, node, oi))
hasso2680aa22004-11-25 20:54:46 +0000531 {
hasso2680aa22004-11-25 20:54:46 +0000532 snprintf (p, end - p, " I/F %s: %d",
533 oi->interface->name, oi->lsdb->count);
534 p = (buffer + strlen (buffer) < end ? buffer + strlen (buffer) : end);
535 }
536
hassoc6487d62004-12-24 06:00:11 +0000537 zlog_debug ("%s", buffer);
hasso2680aa22004-11-25 20:54:46 +0000538}
539
Paul Jakma6ac29a52008-08-15 13:45:30 +0100540static int
paul718e3742002-12-13 20:15:29 +0000541ospf6_spf_calculation_thread (struct thread *t)
542{
hasso508e53e2004-05-18 18:57:06 +0000543 struct ospf6_area *oa;
Dinesh Dutt3810e062013-08-24 07:54:09 +0000544 struct ospf6 *ospf6;
hasso508e53e2004-05-18 18:57:06 +0000545 struct timeval start, end, runtime;
Dinesh Dutt3810e062013-08-24 07:54:09 +0000546 struct listnode *node;
547 struct ospf6_route *route;
Dinesh Dutta0edf672013-08-26 03:40:23 +0000548 int areas_processed = 0;
549 char rbuf[32];
paul718e3742002-12-13 20:15:29 +0000550
Dinesh Dutt3810e062013-08-24 07:54:09 +0000551 ospf6 = (struct ospf6 *)THREAD_ARG (t);
552 ospf6->t_spf_calc = NULL;
paul718e3742002-12-13 20:15:29 +0000553
554 /* execute SPF calculation */
Takashi Sogabe86f72dc2009-06-22 13:07:02 +0900555 quagga_gettime (QUAGGA_CLK_MONOTONIC, &start);
Dinesh Dutt3810e062013-08-24 07:54:09 +0000556
557 for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, oa))
558 {
559
560 if (oa == ospf6->backbone)
561 continue;
562
563 if (IS_OSPF6_DEBUG_SPF (PROCESS))
564 zlog_debug ("SPF calculation for Area %s", oa->name);
565 if (IS_OSPF6_DEBUG_SPF (DATABASE))
566 ospf6_spf_log_database (oa);
567
568 ospf6_spf_calculation (ospf6->router_id, oa->spf_table, oa);
569 ospf6_intra_route_calculation (oa);
570 ospf6_intra_brouter_calculation (oa);
Dinesh Dutta0edf672013-08-26 03:40:23 +0000571
572 areas_processed++;
Dinesh Dutt3810e062013-08-24 07:54:09 +0000573 }
574
575 if (ospf6->backbone)
576 {
577 if (IS_OSPF6_DEBUG_SPF (PROCESS))
578 zlog_debug ("SPF calculation for Backbone area %s",
579 ospf6->backbone->name);
580 if (IS_OSPF6_DEBUG_SPF (DATABASE))
581 ospf6_spf_log_database(ospf6->backbone);
582
583 ospf6_spf_calculation(ospf6->router_id, ospf6->backbone->spf_table,
584 ospf6->backbone);
585 ospf6_intra_route_calculation(ospf6->backbone);
586 ospf6_intra_brouter_calculation(ospf6->backbone);
Dinesh Dutta0edf672013-08-26 03:40:23 +0000587 areas_processed++;
Dinesh Dutt3810e062013-08-24 07:54:09 +0000588 }
589
590 /* Redo summaries if required */
591 for (route = ospf6_route_head (ospf6->route_table); route;
592 route = ospf6_route_next (route))
593 ospf6_abr_originate_summary(route);
594
Takashi Sogabe86f72dc2009-06-22 13:07:02 +0900595 quagga_gettime (QUAGGA_CLK_MONOTONIC, &end);
hasso2680aa22004-11-25 20:54:46 +0000596 timersub (&end, &start, &runtime);
paul718e3742002-12-13 20:15:29 +0000597
Dinesh Dutt3810e062013-08-24 07:54:09 +0000598 ospf6->ts_spf_duration = runtime;
599
Dinesh Dutta0edf672013-08-26 03:40:23 +0000600 ospf6_spf_reason_string(ospf6->spf_reason, rbuf, sizeof(rbuf));
601
hasso3b687352004-08-19 06:56:53 +0000602 if (IS_OSPF6_DEBUG_SPF (PROCESS) || IS_OSPF6_DEBUG_SPF (TIME))
hassoc6487d62004-12-24 06:00:11 +0000603 zlog_debug ("SPF runtime: %ld sec %ld usec",
604 runtime.tv_sec, runtime.tv_usec);
paul718e3742002-12-13 20:15:29 +0000605
Dinesh Dutta0edf672013-08-26 03:40:23 +0000606 zlog_info("SPF processing: # Areas: %d, SPF runtime: %ld sec %ld usec, "
607 "Reason: %s\n", areas_processed, runtime.tv_sec, runtime.tv_usec,
608 rbuf);
609 ospf6->last_spf_reason = ospf6->spf_reason;
610 ospf6_reset_spf_reason(ospf6);
paul718e3742002-12-13 20:15:29 +0000611 return 0;
612}
613
Dinesh Dutt3810e062013-08-24 07:54:09 +0000614/* Add schedule for SPF calculation. To avoid frequenst SPF calc, we
615 set timer for SPF calc. */
paul718e3742002-12-13 20:15:29 +0000616void
Dinesh Dutta0edf672013-08-26 03:40:23 +0000617ospf6_spf_schedule (struct ospf6 *ospf6, unsigned int reason)
paul718e3742002-12-13 20:15:29 +0000618{
Dinesh Dutt3810e062013-08-24 07:54:09 +0000619 unsigned long delay, elapsed, ht;
620 struct timeval now, result;
621
Dinesh Dutta0edf672013-08-26 03:40:23 +0000622 ospf6_set_spf_reason(ospf6, reason);
623
Dinesh Dutt3810e062013-08-24 07:54:09 +0000624 if (IS_OSPF6_DEBUG_SPF(PROCESS) || IS_OSPF6_DEBUG_SPF (TIME))
Dinesh Dutta0edf672013-08-26 03:40:23 +0000625 {
626 char rbuf[32];
627 ospf6_spf_reason_string(reason, rbuf, sizeof(rbuf));
628 zlog_debug ("SPF: calculation timer scheduled (reason %s)", rbuf);
629 }
Dinesh Dutt3810e062013-08-24 07:54:09 +0000630
631 /* OSPF instance does not exist. */
632 if (ospf6 == NULL)
paul718e3742002-12-13 20:15:29 +0000633 return;
Dinesh Dutt3810e062013-08-24 07:54:09 +0000634
635 /* SPF calculation timer is already scheduled. */
636 if (ospf6->t_spf_calc)
637 {
638 if (IS_OSPF6_DEBUG_SPF(PROCESS) || IS_OSPF6_DEBUG_SPF (TIME))
639 zlog_debug ("SPF: calculation timer is already scheduled: %p",
640 ospf6->t_spf_calc);
641 return;
642 }
643
644 /* XXX Monotic timers: we only care about relative time here. */
645 now = recent_relative_time ();
646 timersub (&now, &ospf6->ts_spf, &result);
647
648 elapsed = (result.tv_sec * 1000) + (result.tv_usec / 1000);
649 ht = ospf6->spf_holdtime * ospf6->spf_hold_multiplier;
650
651 if (ht > ospf6->spf_max_holdtime)
652 ht = ospf6->spf_max_holdtime;
653
654 /* Get SPF calculation delay time. */
655 if (elapsed < ht)
656 {
657 /* Got an event within the hold time of last SPF. We need to
658 * increase the hold_multiplier, if it's not already at/past
659 * maximum value, and wasn't already increased..
660 */
661 if (ht < ospf6->spf_max_holdtime)
662 ospf6->spf_hold_multiplier++;
663
664 /* always honour the SPF initial delay */
665 if ( (ht - elapsed) < ospf6->spf_delay)
666 delay = ospf6->spf_delay;
667 else
668 delay = ht - elapsed;
669 }
670 else
671 {
672 /* Event is past required hold-time of last SPF */
673 delay = ospf6->spf_delay;
674 ospf6->spf_hold_multiplier = 1;
675 }
676
677 if (IS_OSPF6_DEBUG_SPF(PROCESS) || IS_OSPF6_DEBUG_SPF (TIME))
678 zlog_debug ("SPF: calculation timer delay = %ld", delay);
679
680 zlog_info ("SPF: Scheduled in %ld msec", delay);
681
682 ospf6->t_spf_calc =
683 thread_add_timer_msec (master, ospf6_spf_calculation_thread, ospf6, delay);
paul718e3742002-12-13 20:15:29 +0000684}
685
686void
paul0c083ee2004-10-10 12:54:58 +0000687ospf6_spf_display_subtree (struct vty *vty, const char *prefix, int rest,
hasso508e53e2004-05-18 18:57:06 +0000688 struct ospf6_vertex *v)
paul718e3742002-12-13 20:15:29 +0000689{
paul1eb8ef22005-04-07 07:30:20 +0000690 struct listnode *node, *nnode;
hasso508e53e2004-05-18 18:57:06 +0000691 struct ospf6_vertex *c;
692 char *next_prefix;
693 int len;
paul718e3742002-12-13 20:15:29 +0000694 int restnum;
paul718e3742002-12-13 20:15:29 +0000695
hasso508e53e2004-05-18 18:57:06 +0000696 /* "prefix" is the space prefix of the display line */
hasso049207c2004-08-04 20:02:13 +0000697 vty_out (vty, "%s+-%s [%d]%s", prefix, v->name, v->cost, VNL);
paul718e3742002-12-13 20:15:29 +0000698
hasso508e53e2004-05-18 18:57:06 +0000699 len = strlen (prefix) + 4;
700 next_prefix = (char *) malloc (len);
701 if (next_prefix == NULL)
paul718e3742002-12-13 20:15:29 +0000702 {
hasso049207c2004-08-04 20:02:13 +0000703 vty_out (vty, "malloc failed%s", VNL);
paul718e3742002-12-13 20:15:29 +0000704 return;
705 }
hasso508e53e2004-05-18 18:57:06 +0000706 snprintf (next_prefix, len, "%s%s", prefix, (rest ? "| " : " "));
paul718e3742002-12-13 20:15:29 +0000707
hasso508e53e2004-05-18 18:57:06 +0000708 restnum = listcount (v->child_list);
paul1eb8ef22005-04-07 07:30:20 +0000709 for (ALL_LIST_ELEMENTS (v->child_list, node, nnode, c))
paul718e3742002-12-13 20:15:29 +0000710 {
hasso508e53e2004-05-18 18:57:06 +0000711 restnum--;
712 ospf6_spf_display_subtree (vty, next_prefix, restnum, c);
paul718e3742002-12-13 20:15:29 +0000713 }
714
hasso508e53e2004-05-18 18:57:06 +0000715 free (next_prefix);
paul718e3742002-12-13 20:15:29 +0000716}
717
hasso3b687352004-08-19 06:56:53 +0000718DEFUN (debug_ospf6_spf_process,
719 debug_ospf6_spf_process_cmd,
720 "debug ospf6 spf process",
hasso508e53e2004-05-18 18:57:06 +0000721 DEBUG_STR
paul718e3742002-12-13 20:15:29 +0000722 OSPF6_STR
hasso508e53e2004-05-18 18:57:06 +0000723 "Debug SPF Calculation\n"
hasso3b687352004-08-19 06:56:53 +0000724 "Debug Detailed SPF Process\n"
hasso508e53e2004-05-18 18:57:06 +0000725 )
paul718e3742002-12-13 20:15:29 +0000726{
hasso508e53e2004-05-18 18:57:06 +0000727 unsigned char level = 0;
hasso3b687352004-08-19 06:56:53 +0000728 level = OSPF6_DEBUG_SPF_PROCESS;
hasso508e53e2004-05-18 18:57:06 +0000729 OSPF6_DEBUG_SPF_ON (level);
paul718e3742002-12-13 20:15:29 +0000730 return CMD_SUCCESS;
731}
732
hasso3b687352004-08-19 06:56:53 +0000733DEFUN (debug_ospf6_spf_time,
734 debug_ospf6_spf_time_cmd,
735 "debug ospf6 spf time",
hasso508e53e2004-05-18 18:57:06 +0000736 DEBUG_STR
paul718e3742002-12-13 20:15:29 +0000737 OSPF6_STR
hasso508e53e2004-05-18 18:57:06 +0000738 "Debug SPF Calculation\n"
hasso3b687352004-08-19 06:56:53 +0000739 "Measure time taken by SPF Calculation\n"
hasso508e53e2004-05-18 18:57:06 +0000740 )
paul718e3742002-12-13 20:15:29 +0000741{
hasso508e53e2004-05-18 18:57:06 +0000742 unsigned char level = 0;
hasso3b687352004-08-19 06:56:53 +0000743 level = OSPF6_DEBUG_SPF_TIME;
hasso508e53e2004-05-18 18:57:06 +0000744 OSPF6_DEBUG_SPF_ON (level);
745 return CMD_SUCCESS;
paul718e3742002-12-13 20:15:29 +0000746}
747
hasso2680aa22004-11-25 20:54:46 +0000748DEFUN (debug_ospf6_spf_database,
749 debug_ospf6_spf_database_cmd,
750 "debug ospf6 spf database",
751 DEBUG_STR
752 OSPF6_STR
753 "Debug SPF Calculation\n"
754 "Log number of LSAs at SPF Calculation time\n"
755 )
756{
757 unsigned char level = 0;
758 level = OSPF6_DEBUG_SPF_DATABASE;
759 OSPF6_DEBUG_SPF_ON (level);
760 return CMD_SUCCESS;
761}
762
hasso3b687352004-08-19 06:56:53 +0000763DEFUN (no_debug_ospf6_spf_process,
764 no_debug_ospf6_spf_process_cmd,
765 "no debug ospf6 spf process",
hasso508e53e2004-05-18 18:57:06 +0000766 NO_STR
767 DEBUG_STR
paul718e3742002-12-13 20:15:29 +0000768 OSPF6_STR
hasso508e53e2004-05-18 18:57:06 +0000769 "Quit Debugging SPF Calculation\n"
hasso3b687352004-08-19 06:56:53 +0000770 "Quit Debugging Detailed SPF Process\n"
hasso508e53e2004-05-18 18:57:06 +0000771 )
772{
773 unsigned char level = 0;
hasso3b687352004-08-19 06:56:53 +0000774 level = OSPF6_DEBUG_SPF_PROCESS;
hasso508e53e2004-05-18 18:57:06 +0000775 OSPF6_DEBUG_SPF_OFF (level);
776 return CMD_SUCCESS;
777}
paul718e3742002-12-13 20:15:29 +0000778
hasso3b687352004-08-19 06:56:53 +0000779DEFUN (no_debug_ospf6_spf_time,
780 no_debug_ospf6_spf_time_cmd,
781 "no debug ospf6 spf time",
hasso508e53e2004-05-18 18:57:06 +0000782 NO_STR
783 DEBUG_STR
paul718e3742002-12-13 20:15:29 +0000784 OSPF6_STR
hasso508e53e2004-05-18 18:57:06 +0000785 "Quit Debugging SPF Calculation\n"
hasso3b687352004-08-19 06:56:53 +0000786 "Quit Measuring time taken by SPF Calculation\n"
hasso508e53e2004-05-18 18:57:06 +0000787 )
788{
789 unsigned char level = 0;
hasso3b687352004-08-19 06:56:53 +0000790 level = OSPF6_DEBUG_SPF_TIME;
hasso508e53e2004-05-18 18:57:06 +0000791 OSPF6_DEBUG_SPF_OFF (level);
792 return CMD_SUCCESS;
793}
794
hasso2680aa22004-11-25 20:54:46 +0000795DEFUN (no_debug_ospf6_spf_database,
796 no_debug_ospf6_spf_database_cmd,
797 "no debug ospf6 spf database",
798 NO_STR
799 DEBUG_STR
800 OSPF6_STR
801 "Debug SPF Calculation\n"
802 "Quit Logging number of LSAs at SPF Calculation time\n"
803 )
804{
805 unsigned char level = 0;
806 level = OSPF6_DEBUG_SPF_DATABASE;
807 OSPF6_DEBUG_SPF_OFF (level);
808 return CMD_SUCCESS;
809}
810
Dinesh Dutt3810e062013-08-24 07:54:09 +0000811static int
812ospf6_timers_spf_set (struct vty *vty, unsigned int delay,
813 unsigned int hold,
814 unsigned int max)
815{
816 struct ospf6 *ospf = vty->index;
817
818 ospf->spf_delay = delay;
819 ospf->spf_holdtime = hold;
820 ospf->spf_max_holdtime = max;
821
822 return CMD_SUCCESS;
823}
824
825DEFUN (ospf6_timers_throttle_spf,
826 ospf6_timers_throttle_spf_cmd,
827 "timers throttle spf <0-600000> <0-600000> <0-600000>",
828 "Adjust routing timers\n"
829 "Throttling adaptive timer\n"
830 "OSPF6 SPF timers\n"
831 "Delay (msec) from first change received till SPF calculation\n"
832 "Initial hold time (msec) between consecutive SPF calculations\n"
833 "Maximum hold time (msec)\n")
834{
835 unsigned int delay, hold, max;
836
837 if (argc != 3)
838 {
839 vty_out (vty, "Insufficient arguments%s", VTY_NEWLINE);
840 return CMD_WARNING;
841 }
842
843 VTY_GET_INTEGER_RANGE ("SPF delay timer", delay, argv[0], 0, 600000);
844 VTY_GET_INTEGER_RANGE ("SPF hold timer", hold, argv[1], 0, 600000);
845 VTY_GET_INTEGER_RANGE ("SPF max-hold timer", max, argv[2], 0, 600000);
846
847 return ospf6_timers_spf_set (vty, delay, hold, max);
848}
849
850DEFUN (no_ospf6_timers_throttle_spf,
851 no_ospf6_timers_throttle_spf_cmd,
852 "no timers throttle spf",
853 NO_STR
854 "Adjust routing timers\n"
855 "Throttling adaptive timer\n"
856 "OSPF6 SPF timers\n")
857{
858 return ospf6_timers_spf_set (vty,
859 OSPF_SPF_DELAY_DEFAULT,
860 OSPF_SPF_HOLDTIME_DEFAULT,
861 OSPF_SPF_MAX_HOLDTIME_DEFAULT);
862}
863
hasso508e53e2004-05-18 18:57:06 +0000864int
865config_write_ospf6_debug_spf (struct vty *vty)
866{
hasso3b687352004-08-19 06:56:53 +0000867 if (IS_OSPF6_DEBUG_SPF (PROCESS))
868 vty_out (vty, "debug ospf6 spf process%s", VNL);
869 if (IS_OSPF6_DEBUG_SPF (TIME))
870 vty_out (vty, "debug ospf6 spf time%s", VNL);
hasso2680aa22004-11-25 20:54:46 +0000871 if (IS_OSPF6_DEBUG_SPF (DATABASE))
872 vty_out (vty, "debug ospf6 spf database%s", VNL);
hasso508e53e2004-05-18 18:57:06 +0000873 return 0;
874}
875
876void
Dinesh Dutt3810e062013-08-24 07:54:09 +0000877ospf6_spf_config_write (struct vty *vty)
878{
879
880 if (ospf6->spf_delay != OSPF_SPF_DELAY_DEFAULT ||
881 ospf6->spf_holdtime != OSPF_SPF_HOLDTIME_DEFAULT ||
882 ospf6->spf_max_holdtime != OSPF_SPF_MAX_HOLDTIME_DEFAULT)
883 vty_out (vty, " timers throttle spf %d %d %d%s",
884 ospf6->spf_delay, ospf6->spf_holdtime,
885 ospf6->spf_max_holdtime, VTY_NEWLINE);
886
887}
888
889void
Paul Jakma6ac29a52008-08-15 13:45:30 +0100890install_element_ospf6_debug_spf (void)
hasso508e53e2004-05-18 18:57:06 +0000891{
hasso3b687352004-08-19 06:56:53 +0000892 install_element (ENABLE_NODE, &debug_ospf6_spf_process_cmd);
893 install_element (ENABLE_NODE, &debug_ospf6_spf_time_cmd);
hasso2680aa22004-11-25 20:54:46 +0000894 install_element (ENABLE_NODE, &debug_ospf6_spf_database_cmd);
hasso3b687352004-08-19 06:56:53 +0000895 install_element (ENABLE_NODE, &no_debug_ospf6_spf_process_cmd);
896 install_element (ENABLE_NODE, &no_debug_ospf6_spf_time_cmd);
hasso2680aa22004-11-25 20:54:46 +0000897 install_element (ENABLE_NODE, &no_debug_ospf6_spf_database_cmd);
hasso3b687352004-08-19 06:56:53 +0000898 install_element (CONFIG_NODE, &debug_ospf6_spf_process_cmd);
899 install_element (CONFIG_NODE, &debug_ospf6_spf_time_cmd);
hasso2680aa22004-11-25 20:54:46 +0000900 install_element (CONFIG_NODE, &debug_ospf6_spf_database_cmd);
hasso3b687352004-08-19 06:56:53 +0000901 install_element (CONFIG_NODE, &no_debug_ospf6_spf_process_cmd);
902 install_element (CONFIG_NODE, &no_debug_ospf6_spf_time_cmd);
hasso2680aa22004-11-25 20:54:46 +0000903 install_element (CONFIG_NODE, &no_debug_ospf6_spf_database_cmd);
hasso508e53e2004-05-18 18:57:06 +0000904}
paul718e3742002-12-13 20:15:29 +0000905
906void
Paul Jakma6ac29a52008-08-15 13:45:30 +0100907ospf6_spf_init (void)
paul718e3742002-12-13 20:15:29 +0000908{
Dinesh Dutt3810e062013-08-24 07:54:09 +0000909 install_element (OSPF6_NODE, &ospf6_timers_throttle_spf_cmd);
910 install_element (OSPF6_NODE, &no_ospf6_timers_throttle_spf_cmd);
paul718e3742002-12-13 20:15:29 +0000911}