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