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");
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 &(ADV_ROUTER_IN_PREFIX(&route
->prefix
)), buf
,
202 "%s: Skipping ASBR announcement for ABR (%s)",
208 if (route
->type
== OSPF6_DEST_TYPE_ROUTER
) {
209 if (IS_OSPF6_DEBUG_ABR
210 || IS_OSPF6_DEBUG_ORIGINATE(INTER_ROUTER
)) {
213 &(ADV_ROUTER_IN_PREFIX(&route
->prefix
)), buf
,
215 zlog_debug("Originating summary in area %s for ASBR %s",
218 summary_table
= area
->summary_router
;
220 if (IS_OSPF6_DEBUG_ABR
221 || IS_OSPF6_DEBUG_ORIGINATE(INTER_PREFIX
))
224 if (route
->type
== OSPF6_DEST_TYPE_NETWORK
&&
225 route
->path
.origin
.type
==
226 htons(OSPF6_LSTYPE_INTER_PREFIX
)) {
227 if (!CHECK_FLAG(route
->flag
, OSPF6_ROUTE_BEST
)) {
230 &(ADV_ROUTER_IN_PREFIX(
231 &route
->prefix
)), buf
,
234 "%s: route %s with cost %u is not best, ignore."
235 , __PRETTY_FUNCTION__
, buf
,
243 prefix2str(&route
->prefix
, buf
, sizeof(buf
));
244 zlog_debug("Originating summary in area %s for %s cost %u",
245 area
->name
, buf
, route
->path
.cost
);
247 summary_table
= area
->summary_prefix
;
250 summary
= ospf6_route_lookup(&route
->prefix
, summary_table
);
252 old
= ospf6_lsdb_lookup(summary
->path
.origin
.type
,
253 summary
->path
.origin
.id
,
254 area
->ospf6
->router_id
, area
->lsdb
);
256 /* if this route has just removed, remove corresponding LSA */
257 if (CHECK_FLAG(route
->flag
, OSPF6_ROUTE_REMOVE
)) {
260 "The route has just removed, purge previous LSA");
262 if (route
->type
== OSPF6_DEST_TYPE_RANGE
) {
263 /* Whether the route have active longer prefix */
264 if (!CHECK_FLAG(route
->flag
,
265 OSPF6_ROUTE_ACTIVE_SUMMARY
)) {
268 "The range is not active. withdraw");
270 ospf6_abr_delete_route(route
, summary
,
274 ospf6_lsa_purge(old
);
279 if ((route
->type
== OSPF6_DEST_TYPE_ROUTER
) && IS_AREA_STUB(area
)) {
282 "Area has been stubbed, purge Inter-Router LSA");
284 ospf6_abr_delete_route(route
, summary
, summary_table
, old
);
289 && (route
->path
.subtype
!= OSPF6_PATH_SUBTYPE_DEFAULT_RT
)) {
291 zlog_debug("Area has been stubbed, purge prefix LSA");
293 ospf6_abr_delete_route(route
, summary
, summary_table
, old
);
297 /* do not generate if the route cost is greater or equal to LSInfinity
299 if (route
->path
.cost
>= OSPF_LS_INFINITY
) {
300 /* When we're clearing the range route because all active
302 * under the range are gone, we set the range's cost to
303 * OSPF_AREA_RANGE_COST_UNSPEC, which is > OSPF_LS_INFINITY. We
304 * don't want to trigger the code here for that. This code is
306 * handling routes that have gone to infinity. The range removal
310 if ((route
->type
!= OSPF6_DEST_TYPE_RANGE
)
311 && (route
->path
.cost
!= OSPF_AREA_RANGE_COST_UNSPEC
)) {
314 "The cost exceeds LSInfinity, withdraw");
316 ospf6_lsa_purge(old
);
321 /* if this is a route to ASBR */
322 if (route
->type
== OSPF6_DEST_TYPE_ROUTER
) {
323 /* Only the prefered best path is considered */
324 if (!CHECK_FLAG(route
->flag
, OSPF6_ROUTE_BEST
)) {
327 "This is the secondary path to the ASBR, ignore");
328 ospf6_abr_delete_route(route
, summary
, summary_table
,
333 /* Do not generate if the area is stub */
337 /* if this is an intra-area route, this may be suppressed by aggregation
339 if (route
->type
== OSPF6_DEST_TYPE_NETWORK
340 && route
->path
.type
== OSPF6_PATH_TYPE_INTRA
) {
341 /* search for configured address range for the route's area */
343 ospf6_area_lookup(route
->path
.area_id
, area
->ospf6
);
345 range
= ospf6_route_lookup_bestmatch(&route
->prefix
,
346 route_area
->range_table
);
348 /* ranges are ignored when originate backbone routes to transit
350 Otherwise, if ranges are configured, the route is suppressed.
352 if (range
&& !CHECK_FLAG(range
->flag
, OSPF6_ROUTE_REMOVE
)
353 && (route
->path
.area_id
!= OSPF_AREA_BACKBONE
354 || !IS_AREA_TRANSIT(area
))) {
356 prefix2str(&range
->prefix
, buf
, sizeof(buf
));
357 zlog_debug("Suppressed by range %s of area %s",
358 buf
, route_area
->name
);
360 ospf6_abr_delete_route(route
, summary
, summary_table
,
366 /* If this is a configured address range */
367 if (route
->type
== OSPF6_DEST_TYPE_RANGE
) {
368 /* If DoNotAdvertise is set */
369 if (CHECK_FLAG(route
->flag
, OSPF6_ROUTE_DO_NOT_ADVERTISE
)) {
372 "This is the range with DoNotAdvertise set. ignore");
373 ospf6_abr_delete_route(route
, summary
, summary_table
,
378 /* If there are no active prefixes in this range, remove */
379 if (!CHECK_FLAG(route
->flag
, OSPF6_ROUTE_ACTIVE_SUMMARY
)) {
381 zlog_debug("The range is not active. withdraw");
382 ospf6_abr_delete_route(route
, summary
, summary_table
,
388 /* Check export list */
389 if (EXPORT_NAME(area
)) {
390 if (EXPORT_LIST(area
) == NULL
)
392 access_list_lookup(AFI_IP6
, EXPORT_NAME(area
));
394 if (EXPORT_LIST(area
))
395 if (access_list_apply(EXPORT_LIST(area
), &route
->prefix
)
399 &(ADV_ROUTER_IN_PREFIX(
403 "prefix %s was denied by export list",
410 /* Check filter-list */
411 if (PREFIX_LIST_OUT(area
))
412 if (prefix_list_apply(PREFIX_LIST_OUT(area
), &route
->prefix
)
417 &(ADV_ROUTER_IN_PREFIX(&route
->prefix
)),
420 "prefix %s was denied by filter-list out",
426 /* the route is going to be originated. store it in area's summary_table
428 if (summary
== NULL
) {
429 summary
= ospf6_route_copy(route
);
430 summary
->path
.origin
.adv_router
= area
->ospf6
->router_id
;
432 if (route
->type
== OSPF6_DEST_TYPE_ROUTER
) {
433 summary
->path
.origin
.type
=
434 htons(OSPF6_LSTYPE_INTER_ROUTER
);
435 summary
->path
.origin
.id
=
436 ADV_ROUTER_IN_PREFIX(&route
->prefix
);
438 summary
->path
.origin
.type
=
439 htons(OSPF6_LSTYPE_INTER_PREFIX
);
440 summary
->path
.origin
.id
= ospf6_new_ls_id(
441 summary
->path
.origin
.type
,
442 summary
->path
.origin
.adv_router
, area
->lsdb
);
444 summary
= ospf6_route_add(summary
, summary_table
);
446 summary
->type
= route
->type
;
447 monotime(&summary
->changed
);
450 summary
->path
.router_bits
= route
->path
.router_bits
;
451 summary
->path
.options
[0] = route
->path
.options
[0];
452 summary
->path
.options
[1] = route
->path
.options
[1];
453 summary
->path
.options
[2] = route
->path
.options
[2];
454 summary
->path
.prefix_options
= route
->path
.prefix_options
;
455 summary
->path
.area_id
= area
->area_id
;
456 summary
->path
.type
= OSPF6_PATH_TYPE_INTER
;
457 summary
->path
.subtype
= route
->path
.subtype
;
458 summary
->path
.cost
= route
->path
.cost
;
459 /* summary->nexthop[0] = route->nexthop[0]; */
462 memset(buffer
, 0, sizeof(buffer
));
463 lsa_header
= (struct ospf6_lsa_header
*)buffer
;
465 if (route
->type
== OSPF6_DEST_TYPE_ROUTER
) {
466 router_lsa
= (struct ospf6_inter_router_lsa
467 *)((caddr_t
)lsa_header
468 + sizeof(struct ospf6_lsa_header
));
469 p
= (caddr_t
)router_lsa
+ sizeof(struct ospf6_inter_router_lsa
);
471 /* Fill Inter-Area-Router-LSA */
472 router_lsa
->options
[0] = route
->path
.options
[0];
473 router_lsa
->options
[1] = route
->path
.options
[1];
474 router_lsa
->options
[2] = route
->path
.options
[2];
475 OSPF6_ABR_SUMMARY_METRIC_SET(router_lsa
, route
->path
.cost
);
476 router_lsa
->router_id
= ADV_ROUTER_IN_PREFIX(&route
->prefix
);
477 type
= htons(OSPF6_LSTYPE_INTER_ROUTER
);
479 prefix_lsa
= (struct ospf6_inter_prefix_lsa
480 *)((caddr_t
)lsa_header
481 + sizeof(struct ospf6_lsa_header
));
482 p
= (caddr_t
)prefix_lsa
+ sizeof(struct ospf6_inter_prefix_lsa
);
484 /* Fill Inter-Area-Prefix-LSA */
485 OSPF6_ABR_SUMMARY_METRIC_SET(prefix_lsa
, route
->path
.cost
);
486 prefix_lsa
->prefix
.prefix_length
= route
->prefix
.prefixlen
;
487 prefix_lsa
->prefix
.prefix_options
= route
->path
.prefix_options
;
490 memcpy(p
, &route
->prefix
.u
.prefix6
,
491 OSPF6_PREFIX_SPACE(route
->prefix
.prefixlen
));
492 ospf6_prefix_apply_mask(&prefix_lsa
->prefix
);
493 p
+= OSPF6_PREFIX_SPACE(route
->prefix
.prefixlen
);
494 type
= htons(OSPF6_LSTYPE_INTER_PREFIX
);
497 /* Fill LSA Header */
499 lsa_header
->type
= type
;
500 lsa_header
->id
= summary
->path
.origin
.id
;
501 lsa_header
->adv_router
= area
->ospf6
->router_id
;
503 ospf6_new_ls_seqnum(lsa_header
->type
, lsa_header
->id
,
504 lsa_header
->adv_router
, area
->lsdb
);
505 lsa_header
->length
= htons((caddr_t
)p
- (caddr_t
)lsa_header
);
508 ospf6_lsa_checksum(lsa_header
);
511 lsa
= ospf6_lsa_create(lsa_header
);
514 ospf6_lsa_originate_area(lsa
, area
);
519 void ospf6_abr_range_reset_cost(struct ospf6
*ospf6
)
521 struct listnode
*node
, *nnode
;
522 struct ospf6_area
*oa
;
523 struct ospf6_route
*range
;
525 for (ALL_LIST_ELEMENTS(ospf6
->area_list
, node
, nnode
, oa
))
526 for (range
= ospf6_route_head(oa
->range_table
); range
;
527 range
= ospf6_route_next(range
))
528 OSPF6_ABR_RANGE_CLEAR_COST(range
);
531 static inline uint32_t ospf6_abr_range_compute_cost(struct ospf6_route
*range
,
534 struct ospf6_route
*ro
;
537 for (ro
= ospf6_route_match_head(&range
->prefix
, o
->route_table
); ro
;
538 ro
= ospf6_route_match_next(&range
->prefix
, ro
)) {
539 if (ro
->path
.area_id
== range
->path
.area_id
540 && (ro
->path
.type
== OSPF6_PATH_TYPE_INTRA
)
541 && !CHECK_FLAG(ro
->flag
, OSPF6_ROUTE_REMOVE
))
542 cost
= MAX(cost
, ro
->path
.cost
);
549 ospf6_abr_range_summary_needs_update(struct ospf6_route
*range
, uint32_t cost
)
551 int redo_summary
= 0;
553 if (CHECK_FLAG(range
->flag
, OSPF6_ROUTE_REMOVE
)) {
554 UNSET_FLAG(range
->flag
, OSPF6_ROUTE_ACTIVE_SUMMARY
);
556 } else if (CHECK_FLAG(range
->flag
, OSPF6_ROUTE_DO_NOT_ADVERTISE
)) {
557 if (range
->path
.cost
!= 0) {
558 range
->path
.cost
= 0;
562 if ((OSPF6_PATH_COST_IS_CONFIGURED(range
->path
)
563 && range
->path
.cost
!= range
->path
.u
.cost_config
)) {
564 range
->path
.cost
= range
->path
.u
.cost_config
;
565 SET_FLAG(range
->flag
, OSPF6_ROUTE_ACTIVE_SUMMARY
);
567 } else if (!OSPF6_PATH_COST_IS_CONFIGURED(range
->path
)
568 && range
->path
.cost
!= cost
) {
569 range
->path
.cost
= cost
;
570 SET_FLAG(range
->flag
, OSPF6_ROUTE_ACTIVE_SUMMARY
);
573 } else if (CHECK_FLAG(range
->flag
, OSPF6_ROUTE_ACTIVE_SUMMARY
)) {
574 /* Cost is zero, meaning no active range */
575 UNSET_FLAG(range
->flag
, OSPF6_ROUTE_ACTIVE_SUMMARY
);
576 range
->path
.cost
= OSPF_AREA_RANGE_COST_UNSPEC
;
580 return (redo_summary
);
583 static void ospf6_abr_range_update(struct ospf6_route
*range
)
586 struct listnode
*node
, *nnode
;
587 struct ospf6_area
*oa
;
588 int summary_orig
= 0;
590 assert(range
->type
== OSPF6_DEST_TYPE_RANGE
);
592 /* update range's cost and active flag */
593 cost
= ospf6_abr_range_compute_cost(range
, ospf6
);
595 /* Non-zero cost is a proxy for active longer prefixes in this range.
596 * If there are active routes covered by this range AND either the
598 * cost has changed or the summarized cost has changed then redo
600 * Alternately, if there are no longer active prefixes and there are
601 * summary announcements, withdraw those announcements.
603 * The don't advertise code relies on the path.cost being set to UNSPEC
605 * work the first time. Subsequent times the path.cost is not 0 anyway
607 * were active ranges.
610 if (ospf6_abr_range_summary_needs_update(range
, cost
)) {
611 for (ALL_LIST_ELEMENTS(ospf6
->area_list
, node
, nnode
, oa
))
613 ospf6_abr_originate_summary_to_area(range
, oa
);
615 if (CHECK_FLAG(range
->flag
, OSPF6_ROUTE_ACTIVE_SUMMARY
)
617 if (!CHECK_FLAG(range
->flag
,
618 OSPF6_ROUTE_BLACKHOLE_ADDED
)) {
619 if (IS_OSPF6_DEBUG_ABR
)
620 zlog_debug("Add discard route");
622 ospf6_zebra_add_discard(range
);
625 /* Summary removed or no summary generated as no
627 if (CHECK_FLAG(range
->flag
,
628 OSPF6_ROUTE_BLACKHOLE_ADDED
)) {
629 if (IS_OSPF6_DEBUG_ABR
)
630 zlog_debug("Delete discard route");
632 ospf6_zebra_delete_discard(range
);
638 void ospf6_abr_originate_summary(struct ospf6_route
*route
)
640 struct listnode
*node
, *nnode
;
641 struct ospf6_area
*oa
;
642 struct ospf6_route
*range
= NULL
;
644 if (route
->type
== OSPF6_DEST_TYPE_NETWORK
) {
645 oa
= ospf6_area_lookup(route
->path
.area_id
, ospf6
);
647 zlog_err("OSPFv6 area lookup failed");
651 range
= ospf6_route_lookup_bestmatch(&route
->prefix
,
654 ospf6_abr_range_update(range
);
658 for (ALL_LIST_ELEMENTS(ospf6
->area_list
, node
, nnode
, oa
))
659 ospf6_abr_originate_summary_to_area(route
, oa
);
662 void ospf6_abr_defaults_to_stub(struct ospf6
*o
)
664 struct listnode
*node
, *nnode
;
665 struct ospf6_area
*oa
;
666 struct ospf6_route
*def
, *route
;
671 def
= ospf6_route_create();
672 def
->type
= OSPF6_DEST_TYPE_NETWORK
;
673 def
->prefix
.family
= AF_INET6
;
674 def
->prefix
.prefixlen
= 0;
675 memset(&def
->prefix
.u
.prefix6
, 0, sizeof(struct in6_addr
));
676 def
->type
= OSPF6_DEST_TYPE_NETWORK
;
677 def
->path
.type
= OSPF6_PATH_TYPE_INTER
;
678 def
->path
.subtype
= OSPF6_PATH_SUBTYPE_DEFAULT_RT
;
679 def
->path
.area_id
= o
->backbone
->area_id
;
681 for (ALL_LIST_ELEMENTS(ospf6
->area_list
, node
, nnode
, oa
)) {
682 if (!IS_AREA_STUB(oa
)) {
683 /* withdraw defaults when an area switches from stub to
685 route
= ospf6_route_lookup(&def
->prefix
,
688 && (route
->path
.subtype
== def
->path
.subtype
)) {
689 if (IS_OSPF6_DEBUG_ABR
)
691 "Withdrawing default route from non-stubby area %s",
693 SET_FLAG(def
->flag
, OSPF6_ROUTE_REMOVE
);
694 ospf6_abr_originate_summary_to_area(def
, oa
);
697 /* announce defaults to stubby areas */
698 if (IS_OSPF6_DEBUG_ABR
)
700 "Announcing default route into stubby area %s",
702 UNSET_FLAG(def
->flag
, OSPF6_ROUTE_REMOVE
);
703 ospf6_abr_originate_summary_to_area(def
, oa
);
706 ospf6_route_delete(def
);
709 void ospf6_abr_old_path_update(struct ospf6_route
*old_route
,
710 struct ospf6_route
*route
,
711 struct ospf6_route_table
*table
)
713 struct ospf6_path
*o_path
= NULL
;
714 struct listnode
*anode
, *anext
;
715 struct listnode
*nnode
, *rnode
, *rnext
;
716 struct ospf6_nexthop
*nh
, *rnh
;
718 for (ALL_LIST_ELEMENTS(old_route
->paths
, anode
, anext
, o_path
)) {
719 if (o_path
->area_id
!= route
->path
.area_id
||
720 (memcmp(&(o_path
)->origin
, &(route
)->path
.origin
,
721 sizeof(struct ospf6_ls_origin
)) != 0))
724 if ((o_path
->cost
== route
->path
.cost
) &&
725 (o_path
->u
.cost_e2
== route
->path
.u
.cost_e2
))
728 for (ALL_LIST_ELEMENTS_RO(o_path
->nh_list
, nnode
, nh
)) {
729 for (ALL_LIST_ELEMENTS(old_route
->nh_list
, rnode
,
731 if (!ospf6_nexthop_is_same(rnh
, nh
))
733 listnode_delete(old_route
->nh_list
, rnh
);
734 ospf6_nexthop_delete(rnh
);
739 listnode_delete(old_route
->paths
, o_path
);
740 ospf6_path_free(o_path
);
742 for (ALL_LIST_ELEMENTS(old_route
->paths
, anode
,
744 ospf6_merge_nexthops(old_route
->nh_list
,
748 if (IS_OSPF6_DEBUG_ABR
|| IS_OSPF6_DEBUG_EXAMIN(INTER_PREFIX
))
749 zlog_debug("%s: paths %u nh %u", __PRETTY_FUNCTION__
,
751 listcount(old_route
->paths
) : 0,
753 listcount(old_route
->nh_list
) : 0);
756 (*table
->hook_add
)(old_route
);
758 if (old_route
->path
.origin
.id
== route
->path
.origin
.id
&&
759 old_route
->path
.origin
.adv_router
==
760 route
->path
.origin
.adv_router
) {
761 struct ospf6_path
*h_path
;
763 h_path
= (struct ospf6_path
*)
764 listgetdata(listhead(old_route
->paths
));
765 old_route
->path
.origin
.type
= h_path
->origin
.type
;
766 old_route
->path
.origin
.id
= h_path
->origin
.id
;
767 old_route
->path
.origin
.adv_router
=
768 h_path
->origin
.adv_router
;
773 void ospf6_abr_old_route_remove(struct ospf6_lsa
*lsa
,
774 struct ospf6_route
*old
,
775 struct ospf6_route_table
*table
)
777 if (listcount(old
->paths
) > 1) {
778 struct listnode
*anode
, *anext
, *nnode
, *rnode
, *rnext
;
779 struct ospf6_path
*o_path
;
780 struct ospf6_nexthop
*nh
, *rnh
;
781 bool nh_updated
= false;
782 char buf
[PREFIX2STR_BUFFER
];
784 for (ALL_LIST_ELEMENTS(old
->paths
, anode
, anext
, o_path
)) {
785 if (o_path
->origin
.adv_router
!= lsa
->header
->adv_router
786 && o_path
->origin
.id
!= lsa
->header
->id
)
788 for (ALL_LIST_ELEMENTS_RO(o_path
->nh_list
, nnode
, nh
)) {
789 for (ALL_LIST_ELEMENTS(old
->nh_list
,
790 rnode
, rnext
, rnh
)) {
791 if (!ospf6_nexthop_is_same(rnh
, nh
))
793 listnode_delete(old
->nh_list
, rnh
);
794 ospf6_nexthop_delete(rnh
);
797 listnode_delete(old
->paths
, o_path
);
798 ospf6_path_free(o_path
);
803 if (listcount(old
->paths
)) {
804 if (IS_OSPF6_DEBUG_ABR
||
805 IS_OSPF6_DEBUG_EXAMIN(INTER_PREFIX
)) {
806 prefix2str(&old
->prefix
, buf
,
808 zlog_debug("%s: old %s updated nh %u",
809 __PRETTY_FUNCTION__
, buf
,
811 listcount(old
->nh_list
) : 0);
815 (*table
->hook_add
)(old
);
817 if ((old
->path
.origin
.id
== lsa
->header
->id
) &&
818 (old
->path
.origin
.adv_router
819 == lsa
->header
->adv_router
)) {
820 struct ospf6_path
*h_path
;
822 h_path
= (struct ospf6_path
*)
824 listhead(old
->paths
));
825 old
->path
.origin
.type
=
827 old
->path
.origin
.id
= h_path
->origin
.id
;
828 old
->path
.origin
.adv_router
=
829 h_path
->origin
.adv_router
;
832 ospf6_route_remove(old
, table
);
835 ospf6_route_remove(old
, table
);
839 /* RFC 2328 16.2. Calculating the inter-area routes */
840 void ospf6_abr_examin_summary(struct ospf6_lsa
*lsa
, struct ospf6_area
*oa
)
842 struct prefix prefix
, abr_prefix
;
843 struct ospf6_route_table
*table
= NULL
;
844 struct ospf6_route
*range
, *route
, *old
= NULL
, *old_route
;
845 struct ospf6_route
*abr_entry
;
847 char options
[3] = {0, 0, 0};
848 uint8_t prefix_options
= 0;
850 uint8_t router_bits
= 0;
851 char buf
[PREFIX2STR_BUFFER
];
853 struct ospf6_inter_prefix_lsa
*prefix_lsa
= NULL
;
854 struct ospf6_inter_router_lsa
*router_lsa
= NULL
;
855 bool old_entry_updated
= false;
856 struct ospf6_path
*path
, *o_path
, *ecmp_path
;
857 struct listnode
*anode
;
860 memset(&prefix
, 0, sizeof(prefix
));
862 if (lsa
->header
->type
== htons(OSPF6_LSTYPE_INTER_PREFIX
)) {
863 if (IS_OSPF6_DEBUG_EXAMIN(INTER_PREFIX
)) {
865 zlog_debug("%s: Examin %s in area %s",
866 __PRETTY_FUNCTION__
, lsa
->name
, oa
->name
);
870 (struct ospf6_inter_prefix_lsa
*)OSPF6_LSA_HEADER_END(
872 prefix
.family
= AF_INET6
;
873 prefix
.prefixlen
= prefix_lsa
->prefix
.prefix_length
;
874 ospf6_prefix_in6_addr(&prefix
.u
.prefix6
, prefix_lsa
,
875 &prefix_lsa
->prefix
);
877 prefix2str(&prefix
, buf
, sizeof(buf
));
878 table
= oa
->ospf6
->route_table
;
879 type
= OSPF6_DEST_TYPE_NETWORK
;
880 prefix_options
= prefix_lsa
->prefix
.prefix_options
;
881 cost
= OSPF6_ABR_SUMMARY_METRIC(prefix_lsa
);
882 } else if (lsa
->header
->type
== htons(OSPF6_LSTYPE_INTER_ROUTER
)) {
883 if (IS_OSPF6_DEBUG_EXAMIN(INTER_ROUTER
)) {
885 zlog_debug("%s: Examin %s in area %s",
886 __PRETTY_FUNCTION__
, lsa
->name
, oa
->name
);
890 (struct ospf6_inter_router_lsa
*)OSPF6_LSA_HEADER_END(
892 ospf6_linkstate_prefix(router_lsa
->router_id
, htonl(0),
895 inet_ntop(AF_INET
, &router_lsa
->router_id
, buf
,
898 table
= oa
->ospf6
->brouter_table
;
899 type
= OSPF6_DEST_TYPE_ROUTER
;
900 options
[0] = router_lsa
->options
[0];
901 options
[1] = router_lsa
->options
[1];
902 options
[2] = router_lsa
->options
[2];
903 cost
= OSPF6_ABR_SUMMARY_METRIC(router_lsa
);
904 SET_FLAG(router_bits
, OSPF6_ROUTER_BIT_E
);
908 /* Find existing route */
909 route
= ospf6_route_lookup(&prefix
, table
);
911 ospf6_route_lock(route
);
912 while (route
&& ospf6_route_is_prefix(&prefix
, route
)) {
913 if (route
->path
.area_id
== oa
->area_id
914 && route
->path
.origin
.type
== lsa
->header
->type
915 && !CHECK_FLAG(route
->flag
, OSPF6_ROUTE_WAS_REMOVED
)) {
916 /* LSA adv. router could be part of route's
917 * paths list. Find the existing path and set
920 if (listcount(route
->paths
) > 1) {
921 struct listnode
*anode
;
922 struct ospf6_path
*o_path
;
924 for (ALL_LIST_ELEMENTS_RO(route
->paths
, anode
,
927 &o_path
->origin
.adv_router
,
930 if (o_path
->origin
.id
== lsa
->header
->id
931 && o_path
->origin
.adv_router
==
932 lsa
->header
->adv_router
) {
936 zlog_debug("%s: old entry found in paths, adv_router %s",
943 } else if (route
->path
.origin
.id
== lsa
->header
->id
&&
944 route
->path
.origin
.adv_router
==
945 lsa
->header
->adv_router
)
948 route
= ospf6_route_next(route
);
951 ospf6_route_unlock(route
);
953 /* (1) if cost == LSInfinity or if the LSA is MaxAge */
954 if (cost
== OSPF_LS_INFINITY
) {
956 zlog_debug("cost is LS_INFINITY, ignore");
958 ospf6_abr_old_route_remove(lsa
, old
, table
);
961 if (OSPF6_LSA_IS_MAXAGE(lsa
)) {
963 zlog_debug("%s: LSA %s is MaxAge, ignore",
964 __PRETTY_FUNCTION__
, lsa
->name
);
966 ospf6_abr_old_route_remove(lsa
, old
, table
);
970 /* (2) if the LSA is self-originated, ignore */
971 if (lsa
->header
->adv_router
== oa
->ospf6
->router_id
) {
973 zlog_debug("LSA %s is self-originated, ignore",
976 ospf6_route_remove(old
, table
);
980 /* (3) if the prefix is equal to an active configured address range */
981 /* or if the NU bit is set in the prefix */
982 if (lsa
->header
->type
== htons(OSPF6_LSTYPE_INTER_PREFIX
)) {
983 /* must have been set in previous block */
986 range
= ospf6_route_lookup(&prefix
, oa
->range_table
);
990 "Prefix is equal to address range, ignore");
992 ospf6_route_remove(old
, table
);
996 if (CHECK_FLAG(prefix_lsa
->prefix
.prefix_options
,
997 OSPF6_PREFIX_OPTION_NU
)
998 || CHECK_FLAG(prefix_lsa
->prefix
.prefix_options
,
999 OSPF6_PREFIX_OPTION_LA
)) {
1001 zlog_debug("Prefix has NU/LA bit set, ignore");
1003 ospf6_route_remove(old
, table
);
1008 if (lsa
->header
->type
== htons(OSPF6_LSTYPE_INTER_ROUTER
)) {
1009 /* To pass test suites */
1010 if (!OSPF6_OPT_ISSET(router_lsa
->options
, OSPF6_OPT_R
)
1011 || !OSPF6_OPT_ISSET(router_lsa
->options
, OSPF6_OPT_V6
)) {
1013 zlog_debug("Prefix has NU/LA bit set, ignore");
1015 ospf6_route_remove(old
, table
);
1019 /* Avoid infinite recursion if someone has maliciously announced
1021 Inter-Router LSA for an ABR
1023 if (lsa
->header
->adv_router
== router_lsa
->router_id
) {
1026 "Ignorning Inter-Router LSA for an ABR (%s)",
1029 ospf6_route_remove(old
, table
);
1035 /* (4) if the routing table entry for the ABR does not exist */
1036 ospf6_linkstate_prefix(lsa
->header
->adv_router
, htonl(0), &abr_prefix
);
1037 abr_entry
= ospf6_route_lookup(&abr_prefix
, oa
->ospf6
->brouter_table
);
1038 if (abr_entry
== NULL
|| abr_entry
->path
.area_id
!= oa
->area_id
1039 || CHECK_FLAG(abr_entry
->flag
, OSPF6_ROUTE_REMOVE
)
1040 || !CHECK_FLAG(abr_entry
->path
.router_bits
, OSPF6_ROUTER_BIT_B
)) {
1042 zlog_debug("%s: ABR router entry does not exist, ignore",
1043 __PRETTY_FUNCTION__
);
1045 if (old
->type
== OSPF6_DEST_TYPE_ROUTER
&&
1046 oa
->intra_brouter_calc
) {
1049 "%s: intra_brouter_calc is on, skip brouter remove: %s (%p)",
1050 __PRETTY_FUNCTION__
, buf
,
1054 zlog_debug("%s: remove old entry: %s %p ",
1055 __PRETTY_FUNCTION__
, buf
,
1057 ospf6_route_remove(old
, table
);
1063 /* Check import list */
1064 if (IMPORT_NAME(oa
)) {
1065 if (IMPORT_LIST(oa
) == NULL
)
1067 access_list_lookup(AFI_IP6
, IMPORT_NAME(oa
));
1069 if (IMPORT_LIST(oa
))
1070 if (access_list_apply(IMPORT_LIST(oa
), &prefix
)
1074 "Prefix was denied by import-list");
1076 ospf6_route_remove(old
, table
);
1081 /* Check input prefix-list */
1082 if (PREFIX_LIST_IN(oa
)) {
1083 if (prefix_list_apply(PREFIX_LIST_IN(oa
), &prefix
)
1086 zlog_debug("Prefix was denied by prefix-list");
1088 ospf6_route_remove(old
, table
);
1093 /* (5),(6): the path preference is handled by the sorting
1094 in the routing table. Always install the path by substituting
1095 old route (if any). */
1096 route
= ospf6_route_create();
1099 route
->prefix
= prefix
;
1100 route
->path
.origin
.type
= lsa
->header
->type
;
1101 route
->path
.origin
.id
= lsa
->header
->id
;
1102 route
->path
.origin
.adv_router
= lsa
->header
->adv_router
;
1103 route
->path
.router_bits
= router_bits
;
1104 route
->path
.options
[0] = options
[0];
1105 route
->path
.options
[1] = options
[1];
1106 route
->path
.options
[2] = options
[2];
1107 route
->path
.prefix_options
= prefix_options
;
1108 route
->path
.area_id
= oa
->area_id
;
1109 route
->path
.type
= OSPF6_PATH_TYPE_INTER
;
1110 route
->path
.cost
= abr_entry
->path
.cost
+ cost
;
1112 /* copy brouter rechable nexthops into the route. */
1113 ospf6_route_copy_nexthops(route
, abr_entry
);
1115 /* (7) If the routes are identical, copy the next hops over to existing
1116 route. ospf6's route table implementation will otherwise string both
1117 routes, but keep the older one as the best route since the routes
1120 old
= ospf6_route_lookup(&prefix
, table
);
1122 for (old_route
= old
; old_route
; old_route
= old_route
->next
) {
1123 if (!ospf6_route_is_same(old_route
, route
) ||
1124 (old_route
->type
!= route
->type
) ||
1125 (old_route
->path
.type
!= route
->path
.type
))
1128 if ((ospf6_route_cmp(route
, old_route
) != 0)) {
1130 prefix2str(&prefix
, buf
, sizeof(buf
));
1131 zlog_debug("%s: old %p %s cost %u new route cost %u are not same",
1132 __PRETTY_FUNCTION__
,
1133 (void *)old_route
, buf
,
1134 old_route
->path
.cost
,
1138 /* Check new route's adv. router is same in one of
1139 * the paths with differed cost, if so remove the
1140 * old path as later new route will be added.
1142 if (listcount(old_route
->paths
) > 1)
1143 ospf6_abr_old_path_update(old_route
, route
,
1148 ospf6_route_merge_nexthops(old_route
, route
);
1149 old_entry_updated
= true;
1151 for (ALL_LIST_ELEMENTS_RO(old_route
->paths
, anode
,
1153 if (o_path
->area_id
== route
->path
.area_id
&&
1154 (memcmp(&(o_path
)->origin
, &(route
)->path
.origin
,
1155 sizeof(struct ospf6_ls_origin
)) == 0))
1159 /* New adv. router for a existing path add to paths list */
1160 if (o_path
== NULL
) {
1161 ecmp_path
= ospf6_path_dup(&route
->path
);
1163 /* Add a nh_list to new ecmp path */
1164 ospf6_copy_nexthops(ecmp_path
->nh_list
, route
->nh_list
);
1166 /* Add the new path to route's path list */
1167 listnode_add_sort(old_route
->paths
, ecmp_path
);
1170 prefix2str(&route
->prefix
, buf
, sizeof(buf
));
1172 &ecmp_path
->origin
.adv_router
,
1173 adv_router
, sizeof(adv_router
));
1174 zlog_debug("%s: route %s cost %u another path %s added with nh %u, effective paths %u nh %u",
1175 __PRETTY_FUNCTION__
, buf
,
1176 old_route
->path
.cost
,
1178 listcount(ecmp_path
->nh_list
),
1180 listcount(old_route
->paths
) : 0,
1181 listcount(old_route
->nh_list
));
1184 /* adv. router exists in the list, update the nhs */
1185 list_delete_all_node(o_path
->nh_list
);
1186 ospf6_copy_nexthops(o_path
->nh_list
, route
->nh_list
);
1190 zlog_debug("%s: Update route: %s %p old cost %u new cost %u nh %u",
1191 __PRETTY_FUNCTION__
, buf
, (void *)old_route
,
1192 old_route
->path
.cost
, route
->path
.cost
,
1193 listcount(route
->nh_list
));
1195 /* For Inter-Prefix route: Update RIB/FIB,
1196 * For Inter-Router trigger summary update
1198 if (table
->hook_add
)
1199 (*table
->hook_add
)(old_route
);
1201 /* Delete new route */
1202 ospf6_route_delete(route
);
1206 if (old_entry_updated
== false) {
1208 inet_ntop(AF_INET
, &route
->path
.origin
.adv_router
,
1209 adv_router
, sizeof(adv_router
));
1210 zlog_debug("%s: Install route: %s cost %u nh %u adv_router %s ",
1211 __PRETTY_FUNCTION__
, buf
, route
->path
.cost
,
1212 listcount(route
->nh_list
), adv_router
);
1215 path
= ospf6_path_dup(&route
->path
);
1216 ospf6_copy_nexthops(path
->nh_list
, abr_entry
->nh_list
);
1217 listnode_add_sort(route
->paths
, path
);
1219 /* ospf6_ia_add_nw_route (table, &prefix, route); */
1220 ospf6_route_add(route
, table
);
1224 void ospf6_abr_examin_brouter(uint32_t router_id
)
1226 struct ospf6_lsa
*lsa
;
1227 struct ospf6_area
*oa
;
1230 if (ospf6_is_router_abr(ospf6
))
1231 oa
= ospf6
->backbone
;
1233 oa
= listgetdata(listhead(ospf6
->area_list
));
1236 * It is possible to designate a non backbone
1237 * area first. If that is the case safely
1238 * fall out of this function.
1243 type
= htons(OSPF6_LSTYPE_INTER_ROUTER
);
1244 for (ALL_LSDB_TYPED_ADVRTR(oa
->lsdb
, type
, router_id
, lsa
))
1245 ospf6_abr_examin_summary(lsa
, oa
);
1247 type
= htons(OSPF6_LSTYPE_INTER_PREFIX
);
1248 for (ALL_LSDB_TYPED_ADVRTR(oa
->lsdb
, type
, router_id
, lsa
))
1249 ospf6_abr_examin_summary(lsa
, oa
);
1252 void ospf6_abr_reimport(struct ospf6_area
*oa
)
1254 struct ospf6_lsa
*lsa
;
1257 type
= htons(OSPF6_LSTYPE_INTER_ROUTER
);
1258 for (ALL_LSDB_TYPED(oa
->lsdb
, type
, lsa
))
1259 ospf6_abr_examin_summary(lsa
, oa
);
1261 type
= htons(OSPF6_LSTYPE_INTER_PREFIX
);
1262 for (ALL_LSDB_TYPED(oa
->lsdb
, type
, lsa
))
1263 ospf6_abr_examin_summary(lsa
, oa
);
1266 void ospf6_abr_prefix_resummarize(struct ospf6
*o
)
1268 struct ospf6_route
*route
;
1270 if (IS_OSPF6_DEBUG_ABR
)
1271 zlog_debug("Re-examining Inter-Prefix Summaries");
1273 for (route
= ospf6_route_head(o
->route_table
); route
;
1274 route
= ospf6_route_next(route
))
1275 ospf6_abr_originate_summary(route
);
1277 if (IS_OSPF6_DEBUG_ABR
)
1278 zlog_debug("Finished re-examining Inter-Prefix Summaries");
1282 /* Display functions */
1283 static char *ospf6_inter_area_prefix_lsa_get_prefix_str(struct ospf6_lsa
*lsa
,
1284 char *buf
, int buflen
,
1287 struct ospf6_inter_prefix_lsa
*prefix_lsa
;
1288 struct in6_addr in6
;
1292 (struct ospf6_inter_prefix_lsa
*)OSPF6_LSA_HEADER_END(
1295 ospf6_prefix_in6_addr(&in6
, prefix_lsa
, &prefix_lsa
->prefix
);
1297 inet_ntop(AF_INET6
, &in6
, buf
, buflen
);
1298 sprintf(&buf
[strlen(buf
)], "/%d",
1299 prefix_lsa
->prefix
.prefix_length
);
1306 static int ospf6_inter_area_prefix_lsa_show(struct vty
*vty
,
1307 struct ospf6_lsa
*lsa
)
1309 struct ospf6_inter_prefix_lsa
*prefix_lsa
;
1310 char buf
[INET6_ADDRSTRLEN
];
1312 prefix_lsa
= (struct ospf6_inter_prefix_lsa
*)OSPF6_LSA_HEADER_END(
1315 vty_out(vty
, " Metric: %lu\n",
1316 (unsigned long)OSPF6_ABR_SUMMARY_METRIC(prefix_lsa
));
1318 ospf6_prefix_options_printbuf(prefix_lsa
->prefix
.prefix_options
, buf
,
1320 vty_out(vty
, " Prefix Options: %s\n", buf
);
1322 vty_out(vty
, " Prefix: %s\n",
1323 ospf6_inter_area_prefix_lsa_get_prefix_str(lsa
, buf
,
1329 static char *ospf6_inter_area_router_lsa_get_prefix_str(struct ospf6_lsa
*lsa
,
1330 char *buf
, int buflen
,
1333 struct ospf6_inter_router_lsa
*router_lsa
;
1337 (struct ospf6_inter_router_lsa
*)OSPF6_LSA_HEADER_END(
1342 inet_ntop(AF_INET
, &router_lsa
->router_id
, buf
, buflen
);
1348 static int ospf6_inter_area_router_lsa_show(struct vty
*vty
,
1349 struct ospf6_lsa
*lsa
)
1351 struct ospf6_inter_router_lsa
*router_lsa
;
1354 router_lsa
= (struct ospf6_inter_router_lsa
*)OSPF6_LSA_HEADER_END(
1357 ospf6_options_printbuf(router_lsa
->options
, buf
, sizeof(buf
));
1358 vty_out(vty
, " Options: %s\n", buf
);
1359 vty_out(vty
, " Metric: %lu\n",
1360 (unsigned long)OSPF6_ABR_SUMMARY_METRIC(router_lsa
));
1362 inet_ntop(AF_INET
, &router_lsa
->router_id
, buf
, sizeof(buf
));
1363 vty_out(vty
, " Destination Router ID: %s\n", buf
);
1368 /* Debug commands */
1369 DEFUN (debug_ospf6_abr
,
1370 debug_ospf6_abr_cmd
,
1374 "Debug OSPFv3 ABR function\n"
1377 OSPF6_DEBUG_ABR_ON();
1381 DEFUN (no_debug_ospf6_abr
,
1382 no_debug_ospf6_abr_cmd
,
1383 "no debug ospf6 abr",
1387 "Debug OSPFv3 ABR function\n"
1390 OSPF6_DEBUG_ABR_OFF();
1394 int config_write_ospf6_debug_abr(struct vty
*vty
)
1396 if (IS_OSPF6_DEBUG_ABR
)
1397 vty_out(vty
, "debug ospf6 abr\n");
1401 void install_element_ospf6_debug_abr(void)
1403 install_element(ENABLE_NODE
, &debug_ospf6_abr_cmd
);
1404 install_element(ENABLE_NODE
, &no_debug_ospf6_abr_cmd
);
1405 install_element(CONFIG_NODE
, &debug_ospf6_abr_cmd
);
1406 install_element(CONFIG_NODE
, &no_debug_ospf6_abr_cmd
);
1409 struct ospf6_lsa_handler inter_prefix_handler
= {
1410 .lh_type
= OSPF6_LSTYPE_INTER_PREFIX
,
1411 .lh_name
= "Inter-Prefix",
1412 .lh_short_name
= "IAP",
1413 .lh_show
= ospf6_inter_area_prefix_lsa_show
,
1414 .lh_get_prefix_str
= ospf6_inter_area_prefix_lsa_get_prefix_str
,
1417 struct ospf6_lsa_handler inter_router_handler
= {
1418 .lh_type
= OSPF6_LSTYPE_INTER_ROUTER
,
1419 .lh_name
= "Inter-Router",
1420 .lh_short_name
= "IAR",
1421 .lh_show
= ospf6_inter_area_router_lsa_show
,
1422 .lh_get_prefix_str
= ospf6_inter_area_router_lsa_get_prefix_str
,
1425 void ospf6_abr_init(void)
1427 ospf6_install_lsa_handler(&inter_prefix_handler
);
1428 ospf6_install_lsa_handler(&inter_router_handler
);