diff options
Diffstat (limited to 'ospf6d/ospf6_abr.c')
-rw-r--r-- | ospf6d/ospf6_abr.c | 1595 |
1 files changed, 1595 insertions, 0 deletions
diff --git a/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c new file mode 100644 index 0000000..f4202a4 --- /dev/null +++ b/ospf6d/ospf6_abr.c @@ -0,0 +1,1595 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Area Border Router function. + * Copyright (C) 2004 Yasuhiro Ohara + */ + +#include <zebra.h> + +#include "log.h" +#include "prefix.h" +#include "table.h" +#include "vty.h" +#include "linklist.h" +#include "command.h" +#include "frrevent.h" +#include "plist.h" +#include "filter.h" + +#include "ospf6_proto.h" +#include "ospf6_route.h" +#include "ospf6_lsa.h" +#include "ospf6_route.h" +#include "ospf6_lsdb.h" +#include "ospf6_message.h" +#include "ospf6_zebra.h" + +#include "ospf6_top.h" +#include "ospf6_area.h" +#include "ospf6_interface.h" +#include "ospf6_neighbor.h" + +#include "ospf6_flood.h" +#include "ospf6_intra.h" +#include "ospf6_asbr.h" +#include "ospf6_abr.h" +#include "ospf6d.h" +#include "ospf6_nssa.h" + +unsigned char conf_debug_ospf6_abr; + +int ospf6_ls_origin_same(struct ospf6_path *o_path, struct ospf6_path *r_path) +{ + if (((o_path->origin.type == r_path->origin.type) + && (o_path->origin.id == r_path->origin.id) + && (o_path->origin.adv_router == r_path->origin.adv_router))) + return 1; + else + return 0; +} + +bool ospf6_check_and_set_router_abr(struct ospf6 *o) +{ + struct listnode *node; + struct ospf6_area *oa; + int area_count = 0; + bool is_backbone = false; + + for (ALL_LIST_ELEMENTS_RO(o->area_list, node, oa)) { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s, area_id %pI4", __func__, &oa->area_id); + if (IS_AREA_ENABLED(oa)) + area_count++; + + if (o->backbone == oa) + is_backbone = true; + } + + if ((area_count > 1) && (is_backbone)) { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s : set flag OSPF6_FLAG_ABR", __func__); + SET_FLAG(o->flag, OSPF6_FLAG_ABR); + return true; + } else { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s : reset flag OSPF6_FLAG_ABR", __func__); + UNSET_FLAG(o->flag, OSPF6_FLAG_ABR); + return false; + } +} + +static int ospf6_abr_nexthops_belong_to_area(struct ospf6_route *route, + struct ospf6_area *area) +{ + struct ospf6_interface *oi; + + oi = ospf6_interface_lookup_by_ifindex( + ospf6_route_get_first_nh_index(route), area->ospf6->vrf_id); + if (oi && oi->area && oi->area == area) + return 1; + else + return 0; +} + +static void ospf6_abr_delete_route(struct ospf6_route *summary, + struct ospf6_route_table *summary_table, + struct ospf6_lsa *old) +{ + if (summary) { + ospf6_route_remove(summary, summary_table); + } + + if (old && !OSPF6_LSA_IS_MAXAGE(old)) + ospf6_lsa_purge(old); +} + +void ospf6_abr_enable_area(struct ospf6_area *area) +{ + struct ospf6_area *oa; + struct listnode *node, *nnode; + + for (ALL_LIST_ELEMENTS(area->ospf6->area_list, node, nnode, oa)) + /* update B bit for each area */ + OSPF6_ROUTER_LSA_SCHEDULE(oa); +} + +void ospf6_abr_disable_area(struct ospf6_area *area) +{ + struct ospf6_area *oa; + struct ospf6_route *ro, *nro; + struct ospf6_lsa *old; + struct listnode *node, *nnode; + + /* Withdraw all summary prefixes previously originated */ + for (ro = ospf6_route_head(area->summary_prefix); ro; ro = nro) { + nro = ospf6_route_next(ro); + old = ospf6_lsdb_lookup(ro->path.origin.type, + ro->path.origin.id, + area->ospf6->router_id, area->lsdb); + if (old) + ospf6_lsa_purge(old); + ospf6_route_remove(ro, area->summary_prefix); + } + + /* Withdraw all summary router-routes previously originated */ + for (ro = ospf6_route_head(area->summary_router); ro; ro = nro) { + nro = ospf6_route_next(ro); + old = ospf6_lsdb_lookup(ro->path.origin.type, + ro->path.origin.id, + area->ospf6->router_id, area->lsdb); + if (old) + ospf6_lsa_purge(old); + ospf6_route_remove(ro, area->summary_router); + } + + /* Schedule Router-LSA for each area (ABR status may change) */ + for (ALL_LIST_ELEMENTS(area->ospf6->area_list, node, nnode, oa)) + /* update B bit for each area */ + OSPF6_ROUTER_LSA_SCHEDULE(oa); +} + +/* RFC 2328 12.4.3. Summary-LSAs */ +/* Returns 1 if a summary LSA has been generated for the area */ +/* This is used by the area/range logic to add/remove blackhole routes */ +int ospf6_abr_originate_summary_to_area(struct ospf6_route *route, + struct ospf6_area *area) +{ + struct ospf6_lsa *lsa, *old = NULL; + struct ospf6_route *summary, *range = NULL; + struct ospf6_area *route_area; + char buffer[OSPF6_MAX_LSASIZE]; + struct ospf6_lsa_header *lsa_header; + caddr_t p; + struct ospf6_inter_prefix_lsa *prefix_lsa; + struct ospf6_inter_router_lsa *router_lsa; + struct ospf6_route_table *summary_table = NULL; + uint16_t type; + int is_debug = 0; + + if (IS_OSPF6_DEBUG_ABR) { + char buf[BUFSIZ]; + + if (route->type == OSPF6_DEST_TYPE_ROUTER) + inet_ntop(AF_INET, + &ADV_ROUTER_IN_PREFIX(&route->prefix), buf, + sizeof(buf)); + else + prefix2str(&route->prefix, buf, sizeof(buf)); + + zlog_debug("%s : start area %s, route %s", __func__, area->name, + buf); + } + + if (route->type == OSPF6_DEST_TYPE_ROUTER) + summary_table = area->summary_router; + else + summary_table = area->summary_prefix; + + summary = ospf6_route_lookup(&route->prefix, summary_table); + if (summary) { + old = ospf6_lsdb_lookup(summary->path.origin.type, + summary->path.origin.id, + area->ospf6->router_id, area->lsdb); + /* Reset the OSPF6_LSA_UNAPPROVED flag */ + if (old) + UNSET_FLAG(old->flag, OSPF6_LSA_UNAPPROVED); + } + + /* Only destination type network, range or ASBR are considered */ + if (route->type != OSPF6_DEST_TYPE_NETWORK + && route->type != OSPF6_DEST_TYPE_RANGE + && ((route->type != OSPF6_DEST_TYPE_ROUTER) + || !CHECK_FLAG(route->path.router_bits, OSPF6_ROUTER_BIT_E))) { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug( + "%s: Route type %d flag 0x%x is none of network, range nor ASBR, ignore", + __func__, route->type, route->path.router_bits); + return 0; + } + + /* AS External routes are never considered */ + if (route->path.type == OSPF6_PATH_TYPE_EXTERNAL1 + || route->path.type == OSPF6_PATH_TYPE_EXTERNAL2) { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s : Path type is external, skip", + __func__); + return 0; + } + + /* do not generate if the path's area is the same as target area */ + if (route->path.area_id == area->area_id) { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug( + "%s: The route is in the area itself, ignore", + __func__); + return 0; + } + + if (route->type == OSPF6_DEST_TYPE_NETWORK) { + bool filter = false; + + route_area = + ospf6_area_lookup(route->path.area_id, area->ospf6); + assert(route_area); + + /* Check export-list */ + if (EXPORT_LIST(route_area) + && access_list_apply(EXPORT_LIST(route_area), + &route->prefix) + == FILTER_DENY) { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug( + "%s: prefix %pFX was denied by export-list", + __func__, &route->prefix); + filter = true; + } + + /* Check output prefix-list */ + if (PREFIX_LIST_OUT(route_area) + && prefix_list_apply(PREFIX_LIST_OUT(route_area), + &route->prefix) + != PREFIX_PERMIT) { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug( + "%s: prefix %pFX was denied by prefix-list out", + __func__, &route->prefix); + filter = true; + } + + /* Check import-list */ + if (IMPORT_LIST(area) + && access_list_apply(IMPORT_LIST(area), &route->prefix) + == FILTER_DENY) { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug( + "%s: prefix %pFX was denied by import-list", + __func__, &route->prefix); + filter = true; + } + + /* Check input prefix-list */ + if (PREFIX_LIST_IN(area) + && prefix_list_apply(PREFIX_LIST_IN(area), &route->prefix) + != PREFIX_PERMIT) { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug( + "%s: prefix %pFX was denied by prefix-list in", + __func__, &route->prefix); + filter = true; + } + + if (filter) { + if (summary) { + ospf6_route_remove(summary, summary_table); + if (old) + ospf6_lsa_purge(old); + } + return 0; + } + } + + /* do not generate if the nexthops belongs to the target area */ + if (ospf6_abr_nexthops_belong_to_area(route, area)) { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug( + "%s: The route's nexthop is in the same area, ignore", + __func__); + return 0; + } + + if (route->type == OSPF6_DEST_TYPE_ROUTER) { + if (ADV_ROUTER_IN_PREFIX(&route->prefix) + == area->ospf6->router_id) { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug( + "%s: Skipping ASBR announcement for ABR (%pI4)", + __func__, + &ADV_ROUTER_IN_PREFIX(&route->prefix)); + return 0; + } + } + + if (route->type == OSPF6_DEST_TYPE_ROUTER) { + if (IS_OSPF6_DEBUG_ABR + || IS_OSPF6_DEBUG_ORIGINATE(INTER_ROUTER)) { + is_debug++; + if (IS_OSPF6_DEBUG_ABR) + zlog_debug( + "Originating summary in area %s for ASBR %pI4", + area->name, + &ADV_ROUTER_IN_PREFIX(&route->prefix)); + } + } else { + if (IS_OSPF6_DEBUG_ABR + || IS_OSPF6_DEBUG_ORIGINATE(INTER_PREFIX)) + is_debug++; + + if (route->type == OSPF6_DEST_TYPE_NETWORK && + route->path.origin.type == + htons(OSPF6_LSTYPE_INTER_PREFIX)) { + if (!CHECK_FLAG(route->flag, OSPF6_ROUTE_BEST)) { + if (is_debug) + zlog_debug( + "%s: route %pFX with cost %u is not best, ignore.", + __func__, &route->prefix, + route->path.cost); + return 0; + } + } + + if (route->path.origin.type == + htons(OSPF6_LSTYPE_INTRA_PREFIX)) { + if (!CHECK_FLAG(route->flag, OSPF6_ROUTE_BEST)) { + if (is_debug) + zlog_debug( + "%s: intra-prefix route %pFX with cost %u is not best, ignore.", + __func__, &route->prefix, + route->path.cost); + return 0; + } + } + + if (is_debug) + zlog_debug( + "Originating summary in area %s for %pFX cost %u", + area->name, &route->prefix, route->path.cost); + } + + /* if this route has just removed, remove corresponding LSA */ + if (CHECK_FLAG(route->flag, OSPF6_ROUTE_REMOVE)) { + if (is_debug) + zlog_debug( + "The route has just removed, purge previous LSA"); + + if (route->type == OSPF6_DEST_TYPE_RANGE) { + /* Whether the route have active longer prefix */ + if (!CHECK_FLAG(route->flag, + OSPF6_ROUTE_ACTIVE_SUMMARY)) { + if (is_debug) + zlog_debug( + "The range is not active. withdraw"); + + ospf6_abr_delete_route(summary, summary_table, + old); + } + } else if (old) { + ospf6_route_remove(summary, summary_table); + ospf6_lsa_purge(old); + } + return 0; + } + + if ((route->type == OSPF6_DEST_TYPE_ROUTER) + && (IS_AREA_STUB(area) || IS_AREA_NSSA(area))) { + if (is_debug) + zlog_debug( + "Area has been stubbed, purge Inter-Router LSA"); + + ospf6_abr_delete_route(summary, summary_table, old); + return 0; + } + + if (area->no_summary + && (route->path.subtype != OSPF6_PATH_SUBTYPE_DEFAULT_RT)) { + if (is_debug) + zlog_debug("Area has been stubbed, purge prefix LSA"); + + ospf6_abr_delete_route(summary, summary_table, old); + return 0; + } + + /* do not generate if the route cost is greater or equal to LSInfinity + */ + if (route->path.cost >= OSPF_LS_INFINITY) { + /* When we're clearing the range route because all active + * prefixes + * under the range are gone, we set the range's cost to + * OSPF_AREA_RANGE_COST_UNSPEC, which is > OSPF_LS_INFINITY. We + * don't want to trigger the code here for that. This code is + * for + * handling routes that have gone to infinity. The range removal + * happens + * elsewhere. + */ + if ((route->type != OSPF6_DEST_TYPE_RANGE) + && (route->path.cost != OSPF_AREA_RANGE_COST_UNSPEC)) { + if (is_debug) + zlog_debug( + "The cost exceeds LSInfinity, withdraw"); + if (old) + ospf6_lsa_purge(old); + return 0; + } + } + + /* if this is a route to ASBR */ + if (route->type == OSPF6_DEST_TYPE_ROUTER) { + /* Only the preferred best path is considered */ + if (!CHECK_FLAG(route->flag, OSPF6_ROUTE_BEST)) { + if (is_debug) + zlog_debug( + "This is the secondary path to the ASBR, ignore"); + ospf6_abr_delete_route(summary, summary_table, old); + return 0; + } + + /* Do not generate if area is NSSA */ + route_area = + ospf6_area_lookup(route->path.area_id, area->ospf6); + assert(route_area); + + if (IS_AREA_NSSA(route_area)) { + if (is_debug) + zlog_debug( + "%s: The route comes from NSSA area, skip", + __func__); + ospf6_abr_delete_route(summary, summary_table, old); + return 0; + } + + /* Do not generate if the area is stub */ + /* XXX */ + } + + /* if this is an intra-area route, this may be suppressed by aggregation + */ + if (route->type == OSPF6_DEST_TYPE_NETWORK + && route->path.type == OSPF6_PATH_TYPE_INTRA) { + /* search for configured address range for the route's area */ + route_area = + ospf6_area_lookup(route->path.area_id, area->ospf6); + assert(route_area); + range = ospf6_route_lookup_bestmatch(&route->prefix, + route_area->range_table); + + /* ranges are ignored when originate backbone routes to transit + area. + Otherwise, if ranges are configured, the route is suppressed. + */ + if (range && !CHECK_FLAG(range->flag, OSPF6_ROUTE_REMOVE) + && (route->path.area_id != OSPF_AREA_BACKBONE + || !IS_AREA_TRANSIT(area))) { + if (is_debug) + zlog_debug( + "Suppressed by range %pFX of area %s", + &range->prefix, route_area->name); + /* The existing summary route could be a range, don't + * remove it in this case + */ + if (summary && summary->type != OSPF6_DEST_TYPE_RANGE) + ospf6_abr_delete_route(summary, summary_table, + old); + return 0; + } + } + + /* If this is a configured address range */ + if (route->type == OSPF6_DEST_TYPE_RANGE) { + /* If DoNotAdvertise is set */ + if (CHECK_FLAG(route->flag, OSPF6_ROUTE_DO_NOT_ADVERTISE)) { + if (is_debug) + zlog_debug( + "This is the range with DoNotAdvertise set. ignore"); + ospf6_abr_delete_route(summary, summary_table, old); + return 0; + } + + /* If there are no active prefixes in this range, remove */ + if (!CHECK_FLAG(route->flag, OSPF6_ROUTE_ACTIVE_SUMMARY)) { + if (is_debug) + zlog_debug("The range is not active. withdraw"); + ospf6_abr_delete_route(summary, summary_table, old); + return 0; + } + } + + /* the route is going to be originated. store it in area's summary_table + */ + if (summary == NULL) { + summary = ospf6_route_copy(route); + summary->path.origin.adv_router = area->ospf6->router_id; + + if (route->type == OSPF6_DEST_TYPE_ROUTER) { + summary->path.origin.type = + htons(OSPF6_LSTYPE_INTER_ROUTER); + summary->path.origin.id = + ADV_ROUTER_IN_PREFIX(&route->prefix); + } else { + struct ospf6_lsa *old; + + summary->path.origin.type = + htons(OSPF6_LSTYPE_INTER_PREFIX); + + /* Try to reuse LS-ID from previous running instance. */ + old = ospf6_find_inter_prefix_lsa(area->ospf6, area, + &route->prefix); + if (old) + summary->path.origin.id = old->header->id; + else + summary->path.origin.id = ospf6_new_ls_id( + summary->path.origin.type, + summary->path.origin.adv_router, + area->lsdb); + } + summary = ospf6_route_add(summary, summary_table); + } else { + summary->type = route->type; + monotime(&summary->changed); + } + + summary->prefix_options = route->prefix_options; + summary->path.router_bits = route->path.router_bits; + summary->path.options[0] = route->path.options[0]; + summary->path.options[1] = route->path.options[1]; + summary->path.options[2] = route->path.options[2]; + summary->path.area_id = area->area_id; + summary->path.type = OSPF6_PATH_TYPE_INTER; + summary->path.subtype = route->path.subtype; + summary->path.cost = route->path.cost; + /* summary->nexthop[0] = route->nexthop[0]; */ + + /* prepare buffer */ + memset(buffer, 0, sizeof(buffer)); + lsa_header = (struct ospf6_lsa_header *)buffer; + + if (route->type == OSPF6_DEST_TYPE_ROUTER) { + router_lsa = (struct ospf6_inter_router_lsa + *)((caddr_t)lsa_header + + sizeof(struct ospf6_lsa_header)); + p = (caddr_t)router_lsa + sizeof(struct ospf6_inter_router_lsa); + + /* Fill Inter-Area-Router-LSA */ + router_lsa->options[0] = route->path.options[0]; + router_lsa->options[1] = route->path.options[1]; + router_lsa->options[2] = route->path.options[2]; + OSPF6_ABR_SUMMARY_METRIC_SET(router_lsa, route->path.cost); + router_lsa->router_id = ADV_ROUTER_IN_PREFIX(&route->prefix); + type = htons(OSPF6_LSTYPE_INTER_ROUTER); + } else { + prefix_lsa = (struct ospf6_inter_prefix_lsa + *)((caddr_t)lsa_header + + sizeof(struct ospf6_lsa_header)); + p = (caddr_t)prefix_lsa + sizeof(struct ospf6_inter_prefix_lsa); + + /* Fill Inter-Area-Prefix-LSA */ + OSPF6_ABR_SUMMARY_METRIC_SET(prefix_lsa, route->path.cost); + prefix_lsa->prefix.prefix_length = route->prefix.prefixlen; + prefix_lsa->prefix.prefix_options = route->prefix_options; + + /* set Prefix */ + memcpy(p, &route->prefix.u.prefix6, + OSPF6_PREFIX_SPACE(route->prefix.prefixlen)); + ospf6_prefix_apply_mask(&prefix_lsa->prefix); + p += OSPF6_PREFIX_SPACE(route->prefix.prefixlen); + type = htons(OSPF6_LSTYPE_INTER_PREFIX); + } + + /* Fill LSA Header */ + lsa_header->age = 0; + lsa_header->type = type; + lsa_header->id = summary->path.origin.id; + lsa_header->adv_router = area->ospf6->router_id; + lsa_header->seqnum = + ospf6_new_ls_seqnum(lsa_header->type, lsa_header->id, + lsa_header->adv_router, area->lsdb); + lsa_header->length = htons((caddr_t)p - (caddr_t)lsa_header); + + /* LSA checksum */ + ospf6_lsa_checksum(lsa_header); + + /* create LSA */ + lsa = ospf6_lsa_create(lsa_header); + + /* Reset the unapproved flag */ + UNSET_FLAG(lsa->flag, OSPF6_LSA_UNAPPROVED); + + /* Originate */ + ospf6_lsa_originate_area(lsa, area); + + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s : finish area %s", __func__, area->name); + + return 1; +} + +void ospf6_abr_range_reset_cost(struct ospf6 *ospf6) +{ + struct listnode *node, *nnode; + struct ospf6_area *oa; + struct ospf6_route *range; + + for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, oa)) { + for (range = ospf6_route_head(oa->range_table); range; + range = ospf6_route_next(range)) + OSPF6_ABR_RANGE_CLEAR_COST(range); + for (range = ospf6_route_head(oa->nssa_range_table); range; + range = ospf6_route_next(range)) + OSPF6_ABR_RANGE_CLEAR_COST(range); + } +} + +static inline uint32_t ospf6_abr_range_compute_cost(struct ospf6_route *range, + struct ospf6 *o) +{ + struct ospf6_route *ro; + uint32_t cost = 0; + + for (ro = ospf6_route_match_head(&range->prefix, o->route_table); ro; + ro = ospf6_route_match_next(&range->prefix, ro)) { + if (CHECK_FLAG(ro->flag, OSPF6_ROUTE_REMOVE)) + continue; + if (ro->path.area_id != range->path.area_id) + continue; + if (CHECK_FLAG(range->flag, OSPF6_ROUTE_NSSA_RANGE) + && ro->path.type != OSPF6_PATH_TYPE_EXTERNAL1 + && ro->path.type != OSPF6_PATH_TYPE_EXTERNAL2) + continue; + if (!CHECK_FLAG(range->flag, OSPF6_ROUTE_NSSA_RANGE) + && ro->path.type != OSPF6_PATH_TYPE_INTRA) + continue; + + cost = MAX(cost, ro->path.cost); + } + + return cost; +} + +static inline int +ospf6_abr_range_summary_needs_update(struct ospf6_route *range, uint32_t cost) +{ + int redo_summary = 0; + + if (CHECK_FLAG(range->flag, OSPF6_ROUTE_REMOVE)) { + UNSET_FLAG(range->flag, OSPF6_ROUTE_ACTIVE_SUMMARY); + redo_summary = 1; + } else if (CHECK_FLAG(range->flag, OSPF6_ROUTE_DO_NOT_ADVERTISE)) { + if (range->path.cost != 0) { + range->path.cost = 0; + redo_summary = 1; + } + } else if (cost) { + if ((OSPF6_PATH_COST_IS_CONFIGURED(range->path) + && range->path.cost != range->path.u.cost_config)) { + range->path.cost = range->path.u.cost_config; + SET_FLAG(range->flag, OSPF6_ROUTE_ACTIVE_SUMMARY); + redo_summary = 1; + } else if (!OSPF6_PATH_COST_IS_CONFIGURED(range->path) + && range->path.cost != cost) { + range->path.cost = cost; + SET_FLAG(range->flag, OSPF6_ROUTE_ACTIVE_SUMMARY); + redo_summary = 1; + } + } else if (CHECK_FLAG(range->flag, OSPF6_ROUTE_ACTIVE_SUMMARY)) { + /* Cost is zero, meaning no active range */ + UNSET_FLAG(range->flag, OSPF6_ROUTE_ACTIVE_SUMMARY); + range->path.cost = OSPF_AREA_RANGE_COST_UNSPEC; + redo_summary = 1; + } + + return (redo_summary); +} + +void ospf6_abr_range_update(struct ospf6_route *range, struct ospf6 *ospf6) +{ + uint32_t cost = 0; + struct listnode *node, *nnode; + struct ospf6_area *oa; + int summary_orig = 0; + + assert(range->type == OSPF6_DEST_TYPE_RANGE); + oa = ospf6_area_lookup(range->path.area_id, ospf6); + assert(oa); + + /* update range's cost and active flag */ + cost = ospf6_abr_range_compute_cost(range, ospf6); + + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s: range %pFX, cost %d", __func__, &range->prefix, + cost); + + /* Non-zero cost is a proxy for active longer prefixes in this range. + * If there are active routes covered by this range AND either the + * configured + * cost has changed or the summarized cost has changed then redo + * summaries. + * Alternately, if there are no longer active prefixes and there are + * summary announcements, withdraw those announcements. + * + * The don't advertise code relies on the path.cost being set to UNSPEC + * to + * work the first time. Subsequent times the path.cost is not 0 anyway + * if there + * were active ranges. + */ + if (!ospf6_abr_range_summary_needs_update(range, cost)) + return; + + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s: range %pFX update", __func__, &range->prefix); + + if (CHECK_FLAG(range->flag, OSPF6_ROUTE_NSSA_RANGE)) { + if (CHECK_FLAG(range->flag, OSPF6_ROUTE_ACTIVE_SUMMARY) + && !CHECK_FLAG(range->flag, OSPF6_ROUTE_DO_NOT_ADVERTISE)) { + ospf6_nssa_lsa_originate(range, oa, true); + summary_orig = 1; + } else { + struct ospf6_lsa *lsa; + + lsa = ospf6_lsdb_lookup(range->path.origin.type, + range->path.origin.id, + ospf6->router_id, oa->lsdb); + if (lsa) + ospf6_lsa_premature_aging(lsa); + } + } else { + for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, oa)) { + summary_orig += + ospf6_abr_originate_summary_to_area(range, oa); + } + } + + if (CHECK_FLAG(range->flag, OSPF6_ROUTE_ACTIVE_SUMMARY) + && summary_orig) { + if (!CHECK_FLAG(range->flag, OSPF6_ROUTE_BLACKHOLE_ADDED)) { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("Add discard route"); + + ospf6_zebra_add_discard(range, ospf6); + } + } else { + /* Summary removed or no summary generated as no + * specifics exist */ + if (CHECK_FLAG(range->flag, OSPF6_ROUTE_BLACKHOLE_ADDED)) { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("Delete discard route"); + + ospf6_zebra_delete_discard(range, ospf6); + } + } +} + +void ospf6_abr_originate_summary(struct ospf6_route *route, struct ospf6 *ospf6) +{ + struct listnode *node, *nnode; + struct ospf6_area *oa; + struct ospf6_route *range = NULL; + + if (IS_OSPF6_DEBUG_ABR) { + char buf[BUFSIZ]; + + if (route->type == OSPF6_DEST_TYPE_ROUTER) + inet_ntop(AF_INET, + &ADV_ROUTER_IN_PREFIX(&route->prefix), buf, + sizeof(buf)); + else + prefix2str(&route->prefix, buf, sizeof(buf)); + + zlog_debug("%s: route %s", __func__, buf); + } + + if (route->type == OSPF6_DEST_TYPE_NETWORK) { + oa = ospf6_area_lookup(route->path.area_id, ospf6); + if (!oa) { + zlog_err("OSPFv6 area lookup failed"); + return; + } + + range = ospf6_route_lookup_bestmatch(&route->prefix, + oa->range_table); + if (range) { + ospf6_abr_range_update(range, ospf6); + } + } + + for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, oa)) + ospf6_abr_originate_summary_to_area(route, oa); +} + +void ospf6_abr_defaults_to_stub(struct ospf6 *o) +{ + struct listnode *node, *nnode; + struct ospf6_area *oa; + struct ospf6_route *def, *route; + int type = DEFAULT_ROUTE; + + if (!o->backbone) + return; + + def = ospf6_route_create(o); + def->type = OSPF6_DEST_TYPE_NETWORK; + def->prefix.family = AF_INET6; + def->prefix.prefixlen = 0; + memset(&def->prefix.u.prefix6, 0, sizeof(struct in6_addr)); + def->type = OSPF6_DEST_TYPE_NETWORK; + def->path.type = OSPF6_PATH_TYPE_INTER; + def->path.subtype = OSPF6_PATH_SUBTYPE_DEFAULT_RT; + def->path.area_id = o->backbone->area_id; + def->path.metric_type = metric_type(o, type, 0); + def->path.cost = metric_value(o, type, 0); + + for (ALL_LIST_ELEMENTS(o->area_list, node, nnode, oa)) { + if (IS_AREA_STUB(oa) || (IS_AREA_NSSA(oa) && oa->no_summary)) { + /* announce defaults to stubby areas */ + if (IS_OSPF6_DEBUG_ABR) + zlog_debug( + "Announcing default route into stubby area %s", + oa->name); + UNSET_FLAG(def->flag, OSPF6_ROUTE_REMOVE); + ospf6_abr_originate_summary_to_area(def, oa); + } else { + /* withdraw defaults when an area switches from stub to + * non-stub */ + route = ospf6_route_lookup(&def->prefix, + oa->summary_prefix); + if (route + && (route->path.subtype == def->path.subtype)) { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug( + "Withdrawing default route from non-stubby area %s", + oa->name); + SET_FLAG(def->flag, OSPF6_ROUTE_REMOVE); + ospf6_abr_originate_summary_to_area(def, oa); + } + } + } + ospf6_route_delete(def); +} + +void ospf6_abr_old_path_update(struct ospf6_route *old_route, + struct ospf6_route *route, + struct ospf6_route_table *table) +{ + struct ospf6_path *o_path = NULL; + struct listnode *anode, *anext; + struct listnode *nnode, *rnode, *rnext; + struct ospf6_nexthop *nh, *rnh; + + for (ALL_LIST_ELEMENTS(old_route->paths, anode, anext, o_path)) { + if (o_path->area_id != route->path.area_id + || !ospf6_ls_origin_same(o_path, &route->path)) + continue; + + if ((o_path->cost == route->path.cost) && + (o_path->u.cost_e2 == route->path.u.cost_e2)) + continue; + + for (ALL_LIST_ELEMENTS_RO(o_path->nh_list, nnode, nh)) { + for (ALL_LIST_ELEMENTS(old_route->nh_list, rnode, + rnext, rnh)) { + if (!ospf6_nexthop_is_same(rnh, nh)) + continue; + listnode_delete(old_route->nh_list, rnh); + ospf6_nexthop_delete(rnh); + } + + } + + listnode_delete(old_route->paths, o_path); + ospf6_path_free(o_path); + + for (ALL_LIST_ELEMENTS(old_route->paths, anode, + anext, o_path)) { + ospf6_merge_nexthops(old_route->nh_list, + o_path->nh_list); + } + + if (IS_OSPF6_DEBUG_ABR || IS_OSPF6_DEBUG_EXAMIN(INTER_PREFIX)) + zlog_debug("%s: paths %u nh %u", __func__, + old_route->paths + ? listcount(old_route->paths) + : 0, + old_route->nh_list + ? listcount(old_route->nh_list) + : 0); + + if (table->hook_add) + (*table->hook_add)(old_route); + + if (old_route->path.origin.id == route->path.origin.id && + old_route->path.origin.adv_router == + route->path.origin.adv_router) { + struct ospf6_path *h_path; + + h_path = (struct ospf6_path *) + listgetdata(listhead(old_route->paths)); + old_route->path.origin.type = h_path->origin.type; + old_route->path.origin.id = h_path->origin.id; + old_route->path.origin.adv_router = + h_path->origin.adv_router; + } + } +} + +void ospf6_abr_old_route_remove(struct ospf6_lsa *lsa, struct ospf6_route *old, + struct ospf6_route_table *table) +{ + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s: route %pFX, paths %d", __func__, &old->prefix, + listcount(old->paths)); + + if (listcount(old->paths) > 1) { + struct listnode *anode, *anext, *nnode, *rnode, *rnext; + struct ospf6_path *o_path; + struct ospf6_nexthop *nh, *rnh; + bool nh_updated = false; + + for (ALL_LIST_ELEMENTS(old->paths, anode, anext, o_path)) { + if (o_path->origin.adv_router != lsa->header->adv_router + || o_path->origin.id != lsa->header->id) + continue; + for (ALL_LIST_ELEMENTS_RO(o_path->nh_list, nnode, nh)) { + for (ALL_LIST_ELEMENTS(old->nh_list, + rnode, rnext, rnh)) { + if (!ospf6_nexthop_is_same(rnh, nh)) + continue; + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("deleted nexthop"); + listnode_delete(old->nh_list, rnh); + ospf6_nexthop_delete(rnh); + } + } + listnode_delete(old->paths, o_path); + ospf6_path_free(o_path); + nh_updated = true; + } + + if (nh_updated) { + if (listcount(old->paths)) { + if (IS_OSPF6_DEBUG_ABR + || IS_OSPF6_DEBUG_EXAMIN(INTER_PREFIX)) + zlog_debug("%s: old %pFX updated nh %u", + __func__, &old->prefix, + old->nh_list ? listcount( + old->nh_list) + : 0); + + if (table->hook_add) + (*table->hook_add)(old); + + if ((old->path.origin.id == lsa->header->id) && + (old->path.origin.adv_router + == lsa->header->adv_router)) { + struct ospf6_path *h_path; + + h_path = (struct ospf6_path *) + listgetdata( + listhead(old->paths)); + old->path.origin.type = + h_path->origin.type; + old->path.origin.id = h_path->origin.id; + old->path.origin.adv_router = + h_path->origin.adv_router; + } + } else + ospf6_route_remove(old, table); + } + } else + ospf6_route_remove(old, table); +} + +/* RFC 2328 16.2. Calculating the inter-area routes */ +void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa) +{ + struct prefix prefix, abr_prefix; + struct ospf6_route_table *table = NULL; + struct ospf6_route *range, *route, *old = NULL, *old_route; + struct ospf6_route *abr_entry; + uint8_t type = 0; + char options[3] = {0, 0, 0}; + uint8_t prefix_options = 0; + uint32_t cost = 0; + uint8_t router_bits = 0; + char buf[PREFIX2STR_BUFFER]; + int is_debug = 0; + struct ospf6_inter_prefix_lsa *prefix_lsa = NULL; + struct ospf6_inter_router_lsa *router_lsa = NULL; + bool old_entry_updated = false; + struct ospf6_path *path, *o_path, *ecmp_path; + struct listnode *anode; + bool add_route = false; + + memset(&prefix, 0, sizeof(prefix)); + + if (lsa->header->type == htons(OSPF6_LSTYPE_INTER_PREFIX)) { + if (IS_OSPF6_DEBUG_EXAMIN(INTER_PREFIX)) { + is_debug++; + zlog_debug("%s: LSA %s age %d in area %s", __func__, + lsa->name, ospf6_lsa_age_current(lsa), + oa->name); + } + + prefix_lsa = + (struct ospf6_inter_prefix_lsa *)OSPF6_LSA_HEADER_END( + lsa->header); + prefix.family = AF_INET6; + prefix.prefixlen = prefix_lsa->prefix.prefix_length; + ospf6_prefix_in6_addr(&prefix.u.prefix6, prefix_lsa, + &prefix_lsa->prefix); + if (is_debug) + prefix2str(&prefix, buf, sizeof(buf)); + table = oa->ospf6->route_table; + type = OSPF6_DEST_TYPE_NETWORK; + prefix_options = prefix_lsa->prefix.prefix_options; + cost = OSPF6_ABR_SUMMARY_METRIC(prefix_lsa); + } else if (lsa->header->type == htons(OSPF6_LSTYPE_INTER_ROUTER)) { + if (IS_OSPF6_DEBUG_EXAMIN(INTER_ROUTER)) { + is_debug++; + zlog_debug("%s: LSA %s age %d in area %s", __func__, + lsa->name, ospf6_lsa_age_current(lsa), + oa->name); + } + + router_lsa = + (struct ospf6_inter_router_lsa *)OSPF6_LSA_HEADER_END( + lsa->header); + ospf6_linkstate_prefix(router_lsa->router_id, htonl(0), + &prefix); + if (is_debug) + inet_ntop(AF_INET, &router_lsa->router_id, buf, + sizeof(buf)); + + table = oa->ospf6->brouter_table; + type = OSPF6_DEST_TYPE_ROUTER; + options[0] = router_lsa->options[0]; + options[1] = router_lsa->options[1]; + options[2] = router_lsa->options[2]; + cost = OSPF6_ABR_SUMMARY_METRIC(router_lsa); + SET_FLAG(router_bits, OSPF6_ROUTER_BIT_E); + } else + assert(0); + + /* Find existing route */ + route = ospf6_route_lookup(&prefix, table); + if (route) { + ospf6_route_lock(route); + if (is_debug) + zlog_debug("%s: route %pFX, paths %d", __func__, + &prefix, listcount(route->paths)); + } + while (route && ospf6_route_is_prefix(&prefix, route)) { + if (route->path.area_id == oa->area_id + && route->path.origin.type == lsa->header->type + && !CHECK_FLAG(route->flag, OSPF6_ROUTE_WAS_REMOVED)) { + /* LSA adv. router could be part of route's + * paths list. Find the existing path and set + * old as the route. + */ + if (listcount(route->paths) > 1) { + for (ALL_LIST_ELEMENTS_RO(route->paths, anode, + o_path)) { + if (o_path->origin.id == lsa->header->id + && o_path->origin.adv_router == + lsa->header->adv_router) { + old = route; + + if (is_debug) + zlog_debug( + "%s: old entry found in paths, adv_router %pI4", + __func__, + &o_path->origin.adv_router); + + break; + } + } + } else if (route->path.origin.id == lsa->header->id && + route->path.origin.adv_router == + lsa->header->adv_router) + old = route; + } + route = ospf6_route_next(route); + } + if (route) + ospf6_route_unlock(route); + + /* (1) if cost == LSInfinity or if the LSA is MaxAge */ + if (cost == OSPF_LS_INFINITY) { + if (is_debug) + zlog_debug("cost is LS_INFINITY, ignore"); + if (old) + ospf6_abr_old_route_remove(lsa, old, table); + return; + } + if (OSPF6_LSA_IS_MAXAGE(lsa)) { + if (is_debug) + zlog_debug("%s: LSA %s is MaxAge, ignore", __func__, + lsa->name); + if (old) + ospf6_abr_old_route_remove(lsa, old, table); + return; + } + + + /* (2) if the LSA is self-originated, ignore */ + if (lsa->header->adv_router == oa->ospf6->router_id) { + if (is_debug) + zlog_debug("LSA %s is self-originated, ignore", + lsa->name); + if (old) + ospf6_route_remove(old, table); + return; + } + + /* (3) if the prefix is equal to an active configured address range */ + /* or if the NU bit is set in the prefix */ + if (lsa->header->type == htons(OSPF6_LSTYPE_INTER_PREFIX)) { + /* must have been set in previous block */ + assert(prefix_lsa); + + range = ospf6_route_lookup(&prefix, oa->range_table); + if (range) { + if (is_debug) + zlog_debug( + "Prefix is equal to address range, ignore"); + if (old) + ospf6_route_remove(old, table); + return; + } + + if (CHECK_FLAG(prefix_lsa->prefix.prefix_options, + OSPF6_PREFIX_OPTION_NU)) { + if (is_debug) + zlog_debug("Prefix has the NU bit set, ignore"); + if (old) + ospf6_route_remove(old, table); + return; + } + } + + if (lsa->header->type == htons(OSPF6_LSTYPE_INTER_ROUTER)) { + /* To pass test suites */ + assert(router_lsa); + if (!OSPF6_OPT_ISSET(router_lsa->options, OSPF6_OPT_R) + || !OSPF6_OPT_ISSET(router_lsa->options, OSPF6_OPT_V6)) { + if (is_debug) + zlog_debug( + "Router-LSA has the V6-bit or R-bit unset, ignore"); + if (old) + ospf6_route_remove(old, table); + + return; + } + /* Avoid infinite recursion if someone has maliciously announced + an + Inter-Router LSA for an ABR + */ + if (lsa->header->adv_router == router_lsa->router_id) { + if (is_debug) + zlog_debug( + "Ignoring Inter-Router LSA for an ABR (%s)", + buf); + if (old) + ospf6_route_remove(old, table); + + return; + } + } + + /* (4) if the routing table entry for the ABR does not exist */ + ospf6_linkstate_prefix(lsa->header->adv_router, htonl(0), &abr_prefix); + abr_entry = ospf6_route_lookup(&abr_prefix, oa->ospf6->brouter_table); + if (abr_entry == NULL || abr_entry->path.area_id != oa->area_id + || CHECK_FLAG(abr_entry->flag, OSPF6_ROUTE_REMOVE) + || !CHECK_FLAG(abr_entry->path.router_bits, OSPF6_ROUTER_BIT_B)) { + if (is_debug) + zlog_debug( + "%s: ABR router entry %pFX does not exist, ignore", + __func__, &abr_prefix); + if (old) { + if (old->type == OSPF6_DEST_TYPE_ROUTER && + oa->intra_brouter_calc) { + if (is_debug) + zlog_debug( + "%s: intra_brouter_calc is on, skip brouter remove: %s (%p)", + __func__, buf, (void *)old); + } else { + if (is_debug) + zlog_debug( + "%s: remove old entry: %s %p ", + __func__, buf, (void *)old); + ospf6_abr_old_route_remove(lsa, old, table); + } + } + return; + } + + /* (5),(6): the path preference is handled by the sorting + in the routing table. Always install the path by substituting + old route (if any). */ + route = ospf6_route_create(oa->ospf6); + + route->type = type; + route->prefix = prefix; + route->prefix_options = prefix_options; + route->path.origin.type = lsa->header->type; + route->path.origin.id = lsa->header->id; + route->path.origin.adv_router = lsa->header->adv_router; + route->path.router_bits = router_bits; + route->path.options[0] = options[0]; + route->path.options[1] = options[1]; + route->path.options[2] = options[2]; + route->path.area_id = oa->area_id; + route->path.type = OSPF6_PATH_TYPE_INTER; + route->path.cost = abr_entry->path.cost + cost; + + /* copy brouter rechable nexthops into the route. */ + ospf6_route_copy_nexthops(route, abr_entry); + + /* (7) If the routes are identical, copy the next hops over to existing + route. ospf6's route table implementation will otherwise string both + routes, but keep the older one as the best route since the routes + are identical. + */ + old = ospf6_route_lookup(&prefix, table); + if (old) { + if (is_debug) + zlog_debug("%s: found old route %pFX, paths %d", + __func__, &prefix, listcount(old->paths)); + } + for (old_route = old; old_route; old_route = old_route->next) { + + /* The route linked-list is grouped in batches of prefix. + * If the new prefix is not the same as the one of interest + * then we have walked over the end of the batch and so we + * should break rather than continuing unnecessarily. + */ + if (!ospf6_route_is_same(old_route, route)) + break; + if ((old_route->type != route->type) + || (old_route->path.type != route->path.type)) + continue; + + if ((ospf6_route_cmp(route, old_route) != 0)) { + if (is_debug) + zlog_debug( + "%s: old %p %pFX cost %u new route cost %u are not same", + __func__, (void *)old_route, &prefix, + old_route->path.cost, route->path.cost); + + /* Check new route's adv. router is same in one of + * the paths with differed cost, if so remove the + * old path as later new route will be added. + */ + if (listcount(old_route->paths) > 1) + ospf6_abr_old_path_update(old_route, route, + table); + continue; + } + + list_delete_all_node(old_route->nh_list); + ospf6_route_copy_nexthops(old_route, route); + old_entry_updated = true; + + for (ALL_LIST_ELEMENTS_RO(old_route->paths, anode, + o_path)) { + if (o_path->area_id == route->path.area_id + && ospf6_ls_origin_same(o_path, &route->path)) + break; + } + + /* New adv. router for a existing path add to paths list */ + if (o_path == NULL) { + ecmp_path = ospf6_path_dup(&route->path); + + /* Add a nh_list to new ecmp path */ + ospf6_copy_nexthops(ecmp_path->nh_list, route->nh_list); + + /* Add the new path to route's path list */ + listnode_add_sort(old_route->paths, ecmp_path); + + if (is_debug) { + zlog_debug( + "%s: route %pFX cost %u another path %pI4 added with nh %u, effective paths %u nh %u", + __func__, &route->prefix, + old_route->path.cost, + &ecmp_path->origin.adv_router, + listcount(ecmp_path->nh_list), + old_route->paths + ? listcount(old_route->paths) + : 0, + listcount(old_route->nh_list)); + } + } else { + struct ospf6_route *tmp_route; + + tmp_route = ospf6_route_create(oa->ospf6); + + ospf6_copy_nexthops(tmp_route->nh_list, + o_path->nh_list); + + if (!ospf6_route_cmp_nexthops(tmp_route, route)) { + /* adv. router exists in the list, update nhs */ + list_delete_all_node(o_path->nh_list); + ospf6_copy_nexthops(o_path->nh_list, + route->nh_list); + ospf6_route_delete(tmp_route); + } else { + /* adv. router has no change in nhs */ + old_entry_updated = false; + ospf6_route_delete(tmp_route); + continue; + } + } + + if (is_debug) + zlog_debug( + "%s: Update route: %s %p old cost %u new cost %u nh %u", + __func__, buf, (void *)old_route, + old_route->path.cost, route->path.cost, + listcount(old_route->nh_list)); + + /* For Inter-Prefix route: Update RIB/FIB, + * For Inter-Router trigger summary update + */ + if (table->hook_add) + (*table->hook_add)(old_route); + + break; + } + + /* If the old entry is not updated and old entry not found or old entry + * does not match with the new entry then add the new route + */ + if (old_entry_updated == false) { + if ((old == NULL) || (old->type != route->type) + || (old->path.type != route->path.type) + || (old->path.cost != route->path.cost)) + add_route = true; + } + + if (add_route) { + if (is_debug) { + zlog_debug( + "%s: Install new route: %s cost %u nh %u adv_router %pI4", + __func__, buf, route->path.cost, + listcount(route->nh_list), + &route->path.origin.adv_router); + } + + path = ospf6_path_dup(&route->path); + ospf6_copy_nexthops(path->nh_list, abr_entry->nh_list); + listnode_add_sort(route->paths, path); + /* ospf6_ia_add_nw_route (table, &prefix, route); */ + ospf6_route_add(route, table); + } else + /* if we did not add the route remove it */ + ospf6_route_delete(route); +} + +void ospf6_abr_examin_brouter(uint32_t router_id, struct ospf6_route *route, + struct ospf6 *ospf6) +{ + struct ospf6_lsa *lsa; + struct ospf6_area *oa; + uint16_t type; + + oa = ospf6_area_lookup(route->path.area_id, ospf6); + /* + * It is possible to designate a non backbone + * area first. If that is the case safely + * fall out of this function. + */ + if (oa == NULL) + return; + + type = htons(OSPF6_LSTYPE_INTER_ROUTER); + for (ALL_LSDB_TYPED_ADVRTR(oa->lsdb, type, router_id, lsa)) + ospf6_abr_examin_summary(lsa, oa); + + type = htons(OSPF6_LSTYPE_INTER_PREFIX); + for (ALL_LSDB_TYPED_ADVRTR(oa->lsdb, type, router_id, lsa)) + ospf6_abr_examin_summary(lsa, oa); +} + +void ospf6_abr_prefix_resummarize(struct ospf6 *o) +{ + struct ospf6_route *route; + + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("Re-examining Inter-Prefix Summaries"); + + for (route = ospf6_route_head(o->route_table); route; + route = ospf6_route_next(route)) + ospf6_abr_originate_summary(route, o); + + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("Finished re-examining Inter-Prefix Summaries"); +} + + +/* Display functions */ +static char *ospf6_inter_area_prefix_lsa_get_prefix_str(struct ospf6_lsa *lsa, + char *buf, int buflen, + int pos) +{ + struct ospf6_inter_prefix_lsa *prefix_lsa; + struct in6_addr in6; + char tbuf[16]; + + if (lsa != NULL) { + prefix_lsa = + (struct ospf6_inter_prefix_lsa *)OSPF6_LSA_HEADER_END( + lsa->header); + + ospf6_prefix_in6_addr(&in6, prefix_lsa, &prefix_lsa->prefix); + if (buf) { + inet_ntop(AF_INET6, &in6, buf, buflen); + snprintf(tbuf, sizeof(tbuf), "/%d", + prefix_lsa->prefix.prefix_length); + strlcat(buf, tbuf, buflen); + } + } + + return (buf); +} + +static int ospf6_inter_area_prefix_lsa_show(struct vty *vty, + struct ospf6_lsa *lsa, + json_object *json_obj, + bool use_json) +{ + struct ospf6_inter_prefix_lsa *prefix_lsa; + char buf[INET6_ADDRSTRLEN]; + + prefix_lsa = (struct ospf6_inter_prefix_lsa *)OSPF6_LSA_HEADER_END( + lsa->header); + + if (use_json) { + json_object_int_add( + json_obj, "metric", + (unsigned long)OSPF6_ABR_SUMMARY_METRIC(prefix_lsa)); + ospf6_prefix_options_printbuf(prefix_lsa->prefix.prefix_options, + buf, sizeof(buf)); + json_object_string_add(json_obj, "prefixOptions", buf); + json_object_string_add( + json_obj, "prefix", + ospf6_inter_area_prefix_lsa_get_prefix_str( + lsa, buf, sizeof(buf), 0)); + } else { + vty_out(vty, " Metric: %lu\n", + (unsigned long)OSPF6_ABR_SUMMARY_METRIC(prefix_lsa)); + + ospf6_prefix_options_printbuf(prefix_lsa->prefix.prefix_options, + buf, sizeof(buf)); + vty_out(vty, " Prefix Options: %s\n", buf); + + vty_out(vty, " Prefix: %s\n", + ospf6_inter_area_prefix_lsa_get_prefix_str( + lsa, buf, sizeof(buf), 0)); + } + + return 0; +} + +static char *ospf6_inter_area_router_lsa_get_prefix_str(struct ospf6_lsa *lsa, + char *buf, int buflen, + int pos) +{ + struct ospf6_inter_router_lsa *router_lsa; + + if (lsa != NULL) { + router_lsa = + (struct ospf6_inter_router_lsa *)OSPF6_LSA_HEADER_END( + lsa->header); + + + if (buf) + inet_ntop(AF_INET, &router_lsa->router_id, buf, buflen); + } + + return (buf); +} + +static int ospf6_inter_area_router_lsa_show(struct vty *vty, + struct ospf6_lsa *lsa, + json_object *json_obj, + bool use_json) +{ + struct ospf6_inter_router_lsa *router_lsa; + char buf[64]; + + router_lsa = (struct ospf6_inter_router_lsa *)OSPF6_LSA_HEADER_END( + lsa->header); + + ospf6_options_printbuf(router_lsa->options, buf, sizeof(buf)); + if (use_json) { + json_object_string_add(json_obj, "options", buf); + json_object_int_add( + json_obj, "metric", + (unsigned long)OSPF6_ABR_SUMMARY_METRIC(router_lsa)); + } else { + vty_out(vty, " Options: %s\n", buf); + vty_out(vty, " Metric: %lu\n", + (unsigned long)OSPF6_ABR_SUMMARY_METRIC(router_lsa)); + } + + inet_ntop(AF_INET, &router_lsa->router_id, buf, sizeof(buf)); + if (use_json) + json_object_string_add(json_obj, "destinationRouterId", buf); + else + vty_out(vty, " Destination Router ID: %s\n", buf); + + return 0; +} + +/* Debug commands */ +DEFUN (debug_ospf6_abr, + debug_ospf6_abr_cmd, + "debug ospf6 abr", + DEBUG_STR + OSPF6_STR + "Debug OSPFv3 ABR function\n" + ) +{ + OSPF6_DEBUG_ABR_ON(); + return CMD_SUCCESS; +} + +DEFUN (no_debug_ospf6_abr, + no_debug_ospf6_abr_cmd, + "no debug ospf6 abr", + NO_STR + DEBUG_STR + OSPF6_STR + "Debug OSPFv3 ABR function\n" + ) +{ + OSPF6_DEBUG_ABR_OFF(); + return CMD_SUCCESS; +} + +int config_write_ospf6_debug_abr(struct vty *vty) +{ + if (IS_OSPF6_DEBUG_ABR) + vty_out(vty, "debug ospf6 abr\n"); + return 0; +} + +void install_element_ospf6_debug_abr(void) +{ + install_element(ENABLE_NODE, &debug_ospf6_abr_cmd); + install_element(ENABLE_NODE, &no_debug_ospf6_abr_cmd); + install_element(CONFIG_NODE, &debug_ospf6_abr_cmd); + install_element(CONFIG_NODE, &no_debug_ospf6_abr_cmd); +} + +static struct ospf6_lsa_handler inter_prefix_handler = { + .lh_type = OSPF6_LSTYPE_INTER_PREFIX, + .lh_name = "Inter-Prefix", + .lh_short_name = "IAP", + .lh_show = ospf6_inter_area_prefix_lsa_show, + .lh_get_prefix_str = ospf6_inter_area_prefix_lsa_get_prefix_str, + .lh_debug = 0}; + +static struct ospf6_lsa_handler inter_router_handler = { + .lh_type = OSPF6_LSTYPE_INTER_ROUTER, + .lh_name = "Inter-Router", + .lh_short_name = "IAR", + .lh_show = ospf6_inter_area_router_lsa_show, + .lh_get_prefix_str = ospf6_inter_area_router_lsa_get_prefix_str, + .lh_debug = 0}; + +void ospf6_abr_init(void) +{ + ospf6_install_lsa_handler(&inter_prefix_handler); + ospf6_install_lsa_handler(&inter_router_handler); +} |