2 * Copyright (C) 2003 Yasuhiro Ohara
4 * This file is part of GNU Zebra.
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
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.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; see the file COPYING; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 /* Shortest Path First calculation for OSPFv3 */
32 #include "lib_errors.h"
34 #include "ospf6_lsa.h"
35 #include "ospf6_lsdb.h"
36 #include "ospf6_route.h"
37 #include "ospf6_area.h"
38 #include "ospf6_proto.h"
39 #include "ospf6_abr.h"
40 #include "ospf6_asbr.h"
41 #include "ospf6_spf.h"
42 #include "ospf6_intra.h"
43 #include "ospf6_interface.h"
45 #include "ospf6_abr.h"
46 #include "ospf6_nssa.h"
47 #include "ospf6_zebra.h"
49 DEFINE_MTYPE_STATIC(OSPF6D
, OSPF6_VERTEX
, "OSPF6 vertex");
51 unsigned char conf_debug_ospf6_spf
= 0;
53 static void ospf6_spf_copy_nexthops_to_route(struct ospf6_route
*rt
,
54 struct ospf6_vertex
*v
)
57 ospf6_copy_nexthops(rt
->nh_list
, v
->nh_list
);
60 static void ospf6_spf_merge_nexthops_to_route(struct ospf6_route
*rt
,
61 struct ospf6_vertex
*v
)
64 ospf6_merge_nexthops(rt
->nh_list
, v
->nh_list
);
67 static unsigned int ospf6_spf_get_ifindex_from_nh(struct ospf6_vertex
*v
)
69 struct ospf6_nexthop
*nh
;
70 struct listnode
*node
;
73 node
= listhead(v
->nh_list
);
75 nh
= listgetdata(node
);
83 static int ospf6_vertex_cmp(const struct ospf6_vertex
*va
,
84 const struct ospf6_vertex
*vb
)
87 if (va
->cost
!= vb
->cost
)
88 return (va
->cost
- vb
->cost
);
89 if (va
->hops
!= vb
->hops
)
90 return (va
->hops
- vb
->hops
);
93 DECLARE_SKIPLIST_NONUNIQ(vertex_pqueue
, struct ospf6_vertex
, pqi
,
96 static int ospf6_vertex_id_cmp(void *a
, void *b
)
98 struct ospf6_vertex
*va
= (struct ospf6_vertex
*)a
;
99 struct ospf6_vertex
*vb
= (struct ospf6_vertex
*)b
;
102 ret
= ntohl(ospf6_linkstate_prefix_adv_router(&va
->vertex_id
))
103 - ntohl(ospf6_linkstate_prefix_adv_router(&vb
->vertex_id
));
107 ret
= ntohl(ospf6_linkstate_prefix_id(&va
->vertex_id
))
108 - ntohl(ospf6_linkstate_prefix_id(&vb
->vertex_id
));
112 static struct ospf6_vertex
*ospf6_vertex_create(struct ospf6_lsa
*lsa
)
114 struct ospf6_vertex
*v
;
116 v
= XMALLOC(MTYPE_OSPF6_VERTEX
, sizeof(struct ospf6_vertex
));
119 if (ntohs(lsa
->header
->type
) == OSPF6_LSTYPE_ROUTER
) {
120 v
->type
= OSPF6_VERTEX_TYPE_ROUTER
;
121 /* Router LSA use Link ID 0 as base in vertex_id */
122 ospf6_linkstate_prefix(lsa
->header
->adv_router
, htonl(0),
124 } else if (ntohs(lsa
->header
->type
) == OSPF6_LSTYPE_NETWORK
) {
125 v
->type
= OSPF6_VERTEX_TYPE_NETWORK
;
127 ospf6_linkstate_prefix(lsa
->header
->adv_router
, lsa
->header
->id
,
133 ospf6_linkstate_prefix2str(&v
->vertex_id
, v
->name
, sizeof(v
->name
));
135 if (IS_OSPF6_DEBUG_SPF(PROCESS
))
136 zlog_debug("%s: Creating vertex %s of type %s (0x%04hx) lsa %s",
138 ((ntohs(lsa
->header
->type
) == OSPF6_LSTYPE_ROUTER
)
141 ntohs(lsa
->header
->type
), lsa
->name
);
147 /* capability bits + options */
148 v
->capability
= *(uint8_t *)(OSPF6_LSA_HEADER_END(lsa
->header
));
149 v
->options
[0] = *(uint8_t *)(OSPF6_LSA_HEADER_END(lsa
->header
) + 1);
150 v
->options
[1] = *(uint8_t *)(OSPF6_LSA_HEADER_END(lsa
->header
) + 2);
151 v
->options
[2] = *(uint8_t *)(OSPF6_LSA_HEADER_END(lsa
->header
) + 3);
153 v
->nh_list
= list_new();
154 v
->nh_list
->cmp
= (int (*)(void *, void *))ospf6_nexthop_cmp
;
155 v
->nh_list
->del
= (void (*)(void *))ospf6_nexthop_delete
;
158 v
->child_list
= list_new();
159 v
->child_list
->cmp
= ospf6_vertex_id_cmp
;
164 static void ospf6_vertex_delete(struct ospf6_vertex
*v
)
166 list_delete(&v
->nh_list
);
167 list_delete(&v
->child_list
);
168 XFREE(MTYPE_OSPF6_VERTEX
, v
);
171 static struct ospf6_lsa
*ospf6_lsdesc_lsa(caddr_t lsdesc
,
172 struct ospf6_vertex
*v
)
174 struct ospf6_lsa
*lsa
= NULL
;
176 uint32_t id
= 0, adv_router
= 0;
178 if (VERTEX_IS_TYPE(NETWORK
, v
)) {
179 type
= htons(OSPF6_LSTYPE_ROUTER
);
181 adv_router
= NETWORK_LSDESC_GET_NBR_ROUTERID(lsdesc
);
183 if (ROUTER_LSDESC_IS_TYPE(POINTTOPOINT
, lsdesc
)) {
184 type
= htons(OSPF6_LSTYPE_ROUTER
);
186 adv_router
= ROUTER_LSDESC_GET_NBR_ROUTERID(lsdesc
);
187 } else if (ROUTER_LSDESC_IS_TYPE(TRANSIT_NETWORK
, lsdesc
)) {
188 type
= htons(OSPF6_LSTYPE_NETWORK
);
189 id
= htonl(ROUTER_LSDESC_GET_NBR_IFID(lsdesc
));
190 adv_router
= ROUTER_LSDESC_GET_NBR_ROUTERID(lsdesc
);
194 if (type
== htons(OSPF6_LSTYPE_NETWORK
))
195 lsa
= ospf6_lsdb_lookup(type
, id
, adv_router
, v
->area
->lsdb
);
197 lsa
= ospf6_create_single_router_lsa(v
->area
, v
->area
->lsdb
,
199 if (IS_OSPF6_DEBUG_SPF(PROCESS
)) {
200 char ibuf
[16], abuf
[16];
201 inet_ntop(AF_INET
, &id
, ibuf
, sizeof(ibuf
));
202 inet_ntop(AF_INET
, &adv_router
, abuf
, sizeof(abuf
));
204 zlog_debug(" Link to: %s len %u, V %s", lsa
->name
,
205 ntohs(lsa
->header
->length
), v
->name
);
207 zlog_debug(" Link to: [%s Id:%s Adv:%s] No LSA , V %s",
208 ospf6_lstype_name(type
), ibuf
, abuf
,
215 static char *ospf6_lsdesc_backlink(struct ospf6_lsa
*lsa
, caddr_t lsdesc
,
216 struct ospf6_vertex
*v
)
218 caddr_t backlink
, found
= NULL
;
221 size
= (OSPF6_LSA_IS_TYPE(ROUTER
, lsa
)
222 ? sizeof(struct ospf6_router_lsdesc
)
223 : sizeof(struct ospf6_network_lsdesc
));
224 for (backlink
= OSPF6_LSA_HEADER_END(lsa
->header
) + 4;
225 backlink
+ size
<= OSPF6_LSA_END(lsa
->header
); backlink
+= size
) {
226 assert(!(OSPF6_LSA_IS_TYPE(NETWORK
, lsa
)
227 && VERTEX_IS_TYPE(NETWORK
, v
)));
229 if (OSPF6_LSA_IS_TYPE(NETWORK
, lsa
)) {
230 if (NETWORK_LSDESC_GET_NBR_ROUTERID(backlink
)
231 == v
->lsa
->header
->adv_router
)
233 } else if (VERTEX_IS_TYPE(NETWORK
, v
)) {
234 if (ROUTER_LSDESC_IS_TYPE(TRANSIT_NETWORK
, backlink
)
235 && ROUTER_LSDESC_GET_NBR_ROUTERID(backlink
)
236 == v
->lsa
->header
->adv_router
237 && ROUTER_LSDESC_GET_NBR_IFID(backlink
)
238 == ntohl(v
->lsa
->header
->id
))
241 assert(OSPF6_LSA_IS_TYPE(ROUTER
, lsa
)
242 && VERTEX_IS_TYPE(ROUTER
, v
));
244 if (!ROUTER_LSDESC_IS_TYPE(POINTTOPOINT
, backlink
)
245 || !ROUTER_LSDESC_IS_TYPE(POINTTOPOINT
, lsdesc
))
248 if (ROUTER_LSDESC_GET_NBR_IFID(backlink
)
249 != ROUTER_LSDESC_GET_IFID(lsdesc
)
250 || ROUTER_LSDESC_GET_NBR_IFID(lsdesc
)
251 != ROUTER_LSDESC_GET_IFID(backlink
))
253 if (ROUTER_LSDESC_GET_NBR_ROUTERID(backlink
)
254 != v
->lsa
->header
->adv_router
255 || ROUTER_LSDESC_GET_NBR_ROUTERID(lsdesc
)
256 != lsa
->header
->adv_router
)
262 if (IS_OSPF6_DEBUG_SPF(PROCESS
))
263 zlog_debug("Vertex %s Lsa %s Backlink %s", v
->name
, lsa
->name
,
264 (found
? "OK" : "FAIL"));
269 static void ospf6_nexthop_calc(struct ospf6_vertex
*w
, struct ospf6_vertex
*v
,
270 caddr_t lsdesc
, struct ospf6
*ospf6
)
274 struct ospf6_interface
*oi
;
277 struct ospf6_lsa
*lsa
;
278 struct ospf6_link_lsa
*link_lsa
;
281 assert(VERTEX_IS_TYPE(ROUTER
, w
));
282 ifindex
= (VERTEX_IS_TYPE(NETWORK
, v
) ? ospf6_spf_get_ifindex_from_nh(v
)
283 : ROUTER_LSDESC_GET_IFID(lsdesc
));
285 flog_err(EC_LIB_DEVELOPMENT
, "No nexthop ifindex at vertex %s",
290 oi
= ospf6_interface_lookup_by_ifindex(ifindex
, ospf6
->vrf_id
);
292 zlog_warn("Can't find interface in SPF: ifindex %d", ifindex
);
296 type
= htons(OSPF6_LSTYPE_LINK
);
297 adv_router
= (VERTEX_IS_TYPE(NETWORK
, v
)
298 ? NETWORK_LSDESC_GET_NBR_ROUTERID(lsdesc
)
299 : ROUTER_LSDESC_GET_NBR_ROUTERID(lsdesc
));
302 for (ALL_LSDB_TYPED_ADVRTR(oi
->lsdb
, type
, adv_router
, lsa
)) {
303 if (VERTEX_IS_TYPE(ROUTER
, v
)
304 && htonl(ROUTER_LSDESC_GET_NBR_IFID(lsdesc
))
308 link_lsa
= (struct ospf6_link_lsa
*)OSPF6_LSA_HEADER_END(
310 if (IS_OSPF6_DEBUG_SPF(PROCESS
)) {
311 inet_ntop(AF_INET6
, &link_lsa
->linklocal_addr
, buf
,
313 zlog_debug(" nexthop %s from %s", buf
, lsa
->name
);
316 ospf6_add_nexthop(w
->nh_list
, ifindex
,
317 &link_lsa
->linklocal_addr
);
321 if (i
== 0 && IS_OSPF6_DEBUG_SPF(PROCESS
))
322 zlog_debug("No nexthop for %s found", w
->name
);
325 static int ospf6_spf_install(struct ospf6_vertex
*v
,
326 struct ospf6_route_table
*result_table
)
328 struct ospf6_route
*route
, *parent_route
;
329 struct ospf6_vertex
*prev
;
331 if (IS_OSPF6_DEBUG_SPF(PROCESS
))
332 zlog_debug("SPF install %s (lsa %s) hops %d cost %d", v
->name
,
333 v
->lsa
->name
, v
->hops
, v
->cost
);
335 route
= ospf6_route_lookup(&v
->vertex_id
, result_table
);
336 if (route
&& route
->path
.cost
< v
->cost
) {
337 if (IS_OSPF6_DEBUG_SPF(PROCESS
))
339 " already installed with lower cost (%d), ignore",
341 ospf6_vertex_delete(v
);
343 } else if (route
&& route
->path
.cost
== v
->cost
) {
344 if (IS_OSPF6_DEBUG_SPF(PROCESS
))
346 " another path found to route %pFX lsa %s, merge",
347 &route
->prefix
, v
->lsa
->name
);
348 ospf6_spf_merge_nexthops_to_route(route
, v
);
350 prev
= (struct ospf6_vertex
*)route
->route_option
;
351 assert(prev
->hops
<= v
->hops
);
353 if ((VERTEX_IS_TYPE(ROUTER
, v
)
354 && route
->path
.origin
.id
!= v
->lsa
->header
->id
)) {
355 if (IS_OSPF6_DEBUG_SPF(PROCESS
)) {
357 "%s: V lsa %s id %u, route id %u are different",
358 __func__
, v
->lsa
->name
,
359 ntohl(v
->lsa
->header
->id
),
360 ntohl(route
->path
.origin
.id
));
365 ospf6_vertex_delete(v
);
369 /* There should be no case where candidate being installed (variable
370 "v") is closer than the one in the SPF tree (variable "route").
371 In the case something has gone wrong with the behavior of
374 /* the case where the route exists already is handled and returned
376 assert(route
== NULL
);
378 route
= ospf6_route_create(v
->area
->ospf6
);
379 memcpy(&route
->prefix
, &v
->vertex_id
, sizeof(struct prefix
));
380 route
->type
= OSPF6_DEST_TYPE_LINKSTATE
;
381 route
->path
.type
= OSPF6_PATH_TYPE_INTRA
;
382 route
->path
.origin
.type
= v
->lsa
->header
->type
;
383 route
->path
.origin
.id
= v
->lsa
->header
->id
;
384 route
->path
.origin
.adv_router
= v
->lsa
->header
->adv_router
;
385 route
->path
.metric_type
= 1;
386 route
->path
.cost
= v
->cost
;
387 route
->path
.u
.cost_e2
= v
->hops
;
388 route
->path
.router_bits
= v
->capability
;
389 route
->path
.options
[0] = v
->options
[0];
390 route
->path
.options
[1] = v
->options
[1];
391 route
->path
.options
[2] = v
->options
[2];
393 ospf6_spf_copy_nexthops_to_route(route
, v
);
396 * The SPF logic implementation does not transfer the multipathing
398 * of a parent to a child node. Thus if there was a 3-way multipath to a
399 * node's parent and a single hop from the parent to the child, the
401 * creating new vertices and computing next hops prevents there from
403 * paths to the child node. This is primarily because the resolution of
404 * multipath is done in this routine, not in the main spf loop.
406 * The following logic addresses that problem by merging the parent's
408 * information with the child's, if the parent is not the root of the
410 * This is based on the assumption that before a node's route is
412 * its parent's route's nexthops have already been installed.
414 if (v
->parent
&& v
->parent
->hops
) {
416 ospf6_route_lookup(&v
->parent
->vertex_id
, result_table
);
418 ospf6_route_merge_nexthops(route
, parent_route
);
423 listnode_add_sort(v
->parent
->child_list
, v
);
424 route
->route_option
= v
;
426 ospf6_route_add(route
, result_table
);
430 void ospf6_spf_table_finish(struct ospf6_route_table
*result_table
)
432 struct ospf6_route
*route
, *nroute
;
433 struct ospf6_vertex
*v
;
434 for (route
= ospf6_route_head(result_table
); route
; route
= nroute
) {
435 nroute
= ospf6_route_next(route
);
436 v
= (struct ospf6_vertex
*)route
->route_option
;
437 ospf6_vertex_delete(v
);
438 ospf6_route_remove(route
, result_table
);
442 static const char *const ospf6_spf_reason_str
[] = {
443 "R+", /* OSPF6_SPF_FLAGS_ROUTER_LSA_ADDED */
444 "R-", /* OSPF6_SPF_FLAGS_ROUTER_LSA_REMOVED */
445 "N+", /* OSPF6_SPF_FLAGS_NETWORK_LSA_ADDED */
446 "N-", /* OSPF6_SPF_FLAGS_NETWORK_LSA_REMOVED */
447 "L+", /* OSPF6_SPF_FLAGS_NETWORK_LINK_LSA_ADDED */
448 "L-", /* OSPF6_SPF_FLAGS_NETWORK_LINK_LSA_REMOVED */
449 "R*", /* OSPF6_SPF_FLAGS_ROUTER_LSA_ORIGINATED */
450 "N*", /* OSPF6_SPF_FLAGS_NETWORK_LSA_ORIGINATED */
451 "C", /* OSPF6_SPF_FLAGS_CONFIG_CHANGE */
452 "A", /* OSPF6_SPF_FLAGS_ASBR_STATUS_CHANGE */
453 "GR", /* OSPF6_SPF_FLAGS_GR_FINISH */
456 void ospf6_spf_reason_string(uint32_t reason
, char *buf
, int size
)
468 for (bit
= 0; bit
< array_size(ospf6_spf_reason_str
); bit
++) {
469 if ((reason
& (1 << bit
)) && (len
< size
)) {
470 len
+= snprintf((buf
+ len
), (size
- len
), "%s%s",
471 (len
> 0) ? ", " : "",
472 ospf6_spf_reason_str
[bit
]);
477 /* RFC2328 16.1. Calculating the shortest-path tree for an area */
478 /* RFC2740 3.8.1. Calculating the shortest path tree for an area */
479 void ospf6_spf_calculation(uint32_t router_id
,
480 struct ospf6_route_table
*result_table
,
481 struct ospf6_area
*oa
)
483 struct vertex_pqueue_head candidate_list
;
484 struct ospf6_vertex
*root
, *v
, *w
;
487 struct ospf6_lsa
*lsa
;
488 struct in6_addr address
;
490 ospf6_spf_table_finish(result_table
);
492 /* Install the calculating router itself as the root of the SPF tree */
493 /* construct root vertex */
494 lsa
= ospf6_create_single_router_lsa(oa
, oa
->lsdb_self
, router_id
);
496 zlog_warn("%s: No router LSA for area %s", __func__
, oa
->name
);
501 vertex_pqueue_init(&candidate_list
);
503 root
= ospf6_vertex_create(lsa
);
507 root
->link_id
= lsa
->header
->id
;
508 inet_pton(AF_INET6
, "::1", &address
);
510 /* Actually insert root to the candidate-list as the only candidate */
511 vertex_pqueue_add(&candidate_list
, root
);
513 /* Iterate until candidate-list becomes empty */
514 while ((v
= vertex_pqueue_pop(&candidate_list
))) {
515 /* installing may result in merging or rejecting of the vertex
517 if (ospf6_spf_install(v
, result_table
) < 0)
520 /* Skip overloaded routers */
521 if ((OSPF6_LSA_IS_TYPE(ROUTER
, v
->lsa
)
522 && ospf6_router_is_stub_router(v
->lsa
)))
525 /* For each LS description in the just-added vertex V's LSA */
526 size
= (VERTEX_IS_TYPE(ROUTER
, v
)
527 ? sizeof(struct ospf6_router_lsdesc
)
528 : sizeof(struct ospf6_network_lsdesc
));
529 for (lsdesc
= OSPF6_LSA_HEADER_END(v
->lsa
->header
) + 4;
530 lsdesc
+ size
<= OSPF6_LSA_END(v
->lsa
->header
);
532 lsa
= ospf6_lsdesc_lsa(lsdesc
, v
);
536 if (OSPF6_LSA_IS_MAXAGE(lsa
))
539 if (!ospf6_lsdesc_backlink(lsa
, lsdesc
, v
))
542 w
= ospf6_vertex_create(lsa
);
545 if (VERTEX_IS_TYPE(ROUTER
, v
)) {
547 + ROUTER_LSDESC_GET_METRIC(lsdesc
);
550 + (VERTEX_IS_TYPE(NETWORK
, w
) ? 0 : 1);
554 w
->hops
= v
->hops
+ 1;
557 /* nexthop calculation */
561 ROUTER_LSDESC_GET_IFID(lsdesc
), NULL
);
562 else if (w
->hops
== 1 && v
->hops
== 0)
563 ospf6_nexthop_calc(w
, v
, lsdesc
, oa
->ospf6
);
565 ospf6_copy_nexthops(w
->nh_list
, v
->nh_list
);
568 /* add new candidate to the candidate_list */
569 if (IS_OSPF6_DEBUG_SPF(PROCESS
))
571 " New candidate: %s hops %d cost %d",
572 w
->name
, w
->hops
, w
->cost
);
573 vertex_pqueue_add(&candidate_list
, w
);
577 //vertex_pqueue_fini(&candidate_list);
579 ospf6_remove_temp_router_lsa(oa
);
581 oa
->spf_calculation
++;
584 static void ospf6_spf_log_database(struct ospf6_area
*oa
)
586 char *p
, *end
, buffer
[256];
587 struct listnode
*node
;
588 struct ospf6_interface
*oi
;
591 end
= buffer
+ sizeof(buffer
);
593 snprintf(p
, end
- p
, "SPF on DB (#LSAs):");
594 p
= (buffer
+ strlen(buffer
) < end
? buffer
+ strlen(buffer
) : end
);
595 snprintf(p
, end
- p
, " Area %s: %d", oa
->name
, oa
->lsdb
->count
);
596 p
= (buffer
+ strlen(buffer
) < end
? buffer
+ strlen(buffer
) : end
);
598 for (ALL_LIST_ELEMENTS_RO(oa
->if_list
, node
, oi
)) {
599 snprintf(p
, end
- p
, " I/F %s: %d", oi
->interface
->name
,
601 p
= (buffer
+ strlen(buffer
) < end
? buffer
+ strlen(buffer
)
605 zlog_debug("%s", buffer
);
608 static void ospf6_spf_calculation_thread(struct thread
*t
)
610 struct ospf6_area
*oa
;
612 struct timeval start
, end
, runtime
;
613 struct listnode
*node
;
614 int areas_processed
= 0;
617 ospf6
= (struct ospf6
*)THREAD_ARG(t
);
619 /* execute SPF calculation */
621 ospf6
->ts_spf
= start
;
623 if (ospf6_check_and_set_router_abr(ospf6
))
624 ospf6_abr_range_reset_cost(ospf6
);
626 for (ALL_LIST_ELEMENTS_RO(ospf6
->area_list
, node
, oa
)) {
628 if (oa
== ospf6
->backbone
)
631 monotime(&oa
->ts_spf
);
632 if (IS_OSPF6_DEBUG_SPF(PROCESS
))
633 zlog_debug("SPF calculation for Area %s", oa
->name
);
634 if (IS_OSPF6_DEBUG_SPF(DATABASE
))
635 ospf6_spf_log_database(oa
);
637 ospf6_spf_calculation(ospf6
->router_id
, oa
->spf_table
, oa
);
638 ospf6_intra_route_calculation(oa
);
639 ospf6_intra_brouter_calculation(oa
);
644 if (ospf6
->backbone
) {
645 monotime(&ospf6
->backbone
->ts_spf
);
646 if (IS_OSPF6_DEBUG_SPF(PROCESS
))
647 zlog_debug("SPF calculation for Backbone area %s",
648 ospf6
->backbone
->name
);
649 if (IS_OSPF6_DEBUG_SPF(DATABASE
))
650 ospf6_spf_log_database(ospf6
->backbone
);
652 ospf6_spf_calculation(ospf6
->router_id
,
653 ospf6
->backbone
->spf_table
,
655 ospf6_intra_route_calculation(ospf6
->backbone
);
656 ospf6_intra_brouter_calculation(ospf6
->backbone
);
660 /* External LSA calculation */
661 ospf6_ase_calculate_timer_add(ospf6
);
663 if (ospf6_check_and_set_router_abr(ospf6
)) {
664 ospf6_abr_defaults_to_stub(ospf6
);
665 ospf6_abr_nssa_type_7_defaults(ospf6
);
669 timersub(&end
, &start
, &runtime
);
671 ospf6
->ts_spf_duration
= runtime
;
673 ospf6_spf_reason_string(ospf6
->spf_reason
, rbuf
, sizeof(rbuf
));
675 if (IS_OSPF6_DEBUG_SPF(PROCESS
) || IS_OSPF6_DEBUG_SPF(TIME
))
677 "SPF processing: # Areas: %d, SPF runtime: %lld sec %lld usec, Reason: %s",
678 areas_processed
, (long long)runtime
.tv_sec
,
679 (long long)runtime
.tv_usec
, rbuf
);
681 ospf6
->last_spf_reason
= ospf6
->spf_reason
;
682 ospf6_reset_spf_reason(ospf6
);
685 /* Add schedule for SPF calculation. To avoid frequenst SPF calc, we
686 set timer for SPF calc. */
687 void ospf6_spf_schedule(struct ospf6
*ospf6
, unsigned int reason
)
689 unsigned long delay
, elapsed
, ht
;
691 /* OSPF instance does not exist. */
695 ospf6_set_spf_reason(ospf6
, reason
);
697 if (IS_OSPF6_DEBUG_SPF(PROCESS
) || IS_OSPF6_DEBUG_SPF(TIME
)) {
699 ospf6_spf_reason_string(reason
, rbuf
, sizeof(rbuf
));
700 zlog_debug("SPF: calculation timer scheduled (reason %s)",
704 /* SPF calculation timer is already scheduled. */
705 if (thread_is_scheduled(ospf6
->t_spf_calc
)) {
706 if (IS_OSPF6_DEBUG_SPF(PROCESS
) || IS_OSPF6_DEBUG_SPF(TIME
))
708 "SPF: calculation timer is already scheduled: %p",
709 (void *)ospf6
->t_spf_calc
);
713 elapsed
= monotime_since(&ospf6
->ts_spf
, NULL
) / 1000LL;
714 ht
= ospf6
->spf_holdtime
* ospf6
->spf_hold_multiplier
;
716 if (ht
> ospf6
->spf_max_holdtime
)
717 ht
= ospf6
->spf_max_holdtime
;
719 /* Get SPF calculation delay time. */
721 /* Got an event within the hold time of last SPF. We need to
722 * increase the hold_multiplier, if it's not already at/past
723 * maximum value, and wasn't already increased..
725 if (ht
< ospf6
->spf_max_holdtime
)
726 ospf6
->spf_hold_multiplier
++;
728 /* always honour the SPF initial delay */
729 if ((ht
- elapsed
) < ospf6
->spf_delay
)
730 delay
= ospf6
->spf_delay
;
732 delay
= ht
- elapsed
;
734 /* Event is past required hold-time of last SPF */
735 delay
= ospf6
->spf_delay
;
736 ospf6
->spf_hold_multiplier
= 1;
739 if (IS_OSPF6_DEBUG_SPF(PROCESS
) || IS_OSPF6_DEBUG_SPF(TIME
))
740 zlog_debug("SPF: Rescheduling in %ld msec", delay
);
742 THREAD_OFF(ospf6
->t_spf_calc
);
743 thread_add_timer_msec(master
, ospf6_spf_calculation_thread
, ospf6
,
744 delay
, &ospf6
->t_spf_calc
);
747 void ospf6_spf_display_subtree(struct vty
*vty
, const char *prefix
, int rest
,
748 struct ospf6_vertex
*v
, json_object
*json_obj
,
751 struct listnode
*node
, *nnode
;
752 struct ospf6_vertex
*c
;
756 json_object
*json_childs
= NULL
;
757 json_object
*json_child
= NULL
;
760 json_childs
= json_object_new_object();
761 json_object_int_add(json_obj
, "cost", v
->cost
);
763 /* "prefix" is the space prefix of the display line */
764 vty_out(vty
, "%s+-%s [%d]\n", prefix
, v
->name
, v
->cost
);
767 len
= strlen(prefix
) + 4;
768 next_prefix
= (char *)malloc(len
);
769 if (next_prefix
== NULL
) {
770 vty_out(vty
, "malloc failed\n");
773 snprintf(next_prefix
, len
, "%s%s", prefix
, (rest
? "| " : " "));
775 restnum
= listcount(v
->child_list
);
776 for (ALL_LIST_ELEMENTS(v
->child_list
, node
, nnode
, c
)) {
778 json_child
= json_object_new_object();
782 ospf6_spf_display_subtree(vty
, next_prefix
, restnum
, c
,
783 json_child
, use_json
);
786 json_object_object_add(json_childs
, c
->name
,
790 json_object_boolean_add(json_obj
, "isLeafNode",
791 !listcount(v
->child_list
));
792 if (listcount(v
->child_list
))
793 json_object_object_add(json_obj
, "children",
796 json_object_free(json_childs
);
801 DEFUN (debug_ospf6_spf_process
,
802 debug_ospf6_spf_process_cmd
,
803 "debug ospf6 spf process",
806 "Debug SPF Calculation\n"
807 "Debug Detailed SPF Process\n"
810 unsigned char level
= 0;
811 level
= OSPF6_DEBUG_SPF_PROCESS
;
812 OSPF6_DEBUG_SPF_ON(level
);
816 DEFUN (debug_ospf6_spf_time
,
817 debug_ospf6_spf_time_cmd
,
818 "debug ospf6 spf time",
821 "Debug SPF Calculation\n"
822 "Measure time taken by SPF Calculation\n"
825 unsigned char level
= 0;
826 level
= OSPF6_DEBUG_SPF_TIME
;
827 OSPF6_DEBUG_SPF_ON(level
);
831 DEFUN (debug_ospf6_spf_database
,
832 debug_ospf6_spf_database_cmd
,
833 "debug ospf6 spf database",
836 "Debug SPF Calculation\n"
837 "Log number of LSAs at SPF Calculation time\n"
840 unsigned char level
= 0;
841 level
= OSPF6_DEBUG_SPF_DATABASE
;
842 OSPF6_DEBUG_SPF_ON(level
);
846 DEFUN (no_debug_ospf6_spf_process
,
847 no_debug_ospf6_spf_process_cmd
,
848 "no debug ospf6 spf process",
852 "Quit Debugging SPF Calculation\n"
853 "Quit Debugging Detailed SPF Process\n"
856 unsigned char level
= 0;
857 level
= OSPF6_DEBUG_SPF_PROCESS
;
858 OSPF6_DEBUG_SPF_OFF(level
);
862 DEFUN (no_debug_ospf6_spf_time
,
863 no_debug_ospf6_spf_time_cmd
,
864 "no debug ospf6 spf time",
868 "Quit Debugging SPF Calculation\n"
869 "Quit Measuring time taken by SPF Calculation\n"
872 unsigned char level
= 0;
873 level
= OSPF6_DEBUG_SPF_TIME
;
874 OSPF6_DEBUG_SPF_OFF(level
);
878 DEFUN (no_debug_ospf6_spf_database
,
879 no_debug_ospf6_spf_database_cmd
,
880 "no debug ospf6 spf database",
884 "Debug SPF Calculation\n"
885 "Quit Logging number of LSAs at SPF Calculation time\n"
888 unsigned char level
= 0;
889 level
= OSPF6_DEBUG_SPF_DATABASE
;
890 OSPF6_DEBUG_SPF_OFF(level
);
894 static int ospf6_timers_spf_set(struct vty
*vty
, unsigned int delay
,
895 unsigned int hold
, unsigned int max
)
897 VTY_DECLVAR_CONTEXT(ospf6
, ospf
);
899 ospf
->spf_delay
= delay
;
900 ospf
->spf_holdtime
= hold
;
901 ospf
->spf_max_holdtime
= max
;
906 DEFUN (ospf6_timers_throttle_spf
,
907 ospf6_timers_throttle_spf_cmd
,
908 "timers throttle spf (0-600000) (0-600000) (0-600000)",
909 "Adjust routing timers\n"
910 "Throttling adaptive timer\n"
912 "Delay (msec) from first change received till SPF calculation\n"
913 "Initial hold time (msec) between consecutive SPF calculations\n"
914 "Maximum hold time (msec)\n")
917 int idx_number_2
= 4;
918 int idx_number_3
= 5;
919 unsigned int delay
, hold
, max
;
921 delay
= strtoul(argv
[idx_number
]->arg
, NULL
, 10);
922 hold
= strtoul(argv
[idx_number_2
]->arg
, NULL
, 10);
923 max
= strtoul(argv
[idx_number_3
]->arg
, NULL
, 10);
925 return ospf6_timers_spf_set(vty
, delay
, hold
, max
);
928 DEFUN (no_ospf6_timers_throttle_spf
,
929 no_ospf6_timers_throttle_spf_cmd
,
930 "no timers throttle spf [(0-600000) (0-600000) (0-600000)]",
932 "Adjust routing timers\n"
933 "Throttling adaptive timer\n"
935 "Delay (msec) from first change received till SPF calculation\n"
936 "Initial hold time (msec) between consecutive SPF calculations\n"
937 "Maximum hold time (msec)\n")
939 return ospf6_timers_spf_set(vty
, OSPF_SPF_DELAY_DEFAULT
,
940 OSPF_SPF_HOLDTIME_DEFAULT
,
941 OSPF_SPF_MAX_HOLDTIME_DEFAULT
);
945 int config_write_ospf6_debug_spf(struct vty
*vty
)
947 if (IS_OSPF6_DEBUG_SPF(PROCESS
))
948 vty_out(vty
, "debug ospf6 spf process\n");
949 if (IS_OSPF6_DEBUG_SPF(TIME
))
950 vty_out(vty
, "debug ospf6 spf time\n");
951 if (IS_OSPF6_DEBUG_SPF(DATABASE
))
952 vty_out(vty
, "debug ospf6 spf database\n");
956 void ospf6_spf_config_write(struct vty
*vty
, struct ospf6
*ospf6
)
959 if (ospf6
->spf_delay
!= OSPF_SPF_DELAY_DEFAULT
960 || ospf6
->spf_holdtime
!= OSPF_SPF_HOLDTIME_DEFAULT
961 || ospf6
->spf_max_holdtime
!= OSPF_SPF_MAX_HOLDTIME_DEFAULT
)
962 vty_out(vty
, " timers throttle spf %d %d %d\n",
963 ospf6
->spf_delay
, ospf6
->spf_holdtime
,
964 ospf6
->spf_max_holdtime
);
967 void install_element_ospf6_debug_spf(void)
969 install_element(ENABLE_NODE
, &debug_ospf6_spf_process_cmd
);
970 install_element(ENABLE_NODE
, &debug_ospf6_spf_time_cmd
);
971 install_element(ENABLE_NODE
, &debug_ospf6_spf_database_cmd
);
972 install_element(ENABLE_NODE
, &no_debug_ospf6_spf_process_cmd
);
973 install_element(ENABLE_NODE
, &no_debug_ospf6_spf_time_cmd
);
974 install_element(ENABLE_NODE
, &no_debug_ospf6_spf_database_cmd
);
975 install_element(CONFIG_NODE
, &debug_ospf6_spf_process_cmd
);
976 install_element(CONFIG_NODE
, &debug_ospf6_spf_time_cmd
);
977 install_element(CONFIG_NODE
, &debug_ospf6_spf_database_cmd
);
978 install_element(CONFIG_NODE
, &no_debug_ospf6_spf_process_cmd
);
979 install_element(CONFIG_NODE
, &no_debug_ospf6_spf_time_cmd
);
980 install_element(CONFIG_NODE
, &no_debug_ospf6_spf_database_cmd
);
983 void ospf6_spf_init(void)
985 install_element(OSPF6_NODE
, &ospf6_timers_throttle_spf_cmd
);
986 install_element(OSPF6_NODE
, &no_ospf6_timers_throttle_spf_cmd
);
989 /* Create Aggregated Large Router-LSA from multiple Link-State IDs
991 * When more than one router-LSA is received from a single router,
992 * the links are processed as if concatenated into a single LSA.*/
993 struct ospf6_lsa
*ospf6_create_single_router_lsa(struct ospf6_area
*area
,
994 struct ospf6_lsdb
*lsdb
,
997 struct ospf6_lsa
*lsa
= NULL
;
998 struct ospf6_lsa
*rtr_lsa
= NULL
;
999 struct ospf6_lsa_header
*lsa_header
= NULL
;
1000 uint8_t *new_header
= NULL
;
1001 const struct route_node
*end
= NULL
;
1002 uint16_t lsa_length
, total_lsa_length
= 0, num_lsa
= 0;
1005 uint32_t interface_id
;
1008 lsa_length
= sizeof(struct ospf6_lsa_header
)
1009 + sizeof(struct ospf6_router_lsa
);
1010 total_lsa_length
= lsa_length
;
1011 type
= htons(OSPF6_LSTYPE_ROUTER
);
1013 /* First check Aggregated LSA formed earlier in Cache */
1014 lsa
= ospf6_lsdb_lookup(type
, htonl(0), adv_router
,
1015 area
->temp_router_lsa_lsdb
);
1019 inet_ntop(AF_INET
, &adv_router
, ifbuf
, sizeof(ifbuf
));
1021 /* Determine total LSA length from all link state ids */
1022 end
= ospf6_lsdb_head(lsdb
, 2, type
, adv_router
, &rtr_lsa
);
1025 if (OSPF6_LSA_IS_MAXAGE(rtr_lsa
)) {
1026 rtr_lsa
= ospf6_lsdb_next(end
, rtr_lsa
);
1029 lsa_header
= rtr_lsa
->header
;
1030 total_lsa_length
+= (ntohs(lsa_header
->length
) - lsa_length
);
1032 rtr_lsa
= ospf6_lsdb_next(end
, rtr_lsa
);
1034 if (IS_OSPF6_DEBUG_SPF(PROCESS
))
1035 zlog_debug("%s: adv_router %s num_lsa %u to convert.", __func__
,
1041 if (IS_OSPF6_DEBUG_SPF(PROCESS
))
1042 zlog_debug("%s: adv_router %s not found in LSDB.",
1047 lsa
= ospf6_lsa_alloc(total_lsa_length
);
1048 new_header
= (uint8_t *)lsa
->header
;
1050 lsa
->lsdb
= area
->temp_router_lsa_lsdb
;
1052 /* Fill Larger LSA Payload */
1053 end
= ospf6_lsdb_head(lsdb
, 2, type
, adv_router
, &rtr_lsa
);
1056 * We assume at this point in time that rtr_lsa is
1060 if (!OSPF6_LSA_IS_MAXAGE(rtr_lsa
)) {
1061 /* Append first Link State ID LSA */
1062 lsa_header
= rtr_lsa
->header
;
1063 memcpy(new_header
, lsa_header
, ntohs(lsa_header
->length
));
1064 /* Assign new lsa length as aggregated length. */
1065 ((struct ospf6_lsa_header
*)new_header
)->length
=
1066 htons(total_lsa_length
);
1067 new_header
+= ntohs(lsa_header
->length
);
1071 /* Print LSA Name */
1072 ospf6_lsa_printbuf(lsa
, lsa
->name
, sizeof(lsa
->name
));
1074 rtr_lsa
= ospf6_lsdb_next(end
, rtr_lsa
);
1076 if (OSPF6_LSA_IS_MAXAGE(rtr_lsa
)) {
1077 rtr_lsa
= ospf6_lsdb_next(end
, rtr_lsa
);
1081 if (IS_OSPF6_DEBUG_SPF(PROCESS
)) {
1082 lsd
= OSPF6_LSA_HEADER_END(rtr_lsa
->header
) + 4;
1083 interface_id
= ROUTER_LSDESC_GET_IFID(lsd
);
1084 inet_ntop(AF_INET
, &interface_id
, ifbuf
, sizeof(ifbuf
));
1086 "%s: Next Router LSA %s to aggreat with len %u interface_id %s",
1087 __func__
, rtr_lsa
->name
,
1088 ntohs(lsa_header
->length
), ifbuf
);
1091 /* Append Next Link State ID LSA */
1092 lsa_header
= rtr_lsa
->header
;
1093 memcpy(new_header
, (OSPF6_LSA_HEADER_END(rtr_lsa
->header
) + 4),
1094 (ntohs(lsa_header
->length
) - lsa_length
));
1095 new_header
+= (ntohs(lsa_header
->length
) - lsa_length
);
1098 rtr_lsa
= ospf6_lsdb_next(end
, rtr_lsa
);
1101 /* Calculate birth of this lsa */
1102 ospf6_lsa_age_set(lsa
);
1104 /* Store Aggregated LSA into area temp lsdb */
1105 ospf6_lsdb_add(lsa
, area
->temp_router_lsa_lsdb
);
1107 if (IS_OSPF6_DEBUG_SPF(PROCESS
))
1108 zlog_debug("%s: LSA %s id %u type 0%x len %u num_lsa %u",
1109 __func__
, lsa
->name
, ntohl(lsa
->header
->id
),
1110 ntohs(lsa
->header
->type
), ntohs(lsa
->header
->length
),
1116 void ospf6_remove_temp_router_lsa(struct ospf6_area
*area
)
1118 struct ospf6_lsa
*lsa
= NULL
, *lsanext
;
1120 for (ALL_LSDB(area
->temp_router_lsa_lsdb
, lsa
, lsanext
)) {
1121 if (IS_OSPF6_DEBUG_SPF(PROCESS
))
1123 "%s Remove LSA %s lsa->lock %u lsdb count %u",
1124 __func__
, lsa
->name
, lsa
->lock
,
1125 area
->temp_router_lsa_lsdb
->count
);
1126 ospf6_lsdb_remove(lsa
, area
->temp_router_lsa_lsdb
);
1130 int ospf6_ase_calculate_route(struct ospf6
*ospf6
, struct ospf6_lsa
*lsa
,
1131 struct ospf6_area
*area
)
1133 struct ospf6_route
*route
;
1134 struct ospf6_as_external_lsa
*external
;
1135 struct prefix prefix
;
1136 void (*hook_add
)(struct ospf6_route
*) = NULL
;
1137 void (*hook_remove
)(struct ospf6_route
*) = NULL
;
1141 if (IS_OSPF6_DEBUG_SPF(PROCESS
))
1142 zlog_debug("%s : start", __func__
);
1144 if (ntohs(lsa
->header
->type
) == OSPF6_LSTYPE_TYPE_7
)
1145 if (IS_OSPF6_DEBUG_SPF(PROCESS
))
1146 zlog_debug("%s: Processing Type-7", __func__
);
1148 /* Stay away from any Local Translated Type-7 LSAs */
1149 if (CHECK_FLAG(lsa
->flag
, OSPF6_LSA_LOCAL_XLT
)) {
1150 if (IS_OSPF6_DEBUG_SPF(PROCESS
))
1151 zlog_debug("%s: Rejecting Local translated LSA",
1156 external
= (struct ospf6_as_external_lsa
*)OSPF6_LSA_HEADER_END(
1158 prefix
.family
= AF_INET6
;
1159 prefix
.prefixlen
= external
->prefix
.prefix_length
;
1160 ospf6_prefix_in6_addr(&prefix
.u
.prefix6
, external
, &external
->prefix
);
1162 if (ntohs(lsa
->header
->type
) == OSPF6_LSTYPE_AS_EXTERNAL
) {
1163 hook_add
= ospf6
->route_table
->hook_add
;
1164 hook_remove
= ospf6
->route_table
->hook_remove
;
1165 ospf6
->route_table
->hook_add
= NULL
;
1166 ospf6
->route_table
->hook_remove
= NULL
;
1168 if (!OSPF6_LSA_IS_MAXAGE(lsa
))
1169 ospf6_asbr_lsa_add(lsa
);
1171 ospf6
->route_table
->hook_add
= hook_add
;
1172 ospf6
->route_table
->hook_remove
= hook_remove
;
1174 route
= ospf6_route_lookup(&prefix
, ospf6
->route_table
);
1175 if (route
== NULL
) {
1176 if (IS_OSPF6_DEBUG_SPF(PROCESS
))
1177 zlog_debug("%s: no external route %pFX",
1182 if (CHECK_FLAG(route
->flag
, OSPF6_ROUTE_REMOVE
)
1183 && CHECK_FLAG(route
->flag
, OSPF6_ROUTE_ADD
)) {
1184 UNSET_FLAG(route
->flag
, OSPF6_ROUTE_REMOVE
);
1185 UNSET_FLAG(route
->flag
, OSPF6_ROUTE_ADD
);
1188 if (CHECK_FLAG(route
->flag
, OSPF6_ROUTE_REMOVE
))
1189 ospf6_route_remove(route
, ospf6
->route_table
);
1190 else if (CHECK_FLAG(route
->flag
, OSPF6_ROUTE_ADD
)
1191 || CHECK_FLAG(route
->flag
, OSPF6_ROUTE_CHANGE
)) {
1193 if (IS_OSPF6_DEBUG_SPF(PROCESS
))
1195 "%s: add external route %pFX",
1200 } else if (ntohs(lsa
->header
->type
) == OSPF6_LSTYPE_TYPE_7
) {
1201 hook_add
= area
->route_table
->hook_add
;
1202 hook_remove
= area
->route_table
->hook_remove
;
1203 area
->route_table
->hook_add
= NULL
;
1204 area
->route_table
->hook_remove
= NULL
;
1206 if (!OSPF6_LSA_IS_MAXAGE(lsa
))
1207 ospf6_asbr_lsa_add(lsa
);
1209 area
->route_table
->hook_add
= hook_add
;
1210 area
->route_table
->hook_remove
= hook_remove
;
1212 route
= ospf6_route_lookup(&prefix
, area
->route_table
);
1213 if (route
== NULL
) {
1214 if (IS_OSPF6_DEBUG_SPF(PROCESS
))
1215 zlog_debug("%s: no route %pFX, area %s",
1216 __func__
, &prefix
, area
->name
);
1220 if (CHECK_FLAG(route
->flag
, OSPF6_ROUTE_REMOVE
)
1221 && CHECK_FLAG(route
->flag
, OSPF6_ROUTE_ADD
)) {
1222 UNSET_FLAG(route
->flag
, OSPF6_ROUTE_REMOVE
);
1223 UNSET_FLAG(route
->flag
, OSPF6_ROUTE_ADD
);
1226 if (CHECK_FLAG(route
->flag
, OSPF6_ROUTE_REMOVE
)) {
1227 if (IS_OSPF6_DEBUG_SPF(PROCESS
))
1228 zlog_debug("%s : remove route %pFX, area %s",
1229 __func__
, &prefix
, area
->name
);
1230 ospf6_route_remove(route
, area
->route_table
);
1231 } else if (CHECK_FLAG(route
->flag
, OSPF6_ROUTE_ADD
)
1232 || CHECK_FLAG(route
->flag
, OSPF6_ROUTE_CHANGE
)) {
1234 if (IS_OSPF6_DEBUG_SPF(PROCESS
))
1236 "%s: add nssa route %pFX, area %s",
1237 __func__
, &prefix
, area
->name
);
1240 ospf6_abr_check_translate_nssa(area
, lsa
);
1246 static void ospf6_ase_calculate_timer(struct thread
*t
)
1248 struct ospf6
*ospf6
;
1249 struct ospf6_lsa
*lsa
;
1250 struct listnode
*node
, *nnode
;
1251 struct ospf6_area
*area
;
1254 ospf6
= THREAD_ARG(t
);
1256 /* Calculate external route for each AS-external-LSA */
1257 type
= htons(OSPF6_LSTYPE_AS_EXTERNAL
);
1258 for (ALL_LSDB_TYPED(ospf6
->lsdb
, type
, lsa
))
1259 ospf6_ase_calculate_route(ospf6
, lsa
, NULL
);
1261 /* This version simple adds to the table all NSSA areas */
1262 if (ospf6
->anyNSSA
) {
1263 for (ALL_LIST_ELEMENTS(ospf6
->area_list
, node
, nnode
, area
)) {
1264 if (IS_OSPF6_DEBUG_SPF(PROCESS
))
1265 zlog_debug("%s : looking at area %s", __func__
,
1268 type
= htons(OSPF6_LSTYPE_TYPE_7
);
1269 for (ALL_LSDB_TYPED(area
->lsdb
, type
, lsa
))
1270 ospf6_ase_calculate_route(ospf6
, lsa
, area
);
1274 if (ospf6
->gr_info
.finishing_restart
) {
1276 * The routing table computation is complete. Uninstall remnant
1277 * routes that were installed before the restart, but that are
1280 ospf6_zebra_gr_disable(ospf6
);
1281 ospf6
->gr_info
.finishing_restart
= false;
1285 void ospf6_ase_calculate_timer_add(struct ospf6
*ospf6
)
1290 thread_add_timer(master
, ospf6_ase_calculate_timer
, ospf6
,
1291 OSPF6_ASE_CALC_INTERVAL
, &ospf6
->t_ase_calc
);