2 * Area Border Router function.
3 * Copyright (C) 2004 Yasuhiro Ohara
5 * This file is part of GNU Zebra.
7 * GNU Zebra is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2, or (at your option) any
12 * GNU Zebra is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; see the file COPYING; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
34 #include "ospf6_proto.h"
35 #include "ospf6_route.h"
36 #include "ospf6_lsa.h"
37 #include "ospf6_route.h"
38 #include "ospf6_lsdb.h"
39 #include "ospf6_message.h"
40 #include "ospf6_zebra.h"
42 #include "ospf6_top.h"
43 #include "ospf6_area.h"
44 #include "ospf6_interface.h"
45 #include "ospf6_neighbor.h"
47 #include "ospf6_flood.h"
48 #include "ospf6_intra.h"
49 #include "ospf6_abr.h"
52 unsigned char conf_debug_ospf6_abr
;
54 int ospf6_is_router_abr(struct ospf6
*o
)
56 struct listnode
*node
;
57 struct ospf6_area
*oa
;
60 for (ALL_LIST_ELEMENTS_RO(o
->area_list
, node
, oa
))
61 if (IS_AREA_ENABLED(oa
))
69 static int ospf6_abr_nexthops_belong_to_area(struct ospf6_route
*route
,
70 struct ospf6_area
*area
)
72 struct ospf6_interface
*oi
;
74 oi
= ospf6_interface_lookup_by_ifindex(
75 ospf6_route_get_first_nh_index(route
));
76 if (oi
&& oi
->area
&& oi
->area
== area
)
82 static void ospf6_abr_delete_route(struct ospf6_route
*range
,
83 struct ospf6_route
*summary
,
84 struct ospf6_route_table
*summary_table
,
85 struct ospf6_lsa
*old
)
88 ospf6_route_remove(summary
, summary_table
);
91 if (old
&& !OSPF6_LSA_IS_MAXAGE(old
))
95 void ospf6_abr_enable_area(struct ospf6_area
*area
)
97 struct ospf6_area
*oa
;
98 struct listnode
*node
, *nnode
;
100 for (ALL_LIST_ELEMENTS(area
->ospf6
->area_list
, node
, nnode
, oa
))
101 /* update B bit for each area */
102 OSPF6_ROUTER_LSA_SCHEDULE(oa
);
105 void ospf6_abr_disable_area(struct ospf6_area
*area
)
107 struct ospf6_area
*oa
;
108 struct ospf6_route
*ro
, *nro
;
109 struct ospf6_lsa
*old
;
110 struct listnode
*node
, *nnode
;
112 /* Withdraw all summary prefixes previously originated */
113 for (ro
= ospf6_route_head(area
->summary_prefix
); ro
; ro
= nro
) {
114 nro
= ospf6_route_next(ro
);
115 old
= ospf6_lsdb_lookup(ro
->path
.origin
.type
,
117 area
->ospf6
->router_id
, area
->lsdb
);
119 ospf6_lsa_purge(old
);
120 ospf6_route_remove(ro
, area
->summary_prefix
);
123 /* Withdraw all summary router-routes previously originated */
124 for (ro
= ospf6_route_head(area
->summary_router
); ro
; ro
= nro
) {
125 nro
= ospf6_route_next(ro
);
126 old
= ospf6_lsdb_lookup(ro
->path
.origin
.type
,
128 area
->ospf6
->router_id
, area
->lsdb
);
130 ospf6_lsa_purge(old
);
131 ospf6_route_remove(ro
, area
->summary_router
);
134 /* Schedule Router-LSA for each area (ABR status may change) */
135 for (ALL_LIST_ELEMENTS(area
->ospf6
->area_list
, node
, nnode
, oa
))
136 /* update B bit for each area */
137 OSPF6_ROUTER_LSA_SCHEDULE(oa
);
140 /* RFC 2328 12.4.3. Summary-LSAs */
141 /* Returns 1 if a summary LSA has been generated for the area */
142 /* This is used by the area/range logic to add/remove blackhole routes */
143 int ospf6_abr_originate_summary_to_area(struct ospf6_route
*route
,
144 struct ospf6_area
*area
)
146 struct ospf6_lsa
*lsa
, *old
= NULL
;
147 struct ospf6_route
*summary
, *range
= NULL
;
148 struct ospf6_area
*route_area
;
149 char buffer
[OSPF6_MAX_LSASIZE
];
150 struct ospf6_lsa_header
*lsa_header
;
152 struct ospf6_inter_prefix_lsa
*prefix_lsa
;
153 struct ospf6_inter_router_lsa
*router_lsa
;
154 struct ospf6_route_table
*summary_table
= NULL
;
156 char buf
[PREFIX2STR_BUFFER
];
159 /* Only destination type network, range or ASBR are considered */
160 if (route
->type
!= OSPF6_DEST_TYPE_NETWORK
161 && route
->type
!= OSPF6_DEST_TYPE_RANGE
162 && ((route
->type
!= OSPF6_DEST_TYPE_ROUTER
)
163 || !CHECK_FLAG(route
->path
.router_bits
, OSPF6_ROUTER_BIT_E
))) {
166 "Route type is none of network, range nor ASBR, ignore");
171 /* AS External routes are never considered */
172 if (route
->path
.type
== OSPF6_PATH_TYPE_EXTERNAL1
173 || route
->path
.type
== OSPF6_PATH_TYPE_EXTERNAL2
) {
175 zlog_debug("Path type is external, skip");
180 /* do not generate if the path's area is the same as target area */
181 if (route
->path
.area_id
== area
->area_id
) {
183 zlog_debug("The route is in the area itself, ignore");
188 /* do not generate if the nexthops belongs to the target area */
189 if (ospf6_abr_nexthops_belong_to_area(route
, area
)) {
191 zlog_debug("The route's nexthop is in the same area, ignore");
196 if (route
->type
== OSPF6_DEST_TYPE_ROUTER
) {
197 if (ADV_ROUTER_IN_PREFIX(&route
->prefix
)
198 == area
->ospf6
->router_id
) {
200 &(ADV_ROUTER_IN_PREFIX(&route
->prefix
)), buf
,
203 "%s: Skipping ASBR announcement for ABR (%s)",
209 if (route
->type
== OSPF6_DEST_TYPE_ROUTER
) {
210 if (IS_OSPF6_DEBUG_ABR
211 || IS_OSPF6_DEBUG_ORIGINATE(INTER_ROUTER
)) {
214 &(ADV_ROUTER_IN_PREFIX(&route
->prefix
)), buf
,
216 zlog_debug("Originating summary in area %s for ASBR %s",
219 summary_table
= area
->summary_router
;
221 if (IS_OSPF6_DEBUG_ABR
222 || IS_OSPF6_DEBUG_ORIGINATE(INTER_PREFIX
))
225 if (route
->type
== OSPF6_DEST_TYPE_NETWORK
&&
226 route
->path
.origin
.type
==
227 htons(OSPF6_LSTYPE_INTER_PREFIX
)) {
228 if (!CHECK_FLAG(route
->flag
, OSPF6_ROUTE_BEST
)) {
231 &(ADV_ROUTER_IN_PREFIX(
232 &route
->prefix
)), buf
,
235 "%s: route %s with cost %u is not best, ignore."
236 , __PRETTY_FUNCTION__
, buf
,
243 if (route
->path
.origin
.type
==
244 htons(OSPF6_LSTYPE_INTRA_PREFIX
)) {
245 if (!CHECK_FLAG(route
->flag
, OSPF6_ROUTE_BEST
)) {
247 prefix2str(&route
->prefix
, buf
,
249 zlog_debug("%s: intra-prefix route %s with cost %u is not best, ignore."
250 , __PRETTY_FUNCTION__
, buf
,
258 prefix2str(&route
->prefix
, buf
, sizeof(buf
));
259 zlog_debug("Originating summary in area %s for %s cost %u",
260 area
->name
, buf
, route
->path
.cost
);
262 summary_table
= area
->summary_prefix
;
265 summary
= ospf6_route_lookup(&route
->prefix
, summary_table
);
267 old
= ospf6_lsdb_lookup(summary
->path
.origin
.type
,
268 summary
->path
.origin
.id
,
269 area
->ospf6
->router_id
, area
->lsdb
);
271 /* if this route has just removed, remove corresponding LSA */
272 if (CHECK_FLAG(route
->flag
, OSPF6_ROUTE_REMOVE
)) {
275 "The route has just removed, purge previous LSA");
277 if (route
->type
== OSPF6_DEST_TYPE_RANGE
) {
278 /* Whether the route have active longer prefix */
279 if (!CHECK_FLAG(route
->flag
,
280 OSPF6_ROUTE_ACTIVE_SUMMARY
)) {
283 "The range is not active. withdraw");
285 ospf6_abr_delete_route(route
, summary
,
289 ospf6_route_remove(summary
, summary_table
);
290 ospf6_lsa_purge(old
);
295 if ((route
->type
== OSPF6_DEST_TYPE_ROUTER
) && IS_AREA_STUB(area
)) {
298 "Area has been stubbed, purge Inter-Router LSA");
300 ospf6_abr_delete_route(route
, summary
, summary_table
, old
);
305 && (route
->path
.subtype
!= OSPF6_PATH_SUBTYPE_DEFAULT_RT
)) {
307 zlog_debug("Area has been stubbed, purge prefix LSA");
309 ospf6_abr_delete_route(route
, summary
, summary_table
, old
);
313 /* do not generate if the route cost is greater or equal to LSInfinity
315 if (route
->path
.cost
>= OSPF_LS_INFINITY
) {
316 /* When we're clearing the range route because all active
318 * under the range are gone, we set the range's cost to
319 * OSPF_AREA_RANGE_COST_UNSPEC, which is > OSPF_LS_INFINITY. We
320 * don't want to trigger the code here for that. This code is
322 * handling routes that have gone to infinity. The range removal
326 if ((route
->type
!= OSPF6_DEST_TYPE_RANGE
)
327 && (route
->path
.cost
!= OSPF_AREA_RANGE_COST_UNSPEC
)) {
330 "The cost exceeds LSInfinity, withdraw");
332 ospf6_lsa_purge(old
);
337 /* if this is a route to ASBR */
338 if (route
->type
== OSPF6_DEST_TYPE_ROUTER
) {
339 /* Only the preferred best path is considered */
340 if (!CHECK_FLAG(route
->flag
, OSPF6_ROUTE_BEST
)) {
343 "This is the secondary path to the ASBR, ignore");
344 ospf6_abr_delete_route(route
, summary
, summary_table
,
349 /* Do not generate if the area is stub */
353 /* if this is an intra-area route, this may be suppressed by aggregation
355 if (route
->type
== OSPF6_DEST_TYPE_NETWORK
356 && route
->path
.type
== OSPF6_PATH_TYPE_INTRA
) {
357 /* search for configured address range for the route's area */
359 ospf6_area_lookup(route
->path
.area_id
, area
->ospf6
);
361 range
= ospf6_route_lookup_bestmatch(&route
->prefix
,
362 route_area
->range_table
);
364 /* ranges are ignored when originate backbone routes to transit
366 Otherwise, if ranges are configured, the route is suppressed.
368 if (range
&& !CHECK_FLAG(range
->flag
, OSPF6_ROUTE_REMOVE
)
369 && (route
->path
.area_id
!= OSPF_AREA_BACKBONE
370 || !IS_AREA_TRANSIT(area
))) {
372 prefix2str(&range
->prefix
, buf
, sizeof(buf
));
373 zlog_debug("Suppressed by range %s of area %s",
374 buf
, route_area
->name
);
376 ospf6_abr_delete_route(route
, summary
, summary_table
,
382 /* If this is a configured address range */
383 if (route
->type
== OSPF6_DEST_TYPE_RANGE
) {
384 /* If DoNotAdvertise is set */
385 if (CHECK_FLAG(route
->flag
, OSPF6_ROUTE_DO_NOT_ADVERTISE
)) {
388 "This is the range with DoNotAdvertise set. ignore");
389 ospf6_abr_delete_route(route
, summary
, summary_table
,
394 /* If there are no active prefixes in this range, remove */
395 if (!CHECK_FLAG(route
->flag
, OSPF6_ROUTE_ACTIVE_SUMMARY
)) {
397 zlog_debug("The range is not active. withdraw");
398 ospf6_abr_delete_route(route
, summary
, summary_table
,
404 /* Check export list */
405 if (EXPORT_NAME(area
)) {
406 if (EXPORT_LIST(area
) == NULL
)
408 access_list_lookup(AFI_IP6
, EXPORT_NAME(area
));
410 if (EXPORT_LIST(area
))
411 if (access_list_apply(EXPORT_LIST(area
), &route
->prefix
)
415 &(ADV_ROUTER_IN_PREFIX(
419 "prefix %s was denied by export list",
426 /* Check filter-list */
427 if (PREFIX_LIST_OUT(area
))
428 if (prefix_list_apply(PREFIX_LIST_OUT(area
), &route
->prefix
)
433 &(ADV_ROUTER_IN_PREFIX(&route
->prefix
)),
436 "prefix %s was denied by filter-list out",
442 /* the route is going to be originated. store it in area's summary_table
444 if (summary
== NULL
) {
445 summary
= ospf6_route_copy(route
);
446 summary
->path
.origin
.adv_router
= area
->ospf6
->router_id
;
448 if (route
->type
== OSPF6_DEST_TYPE_ROUTER
) {
449 summary
->path
.origin
.type
=
450 htons(OSPF6_LSTYPE_INTER_ROUTER
);
451 summary
->path
.origin
.id
=
452 ADV_ROUTER_IN_PREFIX(&route
->prefix
);
454 summary
->path
.origin
.type
=
455 htons(OSPF6_LSTYPE_INTER_PREFIX
);
456 summary
->path
.origin
.id
= ospf6_new_ls_id(
457 summary
->path
.origin
.type
,
458 summary
->path
.origin
.adv_router
, area
->lsdb
);
460 summary
= ospf6_route_add(summary
, summary_table
);
462 summary
->type
= route
->type
;
463 monotime(&summary
->changed
);
466 summary
->path
.router_bits
= route
->path
.router_bits
;
467 summary
->path
.options
[0] = route
->path
.options
[0];
468 summary
->path
.options
[1] = route
->path
.options
[1];
469 summary
->path
.options
[2] = route
->path
.options
[2];
470 summary
->path
.prefix_options
= route
->path
.prefix_options
;
471 summary
->path
.area_id
= area
->area_id
;
472 summary
->path
.type
= OSPF6_PATH_TYPE_INTER
;
473 summary
->path
.subtype
= route
->path
.subtype
;
474 summary
->path
.cost
= route
->path
.cost
;
475 /* summary->nexthop[0] = route->nexthop[0]; */
478 memset(buffer
, 0, sizeof(buffer
));
479 lsa_header
= (struct ospf6_lsa_header
*)buffer
;
481 if (route
->type
== OSPF6_DEST_TYPE_ROUTER
) {
482 router_lsa
= (struct ospf6_inter_router_lsa
483 *)((caddr_t
)lsa_header
484 + sizeof(struct ospf6_lsa_header
));
485 p
= (caddr_t
)router_lsa
+ sizeof(struct ospf6_inter_router_lsa
);
487 /* Fill Inter-Area-Router-LSA */
488 router_lsa
->options
[0] = route
->path
.options
[0];
489 router_lsa
->options
[1] = route
->path
.options
[1];
490 router_lsa
->options
[2] = route
->path
.options
[2];
491 OSPF6_ABR_SUMMARY_METRIC_SET(router_lsa
, route
->path
.cost
);
492 router_lsa
->router_id
= ADV_ROUTER_IN_PREFIX(&route
->prefix
);
493 type
= htons(OSPF6_LSTYPE_INTER_ROUTER
);
495 prefix_lsa
= (struct ospf6_inter_prefix_lsa
496 *)((caddr_t
)lsa_header
497 + sizeof(struct ospf6_lsa_header
));
498 p
= (caddr_t
)prefix_lsa
+ sizeof(struct ospf6_inter_prefix_lsa
);
500 /* Fill Inter-Area-Prefix-LSA */
501 OSPF6_ABR_SUMMARY_METRIC_SET(prefix_lsa
, route
->path
.cost
);
502 prefix_lsa
->prefix
.prefix_length
= route
->prefix
.prefixlen
;
503 prefix_lsa
->prefix
.prefix_options
= route
->path
.prefix_options
;
506 memcpy(p
, &route
->prefix
.u
.prefix6
,
507 OSPF6_PREFIX_SPACE(route
->prefix
.prefixlen
));
508 ospf6_prefix_apply_mask(&prefix_lsa
->prefix
);
509 p
+= OSPF6_PREFIX_SPACE(route
->prefix
.prefixlen
);
510 type
= htons(OSPF6_LSTYPE_INTER_PREFIX
);
513 /* Fill LSA Header */
515 lsa_header
->type
= type
;
516 lsa_header
->id
= summary
->path
.origin
.id
;
517 lsa_header
->adv_router
= area
->ospf6
->router_id
;
519 ospf6_new_ls_seqnum(lsa_header
->type
, lsa_header
->id
,
520 lsa_header
->adv_router
, area
->lsdb
);
521 lsa_header
->length
= htons((caddr_t
)p
- (caddr_t
)lsa_header
);
524 ospf6_lsa_checksum(lsa_header
);
527 lsa
= ospf6_lsa_create(lsa_header
);
530 ospf6_lsa_originate_area(lsa
, area
);
535 void ospf6_abr_range_reset_cost(struct ospf6
*ospf6
)
537 struct listnode
*node
, *nnode
;
538 struct ospf6_area
*oa
;
539 struct ospf6_route
*range
;
541 for (ALL_LIST_ELEMENTS(ospf6
->area_list
, node
, nnode
, oa
))
542 for (range
= ospf6_route_head(oa
->range_table
); range
;
543 range
= ospf6_route_next(range
))
544 OSPF6_ABR_RANGE_CLEAR_COST(range
);
547 static inline uint32_t ospf6_abr_range_compute_cost(struct ospf6_route
*range
,
550 struct ospf6_route
*ro
;
553 for (ro
= ospf6_route_match_head(&range
->prefix
, o
->route_table
); ro
;
554 ro
= ospf6_route_match_next(&range
->prefix
, ro
)) {
555 if (ro
->path
.area_id
== range
->path
.area_id
556 && (ro
->path
.type
== OSPF6_PATH_TYPE_INTRA
)
557 && !CHECK_FLAG(ro
->flag
, OSPF6_ROUTE_REMOVE
))
558 cost
= MAX(cost
, ro
->path
.cost
);
565 ospf6_abr_range_summary_needs_update(struct ospf6_route
*range
, uint32_t cost
)
567 int redo_summary
= 0;
569 if (CHECK_FLAG(range
->flag
, OSPF6_ROUTE_REMOVE
)) {
570 UNSET_FLAG(range
->flag
, OSPF6_ROUTE_ACTIVE_SUMMARY
);
572 } else if (CHECK_FLAG(range
->flag
, OSPF6_ROUTE_DO_NOT_ADVERTISE
)) {
573 if (range
->path
.cost
!= 0) {
574 range
->path
.cost
= 0;
578 if ((OSPF6_PATH_COST_IS_CONFIGURED(range
->path
)
579 && range
->path
.cost
!= range
->path
.u
.cost_config
)) {
580 range
->path
.cost
= range
->path
.u
.cost_config
;
581 SET_FLAG(range
->flag
, OSPF6_ROUTE_ACTIVE_SUMMARY
);
583 } else if (!OSPF6_PATH_COST_IS_CONFIGURED(range
->path
)
584 && range
->path
.cost
!= cost
) {
585 range
->path
.cost
= cost
;
586 SET_FLAG(range
->flag
, OSPF6_ROUTE_ACTIVE_SUMMARY
);
589 } else if (CHECK_FLAG(range
->flag
, OSPF6_ROUTE_ACTIVE_SUMMARY
)) {
590 /* Cost is zero, meaning no active range */
591 UNSET_FLAG(range
->flag
, OSPF6_ROUTE_ACTIVE_SUMMARY
);
592 range
->path
.cost
= OSPF_AREA_RANGE_COST_UNSPEC
;
596 return (redo_summary
);
599 static void ospf6_abr_range_update(struct ospf6_route
*range
)
602 struct listnode
*node
, *nnode
;
603 struct ospf6_area
*oa
;
604 int summary_orig
= 0;
606 assert(range
->type
== OSPF6_DEST_TYPE_RANGE
);
608 /* update range's cost and active flag */
609 cost
= ospf6_abr_range_compute_cost(range
, ospf6
);
611 /* Non-zero cost is a proxy for active longer prefixes in this range.
612 * If there are active routes covered by this range AND either the
614 * cost has changed or the summarized cost has changed then redo
616 * Alternately, if there are no longer active prefixes and there are
617 * summary announcements, withdraw those announcements.
619 * The don't advertise code relies on the path.cost being set to UNSPEC
621 * work the first time. Subsequent times the path.cost is not 0 anyway
623 * were active ranges.
626 if (ospf6_abr_range_summary_needs_update(range
, cost
)) {
627 for (ALL_LIST_ELEMENTS(ospf6
->area_list
, node
, nnode
, oa
))
629 ospf6_abr_originate_summary_to_area(range
, oa
);
631 if (CHECK_FLAG(range
->flag
, OSPF6_ROUTE_ACTIVE_SUMMARY
)
633 if (!CHECK_FLAG(range
->flag
,
634 OSPF6_ROUTE_BLACKHOLE_ADDED
)) {
635 if (IS_OSPF6_DEBUG_ABR
)
636 zlog_debug("Add discard route");
638 ospf6_zebra_add_discard(range
);
641 /* Summary removed or no summary generated as no
643 if (CHECK_FLAG(range
->flag
,
644 OSPF6_ROUTE_BLACKHOLE_ADDED
)) {
645 if (IS_OSPF6_DEBUG_ABR
)
646 zlog_debug("Delete discard route");
648 ospf6_zebra_delete_discard(range
);
654 void ospf6_abr_originate_summary(struct ospf6_route
*route
)
656 struct listnode
*node
, *nnode
;
657 struct ospf6_area
*oa
;
658 struct ospf6_route
*range
= NULL
;
660 if (route
->type
== OSPF6_DEST_TYPE_NETWORK
) {
661 oa
= ospf6_area_lookup(route
->path
.area_id
, ospf6
);
663 zlog_err("OSPFv6 area lookup failed");
667 range
= ospf6_route_lookup_bestmatch(&route
->prefix
,
670 ospf6_abr_range_update(range
);
674 for (ALL_LIST_ELEMENTS(ospf6
->area_list
, node
, nnode
, oa
))
675 ospf6_abr_originate_summary_to_area(route
, oa
);
678 void ospf6_abr_defaults_to_stub(struct ospf6
*o
)
680 struct listnode
*node
, *nnode
;
681 struct ospf6_area
*oa
;
682 struct ospf6_route
*def
, *route
;
687 def
= ospf6_route_create();
688 def
->type
= OSPF6_DEST_TYPE_NETWORK
;
689 def
->prefix
.family
= AF_INET6
;
690 def
->prefix
.prefixlen
= 0;
691 memset(&def
->prefix
.u
.prefix6
, 0, sizeof(struct in6_addr
));
692 def
->type
= OSPF6_DEST_TYPE_NETWORK
;
693 def
->path
.type
= OSPF6_PATH_TYPE_INTER
;
694 def
->path
.subtype
= OSPF6_PATH_SUBTYPE_DEFAULT_RT
;
695 def
->path
.area_id
= o
->backbone
->area_id
;
697 for (ALL_LIST_ELEMENTS(ospf6
->area_list
, node
, nnode
, oa
)) {
698 if (!IS_AREA_STUB(oa
)) {
699 /* withdraw defaults when an area switches from stub to
701 route
= ospf6_route_lookup(&def
->prefix
,
704 && (route
->path
.subtype
== def
->path
.subtype
)) {
705 if (IS_OSPF6_DEBUG_ABR
)
707 "Withdrawing default route from non-stubby area %s",
709 SET_FLAG(def
->flag
, OSPF6_ROUTE_REMOVE
);
710 ospf6_abr_originate_summary_to_area(def
, oa
);
713 /* announce defaults to stubby areas */
714 if (IS_OSPF6_DEBUG_ABR
)
716 "Announcing default route into stubby area %s",
718 UNSET_FLAG(def
->flag
, OSPF6_ROUTE_REMOVE
);
719 ospf6_abr_originate_summary_to_area(def
, oa
);
722 ospf6_route_delete(def
);
725 void ospf6_abr_old_path_update(struct ospf6_route
*old_route
,
726 struct ospf6_route
*route
,
727 struct ospf6_route_table
*table
)
729 struct ospf6_path
*o_path
= NULL
;
730 struct listnode
*anode
, *anext
;
731 struct listnode
*nnode
, *rnode
, *rnext
;
732 struct ospf6_nexthop
*nh
, *rnh
;
734 for (ALL_LIST_ELEMENTS(old_route
->paths
, anode
, anext
, o_path
)) {
735 if (o_path
->area_id
!= route
->path
.area_id
||
736 (memcmp(&(o_path
)->origin
, &(route
)->path
.origin
,
737 sizeof(struct ospf6_ls_origin
)) != 0))
740 if ((o_path
->cost
== route
->path
.cost
) &&
741 (o_path
->u
.cost_e2
== route
->path
.u
.cost_e2
))
744 for (ALL_LIST_ELEMENTS_RO(o_path
->nh_list
, nnode
, nh
)) {
745 for (ALL_LIST_ELEMENTS(old_route
->nh_list
, rnode
,
747 if (!ospf6_nexthop_is_same(rnh
, nh
))
749 listnode_delete(old_route
->nh_list
, rnh
);
750 ospf6_nexthop_delete(rnh
);
755 listnode_delete(old_route
->paths
, o_path
);
756 ospf6_path_free(o_path
);
758 for (ALL_LIST_ELEMENTS(old_route
->paths
, anode
,
760 ospf6_merge_nexthops(old_route
->nh_list
,
764 if (IS_OSPF6_DEBUG_ABR
|| IS_OSPF6_DEBUG_EXAMIN(INTER_PREFIX
))
765 zlog_debug("%s: paths %u nh %u", __PRETTY_FUNCTION__
,
767 listcount(old_route
->paths
) : 0,
769 listcount(old_route
->nh_list
) : 0);
772 (*table
->hook_add
)(old_route
);
774 if (old_route
->path
.origin
.id
== route
->path
.origin
.id
&&
775 old_route
->path
.origin
.adv_router
==
776 route
->path
.origin
.adv_router
) {
777 struct ospf6_path
*h_path
;
779 h_path
= (struct ospf6_path
*)
780 listgetdata(listhead(old_route
->paths
));
781 old_route
->path
.origin
.type
= h_path
->origin
.type
;
782 old_route
->path
.origin
.id
= h_path
->origin
.id
;
783 old_route
->path
.origin
.adv_router
=
784 h_path
->origin
.adv_router
;
789 void ospf6_abr_old_route_remove(struct ospf6_lsa
*lsa
,
790 struct ospf6_route
*old
,
791 struct ospf6_route_table
*table
)
793 if (listcount(old
->paths
) > 1) {
794 struct listnode
*anode
, *anext
, *nnode
, *rnode
, *rnext
;
795 struct ospf6_path
*o_path
;
796 struct ospf6_nexthop
*nh
, *rnh
;
797 bool nh_updated
= false;
798 char buf
[PREFIX2STR_BUFFER
];
800 for (ALL_LIST_ELEMENTS(old
->paths
, anode
, anext
, o_path
)) {
801 if (o_path
->origin
.adv_router
!= lsa
->header
->adv_router
802 && o_path
->origin
.id
!= lsa
->header
->id
)
804 for (ALL_LIST_ELEMENTS_RO(o_path
->nh_list
, nnode
, nh
)) {
805 for (ALL_LIST_ELEMENTS(old
->nh_list
,
806 rnode
, rnext
, rnh
)) {
807 if (!ospf6_nexthop_is_same(rnh
, nh
))
809 listnode_delete(old
->nh_list
, rnh
);
810 ospf6_nexthop_delete(rnh
);
813 listnode_delete(old
->paths
, o_path
);
814 ospf6_path_free(o_path
);
819 if (listcount(old
->paths
)) {
820 if (IS_OSPF6_DEBUG_ABR
||
821 IS_OSPF6_DEBUG_EXAMIN(INTER_PREFIX
)) {
822 prefix2str(&old
->prefix
, buf
,
824 zlog_debug("%s: old %s updated nh %u",
825 __PRETTY_FUNCTION__
, buf
,
827 listcount(old
->nh_list
) : 0);
831 (*table
->hook_add
)(old
);
833 if ((old
->path
.origin
.id
== lsa
->header
->id
) &&
834 (old
->path
.origin
.adv_router
835 == lsa
->header
->adv_router
)) {
836 struct ospf6_path
*h_path
;
838 h_path
= (struct ospf6_path
*)
840 listhead(old
->paths
));
841 old
->path
.origin
.type
=
843 old
->path
.origin
.id
= h_path
->origin
.id
;
844 old
->path
.origin
.adv_router
=
845 h_path
->origin
.adv_router
;
848 ospf6_route_remove(old
, table
);
851 ospf6_route_remove(old
, table
);
855 /* RFC 2328 16.2. Calculating the inter-area routes */
856 void ospf6_abr_examin_summary(struct ospf6_lsa
*lsa
, struct ospf6_area
*oa
)
858 struct prefix prefix
, abr_prefix
;
859 struct ospf6_route_table
*table
= NULL
;
860 struct ospf6_route
*range
, *route
, *old
= NULL
, *old_route
;
861 struct ospf6_route
*abr_entry
;
863 char options
[3] = {0, 0, 0};
864 uint8_t prefix_options
= 0;
866 uint8_t router_bits
= 0;
867 char buf
[PREFIX2STR_BUFFER
];
869 struct ospf6_inter_prefix_lsa
*prefix_lsa
= NULL
;
870 struct ospf6_inter_router_lsa
*router_lsa
= NULL
;
871 bool old_entry_updated
= false;
872 struct ospf6_path
*path
, *o_path
, *ecmp_path
;
873 struct listnode
*anode
;
876 memset(&prefix
, 0, sizeof(prefix
));
878 if (lsa
->header
->type
== htons(OSPF6_LSTYPE_INTER_PREFIX
)) {
879 if (IS_OSPF6_DEBUG_EXAMIN(INTER_PREFIX
)) {
881 zlog_debug("%s: Examin %s in area %s",
882 __PRETTY_FUNCTION__
, lsa
->name
, oa
->name
);
886 (struct ospf6_inter_prefix_lsa
*)OSPF6_LSA_HEADER_END(
888 prefix
.family
= AF_INET6
;
889 prefix
.prefixlen
= prefix_lsa
->prefix
.prefix_length
;
890 ospf6_prefix_in6_addr(&prefix
.u
.prefix6
, prefix_lsa
,
891 &prefix_lsa
->prefix
);
893 prefix2str(&prefix
, buf
, sizeof(buf
));
894 table
= oa
->ospf6
->route_table
;
895 type
= OSPF6_DEST_TYPE_NETWORK
;
896 prefix_options
= prefix_lsa
->prefix
.prefix_options
;
897 cost
= OSPF6_ABR_SUMMARY_METRIC(prefix_lsa
);
898 } else if (lsa
->header
->type
== htons(OSPF6_LSTYPE_INTER_ROUTER
)) {
899 if (IS_OSPF6_DEBUG_EXAMIN(INTER_ROUTER
)) {
901 zlog_debug("%s: Examin %s in area %s",
902 __PRETTY_FUNCTION__
, lsa
->name
, oa
->name
);
906 (struct ospf6_inter_router_lsa
*)OSPF6_LSA_HEADER_END(
908 ospf6_linkstate_prefix(router_lsa
->router_id
, htonl(0),
911 inet_ntop(AF_INET
, &router_lsa
->router_id
, buf
,
914 table
= oa
->ospf6
->brouter_table
;
915 type
= OSPF6_DEST_TYPE_ROUTER
;
916 options
[0] = router_lsa
->options
[0];
917 options
[1] = router_lsa
->options
[1];
918 options
[2] = router_lsa
->options
[2];
919 cost
= OSPF6_ABR_SUMMARY_METRIC(router_lsa
);
920 SET_FLAG(router_bits
, OSPF6_ROUTER_BIT_E
);
924 /* Find existing route */
925 route
= ospf6_route_lookup(&prefix
, table
);
927 ospf6_route_lock(route
);
928 while (route
&& ospf6_route_is_prefix(&prefix
, route
)) {
929 if (route
->path
.area_id
== oa
->area_id
930 && route
->path
.origin
.type
== lsa
->header
->type
931 && !CHECK_FLAG(route
->flag
, OSPF6_ROUTE_WAS_REMOVED
)) {
932 /* LSA adv. router could be part of route's
933 * paths list. Find the existing path and set
936 if (listcount(route
->paths
) > 1) {
937 for (ALL_LIST_ELEMENTS_RO(route
->paths
, anode
,
940 &o_path
->origin
.adv_router
,
943 if (o_path
->origin
.id
== lsa
->header
->id
944 && o_path
->origin
.adv_router
==
945 lsa
->header
->adv_router
) {
949 zlog_debug("%s: old entry found in paths, adv_router %s",
956 } else if (route
->path
.origin
.id
== lsa
->header
->id
&&
957 route
->path
.origin
.adv_router
==
958 lsa
->header
->adv_router
)
961 route
= ospf6_route_next(route
);
964 ospf6_route_unlock(route
);
966 /* (1) if cost == LSInfinity or if the LSA is MaxAge */
967 if (cost
== OSPF_LS_INFINITY
) {
969 zlog_debug("cost is LS_INFINITY, ignore");
971 ospf6_abr_old_route_remove(lsa
, old
, table
);
974 if (OSPF6_LSA_IS_MAXAGE(lsa
)) {
976 zlog_debug("%s: LSA %s is MaxAge, ignore",
977 __PRETTY_FUNCTION__
, lsa
->name
);
979 ospf6_abr_old_route_remove(lsa
, old
, table
);
983 /* (2) if the LSA is self-originated, ignore */
984 if (lsa
->header
->adv_router
== oa
->ospf6
->router_id
) {
986 zlog_debug("LSA %s is self-originated, ignore",
989 ospf6_route_remove(old
, table
);
993 /* (3) if the prefix is equal to an active configured address range */
994 /* or if the NU bit is set in the prefix */
995 if (lsa
->header
->type
== htons(OSPF6_LSTYPE_INTER_PREFIX
)) {
996 /* must have been set in previous block */
999 range
= ospf6_route_lookup(&prefix
, oa
->range_table
);
1003 "Prefix is equal to address range, ignore");
1005 ospf6_route_remove(old
, table
);
1009 if (CHECK_FLAG(prefix_lsa
->prefix
.prefix_options
,
1010 OSPF6_PREFIX_OPTION_NU
)
1011 || CHECK_FLAG(prefix_lsa
->prefix
.prefix_options
,
1012 OSPF6_PREFIX_OPTION_LA
)) {
1014 zlog_debug("Prefix has NU/LA bit set, ignore");
1016 ospf6_route_remove(old
, table
);
1021 if (lsa
->header
->type
== htons(OSPF6_LSTYPE_INTER_ROUTER
)) {
1022 /* To pass test suites */
1024 if (!OSPF6_OPT_ISSET(router_lsa
->options
, OSPF6_OPT_R
)
1025 || !OSPF6_OPT_ISSET(router_lsa
->options
, OSPF6_OPT_V6
)) {
1027 zlog_debug("Prefix has NU/LA bit set, ignore");
1029 ospf6_route_remove(old
, table
);
1033 /* Avoid infinite recursion if someone has maliciously announced
1035 Inter-Router LSA for an ABR
1037 if (lsa
->header
->adv_router
== router_lsa
->router_id
) {
1040 "Ignorning Inter-Router LSA for an ABR (%s)",
1043 ospf6_route_remove(old
, table
);
1049 /* (4) if the routing table entry for the ABR does not exist */
1050 ospf6_linkstate_prefix(lsa
->header
->adv_router
, htonl(0), &abr_prefix
);
1051 abr_entry
= ospf6_route_lookup(&abr_prefix
, oa
->ospf6
->brouter_table
);
1052 if (abr_entry
== NULL
|| abr_entry
->path
.area_id
!= oa
->area_id
1053 || CHECK_FLAG(abr_entry
->flag
, OSPF6_ROUTE_REMOVE
)
1054 || !CHECK_FLAG(abr_entry
->path
.router_bits
, OSPF6_ROUTER_BIT_B
)) {
1056 zlog_debug("%s: ABR router entry does not exist, ignore",
1057 __PRETTY_FUNCTION__
);
1059 if (old
->type
== OSPF6_DEST_TYPE_ROUTER
&&
1060 oa
->intra_brouter_calc
) {
1063 "%s: intra_brouter_calc is on, skip brouter remove: %s (%p)",
1064 __PRETTY_FUNCTION__
, buf
,
1068 zlog_debug("%s: remove old entry: %s %p ",
1069 __PRETTY_FUNCTION__
, buf
,
1071 ospf6_route_remove(old
, table
);
1077 /* Check import list */
1078 if (IMPORT_NAME(oa
)) {
1079 if (IMPORT_LIST(oa
) == NULL
)
1081 access_list_lookup(AFI_IP6
, IMPORT_NAME(oa
));
1083 if (IMPORT_LIST(oa
))
1084 if (access_list_apply(IMPORT_LIST(oa
), &prefix
)
1088 "Prefix was denied by import-list");
1090 ospf6_route_remove(old
, table
);
1095 /* Check input prefix-list */
1096 if (PREFIX_LIST_IN(oa
)) {
1097 if (prefix_list_apply(PREFIX_LIST_IN(oa
), &prefix
)
1100 zlog_debug("Prefix was denied by prefix-list");
1102 ospf6_route_remove(old
, table
);
1107 /* (5),(6): the path preference is handled by the sorting
1108 in the routing table. Always install the path by substituting
1109 old route (if any). */
1110 route
= ospf6_route_create();
1113 route
->prefix
= prefix
;
1114 route
->path
.origin
.type
= lsa
->header
->type
;
1115 route
->path
.origin
.id
= lsa
->header
->id
;
1116 route
->path
.origin
.adv_router
= lsa
->header
->adv_router
;
1117 route
->path
.router_bits
= router_bits
;
1118 route
->path
.options
[0] = options
[0];
1119 route
->path
.options
[1] = options
[1];
1120 route
->path
.options
[2] = options
[2];
1121 route
->path
.prefix_options
= prefix_options
;
1122 route
->path
.area_id
= oa
->area_id
;
1123 route
->path
.type
= OSPF6_PATH_TYPE_INTER
;
1124 route
->path
.cost
= abr_entry
->path
.cost
+ cost
;
1126 /* copy brouter rechable nexthops into the route. */
1127 ospf6_route_copy_nexthops(route
, abr_entry
);
1129 /* (7) If the routes are identical, copy the next hops over to existing
1130 route. ospf6's route table implementation will otherwise string both
1131 routes, but keep the older one as the best route since the routes
1134 old
= ospf6_route_lookup(&prefix
, table
);
1136 for (old_route
= old
; old_route
; old_route
= old_route
->next
) {
1137 if (!ospf6_route_is_same(old_route
, route
) ||
1138 (old_route
->type
!= route
->type
) ||
1139 (old_route
->path
.type
!= route
->path
.type
))
1142 if ((ospf6_route_cmp(route
, old_route
) != 0)) {
1144 prefix2str(&prefix
, buf
, sizeof(buf
));
1145 zlog_debug("%s: old %p %s cost %u new route cost %u are not same",
1146 __PRETTY_FUNCTION__
,
1147 (void *)old_route
, buf
,
1148 old_route
->path
.cost
,
1152 /* Check new route's adv. router is same in one of
1153 * the paths with differed cost, if so remove the
1154 * old path as later new route will be added.
1156 if (listcount(old_route
->paths
) > 1)
1157 ospf6_abr_old_path_update(old_route
, route
,
1162 ospf6_route_merge_nexthops(old_route
, route
);
1163 old_entry_updated
= true;
1165 for (ALL_LIST_ELEMENTS_RO(old_route
->paths
, anode
,
1167 if (o_path
->area_id
== route
->path
.area_id
&&
1168 (memcmp(&(o_path
)->origin
, &(route
)->path
.origin
,
1169 sizeof(struct ospf6_ls_origin
)) == 0))
1173 /* New adv. router for a existing path add to paths list */
1174 if (o_path
== NULL
) {
1175 ecmp_path
= ospf6_path_dup(&route
->path
);
1177 /* Add a nh_list to new ecmp path */
1178 ospf6_copy_nexthops(ecmp_path
->nh_list
, route
->nh_list
);
1180 /* Add the new path to route's path list */
1181 listnode_add_sort(old_route
->paths
, ecmp_path
);
1184 prefix2str(&route
->prefix
, buf
, sizeof(buf
));
1186 &ecmp_path
->origin
.adv_router
,
1187 adv_router
, sizeof(adv_router
));
1188 zlog_debug("%s: route %s cost %u another path %s added with nh %u, effective paths %u nh %u",
1189 __PRETTY_FUNCTION__
, buf
,
1190 old_route
->path
.cost
,
1192 listcount(ecmp_path
->nh_list
),
1194 listcount(old_route
->paths
) : 0,
1195 listcount(old_route
->nh_list
));
1198 /* adv. router exists in the list, update the nhs */
1199 list_delete_all_node(o_path
->nh_list
);
1200 ospf6_copy_nexthops(o_path
->nh_list
, route
->nh_list
);
1204 zlog_debug("%s: Update route: %s %p old cost %u new cost %u nh %u",
1205 __PRETTY_FUNCTION__
, buf
, (void *)old_route
,
1206 old_route
->path
.cost
, route
->path
.cost
,
1207 listcount(route
->nh_list
));
1209 /* For Inter-Prefix route: Update RIB/FIB,
1210 * For Inter-Router trigger summary update
1212 if (table
->hook_add
)
1213 (*table
->hook_add
)(old_route
);
1215 /* Delete new route */
1216 ospf6_route_delete(route
);
1220 if (old_entry_updated
== false) {
1222 inet_ntop(AF_INET
, &route
->path
.origin
.adv_router
,
1223 adv_router
, sizeof(adv_router
));
1224 zlog_debug("%s: Install route: %s cost %u nh %u adv_router %s ",
1225 __PRETTY_FUNCTION__
, buf
, route
->path
.cost
,
1226 listcount(route
->nh_list
), adv_router
);
1229 path
= ospf6_path_dup(&route
->path
);
1230 ospf6_copy_nexthops(path
->nh_list
, abr_entry
->nh_list
);
1231 listnode_add_sort(route
->paths
, path
);
1233 /* ospf6_ia_add_nw_route (table, &prefix, route); */
1234 ospf6_route_add(route
, table
);
1238 void ospf6_abr_examin_brouter(uint32_t router_id
)
1240 struct ospf6_lsa
*lsa
;
1241 struct ospf6_area
*oa
;
1244 if (ospf6_is_router_abr(ospf6
))
1245 oa
= ospf6
->backbone
;
1247 oa
= listgetdata(listhead(ospf6
->area_list
));
1250 * It is possible to designate a non backbone
1251 * area first. If that is the case safely
1252 * fall out of this function.
1257 type
= htons(OSPF6_LSTYPE_INTER_ROUTER
);
1258 for (ALL_LSDB_TYPED_ADVRTR(oa
->lsdb
, type
, router_id
, lsa
))
1259 ospf6_abr_examin_summary(lsa
, oa
);
1261 type
= htons(OSPF6_LSTYPE_INTER_PREFIX
);
1262 for (ALL_LSDB_TYPED_ADVRTR(oa
->lsdb
, type
, router_id
, lsa
))
1263 ospf6_abr_examin_summary(lsa
, oa
);
1266 void ospf6_abr_reimport(struct ospf6_area
*oa
)
1268 struct ospf6_lsa
*lsa
;
1271 type
= htons(OSPF6_LSTYPE_INTER_ROUTER
);
1272 for (ALL_LSDB_TYPED(oa
->lsdb
, type
, lsa
))
1273 ospf6_abr_examin_summary(lsa
, oa
);
1275 type
= htons(OSPF6_LSTYPE_INTER_PREFIX
);
1276 for (ALL_LSDB_TYPED(oa
->lsdb
, type
, lsa
))
1277 ospf6_abr_examin_summary(lsa
, oa
);
1280 void ospf6_abr_prefix_resummarize(struct ospf6
*o
)
1282 struct ospf6_route
*route
;
1284 if (IS_OSPF6_DEBUG_ABR
)
1285 zlog_debug("Re-examining Inter-Prefix Summaries");
1287 for (route
= ospf6_route_head(o
->route_table
); route
;
1288 route
= ospf6_route_next(route
))
1289 ospf6_abr_originate_summary(route
);
1291 if (IS_OSPF6_DEBUG_ABR
)
1292 zlog_debug("Finished re-examining Inter-Prefix Summaries");
1296 /* Display functions */
1297 static char *ospf6_inter_area_prefix_lsa_get_prefix_str(struct ospf6_lsa
*lsa
,
1298 char *buf
, int buflen
,
1301 struct ospf6_inter_prefix_lsa
*prefix_lsa
;
1302 struct in6_addr in6
;
1306 (struct ospf6_inter_prefix_lsa
*)OSPF6_LSA_HEADER_END(
1309 ospf6_prefix_in6_addr(&in6
, prefix_lsa
, &prefix_lsa
->prefix
);
1311 inet_ntop(AF_INET6
, &in6
, buf
, buflen
);
1312 sprintf(&buf
[strlen(buf
)], "/%d",
1313 prefix_lsa
->prefix
.prefix_length
);
1320 static int ospf6_inter_area_prefix_lsa_show(struct vty
*vty
,
1321 struct ospf6_lsa
*lsa
)
1323 struct ospf6_inter_prefix_lsa
*prefix_lsa
;
1324 char buf
[INET6_ADDRSTRLEN
];
1326 prefix_lsa
= (struct ospf6_inter_prefix_lsa
*)OSPF6_LSA_HEADER_END(
1329 vty_out(vty
, " Metric: %lu\n",
1330 (unsigned long)OSPF6_ABR_SUMMARY_METRIC(prefix_lsa
));
1332 ospf6_prefix_options_printbuf(prefix_lsa
->prefix
.prefix_options
, buf
,
1334 vty_out(vty
, " Prefix Options: %s\n", buf
);
1336 vty_out(vty
, " Prefix: %s\n",
1337 ospf6_inter_area_prefix_lsa_get_prefix_str(lsa
, buf
,
1343 static char *ospf6_inter_area_router_lsa_get_prefix_str(struct ospf6_lsa
*lsa
,
1344 char *buf
, int buflen
,
1347 struct ospf6_inter_router_lsa
*router_lsa
;
1351 (struct ospf6_inter_router_lsa
*)OSPF6_LSA_HEADER_END(
1356 inet_ntop(AF_INET
, &router_lsa
->router_id
, buf
, buflen
);
1362 static int ospf6_inter_area_router_lsa_show(struct vty
*vty
,
1363 struct ospf6_lsa
*lsa
)
1365 struct ospf6_inter_router_lsa
*router_lsa
;
1368 router_lsa
= (struct ospf6_inter_router_lsa
*)OSPF6_LSA_HEADER_END(
1371 ospf6_options_printbuf(router_lsa
->options
, buf
, sizeof(buf
));
1372 vty_out(vty
, " Options: %s\n", buf
);
1373 vty_out(vty
, " Metric: %lu\n",
1374 (unsigned long)OSPF6_ABR_SUMMARY_METRIC(router_lsa
));
1376 inet_ntop(AF_INET
, &router_lsa
->router_id
, buf
, sizeof(buf
));
1377 vty_out(vty
, " Destination Router ID: %s\n", buf
);
1382 /* Debug commands */
1383 DEFUN (debug_ospf6_abr
,
1384 debug_ospf6_abr_cmd
,
1388 "Debug OSPFv3 ABR function\n"
1391 OSPF6_DEBUG_ABR_ON();
1395 DEFUN (no_debug_ospf6_abr
,
1396 no_debug_ospf6_abr_cmd
,
1397 "no debug ospf6 abr",
1401 "Debug OSPFv3 ABR function\n"
1404 OSPF6_DEBUG_ABR_OFF();
1408 int config_write_ospf6_debug_abr(struct vty
*vty
)
1410 if (IS_OSPF6_DEBUG_ABR
)
1411 vty_out(vty
, "debug ospf6 abr\n");
1415 void install_element_ospf6_debug_abr(void)
1417 install_element(ENABLE_NODE
, &debug_ospf6_abr_cmd
);
1418 install_element(ENABLE_NODE
, &no_debug_ospf6_abr_cmd
);
1419 install_element(CONFIG_NODE
, &debug_ospf6_abr_cmd
);
1420 install_element(CONFIG_NODE
, &no_debug_ospf6_abr_cmd
);
1423 static const struct ospf6_lsa_handler inter_prefix_handler
= {
1424 .lh_type
= OSPF6_LSTYPE_INTER_PREFIX
,
1425 .lh_name
= "Inter-Prefix",
1426 .lh_short_name
= "IAP",
1427 .lh_show
= ospf6_inter_area_prefix_lsa_show
,
1428 .lh_get_prefix_str
= ospf6_inter_area_prefix_lsa_get_prefix_str
,
1431 static const struct ospf6_lsa_handler inter_router_handler
= {
1432 .lh_type
= OSPF6_LSTYPE_INTER_ROUTER
,
1433 .lh_name
= "Inter-Router",
1434 .lh_short_name
= "IAR",
1435 .lh_show
= ospf6_inter_area_router_lsa_show
,
1436 .lh_get_prefix_str
= ospf6_inter_area_router_lsa_get_prefix_str
,
1439 void ospf6_abr_init(void)
1441 ospf6_install_lsa_handler(&inter_prefix_handler
);
1442 ospf6_install_lsa_handler(&inter_router_handler
);