]>
Commit | Line | Data |
---|---|---|
718e3744 | 1 | /* |
508e53e2 | 2 | * Copyright (C) 2003 Yasuhiro Ohara |
718e3744 | 3 | * |
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 | */ | |
508e53e2 | 21 | |
718e3744 | 22 | /* Shortest Path First calculation for OSPFv3 */ |
23 | ||
508e53e2 | 24 | #include <zebra.h> |
718e3744 | 25 | |
508e53e2 | 26 | #include "log.h" |
27 | #include "memory.h" | |
28 | #include "command.h" | |
29 | #include "vty.h" | |
718e3744 | 30 | #include "prefix.h" |
508e53e2 | 31 | #include "pqueue.h" |
32 | #include "linklist.h" | |
33 | #include "thread.h" | |
718e3744 | 34 | |
718e3744 | 35 | #include "ospf6_lsa.h" |
36 | #include "ospf6_lsdb.h" | |
37 | #include "ospf6_route.h" | |
508e53e2 | 38 | #include "ospf6_area.h" |
ca1f4309 DS |
39 | #include "ospf6_proto.h" |
40 | #include "ospf6_abr.h" | |
718e3744 | 41 | #include "ospf6_spf.h" |
508e53e2 | 42 | #include "ospf6_intra.h" |
718e3744 | 43 | #include "ospf6_interface.h" |
049207c3 | 44 | #include "ospf6d.h" |
718e3744 | 45 | |
508e53e2 | 46 | unsigned char conf_debug_ospf6_spf = 0; |
718e3744 | 47 | |
c3c0ac83 DS |
48 | static void |
49 | ospf6_spf_copy_nexthops_to_route (struct ospf6_route *rt, | |
50 | struct ospf6_vertex *v) | |
51 | { | |
52 | if (rt && v) | |
53 | ospf6_copy_nexthops (rt->nh_list, v->nh_list); | |
54 | } | |
55 | ||
56 | static void | |
57 | ospf6_spf_merge_nexthops_to_route (struct ospf6_route *rt, | |
58 | struct ospf6_vertex *v) | |
59 | { | |
60 | if (rt && v) | |
61 | ospf6_merge_nexthops (rt->nh_list, v->nh_list); | |
62 | } | |
63 | ||
ed2eb093 | 64 | static unsigned int |
c3c0ac83 DS |
65 | ospf6_spf_get_ifindex_from_nh (struct ospf6_vertex *v) |
66 | { | |
67 | struct ospf6_nexthop *nh; | |
68 | struct listnode *node; | |
69 | ||
70 | if (v) | |
71 | { | |
72 | node = listhead(v->nh_list); | |
73 | if (node) | |
74 | { | |
75 | nh = listgetdata (node); | |
76 | if (nh) | |
77 | return (nh->ifindex); | |
78 | } | |
79 | } | |
ed2eb093 | 80 | return 0; |
c3c0ac83 DS |
81 | } |
82 | ||
6ac29a51 | 83 | static int |
508e53e2 | 84 | ospf6_vertex_cmp (void *a, void *b) |
718e3744 | 85 | { |
508e53e2 | 86 | struct ospf6_vertex *va = (struct ospf6_vertex *) a; |
87 | struct ospf6_vertex *vb = (struct ospf6_vertex *) b; | |
718e3744 | 88 | |
508e53e2 | 89 | /* ascending order */ |
403138e1 DT |
90 | if (va->cost != vb->cost) |
91 | return (va->cost - vb->cost); | |
92 | return (va->hops - vb->hops); | |
718e3744 | 93 | } |
94 | ||
6ac29a51 | 95 | static int |
508e53e2 | 96 | ospf6_vertex_id_cmp (void *a, void *b) |
718e3744 | 97 | { |
508e53e2 | 98 | struct ospf6_vertex *va = (struct ospf6_vertex *) a; |
99 | struct ospf6_vertex *vb = (struct ospf6_vertex *) b; | |
100 | int ret = 0; | |
718e3744 | 101 | |
508e53e2 | 102 | ret = ntohl (ospf6_linkstate_prefix_adv_router (&va->vertex_id)) - |
103 | ntohl (ospf6_linkstate_prefix_adv_router (&vb->vertex_id)); | |
104 | if (ret) | |
105 | return ret; | |
718e3744 | 106 | |
508e53e2 | 107 | ret = ntohl (ospf6_linkstate_prefix_id (&va->vertex_id)) - |
108 | ntohl (ospf6_linkstate_prefix_id (&vb->vertex_id)); | |
718e3744 | 109 | return ret; |
110 | } | |
111 | ||
6ac29a51 | 112 | static struct ospf6_vertex * |
508e53e2 | 113 | ospf6_vertex_create (struct ospf6_lsa *lsa) |
718e3744 | 114 | { |
508e53e2 | 115 | struct ospf6_vertex *v; |
718e3744 | 116 | |
508e53e2 | 117 | v = (struct ospf6_vertex *) |
118 | XMALLOC (MTYPE_OSPF6_VERTEX, sizeof (struct ospf6_vertex)); | |
718e3744 | 119 | |
508e53e2 | 120 | /* type */ |
121 | if (ntohs (lsa->header->type) == OSPF6_LSTYPE_ROUTER) | |
122 | v->type = OSPF6_VERTEX_TYPE_ROUTER; | |
123 | else if (ntohs (lsa->header->type) == OSPF6_LSTYPE_NETWORK) | |
124 | v->type = OSPF6_VERTEX_TYPE_NETWORK; | |
718e3744 | 125 | else |
508e53e2 | 126 | assert (0); |
718e3744 | 127 | |
508e53e2 | 128 | /* vertex_id */ |
129 | ospf6_linkstate_prefix (lsa->header->adv_router, lsa->header->id, | |
130 | &v->vertex_id); | |
718e3744 | 131 | |
508e53e2 | 132 | /* name */ |
133 | ospf6_linkstate_prefix2str (&v->vertex_id, v->name, sizeof (v->name)); | |
718e3744 | 134 | |
c3c0ac83 DS |
135 | if (IS_OSPF6_DEBUG_SPF (PROCESS)) |
136 | zlog_debug ("%s: Creating vertex %s of type %s", __func__, v->name, | |
137 | ((ntohs (lsa->header->type) == OSPF6_LSTYPE_ROUTER) ? "Router" : "N/W")); | |
138 | ||
508e53e2 | 139 | /* Associated LSA */ |
140 | v->lsa = lsa; | |
718e3744 | 141 | |
508e53e2 | 142 | /* capability bits + options */ |
143 | v->capability = *(u_char *)(OSPF6_LSA_HEADER_END (lsa->header)); | |
144 | v->options[0] = *(u_char *)(OSPF6_LSA_HEADER_END (lsa->header) + 1); | |
145 | v->options[1] = *(u_char *)(OSPF6_LSA_HEADER_END (lsa->header) + 2); | |
146 | v->options[2] = *(u_char *)(OSPF6_LSA_HEADER_END (lsa->header) + 3); | |
718e3744 | 147 | |
c3c0ac83 | 148 | v->nh_list = list_new(); |
718e3744 | 149 | |
508e53e2 | 150 | v->parent = NULL; |
151 | v->child_list = list_new (); | |
152 | v->child_list->cmp = ospf6_vertex_id_cmp; | |
718e3744 | 153 | |
508e53e2 | 154 | return v; |
718e3744 | 155 | } |
156 | ||
6ac29a51 | 157 | static void |
508e53e2 | 158 | ospf6_vertex_delete (struct ospf6_vertex *v) |
718e3744 | 159 | { |
508e53e2 | 160 | list_delete (v->child_list); |
161 | XFREE (MTYPE_OSPF6_VERTEX, v); | |
718e3744 | 162 | } |
163 | ||
6ac29a51 | 164 | static struct ospf6_lsa * |
508e53e2 | 165 | ospf6_lsdesc_lsa (caddr_t lsdesc, struct ospf6_vertex *v) |
718e3744 | 166 | { |
508e53e2 | 167 | struct ospf6_lsa *lsa; |
168 | u_int16_t type = 0; | |
169 | u_int32_t id = 0, adv_router = 0; | |
718e3744 | 170 | |
508e53e2 | 171 | if (VERTEX_IS_TYPE (NETWORK, v)) |
718e3744 | 172 | { |
508e53e2 | 173 | type = htons (OSPF6_LSTYPE_ROUTER); |
174 | id = htonl (0); | |
175 | adv_router = NETWORK_LSDESC_GET_NBR_ROUTERID (lsdesc); | |
718e3744 | 176 | } |
177 | else | |
178 | { | |
508e53e2 | 179 | if (ROUTER_LSDESC_IS_TYPE (POINTTOPOINT, lsdesc)) |
718e3744 | 180 | { |
508e53e2 | 181 | type = htons (OSPF6_LSTYPE_ROUTER); |
718e3744 | 182 | id = htonl (0); |
508e53e2 | 183 | adv_router = ROUTER_LSDESC_GET_NBR_ROUTERID (lsdesc); |
718e3744 | 184 | } |
508e53e2 | 185 | else if (ROUTER_LSDESC_IS_TYPE (TRANSIT_NETWORK, lsdesc)) |
718e3744 | 186 | { |
508e53e2 | 187 | type = htons (OSPF6_LSTYPE_NETWORK); |
188 | id = htonl (ROUTER_LSDESC_GET_NBR_IFID (lsdesc)); | |
189 | adv_router = ROUTER_LSDESC_GET_NBR_ROUTERID (lsdesc); | |
718e3744 | 190 | } |
718e3744 | 191 | } |
192 | ||
508e53e2 | 193 | lsa = ospf6_lsdb_lookup (type, id, adv_router, v->area->lsdb); |
718e3744 | 194 | |
3b68735f | 195 | if (IS_OSPF6_DEBUG_SPF (PROCESS)) |
718e3744 | 196 | { |
508e53e2 | 197 | char ibuf[16], abuf[16]; |
198 | inet_ntop (AF_INET, &id, ibuf, sizeof (ibuf)); | |
199 | inet_ntop (AF_INET, &adv_router, abuf, sizeof (abuf)); | |
200 | if (lsa) | |
c6487d61 | 201 | zlog_debug (" Link to: %s", lsa->name); |
508e53e2 | 202 | else |
c6487d61 | 203 | zlog_debug (" Link to: [%s Id:%s Adv:%s] No LSA", |
204 | ospf6_lstype_name (type), ibuf, abuf); | |
718e3744 | 205 | } |
206 | ||
508e53e2 | 207 | return lsa; |
718e3744 | 208 | } |
209 | ||
6ac29a51 | 210 | static char * |
508e53e2 | 211 | ospf6_lsdesc_backlink (struct ospf6_lsa *lsa, |
212 | caddr_t lsdesc, struct ospf6_vertex *v) | |
718e3744 | 213 | { |
508e53e2 | 214 | caddr_t backlink, found = NULL; |
215 | int size; | |
718e3744 | 216 | |
508e53e2 | 217 | size = (OSPF6_LSA_IS_TYPE (ROUTER, lsa) ? |
218 | sizeof (struct ospf6_router_lsdesc) : | |
219 | sizeof (struct ospf6_network_lsdesc)); | |
220 | for (backlink = OSPF6_LSA_HEADER_END (lsa->header) + 4; | |
221 | backlink + size <= OSPF6_LSA_END (lsa->header); backlink += size) | |
718e3744 | 222 | { |
508e53e2 | 223 | assert (! (OSPF6_LSA_IS_TYPE (NETWORK, lsa) && |
224 | VERTEX_IS_TYPE (NETWORK, v))); | |
718e3744 | 225 | |
508e53e2 | 226 | if (OSPF6_LSA_IS_TYPE (NETWORK, lsa) && |
227 | NETWORK_LSDESC_GET_NBR_ROUTERID (backlink) | |
228 | == v->lsa->header->adv_router) | |
229 | found = backlink; | |
230 | else if (VERTEX_IS_TYPE (NETWORK, v) && | |
231 | ROUTER_LSDESC_IS_TYPE (TRANSIT_NETWORK, backlink) && | |
232 | ROUTER_LSDESC_GET_NBR_ROUTERID (backlink) | |
233 | == v->lsa->header->adv_router && | |
234 | ROUTER_LSDESC_GET_NBR_IFID (backlink) | |
235 | == ntohl (v->lsa->header->id)) | |
236 | found = backlink; | |
237 | else | |
238 | { | |
239 | if (! ROUTER_LSDESC_IS_TYPE (POINTTOPOINT, backlink) || | |
240 | ! ROUTER_LSDESC_IS_TYPE (POINTTOPOINT, lsdesc)) | |
241 | continue; | |
242 | if (ROUTER_LSDESC_GET_NBR_IFID (backlink) != | |
243 | ROUTER_LSDESC_GET_IFID (lsdesc) || | |
244 | ROUTER_LSDESC_GET_NBR_IFID (lsdesc) != | |
245 | ROUTER_LSDESC_GET_IFID (backlink)) | |
246 | continue; | |
247 | if (ROUTER_LSDESC_GET_NBR_ROUTERID (backlink) != | |
248 | v->lsa->header->adv_router || | |
249 | ROUTER_LSDESC_GET_NBR_ROUTERID (lsdesc) != | |
250 | lsa->header->adv_router) | |
251 | continue; | |
252 | found = backlink; | |
253 | } | |
718e3744 | 254 | } |
718e3744 | 255 | |
3b68735f | 256 | if (IS_OSPF6_DEBUG_SPF (PROCESS)) |
c6487d61 | 257 | zlog_debug (" Backlink %s", (found ? "OK" : "FAIL")); |
718e3744 | 258 | |
508e53e2 | 259 | return found; |
718e3744 | 260 | } |
261 | ||
6ac29a51 | 262 | static void |
508e53e2 | 263 | ospf6_nexthop_calc (struct ospf6_vertex *w, struct ospf6_vertex *v, |
264 | caddr_t lsdesc) | |
718e3744 | 265 | { |
c3c0ac83 | 266 | int i; |
ed2eb093 | 267 | unsigned int ifindex; |
508e53e2 | 268 | struct ospf6_interface *oi; |
269 | u_int16_t type; | |
270 | u_int32_t adv_router; | |
271 | struct ospf6_lsa *lsa; | |
272 | struct ospf6_link_lsa *link_lsa; | |
273 | char buf[64]; | |
718e3744 | 274 | |
508e53e2 | 275 | assert (VERTEX_IS_TYPE (ROUTER, w)); |
c3c0ac83 | 276 | ifindex = (VERTEX_IS_TYPE (NETWORK, v) ? ospf6_spf_get_ifindex_from_nh (v) : |
508e53e2 | 277 | ROUTER_LSDESC_GET_IFID (lsdesc)); |
ed2eb093 | 278 | if (ifindex == 0) |
c3c0ac83 DS |
279 | { |
280 | zlog_err ("No nexthop ifindex at vertex %s", v->name); | |
281 | return; | |
282 | } | |
283 | ||
508e53e2 | 284 | oi = ospf6_interface_lookup_by_ifindex (ifindex); |
285 | if (oi == NULL) | |
718e3744 | 286 | { |
3b68735f | 287 | if (IS_OSPF6_DEBUG_SPF (PROCESS)) |
c6487d61 | 288 | zlog_debug ("Can't find interface in SPF: ifindex %d", ifindex); |
508e53e2 | 289 | return; |
718e3744 | 290 | } |
291 | ||
508e53e2 | 292 | type = htons (OSPF6_LSTYPE_LINK); |
293 | adv_router = (VERTEX_IS_TYPE (NETWORK, v) ? | |
294 | NETWORK_LSDESC_GET_NBR_ROUTERID (lsdesc) : | |
295 | ROUTER_LSDESC_GET_NBR_ROUTERID (lsdesc)); | |
718e3744 | 296 | |
508e53e2 | 297 | i = 0; |
298 | for (lsa = ospf6_lsdb_type_router_head (type, adv_router, oi->lsdb); lsa; | |
299 | lsa = ospf6_lsdb_type_router_next (type, adv_router, lsa)) | |
718e3744 | 300 | { |
508e53e2 | 301 | if (VERTEX_IS_TYPE (ROUTER, v) && |
302 | htonl (ROUTER_LSDESC_GET_NBR_IFID (lsdesc)) != lsa->header->id) | |
718e3744 | 303 | continue; |
304 | ||
508e53e2 | 305 | link_lsa = (struct ospf6_link_lsa *) OSPF6_LSA_HEADER_END (lsa->header); |
3b68735f | 306 | if (IS_OSPF6_DEBUG_SPF (PROCESS)) |
508e53e2 | 307 | { |
308 | inet_ntop (AF_INET6, &link_lsa->linklocal_addr, buf, sizeof (buf)); | |
c6487d61 | 309 | zlog_debug (" nexthop %s from %s", buf, lsa->name); |
508e53e2 | 310 | } |
718e3744 | 311 | |
c3c0ac83 DS |
312 | ospf6_add_nexthop (w->nh_list, ifindex, &link_lsa->linklocal_addr); |
313 | i++; | |
718e3744 | 314 | } |
315 | ||
3b68735f | 316 | if (i == 0 && IS_OSPF6_DEBUG_SPF (PROCESS)) |
c6487d61 | 317 | zlog_debug ("No nexthop for %s found", w->name); |
718e3744 | 318 | } |
319 | ||
6ac29a51 | 320 | static int |
508e53e2 | 321 | ospf6_spf_install (struct ospf6_vertex *v, |
322 | struct ospf6_route_table *result_table) | |
718e3744 | 323 | { |
c3c0ac83 | 324 | struct ospf6_route *route, *parent_route; |
87362ceb | 325 | struct ospf6_vertex *prev; |
718e3744 | 326 | |
3b68735f | 327 | if (IS_OSPF6_DEBUG_SPF (PROCESS)) |
c6487d61 | 328 | zlog_debug ("SPF install %s hops %d cost %d", |
329 | v->name, v->hops, v->cost); | |
718e3744 | 330 | |
508e53e2 | 331 | route = ospf6_route_lookup (&v->vertex_id, result_table); |
332 | if (route && route->path.cost < v->cost) | |
718e3744 | 333 | { |
3b68735f | 334 | if (IS_OSPF6_DEBUG_SPF (PROCESS)) |
c6487d61 | 335 | zlog_debug (" already installed with lower cost (%d), ignore", |
336 | route->path.cost); | |
508e53e2 | 337 | ospf6_vertex_delete (v); |
338 | return -1; | |
718e3744 | 339 | } |
508e53e2 | 340 | else if (route && route->path.cost == v->cost) |
718e3744 | 341 | { |
3b68735f | 342 | if (IS_OSPF6_DEBUG_SPF (PROCESS)) |
c6487d61 | 343 | zlog_debug (" another path found, merge"); |
718e3744 | 344 | |
c3c0ac83 | 345 | ospf6_spf_merge_nexthops_to_route (route, v); |
718e3744 | 346 | |
508e53e2 | 347 | prev = (struct ospf6_vertex *) route->route_option; |
403138e1 DT |
348 | assert (prev->hops <= v->hops); |
349 | ospf6_vertex_delete (v); | |
718e3744 | 350 | |
508e53e2 | 351 | return -1; |
718e3744 | 352 | } |
508e53e2 | 353 | |
354 | /* There should be no case where candidate being installed (variable | |
355 | "v") is closer than the one in the SPF tree (variable "route"). | |
6452df09 | 356 | In the case something has gone wrong with the behavior of |
508e53e2 | 357 | Priority-Queue. */ |
6452df09 | 358 | |
359 | /* the case where the route exists already is handled and returned | |
360 | up to here. */ | |
508e53e2 | 361 | assert (route == NULL); |
362 | ||
363 | route = ospf6_route_create (); | |
364 | memcpy (&route->prefix, &v->vertex_id, sizeof (struct prefix)); | |
365 | route->type = OSPF6_DEST_TYPE_LINKSTATE; | |
366 | route->path.type = OSPF6_PATH_TYPE_INTRA; | |
367 | route->path.origin.type = v->lsa->header->type; | |
368 | route->path.origin.id = v->lsa->header->id; | |
369 | route->path.origin.adv_router = v->lsa->header->adv_router; | |
370 | route->path.metric_type = 1; | |
371 | route->path.cost = v->cost; | |
c3c0ac83 | 372 | route->path.u.cost_e2 = v->hops; |
508e53e2 | 373 | route->path.router_bits = v->capability; |
374 | route->path.options[0] = v->options[0]; | |
375 | route->path.options[1] = v->options[1]; | |
376 | route->path.options[2] = v->options[2]; | |
377 | ||
c3c0ac83 DS |
378 | ospf6_spf_copy_nexthops_to_route (route, v); |
379 | ||
380 | /* | |
381 | * The SPF logic implementation does not transfer the multipathing properties | |
382 | * of a parent to a child node. Thus if there was a 3-way multipath to a | |
383 | * node's parent and a single hop from the parent to the child, the logic of | |
384 | * creating new vertices and computing next hops prevents there from being 3 | |
385 | * paths to the child node. This is primarily because the resolution of | |
386 | * multipath is done in this routine, not in the main spf loop. | |
387 | * | |
388 | * The following logic addresses that problem by merging the parent's nexthop | |
389 | * information with the child's, if the parent is not the root of the tree. | |
390 | * This is based on the assumption that before a node's route is installed, | |
391 | * its parent's route's nexthops have already been installed. | |
392 | */ | |
393 | if (v->parent && v->parent->hops) | |
394 | { | |
395 | parent_route = ospf6_route_lookup (&v->parent->vertex_id, result_table); | |
396 | if (parent_route) | |
397 | { | |
398 | ospf6_route_merge_nexthops (route, parent_route); | |
399 | } | |
400 | } | |
508e53e2 | 401 | |
402 | if (v->parent) | |
403 | listnode_add_sort (v->parent->child_list, v); | |
404 | route->route_option = v; | |
405 | ||
406 | ospf6_route_add (route, result_table); | |
407 | return 0; | |
718e3744 | 408 | } |
409 | ||
508e53e2 | 410 | void |
411 | ospf6_spf_table_finish (struct ospf6_route_table *result_table) | |
718e3744 | 412 | { |
0f23bb67 | 413 | struct ospf6_route *route, *nroute; |
718e3744 | 414 | struct ospf6_vertex *v; |
508e53e2 | 415 | for (route = ospf6_route_head (result_table); route; |
0f23bb67 | 416 | route = nroute) |
718e3744 | 417 | { |
0f23bb67 | 418 | nroute = ospf6_route_next (route); |
508e53e2 | 419 | v = (struct ospf6_vertex *) route->route_option; |
420 | ospf6_vertex_delete (v); | |
421 | ospf6_route_remove (route, result_table); | |
718e3744 | 422 | } |
718e3744 | 423 | } |
424 | ||
a0edf674 DD |
425 | static const char *ospf6_spf_reason_str[] = |
426 | { | |
427 | "R+", | |
428 | "R-", | |
429 | "N+", | |
430 | "N-", | |
431 | "L+", | |
432 | "L-", | |
433 | "R*", | |
434 | "N*", | |
435 | }; | |
436 | ||
437 | void ospf6_spf_reason_string (unsigned int reason, char *buf, int size) | |
438 | { | |
ed2eb093 | 439 | unsigned int bit; |
a0edf674 DD |
440 | int len = 0; |
441 | ||
442 | if (!buf) | |
443 | return; | |
444 | ||
445 | for (bit = 0; bit <= (sizeof(ospf6_spf_reason_str) / sizeof(char *)); bit++) | |
446 | { | |
447 | if ((reason & (1 << bit)) && (len < size)) | |
448 | { | |
449 | len += snprintf((buf + len), (size - len), "%s%s", | |
450 | (len > 0) ? ", " : "", ospf6_spf_reason_str[bit]); | |
451 | } | |
452 | } | |
453 | } | |
454 | ||
6452df09 | 455 | /* RFC2328 16.1. Calculating the shortest-path tree for an area */ |
456 | /* RFC2740 3.8.1. Calculating the shortest path tree for an area */ | |
508e53e2 | 457 | void |
458 | ospf6_spf_calculation (u_int32_t router_id, | |
459 | struct ospf6_route_table *result_table, | |
460 | struct ospf6_area *oa) | |
461 | { | |
462 | struct pqueue *candidate_list; | |
463 | struct ospf6_vertex *root, *v, *w; | |
508e53e2 | 464 | int size; |
465 | caddr_t lsdesc; | |
466 | struct ospf6_lsa *lsa; | |
c3c0ac83 | 467 | struct in6_addr address; |
718e3744 | 468 | |
b48cebbb TG |
469 | ospf6_spf_table_finish (result_table); |
470 | ||
508e53e2 | 471 | /* Install the calculating router itself as the root of the SPF tree */ |
472 | /* construct root vertex */ | |
473 | lsa = ospf6_lsdb_lookup (htons (OSPF6_LSTYPE_ROUTER), htonl (0), | |
c3c0ac83 | 474 | router_id, oa->lsdb_self); |
508e53e2 | 475 | if (lsa == NULL) |
c3c0ac83 DS |
476 | { |
477 | if (IS_OSPF6_DEBUG_SPF (PROCESS)) | |
478 | zlog_debug ("%s: No router LSA for area %s\n", | |
479 | __func__, oa->name); | |
480 | return; | |
481 | } | |
1d19234e TG |
482 | |
483 | /* initialize */ | |
484 | candidate_list = pqueue_create (); | |
485 | candidate_list->cmp = ospf6_vertex_cmp; | |
486 | ||
508e53e2 | 487 | root = ospf6_vertex_create (lsa); |
488 | root->area = oa; | |
489 | root->cost = 0; | |
490 | root->hops = 0; | |
c3c0ac83 | 491 | inet_pton (AF_INET6, "::1", &address); |
718e3744 | 492 | |
508e53e2 | 493 | /* Actually insert root to the candidate-list as the only candidate */ |
494 | pqueue_enqueue (root, candidate_list); | |
718e3744 | 495 | |
508e53e2 | 496 | /* Iterate until candidate-list becomes empty */ |
497 | while (candidate_list->size) | |
718e3744 | 498 | { |
508e53e2 | 499 | /* get closest candidate from priority queue */ |
500 | v = pqueue_dequeue (candidate_list); | |
718e3744 | 501 | |
6452df09 | 502 | /* installing may result in merging or rejecting of the vertex */ |
508e53e2 | 503 | if (ospf6_spf_install (v, result_table) < 0) |
504 | continue; | |
718e3744 | 505 | |
f41b4a02 DD |
506 | /* Skip overloaded routers */ |
507 | if ((OSPF6_LSA_IS_TYPE (ROUTER, v->lsa) && | |
508 | ospf6_router_is_stub_router (v->lsa))) | |
509 | continue; | |
510 | ||
508e53e2 | 511 | /* For each LS description in the just-added vertex V's LSA */ |
512 | size = (VERTEX_IS_TYPE (ROUTER, v) ? | |
513 | sizeof (struct ospf6_router_lsdesc) : | |
514 | sizeof (struct ospf6_network_lsdesc)); | |
515 | for (lsdesc = OSPF6_LSA_HEADER_END (v->lsa->header) + 4; | |
516 | lsdesc + size <= OSPF6_LSA_END (v->lsa->header); lsdesc += size) | |
718e3744 | 517 | { |
508e53e2 | 518 | lsa = ospf6_lsdesc_lsa (lsdesc, v); |
519 | if (lsa == NULL) | |
718e3744 | 520 | continue; |
521 | ||
c3c0ac83 DS |
522 | if (OSPF6_LSA_IS_MAXAGE (lsa)) |
523 | continue; | |
524 | ||
508e53e2 | 525 | if (! ospf6_lsdesc_backlink (lsa, lsdesc, v)) |
526 | continue; | |
718e3744 | 527 | |
508e53e2 | 528 | w = ospf6_vertex_create (lsa); |
529 | w->area = oa; | |
530 | w->parent = v; | |
531 | if (VERTEX_IS_TYPE (ROUTER, v)) | |
718e3744 | 532 | { |
508e53e2 | 533 | w->cost = v->cost + ROUTER_LSDESC_GET_METRIC (lsdesc); |
534 | w->hops = v->hops + (VERTEX_IS_TYPE (NETWORK, w) ? 0 : 1); | |
718e3744 | 535 | } |
508e53e2 | 536 | else /* NETWORK */ |
718e3744 | 537 | { |
508e53e2 | 538 | w->cost = v->cost; |
539 | w->hops = v->hops + 1; | |
718e3744 | 540 | } |
541 | ||
508e53e2 | 542 | /* nexthop calculation */ |
543 | if (w->hops == 0) | |
c3c0ac83 | 544 | ospf6_add_nexthop (w->nh_list, ROUTER_LSDESC_GET_IFID (lsdesc), NULL); |
508e53e2 | 545 | else if (w->hops == 1 && v->hops == 0) |
546 | ospf6_nexthop_calc (w, v, lsdesc); | |
718e3744 | 547 | else |
548 | { | |
c3c0ac83 | 549 | ospf6_copy_nexthops (w->nh_list, v->nh_list); |
718e3744 | 550 | } |
551 | ||
508e53e2 | 552 | /* add new candidate to the candidate_list */ |
3b68735f | 553 | if (IS_OSPF6_DEBUG_SPF (PROCESS)) |
c6487d61 | 554 | zlog_debug (" New candidate: %s hops %d cost %d", |
555 | w->name, w->hops, w->cost); | |
508e53e2 | 556 | pqueue_enqueue (w, candidate_list); |
718e3744 | 557 | } |
558 | } | |
559 | ||
508e53e2 | 560 | pqueue_delete (candidate_list); |
ea86e404 VB |
561 | |
562 | oa->spf_calculation++; | |
718e3744 | 563 | } |
564 | ||
6ac29a51 | 565 | static void |
2680aa2b | 566 | ospf6_spf_log_database (struct ospf6_area *oa) |
567 | { | |
568 | char *p, *end, buffer[256]; | |
569 | struct listnode *node; | |
570 | struct ospf6_interface *oi; | |
571 | ||
572 | p = buffer; | |
573 | end = buffer + sizeof (buffer); | |
574 | ||
575 | snprintf (p, end - p, "SPF on DB (#LSAs):"); | |
576 | p = (buffer + strlen (buffer) < end ? buffer + strlen (buffer) : end); | |
577 | snprintf (p, end - p, " Area %s: %d", oa->name, oa->lsdb->count); | |
578 | p = (buffer + strlen (buffer) < end ? buffer + strlen (buffer) : end); | |
579 | ||
1eb8ef25 | 580 | for (ALL_LIST_ELEMENTS_RO (oa->if_list, node, oi)) |
2680aa2b | 581 | { |
2680aa2b | 582 | snprintf (p, end - p, " I/F %s: %d", |
583 | oi->interface->name, oi->lsdb->count); | |
584 | p = (buffer + strlen (buffer) < end ? buffer + strlen (buffer) : end); | |
585 | } | |
586 | ||
c6487d61 | 587 | zlog_debug ("%s", buffer); |
2680aa2b | 588 | } |
589 | ||
6ac29a51 | 590 | static int |
718e3744 | 591 | ospf6_spf_calculation_thread (struct thread *t) |
592 | { | |
508e53e2 | 593 | struct ospf6_area *oa; |
3810e06e | 594 | struct ospf6 *ospf6; |
508e53e2 | 595 | struct timeval start, end, runtime; |
3810e06e | 596 | struct listnode *node; |
a0edf674 DD |
597 | int areas_processed = 0; |
598 | char rbuf[32]; | |
718e3744 | 599 | |
3810e06e DD |
600 | ospf6 = (struct ospf6 *)THREAD_ARG (t); |
601 | ospf6->t_spf_calc = NULL; | |
718e3744 | 602 | |
603 | /* execute SPF calculation */ | |
86f72dcb | 604 | quagga_gettime (QUAGGA_CLK_MONOTONIC, &start); |
3810e06e | 605 | |
ca1f4309 DS |
606 | if (ospf6_is_router_abr (ospf6)) |
607 | ospf6_abr_range_reset_cost (ospf6); | |
608 | ||
3810e06e DD |
609 | for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, oa)) |
610 | { | |
611 | ||
612 | if (oa == ospf6->backbone) | |
613 | continue; | |
614 | ||
615 | if (IS_OSPF6_DEBUG_SPF (PROCESS)) | |
616 | zlog_debug ("SPF calculation for Area %s", oa->name); | |
617 | if (IS_OSPF6_DEBUG_SPF (DATABASE)) | |
618 | ospf6_spf_log_database (oa); | |
619 | ||
620 | ospf6_spf_calculation (ospf6->router_id, oa->spf_table, oa); | |
621 | ospf6_intra_route_calculation (oa); | |
622 | ospf6_intra_brouter_calculation (oa); | |
a0edf674 DD |
623 | |
624 | areas_processed++; | |
3810e06e DD |
625 | } |
626 | ||
627 | if (ospf6->backbone) | |
628 | { | |
629 | if (IS_OSPF6_DEBUG_SPF (PROCESS)) | |
630 | zlog_debug ("SPF calculation for Backbone area %s", | |
631 | ospf6->backbone->name); | |
632 | if (IS_OSPF6_DEBUG_SPF (DATABASE)) | |
633 | ospf6_spf_log_database(ospf6->backbone); | |
634 | ||
635 | ospf6_spf_calculation(ospf6->router_id, ospf6->backbone->spf_table, | |
636 | ospf6->backbone); | |
637 | ospf6_intra_route_calculation(ospf6->backbone); | |
638 | ospf6_intra_brouter_calculation(ospf6->backbone); | |
a0edf674 | 639 | areas_processed++; |
3810e06e DD |
640 | } |
641 | ||
ca1f4309 DS |
642 | if (ospf6_is_router_abr (ospf6)) |
643 | ospf6_abr_defaults_to_stub (ospf6); | |
3810e06e | 644 | |
86f72dcb | 645 | quagga_gettime (QUAGGA_CLK_MONOTONIC, &end); |
2680aa2b | 646 | timersub (&end, &start, &runtime); |
718e3744 | 647 | |
3810e06e DD |
648 | ospf6->ts_spf_duration = runtime; |
649 | ||
a0edf674 DD |
650 | ospf6_spf_reason_string(ospf6->spf_reason, rbuf, sizeof(rbuf)); |
651 | ||
3b68735f | 652 | if (IS_OSPF6_DEBUG_SPF (PROCESS) || IS_OSPF6_DEBUG_SPF (TIME)) |
c6487d61 | 653 | zlog_debug ("SPF runtime: %ld sec %ld usec", |
654 | runtime.tv_sec, runtime.tv_usec); | |
718e3744 | 655 | |
a0edf674 DD |
656 | zlog_info("SPF processing: # Areas: %d, SPF runtime: %ld sec %ld usec, " |
657 | "Reason: %s\n", areas_processed, runtime.tv_sec, runtime.tv_usec, | |
658 | rbuf); | |
659 | ospf6->last_spf_reason = ospf6->spf_reason; | |
660 | ospf6_reset_spf_reason(ospf6); | |
718e3744 | 661 | return 0; |
662 | } | |
663 | ||
3810e06e DD |
664 | /* Add schedule for SPF calculation. To avoid frequenst SPF calc, we |
665 | set timer for SPF calc. */ | |
718e3744 | 666 | void |
a0edf674 | 667 | ospf6_spf_schedule (struct ospf6 *ospf6, unsigned int reason) |
718e3744 | 668 | { |
3810e06e DD |
669 | unsigned long delay, elapsed, ht; |
670 | struct timeval now, result; | |
671 | ||
a0edf674 DD |
672 | ospf6_set_spf_reason(ospf6, reason); |
673 | ||
3810e06e | 674 | if (IS_OSPF6_DEBUG_SPF(PROCESS) || IS_OSPF6_DEBUG_SPF (TIME)) |
a0edf674 DD |
675 | { |
676 | char rbuf[32]; | |
677 | ospf6_spf_reason_string(reason, rbuf, sizeof(rbuf)); | |
678 | zlog_debug ("SPF: calculation timer scheduled (reason %s)", rbuf); | |
679 | } | |
3810e06e DD |
680 | |
681 | /* OSPF instance does not exist. */ | |
682 | if (ospf6 == NULL) | |
718e3744 | 683 | return; |
3810e06e DD |
684 | |
685 | /* SPF calculation timer is already scheduled. */ | |
686 | if (ospf6->t_spf_calc) | |
687 | { | |
688 | if (IS_OSPF6_DEBUG_SPF(PROCESS) || IS_OSPF6_DEBUG_SPF (TIME)) | |
689 | zlog_debug ("SPF: calculation timer is already scheduled: %p", | |
690 | ospf6->t_spf_calc); | |
691 | return; | |
692 | } | |
693 | ||
694 | /* XXX Monotic timers: we only care about relative time here. */ | |
695 | now = recent_relative_time (); | |
696 | timersub (&now, &ospf6->ts_spf, &result); | |
697 | ||
698 | elapsed = (result.tv_sec * 1000) + (result.tv_usec / 1000); | |
699 | ht = ospf6->spf_holdtime * ospf6->spf_hold_multiplier; | |
700 | ||
701 | if (ht > ospf6->spf_max_holdtime) | |
702 | ht = ospf6->spf_max_holdtime; | |
703 | ||
704 | /* Get SPF calculation delay time. */ | |
705 | if (elapsed < ht) | |
706 | { | |
707 | /* Got an event within the hold time of last SPF. We need to | |
708 | * increase the hold_multiplier, if it's not already at/past | |
709 | * maximum value, and wasn't already increased.. | |
710 | */ | |
711 | if (ht < ospf6->spf_max_holdtime) | |
712 | ospf6->spf_hold_multiplier++; | |
713 | ||
714 | /* always honour the SPF initial delay */ | |
715 | if ( (ht - elapsed) < ospf6->spf_delay) | |
716 | delay = ospf6->spf_delay; | |
717 | else | |
718 | delay = ht - elapsed; | |
719 | } | |
720 | else | |
721 | { | |
722 | /* Event is past required hold-time of last SPF */ | |
723 | delay = ospf6->spf_delay; | |
724 | ospf6->spf_hold_multiplier = 1; | |
725 | } | |
726 | ||
727 | if (IS_OSPF6_DEBUG_SPF(PROCESS) || IS_OSPF6_DEBUG_SPF (TIME)) | |
728 | zlog_debug ("SPF: calculation timer delay = %ld", delay); | |
729 | ||
730 | zlog_info ("SPF: Scheduled in %ld msec", delay); | |
731 | ||
732 | ospf6->t_spf_calc = | |
733 | thread_add_timer_msec (master, ospf6_spf_calculation_thread, ospf6, delay); | |
718e3744 | 734 | } |
735 | ||
736 | void | |
0c083ee9 | 737 | ospf6_spf_display_subtree (struct vty *vty, const char *prefix, int rest, |
508e53e2 | 738 | struct ospf6_vertex *v) |
718e3744 | 739 | { |
1eb8ef25 | 740 | struct listnode *node, *nnode; |
508e53e2 | 741 | struct ospf6_vertex *c; |
742 | char *next_prefix; | |
743 | int len; | |
744 | int restnum; | |
718e3744 | 745 | |
508e53e2 | 746 | /* "prefix" is the space prefix of the display line */ |
049207c3 | 747 | vty_out (vty, "%s+-%s [%d]%s", prefix, v->name, v->cost, VNL); |
718e3744 | 748 | |
508e53e2 | 749 | len = strlen (prefix) + 4; |
750 | next_prefix = (char *) malloc (len); | |
751 | if (next_prefix == NULL) | |
718e3744 | 752 | { |
049207c3 | 753 | vty_out (vty, "malloc failed%s", VNL); |
508e53e2 | 754 | return; |
718e3744 | 755 | } |
508e53e2 | 756 | snprintf (next_prefix, len, "%s%s", prefix, (rest ? "| " : " ")); |
718e3744 | 757 | |
508e53e2 | 758 | restnum = listcount (v->child_list); |
1eb8ef25 | 759 | for (ALL_LIST_ELEMENTS (v->child_list, node, nnode, c)) |
718e3744 | 760 | { |
508e53e2 | 761 | restnum--; |
762 | ospf6_spf_display_subtree (vty, next_prefix, restnum, c); | |
718e3744 | 763 | } |
718e3744 | 764 | |
508e53e2 | 765 | free (next_prefix); |
718e3744 | 766 | } |
767 | ||
3b68735f | 768 | DEFUN (debug_ospf6_spf_process, |
769 | debug_ospf6_spf_process_cmd, | |
770 | "debug ospf6 spf process", | |
508e53e2 | 771 | DEBUG_STR |
772 | OSPF6_STR | |
773 | "Debug SPF Calculation\n" | |
3b68735f | 774 | "Debug Detailed SPF Process\n" |
508e53e2 | 775 | ) |
718e3744 | 776 | { |
508e53e2 | 777 | unsigned char level = 0; |
3b68735f | 778 | level = OSPF6_DEBUG_SPF_PROCESS; |
508e53e2 | 779 | OSPF6_DEBUG_SPF_ON (level); |
780 | return CMD_SUCCESS; | |
718e3744 | 781 | } |
782 | ||
3b68735f | 783 | DEFUN (debug_ospf6_spf_time, |
784 | debug_ospf6_spf_time_cmd, | |
785 | "debug ospf6 spf time", | |
508e53e2 | 786 | DEBUG_STR |
718e3744 | 787 | OSPF6_STR |
508e53e2 | 788 | "Debug SPF Calculation\n" |
3b68735f | 789 | "Measure time taken by SPF Calculation\n" |
508e53e2 | 790 | ) |
718e3744 | 791 | { |
508e53e2 | 792 | unsigned char level = 0; |
3b68735f | 793 | level = OSPF6_DEBUG_SPF_TIME; |
508e53e2 | 794 | OSPF6_DEBUG_SPF_ON (level); |
718e3744 | 795 | return CMD_SUCCESS; |
796 | } | |
797 | ||
2680aa2b | 798 | DEFUN (debug_ospf6_spf_database, |
799 | debug_ospf6_spf_database_cmd, | |
800 | "debug ospf6 spf database", | |
801 | DEBUG_STR | |
802 | OSPF6_STR | |
803 | "Debug SPF Calculation\n" | |
804 | "Log number of LSAs at SPF Calculation time\n" | |
805 | ) | |
806 | { | |
807 | unsigned char level = 0; | |
808 | level = OSPF6_DEBUG_SPF_DATABASE; | |
809 | OSPF6_DEBUG_SPF_ON (level); | |
810 | return CMD_SUCCESS; | |
811 | } | |
812 | ||
3b68735f | 813 | DEFUN (no_debug_ospf6_spf_process, |
814 | no_debug_ospf6_spf_process_cmd, | |
815 | "no debug ospf6 spf process", | |
508e53e2 | 816 | NO_STR |
817 | DEBUG_STR | |
818 | OSPF6_STR | |
819 | "Quit Debugging SPF Calculation\n" | |
3b68735f | 820 | "Quit Debugging Detailed SPF Process\n" |
508e53e2 | 821 | ) |
718e3744 | 822 | { |
508e53e2 | 823 | unsigned char level = 0; |
3b68735f | 824 | level = OSPF6_DEBUG_SPF_PROCESS; |
508e53e2 | 825 | OSPF6_DEBUG_SPF_OFF (level); |
826 | return CMD_SUCCESS; | |
718e3744 | 827 | } |
828 | ||
3b68735f | 829 | DEFUN (no_debug_ospf6_spf_time, |
830 | no_debug_ospf6_spf_time_cmd, | |
831 | "no debug ospf6 spf time", | |
508e53e2 | 832 | NO_STR |
833 | DEBUG_STR | |
718e3744 | 834 | OSPF6_STR |
508e53e2 | 835 | "Quit Debugging SPF Calculation\n" |
3b68735f | 836 | "Quit Measuring time taken by SPF Calculation\n" |
508e53e2 | 837 | ) |
718e3744 | 838 | { |
508e53e2 | 839 | unsigned char level = 0; |
3b68735f | 840 | level = OSPF6_DEBUG_SPF_TIME; |
508e53e2 | 841 | OSPF6_DEBUG_SPF_OFF (level); |
718e3744 | 842 | return CMD_SUCCESS; |
843 | } | |
844 | ||
2680aa2b | 845 | DEFUN (no_debug_ospf6_spf_database, |
846 | no_debug_ospf6_spf_database_cmd, | |
847 | "no debug ospf6 spf database", | |
848 | NO_STR | |
849 | DEBUG_STR | |
850 | OSPF6_STR | |
851 | "Debug SPF Calculation\n" | |
852 | "Quit Logging number of LSAs at SPF Calculation time\n" | |
853 | ) | |
854 | { | |
855 | unsigned char level = 0; | |
856 | level = OSPF6_DEBUG_SPF_DATABASE; | |
857 | OSPF6_DEBUG_SPF_OFF (level); | |
858 | return CMD_SUCCESS; | |
859 | } | |
860 | ||
3810e06e DD |
861 | static int |
862 | ospf6_timers_spf_set (struct vty *vty, unsigned int delay, | |
863 | unsigned int hold, | |
864 | unsigned int max) | |
865 | { | |
866 | struct ospf6 *ospf = vty->index; | |
867 | ||
868 | ospf->spf_delay = delay; | |
869 | ospf->spf_holdtime = hold; | |
870 | ospf->spf_max_holdtime = max; | |
871 | ||
872 | return CMD_SUCCESS; | |
873 | } | |
874 | ||
875 | DEFUN (ospf6_timers_throttle_spf, | |
876 | ospf6_timers_throttle_spf_cmd, | |
877 | "timers throttle spf <0-600000> <0-600000> <0-600000>", | |
878 | "Adjust routing timers\n" | |
879 | "Throttling adaptive timer\n" | |
880 | "OSPF6 SPF timers\n" | |
881 | "Delay (msec) from first change received till SPF calculation\n" | |
882 | "Initial hold time (msec) between consecutive SPF calculations\n" | |
883 | "Maximum hold time (msec)\n") | |
884 | { | |
885 | unsigned int delay, hold, max; | |
886 | ||
887 | if (argc != 3) | |
888 | { | |
889 | vty_out (vty, "Insufficient arguments%s", VTY_NEWLINE); | |
890 | return CMD_WARNING; | |
891 | } | |
892 | ||
893 | VTY_GET_INTEGER_RANGE ("SPF delay timer", delay, argv[0], 0, 600000); | |
894 | VTY_GET_INTEGER_RANGE ("SPF hold timer", hold, argv[1], 0, 600000); | |
895 | VTY_GET_INTEGER_RANGE ("SPF max-hold timer", max, argv[2], 0, 600000); | |
896 | ||
897 | return ospf6_timers_spf_set (vty, delay, hold, max); | |
898 | } | |
899 | ||
900 | DEFUN (no_ospf6_timers_throttle_spf, | |
901 | no_ospf6_timers_throttle_spf_cmd, | |
902 | "no timers throttle spf", | |
903 | NO_STR | |
904 | "Adjust routing timers\n" | |
905 | "Throttling adaptive timer\n" | |
906 | "OSPF6 SPF timers\n") | |
907 | { | |
908 | return ospf6_timers_spf_set (vty, | |
909 | OSPF_SPF_DELAY_DEFAULT, | |
910 | OSPF_SPF_HOLDTIME_DEFAULT, | |
911 | OSPF_SPF_MAX_HOLDTIME_DEFAULT); | |
912 | } | |
913 | ||
813d4307 DW |
914 | ALIAS (no_ospf6_timers_throttle_spf, |
915 | no_ospf6_timers_throttle_spf_val_cmd, | |
916 | "no timers throttle spf <0-600000> <0-600000> <0-600000>", | |
917 | NO_STR | |
918 | "Adjust routing timers\n" | |
919 | "Throttling adaptive timer\n" | |
920 | "OSPF6 SPF timers\n" | |
921 | "Delay (msec) from first change received till SPF calculation\n" | |
922 | "Initial hold time (msec) between consecutive SPF calculations\n" | |
923 | "Maximum hold time (msec)\n") | |
924 | ||
508e53e2 | 925 | int |
926 | config_write_ospf6_debug_spf (struct vty *vty) | |
718e3744 | 927 | { |
3b68735f | 928 | if (IS_OSPF6_DEBUG_SPF (PROCESS)) |
929 | vty_out (vty, "debug ospf6 spf process%s", VNL); | |
930 | if (IS_OSPF6_DEBUG_SPF (TIME)) | |
931 | vty_out (vty, "debug ospf6 spf time%s", VNL); | |
2680aa2b | 932 | if (IS_OSPF6_DEBUG_SPF (DATABASE)) |
933 | vty_out (vty, "debug ospf6 spf database%s", VNL); | |
508e53e2 | 934 | return 0; |
718e3744 | 935 | } |
936 | ||
3810e06e DD |
937 | void |
938 | ospf6_spf_config_write (struct vty *vty) | |
939 | { | |
940 | ||
941 | if (ospf6->spf_delay != OSPF_SPF_DELAY_DEFAULT || | |
942 | ospf6->spf_holdtime != OSPF_SPF_HOLDTIME_DEFAULT || | |
943 | ospf6->spf_max_holdtime != OSPF_SPF_MAX_HOLDTIME_DEFAULT) | |
944 | vty_out (vty, " timers throttle spf %d %d %d%s", | |
945 | ospf6->spf_delay, ospf6->spf_holdtime, | |
946 | ospf6->spf_max_holdtime, VTY_NEWLINE); | |
947 | ||
948 | } | |
949 | ||
508e53e2 | 950 | void |
6ac29a51 | 951 | install_element_ospf6_debug_spf (void) |
508e53e2 | 952 | { |
3b68735f | 953 | install_element (ENABLE_NODE, &debug_ospf6_spf_process_cmd); |
954 | install_element (ENABLE_NODE, &debug_ospf6_spf_time_cmd); | |
2680aa2b | 955 | install_element (ENABLE_NODE, &debug_ospf6_spf_database_cmd); |
3b68735f | 956 | install_element (ENABLE_NODE, &no_debug_ospf6_spf_process_cmd); |
957 | install_element (ENABLE_NODE, &no_debug_ospf6_spf_time_cmd); | |
2680aa2b | 958 | install_element (ENABLE_NODE, &no_debug_ospf6_spf_database_cmd); |
3b68735f | 959 | install_element (CONFIG_NODE, &debug_ospf6_spf_process_cmd); |
960 | install_element (CONFIG_NODE, &debug_ospf6_spf_time_cmd); | |
2680aa2b | 961 | install_element (CONFIG_NODE, &debug_ospf6_spf_database_cmd); |
3b68735f | 962 | install_element (CONFIG_NODE, &no_debug_ospf6_spf_process_cmd); |
963 | install_element (CONFIG_NODE, &no_debug_ospf6_spf_time_cmd); | |
2680aa2b | 964 | install_element (CONFIG_NODE, &no_debug_ospf6_spf_database_cmd); |
508e53e2 | 965 | } |
718e3744 | 966 | |
967 | void | |
6ac29a51 | 968 | ospf6_spf_init (void) |
718e3744 | 969 | { |
3810e06e DD |
970 | install_element (OSPF6_NODE, &ospf6_timers_throttle_spf_cmd); |
971 | install_element (OSPF6_NODE, &no_ospf6_timers_throttle_spf_cmd); | |
813d4307 | 972 | install_element (OSPF6_NODE, &no_ospf6_timers_throttle_spf_val_cmd); |
718e3744 | 973 | } |