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
,
244 prefix2str(&route
->prefix
, buf
, sizeof(buf
));
245 zlog_debug("Originating summary in area %s for %s cost %u",
246 area
->name
, buf
, route
->path
.cost
);
248 summary_table
= area
->summary_prefix
;
251 summary
= ospf6_route_lookup(&route
->prefix
, summary_table
);
253 old
= ospf6_lsdb_lookup(summary
->path
.origin
.type
,
254 summary
->path
.origin
.id
,
255 area
->ospf6
->router_id
, area
->lsdb
);
257 /* if this route has just removed, remove corresponding LSA */
258 if (CHECK_FLAG(route
->flag
, OSPF6_ROUTE_REMOVE
)) {
261 "The route has just removed, purge previous LSA");
263 if (route
->type
== OSPF6_DEST_TYPE_RANGE
) {
264 /* Whether the route have active longer prefix */
265 if (!CHECK_FLAG(route
->flag
,
266 OSPF6_ROUTE_ACTIVE_SUMMARY
)) {
269 "The range is not active. withdraw");
271 ospf6_abr_delete_route(route
, summary
,
275 ospf6_lsa_purge(old
);
280 if ((route
->type
== OSPF6_DEST_TYPE_ROUTER
) && IS_AREA_STUB(area
)) {
283 "Area has been stubbed, purge Inter-Router LSA");
285 ospf6_abr_delete_route(route
, summary
, summary_table
, old
);
290 && (route
->path
.subtype
!= OSPF6_PATH_SUBTYPE_DEFAULT_RT
)) {
292 zlog_debug("Area has been stubbed, purge prefix LSA");
294 ospf6_abr_delete_route(route
, summary
, summary_table
, old
);
298 /* do not generate if the route cost is greater or equal to LSInfinity
300 if (route
->path
.cost
>= OSPF_LS_INFINITY
) {
301 /* When we're clearing the range route because all active
303 * under the range are gone, we set the range's cost to
304 * OSPF_AREA_RANGE_COST_UNSPEC, which is > OSPF_LS_INFINITY. We
305 * don't want to trigger the code here for that. This code is
307 * handling routes that have gone to infinity. The range removal
311 if ((route
->type
!= OSPF6_DEST_TYPE_RANGE
)
312 && (route
->path
.cost
!= OSPF_AREA_RANGE_COST_UNSPEC
)) {
315 "The cost exceeds LSInfinity, withdraw");
317 ospf6_lsa_purge(old
);
322 /* if this is a route to ASBR */
323 if (route
->type
== OSPF6_DEST_TYPE_ROUTER
) {
324 /* Only the preferred best path is considered */
325 if (!CHECK_FLAG(route
->flag
, OSPF6_ROUTE_BEST
)) {
328 "This is the secondary path to the ASBR, ignore");
329 ospf6_abr_delete_route(route
, summary
, summary_table
,
334 /* Do not generate if the area is stub */
338 /* if this is an intra-area route, this may be suppressed by aggregation
340 if (route
->type
== OSPF6_DEST_TYPE_NETWORK
341 && route
->path
.type
== OSPF6_PATH_TYPE_INTRA
) {
342 /* search for configured address range for the route's area */
344 ospf6_area_lookup(route
->path
.area_id
, area
->ospf6
);
346 range
= ospf6_route_lookup_bestmatch(&route
->prefix
,
347 route_area
->range_table
);
349 /* ranges are ignored when originate backbone routes to transit
351 Otherwise, if ranges are configured, the route is suppressed.
353 if (range
&& !CHECK_FLAG(range
->flag
, OSPF6_ROUTE_REMOVE
)
354 && (route
->path
.area_id
!= OSPF_AREA_BACKBONE
355 || !IS_AREA_TRANSIT(area
))) {
357 prefix2str(&range
->prefix
, buf
, sizeof(buf
));
358 zlog_debug("Suppressed by range %s of area %s",
359 buf
, route_area
->name
);
361 ospf6_abr_delete_route(route
, summary
, summary_table
,
367 /* If this is a configured address range */
368 if (route
->type
== OSPF6_DEST_TYPE_RANGE
) {
369 /* If DoNotAdvertise is set */
370 if (CHECK_FLAG(route
->flag
, OSPF6_ROUTE_DO_NOT_ADVERTISE
)) {
373 "This is the range with DoNotAdvertise set. ignore");
374 ospf6_abr_delete_route(route
, summary
, summary_table
,
379 /* If there are no active prefixes in this range, remove */
380 if (!CHECK_FLAG(route
->flag
, OSPF6_ROUTE_ACTIVE_SUMMARY
)) {
382 zlog_debug("The range is not active. withdraw");
383 ospf6_abr_delete_route(route
, summary
, summary_table
,
389 /* Check export list */
390 if (EXPORT_NAME(area
)) {
391 if (EXPORT_LIST(area
) == NULL
)
393 access_list_lookup(AFI_IP6
, EXPORT_NAME(area
));
395 if (EXPORT_LIST(area
))
396 if (access_list_apply(EXPORT_LIST(area
), &route
->prefix
)
400 &(ADV_ROUTER_IN_PREFIX(
404 "prefix %s was denied by export list",
411 /* Check filter-list */
412 if (PREFIX_LIST_OUT(area
))
413 if (prefix_list_apply(PREFIX_LIST_OUT(area
), &route
->prefix
)
418 &(ADV_ROUTER_IN_PREFIX(&route
->prefix
)),
421 "prefix %s was denied by filter-list out",
427 /* the route is going to be originated. store it in area's summary_table
429 if (summary
== NULL
) {
430 summary
= ospf6_route_copy(route
);
431 summary
->path
.origin
.adv_router
= area
->ospf6
->router_id
;
433 if (route
->type
== OSPF6_DEST_TYPE_ROUTER
) {
434 summary
->path
.origin
.type
=
435 htons(OSPF6_LSTYPE_INTER_ROUTER
);
436 summary
->path
.origin
.id
=
437 ADV_ROUTER_IN_PREFIX(&route
->prefix
);
439 summary
->path
.origin
.type
=
440 htons(OSPF6_LSTYPE_INTER_PREFIX
);
441 summary
->path
.origin
.id
= ospf6_new_ls_id(
442 summary
->path
.origin
.type
,
443 summary
->path
.origin
.adv_router
, area
->lsdb
);
445 summary
= ospf6_route_add(summary
, summary_table
);
447 summary
->type
= route
->type
;
448 monotime(&summary
->changed
);
451 summary
->path
.router_bits
= route
->path
.router_bits
;
452 summary
->path
.options
[0] = route
->path
.options
[0];
453 summary
->path
.options
[1] = route
->path
.options
[1];
454 summary
->path
.options
[2] = route
->path
.options
[2];
455 summary
->path
.prefix_options
= route
->path
.prefix_options
;
456 summary
->path
.area_id
= area
->area_id
;
457 summary
->path
.type
= OSPF6_PATH_TYPE_INTER
;
458 summary
->path
.subtype
= route
->path
.subtype
;
459 summary
->path
.cost
= route
->path
.cost
;
460 /* summary->nexthop[0] = route->nexthop[0]; */
463 memset(buffer
, 0, sizeof(buffer
));
464 lsa_header
= (struct ospf6_lsa_header
*)buffer
;
466 if (route
->type
== OSPF6_DEST_TYPE_ROUTER
) {
467 router_lsa
= (struct ospf6_inter_router_lsa
468 *)((caddr_t
)lsa_header
469 + sizeof(struct ospf6_lsa_header
));
470 p
= (caddr_t
)router_lsa
+ sizeof(struct ospf6_inter_router_lsa
);
472 /* Fill Inter-Area-Router-LSA */
473 router_lsa
->options
[0] = route
->path
.options
[0];
474 router_lsa
->options
[1] = route
->path
.options
[1];
475 router_lsa
->options
[2] = route
->path
.options
[2];
476 OSPF6_ABR_SUMMARY_METRIC_SET(router_lsa
, route
->path
.cost
);
477 router_lsa
->router_id
= ADV_ROUTER_IN_PREFIX(&route
->prefix
);
478 type
= htons(OSPF6_LSTYPE_INTER_ROUTER
);
480 prefix_lsa
= (struct ospf6_inter_prefix_lsa
481 *)((caddr_t
)lsa_header
482 + sizeof(struct ospf6_lsa_header
));
483 p
= (caddr_t
)prefix_lsa
+ sizeof(struct ospf6_inter_prefix_lsa
);
485 /* Fill Inter-Area-Prefix-LSA */
486 OSPF6_ABR_SUMMARY_METRIC_SET(prefix_lsa
, route
->path
.cost
);
487 prefix_lsa
->prefix
.prefix_length
= route
->prefix
.prefixlen
;
488 prefix_lsa
->prefix
.prefix_options
= route
->path
.prefix_options
;
491 memcpy(p
, &route
->prefix
.u
.prefix6
,
492 OSPF6_PREFIX_SPACE(route
->prefix
.prefixlen
));
493 ospf6_prefix_apply_mask(&prefix_lsa
->prefix
);
494 p
+= OSPF6_PREFIX_SPACE(route
->prefix
.prefixlen
);
495 type
= htons(OSPF6_LSTYPE_INTER_PREFIX
);
498 /* Fill LSA Header */
500 lsa_header
->type
= type
;
501 lsa_header
->id
= summary
->path
.origin
.id
;
502 lsa_header
->adv_router
= area
->ospf6
->router_id
;
504 ospf6_new_ls_seqnum(lsa_header
->type
, lsa_header
->id
,
505 lsa_header
->adv_router
, area
->lsdb
);
506 lsa_header
->length
= htons((caddr_t
)p
- (caddr_t
)lsa_header
);
509 ospf6_lsa_checksum(lsa_header
);
512 lsa
= ospf6_lsa_create(lsa_header
);
515 ospf6_lsa_originate_area(lsa
, area
);
520 void ospf6_abr_range_reset_cost(struct ospf6
*ospf6
)
522 struct listnode
*node
, *nnode
;
523 struct ospf6_area
*oa
;
524 struct ospf6_route
*range
;
526 for (ALL_LIST_ELEMENTS(ospf6
->area_list
, node
, nnode
, oa
))
527 for (range
= ospf6_route_head(oa
->range_table
); range
;
528 range
= ospf6_route_next(range
))
529 OSPF6_ABR_RANGE_CLEAR_COST(range
);
532 static inline uint32_t ospf6_abr_range_compute_cost(struct ospf6_route
*range
,
535 struct ospf6_route
*ro
;
538 for (ro
= ospf6_route_match_head(&range
->prefix
, o
->route_table
); ro
;
539 ro
= ospf6_route_match_next(&range
->prefix
, ro
)) {
540 if (ro
->path
.area_id
== range
->path
.area_id
541 && (ro
->path
.type
== OSPF6_PATH_TYPE_INTRA
)
542 && !CHECK_FLAG(ro
->flag
, OSPF6_ROUTE_REMOVE
))
543 cost
= MAX(cost
, ro
->path
.cost
);
550 ospf6_abr_range_summary_needs_update(struct ospf6_route
*range
, uint32_t cost
)
552 int redo_summary
= 0;
554 if (CHECK_FLAG(range
->flag
, OSPF6_ROUTE_REMOVE
)) {
555 UNSET_FLAG(range
->flag
, OSPF6_ROUTE_ACTIVE_SUMMARY
);
557 } else if (CHECK_FLAG(range
->flag
, OSPF6_ROUTE_DO_NOT_ADVERTISE
)) {
558 if (range
->path
.cost
!= 0) {
559 range
->path
.cost
= 0;
563 if ((OSPF6_PATH_COST_IS_CONFIGURED(range
->path
)
564 && range
->path
.cost
!= range
->path
.u
.cost_config
)) {
565 range
->path
.cost
= range
->path
.u
.cost_config
;
566 SET_FLAG(range
->flag
, OSPF6_ROUTE_ACTIVE_SUMMARY
);
568 } else if (!OSPF6_PATH_COST_IS_CONFIGURED(range
->path
)
569 && range
->path
.cost
!= cost
) {
570 range
->path
.cost
= cost
;
571 SET_FLAG(range
->flag
, OSPF6_ROUTE_ACTIVE_SUMMARY
);
574 } else if (CHECK_FLAG(range
->flag
, OSPF6_ROUTE_ACTIVE_SUMMARY
)) {
575 /* Cost is zero, meaning no active range */
576 UNSET_FLAG(range
->flag
, OSPF6_ROUTE_ACTIVE_SUMMARY
);
577 range
->path
.cost
= OSPF_AREA_RANGE_COST_UNSPEC
;
581 return (redo_summary
);
584 static void ospf6_abr_range_update(struct ospf6_route
*range
)
587 struct listnode
*node
, *nnode
;
588 struct ospf6_area
*oa
;
589 int summary_orig
= 0;
591 assert(range
->type
== OSPF6_DEST_TYPE_RANGE
);
593 /* update range's cost and active flag */
594 cost
= ospf6_abr_range_compute_cost(range
, ospf6
);
596 /* Non-zero cost is a proxy for active longer prefixes in this range.
597 * If there are active routes covered by this range AND either the
599 * cost has changed or the summarized cost has changed then redo
601 * Alternately, if there are no longer active prefixes and there are
602 * summary announcements, withdraw those announcements.
604 * The don't advertise code relies on the path.cost being set to UNSPEC
606 * work the first time. Subsequent times the path.cost is not 0 anyway
608 * were active ranges.
611 if (ospf6_abr_range_summary_needs_update(range
, cost
)) {
612 for (ALL_LIST_ELEMENTS(ospf6
->area_list
, node
, nnode
, oa
))
614 ospf6_abr_originate_summary_to_area(range
, oa
);
616 if (CHECK_FLAG(range
->flag
, OSPF6_ROUTE_ACTIVE_SUMMARY
)
618 if (!CHECK_FLAG(range
->flag
,
619 OSPF6_ROUTE_BLACKHOLE_ADDED
)) {
620 if (IS_OSPF6_DEBUG_ABR
)
621 zlog_debug("Add discard route");
623 ospf6_zebra_add_discard(range
);
626 /* Summary removed or no summary generated as no
628 if (CHECK_FLAG(range
->flag
,
629 OSPF6_ROUTE_BLACKHOLE_ADDED
)) {
630 if (IS_OSPF6_DEBUG_ABR
)
631 zlog_debug("Delete discard route");
633 ospf6_zebra_delete_discard(range
);
639 void ospf6_abr_originate_summary(struct ospf6_route
*route
)
641 struct listnode
*node
, *nnode
;
642 struct ospf6_area
*oa
;
643 struct ospf6_route
*range
= NULL
;
645 if (route
->type
== OSPF6_DEST_TYPE_NETWORK
) {
646 oa
= ospf6_area_lookup(route
->path
.area_id
, ospf6
);
648 zlog_err("OSPFv6 area lookup failed");
652 range
= ospf6_route_lookup_bestmatch(&route
->prefix
,
655 ospf6_abr_range_update(range
);
659 for (ALL_LIST_ELEMENTS(ospf6
->area_list
, node
, nnode
, oa
))
660 ospf6_abr_originate_summary_to_area(route
, oa
);
663 void ospf6_abr_defaults_to_stub(struct ospf6
*o
)
665 struct listnode
*node
, *nnode
;
666 struct ospf6_area
*oa
;
667 struct ospf6_route
*def
, *route
;
672 def
= ospf6_route_create();
673 def
->type
= OSPF6_DEST_TYPE_NETWORK
;
674 def
->prefix
.family
= AF_INET6
;
675 def
->prefix
.prefixlen
= 0;
676 memset(&def
->prefix
.u
.prefix6
, 0, sizeof(struct in6_addr
));
677 def
->type
= OSPF6_DEST_TYPE_NETWORK
;
678 def
->path
.type
= OSPF6_PATH_TYPE_INTER
;
679 def
->path
.subtype
= OSPF6_PATH_SUBTYPE_DEFAULT_RT
;
680 def
->path
.area_id
= o
->backbone
->area_id
;
682 for (ALL_LIST_ELEMENTS(ospf6
->area_list
, node
, nnode
, oa
)) {
683 if (!IS_AREA_STUB(oa
)) {
684 /* withdraw defaults when an area switches from stub to
686 route
= ospf6_route_lookup(&def
->prefix
,
689 && (route
->path
.subtype
== def
->path
.subtype
)) {
690 if (IS_OSPF6_DEBUG_ABR
)
692 "Withdrawing default route from non-stubby area %s",
694 SET_FLAG(def
->flag
, OSPF6_ROUTE_REMOVE
);
695 ospf6_abr_originate_summary_to_area(def
, oa
);
698 /* announce defaults to stubby areas */
699 if (IS_OSPF6_DEBUG_ABR
)
701 "Announcing default route into stubby area %s",
703 UNSET_FLAG(def
->flag
, OSPF6_ROUTE_REMOVE
);
704 ospf6_abr_originate_summary_to_area(def
, oa
);
707 ospf6_route_delete(def
);
710 void ospf6_abr_old_path_update(struct ospf6_route
*old_route
,
711 struct ospf6_route
*route
,
712 struct ospf6_route_table
*table
)
714 struct ospf6_path
*o_path
= NULL
;
715 struct listnode
*anode
, *anext
;
716 struct listnode
*nnode
, *rnode
, *rnext
;
717 struct ospf6_nexthop
*nh
, *rnh
;
719 for (ALL_LIST_ELEMENTS(old_route
->paths
, anode
, anext
, o_path
)) {
720 if (o_path
->area_id
!= route
->path
.area_id
||
721 (memcmp(&(o_path
)->origin
, &(route
)->path
.origin
,
722 sizeof(struct ospf6_ls_origin
)) != 0))
725 if ((o_path
->cost
== route
->path
.cost
) &&
726 (o_path
->u
.cost_e2
== route
->path
.u
.cost_e2
))
729 for (ALL_LIST_ELEMENTS_RO(o_path
->nh_list
, nnode
, nh
)) {
730 for (ALL_LIST_ELEMENTS(old_route
->nh_list
, rnode
,
732 if (!ospf6_nexthop_is_same(rnh
, nh
))
734 listnode_delete(old_route
->nh_list
, rnh
);
735 ospf6_nexthop_delete(rnh
);
740 listnode_delete(old_route
->paths
, o_path
);
741 ospf6_path_free(o_path
);
743 for (ALL_LIST_ELEMENTS(old_route
->paths
, anode
,
745 ospf6_merge_nexthops(old_route
->nh_list
,
749 if (IS_OSPF6_DEBUG_ABR
|| IS_OSPF6_DEBUG_EXAMIN(INTER_PREFIX
))
750 zlog_debug("%s: paths %u nh %u", __PRETTY_FUNCTION__
,
752 listcount(old_route
->paths
) : 0,
754 listcount(old_route
->nh_list
) : 0);
757 (*table
->hook_add
)(old_route
);
759 if (old_route
->path
.origin
.id
== route
->path
.origin
.id
&&
760 old_route
->path
.origin
.adv_router
==
761 route
->path
.origin
.adv_router
) {
762 struct ospf6_path
*h_path
;
764 h_path
= (struct ospf6_path
*)
765 listgetdata(listhead(old_route
->paths
));
766 old_route
->path
.origin
.type
= h_path
->origin
.type
;
767 old_route
->path
.origin
.id
= h_path
->origin
.id
;
768 old_route
->path
.origin
.adv_router
=
769 h_path
->origin
.adv_router
;
774 void ospf6_abr_old_route_remove(struct ospf6_lsa
*lsa
,
775 struct ospf6_route
*old
,
776 struct ospf6_route_table
*table
)
778 if (listcount(old
->paths
) > 1) {
779 struct listnode
*anode
, *anext
, *nnode
, *rnode
, *rnext
;
780 struct ospf6_path
*o_path
;
781 struct ospf6_nexthop
*nh
, *rnh
;
782 bool nh_updated
= false;
783 char buf
[PREFIX2STR_BUFFER
];
785 for (ALL_LIST_ELEMENTS(old
->paths
, anode
, anext
, o_path
)) {
786 if (o_path
->origin
.adv_router
!= lsa
->header
->adv_router
787 && o_path
->origin
.id
!= lsa
->header
->id
)
789 for (ALL_LIST_ELEMENTS_RO(o_path
->nh_list
, nnode
, nh
)) {
790 for (ALL_LIST_ELEMENTS(old
->nh_list
,
791 rnode
, rnext
, rnh
)) {
792 if (!ospf6_nexthop_is_same(rnh
, nh
))
794 listnode_delete(old
->nh_list
, rnh
);
795 ospf6_nexthop_delete(rnh
);
798 listnode_delete(old
->paths
, o_path
);
799 ospf6_path_free(o_path
);
804 if (listcount(old
->paths
)) {
805 if (IS_OSPF6_DEBUG_ABR
||
806 IS_OSPF6_DEBUG_EXAMIN(INTER_PREFIX
)) {
807 prefix2str(&old
->prefix
, buf
,
809 zlog_debug("%s: old %s updated nh %u",
810 __PRETTY_FUNCTION__
, buf
,
812 listcount(old
->nh_list
) : 0);
816 (*table
->hook_add
)(old
);
818 if ((old
->path
.origin
.id
== lsa
->header
->id
) &&
819 (old
->path
.origin
.adv_router
820 == lsa
->header
->adv_router
)) {
821 struct ospf6_path
*h_path
;
823 h_path
= (struct ospf6_path
*)
825 listhead(old
->paths
));
826 old
->path
.origin
.type
=
828 old
->path
.origin
.id
= h_path
->origin
.id
;
829 old
->path
.origin
.adv_router
=
830 h_path
->origin
.adv_router
;
833 ospf6_route_remove(old
, table
);
836 ospf6_route_remove(old
, table
);
840 /* RFC 2328 16.2. Calculating the inter-area routes */
841 void ospf6_abr_examin_summary(struct ospf6_lsa
*lsa
, struct ospf6_area
*oa
)
843 struct prefix prefix
, abr_prefix
;
844 struct ospf6_route_table
*table
= NULL
;
845 struct ospf6_route
*range
, *route
, *old
= NULL
, *old_route
;
846 struct ospf6_route
*abr_entry
;
848 char options
[3] = {0, 0, 0};
849 uint8_t prefix_options
= 0;
851 uint8_t router_bits
= 0;
852 char buf
[PREFIX2STR_BUFFER
];
854 struct ospf6_inter_prefix_lsa
*prefix_lsa
= NULL
;
855 struct ospf6_inter_router_lsa
*router_lsa
= NULL
;
856 bool old_entry_updated
= false;
857 struct ospf6_path
*path
, *o_path
, *ecmp_path
;
858 struct listnode
*anode
;
861 memset(&prefix
, 0, sizeof(prefix
));
863 if (lsa
->header
->type
== htons(OSPF6_LSTYPE_INTER_PREFIX
)) {
864 if (IS_OSPF6_DEBUG_EXAMIN(INTER_PREFIX
)) {
866 zlog_debug("%s: Examin %s in area %s",
867 __PRETTY_FUNCTION__
, lsa
->name
, oa
->name
);
871 (struct ospf6_inter_prefix_lsa
*)OSPF6_LSA_HEADER_END(
873 prefix
.family
= AF_INET6
;
874 prefix
.prefixlen
= prefix_lsa
->prefix
.prefix_length
;
875 ospf6_prefix_in6_addr(&prefix
.u
.prefix6
, prefix_lsa
,
876 &prefix_lsa
->prefix
);
878 prefix2str(&prefix
, buf
, sizeof(buf
));
879 table
= oa
->ospf6
->route_table
;
880 type
= OSPF6_DEST_TYPE_NETWORK
;
881 prefix_options
= prefix_lsa
->prefix
.prefix_options
;
882 cost
= OSPF6_ABR_SUMMARY_METRIC(prefix_lsa
);
883 } else if (lsa
->header
->type
== htons(OSPF6_LSTYPE_INTER_ROUTER
)) {
884 if (IS_OSPF6_DEBUG_EXAMIN(INTER_ROUTER
)) {
886 zlog_debug("%s: Examin %s in area %s",
887 __PRETTY_FUNCTION__
, lsa
->name
, oa
->name
);
891 (struct ospf6_inter_router_lsa
*)OSPF6_LSA_HEADER_END(
893 ospf6_linkstate_prefix(router_lsa
->router_id
, htonl(0),
896 inet_ntop(AF_INET
, &router_lsa
->router_id
, buf
,
899 table
= oa
->ospf6
->brouter_table
;
900 type
= OSPF6_DEST_TYPE_ROUTER
;
901 options
[0] = router_lsa
->options
[0];
902 options
[1] = router_lsa
->options
[1];
903 options
[2] = router_lsa
->options
[2];
904 cost
= OSPF6_ABR_SUMMARY_METRIC(router_lsa
);
905 SET_FLAG(router_bits
, OSPF6_ROUTER_BIT_E
);
909 /* Find existing route */
910 route
= ospf6_route_lookup(&prefix
, table
);
912 ospf6_route_lock(route
);
913 while (route
&& ospf6_route_is_prefix(&prefix
, route
)) {
914 if (route
->path
.area_id
== oa
->area_id
915 && route
->path
.origin
.type
== lsa
->header
->type
916 && !CHECK_FLAG(route
->flag
, OSPF6_ROUTE_WAS_REMOVED
)) {
917 /* LSA adv. router could be part of route's
918 * paths list. Find the existing path and set
921 if (listcount(route
->paths
) > 1) {
922 for (ALL_LIST_ELEMENTS_RO(route
->paths
, anode
,
925 &o_path
->origin
.adv_router
,
928 if (o_path
->origin
.id
== lsa
->header
->id
929 && o_path
->origin
.adv_router
==
930 lsa
->header
->adv_router
) {
934 zlog_debug("%s: old entry found in paths, adv_router %s",
941 } else if (route
->path
.origin
.id
== lsa
->header
->id
&&
942 route
->path
.origin
.adv_router
==
943 lsa
->header
->adv_router
)
946 route
= ospf6_route_next(route
);
949 ospf6_route_unlock(route
);
951 /* (1) if cost == LSInfinity or if the LSA is MaxAge */
952 if (cost
== OSPF_LS_INFINITY
) {
954 zlog_debug("cost is LS_INFINITY, ignore");
956 ospf6_abr_old_route_remove(lsa
, old
, table
);
959 if (OSPF6_LSA_IS_MAXAGE(lsa
)) {
961 zlog_debug("%s: LSA %s is MaxAge, ignore",
962 __PRETTY_FUNCTION__
, lsa
->name
);
964 ospf6_abr_old_route_remove(lsa
, old
, table
);
968 /* (2) if the LSA is self-originated, ignore */
969 if (lsa
->header
->adv_router
== oa
->ospf6
->router_id
) {
971 zlog_debug("LSA %s is self-originated, ignore",
974 ospf6_route_remove(old
, table
);
978 /* (3) if the prefix is equal to an active configured address range */
979 /* or if the NU bit is set in the prefix */
980 if (lsa
->header
->type
== htons(OSPF6_LSTYPE_INTER_PREFIX
)) {
981 /* must have been set in previous block */
984 range
= ospf6_route_lookup(&prefix
, oa
->range_table
);
988 "Prefix is equal to address range, ignore");
990 ospf6_route_remove(old
, table
);
994 if (CHECK_FLAG(prefix_lsa
->prefix
.prefix_options
,
995 OSPF6_PREFIX_OPTION_NU
)
996 || CHECK_FLAG(prefix_lsa
->prefix
.prefix_options
,
997 OSPF6_PREFIX_OPTION_LA
)) {
999 zlog_debug("Prefix has NU/LA bit set, ignore");
1001 ospf6_route_remove(old
, table
);
1006 if (lsa
->header
->type
== htons(OSPF6_LSTYPE_INTER_ROUTER
)) {
1007 /* To pass test suites */
1009 if (!OSPF6_OPT_ISSET(router_lsa
->options
, OSPF6_OPT_R
)
1010 || !OSPF6_OPT_ISSET(router_lsa
->options
, OSPF6_OPT_V6
)) {
1012 zlog_debug("Prefix has NU/LA bit set, ignore");
1014 ospf6_route_remove(old
, table
);
1018 /* Avoid infinite recursion if someone has maliciously announced
1020 Inter-Router LSA for an ABR
1022 if (lsa
->header
->adv_router
== router_lsa
->router_id
) {
1025 "Ignorning Inter-Router LSA for an ABR (%s)",
1028 ospf6_route_remove(old
, table
);
1034 /* (4) if the routing table entry for the ABR does not exist */
1035 ospf6_linkstate_prefix(lsa
->header
->adv_router
, htonl(0), &abr_prefix
);
1036 abr_entry
= ospf6_route_lookup(&abr_prefix
, oa
->ospf6
->brouter_table
);
1037 if (abr_entry
== NULL
|| abr_entry
->path
.area_id
!= oa
->area_id
1038 || CHECK_FLAG(abr_entry
->flag
, OSPF6_ROUTE_REMOVE
)
1039 || !CHECK_FLAG(abr_entry
->path
.router_bits
, OSPF6_ROUTER_BIT_B
)) {
1041 zlog_debug("%s: ABR router entry does not exist, ignore",
1042 __PRETTY_FUNCTION__
);
1044 if (old
->type
== OSPF6_DEST_TYPE_ROUTER
&&
1045 oa
->intra_brouter_calc
) {
1048 "%s: intra_brouter_calc is on, skip brouter remove: %s (%p)",
1049 __PRETTY_FUNCTION__
, buf
,
1053 zlog_debug("%s: remove old entry: %s %p ",
1054 __PRETTY_FUNCTION__
, buf
,
1056 ospf6_route_remove(old
, table
);
1062 /* Check import list */
1063 if (IMPORT_NAME(oa
)) {
1064 if (IMPORT_LIST(oa
) == NULL
)
1066 access_list_lookup(AFI_IP6
, IMPORT_NAME(oa
));
1068 if (IMPORT_LIST(oa
))
1069 if (access_list_apply(IMPORT_LIST(oa
), &prefix
)
1073 "Prefix was denied by import-list");
1075 ospf6_route_remove(old
, table
);
1080 /* Check input prefix-list */
1081 if (PREFIX_LIST_IN(oa
)) {
1082 if (prefix_list_apply(PREFIX_LIST_IN(oa
), &prefix
)
1085 zlog_debug("Prefix was denied by prefix-list");
1087 ospf6_route_remove(old
, table
);
1092 /* (5),(6): the path preference is handled by the sorting
1093 in the routing table. Always install the path by substituting
1094 old route (if any). */
1095 route
= ospf6_route_create();
1098 route
->prefix
= prefix
;
1099 route
->path
.origin
.type
= lsa
->header
->type
;
1100 route
->path
.origin
.id
= lsa
->header
->id
;
1101 route
->path
.origin
.adv_router
= lsa
->header
->adv_router
;
1102 route
->path
.router_bits
= router_bits
;
1103 route
->path
.options
[0] = options
[0];
1104 route
->path
.options
[1] = options
[1];
1105 route
->path
.options
[2] = options
[2];
1106 route
->path
.prefix_options
= prefix_options
;
1107 route
->path
.area_id
= oa
->area_id
;
1108 route
->path
.type
= OSPF6_PATH_TYPE_INTER
;
1109 route
->path
.cost
= abr_entry
->path
.cost
+ cost
;
1111 /* copy brouter rechable nexthops into the route. */
1112 ospf6_route_copy_nexthops(route
, abr_entry
);
1114 /* (7) If the routes are identical, copy the next hops over to existing
1115 route. ospf6's route table implementation will otherwise string both
1116 routes, but keep the older one as the best route since the routes
1119 old
= ospf6_route_lookup(&prefix
, table
);
1121 for (old_route
= old
; old_route
; old_route
= old_route
->next
) {
1122 if (!ospf6_route_is_same(old_route
, route
) ||
1123 (old_route
->type
!= route
->type
) ||
1124 (old_route
->path
.type
!= route
->path
.type
))
1127 if ((ospf6_route_cmp(route
, old_route
) != 0)) {
1129 prefix2str(&prefix
, buf
, sizeof(buf
));
1130 zlog_debug("%s: old %p %s cost %u new route cost %u are not same",
1131 __PRETTY_FUNCTION__
,
1132 (void *)old_route
, buf
,
1133 old_route
->path
.cost
,
1137 /* Check new route's adv. router is same in one of
1138 * the paths with differed cost, if so remove the
1139 * old path as later new route will be added.
1141 if (listcount(old_route
->paths
) > 1)
1142 ospf6_abr_old_path_update(old_route
, route
,
1147 ospf6_route_merge_nexthops(old_route
, route
);
1148 old_entry_updated
= true;
1150 for (ALL_LIST_ELEMENTS_RO(old_route
->paths
, anode
,
1152 if (o_path
->area_id
== route
->path
.area_id
&&
1153 (memcmp(&(o_path
)->origin
, &(route
)->path
.origin
,
1154 sizeof(struct ospf6_ls_origin
)) == 0))
1158 /* New adv. router for a existing path add to paths list */
1159 if (o_path
== NULL
) {
1160 ecmp_path
= ospf6_path_dup(&route
->path
);
1162 /* Add a nh_list to new ecmp path */
1163 ospf6_copy_nexthops(ecmp_path
->nh_list
, route
->nh_list
);
1165 /* Add the new path to route's path list */
1166 listnode_add_sort(old_route
->paths
, ecmp_path
);
1169 prefix2str(&route
->prefix
, buf
, sizeof(buf
));
1171 &ecmp_path
->origin
.adv_router
,
1172 adv_router
, sizeof(adv_router
));
1173 zlog_debug("%s: route %s cost %u another path %s added with nh %u, effective paths %u nh %u",
1174 __PRETTY_FUNCTION__
, buf
,
1175 old_route
->path
.cost
,
1177 listcount(ecmp_path
->nh_list
),
1179 listcount(old_route
->paths
) : 0,
1180 listcount(old_route
->nh_list
));
1183 /* adv. router exists in the list, update the nhs */
1184 list_delete_all_node(o_path
->nh_list
);
1185 ospf6_copy_nexthops(o_path
->nh_list
, route
->nh_list
);
1189 zlog_debug("%s: Update route: %s %p old cost %u new cost %u nh %u",
1190 __PRETTY_FUNCTION__
, buf
, (void *)old_route
,
1191 old_route
->path
.cost
, route
->path
.cost
,
1192 listcount(route
->nh_list
));
1194 /* For Inter-Prefix route: Update RIB/FIB,
1195 * For Inter-Router trigger summary update
1197 if (table
->hook_add
)
1198 (*table
->hook_add
)(old_route
);
1200 /* Delete new route */
1201 ospf6_route_delete(route
);
1205 if (old_entry_updated
== false) {
1207 inet_ntop(AF_INET
, &route
->path
.origin
.adv_router
,
1208 adv_router
, sizeof(adv_router
));
1209 zlog_debug("%s: Install route: %s cost %u nh %u adv_router %s ",
1210 __PRETTY_FUNCTION__
, buf
, route
->path
.cost
,
1211 listcount(route
->nh_list
), adv_router
);
1214 path
= ospf6_path_dup(&route
->path
);
1215 ospf6_copy_nexthops(path
->nh_list
, abr_entry
->nh_list
);
1216 listnode_add_sort(route
->paths
, path
);
1218 /* ospf6_ia_add_nw_route (table, &prefix, route); */
1219 ospf6_route_add(route
, table
);
1223 void ospf6_abr_examin_brouter(uint32_t router_id
)
1225 struct ospf6_lsa
*lsa
;
1226 struct ospf6_area
*oa
;
1229 if (ospf6_is_router_abr(ospf6
))
1230 oa
= ospf6
->backbone
;
1232 oa
= listgetdata(listhead(ospf6
->area_list
));
1235 * It is possible to designate a non backbone
1236 * area first. If that is the case safely
1237 * fall out of this function.
1242 type
= htons(OSPF6_LSTYPE_INTER_ROUTER
);
1243 for (ALL_LSDB_TYPED_ADVRTR(oa
->lsdb
, type
, router_id
, lsa
))
1244 ospf6_abr_examin_summary(lsa
, oa
);
1246 type
= htons(OSPF6_LSTYPE_INTER_PREFIX
);
1247 for (ALL_LSDB_TYPED_ADVRTR(oa
->lsdb
, type
, router_id
, lsa
))
1248 ospf6_abr_examin_summary(lsa
, oa
);
1251 void ospf6_abr_reimport(struct ospf6_area
*oa
)
1253 struct ospf6_lsa
*lsa
;
1256 type
= htons(OSPF6_LSTYPE_INTER_ROUTER
);
1257 for (ALL_LSDB_TYPED(oa
->lsdb
, type
, lsa
))
1258 ospf6_abr_examin_summary(lsa
, oa
);
1260 type
= htons(OSPF6_LSTYPE_INTER_PREFIX
);
1261 for (ALL_LSDB_TYPED(oa
->lsdb
, type
, lsa
))
1262 ospf6_abr_examin_summary(lsa
, oa
);
1265 void ospf6_abr_prefix_resummarize(struct ospf6
*o
)
1267 struct ospf6_route
*route
;
1269 if (IS_OSPF6_DEBUG_ABR
)
1270 zlog_debug("Re-examining Inter-Prefix Summaries");
1272 for (route
= ospf6_route_head(o
->route_table
); route
;
1273 route
= ospf6_route_next(route
))
1274 ospf6_abr_originate_summary(route
);
1276 if (IS_OSPF6_DEBUG_ABR
)
1277 zlog_debug("Finished re-examining Inter-Prefix Summaries");
1281 /* Display functions */
1282 static char *ospf6_inter_area_prefix_lsa_get_prefix_str(struct ospf6_lsa
*lsa
,
1283 char *buf
, int buflen
,
1286 struct ospf6_inter_prefix_lsa
*prefix_lsa
;
1287 struct in6_addr in6
;
1291 (struct ospf6_inter_prefix_lsa
*)OSPF6_LSA_HEADER_END(
1294 ospf6_prefix_in6_addr(&in6
, prefix_lsa
, &prefix_lsa
->prefix
);
1296 inet_ntop(AF_INET6
, &in6
, buf
, buflen
);
1297 sprintf(&buf
[strlen(buf
)], "/%d",
1298 prefix_lsa
->prefix
.prefix_length
);
1305 static int ospf6_inter_area_prefix_lsa_show(struct vty
*vty
,
1306 struct ospf6_lsa
*lsa
)
1308 struct ospf6_inter_prefix_lsa
*prefix_lsa
;
1309 char buf
[INET6_ADDRSTRLEN
];
1311 prefix_lsa
= (struct ospf6_inter_prefix_lsa
*)OSPF6_LSA_HEADER_END(
1314 vty_out(vty
, " Metric: %lu\n",
1315 (unsigned long)OSPF6_ABR_SUMMARY_METRIC(prefix_lsa
));
1317 ospf6_prefix_options_printbuf(prefix_lsa
->prefix
.prefix_options
, buf
,
1319 vty_out(vty
, " Prefix Options: %s\n", buf
);
1321 vty_out(vty
, " Prefix: %s\n",
1322 ospf6_inter_area_prefix_lsa_get_prefix_str(lsa
, buf
,
1328 static char *ospf6_inter_area_router_lsa_get_prefix_str(struct ospf6_lsa
*lsa
,
1329 char *buf
, int buflen
,
1332 struct ospf6_inter_router_lsa
*router_lsa
;
1336 (struct ospf6_inter_router_lsa
*)OSPF6_LSA_HEADER_END(
1341 inet_ntop(AF_INET
, &router_lsa
->router_id
, buf
, buflen
);
1347 static int ospf6_inter_area_router_lsa_show(struct vty
*vty
,
1348 struct ospf6_lsa
*lsa
)
1350 struct ospf6_inter_router_lsa
*router_lsa
;
1353 router_lsa
= (struct ospf6_inter_router_lsa
*)OSPF6_LSA_HEADER_END(
1356 ospf6_options_printbuf(router_lsa
->options
, buf
, sizeof(buf
));
1357 vty_out(vty
, " Options: %s\n", buf
);
1358 vty_out(vty
, " Metric: %lu\n",
1359 (unsigned long)OSPF6_ABR_SUMMARY_METRIC(router_lsa
));
1361 inet_ntop(AF_INET
, &router_lsa
->router_id
, buf
, sizeof(buf
));
1362 vty_out(vty
, " Destination Router ID: %s\n", buf
);
1367 /* Debug commands */
1368 DEFUN (debug_ospf6_abr
,
1369 debug_ospf6_abr_cmd
,
1373 "Debug OSPFv3 ABR function\n"
1376 OSPF6_DEBUG_ABR_ON();
1380 DEFUN (no_debug_ospf6_abr
,
1381 no_debug_ospf6_abr_cmd
,
1382 "no debug ospf6 abr",
1386 "Debug OSPFv3 ABR function\n"
1389 OSPF6_DEBUG_ABR_OFF();
1393 int config_write_ospf6_debug_abr(struct vty
*vty
)
1395 if (IS_OSPF6_DEBUG_ABR
)
1396 vty_out(vty
, "debug ospf6 abr\n");
1400 void install_element_ospf6_debug_abr(void)
1402 install_element(ENABLE_NODE
, &debug_ospf6_abr_cmd
);
1403 install_element(ENABLE_NODE
, &no_debug_ospf6_abr_cmd
);
1404 install_element(CONFIG_NODE
, &debug_ospf6_abr_cmd
);
1405 install_element(CONFIG_NODE
, &no_debug_ospf6_abr_cmd
);
1408 struct ospf6_lsa_handler inter_prefix_handler
= {
1409 .lh_type
= OSPF6_LSTYPE_INTER_PREFIX
,
1410 .lh_name
= "Inter-Prefix",
1411 .lh_short_name
= "IAP",
1412 .lh_show
= ospf6_inter_area_prefix_lsa_show
,
1413 .lh_get_prefix_str
= ospf6_inter_area_prefix_lsa_get_prefix_str
,
1416 struct ospf6_lsa_handler inter_router_handler
= {
1417 .lh_type
= OSPF6_LSTYPE_INTER_ROUTER
,
1418 .lh_name
= "Inter-Router",
1419 .lh_short_name
= "IAR",
1420 .lh_show
= ospf6_inter_area_router_lsa_show
,
1421 .lh_get_prefix_str
= ospf6_inter_area_router_lsa_get_prefix_str
,
1424 void ospf6_abr_init(void)
1426 ospf6_install_lsa_handler(&inter_prefix_handler
);
1427 ospf6_install_lsa_handler(&inter_router_handler
);