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
), area
->ospf6
->vrf_id
);
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
;
158 /* Only destination type network, range or ASBR are considered */
159 if (route
->type
!= OSPF6_DEST_TYPE_NETWORK
160 && route
->type
!= OSPF6_DEST_TYPE_RANGE
161 && ((route
->type
!= OSPF6_DEST_TYPE_ROUTER
)
162 || !CHECK_FLAG(route
->path
.router_bits
, OSPF6_ROUTER_BIT_E
))) {
165 "Route type is none of network, range nor ASBR, ignore");
170 /* AS External routes are never considered */
171 if (route
->path
.type
== OSPF6_PATH_TYPE_EXTERNAL1
172 || route
->path
.type
== OSPF6_PATH_TYPE_EXTERNAL2
) {
174 zlog_debug("Path type is external, skip");
179 /* do not generate if the path's area is the same as target area */
180 if (route
->path
.area_id
== area
->area_id
) {
182 zlog_debug("The route is in the area itself, ignore");
187 /* do not generate if the nexthops belongs to the target area */
188 if (ospf6_abr_nexthops_belong_to_area(route
, area
)) {
190 zlog_debug("The route's nexthop is in the same area, ignore");
195 if (route
->type
== OSPF6_DEST_TYPE_ROUTER
) {
196 if (ADV_ROUTER_IN_PREFIX(&route
->prefix
)
197 == area
->ospf6
->router_id
) {
199 "%s: Skipping ASBR announcement for ABR (%pI4)",
201 &ADV_ROUTER_IN_PREFIX(&route
->prefix
));
206 if (route
->type
== OSPF6_DEST_TYPE_ROUTER
) {
207 if (IS_OSPF6_DEBUG_ABR
208 || IS_OSPF6_DEBUG_ORIGINATE(INTER_ROUTER
)) {
211 "Originating summary in area %s for ASBR %pI4",
213 &ADV_ROUTER_IN_PREFIX(&route
->prefix
));
215 summary_table
= area
->summary_router
;
217 if (IS_OSPF6_DEBUG_ABR
218 || IS_OSPF6_DEBUG_ORIGINATE(INTER_PREFIX
))
221 if (route
->type
== OSPF6_DEST_TYPE_NETWORK
&&
222 route
->path
.origin
.type
==
223 htons(OSPF6_LSTYPE_INTER_PREFIX
)) {
224 if (!CHECK_FLAG(route
->flag
, OSPF6_ROUTE_BEST
)) {
227 "%s: route %pFX with cost %u is not best, ignore.",
228 __func__
, &route
->prefix
,
234 if (route
->path
.origin
.type
==
235 htons(OSPF6_LSTYPE_INTRA_PREFIX
)) {
236 if (!CHECK_FLAG(route
->flag
, OSPF6_ROUTE_BEST
)) {
239 "%s: intra-prefix route %pFX with cost %u is not best, ignore.",
240 __func__
, &route
->prefix
,
248 "Originating summary in area %s for %pFX cost %u",
249 area
->name
, &route
->prefix
, route
->path
.cost
);
250 summary_table
= area
->summary_prefix
;
253 summary
= ospf6_route_lookup(&route
->prefix
, summary_table
);
255 old
= ospf6_lsdb_lookup(summary
->path
.origin
.type
,
256 summary
->path
.origin
.id
,
257 area
->ospf6
->router_id
, area
->lsdb
);
259 /* if this route has just removed, remove corresponding LSA */
260 if (CHECK_FLAG(route
->flag
, OSPF6_ROUTE_REMOVE
)) {
263 "The route has just removed, purge previous LSA");
265 if (route
->type
== OSPF6_DEST_TYPE_RANGE
) {
266 /* Whether the route have active longer prefix */
267 if (!CHECK_FLAG(route
->flag
,
268 OSPF6_ROUTE_ACTIVE_SUMMARY
)) {
271 "The range is not active. withdraw");
273 ospf6_abr_delete_route(route
, summary
,
277 ospf6_route_remove(summary
, summary_table
);
278 ospf6_lsa_purge(old
);
283 if ((route
->type
== OSPF6_DEST_TYPE_ROUTER
) && IS_AREA_STUB(area
)) {
286 "Area has been stubbed, purge Inter-Router LSA");
288 ospf6_abr_delete_route(route
, summary
, summary_table
, old
);
293 && (route
->path
.subtype
!= OSPF6_PATH_SUBTYPE_DEFAULT_RT
)) {
295 zlog_debug("Area has been stubbed, purge prefix LSA");
297 ospf6_abr_delete_route(route
, summary
, summary_table
, old
);
301 /* do not generate if the route cost is greater or equal to LSInfinity
303 if (route
->path
.cost
>= OSPF_LS_INFINITY
) {
304 /* When we're clearing the range route because all active
306 * under the range are gone, we set the range's cost to
307 * OSPF_AREA_RANGE_COST_UNSPEC, which is > OSPF_LS_INFINITY. We
308 * don't want to trigger the code here for that. This code is
310 * handling routes that have gone to infinity. The range removal
314 if ((route
->type
!= OSPF6_DEST_TYPE_RANGE
)
315 && (route
->path
.cost
!= OSPF_AREA_RANGE_COST_UNSPEC
)) {
318 "The cost exceeds LSInfinity, withdraw");
320 ospf6_lsa_purge(old
);
325 /* if this is a route to ASBR */
326 if (route
->type
== OSPF6_DEST_TYPE_ROUTER
) {
327 /* Only the preferred best path is considered */
328 if (!CHECK_FLAG(route
->flag
, OSPF6_ROUTE_BEST
)) {
331 "This is the secondary path to the ASBR, ignore");
332 ospf6_abr_delete_route(route
, summary
, summary_table
,
337 /* Do not generate if the area is stub */
341 /* if this is an intra-area route, this may be suppressed by aggregation
343 if (route
->type
== OSPF6_DEST_TYPE_NETWORK
344 && route
->path
.type
== OSPF6_PATH_TYPE_INTRA
) {
345 /* search for configured address range for the route's area */
347 ospf6_area_lookup(route
->path
.area_id
, area
->ospf6
);
349 range
= ospf6_route_lookup_bestmatch(&route
->prefix
,
350 route_area
->range_table
);
352 /* ranges are ignored when originate backbone routes to transit
354 Otherwise, if ranges are configured, the route is suppressed.
356 if (range
&& !CHECK_FLAG(range
->flag
, OSPF6_ROUTE_REMOVE
)
357 && (route
->path
.area_id
!= OSPF_AREA_BACKBONE
358 || !IS_AREA_TRANSIT(area
))) {
361 "Suppressed by range %pFX of area %s",
362 &range
->prefix
, route_area
->name
);
363 ospf6_abr_delete_route(route
, summary
, summary_table
,
369 /* If this is a configured address range */
370 if (route
->type
== OSPF6_DEST_TYPE_RANGE
) {
371 /* If DoNotAdvertise is set */
372 if (CHECK_FLAG(route
->flag
, OSPF6_ROUTE_DO_NOT_ADVERTISE
)) {
375 "This is the range with DoNotAdvertise set. ignore");
376 ospf6_abr_delete_route(route
, summary
, summary_table
,
381 /* If there are no active prefixes in this range, remove */
382 if (!CHECK_FLAG(route
->flag
, OSPF6_ROUTE_ACTIVE_SUMMARY
)) {
384 zlog_debug("The range is not active. withdraw");
385 ospf6_abr_delete_route(route
, summary
, summary_table
,
391 /* Check export list */
392 if (EXPORT_NAME(area
)) {
393 if (EXPORT_LIST(area
) == NULL
)
395 access_list_lookup(AFI_IP6
, EXPORT_NAME(area
));
397 if (EXPORT_LIST(area
))
398 if (access_list_apply(EXPORT_LIST(area
), &route
->prefix
)
402 "prefix %pFX was denied by export list",
408 /* Check filter-list */
409 if (PREFIX_LIST_OUT(area
))
410 if (prefix_list_apply(PREFIX_LIST_OUT(area
), &route
->prefix
)
414 "prefix %pFX was denied by filter-list out",
419 /* the route is going to be originated. store it in area's summary_table
421 if (summary
== NULL
) {
422 summary
= ospf6_route_copy(route
);
423 summary
->path
.origin
.adv_router
= area
->ospf6
->router_id
;
425 if (route
->type
== OSPF6_DEST_TYPE_ROUTER
) {
426 summary
->path
.origin
.type
=
427 htons(OSPF6_LSTYPE_INTER_ROUTER
);
428 summary
->path
.origin
.id
=
429 ADV_ROUTER_IN_PREFIX(&route
->prefix
);
431 summary
->path
.origin
.type
=
432 htons(OSPF6_LSTYPE_INTER_PREFIX
);
433 summary
->path
.origin
.id
= ospf6_new_ls_id(
434 summary
->path
.origin
.type
,
435 summary
->path
.origin
.adv_router
, area
->lsdb
);
437 summary
= ospf6_route_add(summary
, summary_table
);
439 summary
->type
= route
->type
;
440 monotime(&summary
->changed
);
443 summary
->path
.router_bits
= route
->path
.router_bits
;
444 summary
->path
.options
[0] = route
->path
.options
[0];
445 summary
->path
.options
[1] = route
->path
.options
[1];
446 summary
->path
.options
[2] = route
->path
.options
[2];
447 summary
->path
.prefix_options
= route
->path
.prefix_options
;
448 summary
->path
.area_id
= area
->area_id
;
449 summary
->path
.type
= OSPF6_PATH_TYPE_INTER
;
450 summary
->path
.subtype
= route
->path
.subtype
;
451 summary
->path
.cost
= route
->path
.cost
;
452 /* summary->nexthop[0] = route->nexthop[0]; */
455 memset(buffer
, 0, sizeof(buffer
));
456 lsa_header
= (struct ospf6_lsa_header
*)buffer
;
458 if (route
->type
== OSPF6_DEST_TYPE_ROUTER
) {
459 router_lsa
= (struct ospf6_inter_router_lsa
460 *)((caddr_t
)lsa_header
461 + sizeof(struct ospf6_lsa_header
));
462 p
= (caddr_t
)router_lsa
+ sizeof(struct ospf6_inter_router_lsa
);
464 /* Fill Inter-Area-Router-LSA */
465 router_lsa
->options
[0] = route
->path
.options
[0];
466 router_lsa
->options
[1] = route
->path
.options
[1];
467 router_lsa
->options
[2] = route
->path
.options
[2];
468 OSPF6_ABR_SUMMARY_METRIC_SET(router_lsa
, route
->path
.cost
);
469 router_lsa
->router_id
= ADV_ROUTER_IN_PREFIX(&route
->prefix
);
470 type
= htons(OSPF6_LSTYPE_INTER_ROUTER
);
472 prefix_lsa
= (struct ospf6_inter_prefix_lsa
473 *)((caddr_t
)lsa_header
474 + sizeof(struct ospf6_lsa_header
));
475 p
= (caddr_t
)prefix_lsa
+ sizeof(struct ospf6_inter_prefix_lsa
);
477 /* Fill Inter-Area-Prefix-LSA */
478 OSPF6_ABR_SUMMARY_METRIC_SET(prefix_lsa
, route
->path
.cost
);
479 prefix_lsa
->prefix
.prefix_length
= route
->prefix
.prefixlen
;
480 prefix_lsa
->prefix
.prefix_options
= route
->path
.prefix_options
;
483 memcpy(p
, &route
->prefix
.u
.prefix6
,
484 OSPF6_PREFIX_SPACE(route
->prefix
.prefixlen
));
485 ospf6_prefix_apply_mask(&prefix_lsa
->prefix
);
486 p
+= OSPF6_PREFIX_SPACE(route
->prefix
.prefixlen
);
487 type
= htons(OSPF6_LSTYPE_INTER_PREFIX
);
490 /* Fill LSA Header */
492 lsa_header
->type
= type
;
493 lsa_header
->id
= summary
->path
.origin
.id
;
494 lsa_header
->adv_router
= area
->ospf6
->router_id
;
496 ospf6_new_ls_seqnum(lsa_header
->type
, lsa_header
->id
,
497 lsa_header
->adv_router
, area
->lsdb
);
498 lsa_header
->length
= htons((caddr_t
)p
- (caddr_t
)lsa_header
);
501 ospf6_lsa_checksum(lsa_header
);
504 lsa
= ospf6_lsa_create(lsa_header
);
507 ospf6_lsa_originate_area(lsa
, area
);
512 void ospf6_abr_range_reset_cost(struct ospf6
*ospf6
)
514 struct listnode
*node
, *nnode
;
515 struct ospf6_area
*oa
;
516 struct ospf6_route
*range
;
518 for (ALL_LIST_ELEMENTS(ospf6
->area_list
, node
, nnode
, oa
))
519 for (range
= ospf6_route_head(oa
->range_table
); range
;
520 range
= ospf6_route_next(range
))
521 OSPF6_ABR_RANGE_CLEAR_COST(range
);
524 static inline uint32_t ospf6_abr_range_compute_cost(struct ospf6_route
*range
,
527 struct ospf6_route
*ro
;
530 for (ro
= ospf6_route_match_head(&range
->prefix
, o
->route_table
); ro
;
531 ro
= ospf6_route_match_next(&range
->prefix
, ro
)) {
532 if (ro
->path
.area_id
== range
->path
.area_id
533 && (ro
->path
.type
== OSPF6_PATH_TYPE_INTRA
)
534 && !CHECK_FLAG(ro
->flag
, OSPF6_ROUTE_REMOVE
))
535 cost
= MAX(cost
, ro
->path
.cost
);
542 ospf6_abr_range_summary_needs_update(struct ospf6_route
*range
, uint32_t cost
)
544 int redo_summary
= 0;
546 if (CHECK_FLAG(range
->flag
, OSPF6_ROUTE_REMOVE
)) {
547 UNSET_FLAG(range
->flag
, OSPF6_ROUTE_ACTIVE_SUMMARY
);
549 } else if (CHECK_FLAG(range
->flag
, OSPF6_ROUTE_DO_NOT_ADVERTISE
)) {
550 if (range
->path
.cost
!= 0) {
551 range
->path
.cost
= 0;
555 if ((OSPF6_PATH_COST_IS_CONFIGURED(range
->path
)
556 && range
->path
.cost
!= range
->path
.u
.cost_config
)) {
557 range
->path
.cost
= range
->path
.u
.cost_config
;
558 SET_FLAG(range
->flag
, OSPF6_ROUTE_ACTIVE_SUMMARY
);
560 } else if (!OSPF6_PATH_COST_IS_CONFIGURED(range
->path
)
561 && range
->path
.cost
!= cost
) {
562 range
->path
.cost
= cost
;
563 SET_FLAG(range
->flag
, OSPF6_ROUTE_ACTIVE_SUMMARY
);
566 } else if (CHECK_FLAG(range
->flag
, OSPF6_ROUTE_ACTIVE_SUMMARY
)) {
567 /* Cost is zero, meaning no active range */
568 UNSET_FLAG(range
->flag
, OSPF6_ROUTE_ACTIVE_SUMMARY
);
569 range
->path
.cost
= OSPF_AREA_RANGE_COST_UNSPEC
;
573 return (redo_summary
);
576 static void ospf6_abr_range_update(struct ospf6_route
*range
,
580 struct listnode
*node
, *nnode
;
581 struct ospf6_area
*oa
;
582 int summary_orig
= 0;
584 assert(range
->type
== OSPF6_DEST_TYPE_RANGE
);
586 /* update range's cost and active flag */
587 cost
= ospf6_abr_range_compute_cost(range
, ospf6
);
589 /* Non-zero cost is a proxy for active longer prefixes in this range.
590 * If there are active routes covered by this range AND either the
592 * cost has changed or the summarized cost has changed then redo
594 * Alternately, if there are no longer active prefixes and there are
595 * summary announcements, withdraw those announcements.
597 * The don't advertise code relies on the path.cost being set to UNSPEC
599 * work the first time. Subsequent times the path.cost is not 0 anyway
601 * were active ranges.
604 if (ospf6_abr_range_summary_needs_update(range
, cost
)) {
605 for (ALL_LIST_ELEMENTS(ospf6
->area_list
, node
, nnode
, oa
))
607 ospf6_abr_originate_summary_to_area(range
, oa
);
609 if (CHECK_FLAG(range
->flag
, OSPF6_ROUTE_ACTIVE_SUMMARY
)
611 if (!CHECK_FLAG(range
->flag
,
612 OSPF6_ROUTE_BLACKHOLE_ADDED
)) {
613 if (IS_OSPF6_DEBUG_ABR
)
614 zlog_debug("Add discard route");
616 ospf6_zebra_add_discard(range
, ospf6
);
619 /* Summary removed or no summary generated as no
621 if (CHECK_FLAG(range
->flag
,
622 OSPF6_ROUTE_BLACKHOLE_ADDED
)) {
623 if (IS_OSPF6_DEBUG_ABR
)
624 zlog_debug("Delete discard route");
626 ospf6_zebra_delete_discard(range
, ospf6
);
632 void ospf6_abr_originate_summary(struct ospf6_route
*route
, struct ospf6
*ospf6
)
634 struct listnode
*node
, *nnode
;
635 struct ospf6_area
*oa
;
636 struct ospf6_route
*range
= NULL
;
639 if (route
->type
== OSPF6_DEST_TYPE_NETWORK
) {
640 oa
= ospf6_area_lookup(route
->path
.area_id
, ospf6
);
642 zlog_err("OSPFv6 area lookup failed");
646 range
= ospf6_route_lookup_bestmatch(&route
->prefix
,
649 ospf6_abr_range_update(range
, ospf6
);
653 for (ALL_LIST_ELEMENTS(ospf6
->area_list
, node
, nnode
, oa
))
654 ospf6_abr_originate_summary_to_area(route
, oa
);
657 void ospf6_abr_defaults_to_stub(struct ospf6
*o
)
659 struct listnode
*node
, *nnode
;
660 struct ospf6_area
*oa
;
661 struct ospf6_route
*def
, *route
;
666 def
= ospf6_route_create();
667 def
->type
= OSPF6_DEST_TYPE_NETWORK
;
668 def
->prefix
.family
= AF_INET6
;
669 def
->prefix
.prefixlen
= 0;
670 memset(&def
->prefix
.u
.prefix6
, 0, sizeof(struct in6_addr
));
671 def
->type
= OSPF6_DEST_TYPE_NETWORK
;
672 def
->path
.type
= OSPF6_PATH_TYPE_INTER
;
673 def
->path
.subtype
= OSPF6_PATH_SUBTYPE_DEFAULT_RT
;
674 def
->path
.area_id
= o
->backbone
->area_id
;
676 for (ALL_LIST_ELEMENTS(o
->area_list
, node
, nnode
, oa
)) {
677 if (!IS_AREA_STUB(oa
)) {
678 /* withdraw defaults when an area switches from stub to
680 route
= ospf6_route_lookup(&def
->prefix
,
683 && (route
->path
.subtype
== def
->path
.subtype
)) {
684 if (IS_OSPF6_DEBUG_ABR
)
686 "Withdrawing default route from non-stubby area %s",
688 SET_FLAG(def
->flag
, OSPF6_ROUTE_REMOVE
);
689 ospf6_abr_originate_summary_to_area(def
, oa
);
692 /* announce defaults to stubby areas */
693 if (IS_OSPF6_DEBUG_ABR
)
695 "Announcing default route into stubby area %s",
697 UNSET_FLAG(def
->flag
, OSPF6_ROUTE_REMOVE
);
698 ospf6_abr_originate_summary_to_area(def
, oa
);
701 ospf6_route_delete(def
);
704 void ospf6_abr_old_path_update(struct ospf6_route
*old_route
,
705 struct ospf6_route
*route
,
706 struct ospf6_route_table
*table
)
708 struct ospf6_path
*o_path
= NULL
;
709 struct listnode
*anode
, *anext
;
710 struct listnode
*nnode
, *rnode
, *rnext
;
711 struct ospf6_nexthop
*nh
, *rnh
;
713 for (ALL_LIST_ELEMENTS(old_route
->paths
, anode
, anext
, o_path
)) {
714 if (o_path
->area_id
!= route
->path
.area_id
||
715 (memcmp(&(o_path
)->origin
, &(route
)->path
.origin
,
716 sizeof(struct ospf6_ls_origin
)) != 0))
719 if ((o_path
->cost
== route
->path
.cost
) &&
720 (o_path
->u
.cost_e2
== route
->path
.u
.cost_e2
))
723 for (ALL_LIST_ELEMENTS_RO(o_path
->nh_list
, nnode
, nh
)) {
724 for (ALL_LIST_ELEMENTS(old_route
->nh_list
, rnode
,
726 if (!ospf6_nexthop_is_same(rnh
, nh
))
728 listnode_delete(old_route
->nh_list
, rnh
);
729 ospf6_nexthop_delete(rnh
);
734 listnode_delete(old_route
->paths
, o_path
);
735 ospf6_path_free(o_path
);
737 for (ALL_LIST_ELEMENTS(old_route
->paths
, anode
,
739 ospf6_merge_nexthops(old_route
->nh_list
,
743 if (IS_OSPF6_DEBUG_ABR
|| IS_OSPF6_DEBUG_EXAMIN(INTER_PREFIX
))
744 zlog_debug("%s: paths %u nh %u", __func__
,
746 ? listcount(old_route
->paths
)
749 ? listcount(old_route
->nh_list
)
753 (*table
->hook_add
)(old_route
);
755 if (old_route
->path
.origin
.id
== route
->path
.origin
.id
&&
756 old_route
->path
.origin
.adv_router
==
757 route
->path
.origin
.adv_router
) {
758 struct ospf6_path
*h_path
;
760 h_path
= (struct ospf6_path
*)
761 listgetdata(listhead(old_route
->paths
));
762 old_route
->path
.origin
.type
= h_path
->origin
.type
;
763 old_route
->path
.origin
.id
= h_path
->origin
.id
;
764 old_route
->path
.origin
.adv_router
=
765 h_path
->origin
.adv_router
;
770 void ospf6_abr_old_route_remove(struct ospf6_lsa
*lsa
, struct ospf6_route
*old
,
771 struct ospf6_route_table
*table
)
773 if (listcount(old
->paths
) > 1) {
774 struct listnode
*anode
, *anext
, *nnode
, *rnode
, *rnext
;
775 struct ospf6_path
*o_path
;
776 struct ospf6_nexthop
*nh
, *rnh
;
777 bool nh_updated
= false;
779 for (ALL_LIST_ELEMENTS(old
->paths
, anode
, anext
, o_path
)) {
780 if (o_path
->origin
.adv_router
!= lsa
->header
->adv_router
781 && o_path
->origin
.id
!= lsa
->header
->id
)
783 for (ALL_LIST_ELEMENTS_RO(o_path
->nh_list
, nnode
, nh
)) {
784 for (ALL_LIST_ELEMENTS(old
->nh_list
,
785 rnode
, rnext
, rnh
)) {
786 if (!ospf6_nexthop_is_same(rnh
, nh
))
788 listnode_delete(old
->nh_list
, rnh
);
789 ospf6_nexthop_delete(rnh
);
792 listnode_delete(old
->paths
, o_path
);
793 ospf6_path_free(o_path
);
798 if (listcount(old
->paths
)) {
799 if (IS_OSPF6_DEBUG_ABR
800 || IS_OSPF6_DEBUG_EXAMIN(INTER_PREFIX
))
801 zlog_debug("%s: old %pFX updated nh %u",
802 __func__
, &old
->prefix
,
803 old
->nh_list
? listcount(
808 (*table
->hook_add
)(old
);
810 if ((old
->path
.origin
.id
== lsa
->header
->id
) &&
811 (old
->path
.origin
.adv_router
812 == lsa
->header
->adv_router
)) {
813 struct ospf6_path
*h_path
;
815 h_path
= (struct ospf6_path
*)
817 listhead(old
->paths
));
818 old
->path
.origin
.type
=
820 old
->path
.origin
.id
= h_path
->origin
.id
;
821 old
->path
.origin
.adv_router
=
822 h_path
->origin
.adv_router
;
825 ospf6_route_remove(old
, table
);
828 ospf6_route_remove(old
, table
);
831 /* RFC 2328 16.2. Calculating the inter-area routes */
832 void ospf6_abr_examin_summary(struct ospf6_lsa
*lsa
, struct ospf6_area
*oa
)
834 struct prefix prefix
, abr_prefix
;
835 struct ospf6_route_table
*table
= NULL
;
836 struct ospf6_route
*range
, *route
, *old
= NULL
, *old_route
;
837 struct ospf6_route
*abr_entry
;
839 char options
[3] = {0, 0, 0};
840 uint8_t prefix_options
= 0;
842 uint8_t router_bits
= 0;
843 char buf
[PREFIX2STR_BUFFER
];
845 struct ospf6_inter_prefix_lsa
*prefix_lsa
= NULL
;
846 struct ospf6_inter_router_lsa
*router_lsa
= NULL
;
847 bool old_entry_updated
= false;
848 struct ospf6_path
*path
, *o_path
, *ecmp_path
;
849 struct listnode
*anode
;
851 memset(&prefix
, 0, sizeof(prefix
));
853 if (lsa
->header
->type
== htons(OSPF6_LSTYPE_INTER_PREFIX
)) {
854 if (IS_OSPF6_DEBUG_EXAMIN(INTER_PREFIX
)) {
856 zlog_debug("%s: Examin %s in area %s", __func__
,
857 lsa
->name
, oa
->name
);
861 (struct ospf6_inter_prefix_lsa
*)OSPF6_LSA_HEADER_END(
863 prefix
.family
= AF_INET6
;
864 prefix
.prefixlen
= prefix_lsa
->prefix
.prefix_length
;
865 ospf6_prefix_in6_addr(&prefix
.u
.prefix6
, prefix_lsa
,
866 &prefix_lsa
->prefix
);
868 prefix2str(&prefix
, buf
, sizeof(buf
));
869 table
= oa
->ospf6
->route_table
;
870 type
= OSPF6_DEST_TYPE_NETWORK
;
871 prefix_options
= prefix_lsa
->prefix
.prefix_options
;
872 cost
= OSPF6_ABR_SUMMARY_METRIC(prefix_lsa
);
873 } else if (lsa
->header
->type
== htons(OSPF6_LSTYPE_INTER_ROUTER
)) {
874 if (IS_OSPF6_DEBUG_EXAMIN(INTER_ROUTER
)) {
876 zlog_debug("%s: Examin %s in area %s", __func__
,
877 lsa
->name
, oa
->name
);
881 (struct ospf6_inter_router_lsa
*)OSPF6_LSA_HEADER_END(
883 ospf6_linkstate_prefix(router_lsa
->router_id
, htonl(0),
886 inet_ntop(AF_INET
, &router_lsa
->router_id
, buf
,
889 table
= oa
->ospf6
->brouter_table
;
890 type
= OSPF6_DEST_TYPE_ROUTER
;
891 options
[0] = router_lsa
->options
[0];
892 options
[1] = router_lsa
->options
[1];
893 options
[2] = router_lsa
->options
[2];
894 cost
= OSPF6_ABR_SUMMARY_METRIC(router_lsa
);
895 SET_FLAG(router_bits
, OSPF6_ROUTER_BIT_E
);
899 /* Find existing route */
900 route
= ospf6_route_lookup(&prefix
, table
);
902 ospf6_route_lock(route
);
903 while (route
&& ospf6_route_is_prefix(&prefix
, route
)) {
904 if (route
->path
.area_id
== oa
->area_id
905 && route
->path
.origin
.type
== lsa
->header
->type
906 && !CHECK_FLAG(route
->flag
, OSPF6_ROUTE_WAS_REMOVED
)) {
907 /* LSA adv. router could be part of route's
908 * paths list. Find the existing path and set
911 if (listcount(route
->paths
) > 1) {
912 for (ALL_LIST_ELEMENTS_RO(route
->paths
, anode
,
914 if (o_path
->origin
.id
== lsa
->header
->id
915 && o_path
->origin
.adv_router
==
916 lsa
->header
->adv_router
) {
921 "%s: old entry found in paths, adv_router %pI4",
923 &o_path
->origin
.adv_router
);
928 } else if (route
->path
.origin
.id
== lsa
->header
->id
&&
929 route
->path
.origin
.adv_router
==
930 lsa
->header
->adv_router
)
933 route
= ospf6_route_next(route
);
936 ospf6_route_unlock(route
);
938 /* (1) if cost == LSInfinity or if the LSA is MaxAge */
939 if (cost
== OSPF_LS_INFINITY
) {
941 zlog_debug("cost is LS_INFINITY, ignore");
943 ospf6_abr_old_route_remove(lsa
, old
, table
);
946 if (OSPF6_LSA_IS_MAXAGE(lsa
)) {
948 zlog_debug("%s: LSA %s is MaxAge, ignore", __func__
,
951 ospf6_abr_old_route_remove(lsa
, old
, table
);
955 /* (2) if the LSA is self-originated, ignore */
956 if (lsa
->header
->adv_router
== oa
->ospf6
->router_id
) {
958 zlog_debug("LSA %s is self-originated, ignore",
961 ospf6_route_remove(old
, table
);
965 /* (3) if the prefix is equal to an active configured address range */
966 /* or if the NU bit is set in the prefix */
967 if (lsa
->header
->type
== htons(OSPF6_LSTYPE_INTER_PREFIX
)) {
968 /* must have been set in previous block */
971 range
= ospf6_route_lookup(&prefix
, oa
->range_table
);
975 "Prefix is equal to address range, ignore");
977 ospf6_route_remove(old
, table
);
981 if (CHECK_FLAG(prefix_lsa
->prefix
.prefix_options
,
982 OSPF6_PREFIX_OPTION_NU
)
983 || CHECK_FLAG(prefix_lsa
->prefix
.prefix_options
,
984 OSPF6_PREFIX_OPTION_LA
)) {
986 zlog_debug("Prefix has NU/LA bit set, ignore");
988 ospf6_route_remove(old
, table
);
993 if (lsa
->header
->type
== htons(OSPF6_LSTYPE_INTER_ROUTER
)) {
994 /* To pass test suites */
996 if (!OSPF6_OPT_ISSET(router_lsa
->options
, OSPF6_OPT_R
)
997 || !OSPF6_OPT_ISSET(router_lsa
->options
, OSPF6_OPT_V6
)) {
999 zlog_debug("Prefix has NU/LA bit set, ignore");
1001 ospf6_route_remove(old
, table
);
1005 /* Avoid infinite recursion if someone has maliciously announced
1007 Inter-Router LSA for an ABR
1009 if (lsa
->header
->adv_router
== router_lsa
->router_id
) {
1012 "Ignorning Inter-Router LSA for an ABR (%s)",
1015 ospf6_route_remove(old
, table
);
1021 /* (4) if the routing table entry for the ABR does not exist */
1022 ospf6_linkstate_prefix(lsa
->header
->adv_router
, htonl(0), &abr_prefix
);
1023 abr_entry
= ospf6_route_lookup(&abr_prefix
, oa
->ospf6
->brouter_table
);
1024 if (abr_entry
== NULL
|| abr_entry
->path
.area_id
!= oa
->area_id
1025 || CHECK_FLAG(abr_entry
->flag
, OSPF6_ROUTE_REMOVE
)
1026 || !CHECK_FLAG(abr_entry
->path
.router_bits
, OSPF6_ROUTER_BIT_B
)) {
1029 "%s: ABR router entry does not exist, ignore",
1032 if (old
->type
== OSPF6_DEST_TYPE_ROUTER
&&
1033 oa
->intra_brouter_calc
) {
1036 "%s: intra_brouter_calc is on, skip brouter remove: %s (%p)",
1037 __func__
, buf
, (void *)old
);
1041 "%s: remove old entry: %s %p ",
1042 __func__
, buf
, (void *)old
);
1043 ospf6_route_remove(old
, table
);
1049 /* Check import list */
1050 if (IMPORT_NAME(oa
)) {
1051 if (IMPORT_LIST(oa
) == NULL
)
1053 access_list_lookup(AFI_IP6
, IMPORT_NAME(oa
));
1055 if (IMPORT_LIST(oa
))
1056 if (access_list_apply(IMPORT_LIST(oa
), &prefix
)
1060 "Prefix was denied by import-list");
1062 ospf6_route_remove(old
, table
);
1067 /* Check input prefix-list */
1068 if (PREFIX_LIST_IN(oa
)) {
1069 if (prefix_list_apply(PREFIX_LIST_IN(oa
), &prefix
)
1072 zlog_debug("Prefix was denied by prefix-list");
1074 ospf6_route_remove(old
, table
);
1079 /* (5),(6): the path preference is handled by the sorting
1080 in the routing table. Always install the path by substituting
1081 old route (if any). */
1082 route
= ospf6_route_create();
1085 route
->prefix
= prefix
;
1086 route
->path
.origin
.type
= lsa
->header
->type
;
1087 route
->path
.origin
.id
= lsa
->header
->id
;
1088 route
->path
.origin
.adv_router
= lsa
->header
->adv_router
;
1089 route
->path
.router_bits
= router_bits
;
1090 route
->path
.options
[0] = options
[0];
1091 route
->path
.options
[1] = options
[1];
1092 route
->path
.options
[2] = options
[2];
1093 route
->path
.prefix_options
= prefix_options
;
1094 route
->path
.area_id
= oa
->area_id
;
1095 route
->path
.type
= OSPF6_PATH_TYPE_INTER
;
1096 route
->path
.cost
= abr_entry
->path
.cost
+ cost
;
1098 /* copy brouter rechable nexthops into the route. */
1099 ospf6_route_copy_nexthops(route
, abr_entry
);
1101 /* (7) If the routes are identical, copy the next hops over to existing
1102 route. ospf6's route table implementation will otherwise string both
1103 routes, but keep the older one as the best route since the routes
1106 old
= ospf6_route_lookup(&prefix
, table
);
1108 for (old_route
= old
; old_route
; old_route
= old_route
->next
) {
1109 if (!ospf6_route_is_same(old_route
, route
) ||
1110 (old_route
->type
!= route
->type
) ||
1111 (old_route
->path
.type
!= route
->path
.type
))
1114 if ((ospf6_route_cmp(route
, old_route
) != 0)) {
1117 "%s: old %p %pFX cost %u new route cost %u are not same",
1118 __func__
, (void *)old_route
, &prefix
,
1119 old_route
->path
.cost
, route
->path
.cost
);
1121 /* Check new route's adv. router is same in one of
1122 * the paths with differed cost, if so remove the
1123 * old path as later new route will be added.
1125 if (listcount(old_route
->paths
) > 1)
1126 ospf6_abr_old_path_update(old_route
, route
,
1131 ospf6_route_merge_nexthops(old_route
, route
);
1132 old_entry_updated
= true;
1134 for (ALL_LIST_ELEMENTS_RO(old_route
->paths
, anode
,
1136 if (o_path
->area_id
== route
->path
.area_id
&&
1137 (memcmp(&(o_path
)->origin
, &(route
)->path
.origin
,
1138 sizeof(struct ospf6_ls_origin
)) == 0))
1142 /* New adv. router for a existing path add to paths list */
1143 if (o_path
== NULL
) {
1144 ecmp_path
= ospf6_path_dup(&route
->path
);
1146 /* Add a nh_list to new ecmp path */
1147 ospf6_copy_nexthops(ecmp_path
->nh_list
, route
->nh_list
);
1149 /* Add the new path to route's path list */
1150 listnode_add_sort(old_route
->paths
, ecmp_path
);
1154 "%s: route %pFX cost %u another path %pI4 added with nh %u, effective paths %u nh %u",
1155 __func__
, &route
->prefix
,
1156 old_route
->path
.cost
,
1157 &ecmp_path
->origin
.adv_router
,
1158 listcount(ecmp_path
->nh_list
),
1160 ? listcount(old_route
->paths
)
1162 listcount(old_route
->nh_list
));
1165 struct ospf6_route
*tmp_route
= ospf6_route_create();
1167 ospf6_copy_nexthops(tmp_route
->nh_list
,
1170 if (ospf6_route_cmp_nexthops(tmp_route
, route
) != 0) {
1171 /* adv. router exists in the list, update nhs */
1172 list_delete_all_node(o_path
->nh_list
);
1173 ospf6_copy_nexthops(o_path
->nh_list
,
1175 ospf6_route_delete(tmp_route
);
1177 /* adv. router has no change in nhs */
1178 old_entry_updated
= false;
1179 ospf6_route_delete(tmp_route
);
1186 "%s: Update route: %s %p old cost %u new cost %u nh %u",
1187 __func__
, buf
, (void *)old_route
,
1188 old_route
->path
.cost
, route
->path
.cost
,
1189 listcount(route
->nh_list
));
1191 /* For Inter-Prefix route: Update RIB/FIB,
1192 * For Inter-Router trigger summary update
1194 if (table
->hook_add
)
1195 (*table
->hook_add
)(old_route
);
1197 /* Delete new route */
1198 ospf6_route_delete(route
);
1202 if (old_entry_updated
== false) {
1205 "%s: Install route: %s cost %u nh %u adv_router %pI4",
1206 __func__
, buf
, route
->path
.cost
,
1207 listcount(route
->nh_list
),
1208 &route
->path
.origin
.adv_router
);
1211 path
= ospf6_path_dup(&route
->path
);
1212 ospf6_copy_nexthops(path
->nh_list
, abr_entry
->nh_list
);
1213 listnode_add_sort(route
->paths
, path
);
1214 /* ospf6_ia_add_nw_route (table, &prefix, route); */
1215 ospf6_route_add(route
, table
);
1219 void ospf6_abr_examin_brouter(uint32_t router_id
, struct ospf6_route
*route
,
1220 struct ospf6
*ospf6
)
1222 struct ospf6_lsa
*lsa
;
1223 struct ospf6_area
*oa
;
1226 oa
= ospf6_area_lookup(route
->path
.area_id
, ospf6
);
1228 * It is possible to designate a non backbone
1229 * area first. If that is the case safely
1230 * fall out of this function.
1235 type
= htons(OSPF6_LSTYPE_INTER_ROUTER
);
1236 for (ALL_LSDB_TYPED_ADVRTR(oa
->lsdb
, type
, router_id
, lsa
))
1237 ospf6_abr_examin_summary(lsa
, oa
);
1239 type
= htons(OSPF6_LSTYPE_INTER_PREFIX
);
1240 for (ALL_LSDB_TYPED_ADVRTR(oa
->lsdb
, type
, router_id
, lsa
))
1241 ospf6_abr_examin_summary(lsa
, oa
);
1244 void ospf6_abr_reimport(struct ospf6_area
*oa
)
1246 struct ospf6_lsa
*lsa
;
1249 type
= htons(OSPF6_LSTYPE_INTER_ROUTER
);
1250 for (ALL_LSDB_TYPED(oa
->lsdb
, type
, lsa
))
1251 ospf6_abr_examin_summary(lsa
, oa
);
1253 type
= htons(OSPF6_LSTYPE_INTER_PREFIX
);
1254 for (ALL_LSDB_TYPED(oa
->lsdb
, type
, lsa
))
1255 ospf6_abr_examin_summary(lsa
, oa
);
1258 void ospf6_abr_prefix_resummarize(struct ospf6
*o
)
1260 struct ospf6_route
*route
;
1262 if (IS_OSPF6_DEBUG_ABR
)
1263 zlog_debug("Re-examining Inter-Prefix Summaries");
1265 for (route
= ospf6_route_head(o
->route_table
); route
;
1266 route
= ospf6_route_next(route
))
1267 ospf6_abr_originate_summary(route
, o
);
1269 if (IS_OSPF6_DEBUG_ABR
)
1270 zlog_debug("Finished re-examining Inter-Prefix Summaries");
1274 /* Display functions */
1275 static char *ospf6_inter_area_prefix_lsa_get_prefix_str(struct ospf6_lsa
*lsa
,
1276 char *buf
, int buflen
,
1279 struct ospf6_inter_prefix_lsa
*prefix_lsa
;
1280 struct in6_addr in6
;
1284 (struct ospf6_inter_prefix_lsa
*)OSPF6_LSA_HEADER_END(
1287 ospf6_prefix_in6_addr(&in6
, prefix_lsa
, &prefix_lsa
->prefix
);
1289 inet_ntop(AF_INET6
, &in6
, buf
, buflen
);
1290 sprintf(&buf
[strlen(buf
)], "/%d",
1291 prefix_lsa
->prefix
.prefix_length
);
1298 static int ospf6_inter_area_prefix_lsa_show(struct vty
*vty
,
1299 struct ospf6_lsa
*lsa
,
1300 json_object
*json_obj
,
1303 struct ospf6_inter_prefix_lsa
*prefix_lsa
;
1304 char buf
[INET6_ADDRSTRLEN
];
1306 prefix_lsa
= (struct ospf6_inter_prefix_lsa
*)OSPF6_LSA_HEADER_END(
1310 json_object_int_add(
1312 (unsigned long)OSPF6_ABR_SUMMARY_METRIC(prefix_lsa
));
1313 ospf6_prefix_options_printbuf(prefix_lsa
->prefix
.prefix_options
,
1315 json_object_string_add(json_obj
, "prefixOptions", buf
);
1316 json_object_string_add(
1318 ospf6_inter_area_prefix_lsa_get_prefix_str(
1319 lsa
, buf
, sizeof(buf
), 0));
1321 vty_out(vty
, " Metric: %lu\n",
1322 (unsigned long)OSPF6_ABR_SUMMARY_METRIC(prefix_lsa
));
1324 ospf6_prefix_options_printbuf(prefix_lsa
->prefix
.prefix_options
,
1326 vty_out(vty
, " Prefix Options: %s\n", buf
);
1328 vty_out(vty
, " Prefix: %s\n",
1329 ospf6_inter_area_prefix_lsa_get_prefix_str(
1330 lsa
, buf
, sizeof(buf
), 0));
1336 static char *ospf6_inter_area_router_lsa_get_prefix_str(struct ospf6_lsa
*lsa
,
1337 char *buf
, int buflen
,
1340 struct ospf6_inter_router_lsa
*router_lsa
;
1344 (struct ospf6_inter_router_lsa
*)OSPF6_LSA_HEADER_END(
1349 inet_ntop(AF_INET
, &router_lsa
->router_id
, buf
, buflen
);
1355 static int ospf6_inter_area_router_lsa_show(struct vty
*vty
,
1356 struct ospf6_lsa
*lsa
,
1357 json_object
*json_obj
,
1360 struct ospf6_inter_router_lsa
*router_lsa
;
1363 router_lsa
= (struct ospf6_inter_router_lsa
*)OSPF6_LSA_HEADER_END(
1366 ospf6_options_printbuf(router_lsa
->options
, buf
, sizeof(buf
));
1368 json_object_string_add(json_obj
, "options", buf
);
1369 json_object_int_add(
1371 (unsigned long)OSPF6_ABR_SUMMARY_METRIC(router_lsa
));
1373 vty_out(vty
, " Options: %s\n", buf
);
1374 vty_out(vty
, " Metric: %lu\n",
1375 (unsigned long)OSPF6_ABR_SUMMARY_METRIC(router_lsa
));
1378 inet_ntop(AF_INET
, &router_lsa
->router_id
, buf
, sizeof(buf
));
1380 json_object_string_add(json_obj
, "destinationRouterId", buf
);
1382 vty_out(vty
, " Destination Router ID: %s\n", buf
);
1387 /* Debug commands */
1388 DEFUN (debug_ospf6_abr
,
1389 debug_ospf6_abr_cmd
,
1393 "Debug OSPFv3 ABR function\n"
1396 OSPF6_DEBUG_ABR_ON();
1400 DEFUN (no_debug_ospf6_abr
,
1401 no_debug_ospf6_abr_cmd
,
1402 "no debug ospf6 abr",
1406 "Debug OSPFv3 ABR function\n"
1409 OSPF6_DEBUG_ABR_OFF();
1413 int config_write_ospf6_debug_abr(struct vty
*vty
)
1415 if (IS_OSPF6_DEBUG_ABR
)
1416 vty_out(vty
, "debug ospf6 abr\n");
1420 void install_element_ospf6_debug_abr(void)
1422 install_element(ENABLE_NODE
, &debug_ospf6_abr_cmd
);
1423 install_element(ENABLE_NODE
, &no_debug_ospf6_abr_cmd
);
1424 install_element(CONFIG_NODE
, &debug_ospf6_abr_cmd
);
1425 install_element(CONFIG_NODE
, &no_debug_ospf6_abr_cmd
);
1428 static struct ospf6_lsa_handler inter_prefix_handler
= {
1429 .lh_type
= OSPF6_LSTYPE_INTER_PREFIX
,
1430 .lh_name
= "Inter-Prefix",
1431 .lh_short_name
= "IAP",
1432 .lh_show
= ospf6_inter_area_prefix_lsa_show
,
1433 .lh_get_prefix_str
= ospf6_inter_area_prefix_lsa_get_prefix_str
,
1436 static struct ospf6_lsa_handler inter_router_handler
= {
1437 .lh_type
= OSPF6_LSTYPE_INTER_ROUTER
,
1438 .lh_name
= "Inter-Router",
1439 .lh_short_name
= "IAR",
1440 .lh_show
= ospf6_inter_area_router_lsa_show
,
1441 .lh_get_prefix_str
= ospf6_inter_area_router_lsa_get_prefix_str
,
1444 void ospf6_abr_init(void)
1446 ospf6_install_lsa_handler(&inter_prefix_handler
);
1447 ospf6_install_lsa_handler(&inter_router_handler
);