diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-09 13:16:35 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-09 13:16:35 +0000 |
commit | e2bbf175a2184bd76f6c54ccf8456babeb1a46fc (patch) | |
tree | f0b76550d6e6f500ada964a3a4ee933a45e5a6f1 /ospf6d | |
parent | Initial commit. (diff) | |
download | frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.tar.xz frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.zip |
Adding upstream version 9.1.upstream/9.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ospf6d')
52 files changed, 38394 insertions, 0 deletions
diff --git a/ospf6d/.gitignore b/ospf6d/.gitignore new file mode 100644 index 0000000..e398b1c --- /dev/null +++ b/ospf6d/.gitignore @@ -0,0 +1,2 @@ +ospf6d +ospf6d.conf diff --git a/ospf6d/Makefile b/ospf6d/Makefile new file mode 100644 index 0000000..97b37b8 --- /dev/null +++ b/ospf6d/Makefile @@ -0,0 +1,10 @@ +all: ALWAYS + @$(MAKE) -s -C .. ospf6d/ospf6d +%: ALWAYS + @$(MAKE) -s -C .. ospf6d/$@ + +Makefile: + #nothing +ALWAYS: +.PHONY: ALWAYS makefiles +.SUFFIXES: diff --git a/ospf6d/README b/ospf6d/README new file mode 100644 index 0000000..f5a0046 --- /dev/null +++ b/ospf6d/README @@ -0,0 +1,102 @@ + + Zebra OSPF daemon for IPv6 network + + 2003/08/18 + +README for newer code is not yet. General usage should remain +the same. For further usage, see command helps by typing '?' +in vty, and then imagin ! ;p) Previous README contents follows. + + Zebra OSPF daemon for IPv6 network + + 2001/12/20 + +Zebra OSPF6d is OSPF version 3 daemon which is specified by +"OSPF for IPv6" (RFC 2740). + +*** NOTE *** + Zebra ospf6d is in development yet. It may lack some functionalities, + and may have some bugs. Use the latest version from the anoncvs + repository (http://www.zebra.org/cvs.html) ! + +This file README is like memo yet, so please feel free to ask +<yasu@sfc.wide.ad.jp> by E-mail. Patches will be appriciated. + +ospf6d's vty port was default to 2606/tcp. +Use commands below. + +VIEW NODE: + show ipv6 ospf6 + To see Router-ID, uptime of ospf6d, some statistics. + + show ipv6 ospf6 database ... + This command shows LSA database. You can specify + LS-type/LS-ID/Advertising-Router of LSAs. '*' is recognized. + + show ipv6 ospf6 interface ... + To see the status of the OSPF interface, and the configuration + like interface costs. + + show ipv6 ospf6 neighbor ... + Shows state of neighbors and choosed (Backup) DR on the I/F. + + show ipv6 ospf6 route (X::X) + This command shows internal routing table of the ospf6d. + Routes not calculated by OSPFv3 (like connected routes) + are not shown. If Address is specified (X::X), shows the route + that the address matches. + + show ipv6 ospf6 route redistribute (X::X) + Shows the routes advertised as AS-External routes by the router + itself. If Address is specified (X::X), shows the route + that the address matches. + +CONFIG NODE: + interface NAME + To enter INTERFACE NODE + + router ospf6 ... + To enter OSPF6 NODE + +INTERFACE NODE: + ipv6 ospf6 cost COST + Sets the interface's output cost. Depends on interface bandwidth by default. + + ipv6 ospf6 hello-interval HELLOINTERVAL + Sets the interface's Hello Interval. default 10 + + ipv6 ospf6 dead-interval DEADINTERVAL + Sets the interface's Router Dead Interval. default 40 + + ipv6 ospf6 retransmit-interval RETRANSMITINTERVAL + Sets the interface's Rxmt Interval. default 5 + + ipv6 ospf6 priority PRIORITY + Sets the interface's Router Priority. default 1 + + ipv6 ospf6 transmit-delay TRANSMITDELAY + Sets the interface's Inf-Trans-Delay. default 1 + +OSPF6 NODE: + router-id A.B.C.D + Sets the router's Router-ID + + interface NAME area AREA + Binds interface to specified Area, and start + sending OSPFv3 packets. + + auto-cost reference-bandwidth COST + Sets the reference bandwidth for cost calculations, where this + bandwidth is considered equivalent to an OSPF cost of 1, specified + in Mbits/s. The default is 100Mbit/s (i.e. a link of bandwidth + 100Mbit/s or higher will have a cost of 1. Cost of lower bandwidth + links will be scaled with reference to this cost). This + configuration setting MUST be consistent across all routers within + the OSPF domain. + +Sample configuration is in ospf6d.conf.sample. + +-- +Yasuhiro Ohara <yasu@sfc.wide.ad.jp> +Kunihiro Ishiguro <kunihiro@zebra.org> + 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); +} diff --git a/ospf6d/ospf6_abr.h b/ospf6d/ospf6_abr.h new file mode 100644 index 0000000..7bb1619 --- /dev/null +++ b/ospf6d/ospf6_abr.h @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2004 Yasuhiro Ohara + */ + +#ifndef OSPF6_ABR_H +#define OSPF6_ABR_H + +/* for struct ospf6_route */ +#include "ospf6_route.h" +/* for struct ospf6_prefix */ +#include "ospf6_proto.h" + +/* Debug option */ +extern unsigned char conf_debug_ospf6_abr; +#define OSPF6_DEBUG_ABR_ON() (conf_debug_ospf6_abr = 1) +#define OSPF6_DEBUG_ABR_OFF() (conf_debug_ospf6_abr = 0) +#define IS_OSPF6_DEBUG_ABR (conf_debug_ospf6_abr) + +/* Inter-Area-Prefix-LSA */ +#define OSPF6_INTER_PREFIX_LSA_MIN_SIZE 4U /* w/o IPv6 prefix */ +struct ospf6_inter_prefix_lsa { + uint32_t metric; + struct ospf6_prefix prefix; +}; + +/* Inter-Area-Router-LSA */ +#define OSPF6_INTER_ROUTER_LSA_FIX_SIZE 12U +struct ospf6_inter_router_lsa { + uint8_t mbz; + uint8_t options[3]; + uint32_t metric; + uint32_t router_id; +}; + +#define OSPF6_ABR_SUMMARY_METRIC(E) (ntohl ((E)->metric & htonl (0x00ffffff))) +#define OSPF6_ABR_SUMMARY_METRIC_SET(E, C) \ + { \ + (E)->metric &= htonl(0x00000000); \ + (E)->metric |= htonl(0x00ffffff) & htonl(C); \ + } + +#define OSPF6_ABR_RANGE_CLEAR_COST(range) (range->path.cost = OSPF_AREA_RANGE_COST_UNSPEC) +#define IS_OSPF6_ABR(o) ((o)->flag & OSPF6_FLAG_ABR) + +extern bool ospf6_check_and_set_router_abr(struct ospf6 *o); + +extern void ospf6_abr_enable_area(struct ospf6_area *oa); +extern void ospf6_abr_disable_area(struct ospf6_area *oa); + +extern int ospf6_abr_originate_summary_to_area(struct ospf6_route *route, + struct ospf6_area *area); +extern void ospf6_abr_originate_summary(struct ospf6_route *route, + struct ospf6 *ospf6); +extern void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, + struct ospf6_area *oa); +extern void ospf6_abr_defaults_to_stub(struct ospf6 *ospf6); +extern void ospf6_abr_examin_brouter(uint32_t router_id, + struct ospf6_route *route, + struct ospf6 *ospf6); +extern void ospf6_abr_range_reset_cost(struct ospf6 *ospf6); +extern void ospf6_abr_prefix_resummarize(struct ospf6 *ospf6); + +extern int config_write_ospf6_debug_abr(struct vty *vty); +extern void install_element_ospf6_debug_abr(void); +extern int ospf6_abr_config_write(struct vty *vty); +extern void ospf6_abr_old_route_remove(struct ospf6_lsa *lsa, + struct ospf6_route *old, + struct ospf6_route_table *table); +extern void ospf6_abr_old_path_update(struct ospf6_route *old_route, + struct ospf6_route *route, + struct ospf6_route_table *table); +extern void ospf6_abr_init(void); +extern void ospf6_abr_range_update(struct ospf6_route *range, + struct ospf6 *ospf6); +extern void ospf6_abr_remove_unapproved_summaries(struct ospf6 *ospf6); +extern int ospf6_ls_origin_same(struct ospf6_path *o_path, + struct ospf6_path *r_path); + +#endif /*OSPF6_ABR_H*/ diff --git a/ospf6d/ospf6_area.c b/ospf6d/ospf6_area.c new file mode 100644 index 0000000..69c711b --- /dev/null +++ b/ospf6d/ospf6_area.c @@ -0,0 +1,1463 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2003 Yasuhiro Ohara + */ + +#include <zebra.h> + +#include "log.h" +#include "memory.h" +#include "linklist.h" +#include "frrevent.h" +#include "vty.h" +#include "command.h" +#include "if.h" +#include "prefix.h" +#include "table.h" +#include "plist.h" +#include "filter.h" + +#include "ospf6_proto.h" +#include "ospf6_lsa.h" +#include "ospf6_lsdb.h" +#include "ospf6_route.h" +#include "ospf6_spf.h" +#include "ospf6_top.h" +#include "ospf6_area.h" +#include "ospf6_message.h" +#include "ospf6_neighbor.h" +#include "ospf6_interface.h" +#include "ospf6_intra.h" +#include "ospf6_abr.h" +#include "ospf6_asbr.h" +#include "ospf6_zebra.h" +#include "ospf6d.h" +#include "lib/json.h" +#include "ospf6_nssa.h" +#include "ospf6d/ospf6_area_clippy.c" + +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_AREA, "OSPF6 area"); +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_PLISTNAME, "Prefix list name"); + +int str2area_id(const char *str, uint32_t *area_id, int *area_id_fmt) +{ + char *ep; + + *area_id = htonl(strtoul(str, &ep, 10)); + if (*ep && inet_pton(AF_INET, str, area_id) != 1) + return -1; + + *area_id_fmt = + !*ep ? OSPF6_AREA_FMT_DECIMAL : OSPF6_AREA_FMT_DOTTEDQUAD; + + return 0; +} + +void area_id2str(char *buf, int len, uint32_t area_id, int area_id_fmt) +{ + if (area_id_fmt == OSPF6_AREA_FMT_DECIMAL) + snprintf(buf, len, "%u", ntohl(area_id)); + else + inet_ntop(AF_INET, &area_id, buf, len); +} + +int ospf6_area_cmp(void *va, void *vb) +{ + struct ospf6_area *oa = (struct ospf6_area *)va; + struct ospf6_area *ob = (struct ospf6_area *)vb; + return (ntohl(oa->area_id) < ntohl(ob->area_id) ? -1 : 1); +} + +/* schedule routing table recalculation */ +static void ospf6_area_lsdb_hook_add(struct ospf6_lsa *lsa) +{ + switch (ntohs(lsa->header->type)) { + + case OSPF6_LSTYPE_ROUTER: + case OSPF6_LSTYPE_NETWORK: + if (IS_OSPF6_DEBUG_EXAMIN_TYPE(lsa->header->type)) { + zlog_debug("%s Examin LSA %s", __func__, lsa->name); + zlog_debug(" Schedule SPF Calculation for %s", + OSPF6_AREA(lsa->lsdb->data)->name); + } + ospf6_spf_schedule( + OSPF6_PROCESS(OSPF6_AREA(lsa->lsdb->data)->ospf6), + ospf6_lsadd_to_spf_reason(lsa)); + break; + + case OSPF6_LSTYPE_INTRA_PREFIX: + ospf6_intra_prefix_lsa_add(lsa); + break; + + case OSPF6_LSTYPE_INTER_PREFIX: + case OSPF6_LSTYPE_INTER_ROUTER: + ospf6_abr_examin_summary(lsa, + (struct ospf6_area *)lsa->lsdb->data); + break; + + case OSPF6_LSTYPE_TYPE_7: + ospf6_asbr_lsa_add(lsa); + break; + + default: + break; + } +} + +static void ospf6_area_lsdb_hook_remove(struct ospf6_lsa *lsa) +{ + switch (ntohs(lsa->header->type)) { + case OSPF6_LSTYPE_ROUTER: + case OSPF6_LSTYPE_NETWORK: + if (IS_OSPF6_DEBUG_EXAMIN_TYPE(lsa->header->type)) { + zlog_debug("LSA disappearing: %s", lsa->name); + zlog_debug("Schedule SPF Calculation for %s", + OSPF6_AREA(lsa->lsdb->data)->name); + } + ospf6_spf_schedule( + OSPF6_PROCESS(OSPF6_AREA(lsa->lsdb->data)->ospf6), + ospf6_lsremove_to_spf_reason(lsa)); + break; + + case OSPF6_LSTYPE_INTRA_PREFIX: + ospf6_intra_prefix_lsa_remove(lsa); + break; + + case OSPF6_LSTYPE_INTER_PREFIX: + case OSPF6_LSTYPE_INTER_ROUTER: + ospf6_abr_examin_summary(lsa, + (struct ospf6_area *)lsa->lsdb->data); + break; + case OSPF6_LSTYPE_TYPE_7: + ospf6_asbr_lsa_remove(lsa, NULL); + break; + default: + break; + } +} + +static void ospf6_area_route_hook_add(struct ospf6_route *route) +{ + struct ospf6_area *oa = route->table->scope; + struct ospf6 *ospf6 = oa->ospf6; + struct ospf6_route *copy; + + copy = ospf6_route_copy(route); + ospf6_route_add(copy, ospf6->route_table); +} + +static void ospf6_area_route_hook_remove(struct ospf6_route *route) +{ + struct ospf6_area *oa = route->table->scope; + struct ospf6 *ospf6 = oa->ospf6; + struct ospf6_route *copy; + + copy = ospf6_route_lookup_identical(route, ospf6->route_table); + if (copy) + ospf6_route_remove(copy, ospf6->route_table); +} + +static void ospf6_area_stub_update(struct ospf6_area *area) +{ + + if (IS_AREA_STUB(area)) { + if (IS_OSPF6_DEBUG_ORIGINATE(ROUTER)) + zlog_debug("Stubbing out area for area %s", area->name); + OSPF6_OPT_CLEAR(area->options, OSPF6_OPT_E); + ospf6_asbr_remove_externals_from_area(area); + } else if (IS_AREA_ENABLED(area)) { + if (IS_OSPF6_DEBUG_ORIGINATE(ROUTER)) + zlog_debug("Normal area for area %s", area->name); + OSPF6_OPT_SET(area->options, OSPF6_OPT_E); + ospf6_asbr_send_externals_to_area(area); + } + + OSPF6_ROUTER_LSA_SCHEDULE(area); +} + +static int ospf6_area_stub_set(struct ospf6 *ospf6, struct ospf6_area *area) +{ + if (!IS_AREA_STUB(area)) { + /* Disable NSSA first. */ + ospf6_area_nssa_unset(ospf6, area); + + SET_FLAG(area->flag, OSPF6_AREA_STUB); + ospf6_area_stub_update(area); + } + + return 1; +} + +void ospf6_area_stub_unset(struct ospf6 *ospf6, struct ospf6_area *area) +{ + if (IS_AREA_STUB(area)) { + UNSET_FLAG(area->flag, OSPF6_AREA_STUB); + ospf6_area_stub_update(area); + } +} + +static void ospf6_area_no_summary_set(struct ospf6 *ospf6, + struct ospf6_area *area) +{ + if (area) { + if (!area->no_summary) { + area->no_summary = 1; + ospf6_abr_range_reset_cost(ospf6); + ospf6_abr_prefix_resummarize(ospf6); + } + } +} + +static void ospf6_area_no_summary_unset(struct ospf6 *ospf6, + struct ospf6_area *area) +{ + if (area) { + if (area->no_summary) { + area->no_summary = 0; + ospf6_abr_range_reset_cost(ospf6); + ospf6_abr_prefix_resummarize(ospf6); + } + } +} + +static void ospf6_nssa_default_originate_set(struct ospf6 *ospf6, + struct ospf6_area *area, + int metric, int metric_type) +{ + if (!area->nssa_default_originate.enabled) { + area->nssa_default_originate.enabled = true; + if (++ospf6->nssa_default_import_check.refcnt == 1) { + ospf6->nssa_default_import_check.status = false; + ospf6_zebra_import_default_route(ospf6, false); + } + } + + area->nssa_default_originate.metric_value = metric; + area->nssa_default_originate.metric_type = metric_type; +} + +static void ospf6_nssa_default_originate_unset(struct ospf6 *ospf6, + struct ospf6_area *area) +{ + if (area->nssa_default_originate.enabled) { + area->nssa_default_originate.enabled = false; + if (--ospf6->nssa_default_import_check.refcnt == 0) { + ospf6->nssa_default_import_check.status = false; + ospf6_zebra_import_default_route(ospf6, true); + } + area->nssa_default_originate.metric_value = -1; + area->nssa_default_originate.metric_type = -1; + } +} + +/** + * Make new area structure. + * + * @param area_id - ospf6 area ID + * @param o - ospf6 instance + * @param df - display format for area ID + */ +struct ospf6_area *ospf6_area_create(uint32_t area_id, struct ospf6 *o, int df) +{ + struct ospf6_area *oa; + + oa = XCALLOC(MTYPE_OSPF6_AREA, sizeof(struct ospf6_area)); + + switch (df) { + case OSPF6_AREA_FMT_DECIMAL: + snprintf(oa->name, sizeof(oa->name), "%u", ntohl(area_id)); + break; + default: + case OSPF6_AREA_FMT_DOTTEDQUAD: + inet_ntop(AF_INET, &area_id, oa->name, sizeof(oa->name)); + break; + } + + oa->area_id = area_id; + oa->if_list = list_new(); + + oa->lsdb = ospf6_lsdb_create(oa); + oa->lsdb->hook_add = ospf6_area_lsdb_hook_add; + oa->lsdb->hook_remove = ospf6_area_lsdb_hook_remove; + oa->lsdb_self = ospf6_lsdb_create(oa); + oa->temp_router_lsa_lsdb = ospf6_lsdb_create(oa); + + oa->spf_table = OSPF6_ROUTE_TABLE_CREATE(AREA, SPF_RESULTS); + oa->spf_table->scope = oa; + oa->route_table = OSPF6_ROUTE_TABLE_CREATE(AREA, ROUTES); + oa->route_table->scope = oa; + oa->route_table->hook_add = ospf6_area_route_hook_add; + oa->route_table->hook_remove = ospf6_area_route_hook_remove; + + oa->range_table = OSPF6_ROUTE_TABLE_CREATE(AREA, PREFIX_RANGES); + oa->range_table->scope = oa; + oa->nssa_range_table = OSPF6_ROUTE_TABLE_CREATE(AREA, PREFIX_RANGES); + oa->nssa_range_table->scope = oa; + oa->summary_prefix = OSPF6_ROUTE_TABLE_CREATE(AREA, SUMMARY_PREFIXES); + oa->summary_prefix->scope = oa; + oa->summary_router = OSPF6_ROUTE_TABLE_CREATE(AREA, SUMMARY_ROUTERS); + oa->summary_router->scope = oa; + oa->router_lsa_size_limit = 1024 + 256; + + /* set default options */ + if (CHECK_FLAG(o->flag, OSPF6_STUB_ROUTER)) { + OSPF6_OPT_CLEAR(oa->options, OSPF6_OPT_V6); + OSPF6_OPT_CLEAR(oa->options, OSPF6_OPT_R); + } else { + OSPF6_OPT_SET(oa->options, OSPF6_OPT_V6); + OSPF6_OPT_SET(oa->options, OSPF6_OPT_R); + } + + OSPF6_OPT_SET(oa->options, OSPF6_OPT_E); + + SET_FLAG(oa->flag, OSPF6_AREA_ACTIVE); + SET_FLAG(oa->flag, OSPF6_AREA_ENABLE); + + oa->ospf6 = o; + listnode_add_sort(o->area_list, oa); + + if (area_id == OSPF_AREA_BACKBONE) { + o->backbone = oa; + } + + return oa; +} + +void ospf6_area_delete(struct ospf6_area *oa) +{ + struct listnode *n; + struct ospf6_interface *oi; + + /* The ospf6_interface structs store configuration + * information which should not be lost/reset when + * deleting an area. + * So just detach the interface from the area and + * keep it around. */ + for (ALL_LIST_ELEMENTS_RO(oa->if_list, n, oi)) { + oi->area = NULL; + + struct listnode *node; + struct listnode *nnode; + struct ospf6_neighbor *on; + + for (ALL_LIST_ELEMENTS(oi->neighbor_list, node, nnode, on)) + ospf6_neighbor_delete(on); + } + + list_delete(&oa->if_list); + + ospf6_lsdb_delete(oa->lsdb); + ospf6_lsdb_delete(oa->lsdb_self); + ospf6_lsdb_delete(oa->temp_router_lsa_lsdb); + + ospf6_spf_table_finish(oa->spf_table); + ospf6_route_table_delete(oa->spf_table); + ospf6_route_table_delete(oa->route_table); + + ospf6_route_table_delete(oa->range_table); + ospf6_route_table_delete(oa->nssa_range_table); + ospf6_route_table_delete(oa->summary_prefix); + ospf6_route_table_delete(oa->summary_router); + + listnode_delete(oa->ospf6->area_list, oa); + oa->ospf6 = NULL; + + /* free area */ + XFREE(MTYPE_OSPF6_AREA, oa); +} + +struct ospf6_area *ospf6_area_lookup_by_area_id(uint32_t area_id) +{ + struct ospf6_area *oa; + struct listnode *n, *node, *nnode; + struct ospf6 *ospf6; + + for (ALL_LIST_ELEMENTS(om6->ospf6, node, nnode, ospf6)) { + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, n, oa)) + if (oa->area_id == area_id) + return oa; + } + return (struct ospf6_area *)NULL; +} + +struct ospf6_area *ospf6_area_lookup(uint32_t area_id, struct ospf6 *ospf6) +{ + struct ospf6_area *oa; + struct listnode *n; + + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, n, oa)) + if (oa->area_id == area_id) + return oa; + + return (struct ospf6_area *)NULL; +} + +void ospf6_area_enable(struct ospf6_area *oa) +{ + struct listnode *node, *nnode; + struct ospf6_interface *oi; + + SET_FLAG(oa->flag, OSPF6_AREA_ENABLE); + + for (ALL_LIST_ELEMENTS(oa->if_list, node, nnode, oi)) + ospf6_interface_enable(oi); + ospf6_abr_enable_area(oa); +} + +void ospf6_area_disable(struct ospf6_area *oa) +{ + struct listnode *node, *nnode; + struct ospf6_interface *oi; + + UNSET_FLAG(oa->flag, OSPF6_AREA_ENABLE); + + for (ALL_LIST_ELEMENTS(oa->if_list, node, nnode, oi)) + ospf6_interface_disable(oi); + + ospf6_abr_disable_area(oa); + ospf6_lsdb_remove_all(oa->lsdb); + ospf6_lsdb_remove_all(oa->lsdb_self); + + ospf6_spf_table_finish(oa->spf_table); + ospf6_route_remove_all(oa->route_table); + + EVENT_OFF(oa->thread_router_lsa); + EVENT_OFF(oa->thread_intra_prefix_lsa); +} + + +void ospf6_area_show(struct vty *vty, struct ospf6_area *oa, + json_object *json_areas, bool use_json) +{ + struct listnode *i; + struct ospf6_interface *oi; + unsigned long result; + json_object *json_area; + json_object *array_interfaces; + + if (use_json) { + json_area = json_object_new_object(); + json_object_boolean_add(json_area, "areaIsStub", + IS_AREA_STUB(oa)); + json_object_boolean_add(json_area, "areaIsNSSA", + IS_AREA_NSSA(oa)); + if (IS_AREA_STUB(oa) || IS_AREA_NSSA(oa)) { + json_object_boolean_add(json_area, "areaNoSummary", + oa->no_summary); + } + + json_object_int_add(json_area, "numberOfAreaScopedLsa", + oa->lsdb->count); + json_object_object_add( + json_area, "lsaStatistics", + JSON_OBJECT_NEW_ARRAY(json_object_new_int, + oa->lsdb->stats, + OSPF6_LSTYPE_SIZE)); + + /* Interfaces Attached */ + array_interfaces = json_object_new_array(); + for (ALL_LIST_ELEMENTS_RO(oa->if_list, i, oi)) + json_object_array_add( + array_interfaces, + json_object_new_string(oi->interface->name)); + + json_object_object_add(json_area, "interfacesAttachedToArea", + array_interfaces); + + if (oa->ts_spf.tv_sec || oa->ts_spf.tv_usec) { + json_object_boolean_true_add(json_area, "spfHasRun"); + result = monotime_since(&oa->ts_spf, NULL); + if (result / TIMER_SECOND_MICRO > 0) { + json_object_int_add( + json_area, "spfLastExecutedSecs", + result / TIMER_SECOND_MICRO); + + json_object_int_add( + json_area, "spfLastExecutedMicroSecs", + result % TIMER_SECOND_MICRO); + } else { + json_object_int_add(json_area, + "spfLastExecutedSecs", 0); + json_object_int_add(json_area, + "spfLastExecutedMicroSecs", + result); + } + } else + json_object_boolean_false_add(json_area, "spfHasRun"); + + + json_object_object_add(json_areas, oa->name, json_area); + + } else { + + if (!IS_AREA_STUB(oa) && !IS_AREA_NSSA(oa)) + vty_out(vty, " Area %s\n", oa->name); + else { + if (oa->no_summary) { + vty_out(vty, " Area %s[%s, No Summary]\n", + oa->name, + IS_AREA_STUB(oa) ? "Stub" : "NSSA"); + } else { + vty_out(vty, " Area %s[%s]\n", oa->name, + IS_AREA_STUB(oa) ? "Stub" : "NSSA"); + } + } + vty_out(vty, " Number of Area scoped LSAs is %u\n", + oa->lsdb->count); + + vty_out(vty, " Interface attached to this area:"); + for (ALL_LIST_ELEMENTS_RO(oa->if_list, i, oi)) + vty_out(vty, " %s", oi->interface->name); + vty_out(vty, "\n"); + + if (oa->ts_spf.tv_sec || oa->ts_spf.tv_usec) { + result = monotime_since(&oa->ts_spf, NULL); + if (result / TIMER_SECOND_MICRO > 0) { + vty_out(vty, + " SPF last executed %ld.%lds ago\n", + result / TIMER_SECOND_MICRO, + result % TIMER_SECOND_MICRO); + } else { + vty_out(vty, + " SPF last executed %ldus ago\n", + result); + } + } else + vty_out(vty, "SPF has not been run\n"); + } +} + +DEFUN (area_range, + area_range_cmd, + "area <A.B.C.D|(0-4294967295)> range X:X::X:X/M [<advertise|not-advertise|cost (0-16777215)>]", + "OSPF6 area parameters\n" + "OSPF6 area ID in IP address format\n" + "OSPF6 area ID as a decimal value\n" + "Configured address range\n" + "Specify IPv6 prefix\n" + "Advertise\n" + "Do not advertise\n" + "User specified metric for this range\n" + "Advertised metric for this range\n") +{ + int idx_ipv4 = 1; + int idx_ipv6_prefixlen = 3; + int idx_type = 4; + int ret; + struct ospf6_area *oa; + struct prefix prefix; + struct ospf6_route *range; + uint32_t cost; + + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + OSPF6_CMD_AREA_GET(argv[idx_ipv4]->arg, oa, ospf6); + + ret = str2prefix(argv[idx_ipv6_prefixlen]->arg, &prefix); + if (ret != 1 || prefix.family != AF_INET6) { + vty_out(vty, "Malformed argument: %s\n", + argv[idx_ipv6_prefixlen]->arg); + return CMD_SUCCESS; + } + + range = ospf6_route_lookup(&prefix, oa->range_table); + if (range == NULL) { + range = ospf6_route_create(ospf6); + range->type = OSPF6_DEST_TYPE_RANGE; + range->prefix = prefix; + range->path.area_id = oa->area_id; + range->path.cost = OSPF_AREA_RANGE_COST_UNSPEC; + } + + /* default settings */ + cost = OSPF_AREA_RANGE_COST_UNSPEC; + UNSET_FLAG(range->flag, OSPF6_ROUTE_DO_NOT_ADVERTISE); + + if (argc > idx_type) { + if (strmatch(argv[idx_type]->text, "not-advertise")) + SET_FLAG(range->flag, OSPF6_ROUTE_DO_NOT_ADVERTISE); + else if (strmatch(argv[idx_type]->text, "cost")) + cost = strtoul(argv[5]->arg, NULL, 10); + } + + range->path.u.cost_config = cost; + + if (range->rnode == NULL) { + ospf6_route_add(range, oa->range_table); + } + + if (ospf6_check_and_set_router_abr(ospf6)) { + /* Redo summaries if required */ + ospf6_abr_prefix_resummarize(ospf6); + } + + return CMD_SUCCESS; +} + +DEFUN (no_area_range, + no_area_range_cmd, + "no area <A.B.C.D|(0-4294967295)> range X:X::X:X/M [<advertise|not-advertise|cost (0-16777215)>]", + NO_STR + "OSPF6 area parameters\n" + "OSPF6 area ID in IP address format\n" + "OSPF6 area ID as a decimal value\n" + "Configured address range\n" + "Specify IPv6 prefix\n" + "Advertise\n" + "Do not advertise\n" + "User specified metric for this range\n" + "Advertised metric for this range\n") +{ + int idx_ipv4 = 2; + int idx_ipv6 = 4; + int ret; + struct ospf6_area *oa; + struct prefix prefix; + struct ospf6_route *range, *route; + + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + OSPF6_CMD_AREA_GET(argv[idx_ipv4]->arg, oa, ospf6); + + ret = str2prefix(argv[idx_ipv6]->arg, &prefix); + if (ret != 1 || prefix.family != AF_INET6) { + vty_out(vty, "Malformed argument: %s\n", argv[idx_ipv6]->arg); + return CMD_SUCCESS; + } + + range = ospf6_route_lookup(&prefix, oa->range_table); + if (range == NULL) { + vty_out(vty, "Range %s does not exists.\n", + argv[idx_ipv6]->arg); + return CMD_SUCCESS; + } + + if (ospf6_check_and_set_router_abr(oa->ospf6)) { + /* Blow away the aggregated LSA and route */ + SET_FLAG(range->flag, OSPF6_ROUTE_REMOVE); + + /* Redo summaries if required */ + for (route = ospf6_route_head(oa->ospf6->route_table); route; + route = ospf6_route_next(route)) + ospf6_abr_originate_summary(route, oa->ospf6); + + /* purge the old aggregated summary LSA */ + ospf6_abr_originate_summary(range, oa->ospf6); + } + ospf6_route_remove(range, oa->range_table); + + return CMD_SUCCESS; +} + +void ospf6_area_config_write(struct vty *vty, struct ospf6 *ospf6) +{ + struct listnode *node; + struct ospf6_area *oa; + struct ospf6_route *range; + + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, oa)) { + for (range = ospf6_route_head(oa->range_table); range; + range = ospf6_route_next(range)) { + vty_out(vty, " area %s range %pFX", oa->name, + &range->prefix); + + if (CHECK_FLAG(range->flag, + OSPF6_ROUTE_DO_NOT_ADVERTISE)) { + vty_out(vty, " not-advertise"); + } else { + // "advertise" is the default so we do not + // display it + if (range->path.u.cost_config + != OSPF_AREA_RANGE_COST_UNSPEC) + vty_out(vty, " cost %d", + range->path.u.cost_config); + } + vty_out(vty, "\n"); + } + if (IS_AREA_STUB(oa)) { + if (oa->no_summary) + vty_out(vty, " area %s stub no-summary\n", + oa->name); + else + vty_out(vty, " area %s stub\n", oa->name); + } + if (IS_AREA_NSSA(oa)) { + vty_out(vty, " area %s nssa", oa->name); + if (oa->nssa_default_originate.enabled) { + vty_out(vty, " default-information-originate"); + if (oa->nssa_default_originate.metric_value + != -1) + vty_out(vty, " metric %d", + oa->nssa_default_originate + .metric_value); + if (oa->nssa_default_originate.metric_type + != DEFAULT_METRIC_TYPE) + vty_out(vty, " metric-type 1"); + } + if (oa->no_summary) + vty_out(vty, " no-summary"); + vty_out(vty, "\n"); + } + for (range = ospf6_route_head(oa->nssa_range_table); range; + range = ospf6_route_next(range)) { + vty_out(vty, " area %s nssa range %pFX", oa->name, + &range->prefix); + + if (CHECK_FLAG(range->flag, + OSPF6_ROUTE_DO_NOT_ADVERTISE)) { + vty_out(vty, " not-advertise"); + } else { + if (range->path.u.cost_config + != OSPF_AREA_RANGE_COST_UNSPEC) + vty_out(vty, " cost %u", + range->path.u.cost_config); + } + vty_out(vty, "\n"); + } + if (PREFIX_NAME_IN(oa)) + vty_out(vty, " area %s filter-list prefix %s in\n", + oa->name, PREFIX_NAME_IN(oa)); + if (PREFIX_NAME_OUT(oa)) + vty_out(vty, " area %s filter-list prefix %s out\n", + oa->name, PREFIX_NAME_OUT(oa)); + if (IMPORT_NAME(oa)) + vty_out(vty, " area %s import-list %s\n", oa->name, + IMPORT_NAME(oa)); + if (EXPORT_NAME(oa)) + vty_out(vty, " area %s export-list %s\n", oa->name, + EXPORT_NAME(oa)); + } +} + +DEFUN (area_filter_list, + area_filter_list_cmd, + "area <A.B.C.D|(0-4294967295)> filter-list prefix PREFIXLIST_NAME <in|out>", + "OSPF6 area parameters\n" + "OSPF6 area ID in IP address format\n" + "OSPF6 area ID as a decimal value\n" + "Filter networks between OSPF6 areas\n" + "Filter prefixes between OSPF6 areas\n" + "Name of an IPv6 prefix-list\n" + "Filter networks sent to this area\n" + "Filter networks sent from this area\n") +{ + char *inout = argv[argc - 1]->text; + char *areaid = argv[1]->arg; + char *plistname = argv[4]->arg; + + struct ospf6_area *area; + struct prefix_list *plist; + + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + OSPF6_CMD_AREA_GET(areaid, area, ospf6); + + plist = prefix_list_lookup(AFI_IP6, plistname); + if (strmatch(inout, "in")) { + PREFIX_LIST_IN(area) = plist; + XFREE(MTYPE_OSPF6_PLISTNAME, PREFIX_NAME_IN(area)); + PREFIX_NAME_IN(area) = + XSTRDUP(MTYPE_OSPF6_PLISTNAME, plistname); + } else { + PREFIX_LIST_OUT(area) = plist; + XFREE(MTYPE_OSPF6_PLISTNAME, PREFIX_NAME_OUT(area)); + PREFIX_NAME_OUT(area) = + XSTRDUP(MTYPE_OSPF6_PLISTNAME, plistname); + } + + /* Redo summaries if required */ + if (ospf6_check_and_set_router_abr(area->ospf6)) + ospf6_schedule_abr_task(ospf6); + + return CMD_SUCCESS; +} + +DEFUN (no_area_filter_list, + no_area_filter_list_cmd, + "no area <A.B.C.D|(0-4294967295)> filter-list prefix PREFIXLIST_NAME <in|out>", + NO_STR + "OSPF6 area parameters\n" + "OSPF6 area ID in IP address format\n" + "OSPF6 area ID as a decimal value\n" + "Filter networks between OSPF6 areas\n" + "Filter prefixes between OSPF6 areas\n" + "Name of an IPv6 prefix-list\n" + "Filter networks sent to this area\n" + "Filter networks sent from this area\n") +{ + char *inout = argv[argc - 1]->text; + char *areaid = argv[2]->arg; + char *plistname = argv[5]->arg; + + struct ospf6_area *area; + + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + OSPF6_CMD_AREA_GET(areaid, area, ospf6); + + if (strmatch(inout, "in")) { + if (PREFIX_NAME_IN(area)) + if (!strmatch(PREFIX_NAME_IN(area), plistname)) + return CMD_SUCCESS; + + PREFIX_LIST_IN(area) = NULL; + XFREE(MTYPE_OSPF6_PLISTNAME, PREFIX_NAME_IN(area)); + } else { + if (PREFIX_NAME_OUT(area)) + if (!strmatch(PREFIX_NAME_OUT(area), plistname)) + return CMD_SUCCESS; + + XFREE(MTYPE_OSPF6_PLISTNAME, PREFIX_NAME_OUT(area)); + PREFIX_LIST_OUT(area) = NULL; + } + + /* Redo summaries if required */ + if (ospf6_check_and_set_router_abr(area->ospf6)) + ospf6_schedule_abr_task(ospf6); + + return CMD_SUCCESS; +} + +void ospf6_filter_update(struct access_list *access) +{ + struct ospf6_area *oa; + struct listnode *n, *node, *nnode; + struct ospf6 *ospf6; + + for (ALL_LIST_ELEMENTS(om6->ospf6, node, nnode, ospf6)) { + bool update = false; + + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, n, oa)) { + if (IMPORT_NAME(oa) + && strcmp(IMPORT_NAME(oa), access->name) == 0) { + IMPORT_LIST(oa) = access_list_lookup( + AFI_IP6, IMPORT_NAME(oa)); + update = true; + } + + if (EXPORT_NAME(oa) + && strcmp(EXPORT_NAME(oa), access->name) == 0) { + EXPORT_LIST(oa) = access_list_lookup( + AFI_IP6, EXPORT_NAME(oa)); + update = true; + } + } + + if (update && ospf6_check_and_set_router_abr(ospf6)) + ospf6_schedule_abr_task(ospf6); + } +} + +void ospf6_plist_update(struct prefix_list *plist) +{ + struct listnode *node, *nnode; + struct ospf6_area *oa; + struct listnode *n; + const char *name = prefix_list_name(plist); + struct ospf6 *ospf6 = NULL; + + if (prefix_list_afi(plist) != AFI_IP6) + return; + + for (ALL_LIST_ELEMENTS(om6->ospf6, node, nnode, ospf6)) { + bool update = false; + + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, n, oa)) { + if (PREFIX_NAME_IN(oa) + && !strcmp(PREFIX_NAME_IN(oa), name)) { + PREFIX_LIST_IN(oa) = prefix_list_lookup( + AFI_IP6, PREFIX_NAME_IN(oa)); + update = true; + } + if (PREFIX_NAME_OUT(oa) + && !strcmp(PREFIX_NAME_OUT(oa), name)) { + PREFIX_LIST_OUT(oa) = prefix_list_lookup( + AFI_IP6, PREFIX_NAME_OUT(oa)); + update = true; + } + } + + if (update && ospf6_check_and_set_router_abr(ospf6)) + ospf6_schedule_abr_task(ospf6); + } +} + +DEFUN (area_import_list, + area_import_list_cmd, + "area <A.B.C.D|(0-4294967295)> import-list ACCESSLIST6_NAME", + "OSPF6 area parameters\n" + "OSPF6 area ID in IP address format\n" + "OSPF6 area ID as a decimal value\n" + "Set the filter for networks from other areas announced to the specified one\n" + "Name of the access-list\n") +{ + int idx_ipv4 = 1; + int idx_name = 3; + struct ospf6_area *area; + struct access_list *list; + + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + OSPF6_CMD_AREA_GET(argv[idx_ipv4]->arg, area, ospf6); + + list = access_list_lookup(AFI_IP6, argv[idx_name]->arg); + + IMPORT_LIST(area) = list; + + if (IMPORT_NAME(area)) + free(IMPORT_NAME(area)); + + IMPORT_NAME(area) = strdup(argv[idx_name]->arg); + if (ospf6_check_and_set_router_abr(area->ospf6)) + ospf6_schedule_abr_task(ospf6); + + return CMD_SUCCESS; +} + +DEFUN (no_area_import_list, + no_area_import_list_cmd, + "no area <A.B.C.D|(0-4294967295)> import-list ACCESSLIST6_NAME", + NO_STR + "OSPF6 area parameters\n" + "OSPF6 area ID in IP address format\n" + "OSPF6 area ID as a decimal value\n" + "Unset the filter for networks announced to other areas\n" + "Name of the access-list\n") +{ + int idx_ipv4 = 2; + struct ospf6_area *area; + + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + OSPF6_CMD_AREA_GET(argv[idx_ipv4]->arg, area, ospf6); + + IMPORT_LIST(area) = NULL; + + if (IMPORT_NAME(area)) + free(IMPORT_NAME(area)); + + IMPORT_NAME(area) = NULL; + if (ospf6_check_and_set_router_abr(area->ospf6)) + ospf6_schedule_abr_task(ospf6); + + return CMD_SUCCESS; +} + +DEFUN (area_export_list, + area_export_list_cmd, + "area <A.B.C.D|(0-4294967295)> export-list ACCESSLIST6_NAME", + "OSPF6 area parameters\n" + "OSPF6 area ID in IP address format\n" + "OSPF6 area ID as a decimal value\n" + "Set the filter for networks announced to other areas\n" + "Name of the access-list\n") +{ + int idx_ipv4 = 1; + int idx_name = 3; + struct ospf6_area *area; + struct access_list *list; + + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + OSPF6_CMD_AREA_GET(argv[idx_ipv4]->arg, area, ospf6); + + list = access_list_lookup(AFI_IP6, argv[idx_name]->arg); + + EXPORT_LIST(area) = list; + + if (EXPORT_NAME(area)) + free(EXPORT_NAME(area)); + + EXPORT_NAME(area) = strdup(argv[idx_name]->arg); + + /* Redo summaries if required */ + if (ospf6_check_and_set_router_abr(area->ospf6)) + ospf6_schedule_abr_task(ospf6); + + return CMD_SUCCESS; +} + +DEFUN (no_area_export_list, + no_area_export_list_cmd, + "no area <A.B.C.D|(0-4294967295)> export-list ACCESSLIST6_NAME", + NO_STR + "OSPF6 area parameters\n" + "OSPF6 area ID in IP address format\n" + "OSPF6 area ID as a decimal value\n" + "Unset the filter for networks announced to other areas\n" + "Name of the access-list\n") +{ + int idx_ipv4 = 2; + struct ospf6_area *area; + + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + OSPF6_CMD_AREA_GET(argv[idx_ipv4]->arg, area, ospf6); + + EXPORT_LIST(area) = NULL; + + if (EXPORT_NAME(area)) + free(EXPORT_NAME(area)); + + EXPORT_NAME(area) = NULL; + if (ospf6_check_and_set_router_abr(area->ospf6)) + ospf6_schedule_abr_task(ospf6); + + return CMD_SUCCESS; +} + +static int ipv6_ospf6_spf_tree_common(struct vty *vty, struct ospf6 *ospf6, + bool uj) +{ + struct listnode *node; + struct ospf6_area *oa; + struct prefix prefix; + struct ospf6_vertex *root; + struct ospf6_route *route; + json_object *json = NULL; + json_object *json_area = NULL; + json_object *json_head = NULL; + + if (uj) + json = json_object_new_object(); + ospf6_linkstate_prefix(ospf6->router_id, htonl(0), &prefix); + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, oa)) { + if (uj) { + json_area = json_object_new_object(); + json_head = json_object_new_object(); + } + route = ospf6_route_lookup(&prefix, oa->spf_table); + if (route == NULL) { + if (uj) { + json_object_string_add( + json, oa->name, + "LS entry for not not found"); + json_object_free(json_head); + json_object_free(json_area); + } else + vty_out(vty, + "LS entry for root not found in area %s\n", + oa->name); + continue; + } + root = (struct ospf6_vertex *)route->route_option; + ospf6_spf_display_subtree(vty, "", 0, root, json_head, uj); + + if (uj) { + json_object_object_add(json_area, root->name, + json_head); + json_object_object_add(json, oa->name, json_area); + } + } + + if (uj) + vty_json(vty, json); + + return CMD_SUCCESS; +} + +DEFUN(show_ipv6_ospf6_spf_tree, show_ipv6_ospf6_spf_tree_cmd, + "show ipv6 ospf6 [vrf <NAME|all>] spf tree [json]", + SHOW_STR IP6_STR OSPF6_STR VRF_CMD_HELP_STR + "All VRFs\n" + "Shortest Path First calculation\n" + "Show SPF tree\n" JSON_STR) +{ + struct listnode *node; + struct ospf6 *ospf6; + const char *vrf_name = NULL; + bool all_vrf = false; + int idx_vrf = 0; + bool uj = use_json(argc, argv); + + OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { + if (all_vrf || strcmp(ospf6->name, vrf_name) == 0) { + ipv6_ospf6_spf_tree_common(vty, ospf6, uj); + if (!all_vrf) + break; + } + } + + OSPF6_CMD_CHECK_VRF(uj, all_vrf, ospf6); + + return CMD_SUCCESS; +} + +static int show_ospf6_area_spf_tree_common(struct vty *vty, + struct cmd_token **argv, + struct ospf6 *ospf6, + uint32_t area_id, int idx_ipv4) +{ + + struct ospf6_area *oa; + struct prefix prefix; + struct ospf6_vertex *root; + struct ospf6_route *route; + + ospf6_linkstate_prefix(ospf6->router_id, htonl(0), &prefix); + + oa = ospf6_area_lookup(area_id, ospf6); + if (oa == NULL) { + vty_out(vty, "No such Area: %s\n", argv[idx_ipv4]->arg); + return CMD_SUCCESS; + } + + route = ospf6_route_lookup(&prefix, oa->spf_table); + if (route == NULL) { + vty_out(vty, "LS entry for root not found in area %s\n", + oa->name); + return CMD_SUCCESS; + } + root = (struct ospf6_vertex *)route->route_option; + ospf6_spf_display_subtree(vty, "", 0, root, NULL, false); + + return CMD_SUCCESS; +} + +DEFUN(show_ipv6_ospf6_area_spf_tree, show_ipv6_ospf6_area_spf_tree_cmd, + "show ipv6 ospf6 [vrf <NAME|all>] area A.B.C.D spf tree", + SHOW_STR IP6_STR OSPF6_STR VRF_CMD_HELP_STR + "All VRFs\n" OSPF6_AREA_STR OSPF6_AREA_ID_STR + "Shortest Path First calculation\n" + "Show SPF tree\n") +{ + int idx_ipv4 = 4; + uint32_t area_id; + struct ospf6 *ospf6; + struct listnode *node; + const char *vrf_name = NULL; + bool all_vrf = false; + int idx_vrf = 0; + + OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + if (idx_vrf > 0) + idx_ipv4 += 2; + + if (inet_pton(AF_INET, argv[idx_ipv4]->arg, &area_id) != 1) { + vty_out(vty, "Malformed Area-ID: %s\n", argv[idx_ipv4]->arg); + return CMD_SUCCESS; + } + + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { + if (all_vrf || strcmp(ospf6->name, vrf_name) == 0) { + show_ospf6_area_spf_tree_common(vty, argv, ospf6, + area_id, idx_ipv4); + if (!all_vrf) + break; + } + } + + OSPF6_CMD_CHECK_VRF(false, all_vrf, ospf6); + + return CMD_SUCCESS; +} + +static int +show_ospf6_simulate_spf_tree_commen(struct vty *vty, struct cmd_token **argv, + struct ospf6 *ospf6, uint32_t router_id, + uint32_t area_id, struct prefix prefix, + int idx_ipv4, int idx_ipv4_2) +{ + struct ospf6_area *oa; + struct ospf6_vertex *root; + struct ospf6_route *route; + struct ospf6_route_table *spf_table; + unsigned char tmp_debug_ospf6_spf = 0; + + oa = ospf6_area_lookup(area_id, ospf6); + if (oa == NULL) { + vty_out(vty, "No such Area: %s\n", argv[idx_ipv4_2]->arg); + return CMD_SUCCESS; + } + + tmp_debug_ospf6_spf = conf_debug_ospf6_spf; + conf_debug_ospf6_spf = 0; + + spf_table = OSPF6_ROUTE_TABLE_CREATE(NONE, SPF_RESULTS); + ospf6_spf_calculation(router_id, spf_table, oa); + + conf_debug_ospf6_spf = tmp_debug_ospf6_spf; + + route = ospf6_route_lookup(&prefix, spf_table); + if (route == NULL) { + ospf6_spf_table_finish(spf_table); + ospf6_route_table_delete(spf_table); + return CMD_SUCCESS; + } + root = (struct ospf6_vertex *)route->route_option; + ospf6_spf_display_subtree(vty, "", 0, root, NULL, false); + + ospf6_spf_table_finish(spf_table); + ospf6_route_table_delete(spf_table); + + return CMD_SUCCESS; +} + +DEFUN(show_ipv6_ospf6_simulate_spf_tree_root, + show_ipv6_ospf6_simulate_spf_tree_root_cmd, + "show ipv6 ospf6 [vrf <NAME|all>] simulate spf-tree A.B.C.D area A.B.C.D", + SHOW_STR IP6_STR OSPF6_STR VRF_CMD_HELP_STR + "All VRFs\n" + "Shortest Path First calculation\n" + "Show SPF tree\n" + "Specify root's router-id to calculate another router's SPF tree\n" + "OSPF6 area parameters\n" OSPF6_AREA_ID_STR) +{ + int idx_ipv4 = 5; + int idx_ipv4_2 = 7; + uint32_t area_id; + struct prefix prefix; + uint32_t router_id; + struct ospf6 *ospf6; + struct listnode *node; + const char *vrf_name = NULL; + bool all_vrf = false; + int idx_vrf = 0; + + OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + if (idx_vrf > 0) { + idx_ipv4 += 2; + idx_ipv4_2 += 2; + } + inet_pton(AF_INET, argv[idx_ipv4]->arg, &router_id); + ospf6_linkstate_prefix(router_id, htonl(0), &prefix); + + if (inet_pton(AF_INET, argv[idx_ipv4_2]->arg, &area_id) != 1) { + vty_out(vty, "Malformed Area-ID: %s\n", argv[idx_ipv4_2]->arg); + return CMD_SUCCESS; + } + + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { + if (all_vrf || strcmp(ospf6->name, vrf_name) == 0) { + show_ospf6_simulate_spf_tree_commen( + vty, argv, ospf6, router_id, area_id, prefix, + idx_ipv4, idx_ipv4_2); + if (!all_vrf) + break; + } + } + + OSPF6_CMD_CHECK_VRF(false, all_vrf, ospf6); + + return CMD_SUCCESS; +} + +DEFUN (ospf6_area_stub, + ospf6_area_stub_cmd, + "area <A.B.C.D|(0-4294967295)> stub", + "OSPF6 area parameters\n" + "OSPF6 area ID in IP address format\n" + "OSPF6 area ID as a decimal value\n" + "Configure OSPF6 area as stub\n") +{ + int idx_ipv4_number = 1; + struct ospf6_area *area; + + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + OSPF6_CMD_AREA_GET(argv[idx_ipv4_number]->arg, area, ospf6); + + if (!ospf6_area_stub_set(ospf6, area)) { + vty_out(vty, + "First deconfigure all virtual link through this area\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + ospf6_area_no_summary_unset(ospf6, area); + + return CMD_SUCCESS; +} + +DEFUN (ospf6_area_stub_no_summary, + ospf6_area_stub_no_summary_cmd, + "area <A.B.C.D|(0-4294967295)> stub no-summary", + "OSPF6 stub parameters\n" + "OSPF6 area ID in IP address format\n" + "OSPF6 area ID as a decimal value\n" + "Configure OSPF6 area as stub\n" + "Do not inject inter-area routes into stub\n") +{ + int idx_ipv4_number = 1; + struct ospf6_area *area; + + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + OSPF6_CMD_AREA_GET(argv[idx_ipv4_number]->arg, area, ospf6); + + if (!ospf6_area_stub_set(ospf6, area)) { + vty_out(vty, + "First deconfigure all virtual link through this area\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + ospf6_area_no_summary_set(ospf6, area); + + return CMD_SUCCESS; +} + +DEFUN (no_ospf6_area_stub, + no_ospf6_area_stub_cmd, + "no area <A.B.C.D|(0-4294967295)> stub", + NO_STR + "OSPF6 area parameters\n" + "OSPF6 area ID in IP address format\n" + "OSPF6 area ID as a decimal value\n" + "Configure OSPF6 area as stub\n") +{ + int idx_ipv4_number = 2; + struct ospf6_area *area; + + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + OSPF6_CMD_AREA_GET(argv[idx_ipv4_number]->arg, area, ospf6); + + ospf6_area_stub_unset(ospf6, area); + ospf6_area_no_summary_unset(ospf6, area); + + return CMD_SUCCESS; +} + +DEFUN (no_ospf6_area_stub_no_summary, + no_ospf6_area_stub_no_summary_cmd, + "no area <A.B.C.D|(0-4294967295)> stub no-summary", + NO_STR + "OSPF6 area parameters\n" + "OSPF6 area ID in IP address format\n" + "OSPF6 area ID as a decimal value\n" + "Configure OSPF6 area as stub\n" + "Do not inject inter-area routes into area\n") +{ + int idx_ipv4_number = 2; + struct ospf6_area *area; + + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + OSPF6_CMD_AREA_GET(argv[idx_ipv4_number]->arg, area, ospf6); + + ospf6_area_stub_unset(ospf6, area); + ospf6_area_no_summary_unset(ospf6, area); + + return CMD_SUCCESS; +} + +DEFPY(ospf6_area_nssa, ospf6_area_nssa_cmd, + "area <A.B.C.D|(0-4294967295)>$area_str nssa\ + [{\ + default-information-originate$dflt_originate [{metric (0-16777214)$mval|metric-type (1-2)$mtype}]\ + |no-summary$no_summary\ + }]", + "OSPF6 area parameters\n" + "OSPF6 area ID in IP address format\n" + "OSPF6 area ID as a decimal value\n" + "Configure OSPF6 area as nssa\n" + "Originate Type 7 default into NSSA area\n" + "OSPFv3 default metric\n" + "OSPFv3 metric\n" + "OSPFv3 metric type for default routes\n" + "Set OSPFv3 External Type 1/2 metrics\n" + "Do not inject inter-area routes into area\n") +{ + struct ospf6_area *area; + + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + OSPF6_CMD_AREA_GET(area_str, area, ospf6); + + if (!ospf6_area_nssa_set(ospf6, area)) { + vty_out(vty, + "First deconfigure all virtual link through this area\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (dflt_originate) { + if (mval_str == NULL) + mval = -1; + if (mtype_str == NULL) + mtype = DEFAULT_METRIC_TYPE; + ospf6_nssa_default_originate_set(ospf6, area, mval, mtype); + } else + ospf6_nssa_default_originate_unset(ospf6, area); + + if (no_summary) + ospf6_area_no_summary_set(ospf6, area); + else + ospf6_area_no_summary_unset(ospf6, area); + + if (ospf6_check_and_set_router_abr(ospf6)) { + ospf6_abr_defaults_to_stub(ospf6); + ospf6_abr_nssa_type_7_defaults(ospf6); + } + + return CMD_SUCCESS; +} + +DEFPY(no_ospf6_area_nssa, no_ospf6_area_nssa_cmd, + "no area <A.B.C.D|(0-4294967295)>$area_str nssa\ + [{\ + default-information-originate [{metric (0-16777214)|metric-type (1-2)}]\ + |no-summary\ + }]", + NO_STR + "OSPF6 area parameters\n" + "OSPF6 area ID in IP address format\n" + "OSPF6 area ID as a decimal value\n" + "Configure OSPF6 area as nssa\n" + "Originate Type 7 default into NSSA area\n" + "OSPFv3 default metric\n" + "OSPFv3 metric\n" + "OSPFv3 metric type for default routes\n" + "Set OSPFv3 External Type 1/2 metrics\n" + "Do not inject inter-area routes into area\n") +{ + struct ospf6_area *area; + + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + OSPF6_CMD_AREA_GET(area_str, area, ospf6); + + ospf6_area_nssa_unset(ospf6, area); + ospf6_area_no_summary_unset(ospf6, area); + ospf6_nssa_default_originate_unset(ospf6, area); + + return CMD_SUCCESS; +} + + +void ospf6_area_init(void) +{ + install_element(VIEW_NODE, &show_ipv6_ospf6_spf_tree_cmd); + install_element(VIEW_NODE, &show_ipv6_ospf6_area_spf_tree_cmd); + install_element(VIEW_NODE, &show_ipv6_ospf6_simulate_spf_tree_root_cmd); + + install_element(OSPF6_NODE, &area_range_cmd); + install_element(OSPF6_NODE, &no_area_range_cmd); + install_element(OSPF6_NODE, &ospf6_area_stub_no_summary_cmd); + install_element(OSPF6_NODE, &ospf6_area_stub_cmd); + install_element(OSPF6_NODE, &no_ospf6_area_stub_no_summary_cmd); + install_element(OSPF6_NODE, &no_ospf6_area_stub_cmd); + + + install_element(OSPF6_NODE, &area_import_list_cmd); + install_element(OSPF6_NODE, &no_area_import_list_cmd); + install_element(OSPF6_NODE, &area_export_list_cmd); + install_element(OSPF6_NODE, &no_area_export_list_cmd); + + install_element(OSPF6_NODE, &area_filter_list_cmd); + install_element(OSPF6_NODE, &no_area_filter_list_cmd); + + /* "area nssa" commands. */ + install_element(OSPF6_NODE, &ospf6_area_nssa_cmd); + install_element(OSPF6_NODE, &no_ospf6_area_nssa_cmd); +} + +void ospf6_area_interface_delete(struct ospf6_interface *oi) +{ + struct ospf6_area *oa; + struct listnode *node, *nnode; + struct ospf6 *ospf6; + + for (ALL_LIST_ELEMENTS(om6->ospf6, node, nnode, ospf6)) { + for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, oa)) + if (listnode_lookup(oa->if_list, oi)) + listnode_delete(oa->if_list, oi); + } +} diff --git a/ospf6d/ospf6_area.h b/ospf6d/ospf6_area.h new file mode 100644 index 0000000..d9afd65 --- /dev/null +++ b/ospf6d/ospf6_area.h @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2003 Yasuhiro Ohara + */ + +#ifndef OSPF_AREA_H +#define OSPF_AREA_H + +#include "ospf6_top.h" +#include "lib/json.h" + +struct ospf6_area { + /* Reference to Top data structure */ + struct ospf6 *ospf6; + + /* Area-ID */ + in_addr_t area_id; + +#define OSPF6_AREA_FMT_UNSET 0 +#define OSPF6_AREA_FMT_DOTTEDQUAD 1 +#define OSPF6_AREA_FMT_DECIMAL 2 + /* Area-ID string */ + char name[16]; + + /* flag */ + uint8_t flag; + + /* OSPF Option */ + uint8_t options[3]; + + /* Summary routes to be originated (includes Configured Address Ranges) + */ + struct ospf6_route_table *range_table; + struct ospf6_route_table *nssa_range_table; + struct ospf6_route_table *summary_prefix; + struct ospf6_route_table *summary_router; + + /* Area type */ + int no_summary; + + /* NSSA default-information-originate */ + struct { + bool enabled; + int metric_type; + int metric_value; + } nssa_default_originate; + + /* Brouter traversal protection */ + bool intra_brouter_calc; + + /* OSPF interface list */ + struct list *if_list; + + struct ospf6_lsdb *lsdb; + struct ospf6_lsdb *lsdb_self; + struct ospf6_lsdb *temp_router_lsa_lsdb; + + struct ospf6_route_table *spf_table; + struct ospf6_route_table *route_table; + + uint32_t spf_calculation; /* SPF calculation count */ + + struct event *thread_router_lsa; + struct event *thread_intra_prefix_lsa; + uint32_t router_lsa_size_limit; + + /* Area announce list */ + struct { + char *name; + struct access_list *list; + } _export; +#define EXPORT_NAME(A) (A)->_export.name +#define EXPORT_LIST(A) (A)->_export.list + + /* Area acceptance list */ + struct { + char *name; + struct access_list *list; + } import; +#define IMPORT_NAME(A) (A)->import.name +#define IMPORT_LIST(A) (A)->import.list + + /* Type 3 LSA Area prefix-list */ + struct { + char *name; + struct prefix_list *list; + } plist_in; +#define PREFIX_NAME_IN(A) (A)->plist_in.name +#define PREFIX_LIST_IN(A) (A)->plist_in.list + + struct { + char *name; + struct prefix_list *list; + } plist_out; +#define PREFIX_NAME_OUT(A) (A)->plist_out.name +#define PREFIX_LIST_OUT(A) (A)->plist_out.list + + /* Time stamps. */ + struct timeval ts_spf; /* SPF calculation time stamp. */ + + uint32_t full_nbrs; /* Fully adjacent neighbors. */ + uint8_t intra_prefix_originate; /* Force intra_prefix lsa originate */ + uint8_t NSSATranslatorRole; /* NSSA configured role */ +#define OSPF6_NSSA_ROLE_NEVER 0 +#define OSPF6_NSSA_ROLE_CANDIDATE 1 +#define OSPF6_NSSA_ROLE_ALWAYS 2 + uint8_t NSSATranslatorState; /* NSSA operational role */ +#define OSPF6_NSSA_TRANSLATE_DISABLED 0 +#define OSPF6_NSSA_TRANSLATE_ENABLED 1 +}; + +#define OSPF6_AREA_ENABLE 0x01 +#define OSPF6_AREA_ACTIVE 0x02 +#define OSPF6_AREA_TRANSIT 0x04 /* TransitCapability */ +#define OSPF6_AREA_STUB 0x08 +#define OSPF6_AREA_NSSA 0x10 + +#define IS_AREA_ENABLED(oa) (CHECK_FLAG ((oa)->flag, OSPF6_AREA_ENABLE)) +#define IS_AREA_ACTIVE(oa) (CHECK_FLAG ((oa)->flag, OSPF6_AREA_ACTIVE)) +#define IS_AREA_TRANSIT(oa) (CHECK_FLAG ((oa)->flag, OSPF6_AREA_TRANSIT)) +#define IS_AREA_STUB(oa) (CHECK_FLAG ((oa)->flag, OSPF6_AREA_STUB)) +#define IS_AREA_NSSA(oa) (CHECK_FLAG((oa)->flag, OSPF6_AREA_NSSA)) + +#define OSPF6_CMD_AREA_GET(str, oa, ospf6) \ + { \ + uint32_t area_id; \ + int format, ret; \ + ret = str2area_id(str, &area_id, &format); \ + if (ret) { \ + vty_out(vty, "Malformed Area-ID: %s\n", str); \ + return CMD_WARNING; \ + } \ + oa = ospf6_area_lookup(area_id, ospf6); \ + if (oa == NULL) \ + oa = ospf6_area_create(area_id, ospf6, format); \ + } + +/* prototypes */ +extern int str2area_id(const char *str, uint32_t *area_id, int *area_id_fmt); +extern void area_id2str(char *buf, int len, uint32_t area_id, int area_id_fmt); + +extern int ospf6_area_cmp(void *va, void *vb); + +extern struct ospf6_area *ospf6_area_create(uint32_t area_id, + struct ospf6 *ospf6, int df); +extern void ospf6_area_delete(struct ospf6_area *oa); +extern struct ospf6_area *ospf6_area_lookup(uint32_t area_id, + struct ospf6 *ospf6); +extern struct ospf6_area *ospf6_area_lookup_by_area_id(uint32_t area_id); + +extern void ospf6_area_stub_unset(struct ospf6 *ospf6, struct ospf6_area *area); +extern void ospf6_area_enable(struct ospf6_area *oa); +extern void ospf6_area_disable(struct ospf6_area *oa); + +extern void ospf6_area_show(struct vty *vty, struct ospf6_area *oa, + json_object *json_areas, bool use_json); + +extern void ospf6_plist_update(struct prefix_list *plist); +extern void ospf6_filter_update(struct access_list *access); +extern void ospf6_area_config_write(struct vty *vty, struct ospf6 *ospf6); +extern void ospf6_area_init(void); +struct ospf6_interface; +extern void ospf6_area_interface_delete(struct ospf6_interface *oi); + +#endif /* OSPF_AREA_H */ diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c new file mode 100644 index 0000000..99c3e16 --- /dev/null +++ b/ospf6d/ospf6_asbr.c @@ -0,0 +1,3723 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2003 Yasuhiro Ohara + */ + +#include <zebra.h> + +#include "log.h" +#include "memory.h" +#include "prefix.h" +#include "command.h" +#include "vty.h" +#include "routemap.h" +#include "table.h" +#include "plist.h" +#include "frrevent.h" +#include "linklist.h" +#include "lib/northbound_cli.h" + +#include "ospf6_proto.h" +#include "ospf6_lsa.h" +#include "ospf6_lsdb.h" +#include "ospf6_route.h" +#include "ospf6_zebra.h" +#include "ospf6_message.h" +#include "ospf6_spf.h" + +#include "ospf6_top.h" +#include "ospf6d.h" +#include "ospf6_area.h" +#include "ospf6_interface.h" +#include "ospf6_neighbor.h" +#include "ospf6_asbr.h" +#include "ospf6_abr.h" +#include "ospf6_intra.h" +#include "ospf6_flood.h" +#include "ospf6_nssa.h" +#include "ospf6d.h" +#include "ospf6_spf.h" +#include "ospf6_nssa.h" +#include "ospf6_gr.h" +#include "lib/json.h" + +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_EXTERNAL_INFO, "OSPF6 ext. info"); +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_DIST_ARGS, "OSPF6 Distribute arguments"); +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_REDISTRIBUTE, "OSPF6 Redistribute arguments"); +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_EXTERNAL_RT_AGGR, "OSPF6 ASBR Summarisation"); + +static void ospf6_asbr_redistribute_set(struct ospf6 *ospf6, int type); +static void ospf6_asbr_redistribute_unset(struct ospf6 *ospf6, + struct ospf6_redist *red, int type); + +#include "ospf6d/ospf6_asbr_clippy.c" + +unsigned char conf_debug_ospf6_asbr = 0; + +#define ZROUTE_NAME(x) zebra_route_string(x) + +/* Originate Type-5 and Type-7 LSA */ +static struct ospf6_lsa *ospf6_originate_type5_type7_lsas( + struct ospf6_route *route, + struct ospf6 *ospf6) +{ + struct ospf6_lsa *lsa; + struct listnode *lnode; + struct ospf6_area *oa = NULL; + + lsa = ospf6_as_external_lsa_originate(route, ospf6); + + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, lnode, oa)) { + if (IS_AREA_NSSA(oa)) + ospf6_nssa_lsa_originate(route, oa, true); + } + + return lsa; +} + +/* AS External LSA origination */ +struct ospf6_lsa *ospf6_as_external_lsa_originate(struct ospf6_route *route, + struct ospf6 *ospf6) +{ + char buffer[OSPF6_MAX_LSASIZE]; + struct ospf6_lsa_header *lsa_header; + struct ospf6_lsa *lsa; + struct ospf6_external_info *info = route->route_option; + + struct ospf6_as_external_lsa *as_external_lsa; + caddr_t p; + + if (ospf6->gr_info.restart_in_progress) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "Graceful Restart in progress, don't originate LSA"); + return NULL; + } + + if (IS_OSPF6_DEBUG_ASBR || IS_OSPF6_DEBUG_ORIGINATE(AS_EXTERNAL)) + zlog_debug("Originate AS-External-LSA for %pFX", + &route->prefix); + + /* prepare buffer */ + memset(buffer, 0, sizeof(buffer)); + lsa_header = (struct ospf6_lsa_header *)buffer; + as_external_lsa = (struct ospf6_as_external_lsa + *)((caddr_t)lsa_header + + sizeof(struct ospf6_lsa_header)); + p = (caddr_t)((caddr_t)as_external_lsa + + sizeof(struct ospf6_as_external_lsa)); + + /* Fill AS-External-LSA */ + /* Metric type */ + if (route->path.metric_type == 2) + SET_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_E); + else + UNSET_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_E); + + /* forwarding address */ + if (!IN6_IS_ADDR_UNSPECIFIED(&info->forwarding)) + SET_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_F); + else + UNSET_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_F); + + /* external route tag */ + if (info->tag) + SET_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_T); + else + UNSET_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_T); + + /* Set metric */ + OSPF6_ASBR_METRIC_SET(as_external_lsa, route->path.cost); + + /* prefixlen */ + as_external_lsa->prefix.prefix_length = route->prefix.prefixlen; + + /* PrefixOptions */ + as_external_lsa->prefix.prefix_options = route->prefix_options; + + /* don't use refer LS-type */ + as_external_lsa->prefix.prefix_refer_lstype = htons(0); + + /* set Prefix */ + memcpy(p, &route->prefix.u.prefix6, + OSPF6_PREFIX_SPACE(route->prefix.prefixlen)); + ospf6_prefix_apply_mask(&as_external_lsa->prefix); + p += OSPF6_PREFIX_SPACE(route->prefix.prefixlen); + + /* Forwarding address */ + if (CHECK_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_F)) { + memcpy(p, &info->forwarding, sizeof(struct in6_addr)); + p += sizeof(struct in6_addr); + } + + /* External Route Tag */ + if (CHECK_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_T)) { + route_tag_t network_order = htonl(info->tag); + + memcpy(p, &network_order, sizeof(network_order)); + p += sizeof(network_order); + } + + /* Fill LSA Header */ + lsa_header->age = 0; + lsa_header->type = htons(OSPF6_LSTYPE_AS_EXTERNAL); + lsa_header->id = route->path.origin.id; + lsa_header->adv_router = ospf6->router_id; + lsa_header->seqnum = + ospf6_new_ls_seqnum(lsa_header->type, lsa_header->id, + lsa_header->adv_router, ospf6->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); + + /* Originate */ + ospf6_lsa_originate_process(lsa, ospf6); + + return lsa; +} + +void ospf6_orig_as_external_lsa(struct event *thread) +{ + struct ospf6_interface *oi; + struct ospf6_lsa *lsa; + uint32_t type, adv_router; + + oi = (struct ospf6_interface *)EVENT_ARG(thread); + + if (oi->state == OSPF6_INTERFACE_DOWN) + return; + if (IS_AREA_NSSA(oi->area) || IS_AREA_STUB(oi->area)) + return; + + type = htons(OSPF6_LSTYPE_AS_EXTERNAL); + adv_router = oi->area->ospf6->router_id; + for (ALL_LSDB_TYPED_ADVRTR(oi->area->ospf6->lsdb, type, adv_router, + lsa)) { + if (IS_OSPF6_DEBUG_ASBR) + zlog_debug( + "%s: Send update of AS-External LSA %s seq 0x%x", + __func__, lsa->name, + ntohl(lsa->header->seqnum)); + + ospf6_flood_interface(NULL, lsa, oi); + } +} + +static route_tag_t ospf6_as_external_lsa_get_tag(struct ospf6_lsa *lsa) +{ + struct ospf6_as_external_lsa *external; + ptrdiff_t tag_offset; + route_tag_t network_order; + + if (!lsa) + return 0; + + external = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END( + lsa->header); + + if (!CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_T)) + return 0; + + tag_offset = sizeof(*external) + + OSPF6_PREFIX_SPACE(external->prefix.prefix_length); + if (CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_F)) + tag_offset += sizeof(struct in6_addr); + + memcpy(&network_order, (caddr_t)external + tag_offset, + sizeof(network_order)); + return ntohl(network_order); +} + +void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old, + struct ospf6_route *route, + struct ospf6 *ospf6) +{ + struct ospf6_route *old_route, *next_route; + struct ospf6_path *ecmp_path, *o_path = NULL; + struct listnode *anode, *anext; + struct listnode *nnode, *rnode, *rnext; + struct ospf6_nexthop *nh, *rnh; + bool route_found = false; + + /* check for old entry match with new route origin, + * delete old entry. + */ + for (old_route = old; old_route; old_route = next_route) { + bool route_updated = false; + + next_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->path.type != route->path.type) + continue; + + /* Current and New route has same origin, + * delete old entry. + */ + for (ALL_LIST_ELEMENTS(old_route->paths, anode, anext, + o_path)) { + /* Check old route path and route has same + * origin. + */ + if (o_path->area_id != route->path.area_id + || !ospf6_ls_origin_same(o_path, &route->path)) + continue; + + /* Cost is not same then delete current path */ + if ((o_path->cost == route->path.cost) + && (o_path->u.cost_e2 == route->path.u.cost_e2)) + continue; + + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) { + zlog_debug( + "%s: route %pFX cost old %u new %u is not same, replace route", + __func__, &old_route->prefix, o_path->cost, + route->path.cost); + } + + /* Remove selected current rout path's nh from + * effective nh list. + */ + 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); + route_updated = true; + + /* Current route's path (adv_router info) is similar + * to route being added. + * Replace current route's path with paths list head. + * Update FIB with effective NHs. + */ + if (listcount(old_route->paths)) { + for (ALL_LIST_ELEMENTS(old_route->paths, + anode, anext, o_path)) { + ospf6_merge_nexthops( + old_route->nh_list, + o_path->nh_list); + } + /* Update RIB/FIB with effective + * nh_list + */ + if (ospf6->route_table->hook_add) + (*ospf6->route_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; + } + } else { + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) { + zlog_debug( + "%s: route %pFX old cost %u new cost %u, delete old entry.", + __func__, &old_route->prefix, + old_route->path.cost, + route->path.cost); + } + if (old == old_route) + old = next_route; + ospf6_route_remove(old_route, + ospf6->route_table); + } + } + if (route_updated) + break; + } + + /* Add new route */ + 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->path.type != route->path.type) + continue; + + /* Old Route and New Route have Equal Cost, Merge NHs */ + if ((old_route->path.cost == route->path.cost) + && (old_route->path.u.cost_e2 == route->path.u.cost_e2)) { + + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) { + zlog_debug( + "%s: old route %pFX path cost %u e2 %u", + __func__, &old_route->prefix, + old_route->path.cost, + old_route->path.u.cost_e2); + } + route_found = true; + /* check if this path exists already in + * route->paths list, if so, replace nh_list + * from asbr_entry. + */ + 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; + } + /* If path is not found in old_route paths's list, + * add a new path to route paths list and merge + * nexthops in route->path->nh_list. + * Otherwise replace existing path's nh_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_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) { + zlog_debug( + "%s: route %pFX another path added with nh %u, effective paths %u nh %u", + __func__, &route->prefix, + listcount(ecmp_path->nh_list), + old_route->paths ? listcount( + old_route->paths) + : 0, + listcount(old_route->nh_list)); + } + } else { + list_delete_all_node(o_path->nh_list); + ospf6_copy_nexthops(o_path->nh_list, + route->nh_list); + } + + /* Reset nexthop lists, rebuild from brouter table + * for each adv. router. + */ + list_delete_all_node(old_route->nh_list); + + for (ALL_LIST_ELEMENTS_RO(old_route->paths, anode, + o_path)) { + struct ospf6_route *asbr_entry; + + asbr_entry = ospf6_route_lookup( + &o_path->ls_prefix, + ospf6->brouter_table); + if (asbr_entry == NULL) { + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) + zlog_debug( + "%s: ls_prfix %pFX asbr_entry not found.", + __func__, + &old_route->prefix); + continue; + } + ospf6_route_merge_nexthops(old_route, + asbr_entry); + } + + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) + zlog_debug( + "%s: route %pFX with effective paths %u nh %u", + __func__, &route->prefix, + old_route->paths + ? listcount(old_route->paths) + : 0, + old_route->nh_list + ? listcount(old_route->nh_list) + : 0); + + /* Update RIB/FIB */ + if (ospf6->route_table->hook_add) + (*ospf6->route_table->hook_add)(old_route); + + /* Delete the new route its info added to existing + * route. + */ + ospf6_route_delete(route); + + break; + } + } + + if (!route_found) { + /* Add new route to existing node in ospf6 route table. */ + ospf6_route_add(route, ospf6->route_table); + } +} + +/* Check if the forwarding address is local address */ +static int ospf6_ase_forward_address_check(struct ospf6 *ospf6, + struct in6_addr *fwd_addr) +{ + struct listnode *anode, *node, *cnode; + struct ospf6_interface *oi; + struct ospf6_area *oa; + struct interface *ifp; + struct connected *c; + + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, anode, oa)) { + for (ALL_LIST_ELEMENTS_RO(oa->if_list, node, oi)) { + if (!if_is_operative(oi->interface) + || oi->type == OSPF_IFTYPE_VIRTUALLINK) + continue; + + ifp = oi->interface; + for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, c)) { + if (IPV6_ADDR_SAME(&c->address->u.prefix6, + fwd_addr)) + return 0; + } + } + } + + return 1; +} + +void ospf6_asbr_lsa_add(struct ospf6_lsa *lsa) +{ + struct ospf6_as_external_lsa *external; + struct prefix asbr_id; + struct ospf6_route *asbr_entry, *route, *old = NULL; + struct ospf6_path *path; + struct ospf6 *ospf6; + int type; + struct ospf6_area *oa = NULL; + struct prefix fwd_addr; + ptrdiff_t offset; + + type = ntohs(lsa->header->type); + oa = lsa->lsdb->data; + + external = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END( + lsa->header); + + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) + zlog_debug("Calculate AS-External route for %s", lsa->name); + + ospf6 = ospf6_get_by_lsdb(lsa); + + if (lsa->header->adv_router == ospf6->router_id) { + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) + zlog_debug("Ignore self-originated AS-External-LSA"); + return; + } + + if (OSPF6_ASBR_METRIC(external) == OSPF_LS_INFINITY) { + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) + zlog_debug("Ignore LSA with LSInfinity Metric"); + return; + } + + if (CHECK_FLAG(external->prefix.prefix_options, + OSPF6_PREFIX_OPTION_NU)) { + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) + zlog_debug("Ignore LSA with NU bit set Metric"); + return; + } + + ospf6_linkstate_prefix(lsa->header->adv_router, htonl(0), &asbr_id); + asbr_entry = ospf6_route_lookup(&asbr_id, ospf6->brouter_table); + if (asbr_entry == NULL) { + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) + zlog_debug("ASBR entry not found: %pFX", &asbr_id); + return; + } else { + /* The router advertising external LSA can be ASBR or ABR */ + if (!CHECK_FLAG(asbr_entry->path.router_bits, + OSPF6_ROUTER_BIT_E)) { + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) + zlog_debug( + "External bit reset ASBR route entry : %pFX", + &asbr_id); + return; + } + + /* + * RFC 3101 - Section 2.5: + * "For a Type-7 LSA the matching routing table entry must + * specify an intra-area path through the LSA's originating + * NSSA". + */ + if (ntohs(lsa->header->type) == OSPF6_LSTYPE_TYPE_7 + && (asbr_entry->path.area_id != oa->area_id + || asbr_entry->path.type != OSPF6_PATH_TYPE_INTRA)) { + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) + zlog_debug( + "Intra-area route to NSSA ASBR not found: %pFX", + &asbr_id); + return; + } + } + + /* + * RFC 3101 - Section 2.5: + * "If the destination is a Type-7 default route (destination ID = + * DefaultDestination) and one of the following is true, then do + * nothing with this LSA and consider the next in the list: + * + * o The calculating router is a border router and the LSA has + * its P-bit clear. Appendix E describes a technique + * whereby an NSSA border router installs a Type-7 default + * LSA without propagating it. + * + * o The calculating router is a border router and is + * suppressing the import of summary routes as Type-3 + * summary-LSAs". + */ + if (ntohs(lsa->header->type) == OSPF6_LSTYPE_TYPE_7 + && external->prefix.prefix_length == 0 + && CHECK_FLAG(ospf6->flag, OSPF6_FLAG_ABR) + && (CHECK_FLAG(external->prefix.prefix_options, + OSPF6_PREFIX_OPTION_P) + || oa->no_summary)) { + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) + zlog_debug("Skipping Type-7 default route"); + return; + } + + /* Check the forwarding address */ + if (CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_F)) { + offset = sizeof(*external) + + OSPF6_PREFIX_SPACE(external->prefix.prefix_length); + memset(&fwd_addr, 0, sizeof(fwd_addr)); + fwd_addr.family = AF_INET6; + fwd_addr.prefixlen = IPV6_MAX_BITLEN; + memcpy(&fwd_addr.u.prefix6, (caddr_t)external + offset, + sizeof(struct in6_addr)); + + if (!IN6_IS_ADDR_UNSPECIFIED(&fwd_addr.u.prefix6)) { + if (!ospf6_ase_forward_address_check( + ospf6, &fwd_addr.u.prefix6)) { + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) + zlog_debug( + "Fwd address %pFX is local address", + &fwd_addr); + return; + } + + /* Find the forwarding entry */ + asbr_entry = ospf6_route_lookup_bestmatch( + &fwd_addr, ospf6->route_table); + if (asbr_entry == NULL) { + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) + zlog_debug( + "Fwd address not found: %pFX", + &fwd_addr); + return; + } + } + } + + route = ospf6_route_create(ospf6); + route->type = OSPF6_DEST_TYPE_NETWORK; + route->prefix.family = AF_INET6; + route->prefix.prefixlen = external->prefix.prefix_length; + ospf6_prefix_in6_addr(&route->prefix.u.prefix6, external, + &external->prefix); + route->prefix_options = external->prefix.prefix_options; + + route->path.area_id = asbr_entry->path.area_id; + route->path.origin.type = lsa->header->type; + route->path.origin.id = lsa->header->id; + route->path.origin.adv_router = lsa->header->adv_router; + memcpy(&route->path.ls_prefix, &asbr_id, sizeof(struct prefix)); + + if (CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_E)) { + route->path.type = OSPF6_PATH_TYPE_EXTERNAL2; + route->path.metric_type = 2; + route->path.cost = asbr_entry->path.cost; + route->path.u.cost_e2 = OSPF6_ASBR_METRIC(external); + } else { + route->path.type = OSPF6_PATH_TYPE_EXTERNAL1; + route->path.metric_type = 1; + route->path.cost = + asbr_entry->path.cost + OSPF6_ASBR_METRIC(external); + route->path.u.cost_e2 = 0; + } + + route->path.tag = ospf6_as_external_lsa_get_tag(lsa); + + ospf6_route_copy_nexthops(route, asbr_entry); + + path = ospf6_path_dup(&route->path); + ospf6_copy_nexthops(path->nh_list, asbr_entry->nh_list); + listnode_add_sort(route->paths, path); + + + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) + zlog_debug( + "%s: %s %u route add %pFX cost %u(%u) nh %u", __func__, + (type == OSPF6_LSTYPE_AS_EXTERNAL) ? "AS-External" + : "NSSA", + (route->path.type == OSPF6_PATH_TYPE_EXTERNAL1) ? 1 : 2, + &route->prefix, route->path.cost, route->path.u.cost_e2, + listcount(route->nh_list)); + + if (type == OSPF6_LSTYPE_AS_EXTERNAL) + old = ospf6_route_lookup(&route->prefix, ospf6->route_table); + else if (type == OSPF6_LSTYPE_TYPE_7) + old = ospf6_route_lookup(&route->prefix, oa->route_table); + if (!old) { + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) + zlog_debug("%s: Adding new route", __func__); + /* Add the new route to ospf6 instance route table. */ + if (type == OSPF6_LSTYPE_AS_EXTERNAL) + ospf6_route_add(route, ospf6->route_table); + /* Add the route to the area route table */ + else if (type == OSPF6_LSTYPE_TYPE_7) { + ospf6_route_add(route, oa->route_table); + } + } else { + /* RFC 2328 16.4 (6) + * ECMP: Keep new equal preference path in current + * route's path list, update zebra with new effective + * list along with addition of ECMP path. + */ + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) + zlog_debug("%s : old route %pFX cost %u(%u) nh %u", + __func__, &route->prefix, route->path.cost, + route->path.u.cost_e2, + listcount(route->nh_list)); + ospf6_asbr_update_route_ecmp_path(old, route, ospf6); + } +} + +void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa, + struct ospf6_route *asbr_entry) +{ + struct ospf6_as_external_lsa *external; + struct prefix prefix; + struct ospf6_route *route, *nroute, *route_to_del; + struct ospf6_area *oa = NULL; + struct ospf6 *ospf6; + int type; + bool debug = false; + + external = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END( + lsa->header); + + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL) || (IS_OSPF6_DEBUG_NSSA)) + debug = true; + + ospf6 = ospf6_get_by_lsdb(lsa); + type = ntohs(lsa->header->type); + + if (type == OSPF6_LSTYPE_TYPE_7) { + if (debug) + zlog_debug("%s: Withdraw Type 7 route for %s", + __func__, lsa->name); + oa = lsa->lsdb->data; + } else { + if (debug) + zlog_debug("%s: Withdraw AS-External route for %s", + __func__, lsa->name); + + if (ospf6_check_and_set_router_abr(ospf6)) + oa = ospf6->backbone; + else + oa = listnode_head(ospf6->area_list); + } + + if (oa == NULL) { + if (debug) + zlog_debug("%s: Invalid area", __func__); + return; + } + + if (lsa->header->adv_router == oa->ospf6->router_id) { + if (debug) + zlog_debug("Ignore self-originated AS-External-LSA"); + return; + } + + route_to_del = ospf6_route_create(ospf6); + route_to_del->type = OSPF6_DEST_TYPE_NETWORK; + route_to_del->prefix.family = AF_INET6; + route_to_del->prefix.prefixlen = external->prefix.prefix_length; + ospf6_prefix_in6_addr(&route_to_del->prefix.u.prefix6, external, + &external->prefix); + + route_to_del->path.origin.type = lsa->header->type; + route_to_del->path.origin.id = lsa->header->id; + route_to_del->path.origin.adv_router = lsa->header->adv_router; + + if (asbr_entry) { + route_to_del->path.area_id = asbr_entry->path.area_id; + if (CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_E)) { + route_to_del->path.type = OSPF6_PATH_TYPE_EXTERNAL2; + route_to_del->path.metric_type = 2; + route_to_del->path.cost = asbr_entry->path.cost; + route_to_del->path.u.cost_e2 = + OSPF6_ASBR_METRIC(external); + } else { + route_to_del->path.type = OSPF6_PATH_TYPE_EXTERNAL1; + route_to_del->path.metric_type = 1; + route_to_del->path.cost = asbr_entry->path.cost + + OSPF6_ASBR_METRIC(external); + route_to_del->path.u.cost_e2 = 0; + } + } + + memset(&prefix, 0, sizeof(struct prefix)); + prefix.family = AF_INET6; + prefix.prefixlen = external->prefix.prefix_length; + ospf6_prefix_in6_addr(&prefix.u.prefix6, external, &external->prefix); + + if (type == OSPF6_LSTYPE_TYPE_7) + route = ospf6_route_lookup(&prefix, oa->route_table); + else + route = ospf6_route_lookup(&prefix, oa->ospf6->route_table); + + if (route == NULL) { + if (debug) + zlog_debug("AS-External route %pFX not found", &prefix); + ospf6_route_delete(route_to_del); + return; + } + + if (debug) + zlog_debug( + "%s: Current route %pFX cost %u e2 %u, route to del cost %u e2 %u", + __func__, &prefix, route->path.cost, route->path.u.cost_e2, + route_to_del->path.cost, route_to_del->path.u.cost_e2); + + for (ospf6_route_lock(route); + route && ospf6_route_is_prefix(&prefix, route); route = nroute) { + nroute = ospf6_route_next(route); + + if (route->type != OSPF6_DEST_TYPE_NETWORK) + continue; + + /* Route has multiple ECMP paths, remove matching + * path. Update current route's effective nh list + * after removal of one of the path. + */ + if (listcount(route->paths) > 1) { + struct listnode *anode, *anext; + struct listnode *nnode, *rnode, *rnext; + struct ospf6_nexthop *nh, *rnh; + struct ospf6_path *o_path; + bool nh_updated = false; + + /* Iterate all paths of route to find maching with LSA + * remove from route path list. If route->path is same, + * replace from paths list. + */ + for (ALL_LIST_ELEMENTS(route->paths, anode, anext, + o_path)) { + if ((o_path->origin.type != lsa->header->type) + || (o_path->origin.adv_router + != lsa->header->adv_router) + || (o_path->origin.id != lsa->header->id)) + continue; + + /* Compare LSA cost with current + * route info. + */ + if (asbr_entry + && (o_path->cost != route_to_del->path.cost + || o_path->u.cost_e2 + != route_to_del->path.u + .cost_e2)) { + if (IS_OSPF6_DEBUG_EXAMIN( + AS_EXTERNAL)) { + zlog_debug( + "%s: route %pFX to delete is not same, cost %u del cost %u. skip", + __func__, &prefix, + route->path.cost, + route_to_del->path + .cost); + } + continue; + } + + if (debug) { + zlog_debug( + "%s: route %pFX path found with cost %u nh %u to remove.", + __func__, &prefix, route->path.cost, + listcount(o_path->nh_list)); + } + + /* Remove found path's nh_list from + * the route's nh_list. + */ + for (ALL_LIST_ELEMENTS_RO(o_path->nh_list, + nnode, nh)) { + for (ALL_LIST_ELEMENTS(route->nh_list, + rnode, rnext, + rnh)) { + if (!ospf6_nexthop_is_same(rnh, + nh)) + continue; + listnode_delete(route->nh_list, + rnh); + ospf6_nexthop_delete(rnh); + } + } + /* Delete the path from route's path list */ + listnode_delete(route->paths, o_path); + ospf6_path_free(o_path); + nh_updated = true; + } + + if (nh_updated) { + /* Iterate all paths and merge nexthop, + * unlesss any of the nexthop similar to + * ones deleted as part of path deletion. + */ + + for (ALL_LIST_ELEMENTS(route->paths, anode, + anext, o_path)) { + ospf6_merge_nexthops(route->nh_list, + o_path->nh_list); + } + + if (debug) { + zlog_debug( + "%s: AS-External %u route %pFX update paths %u nh %u", + __func__, + (route->path.type + == OSPF6_PATH_TYPE_EXTERNAL1) + ? 1 + : 2, + &route->prefix, listcount(route->paths), + route->nh_list ? listcount( + route->nh_list) + : 0); + } + + if (listcount(route->paths)) { + /* Update RIB/FIB with effective + * nh_list + */ + if (oa->ospf6->route_table->hook_add) + (*oa->ospf6->route_table + ->hook_add)(route); + + /* route's primary path is similar + * to LSA, replace route's primary + * path with route's paths list head. + */ + if ((route->path.origin.id == + lsa->header->id) && + (route->path.origin.adv_router + == lsa->header->adv_router)) { + struct ospf6_path *h_path; + + h_path = (struct ospf6_path *) + listgetdata( + listhead(route->paths)); + route->path.origin.type = + h_path->origin.type; + route->path.origin.id = + h_path->origin.id; + route->path.origin.adv_router = + h_path->origin.adv_router; + } + } else { + if (type == OSPF6_LSTYPE_TYPE_7) + ospf6_route_remove( + route, oa->route_table); + else + ospf6_route_remove( + route, + oa->ospf6->route_table); + } + } + continue; + + } else { + /* Compare LSA origin and cost with current route info. + * if any check fails skip del this route node. + */ + if (asbr_entry + && (!ospf6_route_is_same_origin(route, route_to_del) + || (route->path.type != route_to_del->path.type) + || (route->path.cost != route_to_del->path.cost) + || (route->path.u.cost_e2 + != route_to_del->path.u.cost_e2))) { + if (debug) { + zlog_debug( + "%s: route %pFX to delete is not same, cost %u del cost %u. skip", + __func__, &prefix, route->path.cost, + route_to_del->path.cost); + } + continue; + } + + if ((route->path.origin.type != lsa->header->type) + || (route->path.origin.adv_router + != lsa->header->adv_router) + || (route->path.origin.id != lsa->header->id)) + continue; + } + if (debug) { + zlog_debug( + "%s: AS-External %u route remove %pFX cost %u(%u) nh %u", + __func__, + route->path.type == OSPF6_PATH_TYPE_EXTERNAL1 + ? 1 + : 2, + &route->prefix, route->path.cost, route->path.u.cost_e2, + listcount(route->nh_list)); + } + if (type == OSPF6_LSTYPE_TYPE_7) + ospf6_route_remove(route, oa->route_table); + else + ospf6_route_remove(route, oa->ospf6->route_table); + } + if (route != NULL) + ospf6_route_unlock(route); + + ospf6_route_delete(route_to_del); +} + +void ospf6_asbr_lsentry_add(struct ospf6_route *asbr_entry, struct ospf6 *ospf6) +{ + struct ospf6_lsa *lsa; + uint16_t type; + uint32_t router; + + if (!CHECK_FLAG(asbr_entry->flag, OSPF6_ROUTE_BEST)) { + char buf[16]; + inet_ntop(AF_INET, &ADV_ROUTER_IN_PREFIX(&asbr_entry->prefix), + buf, sizeof(buf)); + zlog_info("ignore non-best path: lsentry %s add", buf); + return; + } + + type = htons(OSPF6_LSTYPE_AS_EXTERNAL); + router = ospf6_linkstate_prefix_adv_router(&asbr_entry->prefix); + for (ALL_LSDB_TYPED_ADVRTR(ospf6->lsdb, type, router, lsa)) { + if (!OSPF6_LSA_IS_MAXAGE(lsa)) + ospf6_asbr_lsa_add(lsa); + } +} + +void ospf6_asbr_lsentry_remove(struct ospf6_route *asbr_entry, + struct ospf6 *ospf6) +{ + struct ospf6_lsa *lsa; + uint16_t type; + uint32_t router; + + type = htons(OSPF6_LSTYPE_AS_EXTERNAL); + router = ospf6_linkstate_prefix_adv_router(&asbr_entry->prefix); + for (ALL_LSDB_TYPED_ADVRTR(ospf6->lsdb, type, router, lsa)) + ospf6_asbr_lsa_remove(lsa, asbr_entry); +} + + +/* redistribute function */ +static void ospf6_asbr_routemap_set(struct ospf6_redist *red, + const char *mapname) +{ + if (ROUTEMAP_NAME(red)) { + route_map_counter_decrement(ROUTEMAP(red)); + free(ROUTEMAP_NAME(red)); + } + + ROUTEMAP_NAME(red) = strdup(mapname); + ROUTEMAP(red) = route_map_lookup_by_name(mapname); + route_map_counter_increment(ROUTEMAP(red)); +} + +static void ospf6_asbr_routemap_unset(struct ospf6_redist *red) +{ + if (ROUTEMAP_NAME(red)) + free(ROUTEMAP_NAME(red)); + + route_map_counter_decrement(ROUTEMAP(red)); + + ROUTEMAP_NAME(red) = NULL; + ROUTEMAP(red) = NULL; +} + +static void ospf6_asbr_routemap_update_timer(struct event *thread) +{ + struct ospf6 *ospf6 = EVENT_ARG(thread); + struct ospf6_redist *red; + int type; + + for (type = 0; type < ZEBRA_ROUTE_MAX; type++) { + red = ospf6_redist_lookup(ospf6, type, 0); + + if (!red) + continue; + + if (!CHECK_FLAG(red->flag, OSPF6_IS_RMAP_CHANGED)) + continue; + + if (ROUTEMAP_NAME(red)) + ROUTEMAP(red) = + route_map_lookup_by_name(ROUTEMAP_NAME(red)); + + if (ROUTEMAP(red)) { + if (IS_OSPF6_DEBUG_ASBR) + zlog_debug( + "%s: route-map %s update, reset redist %s", + __func__, ROUTEMAP_NAME(red), + ZROUTE_NAME(type)); + + ospf6_zebra_no_redistribute(type, ospf6->vrf_id); + ospf6_zebra_redistribute(type, ospf6->vrf_id); + } + + UNSET_FLAG(red->flag, OSPF6_IS_RMAP_CHANGED); + } +} + +void ospf6_asbr_distribute_list_update(struct ospf6 *ospf6, + struct ospf6_redist *red) +{ + SET_FLAG(red->flag, OSPF6_IS_RMAP_CHANGED); + + if (event_is_scheduled(ospf6->t_distribute_update)) + return; + + if (IS_OSPF6_DEBUG_ASBR) + zlog_debug("%s: trigger redistribute reset thread", __func__); + + event_add_timer_msec(master, ospf6_asbr_routemap_update_timer, ospf6, + OSPF_MIN_LS_INTERVAL, &ospf6->t_distribute_update); +} + +void ospf6_asbr_routemap_update(const char *mapname) +{ + int type; + struct listnode *node, *nnode; + struct ospf6 *ospf6 = NULL; + struct ospf6_redist *red; + + if (om6 == NULL) + return; + + for (ALL_LIST_ELEMENTS(om6->ospf6, node, nnode, ospf6)) { + for (type = 0; type < ZEBRA_ROUTE_MAX; type++) { + red = ospf6_redist_lookup(ospf6, type, 0); + if (!red || (ROUTEMAP_NAME(red) == NULL)) + continue; + ROUTEMAP(red) = + route_map_lookup_by_name(ROUTEMAP_NAME(red)); + + if (mapname == NULL + || strcmp(ROUTEMAP_NAME(red), mapname)) + continue; + if (ROUTEMAP(red)) { + if (IS_OSPF6_DEBUG_ASBR) + zlog_debug( + "%s: route-map %s update, reset redist %s", + __func__, + mapname, + ZROUTE_NAME( + type)); + + route_map_counter_increment(ROUTEMAP(red)); + ospf6_asbr_distribute_list_update(ospf6, red); + } else { + /* + * if the mapname matches a + * route-map on ospf6 but the + * map doesn't exist, it is + * being deleted. flush and then + * readvertise + */ + if (IS_OSPF6_DEBUG_ASBR) + zlog_debug( + "%s: route-map %s deleted, reset redist %s", + __func__, + mapname, + ZROUTE_NAME( + type)); + ospf6_asbr_redistribute_unset(ospf6, red, type); + ospf6_asbr_routemap_set(red, mapname); + ospf6_asbr_redistribute_set(ospf6, type); + } + } + } +} + +static void ospf6_asbr_routemap_event(const char *name) +{ + int type; + struct listnode *node, *nnode; + struct ospf6 *ospf6; + struct ospf6_redist *red; + + if (om6 == NULL) + return; + for (ALL_LIST_ELEMENTS(om6->ospf6, node, nnode, ospf6)) { + for (type = 0; type < ZEBRA_ROUTE_MAX; type++) { + red = ospf6_redist_lookup(ospf6, type, 0); + if (red && ROUTEMAP_NAME(red) + && (strcmp(ROUTEMAP_NAME(red), name) == 0)) + ospf6_asbr_distribute_list_update(ospf6, red); + } + } +} + +int ospf6_asbr_is_asbr(struct ospf6 *o) +{ + return (o->external_table->count || IS_OSPF6_ASBR(o)); +} + +struct ospf6_redist *ospf6_redist_lookup(struct ospf6 *ospf6, int type, + unsigned short instance) +{ + struct list *red_list; + struct listnode *node; + struct ospf6_redist *red; + + red_list = ospf6->redist[type]; + if (!red_list) + return (NULL); + + for (ALL_LIST_ELEMENTS_RO(red_list, node, red)) + if (red->instance == instance) + return red; + + return NULL; +} + +static struct ospf6_redist *ospf6_redist_add(struct ospf6 *ospf6, int type, + uint8_t instance) +{ + struct ospf6_redist *red; + + red = ospf6_redist_lookup(ospf6, type, instance); + if (red) + return red; + + if (!ospf6->redist[type]) + ospf6->redist[type] = list_new(); + + red = XCALLOC(MTYPE_OSPF6_REDISTRIBUTE, sizeof(struct ospf6_redist)); + red->instance = instance; + red->dmetric.type = -1; + red->dmetric.value = -1; + ROUTEMAP_NAME(red) = NULL; + ROUTEMAP(red) = NULL; + + listnode_add(ospf6->redist[type], red); + ospf6->redistribute++; + + return red; +} + +static void ospf6_redist_del(struct ospf6 *ospf6, struct ospf6_redist *red, + int type) +{ + if (red) { + listnode_delete(ospf6->redist[type], red); + if (!ospf6->redist[type]->count) { + list_delete(&ospf6->redist[type]); + } + XFREE(MTYPE_OSPF6_REDISTRIBUTE, red); + ospf6->redistribute--; + } +} + +/*Set the status of the ospf instance to ASBR based on the status parameter, + * rechedule SPF calculation, originate router LSA*/ +void ospf6_asbr_status_update(struct ospf6 *ospf6, int status) +{ + struct listnode *lnode, *lnnode; + struct ospf6_area *oa; + + zlog_info("ASBR[%s:Status:%d]: Update", ospf6->name, status); + + if (status) { + if (IS_OSPF6_ASBR(ospf6)) { + zlog_info("ASBR[%s:Status:%d]: Already ASBR", + ospf6->name, status); + return; + } + SET_FLAG(ospf6->flag, OSPF6_FLAG_ASBR); + } else { + if (!IS_OSPF6_ASBR(ospf6)) { + zlog_info("ASBR[%s:Status:%d]: Already non ASBR", + ospf6->name, status); + return; + } + UNSET_FLAG(ospf6->flag, OSPF6_FLAG_ASBR); + } + + /* Transition from/to status ASBR, schedule timer. */ + ospf6_spf_schedule(ospf6, OSPF6_SPF_FLAGS_ASBR_STATUS_CHANGE); + + /* Reoriginate router LSA for all areas */ + for (ALL_LIST_ELEMENTS(ospf6->area_list, lnode, lnnode, oa)) + OSPF6_ROUTER_LSA_SCHEDULE(oa); +} + +static void ospf6_asbr_redistribute_set(struct ospf6 *ospf6, int type) +{ + ospf6_zebra_redistribute(type, ospf6->vrf_id); + + ++ospf6->redist_count; + ospf6_asbr_status_update(ospf6, ospf6->redist_count); +} + +static void ospf6_asbr_redistribute_unset(struct ospf6 *ospf6, + struct ospf6_redist *red, int type) +{ + struct ospf6_route *route; + struct ospf6_external_info *info; + + ospf6_zebra_no_redistribute(type, ospf6->vrf_id); + + for (route = ospf6_route_head(ospf6->external_table); route; + route = ospf6_route_next(route)) { + info = route->route_option; + if (info->type != type) + continue; + + ospf6_asbr_redistribute_remove(info->type, 0, &route->prefix, + ospf6); + } + + ospf6_asbr_routemap_unset(red); + --ospf6->redist_count; + ospf6_asbr_status_update(ospf6, ospf6->redist_count); +} + +/* When an area is unstubified, flood all the external LSAs in the area */ +void ospf6_asbr_send_externals_to_area(struct ospf6_area *oa) +{ + struct ospf6_lsa *lsa, *lsanext; + + for (ALL_LSDB(oa->ospf6->lsdb, lsa, lsanext)) { + if (ntohs(lsa->header->type) == OSPF6_LSTYPE_AS_EXTERNAL) { + if (IS_OSPF6_DEBUG_ASBR) + zlog_debug("%s: Flooding AS-External LSA %s", + __func__, lsa->name); + + ospf6_flood_area(NULL, lsa, oa); + } + } +} + +/* When an area is stubified, remove all the external LSAs in the area */ +void ospf6_asbr_remove_externals_from_area(struct ospf6_area *oa) +{ + struct ospf6_lsa *lsa, *lsanext; + struct listnode *node, *nnode; + struct ospf6_area *area; + struct ospf6 *ospf6 = oa->ospf6; + const struct route_node *iterend; + + /* skip if router is in other non-stub/non-NSSA areas */ + for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, area)) + if (!IS_AREA_STUB(area) && !IS_AREA_NSSA(area)) + return; + + /* if router is only in a stub area then purge AS-External LSAs */ + iterend = ospf6_lsdb_head(ospf6->lsdb, 0, 0, 0, &lsa); + while (lsa != NULL) { + assert(lsa->lock > 1); + lsanext = ospf6_lsdb_next(iterend, lsa); + if (ntohs(lsa->header->type) == OSPF6_LSTYPE_AS_EXTERNAL) + ospf6_lsdb_remove(lsa, ospf6->lsdb); + lsa = lsanext; + } +} + +static struct ospf6_external_aggr_rt * +ospf6_external_aggr_match(struct ospf6 *ospf6, struct prefix *p) +{ + struct route_node *node; + + node = route_node_match(ospf6->rt_aggr_tbl, p); + if (node == NULL) + return NULL; + + if (IS_OSPF6_DEBUG_AGGR) { + struct ospf6_external_aggr_rt *ag = node->info; + zlog_debug("%s: Matching aggregator found.prefix: %pFX Aggregator %pFX", + __func__, + p, + &ag->p); + } + + route_unlock_node(node); + + return node->info; +} + +static void ospf6_external_lsa_fwd_addr_set(struct ospf6 *ospf6, + const struct in6_addr *nexthop, + struct in6_addr *fwd_addr) +{ + struct vrf *vrf; + struct interface *ifp; + struct prefix nh; + + /* Initialize forwarding address to zero. */ + memset(fwd_addr, 0, sizeof(*fwd_addr)); + + vrf = vrf_lookup_by_id(ospf6->vrf_id); + if (!vrf) + return; + + nh.family = AF_INET6; + nh.u.prefix6 = *nexthop; + nh.prefixlen = IPV6_MAX_BITLEN; + + /* + * Use the route's nexthop as the forwarding address if it meets the + * following conditions: + * - It's a global address. + * - The associated nexthop interface is OSPF-enabled. + */ + if (IN6_IS_ADDR_UNSPECIFIED(nexthop) || IN6_IS_ADDR_LINKLOCAL(nexthop)) + return; + + FOR_ALL_INTERFACES (vrf, ifp) { + struct ospf6_interface *oi = ifp->info; + struct connected *connected; + struct listnode *node; + + if (!oi || CHECK_FLAG(oi->flag, OSPF6_INTERFACE_DISABLE)) + continue; + + FOR_ALL_INTERFACES_ADDRESSES (ifp, connected, node) { + if (connected->address->family != AF_INET6) + continue; + if (IN6_IS_ADDR_LINKLOCAL(&connected->address->u.prefix6)) + continue; + if (!prefix_match(connected->address, &nh)) + continue; + + *fwd_addr = *nexthop; + return; + } + } +} + +void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex, + struct prefix *prefix, + unsigned int nexthop_num, + const struct in6_addr *nexthop, + route_tag_t tag, struct ospf6 *ospf6) +{ + route_map_result_t ret; + struct ospf6_route troute; + struct ospf6_external_info tinfo; + struct ospf6_route *route, *match; + struct ospf6_external_info *info; + struct ospf6_redist *red; + + red = ospf6_redist_lookup(ospf6, type, 0); + + if (!red) + return; + + if ((type != DEFAULT_ROUTE) + && !ospf6_zebra_is_redistribute(type, ospf6->vrf_id)) + return; + + memset(&troute, 0, sizeof(troute)); + memset(&tinfo, 0, sizeof(tinfo)); + + if (IS_OSPF6_DEBUG_ASBR) + zlog_debug("Redistribute %pFX (%s)", prefix, + type == DEFAULT_ROUTE + ? "default-information-originate" + : ZROUTE_NAME(type)); + + /* if route-map was specified but not found, do not advertise */ + if (ROUTEMAP_NAME(red)) { + if (ROUTEMAP(red) == NULL) + ospf6_asbr_routemap_update(NULL); + if (ROUTEMAP(red) == NULL) { + zlog_warn( + "route-map \"%s\" not found, suppress redistributing", + ROUTEMAP_NAME(red)); + return; + } + } + + /* apply route-map */ + if (ROUTEMAP(red)) { + troute.route_option = &tinfo; + troute.ospf6 = ospf6; + tinfo.ifindex = ifindex; + tinfo.tag = tag; + + ret = route_map_apply(ROUTEMAP(red), prefix, &troute); + if (ret == RMAP_DENYMATCH) { + if (IS_OSPF6_DEBUG_ASBR) + zlog_debug("Denied by route-map \"%s\"", + ROUTEMAP_NAME(red)); + ospf6_asbr_redistribute_remove(type, ifindex, prefix, + ospf6); + return; + } + } + + match = ospf6_route_lookup(prefix, ospf6->external_table); + if (match) { + info = match->route_option; + /* copy result of route-map */ + if (ROUTEMAP(red)) { + if (troute.path.metric_type) + match->path.metric_type = + troute.path.metric_type; + else + match->path.metric_type = + metric_type(ospf6, type, 0); + if (troute.path.cost) + match->path.cost = troute.path.cost; + else + match->path.cost = metric_value(ospf6, type, 0); + + if (!IN6_IS_ADDR_UNSPECIFIED(&tinfo.forwarding)) + memcpy(&info->forwarding, &tinfo.forwarding, + sizeof(struct in6_addr)); + info->tag = tinfo.tag; + } else { + /* If there is no route-map, simply update the tag and + * metric fields + */ + match->path.metric_type = metric_type(ospf6, type, 0); + match->path.cost = metric_value(ospf6, type, 0); + info->tag = tag; + } + + info->type = type; + + if (nexthop_num && nexthop) { + ospf6_route_add_nexthop(match, ifindex, nexthop); + ospf6_external_lsa_fwd_addr_set(ospf6, nexthop, + &info->forwarding); + } else + ospf6_route_add_nexthop(match, ifindex, NULL); + + match->path.origin.id = htonl(info->id); + ospf6_handle_external_lsa_origination(ospf6, match, prefix); + + ospf6_asbr_status_update(ospf6, ospf6->redistribute); + + return; + } + + /* create new entry */ + route = ospf6_route_create(ospf6); + route->type = OSPF6_DEST_TYPE_NETWORK; + prefix_copy(&route->prefix, prefix); + + info = (struct ospf6_external_info *)XCALLOC( + MTYPE_OSPF6_EXTERNAL_INFO, sizeof(struct ospf6_external_info)); + route->route_option = info; + + /* copy result of route-map */ + if (ROUTEMAP(red)) { + if (troute.path.metric_type) + route->path.metric_type = troute.path.metric_type; + else + route->path.metric_type = metric_type(ospf6, type, 0); + if (troute.path.cost) + route->path.cost = troute.path.cost; + else + route->path.cost = metric_value(ospf6, type, 0); + if (!IN6_IS_ADDR_UNSPECIFIED(&tinfo.forwarding)) + memcpy(&info->forwarding, &tinfo.forwarding, + sizeof(struct in6_addr)); + info->tag = tinfo.tag; + } else { + /* If there is no route-map, simply update the tag and metric + * fields + */ + route->path.metric_type = metric_type(ospf6, type, 0); + route->path.cost = metric_value(ospf6, type, 0); + info->tag = tag; + } + + info->type = type; + if (nexthop_num && nexthop) { + ospf6_route_add_nexthop(route, ifindex, nexthop); + ospf6_external_lsa_fwd_addr_set(ospf6, nexthop, + &info->forwarding); + } else + ospf6_route_add_nexthop(route, ifindex, NULL); + + route = ospf6_route_add(route, ospf6->external_table); + ospf6_handle_external_lsa_origination(ospf6, route, prefix); + + ospf6_asbr_status_update(ospf6, ospf6->redistribute); + +} + +static void ospf6_asbr_external_lsa_remove_by_id(struct ospf6 *ospf6, + uint32_t id) +{ + struct ospf6_lsa *lsa; + + lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL), + htonl(id), ospf6->router_id, ospf6->lsdb); + if (!lsa) + return; + + ospf6_external_lsa_purge(ospf6, lsa); + +} + +static void +ospf6_link_route_to_aggr(struct ospf6_external_aggr_rt *aggr, + struct ospf6_route *rt) +{ + (void)hash_get(aggr->match_extnl_hash, rt, hash_alloc_intern); + rt->aggr_route = aggr; +} + +static void +ospf6_asbr_summary_remove_lsa_and_route(struct ospf6 *ospf6, + struct ospf6_external_aggr_rt *aggr) +{ + + /* Send a Max age LSA if it is already originated.*/ + if (!CHECK_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_ORIGINATED)) + return; + + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: Flushing Aggregate route (%pFX)", + __func__, + &aggr->p); + + ospf6_asbr_external_lsa_remove_by_id(ospf6, aggr->id); + + if (aggr->route) { + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug( + "%s: Remove the blackhole route", + __func__); + + ospf6_zebra_route_update_remove(aggr->route, ospf6); + if (aggr->route->route_option) + XFREE(MTYPE_OSPF6_EXTERNAL_INFO, + aggr->route->route_option); + ospf6_route_delete(aggr->route); + aggr->route = NULL; + } + + aggr->id = 0; + /* Unset the Origination flag */ + UNSET_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_ORIGINATED); +} + +static void +ospf6_unlink_route_from_aggr(struct ospf6 *ospf6, + struct ospf6_external_aggr_rt *aggr, + struct ospf6_route *rt) +{ + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: Unlinking external route(%pFX) from aggregator(%pFX), external route count:%ld", + __func__, + &rt->prefix, + &aggr->p, + OSPF6_EXTERNAL_RT_COUNT(aggr)); + + hash_release(aggr->match_extnl_hash, rt); + rt->aggr_route = NULL; + + /* Flush the aggregate route if matching + * external route count becomes zero. + */ + if (!OSPF6_EXTERNAL_RT_COUNT(aggr)) + ospf6_asbr_summary_remove_lsa_and_route(ospf6, aggr); +} + +void ospf6_asbr_redistribute_remove(int type, ifindex_t ifindex, + struct prefix *prefix, struct ospf6 *ospf6) +{ + struct ospf6_route *match; + struct ospf6_external_info *info = NULL; + + match = ospf6_route_lookup(prefix, ospf6->external_table); + if (match == NULL) { + if (IS_OSPF6_DEBUG_ASBR) + zlog_debug("No such route %pFX to withdraw", prefix); + return; + } + + info = match->route_option; + assert(info); + + if (info->type != type) { + if (IS_OSPF6_DEBUG_ASBR) + zlog_debug("Original protocol mismatch: %pFX", prefix); + return; + } + + /* This means aggregation on this route was not done, hence remove LSA + * if any originated for this prefix + */ + if (!match->aggr_route) + ospf6_asbr_external_lsa_remove_by_id(ospf6, info->id); + else + ospf6_unlink_route_from_aggr(ospf6, match->aggr_route, match); + + if (IS_OSPF6_DEBUG_ASBR) + zlog_debug("Removing route from external table %pFX", + prefix); + + ospf6_route_remove(match, ospf6->external_table); + XFREE(MTYPE_OSPF6_EXTERNAL_INFO, info); + + ospf6_asbr_status_update(ospf6, ospf6->redistribute); +} + +DEFPY (ospf6_redistribute, + ospf6_redistribute_cmd, + "redistribute " FRR_REDIST_STR_OSPF6D "[{metric (0-16777214)|metric-type (1-2)$metric_type|route-map RMAP_NAME$rmap_str}]", + "Redistribute\n" + FRR_REDIST_HELP_STR_OSPF6D + "Metric for redistributed routes\n" + "OSPF default metric\n" + "OSPF exterior metric type for redistributed routes\n" + "Set OSPF External Type 1/2 metrics\n" + "Route map reference\n" + "Route map name\n") +{ + int type; + struct ospf6_redist *red; + int idx_protocol = 1; + char *proto = argv[idx_protocol]->text; + + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + type = proto_redistnum(AFI_IP6, proto); + if (type < 0) + return CMD_WARNING_CONFIG_FAILED; + + if (!metric_str) + metric = -1; + if (!metric_type_str) + metric_type = -1; + + red = ospf6_redist_lookup(ospf6, type, 0); + if (!red) { + red = ospf6_redist_add(ospf6, type, 0); + } else { + /* Check if nothing has changed. */ + if (red->dmetric.value == metric + && red->dmetric.type == metric_type + && ((!ROUTEMAP_NAME(red) && !rmap_str) + || (ROUTEMAP_NAME(red) && rmap_str + && strmatch(ROUTEMAP_NAME(red), rmap_str)))) + return CMD_SUCCESS; + + ospf6_asbr_redistribute_unset(ospf6, red, type); + } + + red->dmetric.value = metric; + red->dmetric.type = metric_type; + if (rmap_str) + ospf6_asbr_routemap_set(red, rmap_str); + else + ospf6_asbr_routemap_unset(red); + ospf6_asbr_redistribute_set(ospf6, type); + + return CMD_SUCCESS; +} + +DEFUN (no_ospf6_redistribute, + no_ospf6_redistribute_cmd, + "no redistribute " FRR_REDIST_STR_OSPF6D "[{metric (0-16777214)|metric-type (1-2)|route-map RMAP_NAME}]", + NO_STR + "Redistribute\n" + FRR_REDIST_HELP_STR_OSPF6D + "Metric for redistributed routes\n" + "OSPF default metric\n" + "OSPF exterior metric type for redistributed routes\n" + "Set OSPF External Type 1/2 metrics\n" + "Route map reference\n" + "Route map name\n") +{ + int type; + struct ospf6_redist *red; + int idx_protocol = 2; + char *proto = argv[idx_protocol]->text; + + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + type = proto_redistnum(AFI_IP6, proto); + if (type < 0) + return CMD_WARNING_CONFIG_FAILED; + + red = ospf6_redist_lookup(ospf6, type, 0); + if (!red) + return CMD_SUCCESS; + + ospf6_asbr_redistribute_unset(ospf6, red, type); + ospf6_redist_del(ospf6, red, type); + + return CMD_SUCCESS; +} + +int ospf6_redistribute_config_write(struct vty *vty, struct ospf6 *ospf6) +{ + int type; + struct ospf6_redist *red; + + for (type = 0; type < ZEBRA_ROUTE_MAX; type++) { + red = ospf6_redist_lookup(ospf6, type, 0); + if (!red) + continue; + if (type == ZEBRA_ROUTE_OSPF6) + continue; + + vty_out(vty, " redistribute %s", ZROUTE_NAME(type)); + if (red->dmetric.value >= 0) + vty_out(vty, " metric %d", red->dmetric.value); + if (red->dmetric.type == 1) + vty_out(vty, " metric-type 1"); + if (ROUTEMAP_NAME(red)) + vty_out(vty, " route-map %s", ROUTEMAP_NAME(red)); + vty_out(vty, "\n"); + } + + return 0; +} + +static void ospf6_redistribute_show_config(struct vty *vty, struct ospf6 *ospf6, + json_object *json_array, + json_object *json, bool use_json) +{ + int type; + int nroute[ZEBRA_ROUTE_MAX]; + int total; + struct ospf6_route *route; + struct ospf6_external_info *info; + json_object *json_route; + struct ospf6_redist *red; + + total = 0; + memset(nroute, 0, sizeof(nroute)); + for (route = ospf6_route_head(ospf6->external_table); route; + route = ospf6_route_next(route)) { + info = route->route_option; + nroute[info->type]++; + total++; + } + + if (!use_json) + vty_out(vty, "Redistributing External Routes from:\n"); + + for (type = 0; type < ZEBRA_ROUTE_MAX; type++) { + + red = ospf6_redist_lookup(ospf6, type, 0); + + if (!red) + continue; + if (type == ZEBRA_ROUTE_OSPF6) + continue; + + if (use_json) { + json_route = json_object_new_object(); + json_object_string_add(json_route, "routeType", + ZROUTE_NAME(type)); + json_object_int_add(json_route, "numberOfRoutes", + nroute[type]); + json_object_boolean_add(json_route, + "routeMapNamePresent", + ROUTEMAP_NAME(red)); + } + + if (ROUTEMAP_NAME(red)) { + if (use_json) { + json_object_string_add(json_route, + "routeMapName", + ROUTEMAP_NAME(red)); + json_object_boolean_add(json_route, + "routeMapFound", + ROUTEMAP(red)); + } else + vty_out(vty, + " %d: %s with route-map \"%s\"%s\n", + nroute[type], ZROUTE_NAME(type), + ROUTEMAP_NAME(red), + (ROUTEMAP(red) ? "" + : " (not found !)")); + } else { + if (!use_json) + vty_out(vty, " %d: %s\n", nroute[type], + ZROUTE_NAME(type)); + } + + if (use_json) + json_object_array_add(json_array, json_route); + } + if (use_json) { + json_object_object_add(json, "redistributedRoutes", json_array); + json_object_int_add(json, "totalRoutes", total); + } else + vty_out(vty, "Total %d routes\n", total); +} + +static void ospf6_redistribute_default_set(struct ospf6 *ospf6, int originate) +{ + struct prefix_ipv6 p = {}; + struct in6_addr nexthop = {}; + int cur_originate = ospf6->default_originate; + + p.family = AF_INET6; + p.prefixlen = 0; + + ospf6->default_originate = originate; + + switch (cur_originate) { + case DEFAULT_ORIGINATE_NONE: + break; + case DEFAULT_ORIGINATE_ZEBRA: + zclient_redistribute_default(ZEBRA_REDISTRIBUTE_DEFAULT_DELETE, + zclient, AFI_IP6, ospf6->vrf_id); + ospf6_asbr_redistribute_remove(DEFAULT_ROUTE, 0, + (struct prefix *)&p, ospf6); + + break; + case DEFAULT_ORIGINATE_ALWAYS: + ospf6_asbr_redistribute_remove(DEFAULT_ROUTE, 0, + (struct prefix *)&p, ospf6); + break; + } + + switch (originate) { + case DEFAULT_ORIGINATE_NONE: + break; + case DEFAULT_ORIGINATE_ZEBRA: + zclient_redistribute_default(ZEBRA_REDISTRIBUTE_DEFAULT_ADD, + zclient, AFI_IP6, ospf6->vrf_id); + + break; + case DEFAULT_ORIGINATE_ALWAYS: + ospf6_asbr_redistribute_add(DEFAULT_ROUTE, 0, + (struct prefix *)&p, 0, &nexthop, 0, + ospf6); + break; + } +} + +/* Default Route originate. */ +DEFPY (ospf6_default_route_originate, + ospf6_default_route_originate_cmd, + "default-information originate [{always$always|metric (0-16777214)$mval|metric-type (1-2)$mtype|route-map RMAP_NAME$rtmap}]", + "Control distribution of default route\n" + "Distribute a default route\n" + "Always advertise default route\n" + "OSPFv3 default metric\n" + "OSPFv3 metric\n" + "OSPFv3 metric type for default routes\n" + "Set OSPFv3 External Type 1/2 metrics\n" + "Route map reference\n" + "Pointer to route-map entries\n") +{ + int default_originate = DEFAULT_ORIGINATE_ZEBRA; + struct ospf6_redist *red; + bool sameRtmap = false; + + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + int cur_originate = ospf6->default_originate; + + red = ospf6_redist_add(ospf6, DEFAULT_ROUTE, 0); + + if (always != NULL) + default_originate = DEFAULT_ORIGINATE_ALWAYS; + + if (mval_str == NULL) + mval = -1; + + if (mtype_str == NULL) + mtype = -1; + + /* To check if user is providing same route map */ + if ((!rtmap && !ROUTEMAP_NAME(red)) || + (rtmap && ROUTEMAP_NAME(red) && + (strcmp(rtmap, ROUTEMAP_NAME(red)) == 0))) + sameRtmap = true; + + /* Don't allow if the same lsa is already originated. */ + if ((sameRtmap) && (red->dmetric.type == mtype) + && (red->dmetric.value == mval) + && (cur_originate == default_originate)) + return CMD_SUCCESS; + + /* Updating Metric details */ + red->dmetric.type = mtype; + red->dmetric.value = mval; + + /* updating route map details */ + if (rtmap) + ospf6_asbr_routemap_set(red, rtmap); + else + ospf6_asbr_routemap_unset(red); + + ospf6_redistribute_default_set(ospf6, default_originate); + return CMD_SUCCESS; +} + +DEFPY (no_ospf6_default_information_originate, + no_ospf6_default_information_originate_cmd, + "no default-information originate [{always|metric (0-16777214)|metric-type (1-2)|route-map RMAP_NAME}]", + NO_STR + "Control distribution of default information\n" + "Distribute a default route\n" + "Always advertise default route\n" + "OSPFv3 default metric\n" + "OSPFv3 metric\n" + "OSPFv3 metric type for default routes\n" + "Set OSPFv3 External Type 1/2 metrics\n" + "Route map reference\n" + "Pointer to route-map entries\n") +{ + struct ospf6_redist *red; + + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + red = ospf6_redist_lookup(ospf6, DEFAULT_ROUTE, 0); + if (!red) + return CMD_SUCCESS; + + ospf6_asbr_routemap_unset(red); + ospf6_redist_del(ospf6, red, DEFAULT_ROUTE); + + ospf6_redistribute_default_set(ospf6, DEFAULT_ORIGINATE_NONE); + return CMD_SUCCESS; +} + +/* Routemap Functions */ +static enum route_map_cmd_result_t +ospf6_routemap_rule_match_address_prefixlist(void *rule, + const struct prefix *prefix, + + void *object) +{ + struct prefix_list *plist; + + plist = prefix_list_lookup(AFI_IP6, (char *)rule); + if (plist == NULL) + return RMAP_NOMATCH; + + return (prefix_list_apply(plist, prefix) == PREFIX_DENY ? RMAP_NOMATCH + : RMAP_MATCH); +} + +static void * +ospf6_routemap_rule_match_address_prefixlist_compile(const char *arg) +{ + return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg); +} + +static void ospf6_routemap_rule_match_address_prefixlist_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +static const struct route_map_rule_cmd + ospf6_routemap_rule_match_address_prefixlist_cmd = { + "ipv6 address prefix-list", + ospf6_routemap_rule_match_address_prefixlist, + ospf6_routemap_rule_match_address_prefixlist_compile, + ospf6_routemap_rule_match_address_prefixlist_free, +}; + +/* `match interface IFNAME' */ +/* Match function should return 1 if match is success else return + zero. */ +static enum route_map_cmd_result_t +ospf6_routemap_rule_match_interface(void *rule, const struct prefix *prefix, + void *object) +{ + struct interface *ifp; + struct ospf6_route *route; + struct ospf6_external_info *ei; + + route = object; + ei = route->route_option; + ifp = if_lookup_by_name((char *)rule, route->ospf6->vrf_id); + + if (ifp != NULL && ei->ifindex == ifp->ifindex) + return RMAP_MATCH; + + return RMAP_NOMATCH; +} + +/* Route map `interface' match statement. `arg' should be + interface name. */ +static void *ospf6_routemap_rule_match_interface_compile(const char *arg) +{ + return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg); +} + +/* Free route map's compiled `interface' value. */ +static void ospf6_routemap_rule_match_interface_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Route map commands for interface matching. */ +static const struct route_map_rule_cmd + ospf6_routemap_rule_match_interface_cmd = { + "interface", + ospf6_routemap_rule_match_interface, + ospf6_routemap_rule_match_interface_compile, + ospf6_routemap_rule_match_interface_free +}; + +/* Match function for matching route tags */ +static enum route_map_cmd_result_t +ospf6_routemap_rule_match_tag(void *rule, const struct prefix *p, void *object) +{ + route_tag_t *tag = rule; + struct ospf6_route *route = object; + struct ospf6_external_info *info = route->route_option; + + if (info->tag == *tag) + return RMAP_MATCH; + + return RMAP_NOMATCH; +} + +static const struct route_map_rule_cmd + ospf6_routemap_rule_match_tag_cmd = { + "tag", + ospf6_routemap_rule_match_tag, + route_map_rule_tag_compile, + route_map_rule_tag_free, +}; + +static enum route_map_cmd_result_t +ospf6_routemap_rule_set_metric_type(void *rule, const struct prefix *prefix, + void *object) +{ + char *metric_type = rule; + struct ospf6_route *route = object; + + if (strcmp(metric_type, "type-2") == 0) + route->path.metric_type = 2; + else + route->path.metric_type = 1; + + return RMAP_OKAY; +} + +static void *ospf6_routemap_rule_set_metric_type_compile(const char *arg) +{ + if (strcmp(arg, "type-2") && strcmp(arg, "type-1")) + return NULL; + return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg); +} + +static void ospf6_routemap_rule_set_metric_type_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +static const struct route_map_rule_cmd + ospf6_routemap_rule_set_metric_type_cmd = { + "metric-type", + ospf6_routemap_rule_set_metric_type, + ospf6_routemap_rule_set_metric_type_compile, + ospf6_routemap_rule_set_metric_type_free, +}; + +static enum route_map_cmd_result_t +ospf6_routemap_rule_set_metric(void *rule, const struct prefix *prefix, + void *object) +{ + char *metric = rule; + struct ospf6_route *route = object; + + route->path.cost = atoi(metric); + return RMAP_OKAY; +} + +static void *ospf6_routemap_rule_set_metric_compile(const char *arg) +{ + uint32_t metric; + char *endp; + metric = strtoul(arg, &endp, 0); + if (metric > OSPF_LS_INFINITY || *endp != '\0') + return NULL; + return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg); +} + +static void ospf6_routemap_rule_set_metric_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +static const struct route_map_rule_cmd + ospf6_routemap_rule_set_metric_cmd = { + "metric", + ospf6_routemap_rule_set_metric, + ospf6_routemap_rule_set_metric_compile, + ospf6_routemap_rule_set_metric_free, +}; + +static enum route_map_cmd_result_t +ospf6_routemap_rule_set_forwarding(void *rule, const struct prefix *prefix, + void *object) +{ + char *forwarding = rule; + struct ospf6_route *route = object; + struct ospf6_external_info *info = route->route_option; + + if (inet_pton(AF_INET6, forwarding, &info->forwarding) != 1) { + memset(&info->forwarding, 0, sizeof(struct in6_addr)); + return RMAP_ERROR; + } + + return RMAP_OKAY; +} + +static void *ospf6_routemap_rule_set_forwarding_compile(const char *arg) +{ + struct in6_addr a; + if (inet_pton(AF_INET6, arg, &a) != 1) + return NULL; + return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg); +} + +static void ospf6_routemap_rule_set_forwarding_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +static const struct route_map_rule_cmd + ospf6_routemap_rule_set_forwarding_cmd = { + "forwarding-address", + ospf6_routemap_rule_set_forwarding, + ospf6_routemap_rule_set_forwarding_compile, + ospf6_routemap_rule_set_forwarding_free, +}; + +static enum route_map_cmd_result_t +ospf6_routemap_rule_set_tag(void *rule, const struct prefix *p, void *object) +{ + route_tag_t *tag = rule; + struct ospf6_route *route = object; + struct ospf6_external_info *info = route->route_option; + + info->tag = *tag; + return RMAP_OKAY; +} + +static const struct route_map_rule_cmd ospf6_routemap_rule_set_tag_cmd = { + "tag", + ospf6_routemap_rule_set_tag, + route_map_rule_tag_compile, + route_map_rule_tag_free, +}; + +/* add "set metric-type" */ +DEFUN_YANG (ospf6_routemap_set_metric_type, ospf6_routemap_set_metric_type_cmd, + "set metric-type <type-1|type-2>", + SET_STR + "Type of metric for destination routing protocol\n" + "OSPF[6] external type 1 metric\n" + "OSPF[6] external type 2 metric\n") +{ + char *ext = argv[2]->text; + + const char *xpath = + "./set-action[action='frr-ospf-route-map:metric-type']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-ospf-route-map:metric-type", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, ext); + return nb_cli_apply_changes(vty, NULL); +} + +/* delete "set metric-type" */ +DEFUN_YANG (ospf6_routemap_no_set_metric_type, ospf6_routemap_no_set_metric_type_cmd, + "no set metric-type [<type-1|type-2>]", + NO_STR + SET_STR + "Type of metric for destination routing protocol\n" + "OSPF[6] external type 1 metric\n" + "OSPF[6] external type 2 metric\n") +{ + const char *xpath = + "./set-action[action='frr-ospf-route-map:metric-type']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +/* add "set forwarding-address" */ +DEFUN_YANG (ospf6_routemap_set_forwarding, ospf6_routemap_set_forwarding_cmd, + "set forwarding-address X:X::X:X", + "Set value\n" + "Forwarding Address\n" + "IPv6 Address\n") +{ + int idx_ipv6 = 2; + const char *xpath = + "./set-action[action='frr-ospf6-route-map:forwarding-address']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-ospf6-route-map:ipv6-address", xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, + argv[idx_ipv6]->arg); + return nb_cli_apply_changes(vty, NULL); +} + +/* delete "set forwarding-address" */ +DEFUN_YANG (ospf6_routemap_no_set_forwarding, ospf6_routemap_no_set_forwarding_cmd, + "no set forwarding-address [X:X::X:X]", + NO_STR + "Set value\n" + "Forwarding Address\n" + "IPv6 Address\n") +{ + const char *xpath = + "./set-action[action='frr-ospf6-route-map:forwarding-address']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +static void ospf6_routemap_init(void) +{ + route_map_init(); + + route_map_add_hook(ospf6_asbr_routemap_update); + route_map_delete_hook(ospf6_asbr_routemap_update); + route_map_event_hook(ospf6_asbr_routemap_event); + + route_map_set_metric_hook(generic_set_add); + route_map_no_set_metric_hook(generic_set_delete); + + route_map_set_tag_hook(generic_set_add); + route_map_no_set_tag_hook(generic_set_delete); + + route_map_match_tag_hook(generic_match_add); + route_map_no_match_tag_hook(generic_match_delete); + + route_map_match_ipv6_address_prefix_list_hook(generic_match_add); + route_map_no_match_ipv6_address_prefix_list_hook(generic_match_delete); + + route_map_match_interface_hook(generic_match_add); + route_map_no_match_interface_hook(generic_match_delete); + + route_map_install_match( + &ospf6_routemap_rule_match_address_prefixlist_cmd); + route_map_install_match(&ospf6_routemap_rule_match_interface_cmd); + route_map_install_match(&ospf6_routemap_rule_match_tag_cmd); + + route_map_install_set(&ospf6_routemap_rule_set_metric_type_cmd); + route_map_install_set(&ospf6_routemap_rule_set_metric_cmd); + route_map_install_set(&ospf6_routemap_rule_set_forwarding_cmd); + route_map_install_set(&ospf6_routemap_rule_set_tag_cmd); + + /* ASE Metric Type (e.g. Type-1/Type-2) */ + install_element(RMAP_NODE, &ospf6_routemap_set_metric_type_cmd); + install_element(RMAP_NODE, &ospf6_routemap_no_set_metric_type_cmd); + + /* ASE Metric */ + install_element(RMAP_NODE, &ospf6_routemap_set_forwarding_cmd); + install_element(RMAP_NODE, &ospf6_routemap_no_set_forwarding_cmd); +} + + +/* Display functions */ +static char *ospf6_as_external_lsa_get_prefix_str(struct ospf6_lsa *lsa, + char *buf, int buflen, + int pos) +{ + struct ospf6_as_external_lsa *external; + struct in6_addr in6; + int prefix_length = 0; + char tbuf[16]; + + if (lsa) { + external = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END( + lsa->header); + + if (pos == 0) { + ospf6_prefix_in6_addr(&in6, external, + &external->prefix); + prefix_length = external->prefix.prefix_length; + } else { + in6 = *((struct in6_addr + *)((caddr_t)external + + sizeof(struct + ospf6_as_external_lsa) + + OSPF6_PREFIX_SPACE( + external->prefix + .prefix_length))); + } + if (buf) { + inet_ntop(AF_INET6, &in6, buf, buflen); + if (prefix_length) { + snprintf(tbuf, sizeof(tbuf), "/%d", + prefix_length); + strlcat(buf, tbuf, buflen); + } + } + } + return (buf); +} + +static int ospf6_as_external_lsa_show(struct vty *vty, struct ospf6_lsa *lsa, + json_object *json_obj, bool use_json) +{ + struct ospf6_as_external_lsa *external; + char buf[64]; + + assert(lsa->header); + external = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END( + lsa->header); + + /* bits */ + snprintf(buf, sizeof(buf), "%c%c%c", + (CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_E) ? 'E' + : '-'), + (CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_F) ? 'F' + : '-'), + (CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_T) ? 'T' + : '-')); + + if (use_json) { + json_object_string_add(json_obj, "bits", buf); + json_object_int_add(json_obj, "metric", + (unsigned long)OSPF6_ASBR_METRIC(external)); + ospf6_prefix_options_printbuf(external->prefix.prefix_options, + buf, sizeof(buf)); + json_object_string_add(json_obj, "prefixOptions", buf); + json_object_int_add( + json_obj, "referenceLsType", + ntohs(external->prefix.prefix_refer_lstype)); + json_object_string_add(json_obj, "prefix", + ospf6_as_external_lsa_get_prefix_str( + lsa, buf, sizeof(buf), 0)); + + /* Forwarding-Address */ + json_object_boolean_add( + json_obj, "forwardingAddressPresent", + CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_F)); + if (CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_F)) + json_object_string_add( + json_obj, "forwardingAddress", + ospf6_as_external_lsa_get_prefix_str( + lsa, buf, sizeof(buf), 1)); + + /* Tag */ + json_object_boolean_add( + json_obj, "tagPresent", + CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_T)); + if (CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_T)) + json_object_int_add(json_obj, "tag", + ospf6_as_external_lsa_get_tag(lsa)); + } else { + vty_out(vty, " Bits: %s\n", buf); + vty_out(vty, " Metric: %5lu\n", + (unsigned long)OSPF6_ASBR_METRIC(external)); + + ospf6_prefix_options_printbuf(external->prefix.prefix_options, + buf, sizeof(buf)); + vty_out(vty, " Prefix Options: %s\n", buf); + + vty_out(vty, " Referenced LSType: %d\n", + ntohs(external->prefix.prefix_refer_lstype)); + + vty_out(vty, " Prefix: %s\n", + ospf6_as_external_lsa_get_prefix_str(lsa, buf, + sizeof(buf), 0)); + + /* Forwarding-Address */ + if (CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_F)) { + vty_out(vty, " Forwarding-Address: %s\n", + ospf6_as_external_lsa_get_prefix_str( + lsa, buf, sizeof(buf), 1)); + } + + /* Tag */ + if (CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_T)) { + vty_out(vty, " Tag: %" ROUTE_TAG_PRI "\n", + ospf6_as_external_lsa_get_tag(lsa)); + } + } + + return 0; +} + +static void ospf6_asbr_external_route_show(struct vty *vty, + struct ospf6_route *route, + json_object *json_array, + bool use_json) +{ + struct ospf6_external_info *info = route->route_option; + char prefix[PREFIX2STR_BUFFER], id[16], forwarding[64]; + uint32_t tmp_id; + json_object *json_route; + char route_type[2]; + + prefix2str(&route->prefix, prefix, sizeof(prefix)); + tmp_id = ntohl(info->id); + inet_ntop(AF_INET, &tmp_id, id, sizeof(id)); + if (!IN6_IS_ADDR_UNSPECIFIED(&info->forwarding)) + inet_ntop(AF_INET6, &info->forwarding, forwarding, + sizeof(forwarding)); + else + snprintf(forwarding, sizeof(forwarding), ":: (ifindex %d)", + ospf6_route_get_first_nh_index(route)); + + if (use_json) { + json_route = json_object_new_object(); + snprintf(route_type, sizeof(route_type), "%c", + zebra_route_char(info->type)); + json_object_string_add(json_route, "routeType", route_type); + json_object_string_add(json_route, "destination", prefix); + json_object_string_add(json_route, "id", id); + json_object_int_add(json_route, "metricType", + route->path.metric_type); + json_object_int_add( + json_route, "routeCost", + (unsigned long)(route->path.metric_type == 2 + ? route->path.u.cost_e2 + : route->path.cost)); + json_object_string_add(json_route, "forwarding", forwarding); + + json_object_array_add(json_array, json_route); + } else + + vty_out(vty, "%c %-32pFX %-15s type-%d %5lu %s\n", + zebra_route_char(info->type), &route->prefix, id, + route->path.metric_type, + (unsigned long)(route->path.metric_type == 2 + ? route->path.u.cost_e2 + : route->path.cost), + forwarding); +} + +DEFUN(show_ipv6_ospf6_redistribute, show_ipv6_ospf6_redistribute_cmd, + "show ipv6 ospf6 [vrf <NAME|all>] redistribute [json]", + SHOW_STR IP6_STR OSPF6_STR VRF_CMD_HELP_STR + "All VRFs\n" + "redistributing External information\n" JSON_STR) +{ + struct ospf6_route *route; + struct ospf6 *ospf6 = NULL; + json_object *json = NULL; + bool uj = use_json(argc, argv); + struct listnode *node; + const char *vrf_name = NULL; + bool all_vrf = false; + int idx_vrf = 0; + + json_object *json_array_routes = NULL; + json_object *json_array_redistribute = NULL; + + OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + + if (uj) { + json = json_object_new_object(); + json_array_routes = json_object_new_array(); + json_array_redistribute = json_object_new_array(); + } + + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { + if (all_vrf + || ((ospf6->name == NULL && vrf_name == NULL) + || (ospf6->name && vrf_name + && strcmp(ospf6->name, vrf_name) == 0))) { + ospf6_redistribute_show_config( + vty, ospf6, json_array_redistribute, json, uj); + + for (route = ospf6_route_head(ospf6->external_table); + route; route = ospf6_route_next(route)) { + ospf6_asbr_external_route_show( + vty, route, json_array_routes, uj); + } + + if (uj) { + json_object_object_add(json, "routes", + json_array_routes); + vty_json(vty, json); + } + + if (!all_vrf) + break; + } + } + + OSPF6_CMD_CHECK_VRF(uj, all_vrf, ospf6); + + return CMD_SUCCESS; +} + +static struct ospf6_lsa_handler as_external_handler = { + .lh_type = OSPF6_LSTYPE_AS_EXTERNAL, + .lh_name = "AS-External", + .lh_short_name = "ASE", + .lh_show = ospf6_as_external_lsa_show, + .lh_get_prefix_str = ospf6_as_external_lsa_get_prefix_str, + .lh_debug = 0}; + +static struct ospf6_lsa_handler nssa_external_handler = { + .lh_type = OSPF6_LSTYPE_TYPE_7, + .lh_name = "NSSA", + .lh_short_name = "Type7", + .lh_show = ospf6_as_external_lsa_show, + .lh_get_prefix_str = ospf6_as_external_lsa_get_prefix_str, + .lh_debug = 0}; + +void ospf6_asbr_init(void) +{ + ospf6_routemap_init(); + + ospf6_install_lsa_handler(&as_external_handler); + ospf6_install_lsa_handler(&nssa_external_handler); + + install_element(VIEW_NODE, &show_ipv6_ospf6_redistribute_cmd); + + install_element(OSPF6_NODE, &ospf6_default_route_originate_cmd); + install_element(OSPF6_NODE, + &no_ospf6_default_information_originate_cmd); + install_element(OSPF6_NODE, &ospf6_redistribute_cmd); + install_element(OSPF6_NODE, &no_ospf6_redistribute_cmd); +} + +void ospf6_asbr_redistribute_disable(struct ospf6 *ospf6) +{ + int type; + struct ospf6_redist *red; + + for (type = 0; type < ZEBRA_ROUTE_MAX; type++) { + red = ospf6_redist_lookup(ospf6, type, 0); + if (!red) + continue; + if (type == ZEBRA_ROUTE_OSPF6) + continue; + ospf6_asbr_redistribute_unset(ospf6, red, type); + ospf6_redist_del(ospf6, red, type); + } + red = ospf6_redist_lookup(ospf6, DEFAULT_ROUTE, 0); + if (red) { + ospf6_asbr_routemap_unset(red); + ospf6_redist_del(ospf6, red, type); + ospf6_redistribute_default_set(ospf6, DEFAULT_ORIGINATE_NONE); + } +} + +void ospf6_asbr_redistribute_reset(struct ospf6 *ospf6) +{ + int type; + struct ospf6_redist *red; + char buf[RMAP_NAME_MAXLEN]; + + for (type = 0; type <= ZEBRA_ROUTE_MAX; type++) { + buf[0] = '\0'; + if (type == ZEBRA_ROUTE_OSPF6) + continue; + red = ospf6_redist_lookup(ospf6, type, 0); + if (!red) + continue; + + if (type == DEFAULT_ROUTE) { + ospf6_redistribute_default_set( + ospf6, ospf6->default_originate); + continue; + } + if (ROUTEMAP_NAME(red)) + strlcpy(buf, ROUTEMAP_NAME(red), sizeof(buf)); + + ospf6_asbr_redistribute_unset(ospf6, red, type); + if (buf[0]) + ospf6_asbr_routemap_set(red, buf); + ospf6_asbr_redistribute_set(ospf6, type); + } +} + +void ospf6_asbr_terminate(void) +{ + /* Cleanup route maps */ + route_map_finish(); +} + +DEFUN (debug_ospf6_asbr, + debug_ospf6_asbr_cmd, + "debug ospf6 asbr", + DEBUG_STR + OSPF6_STR + "Debug OSPFv3 ASBR function\n" + ) +{ + OSPF6_DEBUG_ASBR_ON(); + return CMD_SUCCESS; +} + +DEFUN (no_debug_ospf6_asbr, + no_debug_ospf6_asbr_cmd, + "no debug ospf6 asbr", + NO_STR + DEBUG_STR + OSPF6_STR + "Debug OSPFv3 ASBR function\n" + ) +{ + OSPF6_DEBUG_ASBR_OFF(); + return CMD_SUCCESS; +} + +int config_write_ospf6_debug_asbr(struct vty *vty) +{ + if (IS_OSPF6_DEBUG_ASBR) + vty_out(vty, "debug ospf6 asbr\n"); + return 0; +} + +static void ospf6_default_originate_write(struct vty *vty, struct ospf6 *o) +{ + struct ospf6_redist *red; + + vty_out(vty, " default-information originate"); + if (o->default_originate == DEFAULT_ORIGINATE_ALWAYS) + vty_out(vty, " always"); + + red = ospf6_redist_lookup(o, DEFAULT_ROUTE, 0); + if (red == NULL) { + vty_out(vty, "\n"); + return; + } + + if (red->dmetric.value >= 0) + vty_out(vty, " metric %d", red->dmetric.value); + + if (red->dmetric.type >= 0) + vty_out(vty, " metric-type %d", red->dmetric.type); + + if (ROUTEMAP_NAME(red)) + vty_out(vty, " route-map %s", ROUTEMAP_NAME(red)); + + vty_out(vty, "\n"); +} + +int ospf6_distribute_config_write(struct vty *vty, struct ospf6 *o) +{ + if (o == NULL) + return 0; + + /* Print default originate configuration. */ + if (o->default_originate != DEFAULT_ORIGINATE_NONE) + ospf6_default_originate_write(vty, o); + + return 0; +} + +void install_element_ospf6_debug_asbr(void) +{ + install_element(ENABLE_NODE, &debug_ospf6_asbr_cmd); + install_element(ENABLE_NODE, &no_debug_ospf6_asbr_cmd); + install_element(CONFIG_NODE, &debug_ospf6_asbr_cmd); + install_element(CONFIG_NODE, &no_debug_ospf6_asbr_cmd); +} + +/* ASBR Summarisation */ +void ospf6_fill_aggr_route_details(struct ospf6 *ospf6, + struct ospf6_external_aggr_rt *aggr) +{ + struct ospf6_route *rt_aggr = aggr->route; + struct ospf6_external_info *ei_aggr = rt_aggr->route_option; + + rt_aggr->prefix = aggr->p; + ei_aggr->tag = aggr->tag; + ei_aggr->type = 0; + ei_aggr->id = aggr->id; + + /* When metric is not configured, apply the default metric */ + rt_aggr->path.cost = ((aggr->metric == -1) ? + DEFAULT_DEFAULT_METRIC + : (unsigned int)(aggr->metric)); + rt_aggr->path.metric_type = aggr->mtype; + + rt_aggr->path.origin.id = htonl(aggr->id); +} + +static void +ospf6_summary_add_aggr_route_and_blackhole(struct ospf6 *ospf6, + struct ospf6_external_aggr_rt *aggr) +{ + struct ospf6_route *rt_aggr; + struct ospf6_route *old_rt = NULL; + struct ospf6_external_info *info; + + /* Check if a route is already present. */ + if (aggr->route) + old_rt = aggr->route; + + /* Create summary route and save it. */ + rt_aggr = ospf6_route_create(ospf6); + rt_aggr->type = OSPF6_DEST_TYPE_NETWORK; + /* Needed to install route while calling zebra api */ + SET_FLAG(rt_aggr->flag, OSPF6_ROUTE_BEST); + + info = XCALLOC(MTYPE_OSPF6_EXTERNAL_INFO, sizeof(*info)); + rt_aggr->route_option = info; + aggr->route = rt_aggr; + + /* Prepare the external_info for aggregator + * Fill all the details which will get advertised + */ + ospf6_fill_aggr_route_details(ospf6, aggr); + + /* Add next-hop to Null interface. */ + ospf6_add_route_nexthop_blackhole(rt_aggr); + + /* Free the old route, if any. */ + if (old_rt) { + ospf6_zebra_route_update_remove(old_rt, ospf6); + + if (old_rt->route_option) + XFREE(MTYPE_OSPF6_EXTERNAL_INFO, old_rt->route_option); + + ospf6_route_delete(old_rt); + } + + ospf6_zebra_route_update_add(rt_aggr, ospf6); +} + +static void ospf6_originate_new_aggr_lsa(struct ospf6 *ospf6, + struct ospf6_external_aggr_rt *aggr) +{ + struct prefix prefix_id; + struct ospf6_lsa *lsa = NULL; + + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: Originate new aggregate route(%pFX)", __func__, + &aggr->p); + + aggr->id = ospf6->external_id++; + + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug( + "Advertise AS-External Id:%pI4 prefix %pFX metric %u", + &prefix_id.u.prefix4, &aggr->p, aggr->metric); + + ospf6_summary_add_aggr_route_and_blackhole(ospf6, aggr); + + /* Originate summary LSA */ + lsa = ospf6_originate_type5_type7_lsas(aggr->route, ospf6); + if (lsa) { + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: Set the origination bit for aggregator", + __func__); + SET_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_ORIGINATED); + } +} + +static void +ospf6_aggr_handle_advertise_change(struct ospf6 *ospf6, + struct ospf6_external_aggr_rt *aggr) +{ + /* Check if advertise option modified. */ + if (CHECK_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE)) { + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: Don't originate the summary address,It is configured to not-advertise.", + __func__); + ospf6_asbr_summary_remove_lsa_and_route(ospf6, aggr); + + return; + } + + /* There are no routes present under this aggregation config, hence + * nothing to originate here + */ + if (OSPF6_EXTERNAL_RT_COUNT(aggr) == 0) { + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: No routes present under this aggregation", + __func__); + return; + } + + if (!CHECK_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_ORIGINATED)) { + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: Now it is advertisable", + __func__); + + ospf6_originate_new_aggr_lsa(ospf6, aggr); + + return; + } +} + +static void +ospf6_originate_summary_lsa(struct ospf6 *ospf6, + struct ospf6_external_aggr_rt *aggr, + struct ospf6_route *rt) +{ + struct ospf6_lsa *lsa = NULL, *aggr_lsa = NULL; + struct ospf6_external_info *info = NULL; + struct ospf6_external_aggr_rt *old_aggr; + struct ospf6_as_external_lsa *external; + struct ospf6_route *rt_aggr = NULL; + route_tag_t tag = 0; + unsigned int metric = 0; + int mtype; + + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: Prepare to originate Summary route(%pFX)", + __func__, &aggr->p); + + /* This case to handle when the overlapping aggregator address + * is available. Best match will be considered.So need to delink + * from old aggregator and link to the new aggr. + */ + if (rt->aggr_route) { + if (rt->aggr_route != aggr) { + old_aggr = rt->aggr_route; + ospf6_unlink_route_from_aggr(ospf6, old_aggr, rt); + } + } + + /* Add the external route to hash table */ + ospf6_link_route_to_aggr(aggr, rt); + + /* The key for ID field is a running number and not prefix */ + info = rt->route_option; + assert(info); + if (info->id) + lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL), + htonl(info->id), ospf6->router_id, + ospf6->lsdb); + + aggr_lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL), + htonl(aggr->id), ospf6->router_id, ospf6->lsdb); + + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: Aggr LSA ID: %d flags %x.", + __func__, aggr->id, aggr->aggrflags); + /* Don't originate external LSA, + * If it is configured not to advertise. + */ + if (CHECK_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE)) { + /* If it is already originated as external LSA, + * But, it is configured not to advertise then + * flush the originated external lsa. + */ + if (lsa) { + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: Purge the external LSA %s.", + __func__, lsa->name); + ospf6_external_lsa_purge(ospf6, lsa); + info->id = 0; + rt->path.origin.id = 0; + } + + if (aggr_lsa) { + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: Purge the aggr external LSA %s.", + __func__, lsa->name); + ospf6_asbr_summary_remove_lsa_and_route(ospf6, aggr); + } + + UNSET_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_ORIGINATED); + + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: Don't originate the summary address,It is configured to not-advertise.", + __func__); + return; + } + + /* Summary route already originated, + * So, Do nothing. + */ + if (CHECK_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_ORIGINATED)) { + if (!aggr_lsa) { + zlog_warn( + "%s: Could not refresh/originate %pFX", + __func__, + &aggr->p); + /* Remove the assert later */ + assert(aggr_lsa); + return; + } + + external = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END + (aggr_lsa->header); + metric = (unsigned long)OSPF6_ASBR_METRIC(external); + tag = ospf6_as_external_lsa_get_tag(aggr_lsa); + mtype = CHECK_FLAG(external->bits_metric, + OSPF6_ASBR_BIT_E) ? 2 : 1; + + /* Prepare the external_info for aggregator */ + ospf6_fill_aggr_route_details(ospf6, aggr); + rt_aggr = aggr->route; + /* If tag/metric/metric-type modified , then re-originate the + * route with modified tag/metric/metric-type details. + */ + if ((tag != aggr->tag) + || (metric != (unsigned int)rt_aggr->path.cost) + || (mtype != aggr->mtype)) { + + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug( + "%s: Routetag(old:%d new:%d)/Metric(o:%u,n:%u)/mtype(o:%d n:%d) modified,So refresh the summary route.(%pFX)", + __func__, tag, aggr->tag, + metric, + aggr->metric, + mtype, aggr->mtype, + &aggr->p); + + aggr_lsa = ospf6_originate_type5_type7_lsas(aggr->route, + ospf6); + if (aggr_lsa) + SET_FLAG(aggr->aggrflags, + OSPF6_EXTERNAL_AGGRT_ORIGINATED); + } + + return; + } + + /* If the external route prefix same as aggregate route + * and if external route is already originated as TYPE-5 + * then just update the aggr info and remove the route info + */ + if (lsa && prefix_same(&aggr->p, &rt->prefix)) { + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug( + "%s: Route prefix is same as aggr so no need to re-originate LSA(%pFX)", + __PRETTY_FUNCTION__, &aggr->p); + + aggr->id = info->id; + info->id = 0; + rt->path.origin.id = 0; + + ospf6_summary_add_aggr_route_and_blackhole(ospf6, aggr); + + SET_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_ORIGINATED); + + return; + } + + ospf6_originate_new_aggr_lsa(ospf6, aggr); +} + +static void ospf6_aggr_handle_external_info(void *data) +{ + struct ospf6_route *rt = (struct ospf6_route *)data; + struct ospf6_external_aggr_rt *aggr = NULL; + struct ospf6_lsa *lsa = NULL; + struct ospf6_external_info *info; + struct ospf6 *ospf6 = NULL; + + rt->aggr_route = NULL; + + rt->to_be_processed = true; + + if (IS_OSPF6_DEBUG_ASBR || IS_OSPF6_DEBUG_ORIGINATE(AS_EXTERNAL)) + zlog_debug("%s: Handle external route for origination/refresh (%pFX)", + __func__, + &rt->prefix); + + ospf6 = rt->ospf6; + assert(ospf6); + + aggr = ospf6_external_aggr_match(ospf6, + &rt->prefix); + if (aggr) { + ospf6_originate_summary_lsa(ospf6, aggr, rt); + return; + } + + info = rt->route_option; + if (info->id) { + lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL), + htonl(info->id), ospf6->router_id, + ospf6->lsdb); + if (lsa) { + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: LSA found, refresh it", + __func__); + EVENT_OFF(lsa->refresh); + event_add_event(master, ospf6_lsa_refresh, lsa, 0, + &lsa->refresh); + return; + } + } + + info->id = ospf6->external_id++; + rt->path.origin.id = htonl(info->id); + + (void)ospf6_originate_type5_type7_lsas(rt, ospf6); +} + +void ospf6_asbr_summary_config_delete(struct ospf6 *ospf6, + struct route_node *rn) +{ + struct ospf6_external_aggr_rt *aggr = rn->info; + + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: Deleting Aggregate route (%pFX)", + __func__, + &aggr->p); + + ospf6_asbr_summary_remove_lsa_and_route(ospf6, aggr); + + rn->info = NULL; + route_unlock_node(rn); +} + +static int +ospf6_handle_external_aggr_modify(struct ospf6 *ospf6, + struct ospf6_external_aggr_rt *aggr) +{ + struct ospf6_lsa *lsa = NULL; + struct ospf6_as_external_lsa *asel = NULL; + struct ospf6_route *rt_aggr; + unsigned int metric = 0; + route_tag_t tag = 0; + int mtype; + + lsa = ospf6_lsdb_lookup( + htons(OSPF6_LSTYPE_AS_EXTERNAL), + htonl(aggr->id), ospf6->router_id, + ospf6->lsdb); + if (!lsa) { + zlog_warn( + "%s: Could not refresh/originate %pFX", + __func__, + &aggr->p); + + return OSPF6_FAILURE; + } + + asel = (struct ospf6_as_external_lsa *) + OSPF6_LSA_HEADER_END(lsa->header); + metric = (unsigned long)OSPF6_ASBR_METRIC(asel); + tag = ospf6_as_external_lsa_get_tag(lsa); + mtype = CHECK_FLAG(asel->bits_metric, + OSPF6_ASBR_BIT_E) ? 2 : 1; + + /* Fill all the details for advertisement */ + ospf6_fill_aggr_route_details(ospf6, aggr); + rt_aggr = aggr->route; + /* If tag/metric/metric-type modified , then + * re-originate the route with modified + * tag/metric/metric-type details. + */ + if ((tag != aggr->tag) + || (metric + != (unsigned int)rt_aggr->path.cost) + || (mtype + != aggr->mtype)) { + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug( + "%s: Changed tag(old:%d new:%d)/metric(o:%u n:%d)/mtype(o:%d n:%d),So refresh the summary route.(%pFX)", + __func__, tag, + aggr->tag, + metric, + (unsigned int)rt_aggr->path.cost, + mtype, aggr->mtype, + &aggr->p); + + (void)ospf6_originate_type5_type7_lsas( + aggr->route, + ospf6); + } + + return OSPF6_SUCCESS; +} + +static void ospf6_handle_external_aggr_update(struct ospf6 *ospf6) +{ + struct route_node *rn = NULL; + int ret; + + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: Process modified aggregators.", __func__); + + for (rn = route_top(ospf6->rt_aggr_tbl); rn; rn = route_next(rn)) { + struct ospf6_external_aggr_rt *aggr; + + if (!rn->info) + continue; + + aggr = rn->info; + + if (aggr->action == OSPF6_ROUTE_AGGR_DEL) { + aggr->action = OSPF6_ROUTE_AGGR_NONE; + ospf6_asbr_summary_config_delete(ospf6, rn); + + hash_clean_and_free(&aggr->match_extnl_hash, + ospf6_aggr_handle_external_info); + + XFREE(MTYPE_OSPF6_EXTERNAL_RT_AGGR, aggr); + + } else if (aggr->action == OSPF6_ROUTE_AGGR_MODIFY) { + + aggr->action = OSPF6_ROUTE_AGGR_NONE; + + /* Check if tag/metric/metric-type modified */ + if (CHECK_FLAG(aggr->aggrflags, + OSPF6_EXTERNAL_AGGRT_ORIGINATED) + && !CHECK_FLAG(aggr->aggrflags, + OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE)) { + + ret = ospf6_handle_external_aggr_modify(ospf6, + aggr); + if (ret == OSPF6_FAILURE) + continue; + } + + /* Advertise option modified ? + * If so, handled it here. + */ + ospf6_aggr_handle_advertise_change(ospf6, aggr); + } + } +} + +static void ospf6_aggr_unlink_external_info(void *data) +{ + struct ospf6_route *rt = (struct ospf6_route *)data; + + rt->aggr_route = NULL; + + rt->to_be_processed = true; +} + +void ospf6_external_aggregator_free(struct ospf6_external_aggr_rt *aggr) +{ + hash_clean_and_free(&aggr->match_extnl_hash, + ospf6_aggr_unlink_external_info); + + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: Release the aggregator Address(%pFX)", + __func__, + &aggr->p); + + XFREE(MTYPE_OSPF6_EXTERNAL_RT_AGGR, aggr); +} + +static void +ospf6_delete_all_marked_aggregators(struct ospf6 *ospf6) +{ + struct route_node *rn = NULL; + struct ospf6_external_aggr_rt *aggr; + + /* Loop through all the aggregators, Delete all aggregators + * which are marked as DELETE. Set action to NONE for remaining + * aggregators + */ + for (rn = route_top(ospf6->rt_aggr_tbl); rn; rn = route_next(rn)) { + if (!rn->info) + continue; + + aggr = rn->info; + + if (aggr->action != OSPF6_ROUTE_AGGR_DEL) { + aggr->action = OSPF6_ROUTE_AGGR_NONE; + continue; + } + ospf6_asbr_summary_config_delete(ospf6, rn); + ospf6_external_aggregator_free(aggr); + } +} + +static void ospf6_handle_exnl_rt_after_aggr_del(struct ospf6 *ospf6, + struct ospf6_route *rt) +{ + struct ospf6_lsa *lsa; + + /* Process only marked external routes. + * These routes were part of a deleted + * aggregator.So, originate now. + */ + if (!rt->to_be_processed) + return; + + rt->to_be_processed = false; + + lsa = ospf6_find_external_lsa(ospf6, &rt->prefix); + + if (lsa) { + EVENT_OFF(lsa->refresh); + event_add_event(master, ospf6_lsa_refresh, lsa, 0, + &lsa->refresh); + } else { + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: Originate external route(%pFX)", + __func__, + &rt->prefix); + + (void)ospf6_originate_type5_type7_lsas(rt, ospf6); + } +} + +static void ospf6_handle_aggregated_exnl_rt(struct ospf6 *ospf6, + struct ospf6_external_aggr_rt *aggr, + struct ospf6_route *rt) +{ + struct ospf6_lsa *lsa; + struct ospf6_as_external_lsa *ext_lsa; + struct ospf6_external_info *info; + + /* Handling the case where the external route prefix + * and aggegate prefix is same + * If same don't flush the originated external LSA. + */ + if (prefix_same(&aggr->p, &rt->prefix)) { + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: External Route prefix same as Aggregator(%pFX), so don't flush.", + __func__, + &rt->prefix); + + return; + } + + info = rt->route_option; + assert(info); + + lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL), + htonl(info->id), ospf6->router_id, ospf6->lsdb); + if (lsa) { + ext_lsa = (struct ospf6_as_external_lsa + *)((char *)(lsa->header) + + sizeof(struct ospf6_lsa_header)); + + if (rt->prefix.prefixlen != ext_lsa->prefix.prefix_length) + return; + + ospf6_external_lsa_purge(ospf6, lsa); + + /* Resetting the ID of route */ + rt->path.origin.id = 0; + info->id = 0; + } +} + +static void +ospf6_handle_external_aggr_add(struct ospf6 *ospf6) +{ + struct ospf6_route *rt = NULL; + struct ospf6_external_info *ei = NULL; + struct ospf6_external_aggr_rt *aggr; + + /* Delete all the aggregators which are marked as + * OSPF6_ROUTE_AGGR_DEL. + */ + ospf6_delete_all_marked_aggregators(ospf6); + + for (rt = ospf6_route_head(ospf6->external_table); rt; + rt = ospf6_route_next(rt)) { + ei = rt->route_option; + if (ei == NULL) + continue; + + if (is_default_prefix(&rt->prefix)) + continue; + + aggr = ospf6_external_aggr_match(ospf6, + &rt->prefix); + + /* If matching aggregator found, Add + * the external route refrenace to the + * aggregator and originate the aggr + * route if it is advertisable. + * flush the external LSA if it is + * already originated for this external + * prefix. + */ + if (aggr) { + ospf6_originate_summary_lsa(ospf6, aggr, rt); + + /* All aggregated external rts + * are handled here. + */ + ospf6_handle_aggregated_exnl_rt( + ospf6, aggr, rt); + continue; + } + + /* External routes which are only out + * of aggregation will be handled here. + */ + ospf6_handle_exnl_rt_after_aggr_del( + ospf6, rt); + } +} + +static void ospf6_asbr_summary_process(struct event *thread) +{ + struct ospf6 *ospf6 = EVENT_ARG(thread); + int operation = 0; + + operation = ospf6->aggr_action; + + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: operation:%d", + __func__, + operation); + + switch (operation) { + case OSPF6_ROUTE_AGGR_ADD: + ospf6_handle_external_aggr_add(ospf6); + break; + case OSPF6_ROUTE_AGGR_DEL: + case OSPF6_ROUTE_AGGR_MODIFY: + ospf6_handle_external_aggr_update(ospf6); + break; + default: + break; + } +} + +static void +ospf6_start_asbr_summary_delay_timer(struct ospf6 *ospf6, + struct ospf6_external_aggr_rt *aggr, + ospf6_aggr_action_t operation) +{ + aggr->action = operation; + + if (event_is_scheduled(ospf6->t_external_aggr)) { + if (ospf6->aggr_action == OSPF6_ROUTE_AGGR_ADD) { + + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: Not required to restart timer,set is already added.", + __func__); + return; + } + + if (operation == OSPF6_ROUTE_AGGR_ADD) { + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s, Restarting Aggregator delay timer.", + __func__); + EVENT_OFF(ospf6->t_external_aggr); + } + } + + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: Start Aggregator delay timer %u(in seconds).", + __func__, ospf6->aggr_delay_interval); + + ospf6->aggr_action = operation; + event_add_timer(master, ospf6_asbr_summary_process, ospf6, + ospf6->aggr_delay_interval, &ospf6->t_external_aggr); +} + +int ospf6_asbr_external_rt_advertise(struct ospf6 *ospf6, + struct prefix *p) +{ + struct route_node *rn; + struct ospf6_external_aggr_rt *aggr; + + rn = route_node_lookup(ospf6->rt_aggr_tbl, p); + if (!rn) + return OSPF6_INVALID; + + aggr = rn->info; + + route_unlock_node(rn); + + if (!CHECK_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE)) + return OSPF6_INVALID; + + UNSET_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE); + + if (!OSPF6_EXTERNAL_RT_COUNT(aggr)) + return OSPF6_SUCCESS; + + ospf6_start_asbr_summary_delay_timer(ospf6, aggr, + OSPF6_ROUTE_AGGR_MODIFY); + + return OSPF6_SUCCESS; +} + +int ospf6_external_aggr_delay_timer_set(struct ospf6 *ospf6, uint16_t interval) +{ + ospf6->aggr_delay_interval = interval; + + return OSPF6_SUCCESS; +} + +static unsigned int ospf6_external_rt_hash_key(const void *data) +{ + const struct ospf6_route *rt = data; + unsigned int key = 0; + + key = prefix_hash_key(&rt->prefix); + return key; +} + +static bool ospf6_external_rt_hash_cmp(const void *d1, const void *d2) +{ + const struct ospf6_route *rt1 = d1; + const struct ospf6_route *rt2 = d2; + + return prefix_same(&rt1->prefix, &rt2->prefix); +} + +static struct ospf6_external_aggr_rt * +ospf6_external_aggr_new(struct prefix *p) +{ + struct ospf6_external_aggr_rt *aggr; + + aggr = XCALLOC(MTYPE_OSPF6_EXTERNAL_RT_AGGR, + sizeof(struct ospf6_external_aggr_rt)); + + prefix_copy(&aggr->p, p); + aggr->metric = -1; + aggr->mtype = DEFAULT_METRIC_TYPE; + aggr->match_extnl_hash = hash_create(ospf6_external_rt_hash_key, + ospf6_external_rt_hash_cmp, + "Ospf6 external route hash"); + return aggr; +} + +static void ospf6_external_aggr_add(struct ospf6 *ospf6, + struct ospf6_external_aggr_rt *aggr) +{ + struct route_node *rn; + + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: Adding Aggregate route to Aggr table (%pFX)", + __func__, + &aggr->p); + + rn = route_node_get(ospf6->rt_aggr_tbl, &aggr->p); + if (rn->info) + route_unlock_node(rn); + else + rn->info = aggr; +} + +int ospf6_asbr_external_rt_no_advertise(struct ospf6 *ospf6, + struct prefix *p) +{ + struct ospf6_external_aggr_rt *aggr; + route_tag_t tag = 0; + + aggr = ospf6_external_aggr_config_lookup(ospf6, p); + if (aggr) { + if (CHECK_FLAG(aggr->aggrflags, + OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE)) + return OSPF6_SUCCESS; + + SET_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE); + + aggr->tag = tag; + aggr->metric = -1; + + if (!OSPF6_EXTERNAL_RT_COUNT(aggr)) + return OSPF6_SUCCESS; + + ospf6_start_asbr_summary_delay_timer(ospf6, aggr, + OSPF6_ROUTE_AGGR_MODIFY); + } else { + aggr = ospf6_external_aggr_new(p); + + if (!aggr) + return OSPF6_FAILURE; + + SET_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE); + ospf6_external_aggr_add(ospf6, aggr); + ospf6_start_asbr_summary_delay_timer(ospf6, aggr, + OSPF6_ROUTE_AGGR_ADD); + } + + return OSPF6_SUCCESS; +} + +struct ospf6_external_aggr_rt * +ospf6_external_aggr_config_lookup(struct ospf6 *ospf6, struct prefix *p) +{ + struct route_node *rn; + + rn = route_node_lookup(ospf6->rt_aggr_tbl, p); + if (rn) { + route_unlock_node(rn); + return rn->info; + } + + return NULL; +} + + +int ospf6_external_aggr_config_set(struct ospf6 *ospf6, struct prefix *p, + route_tag_t tag, int metric, int mtype) +{ + struct ospf6_external_aggr_rt *aggregator; + + aggregator = ospf6_external_aggr_config_lookup(ospf6, p); + + if (aggregator) { + if (CHECK_FLAG(aggregator->aggrflags, + OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE)) + UNSET_FLAG(aggregator->aggrflags, + OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE); + else if ((aggregator->tag == tag) + && (aggregator->metric == metric) + && (aggregator->mtype == mtype)) + return OSPF6_SUCCESS; + + aggregator->tag = tag; + aggregator->metric = metric; + aggregator->mtype = mtype; + + ospf6_start_asbr_summary_delay_timer(ospf6, aggregator, + OSPF6_ROUTE_AGGR_MODIFY); + } else { + aggregator = ospf6_external_aggr_new(p); + if (!aggregator) + return OSPF6_FAILURE; + + aggregator->tag = tag; + aggregator->metric = metric; + aggregator->mtype = mtype; + + ospf6_external_aggr_add(ospf6, aggregator); + ospf6_start_asbr_summary_delay_timer(ospf6, aggregator, + OSPF6_ROUTE_AGGR_ADD); + } + + return OSPF6_SUCCESS; +} + +int ospf6_external_aggr_config_unset(struct ospf6 *ospf6, + struct prefix *p) +{ + struct route_node *rn; + struct ospf6_external_aggr_rt *aggr; + + rn = route_node_lookup(ospf6->rt_aggr_tbl, p); + if (!rn) + return OSPF6_INVALID; + + aggr = rn->info; + + route_unlock_node(rn); + + if (!OSPF6_EXTERNAL_RT_COUNT(aggr)) { + ospf6_asbr_summary_config_delete(ospf6, rn); + ospf6_external_aggregator_free(aggr); + return OSPF6_SUCCESS; + } + + ospf6_start_asbr_summary_delay_timer(ospf6, aggr, + OSPF6_ROUTE_AGGR_DEL); + + return OSPF6_SUCCESS; +} + +void ospf6_handle_external_lsa_origination(struct ospf6 *ospf6, + struct ospf6_route *rt, + struct prefix *p) +{ + + struct ospf6_external_aggr_rt *aggr; + struct ospf6_external_info *info; + struct prefix prefix_id; + + if (!is_default_prefix(p)) { + aggr = ospf6_external_aggr_match(ospf6, + p); + + if (aggr) { + + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("%s: Send Aggregate LSA (%pFX)", + __func__, + &aggr->p); + + ospf6_originate_summary_lsa( + ospf6, aggr, rt); + + /* Handling the case where the + * external route prefix + * and aggegate prefix is same + * If same don't flush the + * originated + * external LSA. + */ + ospf6_handle_aggregated_exnl_rt( + ospf6, aggr, rt); + return; + } + } + + info = rt->route_option; + + /* When the info->id = 0, it means it is being originated for the + * first time. + */ + if (!info->id) { + info->id = ospf6->external_id++; + } else { + prefix_id.family = AF_INET; + prefix_id.prefixlen = 32; + prefix_id.u.prefix4.s_addr = htonl(info->id); + } + + rt->path.origin.id = htonl(info->id); + + if (IS_OSPF6_DEBUG_ASBR) { + zlog_debug("Advertise new AS-External Id:%pI4 prefix %pFX metric %u", + &prefix_id.u.prefix4, p, rt->path.metric_type); + } + + ospf6_originate_type5_type7_lsas(rt, ospf6); + +} + +void ospf6_unset_all_aggr_flag(struct ospf6 *ospf6) +{ + struct route_node *rn = NULL; + struct ospf6_external_aggr_rt *aggr; + + if (IS_OSPF6_DEBUG_AGGR) + zlog_debug("Unset the origination bit for all aggregator"); + + /* Resetting the running external ID counter so that the origination + * of external LSAs starts from the beginning 0.0.0.1 + */ + ospf6->external_id = OSPF6_EXT_INIT_LS_ID; + + for (rn = route_top(ospf6->rt_aggr_tbl); rn; rn = route_next(rn)) { + if (!rn->info) + continue; + + aggr = rn->info; + + UNSET_FLAG(aggr->aggrflags, OSPF6_EXTERNAL_AGGRT_ORIGINATED); + } +} diff --git a/ospf6d/ospf6_asbr.h b/ospf6d/ospf6_asbr.h new file mode 100644 index 0000000..d63e467 --- /dev/null +++ b/ospf6d/ospf6_asbr.h @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2001 Yasuhiro Ohara + */ + +#ifndef OSPF6_ASBR_H +#define OSPF6_ASBR_H + +/* for struct ospf6_prefix */ +#include "ospf6_proto.h" +/* for struct ospf6_lsa */ +#include "ospf6_lsa.h" +/* for struct ospf6_route */ +#include "ospf6_route.h" + +/* Debug option */ +extern unsigned char conf_debug_ospf6_asbr; +#define OSPF6_DEBUG_ASBR_ON() (conf_debug_ospf6_asbr = 1) +#define OSPF6_DEBUG_ASBR_OFF() (conf_debug_ospf6_asbr = 0) +#define IS_OSPF6_DEBUG_ASBR (conf_debug_ospf6_asbr) + +struct ospf6_external_info { + /* External route type */ + int type; + + /* Originating Link State ID */ + uint32_t id; + + struct in6_addr forwarding; + + route_tag_t tag; + + ifindex_t ifindex; + +}; + +/* OSPF6 ASBR Summarisation */ +typedef enum { + OSPF6_ROUTE_AGGR_NONE = 0, + OSPF6_ROUTE_AGGR_ADD, + OSPF6_ROUTE_AGGR_DEL, + OSPF6_ROUTE_AGGR_MODIFY +} ospf6_aggr_action_t; + +#define OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE 0x1 +#define OSPF6_EXTERNAL_AGGRT_ORIGINATED 0x2 + +#define OSPF6_EXTERNAL_RT_COUNT(aggr) \ + (((struct ospf6_external_aggr_rt *)aggr)->match_extnl_hash->count) + +struct ospf6_external_aggr_rt { + /* range address and masklen */ + struct prefix p; + + /* use bits for OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE and + * OSPF6_EXTERNAL_AGGRT_ORIGINATED + */ + uint16_t aggrflags; + + /* To store external metric-type */ + uint8_t mtype; + + /* Route tag for summary address */ + route_tag_t tag; + + /* To store aggregated metric config */ + int metric; + + /* To Store the LS ID when LSA is originated */ + uint32_t id; + + /* Action to be done after delay timer expiry */ + int action; + + /* OSPFv3 route generated by summary address. */ + struct ospf6_route *route; + + /* Hash table of matching external routes */ + struct hash *match_extnl_hash; +}; + +/* AS-External-LSA */ +#define OSPF6_AS_EXTERNAL_LSA_MIN_SIZE 4U /* w/o IPv6 prefix */ +struct ospf6_as_external_lsa { + uint32_t bits_metric; + + struct ospf6_prefix prefix; + /* followed by none or one forwarding address */ + /* followed by none or one external route tag */ + /* followed by none or one referenced LS-ID */ +}; + +#define OSPF6_ASBR_BIT_T ntohl (0x01000000) +#define OSPF6_ASBR_BIT_F ntohl (0x02000000) +#define OSPF6_ASBR_BIT_E ntohl (0x04000000) + +#define OSPF6_ASBR_METRIC(E) (ntohl ((E)->bits_metric & htonl (0x00ffffff))) +#define OSPF6_ASBR_METRIC_SET(E, C) \ + { \ + (E)->bits_metric &= htonl(0xff000000); \ + (E)->bits_metric |= htonl(0x00ffffff) & htonl(C); \ + } + +extern void ospf6_asbr_lsa_add(struct ospf6_lsa *lsa); + +extern void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa, + struct ospf6_route *asbr_entry); +extern void ospf6_asbr_lsentry_add(struct ospf6_route *asbr_entry, + struct ospf6 *ospf6); +extern void ospf6_asbr_lsentry_remove(struct ospf6_route *asbr_entry, + struct ospf6 *ospf6); + +extern int ospf6_asbr_is_asbr(struct ospf6 *o); +extern void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex, + struct prefix *prefix, + unsigned int nexthop_num, + const struct in6_addr *nexthop, + route_tag_t tag, struct ospf6 *ospf6); +extern void ospf6_asbr_redistribute_remove(int type, ifindex_t ifindex, + struct prefix *prefix, + struct ospf6 *ospf6); + +extern int ospf6_redistribute_config_write(struct vty *vty, + struct ospf6 *ospf6); + +extern void ospf6_asbr_init(void); +extern void ospf6_asbr_redistribute_disable(struct ospf6 *ospf6); +extern void ospf6_asbr_redistribute_reset(struct ospf6 *ospf6); +extern void ospf6_asbr_terminate(void); +extern void ospf6_asbr_send_externals_to_area(struct ospf6_area *); +extern void ospf6_asbr_remove_externals_from_area(struct ospf6_area *oa); + +extern int config_write_ospf6_debug_asbr(struct vty *vty); +extern int ospf6_distribute_config_write(struct vty *vty, struct ospf6 *ospf6); +extern void install_element_ospf6_debug_asbr(void); +extern void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old, + struct ospf6_route *route, + struct ospf6 *ospf6); +extern void ospf6_asbr_distribute_list_update(struct ospf6 *ospf6, + struct ospf6_redist *red); +struct ospf6_redist *ospf6_redist_lookup(struct ospf6 *ospf6, int type, + unsigned short instance); +extern void ospf6_asbr_routemap_update(const char *mapname); +extern struct ospf6_lsa * +ospf6_as_external_lsa_originate(struct ospf6_route *route, + struct ospf6 *ospf6); +extern void ospf6_asbr_status_update(struct ospf6 *ospf6, int status); + +int ospf6_asbr_external_rt_advertise(struct ospf6 *ospf6, + struct prefix *p); +int ospf6_external_aggr_delay_timer_set(struct ospf6 *ospf6, uint16_t interval); +int ospf6_asbr_external_rt_no_advertise(struct ospf6 *ospf6, + struct prefix *p); + +struct ospf6_external_aggr_rt * +ospf6_external_aggr_config_lookup(struct ospf6 *ospf6, struct prefix *p); + +int ospf6_external_aggr_config_set(struct ospf6 *ospf6, struct prefix *p, + route_tag_t tag, int metric, int mtype); + +int ospf6_external_aggr_config_unset(struct ospf6 *ospf6, + struct prefix *p); +void ospf6_handle_external_lsa_origination(struct ospf6 *ospf6, + struct ospf6_route *rt, + struct prefix *p); +void ospf6_external_aggregator_free(struct ospf6_external_aggr_rt *aggr); +void ospf6_unset_all_aggr_flag(struct ospf6 *ospf6); +void ospf6_fill_aggr_route_details(struct ospf6 *ospf6, + struct ospf6_external_aggr_rt *aggr); +void ospf6_asbr_summary_config_delete(struct ospf6 *ospf6, + struct route_node *rn); +#endif /* OSPF6_ASBR_H */ diff --git a/ospf6d/ospf6_auth_trailer.c b/ospf6d/ospf6_auth_trailer.c new file mode 100644 index 0000000..10e0092 --- /dev/null +++ b/ospf6d/ospf6_auth_trailer.c @@ -0,0 +1,988 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2021 Abhinay Ramesh + */ + +#include "zebra.h" +#include "config.h" +#include "memory.h" +#include "ospf6d.h" +#include "vty.h" +#include "command.h" +#include "md5.h" +#include "sha256.h" +#include "lib/zlog.h" +#include "ospf6_message.h" +#include "ospf6_interface.h" +#include "ospf6_neighbor.h" +#include "ospf6_proto.h" +#include "ospf6_top.h" +#include "ospf6_area.h" +#include "ospf6_auth_trailer.h" +#include "ospf6_route.h" +#include "ospf6_zebra.h" +#include "lib/keychain.h" + +unsigned char conf_debug_ospf6_auth[2]; +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_AUTH_HASH_XOR, "OSPF6 auth hash xor"); + +/*Apad is the hexadecimal value 0x878FE1F3. */ +const uint8_t ospf6_hash_apad_max[KEYCHAIN_MAX_HASH_SIZE] = { + 0x87, 0x8f, 0xe1, 0xf3, 0x87, 0x8f, 0xe1, 0xf3, 0x87, 0x8f, 0xe1, + 0xf3, 0x87, 0x8f, 0xe1, 0xf3, 0x87, 0x8f, 0xe1, 0xf3, 0x87, 0x8f, + 0xe1, 0xf3, 0x87, 0x8f, 0xe1, 0xf3, 0x87, 0x8f, 0xe1, 0xf3, 0x87, + 0x8f, 0xe1, 0xf3, 0x87, 0x8f, 0xe1, 0xf3, 0x87, 0x8f, 0xe1, 0xf3, + 0x87, 0x8f, 0xe1, 0xf3, 0x87, 0x8f, 0xe1, 0xf3, 0x87, 0x8f, 0xe1, + 0xf3, 0x87, 0x8f, 0xe1, 0xf3, 0x87, 0x8f, 0xe1, 0xf3, +}; + +const uint8_t ospf6_hash_ipad_max[KEYCHAIN_ALGO_MAX_INTERNAL_BLK_SIZE] = { + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, +}; + +const uint8_t ospf6_hash_opad_max[KEYCHAIN_ALGO_MAX_INTERNAL_BLK_SIZE] = { + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, +}; + +void ospf6_auth_hdr_dump_send(struct ospf6_header *ospfh, uint16_t length) +{ + struct ospf6_auth_hdr *ospf6_at_hdr; + uint16_t at_len, oh_len, at_hdr_len, hash_len; + unsigned char temp[KEYCHAIN_MAX_HASH_SIZE + 1]; + + oh_len = htons(ospfh->length); + at_len = length - oh_len; + if (at_len > 0) { + ospf6_at_hdr = (struct ospf6_auth_hdr *) + ((uint8_t *)ospfh + oh_len); + at_hdr_len = htons(ospf6_at_hdr->length); + hash_len = at_hdr_len - OSPF6_AUTH_HDR_MIN_SIZE; + memcpy(temp, ospf6_at_hdr->data, hash_len); + temp[hash_len] = '\0'; + zlog_debug("OSPF6 Authentication Trailer"); + zlog_debug(" Type %d", htons(ospf6_at_hdr->type)); + zlog_debug(" Length %d", at_hdr_len); + zlog_debug(" Reserved %d", ospf6_at_hdr->reserved); + zlog_debug(" SA ID %d", htons(ospf6_at_hdr->id)); + zlog_debug(" seqnum high 0x%08x", + htonl(ospf6_at_hdr->seqnum_h)); + zlog_debug(" seqnum high 0x%08x", + htonl(ospf6_at_hdr->seqnum_l)); + zlog_debug(" Data %s", temp); + } +} + +void ospf6_auth_hdr_dump_recv(struct ospf6_header *ospfh, uint16_t length, + unsigned int lls_len) +{ + struct ospf6_auth_hdr *ospf6_at_hdr; + uint16_t at_len, oh_len, at_hdr_len, hash_len; + unsigned char temp[KEYCHAIN_MAX_HASH_SIZE + 1]; + + oh_len = ntohs(ospfh->length); + at_len = length - (oh_len + lls_len); + if (at_len > 0) { + ospf6_at_hdr = + (struct ospf6_auth_hdr *)((uint8_t *)ospfh + oh_len); + at_hdr_len = ntohs(ospf6_at_hdr->length); + hash_len = at_hdr_len - (uint16_t)OSPF6_AUTH_HDR_MIN_SIZE; + if (hash_len > KEYCHAIN_MAX_HASH_SIZE) { + zlog_debug( + "Specified value for hash_len %u is greater than expected %u", + hash_len, KEYCHAIN_MAX_HASH_SIZE); + return; + } + memcpy(temp, ospf6_at_hdr->data, hash_len); + temp[hash_len] = '\0'; + zlog_debug("OSPF6 Authentication Trailer"); + zlog_debug(" Type %d", ntohs(ospf6_at_hdr->type)); + zlog_debug(" Length %d", at_hdr_len); + zlog_debug(" Reserved %d", ospf6_at_hdr->reserved); + zlog_debug(" SA ID %d", ntohs(ospf6_at_hdr->id)); + zlog_debug(" seqnum high 0x%08x", + ntohl(ospf6_at_hdr->seqnum_h)); + zlog_debug(" seqnum high 0x%08x", + ntohl(ospf6_at_hdr->seqnum_l)); + zlog_debug(" Data %s", temp); + } +} + +unsigned char *ospf6_hash_message_xor(unsigned char *mes1, + unsigned char *mes2, + uint32_t len) +{ + unsigned char *result; + uint32_t i; + + result = XCALLOC(MTYPE_OSPF6_AUTH_HASH_XOR, len); + + for (i = 0; i < len; i++) + result[i] = mes1[i] ^ mes2[i]; + + return result; +} + +static void md5_digest(unsigned char *mes, uint32_t len, + unsigned char *digest) +{ +#ifdef CRYPTO_OPENSSL + unsigned int size = KEYCHAIN_MD5_HASH_SIZE; + EVP_MD_CTX *ctx; +#elif CRYPTO_INTERNAL + MD5_CTX ctx; +#endif + +#ifdef CRYPTO_OPENSSL + ctx = EVP_MD_CTX_new(); + EVP_DigestInit(ctx, EVP_md5()); + EVP_DigestUpdate(ctx, mes, len); + EVP_DigestFinal(ctx, digest, &size); + EVP_MD_CTX_free(ctx); +#elif CRYPTO_INTERNAL + memset(&ctx, 0, sizeof(ctx)); + MD5Init(&ctx); + MD5Update(&ctx, mes, len); + MD5Final(digest, &ctx); +#endif +} + +static void sha256_digest(unsigned char *mes, uint32_t len, + unsigned char *digest) +{ +#ifdef CRYPTO_OPENSSL + unsigned int size = KEYCHAIN_HMAC_SHA256_HASH_SIZE; + EVP_MD_CTX *ctx; +#elif CRYPTO_INTERNAL + SHA256_CTX ctx; +#endif + +#ifdef CRYPTO_OPENSSL + ctx = EVP_MD_CTX_new(); + EVP_DigestInit(ctx, EVP_sha256()); + EVP_DigestUpdate(ctx, mes, len); + EVP_DigestFinal(ctx, digest, &size); + EVP_MD_CTX_free(ctx); +#elif CRYPTO_INTERNAL + memset(&ctx, 0, sizeof(ctx)); + SHA256_Init(&ctx); + SHA256_Update(&ctx, mes, len); + SHA256_Final(digest, &ctx); +#endif +} + +#ifdef CRYPTO_OPENSSL +static void sha1_digest(unsigned char *mes, uint32_t len, + unsigned char *digest) +{ + EVP_MD_CTX *ctx; + unsigned int size = KEYCHAIN_HMAC_SHA1_HASH_SIZE; + + ctx = EVP_MD_CTX_new(); + EVP_DigestInit(ctx, EVP_sha1()); + EVP_DigestUpdate(ctx, mes, len); + EVP_DigestFinal(ctx, digest, &size); + EVP_MD_CTX_free(ctx); +} + +static void sha384_digest(unsigned char *mes, uint32_t len, + unsigned char *digest) +{ + EVP_MD_CTX *ctx; + unsigned int size = KEYCHAIN_HMAC_SHA384_HASH_SIZE; + + ctx = EVP_MD_CTX_new(); + EVP_DigestInit(ctx, EVP_sha384()); + EVP_DigestUpdate(ctx, mes, len); + EVP_DigestFinal(ctx, digest, &size); + EVP_MD_CTX_free(ctx); +} + +static void sha512_digest(unsigned char *mes, uint32_t len, + unsigned char *digest) +{ + EVP_MD_CTX *ctx; + unsigned int size = KEYCHAIN_HMAC_SHA512_HASH_SIZE; + + ctx = EVP_MD_CTX_new(); + EVP_DigestInit(ctx, EVP_sha512()); + EVP_DigestUpdate(ctx, mes, len); + EVP_DigestFinal(ctx, digest, &size); + EVP_MD_CTX_free(ctx); +} +#endif /* CRYPTO_OPENSSL */ + +static void ospf6_hash_hmac_sha_digest(enum keychain_hash_algo key, + unsigned char *mes, uint32_t len, + unsigned char *digest) +{ + if ((key < KEYCHAIN_ALGO_NULL) || (key > KEYCHAIN_ALGO_MAX)) + return; + + switch (key) { + case KEYCHAIN_ALGO_MD5: + md5_digest(mes, len, digest); + break; + case KEYCHAIN_ALGO_HMAC_SHA1: +#ifdef CRYPTO_OPENSSL + sha1_digest(mes, len, digest); +#endif + break; + case KEYCHAIN_ALGO_HMAC_SHA256: + sha256_digest(mes, len, digest); + break; + case KEYCHAIN_ALGO_HMAC_SHA384: +#ifdef CRYPTO_OPENSSL + sha384_digest(mes, len, digest); +#endif + break; + case KEYCHAIN_ALGO_HMAC_SHA512: +#ifdef CRYPTO_OPENSSL + sha512_digest(mes, len, digest); +#endif + break; + case KEYCHAIN_ALGO_NULL: + case KEYCHAIN_ALGO_MAX: + default: + /* no action */ + break; + } +} + +uint16_t ospf6_auth_len_get(struct ospf6_interface *oi) +{ + uint16_t at_len = 0; + char *keychain_name = NULL; + struct keychain *keychain = NULL; + struct key *key = NULL; + + if (CHECK_FLAG(oi->at_data.flags, OSPF6_AUTH_TRAILER_KEYCHAIN)) { + if (CHECK_FLAG(oi->at_data.flags, + OSPF6_AUTH_TRAILER_KEYCHAIN_VALID)) { + at_len = OSPF6_AUTH_HDR_MIN_SIZE + + keychain_get_hash_len(oi->at_data.hash_algo); + } else { + keychain_name = oi->at_data.keychain; + keychain = keychain_lookup(keychain_name); + if (keychain) { + key = key_lookup_for_send(keychain); + if (key && key->string + && key->hash_algo != KEYCHAIN_ALGO_NULL) { + at_len = OSPF6_AUTH_HDR_MIN_SIZE + + keychain_get_hash_len( + key->hash_algo); + } + } + } + } else if (CHECK_FLAG(oi->at_data.flags, + OSPF6_AUTH_TRAILER_MANUAL_KEY)) { + at_len = OSPF6_AUTH_HDR_MIN_SIZE + + keychain_get_hash_len(oi->at_data.hash_algo); + } + + return at_len; +} + +int ospf6_auth_validate_pkt(struct ospf6_interface *oi, unsigned int *pkt_len, + struct ospf6_header *oh, unsigned int *at_len, + unsigned int *lls_block_len) +{ + struct ospf6_hello *hello = NULL; + struct ospf6_dbdesc *dbdesc = NULL; + struct ospf6_neighbor *on = NULL; + struct ospf6_auth_hdr ospf6_auth_info; + uint16_t hdr_len = 0; + uint32_t oh_seqnum_h = 0; + uint32_t oh_seqnum_l = 0; + bool auth_present = false; + bool lls_present = false; + struct ospf6_lls_hdr *lls_hdr = NULL; + + on = ospf6_neighbor_lookup(oh->router_id, oi); + hdr_len = ntohs(oh->length); + if (*pkt_len < hdr_len) { + if (IS_OSPF6_DEBUG_AUTH_RX) + zlog_err("RECV[%s] Received incomplete %s packet", + oi->interface->name, + ospf6_message_type(oh->type)); + return OSPF6_AUTH_VALIDATE_FAILURE; + } else if (*pkt_len == hdr_len) { + if (oi->at_data.flags != 0) + return OSPF6_AUTH_VALIDATE_FAILURE; + /* No auth info to be considered. + */ + return OSPF6_AUTH_PROCESS_NORMAL; + } + + switch (oh->type) { + case OSPF6_MESSAGE_TYPE_HELLO: + hello = (struct ospf6_hello *)((uint8_t *)oh + + sizeof(struct ospf6_header)); + if (OSPF6_OPT_ISSET_EXT(hello->options, OSPF6_OPT_L)) + lls_present = true; + + if (OSPF6_OPT_ISSET_EXT(hello->options, OSPF6_OPT_AT)) + auth_present = true; + break; + case OSPF6_MESSAGE_TYPE_DBDESC: + dbdesc = (struct ospf6_dbdesc *)((uint8_t *)oh + + sizeof(struct ospf6_header)); + if (OSPF6_OPT_ISSET_EXT(dbdesc->options, OSPF6_OPT_L)) + lls_present = true; + + if (OSPF6_OPT_ISSET_EXT(dbdesc->options, OSPF6_OPT_AT)) + auth_present = true; + break; + case OSPF6_MESSAGE_TYPE_LSREQ: + case OSPF6_MESSAGE_TYPE_LSUPDATE: + case OSPF6_MESSAGE_TYPE_LSACK: + if (on) { + lls_present = on->lls_present; + auth_present = on->auth_present; + } + break; + default: + if (IS_OSPF6_DEBUG_AUTH_RX) + zlog_err("RECV[%s] : Wrong packet type %d", + oi->interface->name, oh->type); + return OSPF6_AUTH_VALIDATE_FAILURE; + } + + if ((oh->type == OSPF6_MESSAGE_TYPE_HELLO) + || (oh->type == OSPF6_MESSAGE_TYPE_DBDESC)) { + if (on) { + on->auth_present = auth_present; + on->lls_present = lls_present; + } + } + + if ((!auth_present && (oi->at_data.flags != 0)) + || (auth_present && (oi->at_data.flags == 0))) { + if (IS_OSPF6_DEBUG_AUTH_RX) + zlog_err("RECV[%s] : Auth option miss-match in %s pkt", + oi->interface->name, + ospf6_message_type(oh->type)); + return OSPF6_AUTH_VALIDATE_FAILURE; + } + + if (lls_present) { + lls_hdr = (struct ospf6_lls_hdr *)(oh + hdr_len); + *lls_block_len = ntohs(lls_hdr->length) * 4; + } + + if (*lls_block_len > (*pkt_len - hdr_len)) { + if (IS_OSPF6_DEBUG_AUTH_RX) + zlog_err("RECV[%s] : Wrong lls data in %s packet", + oi->interface->name, + ospf6_message_type(oh->type)); + return OSPF6_AUTH_VALIDATE_FAILURE; + } + + memset(&ospf6_auth_info, 0, sizeof(ospf6_auth_info)); + if ((*pkt_len - hdr_len - (*lls_block_len)) > sizeof(ospf6_auth_info)) { + if (IS_OSPF6_DEBUG_AUTH_RX) + zlog_err("RECV[%s] : Wrong auth data in %s packet", + oi->interface->name, + ospf6_message_type(oh->type)); + return OSPF6_AUTH_VALIDATE_FAILURE; + } + + memcpy(&ospf6_auth_info, ((uint8_t *)oh + hdr_len + (*lls_block_len)), + (*pkt_len - hdr_len - (*lls_block_len))); + if (ntohs(ospf6_auth_info.length) > OSPF6_AUTH_HDR_FULL) { + if (IS_OSPF6_DEBUG_AUTH_RX) + zlog_err("RECV[%s] : Wrong auth header length in %s", + oi->interface->name, + ospf6_message_type(oh->type)); + return OSPF6_AUTH_VALIDATE_FAILURE; + } + + /* after authentication header validation is done + * reduce the auth hdr size from the packet length + */ + *at_len = ntohs(ospf6_auth_info.length); + *pkt_len = (*pkt_len) - (*at_len) - (*lls_block_len); + + if (on) { + oh_seqnum_h = ntohl(ospf6_auth_info.seqnum_h); + oh_seqnum_l = ntohl(ospf6_auth_info.seqnum_l); + if ((oh_seqnum_h >= on->seqnum_h[oh->type]) + && (oh_seqnum_l > on->seqnum_l[oh->type])) { + /* valid sequence number received */ + on->seqnum_h[oh->type] = oh_seqnum_h; + on->seqnum_l[oh->type] = oh_seqnum_l; + } else { + if (IS_OSPF6_DEBUG_AUTH_RX) { + zlog_err( + "RECV[%s] : Nbr(%s) Auth Sequence number mismatch in %s ", + oi->interface->name, on->name, + ospf6_message_type(oh->type)); + zlog_err( + "nbr_seq_l %u, nbr_seq_h %u, hdr_seq_l %u, hdr_seq_h %u", + on->seqnum_l[oh->type], + on->seqnum_h[oh->type], oh_seqnum_l, + oh_seqnum_h); + } + + return OSPF6_AUTH_VALIDATE_FAILURE; + } + } + + return OSPF6_AUTH_VALIDATE_SUCCESS; +} + +/* Starting point of packet process function. */ +int ospf6_auth_check_digest(struct ospf6_header *oh, struct ospf6_interface *oi, + struct in6_addr *src, unsigned int lls_block_len) +{ + uint32_t hash_len = KEYCHAIN_MAX_HASH_SIZE; + unsigned char apad[hash_len]; + unsigned char temp_hash[hash_len]; + struct ospf6_auth_hdr *ospf6_auth; + uint32_t ipv6_addr_size = sizeof(struct in6_addr); + struct keychain *keychain = NULL; + struct key *key = NULL; + char *auth_str = NULL; + uint16_t auth_len = 0; + uint8_t hash_algo = 0; + uint16_t oh_len = ntohs(oh->length); + int ret = 0; + + if (oi->at_data.flags == 0) + return OSPF6_AUTH_PROCESS_NORMAL; + + ospf6_auth = (struct ospf6_auth_hdr *)((uint8_t *)oh + + (oh_len + lls_block_len)); + if (CHECK_FLAG(oi->at_data.flags, OSPF6_AUTH_TRAILER_KEYCHAIN)) { + keychain = keychain_lookup(oi->at_data.keychain); + if (!keychain) { + if (IS_OSPF6_DEBUG_AUTH_RX) + zlog_err( + "RECV[%s]: Keychain doesn't exist for %s", + oi->interface->name, + ospf6_message_type(oh->type)); + return OSPF6_AUTH_VALIDATE_FAILURE; + } + + key = key_lookup_for_accept(keychain, ntohs(ospf6_auth->id)); + if (!key) { + if (IS_OSPF6_DEBUG_AUTH_RX) + zlog_err("RECV[%s]: Auth, Invalid SA for %s", + oi->interface->name, + ospf6_message_type(oh->type)); + return OSPF6_AUTH_VALIDATE_FAILURE; + } + + if (key && key->string + && key->hash_algo != KEYCHAIN_ALGO_NULL) { + auth_str = key->string; + hash_algo = key->hash_algo; + } else { + if (IS_OSPF6_DEBUG_AUTH_RX) + zlog_err( + "RECV[%s]: Incomplete keychain config for %s", + oi->interface->name, + ospf6_message_type(oh->type)); + return OSPF6_AUTH_VALIDATE_FAILURE; + } + } else if (CHECK_FLAG(oi->at_data.flags, + OSPF6_AUTH_TRAILER_MANUAL_KEY)) { + auth_str = oi->at_data.auth_key; + hash_algo = oi->at_data.hash_algo; + } + + if (!auth_str) + return OSPF6_AUTH_VALIDATE_FAILURE; + + hash_len = keychain_get_hash_len(hash_algo); + memset(apad, 0, sizeof(apad)); + memset(temp_hash, 0, sizeof(temp_hash)); + + /* start digest verification */ + memcpy(apad, src, ipv6_addr_size); + memcpy(apad + ipv6_addr_size, ospf6_hash_apad_max, + (hash_len - ipv6_addr_size)); + + auth_len = ntohs(ospf6_auth->length); + + memcpy(temp_hash, ospf6_auth->data, hash_len); + memcpy(ospf6_auth->data, apad, hash_len); + + ospf6_auth_update_digest(oi, oh, ospf6_auth, auth_str, + (oh_len + auth_len + lls_block_len), + hash_algo); + +#ifdef CRYPTO_OPENSSL + ret = CRYPTO_memcmp(temp_hash, ospf6_auth->data, hash_len); +#else + ret = memcmp(temp_hash, ospf6_auth->data, hash_len); +#endif + if (ret == 0) + return OSPF6_AUTH_VALIDATE_SUCCESS; + + return OSPF6_AUTH_VALIDATE_FAILURE; +} + +void ospf6_auth_digest_send(struct in6_addr *src, struct ospf6_interface *oi, + struct ospf6_header *oh, uint16_t auth_len, + uint32_t pkt_len) +{ + struct ospf6_auth_hdr *ospf6_auth; + char *keychain_name = NULL; + struct keychain *keychain = NULL; + struct key *key = NULL; + char *auth_str = NULL; + uint16_t key_id = 0; + enum keychain_hash_algo hash_algo = KEYCHAIN_ALGO_NULL; + uint32_t hash_len = KEYCHAIN_MAX_HASH_SIZE; + unsigned char apad[hash_len]; + int ipv6_addr_size = sizeof(struct in6_addr); + struct ospf6 *ospf6 = NULL; + + if (CHECK_FLAG(oi->at_data.flags, OSPF6_AUTH_TRAILER_KEYCHAIN)) { + if (CHECK_FLAG(oi->at_data.flags, + OSPF6_AUTH_TRAILER_KEYCHAIN_VALID)) { + auth_str = oi->at_data.auth_key; + hash_algo = oi->at_data.hash_algo; + key_id = oi->at_data.key_id; + } else { + keychain_name = oi->at_data.keychain; + keychain = keychain_lookup(keychain_name); + if (keychain) { + key = key_lookup_for_send(keychain); + if (key && key->string + && key->hash_algo != KEYCHAIN_ALGO_NULL) { + auth_str = key->string; + hash_algo = key->hash_algo; + key_id = key->index; + } + } + } + } else if (CHECK_FLAG(oi->at_data.flags, + OSPF6_AUTH_TRAILER_MANUAL_KEY)) { + auth_str = oi->at_data.auth_key; + hash_algo = oi->at_data.hash_algo; + key_id = oi->at_data.key_id; + } else { + if (IS_OSPF6_DEBUG_AUTH_TX) + zlog_warn("SEND[%s]: Authentication not configured for %s", + oi->interface->name, + ospf6_message_type(oh->type)); + return; + } + + if (!auth_str) { + if (IS_OSPF6_DEBUG_AUTH_TX) + zlog_warn("SEND[%s]: Authentication key is not configured for %s", + oi->interface->name, + ospf6_message_type(oh->type)); + return; + } + + hash_len = keychain_get_hash_len(hash_algo); + if (oi->area && oi->area->ospf6) + ospf6 = oi->area->ospf6; + else + return; + + if (ospf6->seqnum_l == 0xFFFFFFFF) { + if (ospf6->seqnum_h == 0xFFFFFFFF) { + /* Key must be reset, which is not handled as of now. */ + zlog_err("Sequence number wrapped; key must be reset."); + ospf6->seqnum_h = 0; + } else { + ospf6->seqnum_h++; + } + ospf6_auth_seqno_nvm_update(ospf6); + + ospf6->seqnum_l = 0; + } else { + ospf6->seqnum_l++; + } + + memset(apad, 0, sizeof(apad)); + + if (src) + memcpy(apad, src, ipv6_addr_size); + + memcpy(apad + ipv6_addr_size, ospf6_hash_apad_max, + (hash_len - ipv6_addr_size)); + + ospf6_auth = + (struct ospf6_auth_hdr *)((uint8_t *)oh + ntohs(oh->length)); + ospf6_auth->type = htons(OSPF6_AUTHENTICATION_CRYPTOGRAPHIC); + ospf6_auth->length = htons(auth_len); + ospf6_auth->reserved = 0; + ospf6_auth->id = htons(key_id); + ospf6_auth->seqnum_h = htonl(ospf6->seqnum_h); + ospf6_auth->seqnum_l = htonl(ospf6->seqnum_l); + memcpy(ospf6_auth->data, apad, hash_len); + + ospf6_auth_update_digest(oi, oh, ospf6_auth, auth_str, pkt_len, + hash_algo); + + /* There is a optimisation that is done to ensure that + * for every packet flow keychain lib API are called + * only once and the result are stored in oi->at_data. + * So, After processing the flow it is reset back here. + */ + if (CHECK_FLAG(oi->at_data.flags, OSPF6_AUTH_TRAILER_KEYCHAIN_VALID)) { + oi->at_data.hash_algo = KEYCHAIN_ALGO_NULL; + if (oi->at_data.auth_key) { + XFREE(MTYPE_OSPF6_AUTH_MANUAL_KEY, + oi->at_data.auth_key); + oi->at_data.auth_key = NULL; + } + + oi->at_data.key_id = 0; + UNSET_FLAG(oi->at_data.flags, + OSPF6_AUTH_TRAILER_KEYCHAIN_VALID); + } +} + +void ospf6_auth_update_digest(struct ospf6_interface *oi, + struct ospf6_header *oh, + struct ospf6_auth_hdr *ospf6_auth, char *auth_str, + uint32_t pkt_len, enum keychain_hash_algo algo) +{ + const uint16_t cpid = htons(OSPFV3_CRYPTO_PROTO_ID); + uint32_t hash_len = keychain_get_hash_len(algo); + uint32_t block_s = keychain_get_block_size(algo); + uint32_t k_len = strlen(auth_str); + uint32_t ks_len = strlen(auth_str) + sizeof(cpid); + unsigned char ipad[block_s]; + unsigned char opad[block_s]; + unsigned char ko[block_s], ks[ks_len], tmp[hash_len]; + unsigned char *first = NULL; + unsigned char *second = NULL; + unsigned char first_mes[block_s + pkt_len]; + unsigned char second_mes[block_s + pkt_len]; + unsigned char first_hash[hash_len]; + unsigned char second_hash[hash_len]; + + memset(ko, 0, sizeof(ko)); + memcpy(ks, auth_str, k_len); + memcpy(ks + k_len, &cpid, sizeof(cpid)); + if (ks_len > hash_len) { + ospf6_hash_hmac_sha_digest(algo, ks, ks_len, tmp); + memcpy(ko, tmp, hash_len); + } else + memcpy(ko, ks, ks_len); + + memcpy(ipad, ospf6_hash_ipad_max, block_s); + memcpy(opad, ospf6_hash_opad_max, block_s); + + first = ospf6_hash_message_xor((unsigned char *)&ipad, ko, block_s); + second = ospf6_hash_message_xor((unsigned char *)&opad, ko, block_s); + + memcpy(first_mes, first, block_s); + memcpy(first_mes + block_s, oh, pkt_len); + + ospf6_hash_hmac_sha_digest(algo, first_mes, (block_s + pkt_len), + first_hash); + + memcpy(second_mes, second, block_s); + memcpy(second_mes + block_s, first_hash, hash_len); + + ospf6_hash_hmac_sha_digest(algo, second_mes, (block_s + hash_len), + second_hash); + + memcpy(ospf6_auth->data, second_hash, hash_len); + XFREE(MTYPE_OSPF6_AUTH_HASH_XOR, first); + XFREE(MTYPE_OSPF6_AUTH_HASH_XOR, second); +} + +DEFUN (debug_ospf6_auth, + debug_ospf6_auth_cmd, + "debug ospf6 authentication [<tx|rx>]", + DEBUG_STR + OSPF6_STR + "debug OSPF6 authentication\n" + "debug authentication tx\n" + "debug authentication rx\n") +{ + int auth_opt_idx = 3; + + if (argc == 4) { + if (!strncmp(argv[auth_opt_idx]->arg, "t", 1)) + OSPF6_DEBUG_AUTH_TX_ON(); + else if (!strncmp(argv[auth_opt_idx]->arg, "r", 1)) + OSPF6_DEBUG_AUTH_RX_ON(); + } else { + OSPF6_DEBUG_AUTH_TX_ON(); + OSPF6_DEBUG_AUTH_RX_ON(); + } + + return CMD_SUCCESS; +} + +DEFUN (no_debug_ospf6_auth, + no_debug_ospf6_auth_cmd, + "no debug ospf6 authentication [<tx|rx>]", + NO_STR + DEBUG_STR + OSPF6_STR + "debug OSPF6 authentication\n" + "debug authentication tx\n" + "debug authentication rx\n") +{ + int auth_opt_idx = 3; + + if (argc == 5) { + if (!strncmp(argv[auth_opt_idx]->arg, "t", 1)) + OSPF6_DEBUG_AUTH_TX_OFF(); + else if (!strncmp(argv[auth_opt_idx]->arg, "r", 1)) + OSPF6_DEBUG_AUTH_RX_OFF(); + } else { + OSPF6_DEBUG_AUTH_TX_OFF(); + OSPF6_DEBUG_AUTH_RX_OFF(); + } + + return CMD_SUCCESS; +} + +int config_write_ospf6_debug_auth(struct vty *vty) +{ + if (IS_OSPF6_DEBUG_AUTH_TX) + vty_out(vty, "debug ospf6 authentication tx\n"); + if (IS_OSPF6_DEBUG_AUTH_RX) + vty_out(vty, "debug ospf6 authentication rx\n"); + return 0; +} + +void install_element_ospf6_debug_auth(void) +{ + install_element(ENABLE_NODE, &debug_ospf6_auth_cmd); + install_element(ENABLE_NODE, &no_debug_ospf6_auth_cmd); + install_element(CONFIG_NODE, &debug_ospf6_auth_cmd); + install_element(CONFIG_NODE, &no_debug_ospf6_auth_cmd); +} + +/* Clear the specified interface structure */ +static void ospf6_intf_auth_clear(struct vty *vty, struct interface *ifp) +{ + struct ospf6_interface *oi; + + if (!if_is_operative(ifp)) + return; + + if (ifp->info == NULL) + return; + + oi = (struct ospf6_interface *)ifp->info; + + if (IS_OSPF6_DEBUG_INTERFACE) + zlog_debug( + "Interface %s: clear authentication rx/tx drop counters", + ifp->name); + + /* Reset the interface rx/tx drop counters */ + oi->at_data.tx_drop = 0; + oi->at_data.rx_drop = 0; +} + +/* Clear interface */ +DEFUN(clear_ipv6_ospf6_intf_auth, clear_ipv6_ospf6_intf_auth_cmd, + "clear ipv6 ospf6 [vrf VRF] auth-counters interface [IFNAME]", + CLEAR_STR IP6_STR OSPF6_STR VRF_CMD_HELP_STR + "authentication rx/tx drop counters\n" INTERFACE_STR IFNAME_STR) +{ + int idx_ifname = 0; + int idx_vrf = 0; + struct interface *ifp; + struct listnode *node; + struct ospf6 *ospf6 = NULL; + char *vrf_name = NULL; + vrf_id_t vrf_id = VRF_DEFAULT; + struct vrf *vrf = NULL; + + if (argv_find(argv, argc, "vrf", &idx_vrf)) + vrf_name = argv[idx_vrf + 1]->arg; + + if (vrf_name && strmatch(vrf_name, VRF_DEFAULT_NAME)) + vrf_name = NULL; + + if (vrf_name) { + vrf = vrf_lookup_by_name(vrf_name); + if (vrf) + vrf_id = vrf->vrf_id; + } + + if (!argv_find(argv, argc, "IFNAME", &idx_ifname)) { + /* Clear all the ospfv3 interfaces auth data. */ + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { + if (vrf_id != ospf6->vrf_id) + continue; + + if (!vrf) + vrf = vrf_lookup_by_id(ospf6->vrf_id); + FOR_ALL_INTERFACES (vrf, ifp) + ospf6_intf_auth_clear(vty, ifp); + } + } else { + /* Interface name is specified. */ + ifp = if_lookup_by_name(argv[idx_ifname]->arg, vrf_id); + if (ifp == NULL) + vty_out(vty, "No such interface name\n"); + else + ospf6_intf_auth_clear(vty, ifp); + } + + return CMD_SUCCESS; +} + +void install_element_ospf6_clear_intf_auth(void) +{ + install_element(ENABLE_NODE, &clear_ipv6_ospf6_intf_auth_cmd); +} + +enum ospf6_auth_err ospf6_auth_nvm_file_exist(void) +{ + struct stat buffer; + int exist; + + exist = stat(OSPF6_AUTH_SEQ_NUM_FILE, &buffer); + if (exist == 0) + return OSPF6_AUTH_FILE_EXIST; + else + return OSPF6_AUTH_FILE_DO_NOT_EXIST; +} + +/* + * Record in non-volatile memory the given ospf6 process, + * authentication trailer higher order sequence number. + */ +void ospf6_auth_seqno_nvm_update(struct ospf6 *ospf6) +{ + const char *inst_name; + json_object *json; + json_object *json_instances; + json_object *json_instance; + + zlog_err("Higher order sequence number %d update for %s process", + ospf6->seqnum_h, ospf6->name); + + inst_name = ospf6->name ? ospf6->name : VRF_DEFAULT_NAME; + + json = json_object_from_file((char *)OSPF6_AUTH_SEQ_NUM_FILE); + if (json == NULL) + json = json_object_new_object(); + + json_object_object_get_ex(json, "instances", &json_instances); + if (!json_instances) { + json_instances = json_object_new_object(); + json_object_object_add(json, "instances", json_instances); + } + + json_object_object_get_ex(json_instances, inst_name, &json_instance); + if (!json_instance) { + json_instance = json_object_new_object(); + json_object_object_add(json_instances, inst_name, + json_instance); + } + + /* + * Record higher order sequence number in non volatile memory. + */ + json_object_int_add(json_instance, "sequence_number", ospf6->seqnum_h); + + json_object_to_file_ext((char *)OSPF6_AUTH_SEQ_NUM_FILE, json, + JSON_C_TO_STRING_PRETTY); + json_object_free(json); +} + +/* + * Delete authentication sequence number for a given OSPF6 process + * from non-volatile memory. + */ +void ospf6_auth_seqno_nvm_delete(struct ospf6 *ospf6) +{ + const char *inst_name; + json_object *json; + json_object *json_instances; + + zlog_err("Higher order sequence number delete for %s process", + ospf6->name); + + inst_name = ospf6->name ? ospf6->name : VRF_DEFAULT_NAME; + + json = json_object_from_file((char *)OSPF6_AUTH_SEQ_NUM_FILE); + if (json == NULL) + json = json_object_new_object(); + + json_object_object_get_ex(json, "instances", &json_instances); + if (!json_instances) { + json_instances = json_object_new_object(); + json_object_object_add(json, "instances", json_instances); + } + + json_object_object_del(json_instances, inst_name); + + json_object_to_file_ext((char *)OSPF6_AUTH_SEQ_NUM_FILE, json, + JSON_C_TO_STRING_PRETTY); + json_object_free(json); +} + + +/* + * Fetch from non-volatile memory the stored ospf6 process + * authentication sequence number. + */ +void ospf6_auth_seqno_nvm_read(struct ospf6 *ospf6) +{ + const char *inst_name; + json_object *json; + json_object *json_instances; + json_object *json_instance; + json_object *json_seqnum; + + inst_name = ospf6->name ? ospf6->name : VRF_DEFAULT_NAME; + + json = json_object_from_file((char *)OSPF6_AUTH_SEQ_NUM_FILE); + if (json == NULL) + json = json_object_new_object(); + + json_object_object_get_ex(json, "instances", &json_instances); + if (!json_instances) { + json_instances = json_object_new_object(); + json_object_object_add(json, "instances", json_instances); + } + + json_object_object_get_ex(json_instances, inst_name, &json_instance); + if (!json_instance) { + json_instance = json_object_new_object(); + json_object_object_add(json_instances, inst_name, + json_instance); + } + + json_object_object_get_ex(json_instance, "sequence_number", + &json_seqnum); + ospf6->seqnum_h = json_object_get_int(json_seqnum); + + zlog_err("Higher order sequence number %d read for %s process %s", + ospf6->seqnum_h, ospf6->name, strerror(errno)); + + json_object_object_del(json_instances, inst_name); + json_object_to_file_ext((char *)OSPF6_AUTH_SEQ_NUM_FILE, json, + JSON_C_TO_STRING_PRETTY); + json_object_free(json); +} diff --git a/ospf6d/ospf6_auth_trailer.h b/ospf6d/ospf6_auth_trailer.h new file mode 100644 index 0000000..3f82a7b --- /dev/null +++ b/ospf6d/ospf6_auth_trailer.h @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2021 Abhinay Ramesh + */ + +#ifndef __OSPF6_AUTH_TRAILER_H__ +#define __OSPF6_AUTH_TRAILER_H__ + +#include "lib/keychain.h" +#include "ospf6_message.h" + +#define OSPF6_AUTH_HDR_MIN_SIZE 16 +#define OSPF6_AUTH_HDR_FULL KEYCHAIN_MAX_HASH_SIZE + OSPF6_AUTH_HDR_MIN_SIZE + +#define OSPF6_AUTHENTICATION_NULL 0 +#define OSPF6_AUTHENTICATION_CRYPTOGRAPHIC 1 + +#define OSPFV3_CRYPTO_PROTO_ID 1 + +/* Auth debug options */ +extern unsigned char conf_debug_ospf6_auth[2]; + +#define OSPF6_AUTH_TX 0 +#define OSPF6_AUTH_RX 1 +#define OSPF6_DEBUG_AUTH_TX_ON() (conf_debug_ospf6_auth[OSPF6_AUTH_TX] = 1) +#define OSPF6_DEBUG_AUTH_TX_OFF() (conf_debug_ospf6_auth[OSPF6_AUTH_TX] = 0) +#define OSPF6_DEBUG_AUTH_RX_ON() (conf_debug_ospf6_auth[OSPF6_AUTH_RX] = 1) +#define OSPF6_DEBUG_AUTH_RX_OFF() (conf_debug_ospf6_auth[OSPF6_AUTH_RX] = 0) +#define IS_OSPF6_DEBUG_AUTH_TX (conf_debug_ospf6_auth[OSPF6_AUTH_TX]) +#define IS_OSPF6_DEBUG_AUTH_RX (conf_debug_ospf6_auth[OSPF6_AUTH_RX]) + +#define OSPF6_AUTH_TRAILER_KEYCHAIN (1 << 0) +#define OSPF6_AUTH_TRAILER_MANUAL_KEY (1 << 1) +#define OSPF6_AUTH_TRAILER_KEYCHAIN_VALID (1 << 2) + +/* According to sesion 4.1 of RFC7166 defining the trailer struct */ +struct ospf6_auth_hdr { + uint16_t type; + uint16_t length; + uint16_t reserved; + uint16_t id; + uint32_t seqnum_h; + uint32_t seqnum_l; + unsigned char data[KEYCHAIN_MAX_HASH_SIZE]; +}; + +enum ospf6_auth_err { + OSPF6_AUTH_VALIDATE_SUCCESS = 0, + OSPF6_AUTH_VALIDATE_FAILURE, + OSPF6_AUTH_PROCESS_NORMAL, + OSPF6_AUTH_FILE_EXIST, + OSPF6_AUTH_FILE_DO_NOT_EXIST +}; + +void ospf6_auth_hdr_dump_send(struct ospf6_header *ospfh, uint16_t length); +void ospf6_auth_hdr_dump_recv(struct ospf6_header *ospfh, uint16_t length, + unsigned int lls_len); +unsigned char *ospf6_hash_message_xor(unsigned char *mes1, unsigned char *mes2, + uint32_t len); +uint16_t ospf6_auth_len_get(struct ospf6_interface *oi); +int ospf6_auth_validate_pkt(struct ospf6_interface *oi, unsigned int *pkt_len, + struct ospf6_header *oh, unsigned int *at_len, + unsigned int *lls_block_len); +int ospf6_auth_check_digest(struct ospf6_header *oh, struct ospf6_interface *oi, + struct in6_addr *src, unsigned int lls_len); +void ospf6_auth_update_digest(struct ospf6_interface *oi, + struct ospf6_header *oh, + struct ospf6_auth_hdr *ospf6_auth, char *auth_str, + uint32_t pkt_len, enum keychain_hash_algo algo); +void ospf6_auth_digest_send(struct in6_addr *src, struct ospf6_interface *oi, + struct ospf6_header *oh, uint16_t auth_len, + uint32_t pkt_len); +void install_element_ospf6_debug_auth(void); +int config_write_ospf6_debug_auth(struct vty *vty); +void install_element_ospf6_clear_intf_auth(void); +enum ospf6_auth_err ospf6_auth_nvm_file_exist(void); +void ospf6_auth_seqno_nvm_update(struct ospf6 *ospf6); +void ospf6_auth_seqno_nvm_delete(struct ospf6 *ospf6); +void ospf6_auth_seqno_nvm_read(struct ospf6 *ospf6); +#endif /* __OSPF6_AUTH_TRAILER_H__ */ diff --git a/ospf6d/ospf6_bfd.c b/ospf6d/ospf6_bfd.c new file mode 100644 index 0000000..6379f9d --- /dev/null +++ b/ospf6d/ospf6_bfd.c @@ -0,0 +1,290 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** + * ospf6_bfd.c: IPv6 OSPF BFD handling routines + * + * @copyright Copyright (C) 2015 Cumulus Networks, Inc. + */ + +#include <zebra.h> + +#include "command.h" +#include "linklist.h" +#include "memory.h" +#include "prefix.h" +#include "frrevent.h" +#include "buffer.h" +#include "stream.h" +#include "zclient.h" +#include "vty.h" +#include "table.h" +#include "bfd.h" +#include "if.h" +#include "ospf6d.h" +#include "ospf6_message.h" +#include "ospf6_neighbor.h" +#include "ospf6_interface.h" +#include "ospf6_route.h" +#include "ospf6_zebra.h" +#include "ospf6_bfd.h" + +extern struct zclient *zclient; + +/* + * ospf6_bfd_trigger_event - Neighbor is registered/deregistered with BFD when + * neighbor state is changed to/from 2way. + */ +void ospf6_bfd_trigger_event(struct ospf6_neighbor *on, int old_state, + int state) +{ + int family; + struct in6_addr src, dst; + + /* Skip sessions without BFD. */ + if (on->bfd_session == NULL) + return; + + if (old_state < OSPF6_NEIGHBOR_TWOWAY + && state >= OSPF6_NEIGHBOR_TWOWAY) { + /* + * Check if neighbor address changed. + * + * When the neighbor is configured BFD before having an existing + * connection, then the destination address will be set to `::` + * which will cause session installation failure. This piece of + * code updates the address in that case. + */ + bfd_sess_addresses(on->bfd_session, &family, &src, &dst); + if (memcmp(&on->linklocal_addr, &dst, sizeof(dst))) { + bfd_sess_set_ipv6_addrs(on->bfd_session, &src, + &on->linklocal_addr); + } + + bfd_sess_install(on->bfd_session); + } else if (old_state >= OSPF6_NEIGHBOR_TWOWAY + && state < OSPF6_NEIGHBOR_TWOWAY) + bfd_sess_uninstall(on->bfd_session); +} + +/* + * ospf6_bfd_reg_dereg_all_nbr - Register/Deregister all neighbors associated + * with a interface with BFD through + * zebra for starting/stopping the monitoring of + * the neighbor rechahability. + */ +static void ospf6_bfd_reg_dereg_all_nbr(struct ospf6_interface *oi, + bool install) +{ + struct ospf6_neighbor *on; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, node, on)) { + /* Remove all sessions. */ + if (!install) { + bfd_sess_free(&on->bfd_session); + continue; + } + + /* Always allocate session data even if not enabled. */ + ospf6_bfd_info_nbr_create(oi, on); + + /* + * If not connected yet, don't create any session but defer it + * for later. See function `ospf6_bfd_trigger_event`. + */ + if (on->state < OSPF6_NEIGHBOR_TWOWAY) + continue; + + bfd_sess_install(on->bfd_session); + } +} + +static void ospf6_bfd_callback(struct bfd_session_params *bsp, + const struct bfd_session_status *bss, void *arg) +{ + struct ospf6_neighbor *on = arg; + + if (bss->state == BFD_STATUS_DOWN + && bss->previous_state == BFD_STATUS_UP) { + EVENT_OFF(on->inactivity_timer); + event_add_event(master, inactivity_timer, on, 0, NULL); + } +} + +/* + * ospf6_bfd_info_nbr_create - Create/update BFD information for a neighbor. + */ +void ospf6_bfd_info_nbr_create(struct ospf6_interface *oi, + struct ospf6_neighbor *on) +{ + if (!oi->bfd_config.enabled) + return; + + if (on->bfd_session == NULL) + on->bfd_session = bfd_sess_new(ospf6_bfd_callback, on); + + bfd_sess_set_timers(on->bfd_session, + oi->bfd_config.detection_multiplier, + oi->bfd_config.min_rx, oi->bfd_config.min_tx); + bfd_sess_set_ipv6_addrs(on->bfd_session, on->ospf6_if->linklocal_addr, + &on->linklocal_addr); + bfd_sess_set_interface(on->bfd_session, oi->interface->name); + bfd_sess_set_vrf(on->bfd_session, oi->interface->vrf->vrf_id); + bfd_sess_set_profile(on->bfd_session, oi->bfd_config.profile); +} + +/* + * ospf6_bfd_write_config - Write the interface BFD configuration. + */ +void ospf6_bfd_write_config(struct vty *vty, struct ospf6_interface *oi) +{ + if (!oi->bfd_config.enabled) + return; + +#if HAVE_BFDD == 0 + if (oi->bfd_config.detection_multiplier != BFD_DEF_DETECT_MULT + || oi->bfd_config.min_rx != BFD_DEF_MIN_RX + || oi->bfd_config.min_tx != BFD_DEF_MIN_TX) + vty_out(vty, " ipv6 ospf6 bfd %d %d %d\n", + oi->bfd_config.detection_multiplier, + oi->bfd_config.min_rx, oi->bfd_config.min_tx); + else +#endif /* ! HAVE_BFDD */ + vty_out(vty, " ipv6 ospf6 bfd\n"); + + if (oi->bfd_config.profile) + vty_out(vty, " ipv6 ospf6 bfd profile %s\n", + oi->bfd_config.profile); +} + +DEFUN(ipv6_ospf6_bfd, ipv6_ospf6_bfd_cmd, + "ipv6 ospf6 bfd [profile BFDPROF]", + IP6_STR OSPF6_STR + "Enables BFD support\n" + "BFD Profile selection\n" + "BFD Profile name\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf6_interface *oi; + int prof_idx = 4; + assert(ifp); + + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL) + oi = ospf6_interface_create(ifp); + assert(oi); + + oi->bfd_config.detection_multiplier = BFD_DEF_DETECT_MULT; + oi->bfd_config.min_rx = BFD_DEF_MIN_RX; + oi->bfd_config.min_tx = BFD_DEF_MIN_TX; + oi->bfd_config.enabled = true; + if (argc > prof_idx) { + XFREE(MTYPE_TMP, oi->bfd_config.profile); + oi->bfd_config.profile = + XSTRDUP(MTYPE_TMP, argv[prof_idx]->arg); + } + + ospf6_bfd_reg_dereg_all_nbr(oi, true); + + return CMD_SUCCESS; +} + +DEFUN(no_ipv6_ospf6_bfd_profile, no_ipv6_ospf6_bfd_profile_cmd, + "no ipv6 ospf6 bfd profile [BFDPROF]", + NO_STR IP6_STR OSPF6_STR + "BFD support\n" + "BFD Profile selection\n" + "BFD Profile name\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf6_interface *oi; + assert(ifp); + + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL) + oi = ospf6_interface_create(ifp); + assert(oi); + + /* BFD not enabled, nothing to do. */ + if (!oi->bfd_config.enabled) + return CMD_SUCCESS; + + /* Remove profile and apply new configuration. */ + XFREE(MTYPE_TMP, oi->bfd_config.profile); + ospf6_bfd_reg_dereg_all_nbr(oi, true); + + return CMD_SUCCESS; +} + +#if HAVE_BFDD > 0 +DEFUN_HIDDEN( +#else +DEFUN( +#endif /* HAVE_BFDD */ + ipv6_ospf6_bfd_param, + ipv6_ospf6_bfd_param_cmd, + "ipv6 ospf6 bfd (2-255) (50-60000) (50-60000)", + IP6_STR + OSPF6_STR + "Enables BFD support\n" + "Detect Multiplier\n" + "Required min receive interval\n" + "Desired min transmit interval\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + int idx_number = 3; + int idx_number_2 = 4; + int idx_number_3 = 5; + struct ospf6_interface *oi; + + assert(ifp); + + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL) + oi = ospf6_interface_create(ifp); + assert(oi); + + oi->bfd_config.detection_multiplier = + strtoul(argv[idx_number]->arg, NULL, 10); + oi->bfd_config.min_rx = strtoul(argv[idx_number_2]->arg, NULL, 10); + oi->bfd_config.min_tx = strtoul(argv[idx_number_3]->arg, NULL, 10); + oi->bfd_config.enabled = true; + + ospf6_bfd_reg_dereg_all_nbr(oi, true); + + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_ospf6_bfd, + no_ipv6_ospf6_bfd_cmd, + "no ipv6 ospf6 bfd", + NO_STR + IP6_STR + OSPF6_STR + "Disables BFD support\n" + ) +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf6_interface *oi; + assert(ifp); + + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL) + oi = ospf6_interface_create(ifp); + assert(oi); + + oi->bfd_config.enabled = false; + ospf6_bfd_reg_dereg_all_nbr(oi, false); + + return CMD_SUCCESS; +} + +void ospf6_bfd_init(void) +{ + bfd_protocol_integration_init(zclient, master); + + /* Install BFD command */ + install_element(INTERFACE_NODE, &ipv6_ospf6_bfd_cmd); + install_element(INTERFACE_NODE, &ipv6_ospf6_bfd_param_cmd); + install_element(INTERFACE_NODE, &no_ipv6_ospf6_bfd_profile_cmd); + install_element(INTERFACE_NODE, &no_ipv6_ospf6_bfd_cmd); +} diff --git a/ospf6d/ospf6_bfd.h b/ospf6d/ospf6_bfd.h new file mode 100644 index 0000000..e532ccc --- /dev/null +++ b/ospf6d/ospf6_bfd.h @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** + * ospf6_bfd.h: OSPF6 BFD definitions and structures + * + * @copyright Copyright (C) 2015 Cumulus Networks, Inc. + */ +#include "lib/json.h" +#ifndef OSPF6_BFD_H +#define OSPF6_BFD_H +#include "lib/json.h" + +/** + * Initialize BFD integration. + */ +extern void ospf6_bfd_init(void); + +extern void ospf6_bfd_trigger_event(struct ospf6_neighbor *nbr, int old_state, + int state); + +extern void ospf6_bfd_write_config(struct vty *vty, struct ospf6_interface *oi); + +extern void ospf6_bfd_info_nbr_create(struct ospf6_interface *oi, + struct ospf6_neighbor *on); + +#endif /* OSPF6_BFD_H */ diff --git a/ospf6d/ospf6_flood.c b/ospf6d/ospf6_flood.c new file mode 100644 index 0000000..98d3bbc --- /dev/null +++ b/ospf6d/ospf6_flood.c @@ -0,0 +1,1282 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2003 Yasuhiro Ohara + */ + +#include <zebra.h> + +#include "log.h" +#include "frrevent.h" +#include "linklist.h" +#include "vty.h" +#include "command.h" + +#include "ospf6d.h" +#include "ospf6_proto.h" +#include "ospf6_lsa.h" +#include "ospf6_lsdb.h" +#include "ospf6_message.h" +#include "ospf6_route.h" +#include "ospf6_spf.h" + +#include "ospf6_top.h" +#include "ospf6_area.h" +#include "ospf6_interface.h" +#include "ospf6_neighbor.h" + +#include "ospf6_flood.h" +#include "ospf6_nssa.h" +#include "ospf6_gr.h" + +unsigned char conf_debug_ospf6_flooding; + +struct ospf6_lsdb *ospf6_get_scoped_lsdb(struct ospf6_lsa *lsa) +{ + struct ospf6_lsdb *lsdb = NULL; + switch (OSPF6_LSA_SCOPE(lsa->header->type)) { + case OSPF6_SCOPE_LINKLOCAL: + lsdb = OSPF6_INTERFACE(lsa->lsdb->data)->lsdb; + break; + case OSPF6_SCOPE_AREA: + lsdb = OSPF6_AREA(lsa->lsdb->data)->lsdb; + break; + case OSPF6_SCOPE_AS: + lsdb = OSPF6_PROCESS(lsa->lsdb->data)->lsdb; + break; + default: + assert(0); + break; + } + return lsdb; +} + +struct ospf6_lsdb *ospf6_get_scoped_lsdb_self(struct ospf6_lsa *lsa) +{ + struct ospf6_lsdb *lsdb_self = NULL; + switch (OSPF6_LSA_SCOPE(lsa->header->type)) { + case OSPF6_SCOPE_LINKLOCAL: + lsdb_self = OSPF6_INTERFACE(lsa->lsdb->data)->lsdb_self; + break; + case OSPF6_SCOPE_AREA: + lsdb_self = OSPF6_AREA(lsa->lsdb->data)->lsdb_self; + break; + case OSPF6_SCOPE_AS: + lsdb_self = OSPF6_PROCESS(lsa->lsdb->data)->lsdb_self; + break; + default: + assert(0); + break; + } + return lsdb_self; +} + +void ospf6_lsa_originate(struct ospf6 *ospf6, struct ospf6_lsa *lsa) +{ + struct ospf6_lsa *old; + struct ospf6_lsdb *lsdb_self; + + if (lsa->header->adv_router == INADDR_ANY) { + if (IS_OSPF6_DEBUG_ORIGINATE_TYPE(lsa->header->type)) + zlog_debug( + "Refusing to originate LSA (zero router ID): %s", + lsa->name); + + ospf6_lsa_delete(lsa); + return; + } + + /* find previous LSA */ + old = ospf6_lsdb_lookup(lsa->header->type, lsa->header->id, + lsa->header->adv_router, lsa->lsdb); + + /* if the new LSA does not differ from previous, + suppress this update of the LSA */ + if (old && !OSPF6_LSA_IS_DIFFER(lsa, old) + && !ospf6->gr_info.finishing_restart) { + if (IS_OSPF6_DEBUG_ORIGINATE_TYPE(lsa->header->type)) + zlog_debug("Suppress updating LSA: %s", lsa->name); + ospf6_lsa_delete(lsa); + return; + } + + /* store it in the LSDB for self-originated LSAs */ + lsdb_self = ospf6_get_scoped_lsdb_self(lsa); + ospf6_lsdb_add(ospf6_lsa_copy(lsa), lsdb_self); + + EVENT_OFF(lsa->refresh); + event_add_timer(master, ospf6_lsa_refresh, lsa, OSPF_LS_REFRESH_TIME, + &lsa->refresh); + + if (IS_OSPF6_DEBUG_LSA_TYPE(lsa->header->type) + || IS_OSPF6_DEBUG_ORIGINATE_TYPE(lsa->header->type)) { + zlog_debug("LSA Originate:"); + ospf6_lsa_header_print(lsa); + } + + ospf6_install_lsa(lsa); + ospf6_flood(NULL, lsa); +} + +void ospf6_lsa_originate_process(struct ospf6_lsa *lsa, struct ospf6 *process) +{ + lsa->lsdb = process->lsdb; + ospf6_lsa_originate(process, lsa); +} + +void ospf6_lsa_originate_area(struct ospf6_lsa *lsa, struct ospf6_area *oa) +{ + lsa->lsdb = oa->lsdb; + ospf6_lsa_originate(oa->ospf6, lsa); +} + +void ospf6_lsa_originate_interface(struct ospf6_lsa *lsa, + struct ospf6_interface *oi) +{ + lsa->lsdb = oi->lsdb; + ospf6_lsa_originate(oi->area->ospf6, lsa); +} + +void ospf6_external_lsa_purge(struct ospf6 *ospf6, struct ospf6_lsa *lsa) +{ + uint32_t id = lsa->header->id; + struct ospf6_area *oa; + struct listnode *lnode; + + ospf6_lsa_purge(lsa); + + /* Delete the corresponding NSSA LSA */ + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, lnode, oa)) { + lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_TYPE_7), id, + ospf6->router_id, oa->lsdb); + if (lsa) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("withdraw type 7 lsa, LS ID: %u", + htonl(id)); + + ospf6_lsa_purge(lsa); + } + } +} + +void ospf6_lsa_purge(struct ospf6_lsa *lsa) +{ + struct ospf6_lsa *self; + struct ospf6_lsdb *lsdb_self; + + /* remove it from the LSDB for self-originated LSAs */ + lsdb_self = ospf6_get_scoped_lsdb_self(lsa); + self = ospf6_lsdb_lookup(lsa->header->type, lsa->header->id, + lsa->header->adv_router, lsdb_self); + if (self) { + EVENT_OFF(self->expire); + EVENT_OFF(self->refresh); + ospf6_lsdb_remove(self, lsdb_self); + } + + ospf6_lsa_premature_aging(lsa); +} + +/* Puring Multi Link-State IDs LSAs: + * Same Advertising Router with Multiple Link-State IDs + * LSAs, purging require to traverse all Link-State IDs + */ +void ospf6_lsa_purge_multi_ls_id(struct ospf6_area *oa, struct ospf6_lsa *lsa) +{ + int ls_id = 0; + struct ospf6_lsa *lsa_next; + uint16_t type; + + type = lsa->header->type; + + ospf6_lsa_purge(lsa); + + lsa_next = ospf6_lsdb_lookup(type, htonl(++ls_id), + oa->ospf6->router_id, oa->lsdb); + while (lsa_next) { + ospf6_lsa_purge(lsa_next); + lsa_next = ospf6_lsdb_lookup(type, htonl(++ls_id), + oa->ospf6->router_id, oa->lsdb); + } +} + +void ospf6_increment_retrans_count(struct ospf6_lsa *lsa) +{ + /* The LSA must be the original one (see the description + in ospf6_decrement_retrans_count () below) */ + lsa->retrans_count++; +} + +void ospf6_decrement_retrans_count(struct ospf6_lsa *lsa) +{ + struct ospf6_lsdb *lsdb; + struct ospf6_lsa *orig; + + /* The LSA must be on the retrans-list of a neighbor. It means + the "lsa" is a copied one, and we have to decrement the + retransmission count of the original one (instead of this "lsa"'s). + In order to find the original LSA, first we have to find + appropriate LSDB that have the original LSA. */ + lsdb = ospf6_get_scoped_lsdb(lsa); + + /* Find the original LSA of which the retrans_count should be + * decremented */ + orig = ospf6_lsdb_lookup(lsa->header->type, lsa->header->id, + lsa->header->adv_router, lsdb); + if (orig) { + orig->retrans_count--; + assert(orig->retrans_count >= 0); + } +} + +/* RFC2328 section 13.2 Installing LSAs in the database */ +void ospf6_install_lsa(struct ospf6_lsa *lsa) +{ + struct ospf6 *ospf6; + struct timeval now; + struct ospf6_lsa *old; + struct ospf6_area *area = NULL; + + ospf6 = ospf6_get_by_lsdb(lsa); + assert(ospf6); + + /* Remove the old instance from all neighbors' Link state + retransmission list (RFC2328 13.2 last paragraph) */ + old = ospf6_lsdb_lookup(lsa->header->type, lsa->header->id, + lsa->header->adv_router, lsa->lsdb); + if (old) { + if (ntohs(lsa->header->type) == OSPF6_LSTYPE_TYPE_7) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s : old LSA %s", __func__, + lsa->name); + lsa->external_lsa_id = old->external_lsa_id; + } + EVENT_OFF(old->expire); + EVENT_OFF(old->refresh); + ospf6_flood_clear(old); + } + + monotime(&now); + if (!OSPF6_LSA_IS_MAXAGE(lsa)) { + event_add_timer(master, ospf6_lsa_expire, lsa, + OSPF_LSA_MAXAGE + lsa->birth.tv_sec - + now.tv_sec, + &lsa->expire); + } else + lsa->expire = NULL; + + if (OSPF6_LSA_IS_SEQWRAP(lsa) + && !(CHECK_FLAG(lsa->flag, OSPF6_LSA_SEQWRAPPED) + && lsa->header->seqnum == htonl(OSPF_MAX_SEQUENCE_NUMBER))) { + if (IS_OSPF6_DEBUG_EXAMIN_TYPE(lsa->header->type)) + zlog_debug("lsa install wrapping: sequence 0x%x", + ntohl(lsa->header->seqnum)); + SET_FLAG(lsa->flag, OSPF6_LSA_SEQWRAPPED); + /* in lieu of premature_aging, since we do not want to recreate + * this lsa + * and/or mess with timers etc, we just want to wrap the + * sequence number + * and reflood the lsa before continuing. + * NOTE: Flood needs to be called right after this function + * call, by the + * caller + */ + lsa->header->seqnum = htonl(OSPF_MAX_SEQUENCE_NUMBER); + lsa->header->age = htons(OSPF_LSA_MAXAGE); + ospf6_lsa_checksum(lsa->header); + } + + if (IS_OSPF6_DEBUG_LSA_TYPE(lsa->header->type) + || IS_OSPF6_DEBUG_EXAMIN_TYPE(lsa->header->type)) + zlog_debug("%s Install LSA: %s age %d seqnum %x in LSDB.", + __func__, lsa->name, ntohs(lsa->header->age), + ntohl(lsa->header->seqnum)); + + /* actually install */ + lsa->installed = now; + + /* Topo change handling */ + if (CHECK_LSA_TOPO_CHG_ELIGIBLE(ntohs(lsa->header->type)) + && !CHECK_FLAG(lsa->flag, OSPF6_LSA_DUPLICATE)) { + + /* check if it is new lsa ? or existing lsa got modified ?*/ + if (!old || OSPF6_LSA_IS_CHANGED(old, lsa)) + ospf6_helper_handle_topo_chg(ospf6, lsa); + } + + ospf6_lsdb_add(lsa, lsa->lsdb); + + if (ntohs(lsa->header->type) == OSPF6_LSTYPE_TYPE_7 + && lsa->header->adv_router != ospf6->router_id) { + area = OSPF6_AREA(lsa->lsdb->data); + ospf6_translated_nssa_refresh(area, lsa, NULL); + ospf6_schedule_abr_task(area->ospf6); + } + + if (ntohs(lsa->header->type) == OSPF6_LSTYPE_ROUTER) { + area = OSPF6_AREA(lsa->lsdb->data); + if (old == NULL) { + if (IS_OSPF6_DEBUG_LSA_TYPE(lsa->header->type) + || IS_OSPF6_DEBUG_EXAMIN_TYPE(lsa->header->type)) + zlog_debug("%s: New router LSA %s", __func__, + lsa->name); + ospf6_abr_nssa_check_status(area->ospf6); + } + } + return; +} + +/* RFC2740 section 3.5.2. Sending Link State Update packets */ +/* RFC2328 section 13.3 Next step in the flooding procedure */ +void ospf6_flood_interface(struct ospf6_neighbor *from, struct ospf6_lsa *lsa, + struct ospf6_interface *oi) +{ + struct listnode *node, *nnode; + struct ospf6_neighbor *on; + struct ospf6_lsa *req, *old; + int retrans_added = 0; + int is_debug = 0; + + if (IS_OSPF6_DEBUG_FLOODING + || IS_OSPF6_DEBUG_FLOOD_TYPE(lsa->header->type)) { + is_debug++; + zlog_debug("Flooding on %s: %s", oi->interface->name, + lsa->name); + } + + /* (1) For each neighbor */ + for (ALL_LIST_ELEMENTS(oi->neighbor_list, node, nnode, on)) { + if (is_debug) + zlog_debug("To neighbor %s", on->name); + + /* (a) if neighbor state < Exchange, examin next */ + if (on->state < OSPF6_NEIGHBOR_EXCHANGE) { + if (is_debug) + zlog_debug( + "Neighbor state less than ExChange, next neighbor"); + continue; + } + + /* (b) if neighbor not yet Full, check request-list */ + if (on->state != OSPF6_NEIGHBOR_FULL) { + if (is_debug) + zlog_debug("Neighbor not yet Full"); + + req = ospf6_lsdb_lookup( + lsa->header->type, lsa->header->id, + lsa->header->adv_router, on->request_list); + if (req == NULL) { + if (is_debug) + zlog_debug( + "Not on request-list for this neighbor"); + /* fall through */ + } else { + /* If new LSA less recent, examin next neighbor + */ + if (ospf6_lsa_compare(lsa, req) > 0) { + if (is_debug) + zlog_debug( + "Requesting is older, next neighbor"); + continue; + } + + /* If the same instance, delete from + request-list and + examin next neighbor */ + if (ospf6_lsa_compare(lsa, req) == 0) { + if (is_debug) + zlog_debug( + "Requesting the same, remove it, next neighbor"); + if (req == on->last_ls_req) { + /* sanity check refcount */ + assert(req->lock >= 2); + ospf6_lsa_unlock(&req); + on->last_ls_req = NULL; + } + if (req) + ospf6_lsdb_remove( + req, on->request_list); + ospf6_check_nbr_loading(on); + continue; + } + + /* If the new LSA is more recent, delete from + * request-list */ + if (ospf6_lsa_compare(lsa, req) < 0) { + if (is_debug) + zlog_debug( + "Received is newer, remove requesting"); + if (req == on->last_ls_req) { + ospf6_lsa_unlock(&req); + on->last_ls_req = NULL; + } + if (req) + ospf6_lsdb_remove(req, + on->request_list); + ospf6_check_nbr_loading(on); + /* fall through */ + } + } + } + + /* (c) If the new LSA was received from this neighbor, + examin next neighbor */ + if (from == on) { + if (is_debug) + zlog_debug( + "Received is from the neighbor, next neighbor"); + continue; + } + + if ((oi->area->ospf6->inst_shutdown) + || CHECK_FLAG(lsa->flag, OSPF6_LSA_FLUSH)) { + if (is_debug) + zlog_debug( + "%s: Send LSA %s (age %d) update now", + __func__, lsa->name, + ntohs(lsa->header->age)); + ospf6_lsupdate_send_neighbor_now(on, lsa); + continue; + } else { + /* (d) add retrans-list, schedule retransmission */ + if (is_debug) + zlog_debug("Add retrans-list of neighbor %s ", + on->name); + + /* Do not increment the retrans count if the lsa is + * already present in the retrans list. + */ + old = ospf6_lsdb_lookup( + lsa->header->type, lsa->header->id, + lsa->header->adv_router, on->retrans_list); + if (!old) { + struct ospf6_lsa *orig; + struct ospf6_lsdb *lsdb; + + if (is_debug) + zlog_debug( + "Increment %s from retrans_list of %s", + lsa->name, on->name); + + /* Increment the retrans count on the original + * copy of LSA if present, to maintain the + * counter consistency. + */ + + lsdb = ospf6_get_scoped_lsdb(lsa); + orig = ospf6_lsdb_lookup( + lsa->header->type, lsa->header->id, + lsa->header->adv_router, lsdb); + if (orig) + ospf6_increment_retrans_count(orig); + else + ospf6_increment_retrans_count(lsa); + + ospf6_lsdb_add(ospf6_lsa_copy(lsa), + on->retrans_list); + event_add_timer(master, + ospf6_lsupdate_send_neighbor, + on, on->ospf6_if->rxmt_interval, + &on->thread_send_lsupdate); + retrans_added++; + } + } + } + + /* (2) examin next interface if not added to retrans-list */ + if (retrans_added == 0) { + if (is_debug) + zlog_debug( + "No retransmission scheduled, next interface %s", + oi->interface->name); + return; + } + + /* (3) If the new LSA was received on this interface, + and it was from DR or BDR, examin next interface */ + if (from && from->ospf6_if == oi + && (from->router_id == oi->drouter + || from->router_id == oi->bdrouter)) { + if (is_debug) + zlog_debug( + "Received is from the I/F's DR or BDR, next interface"); + return; + } + + /* (4) If the new LSA was received on this interface, + and the interface state is BDR, examin next interface */ + if (from && from->ospf6_if == oi) { + if (oi->state == OSPF6_INTERFACE_BDR) { + if (is_debug) + zlog_debug( + "Received is from the I/F, itself BDR, next interface"); + return; + } + SET_FLAG(lsa->flag, OSPF6_LSA_FLOODBACK); + } + + /* (5) flood the LSA out the interface. */ + if (is_debug) + zlog_debug("Schedule flooding for the interface"); + if ((oi->type == OSPF_IFTYPE_BROADCAST) + || (oi->type == OSPF_IFTYPE_POINTOPOINT)) { + ospf6_lsdb_add(ospf6_lsa_copy(lsa), oi->lsupdate_list); + event_add_event(master, ospf6_lsupdate_send_interface, oi, 0, + &oi->thread_send_lsupdate); + } else { + /* reschedule retransmissions to all neighbors */ + for (ALL_LIST_ELEMENTS(oi->neighbor_list, node, nnode, on)) { + EVENT_OFF(on->thread_send_lsupdate); + event_add_event(master, ospf6_lsupdate_send_neighbor, + on, 0, &on->thread_send_lsupdate); + } + } +} + +void ospf6_flood_area(struct ospf6_neighbor *from, struct ospf6_lsa *lsa, + struct ospf6_area *oa) +{ + struct listnode *node, *nnode; + struct ospf6_interface *oi; + + for (ALL_LIST_ELEMENTS(oa->if_list, node, nnode, oi)) { + if (OSPF6_LSA_SCOPE(lsa->header->type) == OSPF6_SCOPE_LINKLOCAL + && oi != OSPF6_INTERFACE(lsa->lsdb->data)) + continue; + + ospf6_flood_interface(from, lsa, oi); + } +} + +static void ospf6_flood_process(struct ospf6_neighbor *from, + struct ospf6_lsa *lsa, struct ospf6 *process) +{ + struct listnode *node, *nnode; + struct ospf6_area *oa; + + for (ALL_LIST_ELEMENTS(process->area_list, node, nnode, oa)) { + + /* If unknown LSA and U-bit clear, treat as link local + * flooding scope + */ + if (!OSPF6_LSA_IS_KNOWN(lsa->header->type) + && !(ntohs(lsa->header->type) & OSPF6_LSTYPE_UBIT_MASK) + && (oa != OSPF6_INTERFACE(lsa->lsdb->data)->area)) { + + if (IS_OSPF6_DEBUG_FLOODING) + zlog_debug("Unknown LSA, do not flood"); + continue; + } + + if (OSPF6_LSA_SCOPE(lsa->header->type) == OSPF6_SCOPE_AREA + && oa != OSPF6_AREA(lsa->lsdb->data)) + continue; + if (OSPF6_LSA_SCOPE(lsa->header->type) == OSPF6_SCOPE_LINKLOCAL + && oa != OSPF6_INTERFACE(lsa->lsdb->data)->area) + continue; + + if (ntohs(lsa->header->type) == OSPF6_LSTYPE_AS_EXTERNAL + && (IS_AREA_STUB(oa) || IS_AREA_NSSA(oa))) + continue; + + /* Check for NSSA LSA */ + if (ntohs(lsa->header->type) == OSPF6_LSTYPE_TYPE_7 + && !IS_AREA_NSSA(oa) && !OSPF6_LSA_IS_MAXAGE(lsa)) + continue; + + ospf6_flood_area(from, lsa, oa); + } +} + +void ospf6_flood(struct ospf6_neighbor *from, struct ospf6_lsa *lsa) +{ + struct ospf6 *ospf6; + + ospf6 = ospf6_get_by_lsdb(lsa); + if (ospf6 == NULL) + return; + + ospf6_flood_process(from, lsa, ospf6); +} + +static void ospf6_flood_clear_interface(struct ospf6_lsa *lsa, + struct ospf6_interface *oi) +{ + struct listnode *node, *nnode; + struct ospf6_neighbor *on; + struct ospf6_lsa *rem; + + for (ALL_LIST_ELEMENTS(oi->neighbor_list, node, nnode, on)) { + rem = ospf6_lsdb_lookup(lsa->header->type, lsa->header->id, + lsa->header->adv_router, + on->retrans_list); + if (rem && !ospf6_lsa_compare(rem, lsa)) { + if (IS_OSPF6_DEBUG_FLOODING + || IS_OSPF6_DEBUG_FLOOD_TYPE(lsa->header->type)) + zlog_debug("Remove %s from retrans_list of %s", + rem->name, on->name); + ospf6_decrement_retrans_count(rem); + ospf6_lsdb_remove(rem, on->retrans_list); + } + } +} + +void ospf6_flood_clear_area(struct ospf6_lsa *lsa, struct ospf6_area *oa) +{ + struct listnode *node, *nnode; + struct ospf6_interface *oi; + + for (ALL_LIST_ELEMENTS(oa->if_list, node, nnode, oi)) { + if (OSPF6_LSA_SCOPE(lsa->header->type) == OSPF6_SCOPE_LINKLOCAL + && oi != OSPF6_INTERFACE(lsa->lsdb->data)) + continue; + + ospf6_flood_clear_interface(lsa, oi); + } +} + +static void ospf6_flood_clear_process(struct ospf6_lsa *lsa, + struct ospf6 *process) +{ + struct listnode *node, *nnode; + struct ospf6_area *oa; + + for (ALL_LIST_ELEMENTS(process->area_list, node, nnode, oa)) { + if (OSPF6_LSA_SCOPE(lsa->header->type) == OSPF6_SCOPE_AREA + && oa != OSPF6_AREA(lsa->lsdb->data)) + continue; + if (OSPF6_LSA_SCOPE(lsa->header->type) == OSPF6_SCOPE_LINKLOCAL + && oa != OSPF6_INTERFACE(lsa->lsdb->data)->area) + continue; + + if (ntohs(lsa->header->type) == OSPF6_LSTYPE_AS_EXTERNAL + && (IS_AREA_STUB(oa) || (IS_AREA_NSSA(oa)))) + continue; + /* Check for NSSA LSA */ + if (ntohs(lsa->header->type) == OSPF6_LSTYPE_TYPE_7 + && !IS_AREA_NSSA(oa)) + continue; + + ospf6_flood_clear_area(lsa, oa); + } +} + +void ospf6_flood_clear(struct ospf6_lsa *lsa) +{ + struct ospf6 *ospf6; + + ospf6 = ospf6_get_by_lsdb(lsa); + if (ospf6 == NULL) + return; + ospf6_flood_clear_process(lsa, ospf6); +} + + +/* RFC2328 13.5 (Table 19): Sending link state acknowledgements. */ +static void ospf6_acknowledge_lsa_bdrouter(struct ospf6_lsa *lsa, + int ismore_recent, + struct ospf6_neighbor *from) +{ + struct ospf6_interface *oi; + int is_debug = 0; + + if (IS_OSPF6_DEBUG_FLOODING + || IS_OSPF6_DEBUG_FLOOD_TYPE(lsa->header->type)) + is_debug++; + + assert(from && from->ospf6_if); + oi = from->ospf6_if; + + /* LSA is more recent than database copy, but was not flooded + back out receiving interface. Delayed acknowledgement sent + if advertisement received from Designated Router, + otherwide do nothing. */ + if (ismore_recent < 0) { + if (oi->drouter == from->router_id) { + if (is_debug) + zlog_debug( + "Delayed acknowledgement (BDR & MoreRecent & from DR)"); + /* Delayed acknowledgement */ + ospf6_lsdb_add(ospf6_lsa_copy(lsa), oi->lsack_list); + event_add_timer(master, ospf6_lsack_send_interface, oi, + 3, &oi->thread_send_lsack); + } else { + if (is_debug) + zlog_debug( + "No acknowledgement (BDR & MoreRecent & ! from DR)"); + } + return; + } + + /* LSA is a duplicate, and was treated as an implied acknowledgement. + Delayed acknowledgement sent if advertisement received from + Designated Router, otherwise do nothing */ + if (CHECK_FLAG(lsa->flag, OSPF6_LSA_DUPLICATE) + && CHECK_FLAG(lsa->flag, OSPF6_LSA_IMPLIEDACK)) { + if (oi->drouter == from->router_id) { + if (is_debug) + zlog_debug( + "Delayed acknowledgement (BDR & Duplicate & ImpliedAck & from DR)"); + /* Delayed acknowledgement */ + ospf6_lsdb_add(ospf6_lsa_copy(lsa), oi->lsack_list); + event_add_timer(master, ospf6_lsack_send_interface, oi, + 3, &oi->thread_send_lsack); + } else { + if (is_debug) + zlog_debug( + "No acknowledgement (BDR & Duplicate & ImpliedAck & ! from DR)"); + } + return; + } + + /* LSA is a duplicate, and was not treated as an implied + acknowledgement. + Direct acknowledgement sent */ + if (CHECK_FLAG(lsa->flag, OSPF6_LSA_DUPLICATE) + && !CHECK_FLAG(lsa->flag, OSPF6_LSA_IMPLIEDACK)) { + if (is_debug) + zlog_debug("Direct acknowledgement (BDR & Duplicate)"); + ospf6_lsdb_add(ospf6_lsa_copy(lsa), from->lsack_list); + event_add_event(master, ospf6_lsack_send_neighbor, from, 0, + &from->thread_send_lsack); + return; + } + + /* LSA's LS age is equal to Maxage, and there is no current instance + of the LSA in the link state database, and none of router's + neighbors are in states Exchange or Loading */ + /* Direct acknowledgement sent, but this case is handled in + early of ospf6_receive_lsa () */ +} + +static void ospf6_acknowledge_lsa_allother(struct ospf6_lsa *lsa, + int ismore_recent, + struct ospf6_neighbor *from) +{ + struct ospf6_interface *oi; + int is_debug = 0; + + if (IS_OSPF6_DEBUG_FLOODING + || IS_OSPF6_DEBUG_FLOOD_TYPE(lsa->header->type)) + is_debug++; + + assert(from && from->ospf6_if); + oi = from->ospf6_if; + + /* LSA has been flood back out receiving interface. + No acknowledgement sent. */ + if (CHECK_FLAG(lsa->flag, OSPF6_LSA_FLOODBACK)) { + if (is_debug) + zlog_debug("No acknowledgement (AllOther & FloodBack)"); + return; + } + + /* LSA is more recent than database copy, but was not flooded + back out receiving interface. Delayed acknowledgement sent. */ + if (ismore_recent < 0) { + if (is_debug) + zlog_debug( + "Delayed acknowledgement (AllOther & MoreRecent)"); + /* Delayed acknowledgement */ + ospf6_lsdb_add(ospf6_lsa_copy(lsa), oi->lsack_list); + event_add_timer(master, ospf6_lsack_send_interface, oi, 3, + &oi->thread_send_lsack); + return; + } + + /* LSA is a duplicate, and was treated as an implied acknowledgement. + No acknowledgement sent. */ + if (CHECK_FLAG(lsa->flag, OSPF6_LSA_DUPLICATE) + && CHECK_FLAG(lsa->flag, OSPF6_LSA_IMPLIEDACK)) { + if (is_debug) + zlog_debug( + "No acknowledgement (AllOther & Duplicate & ImpliedAck)"); + return; + } + + /* LSA is a duplicate, and was not treated as an implied + acknowledgement. + Direct acknowledgement sent */ + if (CHECK_FLAG(lsa->flag, OSPF6_LSA_DUPLICATE) + && !CHECK_FLAG(lsa->flag, OSPF6_LSA_IMPLIEDACK)) { + if (is_debug) + zlog_debug( + "Direct acknowledgement (AllOther & Duplicate)"); + ospf6_lsdb_add(ospf6_lsa_copy(lsa), from->lsack_list); + event_add_event(master, ospf6_lsack_send_neighbor, from, 0, + &from->thread_send_lsack); + return; + } + + /* LSA's LS age is equal to Maxage, and there is no current instance + of the LSA in the link state database, and none of router's + neighbors are in states Exchange or Loading */ + /* Direct acknowledgement sent, but this case is handled in + early of ospf6_receive_lsa () */ +} + +static void ospf6_acknowledge_lsa(struct ospf6_lsa *lsa, int ismore_recent, + struct ospf6_neighbor *from) +{ + struct ospf6_interface *oi; + + assert(from && from->ospf6_if); + oi = from->ospf6_if; + + if (oi->state == OSPF6_INTERFACE_BDR) + ospf6_acknowledge_lsa_bdrouter(lsa, ismore_recent, from); + else + ospf6_acknowledge_lsa_allother(lsa, ismore_recent, from); +} + +/* RFC2328 section 13 (4): + if MaxAge LSA and if we have no instance, and no neighbor + is in states Exchange or Loading + returns 1 if match this case, else returns 0 */ +static int ospf6_is_maxage_lsa_drop(struct ospf6_lsa *lsa, + struct ospf6_neighbor *from) +{ + struct ospf6_neighbor *on; + struct ospf6_interface *oi; + struct ospf6_area *oa; + struct ospf6 *process = NULL; + struct listnode *i, *j, *k; + int count = 0; + + if (!OSPF6_LSA_IS_MAXAGE(lsa)) + return 0; + + if (ospf6_lsdb_lookup(lsa->header->type, lsa->header->id, + lsa->header->adv_router, lsa->lsdb)) + return 0; + + process = from->ospf6_if->area->ospf6; + + for (ALL_LIST_ELEMENTS_RO(process->area_list, i, oa)) + for (ALL_LIST_ELEMENTS_RO(oa->if_list, j, oi)) + for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, k, on)) + if (on->state == OSPF6_NEIGHBOR_EXCHANGE + || on->state == OSPF6_NEIGHBOR_LOADING) + count++; + + if (count == 0) + return 1; + return 0; +} + +static bool ospf6_lsa_check_min_arrival(struct ospf6_lsa *lsa, + struct ospf6_neighbor *from) +{ + struct timeval now, res; + unsigned int time_delta_ms; + + monotime(&now); + timersub(&now, &lsa->installed, &res); + time_delta_ms = (res.tv_sec * 1000) + (int)(res.tv_usec / 1000); + + if (time_delta_ms < from->ospf6_if->area->ospf6->lsa_minarrival) { + if (IS_OSPF6_DEBUG_FLOODING || + IS_OSPF6_DEBUG_FLOOD_TYPE(lsa->header->type)) + zlog_debug( + "LSA can't be updated within MinLSArrival, %dms < %dms, discard", + time_delta_ms, + from->ospf6_if->area->ospf6->lsa_minarrival); + return true; + } + return false; +} + +/* RFC2328 section 13 The Flooding Procedure */ +void ospf6_receive_lsa(struct ospf6_neighbor *from, + struct ospf6_lsa_header *lsa_header) +{ + struct ospf6_lsa *new = NULL, *old = NULL, *rem = NULL; + int ismore_recent; + int is_debug = 0; + + ismore_recent = 1; + assert(from); + + /* if we receive a LSA with invalid seqnum drop it */ + if (ntohl(lsa_header->seqnum) - 1 == OSPF_MAX_SEQUENCE_NUMBER) { + if (IS_OSPF6_DEBUG_EXAMIN_TYPE(lsa_header->type)) { + zlog_debug( + "received lsa [%s Id:%pI4 Adv:%pI4] with invalid seqnum 0x%x, ignore", + ospf6_lstype_name(lsa_header->type), + &lsa_header->id, &lsa_header->adv_router, + ntohl(lsa_header->seqnum)); + } + return; + } + + /* make lsa structure for received lsa */ + new = ospf6_lsa_create(lsa_header); + + if (IS_OSPF6_DEBUG_FLOODING + || IS_OSPF6_DEBUG_FLOOD_TYPE(new->header->type)) { + is_debug++; + zlog_debug("LSA Receive from %s", from->name); + ospf6_lsa_header_print(new); + } + + /* (1) LSA Checksum */ + if (!ospf6_lsa_checksum_valid(new->header)) { + if (is_debug) + zlog_debug( + "Wrong LSA Checksum %s (Router-ID: %pI4) [Type:%s Checksum:%#06hx), discard", + from->name, &from->router_id, + ospf6_lstype_name(new->header->type), + ntohs(new->header->checksum)); + ospf6_lsa_delete(new); + return; + } + + /* (2) Examine the LSA's LS type. + RFC2470 3.5.1. Receiving Link State Update packets */ + if (IS_AREA_STUB(from->ospf6_if->area) + && OSPF6_LSA_SCOPE(new->header->type) == OSPF6_SCOPE_AS) { + if (is_debug) + zlog_debug( + "AS-External-LSA (or AS-scope LSA) in stub area, discard"); + ospf6_lsa_delete(new); + return; + } + + /* (3) LSA which have reserved scope is discarded + RFC2470 3.5.1. Receiving Link State Update packets */ + /* Flooding scope check. LSAs with unknown scope are discarded here. + Set appropriate LSDB for the LSA */ + switch (OSPF6_LSA_SCOPE(new->header->type)) { + case OSPF6_SCOPE_LINKLOCAL: + new->lsdb = from->ospf6_if->lsdb; + break; + case OSPF6_SCOPE_AREA: + new->lsdb = from->ospf6_if->area->lsdb; + break; + case OSPF6_SCOPE_AS: + new->lsdb = from->ospf6_if->area->ospf6->lsdb; + break; + default: + if (is_debug) + zlog_debug("LSA has reserved scope, discard"); + ospf6_lsa_delete(new); + return; + } + + /* (4) if MaxAge LSA and if we have no instance, and no neighbor + is in states Exchange or Loading */ + if (ospf6_is_maxage_lsa_drop(new, from)) { + /* log */ + if (is_debug) + zlog_debug( + "Drop MaxAge LSA with direct acknowledgement."); + + /* a) Acknowledge back to neighbor (Direct acknowledgement, + * 13.5) */ + ospf6_lsdb_add(ospf6_lsa_copy(new), from->lsack_list); + event_add_event(master, ospf6_lsack_send_neighbor, from, 0, + &from->thread_send_lsack); + + /* b) Discard */ + ospf6_lsa_delete(new); + return; + } + + /* (5) */ + /* lookup the same database copy in lsdb */ + old = ospf6_lsdb_lookup(new->header->type, new->header->id, + new->header->adv_router, new->lsdb); + if (old) { + ismore_recent = ospf6_lsa_compare(new, old); + if (ntohl(new->header->seqnum) == ntohl(old->header->seqnum)) { + if (is_debug) + zlog_debug("Received is duplicated LSA"); + SET_FLAG(new->flag, OSPF6_LSA_DUPLICATE); + } + } + + /* if no database copy or received is more recent */ + if (old == NULL || ismore_recent < 0) { + bool self_originated; + + /* in case we have no database copy */ + ismore_recent = -1; + + /* (a) MinLSArrival check */ + if (old) { + if (ospf6_lsa_check_min_arrival(old, from)) { + ospf6_lsa_delete(new); + return; /* examin next lsa */ + } + } + + monotime(&new->received); + + if (is_debug) + zlog_debug( + "Install, Flood, Possibly acknowledge the received LSA"); + + /* Remove older copies of this LSA from retx lists */ + if (old) + ospf6_flood_clear(old); + + self_originated = (new->header->adv_router + == from->ospf6_if->area->ospf6->router_id); + + /* Received non-self-originated Grace LSA. */ + if (IS_GRACE_LSA(new) && !self_originated) { + struct ospf6 *ospf6; + + ospf6 = ospf6_get_by_lsdb(new); + + assert(ospf6); + + if (OSPF6_LSA_IS_MAXAGE(new)) { + + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "%s, Received a maxage GraceLSA from router %pI4", + __func__, + &new->header->adv_router); + if (old) { + ospf6_process_maxage_grace_lsa( + ospf6, new, from); + } else { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "%s, GraceLSA doesn't exist in lsdb, so discarding GraceLSA", + __func__); + ospf6_lsa_delete(new); + return; + } + } else { + + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "%s, Received a GraceLSA from router %pI4", + __func__, + &new->header->adv_router); + + if (ospf6_process_grace_lsa(ospf6, new, from) + == OSPF6_GR_NOT_HELPER) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "%s, Not moving to HELPER role, So dicarding GraceLSA", + __func__); + return; + } + } + } + + /* (b) immediately flood and (c) remove from all retrans-list */ + /* Prevent self-originated LSA to be flooded. this is to make + * reoriginated instance of the LSA not to be rejected by other + * routers due to MinLSArrival. + */ + if (!self_originated) + ospf6_flood(from, new); + + /* (d), installing lsdb, which may cause routing + table calculation (replacing database copy) */ + ospf6_install_lsa(new); + + if (OSPF6_LSA_IS_MAXAGE(new)) + ospf6_maxage_remove(from->ospf6_if->area->ospf6); + + /* (e) possibly acknowledge */ + ospf6_acknowledge_lsa(new, ismore_recent, from); + + /* (f) Self Originated LSA, section 13.4 */ + if (self_originated) { + if (from->ospf6_if->area->ospf6->gr_info + .restart_in_progress) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "Graceful Restart in progress -- not flushing self-originated LSA: %s", + new->name); + return; + } + + /* Self-originated LSA (newer than ours) is received + from + another router. We have to make a new instance of the + LSA + or have to flush this LSA. */ + if (is_debug) { + zlog_debug( + "Newer instance of the self-originated LSA"); + zlog_debug("Schedule reorigination"); + } + event_add_event(master, ospf6_lsa_refresh, new, 0, + &new->refresh); + } + + /* GR: check for network topology change. */ + struct ospf6 *ospf6 = from->ospf6_if->area->ospf6; + struct ospf6_area *area = from->ospf6_if->area; + if (ospf6->gr_info.restart_in_progress && + (new->header->type == ntohs(OSPF6_LSTYPE_ROUTER) || + new->header->type == ntohs(OSPF6_LSTYPE_NETWORK))) + ospf6_gr_check_lsdb_consistency(ospf6, area); + + return; + } + + /* (6) if there is instance on sending neighbor's request list */ + if (ospf6_lsdb_lookup(new->header->type, new->header->id, + new->header->adv_router, from->request_list)) { + /* if no database copy, should go above state (5) */ + assert(old); + + zlog_warn( + "Received is not newer, on the neighbor %s request-list", + from->name); + zlog_warn( + "BadLSReq, discard the received LSA lsa %s send badLSReq", + new->name); + + /* BadLSReq */ + event_add_event(master, bad_lsreq, from, 0, NULL); + + ospf6_lsa_delete(new); + return; + } + + /* (7) if neither one is more recent */ + if (ismore_recent == 0) { + if (is_debug) + zlog_debug( + "The same instance as database copy (neither recent)"); + + /* (a) if on retrans-list, Treat this LSA as an Ack: Implied Ack + */ + rem = ospf6_lsdb_lookup(new->header->type, new->header->id, + new->header->adv_router, + from->retrans_list); + if (rem) { + if (is_debug) { + zlog_debug( + "It is on the neighbor's retrans-list."); + zlog_debug( + "Treat as an Implied acknowledgement"); + } + SET_FLAG(new->flag, OSPF6_LSA_IMPLIEDACK); + ospf6_decrement_retrans_count(rem); + ospf6_lsdb_remove(rem, from->retrans_list); + } + + if (is_debug) + zlog_debug("Possibly acknowledge and then discard"); + + /* (b) possibly acknowledge */ + ospf6_acknowledge_lsa(new, ismore_recent, from); + + ospf6_lsa_delete(new); + return; + } + + /* (8) previous database copy is more recent */ + { + assert(old); + + /* If database copy is in 'Seqnumber Wrapping', + simply discard the received LSA */ + if (OSPF6_LSA_IS_MAXAGE(old) + && old->header->seqnum == htonl(OSPF_MAX_SEQUENCE_NUMBER)) { + if (is_debug) { + zlog_debug("The LSA is in Seqnumber Wrapping"); + zlog_debug("MaxAge & MaxSeqNum, discard"); + } + ospf6_lsa_delete(new); + return; + } + + /* Otherwise, Send database copy of this LSA to this neighbor */ + { + if (is_debug) { + zlog_debug("Database copy is more recent."); + zlog_debug( + "Send back directly and then discard"); + } + + /* Neighbor router sent recent age for LSA, + * Router could be restarted while current copy is + * MAXAGEd and not removed.*/ + if (OSPF6_LSA_IS_MAXAGE(old) + && !OSPF6_LSA_IS_MAXAGE(new)) { + if (new->header->adv_router + != from->ospf6_if->area->ospf6->router_id) { + if (is_debug) + zlog_debug( + "%s: Current copy of LSA %s is MAXAGE, but new has recent age, flooding/installing.", + __PRETTY_FUNCTION__, old->name); + ospf6_lsa_purge(old); + ospf6_flood(from, new); + ospf6_install_lsa(new); + return; + } + /* For self-originated LSA, only trust + * ourselves. Fall through and send + * LS Update with our current copy. + */ + if (is_debug) + zlog_debug( + "%s: Current copy of self-originated LSA %s is MAXAGE, but new has recent age, re-sending current one.", + __PRETTY_FUNCTION__, old->name); + } + + /* MinLSArrival check as per RFC 2328 13 (8) */ + if (ospf6_lsa_check_min_arrival(old, from)) { + ospf6_lsa_delete(new); + return; /* examin next lsa */ + } + + ospf6_lsdb_add(ospf6_lsa_copy(old), + from->lsupdate_list); + event_add_event(master, ospf6_lsupdate_send_neighbor, + from, 0, &from->thread_send_lsupdate); + + ospf6_lsa_delete(new); + return; + } + } +} + +DEFUN (debug_ospf6_flooding, + debug_ospf6_flooding_cmd, + "debug ospf6 flooding", + DEBUG_STR + OSPF6_STR + "Debug OSPFv3 flooding function\n" + ) +{ + OSPF6_DEBUG_FLOODING_ON(); + return CMD_SUCCESS; +} + +DEFUN (no_debug_ospf6_flooding, + no_debug_ospf6_flooding_cmd, + "no debug ospf6 flooding", + NO_STR + DEBUG_STR + OSPF6_STR + "Debug OSPFv3 flooding function\n" + ) +{ + OSPF6_DEBUG_FLOODING_OFF(); + return CMD_SUCCESS; +} + +int config_write_ospf6_debug_flood(struct vty *vty) +{ + if (IS_OSPF6_DEBUG_FLOODING) + vty_out(vty, "debug ospf6 flooding\n"); + return 0; +} + +void install_element_ospf6_debug_flood(void) +{ + install_element(ENABLE_NODE, &debug_ospf6_flooding_cmd); + install_element(ENABLE_NODE, &no_debug_ospf6_flooding_cmd); + install_element(CONFIG_NODE, &debug_ospf6_flooding_cmd); + install_element(CONFIG_NODE, &no_debug_ospf6_flooding_cmd); +} diff --git a/ospf6d/ospf6_flood.h b/ospf6d/ospf6_flood.h new file mode 100644 index 0000000..33c2c75 --- /dev/null +++ b/ospf6d/ospf6_flood.h @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2003 Yasuhiro Ohara + */ + +#ifndef OSPF6_FLOOD_H +#define OSPF6_FLOOD_H + +/* Debug option */ +extern unsigned char conf_debug_ospf6_flooding; +#define OSPF6_DEBUG_FLOODING_ON() (conf_debug_ospf6_flooding = 1) +#define OSPF6_DEBUG_FLOODING_OFF() (conf_debug_ospf6_flooding = 0) +#define IS_OSPF6_DEBUG_FLOODING (conf_debug_ospf6_flooding) + +/* Function Prototypes */ +extern struct ospf6_lsdb *ospf6_get_scoped_lsdb(struct ospf6_lsa *lsa); +extern struct ospf6_lsdb *ospf6_get_scoped_lsdb_self(struct ospf6_lsa *lsa); + +/* origination & purging */ +extern void ospf6_lsa_originate(struct ospf6 *ospf6, struct ospf6_lsa *lsa); +extern void ospf6_lsa_originate_process(struct ospf6_lsa *lsa, + struct ospf6 *process); +extern void ospf6_lsa_originate_area(struct ospf6_lsa *lsa, + struct ospf6_area *oa); +extern void ospf6_lsa_originate_interface(struct ospf6_lsa *lsa, + struct ospf6_interface *oi); +void ospf6_external_lsa_purge(struct ospf6 *ospf6, struct ospf6_lsa *lsa); +extern void ospf6_lsa_purge(struct ospf6_lsa *lsa); + +extern void ospf6_lsa_purge_multi_ls_id(struct ospf6_area *oa, + struct ospf6_lsa *lsa); + +/* access method to retrans_count */ +extern void ospf6_increment_retrans_count(struct ospf6_lsa *lsa); +extern void ospf6_decrement_retrans_count(struct ospf6_lsa *lsa); + +/* flooding & clear flooding */ +extern void ospf6_flood_clear(struct ospf6_lsa *lsa); +extern void ospf6_flood(struct ospf6_neighbor *from, struct ospf6_lsa *lsa); +extern void ospf6_flood_area(struct ospf6_neighbor *from, struct ospf6_lsa *lsa, + struct ospf6_area *oa); + +/* receive & install */ +extern void ospf6_receive_lsa(struct ospf6_neighbor *from, + struct ospf6_lsa_header *header); +extern void ospf6_install_lsa(struct ospf6_lsa *lsa); + +extern int config_write_ospf6_debug_flood(struct vty *vty); +extern void install_element_ospf6_debug_flood(void); +extern void ospf6_flood_interface(struct ospf6_neighbor *from, + struct ospf6_lsa *lsa, + struct ospf6_interface *oi); +extern int ospf6_lsupdate_send_neighbor_now(struct ospf6_neighbor *on, + struct ospf6_lsa *lsa); + +extern void ospf6_flood_clear_area(struct ospf6_lsa *lsa, + struct ospf6_area *oa); +#endif /* OSPF6_FLOOD_H */ diff --git a/ospf6d/ospf6_gr.c b/ospf6d/ospf6_gr.c new file mode 100644 index 0000000..69230e5 --- /dev/null +++ b/ospf6d/ospf6_gr.c @@ -0,0 +1,857 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * This is an implementation of RFC 5187 Graceful Restart. + * + * Copyright 2021 NetDEF (c), All rights reserved. + */ + +#include <zebra.h> + +#include "memory.h" +#include "command.h" +#include "table.h" +#include "vty.h" +#include "log.h" +#include "hook.h" +#include "printfrr.h" +#include "lib_errors.h" + +#include "ospf6d/ospf6_lsa.h" +#include "ospf6d/ospf6_lsdb.h" +#include "ospf6d/ospf6_route.h" +#include "ospf6d/ospf6_area.h" +#include "ospf6d/ospf6_interface.h" +#include "ospf6d/ospf6d.h" +#include "ospf6d/ospf6_asbr.h" +#include "ospf6d/ospf6_zebra.h" +#include "ospf6d/ospf6_message.h" +#include "ospf6d/ospf6_neighbor.h" +#include "ospf6d/ospf6_network.h" +#include "ospf6d/ospf6_flood.h" +#include "ospf6d/ospf6_intra.h" +#include "ospf6d/ospf6_spf.h" +#include "ospf6d/ospf6_gr.h" +#include "ospf6d/ospf6_gr_clippy.c" + +static void ospf6_gr_grace_period_expired(struct event *thread); + +/* Originate and install Grace-LSA for a given interface. */ +static int ospf6_gr_lsa_originate(struct ospf6_interface *oi, + enum ospf6_gr_restart_reason reason) +{ + struct ospf6 *ospf6 = oi->area->ospf6; + struct ospf6_gr_info *gr_info = &ospf6->gr_info; + struct ospf6_lsa_header *lsa_header; + struct ospf6_grace_lsa *grace_lsa; + struct ospf6_lsa *lsa; + uint16_t lsa_length; + char buffer[OSPF6_MAX_LSASIZE]; + + if (IS_OSPF6_DEBUG_ORIGINATE(LINK)) + zlog_debug("Originate Grace-LSA for Interface %s", + oi->interface->name); + + /* prepare buffer */ + memset(buffer, 0, sizeof(buffer)); + lsa_header = (struct ospf6_lsa_header *)buffer; + grace_lsa = + (struct ospf6_grace_lsa *)((caddr_t)lsa_header + + sizeof(struct ospf6_lsa_header)); + + /* Put grace period. */ + grace_lsa->tlv_period.header.type = htons(GRACE_PERIOD_TYPE); + grace_lsa->tlv_period.header.length = htons(GRACE_PERIOD_LENGTH); + grace_lsa->tlv_period.interval = htonl(gr_info->grace_period); + + /* Put restart reason. */ + grace_lsa->tlv_reason.header.type = htons(RESTART_REASON_TYPE); + grace_lsa->tlv_reason.header.length = htons(RESTART_REASON_LENGTH); + grace_lsa->tlv_reason.reason = reason; + + /* Fill LSA Header */ + lsa_length = sizeof(*lsa_header) + sizeof(*grace_lsa); + lsa_header->age = 0; + lsa_header->type = htons(OSPF6_LSTYPE_GRACE_LSA); + lsa_header->id = htonl(oi->interface->ifindex); + lsa_header->adv_router = ospf6->router_id; + lsa_header->seqnum = + ospf6_new_ls_seqnum(lsa_header->type, lsa_header->id, + lsa_header->adv_router, oi->lsdb); + lsa_header->length = htons(lsa_length); + + /* LSA checksum */ + ospf6_lsa_checksum(lsa_header); + + if (reason == OSPF6_GR_UNKNOWN_RESTART) { + struct ospf6_header *oh; + uint32_t *uv32; + int n; + uint16_t length = OSPF6_HEADER_SIZE + 4 + lsa_length; + struct iovec iovector[2] = {}; + + /* Reserve space for OSPFv3 header. */ + memmove(&buffer[OSPF6_HEADER_SIZE + 4], buffer, lsa_length); + + /* Fill in the OSPFv3 header. */ + oh = (struct ospf6_header *)buffer; + oh->version = OSPFV3_VERSION; + oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE; + oh->router_id = oi->area->ospf6->router_id; + oh->area_id = oi->area->area_id; + oh->instance_id = oi->instance_id; + oh->reserved = 0; + oh->length = htons(length); + + /* Fill LSA header. */ + uv32 = (uint32_t *)&buffer[sizeof(*oh)]; + *uv32 = htonl(1); + + /* Send packet. */ + iovector[0].iov_base = lsa_header; + iovector[0].iov_len = length; + n = ospf6_sendmsg(oi->linklocal_addr, &allspfrouters6, + oi->interface->ifindex, iovector, ospf6->fd); + if (n != length) + flog_err(EC_LIB_DEVELOPMENT, + "%s: could not send entire message", __func__); + } else { + /* Create and install LSA. */ + lsa = ospf6_lsa_create(lsa_header); + ospf6_lsa_originate_interface(lsa, oi); + } + + return 0; +} + +/* Flush all self-originated Grace-LSAs. */ +static void ospf6_gr_flush_grace_lsas(struct ospf6 *ospf6) +{ + struct ospf6_area *area; + struct listnode *anode; + + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, anode, area)) { + struct ospf6_lsa *lsa; + struct ospf6_interface *oi; + struct listnode *inode; + + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "GR: flushing self-originated Grace-LSAs [area %pI4]", + &area->area_id); + + for (ALL_LIST_ELEMENTS_RO(area->if_list, inode, oi)) { + lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_GRACE_LSA), + htonl(oi->interface->ifindex), + oi->area->ospf6->router_id, + oi->lsdb); + if (!lsa) { + zlog_warn( + "%s: Grace-LSA not found [interface %s] [area %pI4]", + __func__, oi->interface->name, + &area->area_id); + continue; + } + + ospf6_lsa_purge(lsa); + } + } +} + +/* Exit from the Graceful Restart mode. */ +static void ospf6_gr_restart_exit(struct ospf6 *ospf6, const char *reason) +{ + struct ospf6_area *area; + struct listnode *onode, *anode; + struct ospf6_route *route; + + if (IS_DEBUG_OSPF6_GR) + zlog_debug("GR: exiting graceful restart: %s", reason); + + ospf6->gr_info.restart_in_progress = false; + ospf6->gr_info.finishing_restart = true; + XFREE(MTYPE_TMP, ospf6->gr_info.exit_reason); + ospf6->gr_info.exit_reason = XSTRDUP(MTYPE_TMP, reason); + EVENT_OFF(ospf6->gr_info.t_grace_period); + + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, onode, area)) { + struct ospf6_interface *oi; + + /* + * 1) The router should reoriginate its router-LSAs for all + * attached areas in order to make sure they have the correct + * contents. + */ + OSPF6_ROUTER_LSA_EXECUTE(area); + + /* + * Force reorigination of intra-area-prefix-LSAs to handle + * areas without any full adjacency. + */ + OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB(area); + + for (ALL_LIST_ELEMENTS_RO(area->if_list, anode, oi)) { + /* Disable hello delay. */ + if (oi->gr.hello_delay.t_grace_send) { + oi->gr.hello_delay.elapsed_seconds = 0; + EVENT_OFF(oi->gr.hello_delay.t_grace_send); + event_add_event(master, ospf6_hello_send, oi, 0, + &oi->thread_send_hello); + } + + /* Reoriginate Link-LSA. */ + if (oi->type != OSPF_IFTYPE_VIRTUALLINK) + OSPF6_LINK_LSA_EXECUTE(oi); + + /* + * 2) The router should reoriginate network-LSAs on all + * segments where it is the Designated Router. + */ + if (oi->state == OSPF6_INTERFACE_DR) + OSPF6_NETWORK_LSA_EXECUTE(oi); + } + } + + /* + * While all self-originated NSSA and AS-external LSAs were already + * learned from the helping neighbors, we need to reoriginate them in + * order to ensure they will be refreshed periodically. + */ + for (route = ospf6_route_head(ospf6->external_table); route; + route = ospf6_route_next(route)) + ospf6_handle_external_lsa_origination(ospf6, route, + &route->prefix); + + /* + * 3) The router reruns its OSPF routing calculations, this time + * installing the results into the system forwarding table, and + * originating summary-LSAs, Type-7 LSAs and AS-external-LSAs as + * necessary. + * + * 4) Any remnant entries in the system forwarding table that were + * installed before the restart, but that are no longer valid, + * should be removed. + */ + ospf6_spf_schedule(ospf6, OSPF6_SPF_FLAGS_GR_FINISH); + + /* 6) Any grace-LSAs that the router originated should be flushed. */ + ospf6_gr_flush_grace_lsas(ospf6); +} + +/* Enter the Graceful Restart mode. */ +void ospf6_gr_restart_enter(struct ospf6 *ospf6, + enum ospf6_gr_restart_reason reason, + time_t timestamp) +{ + unsigned long remaining_time; + + ospf6->gr_info.restart_in_progress = true; + ospf6->gr_info.reason = reason; + + /* Schedule grace period timeout. */ + remaining_time = timestamp - time(NULL); + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "GR: remaining time until grace period expires: %lu(s)", + remaining_time); + + event_add_timer(master, ospf6_gr_grace_period_expired, ospf6, + remaining_time, &ospf6->gr_info.t_grace_period); +} + +#define RTR_LSA_MISSING 0 +#define RTR_LSA_ADJ_FOUND 1 +#define RTR_LSA_ADJ_NOT_FOUND 2 + +/* Check if a Router-LSA exists and if it contains a given link. */ +static int ospf6_router_lsa_contains_adj(struct ospf6_area *area, + in_addr_t adv_router, + in_addr_t neighbor_router_id) +{ + uint16_t type; + struct ospf6_lsa *lsa; + bool empty = true; + + type = ntohs(OSPF6_LSTYPE_ROUTER); + for (ALL_LSDB_TYPED_ADVRTR(area->lsdb, type, adv_router, lsa)) { + struct ospf6_router_lsa *router_lsa; + char *start, *end, *current; + + empty = false; + router_lsa = (struct ospf6_router_lsa + *)((char *)lsa->header + + sizeof(struct ospf6_lsa_header)); + + /* Iterate over all interfaces in the Router-LSA. */ + start = (char *)router_lsa + sizeof(struct ospf6_router_lsa); + end = (char *)lsa->header + ntohs(lsa->header->length); + for (current = start; + current + sizeof(struct ospf6_router_lsdesc) <= end; + current += sizeof(struct ospf6_router_lsdesc)) { + struct ospf6_router_lsdesc *lsdesc; + + lsdesc = (struct ospf6_router_lsdesc *)current; + if (lsdesc->type != OSPF6_ROUTER_LSDESC_POINTTOPOINT) + continue; + + if (lsdesc->neighbor_router_id == neighbor_router_id) { + ospf6_lsa_unlock(&lsa); + return RTR_LSA_ADJ_FOUND; + } + } + } + + if (empty) + return RTR_LSA_MISSING; + + return RTR_LSA_ADJ_NOT_FOUND; +} + +static bool ospf6_gr_check_router_lsa_consistency(struct ospf6 *ospf6, + struct ospf6_area *area, + struct ospf6_lsa *lsa) +{ + if (lsa->header->adv_router == ospf6->router_id) { + struct ospf6_router_lsa *router_lsa; + char *start, *end, *current; + + router_lsa = (struct ospf6_router_lsa + *)((char *)lsa->header + + sizeof(struct ospf6_lsa_header)); + + /* Iterate over all interfaces in the Router-LSA. */ + start = (char *)router_lsa + sizeof(struct ospf6_router_lsa); + end = (char *)lsa->header + ntohs(lsa->header->length); + for (current = start; + current + sizeof(struct ospf6_router_lsdesc) <= end; + current += sizeof(struct ospf6_router_lsdesc)) { + struct ospf6_router_lsdesc *lsdesc; + + lsdesc = (struct ospf6_router_lsdesc *)current; + if (lsdesc->type != OSPF6_ROUTER_LSDESC_POINTTOPOINT) + continue; + + if (ospf6_router_lsa_contains_adj( + area, lsdesc->neighbor_router_id, + ospf6->router_id) + == RTR_LSA_ADJ_NOT_FOUND) + return false; + } + } else { + int adj1, adj2; + + adj1 = ospf6_router_lsa_contains_adj(area, ospf6->router_id, + lsa->header->adv_router); + adj2 = ospf6_router_lsa_contains_adj( + area, lsa->header->adv_router, ospf6->router_id); + if ((adj1 == RTR_LSA_ADJ_FOUND && adj2 == RTR_LSA_ADJ_NOT_FOUND) + || (adj1 == RTR_LSA_ADJ_NOT_FOUND + && adj2 == RTR_LSA_ADJ_FOUND)) + return false; + } + + return true; +} + +/* + * Check for LSAs that are inconsistent with the pre-restart LSAs, and abort the + * ongoing graceful restart when that's the case. + */ +void ospf6_gr_check_lsdb_consistency(struct ospf6 *ospf6, + struct ospf6_area *area) +{ + uint16_t type; + struct ospf6_lsa *lsa; + + type = ntohs(OSPF6_LSTYPE_ROUTER); + for (ALL_LSDB_TYPED(area->lsdb, type, lsa)) { + if (!ospf6_gr_check_router_lsa_consistency(ospf6, area, lsa)) { + char reason[256]; + + snprintfrr(reason, sizeof(reason), + "detected inconsistent LSA %s [area %pI4]", + lsa->name, &area->area_id); + ospf6_gr_restart_exit(ospf6, reason); + return; + } + } +} + +/* Check if there's a fully formed adjacency with the given neighbor ID. */ +static bool ospf6_gr_check_adj_id(struct ospf6_area *area, + in_addr_t neighbor_router_id) +{ + struct ospf6_neighbor *nbr; + + nbr = ospf6_area_neighbor_lookup(area, neighbor_router_id); + if (!nbr || nbr->state < OSPF6_NEIGHBOR_FULL) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug("GR: missing adjacency to router %pI4", + &neighbor_router_id); + return false; + } + + return true; +} + +static bool ospf6_gr_check_adjs_lsa_transit(struct ospf6_area *area, + in_addr_t neighbor_router_id, + uint32_t neighbor_interface_id) +{ + struct ospf6 *ospf6 = area->ospf6; + + /* Check if we are the DR. */ + if (neighbor_router_id == ospf6->router_id) { + struct ospf6_lsa *lsa; + char *start, *end, *current; + struct ospf6_network_lsa *network_lsa; + struct ospf6_network_lsdesc *lsdesc; + + /* Lookup Network LSA corresponding to this interface. */ + lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_NETWORK), + neighbor_interface_id, + neighbor_router_id, area->lsdb); + if (!lsa) + return false; + + /* Iterate over all routers present in the network. */ + network_lsa = (struct ospf6_network_lsa + *)((char *)lsa->header + + sizeof(struct ospf6_lsa_header)); + start = (char *)network_lsa + sizeof(struct ospf6_network_lsa); + end = (char *)lsa->header + ntohs(lsa->header->length); + for (current = start; + current + sizeof(struct ospf6_network_lsdesc) <= end; + current += sizeof(struct ospf6_network_lsdesc)) { + lsdesc = (struct ospf6_network_lsdesc *)current; + + /* Skip self in the pseudonode. */ + if (lsdesc->router_id == ospf6->router_id) + continue; + + /* + * Check if there's a fully formed adjacency with this + * router. + */ + if (!ospf6_gr_check_adj_id(area, lsdesc->router_id)) + return false; + } + } else { + struct ospf6_neighbor *nbr; + + /* Check if there's a fully formed adjacency with the DR. */ + nbr = ospf6_area_neighbor_lookup(area, neighbor_router_id); + if (!nbr || nbr->state < OSPF6_NEIGHBOR_FULL) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "GR: missing adjacency to DR router %pI4", + &neighbor_router_id); + return false; + } + } + + return true; +} + +static bool ospf6_gr_check_adjs_lsa(struct ospf6_area *area, + struct ospf6_lsa *lsa) +{ + struct ospf6_router_lsa *router_lsa; + char *start, *end, *current; + + router_lsa = + (struct ospf6_router_lsa *)((char *)lsa->header + + sizeof(struct ospf6_lsa_header)); + + /* Iterate over all interfaces in the Router-LSA. */ + start = (char *)router_lsa + sizeof(struct ospf6_router_lsa); + end = (char *)lsa->header + ntohs(lsa->header->length); + for (current = start; + current + sizeof(struct ospf6_router_lsdesc) <= end; + current += sizeof(struct ospf6_router_lsdesc)) { + struct ospf6_router_lsdesc *lsdesc; + + lsdesc = (struct ospf6_router_lsdesc *)current; + switch (lsdesc->type) { + case OSPF6_ROUTER_LSDESC_POINTTOPOINT: + if (!ospf6_gr_check_adj_id(area, + lsdesc->neighbor_router_id)) + return false; + break; + case OSPF6_ROUTER_LSDESC_TRANSIT_NETWORK: + if (!ospf6_gr_check_adjs_lsa_transit( + area, lsdesc->neighbor_router_id, + lsdesc->neighbor_interface_id)) + return false; + break; + default: + break; + } + } + + return true; +} + +/* + * Check if all adjacencies prior to the restart were reestablished. + * + * This is done using pre-restart Router LSAs and pre-restart Network LSAs + * received from the helping neighbors. + */ +static bool ospf6_gr_check_adjs(struct ospf6 *ospf6) +{ + struct ospf6_area *area; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, area)) { + uint16_t type; + uint32_t router; + struct ospf6_lsa *lsa_self; + bool found = false; + + type = ntohs(OSPF6_LSTYPE_ROUTER); + router = ospf6->router_id; + for (ALL_LSDB_TYPED_ADVRTR(area->lsdb, type, router, + lsa_self)) { + found = true; + if (!ospf6_gr_check_adjs_lsa(area, lsa_self)) { + ospf6_lsa_unlock(&lsa_self); + return false; + } + } + if (!found) + return false; + } + + return true; +} + +/* Handling of grace period expiry. */ +static void ospf6_gr_grace_period_expired(struct event *thread) +{ + struct ospf6 *ospf6 = EVENT_ARG(thread); + + ospf6_gr_restart_exit(ospf6, "grace period has expired"); +} + +/* Send extra Grace-LSA out the interface (unplanned outages only). */ +void ospf6_gr_iface_send_grace_lsa(struct event *thread) +{ + struct ospf6_interface *oi = EVENT_ARG(thread); + + ospf6_gr_lsa_originate(oi, oi->area->ospf6->gr_info.reason); + + if (++oi->gr.hello_delay.elapsed_seconds < oi->gr.hello_delay.interval) + event_add_timer(master, ospf6_gr_iface_send_grace_lsa, oi, 1, + &oi->gr.hello_delay.t_grace_send); + else + event_add_event(master, ospf6_hello_send, oi, 0, + &oi->thread_send_hello); +} + +/* + * Record in non-volatile memory that the given OSPF instance is attempting to + * perform a graceful restart. + */ +static void ospf6_gr_nvm_update(struct ospf6 *ospf6, bool prepare) +{ + const char *inst_name; + json_object *json; + json_object *json_instances; + json_object *json_instance; + + inst_name = ospf6->name ? ospf6->name : VRF_DEFAULT_NAME; + + json = json_object_from_file((char *)OSPF6D_GR_STATE); + if (json == NULL) + json = json_object_new_object(); + + json_object_object_get_ex(json, "instances", &json_instances); + if (!json_instances) { + json_instances = json_object_new_object(); + json_object_object_add(json, "instances", json_instances); + } + + json_object_object_get_ex(json_instances, inst_name, &json_instance); + if (!json_instance) { + json_instance = json_object_new_object(); + json_object_object_add(json_instances, inst_name, + json_instance); + } + + json_object_int_add(json_instance, "gracePeriod", + ospf6->gr_info.grace_period); + + /* + * Record not only the grace period, but also a UNIX timestamp + * corresponding to the end of that period. That way, once ospf6d is + * restarted, it will be possible to take into account the time that + * passed while ospf6d wasn't running. + */ + if (prepare) + json_object_int_add(json_instance, "timestamp", + time(NULL) + ospf6->gr_info.grace_period); + + json_object_to_file_ext((char *)OSPF6D_GR_STATE, json, + JSON_C_TO_STRING_PRETTY); + json_object_free(json); +} + +/* + * Delete GR status information about the given OSPF instance from non-volatile + * memory. + */ +void ospf6_gr_nvm_delete(struct ospf6 *ospf6) +{ + const char *inst_name; + json_object *json; + json_object *json_instances; + + inst_name = ospf6->name ? ospf6->name : VRF_DEFAULT_NAME; + + json = json_object_from_file((char *)OSPF6D_GR_STATE); + if (json == NULL) + json = json_object_new_object(); + + json_object_object_get_ex(json, "instances", &json_instances); + if (!json_instances) { + json_instances = json_object_new_object(); + json_object_object_add(json, "instances", json_instances); + } + + json_object_object_del(json_instances, inst_name); + + json_object_to_file_ext((char *)OSPF6D_GR_STATE, json, + JSON_C_TO_STRING_PRETTY); + json_object_free(json); +} + +/* + * Fetch from non-volatile memory whether the given OSPF instance is performing + * a graceful shutdown or not. + */ +void ospf6_gr_nvm_read(struct ospf6 *ospf6) +{ + const char *inst_name; + json_object *json; + json_object *json_instances; + json_object *json_instance; + json_object *json_timestamp; + json_object *json_grace_period; + time_t timestamp = 0; + + inst_name = ospf6->name ? ospf6->name : VRF_DEFAULT_NAME; + + json = json_object_from_file((char *)OSPF6D_GR_STATE); + if (json == NULL) + json = json_object_new_object(); + + json_object_object_get_ex(json, "instances", &json_instances); + if (!json_instances) { + json_instances = json_object_new_object(); + json_object_object_add(json, "instances", json_instances); + } + + json_object_object_get_ex(json_instances, inst_name, &json_instance); + if (!json_instance) { + json_instance = json_object_new_object(); + json_object_object_add(json_instances, inst_name, + json_instance); + } + + json_object_object_get_ex(json_instance, "gracePeriod", + &json_grace_period); + json_object_object_get_ex(json_instance, "timestamp", &json_timestamp); + if (json_timestamp) { + time_t now; + + /* Planned GR: check if the grace period has already expired. */ + now = time(NULL); + timestamp = json_object_get_int(json_timestamp); + if (now > timestamp) { + ospf6_gr_restart_exit( + ospf6, "grace period has expired already"); + } else + ospf6_gr_restart_enter(ospf6, OSPF6_GR_SW_RESTART, + timestamp); + } else if (json_grace_period) { + uint32_t grace_period; + + /* + * Unplanned GR: the Grace-LSAs will be sent later as soon as + * the interfaces are operational. + */ + grace_period = json_object_get_int(json_grace_period); + ospf6->gr_info.grace_period = grace_period; + ospf6_gr_restart_enter(ospf6, OSPF6_GR_UNKNOWN_RESTART, + time(NULL) + + ospf6->gr_info.grace_period); + } + + json_object_object_del(json_instances, inst_name); + + json_object_to_file_ext((char *)OSPF6D_GR_STATE, json, + JSON_C_TO_STRING_PRETTY); + json_object_free(json); +} + +void ospf6_gr_unplanned_start_interface(struct ospf6_interface *oi) +{ + /* + * Can't check OSPF interface state as the OSPF instance might not be + * enabled yet. + */ + if (!if_is_operative(oi->interface) || if_is_loopback(oi->interface)) + return; + + /* Send Grace-LSA. */ + ospf6_gr_lsa_originate(oi, oi->area->ospf6->gr_info.reason); + + /* Start GR hello-delay interval. */ + oi->gr.hello_delay.elapsed_seconds = 0; + event_add_timer(master, ospf6_gr_iface_send_grace_lsa, oi, 1, + &oi->gr.hello_delay.t_grace_send); +} + +/* Prepare to start a Graceful Restart. */ +static void ospf6_gr_prepare(void) +{ + struct ospf6 *ospf6; + struct ospf6_interface *oi; + struct listnode *onode, *anode, *inode; + + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, onode, ospf6)) { + struct ospf6_area *area; + + if (!ospf6->gr_info.restart_support + || ospf6->gr_info.prepare_in_progress) + continue; + + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "GR: preparing to perform a graceful restart [period %u second(s)] [vrf %s]", + ospf6->gr_info.grace_period, + ospf6_vrf_id_to_name(ospf6->vrf_id)); + + /* Send a Grace-LSA to all neighbors. */ + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, anode, area)) { + for (ALL_LIST_ELEMENTS_RO(area->if_list, inode, oi)) { + if (oi->state < OSPF6_INTERFACE_POINTTOPOINT) + continue; + ospf6_gr_lsa_originate(oi, OSPF6_GR_SW_RESTART); + } + } + + /* Record end of the grace period in non-volatile memory. */ + ospf6_gr_nvm_update(ospf6, true); + + /* + * Mark that a Graceful Restart preparation is in progress, to + * prevent ospf6d from flushing its self-originated LSAs on + * exit. + */ + ospf6->gr_info.prepare_in_progress = true; + } +} + +static int ospf6_gr_neighbor_change(struct ospf6_neighbor *on, int next_state, + int prev_state) +{ + struct ospf6 *ospf6 = on->ospf6_if->area->ospf6; + + if (next_state == OSPF6_NEIGHBOR_FULL + && ospf6->gr_info.restart_in_progress) { + if (ospf6_gr_check_adjs(ospf6)) { + ospf6_gr_restart_exit( + ospf6, "all adjacencies were reestablished"); + } else { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "GR: not all adjacencies were reestablished yet"); + } + } + + return 0; +} + +int config_write_ospf6_gr(struct vty *vty, struct ospf6 *ospf6) +{ + if (!ospf6->gr_info.restart_support) + return 0; + + if (ospf6->gr_info.grace_period == OSPF6_DFLT_GRACE_INTERVAL) + vty_out(vty, " graceful-restart\n"); + else + vty_out(vty, " graceful-restart grace-period %u\n", + ospf6->gr_info.grace_period); + + return 0; +} + +DEFPY(ospf6_graceful_restart_prepare, ospf6_graceful_restart_prepare_cmd, + "graceful-restart prepare ipv6 ospf", + "Graceful Restart commands\n" + "Prepare upcoming graceful restart\n" IPV6_STR + "Prepare to restart the OSPFv3 process\n") +{ + ospf6_gr_prepare(); + + return CMD_SUCCESS; +} + +DEFPY(ospf6_graceful_restart, ospf6_graceful_restart_cmd, + "graceful-restart [grace-period (1-1800)$grace_period]", + OSPF_GR_STR + "Maximum length of the 'grace period'\n" + "Maximum length of the 'grace period' in seconds\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf6, ospf6); + + /* Check and get restart period if present. */ + if (!grace_period_str) + grace_period = OSPF6_DFLT_GRACE_INTERVAL; + + ospf6->gr_info.restart_support = true; + ospf6->gr_info.grace_period = grace_period; + + /* Freeze OSPF routes in the RIB. */ + (void)ospf6_zebra_gr_enable(ospf6, ospf6->gr_info.grace_period); + + /* Record that GR is enabled in non-volatile memory. */ + ospf6_gr_nvm_update(ospf6, false); + + return CMD_SUCCESS; +} + +DEFPY(ospf6_no_graceful_restart, ospf6_no_graceful_restart_cmd, + "no graceful-restart [period (1-1800)]", + NO_STR OSPF_GR_STR + "Maximum length of the 'grace period'\n" + "Maximum length of the 'grace period' in seconds\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf6, ospf6); + + if (!ospf6->gr_info.restart_support) + return CMD_SUCCESS; + + if (ospf6->gr_info.prepare_in_progress) { + vty_out(vty, + "%% Error: Graceful Restart preparation in progress\n"); + return CMD_WARNING; + } + + ospf6->gr_info.restart_support = false; + ospf6->gr_info.grace_period = OSPF6_DFLT_GRACE_INTERVAL; + ospf6_gr_nvm_delete(ospf6); + ospf6_zebra_gr_disable(ospf6); + + return CMD_SUCCESS; +} + +void ospf6_gr_init(void) +{ + hook_register(ospf6_neighbor_change, ospf6_gr_neighbor_change); + + install_element(ENABLE_NODE, &ospf6_graceful_restart_prepare_cmd); + install_element(OSPF6_NODE, &ospf6_graceful_restart_cmd); + install_element(OSPF6_NODE, &ospf6_no_graceful_restart_cmd); +} diff --git a/ospf6d/ospf6_gr.h b/ospf6d/ospf6_gr.h new file mode 100644 index 0000000..84ef3ae --- /dev/null +++ b/ospf6d/ospf6_gr.h @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPF6 Graceful Retsart helper functions. + * + * Copyright (C) 2021-22 Vmware, Inc. + * Rajesh Kumar Girada + */ + +#ifndef OSPF6_GR_H +#define OSPF6_GR_H + +#define OSPF6_GR_NOT_HELPER 0 +#define OSPF6_GR_ACTIVE_HELPER 1 + +#define OSPF6_GR_HELPER_NO_LSACHECK 0 +#define OSPF6_GR_HELPER_LSACHECK 1 + +#define OSPF6_MAX_GRACE_INTERVAL 1800 +#define OSPF6_MIN_GRACE_INTERVAL 1 +#define OSPF6_DFLT_GRACE_INTERVAL 120 + +/* Forward declaration(s). */ +struct ospf6_neighbor; + +/* Debug option */ +extern unsigned char conf_debug_ospf6_gr; + +#define OSPF6_DEBUG_GR 0x01 + +#define OSPF6_DEBUG_GR_ON() (conf_debug_ospf6_gr |= OSPF6_DEBUG_GR) + +#define OSPF6_DEBUG_GR_OFF() (conf_debug_ospf6_gr &= ~OSPF6_DEBUG_GR) + +#define IS_DEBUG_OSPF6_GR conf_debug_ospf6_gr + + +enum ospf6_helper_exit_reason { + OSPF6_GR_HELPER_EXIT_NONE = 0, + OSPF6_GR_HELPER_INPROGRESS, + OSPF6_GR_HELPER_TOPO_CHG, + OSPF6_GR_HELPER_GRACE_TIMEOUT, + OSPF6_GR_HELPER_COMPLETED +}; + +enum ospf6_gr_restart_reason { + OSPF6_GR_UNKNOWN_RESTART = 0, + OSPF6_GR_SW_RESTART = 1, + OSPF6_GR_SW_UPGRADE = 2, + OSPF6_GR_SWITCH_REDUNDANT_CARD = 3, + OSPF6_GR_INVALID_REASON_CODE = 4 +}; + +enum ospf6_gr_helper_rejected_reason { + OSPF6_HELPER_REJECTED_NONE, + OSPF6_HELPER_SUPPORT_DISABLED, + OSPF6_HELPER_NOT_A_VALID_NEIGHBOUR, + OSPF6_HELPER_PLANNED_ONLY_RESTART, + OSPF6_HELPER_TOPO_CHANGE_RTXMT_LIST, + OSPF6_HELPER_LSA_AGE_MORE, + OSPF6_HELPER_RESTARTING, +}; + +#ifdef roundup +#define ROUNDUP(val, gran) roundup(val, gran) +#else /* roundup */ +#define ROUNDUP(val, gran) (((val)-1 | (gran)-1) + 1) +#endif /* roundup */ + +/* + * Generic TLV (type, length, value) macros + */ +struct tlv_header { + uint16_t type; /* Type of Value */ + uint16_t length; /* Length of Value portion only, in bytes */ +}; + +#define TLV_HDR_SIZE (sizeof(struct tlv_header)) + +#define TLV_BODY_SIZE(tlvh) (ROUNDUP(ntohs((tlvh)->length), sizeof(uint32_t))) + +#define TLV_SIZE(tlvh) (uint32_t)(TLV_HDR_SIZE + TLV_BODY_SIZE(tlvh)) + +#define TLV_HDR_TOP(lsah) \ + (struct tlv_header *)((char *)(lsah) + OSPF6_LSA_HEADER_SIZE) + +#define TLV_HDR_NEXT(tlvh) \ + (struct tlv_header *)((char *)(tlvh) + TLV_SIZE(tlvh)) + +/* Ref RFC5187 appendix-A */ +/* Grace period TLV */ +#define GRACE_PERIOD_TYPE 1 +#define GRACE_PERIOD_LENGTH 4 +struct grace_tlv_graceperiod { + struct tlv_header header; + uint32_t interval; +}; +#define GRACE_PERIOD_TLV_SIZE sizeof(struct grace_tlv_graceperiod) + +/* Restart reason TLV */ +#define RESTART_REASON_TYPE 2 +#define RESTART_REASON_LENGTH 1 +struct grace_tlv_restart_reason { + struct tlv_header header; + uint8_t reason; + uint8_t reserved[3]; +}; +#define GRACE_RESTART_REASON_TLV_SIZE sizeof(struct grace_tlv_restart_reason) + +#define OSPF6_GRACE_LSA_MIN_SIZE \ + GRACE_PERIOD_TLV_SIZE + GRACE_RESTART_REASON_TLV_SIZE + +struct ospf6_grace_lsa { + struct grace_tlv_graceperiod tlv_period; + struct grace_tlv_restart_reason tlv_reason; +}; + +struct advRtr { + in_addr_t advRtrAddr; +}; + +#define OSPF6_HELPER_ENABLE_RTR_COUNT(ospf) \ + (ospf6->ospf6_helper_cfg.enable_rtr_list->count) + +/* Check , it is a planned restart */ +#define OSPF6_GR_IS_PLANNED_RESTART(reason) \ + ((reason == OSPF6_GR_SW_RESTART) || (reason == OSPF6_GR_SW_UPGRADE)) + +/* Check the router is HELPER for current neighbour */ +#define OSPF6_GR_IS_ACTIVE_HELPER(N) \ + ((N)->gr_helper_info.gr_helper_status == OSPF6_GR_ACTIVE_HELPER) + +/* Check the LSA is GRACE LSA */ +#define IS_GRACE_LSA(lsa) (ntohs(lsa->header->type) == OSPF6_LSTYPE_GRACE_LSA) + +/* Check neighbour is in FULL state */ +#define IS_NBR_STATE_FULL(nbr) (nbr->state == OSPF6_NEIGHBOR_FULL) + +extern const char *ospf6_exit_reason_desc[]; +extern const char *ospf6_restart_reason_desc[]; +extern const char *ospf6_rejected_reason_desc[]; + +extern void ospf6_gr_helper_config_init(void); +extern void ospf6_gr_helper_init(struct ospf6 *ospf6); +extern void ospf6_gr_helper_deinit(struct ospf6 *ospf6); +extern void ospf6_gr_helper_exit(struct ospf6_neighbor *nbr, + enum ospf6_helper_exit_reason reason); +extern int ospf6_process_grace_lsa(struct ospf6 *ospf6, struct ospf6_lsa *lsa, + struct ospf6_neighbor *nbr); +extern void ospf6_process_maxage_grace_lsa(struct ospf6 *ospf, + struct ospf6_lsa *lsa, + struct ospf6_neighbor *nbr); +extern void ospf6_helper_handle_topo_chg(struct ospf6 *ospf6, + struct ospf6_lsa *lsa); +extern int config_write_ospf6_gr(struct vty *vty, struct ospf6 *ospf6); +extern int config_write_ospf6_gr_helper(struct vty *vty, struct ospf6 *ospf6); +extern int config_write_ospf6_debug_gr_helper(struct vty *vty); + +extern void ospf6_gr_iface_send_grace_lsa(struct event *thread); +extern void ospf6_gr_restart_enter(struct ospf6 *ospf6, + enum ospf6_gr_restart_reason reason, + time_t timestamp); +extern void ospf6_gr_check_lsdb_consistency(struct ospf6 *ospf, + struct ospf6_area *area); +extern void ospf6_gr_nvm_read(struct ospf6 *ospf); +extern void ospf6_gr_nvm_delete(struct ospf6 *ospf6); +extern void ospf6_gr_unplanned_start_interface(struct ospf6_interface *oi); +extern void ospf6_gr_init(void); + +#endif /* OSPF6_GR_H */ diff --git a/ospf6d/ospf6_gr_helper.c b/ospf6d/ospf6_gr_helper.c new file mode 100644 index 0000000..be1042f --- /dev/null +++ b/ospf6d/ospf6_gr_helper.c @@ -0,0 +1,1416 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPF6 Graceful Restart helper functions. + * + * Copyright (C) 2021-22 Vmware, Inc. + * Rajesh Kumar Girada + */ + +#include <zebra.h> + +#include "log.h" +#include "vty.h" +#include "command.h" +#include "prefix.h" +#include "stream.h" +#include "zclient.h" +#include "memory.h" +#include "table.h" +#include "lib/bfd.h" +#include "lib_errors.h" +#include "jhash.h" + +#include "ospf6_proto.h" +#include "ospf6_lsa.h" +#include "ospf6_lsdb.h" +#include "ospf6_route.h" +#include "ospf6_message.h" + +#include "ospf6_top.h" +#include "ospf6_area.h" +#include "ospf6_interface.h" +#include "ospf6_neighbor.h" +#include "ospf6_intra.h" +#include "ospf6d.h" +#include "ospf6_gr.h" +#include "lib/json.h" +#include "ospf6d/ospf6_gr_helper_clippy.c" + +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_GR_HELPER, "OSPF6 Graceful restart helper"); + +unsigned char conf_debug_ospf6_gr; + +static int ospf6_grace_lsa_show_info(struct vty *vty, struct ospf6_lsa *lsa, + json_object *json, bool use_json); + +struct ospf6_lsa_handler grace_lsa_handler = {.lh_type = OSPF6_LSTYPE_GRACE_LSA, + .lh_name = "Grace", + .lh_short_name = "GR", + .lh_show = + ospf6_grace_lsa_show_info, + .lh_get_prefix_str = NULL, + .lh_debug = 0}; + +const char *ospf6_exit_reason_desc[] = { + "Unknown reason", + "Helper in progress", + "Topology Change", + "Grace timer expiry", + "Successful graceful restart", +}; + +const char *ospf6_restart_reason_desc[] = { + "Unknown restart", + "Software restart", + "Software reload/upgrade", + "Switch to redundant control processor", +}; + +const char *ospf6_rejected_reason_desc[] = { + "Unknown reason", + "Helper support disabled", + "Neighbour is not in FULL state", + "Supports only planned restart but received for unplanned", + "Topo change due to change in lsa rxmt list", + "LSA age is more than Grace interval", +}; + +static unsigned int ospf6_enable_rtr_hash_key(const void *data) +{ + const struct advRtr *rtr = data; + + return jhash_1word(rtr->advRtrAddr, 0); +} + +static bool ospf6_enable_rtr_hash_cmp(const void *d1, const void *d2) +{ + const struct advRtr *rtr1 = d1; + const struct advRtr *rtr2 = d2; + + return (rtr1->advRtrAddr == rtr2->advRtrAddr); +} + +static void *ospf6_enable_rtr_hash_alloc(void *p) +{ + struct advRtr *rid; + + rid = XCALLOC(MTYPE_OSPF6_GR_HELPER, sizeof(struct advRtr)); + rid->advRtrAddr = ((struct advRtr *)p)->advRtrAddr; + + return rid; +} + +static void ospf6_disable_rtr_hash_free(void *rtr) +{ + XFREE(MTYPE_OSPF6_GR_HELPER, rtr); +} + +static void ospf6_enable_rtr_hash_destroy(struct ospf6 *ospf6) +{ + if (ospf6->ospf6_helper_cfg.enable_rtr_list == NULL) + return; + + hash_clean_and_free(&ospf6->ospf6_helper_cfg.enable_rtr_list, + ospf6_disable_rtr_hash_free); +} + +/* + * Extracting tlv info from GRACE LSA. + * + * lsa + * ospf6 grace lsa + * + * Returns: + * interval : grace interval. + * reason : Restarting reason. + */ +static int ospf6_extract_grace_lsa_fields(struct ospf6_lsa *lsa, + uint32_t *interval, uint8_t *reason) +{ + struct ospf6_lsa_header *lsah = NULL; + struct tlv_header *tlvh = NULL; + struct grace_tlv_graceperiod *gracePeriod; + struct grace_tlv_restart_reason *grReason; + uint16_t length = 0; + int sum = 0; + + lsah = lsa->header; + if (ntohs(lsah->length) <= OSPF6_LSA_HEADER_SIZE) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug("%s: undersized (%u B) lsa", __func__, + ntohs(lsah->length)); + return OSPF6_FAILURE; + } + + length = ntohs(lsah->length) - OSPF6_LSA_HEADER_SIZE; + + for (tlvh = TLV_HDR_TOP(lsah); sum < length && tlvh; + tlvh = TLV_HDR_NEXT(tlvh)) { + + /* Check TLV len against overall LSA */ + if (sum + TLV_SIZE(tlvh) > length) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "%s: Malformed packet: Invalid TLV len:%d", + __func__, TLV_SIZE(tlvh)); + return OSPF6_FAILURE; + } + + switch (ntohs(tlvh->type)) { + case GRACE_PERIOD_TYPE: + gracePeriod = (struct grace_tlv_graceperiod *)tlvh; + *interval = ntohl(gracePeriod->interval); + sum += TLV_SIZE(tlvh); + + /* Check if grace interval is valid */ + if (*interval > OSPF6_MAX_GRACE_INTERVAL + || *interval < OSPF6_MIN_GRACE_INTERVAL) + return OSPF6_FAILURE; + break; + case RESTART_REASON_TYPE: + grReason = (struct grace_tlv_restart_reason *)tlvh; + *reason = grReason->reason; + sum += TLV_SIZE(tlvh); + + if (*reason >= OSPF6_GR_INVALID_REASON_CODE) + return OSPF6_FAILURE; + break; + default: + if (IS_DEBUG_OSPF6_GR) + zlog_debug("%s, Ignoring unknown TLV type:%d", + __func__, ntohs(tlvh->type)); + } + } + + return OSPF6_SUCCESS; +} + +/* + * Grace timer expiry handler. + * HELPER aborts its role at grace timer expiry. + * + * thread + * thread pointer + * + * Returns: + * Nothing + */ +static void ospf6_handle_grace_timer_expiry(struct event *thread) +{ + struct ospf6_neighbor *nbr = EVENT_ARG(thread); + + ospf6_gr_helper_exit(nbr, OSPF6_GR_HELPER_GRACE_TIMEOUT); +} + +/* + * API to check any change in the neighbor's + * retransmission list. + * + * nbr + * ospf6 neighbor + * + * Returns: + * TRUE - if any change in the lsa. + * FALSE - no change in the lsas. + */ +static bool ospf6_check_chg_in_rxmt_list(struct ospf6_neighbor *nbr) +{ + struct ospf6_lsa *lsa, *lsanext; + + for (ALL_LSDB(nbr->retrans_list, lsa, lsanext)) { + struct ospf6_lsa *lsa_in_db = NULL; + + /* Fetching the same copy of LSA form LSDB to validate the + * topochange. + */ + lsa_in_db = + ospf6_lsdb_lookup(lsa->header->type, lsa->header->id, + lsa->header->adv_router, lsa->lsdb); + + if (lsa_in_db && lsa_in_db->tobe_acknowledged) { + ospf6_lsa_unlock(&lsa); + if (lsanext) + ospf6_lsa_unlock(&lsanext); + + return OSPF6_TRUE; + } + } + + return OSPF6_FALSE; +} + +/* + * Process Grace LSA.If it is eligible move to HELPER role. + * Ref rfc3623 section 3.1 and rfc5187 + * + * ospf + * Ospf6 pointer. + * + * lsa + * Grace LSA received from RESTARTER. + * + * restarter + * ospf6 neighbour which requests the router to act as + * HELPER. + * + * Returns: + * status. + * If supported as HELPER : OSPF_GR_HELPER_INPROGRESS + * If Not supported as HELPER : OSPF_GR_HELPER_NONE + */ +int ospf6_process_grace_lsa(struct ospf6 *ospf6, struct ospf6_lsa *lsa, + struct ospf6_neighbor *restarter) +{ + uint8_t restart_reason = 0; + uint32_t grace_interval = 0; + uint32_t actual_grace_interval = 0; + struct advRtr lookup; + int ret; + + /* Extract the grace lsa packet fields */ + ret = ospf6_extract_grace_lsa_fields(lsa, &grace_interval, + &restart_reason); + if (ret != OSPF6_SUCCESS) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug("%s, Wrong Grace LSA packet.", __func__); + return OSPF6_GR_NOT_HELPER; + } + + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "%s, Grace LSA received from %s(%pI4), grace interval:%u, restart reason:%s", + __func__, restarter->name, &restarter->router_id, + grace_interval, + ospf6_restart_reason_desc[restart_reason]); + + /* Verify Helper enabled globally */ + if (!ospf6->ospf6_helper_cfg.is_helper_supported) { + /* Verify Helper support is enabled for the + * current neighbour router-id. + */ + lookup.advRtrAddr = restarter->router_id; + + if (!hash_lookup(ospf6->ospf6_helper_cfg.enable_rtr_list, + &lookup)) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "%s, HELPER support is disabled, So not a HELPER", + __func__); + restarter->gr_helper_info.rejected_reason = + OSPF6_HELPER_SUPPORT_DISABLED; + return OSPF6_GR_NOT_HELPER; + } + } + + /* Check neighbour is in FULL state and + * became a adjacency. + */ + if (!IS_NBR_STATE_FULL(restarter)) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "%s, This Neighbour %pI6 is not in FULL state.", + __func__, &restarter->linklocal_addr); + restarter->gr_helper_info.rejected_reason = + OSPF6_HELPER_NOT_A_VALID_NEIGHBOUR; + return OSPF6_GR_NOT_HELPER; + } + + /* Based on the restart reason from grace lsa + * check the current router is supporting or not + */ + if (ospf6->ospf6_helper_cfg.only_planned_restart + && !OSPF6_GR_IS_PLANNED_RESTART(restart_reason)) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "%s, Router supports only planned restarts but received the GRACE LSA due to an unplanned restart", + __func__); + restarter->gr_helper_info.rejected_reason = + OSPF6_HELPER_PLANNED_ONLY_RESTART; + return OSPF6_GR_NOT_HELPER; + } + + /* Check the retransmission list of this + * neighbour, check any change in lsas. + */ + if (ospf6->ospf6_helper_cfg.strict_lsa_check + && restarter->retrans_list->count + && ospf6_check_chg_in_rxmt_list(restarter)) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "%s, Changed LSA in Rxmt list.So not Helper.", + __func__); + restarter->gr_helper_info.rejected_reason = + OSPF6_HELPER_TOPO_CHANGE_RTXMT_LIST; + return OSPF6_GR_NOT_HELPER; + } + + /* LSA age must be less than the grace period */ + if (ntohs(lsa->header->age) >= grace_interval) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "%s, Grace LSA age(%d) is more than the grace interval(%d)", + __func__, lsa->header->age, grace_interval); + restarter->gr_helper_info.rejected_reason = + OSPF6_HELPER_LSA_AGE_MORE; + return OSPF6_GR_NOT_HELPER; + } + + if (ospf6->gr_info.restart_in_progress) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "%s: router is in the process of graceful restart", + __func__); + restarter->gr_helper_info.rejected_reason = + OSPF6_HELPER_RESTARTING; + return OSPF6_GR_NOT_HELPER; + } + + /* check supported grace period configured + * if configured, use this to start the grace + * timer otherwise use the interval received + * in grace LSA packet. + */ + actual_grace_interval = grace_interval; + if (grace_interval > ospf6->ospf6_helper_cfg.supported_grace_time) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "%s, Received grace period %d is larger than supported grace %d", + __func__, grace_interval, + ospf6->ospf6_helper_cfg.supported_grace_time); + actual_grace_interval = + ospf6->ospf6_helper_cfg.supported_grace_time; + } + + if (OSPF6_GR_IS_ACTIVE_HELPER(restarter)) { + EVENT_OFF(restarter->gr_helper_info.t_grace_timer); + + if (ospf6->ospf6_helper_cfg.active_restarter_cnt > 0) + ospf6->ospf6_helper_cfg.active_restarter_cnt--; + + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "%s, Router is already acting as a HELPER for this nbr,so restart the grace timer", + __func__); + } else { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "%s, This Router becomes a HELPER for the neighbour %pI6", + __func__, &restarter->linklocal_addr); + } + + /* Became a Helper to the RESTART neighbour. + * change the helper status. + */ + restarter->gr_helper_info.gr_helper_status = OSPF6_GR_ACTIVE_HELPER; + restarter->gr_helper_info.recvd_grace_period = grace_interval; + restarter->gr_helper_info.actual_grace_period = actual_grace_interval; + restarter->gr_helper_info.gr_restart_reason = restart_reason; + restarter->gr_helper_info.rejected_reason = OSPF6_HELPER_REJECTED_NONE; + + /* Increment the active restart nbr count */ + ospf6->ospf6_helper_cfg.active_restarter_cnt++; + + if (IS_DEBUG_OSPF6_GR) + zlog_debug("%s, Grace timer started.interval:%u", __func__, + actual_grace_interval); + + /* Start the grace timer */ + event_add_timer(master, ospf6_handle_grace_timer_expiry, restarter, + actual_grace_interval, + &restarter->gr_helper_info.t_grace_timer); + + return OSPF6_GR_ACTIVE_HELPER; +} + +/* + * Api to exit from HELPER role to take all actions + * required at exit. + * Ref rfc3623 section 3. and rfc51872 + * + * ospf6 + * Ospf6 pointer. + * + * nbr + * Ospf6 neighbour for which it is acting as HELPER. + * + * reason + * The reason for exiting from HELPER. + * + * Returns: + * Nothing. + */ +void ospf6_gr_helper_exit(struct ospf6_neighbor *nbr, + enum ospf6_helper_exit_reason reason) +{ + struct ospf6_interface *oi = nbr->ospf6_if; + struct ospf6 *ospf6; + + if (!oi) + return; + + ospf6 = oi->area->ospf6; + + if (!OSPF6_GR_IS_ACTIVE_HELPER(nbr)) + return; + + if (IS_DEBUG_OSPF6_GR) + zlog_debug("%s, Exiting from HELPER support to %pI6, due to %s", + __func__, &nbr->linklocal_addr, + ospf6_exit_reason_desc[reason]); + + /* Reset helper status*/ + nbr->gr_helper_info.gr_helper_status = OSPF6_GR_NOT_HELPER; + nbr->gr_helper_info.helper_exit_reason = reason; + nbr->gr_helper_info.actual_grace_period = 0; + nbr->gr_helper_info.recvd_grace_period = 0; + nbr->gr_helper_info.gr_restart_reason = 0; + ospf6->ospf6_helper_cfg.last_exit_reason = reason; + + /* If the exit not triggered due to grace timer + * expiry, stop the grace timer. + */ + if (reason != OSPF6_GR_HELPER_GRACE_TIMEOUT) + EVENT_OFF(nbr->gr_helper_info.t_grace_timer); + + if (ospf6->ospf6_helper_cfg.active_restarter_cnt <= 0) { + zlog_err( + "OSPF6 GR-Helper: Number of active Restarters should be greater than zero."); + return; + } + /* Decrement active restarter count */ + ospf6->ospf6_helper_cfg.active_restarter_cnt--; + + /* check exit triggered due to successful completion + * of graceful restart. + */ + if (reason != OSPF6_GR_HELPER_COMPLETED) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug("%s, Unsuccessful GR exit. RESTARTER : %pI6", + __func__, &nbr->linklocal_addr); + } + + /*Recalculate the DR for the network segment */ + dr_election(oi); + + /* Originate a router LSA */ + OSPF6_ROUTER_LSA_SCHEDULE(nbr->ospf6_if->area); + + /* Originate network lsa if it is an DR in the LAN */ + if (nbr->ospf6_if->state == OSPF6_INTERFACE_DR) + OSPF6_NETWORK_LSA_SCHEDULE(nbr->ospf6_if); +} + +/* + * Process max age Grace LSA. + * It is a indication for successful completion of GR. + * If router acting as HELPER, It exits from helper role. + * + * ospf6 + * Ospf6 pointer. + * + * lsa + * Grace LSA received from RESTARTER. + * + * nbr + * ospf6 neighbour which request the router to act as + * HELPER. + * + * Returns: + * Nothing. + */ +void ospf6_process_maxage_grace_lsa(struct ospf6 *ospf6, struct ospf6_lsa *lsa, + struct ospf6_neighbor *restarter) +{ + uint8_t restart_reason = 0; + uint32_t grace_interval = 0; + int ret; + + /* Extract the grace lsa packet fields */ + ret = ospf6_extract_grace_lsa_fields(lsa, &grace_interval, + &restart_reason); + if (ret != OSPF6_SUCCESS) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug("%s, Wrong Grace LSA packet.", __func__); + return; + } + + if (IS_DEBUG_OSPF6_GR) + zlog_debug("%s, GraceLSA received for neighbour %pI4.", + __func__, &restarter->router_id); + + ospf6_gr_helper_exit(restarter, OSPF6_GR_HELPER_COMPLETED); +} + +/* + * Actions to be taken when topo change detected + * HELPER will be exited upon a topo change. + * + * ospf6 + * ospf6 pointer + * lsa + * topo change occurred due to this lsa(type (1-5 and 7) + * + * Returns: + * Nothing + */ +void ospf6_helper_handle_topo_chg(struct ospf6 *ospf6, struct ospf6_lsa *lsa) +{ + struct listnode *i, *j, *k; + struct ospf6_neighbor *nbr = NULL; + struct ospf6_area *oa = NULL; + struct ospf6_interface *oi = NULL; + + if (!ospf6->ospf6_helper_cfg.active_restarter_cnt) + return; + + /* Topo change not required to be handled if strict + * LSA check is disabled for this router. + */ + if (!ospf6->ospf6_helper_cfg.strict_lsa_check) + return; + + if (IS_DEBUG_OSPF6_GR) + zlog_debug("%s, Topo change detected due to lsa details : %s", + __func__, lsa->name); + + lsa->tobe_acknowledged = OSPF6_TRUE; + + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, i, oa)) + for (ALL_LIST_ELEMENTS_RO(oa->if_list, j, oi)) { + + /* Ref rfc3623 section 3.2.3.b and rfc5187 + * If change due to external LSA and if the area is + * stub, then it is not a topo change. Since Type-5 + * lsas will not be flooded in stub area. + */ + if (IS_AREA_STUB(oi->area) + && ((lsa->header->type == OSPF6_LSTYPE_AS_EXTERNAL) + || (lsa->header->type == OSPF6_LSTYPE_TYPE_7) + || (lsa->header->type + == OSPF6_LSTYPE_INTER_ROUTER))) { + continue; + } + + for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, k, nbr)) { + + ospf6_gr_helper_exit(nbr, + OSPF6_GR_HELPER_TOPO_CHG); + } + } +} + +/* Configuration handlers */ +/* + * Disable/Enable HELPER support on router level. + * + * ospf6 + * Ospf6 pointer. + * + * status + * TRUE/FALSE + * + * Returns: + * Nothing. + */ +static void ospf6_gr_helper_support_set(struct ospf6 *ospf6, bool support) +{ + struct ospf6_interface *oi; + struct advRtr lookup; + struct listnode *i, *j, *k; + struct ospf6_neighbor *nbr = NULL; + struct ospf6_area *oa = NULL; + + if (ospf6->ospf6_helper_cfg.is_helper_supported == support) + return; + + ospf6->ospf6_helper_cfg.is_helper_supported = support; + + /* If helper support disabled, cease HELPER role for all + * supporting neighbors. + */ + if (support == OSPF6_FALSE) { + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, i, oa)) + for (ALL_LIST_ELEMENTS_RO(oa->if_list, j, oi)) { + + for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, k, + nbr)) { + + lookup.advRtrAddr = nbr->router_id; + /* check if helper support enabled for + * the corresponding routerid. + * If enabled, + * dont exit from helper role. + */ + if (hash_lookup( + ospf6->ospf6_helper_cfg + .enable_rtr_list, + &lookup)) + continue; + + ospf6_gr_helper_exit( + nbr, OSPF6_GR_HELPER_TOPO_CHG); + } + } + } +} + +/* + * Api to enable/disable strict lsa check on the HELPER. + * + * ospf6 + * Ospf6 pointer. + * + * enabled + * True - disable the lsa check. + * False - enable the strict lsa check. + * + * Returns: + * Nothing. + */ +static void ospf6_gr_helper_lsacheck_set(struct ospf6 *ospf6, bool enabled) +{ + if (ospf6->ospf6_helper_cfg.strict_lsa_check == enabled) + return; + + ospf6->ospf6_helper_cfg.strict_lsa_check = enabled; +} + +/* + * Api to set the supported restart reason. + * + * ospf6 + * Ospf6 pointer. + * + * only_planned + * True: support only planned restart. + * False: support for planned/unplanned restarts. + * + * Returns: + * Nothing. + */ + +static void +ospf6_gr_helper_set_supported_onlyPlanned_restart(struct ospf6 *ospf6, + bool only_planned) +{ + ospf6->ospf6_helper_cfg.only_planned_restart = only_planned; +} + +/* + * Api to set the supported grace interval in this router. + * + * ospf6 + * Ospf6 pointer. + * + * interval + * The supported grace interval.. + * + * Returns: + * Nothing. + */ +static void ospf6_gr_helper_supported_gracetime_set(struct ospf6 *ospf6, + uint32_t interval) +{ + ospf6->ospf6_helper_cfg.supported_grace_time = interval; +} + +/* API to walk and print all the Helper supported router ids */ +static int ospf6_print_vty_helper_dis_rtr_walkcb(struct hash_bucket *bucket, + void *arg) +{ + struct advRtr *rtr = bucket->data; + struct vty *vty = (struct vty *)arg; + static unsigned int count; + + vty_out(vty, "%-6pI4,", &rtr->advRtrAddr); + count++; + + if (count % 5 == 0) + vty_out(vty, "\n"); + + return HASHWALK_CONTINUE; +} + +/* API to walk and print all the Helper supported router ids.*/ +static int ospf6_print_json_helper_dis_rtr_walkcb(struct hash_bucket *bucket, + void *arg) +{ + struct advRtr *rtr = bucket->data; + struct json_object *json_rid_array = (struct json_object *)arg; + struct json_object *json_rid; + char router_id[16]; + + inet_ntop(AF_INET, &rtr->advRtrAddr, router_id, sizeof(router_id)); + + json_rid = json_object_new_object(); + + json_object_string_add(json_rid, "routerId", router_id); + json_object_array_add(json_rid_array, json_rid); + + return HASHWALK_CONTINUE; +} + +/* + * Enable/Disable HELPER support on a specified advertisement + * router. + * + * ospf6 + * Ospf6 pointer. + * + * advRtr + * HELPER support for given Advertisement Router. + * + * support + * True - Enable Helper Support. + * False - Disable Helper Support. + * + * Returns: + * Nothing. + */ +static void ospf6_gr_helper_support_set_per_routerid(struct ospf6 *ospf6, + struct in_addr router_id, + bool support) +{ + struct advRtr temp; + struct advRtr *rtr; + struct listnode *i, *j, *k; + struct ospf6_interface *oi; + struct ospf6_neighbor *nbr; + struct ospf6_area *oa; + + temp.advRtrAddr = router_id.s_addr; + + if (support == OSPF6_FALSE) { + /*Delete the routerid from the enable router hash table */ + rtr = hash_lookup(ospf6->ospf6_helper_cfg.enable_rtr_list, + &temp); + + if (rtr) { + hash_release(ospf6->ospf6_helper_cfg.enable_rtr_list, + rtr); + ospf6_disable_rtr_hash_free(rtr); + } + + /* If helper support is enabled globally + * no action is required. + */ + if (ospf6->ospf6_helper_cfg.is_helper_supported) + return; + + /* Cease the HELPER role fore neighbours from the + * specified advertisement router. + */ + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, i, oa)) + for (ALL_LIST_ELEMENTS_RO(oa->if_list, j, oi)) { + + for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, k, + nbr)) { + + if (nbr->router_id != router_id.s_addr) + continue; + + if (OSPF6_GR_IS_ACTIVE_HELPER(nbr)) + ospf6_gr_helper_exit( + nbr, + OSPF6_GR_HELPER_TOPO_CHG); + } + } + + } else { + /* Add the routerid to the enable router hash table */ + (void)hash_get(ospf6->ospf6_helper_cfg.enable_rtr_list, &temp, + ospf6_enable_rtr_hash_alloc); + } +} + +static void show_ospfv6_gr_helper_per_nbr(struct vty *vty, json_object *json, + bool uj, struct ospf6_neighbor *nbr) +{ + if (!uj) { + vty_out(vty, " Routerid : %pI4\n", &nbr->router_id); + vty_out(vty, " Received Grace period : %d(in seconds).\n", + nbr->gr_helper_info.recvd_grace_period); + vty_out(vty, " Actual Grace period : %d(in seconds)\n", + nbr->gr_helper_info.actual_grace_period); + vty_out(vty, " Remaining GraceTime:%ld(in seconds).\n", + event_timer_remain_second( + nbr->gr_helper_info.t_grace_timer)); + vty_out(vty, " Graceful Restart reason: %s.\n\n", + ospf6_restart_reason_desc[nbr->gr_helper_info + .gr_restart_reason]); + } else { + char nbrid[16]; + json_object *json_neigh = NULL; + + inet_ntop(AF_INET, &nbr->router_id, nbrid, sizeof(nbrid)); + json_neigh = json_object_new_object(); + json_object_string_add(json_neigh, "routerid", nbrid); + json_object_int_add(json_neigh, "recvdGraceInterval", + nbr->gr_helper_info.recvd_grace_period); + json_object_int_add(json_neigh, "actualGraceInterval", + nbr->gr_helper_info.actual_grace_period); + json_object_int_add(json_neigh, "remainGracetime", + event_timer_remain_second( + nbr->gr_helper_info.t_grace_timer)); + json_object_string_add(json_neigh, "restartReason", + ospf6_restart_reason_desc[ + nbr->gr_helper_info.gr_restart_reason]); + json_object_object_add(json, nbr->name, json_neigh); + } +} + +static void show_ospf6_gr_helper_details(struct vty *vty, struct ospf6 *ospf6, + json_object *json, bool uj, bool detail) +{ + struct ospf6_interface *oi; + + /* Show Router ID. */ + if (uj) { + char router_id[16]; + + inet_ntop(AF_INET, &ospf6->router_id, router_id, + sizeof(router_id)); + json_object_string_add(json, "routerId", router_id); + } else + vty_out(vty, + " OSPFv3 Routing Process (0) with Router-ID %pI4\n", + &ospf6->router_id); + + if (!uj) { + + if (ospf6->ospf6_helper_cfg.is_helper_supported) + vty_out(vty, + " Graceful restart helper support enabled.\n"); + else + vty_out(vty, + " Graceful restart helper support disabled.\n"); + + if (ospf6->ospf6_helper_cfg.strict_lsa_check) + vty_out(vty, " Strict LSA check is enabled.\n"); + else + vty_out(vty, " Strict LSA check is disabled.\n"); + + if (ospf6->ospf6_helper_cfg.only_planned_restart) + vty_out(vty, + " Helper supported for planned restarts only.\n"); + else + vty_out(vty, + " Helper supported for Planned and Unplanned Restarts.\n"); + + vty_out(vty, + " Supported Graceful restart interval: %d(in seconds).\n", + ospf6->ospf6_helper_cfg.supported_grace_time); + + if (OSPF6_HELPER_ENABLE_RTR_COUNT(ospf)) { + vty_out(vty, " Enable Router list:\n"); + vty_out(vty, " "); + hash_walk(ospf6->ospf6_helper_cfg.enable_rtr_list, + ospf6_print_vty_helper_dis_rtr_walkcb, vty); + vty_out(vty, "\n\n"); + } + + if (ospf6->ospf6_helper_cfg.last_exit_reason + != OSPF6_GR_HELPER_EXIT_NONE) { + vty_out(vty, " Last Helper exit Reason :%s\n", + ospf6_exit_reason_desc + [ospf6->ospf6_helper_cfg + .last_exit_reason]); + + if (ospf6->ospf6_helper_cfg.active_restarter_cnt) + vty_out(vty, + " Number of Active neighbours in graceful restart: %d\n", + ospf6->ospf6_helper_cfg + .active_restarter_cnt); + else + vty_out(vty, "\n"); + } + + + } else { + json_object_string_add( + json, "helperSupport", + (ospf6->ospf6_helper_cfg.is_helper_supported) + ? "Enabled" + : "Disabled"); + json_object_string_add( + json, "strictLsaCheck", + (ospf6->ospf6_helper_cfg.strict_lsa_check) + ? "Enabled" + : "Disabled"); + +#if CONFDATE > 20240401 + CPP_NOTICE("Remove deprecated json key: restartSupoort") +#endif + json_object_string_add( + json, "restartSupoort", + (ospf6->ospf6_helper_cfg.only_planned_restart) + ? "Planned Restart only" + : "Planned and Unplanned Restarts"); + + json_object_string_add( + json, "restartSupport", + (ospf6->ospf6_helper_cfg.only_planned_restart) + ? "Planned Restart only" + : "Planned and Unplanned Restarts"); + + json_object_int_add( + json, "supportedGracePeriod", + ospf6->ospf6_helper_cfg.supported_grace_time); + + if (ospf6->ospf6_helper_cfg.last_exit_reason != + OSPF6_GR_HELPER_EXIT_NONE) + json_object_string_add( + json, "lastExitReason", + ospf6_exit_reason_desc + [ospf6->ospf6_helper_cfg + .last_exit_reason]); + + if (ospf6->ospf6_helper_cfg.active_restarter_cnt) + json_object_int_add( + json, "activeRestarterCnt", + ospf6->ospf6_helper_cfg.active_restarter_cnt); + + if (OSPF6_HELPER_ENABLE_RTR_COUNT(ospf6)) { + struct json_object *json_rid_array = + json_object_new_array(); + + json_object_object_add(json, "enabledRouterIds", + json_rid_array); + + hash_walk(ospf6->ospf6_helper_cfg.enable_rtr_list, + ospf6_print_json_helper_dis_rtr_walkcb, + json_rid_array); + } + } + + if (detail) { + int cnt = 1; + struct listnode *i, *j, *k; + struct ospf6_area *oa; + json_object *json_neighbors = NULL; + + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, i, oa)) + for (ALL_LIST_ELEMENTS_RO(oa->if_list, j, oi)) { + struct ospf6_neighbor *nbr; + + if (uj) { + json_object_object_get_ex( + json, "neighbors", + &json_neighbors); + if (!json_neighbors) { + json_neighbors = + json_object_new_object(); + json_object_object_add( + json, "neighbors", + json_neighbors); + } + } + + for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, k, + nbr)) { + + if (!OSPF6_GR_IS_ACTIVE_HELPER(nbr)) + continue; + + if (!uj) + vty_out(vty, + " Neighbour %d :\n", + cnt++); + + show_ospfv6_gr_helper_per_nbr( + vty, json_neighbors, uj, nbr); + + } + } + } +} + +/* Graceful Restart HELPER config Commands */ +DEFPY(ospf6_gr_helper_enable, + ospf6_gr_helper_enable_cmd, + "graceful-restart helper enable [A.B.C.D$rtr_id]", + "ospf6 graceful restart\n" + "ospf6 GR Helper\n" + "Enable Helper support\n" + "Advertisement Router-ID\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + if (rtr_id_str != NULL) { + + ospf6_gr_helper_support_set_per_routerid(ospf6, rtr_id, + OSPF6_TRUE); + + return CMD_SUCCESS; + } + + ospf6_gr_helper_support_set(ospf6, OSPF6_TRUE); + + return CMD_SUCCESS; +} + +DEFPY(ospf6_gr_helper_disable, + ospf6_gr_helper_disable_cmd, + "no graceful-restart helper enable [A.B.C.D$rtr_id]", + NO_STR + "ospf6 graceful restart\n" + "ospf6 GR Helper\n" + "Enable Helper support\n" + "Advertisement Router-ID\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + if (rtr_id_str != NULL) { + + ospf6_gr_helper_support_set_per_routerid(ospf6, rtr_id, + OSPF6_FALSE); + + return CMD_SUCCESS; + } + + ospf6_gr_helper_support_set(ospf6, OSPF6_FALSE); + + return CMD_SUCCESS; +} + +DEFPY(ospf6_gr_helper_disable_lsacheck, + ospf6_gr_helper_disable_lsacheck_cmd, + "graceful-restart helper lsa-check-disable", + "ospf6 graceful restart\n" + "ospf6 GR Helper\n" + "disable strict LSA check\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + ospf6_gr_helper_lsacheck_set(ospf6, OSPF6_FALSE); + return CMD_SUCCESS; +} + +DEFPY(no_ospf6_gr_helper_disable_lsacheck, + no_ospf6_gr_helper_disable_lsacheck_cmd, + "no graceful-restart helper lsa-check-disable", + NO_STR + "ospf6 graceful restart\n" + "ospf6 GR Helper\n" + "diasble strict LSA check\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + ospf6_gr_helper_lsacheck_set(ospf6, OSPF6_TRUE); + return CMD_SUCCESS; +} + +DEFPY(ospf6_gr_helper_planned_only, + ospf6_gr_helper_planned_only_cmd, + "graceful-restart helper planned-only", + "ospf6 graceful restart\n" + "ospf6 GR Helper\n" + "supported only planned restart\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + ospf6_gr_helper_set_supported_onlyPlanned_restart(ospf6, OSPF6_TRUE); + + return CMD_SUCCESS; +} + +DEFPY(no_ospf6_gr_helper_planned_only, no_ospf6_gr_helper_planned_only_cmd, + "no graceful-restart helper planned-only", + NO_STR + "ospf6 graceful restart\n" + "ospf6 GR Helper\n" + "supported only for planned restart\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + ospf6_gr_helper_set_supported_onlyPlanned_restart(ospf6, OSPF6_FALSE); + + return CMD_SUCCESS; +} + +DEFPY(ospf6_gr_helper_supported_grace_time, + ospf6_gr_helper_supported_grace_time_cmd, + "graceful-restart helper supported-grace-time (10-1800)$interval", + "ospf6 graceful restart\n" + "ospf6 GR Helper\n" + "supported grace timer\n" + "grace interval(in seconds)\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + ospf6_gr_helper_supported_gracetime_set(ospf6, interval); + return CMD_SUCCESS; +} + +DEFPY(no_ospf6_gr_helper_supported_grace_time, + no_ospf6_gr_helper_supported_grace_time_cmd, + "no graceful-restart helper supported-grace-time (10-1800)$interval", + NO_STR + "ospf6 graceful restart\n" + "ospf6 GR Helper\n" + "supported grace timer\n" + "grace interval(in seconds)\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + ospf6_gr_helper_supported_gracetime_set(ospf6, + OSPF6_MAX_GRACE_INTERVAL); + return CMD_SUCCESS; +} + +/* Show commands */ +DEFPY(show_ipv6_ospf6_gr_helper, + show_ipv6_ospf6_gr_helper_cmd, + "show ipv6 ospf6 graceful-restart helper [detail] [json]", + SHOW_STR + "Ipv6 Information\n" + "OSPF6 information\n" + "ospf6 graceful restart\n" + "helper details in the router\n" + "detailed information\n" JSON_STR) +{ + int idx = 0; + bool uj = use_json(argc, argv); + struct ospf6 *ospf6 = NULL; + json_object *json = NULL; + bool detail = false; + + ospf6 = ospf6_lookup_by_vrf_name(VRF_DEFAULT_NAME); + if (ospf6 == NULL) { + vty_out(vty, "OSPFv3 is not configured\n"); + return CMD_SUCCESS; + } + + if (argv_find(argv, argc, "detail", &idx)) + detail = true; + + if (uj) + json = json_object_new_object(); + + show_ospf6_gr_helper_details(vty, ospf6, json, uj, detail); + + if (uj) + vty_json(vty, json); + + return CMD_SUCCESS; +} + +/* Debug commands */ +DEFPY(debug_ospf6_gr, debug_ospf6_gr_cmd, + "[no$no] debug ospf6 graceful-restart", + NO_STR DEBUG_STR OSPF6_STR "Graceful restart\n") +{ + if (!no) + OSPF6_DEBUG_GR_ON(); + else + OSPF6_DEBUG_GR_OFF(); + + return CMD_SUCCESS; +} + +/* + * Api to display the grace LSA information. + * + * vty + * vty pointer. + * lsa + * Grace LSA. + * json + * json object + * + * Returns: + * Nothing. + */ +static int ospf6_grace_lsa_show_info(struct vty *vty, struct ospf6_lsa *lsa, + json_object *json, bool use_json) +{ + struct ospf6_lsa_header *lsah = NULL; + struct tlv_header *tlvh = NULL; + struct grace_tlv_graceperiod *gracePeriod; + struct grace_tlv_restart_reason *grReason; + uint16_t length = 0; + int sum = 0; + + lsah = lsa->header; + if (ntohs(lsah->length) <= OSPF6_LSA_HEADER_SIZE) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug("%s: undersized (%u B) lsa", __func__, + ntohs(lsah->length)); + return OSPF6_FAILURE; + } + + length = ntohs(lsah->length) - OSPF6_LSA_HEADER_SIZE; + + if (vty) { + if (!use_json) + vty_out(vty, "TLV info:\n"); + } else { + zlog_debug(" TLV info:"); + } + + for (tlvh = TLV_HDR_TOP(lsah); sum < length && tlvh; + tlvh = TLV_HDR_NEXT(tlvh)) { + + /* Check TLV len */ + if (sum + TLV_SIZE(tlvh) > length) { + if (vty) + vty_out(vty, "%% Invalid TLV length: %d\n", + TLV_SIZE(tlvh)); + else if (IS_DEBUG_OSPF6_GR) + zlog_debug("%% Invalid TLV length: %d", + TLV_SIZE(tlvh)); + return OSPF6_FAILURE; + } + + switch (ntohs(tlvh->type)) { + case GRACE_PERIOD_TYPE: + gracePeriod = (struct grace_tlv_graceperiod *)tlvh; + sum += TLV_SIZE(tlvh); + + if (vty) { + if (use_json) + json_object_int_add( + json, "gracePeriod", + ntohl(gracePeriod->interval)); + else + vty_out(vty, " Grace period:%d\n", + ntohl(gracePeriod->interval)); + } else { + zlog_debug(" Grace period:%d", + ntohl(gracePeriod->interval)); + } + break; + case RESTART_REASON_TYPE: + grReason = (struct grace_tlv_restart_reason *)tlvh; + sum += TLV_SIZE(tlvh); + if (vty) { + if (use_json) + json_object_string_add( + json, "restartReason", + ospf6_restart_reason_desc + [grReason->reason]); + else + vty_out(vty, " Restart reason:%s\n", + ospf6_restart_reason_desc + [grReason->reason]); + } else { + zlog_debug(" Restart reason:%s", + ospf6_restart_reason_desc + [grReason->reason]); + } + break; + default: + break; + } + } + + return 0; +} + +void ospf6_gr_helper_config_init(void) +{ + + ospf6_install_lsa_handler(&grace_lsa_handler); + + install_element(OSPF6_NODE, &ospf6_gr_helper_enable_cmd); + install_element(OSPF6_NODE, &ospf6_gr_helper_disable_cmd); + install_element(OSPF6_NODE, &ospf6_gr_helper_disable_lsacheck_cmd); + install_element(OSPF6_NODE, &no_ospf6_gr_helper_disable_lsacheck_cmd); + install_element(OSPF6_NODE, &ospf6_gr_helper_planned_only_cmd); + install_element(OSPF6_NODE, &no_ospf6_gr_helper_planned_only_cmd); + install_element(OSPF6_NODE, &ospf6_gr_helper_supported_grace_time_cmd); + install_element(OSPF6_NODE, + &no_ospf6_gr_helper_supported_grace_time_cmd); + + install_element(VIEW_NODE, &show_ipv6_ospf6_gr_helper_cmd); + + install_element(CONFIG_NODE, &debug_ospf6_gr_cmd); + install_element(ENABLE_NODE, &debug_ospf6_gr_cmd); +} + + +/* + * Initialize GR helper config data structure. + * + * ospf6 + * ospf6 pointer + * + * Returns: + * Nothing + */ +void ospf6_gr_helper_init(struct ospf6 *ospf6) +{ + if (IS_DEBUG_OSPF6_GR) + zlog_debug("%s, GR Helper init.", __func__); + + ospf6->ospf6_helper_cfg.is_helper_supported = OSPF6_FALSE; + ospf6->ospf6_helper_cfg.strict_lsa_check = OSPF6_TRUE; + ospf6->ospf6_helper_cfg.only_planned_restart = OSPF6_FALSE; + ospf6->ospf6_helper_cfg.supported_grace_time = OSPF6_MAX_GRACE_INTERVAL; + ospf6->ospf6_helper_cfg.last_exit_reason = OSPF6_GR_HELPER_EXIT_NONE; + ospf6->ospf6_helper_cfg.active_restarter_cnt = 0; + + ospf6->ospf6_helper_cfg.enable_rtr_list = hash_create( + ospf6_enable_rtr_hash_key, ospf6_enable_rtr_hash_cmp, + "Ospf6 enable router hash"); +} + +/* + * De-initialize GR helper config data structure. + * + * ospf6 + * ospf6 pointer + * + * Returns: + * Nothing + */ +void ospf6_gr_helper_deinit(struct ospf6 *ospf6) +{ + + if (IS_DEBUG_OSPF6_GR) + zlog_debug("%s, GR helper deinit.", __func__); + + ospf6_enable_rtr_hash_destroy(ospf6); +} + +static int ospf6_cfg_write_helper_enable_rtr_walkcb(struct hash_bucket *backet, + void *arg) +{ + struct advRtr *rtr = backet->data; + struct vty *vty = (struct vty *)arg; + + vty_out(vty, " graceful-restart helper enable %pI4\n", &rtr->advRtrAddr); + return HASHWALK_CONTINUE; +} + +int config_write_ospf6_gr_helper(struct vty *vty, struct ospf6 *ospf6) +{ + if (ospf6->ospf6_helper_cfg.is_helper_supported) + vty_out(vty, " graceful-restart helper enable\n"); + + if (!ospf6->ospf6_helper_cfg.strict_lsa_check) + vty_out(vty, " graceful-restart helper lsa-check-disable\n"); + + if (ospf6->ospf6_helper_cfg.only_planned_restart) + vty_out(vty, " graceful-restart helper planned-only\n"); + + if (ospf6->ospf6_helper_cfg.supported_grace_time + != OSPF6_MAX_GRACE_INTERVAL) + vty_out(vty, + " graceful-restart helper supported-grace-time %d\n", + ospf6->ospf6_helper_cfg.supported_grace_time); + + if (OSPF6_HELPER_ENABLE_RTR_COUNT(ospf6)) { + hash_walk(ospf6->ospf6_helper_cfg.enable_rtr_list, + ospf6_cfg_write_helper_enable_rtr_walkcb, vty); + } + + return 0; +} + +int config_write_ospf6_debug_gr_helper(struct vty *vty) +{ + if (IS_DEBUG_OSPF6_GR) + vty_out(vty, "debug ospf6 graceful-restart\n"); + return 0; +} diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c new file mode 100644 index 0000000..a79f993 --- /dev/null +++ b/ospf6d/ospf6_interface.c @@ -0,0 +1,3142 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2003 Yasuhiro Ohara + */ + +#include <zebra.h> + +#include "memory.h" +#include "if.h" +#include "log.h" +#include "command.h" +#include "frrevent.h" +#include "prefix.h" +#include "plist.h" +#include "zclient.h" + +#include "ospf6_lsa.h" +#include "ospf6_lsdb.h" +#include "ospf6_top.h" +#include "ospf6_network.h" +#include "ospf6_message.h" +#include "ospf6_route.h" +#include "ospf6_area.h" +#include "ospf6_abr.h" +#include "ospf6_nssa.h" +#include "ospf6_interface.h" +#include "ospf6_neighbor.h" +#include "ospf6_intra.h" +#include "ospf6_spf.h" +#include "ospf6d.h" +#include "ospf6_bfd.h" +#include "ospf6_zebra.h" +#include "ospf6_gr.h" +#include "lib/json.h" +#include "ospf6_proto.h" +#include "lib/keychain.h" +#include "ospf6_auth_trailer.h" +#include "ospf6d/ospf6_interface_clippy.c" + +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_IF, "OSPF6 interface"); +DEFINE_MTYPE(OSPF6D, OSPF6_AUTH_KEYCHAIN, "OSPF6 auth keychain"); +DEFINE_MTYPE(OSPF6D, OSPF6_AUTH_MANUAL_KEY, "OSPF6 auth key"); +DEFINE_MTYPE_STATIC(OSPF6D, CFG_PLIST_NAME, "configured prefix list names"); +DEFINE_QOBJ_TYPE(ospf6_interface); +DEFINE_HOOK(ospf6_interface_change, + (struct ospf6_interface * oi, int state, int old_state), + (oi, state, old_state)); + +unsigned char conf_debug_ospf6_interface = 0; + +const char *const ospf6_interface_state_str[] = { + "None", "Down", "Loopback", "Waiting", "PointToPoint", + "DROther", "BDR", "DR", NULL}; + +int ospf6_interface_neighbor_count(struct ospf6_interface *oi) +{ + int count = 0; + struct ospf6_neighbor *nbr = NULL; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, node, nbr)) { + /* Down state is not shown. */ + if (nbr->state == OSPF6_NEIGHBOR_DOWN) + continue; + count++; + } + + return count; +} + +struct ospf6_interface *ospf6_interface_lookup_by_ifindex(ifindex_t ifindex, + vrf_id_t vrf_id) +{ + struct ospf6_interface *oi; + struct interface *ifp; + + ifp = if_lookup_by_index(ifindex, vrf_id); + if (ifp == NULL) + return (struct ospf6_interface *)NULL; + + oi = (struct ospf6_interface *)ifp->info; + return oi; +} + +/* schedule routing table recalculation */ +static void ospf6_interface_lsdb_hook(struct ospf6_lsa *lsa, + unsigned int reason) +{ + struct ospf6_interface *oi; + + if (lsa == NULL) + return; + + oi = lsa->lsdb->data; + switch (ntohs(lsa->header->type)) { + case OSPF6_LSTYPE_LINK: + if (oi->state == OSPF6_INTERFACE_DR) + OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT(oi); + if (oi->area) + ospf6_spf_schedule(oi->area->ospf6, reason); + break; + + default: + break; + } +} + +static void ospf6_interface_lsdb_hook_add(struct ospf6_lsa *lsa) +{ + ospf6_interface_lsdb_hook(lsa, ospf6_lsadd_to_spf_reason(lsa)); +} + +static void ospf6_interface_lsdb_hook_remove(struct ospf6_lsa *lsa) +{ + ospf6_interface_lsdb_hook(lsa, ospf6_lsremove_to_spf_reason(lsa)); +} + +static uint8_t ospf6_default_iftype(struct interface *ifp) +{ + if (if_is_pointopoint(ifp)) + return OSPF_IFTYPE_POINTOPOINT; + else if (if_is_loopback(ifp)) + return OSPF_IFTYPE_LOOPBACK; + else + return OSPF_IFTYPE_BROADCAST; +} + +static uint32_t ospf6_interface_get_cost(struct ospf6_interface *oi) +{ + /* If all else fails, use default OSPF cost */ + uint32_t cost; + uint32_t bw, refbw; + struct ospf6 *ospf6; + + /* interface speed and bw can be 0 in some platforms, + * use ospf default bw. If bw is configured then it would + * be used. + */ + if (!oi->interface->bandwidth && oi->interface->speed) { + bw = oi->interface->speed; + } else { + bw = oi->interface->bandwidth ? oi->interface->bandwidth + : OSPF6_INTERFACE_BANDWIDTH; + } + + ospf6 = oi->interface->vrf->info; + refbw = ospf6 ? ospf6->ref_bandwidth : OSPF6_REFERENCE_BANDWIDTH; + + /* A specified ip ospf cost overrides a calculated one. */ + if (CHECK_FLAG(oi->flag, OSPF6_INTERFACE_NOAUTOCOST)) + cost = oi->cost; + else { + cost = (uint32_t)((double)refbw / (double)bw + (double)0.5); + if (cost < 1) + cost = 1; + + /* If the interface type is point-to-multipoint or the interface + * is in the state Loopback, the global scope IPv6 addresses + * associated with the interface (if any) are copied into the + * intra-area-prefix-LSA with the PrefixOptions LA-bit set, the + * PrefixLength set to 128, and the metric set to 0. + */ + if (if_is_loopback(oi->interface)) + cost = 0; + } + + return cost; +} + +static void ospf6_interface_force_recalculate_cost(struct ospf6_interface *oi) +{ + /* update cost held in route_connected list in ospf6_interface */ + ospf6_interface_connected_route_update(oi->interface); + + /* execute LSA hooks */ + if (oi->area) { + OSPF6_LINK_LSA_SCHEDULE(oi); + OSPF6_ROUTER_LSA_SCHEDULE(oi->area); + OSPF6_NETWORK_LSA_SCHEDULE(oi); + OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT(oi); + OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB(oi->area); + } +} + +static void ospf6_interface_recalculate_cost(struct ospf6_interface *oi) +{ + uint32_t newcost; + + newcost = ospf6_interface_get_cost(oi); + if (newcost == oi->cost) + return; + oi->cost = newcost; + + ospf6_interface_force_recalculate_cost(oi); +} + +/* Create new ospf6 interface structure */ +struct ospf6_interface *ospf6_interface_create(struct interface *ifp) +{ + struct ospf6_interface *oi; + unsigned int iobuflen; + + oi = XCALLOC(MTYPE_OSPF6_IF, sizeof(struct ospf6_interface)); + + oi->obuf = ospf6_fifo_new(); + + oi->area = (struct ospf6_area *)NULL; + oi->neighbor_list = list_new(); + oi->neighbor_list->cmp = ospf6_neighbor_cmp; + oi->linklocal_addr = (struct in6_addr *)NULL; + oi->instance_id = OSPF6_INTERFACE_INSTANCE_ID; + oi->transdelay = OSPF6_INTERFACE_TRANSDELAY; + oi->priority = OSPF6_INTERFACE_PRIORITY; + + oi->hello_interval = OSPF_HELLO_INTERVAL_DEFAULT; + oi->gr.hello_delay.interval = OSPF_HELLO_DELAY_DEFAULT; + oi->dead_interval = OSPF_ROUTER_DEAD_INTERVAL_DEFAULT; + oi->rxmt_interval = OSPF_RETRANSMIT_INTERVAL_DEFAULT; + oi->type = ospf6_default_iftype(ifp); + oi->state = OSPF6_INTERFACE_DOWN; + oi->flag = 0; + oi->mtu_ignore = 0; + oi->c_ifmtu = 0; + + /* Try to adjust I/O buffer size with IfMtu */ + oi->ifmtu = ifp->mtu6; + iobuflen = ospf6_iobuf_size(ifp->mtu6); + if (oi->ifmtu > iobuflen) { + if (IS_OSPF6_DEBUG_INTERFACE) + zlog_debug( + "Interface %s: IfMtu is adjusted to I/O buffer size: %d.", + ifp->name, iobuflen); + oi->ifmtu = iobuflen; + } + + QOBJ_REG(oi, ospf6_interface); + + oi->lsupdate_list = ospf6_lsdb_create(oi); + oi->lsack_list = ospf6_lsdb_create(oi); + oi->lsdb = ospf6_lsdb_create(oi); + oi->lsdb->hook_add = ospf6_interface_lsdb_hook_add; + oi->lsdb->hook_remove = ospf6_interface_lsdb_hook_remove; + oi->lsdb_self = ospf6_lsdb_create(oi); + + oi->route_connected = + OSPF6_ROUTE_TABLE_CREATE(INTERFACE, CONNECTED_ROUTES); + oi->route_connected->scope = oi; + + /* link both */ + oi->interface = ifp; + ifp->info = oi; + + /* Compute cost. */ + oi->cost = ospf6_interface_get_cost(oi); + + oi->at_data.flags = 0; + + return oi; +} + +void ospf6_interface_delete(struct ospf6_interface *oi) +{ + struct listnode *node, *nnode; + struct ospf6_neighbor *on; + + QOBJ_UNREG(oi); + + ospf6_fifo_free(oi->obuf); + + for (ALL_LIST_ELEMENTS(oi->neighbor_list, node, nnode, on)) + ospf6_neighbor_delete(on); + + list_delete(&oi->neighbor_list); + + EVENT_OFF(oi->thread_send_hello); + EVENT_OFF(oi->thread_send_lsupdate); + EVENT_OFF(oi->thread_send_lsack); + EVENT_OFF(oi->thread_sso); + EVENT_OFF(oi->thread_wait_timer); + + ospf6_lsdb_remove_all(oi->lsdb); + ospf6_lsdb_remove_all(oi->lsupdate_list); + ospf6_lsdb_remove_all(oi->lsack_list); + + ospf6_lsdb_delete(oi->lsdb); + ospf6_lsdb_delete(oi->lsdb_self); + + ospf6_lsdb_delete(oi->lsupdate_list); + ospf6_lsdb_delete(oi->lsack_list); + + ospf6_route_table_delete(oi->route_connected); + + /* cut link */ + oi->interface->info = NULL; + + /* plist_name */ + if (oi->plist_name) + XFREE(MTYPE_CFG_PLIST_NAME, oi->plist_name); + + /* disable from area list if possible */ + ospf6_area_interface_delete(oi); + + if (oi->at_data.auth_key) + XFREE(MTYPE_OSPF6_AUTH_MANUAL_KEY, oi->at_data.auth_key); + + /* Free BFD allocated data. */ + XFREE(MTYPE_TMP, oi->bfd_config.profile); + + XFREE(MTYPE_OSPF6_IF, oi); +} + +void ospf6_interface_enable(struct ospf6_interface *oi) +{ + UNSET_FLAG(oi->flag, OSPF6_INTERFACE_DISABLE); + ospf6_interface_state_update(oi->interface); +} + +void ospf6_interface_disable(struct ospf6_interface *oi) +{ + SET_FLAG(oi->flag, OSPF6_INTERFACE_DISABLE); + + event_execute(master, interface_down, oi, 0, NULL); + + ospf6_lsdb_remove_all(oi->lsdb); + ospf6_lsdb_remove_all(oi->lsdb_self); + ospf6_lsdb_remove_all(oi->lsupdate_list); + ospf6_lsdb_remove_all(oi->lsack_list); + + EVENT_OFF(oi->thread_send_hello); + EVENT_OFF(oi->thread_send_lsupdate); + EVENT_OFF(oi->thread_send_lsack); + EVENT_OFF(oi->thread_sso); + + EVENT_OFF(oi->thread_network_lsa); + EVENT_OFF(oi->thread_link_lsa); + EVENT_OFF(oi->thread_intra_prefix_lsa); + EVENT_OFF(oi->thread_as_extern_lsa); + EVENT_OFF(oi->thread_wait_timer); + + oi->gr.hello_delay.elapsed_seconds = 0; + EVENT_OFF(oi->gr.hello_delay.t_grace_send); +} + +static struct in6_addr * +ospf6_interface_get_linklocal_address(struct interface *ifp) +{ + struct listnode *n; + struct connected *c; + struct in6_addr *l = (struct in6_addr *)NULL; + + /* for each connected address */ + for (ALL_LIST_ELEMENTS_RO(ifp->connected, n, c)) { + /* if family not AF_INET6, ignore */ + if (c->address->family != AF_INET6) + continue; + + /* linklocal scope check */ + if (IN6_IS_ADDR_LINKLOCAL(&c->address->u.prefix6)) + l = &c->address->u.prefix6; + } + return l; +} + +void ospf6_interface_state_update(struct interface *ifp) +{ + struct ospf6_interface *oi; + unsigned int iobuflen; + + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL) + return; + if (CHECK_FLAG(oi->flag, OSPF6_INTERFACE_DISABLE)) + return; + + /* Adjust the mtu values if the kernel told us something new */ + if (ifp->mtu6 != oi->ifmtu) { + /* If nothing configured, accept it and check for buffer size */ + if (!oi->c_ifmtu) { + oi->ifmtu = ifp->mtu6; + iobuflen = ospf6_iobuf_size(ifp->mtu6); + if (oi->ifmtu > iobuflen) { + if (IS_OSPF6_DEBUG_INTERFACE) + zlog_debug( + "Interface %s: IfMtu is adjusted to I/O buffer size: %d.", + ifp->name, iobuflen); + oi->ifmtu = iobuflen; + } + } else if (oi->c_ifmtu > ifp->mtu6) { + oi->ifmtu = ifp->mtu6; + zlog_warn( + "Configured mtu %u on %s overridden by kernel %u", + oi->c_ifmtu, ifp->name, ifp->mtu6); + } else + oi->ifmtu = oi->c_ifmtu; + } + + if (if_is_operative(ifp) + && (ospf6_interface_get_linklocal_address(oi->interface) + || if_is_loopback(oi->interface))) + event_execute(master, interface_up, oi, 0, NULL); + else + event_execute(master, interface_down, oi, 0, NULL); + + return; +} + +void ospf6_interface_connected_route_update(struct interface *ifp) +{ + struct ospf6_interface *oi; + struct ospf6_route *route; + struct connected *c; + struct listnode *node, *nnode; + struct in6_addr nh_addr; + + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL) + return; + + /* reset linklocal pointer */ + oi->linklocal_addr = ospf6_interface_get_linklocal_address(ifp); + + /* if area is null, do not make connected-route list */ + if (oi->area == NULL) + return; + + if (CHECK_FLAG(oi->flag, OSPF6_INTERFACE_DISABLE)) + return; + + /* update "route to advertise" interface route table */ + ospf6_route_remove_all(oi->route_connected); + + for (ALL_LIST_ELEMENTS(oi->interface->connected, node, nnode, c)) { + if (c->address->family != AF_INET6) + continue; + + CONTINUE_IF_ADDRESS_LINKLOCAL(IS_OSPF6_DEBUG_INTERFACE, + c->address); + CONTINUE_IF_ADDRESS_UNSPECIFIED(IS_OSPF6_DEBUG_INTERFACE, + c->address); + CONTINUE_IF_ADDRESS_LOOPBACK(IS_OSPF6_DEBUG_INTERFACE, + c->address); + CONTINUE_IF_ADDRESS_V4COMPAT(IS_OSPF6_DEBUG_INTERFACE, + c->address); + CONTINUE_IF_ADDRESS_V4MAPPED(IS_OSPF6_DEBUG_INTERFACE, + c->address); + + /* apply filter */ + if (oi->plist_name) { + struct prefix_list *plist; + enum prefix_list_type ret; + + plist = prefix_list_lookup(AFI_IP6, oi->plist_name); + ret = prefix_list_apply(plist, (void *)c->address); + if (ret == PREFIX_DENY) { + if (IS_OSPF6_DEBUG_INTERFACE) + zlog_debug( + "%pFX on %s filtered by prefix-list %s ", + c->address, oi->interface->name, + oi->plist_name); + continue; + } + } + + route = ospf6_route_create(oi->area->ospf6); + memcpy(&route->prefix, c->address, sizeof(struct prefix)); + apply_mask(&route->prefix); + route->type = OSPF6_DEST_TYPE_NETWORK; + route->path.area_id = oi->area->area_id; + route->path.type = OSPF6_PATH_TYPE_INTRA; + route->path.cost = oi->cost; + inet_pton(AF_INET6, "::1", &nh_addr); + ospf6_route_add_nexthop(route, oi->interface->ifindex, + &nh_addr); + ospf6_route_add(route, oi->route_connected); + } + + /* create new Link-LSA */ + OSPF6_LINK_LSA_SCHEDULE(oi); + OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT(oi); + OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB(oi->area); +} + +static int ospf6_interface_state_change(uint8_t next_state, + struct ospf6_interface *oi) +{ + uint8_t prev_state; + struct ospf6 *ospf6; + + prev_state = oi->state; + oi->state = next_state; + + if (prev_state == next_state) + return -1; + + if (!oi->area) + return -1; + + /* log */ + if (IS_OSPF6_DEBUG_INTERFACE) { + zlog_debug("Interface state change %s: %s -> %s", + oi->interface->name, + ospf6_interface_state_str[prev_state], + ospf6_interface_state_str[next_state]); + } + oi->state_change++; + + ospf6 = oi->area->ospf6; + + if ((prev_state == OSPF6_INTERFACE_DR + || prev_state == OSPF6_INTERFACE_BDR) + && (next_state != OSPF6_INTERFACE_DR + && next_state != OSPF6_INTERFACE_BDR)) + ospf6_sso(oi->interface->ifindex, &alldrouters6, + IPV6_LEAVE_GROUP, ospf6->fd); + + if ((prev_state != OSPF6_INTERFACE_DR + && prev_state != OSPF6_INTERFACE_BDR) + && (next_state == OSPF6_INTERFACE_DR + || next_state == OSPF6_INTERFACE_BDR)) + ospf6_sso(oi->interface->ifindex, &alldrouters6, + IPV6_JOIN_GROUP, ospf6->fd); + + OSPF6_ROUTER_LSA_SCHEDULE(oi->area); + OSPF6_LINK_LSA_SCHEDULE(oi); + if (next_state == OSPF6_INTERFACE_DOWN) { + OSPF6_NETWORK_LSA_EXECUTE(oi); + OSPF6_INTRA_PREFIX_LSA_EXECUTE_TRANSIT(oi); + OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB(oi->area); + } else if (prev_state == OSPF6_INTERFACE_DR + || next_state == OSPF6_INTERFACE_DR) { + OSPF6_NETWORK_LSA_SCHEDULE(oi); + OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT(oi); + OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB(oi->area); + } + + hook_call(ospf6_interface_change, oi, next_state, prev_state); + + return 0; +} + + +/* DR Election, RFC2328 section 9.4 */ + +#define IS_ELIGIBLE(n) \ + ((n)->state >= OSPF6_NEIGHBOR_TWOWAY && (n)->priority != 0) + +static struct ospf6_neighbor *better_bdrouter(struct ospf6_neighbor *a, + struct ospf6_neighbor *b) +{ + if ((a == NULL || !IS_ELIGIBLE(a) || a->drouter == a->router_id) + && (b == NULL || !IS_ELIGIBLE(b) || b->drouter == b->router_id)) + return NULL; + else if (a == NULL || !IS_ELIGIBLE(a) || a->drouter == a->router_id) + return b; + else if (b == NULL || !IS_ELIGIBLE(b) || b->drouter == b->router_id) + return a; + + if (a->bdrouter == a->router_id && b->bdrouter != b->router_id) + return a; + if (a->bdrouter != a->router_id && b->bdrouter == b->router_id) + return b; + + if (a->priority > b->priority) + return a; + if (a->priority < b->priority) + return b; + + if (ntohl(a->router_id) > ntohl(b->router_id)) + return a; + if (ntohl(a->router_id) < ntohl(b->router_id)) + return b; + + zlog_warn("Router-ID duplicate ?"); + return a; +} + +static struct ospf6_neighbor *better_drouter(struct ospf6_neighbor *a, + struct ospf6_neighbor *b) +{ + if ((a == NULL || !IS_ELIGIBLE(a) || a->drouter != a->router_id) + && (b == NULL || !IS_ELIGIBLE(b) || b->drouter != b->router_id)) + return NULL; + else if (a == NULL || !IS_ELIGIBLE(a) || a->drouter != a->router_id) + return b; + else if (b == NULL || !IS_ELIGIBLE(b) || b->drouter != b->router_id) + return a; + + if (a->drouter == a->router_id && b->drouter != b->router_id) + return a; + if (a->drouter != a->router_id && b->drouter == b->router_id) + return b; + + if (a->priority > b->priority) + return a; + if (a->priority < b->priority) + return b; + + if (ntohl(a->router_id) > ntohl(b->router_id)) + return a; + if (ntohl(a->router_id) < ntohl(b->router_id)) + return b; + + zlog_warn("Router-ID duplicate ?"); + return a; +} + +uint8_t dr_election(struct ospf6_interface *oi) +{ + struct ospf6 *ospf6 = oi->area->ospf6; + struct listnode *node, *nnode; + struct ospf6_neighbor *on, *drouter, *bdrouter, myself; + struct ospf6_neighbor *best_drouter, *best_bdrouter; + uint8_t next_state = 0; + + drouter = bdrouter = NULL; + best_drouter = best_bdrouter = NULL; + + /* pseudo neighbor myself, including noting current DR/BDR (1) */ + memset(&myself, 0, sizeof(myself)); + inet_ntop(AF_INET, &ospf6->router_id, myself.name, sizeof(myself.name)); + myself.state = OSPF6_NEIGHBOR_TWOWAY; + myself.drouter = oi->drouter; + myself.bdrouter = oi->bdrouter; + myself.priority = oi->priority; + myself.router_id = ospf6->router_id; + + /* Electing BDR (2) */ + for (ALL_LIST_ELEMENTS(oi->neighbor_list, node, nnode, on)) + bdrouter = better_bdrouter(bdrouter, on); + + best_bdrouter = bdrouter; + bdrouter = better_bdrouter(best_bdrouter, &myself); + + /* Electing DR (3) */ + for (ALL_LIST_ELEMENTS(oi->neighbor_list, node, nnode, on)) + drouter = better_drouter(drouter, on); + + best_drouter = drouter; + drouter = better_drouter(best_drouter, &myself); + if (drouter == NULL) + drouter = bdrouter; + + /* the router itself is newly/no longer DR/BDR (4) */ + if ((drouter == &myself && myself.drouter != myself.router_id) + || (drouter != &myself && myself.drouter == myself.router_id) + || (bdrouter == &myself && myself.bdrouter != myself.router_id) + || (bdrouter != &myself && myself.bdrouter == myself.router_id)) { + myself.drouter = (drouter ? drouter->router_id : htonl(0)); + myself.bdrouter = (bdrouter ? bdrouter->router_id : htonl(0)); + + /* compatible to Electing BDR (2) */ + bdrouter = better_bdrouter(best_bdrouter, &myself); + + /* compatible to Electing DR (3) */ + drouter = better_drouter(best_drouter, &myself); + if (drouter == NULL) + drouter = bdrouter; + } + + /* Set interface state accordingly (5) */ + if (drouter && drouter == &myself) + next_state = OSPF6_INTERFACE_DR; + else if (bdrouter && bdrouter == &myself) + next_state = OSPF6_INTERFACE_BDR; + else + next_state = OSPF6_INTERFACE_DROTHER; + + /* If NBMA, schedule Start for each neighbor having priority of 0 (6) */ + /* XXX */ + + /* If DR or BDR change, invoke AdjOK? for each neighbor (7) */ + /* RFC 2328 section 12.4. Originating LSAs (3) will be handled + accordingly after AdjOK */ + + if (oi->drouter != (drouter ? drouter->router_id : htonl(0)) || + oi->bdrouter != (bdrouter ? bdrouter->router_id : htonl(0)) || + ospf6->gr_info.restart_in_progress) { + if (IS_OSPF6_DEBUG_INTERFACE) + zlog_debug("DR Election on %s: DR: %s BDR: %s", + oi->interface->name, + (drouter ? drouter->name : "0.0.0.0"), + (bdrouter ? bdrouter->name : "0.0.0.0")); + + for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, node, on)) { + if (on->state < OSPF6_NEIGHBOR_TWOWAY) + continue; + /* Schedule AdjOK. */ + event_add_event(master, adj_ok, on, 0, + &on->thread_adj_ok); + } + } + + oi->drouter = (drouter ? drouter->router_id : htonl(0)); + oi->bdrouter = (bdrouter ? bdrouter->router_id : htonl(0)); + return next_state; +} + +#ifdef __FreeBSD__ + +#include <ifaddrs.h> + +static bool ifmaddr_check(ifindex_t ifindex, struct in6_addr *addr) +{ + struct ifmaddrs *ifmap, *ifma; + struct sockaddr_dl *sdl; + struct sockaddr_in6 *sin6; + bool found = false; + + if (getifmaddrs(&ifmap) != 0) + return false; + + for (ifma = ifmap; ifma; ifma = ifma->ifma_next) { + if (ifma->ifma_name == NULL || ifma->ifma_addr == NULL) + continue; + if (ifma->ifma_name->sa_family != AF_LINK) + continue; + if (ifma->ifma_addr->sa_family != AF_INET6) + continue; + sdl = (struct sockaddr_dl *)ifma->ifma_name; + sin6 = (struct sockaddr_in6 *)ifma->ifma_addr; + if (sdl->sdl_index == ifindex + && memcmp(&sin6->sin6_addr, addr, IPV6_MAX_BYTELEN) == 0) { + found = true; + break; + } + } + + if (ifmap) + freeifmaddrs(ifmap); + + return found; +} + +#endif /* __FreeBSD__ */ + +/* Interface State Machine */ +void interface_up(struct event *thread) +{ + struct ospf6_interface *oi; + struct ospf6 *ospf6; + + oi = (struct ospf6_interface *)EVENT_ARG(thread); + assert(oi && oi->interface); + + if (!oi->type_cfg) + oi->type = ospf6_default_iftype(oi->interface); + + event_cancel(&oi->thread_sso); + + if (IS_OSPF6_DEBUG_INTERFACE) + zlog_debug("Interface Event %s: [InterfaceUp]", + oi->interface->name); + + /* check physical interface is up */ + if (!if_is_operative(oi->interface)) { + zlog_warn("Interface %s is down, can't execute [InterfaceUp]", + oi->interface->name); + return; + } + + /* check interface has a link-local address */ + if (!(ospf6_interface_get_linklocal_address(oi->interface) + || if_is_loopback(oi->interface))) { + zlog_warn( + "Interface %s has no link local address, can't execute [InterfaceUp]", + oi->interface->name); + return; + } + + /* Recompute cost */ + ospf6_interface_recalculate_cost(oi); + + /* if already enabled, do nothing */ + if (oi->state > OSPF6_INTERFACE_DOWN) { + if (IS_OSPF6_DEBUG_INTERFACE) + zlog_debug("Interface %s already enabled", + oi->interface->name); + return; + } + + /* If no area assigned, return */ + if (oi->area == NULL) { + zlog_warn( + "%s: Not scheduling Hello for %s as there is no area assigned yet", + __func__, oi->interface->name); + return; + } + + /* + * RFC 3623 - Section 5 ("Unplanned Outages"): + * "The grace-LSAs are encapsulated in Link State Update Packets + * and sent out to all interfaces, even though the restarted + * router has no adjacencies and no knowledge of previous + * adjacencies". + */ + if (oi->area->ospf6->gr_info.restart_in_progress && + oi->area->ospf6->gr_info.reason == OSPF6_GR_UNKNOWN_RESTART) + ospf6_gr_unplanned_start_interface(oi); + +#ifdef __FreeBSD__ + /* + * There's a delay in FreeBSD between issuing a command to leave a + * multicast group and an actual leave. If we execute "no router ospf6" + * and "router ospf6" fast enough, we can end up in a situation when OS + * performs the leave later than it performs the join and the interface + * remains without a multicast group. We have to do the join only after + * the interface actually left the group. + */ + if (ifmaddr_check(oi->interface->ifindex, &allspfrouters6)) { + zlog_info( + "Interface %s is still in all routers group, rescheduling for SSO", + oi->interface->name); + event_add_timer(master, interface_up, oi, + OSPF6_INTERFACE_SSO_RETRY_INT, &oi->thread_sso); + return; + } +#endif /* __FreeBSD__ */ + + ospf6 = oi->area->ospf6; + + /* Join AllSPFRouters */ + if (ospf6_sso(oi->interface->ifindex, &allspfrouters6, IPV6_JOIN_GROUP, + ospf6->fd) + < 0) { + if (oi->sso_try_cnt++ < OSPF6_INTERFACE_SSO_RETRY_MAX) { + zlog_info( + "Scheduling %s for sso retry, trial count: %d", + oi->interface->name, oi->sso_try_cnt); + event_add_timer(master, interface_up, oi, + OSPF6_INTERFACE_SSO_RETRY_INT, + &oi->thread_sso); + } + return; + } + oi->sso_try_cnt = 0; /* Reset on success */ + + /* Update interface route */ + ospf6_interface_connected_route_update(oi->interface); + + /* Schedule Hello */ + if (!CHECK_FLAG(oi->flag, OSPF6_INTERFACE_PASSIVE) + && !if_is_loopback(oi->interface)) { + event_add_timer(master, ospf6_hello_send, oi, 0, + &oi->thread_send_hello); + } + + /* decide next interface state */ + if (oi->type == OSPF_IFTYPE_LOOPBACK) { + ospf6_interface_state_change(OSPF6_INTERFACE_LOOPBACK, oi); + } else if (oi->type == OSPF_IFTYPE_POINTOPOINT) { + ospf6_interface_state_change(OSPF6_INTERFACE_POINTTOPOINT, oi); + } else if (oi->priority == 0) + ospf6_interface_state_change(OSPF6_INTERFACE_DROTHER, oi); + else { + ospf6_interface_state_change(OSPF6_INTERFACE_WAITING, oi); + event_add_timer(master, wait_timer, oi, oi->dead_interval, + &oi->thread_wait_timer); + } +} + +void wait_timer(struct event *thread) +{ + struct ospf6_interface *oi; + + oi = (struct ospf6_interface *)EVENT_ARG(thread); + assert(oi && oi->interface); + + if (IS_OSPF6_DEBUG_INTERFACE) + zlog_debug("Interface Event %s: [WaitTimer]", + oi->interface->name); + + if (oi->state == OSPF6_INTERFACE_WAITING) + ospf6_interface_state_change(dr_election(oi), oi); +} + +void backup_seen(struct event *thread) +{ + struct ospf6_interface *oi; + + oi = (struct ospf6_interface *)EVENT_ARG(thread); + assert(oi && oi->interface); + + if (IS_OSPF6_DEBUG_INTERFACE) + zlog_debug("Interface Event %s: [BackupSeen]", + oi->interface->name); + + if (oi->state == OSPF6_INTERFACE_WAITING) + ospf6_interface_state_change(dr_election(oi), oi); +} + +void neighbor_change(struct event *thread) +{ + struct ospf6_interface *oi; + + oi = (struct ospf6_interface *)EVENT_ARG(thread); + assert(oi && oi->interface); + + if (IS_OSPF6_DEBUG_INTERFACE) + zlog_debug("Interface Event %s: [NeighborChange]", + oi->interface->name); + + if (oi->state == OSPF6_INTERFACE_DROTHER + || oi->state == OSPF6_INTERFACE_BDR + || oi->state == OSPF6_INTERFACE_DR) + ospf6_interface_state_change(dr_election(oi), oi); +} + +void interface_down(struct event *thread) +{ + struct ospf6_interface *oi; + struct listnode *node, *nnode; + struct ospf6_neighbor *on; + struct ospf6 *ospf6; + + oi = (struct ospf6_interface *)EVENT_ARG(thread); + assert(oi && oi->interface); + + if (IS_OSPF6_DEBUG_INTERFACE) + zlog_debug("Interface Event %s: [InterfaceDown]", + oi->interface->name); + + /* Stop Hellos */ + EVENT_OFF(oi->thread_send_hello); + + /* Stop trying to set socket options. */ + EVENT_OFF(oi->thread_sso); + + /* Cease the HELPER role for all the neighbours + * of this interface. + */ + if (ospf6_interface_neighbor_count(oi)) { + struct listnode *ln; + struct ospf6_neighbor *nbr = NULL; + + for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, ln, nbr)) + ospf6_gr_helper_exit(nbr, OSPF6_GR_HELPER_TOPO_CHG); + } + + for (ALL_LIST_ELEMENTS(oi->neighbor_list, node, nnode, on)) + ospf6_neighbor_delete(on); + + list_delete_all_node(oi->neighbor_list); + + /* When interface state is reset, also reset information about + * DR election, as it is no longer valid. */ + oi->drouter = oi->prev_drouter = htonl(0); + oi->bdrouter = oi->prev_bdrouter = htonl(0); + + if (oi->area == NULL) + return; + + ospf6 = oi->area->ospf6; + /* Leave AllSPFRouters */ + if (oi->state > OSPF6_INTERFACE_DOWN) + ospf6_sso(oi->interface->ifindex, &allspfrouters6, + IPV6_LEAVE_GROUP, ospf6->fd); + + /* deal with write fifo */ + ospf6_fifo_flush(oi->obuf); + if (oi->on_write_q) { + listnode_delete(ospf6->oi_write_q, oi); + if (list_isempty(ospf6->oi_write_q)) + event_cancel(&ospf6->t_write); + oi->on_write_q = 0; + } + + ospf6_interface_state_change(OSPF6_INTERFACE_DOWN, oi); +} + + +static const char *ospf6_iftype_str(uint8_t iftype) +{ + switch (iftype) { + case OSPF_IFTYPE_LOOPBACK: + return "LOOPBACK"; + case OSPF_IFTYPE_BROADCAST: + return "BROADCAST"; + case OSPF_IFTYPE_POINTOPOINT: + return "POINTOPOINT"; + } + return "UNKNOWN"; +} + +/* show specified interface structure */ +static int ospf6_interface_show(struct vty *vty, struct interface *ifp, + json_object *json_obj, bool use_json) +{ + struct ospf6_interface *oi; + struct connected *c; + struct prefix *p; + struct listnode *i; + char strbuf[PREFIX2STR_BUFFER], drouter[32], bdrouter[32]; + uint8_t default_iftype; + struct timeval res, now; + char duration[32]; + struct ospf6_lsa *lsa, *lsanext; + json_object *json_arr; + json_object *json_addr; + struct json_object *json_auth = NULL; + + default_iftype = ospf6_default_iftype(ifp); + + if (use_json) { + json_object_string_add(json_obj, "status", + (if_is_operative(ifp) ? "up" : "down")); + json_object_string_add(json_obj, "type", + ospf6_iftype_str(default_iftype)); + json_object_int_add(json_obj, "interfaceId", ifp->ifindex); + + if (ifp->info == NULL) + return 0; + + oi = (struct ospf6_interface *)ifp->info; + + if (if_is_operative(ifp) && oi->type != default_iftype) + json_object_string_add(json_obj, "operatingAsType", + ospf6_iftype_str(oi->type)); + + } else { + vty_out(vty, "%s is %s, type %s\n", ifp->name, + (if_is_operative(ifp) ? "up" : "down"), + ospf6_iftype_str(default_iftype)); + vty_out(vty, " Interface ID: %d\n", ifp->ifindex); + + if (ifp->info == NULL) { + vty_out(vty, " OSPF not enabled on this interface\n"); + return 0; + } + oi = (struct ospf6_interface *)ifp->info; + + if (if_is_operative(ifp) && oi->type != default_iftype) + vty_out(vty, " Operating as type %s\n", + ospf6_iftype_str(oi->type)); + } + + if (use_json) { + json_arr = json_object_new_array(); + for (ALL_LIST_ELEMENTS_RO(ifp->connected, i, c)) { + json_addr = json_object_new_object(); + p = c->address; + prefix2str(p, strbuf, sizeof(strbuf)); + switch (p->family) { + case AF_INET: + json_object_string_add(json_addr, "type", + "inet"); + json_object_string_add(json_addr, "address", + strbuf); + json_object_array_add(json_arr, json_addr); + break; + case AF_INET6: + json_object_string_add(json_addr, "type", + "inet6"); + json_object_string_add(json_addr, "address", + strbuf); + json_object_array_add(json_arr, json_addr); + break; + default: + json_object_string_add(json_addr, "type", + "unknown"); + json_object_string_add(json_addr, "address", + strbuf); + json_object_array_add(json_arr, json_addr); + break; + } + } + json_object_object_add(json_obj, "internetAddress", json_arr); + } else { + vty_out(vty, " Internet Address:\n"); + + for (ALL_LIST_ELEMENTS_RO(ifp->connected, i, c)) { + p = c->address; + prefix2str(p, strbuf, sizeof(strbuf)); + switch (p->family) { + case AF_INET: + vty_out(vty, " inet : %pFX\n", p); + break; + case AF_INET6: + vty_out(vty, " inet6: %pFX\n", p); + break; + default: + vty_out(vty, " ??? : %pFX\n", p); + break; + } + } + } + + if (use_json) { + if (oi->area) { + json_object_boolean_true_add(json_obj, + "attachedToArea"); + json_object_int_add(json_obj, "instanceId", + oi->instance_id); + json_object_int_add(json_obj, "interfaceMtu", + oi->ifmtu); + json_object_int_add(json_obj, "autoDetect", ifp->mtu6); + json_object_string_add(json_obj, "mtuMismatchDetection", + oi->mtu_ignore ? "disabled" + : "enabled"); + inet_ntop(AF_INET, &oi->area->area_id, strbuf, + sizeof(strbuf)); + json_object_string_add(json_obj, "areaId", strbuf); + json_object_int_add(json_obj, "cost", oi->cost); + } else + json_object_boolean_false_add(json_obj, + "attachedToArea"); + + } else { + if (oi->area) { + vty_out(vty, + " Instance ID %d, Interface MTU %d (autodetect: %d)\n", + oi->instance_id, oi->ifmtu, ifp->mtu6); + vty_out(vty, " MTU mismatch detection: %s\n", + oi->mtu_ignore ? "disabled" : "enabled"); + inet_ntop(AF_INET, &oi->area->area_id, strbuf, + sizeof(strbuf)); + vty_out(vty, " Area ID %s, Cost %u\n", strbuf, + oi->cost); + } else + vty_out(vty, " Not Attached to Area\n"); + } + + if (use_json) { + json_object_string_add(json_obj, "ospf6InterfaceState", + ospf6_interface_state_str[oi->state]); + json_object_int_add(json_obj, "transmitDelaySec", + oi->transdelay); + json_object_int_add(json_obj, "priority", oi->priority); + json_object_int_add(json_obj, "timerIntervalsConfigHello", + oi->hello_interval); + json_object_int_add(json_obj, "timerIntervalsConfigDead", + oi->dead_interval); + json_object_int_add(json_obj, "timerIntervalsConfigRetransmit", + oi->rxmt_interval); + json_object_boolean_add( + json_obj, "timerPassiveIface", + !!CHECK_FLAG(oi->flag, OSPF6_INTERFACE_PASSIVE)); + } else { + vty_out(vty, " State %s, Transmit Delay %d sec, Priority %d\n", + ospf6_interface_state_str[oi->state], oi->transdelay, + oi->priority); + vty_out(vty, " Timer intervals configured:\n"); + if (!CHECK_FLAG(oi->flag, OSPF6_INTERFACE_PASSIVE)) + vty_out(vty, + " Hello %d(%pTHd), Dead %d, Retransmit %d\n", + oi->hello_interval, oi->thread_send_hello, + oi->dead_interval, oi->rxmt_interval); + else + vty_out(vty, " No Hellos (Passive interface)\n"); + } + + inet_ntop(AF_INET, &oi->drouter, drouter, sizeof(drouter)); + inet_ntop(AF_INET, &oi->bdrouter, bdrouter, sizeof(bdrouter)); + if (use_json) { + json_object_string_add(json_obj, "dr", drouter); + json_object_string_add(json_obj, "bdr", bdrouter); + json_object_int_add(json_obj, "numberOfInterfaceScopedLsa", + oi->lsdb->count); + } else { + vty_out(vty, " DR: %s BDR: %s\n", drouter, bdrouter); + vty_out(vty, " Number of I/F scoped LSAs is %u\n", + oi->lsdb->count); + } + + monotime(&now); + + if (use_json) { + timerclear(&res); + if (event_is_scheduled(oi->thread_send_lsupdate)) + timersub(&oi->thread_send_lsupdate->u.sands, &now, + &res); + timerstring(&res, duration, sizeof(duration)); + json_object_int_add(json_obj, "pendingLsaLsUpdateCount", + oi->lsupdate_list->count); + json_object_string_add(json_obj, "pendingLsaLsUpdateTime", + duration); + json_object_string_add( + json_obj, "lsUpdateSendThread", + (event_is_scheduled(oi->thread_send_lsupdate) ? "on" + : "off")); + + json_arr = json_object_new_array(); + for (ALL_LSDB(oi->lsupdate_list, lsa, lsanext)) + json_object_array_add( + json_arr, json_object_new_string(lsa->name)); + json_object_object_add(json_obj, "pendingLsaLsUpdate", + json_arr); + + timerclear(&res); + if (event_is_scheduled(oi->thread_send_lsack)) + timersub(&oi->thread_send_lsack->u.sands, &now, &res); + timerstring(&res, duration, sizeof(duration)); + + json_object_int_add(json_obj, "pendingLsaLsAckCount", + oi->lsack_list->count); + json_object_string_add(json_obj, "pendingLsaLsAckTime", + duration); + json_object_string_add( + json_obj, "lsAckSendThread", + (event_is_scheduled(oi->thread_send_lsack) ? "on" + : "off")); + + json_arr = json_object_new_array(); + for (ALL_LSDB(oi->lsack_list, lsa, lsanext)) + json_object_array_add( + json_arr, json_object_new_string(lsa->name)); + json_object_object_add(json_obj, "pendingLsaLsAck", json_arr); + + if (oi->gr.hello_delay.interval != 0) + json_object_int_add(json_obj, "grHelloDelaySecs", + oi->gr.hello_delay.interval); + } else { + timerclear(&res); + if (event_is_scheduled(oi->thread_send_lsupdate)) + timersub(&oi->thread_send_lsupdate->u.sands, &now, + &res); + timerstring(&res, duration, sizeof(duration)); + vty_out(vty, + " %d Pending LSAs for LSUpdate in Time %s [thread %s]\n", + oi->lsupdate_list->count, duration, + (event_is_scheduled(oi->thread_send_lsupdate) ? "on" + : "off")); + for (ALL_LSDB(oi->lsupdate_list, lsa, lsanext)) + vty_out(vty, " %s\n", lsa->name); + + timerclear(&res); + if (event_is_scheduled(oi->thread_send_lsack)) + timersub(&oi->thread_send_lsack->u.sands, &now, &res); + timerstring(&res, duration, sizeof(duration)); + vty_out(vty, + " %d Pending LSAs for LSAck in Time %s [thread %s]\n", + oi->lsack_list->count, duration, + (event_is_scheduled(oi->thread_send_lsack) ? "on" + : "off")); + for (ALL_LSDB(oi->lsack_list, lsa, lsanext)) + vty_out(vty, " %s\n", lsa->name); + + if (oi->gr.hello_delay.interval != 0) + vty_out(vty, " Graceful Restart hello delay: %us\n", + oi->gr.hello_delay.interval); + } + + /* BFD specific. */ + if (oi->bfd_config.enabled) { + if (use_json) { + struct json_object *json_bfd = json_object_new_object(); + + json_object_int_add( + json_bfd, "detectMultiplier", + oi->bfd_config.detection_multiplier); + json_object_int_add(json_bfd, "rxMinInterval", + oi->bfd_config.min_rx); + json_object_int_add(json_bfd, "txMinInterval", + oi->bfd_config.min_tx); + json_object_object_add(json_obj, "peerBfdInfo", + json_bfd); + } else { + vty_out(vty, + " BFD: Detect Multiplier: %d, Min Rx interval: %d, Min Tx interval: %d\n", + oi->bfd_config.detection_multiplier, + oi->bfd_config.min_rx, oi->bfd_config.min_tx); + } + } + + if (use_json) + json_auth = json_object_new_object(); + if (oi->at_data.flags != 0) { + if (use_json) { + if (CHECK_FLAG(oi->at_data.flags, + OSPF6_AUTH_TRAILER_KEYCHAIN)) { + json_object_string_add(json_auth, "authType", + "keychain"); + json_object_string_add(json_auth, + "keychainName", + oi->at_data.keychain); + } else if (CHECK_FLAG(oi->at_data.flags, + OSPF6_AUTH_TRAILER_MANUAL_KEY)) + json_object_string_add(json_auth, "authType", + "manualkey"); + json_object_int_add(json_auth, "txPktDrop", + oi->at_data.tx_drop); + json_object_int_add(json_auth, "rxPktDrop", + oi->at_data.rx_drop); + } else { + if (CHECK_FLAG(oi->at_data.flags, + OSPF6_AUTH_TRAILER_KEYCHAIN)) + vty_out(vty, + " Authentication Trailer is enabled with key-chain %s\n", + oi->at_data.keychain); + else if (CHECK_FLAG(oi->at_data.flags, + OSPF6_AUTH_TRAILER_MANUAL_KEY)) + vty_out(vty, + " Authentication trailer is enabled with manual key\n"); + vty_out(vty, + " Packet drop Tx %u, Packet drop Rx %u\n", + oi->at_data.tx_drop, oi->at_data.rx_drop); + } + } else { + if (use_json) + json_object_string_add(json_auth, "authType", "NULL"); + else + vty_out(vty, " Authentication Trailer is disabled\n"); + } + + if (use_json) + json_object_object_add(json_obj, "authInfo", json_auth); + + return 0; +} + +/* Find the global address to be used as a forwarding address in NSSA LSA.*/ +struct in6_addr *ospf6_interface_get_global_address(struct interface *ifp) +{ + struct listnode *n; + struct connected *c; + + /* for each connected address */ + for (ALL_LIST_ELEMENTS_RO(ifp->connected, n, c)) { + /* if family not AF_INET6, ignore */ + if (c->address->family != AF_INET6) + continue; + + if (!IN6_IS_ADDR_LINKLOCAL(&c->address->u.prefix6)) + return &c->address->u.prefix6; + } + + return NULL; +} + + +static int show_ospf6_interface_common(struct vty *vty, vrf_id_t vrf_id, + int argc, struct cmd_token **argv, + int idx_ifname, int intf_idx, + int json_idx, bool uj) +{ + + struct vrf *vrf = vrf_lookup_by_id(vrf_id); + struct interface *ifp; + json_object *json; + json_object *json_int; + + if (uj) { + json = json_object_new_object(); + if (argc == json_idx) { + ifp = if_lookup_by_name(argv[idx_ifname]->arg, vrf_id); + json_int = json_object_new_object(); + if (ifp == NULL) { + json_object_string_add(json, "noSuchInterface", + argv[idx_ifname]->arg); + vty_json(vty, json); + json_object_free(json_int); + return CMD_WARNING; + } + ospf6_interface_show(vty, ifp, json_int, uj); + json_object_object_add(json, ifp->name, json_int); + } else { + FOR_ALL_INTERFACES (vrf, ifp) { + json_int = json_object_new_object(); + ospf6_interface_show(vty, ifp, json_int, uj); + json_object_object_add(json, ifp->name, + json_int); + } + } + vty_json(vty, json); + } else { + if (argc == intf_idx) { + ifp = if_lookup_by_name(argv[idx_ifname]->arg, vrf_id); + if (ifp == NULL) { + vty_out(vty, "No such Interface: %s\n", + argv[idx_ifname]->arg); + return CMD_WARNING; + } + ospf6_interface_show(vty, ifp, NULL, uj); + } else { + FOR_ALL_INTERFACES (vrf, ifp) + ospf6_interface_show(vty, ifp, NULL, uj); + } + } + return CMD_SUCCESS; +} + +/* show interface */ +DEFUN(show_ipv6_ospf6_interface, show_ipv6_ospf6_interface_ifname_cmd, + "show ipv6 ospf6 [vrf <NAME|all>] interface [IFNAME] [json]", + SHOW_STR IP6_STR OSPF6_STR VRF_CMD_HELP_STR + "All VRFs\n" INTERFACE_STR IFNAME_STR JSON_STR) +{ + int idx_ifname = 4; + int intf_idx = 5; + int json_idx = 6; + struct listnode *node; + struct ospf6 *ospf6; + const char *vrf_name = NULL; + bool all_vrf = false; + int idx_vrf = 0; + bool uj = use_json(argc, argv); + + OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + if (idx_vrf > 0) { + idx_ifname += 2; + intf_idx += 2; + json_idx += 2; + } + + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { + if (all_vrf || strcmp(ospf6->name, vrf_name) == 0) { + show_ospf6_interface_common(vty, ospf6->vrf_id, argc, + argv, idx_ifname, intf_idx, + json_idx, uj); + + if (!all_vrf) + break; + } + } + + OSPF6_CMD_CHECK_VRF(uj, all_vrf, ospf6); + + return CMD_SUCCESS; +} + +static int ospf6_interface_show_traffic(struct vty *vty, + struct interface *intf_ifp, + int display_once, json_object *json, + bool use_json, vrf_id_t vrf_id) +{ + struct interface *ifp; + struct vrf *vrf = NULL; + struct ospf6_interface *oi = NULL; + json_object *json_interface; + + if (!display_once && !use_json) { + vty_out(vty, "\n"); + vty_out(vty, "%-12s%-17s%-17s%-17s%-17s%-17s\n", "Interface", + " HELLO", " DB-Desc", " LS-Req", " LS-Update", + " LS-Ack"); + vty_out(vty, "%-10s%-18s%-18s%-17s%-17s%-17s\n", "", + " Rx/Tx", " Rx/Tx", " Rx/Tx", " Rx/Tx", + " Rx/Tx"); + vty_out(vty, + "--------------------------------------------------------------------------------------------\n"); + } + + if (intf_ifp == NULL) { + vrf = vrf_lookup_by_id(vrf_id); + FOR_ALL_INTERFACES (vrf, ifp) { + if (ifp->info) + oi = (struct ospf6_interface *)ifp->info; + else + continue; + + if (use_json) { + json_interface = json_object_new_object(); + json_object_int_add(json_interface, "helloRx", + oi->hello_in); + json_object_int_add(json_interface, "helloTx", + oi->hello_out); + json_object_int_add(json_interface, "dbDescRx", + oi->db_desc_in); + json_object_int_add(json_interface, "dbDescTx", + oi->db_desc_out); + json_object_int_add(json_interface, "lsReqRx", + oi->ls_req_in); + json_object_int_add(json_interface, "lsReqTx", + oi->ls_req_out); + json_object_int_add(json_interface, + "lsUpdateRx", + oi->ls_upd_in); + json_object_int_add(json_interface, + "lsUpdateTx", + oi->ls_upd_out); + json_object_int_add(json_interface, "lsAckRx", + oi->ls_ack_in); + json_object_int_add(json_interface, "lsAckTx", + oi->ls_ack_out); + + json_object_object_add(json, + oi->interface->name, + json_interface); + } else + vty_out(vty, + "%-10s %8u/%-8u %7u/%-7u %7u/%-7u %7u/%-7u %7u/%-7u\n", + oi->interface->name, oi->hello_in, + oi->hello_out, oi->db_desc_in, + oi->db_desc_out, oi->ls_req_in, + oi->ls_req_out, oi->ls_upd_in, + oi->ls_upd_out, oi->ls_ack_in, + oi->ls_ack_out); + } + } else { + oi = intf_ifp->info; + if (oi == NULL) + return CMD_WARNING; + + if (use_json) { + json_interface = json_object_new_object(); + json_object_int_add(json_interface, "helloRx", + oi->hello_in); + json_object_int_add(json_interface, "helloTx", + oi->hello_out); + json_object_int_add(json_interface, "dbDescRx", + oi->db_desc_in); + json_object_int_add(json_interface, "dbDescTx", + oi->db_desc_out); + json_object_int_add(json_interface, "lsReqRx", + oi->ls_req_in); + json_object_int_add(json_interface, "lsReqTx", + oi->ls_req_out); + json_object_int_add(json_interface, "lsUpdateRx", + oi->ls_upd_in); + json_object_int_add(json_interface, "lsUpdateTx", + oi->ls_upd_out); + json_object_int_add(json_interface, "lsAckRx", + oi->ls_ack_in); + json_object_int_add(json_interface, "lsAckTx", + oi->ls_ack_out); + + json_object_object_add(json, oi->interface->name, + json_interface); + } else + vty_out(vty, + "%-10s %8u/%-8u %7u/%-7u %7u/%-7u %7u/%-7u %7u/%-7u\n", + oi->interface->name, oi->hello_in, + oi->hello_out, oi->db_desc_in, oi->db_desc_out, + oi->ls_req_in, oi->ls_req_out, oi->ls_upd_in, + oi->ls_upd_out, oi->ls_ack_in, oi->ls_ack_out); + } + + return CMD_SUCCESS; +} + +static int ospf6_interface_show_traffic_common(struct vty *vty, int argc, + struct cmd_token **argv, + vrf_id_t vrf_id, bool uj) +{ + int idx_ifname = 0; + int display_once = 0; + char *intf_name = NULL; + struct interface *ifp = NULL; + json_object *json = NULL; + + if (uj) + json = json_object_new_object(); + + if (argv_find(argv, argc, "IFNAME", &idx_ifname)) { + intf_name = argv[idx_ifname]->arg; + ifp = if_lookup_by_name(intf_name, vrf_id); + if (uj) { + if (ifp == NULL) { + json_object_string_add(json, "status", + "No Such Interface"); + json_object_string_add(json, "interface", + intf_name); + vty_json(vty, json); + return CMD_WARNING; + } + if (ifp->info == NULL) { + json_object_string_add( + json, "status", + "OSPF not enabled on this interface"); + json_object_string_add(json, "interface", + intf_name); + vty_json(vty, json); + return 0; + } + } else { + if (ifp == NULL) { + vty_out(vty, "No such Interface: %s\n", + intf_name); + return CMD_WARNING; + } + if (ifp->info == NULL) { + vty_out(vty, + " OSPF not enabled on this interface %s\n", + intf_name); + return 0; + } + } + } + + ospf6_interface_show_traffic(vty, ifp, display_once, json, uj, vrf_id); + + if (uj) + vty_json(vty, json); + + return CMD_SUCCESS; +} + +/* show interface */ +DEFUN(show_ipv6_ospf6_interface_traffic, show_ipv6_ospf6_interface_traffic_cmd, + "show ipv6 ospf6 [vrf <NAME|all>] interface traffic [IFNAME] [json]", + SHOW_STR IP6_STR OSPF6_STR VRF_CMD_HELP_STR + "All VRFs\n" INTERFACE_STR + "Protocol Packet counters\n" IFNAME_STR JSON_STR) +{ + struct ospf6 *ospf6; + struct listnode *node; + const char *vrf_name = NULL; + bool all_vrf = false; + int idx_vrf = 0; + bool uj = use_json(argc, argv); + + OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { + if (all_vrf || strcmp(ospf6->name, vrf_name) == 0) { + ospf6_interface_show_traffic_common(vty, argc, argv, + ospf6->vrf_id, uj); + + if (!all_vrf) + break; + } + } + + OSPF6_CMD_CHECK_VRF(uj, all_vrf, ospf6); + + return CMD_SUCCESS; +} + + +DEFUN(show_ipv6_ospf6_interface_ifname_prefix, + show_ipv6_ospf6_interface_ifname_prefix_cmd, + "show ipv6 ospf6 [vrf <NAME|all>] interface IFNAME prefix\ + [<\ + detail\ + |<X:X::X:X|X:X::X:X/M> [<match|detail>]\ + >] [json]", + SHOW_STR IP6_STR OSPF6_STR VRF_CMD_HELP_STR + "All VRFs\n" INTERFACE_STR IFNAME_STR + "Display connected prefixes to advertise\n" + "Display details of the prefixes\n" OSPF6_ROUTE_ADDRESS_STR + OSPF6_ROUTE_PREFIX_STR OSPF6_ROUTE_MATCH_STR + "Display details of the prefixes\n" JSON_STR) +{ + int idx_ifname = 4; + int idx_prefix = 6; + struct ospf6_interface *oi; + bool uj = use_json(argc, argv); + + struct ospf6 *ospf6; + struct listnode *node; + struct interface *ifp; + const char *vrf_name = NULL; + bool all_vrf = false; + int idx_vrf = 0; + + OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + if (idx_vrf > 0) { + idx_ifname += 2; + idx_prefix += 2; + } + + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { + if (all_vrf || strcmp(ospf6->name, vrf_name) == 0) { + ifp = if_lookup_by_name(argv[idx_ifname]->arg, + ospf6->vrf_id); + if (ifp == NULL) { + vty_out(vty, "No such Interface: %s\n", + argv[idx_ifname]->arg); + return CMD_WARNING; + } + + oi = ifp->info; + if (oi == NULL + || CHECK_FLAG(oi->flag, OSPF6_INTERFACE_DISABLE)) { + vty_out(vty, + "Interface %s not attached to area\n", + argv[idx_ifname]->arg); + return CMD_WARNING; + } + + ospf6_route_table_show(vty, idx_prefix, argc, argv, + oi->route_connected, uj); + + if (!all_vrf) + break; + } + } + + OSPF6_CMD_CHECK_VRF(uj, all_vrf, ospf6); + + return CMD_SUCCESS; +} + +DEFUN(show_ipv6_ospf6_interface_prefix, show_ipv6_ospf6_interface_prefix_cmd, + "show ipv6 ospf6 [vrf <NAME|all>] interface prefix\ + [<\ + detail\ + |<X:X::X:X|X:X::X:X/M> [<match|detail>]\ + >] [json]", + SHOW_STR IP6_STR OSPF6_STR VRF_CMD_HELP_STR + "All VRFs\n" INTERFACE_STR + "Display connected prefixes to advertise\n" + "Display details of the prefixes\n" OSPF6_ROUTE_ADDRESS_STR + OSPF6_ROUTE_PREFIX_STR OSPF6_ROUTE_MATCH_STR + "Display details of the prefixes\n" JSON_STR) +{ + struct vrf *vrf = NULL; + int idx_prefix = 5; + struct ospf6_interface *oi; + struct interface *ifp; + bool uj = use_json(argc, argv); + struct listnode *node; + struct ospf6 *ospf6; + const char *vrf_name = NULL; + bool all_vrf = false; + int idx_vrf = 0; + + OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + if (idx_vrf > 0) + idx_prefix += 2; + + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { + if (all_vrf || strcmp(ospf6->name, vrf_name) == 0) { + vrf = vrf_lookup_by_id(ospf6->vrf_id); + FOR_ALL_INTERFACES (vrf, ifp) { + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL + || CHECK_FLAG(oi->flag, + OSPF6_INTERFACE_DISABLE)) + continue; + + ospf6_route_table_show(vty, idx_prefix, argc, + argv, + oi->route_connected, uj); + } + if (!all_vrf) + break; + } + } + + OSPF6_CMD_CHECK_VRF(uj, all_vrf, ospf6); + + return CMD_SUCCESS; +} + +void ospf6_interface_start(struct ospf6_interface *oi) +{ + struct ospf6 *ospf6; + struct ospf6_area *oa; + + if (oi->area_id_format == OSPF6_AREA_FMT_UNSET) + return; + + if (oi->area) { + /* Recompute cost */ + ospf6_interface_recalculate_cost(oi); + return; + } + + ospf6 = oi->interface->vrf->info; + if (!ospf6) + return; + + oa = ospf6_area_lookup(oi->area_id, ospf6); + if (oa == NULL) + oa = ospf6_area_create(oi->area_id, ospf6, oi->area_id_format); + + /* attach interface to area */ + listnode_add(oa->if_list, oi); + oi->area = oa; + + SET_FLAG(oa->flag, OSPF6_AREA_ENABLE); + + /* start up */ + ospf6_interface_enable(oi); + + /* If the router is ABR, originate summary routes */ + if (ospf6_check_and_set_router_abr(ospf6)) { + ospf6_abr_enable_area(oa); + ospf6_schedule_abr_task(ospf6); + } +} + +void ospf6_interface_stop(struct ospf6_interface *oi) +{ + struct ospf6_area *oa; + + oa = oi->area; + if (!oa) + return; + + ospf6_interface_disable(oi); + + listnode_delete(oa->if_list, oi); + oi->area = NULL; + + if (oa->if_list->count == 0) { + UNSET_FLAG(oa->flag, OSPF6_AREA_ENABLE); + ospf6_abr_disable_area(oa); + } +} + +/* interface variable set command */ +DEFUN (ipv6_ospf6_area, + ipv6_ospf6_area_cmd, + "ipv6 ospf6 area <A.B.C.D|(0-4294967295)>", + IP6_STR + OSPF6_STR + "Specify the OSPF6 area ID\n" + "OSPF6 area ID in IPv4 address notation\n" + "OSPF6 area ID in decimal notation\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf6_interface *oi; + int idx_ipv4 = 3; + uint32_t area_id; + int format; + + assert(ifp); + + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL) + oi = ospf6_interface_create(ifp); + assert(oi); + + if (oi->area) { + vty_out(vty, "%s already attached to Area %s\n", + oi->interface->name, oi->area->name); + return CMD_SUCCESS; + } + + if (str2area_id(argv[idx_ipv4]->arg, &area_id, &format)) { + vty_out(vty, "Malformed Area-ID: %s\n", argv[idx_ipv4]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + + oi->area_id = area_id; + oi->area_id_format = format; + + ospf6_interface_start(oi); + + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_ospf6_area, + no_ipv6_ospf6_area_cmd, + "no ipv6 ospf6 area [<A.B.C.D|(0-4294967295)>]", + NO_STR + IP6_STR + OSPF6_STR + "Specify the OSPF6 area ID\n" + "OSPF6 area ID in IPv4 address notation\n" + "OSPF6 area ID in decimal notation\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf6_interface *oi; + + assert(ifp); + + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL) + oi = ospf6_interface_create(ifp); + assert(oi); + + ospf6_interface_stop(oi); + + oi->area_id = 0; + oi->area_id_format = OSPF6_AREA_FMT_UNSET; + + return CMD_SUCCESS; +} + +DEFUN (ipv6_ospf6_ifmtu, + ipv6_ospf6_ifmtu_cmd, + "ipv6 ospf6 ifmtu (1-65535)", + IP6_STR + OSPF6_STR + "Interface MTU\n" + "OSPFv3 Interface MTU\n" + ) +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + int idx_number = 3; + struct ospf6_interface *oi; + unsigned int ifmtu, iobuflen; + struct listnode *node, *nnode; + struct ospf6_neighbor *on; + + assert(ifp); + + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL) + oi = ospf6_interface_create(ifp); + assert(oi); + + ifmtu = strtol(argv[idx_number]->arg, NULL, 10); + + if (oi->c_ifmtu == ifmtu) + return CMD_SUCCESS; + + if (ifp->mtu6 != 0 && ifp->mtu6 < ifmtu) { + vty_out(vty, + "%s's ospf6 ifmtu cannot go beyond physical mtu (%d)\n", + ifp->name, ifp->mtu6); + return CMD_WARNING_CONFIG_FAILED; + } + + if (oi->ifmtu < ifmtu) { + iobuflen = ospf6_iobuf_size(ifmtu); + if (iobuflen < ifmtu) { + vty_out(vty, + "%s's ifmtu is adjusted to I/O buffer size (%d).\n", + ifp->name, iobuflen); + oi->ifmtu = oi->c_ifmtu = iobuflen; + } else + oi->ifmtu = oi->c_ifmtu = ifmtu; + } else + oi->ifmtu = oi->c_ifmtu = ifmtu; + + /* re-establish adjacencies */ + for (ALL_LIST_ELEMENTS(oi->neighbor_list, node, nnode, on)) { + EVENT_OFF(on->inactivity_timer); + event_add_event(master, inactivity_timer, on, 0, NULL); + } + + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_ospf6_ifmtu, + no_ipv6_ospf6_ifmtu_cmd, + "no ipv6 ospf6 ifmtu [(1-65535)]", + NO_STR + IP6_STR + OSPF6_STR + "Interface MTU\n" + "OSPFv3 Interface MTU\n" + ) +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf6_interface *oi; + unsigned int iobuflen; + struct listnode *node, *nnode; + struct ospf6_neighbor *on; + + assert(ifp); + + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL) + oi = ospf6_interface_create(ifp); + assert(oi); + + if (oi->ifmtu < ifp->mtu) { + iobuflen = ospf6_iobuf_size(ifp->mtu); + if (iobuflen < ifp->mtu) { + vty_out(vty, + "%s's ifmtu is adjusted to I/O buffer size (%d).\n", + ifp->name, iobuflen); + oi->ifmtu = iobuflen; + } else + oi->ifmtu = ifp->mtu; + } else + oi->ifmtu = ifp->mtu; + + oi->c_ifmtu = 0; + + /* re-establish adjacencies */ + for (ALL_LIST_ELEMENTS(oi->neighbor_list, node, nnode, on)) { + EVENT_OFF(on->inactivity_timer); + event_add_event(master, inactivity_timer, on, 0, NULL); + } + + return CMD_SUCCESS; +} + +DEFUN (ipv6_ospf6_cost, + ipv6_ospf6_cost_cmd, + "ipv6 ospf6 cost (1-65535)", + IP6_STR + OSPF6_STR + "Interface cost\n" + "Outgoing metric of this interface\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + int idx_number = 3; + struct ospf6_interface *oi; + unsigned long int lcost; + + assert(ifp); + + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL) + oi = ospf6_interface_create(ifp); + assert(oi); + + lcost = strtol(argv[idx_number]->arg, NULL, 10); + + if (lcost > UINT32_MAX) { + vty_out(vty, "Cost %ld is out of range\n", lcost); + return CMD_WARNING_CONFIG_FAILED; + } + + SET_FLAG(oi->flag, OSPF6_INTERFACE_NOAUTOCOST); + if (oi->cost == lcost) + return CMD_SUCCESS; + + oi->cost = lcost; + ospf6_interface_force_recalculate_cost(oi); + + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_ospf6_cost, + no_ipv6_ospf6_cost_cmd, + "no ipv6 ospf6 cost [(1-65535)]", + NO_STR + IP6_STR + OSPF6_STR + "Calculate interface cost from bandwidth\n" + "Outgoing metric of this interface\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf6_interface *oi; + assert(ifp); + + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL) + oi = ospf6_interface_create(ifp); + assert(oi); + + UNSET_FLAG(oi->flag, OSPF6_INTERFACE_NOAUTOCOST); + + ospf6_interface_recalculate_cost(oi); + + return CMD_SUCCESS; +} + +DEFUN (auto_cost_reference_bandwidth, + auto_cost_reference_bandwidth_cmd, + "auto-cost reference-bandwidth (1-4294967)", + "Calculate OSPF interface cost according to bandwidth\n" + "Use reference bandwidth method to assign OSPF cost\n" + "The reference bandwidth in terms of Mbits per second\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, o); + int idx_number = 2; + struct ospf6_area *oa; + struct ospf6_interface *oi; + struct listnode *i, *j; + uint32_t refbw; + + refbw = strtol(argv[idx_number]->arg, NULL, 10); + if (refbw < 1 || refbw > 4294967) { + vty_out(vty, "reference-bandwidth value is invalid\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + /* If reference bandwidth is changed. */ + if ((refbw) == o->ref_bandwidth) + return CMD_SUCCESS; + + o->ref_bandwidth = refbw; + for (ALL_LIST_ELEMENTS_RO(o->area_list, i, oa)) + for (ALL_LIST_ELEMENTS_RO(oa->if_list, j, oi)) + ospf6_interface_recalculate_cost(oi); + + return CMD_SUCCESS; +} + +DEFUN (no_auto_cost_reference_bandwidth, + no_auto_cost_reference_bandwidth_cmd, + "no auto-cost reference-bandwidth [(1-4294967)]", + NO_STR + "Calculate OSPF interface cost according to bandwidth\n" + "Use reference bandwidth method to assign OSPF cost\n" + "The reference bandwidth in terms of Mbits per second\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, o); + struct ospf6_area *oa; + struct ospf6_interface *oi; + struct listnode *i, *j; + + if (o->ref_bandwidth == OSPF6_REFERENCE_BANDWIDTH) + return CMD_SUCCESS; + + o->ref_bandwidth = OSPF6_REFERENCE_BANDWIDTH; + for (ALL_LIST_ELEMENTS_RO(o->area_list, i, oa)) + for (ALL_LIST_ELEMENTS_RO(oa->if_list, j, oi)) + ospf6_interface_recalculate_cost(oi); + + return CMD_SUCCESS; +} + + +DEFUN (ospf6_write_multiplier, + ospf6_write_multiplier_cmd, + "write-multiplier (1-100)", + "Write multiplier\n" + "Maximum number of interface serviced per write\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, o); + uint32_t write_oi_count; + + write_oi_count = strtol(argv[1]->arg, NULL, 10); + if (write_oi_count < 1 || write_oi_count > 100) { + vty_out(vty, "write-multiplier value is invalid\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + o->write_oi_count = write_oi_count; + return CMD_SUCCESS; +} + +DEFUN (no_ospf6_write_multiplier, + no_ospf6_write_multiplier_cmd, + "no write-multiplier (1-100)", + NO_STR + "Write multiplier\n" + "Maximum number of interface serviced per write\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, o); + + o->write_oi_count = OSPF6_WRITE_INTERFACE_COUNT_DEFAULT; + return CMD_SUCCESS; +} + +DEFUN (ipv6_ospf6_hellointerval, + ipv6_ospf6_hellointerval_cmd, + "ipv6 ospf6 hello-interval (1-65535)", + IP6_STR + OSPF6_STR + "Time between HELLO packets\n" + SECONDS_STR) +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + int idx_number = 3; + struct ospf6_interface *oi; + assert(ifp); + + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL) + oi = ospf6_interface_create(ifp); + assert(oi); + + oi->hello_interval = strmatch(argv[0]->text, "no") + ? OSPF_HELLO_INTERVAL_DEFAULT + : strtoul(argv[idx_number]->arg, NULL, 10); + + /* + * If the thread is scheduled, send the new hello now. + */ + if (event_is_scheduled(oi->thread_send_hello)) { + EVENT_OFF(oi->thread_send_hello); + + event_add_timer(master, ospf6_hello_send, oi, 0, + &oi->thread_send_hello); + } + return CMD_SUCCESS; +} + +ALIAS (ipv6_ospf6_hellointerval, + no_ipv6_ospf6_hellointerval_cmd, + "no ipv6 ospf6 hello-interval [(1-65535)]", + NO_STR + IP6_STR + OSPF6_STR + "Time between HELLO packets\n" + SECONDS_STR) + +/* interface variable set command */ +DEFUN (ipv6_ospf6_deadinterval, + ipv6_ospf6_deadinterval_cmd, + "ipv6 ospf6 dead-interval (1-65535)", + IP6_STR + OSPF6_STR + "Interval time after which a neighbor is declared down\n" + SECONDS_STR) +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + int idx_number = 3; + struct ospf6_interface *oi; + assert(ifp); + + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL) + oi = ospf6_interface_create(ifp); + assert(oi); + + oi->dead_interval = strmatch(argv[0]->arg, "no") + ? OSPF_ROUTER_DEAD_INTERVAL_DEFAULT + : strtoul(argv[idx_number]->arg, NULL, 10); + return CMD_SUCCESS; +} + +ALIAS (ipv6_ospf6_deadinterval, + no_ipv6_ospf6_deadinterval_cmd, + "no ipv6 ospf6 dead-interval [(1-65535)]", + NO_STR + IP6_STR + OSPF6_STR + "Interval time after which a neighbor is declared down\n" + SECONDS_STR) + +DEFPY(ipv6_ospf6_gr_hdelay, ipv6_ospf6_gr_hdelay_cmd, + "ipv6 ospf6 graceful-restart hello-delay (1-1800)", + IP6_STR + OSPF6_STR + "Graceful Restart parameters\n" + "Delay the sending of the first hello packets.\n" + "Delay in seconds\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf6_interface *oi; + + oi = ifp->info; + if (oi == NULL) + oi = ospf6_interface_create(ifp); + + /* Note: new or updated value won't affect ongoing graceful restart. */ + oi->gr.hello_delay.interval = hello_delay; + + return CMD_SUCCESS; +} + +DEFPY(no_ipv6_ospf6_gr_hdelay, no_ipv6_ospf6_gr_hdelay_cmd, + "no ipv6 ospf6 graceful-restart hello-delay [(1-1800)]", + NO_STR + IP6_STR + OSPF6_STR + "Graceful Restart parameters\n" + "Delay the sending of the first hello packets.\n" + "Delay in seconds\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf6_interface *oi; + + oi = ifp->info; + if (oi == NULL) + oi = ospf6_interface_create(ifp); + + oi->gr.hello_delay.interval = OSPF_HELLO_DELAY_DEFAULT; + oi->gr.hello_delay.elapsed_seconds = 0; + EVENT_OFF(oi->gr.hello_delay.t_grace_send); + + return CMD_SUCCESS; +} + +/* interface variable set command */ +DEFUN (ipv6_ospf6_transmitdelay, + ipv6_ospf6_transmitdelay_cmd, + "ipv6 ospf6 transmit-delay (1-3600)", + IP6_STR + OSPF6_STR + "Link state transmit delay\n" + SECONDS_STR) +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + int idx_number = 3; + struct ospf6_interface *oi; + assert(ifp); + + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL) + oi = ospf6_interface_create(ifp); + assert(oi); + + oi->transdelay = strmatch(argv[0]->text, "no") + ? OSPF6_INTERFACE_TRANSDELAY + : strtoul(argv[idx_number]->arg, NULL, 10); + return CMD_SUCCESS; +} + +ALIAS (ipv6_ospf6_transmitdelay, + no_ipv6_ospf6_transmitdelay_cmd, + "no ipv6 ospf6 transmit-delay [(1-3600)]", + NO_STR + IP6_STR + OSPF6_STR + "Link state transmit delay\n" + SECONDS_STR) + +/* interface variable set command */ +DEFUN (ipv6_ospf6_retransmitinterval, + ipv6_ospf6_retransmitinterval_cmd, + "ipv6 ospf6 retransmit-interval (1-65535)", + IP6_STR + OSPF6_STR + "Time between retransmitting lost link state advertisements\n" + SECONDS_STR) +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + int idx_number = 3; + struct ospf6_interface *oi; + assert(ifp); + + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL) + oi = ospf6_interface_create(ifp); + assert(oi); + + oi->rxmt_interval = strmatch(argv[0]->text, "no") + ? OSPF_RETRANSMIT_INTERVAL_DEFAULT + : strtoul(argv[idx_number]->arg, NULL, 10); + return CMD_SUCCESS; +} + +ALIAS (ipv6_ospf6_retransmitinterval, + no_ipv6_ospf6_retransmitinterval_cmd, + "no ipv6 ospf6 retransmit-interval [(1-65535)]", + NO_STR + IP6_STR + OSPF6_STR + "Time between retransmitting lost link state advertisements\n" + SECONDS_STR) + +/* interface variable set command */ +DEFUN (ipv6_ospf6_priority, + ipv6_ospf6_priority_cmd, + "ipv6 ospf6 priority (0-255)", + IP6_STR + OSPF6_STR + "Router priority\n" + "Priority value\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + int idx_number = 3; + struct ospf6_interface *oi; + assert(ifp); + + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL) + oi = ospf6_interface_create(ifp); + assert(oi); + + oi->priority = strmatch(argv[0]->text, "no") + ? OSPF6_INTERFACE_PRIORITY + : strtoul(argv[idx_number]->arg, NULL, 10); + + if (oi->area + && (oi->state == OSPF6_INTERFACE_DROTHER + || oi->state == OSPF6_INTERFACE_BDR + || oi->state == OSPF6_INTERFACE_DR)) { + if (ospf6_interface_state_change(dr_election(oi), oi) == -1) + OSPF6_LINK_LSA_SCHEDULE(oi); + } + + return CMD_SUCCESS; +} + +ALIAS (ipv6_ospf6_priority, + no_ipv6_ospf6_priority_cmd, + "no ipv6 ospf6 priority [(0-255)]", + NO_STR + IP6_STR + OSPF6_STR + "Router priority\n" + "Priority value\n") + +DEFUN (ipv6_ospf6_instance, + ipv6_ospf6_instance_cmd, + "ipv6 ospf6 instance-id (0-255)", + IP6_STR + OSPF6_STR + "Instance ID for this interface\n" + "Instance ID value\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + int idx_number = 3; + struct ospf6_interface *oi; + assert(ifp); + + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL) + oi = ospf6_interface_create(ifp); + assert(oi); + + oi->instance_id = strmatch(argv[0]->text, "no") + ? OSPF6_INTERFACE_INSTANCE_ID + : strtoul(argv[idx_number]->arg, NULL, 10); + return CMD_SUCCESS; +} + +ALIAS (ipv6_ospf6_instance, + no_ipv6_ospf6_instance_cmd, + "no ipv6 ospf6 instance-id [(0-255)]", + NO_STR + IP6_STR + OSPF6_STR + "Instance ID for this interface\n" + "Instance ID value\n") + +DEFUN (ipv6_ospf6_passive, + ipv6_ospf6_passive_cmd, + "ipv6 ospf6 passive", + IP6_STR + OSPF6_STR + "Passive interface; no adjacency will be formed on this interface\n" + ) +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf6_interface *oi; + struct listnode *node, *nnode; + struct ospf6_neighbor *on; + + assert(ifp); + + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL) + oi = ospf6_interface_create(ifp); + assert(oi); + + SET_FLAG(oi->flag, OSPF6_INTERFACE_PASSIVE); + EVENT_OFF(oi->thread_send_hello); + EVENT_OFF(oi->thread_sso); + + for (ALL_LIST_ELEMENTS(oi->neighbor_list, node, nnode, on)) { + EVENT_OFF(on->inactivity_timer); + event_add_event(master, inactivity_timer, on, 0, NULL); + } + + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_ospf6_passive, + no_ipv6_ospf6_passive_cmd, + "no ipv6 ospf6 passive", + NO_STR + IP6_STR + OSPF6_STR + "passive interface: No Adjacency will be formed on this I/F\n" + ) +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf6_interface *oi; + assert(ifp); + + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL) + oi = ospf6_interface_create(ifp); + assert(oi); + + UNSET_FLAG(oi->flag, OSPF6_INTERFACE_PASSIVE); + EVENT_OFF(oi->thread_send_hello); + EVENT_OFF(oi->thread_sso); + + /* don't send hellos over loopback interface */ + if (!if_is_loopback(oi->interface)) + event_add_timer(master, ospf6_hello_send, oi, 0, + &oi->thread_send_hello); + + return CMD_SUCCESS; +} + +DEFUN (ipv6_ospf6_mtu_ignore, + ipv6_ospf6_mtu_ignore_cmd, + "ipv6 ospf6 mtu-ignore", + IP6_STR + OSPF6_STR + "Disable MTU mismatch detection on this interface\n" + ) +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf6_interface *oi; + assert(ifp); + + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL) + oi = ospf6_interface_create(ifp); + assert(oi); + + oi->mtu_ignore = 1; + + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_ospf6_mtu_ignore, + no_ipv6_ospf6_mtu_ignore_cmd, + "no ipv6 ospf6 mtu-ignore", + NO_STR + IP6_STR + OSPF6_STR + "Disable MTU mismatch detection on this interface\n" + ) +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf6_interface *oi; + assert(ifp); + + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL) + oi = ospf6_interface_create(ifp); + assert(oi); + + oi->mtu_ignore = 0; + + return CMD_SUCCESS; +} + +DEFUN (ipv6_ospf6_advertise_prefix_list, + ipv6_ospf6_advertise_prefix_list_cmd, + "ipv6 ospf6 advertise prefix-list WORD", + IP6_STR + OSPF6_STR + "Advertising options\n" + "Filter prefix using prefix-list\n" + "Prefix list name\n" + ) +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + int idx_word = 4; + struct ospf6_interface *oi; + assert(ifp); + + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL) + oi = ospf6_interface_create(ifp); + assert(oi); + + if (oi->plist_name) + XFREE(MTYPE_CFG_PLIST_NAME, oi->plist_name); + oi->plist_name = XSTRDUP(MTYPE_CFG_PLIST_NAME, argv[idx_word]->arg); + + ospf6_interface_connected_route_update(oi->interface); + + if (oi->area) { + OSPF6_LINK_LSA_SCHEDULE(oi); + if (oi->state == OSPF6_INTERFACE_DR) { + OSPF6_NETWORK_LSA_SCHEDULE(oi); + OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT(oi); + } + OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB(oi->area); + } + + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_ospf6_advertise_prefix_list, + no_ipv6_ospf6_advertise_prefix_list_cmd, + "no ipv6 ospf6 advertise prefix-list [WORD]", + NO_STR + IP6_STR + OSPF6_STR + "Advertising options\n" + "Filter prefix using prefix-list\n" + "Prefix list name\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf6_interface *oi; + assert(ifp); + + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL) + oi = ospf6_interface_create(ifp); + assert(oi); + + if (oi->plist_name) + XFREE(MTYPE_CFG_PLIST_NAME, oi->plist_name); + + ospf6_interface_connected_route_update(oi->interface); + + if (oi->area) { + OSPF6_LINK_LSA_SCHEDULE(oi); + if (oi->state == OSPF6_INTERFACE_DR) { + OSPF6_NETWORK_LSA_SCHEDULE(oi); + OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT(oi); + } + OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB(oi->area); + } + + return CMD_SUCCESS; +} + +DEFUN (ipv6_ospf6_network, + ipv6_ospf6_network_cmd, + "ipv6 ospf6 network <broadcast|point-to-point>", + IP6_STR + OSPF6_STR + "Network type\n" + "Specify OSPF6 broadcast network\n" + "Specify OSPF6 point-to-point network\n" + ) +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + int idx_network = 3; + struct ospf6_interface *oi; + assert(ifp); + + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL) { + oi = ospf6_interface_create(ifp); + } + assert(oi); + + oi->type_cfg = true; + + if (strncmp(argv[idx_network]->arg, "b", 1) == 0) { + if (oi->type == OSPF_IFTYPE_BROADCAST) + return CMD_SUCCESS; + + oi->type = OSPF_IFTYPE_BROADCAST; + } else if (strncmp(argv[idx_network]->arg, "point-to-p", 10) == 0) { + if (oi->type == OSPF_IFTYPE_POINTOPOINT) { + return CMD_SUCCESS; + } + oi->type = OSPF_IFTYPE_POINTOPOINT; + } + + /* Reset the interface */ + event_execute(master, interface_down, oi, 0, NULL); + event_execute(master, interface_up, oi, 0, NULL); + + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_ospf6_network, + no_ipv6_ospf6_network_cmd, + "no ipv6 ospf6 network [<broadcast|point-to-point>]", + NO_STR + IP6_STR + OSPF6_STR + "Set default network type\n" + "Specify OSPF6 broadcast network\n" + "Specify OSPF6 point-to-point network\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf6_interface *oi; + int type; + + assert(ifp); + + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL) { + return CMD_SUCCESS; + } + + oi->type_cfg = false; + + type = ospf6_default_iftype(ifp); + if (oi->type == type) { + return CMD_SUCCESS; + } + oi->type = type; + + /* Reset the interface */ + event_execute(master, interface_down, oi, 0, NULL); + event_execute(master, interface_up, oi, 0, NULL); + + return CMD_SUCCESS; +} + +static int config_write_ospf6_interface(struct vty *vty, struct vrf *vrf) +{ + struct ospf6_interface *oi; + struct interface *ifp; + char buf[INET_ADDRSTRLEN]; + + FOR_ALL_INTERFACES (vrf, ifp) { + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL) + continue; + + if_vty_config_start(vty, ifp); + + if (ifp->desc) + vty_out(vty, " description %s\n", ifp->desc); + if (oi->area_id_format != OSPF6_AREA_FMT_UNSET) { + area_id2str(buf, sizeof(buf), oi->area_id, + oi->area_id_format); + vty_out(vty, " ipv6 ospf6 area %s\n", buf); + } + if (oi->c_ifmtu) + vty_out(vty, " ipv6 ospf6 ifmtu %d\n", oi->c_ifmtu); + + if (CHECK_FLAG(oi->flag, OSPF6_INTERFACE_NOAUTOCOST)) + vty_out(vty, " ipv6 ospf6 cost %d\n", oi->cost); + + if (oi->hello_interval != OSPF6_INTERFACE_HELLO_INTERVAL) + vty_out(vty, " ipv6 ospf6 hello-interval %d\n", + oi->hello_interval); + + if (oi->dead_interval != OSPF6_INTERFACE_DEAD_INTERVAL) + vty_out(vty, " ipv6 ospf6 dead-interval %d\n", + oi->dead_interval); + + if (oi->rxmt_interval != OSPF6_INTERFACE_RXMT_INTERVAL) + vty_out(vty, " ipv6 ospf6 retransmit-interval %d\n", + oi->rxmt_interval); + + if (oi->priority != OSPF6_INTERFACE_PRIORITY) + vty_out(vty, " ipv6 ospf6 priority %d\n", oi->priority); + + if (oi->transdelay != OSPF6_INTERFACE_TRANSDELAY) + vty_out(vty, " ipv6 ospf6 transmit-delay %d\n", + oi->transdelay); + + if (oi->instance_id != OSPF6_INTERFACE_INSTANCE_ID) + vty_out(vty, " ipv6 ospf6 instance-id %d\n", + oi->instance_id); + + if (oi->plist_name) + vty_out(vty, " ipv6 ospf6 advertise prefix-list %s\n", + oi->plist_name); + + if (CHECK_FLAG(oi->flag, OSPF6_INTERFACE_PASSIVE)) + vty_out(vty, " ipv6 ospf6 passive\n"); + + if (oi->mtu_ignore) + vty_out(vty, " ipv6 ospf6 mtu-ignore\n"); + + if (oi->type_cfg && oi->type == OSPF_IFTYPE_POINTOPOINT) + vty_out(vty, " ipv6 ospf6 network point-to-point\n"); + else if (oi->type_cfg && oi->type == OSPF_IFTYPE_BROADCAST) + vty_out(vty, " ipv6 ospf6 network broadcast\n"); + + if (oi->gr.hello_delay.interval != OSPF_HELLO_DELAY_DEFAULT) + vty_out(vty, + " ipv6 ospf6 graceful-restart hello-delay %u\n", + oi->gr.hello_delay.interval); + + ospf6_bfd_write_config(vty, oi); + + ospf6_auth_write_config(vty, &oi->at_data); + if_vty_config_end(vty); + } + return 0; +} + +/* Configuration write function for ospfd. */ +static int config_write_interface(struct vty *vty) +{ + int write = 0; + struct vrf *vrf = NULL; + + /* Display all VRF aware OSPF interface configuration */ + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + write += config_write_ospf6_interface(vty, vrf); + } + + return write; +} + +static int ospf6_ifp_create(struct interface *ifp) +{ + if (IS_OSPF6_DEBUG_ZEBRA(RECV)) + zlog_debug("Zebra Interface add: %s index %d mtu %d", ifp->name, + ifp->ifindex, ifp->mtu6); + + if (ifp->info) + ospf6_interface_start(ifp->info); + + return 0; +} + +static int ospf6_ifp_up(struct interface *ifp) +{ + if (IS_OSPF6_DEBUG_ZEBRA(RECV)) + zlog_debug( + "Zebra Interface state change: %s index %d flags %llx metric %d mtu %d bandwidth %d", + ifp->name, ifp->ifindex, (unsigned long long)ifp->flags, + ifp->metric, ifp->mtu6, ifp->bandwidth); + + ospf6_interface_state_update(ifp); + + return 0; +} + +static int ospf6_ifp_down(struct interface *ifp) +{ + if (IS_OSPF6_DEBUG_ZEBRA(RECV)) + zlog_debug( + "Zebra Interface state change: %s index %d flags %llx metric %d mtu %d bandwidth %d", + ifp->name, ifp->ifindex, (unsigned long long)ifp->flags, + ifp->metric, ifp->mtu6, ifp->bandwidth); + + ospf6_interface_state_update(ifp); + + return 0; +} + +static int ospf6_ifp_destroy(struct interface *ifp) +{ + if (if_is_up(ifp)) + zlog_warn("Zebra: got delete of %s, but interface is still up", + ifp->name); + + if (IS_OSPF6_DEBUG_ZEBRA(RECV)) + zlog_debug("Zebra Interface delete: %s index %d mtu %d", + ifp->name, ifp->ifindex, ifp->mtu6); + + if (ifp->info) + ospf6_interface_stop(ifp->info); + + return 0; +} + +void ospf6_interface_init(void) +{ + /* Install interface node. */ + if_cmd_init(config_write_interface); + if_zapi_callbacks(ospf6_ifp_create, ospf6_ifp_up, + ospf6_ifp_down, ospf6_ifp_destroy); + + install_element(VIEW_NODE, &show_ipv6_ospf6_interface_prefix_cmd); + install_element(VIEW_NODE, &show_ipv6_ospf6_interface_ifname_cmd); + install_element(VIEW_NODE, + &show_ipv6_ospf6_interface_ifname_prefix_cmd); + install_element(VIEW_NODE, &show_ipv6_ospf6_interface_traffic_cmd); + + install_element(INTERFACE_NODE, &ipv6_ospf6_area_cmd); + install_element(INTERFACE_NODE, &no_ipv6_ospf6_area_cmd); + install_element(INTERFACE_NODE, &ipv6_ospf6_cost_cmd); + install_element(INTERFACE_NODE, &no_ipv6_ospf6_cost_cmd); + install_element(INTERFACE_NODE, &ipv6_ospf6_ifmtu_cmd); + install_element(INTERFACE_NODE, &no_ipv6_ospf6_ifmtu_cmd); + + install_element(INTERFACE_NODE, &ipv6_ospf6_deadinterval_cmd); + install_element(INTERFACE_NODE, &ipv6_ospf6_hellointerval_cmd); + install_element(INTERFACE_NODE, &ipv6_ospf6_gr_hdelay_cmd); + install_element(INTERFACE_NODE, &ipv6_ospf6_priority_cmd); + install_element(INTERFACE_NODE, &ipv6_ospf6_retransmitinterval_cmd); + install_element(INTERFACE_NODE, &ipv6_ospf6_transmitdelay_cmd); + install_element(INTERFACE_NODE, &ipv6_ospf6_instance_cmd); + install_element(INTERFACE_NODE, &no_ipv6_ospf6_deadinterval_cmd); + install_element(INTERFACE_NODE, &no_ipv6_ospf6_hellointerval_cmd); + install_element(INTERFACE_NODE, &no_ipv6_ospf6_gr_hdelay_cmd); + install_element(INTERFACE_NODE, &no_ipv6_ospf6_priority_cmd); + install_element(INTERFACE_NODE, &no_ipv6_ospf6_retransmitinterval_cmd); + install_element(INTERFACE_NODE, &no_ipv6_ospf6_transmitdelay_cmd); + install_element(INTERFACE_NODE, &no_ipv6_ospf6_instance_cmd); + + install_element(INTERFACE_NODE, &ipv6_ospf6_passive_cmd); + install_element(INTERFACE_NODE, &no_ipv6_ospf6_passive_cmd); + + install_element(INTERFACE_NODE, &ipv6_ospf6_mtu_ignore_cmd); + install_element(INTERFACE_NODE, &no_ipv6_ospf6_mtu_ignore_cmd); + + install_element(INTERFACE_NODE, &ipv6_ospf6_advertise_prefix_list_cmd); + install_element(INTERFACE_NODE, + &no_ipv6_ospf6_advertise_prefix_list_cmd); + + install_element(INTERFACE_NODE, &ipv6_ospf6_network_cmd); + install_element(INTERFACE_NODE, &no_ipv6_ospf6_network_cmd); + + /* reference bandwidth commands */ + install_element(OSPF6_NODE, &auto_cost_reference_bandwidth_cmd); + install_element(OSPF6_NODE, &no_auto_cost_reference_bandwidth_cmd); + /* write-multiplier commands */ + install_element(OSPF6_NODE, &ospf6_write_multiplier_cmd); + install_element(OSPF6_NODE, &no_ospf6_write_multiplier_cmd); +} + +/* Clear the specified interface structure */ +void ospf6_interface_clear(struct interface *ifp) +{ + struct ospf6_interface *oi; + + if (!if_is_operative(ifp)) + return; + + if (ifp->info == NULL) + return; + + oi = (struct ospf6_interface *)ifp->info; + + if (IS_OSPF6_DEBUG_INTERFACE) + zlog_debug("Interface %s: clear by reset", ifp->name); + + /* Reset the interface */ + event_execute(master, interface_down, oi, 0, NULL); + event_execute(master, interface_up, oi, 0, NULL); +} + +/* Clear interface */ +DEFUN (clear_ipv6_ospf6_interface, + clear_ipv6_ospf6_interface_cmd, + "clear ipv6 ospf6 [vrf NAME] interface [IFNAME]", + CLEAR_STR + IP6_STR + OSPF6_STR + VRF_CMD_HELP_STR + INTERFACE_STR + IFNAME_STR + ) +{ + struct vrf *vrf; + int idx_vrf = 3; + int idx_ifname = 4; + struct interface *ifp; + const char *vrf_name; + + if (argv_find(argv, argc, "vrf", &idx_vrf)) + vrf_name = argv[idx_vrf + 1]->arg; + else + vrf_name = VRF_DEFAULT_NAME; + vrf = vrf_lookup_by_name(vrf_name); + if (!vrf) { + vty_out(vty, "%% VRF %s not found\n", vrf_name); + return CMD_WARNING; + } + + if (!argv_find(argv, argc, "IFNAME", &idx_ifname)) { + /* Clear all the ospfv3 interfaces. */ + FOR_ALL_INTERFACES (vrf, ifp) + ospf6_interface_clear(ifp); + } else { + /* Interface name is specified. */ + ifp = if_lookup_by_name_vrf(argv[idx_ifname]->arg, vrf); + if (!ifp) { + vty_out(vty, "No such Interface: %s\n", + argv[idx_ifname]->arg); + return CMD_WARNING; + } + ospf6_interface_clear(ifp); + } + + return CMD_SUCCESS; +} + +void install_element_ospf6_clear_interface(void) +{ + install_element(ENABLE_NODE, &clear_ipv6_ospf6_interface_cmd); +} + +DEFUN (debug_ospf6_interface, + debug_ospf6_interface_cmd, + "debug ospf6 interface", + DEBUG_STR + OSPF6_STR + "Debug OSPFv3 Interface\n" + ) +{ + OSPF6_DEBUG_INTERFACE_ON(); + return CMD_SUCCESS; +} + +DEFUN (no_debug_ospf6_interface, + no_debug_ospf6_interface_cmd, + "no debug ospf6 interface", + NO_STR + DEBUG_STR + OSPF6_STR + "Debug OSPFv3 Interface\n" + ) +{ + OSPF6_DEBUG_INTERFACE_OFF(); + return CMD_SUCCESS; +} + +int config_write_ospf6_debug_interface(struct vty *vty) +{ + if (IS_OSPF6_DEBUG_INTERFACE) + vty_out(vty, "debug ospf6 interface\n"); + return 0; +} + +void install_element_ospf6_debug_interface(void) +{ + install_element(ENABLE_NODE, &debug_ospf6_interface_cmd); + install_element(ENABLE_NODE, &no_debug_ospf6_interface_cmd); + install_element(CONFIG_NODE, &debug_ospf6_interface_cmd); + install_element(CONFIG_NODE, &no_debug_ospf6_interface_cmd); +} + +void ospf6_auth_write_config(struct vty *vty, struct ospf6_auth_data *at_data) +{ + if (CHECK_FLAG(at_data->flags, OSPF6_AUTH_TRAILER_KEYCHAIN)) + vty_out(vty, " ipv6 ospf6 authentication keychain %s\n", + at_data->keychain); + else if (CHECK_FLAG(at_data->flags, OSPF6_AUTH_TRAILER_MANUAL_KEY)) + vty_out(vty, + " ipv6 ospf6 authentication key-id %d hash-algo %s key %s\n", + at_data->key_id, + keychain_get_algo_name_by_id(at_data->hash_algo), + at_data->auth_key); +} + +DEFUN(ipv6_ospf6_intf_auth_trailer_keychain, + ipv6_ospf6_intf_auth_trailer_keychain_cmd, + "ipv6 ospf6 authentication keychain KEYCHAIN_NAME", + IP6_STR OSPF6_STR + "Enable authentication on this interface\n" + "Keychain\n" + "Keychain name\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + int keychain_idx = 4; + struct ospf6_interface *oi; + + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL) + oi = ospf6_interface_create(ifp); + + assert(oi); + if (CHECK_FLAG(oi->at_data.flags, OSPF6_AUTH_TRAILER_MANUAL_KEY)) { + vty_out(vty, + "Manual key configured, unconfigure it before configuring key chain\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + SET_FLAG(oi->at_data.flags, OSPF6_AUTH_TRAILER_KEYCHAIN); + if (oi->at_data.keychain) + XFREE(MTYPE_OSPF6_AUTH_KEYCHAIN, oi->at_data.keychain); + + oi->at_data.keychain = + XSTRDUP(MTYPE_OSPF6_AUTH_KEYCHAIN, argv[keychain_idx]->arg); + + return CMD_SUCCESS; +} + +DEFUN(no_ipv6_ospf6_intf_auth_trailer_keychain, + no_ipv6_ospf6_intf_auth_trailer_keychain_cmd, + "no ipv6 ospf6 authentication keychain [KEYCHAIN_NAME]", + NO_STR IP6_STR OSPF6_STR + "Enable authentication on this interface\n" + "Keychain\n" + "Keychain name\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf6_interface *oi; + + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL) + oi = ospf6_interface_create(ifp); + + assert(oi); + if (!CHECK_FLAG(oi->at_data.flags, OSPF6_AUTH_TRAILER_KEYCHAIN)) + return CMD_SUCCESS; + + if (oi->at_data.keychain) { + oi->at_data.flags = 0; + XFREE(MTYPE_OSPF6_AUTH_KEYCHAIN, oi->at_data.keychain); + oi->at_data.keychain = NULL; + } + + return CMD_SUCCESS; +} + +DEFUN(ipv6_ospf6_intf_auth_trailer_key, ipv6_ospf6_intf_auth_trailer_key_cmd, + "ipv6 ospf6 authentication key-id (1-65535) hash-algo " + "<md5|hmac-sha-1|hmac-sha-256|hmac-sha-384|hmac-sha-512> " + "key WORD", + IP6_STR OSPF6_STR + "Authentication\n" + "Key ID\n" + "Key ID value\n" + "Cryptographic-algorithm\n" + "Use MD5 algorithm\n" + "Use HMAC-SHA-1 algorithm\n" + "Use HMAC-SHA-256 algorithm\n" + "Use HMAC-SHA-384 algorithm\n" + "Use HMAC-SHA-512 algorithm\n" + "Password\n" + "Password string (key)\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + int key_id_idx = 4; + int hash_algo_idx = 6; + int password_idx = 8; + struct ospf6_interface *oi; + uint8_t hash_algo = KEYCHAIN_ALGO_NULL; + + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL) + oi = ospf6_interface_create(ifp); + + assert(oi); + if (CHECK_FLAG(oi->at_data.flags, OSPF6_AUTH_TRAILER_KEYCHAIN)) { + vty_out(vty, + "key chain configured, unconfigure it before configuring manual key\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + hash_algo = keychain_get_algo_id_by_name(argv[hash_algo_idx]->arg); +#ifndef CRYPTO_OPENSSL + if (hash_algo == KEYCHAIN_ALGO_NULL) { + vty_out(vty, + "Hash algorithm not supported, compile with --with-crypto=openssl\n"); + return CMD_WARNING_CONFIG_FAILED; + } +#endif /* CRYPTO_OPENSSL */ + + SET_FLAG(oi->at_data.flags, OSPF6_AUTH_TRAILER_MANUAL_KEY); + oi->at_data.hash_algo = hash_algo; + oi->at_data.key_id = (uint16_t)strtol(argv[key_id_idx]->arg, NULL, 10); + if (oi->at_data.auth_key) + XFREE(MTYPE_OSPF6_AUTH_MANUAL_KEY, oi->at_data.auth_key); + oi->at_data.auth_key = + XSTRDUP(MTYPE_OSPF6_AUTH_MANUAL_KEY, argv[password_idx]->arg); + + return CMD_SUCCESS; +} + +DEFUN(no_ipv6_ospf6_intf_auth_trailer_key, + no_ipv6_ospf6_intf_auth_trailer_key_cmd, + "no ipv6 ospf6 authentication key-id [(1-65535) hash-algo " + "<md5|hmac-sha-1|hmac-sha-256|hmac-sha-384|hmac-sha-512> " + "key WORD]", + NO_STR IP6_STR OSPF6_STR + "Authentication\n" + "Key ID\n" + "Key ID value\n" + "Cryptographic-algorithm\n" + "Use MD5 algorithm\n" + "Use HMAC-SHA-1 algorithm\n" + "Use HMAC-SHA-256 algorithm\n" + "Use HMAC-SHA-384 algorithm\n" + "Use HMAC-SHA-512 algorithm\n" + "Password\n" + "Password string (key)\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf6_interface *oi; +#ifndef CRYPTO_OPENSSL + int hash_algo_idx = 7; + uint8_t hash_algo = KEYCHAIN_ALGO_NULL; +#endif /* CRYPTO_OPENSSL */ + + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL) + oi = ospf6_interface_create(ifp); + + assert(oi); + if (!CHECK_FLAG(oi->at_data.flags, OSPF6_AUTH_TRAILER_MANUAL_KEY)) + return CMD_SUCCESS; + +#ifndef CRYPTO_OPENSSL + hash_algo = keychain_get_algo_id_by_name(argv[hash_algo_idx]->arg); + if (hash_algo == KEYCHAIN_ALGO_NULL) { + vty_out(vty, + "Hash algorithm not supported, compile with --with-crypto=openssl\n"); + return CMD_WARNING_CONFIG_FAILED; + } +#endif /* CRYPTO_OPENSSL */ + + if (oi->at_data.auth_key) { + oi->at_data.flags = 0; + XFREE(MTYPE_OSPF6_AUTH_MANUAL_KEY, oi->at_data.auth_key); + oi->at_data.auth_key = NULL; + } + + return CMD_SUCCESS; +} + +void ospf6_interface_auth_trailer_cmd_init(void) +{ + /*Install OSPF6 auth trailer commands at interface level */ + install_element(INTERFACE_NODE, + &ipv6_ospf6_intf_auth_trailer_keychain_cmd); + install_element(INTERFACE_NODE, + &no_ipv6_ospf6_intf_auth_trailer_keychain_cmd); + install_element(INTERFACE_NODE, &ipv6_ospf6_intf_auth_trailer_key_cmd); + install_element(INTERFACE_NODE, + &no_ipv6_ospf6_intf_auth_trailer_key_cmd); +} diff --git a/ospf6d/ospf6_interface.h b/ospf6d/ospf6_interface.h new file mode 100644 index 0000000..5942df0 --- /dev/null +++ b/ospf6d/ospf6_interface.h @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2003 Yasuhiro Ohara + */ + +#ifndef OSPF6_INTERFACE_H +#define OSPF6_INTERFACE_H + +#include "qobj.h" +#include "hook.h" +#include "if.h" +#include "ospf6d.h" + +DECLARE_MTYPE(OSPF6_AUTH_MANUAL_KEY); + +/* Debug option */ +extern unsigned char conf_debug_ospf6_interface; +#define OSPF6_DEBUG_INTERFACE_ON() (conf_debug_ospf6_interface = 1) +#define OSPF6_DEBUG_INTERFACE_OFF() (conf_debug_ospf6_interface = 0) +#define IS_OSPF6_DEBUG_INTERFACE (conf_debug_ospf6_interface) + +struct ospf6_auth_data { + /* config data */ + uint8_t hash_algo; /* hash algorithm type */ + uint16_t key_id; /* key-id used as SA in auth packet */ + char *auth_key; /* Auth key */ + char *keychain; /* keychain name */ + + /* operational data */ + uint8_t flags; /* Flags related to auth config */ + + /* Counters and Statistics */ + uint32_t tx_drop; /* Pkt drop due to auth fail while sending */ + uint32_t rx_drop; /* Pkt drop due to auth fail while reading */ +}; + +/* Interface structure */ +struct ospf6_interface { + /* IF info from zebra */ + struct interface *interface; + + /* back pointer */ + struct ospf6_area *area; + + uint32_t area_id; + int area_id_format; + + /* list of ospf6 neighbor */ + struct list *neighbor_list; + + /* linklocal address of this I/F */ + struct in6_addr *linklocal_addr; + + /* Interface ID; use interface->ifindex */ + + /* ospf6 instance id */ + uint8_t instance_id; + + /* I/F transmission delay */ + uint32_t transdelay; + + /* Packet send buffer. */ + struct ospf6_fifo *obuf; /* Output queue */ + + /* Network Type */ + uint8_t type; + bool type_cfg; + + /* Router Priority */ + uint8_t priority; + + /* Time Interval */ + uint16_t hello_interval; + uint16_t dead_interval; + uint32_t rxmt_interval; + + /* Graceful-Restart data. */ + struct { + struct { + uint16_t interval; + uint16_t elapsed_seconds; + struct event *t_grace_send; + } hello_delay; + } gr; + + uint32_t state_change; + + /* Cost */ + uint32_t cost; + + /* I/F MTU */ + uint32_t ifmtu; + + /* Configured MTU */ + uint32_t c_ifmtu; + + /* Interface State */ + uint8_t state; + + /* Interface socket setting trial counter, resets on success */ + uint8_t sso_try_cnt; + struct event *thread_sso; + + /* OSPF6 Interface flag */ + char flag; + + /* MTU mismatch check */ + uint8_t mtu_ignore; + + /* Authentication trailer related config */ + struct ospf6_auth_data at_data; + + /* Decision of DR Election */ + in_addr_t drouter; + in_addr_t bdrouter; + in_addr_t prev_drouter; + in_addr_t prev_bdrouter; + + /* Linklocal LSA Database: includes Link-LSA */ + struct ospf6_lsdb *lsdb; + struct ospf6_lsdb *lsdb_self; + + struct ospf6_lsdb *lsupdate_list; + struct ospf6_lsdb *lsack_list; + + /* Ongoing Tasks */ + struct event *thread_send_hello; + struct event *thread_send_lsupdate; + struct event *thread_send_lsack; + + struct event *thread_network_lsa; + struct event *thread_link_lsa; + struct event *thread_intra_prefix_lsa; + struct event *thread_as_extern_lsa; + struct event *thread_wait_timer; + + struct ospf6_route_table *route_connected; + + /* last hello sent */ + struct timeval last_hello; + + /* prefix-list name to filter connected prefix */ + char *plist_name; + + /* BFD information */ + struct { + bool enabled; + uint8_t detection_multiplier; + uint32_t min_rx; + uint32_t min_tx; + char *profile; + } bfd_config; + + int on_write_q; + + /* Statistics Fields */ + uint32_t hello_in; + uint32_t hello_out; + uint32_t db_desc_in; + uint32_t db_desc_out; + uint32_t ls_req_in; + uint32_t ls_req_out; + uint32_t ls_upd_in; + uint32_t ls_upd_out; + uint32_t ls_ack_in; + uint32_t ls_ack_out; + uint32_t discarded; + + QOBJ_FIELDS; +}; +DECLARE_QOBJ_TYPE(ospf6_interface); + +/* interface state */ +#define OSPF6_INTERFACE_NONE 0 +#define OSPF6_INTERFACE_DOWN 1 +#define OSPF6_INTERFACE_LOOPBACK 2 +#define OSPF6_INTERFACE_WAITING 3 +#define OSPF6_INTERFACE_POINTTOPOINT 4 +#define OSPF6_INTERFACE_DROTHER 5 +#define OSPF6_INTERFACE_BDR 6 +#define OSPF6_INTERFACE_DR 7 +#define OSPF6_INTERFACE_MAX 8 + +extern const char *const ospf6_interface_state_str[]; + +/* flags */ +#define OSPF6_INTERFACE_DISABLE 0x01 +#define OSPF6_INTERFACE_PASSIVE 0x02 +#define OSPF6_INTERFACE_NOAUTOCOST 0x04 + +/* default values */ +#define OSPF6_INTERFACE_HELLO_INTERVAL 10 +#define OSPF6_INTERFACE_DEAD_INTERVAL 40 +#define OSPF6_INTERFACE_RXMT_INTERVAL 5 +#define OSPF6_INTERFACE_COST 1 +#define OSPF6_INTERFACE_PRIORITY 1 +#define OSPF6_INTERFACE_TRANSDELAY 1 +#define OSPF6_INTERFACE_INSTANCE_ID 0 +#define OSPF6_INTERFACE_BANDWIDTH 10000 /* Mbps */ +#define OSPF6_REFERENCE_BANDWIDTH 100000 /* Mbps */ +#define OSPF6_INTERFACE_SSO_RETRY_INT 1 +#define OSPF6_INTERFACE_SSO_RETRY_MAX 5 + +/* Function Prototypes */ + +extern void ospf6_interface_start(struct ospf6_interface *oi); +extern void ospf6_interface_stop(struct ospf6_interface *oi); + +extern struct ospf6_interface * +ospf6_interface_lookup_by_ifindex(ifindex_t, vrf_id_t vrf_id); +extern struct ospf6_interface *ospf6_interface_create(struct interface *ifp); +extern void ospf6_interface_delete(struct ospf6_interface *oi); + +extern void ospf6_interface_enable(struct ospf6_interface *oi); +extern void ospf6_interface_disable(struct ospf6_interface *oi); + +extern void ospf6_interface_state_update(struct interface *ifp); +extern void ospf6_interface_connected_route_update(struct interface *ifp); +extern struct in6_addr * +ospf6_interface_get_global_address(struct interface *ifp); + +/* interface event */ +extern void interface_up(struct event *thread); +extern void interface_down(struct event *thread); +extern void wait_timer(struct event *thread); +extern void backup_seen(struct event *thread); +extern void neighbor_change(struct event *thread); + +extern void ospf6_interface_init(void); +extern void ospf6_interface_clear(struct interface *ifp); + +extern void install_element_ospf6_clear_interface(void); + +extern int config_write_ospf6_debug_interface(struct vty *vty); +extern void install_element_ospf6_debug_interface(void); +extern int ospf6_interface_neighbor_count(struct ospf6_interface *oi); +extern uint8_t dr_election(struct ospf6_interface *oi); + +extern void ospf6_interface_auth_trailer_cmd_init(void); +extern void ospf6_auth_write_config(struct vty *vty, + struct ospf6_auth_data *at_data); +DECLARE_HOOK(ospf6_interface_change, + (struct ospf6_interface * oi, int state, int old_state), + (oi, state, old_state)); + +#endif /* OSPF6_INTERFACE_H */ diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c new file mode 100644 index 0000000..eb5514a --- /dev/null +++ b/ospf6d/ospf6_intra.c @@ -0,0 +1,2520 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2003 Yasuhiro Ohara + */ + +#include <zebra.h> + +#include "log.h" +#include "linklist.h" +#include "frrevent.h" +#include "memory.h" +#include "if.h" +#include "prefix.h" +#include "table.h" +#include "vty.h" +#include "command.h" +#include "vrf.h" + +#include "ospf6_proto.h" +#include "ospf6_message.h" +#include "ospf6_route.h" +#include "ospf6_lsa.h" +#include "ospf6_lsdb.h" + +#include "ospf6_top.h" +#include "ospf6_area.h" +#include "ospf6_interface.h" +#include "ospf6_neighbor.h" +#include "ospf6_intra.h" +#include "ospf6_asbr.h" +#include "ospf6_abr.h" +#include "ospf6_flood.h" +#include "ospf6d.h" +#include "ospf6_spf.h" +#include "ospf6_gr.h" + +unsigned char conf_debug_ospf6_brouter = 0; +uint32_t conf_debug_ospf6_brouter_specific_router_id; +uint32_t conf_debug_ospf6_brouter_specific_area_id; + +#define MAX_LSA_PAYLOAD (1024 + 256) +/******************************/ +/* RFC2740 3.4.3.1 Router-LSA */ +/******************************/ + +static char *ospf6_router_lsa_get_nbr_id(struct ospf6_lsa *lsa, char *buf, + int buflen, int pos) +{ + struct ospf6_router_lsa *router_lsa; + struct ospf6_router_lsdesc *lsdesc; + char *start, *end; + char buf1[INET_ADDRSTRLEN], buf2[INET_ADDRSTRLEN]; + + if (lsa) { + router_lsa = (struct ospf6_router_lsa + *)((char *)lsa->header + + sizeof(struct ospf6_lsa_header)); + start = (char *)router_lsa + sizeof(struct ospf6_router_lsa); + end = (char *)lsa->header + ntohs(lsa->header->length); + + lsdesc = (struct ospf6_router_lsdesc + *)(start + + pos * (sizeof(struct + ospf6_router_lsdesc))); + if ((char *)lsdesc + sizeof(struct ospf6_router_lsdesc) + <= end) { + if (buf && (buflen > INET_ADDRSTRLEN * 2)) { + inet_ntop(AF_INET, + &lsdesc->neighbor_interface_id, buf1, + sizeof(buf1)); + inet_ntop(AF_INET, &lsdesc->neighbor_router_id, + buf2, sizeof(buf2)); + snprintf(buf, buflen, "%s/%s", buf2, buf1); + + return buf; + } + } + } + + return NULL; +} + +static int ospf6_router_lsa_show(struct vty *vty, struct ospf6_lsa *lsa, + json_object *json_obj, bool use_json) +{ + char *start, *end, *current; + char buf[32], name[32], bits[16], options[32]; + struct ospf6_router_lsa *router_lsa; + struct ospf6_router_lsdesc *lsdesc; + json_object *json_arr = NULL; + json_object *json_loop; + + router_lsa = + (struct ospf6_router_lsa *)((char *)lsa->header + + sizeof(struct ospf6_lsa_header)); + + ospf6_capability_printbuf(router_lsa->bits, bits, sizeof(bits)); + ospf6_options_printbuf(router_lsa->options, options, sizeof(options)); + if (use_json) { + json_object_string_add(json_obj, "bits", bits); + json_object_string_add(json_obj, "options", options); + json_arr = json_object_new_array(); + } else + vty_out(vty, " Bits: %s Options: %s\n", bits, options); + + start = (char *)router_lsa + sizeof(struct ospf6_router_lsa); + end = (char *)lsa->header + ntohs(lsa->header->length); + for (current = start; + current + sizeof(struct ospf6_router_lsdesc) <= end; + current += sizeof(struct ospf6_router_lsdesc)) { + lsdesc = (struct ospf6_router_lsdesc *)current; + + if (lsdesc->type == OSPF6_ROUTER_LSDESC_POINTTOPOINT) + snprintf(name, sizeof(name), "Point-To-Point"); + else if (lsdesc->type == OSPF6_ROUTER_LSDESC_TRANSIT_NETWORK) + snprintf(name, sizeof(name), "Transit-Network"); + else if (lsdesc->type == OSPF6_ROUTER_LSDESC_STUB_NETWORK) + snprintf(name, sizeof(name), "Stub-Network"); + else if (lsdesc->type == OSPF6_ROUTER_LSDESC_VIRTUAL_LINK) + snprintf(name, sizeof(name), "Virtual-Link"); + else + snprintf(name, sizeof(name), "Unknown (%#x)", + lsdesc->type); + + if (use_json) { + json_loop = json_object_new_object(); + json_object_string_add(json_loop, "type", name); + json_object_int_add(json_loop, "metric", + ntohs(lsdesc->metric)); + json_object_string_addf( + json_loop, "interfaceId", "%pI4", + (in_addr_t *)&lsdesc->interface_id); + json_object_string_addf( + json_loop, "neighborInterfaceId", "%pI4", + (in_addr_t *)&lsdesc->neighbor_interface_id); + json_object_string_addf(json_loop, "neighborRouterId", + "%pI4", + &lsdesc->neighbor_router_id); + json_object_array_add(json_arr, json_loop); + } else { + vty_out(vty, " Type: %s Metric: %d\n", name, + ntohs(lsdesc->metric)); + vty_out(vty, " Interface ID: %s\n", + inet_ntop(AF_INET, &lsdesc->interface_id, buf, + sizeof(buf))); + vty_out(vty, " Neighbor Interface ID: %s\n", + inet_ntop(AF_INET, + &lsdesc->neighbor_interface_id, buf, + sizeof(buf))); + vty_out(vty, " Neighbor Router ID: %s\n", + inet_ntop(AF_INET, &lsdesc->neighbor_router_id, + buf, sizeof(buf))); + } + } + if (use_json) + json_object_object_add(json_obj, "lsaDescription", json_arr); + + return 0; +} + +static void ospf6_router_lsa_options_set(struct ospf6_area *oa, + struct ospf6_router_lsa *router_lsa) +{ + OSPF6_OPT_CLEAR_ALL(router_lsa->options); + memcpy(router_lsa->options, oa->options, 3); + + if (ospf6_check_and_set_router_abr(oa->ospf6)) + SET_FLAG(router_lsa->bits, OSPF6_ROUTER_BIT_B); + else + UNSET_FLAG(router_lsa->bits, OSPF6_ROUTER_BIT_B); + + if (!IS_AREA_STUB(oa) && ospf6_asbr_is_asbr(oa->ospf6)) { + SET_FLAG(router_lsa->bits, OSPF6_ROUTER_BIT_E); + } else { + UNSET_FLAG(router_lsa->bits, OSPF6_ROUTER_BIT_E); + } + + /* If the router is ASBR and the area-type is NSSA set the + * translate bit in router LSA. + */ + if (IS_AREA_NSSA(oa) + && (ospf6_asbr_is_asbr(oa->ospf6) || IS_OSPF6_ABR(oa->ospf6))) { + if (oa->NSSATranslatorRole == OSPF6_NSSA_ROLE_ALWAYS) + SET_FLAG(router_lsa->bits, OSPF6_ROUTER_BIT_NT); + } else { + UNSET_FLAG(router_lsa->bits, OSPF6_ROUTER_BIT_NT); + } + + UNSET_FLAG(router_lsa->bits, OSPF6_ROUTER_BIT_V); + UNSET_FLAG(router_lsa->bits, OSPF6_ROUTER_BIT_W); +} + +int ospf6_router_is_stub_router(struct ospf6_lsa *lsa) +{ + struct ospf6_router_lsa *rtr_lsa; + + if (lsa != NULL && OSPF6_LSA_IS_TYPE(ROUTER, lsa)) { + rtr_lsa = (struct ospf6_router_lsa + *)((caddr_t)lsa->header + + sizeof(struct ospf6_lsa_header)); + + if (!OSPF6_OPT_ISSET(rtr_lsa->options, OSPF6_OPT_R)) { + return OSPF6_IS_STUB_ROUTER; + } else if (!OSPF6_OPT_ISSET(rtr_lsa->options, OSPF6_OPT_V6)) { + return OSPF6_IS_STUB_ROUTER_V6; + } + } + + return OSPF6_NOT_STUB_ROUTER; +} + +void ospf6_router_lsa_originate(struct event *thread) +{ + struct ospf6_area *oa; + + char buffer[OSPF6_MAX_LSASIZE]; + struct ospf6_lsa_header *lsa_header; + struct ospf6_lsa *lsa; + + uint32_t link_state_id = 0; + struct listnode *node, *nnode; + struct listnode *j; + struct ospf6_interface *oi; + struct ospf6_neighbor *on, *drouter = NULL; + struct ospf6_router_lsa *router_lsa; + struct ospf6_router_lsdesc *lsdesc; + uint16_t type; + uint32_t router; + int count; + + oa = (struct ospf6_area *)EVENT_ARG(thread); + + if (oa->ospf6->gr_info.restart_in_progress) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "Graceful Restart in progress, don't originate LSA"); + return; + } + + if (IS_OSPF6_DEBUG_ORIGINATE(ROUTER)) + zlog_debug("Originate Router-LSA for Area %s", oa->name); + + memset(buffer, 0, sizeof(buffer)); + lsa_header = (struct ospf6_lsa_header *)buffer; + router_lsa = + (struct ospf6_router_lsa *)((caddr_t)lsa_header + + sizeof(struct ospf6_lsa_header)); + + ospf6_router_lsa_options_set(oa, router_lsa); + + /* describe links for each interfaces */ + lsdesc = (struct ospf6_router_lsdesc + *)((caddr_t)router_lsa + + sizeof(struct ospf6_router_lsa)); + + for (ALL_LIST_ELEMENTS(oa->if_list, node, nnode, oi)) { + /* Interfaces in state Down or Loopback are not described */ + if (oi->state == OSPF6_INTERFACE_DOWN + || oi->state == OSPF6_INTERFACE_LOOPBACK) + continue; + + /* Nor are interfaces without any full adjacencies described */ + count = 0; + for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, j, on)) + if (on->state == OSPF6_NEIGHBOR_FULL) + count++; + + if (count == 0) + continue; + + /* Multiple Router-LSA instance according to size limit setting + */ + if ((oa->router_lsa_size_limit != 0) + && ((size_t)((char *)lsdesc - buffer) + + sizeof(struct ospf6_router_lsdesc) + > oa->router_lsa_size_limit)) { + if ((caddr_t)lsdesc + == (caddr_t)router_lsa + + sizeof(struct ospf6_router_lsa)) { + zlog_warn( + "Size limit setting for Router-LSA too short"); + return; + } + + /* Fill LSA Header */ + lsa_header->age = 0; + lsa_header->type = htons(OSPF6_LSTYPE_ROUTER); + lsa_header->id = htonl(link_state_id); + lsa_header->adv_router = oa->ospf6->router_id; + lsa_header->seqnum = ospf6_new_ls_seqnum( + lsa_header->type, lsa_header->id, + lsa_header->adv_router, oa->lsdb); + lsa_header->length = + htons((caddr_t)lsdesc - (caddr_t)buffer); + + /* LSA checksum */ + ospf6_lsa_checksum(lsa_header); + + /* create LSA */ + lsa = ospf6_lsa_create(lsa_header); + + /* Originate */ + ospf6_lsa_originate_area(lsa, oa); + + /* Reset Buffer to fill next Router LSA */ + memset(buffer, 0, sizeof(buffer)); + lsa_header = (struct ospf6_lsa_header *)buffer; + router_lsa = + (struct ospf6_router_lsa + *)((caddr_t)lsa_header + + sizeof(struct ospf6_lsa_header)); + + ospf6_router_lsa_options_set(oa, router_lsa); + + /* describe links for each interfaces */ + lsdesc = (struct ospf6_router_lsdesc + *)((caddr_t)router_lsa + + sizeof(struct ospf6_router_lsa)); + + link_state_id++; + } + + /* Point-to-Point interfaces */ + if (oi->type == OSPF_IFTYPE_POINTOPOINT) { + for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, j, on)) { + if (on->state != OSPF6_NEIGHBOR_FULL) + continue; + + lsdesc->type = OSPF6_ROUTER_LSDESC_POINTTOPOINT; + lsdesc->metric = htons(oi->cost); + lsdesc->interface_id = + htonl(oi->interface->ifindex); + lsdesc->neighbor_interface_id = + htonl(on->ifindex); + lsdesc->neighbor_router_id = on->router_id; + + lsdesc++; + } + } + + /* Broadcast and NBMA interfaces */ + else if (oi->type == OSPF_IFTYPE_BROADCAST) { + /* If this router is not DR, + and If this router not fully adjacent with DR, + this interface is not transit yet: ignore. */ + if (oi->state != OSPF6_INTERFACE_DR) { + drouter = + ospf6_neighbor_lookup(oi->drouter, oi); + if (drouter == NULL + || drouter->state != OSPF6_NEIGHBOR_FULL) + continue; + } + + lsdesc->type = OSPF6_ROUTER_LSDESC_TRANSIT_NETWORK; + lsdesc->metric = htons(oi->cost); + lsdesc->interface_id = htonl(oi->interface->ifindex); + if (oi->state != OSPF6_INTERFACE_DR) { + lsdesc->neighbor_interface_id = + htonl(drouter->ifindex); + lsdesc->neighbor_router_id = drouter->router_id; + } else { + lsdesc->neighbor_interface_id = + htonl(oi->interface->ifindex); + lsdesc->neighbor_router_id = + oi->area->ospf6->router_id; + } + + lsdesc++; + } else { + assert(0); /* Unknown interface type */ + } + + /* Virtual links */ + /* xxx */ + /* Point-to-Multipoint interfaces */ + /* xxx */ + } + + /* Fill LSA Header */ + lsa_header->age = 0; + lsa_header->type = htons(OSPF6_LSTYPE_ROUTER); + lsa_header->id = htonl(link_state_id); + lsa_header->adv_router = oa->ospf6->router_id; + lsa_header->seqnum = + ospf6_new_ls_seqnum(lsa_header->type, lsa_header->id, + lsa_header->adv_router, oa->lsdb); + lsa_header->length = htons((caddr_t)lsdesc - (caddr_t)buffer); + + /* LSA checksum */ + ospf6_lsa_checksum(lsa_header); + + /* create LSA */ + lsa = ospf6_lsa_create(lsa_header); + + /* Originate */ + ospf6_lsa_originate_area(lsa, oa); + + link_state_id++; + + /* Do premature-aging of rest, undesired Router-LSAs */ + type = ntohs(OSPF6_LSTYPE_ROUTER); + router = oa->ospf6->router_id; + count = 0; + for (ALL_LSDB_TYPED_ADVRTR(oa->lsdb, type, router, lsa)) { + if (ntohl(lsa->header->id) < link_state_id) + continue; + ospf6_lsa_purge(lsa); + count++; + } + + /* + * Waiting till the LSA is actually removed from the database to trigger + * SPF delays network convergence. Unlike IPv4, for an ABR, when all + * interfaces associated with an area are gone, triggering an SPF right + * away + * helps convergence with inter-area routes. + */ + if (count && !link_state_id) + ospf6_spf_schedule(oa->ospf6, + OSPF6_SPF_FLAGS_ROUTER_LSA_ORIGINATED); +} + +/*******************************/ +/* RFC2740 3.4.3.2 Network-LSA */ +/*******************************/ + +static char *ospf6_network_lsa_get_ar_id(struct ospf6_lsa *lsa, char *buf, + int buflen, int pos) +{ + char *start, *end, *current; + struct ospf6_network_lsa *network_lsa; + struct ospf6_network_lsdesc *lsdesc; + + if (lsa) { + network_lsa = (struct ospf6_network_lsa + *)((caddr_t)lsa->header + + sizeof(struct ospf6_lsa_header)); + + start = (char *)network_lsa + sizeof(struct ospf6_network_lsa); + end = (char *)lsa->header + ntohs(lsa->header->length); + current = start + pos * (sizeof(struct ospf6_network_lsdesc)); + + if ((current + sizeof(struct ospf6_network_lsdesc)) <= end) { + lsdesc = (struct ospf6_network_lsdesc *)current; + if (buf) { + inet_ntop(AF_INET, &lsdesc->router_id, buf, + buflen); + return buf; + } + } + } + + return NULL; +} + +static int ospf6_network_lsa_show(struct vty *vty, struct ospf6_lsa *lsa, + json_object *json_obj, bool use_json) +{ + char *start, *end, *current; + struct ospf6_network_lsa *network_lsa; + struct ospf6_network_lsdesc *lsdesc; + char buf[128], options[32]; + json_object *json_arr = NULL; + + network_lsa = + (struct ospf6_network_lsa *)((caddr_t)lsa->header + + sizeof(struct ospf6_lsa_header)); + + ospf6_options_printbuf(network_lsa->options, options, sizeof(options)); + if (use_json) + json_object_string_add(json_obj, "options", options); + else + vty_out(vty, " Options: %s\n", options); + + start = (char *)network_lsa + sizeof(struct ospf6_network_lsa); + end = (char *)lsa->header + ntohs(lsa->header->length); + if (use_json) + json_arr = json_object_new_array(); + + for (current = start; + current + sizeof(struct ospf6_network_lsdesc) <= end; + current += sizeof(struct ospf6_network_lsdesc)) { + lsdesc = (struct ospf6_network_lsdesc *)current; + inet_ntop(AF_INET, &lsdesc->router_id, buf, sizeof(buf)); + if (use_json) + json_object_array_add(json_arr, + json_object_new_string(buf)); + else + vty_out(vty, " Attached Router: %s\n", buf); + } + if (use_json) + json_object_object_add(json_obj, "attachedRouter", json_arr); + + return 0; +} + +void ospf6_network_lsa_originate(struct event *thread) +{ + struct ospf6_interface *oi; + + char buffer[OSPF6_MAX_LSASIZE]; + struct ospf6_lsa_header *lsa_header; + + int count; + struct ospf6_lsa *old, *lsa; + struct ospf6_network_lsa *network_lsa; + struct ospf6_network_lsdesc *lsdesc; + struct ospf6_neighbor *on; + struct ospf6_link_lsa *link_lsa; + struct listnode *i; + uint16_t type; + + oi = (struct ospf6_interface *)EVENT_ARG(thread); + + /* The interface must be enabled until here. A Network-LSA of a + disabled interface (but was once enabled) should be flushed + by ospf6_lsa_refresh (), and does not come here. */ + assert(oi->area); + + if (oi->area->ospf6->gr_info.restart_in_progress) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "Graceful Restart in progress, don't originate LSA"); + return; + } + + old = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_NETWORK), + htonl(oi->interface->ifindex), + oi->area->ospf6->router_id, oi->area->lsdb); + + /* Do not originate Network-LSA if not DR */ + if (oi->state != OSPF6_INTERFACE_DR) { + if (old) { + ospf6_lsa_purge(old); + /* + * Waiting till the LSA is actually removed from the + * database to + * trigger SPF delays network convergence. + */ + ospf6_spf_schedule( + oi->area->ospf6, + OSPF6_SPF_FLAGS_NETWORK_LSA_ORIGINATED); + } + return; + } + + if (IS_OSPF6_DEBUG_ORIGINATE(NETWORK)) + zlog_debug("Originate Network-LSA for Interface %s", + oi->interface->name); + + /* If none of neighbor is adjacent to us */ + count = 0; + + for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, i, on)) + if (on->state == OSPF6_NEIGHBOR_FULL) + count++; + + if (count == 0) { + if (IS_OSPF6_DEBUG_ORIGINATE(NETWORK)) + zlog_debug("Interface stub, ignore"); + if (old) + ospf6_lsa_purge(old); + return; + } + + /* prepare buffer */ + memset(buffer, 0, sizeof(buffer)); + lsa_header = (struct ospf6_lsa_header *)buffer; + network_lsa = + (struct ospf6_network_lsa *)((caddr_t)lsa_header + + sizeof(struct ospf6_lsa_header)); + + /* Collect the interface's Link-LSAs to describe + network's optional capabilities */ + type = htons(OSPF6_LSTYPE_LINK); + for (ALL_LSDB_TYPED(oi->lsdb, type, lsa)) { + link_lsa = (struct ospf6_link_lsa + *)((caddr_t)lsa->header + + sizeof(struct ospf6_lsa_header)); + network_lsa->options[0] |= link_lsa->options[0]; + network_lsa->options[1] |= link_lsa->options[1]; + network_lsa->options[2] |= link_lsa->options[2]; + } + + lsdesc = (struct ospf6_network_lsdesc + *)((caddr_t)network_lsa + + sizeof(struct ospf6_network_lsa)); + + /* set Link Description to the router itself */ + lsdesc->router_id = oi->area->ospf6->router_id; + lsdesc++; + + /* Walk through the neighbors */ + for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, i, on)) { + if (on->state != OSPF6_NEIGHBOR_FULL) + continue; + + /* set this neighbor's Router-ID to LSA */ + lsdesc->router_id = on->router_id; + lsdesc++; + } + + /* Fill LSA Header */ + lsa_header->age = 0; + lsa_header->type = htons(OSPF6_LSTYPE_NETWORK); + lsa_header->id = htonl(oi->interface->ifindex); + lsa_header->adv_router = oi->area->ospf6->router_id; + lsa_header->seqnum = + ospf6_new_ls_seqnum(lsa_header->type, lsa_header->id, + lsa_header->adv_router, oi->area->lsdb); + lsa_header->length = htons((caddr_t)lsdesc - (caddr_t)buffer); + + /* LSA checksum */ + ospf6_lsa_checksum(lsa_header); + + /* create LSA */ + lsa = ospf6_lsa_create(lsa_header); + + /* Originate */ + ospf6_lsa_originate_area(lsa, oi->area); +} + + +/****************************/ +/* RFC2740 3.4.3.6 Link-LSA */ +/****************************/ + +static char *ospf6_link_lsa_get_prefix_str(struct ospf6_lsa *lsa, char *buf, + int buflen, int pos) +{ + char *start, *end, *current; + struct ospf6_link_lsa *link_lsa; + struct in6_addr in6; + struct ospf6_prefix *prefix; + int cnt = 0, prefixnum; + + if (lsa) { + link_lsa = (struct ospf6_link_lsa + *)((caddr_t)lsa->header + + sizeof(struct ospf6_lsa_header)); + + if (pos == 0) { + inet_ntop(AF_INET6, &link_lsa->linklocal_addr, buf, + buflen); + return (buf); + } + + prefixnum = ntohl(link_lsa->prefix_num); + if (pos > prefixnum) + return NULL; + + start = (char *)link_lsa + sizeof(struct ospf6_link_lsa); + end = (char *)lsa->header + ntohs(lsa->header->length); + current = start; + + while (current + sizeof(struct ospf6_prefix) <= end) { + prefix = (struct ospf6_prefix *)current; + if (prefix->prefix_length == 0 + || current + OSPF6_PREFIX_SIZE(prefix) > end) { + return NULL; + } + + if (cnt < (pos - 1)) { + current += OSPF6_PREFIX_SIZE(prefix); + cnt++; + } else { + memset(&in6, 0, sizeof(in6)); + memcpy(&in6, OSPF6_PREFIX_BODY(prefix), + OSPF6_PREFIX_SPACE( + prefix->prefix_length)); + inet_ntop(AF_INET6, &in6, buf, buflen); + return (buf); + } + } + } + return NULL; +} + +static int ospf6_link_lsa_show(struct vty *vty, struct ospf6_lsa *lsa, + json_object *json_obj, bool use_json) +{ + char *start, *end, *current; + struct ospf6_link_lsa *link_lsa; + int prefixnum; + char buf[128], options[32]; + struct ospf6_prefix *prefix; + struct in6_addr in6; + json_object *json_loop; + json_object *json_arr = NULL; + char prefix_string[133]; + + link_lsa = (struct ospf6_link_lsa *)((caddr_t)lsa->header + + sizeof(struct ospf6_lsa_header)); + + ospf6_options_printbuf(link_lsa->options, options, sizeof(options)); + inet_ntop(AF_INET6, &link_lsa->linklocal_addr, buf, sizeof(buf)); + prefixnum = ntohl(link_lsa->prefix_num); + + if (use_json) { + json_arr = json_object_new_array(); + json_object_int_add(json_obj, "priority", link_lsa->priority); + json_object_string_add(json_obj, "options", options); + json_object_string_add(json_obj, "linkLocalAddress", buf); + json_object_int_add(json_obj, "numberOfPrefix", prefixnum); + } else { + vty_out(vty, " Priority: %d Options: %s\n", + link_lsa->priority, options); + vty_out(vty, " LinkLocal Address: %s\n", buf); + vty_out(vty, " Number of Prefix: %d\n", prefixnum); + } + + start = (char *)link_lsa + sizeof(struct ospf6_link_lsa); + end = (char *)lsa->header + ntohs(lsa->header->length); + for (current = start; current < end; + current += OSPF6_PREFIX_SIZE(prefix)) { + prefix = (struct ospf6_prefix *)current; + if (prefix->prefix_length == 0 + || current + OSPF6_PREFIX_SIZE(prefix) > end) + break; + + ospf6_prefix_options_printbuf(prefix->prefix_options, buf, + sizeof(buf)); + if (use_json) { + json_loop = json_object_new_object(); + json_object_string_add(json_loop, "prefixOption", buf); + } else + vty_out(vty, " Prefix Options: %s\n", buf); + + memset(&in6, 0, sizeof(in6)); + memcpy(&in6, OSPF6_PREFIX_BODY(prefix), + OSPF6_PREFIX_SPACE(prefix->prefix_length)); + inet_ntop(AF_INET6, &in6, buf, sizeof(buf)); + if (use_json) { + snprintf(prefix_string, sizeof(prefix_string), "%s/%d", + buf, prefix->prefix_length); + json_object_string_add(json_loop, "prefix", + prefix_string); + json_object_array_add(json_arr, json_loop); + } else + vty_out(vty, " Prefix: %s/%d\n", buf, + prefix->prefix_length); + } + if (use_json) + json_object_object_add(json_obj, "prefix", json_arr); + + return 0; +} + +void ospf6_link_lsa_originate(struct event *thread) +{ + struct ospf6_interface *oi; + + char buffer[OSPF6_MAX_LSASIZE]; + struct ospf6_lsa_header *lsa_header; + struct ospf6_lsa *old, *lsa; + + struct ospf6_link_lsa *link_lsa; + struct ospf6_route *route; + struct ospf6_prefix *op; + + oi = (struct ospf6_interface *)EVENT_ARG(thread); + + assert(oi->area); + + if (oi->area->ospf6->gr_info.restart_in_progress) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "Graceful Restart in progress, don't originate LSA"); + return; + } + + + /* find previous LSA */ + old = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_LINK), + htonl(oi->interface->ifindex), + oi->area->ospf6->router_id, oi->lsdb); + + if (CHECK_FLAG(oi->flag, OSPF6_INTERFACE_DISABLE)) { + if (old) + ospf6_lsa_purge(old); + return; + } + + if (IS_OSPF6_DEBUG_ORIGINATE(LINK)) + zlog_debug("Originate Link-LSA for Interface %s", + oi->interface->name); + + /* can't make Link-LSA if linklocal address not set */ + if (oi->linklocal_addr == NULL) { + if (IS_OSPF6_DEBUG_ORIGINATE(LINK)) + zlog_debug( + "No Linklocal address on %s, defer originating", + oi->interface->name); + if (old) + ospf6_lsa_purge(old); + return; + } + + /* prepare buffer */ + memset(buffer, 0, sizeof(buffer)); + lsa_header = (struct ospf6_lsa_header *)buffer; + link_lsa = (struct ospf6_link_lsa *)((caddr_t)lsa_header + + sizeof(struct ospf6_lsa_header)); + + /* Fill Link-LSA */ + link_lsa->priority = oi->priority; + memcpy(link_lsa->options, oi->area->options, 3); + memcpy(&link_lsa->linklocal_addr, oi->linklocal_addr, + sizeof(struct in6_addr)); + link_lsa->prefix_num = htonl(oi->route_connected->count); + + op = (struct ospf6_prefix *)((caddr_t)link_lsa + + sizeof(struct ospf6_link_lsa)); + + /* connected prefix to advertise */ + for (route = ospf6_route_head(oi->route_connected); route; + route = ospf6_route_next(route)) { + op->prefix_length = route->prefix.prefixlen; + op->prefix_options = route->prefix_options; + op->prefix_metric = htons(0); + memcpy(OSPF6_PREFIX_BODY(op), &route->prefix.u.prefix6, + OSPF6_PREFIX_SPACE(op->prefix_length)); + op = OSPF6_PREFIX_NEXT(op); + } + + /* Fill LSA Header */ + lsa_header->age = 0; + lsa_header->type = htons(OSPF6_LSTYPE_LINK); + lsa_header->id = htonl(oi->interface->ifindex); + lsa_header->adv_router = oi->area->ospf6->router_id; + lsa_header->seqnum = + ospf6_new_ls_seqnum(lsa_header->type, lsa_header->id, + lsa_header->adv_router, oi->lsdb); + lsa_header->length = htons((caddr_t)op - (caddr_t)buffer); + + /* LSA checksum */ + ospf6_lsa_checksum(lsa_header); + + /* create LSA */ + lsa = ospf6_lsa_create(lsa_header); + + /* Originate */ + ospf6_lsa_originate_interface(lsa, oi); +} + + +/*****************************************/ +/* RFC2740 3.4.3.7 Intra-Area-Prefix-LSA */ +/*****************************************/ +static char *ospf6_intra_prefix_lsa_get_prefix_str(struct ospf6_lsa *lsa, + char *buf, int buflen, + int pos) +{ + char *start, *end, *current; + struct ospf6_intra_prefix_lsa *intra_prefix_lsa; + struct in6_addr in6; + int prefixnum, cnt = 0; + struct ospf6_prefix *prefix; + char tbuf[16]; + + if (lsa) { + intra_prefix_lsa = + (struct ospf6_intra_prefix_lsa + *)((caddr_t)lsa->header + + sizeof(struct ospf6_lsa_header)); + + prefixnum = ntohs(intra_prefix_lsa->prefix_num); + if ((pos + 1) > prefixnum) + return NULL; + + start = (char *)intra_prefix_lsa + + sizeof(struct ospf6_intra_prefix_lsa); + end = (char *)lsa->header + ntohs(lsa->header->length); + current = start; + + while (current + sizeof(struct ospf6_prefix) <= end) { + prefix = (struct ospf6_prefix *)current; + if (prefix->prefix_length == 0 + || current + OSPF6_PREFIX_SIZE(prefix) > end) { + return NULL; + } + + if (cnt < pos) { + current += OSPF6_PREFIX_SIZE(prefix); + cnt++; + } else { + memset(&in6, 0, sizeof(in6)); + memcpy(&in6, OSPF6_PREFIX_BODY(prefix), + OSPF6_PREFIX_SPACE( + prefix->prefix_length)); + inet_ntop(AF_INET6, &in6, buf, buflen); + snprintf(tbuf, sizeof(tbuf), "/%d", + prefix->prefix_length); + strlcat(buf, tbuf, buflen); + return (buf); + } + } + } + return NULL; +} + +static int ospf6_intra_prefix_lsa_show(struct vty *vty, struct ospf6_lsa *lsa, + json_object *json_obj, bool use_json) +{ + char *start, *end, *current; + struct ospf6_intra_prefix_lsa *intra_prefix_lsa; + int prefixnum; + char buf[128]; + struct ospf6_prefix *prefix; + char id[16], adv_router[16]; + struct in6_addr in6; + json_object *json_loop; + json_object *json_arr = NULL; + char prefix_string[133]; + + intra_prefix_lsa = (struct ospf6_intra_prefix_lsa + *)((caddr_t)lsa->header + + sizeof(struct ospf6_lsa_header)); + + prefixnum = ntohs(intra_prefix_lsa->prefix_num); + + if (use_json) { + json_arr = json_object_new_array(); + json_object_int_add(json_obj, "numberOfPrefix", prefixnum); + } else + vty_out(vty, " Number of Prefix: %d\n", prefixnum); + + inet_ntop(AF_INET, &intra_prefix_lsa->ref_id, id, sizeof(id)); + inet_ntop(AF_INET, &intra_prefix_lsa->ref_adv_router, adv_router, + sizeof(adv_router)); + if (use_json) { + json_object_string_add( + json_obj, "reference", + ospf6_lstype_name(intra_prefix_lsa->ref_type)); + json_object_string_add(json_obj, "referenceId", id); + json_object_string_add(json_obj, "referenceAdv", adv_router); + } else + vty_out(vty, " Reference: %s Id: %s Adv: %s\n", + ospf6_lstype_name(intra_prefix_lsa->ref_type), id, + adv_router); + + start = (char *)intra_prefix_lsa + + sizeof(struct ospf6_intra_prefix_lsa); + end = (char *)lsa->header + ntohs(lsa->header->length); + for (current = start; current < end; + current += OSPF6_PREFIX_SIZE(prefix)) { + prefix = (struct ospf6_prefix *)current; + if (prefix->prefix_length == 0 + || current + OSPF6_PREFIX_SIZE(prefix) > end) + break; + + ospf6_prefix_options_printbuf(prefix->prefix_options, buf, + sizeof(buf)); + if (use_json) { + json_loop = json_object_new_object(); + json_object_string_add(json_loop, "prefixOption", buf); + } else + vty_out(vty, " Prefix Options: %s\n", buf); + + memset(&in6, 0, sizeof(in6)); + memcpy(&in6, OSPF6_PREFIX_BODY(prefix), + OSPF6_PREFIX_SPACE(prefix->prefix_length)); + inet_ntop(AF_INET6, &in6, buf, sizeof(buf)); + if (use_json) { + snprintf(prefix_string, sizeof(prefix_string), "%s/%d", + buf, prefix->prefix_length); + json_object_string_add(json_loop, "prefix", + prefix_string); + json_object_int_add(json_loop, "metric", + ntohs(prefix->prefix_metric)); + json_object_array_add(json_arr, json_loop); + } else { + vty_out(vty, " Prefix: %s/%d\n", buf, + prefix->prefix_length); + vty_out(vty, " Metric: %d\n", + ntohs(prefix->prefix_metric)); + } + } + if (use_json) + json_object_object_add(json_obj, "prefix", json_arr); + + return 0; +} + +void ospf6_intra_prefix_lsa_originate_stub(struct event *thread) +{ + struct ospf6_area *oa; + + char buffer[OSPF6_MAX_LSASIZE]; + struct ospf6_lsa_header *lsa_header; + struct ospf6_lsa *old, *lsa, *old_next = NULL; + + struct ospf6_intra_prefix_lsa *intra_prefix_lsa; + struct ospf6_interface *oi; + struct ospf6_neighbor *on; + struct ospf6_route *route; + struct ospf6_prefix *op; + struct listnode *i, *j; + int full_count = 0; + unsigned short prefix_num = 0; + struct ospf6_route_table *route_advertise; + int ls_id = 0; + + oa = (struct ospf6_area *)EVENT_ARG(thread); + + if (oa->ospf6->gr_info.restart_in_progress) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "Graceful Restart in progress, don't originate LSA"); + return; + } + + /* find previous LSA */ + old = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_INTRA_PREFIX), htonl(0), + oa->ospf6->router_id, oa->lsdb); + + if (!IS_AREA_ENABLED(oa)) { + if (old) { + ospf6_lsa_purge(old); + /* find previous LSA */ + old_next = ospf6_lsdb_lookup( + htons(OSPF6_LSTYPE_INTRA_PREFIX), + htonl(++ls_id), oa->ospf6->router_id, oa->lsdb); + + while (old_next) { + ospf6_lsa_purge(old_next); + old_next = ospf6_lsdb_lookup( + htons(OSPF6_LSTYPE_INTRA_PREFIX), + htonl(++ls_id), oa->ospf6->router_id, + oa->lsdb); + } + } + return; + } + + if (IS_OSPF6_DEBUG_ORIGINATE(INTRA_PREFIX)) + zlog_debug( + "Originate Intra-Area-Prefix-LSA for area %s's stub prefix", + oa->name); + + /* prepare buffer */ + memset(buffer, 0, sizeof(buffer)); + lsa_header = (struct ospf6_lsa_header *)buffer; + intra_prefix_lsa = (struct ospf6_intra_prefix_lsa + *)((caddr_t)lsa_header + + sizeof(struct ospf6_lsa_header)); + + /* Fill Intra-Area-Prefix-LSA */ + intra_prefix_lsa->ref_type = htons(OSPF6_LSTYPE_ROUTER); + intra_prefix_lsa->ref_id = htonl(0); + intra_prefix_lsa->ref_adv_router = oa->ospf6->router_id; + + route_advertise = ospf6_route_table_create(0, 0); + + for (ALL_LIST_ELEMENTS_RO(oa->if_list, i, oi)) { + if (oi->state == OSPF6_INTERFACE_DOWN) { + if (IS_OSPF6_DEBUG_ORIGINATE(INTRA_PREFIX)) + zlog_debug(" Interface %s is down, ignore", + oi->interface->name); + continue; + } + + full_count = 0; + + for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, j, on)) + if (on->state == OSPF6_NEIGHBOR_FULL) + full_count++; + + if (oi->state != OSPF6_INTERFACE_LOOPBACK + && oi->state != OSPF6_INTERFACE_POINTTOPOINT + && full_count != 0) { + if (IS_OSPF6_DEBUG_ORIGINATE(INTRA_PREFIX)) + zlog_debug(" Interface %s is not stub, ignore", + oi->interface->name); + continue; + } + + if (IS_OSPF6_DEBUG_ORIGINATE(INTRA_PREFIX)) + zlog_debug(" Interface %s:", oi->interface->name); + + /* connected prefix to advertise */ + for (route = ospf6_route_head(oi->route_connected); route; + route = ospf6_route_best_next(route)) { + if (IS_OSPF6_DEBUG_ORIGINATE(INTRA_PREFIX)) + zlog_debug(" include %pFX", &route->prefix); + ospf6_route_add(ospf6_route_copy(route), + route_advertise); + } + } + + if (route_advertise->count == 0) { + if (old) { + ls_id = 0; + ospf6_lsa_purge(old); + /* find previous LSA */ + old_next = ospf6_lsdb_lookup( + htons(OSPF6_LSTYPE_INTRA_PREFIX), + htonl(++ls_id), oa->ospf6->router_id, oa->lsdb); + + while (old_next) { + ospf6_lsa_purge(old_next); + old_next = ospf6_lsdb_lookup( + htons(OSPF6_LSTYPE_INTRA_PREFIX), + htonl(++ls_id), oa->ospf6->router_id, + oa->lsdb); + } + } + ospf6_route_table_delete(route_advertise); + return; + } + + /* Neighbor change to FULL, if INTRA-AREA-PREFIX LSA + * has not change, Flush old LSA and Re-Originate INP, + * as ospf6_flood() checks if LSA is same as DB, + * it won't be updated to neighbor's DB. + */ + if (oa->intra_prefix_originate) { + if (IS_OSPF6_DEBUG_ORIGINATE(INTRA_PREFIX)) + zlog_debug( + "%s: Re-originate intra prefix LSA, Current full nbrs %u", + __func__, oa->full_nbrs); + if (old) + ospf6_lsa_purge_multi_ls_id(oa, old); + oa->intra_prefix_originate = 0; + } + + /* put prefixes to advertise */ + prefix_num = 0; + op = (struct ospf6_prefix *)((caddr_t)intra_prefix_lsa + + sizeof(struct ospf6_intra_prefix_lsa)); + for (route = ospf6_route_head(route_advertise); route; + route = ospf6_route_best_next(route)) { + if (((caddr_t)op - (caddr_t)lsa_header) > MAX_LSA_PAYLOAD) { + + intra_prefix_lsa->prefix_num = htons(prefix_num); + + /* Fill LSA Header */ + lsa_header->age = 0; + lsa_header->type = htons(OSPF6_LSTYPE_INTRA_PREFIX); + lsa_header->id = htonl(ls_id++); + lsa_header->adv_router = oa->ospf6->router_id; + lsa_header->seqnum = ospf6_new_ls_seqnum( + lsa_header->type, lsa_header->id, + lsa_header->adv_router, oa->lsdb); + lsa_header->length = + htons((caddr_t)op - (caddr_t)lsa_header); + + /* LSA checksum */ + ospf6_lsa_checksum(lsa_header); + + /* Create LSA */ + lsa = ospf6_lsa_create(lsa_header); + + /* Originate */ + ospf6_lsa_originate_area(lsa, oa); + + /* Prepare next buffer */ + memset(buffer, 0, sizeof(buffer)); + lsa_header = (struct ospf6_lsa_header *)buffer; + intra_prefix_lsa = + (struct ospf6_intra_prefix_lsa + *)((caddr_t)lsa_header + + sizeof(struct ospf6_lsa_header)); + + /* Fill Intra-Area-Prefix-LSA */ + intra_prefix_lsa->ref_type = htons(OSPF6_LSTYPE_ROUTER); + intra_prefix_lsa->ref_id = htonl(0); + intra_prefix_lsa->ref_adv_router = oa->ospf6->router_id; + + /* Put next set of prefixes to advertise */ + prefix_num = 0; + op = (struct ospf6_prefix + *)((caddr_t)intra_prefix_lsa + + sizeof(struct + ospf6_intra_prefix_lsa)); + } + + op->prefix_length = route->prefix.prefixlen; + op->prefix_options = route->prefix_options; + op->prefix_metric = htons(route->path.cost); + memcpy(OSPF6_PREFIX_BODY(op), &route->prefix.u.prefix6, + OSPF6_PREFIX_SPACE(op->prefix_length)); + prefix_num++; + + op = OSPF6_PREFIX_NEXT(op); + } + + ospf6_route_table_delete(route_advertise); + + if (prefix_num == 0) { + if (IS_OSPF6_DEBUG_ORIGINATE(INTRA_PREFIX)) + zlog_debug( + "Quit to Advertise Intra-Prefix: no route to advertise"); + return; + } + + intra_prefix_lsa->prefix_num = htons(prefix_num); + + /* Fill LSA Header */ + lsa_header->age = 0; + lsa_header->type = htons(OSPF6_LSTYPE_INTRA_PREFIX); + lsa_header->id = htonl(ls_id++); + lsa_header->adv_router = oa->ospf6->router_id; + lsa_header->seqnum = + ospf6_new_ls_seqnum(lsa_header->type, lsa_header->id, + lsa_header->adv_router, oa->lsdb); + lsa_header->length = htons((caddr_t)op - (caddr_t)lsa_header); + + /* LSA checksum */ + ospf6_lsa_checksum(lsa_header); + + /* create LSA */ + lsa = ospf6_lsa_create(lsa_header); + + /* Originate */ + ospf6_lsa_originate_area(lsa, oa); +} + + +void ospf6_intra_prefix_lsa_originate_transit(struct event *thread) +{ + struct ospf6_interface *oi; + + char buffer[OSPF6_MAX_LSASIZE]; + struct ospf6_lsa_header *lsa_header; + struct ospf6_lsa *old, *lsa; + + struct ospf6_intra_prefix_lsa *intra_prefix_lsa; + struct ospf6_neighbor *on; + struct ospf6_route *route; + struct ospf6_prefix *op; + struct listnode *i; + int full_count = 0; + unsigned short prefix_num = 0; + struct ospf6_route_table *route_advertise; + struct ospf6_link_lsa *link_lsa; + char *start, *end, *current; + uint16_t type; + + oi = (struct ospf6_interface *)EVENT_ARG(thread); + + assert(oi->area); + + if (oi->area->ospf6->gr_info.restart_in_progress) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "Graceful Restart in progress, don't originate LSA"); + return; + } + + /* find previous LSA */ + old = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_INTRA_PREFIX), + htonl(oi->interface->ifindex), + oi->area->ospf6->router_id, oi->area->lsdb); + + if (CHECK_FLAG(oi->flag, OSPF6_INTERFACE_DISABLE)) { + if (old) + ospf6_lsa_purge(old); + return; + } + + if (IS_OSPF6_DEBUG_ORIGINATE(INTRA_PREFIX)) + zlog_debug( + "Originate Intra-Area-Prefix-LSA for interface %s's prefix", + oi->interface->name); + + /* prepare buffer */ + memset(buffer, 0, sizeof(buffer)); + lsa_header = (struct ospf6_lsa_header *)buffer; + intra_prefix_lsa = (struct ospf6_intra_prefix_lsa + *)((caddr_t)lsa_header + + sizeof(struct ospf6_lsa_header)); + + /* Fill Intra-Area-Prefix-LSA */ + intra_prefix_lsa->ref_type = htons(OSPF6_LSTYPE_NETWORK); + intra_prefix_lsa->ref_id = htonl(oi->interface->ifindex); + intra_prefix_lsa->ref_adv_router = oi->area->ospf6->router_id; + + if (oi->state != OSPF6_INTERFACE_DR) { + if (IS_OSPF6_DEBUG_ORIGINATE(INTRA_PREFIX)) + zlog_debug(" Interface is not DR"); + if (old) + ospf6_lsa_purge(old); + return; + } + + full_count = 0; + for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, i, on)) + if (on->state == OSPF6_NEIGHBOR_FULL) + full_count++; + + if (full_count == 0) { + if (IS_OSPF6_DEBUG_ORIGINATE(INTRA_PREFIX)) + zlog_debug(" Interface is stub"); + if (old) + ospf6_lsa_purge(old); + return; + } + + /* connected prefix to advertise */ + route_advertise = ospf6_route_table_create(0, 0); + + type = ntohs(OSPF6_LSTYPE_LINK); + for (ALL_LSDB_TYPED(oi->lsdb, type, lsa)) { + if (OSPF6_LSA_IS_MAXAGE(lsa)) + continue; + + if (IS_OSPF6_DEBUG_ORIGINATE(INTRA_PREFIX)) + zlog_debug(" include prefix from %s", lsa->name); + + if (lsa->header->adv_router != oi->area->ospf6->router_id) { + on = ospf6_neighbor_lookup(lsa->header->adv_router, oi); + if (on == NULL || on->state != OSPF6_NEIGHBOR_FULL) { + if (IS_OSPF6_DEBUG_ORIGINATE(INTRA_PREFIX)) + zlog_debug( + " Neighbor not found or not Full, ignore"); + continue; + } + } + + link_lsa = (struct ospf6_link_lsa + *)((caddr_t)lsa->header + + sizeof(struct ospf6_lsa_header)); + + prefix_num = (unsigned short)ntohl(link_lsa->prefix_num); + start = (char *)link_lsa + sizeof(struct ospf6_link_lsa); + end = (char *)lsa->header + ntohs(lsa->header->length); + for (current = start; current < end && prefix_num; + current += OSPF6_PREFIX_SIZE(op)) { + op = (struct ospf6_prefix *)current; + if (op->prefix_length == 0 + || current + OSPF6_PREFIX_SIZE(op) > end) + break; + + route = ospf6_route_create(oi->area->ospf6); + + route->type = OSPF6_DEST_TYPE_NETWORK; + route->prefix.family = AF_INET6; + route->prefix.prefixlen = op->prefix_length; + memset(&route->prefix.u.prefix6, 0, + sizeof(struct in6_addr)); + memcpy(&route->prefix.u.prefix6, OSPF6_PREFIX_BODY(op), + OSPF6_PREFIX_SPACE(op->prefix_length)); + route->prefix_options = op->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.options[0] = link_lsa->options[0]; + route->path.options[1] = link_lsa->options[1]; + route->path.options[2] = link_lsa->options[2]; + route->path.area_id = oi->area->area_id; + route->path.type = OSPF6_PATH_TYPE_INTRA; + + if (IS_OSPF6_DEBUG_ORIGINATE(INTRA_PREFIX)) + zlog_debug(" include %pFX", &route->prefix); + + ospf6_route_add(route, route_advertise); + prefix_num--; + } + if (current != end && IS_OSPF6_DEBUG_ORIGINATE(INTRA_PREFIX)) + zlog_debug("Trailing garbage in %s", lsa->name); + } + + op = (struct ospf6_prefix *)((caddr_t)intra_prefix_lsa + + sizeof(struct ospf6_intra_prefix_lsa)); + + prefix_num = 0; + for (route = ospf6_route_head(route_advertise); route; + route = ospf6_route_best_next(route)) { + op->prefix_length = route->prefix.prefixlen; + op->prefix_options = route->prefix_options; + op->prefix_metric = htons(0); + memcpy(OSPF6_PREFIX_BODY(op), &route->prefix.u.prefix6, + OSPF6_PREFIX_SPACE(op->prefix_length)); + op = OSPF6_PREFIX_NEXT(op); + prefix_num++; + } + + ospf6_route_table_delete(route_advertise); + + if (prefix_num == 0) { + if (IS_OSPF6_DEBUG_ORIGINATE(INTRA_PREFIX)) + zlog_debug( + "Quit to Advertise Intra-Prefix: no route to advertise"); + return; + } + + intra_prefix_lsa->prefix_num = htons(prefix_num); + + /* Fill LSA Header */ + lsa_header->age = 0; + lsa_header->type = htons(OSPF6_LSTYPE_INTRA_PREFIX); + lsa_header->id = htonl(oi->interface->ifindex); + lsa_header->adv_router = oi->area->ospf6->router_id; + lsa_header->seqnum = + ospf6_new_ls_seqnum(lsa_header->type, lsa_header->id, + lsa_header->adv_router, oi->area->lsdb); + lsa_header->length = htons((caddr_t)op - (caddr_t)lsa_header); + + /* LSA checksum */ + ospf6_lsa_checksum(lsa_header); + + /* create LSA */ + lsa = ospf6_lsa_create(lsa_header); + + /* Originate */ + ospf6_lsa_originate_area(lsa, oi->area); +} + +static void ospf6_intra_prefix_update_route_origin(struct ospf6_route *oa_route, + struct ospf6 *ospf6) +{ + struct ospf6_path *h_path; + struct ospf6_route *g_route, *nroute; + + /* Update Global ospf6 route path */ + g_route = ospf6_route_lookup(&oa_route->prefix, ospf6->route_table); + + assert(g_route); + + for (ospf6_route_lock(g_route); g_route && + ospf6_route_is_prefix(&oa_route->prefix, g_route); + g_route = nroute) { + nroute = ospf6_route_next(g_route); + if (g_route->type != oa_route->type) + continue; + if (g_route->path.area_id != oa_route->path.area_id) + continue; + if (g_route->path.type != OSPF6_PATH_TYPE_INTRA) + continue; + if (g_route->path.cost != oa_route->path.cost) + continue; + + if (ospf6_route_is_same_origin(g_route, oa_route)) { + h_path = (struct ospf6_path *)listgetdata( + listhead(g_route->paths)); + g_route->path.origin.type = h_path->origin.type; + g_route->path.origin.id = h_path->origin.id; + g_route->path.origin.adv_router = + h_path->origin.adv_router; + if (nroute) + ospf6_route_unlock(nroute); + break; + } + } + + h_path = (struct ospf6_path *)listgetdata( + listhead(oa_route->paths)); + oa_route->path.origin.type = h_path->origin.type; + oa_route->path.origin.id = h_path->origin.id; + oa_route->path.origin.adv_router = h_path->origin.adv_router; +} + +void ospf6_intra_prefix_route_ecmp_path(struct ospf6_area *oa, + struct ospf6_route *old, + struct ospf6_route *route) +{ + struct ospf6_route *old_route, *ls_entry; + struct ospf6_path *ecmp_path, *o_path = NULL; + struct listnode *anode, *anext; + struct listnode *nnode, *rnode, *rnext; + struct ospf6_nexthop *nh, *rnh; + bool route_found = false; + struct interface *ifp = NULL; + struct ospf6_lsa *lsa; + struct ospf6_intra_prefix_lsa *intra_prefix_lsa; + + /* check for old entry match with new route origin, + * delete old entry. + */ + for (old_route = old; old_route; old_route = old_route->next) { + bool route_updated = false; + + /* 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->path.type != route->path.type) + continue; + + /* Current and New route has same origin, + * delete old entry. + */ + for (ALL_LIST_ELEMENTS(old_route->paths, anode, anext, + o_path)) { + /* Check old route path and route has same + * origin. + */ + if (o_path->area_id != route->path.area_id + || !ospf6_ls_origin_same(o_path, &route->path)) + continue; + + /* Cost is not same then delete current path */ + if (o_path->cost == route->path.cost) + continue; + + if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) { + zlog_debug( + "%s: route %pFX cost old %u new %u is not same, replace route", + __func__, &old_route->prefix, o_path->cost, + route->path.cost); + } + + /* Remove selected current path's nh from + * effective nh list. + */ + 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); + route_updated = true; + } + } + + listnode_delete(old_route->paths, o_path); + ospf6_path_free(o_path); + + /* Current route's path (adv_router info) is similar + * to route being added. + * Replace current route's path with paths list head. + * Update FIB with effective NHs. + */ + if (listcount(old_route->paths)) { + if (route_updated) { + for (ALL_LIST_ELEMENTS(old_route->paths, + anode, anext, o_path)) { + ospf6_merge_nexthops( + old_route->nh_list, + o_path->nh_list); + } + /* Update ospf6 route table and + * RIB/FIB with effective + * nh_list + */ + if (oa->route_table->hook_add) + (*oa->route_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) { + ospf6_intra_prefix_update_route_origin( + old_route, oa->ospf6); + } + break; + } + } else { + if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) { + zlog_debug( + "%s: route %pFX old cost %u new cost %u, delete old entry.", + __func__, &old_route->prefix, + old_route->path.cost, + route->path.cost); + } + if (oa->route_table->hook_remove) + ospf6_route_remove(old_route, + oa->route_table); + else + SET_FLAG(old_route->flag, + OSPF6_ROUTE_REMOVE); + break; + } + } + if (route_updated) + break; + } + + 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->path.type != route->path.type) + continue; + + /* Old Route and New Route have Equal Cost, Merge NHs */ + if (old_route->path.cost == route->path.cost) { + route_found = true; + + /* check if this path exists already in + * route->paths list, if so, replace nh_list. + */ + 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; + } + /* If path is not found in old_route paths's list, + * add a new path to route paths list and merge + * nexthops in route->path->nh_list. + * Otherwise replace existing path's nh_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_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) { + zlog_debug( + "%s: route %pFX %p another path added with nh %u, effective paths %u nh %u", + __func__, &route->prefix, + (void *)old_route, + listcount(ecmp_path->nh_list), + old_route->paths ? listcount( + old_route->paths) + : 0, + listcount(old_route->nh_list)); + } + } else { + list_delete_all_node(o_path->nh_list); + ospf6_copy_nexthops(o_path->nh_list, + route->nh_list); + + } + + list_delete_all_node(old_route->nh_list); + + for (ALL_LIST_ELEMENTS_RO(old_route->paths, anode, + o_path)) { + ls_entry = ospf6_route_lookup( + &o_path->ls_prefix, + oa->spf_table); + if (ls_entry == NULL) { + if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) + zlog_debug( + "%s: ls_prfix %pFX ls_entry not found.", + __func__, + &o_path->ls_prefix); + continue; + } + lsa = ospf6_lsdb_lookup(o_path->origin.type, + o_path->origin.id, + o_path->origin.adv_router, + oa->lsdb); + if (lsa == NULL) { + if (IS_OSPF6_DEBUG_EXAMIN( + INTRA_PREFIX)) { + struct prefix adv_prefix; + + ospf6_linkstate_prefix( + o_path->origin.adv_router, + o_path->origin.id, &adv_prefix); + zlog_debug( + "%s: adv_router %pFX lsa not found", + __func__, &adv_prefix); + } + continue; + } + intra_prefix_lsa = + (struct ospf6_intra_prefix_lsa *) + OSPF6_LSA_HEADER_END(lsa->header); + + if (intra_prefix_lsa->ref_adv_router + == oa->ospf6->router_id) { + ifp = if_lookup_prefix( + &old_route->prefix, + oa->ospf6->vrf_id); + } + + if (ifp) { + /* Nexthop interface found */ + ospf6_route_add_nexthop(old_route, + ifp->ifindex, + NULL); + } else { + /* The connected interfaces between + * routers can be in different networks. + * In this case the matching interface + * is not found. Copy nexthops from the + * link state entry + */ + ospf6_route_merge_nexthops(old_route, + ls_entry); + } + } + + if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) + zlog_debug( + "%s: route %pFX %p with final effective paths %u nh %u", + __func__, &route->prefix, + (void *)old_route, + old_route->paths + ? listcount(old_route->paths) + : 0, + listcount(old_route->nh_list)); + + /* used in intra_route_calculation() to add to + * global ospf6 route table. + */ + UNSET_FLAG(old_route->flag, OSPF6_ROUTE_REMOVE); + SET_FLAG(old_route->flag, OSPF6_ROUTE_ADD); + /* Update ospf6 route table and RIB/FIB */ + if (oa->route_table->hook_add) + (*oa->route_table->hook_add)(old_route); + /* Delete the new route its info added to existing + * route. + */ + ospf6_route_delete(route); + + break; + } + } + + if (!route_found) { + /* Add new route to existing node in ospf6 route table. */ + ospf6_route_add(route, oa->route_table); + } +} + +void ospf6_intra_prefix_lsa_add(struct ospf6_lsa *lsa) +{ + struct ospf6_area *oa; + struct ospf6_intra_prefix_lsa *intra_prefix_lsa; + struct prefix ls_prefix; + struct ospf6_route *route, *ls_entry, *old; + int prefix_num; + struct ospf6_prefix *op; + char *start, *current, *end; + char buf[PREFIX2STR_BUFFER]; + struct interface *ifp = NULL; + int direct_connect = 0; + struct ospf6_path *path; + + if (OSPF6_LSA_IS_MAXAGE(lsa)) + return; + + if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) + zlog_debug("%s: LSA %s found", __func__, lsa->name); + + oa = OSPF6_AREA(lsa->lsdb->data); + + intra_prefix_lsa = + (struct ospf6_intra_prefix_lsa *)OSPF6_LSA_HEADER_END( + lsa->header); + if (intra_prefix_lsa->ref_type == htons(OSPF6_LSTYPE_ROUTER) || + intra_prefix_lsa->ref_type == htons(OSPF6_LSTYPE_NETWORK)) + ospf6_linkstate_prefix(intra_prefix_lsa->ref_adv_router, + intra_prefix_lsa->ref_id, &ls_prefix); + else { + if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) + zlog_debug("Unknown reference LS-type: %#hx", + ntohs(intra_prefix_lsa->ref_type)); + return; + } + + ls_entry = ospf6_route_lookup(&ls_prefix, oa->spf_table); + if (ls_entry == NULL) { + if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) { + ospf6_linkstate_prefix2str(&ls_prefix, buf, + sizeof(buf)); + zlog_debug("LS entry does not exist: %s", buf); + } + return; + } + + if (intra_prefix_lsa->ref_adv_router == oa->ospf6->router_id) { + /* the intra-prefix are directly connected */ + direct_connect = 1; + } + + prefix_num = ntohs(intra_prefix_lsa->prefix_num); + start = (caddr_t)intra_prefix_lsa + + sizeof(struct ospf6_intra_prefix_lsa); + end = OSPF6_LSA_END(lsa->header); + for (current = start; current < end; current += OSPF6_PREFIX_SIZE(op)) { + op = (struct ospf6_prefix *)current; + if (prefix_num == 0) + break; + if (end < current + OSPF6_PREFIX_SIZE(op)) + break; + + /* Appendix A.4.1.1 */ + if (CHECK_FLAG(op->prefix_options, OSPF6_PREFIX_OPTION_NU)) { + if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) { + ospf6_linkstate_prefix2str( + (struct prefix *)OSPF6_PREFIX_BODY(op), + buf, sizeof(buf)); + zlog_debug( + "%s: Skipping Prefix %s has NU option set", + __func__, buf); + } + continue; + } + + route = ospf6_route_create(oa->ospf6); + + memset(&route->prefix, 0, sizeof(struct prefix)); + route->prefix.family = AF_INET6; + route->prefix.prefixlen = op->prefix_length; + ospf6_prefix_in6_addr(&route->prefix.u.prefix6, + intra_prefix_lsa, op); + route->prefix_options = op->prefix_options; + + route->type = OSPF6_DEST_TYPE_NETWORK; + 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.area_id = oa->area_id; + route->path.type = OSPF6_PATH_TYPE_INTRA; + route->path.metric_type = 1; + route->path.cost = + ls_entry->path.cost + ntohs(op->prefix_metric); + memcpy(&route->path.ls_prefix, &ls_prefix, + sizeof(struct prefix)); + if (direct_connect) { + ifp = if_lookup_prefix(&route->prefix, + oa->ospf6->vrf_id); + } + + if (ifp) { + /* Nexthop interface found */ + ospf6_route_add_nexthop(route, ifp->ifindex, NULL); + } else { + /* The connected interfaces between routers can be in + * different networks. In this case the matching + * interface is not found. Copy nexthops from the + * link state entry + */ + ospf6_route_copy_nexthops(route, ls_entry); + } + + path = ospf6_path_dup(&route->path); + ospf6_copy_nexthops(path->nh_list, route->path.nh_list); + listnode_add_sort(route->paths, path); + + old = ospf6_route_lookup(&route->prefix, oa->route_table); + if (old) { + if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) { + prefix2str(&route->prefix, buf, sizeof(buf)); + zlog_debug( + "%s Update route: %s old cost %u new cost %u paths %u nh %u", + __func__, buf, old->path.cost, + route->path.cost, + listcount(route->paths), + listcount(route->nh_list)); + } + ospf6_intra_prefix_route_ecmp_path(oa, old, route); + } else { + if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) { + prefix2str(&route->prefix, buf, sizeof(buf)); + zlog_debug( + "%s route %s add with cost %u paths %u nh %u", + __func__, buf, route->path.cost, + listcount(route->paths), + listcount(route->nh_list)); + } + ospf6_route_add(route, oa->route_table); + } + prefix_num--; + } + + if (current != end && IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) + zlog_debug("Trailing garbage ignored"); +} + +static void ospf6_intra_prefix_lsa_remove_update_route(struct ospf6_lsa *lsa, + struct ospf6_area *oa, + struct ospf6_route *route) +{ + struct listnode *anode, *anext; + struct listnode *nnode, *rnode, *rnext; + struct ospf6_nexthop *nh, *rnh; + struct ospf6_path *o_path; + bool nh_updated = false; + char buf[PREFIX2STR_BUFFER]; + + /* Iterate all paths of route to find maching + * with LSA remove info. + * If route->path is same, replace + * from paths list. + */ + for (ALL_LIST_ELEMENTS(route->paths, anode, anext, o_path)) { + if ((o_path->origin.type != lsa->header->type) || + (o_path->origin.adv_router != lsa->header->adv_router) || + (o_path->origin.id != lsa->header->id)) + continue; + + if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) { + prefix2str(&route->prefix, buf, sizeof(buf)); + zlog_debug( + "%s: route %s path found with cost %u nh %u to remove.", + __func__, buf, o_path->cost, + listcount(o_path->nh_list)); + } + + /* Remove found path's nh_list from + * the route's nh_list. + */ + for (ALL_LIST_ELEMENTS_RO(o_path->nh_list, nnode, nh)) { + for (ALL_LIST_ELEMENTS(route->nh_list, rnode, + rnext, rnh)) { + if (!ospf6_nexthop_is_same(rnh, nh)) + continue; + listnode_delete(route->nh_list, rnh); + ospf6_nexthop_delete(rnh); + } + } + /* Delete the path from route's + * path list + */ + listnode_delete(route->paths, o_path); + ospf6_path_free(o_path); + nh_updated = true; + break; + } + + if (nh_updated) { + /* Iterate all paths and merge nexthop, + * unlesss any of the nexthop similar to + * ones deleted as part of path deletion. + */ + for (ALL_LIST_ELEMENTS(route->paths, anode, anext, o_path)) + ospf6_merge_nexthops(route->nh_list, o_path->nh_list); + + + if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) { + prefix2str(&route->prefix, buf, sizeof(buf)); + zlog_debug( + "%s: route %s update paths %u nh %u", __func__, + buf, route->paths ? listcount(route->paths) : 0, + route->nh_list ? listcount(route->nh_list) : 0); + } + + /* Update Global Route table and + * RIB/FIB with effective + * nh_list + */ + if (oa->route_table->hook_add) + (*oa->route_table->hook_add)(route); + + /* route's primary path is similar + * to LSA, replace route's primary + * path with route's paths list + * head. + */ + if ((route->path.origin.id == lsa->header->id) && + (route->path.origin.adv_router == + lsa->header->adv_router)) { + ospf6_intra_prefix_update_route_origin(route, + oa->ospf6); + } + } + +} + +void ospf6_intra_prefix_lsa_remove(struct ospf6_lsa *lsa) +{ + struct ospf6_area *oa; + struct ospf6_intra_prefix_lsa *intra_prefix_lsa; + struct prefix prefix; + struct ospf6_route *route, *nroute; + int prefix_num; + struct ospf6_prefix *op; + char *start, *current, *end; + char buf[PREFIX2STR_BUFFER]; + + if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) + zlog_debug("%s: %s disappearing", __func__, lsa->name); + + oa = OSPF6_AREA(lsa->lsdb->data); + + intra_prefix_lsa = + (struct ospf6_intra_prefix_lsa *)OSPF6_LSA_HEADER_END( + lsa->header); + + prefix_num = ntohs(intra_prefix_lsa->prefix_num); + start = (caddr_t)intra_prefix_lsa + + sizeof(struct ospf6_intra_prefix_lsa); + end = OSPF6_LSA_END(lsa->header); + for (current = start; current < end; current += OSPF6_PREFIX_SIZE(op)) { + op = (struct ospf6_prefix *)current; + if (prefix_num == 0) + break; + if (end < current + OSPF6_PREFIX_SIZE(op)) + break; + prefix_num--; + + memset(&prefix, 0, sizeof(prefix)); + prefix.family = AF_INET6; + prefix.prefixlen = op->prefix_length; + ospf6_prefix_in6_addr(&prefix.u.prefix6, intra_prefix_lsa, op); + + route = ospf6_route_lookup(&prefix, oa->route_table); + if (route == NULL) + continue; + + for (ospf6_route_lock(route); + route && ospf6_route_is_prefix(&prefix, route); + route = nroute) { + nroute = ospf6_route_next(route); + if (route->type != OSPF6_DEST_TYPE_NETWORK) + continue; + if (route->path.area_id != oa->area_id) + continue; + if (route->path.type != OSPF6_PATH_TYPE_INTRA) + continue; + /* Route has multiple ECMP paths, remove matching + * path. Update current route's effective nh list + * after removal of one of the path. + */ + if (listcount(route->paths) > 1) { + ospf6_intra_prefix_lsa_remove_update_route( + lsa, oa, route); + } else { + + if (route->path.origin.type != lsa->header->type + || route->path.origin.id != lsa->header->id + || route->path.origin.adv_router + != lsa->header->adv_router) + continue; + + if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) { + prefix2str(&route->prefix, buf, + sizeof(buf)); + zlog_debug( + "%s: route remove %s with path type %u cost %u paths %u nh %u", + __func__, buf, route->path.type, + route->path.cost, + listcount(route->paths), + listcount(route->nh_list)); + } + ospf6_route_remove(route, oa->route_table); + } + } + if (route) + ospf6_route_unlock(route); + } + + if (current != end && IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) + zlog_debug("Trailing garbage ignored"); +} + +void ospf6_intra_route_calculation(struct ospf6_area *oa) +{ + struct ospf6_route *route, *nroute; + uint16_t type; + struct ospf6_lsa *lsa; + void (*hook_add)(struct ospf6_route *) = NULL; + void (*hook_remove)(struct ospf6_route *) = NULL; + char buf[PREFIX2STR_BUFFER]; + + if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) + zlog_debug("Re-examin intra-routes for area %s", oa->name); + + hook_add = oa->route_table->hook_add; + hook_remove = oa->route_table->hook_remove; + oa->route_table->hook_add = NULL; + oa->route_table->hook_remove = NULL; + + for (route = ospf6_route_head(oa->route_table); route; + route = ospf6_route_next(route)) + route->flag = OSPF6_ROUTE_REMOVE; + + type = htons(OSPF6_LSTYPE_INTRA_PREFIX); + for (ALL_LSDB_TYPED(oa->lsdb, type, lsa)) + ospf6_intra_prefix_lsa_add(lsa); + + oa->route_table->hook_add = hook_add; + oa->route_table->hook_remove = hook_remove; + + for (route = ospf6_route_head(oa->route_table); route; route = nroute) { + if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) { + prefix2str(&route->prefix, buf, sizeof(buf)); + zlog_debug("%s: route %s, flag 0x%x", __func__, buf, + route->flag); + } + + nroute = ospf6_route_next(route); + if (CHECK_FLAG(route->flag, OSPF6_ROUTE_REMOVE) + && CHECK_FLAG(route->flag, OSPF6_ROUTE_ADD)) { + UNSET_FLAG(route->flag, OSPF6_ROUTE_REMOVE); + UNSET_FLAG(route->flag, OSPF6_ROUTE_ADD); + } + + if (CHECK_FLAG(route->flag, OSPF6_ROUTE_REMOVE)) + ospf6_route_remove(route, oa->route_table); + else if (CHECK_FLAG(route->flag, OSPF6_ROUTE_ADD) + || CHECK_FLAG(route->flag, OSPF6_ROUTE_CHANGE)) { + if (hook_add) + (*hook_add)(route); + route->flag = 0; + } else { + /* Redo the summaries as things might have changed */ + if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) + zlog_debug("%s: Originate summary for route %s", + __func__, buf); + ospf6_abr_originate_summary(route, oa->ospf6); + route->flag = 0; + } + } + + if (IS_OSPF6_DEBUG_EXAMIN(INTRA_PREFIX)) + zlog_debug("Re-examin intra-routes for area %s: Done", + oa->name); +} + +static void ospf6_brouter_debug_print(struct ospf6_route *brouter) +{ + uint32_t brouter_id; + char brouter_name[16]; + char area_name[16]; + char destination[64]; + char installed[64], changed[64]; + struct timeval now, res; + char id[16], adv_router[16]; + char capa[16], options[32]; + + brouter_id = ADV_ROUTER_IN_PREFIX(&brouter->prefix); + inet_ntop(AF_INET, &brouter_id, brouter_name, sizeof(brouter_name)); + inet_ntop(AF_INET, &brouter->path.area_id, area_name, + sizeof(area_name)); + ospf6_linkstate_prefix2str(&brouter->prefix, destination, + sizeof(destination)); + + monotime(&now); + timersub(&now, &brouter->installed, &res); + timerstring(&res, installed, sizeof(installed)); + + monotime(&now); + timersub(&now, &brouter->changed, &res); + timerstring(&res, changed, sizeof(changed)); + + inet_ntop(AF_INET, &brouter->path.origin.id, id, sizeof(id)); + inet_ntop(AF_INET, &brouter->path.origin.adv_router, adv_router, + sizeof(adv_router)); + + ospf6_options_printbuf(brouter->path.options, options, sizeof(options)); + ospf6_capability_printbuf(brouter->path.router_bits, capa, + sizeof(capa)); + + zlog_info("Brouter: %s via area %s", brouter_name, area_name); + zlog_info(" memory: prev: %p this: %p next: %p parent rnode: %p", + (void *)brouter->prev, (void *)brouter, (void *)brouter->next, + (void *)brouter->rnode); + zlog_info(" type: %d prefix: %s installed: %s changed: %s", + brouter->type, destination, installed, changed); + zlog_info(" lock: %d flags: %s%s%s%s", brouter->lock, + (CHECK_FLAG(brouter->flag, OSPF6_ROUTE_BEST) ? "B" : "-"), + (CHECK_FLAG(brouter->flag, OSPF6_ROUTE_ADD) ? "A" : "-"), + (CHECK_FLAG(brouter->flag, OSPF6_ROUTE_REMOVE) ? "R" : "-"), + (CHECK_FLAG(brouter->flag, OSPF6_ROUTE_CHANGE) ? "C" : "-")); + zlog_info(" path type: %s ls-origin %s id: %s adv-router %s", + OSPF6_PATH_TYPE_NAME(brouter->path.type), + ospf6_lstype_name(brouter->path.origin.type), id, adv_router); + zlog_info(" options: %s router-bits: %s metric-type: %d metric: %d/%d", + options, capa, brouter->path.metric_type, brouter->path.cost, + brouter->path.u.cost_e2); + zlog_info(" paths %u nh %u", listcount(brouter->paths), + listcount(brouter->nh_list)); +} + +void ospf6_intra_brouter_calculation(struct ospf6_area *oa) +{ + struct ospf6_route *brouter, *nbrouter, *copy; + void (*hook_add)(struct ospf6_route *) = NULL; + void (*hook_remove)(struct ospf6_route *) = NULL; + uint32_t brouter_id; + char brouter_name[16]; + + if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID(oa->area_id) || + IS_OSPF6_DEBUG_ROUTE(MEMORY)) + zlog_debug("%s: border-router calculation for area %s", + __func__, oa->name); + + hook_add = oa->ospf6->brouter_table->hook_add; + hook_remove = oa->ospf6->brouter_table->hook_remove; + oa->ospf6->brouter_table->hook_add = NULL; + oa->ospf6->brouter_table->hook_remove = NULL; + + /* withdraw the previous router entries for the area */ + for (brouter = ospf6_route_head(oa->ospf6->brouter_table); brouter; + brouter = ospf6_route_next(brouter)) { + brouter_id = ADV_ROUTER_IN_PREFIX(&brouter->prefix); + inet_ntop(AF_INET, &brouter_id, brouter_name, + sizeof(brouter_name)); + + if (brouter->path.area_id != oa->area_id) + continue; + + SET_FLAG(brouter->flag, OSPF6_ROUTE_REMOVE); + + if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER_ID(brouter_id) + || IS_OSPF6_DEBUG_ROUTE(MEMORY)) { + zlog_debug("%p: mark as removing: area %s brouter %s", + (void *)brouter, oa->name, brouter_name); + ospf6_brouter_debug_print(brouter); + } + } + + for (brouter = ospf6_route_head(oa->spf_table); brouter; + brouter = ospf6_route_next(brouter)) { + brouter_id = ADV_ROUTER_IN_PREFIX(&brouter->prefix); + inet_ntop(AF_INET, &brouter_id, brouter_name, + sizeof(brouter_name)); + + if (brouter->type != OSPF6_DEST_TYPE_LINKSTATE) + continue; + + if (ospf6_linkstate_prefix_id(&brouter->prefix) != htonl(0)) + continue; + + if (!CHECK_FLAG(brouter->path.router_bits, OSPF6_ROUTER_BIT_E) + && !CHECK_FLAG(brouter->path.router_bits, + OSPF6_ROUTER_BIT_B)) + continue; + + if (!OSPF6_OPT_ISSET(brouter->path.options, OSPF6_OPT_V6) + || !OSPF6_OPT_ISSET(brouter->path.options, OSPF6_OPT_R)) + continue; + + copy = ospf6_route_copy(brouter); + copy->type = OSPF6_DEST_TYPE_ROUTER; + copy->path.area_id = oa->area_id; + ospf6_route_add(copy, oa->ospf6->brouter_table); + + if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER_ID(brouter_id) + || IS_OSPF6_DEBUG_ROUTE(MEMORY)) { + zlog_debug("%p: transfer: area %s brouter %s", + (void *)brouter, oa->name, brouter_name); + ospf6_brouter_debug_print(brouter); + } + } + + oa->ospf6->brouter_table->hook_add = hook_add; + oa->ospf6->brouter_table->hook_remove = hook_remove; + + for (brouter = ospf6_route_head(oa->ospf6->brouter_table); brouter; + brouter = nbrouter) { + + /* + * brouter may have been "deleted" in the last loop iteration. + * If this is the case there is still 1 final refcount lock + * taken by ospf6_route_next, that will be released by the same + * call and result in deletion. To avoid heap UAF we must then + * skip processing the deleted route. + */ + if (brouter->lock == 1) { + if (IS_OSPF6_DEBUG_ROUTE(MEMORY)) + ospf6_brouter_debug_print(brouter); + nbrouter = ospf6_route_next(brouter); + continue; + } else { + nbrouter = ospf6_route_next(brouter); + } + + brouter_id = ADV_ROUTER_IN_PREFIX(&brouter->prefix); + inet_ntop(AF_INET, &brouter_id, brouter_name, + sizeof(brouter_name)); + + if (brouter->path.area_id != oa->area_id) + continue; + + if (CHECK_FLAG(brouter->flag, OSPF6_ROUTE_WAS_REMOVED)) + continue; + + /* After iterating spf_table for all routers including + * intra brouter, clear mark for remove flag for + * inter border router if its adv router present in + * SPF table. + */ + if (brouter->path.type == OSPF6_PATH_TYPE_INTER) { + struct prefix adv_prefix; + + ospf6_linkstate_prefix(brouter->path.origin.adv_router, + htonl(0), &adv_prefix); + + if (ospf6_route_lookup(&adv_prefix, oa->spf_table)) { + if (IS_OSPF6_DEBUG_BROUTER) { + zlog_debug( + "%s: keep inter brouter %s as adv router 0x%x found in spf", + __func__, brouter_name, + brouter->path.origin + .adv_router); + ospf6_brouter_debug_print(brouter); + } + UNSET_FLAG(brouter->flag, OSPF6_ROUTE_REMOVE); + } + } + + if (CHECK_FLAG(brouter->flag, OSPF6_ROUTE_REMOVE) + && CHECK_FLAG(brouter->flag, OSPF6_ROUTE_ADD)) { + UNSET_FLAG(brouter->flag, OSPF6_ROUTE_REMOVE); + UNSET_FLAG(brouter->flag, OSPF6_ROUTE_ADD); + } + + if (CHECK_FLAG(brouter->flag, OSPF6_ROUTE_REMOVE)) { + if (IS_OSPF6_DEBUG_BROUTER + || IS_OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER_ID( + brouter_id) + || IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID( + oa->area_id)) + zlog_debug( + "%s: brouter %s disappears via area %s", + __func__, brouter_name, oa->name); + /* This is used to protect nbrouter from removed from + * the table. For an example, ospf6_abr_examin_summary, + * removes brouters which are marked for remove. + */ + oa->intra_brouter_calc = true; + ospf6_route_remove(brouter, oa->ospf6->brouter_table); + brouter = NULL; + } else if (CHECK_FLAG(brouter->flag, OSPF6_ROUTE_ADD) + || CHECK_FLAG(brouter->flag, OSPF6_ROUTE_CHANGE)) { + if (IS_OSPF6_DEBUG_BROUTER + || IS_OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER_ID( + brouter_id) + || IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID( + oa->area_id)) + zlog_info("%s: brouter %s appears via area %s", + __func__, brouter_name, oa->name); + + /* newly added */ + if (hook_add) + (*hook_add)(brouter); + } else { + if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER_ID( + brouter_id) + || IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID( + oa->area_id)) + zlog_debug( + "brouter %s still exists via area %s", + brouter_name, oa->name); + /* But re-originate summaries */ + ospf6_abr_originate_summary(brouter, oa->ospf6); + } + + if (brouter) { + UNSET_FLAG(brouter->flag, OSPF6_ROUTE_ADD); + UNSET_FLAG(brouter->flag, OSPF6_ROUTE_CHANGE); + } + /* Reset for nbrouter */ + oa->intra_brouter_calc = false; + } + + if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID(oa->area_id) || + IS_OSPF6_DEBUG_ROUTE(MEMORY)) + zlog_debug("%s: border-router calculation for area %s: done", + __func__, oa->name); +} + +static struct ospf6_lsa_handler router_handler = { + .lh_type = OSPF6_LSTYPE_ROUTER, + .lh_name = "Router", + .lh_short_name = "Rtr", + .lh_show = ospf6_router_lsa_show, + .lh_get_prefix_str = ospf6_router_lsa_get_nbr_id, + .lh_debug = 0}; + +static struct ospf6_lsa_handler network_handler = { + .lh_type = OSPF6_LSTYPE_NETWORK, + .lh_name = "Network", + .lh_short_name = "Net", + .lh_show = ospf6_network_lsa_show, + .lh_get_prefix_str = ospf6_network_lsa_get_ar_id, + .lh_debug = 0}; + +static struct ospf6_lsa_handler link_handler = { + .lh_type = OSPF6_LSTYPE_LINK, + .lh_name = "Link", + .lh_short_name = "Lnk", + .lh_show = ospf6_link_lsa_show, + .lh_get_prefix_str = ospf6_link_lsa_get_prefix_str, + .lh_debug = 0}; + +static struct ospf6_lsa_handler intra_prefix_handler = { + .lh_type = OSPF6_LSTYPE_INTRA_PREFIX, + .lh_name = "Intra-Prefix", + .lh_short_name = "INP", + .lh_show = ospf6_intra_prefix_lsa_show, + .lh_get_prefix_str = ospf6_intra_prefix_lsa_get_prefix_str, + .lh_debug = 0}; + +void ospf6_intra_init(void) +{ + ospf6_install_lsa_handler(&router_handler); + ospf6_install_lsa_handler(&network_handler); + ospf6_install_lsa_handler(&link_handler); + ospf6_install_lsa_handler(&intra_prefix_handler); +} + +DEFUN (debug_ospf6_brouter, + debug_ospf6_brouter_cmd, + "debug ospf6 border-routers", + DEBUG_STR + OSPF6_STR + "Debug border router\n" + ) +{ + OSPF6_DEBUG_BROUTER_ON(); + return CMD_SUCCESS; +} + +DEFUN (no_debug_ospf6_brouter, + no_debug_ospf6_brouter_cmd, + "no debug ospf6 border-routers", + NO_STR + DEBUG_STR + OSPF6_STR + "Debug border router\n" + ) +{ + OSPF6_DEBUG_BROUTER_OFF(); + return CMD_SUCCESS; +} + +DEFUN (debug_ospf6_brouter_router, + debug_ospf6_brouter_router_cmd, + "debug ospf6 border-routers router-id A.B.C.D", + DEBUG_STR + OSPF6_STR + "Debug border router\n" + "Debug specific border router\n" + "Specify border-router's router-id\n" + ) +{ + int idx_ipv4 = 4; + uint32_t router_id; + inet_pton(AF_INET, argv[idx_ipv4]->arg, &router_id); + OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER_ON(router_id); + return CMD_SUCCESS; +} + +DEFUN (no_debug_ospf6_brouter_router, + no_debug_ospf6_brouter_router_cmd, + "no debug ospf6 border-routers router-id [A.B.C.D]", + NO_STR + DEBUG_STR + OSPF6_STR + "Debug border router\n" + "Debug specific border router\n" + "Specify border-router's router-id\n" + ) +{ + OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER_OFF(); + return CMD_SUCCESS; +} + +DEFUN (debug_ospf6_brouter_area, + debug_ospf6_brouter_area_cmd, + "debug ospf6 border-routers area-id A.B.C.D", + DEBUG_STR + OSPF6_STR + "Debug border router\n" + "Debug border routers in specific Area\n" + "Specify Area-ID\n" + ) +{ + int idx_ipv4 = 4; + uint32_t area_id; + inet_pton(AF_INET, argv[idx_ipv4]->arg, &area_id); + OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ON(area_id); + return CMD_SUCCESS; +} + +DEFUN (no_debug_ospf6_brouter_area, + no_debug_ospf6_brouter_area_cmd, + "no debug ospf6 border-routers area-id [A.B.C.D]", + NO_STR + DEBUG_STR + OSPF6_STR + "Debug border router\n" + "Debug border routers in specific Area\n" + "Specify Area-ID\n" + ) +{ + OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_OFF(); + return CMD_SUCCESS; +} + +int config_write_ospf6_debug_brouter(struct vty *vty) +{ + char buf[16]; + if (IS_OSPF6_DEBUG_BROUTER) + vty_out(vty, "debug ospf6 border-routers\n"); + if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER) { + inet_ntop(AF_INET, &conf_debug_ospf6_brouter_specific_router_id, + buf, sizeof(buf)); + vty_out(vty, "debug ospf6 border-routers router-id %s\n", buf); + } + if (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA) { + inet_ntop(AF_INET, &conf_debug_ospf6_brouter_specific_area_id, + buf, sizeof(buf)); + vty_out(vty, "debug ospf6 border-routers area-id %s\n", buf); + } + return 0; +} + +void install_element_ospf6_debug_brouter(void) +{ + install_element(ENABLE_NODE, &debug_ospf6_brouter_cmd); + install_element(ENABLE_NODE, &debug_ospf6_brouter_router_cmd); + install_element(ENABLE_NODE, &debug_ospf6_brouter_area_cmd); + install_element(ENABLE_NODE, &no_debug_ospf6_brouter_cmd); + install_element(ENABLE_NODE, &no_debug_ospf6_brouter_router_cmd); + install_element(ENABLE_NODE, &no_debug_ospf6_brouter_area_cmd); + install_element(CONFIG_NODE, &debug_ospf6_brouter_cmd); + install_element(CONFIG_NODE, &debug_ospf6_brouter_router_cmd); + install_element(CONFIG_NODE, &debug_ospf6_brouter_area_cmd); + install_element(CONFIG_NODE, &no_debug_ospf6_brouter_cmd); + install_element(CONFIG_NODE, &no_debug_ospf6_brouter_router_cmd); + install_element(CONFIG_NODE, &no_debug_ospf6_brouter_area_cmd); +} diff --git a/ospf6d/ospf6_intra.h b/ospf6d/ospf6_intra.h new file mode 100644 index 0000000..7d154cb --- /dev/null +++ b/ospf6d/ospf6_intra.h @@ -0,0 +1,243 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2003 Yasuhiro Ohara + */ + +#ifndef OSPF6_INTRA_H +#define OSPF6_INTRA_H + +/* Debug option */ +extern unsigned char conf_debug_ospf6_brouter; +extern in_addr_t conf_debug_ospf6_brouter_specific_router_id; +extern in_addr_t conf_debug_ospf6_brouter_specific_area_id; +#define OSPF6_DEBUG_BROUTER_SUMMARY 0x01 +#define OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER 0x02 +#define OSPF6_DEBUG_BROUTER_SPECIFIC_AREA 0x04 +#define OSPF6_DEBUG_BROUTER_ON() \ + (conf_debug_ospf6_brouter |= OSPF6_DEBUG_BROUTER_SUMMARY) +#define OSPF6_DEBUG_BROUTER_OFF() \ + (conf_debug_ospf6_brouter &= ~OSPF6_DEBUG_BROUTER_SUMMARY) +#define IS_OSPF6_DEBUG_BROUTER \ + (conf_debug_ospf6_brouter & OSPF6_DEBUG_BROUTER_SUMMARY) + +#define OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER_ON(router_id) \ + do { \ + conf_debug_ospf6_brouter_specific_router_id = (router_id); \ + conf_debug_ospf6_brouter |= \ + OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER; \ + } while (0) +#define OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER_OFF() \ + do { \ + conf_debug_ospf6_brouter_specific_router_id = 0; \ + conf_debug_ospf6_brouter &= \ + ~OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER; \ + } while (0) +#define IS_OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER \ + (conf_debug_ospf6_brouter & OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER) +#define IS_OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER_ID(router_id) \ + (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER \ + && conf_debug_ospf6_brouter_specific_router_id == (router_id)) + +#define OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ON(area_id) \ + do { \ + conf_debug_ospf6_brouter_specific_area_id = (area_id); \ + conf_debug_ospf6_brouter |= OSPF6_DEBUG_BROUTER_SPECIFIC_AREA; \ + } while (0) +#define OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_OFF() \ + do { \ + conf_debug_ospf6_brouter_specific_area_id = 0; \ + conf_debug_ospf6_brouter &= \ + ~OSPF6_DEBUG_BROUTER_SPECIFIC_AREA; \ + } while (0) +#define IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA \ + (conf_debug_ospf6_brouter & OSPF6_DEBUG_BROUTER_SPECIFIC_AREA) +#define IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_ID(area_id) \ + (IS_OSPF6_DEBUG_BROUTER_SPECIFIC_AREA \ + && conf_debug_ospf6_brouter_specific_area_id == (area_id)) + +/* Router-LSA */ +#define OSPF6_ROUTER_LSA_MIN_SIZE 4U +struct ospf6_router_lsa { + uint8_t bits; + uint8_t options[3]; + /* followed by ospf6_router_lsdesc(s) */ +}; + +/* Link State Description in Router-LSA */ +#define OSPF6_ROUTER_LSDESC_FIX_SIZE 16U +struct ospf6_router_lsdesc { + uint8_t type; + uint8_t reserved; + uint16_t metric; /* output cost */ + uint32_t interface_id; + uint32_t neighbor_interface_id; + in_addr_t neighbor_router_id; +}; + +#define OSPF6_ROUTER_LSDESC_POINTTOPOINT 1 +#define OSPF6_ROUTER_LSDESC_TRANSIT_NETWORK 2 +#define OSPF6_ROUTER_LSDESC_STUB_NETWORK 3 +#define OSPF6_ROUTER_LSDESC_VIRTUAL_LINK 4 + +enum stub_router_mode { + OSPF6_NOT_STUB_ROUTER, + OSPF6_IS_STUB_ROUTER, + OSPF6_IS_STUB_ROUTER_V6, +}; + +#define ROUTER_LSDESC_IS_TYPE(t, x) \ + ((((struct ospf6_router_lsdesc *)(x))->type \ + == OSPF6_ROUTER_LSDESC_##t) \ + ? 1 \ + : 0) +#define ROUTER_LSDESC_GET_METRIC(x) \ + (ntohs(((struct ospf6_router_lsdesc *)(x))->metric)) +#define ROUTER_LSDESC_GET_IFID(x) \ + (ntohl(((struct ospf6_router_lsdesc *)(x))->interface_id)) +#define ROUTER_LSDESC_GET_NBR_IFID(x) \ + (ntohl(((struct ospf6_router_lsdesc *)(x))->neighbor_interface_id)) +#define ROUTER_LSDESC_GET_NBR_ROUTERID(x) \ + (((struct ospf6_router_lsdesc *)(x))->neighbor_router_id) + +/* Network-LSA */ +#define OSPF6_NETWORK_LSA_MIN_SIZE 4U +struct ospf6_network_lsa { + uint8_t reserved; + uint8_t options[3]; + /* followed by ospf6_netowrk_lsd(s) */ +}; + +/* Link State Description in Router-LSA */ +#define OSPF6_NETWORK_LSDESC_FIX_SIZE 4U +struct ospf6_network_lsdesc { + in_addr_t router_id; +}; +#define NETWORK_LSDESC_GET_NBR_ROUTERID(x) \ + (((struct ospf6_network_lsdesc *)(x))->router_id) + +/* Link-LSA */ +#define OSPF6_LINK_LSA_MIN_SIZE 24U /* w/o 1st IPv6 prefix */ +struct ospf6_link_lsa { + uint8_t priority; + uint8_t options[3]; + struct in6_addr linklocal_addr; + uint32_t prefix_num; + /* followed by ospf6 prefix(es) */ +}; + +/* Intra-Area-Prefix-LSA */ +#define OSPF6_INTRA_PREFIX_LSA_MIN_SIZE 12U /* w/o 1st IPv6 prefix */ +struct ospf6_intra_prefix_lsa { + uint16_t prefix_num; + uint16_t ref_type; + uint32_t ref_id; + in_addr_t ref_adv_router; + /* followed by ospf6 prefix(es) */ +}; + + +#define OSPF6_ROUTER_LSA_SCHEDULE(oa) \ + do { \ + if (CHECK_FLAG((oa)->flag, OSPF6_AREA_ENABLE)) \ + event_add_event(master, ospf6_router_lsa_originate, \ + oa, 0, &(oa)->thread_router_lsa); \ + } while (0) +#define OSPF6_NETWORK_LSA_SCHEDULE(oi) \ + do { \ + if (!CHECK_FLAG((oi)->flag, OSPF6_INTERFACE_DISABLE)) \ + event_add_event(master, ospf6_network_lsa_originate, \ + oi, 0, &(oi)->thread_network_lsa); \ + } while (0) +#define OSPF6_LINK_LSA_SCHEDULE(oi) \ + do { \ + if (!CHECK_FLAG((oi)->flag, OSPF6_INTERFACE_DISABLE)) \ + event_add_event(master, ospf6_link_lsa_originate, oi, \ + 0, &(oi)->thread_link_lsa); \ + } while (0) +#define OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB(oa) \ + do { \ + if (CHECK_FLAG((oa)->flag, OSPF6_AREA_ENABLE)) \ + event_add_event( \ + master, ospf6_intra_prefix_lsa_originate_stub, \ + oa, 0, &(oa)->thread_intra_prefix_lsa); \ + } while (0) +#define OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT(oi) \ + do { \ + if (!CHECK_FLAG((oi)->flag, OSPF6_INTERFACE_DISABLE)) \ + event_add_event( \ + master, \ + ospf6_intra_prefix_lsa_originate_transit, oi, \ + 0, &(oi)->thread_intra_prefix_lsa); \ + } while (0) + +#define OSPF6_AS_EXTERN_LSA_SCHEDULE(oi) \ + do { \ + if (!CHECK_FLAG((oi)->flag, OSPF6_INTERFACE_DISABLE)) \ + event_add_event(master, ospf6_orig_as_external_lsa, \ + oi, 0, &(oi)->thread_as_extern_lsa); \ + } while (0) + +#define OSPF6_ROUTER_LSA_EXECUTE(oa) \ + do { \ + if (CHECK_FLAG((oa)->flag, OSPF6_AREA_ENABLE)) \ + event_execute(master, ospf6_router_lsa_originate, oa, \ + 0, NULL); \ + } while (0) + +#define OSPF6_NETWORK_LSA_EXECUTE(oi) \ + do { \ + EVENT_OFF((oi)->thread_network_lsa); \ + event_execute(master, ospf6_network_lsa_originate, oi, 0, \ + NULL); \ + } while (0) + +#define OSPF6_LINK_LSA_EXECUTE(oi) \ + do { \ + if (!CHECK_FLAG((oi)->flag, OSPF6_INTERFACE_DISABLE)) \ + event_execute(master, ospf6_link_lsa_originate, oi, \ + 0, NULL); \ + } while (0) + +#define OSPF6_INTRA_PREFIX_LSA_EXECUTE_TRANSIT(oi) \ + do { \ + EVENT_OFF((oi)->thread_intra_prefix_lsa); \ + event_execute(master, \ + ospf6_intra_prefix_lsa_originate_transit, oi, \ + 0, NULL); \ + } while (0) + +#define OSPF6_AS_EXTERN_LSA_EXECUTE(oi) \ + do { \ + EVENT_OFF((oi)->thread_as_extern_lsa); \ + event_execute(master, ospf6_orig_as_external_lsa, oi, 0, NULL);\ + } while (0) + +/* Function Prototypes */ +extern char *ospf6_router_lsdesc_lookup(uint8_t type, uint32_t interface_id, + uint32_t neighbor_interface_id, + uint32_t neighbor_router_id, + struct ospf6_lsa *lsa); +extern char *ospf6_network_lsdesc_lookup(uint32_t router_id, + struct ospf6_lsa *lsa); + +extern int ospf6_router_is_stub_router(struct ospf6_lsa *lsa); +extern void ospf6_router_lsa_originate(struct event *thread); +extern void ospf6_network_lsa_originate(struct event *thread); +extern void ospf6_link_lsa_originate(struct event *thread); +extern void ospf6_intra_prefix_lsa_originate_transit(struct event *thread); +extern void ospf6_intra_prefix_lsa_originate_stub(struct event *thread); +extern void ospf6_intra_prefix_lsa_add(struct ospf6_lsa *lsa); +extern void ospf6_intra_prefix_lsa_remove(struct ospf6_lsa *lsa); +extern void ospf6_orig_as_external_lsa(struct event *thread); +extern void ospf6_intra_route_calculation(struct ospf6_area *oa); +extern void ospf6_intra_brouter_calculation(struct ospf6_area *oa); +extern void ospf6_intra_prefix_route_ecmp_path(struct ospf6_area *oa, + struct ospf6_route *old, + struct ospf6_route *route); + +extern void ospf6_intra_init(void); + +extern int config_write_ospf6_debug_brouter(struct vty *vty); +extern void install_element_ospf6_debug_brouter(void); + +#endif /* OSPF6_LSA_H */ diff --git a/ospf6d/ospf6_lsa.c b/ospf6d/ospf6_lsa.c new file mode 100644 index 0000000..bc39579 --- /dev/null +++ b/ospf6d/ospf6_lsa.c @@ -0,0 +1,1219 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2003 Yasuhiro Ohara + */ + +#include <zebra.h> + +/* Include other stuffs */ +#include "log.h" +#include "linklist.h" +#include "vector.h" +#include "vty.h" +#include "command.h" +#include "memory.h" +#include "frrevent.h" +#include "checksum.h" +#include "frrstr.h" + +#include "ospf6_proto.h" +#include "ospf6_lsa.h" +#include "ospf6_lsdb.h" +#include "ospf6_message.h" +#include "ospf6_asbr.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 "ospf6d.h" + +#include "ospf6d/ospf6_lsa_clippy.c" + +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_LSA, "OSPF6 LSA"); +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_LSA_HEADER, "OSPF6 LSA header"); +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_LSA_SUMMARY, "OSPF6 LSA summary"); + +static struct ospf6_lsa_handler *lsa_handlers[OSPF6_LSTYPE_SIZE]; + +struct ospf6 *ospf6_get_by_lsdb(struct ospf6_lsa *lsa) +{ + struct ospf6 *ospf6 = NULL; + + switch (OSPF6_LSA_SCOPE(lsa->header->type)) { + case OSPF6_SCOPE_LINKLOCAL: + ospf6 = OSPF6_INTERFACE(lsa->lsdb->data)->area->ospf6; + break; + case OSPF6_SCOPE_AREA: + ospf6 = OSPF6_AREA(lsa->lsdb->data)->ospf6; + break; + case OSPF6_SCOPE_AS: + ospf6 = OSPF6_PROCESS(lsa->lsdb->data); + break; + default: + assert(0); + break; + } + return ospf6; +} + +static int ospf6_unknown_lsa_show(struct vty *vty, struct ospf6_lsa *lsa, + json_object *json_obj, bool use_json) +{ + uint8_t *start, *end, *current; + + start = (uint8_t *)lsa->header + sizeof(struct ospf6_lsa_header); + end = (uint8_t *)lsa->header + ntohs(lsa->header->length); + + if (use_json) { + json_object_string_add(json_obj, "lsaType", "unknown"); + } else { + vty_out(vty, " Unknown contents:\n"); + for (current = start; current < end; current++) { + if ((current - start) % 16 == 0) + vty_out(vty, "\n "); + else if ((current - start) % 4 == 0) + vty_out(vty, " "); + + vty_out(vty, "%02x", *current); + } + + vty_out(vty, "\n\n"); + } + return 0; +} + +static struct ospf6_lsa_handler unknown_handler = { + .lh_type = OSPF6_LSTYPE_UNKNOWN, + .lh_name = "Unknown", + .lh_short_name = "Unk", + .lh_show = ospf6_unknown_lsa_show, + .lh_get_prefix_str = NULL, + .lh_debug = 0 /* No default debug */ +}; + +void ospf6_install_lsa_handler(struct ospf6_lsa_handler *handler) +{ + /* type in handler is host byte order */ + unsigned int index = handler->lh_type & OSPF6_LSTYPE_FCODE_MASK; + + assertf(index < array_size(lsa_handlers), "index=%x", index); + assertf(lsa_handlers[index] == NULL, "old=%s, new=%s", + lsa_handlers[index]->lh_name, handler->lh_name); + + lsa_handlers[index] = handler; +} + +struct ospf6_lsa_handler *ospf6_get_lsa_handler(uint16_t type) +{ + struct ospf6_lsa_handler *handler = NULL; + unsigned int index = ntohs(type) & OSPF6_LSTYPE_FCODE_MASK; + + if (index < array_size(lsa_handlers)) + handler = lsa_handlers[index]; + + if (handler == NULL) + handler = &unknown_handler; + + return handler; +} + +const char *ospf6_lstype_name(uint16_t type) +{ + static char buf[8]; + const struct ospf6_lsa_handler *handler; + + handler = ospf6_get_lsa_handler(type); + if (handler && handler != &unknown_handler) + return handler->lh_name; + + snprintf(buf, sizeof(buf), "0x%04hx", ntohs(type)); + return buf; +} + +const char *ospf6_lstype_short_name(uint16_t type) +{ + static char buf[8]; + const struct ospf6_lsa_handler *handler; + + handler = ospf6_get_lsa_handler(type); + if (handler) + return handler->lh_short_name; + + snprintf(buf, sizeof(buf), "0x%04hx", ntohs(type)); + return buf; +} + +uint8_t ospf6_lstype_debug(uint16_t type) +{ + const struct ospf6_lsa_handler *handler; + handler = ospf6_get_lsa_handler(type); + return handler->lh_debug; +} + +int metric_type(struct ospf6 *ospf6, int type, uint8_t instance) +{ + struct ospf6_redist *red; + + red = ospf6_redist_lookup(ospf6, type, instance); + + return ((!red || red->dmetric.type < 0) ? DEFAULT_METRIC_TYPE + : red->dmetric.type); +} + +int metric_value(struct ospf6 *ospf6, int type, uint8_t instance) +{ + struct ospf6_redist *red; + + red = ospf6_redist_lookup(ospf6, type, instance); + if (!red || red->dmetric.value < 0) { + if (type == DEFAULT_ROUTE) { + if (ospf6->default_originate == DEFAULT_ORIGINATE_ZEBRA) + return DEFAULT_DEFAULT_ORIGINATE_METRIC; + else + return DEFAULT_DEFAULT_ALWAYS_METRIC; + } else + return DEFAULT_DEFAULT_METRIC; + } + + return red->dmetric.value; +} + +/* RFC2328: Section 13.2 */ +int ospf6_lsa_is_differ(struct ospf6_lsa *lsa1, struct ospf6_lsa *lsa2) +{ + int len; + + assert(OSPF6_LSA_IS_SAME(lsa1, lsa2)); + + /* XXX, Options ??? */ + + ospf6_lsa_age_current(lsa1); + ospf6_lsa_age_current(lsa2); + if (ntohs(lsa1->header->age) == OSPF_LSA_MAXAGE + && ntohs(lsa2->header->age) != OSPF_LSA_MAXAGE) + return 1; + if (ntohs(lsa1->header->age) != OSPF_LSA_MAXAGE + && ntohs(lsa2->header->age) == OSPF_LSA_MAXAGE) + return 1; + + /* compare body */ + if (ntohs(lsa1->header->length) != ntohs(lsa2->header->length)) + return 1; + + len = ntohs(lsa1->header->length) - sizeof(struct ospf6_lsa_header); + return memcmp(lsa1->header + 1, lsa2->header + 1, len); +} + +int ospf6_lsa_is_changed(struct ospf6_lsa *lsa1, struct ospf6_lsa *lsa2) +{ + int length; + + if (OSPF6_LSA_IS_MAXAGE(lsa1) ^ OSPF6_LSA_IS_MAXAGE(lsa2)) + return 1; + if (ntohs(lsa1->header->length) != ntohs(lsa2->header->length)) + return 1; + /* Going beyond LSA headers to compare the payload only makes sense, + * when both LSAs aren't header-only. */ + if (CHECK_FLAG(lsa1->flag, OSPF6_LSA_HEADERONLY) + != CHECK_FLAG(lsa2->flag, OSPF6_LSA_HEADERONLY)) { + zlog_warn( + "%s: only one of two (%s, %s) LSAs compared is header-only", + __func__, lsa1->name, lsa2->name); + return 1; + } + if (CHECK_FLAG(lsa1->flag, OSPF6_LSA_HEADERONLY)) + return 0; + + length = OSPF6_LSA_SIZE(lsa1->header) - sizeof(struct ospf6_lsa_header); + /* Once upper layer verifies LSAs received, length underrun should + * become a warning. */ + if (length <= 0) + return 0; + + return memcmp(OSPF6_LSA_HEADER_END(lsa1->header), + OSPF6_LSA_HEADER_END(lsa2->header), length); +} + +/* ospf6 age functions */ +/* calculate birth */ +void ospf6_lsa_age_set(struct ospf6_lsa *lsa) +{ + struct timeval now; + + assert(lsa && lsa->header); + + monotime(&now); + + lsa->birth.tv_sec = now.tv_sec - ntohs(lsa->header->age); + lsa->birth.tv_usec = now.tv_usec; + + return; +} + +/* this function calculates current age from its birth, + then update age field of LSA header. return value is current age */ +uint16_t ospf6_lsa_age_current(struct ospf6_lsa *lsa) +{ + struct timeval now; + uint32_t ulage; + uint16_t age; + + assert(lsa); + assert(lsa->header); + + /* current time */ + monotime(&now); + + if (ntohs(lsa->header->age) >= OSPF_LSA_MAXAGE) { + /* ospf6_lsa_premature_aging () sets age to MAXAGE; when using + relative time, we cannot compare against lsa birth time, so + we catch this special case here. */ + lsa->header->age = htons(OSPF_LSA_MAXAGE); + return OSPF_LSA_MAXAGE; + } + /* calculate age */ + ulage = now.tv_sec - lsa->birth.tv_sec; + + /* if over MAXAGE, set to it */ + age = (ulage > OSPF_LSA_MAXAGE ? OSPF_LSA_MAXAGE : ulage); + + lsa->header->age = htons(age); + return age; +} + +/* update age field of LSA header with adding InfTransDelay */ +void ospf6_lsa_age_update_to_send(struct ospf6_lsa *lsa, uint32_t transdelay) +{ + unsigned short age; + + age = ospf6_lsa_age_current(lsa) + transdelay; + if (age > OSPF_LSA_MAXAGE) + age = OSPF_LSA_MAXAGE; + lsa->header->age = htons(age); +} + +void ospf6_lsa_premature_aging(struct ospf6_lsa *lsa) +{ + /* log */ + if (IS_OSPF6_DEBUG_LSA_TYPE(lsa->header->type)) + zlog_debug("LSA: Premature aging: %s", lsa->name); + + EVENT_OFF(lsa->expire); + EVENT_OFF(lsa->refresh); + + /* + * We clear the LSA from the neighbor retx lists now because it + * will not get deleted later. Essentially, changing the age to + * MaxAge will prevent this LSA from being matched with its + * existing entries in the retx list thereby causing those entries + * to be silently replaced with its MaxAged version, but with ever + * increasing retx count causing this LSA to remain forever and + * for the MaxAge remover thread to be called forever too. + * + * The reason the previous entry silently disappears is that when + * entry is added to a neighbor's retx list, it replaces the existing + * entry. But since the ospf6_lsdb_add() routine is generic and not + * aware + * of the special semantics of retx count, the retx count is not + * decremented when its replaced. Attempting to add the incr and decr + * retx count routines as the hook_add and hook_remove for the retx + * lists + * have a problem because the hook_remove routine is called for MaxAge + * entries (as will be the case in a traditional LSDB, unlike in this + * case + * where an LSDB is used as an efficient tree structure to store all + * kinds + * of data) that are added instead of calling the hook_add routine. + */ + + ospf6_flood_clear(lsa); + + lsa->header->age = htons(OSPF_LSA_MAXAGE); + event_execute(master, ospf6_lsa_expire, lsa, 0, NULL); +} + +/* check which is more recent. if a is more recent, return -1; + if the same, return 0; otherwise(b is more recent), return 1 */ +int ospf6_lsa_compare(struct ospf6_lsa *a, struct ospf6_lsa *b) +{ + int32_t seqnuma, seqnumb; + uint16_t cksuma, cksumb; + uint16_t agea, ageb; + + assert(a && a->header); + assert(b && b->header); + assert(OSPF6_LSA_IS_SAME(a, b)); + + seqnuma = (int32_t)ntohl(a->header->seqnum); + seqnumb = (int32_t)ntohl(b->header->seqnum); + + /* compare by sequence number */ + if (seqnuma > seqnumb) + return -1; + if (seqnuma < seqnumb) + return 1; + + /* Checksum */ + cksuma = ntohs(a->header->checksum); + cksumb = ntohs(b->header->checksum); + if (cksuma > cksumb) + return -1; + if (cksuma < cksumb) + return 0; + + /* Update Age */ + agea = ospf6_lsa_age_current(a); + ageb = ospf6_lsa_age_current(b); + + /* MaxAge check */ + if (agea == OSPF_LSA_MAXAGE && ageb != OSPF_LSA_MAXAGE) + return -1; + else if (agea != OSPF_LSA_MAXAGE && ageb == OSPF_LSA_MAXAGE) + return 1; + + /* Age check */ + if (agea > ageb && agea - ageb >= OSPF_LSA_MAXAGE_DIFF) + return 1; + else if (agea < ageb && ageb - agea >= OSPF_LSA_MAXAGE_DIFF) + return -1; + + /* neither recent */ + return 0; +} + +char *ospf6_lsa_printbuf(struct ospf6_lsa *lsa, char *buf, int size) +{ + char id[16], adv_router[16]; + inet_ntop(AF_INET, &lsa->header->id, id, sizeof(id)); + inet_ntop(AF_INET, &lsa->header->adv_router, adv_router, + sizeof(adv_router)); + snprintf(buf, size, "[%s Id:%s Adv:%s]", + ospf6_lstype_name(lsa->header->type), id, adv_router); + return buf; +} + +void ospf6_lsa_header_print_raw(struct ospf6_lsa_header *header) +{ + char id[16], adv_router[16]; + inet_ntop(AF_INET, &header->id, id, sizeof(id)); + inet_ntop(AF_INET, &header->adv_router, adv_router, sizeof(adv_router)); + zlog_debug(" [%s Id:%s Adv:%s]", ospf6_lstype_name(header->type), id, + adv_router); + zlog_debug(" Age: %4hu SeqNum: %#08lx Cksum: %04hx Len: %d", + ntohs(header->age), (unsigned long)ntohl(header->seqnum), + ntohs(header->checksum), ntohs(header->length)); +} + +void ospf6_lsa_header_print(struct ospf6_lsa *lsa) +{ + ospf6_lsa_age_current(lsa); + ospf6_lsa_header_print_raw(lsa->header); +} + +void ospf6_lsa_show_summary_header(struct vty *vty) +{ + vty_out(vty, "%-4s %-15s%-15s%4s %8s %30s\n", "Type", "LSId", + "AdvRouter", "Age", "SeqNum", "Payload"); +} + +void ospf6_lsa_show_summary(struct vty *vty, struct ospf6_lsa *lsa, + json_object *json_array, bool use_json) +{ + char adv_router[16], id[16]; + int type; + const struct ospf6_lsa_handler *handler; + char buf[64]; + int cnt = 0; + json_object *json_obj = NULL; + + assert(lsa); + assert(lsa->header); + + inet_ntop(AF_INET, &lsa->header->id, id, sizeof(id)); + inet_ntop(AF_INET, &lsa->header->adv_router, adv_router, + sizeof(adv_router)); + + type = ntohs(lsa->header->type); + handler = ospf6_get_lsa_handler(lsa->header->type); + + if (use_json) + json_obj = json_object_new_object(); + + switch (type) { + case OSPF6_LSTYPE_INTER_PREFIX: + case OSPF6_LSTYPE_INTER_ROUTER: + case OSPF6_LSTYPE_AS_EXTERNAL: + case OSPF6_LSTYPE_TYPE_7: + if (use_json) { + json_object_string_add( + json_obj, "type", + ospf6_lstype_short_name(lsa->header->type)); + json_object_string_add(json_obj, "lsId", id); + json_object_string_add(json_obj, "advRouter", + adv_router); + json_object_int_add(json_obj, "age", + ospf6_lsa_age_current(lsa)); + json_object_int_add( + json_obj, "seqNum", + (unsigned long)ntohl(lsa->header->seqnum)); + json_object_string_add( + json_obj, "payload", + handler->lh_get_prefix_str(lsa, buf, + sizeof(buf), 0)); + json_object_array_add(json_array, json_obj); + } else + vty_out(vty, "%-4s %-15s%-15s%4hu %8lx %30s\n", + ospf6_lstype_short_name(lsa->header->type), id, + adv_router, ospf6_lsa_age_current(lsa), + (unsigned long)ntohl(lsa->header->seqnum), + handler->lh_get_prefix_str(lsa, buf, + sizeof(buf), 0)); + break; + case OSPF6_LSTYPE_ROUTER: + case OSPF6_LSTYPE_NETWORK: + case OSPF6_LSTYPE_GROUP_MEMBERSHIP: + case OSPF6_LSTYPE_LINK: + case OSPF6_LSTYPE_INTRA_PREFIX: + while (handler->lh_get_prefix_str(lsa, buf, sizeof(buf), cnt) + != NULL) { + if (use_json) { + json_object_string_add( + json_obj, "type", + ospf6_lstype_short_name( + lsa->header->type)); + json_object_string_add(json_obj, "lsId", id); + json_object_string_add(json_obj, "advRouter", + adv_router); + json_object_int_add(json_obj, "age", + ospf6_lsa_age_current(lsa)); + json_object_int_add( + json_obj, "seqNum", + (unsigned long)ntohl( + lsa->header->seqnum)); + json_object_string_add(json_obj, "payload", + buf); + json_object_array_add(json_array, json_obj); + json_obj = json_object_new_object(); + } else + vty_out(vty, "%-4s %-15s%-15s%4hu %8lx %30s\n", + ospf6_lstype_short_name( + lsa->header->type), + id, adv_router, + ospf6_lsa_age_current(lsa), + (unsigned long)ntohl( + lsa->header->seqnum), + buf); + cnt++; + } + if (use_json) + json_object_free(json_obj); + break; + default: + if (use_json) { + json_object_string_add( + json_obj, "type", + ospf6_lstype_short_name(lsa->header->type)); + json_object_string_add(json_obj, "lsId", id); + json_object_string_add(json_obj, "advRouter", + adv_router); + json_object_int_add(json_obj, "age", + ospf6_lsa_age_current(lsa)); + json_object_int_add( + json_obj, "seqNum", + (unsigned long)ntohl(lsa->header->seqnum)); + json_object_array_add(json_array, json_obj); + } else + vty_out(vty, "%-4s %-15s%-15s%4hu %8lx\n", + ospf6_lstype_short_name(lsa->header->type), id, + adv_router, ospf6_lsa_age_current(lsa), + (unsigned long)ntohl(lsa->header->seqnum)); + break; + } +} + +void ospf6_lsa_show_dump(struct vty *vty, struct ospf6_lsa *lsa, + json_object *json_array, bool use_json) +{ + uint8_t *start = NULL; + uint8_t *end = NULL; + uint8_t *current = NULL; + char byte[4]; + char *header_str = NULL; + char adv_router[INET6_ADDRSTRLEN]; + char id[INET6_ADDRSTRLEN]; + json_object *json = NULL; + + start = (uint8_t *)lsa->header; + end = (uint8_t *)lsa->header + ntohs(lsa->header->length); + + if (use_json) { + json = json_object_new_object(); + size_t header_str_sz = (2 * (end - start)) + 1; + + header_str = XMALLOC(MTYPE_OSPF6_LSA_HEADER, header_str_sz); + + inet_ntop(AF_INET, &lsa->header->id, id, sizeof(id)); + inet_ntop(AF_INET, &lsa->header->adv_router, adv_router, + sizeof(adv_router)); + + frrstr_hex(header_str, header_str_sz, start, end - start); + + json_object_string_add(json, "linkStateId", id); + json_object_string_add(json, "advertisingRouter", adv_router); + json_object_string_add(json, "header", header_str); + json_object_array_add(json_array, json); + + XFREE(MTYPE_OSPF6_LSA_HEADER, header_str); + } else { + vty_out(vty, "\n%s:\n", lsa->name); + + for (current = start; current < end; current++) { + if ((current - start) % 16 == 0) + vty_out(vty, "\n "); + else if ((current - start) % 4 == 0) + vty_out(vty, " "); + + snprintf(byte, sizeof(byte), "%02x", *current); + vty_out(vty, "%s", byte); + } + + vty_out(vty, "\n\n"); + } + + return; +} + +void ospf6_lsa_show_internal(struct vty *vty, struct ospf6_lsa *lsa, + json_object *json_array, bool use_json) +{ + char adv_router[64], id[64]; + json_object *json_obj; + + assert(lsa && lsa->header); + + inet_ntop(AF_INET, &lsa->header->id, id, sizeof(id)); + inet_ntop(AF_INET, &lsa->header->adv_router, adv_router, + sizeof(adv_router)); + + if (use_json) { + json_obj = json_object_new_object(); + json_object_int_add(json_obj, "age", + ospf6_lsa_age_current(lsa)); + json_object_string_add(json_obj, "type", + ospf6_lstype_name(lsa->header->type)); + json_object_string_add(json_obj, "linkStateId", id); + json_object_string_add(json_obj, "advertisingRouter", + adv_router); + json_object_int_add(json_obj, "lsSequenceNumber", + (unsigned long)ntohl(lsa->header->seqnum)); + json_object_int_add(json_obj, "checksum", + ntohs(lsa->header->checksum)); + json_object_int_add(json_obj, "length", + ntohs(lsa->header->length)); + json_object_int_add(json_obj, "flag", lsa->flag); + json_object_int_add(json_obj, "lock", lsa->lock); + json_object_int_add(json_obj, "reTxCount", lsa->retrans_count); + + /* Threads Data not added */ + json_object_array_add(json_array, json_obj); + } else { + vty_out(vty, "\n"); + vty_out(vty, "Age: %4hu Type: %s\n", ospf6_lsa_age_current(lsa), + ospf6_lstype_name(lsa->header->type)); + vty_out(vty, "Link State ID: %s\n", id); + vty_out(vty, "Advertising Router: %s\n", adv_router); + vty_out(vty, "LS Sequence Number: %#010lx\n", + (unsigned long)ntohl(lsa->header->seqnum)); + vty_out(vty, "CheckSum: %#06hx Length: %hu\n", + ntohs(lsa->header->checksum), + ntohs(lsa->header->length)); + vty_out(vty, "Flag: %x \n", lsa->flag); + vty_out(vty, "Lock: %d \n", lsa->lock); + vty_out(vty, "ReTx Count: %d\n", lsa->retrans_count); + vty_out(vty, "Threads: Expire: %p, Refresh: %p\n", lsa->expire, + lsa->refresh); + vty_out(vty, "\n"); + } + return; +} + +void ospf6_lsa_show(struct vty *vty, struct ospf6_lsa *lsa, + json_object *json_array, bool use_json) +{ + char adv_router[64], id[64]; + const struct ospf6_lsa_handler *handler; + struct timeval now, res; + char duration[64]; + json_object *json_obj = NULL; + + assert(lsa && lsa->header); + + inet_ntop(AF_INET, &lsa->header->id, id, sizeof(id)); + inet_ntop(AF_INET, &lsa->header->adv_router, adv_router, + sizeof(adv_router)); + + monotime(&now); + timersub(&now, &lsa->installed, &res); + timerstring(&res, duration, sizeof(duration)); + if (use_json) { + json_obj = json_object_new_object(); + json_object_int_add(json_obj, "age", + ospf6_lsa_age_current(lsa)); + json_object_string_add(json_obj, "type", + ospf6_lstype_name(lsa->header->type)); + json_object_string_add(json_obj, "linkStateId", id); + json_object_string_add(json_obj, "advertisingRouter", + adv_router); + json_object_int_add(json_obj, "lsSequenceNumber", + (unsigned long)ntohl(lsa->header->seqnum)); + json_object_int_add(json_obj, "checksum", + ntohs(lsa->header->checksum)); + json_object_int_add(json_obj, "length", + ntohs(lsa->header->length)); + json_object_string_add(json_obj, "duration", duration); + } else { + vty_out(vty, "Age: %4hu Type: %s\n", ospf6_lsa_age_current(lsa), + ospf6_lstype_name(lsa->header->type)); + vty_out(vty, "Link State ID: %s\n", id); + vty_out(vty, "Advertising Router: %s\n", adv_router); + vty_out(vty, "LS Sequence Number: %#010lx\n", + (unsigned long)ntohl(lsa->header->seqnum)); + vty_out(vty, "CheckSum: %#06hx Length: %hu\n", + ntohs(lsa->header->checksum), + ntohs(lsa->header->length)); + vty_out(vty, "Duration: %s\n", duration); + } + + handler = ospf6_get_lsa_handler(lsa->header->type); + + if (handler->lh_show != NULL) + handler->lh_show(vty, lsa, json_obj, use_json); + else { + assert(unknown_handler.lh_show != NULL); + unknown_handler.lh_show(vty, lsa, json_obj, use_json); + } + + if (use_json) + json_object_array_add(json_array, json_obj); + else + vty_out(vty, "\n"); +} + +struct ospf6_lsa *ospf6_lsa_alloc(size_t lsa_length) +{ + struct ospf6_lsa *lsa; + + lsa = XCALLOC(MTYPE_OSPF6_LSA, sizeof(struct ospf6_lsa)); + lsa->header = XMALLOC(MTYPE_OSPF6_LSA_HEADER, lsa_length); + + return lsa; +} + +/* OSPFv3 LSA creation/deletion function */ +struct ospf6_lsa *ospf6_lsa_create(struct ospf6_lsa_header *header) +{ + struct ospf6_lsa *lsa = NULL; + uint16_t lsa_size = 0; + + /* size of the entire LSA */ + lsa_size = ntohs(header->length); /* XXX vulnerable */ + + lsa = ospf6_lsa_alloc(lsa_size); + + /* copy LSA from original header */ + memcpy(lsa->header, header, lsa_size); + + /* dump string */ + ospf6_lsa_printbuf(lsa, lsa->name, sizeof(lsa->name)); + + /* calculate birth of this lsa */ + ospf6_lsa_age_set(lsa); + + return lsa; +} + +struct ospf6_lsa *ospf6_lsa_create_headeronly(struct ospf6_lsa_header *header) +{ + struct ospf6_lsa *lsa = NULL; + + lsa = ospf6_lsa_alloc(sizeof(struct ospf6_lsa_header)); + + memcpy(lsa->header, header, sizeof(struct ospf6_lsa_header)); + + SET_FLAG(lsa->flag, OSPF6_LSA_HEADERONLY); + + /* dump string */ + ospf6_lsa_printbuf(lsa, lsa->name, sizeof(lsa->name)); + + /* calculate birth of this lsa */ + ospf6_lsa_age_set(lsa); + + return lsa; +} + +void ospf6_lsa_delete(struct ospf6_lsa *lsa) +{ + assert(lsa->lock == 0); + + /* cancel threads */ + EVENT_OFF(lsa->expire); + EVENT_OFF(lsa->refresh); + + /* do free */ + XFREE(MTYPE_OSPF6_LSA_HEADER, lsa->header); + XFREE(MTYPE_OSPF6_LSA, lsa); +} + +struct ospf6_lsa *ospf6_lsa_copy(struct ospf6_lsa *lsa) +{ + struct ospf6_lsa *copy = NULL; + + ospf6_lsa_age_current(lsa); + if (CHECK_FLAG(lsa->flag, OSPF6_LSA_HEADERONLY)) + copy = ospf6_lsa_create_headeronly(lsa->header); + else + copy = ospf6_lsa_create(lsa->header); + assert(copy->lock == 0); + + copy->birth = lsa->birth; + copy->originated = lsa->originated; + copy->received = lsa->received; + copy->installed = lsa->installed; + copy->lsdb = lsa->lsdb; + copy->rn = NULL; + + return copy; +} + +/* increment reference counter of struct ospf6_lsa */ +struct ospf6_lsa *ospf6_lsa_lock(struct ospf6_lsa *lsa) +{ + lsa->lock++; + return lsa; +} + +/* decrement reference counter of struct ospf6_lsa */ +void ospf6_lsa_unlock(struct ospf6_lsa **lsa) +{ + /* decrement reference counter */ + assert((*lsa)->lock > 0); + (*lsa)->lock--; + + if ((*lsa)->lock != 0) + return; + + ospf6_lsa_delete(*lsa); + *lsa = NULL; +} + + +/* ospf6 lsa expiry */ +void ospf6_lsa_expire(struct event *thread) +{ + struct ospf6_lsa *lsa; + struct ospf6 *ospf6; + + lsa = (struct ospf6_lsa *)EVENT_ARG(thread); + + assert(lsa && lsa->header); + assert(OSPF6_LSA_IS_MAXAGE(lsa)); + assert(!lsa->refresh); + + lsa->expire = (struct event *)NULL; + + if (IS_OSPF6_DEBUG_LSA_TYPE(lsa->header->type)) { + zlog_debug("LSA Expire:"); + ospf6_lsa_header_print(lsa); + } + + if (CHECK_FLAG(lsa->flag, OSPF6_LSA_HEADERONLY)) + return; /* dbexchange will do something ... */ + ospf6 = ospf6_get_by_lsdb(lsa); + assert(ospf6); + + /* reinstall lsa */ + ospf6_install_lsa(lsa); + + /* reflood lsa */ + ospf6_flood(NULL, lsa); + + /* schedule maxage remover */ + ospf6_maxage_remove(ospf6); +} + +void ospf6_lsa_refresh(struct event *thread) +{ + struct ospf6_lsa *old, *self, *new; + struct ospf6_lsdb *lsdb_self; + + old = (struct ospf6_lsa *)EVENT_ARG(thread); + assert(old && old->header); + + old->refresh = (struct event *)NULL; + + lsdb_self = ospf6_get_scoped_lsdb_self(old); + self = ospf6_lsdb_lookup(old->header->type, old->header->id, + old->header->adv_router, lsdb_self); + if (self == NULL) { + if (IS_OSPF6_DEBUG_LSA_TYPE(old->header->type)) + zlog_debug("Refresh: could not find self LSA, flush %s", + old->name); + ospf6_lsa_premature_aging(old); + return; + } + + /* Reset age, increment LS sequence number. */ + self->header->age = htons(0); + self->header->seqnum = + ospf6_new_ls_seqnum(self->header->type, self->header->id, + self->header->adv_router, old->lsdb); + ospf6_lsa_checksum(self->header); + + new = ospf6_lsa_create(self->header); + new->lsdb = old->lsdb; + event_add_timer(master, ospf6_lsa_refresh, new, OSPF_LS_REFRESH_TIME, + &new->refresh); + + /* store it in the LSDB for self-originated LSAs */ + ospf6_lsdb_add(ospf6_lsa_copy(new), lsdb_self); + + if (IS_OSPF6_DEBUG_LSA_TYPE(new->header->type)) { + zlog_debug("LSA Refresh:"); + ospf6_lsa_header_print(new); + } + + ospf6_install_lsa(new); + ospf6_flood(NULL, new); +} + +void ospf6_flush_self_originated_lsas_now(struct ospf6 *ospf6) +{ + struct listnode *node, *nnode; + struct ospf6_area *oa; + struct ospf6_lsa *lsa; + const struct route_node *end = NULL; + uint32_t type, adv_router; + struct ospf6_interface *oi; + + ospf6->inst_shutdown = 1; + + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, oa)) { + end = ospf6_lsdb_head(oa->lsdb_self, 0, 0, ospf6->router_id, + &lsa); + while (lsa) { + /* RFC 2328 (14.1): Set MAXAGE */ + lsa->header->age = htons(OSPF_LSA_MAXAGE); + /* Flood MAXAGE LSA*/ + ospf6_flood(NULL, lsa); + + lsa = ospf6_lsdb_next(end, lsa); + } + + for (ALL_LIST_ELEMENTS(oa->if_list, node, nnode, oi)) { + end = ospf6_lsdb_head(oi->lsdb_self, 0, 0, + ospf6->router_id, &lsa); + while (lsa) { + /* RFC 2328 (14.1): Set MAXAGE */ + lsa->header->age = htons(OSPF_LSA_MAXAGE); + /* Flood MAXAGE LSA*/ + ospf6_flood(NULL, lsa); + + lsa = ospf6_lsdb_next(end, lsa); + } + } + } + + type = htons(OSPF6_LSTYPE_AS_EXTERNAL); + adv_router = ospf6->router_id; + for (ALL_LSDB_TYPED_ADVRTR(ospf6->lsdb, type, adv_router, lsa)) { + /* RFC 2328 (14.1): Set MAXAGE */ + lsa->header->age = htons(OSPF_LSA_MAXAGE); + ospf6_flood(NULL, lsa); + } +} + +/* Fletcher Checksum -- Refer to RFC1008. */ + +/* All the offsets are zero-based. The offsets in the RFC1008 are + one-based. */ +unsigned short ospf6_lsa_checksum(struct ospf6_lsa_header *lsa_header) +{ + uint8_t *buffer = (uint8_t *)&lsa_header->type; + int type_offset = + buffer - (uint8_t *)&lsa_header->age; /* should be 2 */ + + /* Skip the AGE field */ + uint16_t len = ntohs(lsa_header->length) - type_offset; + + /* Checksum offset starts from "type" field, not the beginning of the + lsa_header struct. The offset is 14, rather than 16. */ + int checksum_offset = (uint8_t *)&lsa_header->checksum - buffer; + + return (unsigned short)fletcher_checksum(buffer, len, checksum_offset); +} + +int ospf6_lsa_checksum_valid(struct ospf6_lsa_header *lsa_header) +{ + uint8_t *buffer = (uint8_t *)&lsa_header->type; + int type_offset = + buffer - (uint8_t *)&lsa_header->age; /* should be 2 */ + + /* Skip the AGE field */ + uint16_t len = ntohs(lsa_header->length) - type_offset; + + return (fletcher_checksum(buffer, len, FLETCHER_CHECKSUM_VALIDATE) + == 0); +} + +void ospf6_lsa_init(void) +{ + ospf6_install_lsa_handler(&unknown_handler); +} + +void ospf6_lsa_terminate(void) +{ +} + +static char *ospf6_lsa_handler_name(const struct ospf6_lsa_handler *h) +{ + static char buf[64]; + unsigned int i; + unsigned int size = strlen(h->lh_name); + + if (!strcmp(h->lh_name, "unknown") + && h->lh_type != OSPF6_LSTYPE_UNKNOWN) { + snprintf(buf, sizeof(buf), "%#04hx", h->lh_type); + return buf; + } + + for (i = 0; i < MIN(size, sizeof(buf)); i++) { + if (!islower((unsigned char)h->lh_name[i])) + buf[i] = tolower((unsigned char)h->lh_name[i]); + else + buf[i] = h->lh_name[i]; + } + buf[size] = '\0'; + return buf; +} + +void ospf6_lsa_debug_set_all(bool val) +{ + unsigned int i; + struct ospf6_lsa_handler *handler = NULL; + + for (i = 0; i < array_size(lsa_handlers); i++) { + handler = lsa_handlers[i]; + if (handler == NULL) + continue; + if (val) + SET_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_ALL); + else + UNSET_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_ALL); + } +} + +DEFPY (debug_ospf6_lsa_all, + debug_ospf6_lsa_all_cmd, + "[no$no] debug ospf6 lsa all", + NO_STR + DEBUG_STR + OSPF6_STR + "Debug Link State Advertisements (LSAs)\n" + "Display for all types of LSAs\n") +{ + ospf6_lsa_debug_set_all(!no); + return CMD_SUCCESS; +} + +DEFPY (debug_ospf6_lsa_aggregation, + debug_ospf6_lsa_aggregation_cmd, + "[no] debug ospf6 lsa aggregation", + NO_STR + DEBUG_STR + OSPF6_STR + "Debug Link State Advertisements (LSAs)\n" + "External LSA Aggregation\n") +{ + + struct ospf6_lsa_handler *handler; + + handler = ospf6_get_lsa_handler(OSPF6_LSTYPE_AS_EXTERNAL); + if (handler == NULL) + return CMD_WARNING_CONFIG_FAILED; + + if (no) + UNSET_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_AGGR); + else + SET_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_AGGR); + + return CMD_SUCCESS; +} + +DEFUN (debug_ospf6_lsa_type, + debug_ospf6_lsa_hex_cmd, + "debug ospf6 lsa <router|network|inter-prefix|inter-router|as-external|nssa|link|intra-prefix|unknown> [<originate|examine|flooding>]", + DEBUG_STR + OSPF6_STR + "Debug Link State Advertisements (LSAs)\n" + "Display Router LSAs\n" + "Display Network LSAs\n" + "Display Inter-Area-Prefix LSAs\n" + "Display Inter-Router LSAs\n" + "Display As-External LSAs\n" + "Display NSSA LSAs\n" + "Display Link LSAs\n" + "Display Intra-Area-Prefix LSAs\n" + "Display LSAs of unknown origin\n" + "Display details of LSAs\n" + "Dump LSAs\n" + "Display LSA's internal information\n") +{ + int idx_lsa = 3; + int idx_type = 4; + unsigned int i; + struct ospf6_lsa_handler *handler = NULL; + + for (i = 0; i < array_size(lsa_handlers); i++) { + handler = lsa_handlers[i]; + if (handler == NULL) + continue; + if (strncmp(argv[idx_lsa]->arg, ospf6_lsa_handler_name(handler), + strlen(argv[idx_lsa]->arg)) + == 0) + break; + if (!strcasecmp(argv[idx_lsa]->arg, handler->lh_name)) + break; + handler = NULL; + } + + if (handler == NULL) + handler = &unknown_handler; + + if (argc == 5) { + if (strmatch(argv[idx_type]->text, "originate")) + SET_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_ORIGINATE); + else if (strmatch(argv[idx_type]->text, "examine")) + SET_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_EXAMIN); + else if (strmatch(argv[idx_type]->text, "flooding")) + SET_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_FLOOD); + } else + SET_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG); + + return CMD_SUCCESS; +} + +DEFUN (no_debug_ospf6_lsa_type, + no_debug_ospf6_lsa_hex_cmd, + "no debug ospf6 lsa <router|network|inter-prefix|inter-router|as-external|nssa|link|intra-prefix|unknown> [<originate|examine|flooding>]", + NO_STR + DEBUG_STR + OSPF6_STR + "Debug Link State Advertisements (LSAs)\n" + "Display Router LSAs\n" + "Display Network LSAs\n" + "Display Inter-Area-Prefix LSAs\n" + "Display Inter-Router LSAs\n" + "Display As-External LSAs\n" + "Display NSSA LSAs\n" + "Display Link LSAs\n" + "Display Intra-Area-Prefix LSAs\n" + "Display LSAs of unknown origin\n" + "Display details of LSAs\n" + "Dump LSAs\n" + "Display LSA's internal information\n") +{ + int idx_lsa = 4; + int idx_type = 5; + unsigned int i; + struct ospf6_lsa_handler *handler = NULL; + + for (i = 0; i < array_size(lsa_handlers); i++) { + handler = lsa_handlers[i]; + if (handler == NULL) + continue; + if (strncmp(argv[idx_lsa]->arg, ospf6_lsa_handler_name(handler), + strlen(argv[idx_lsa]->arg)) + == 0) + break; + if (!strcasecmp(argv[idx_lsa]->arg, handler->lh_name)) + break; + } + + if (handler == NULL) + return CMD_SUCCESS; + + if (argc == 6) { + if (strmatch(argv[idx_type]->text, "originate")) + UNSET_FLAG(handler->lh_debug, + OSPF6_LSA_DEBUG_ORIGINATE); + if (strmatch(argv[idx_type]->text, "examine")) + UNSET_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_EXAMIN); + if (strmatch(argv[idx_type]->text, "flooding")) + UNSET_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_FLOOD); + } else + UNSET_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG); + + return CMD_SUCCESS; +} + +void install_element_ospf6_debug_lsa(void) +{ + install_element(ENABLE_NODE, &debug_ospf6_lsa_all_cmd); + install_element(CONFIG_NODE, &debug_ospf6_lsa_all_cmd); + install_element(ENABLE_NODE, &debug_ospf6_lsa_hex_cmd); + install_element(ENABLE_NODE, &no_debug_ospf6_lsa_hex_cmd); + install_element(CONFIG_NODE, &debug_ospf6_lsa_hex_cmd); + install_element(CONFIG_NODE, &no_debug_ospf6_lsa_hex_cmd); + + install_element(ENABLE_NODE, &debug_ospf6_lsa_aggregation_cmd); + install_element(CONFIG_NODE, &debug_ospf6_lsa_aggregation_cmd); +} + +int config_write_ospf6_debug_lsa(struct vty *vty) +{ + unsigned int i; + const struct ospf6_lsa_handler *handler; + bool debug_all = true; + + for (i = 0; i < array_size(lsa_handlers); i++) { + handler = lsa_handlers[i]; + if (handler == NULL) + continue; + if (CHECK_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_ALL) + < OSPF6_LSA_DEBUG_ALL) { + debug_all = false; + break; + } + } + + if (debug_all) { + vty_out(vty, "debug ospf6 lsa all\n"); + return 0; + } + + for (i = 0; i < array_size(lsa_handlers); i++) { + handler = lsa_handlers[i]; + if (handler == NULL) + continue; + if (CHECK_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG)) + vty_out(vty, "debug ospf6 lsa %s\n", + ospf6_lsa_handler_name(handler)); + if (CHECK_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_ORIGINATE)) + vty_out(vty, "debug ospf6 lsa %s originate\n", + ospf6_lsa_handler_name(handler)); + if (CHECK_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_EXAMIN)) + vty_out(vty, "debug ospf6 lsa %s examine\n", + ospf6_lsa_handler_name(handler)); + if (CHECK_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_FLOOD)) + vty_out(vty, "debug ospf6 lsa %s flooding\n", + ospf6_lsa_handler_name(handler)); + if (CHECK_FLAG(handler->lh_debug, OSPF6_LSA_DEBUG_AGGR)) + vty_out(vty, "debug ospf6 lsa aggregation\n"); + } + + return 0; +} diff --git a/ospf6d/ospf6_lsa.h b/ospf6d/ospf6_lsa.h new file mode 100644 index 0000000..c9ac27d --- /dev/null +++ b/ospf6d/ospf6_lsa.h @@ -0,0 +1,266 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2003 Yasuhiro Ohara + */ + +#ifndef OSPF6_LSA_H +#define OSPF6_LSA_H +#include "ospf6_top.h" +#include "lib/json.h" + +/* Debug option */ +#define OSPF6_LSA_DEBUG 0x01 +#define OSPF6_LSA_DEBUG_ORIGINATE 0x02 +#define OSPF6_LSA_DEBUG_EXAMIN 0x04 +#define OSPF6_LSA_DEBUG_FLOOD 0x08 +#define OSPF6_LSA_DEBUG_ALL \ + (OSPF6_LSA_DEBUG | OSPF6_LSA_DEBUG_ORIGINATE | OSPF6_LSA_DEBUG_EXAMIN \ + | OSPF6_LSA_DEBUG_FLOOD) +#define OSPF6_LSA_DEBUG_AGGR 0x10 + +/* OSPF LSA Default metric values */ +#define DEFAULT_DEFAULT_METRIC 20 +#define DEFAULT_DEFAULT_ORIGINATE_METRIC 10 +#define DEFAULT_DEFAULT_ALWAYS_METRIC 1 +#define DEFAULT_METRIC_TYPE 2 + +#define IS_OSPF6_DEBUG_LSA(name) \ + (ospf6_lstype_debug(htons(OSPF6_LSTYPE_##name)) & OSPF6_LSA_DEBUG) +#define IS_OSPF6_DEBUG_ORIGINATE(name) \ + (ospf6_lstype_debug(htons(OSPF6_LSTYPE_##name)) \ + & OSPF6_LSA_DEBUG_ORIGINATE) +#define IS_OSPF6_DEBUG_EXAMIN(name) \ + (ospf6_lstype_debug(htons(OSPF6_LSTYPE_##name)) \ + & OSPF6_LSA_DEBUG_EXAMIN) +#define IS_OSPF6_DEBUG_LSA_TYPE(type) \ + (ospf6_lstype_debug(type) & OSPF6_LSA_DEBUG) +#define IS_OSPF6_DEBUG_ORIGINATE_TYPE(type) \ + (ospf6_lstype_debug(type) & OSPF6_LSA_DEBUG_ORIGINATE) +#define IS_OSPF6_DEBUG_EXAMIN_TYPE(type) \ + (ospf6_lstype_debug(type) & OSPF6_LSA_DEBUG_EXAMIN) +#define IS_OSPF6_DEBUG_FLOOD_TYPE(type) \ + (ospf6_lstype_debug(type) & OSPF6_LSA_DEBUG_FLOOD) +#define IS_OSPF6_DEBUG_AGGR \ + (ospf6_lstype_debug(OSPF6_LSTYPE_AS_EXTERNAL) & OSPF6_LSA_DEBUG_AGGR) \ + +/* LSA definition */ + +#define OSPF6_MAX_LSASIZE 4096 + +/* Type */ +#define OSPF6_LSTYPE_UNKNOWN 0x0000 +#define OSPF6_LSTYPE_ROUTER 0x2001 +#define OSPF6_LSTYPE_NETWORK 0x2002 +#define OSPF6_LSTYPE_INTER_PREFIX 0x2003 +#define OSPF6_LSTYPE_INTER_ROUTER 0x2004 +#define OSPF6_LSTYPE_AS_EXTERNAL 0x4005 +#define OSPF6_LSTYPE_GROUP_MEMBERSHIP 0x2006 +#define OSPF6_LSTYPE_TYPE_7 0x2007 +#define OSPF6_LSTYPE_LINK 0x0008 +#define OSPF6_LSTYPE_INTRA_PREFIX 0x2009 +#define OSPF6_LSTYPE_GRACE_LSA 0x000b +#define OSPF6_LSTYPE_SIZE 0x000c + +/* Masks for LS Type : RFC 2740 A.4.2.1 "LS type" */ +#define OSPF6_LSTYPE_UBIT_MASK 0x8000 +#define OSPF6_LSTYPE_SCOPE_MASK 0x6000 +#define OSPF6_LSTYPE_FCODE_MASK 0x1fff + +/* LSA scope */ +#define OSPF6_SCOPE_LINKLOCAL 0x0000 +#define OSPF6_SCOPE_AREA 0x2000 +#define OSPF6_SCOPE_AS 0x4000 +#define OSPF6_SCOPE_RESERVED 0x6000 + +/* XXX U-bit handling should be treated here */ +#define OSPF6_LSA_SCOPE(type) (ntohs(type) & OSPF6_LSTYPE_SCOPE_MASK) + +/* LSA Header */ +#define OSPF6_LSA_HEADER_SIZE 20U +struct ospf6_lsa_header { + uint16_t age; /* LS age */ + uint16_t type; /* LS type */ + in_addr_t id; /* Link State ID */ + in_addr_t adv_router; /* Advertising Router */ + uint32_t seqnum; /* LS sequence number */ + uint16_t checksum; /* LS checksum */ + uint16_t length; /* LSA length */ +}; + +#define OSPF6_LSA_HEADER_END(h) ((caddr_t)(h) + sizeof(struct ospf6_lsa_header)) +#define OSPF6_LSA_SIZE(h) (ntohs(((struct ospf6_lsa_header *)(h))->length)) +#define OSPF6_LSA_END(h) \ + ((caddr_t)(h) + ntohs(((struct ospf6_lsa_header *)(h))->length)) +#define OSPF6_LSA_IS_TYPE(t, L) \ + ((L)->header->type == htons(OSPF6_LSTYPE_##t) ? 1 : 0) +#define OSPF6_LSA_IS_SAME(L1, L2) \ + ((L1)->header->adv_router == (L2)->header->adv_router \ + && (L1)->header->id == (L2)->header->id \ + && (L1)->header->type == (L2)->header->type) +#define OSPF6_LSA_IS_MATCH(t, i, a, L) \ + ((L)->header->adv_router == (a) && (L)->header->id == (i) \ + && (L)->header->type == (t)) +#define OSPF6_LSA_IS_DIFFER(L1, L2) ospf6_lsa_is_differ (L1, L2) +#define OSPF6_LSA_IS_MAXAGE(L) (ospf6_lsa_age_current (L) == OSPF_LSA_MAXAGE) +#define OSPF6_LSA_IS_CHANGED(L1, L2) ospf6_lsa_is_changed (L1, L2) +#define OSPF6_LSA_IS_SEQWRAP(L) ((L)->header->seqnum == htonl(OSPF_MAX_SEQUENCE_NUMBER + 1)) + + +struct ospf6_lsa { + char name[64]; /* dump string */ + + struct route_node *rn; + + unsigned char lock; /* reference counter */ + unsigned char flag; /* special meaning (e.g. floodback) */ + + struct timeval birth; /* tv_sec when LS age 0 */ + struct timeval originated; /* used by MinLSInterval check */ + struct timeval received; /* used by MinLSArrival check */ + struct timeval installed; + + struct event *expire; + struct event *refresh; /* For self-originated LSA */ + + int retrans_count; + + struct ospf6_lsdb *lsdb; + + in_addr_t external_lsa_id; + + /* lsa instance */ + struct ospf6_lsa_header *header; + + /*For topo chg detection in HELPER role*/ + bool tobe_acknowledged; +}; + +#define OSPF6_LSA_HEADERONLY 0x01 +#define OSPF6_LSA_FLOODBACK 0x02 +#define OSPF6_LSA_DUPLICATE 0x04 +#define OSPF6_LSA_IMPLIEDACK 0x08 +#define OSPF6_LSA_UNAPPROVED 0x10 +#define OSPF6_LSA_SEQWRAPPED 0x20 +#define OSPF6_LSA_FLUSH 0x40 + +struct ospf6_lsa_handler { + uint16_t lh_type; /* host byte order */ + const char *lh_name; + const char *lh_short_name; + int (*lh_show)(struct vty *, struct ospf6_lsa *, json_object *json_obj, + bool use_json); + char *(*lh_get_prefix_str)(struct ospf6_lsa *, char *buf, int buflen, + int pos); + + uint8_t lh_debug; +}; + +#define OSPF6_LSA_IS_KNOWN(t) \ + (ospf6_get_lsa_handler(t)->lh_type != OSPF6_LSTYPE_UNKNOWN ? 1 : 0) + +/* Macro for LSA Origination */ +/* addr is (struct prefix *) */ +#define CONTINUE_IF_ADDRESS_LINKLOCAL(debug, addr) \ + if (IN6_IS_ADDR_LINKLOCAL(&(addr)->u.prefix6)) { \ + if (debug) \ + zlog_debug("Filter out Linklocal: %pFX", addr); \ + continue; \ + } + +#define CONTINUE_IF_ADDRESS_UNSPECIFIED(debug, addr) \ + if (IN6_IS_ADDR_UNSPECIFIED(&(addr)->u.prefix6)) { \ + if (debug) \ + zlog_debug("Filter out Unspecified: %pFX", addr); \ + continue; \ + } + +#define CONTINUE_IF_ADDRESS_LOOPBACK(debug, addr) \ + if (IN6_IS_ADDR_LOOPBACK(&(addr)->u.prefix6)) { \ + if (debug) \ + zlog_debug("Filter out Loopback: %pFX", addr); \ + continue; \ + } + +#define CONTINUE_IF_ADDRESS_V4COMPAT(debug, addr) \ + if (IN6_IS_ADDR_V4COMPAT(&(addr)->u.prefix6)) { \ + if (debug) \ + zlog_debug("Filter out V4Compat: %pFX", addr); \ + continue; \ + } + +#define CONTINUE_IF_ADDRESS_V4MAPPED(debug, addr) \ + if (IN6_IS_ADDR_V4MAPPED(&(addr)->u.prefix6)) { \ + if (debug) \ + zlog_debug("Filter out V4Mapped: %pFX", addr); \ + continue; \ + } + +#define CHECK_LSA_TOPO_CHG_ELIGIBLE(type) \ + ((type == OSPF6_LSTYPE_ROUTER) \ + || (type == OSPF6_LSTYPE_NETWORK) \ + || (type == OSPF6_LSTYPE_INTER_PREFIX) \ + || (type == OSPF6_LSTYPE_INTER_ROUTER) \ + || (type == OSPF6_LSTYPE_AS_EXTERNAL) \ + || (type == OSPF6_LSTYPE_TYPE_7) \ + || (type == OSPF6_LSTYPE_INTRA_PREFIX)) + +/* Function Prototypes */ +extern const char *ospf6_lstype_name(uint16_t type); +extern const char *ospf6_lstype_short_name(uint16_t type); +extern uint8_t ospf6_lstype_debug(uint16_t type); +extern int metric_type(struct ospf6 *ospf6, int type, uint8_t instance); +extern int metric_value(struct ospf6 *ospf6, int type, uint8_t instance); +extern int ospf6_lsa_is_differ(struct ospf6_lsa *lsa1, struct ospf6_lsa *lsa2); +extern int ospf6_lsa_is_changed(struct ospf6_lsa *lsa1, struct ospf6_lsa *lsa2); +extern uint16_t ospf6_lsa_age_current(struct ospf6_lsa *lsa); +extern void ospf6_lsa_age_update_to_send(struct ospf6_lsa *lsa, + uint32_t transdelay); +extern void ospf6_lsa_premature_aging(struct ospf6_lsa *lsa); +extern int ospf6_lsa_compare(struct ospf6_lsa *lsa1, struct ospf6_lsa *lsa2); + +extern char *ospf6_lsa_printbuf(struct ospf6_lsa *lsa, char *buf, int size); +extern void ospf6_lsa_header_print_raw(struct ospf6_lsa_header *header); +extern void ospf6_lsa_header_print(struct ospf6_lsa *lsa); +extern void ospf6_lsa_show_summary_header(struct vty *vty); +extern void ospf6_lsa_show_summary(struct vty *vty, struct ospf6_lsa *lsa, + json_object *json, bool use_json); +extern void ospf6_lsa_show_dump(struct vty *vty, struct ospf6_lsa *lsa, + json_object *json, bool use_json); +extern void ospf6_lsa_show_internal(struct vty *vty, struct ospf6_lsa *lsa, + json_object *json, bool use_json); +extern void ospf6_lsa_show(struct vty *vty, struct ospf6_lsa *lsa, + json_object *json, bool use_json); + +extern struct ospf6_lsa *ospf6_lsa_alloc(size_t lsa_length); +extern struct ospf6_lsa *ospf6_lsa_create(struct ospf6_lsa_header *header); +extern struct ospf6_lsa * +ospf6_lsa_create_headeronly(struct ospf6_lsa_header *header); +extern void ospf6_lsa_delete(struct ospf6_lsa *lsa); +extern struct ospf6_lsa *ospf6_lsa_copy(struct ospf6_lsa *lsa); + +extern struct ospf6_lsa *ospf6_lsa_lock(struct ospf6_lsa *lsa); +extern void ospf6_lsa_unlock(struct ospf6_lsa **lsa); + +extern void ospf6_lsa_expire(struct event *thread); +extern void ospf6_lsa_refresh(struct event *thread); + +extern unsigned short ospf6_lsa_checksum(struct ospf6_lsa_header *lsah); +extern int ospf6_lsa_checksum_valid(struct ospf6_lsa_header *lsah); +extern int ospf6_lsa_prohibited_duration(uint16_t type, uint32_t id, + uint32_t adv_router, void *scope); + +extern void ospf6_install_lsa_handler(struct ospf6_lsa_handler *handler); +extern struct ospf6_lsa_handler *ospf6_get_lsa_handler(uint16_t type); +extern void ospf6_lsa_debug_set_all(bool val); + +extern void ospf6_lsa_init(void); +extern void ospf6_lsa_terminate(void); + +extern int config_write_ospf6_debug_lsa(struct vty *vty); +extern void install_element_ospf6_debug_lsa(void); +extern void ospf6_lsa_age_set(struct ospf6_lsa *lsa); +extern void ospf6_flush_self_originated_lsas_now(struct ospf6 *ospf6); +extern struct ospf6 *ospf6_get_by_lsdb(struct ospf6_lsa *lsa); +struct ospf6_lsa *ospf6_find_external_lsa(struct ospf6 *ospf6, + struct prefix *p); +#endif /* OSPF6_LSA_H */ diff --git a/ospf6d/ospf6_lsdb.c b/ospf6d/ospf6_lsdb.c new file mode 100644 index 0000000..c9cbdf8 --- /dev/null +++ b/ospf6d/ospf6_lsdb.c @@ -0,0 +1,451 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2003 Yasuhiro Ohara + */ + +#include <zebra.h> + +#include "memory.h" +#include "log.h" +#include "command.h" +#include "prefix.h" +#include "table.h" +#include "vty.h" + +#include "ospf6_proto.h" +#include "ospf6_area.h" +#include "ospf6_lsa.h" +#include "ospf6_lsdb.h" +#include "ospf6_abr.h" +#include "ospf6_asbr.h" +#include "ospf6_route.h" +#include "ospf6d.h" +#include "bitfield.h" + +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_LSDB, "OSPF6 LSA database"); + +struct ospf6_lsdb *ospf6_lsdb_create(void *data) +{ + struct ospf6_lsdb *lsdb; + + lsdb = XCALLOC(MTYPE_OSPF6_LSDB, sizeof(struct ospf6_lsdb)); + memset(lsdb, 0, sizeof(struct ospf6_lsdb)); + + lsdb->data = data; + lsdb->table = route_table_init(); + return lsdb; +} + +void ospf6_lsdb_delete(struct ospf6_lsdb *lsdb) +{ + if (lsdb != NULL) { + ospf6_lsdb_remove_all(lsdb); + route_table_finish(lsdb->table); + XFREE(MTYPE_OSPF6_LSDB, lsdb); + } +} + +static void ospf6_lsdb_set_key(struct prefix_ipv6 *key, const void *value, + int len) +{ + assert(key->prefixlen % 8 == 0); + + memcpy((caddr_t)&key->prefix + key->prefixlen / 8, (caddr_t)value, len); + key->family = AF_INET6; + key->prefixlen += len * 8; +} + +#ifdef DEBUG +static void _lsdb_count_assert(struct ospf6_lsdb *lsdb) +{ + struct ospf6_lsa *debug, *debugnext; + unsigned int num = 0; + for (ALL_LSDB(lsdb, debug, debugnext)) + num++; + + if (num == lsdb->count) + return; + + zlog_debug("PANIC !! lsdb[%p]->count = %d, real = %d", lsdb, + lsdb->count, num); + for (ALL_LSDB(lsdb, debug, debugnext)) + zlog_debug("%s lsdb[%p]", debug->name, debug->lsdb); + zlog_debug("DUMP END"); + + assert(num == lsdb->count); +} +#define ospf6_lsdb_count_assert(t) (_lsdb_count_assert (t)) +#else /*DEBUG*/ +#define ospf6_lsdb_count_assert(t) ((void) 0) +#endif /*DEBUG*/ + +static inline void ospf6_lsdb_stats_update(struct ospf6_lsa *lsa, + struct ospf6_lsdb *lsdb, int count) +{ + uint16_t stat = ntohs(lsa->header->type) & OSPF6_LSTYPE_FCODE_MASK; + + if (stat >= OSPF6_LSTYPE_SIZE) + stat = OSPF6_LSTYPE_UNKNOWN; + lsdb->stats[stat] += count; +} + +void ospf6_lsdb_add(struct ospf6_lsa *lsa, struct ospf6_lsdb *lsdb) +{ + struct prefix_ipv6 key; + struct route_node *current; + struct ospf6_lsa *old = NULL; + + memset(&key, 0, sizeof(key)); + ospf6_lsdb_set_key(&key, &lsa->header->type, sizeof(lsa->header->type)); + ospf6_lsdb_set_key(&key, &lsa->header->adv_router, + sizeof(lsa->header->adv_router)); + ospf6_lsdb_set_key(&key, &lsa->header->id, sizeof(lsa->header->id)); + + current = route_node_get(lsdb->table, (struct prefix *)&key); + old = current->info; + current->info = lsa; + lsa->rn = current; + ospf6_lsa_lock(lsa); + + if (!old) { + lsdb->count++; + ospf6_lsdb_stats_update(lsa, lsdb, 1); + + if (OSPF6_LSA_IS_MAXAGE(lsa)) { + if (lsdb->hook_remove) + (*lsdb->hook_remove)(lsa); + } else { + if (lsdb->hook_add) + (*lsdb->hook_add)(lsa); + } + } else { + lsa->retrans_count = old->retrans_count; + + if (OSPF6_LSA_IS_CHANGED(old, lsa)) { + if (OSPF6_LSA_IS_MAXAGE(lsa)) { + if (lsdb->hook_remove) { + (*lsdb->hook_remove)(old); + (*lsdb->hook_remove)(lsa); + } + } else if (OSPF6_LSA_IS_MAXAGE(old)) { + if (lsdb->hook_add) + (*lsdb->hook_add)(lsa); + } else { + if (lsdb->hook_remove) + (*lsdb->hook_remove)(old); + if (lsdb->hook_add) + (*lsdb->hook_add)(lsa); + } + } + /* to free the lookup lock in node get*/ + route_unlock_node(current); + ospf6_lsa_unlock(&old); + } + + ospf6_lsdb_count_assert(lsdb); +} + +void ospf6_lsdb_remove(struct ospf6_lsa *lsa, struct ospf6_lsdb *lsdb) +{ + struct route_node *node; + struct prefix_ipv6 key; + + memset(&key, 0, sizeof(key)); + ospf6_lsdb_set_key(&key, &lsa->header->type, sizeof(lsa->header->type)); + ospf6_lsdb_set_key(&key, &lsa->header->adv_router, + sizeof(lsa->header->adv_router)); + ospf6_lsdb_set_key(&key, &lsa->header->id, sizeof(lsa->header->id)); + + node = route_node_lookup(lsdb->table, (struct prefix *)&key); + assert(node && node->info == lsa); + + node->info = NULL; + lsdb->count--; + ospf6_lsdb_stats_update(lsa, lsdb, -1); + + if (lsdb->hook_remove) + (*lsdb->hook_remove)(lsa); + + route_unlock_node(node); /* to free the lookup lock */ + route_unlock_node(node); /* to free the original lock */ + ospf6_lsa_unlock(&lsa); + + ospf6_lsdb_count_assert(lsdb); +} + +struct ospf6_lsa *ospf6_lsdb_lookup(uint16_t type, uint32_t id, + uint32_t adv_router, + struct ospf6_lsdb *lsdb) +{ + struct route_node *node; + struct prefix_ipv6 key; + + if (lsdb == NULL) + return NULL; + + memset(&key, 0, sizeof(key)); + ospf6_lsdb_set_key(&key, &type, sizeof(type)); + ospf6_lsdb_set_key(&key, &adv_router, sizeof(adv_router)); + ospf6_lsdb_set_key(&key, &id, sizeof(id)); + + node = route_node_lookup(lsdb->table, (struct prefix *)&key); + if (node == NULL || node->info == NULL) + return NULL; + + route_unlock_node(node); + return (struct ospf6_lsa *)node->info; +} + +struct ospf6_lsa *ospf6_find_external_lsa(struct ospf6 *ospf6, struct prefix *p) +{ + struct ospf6_route *match; + struct ospf6_lsa *lsa; + struct ospf6_external_info *info; + + match = ospf6_route_lookup(p, ospf6->external_table); + if (match == NULL) { + if (IS_OSPF6_DEBUG_ASBR) + zlog_debug("No such route %pFX to withdraw", p); + + return NULL; + } + + info = match->route_option; + assert(info); + + lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL), + htonl(info->id), ospf6->router_id, ospf6->lsdb); + return lsa; +} + +struct ospf6_lsa *ospf6_find_inter_prefix_lsa(struct ospf6 *ospf6, + struct ospf6_area *area, + struct prefix *p) +{ + struct ospf6_lsa *lsa; + uint16_t type = htons(OSPF6_LSTYPE_INTER_PREFIX); + + for (ALL_LSDB_TYPED_ADVRTR(area->lsdb, type, ospf6->router_id, lsa)) { + struct ospf6_inter_prefix_lsa *prefix_lsa; + struct prefix prefix; + + 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 (prefix_same(p, &prefix)) { + ospf6_lsa_unlock(&lsa); + return lsa; + } + } + + return NULL; +} + +struct ospf6_lsa *ospf6_lsdb_lookup_next(uint16_t type, uint32_t id, + uint32_t adv_router, + struct ospf6_lsdb *lsdb) +{ + struct route_node *node; + struct prefix_ipv6 key; + + if (lsdb == NULL) + return NULL; + + memset(&key, 0, sizeof(key)); + ospf6_lsdb_set_key(&key, &type, sizeof(type)); + ospf6_lsdb_set_key(&key, &adv_router, sizeof(adv_router)); + ospf6_lsdb_set_key(&key, &id, sizeof(id)); + + zlog_debug("lsdb_lookup_next: key: %pFX", &key); + + node = route_table_get_next(lsdb->table, &key); + + /* skip to real existing entry */ + while (node && node->info == NULL) + node = route_next(node); + + if (!node) + return NULL; + + route_unlock_node(node); + if (!node->info) + return NULL; + + return (struct ospf6_lsa *)node->info; +} + +const struct route_node *ospf6_lsdb_head(struct ospf6_lsdb *lsdb, int argmode, + uint16_t type, uint32_t adv_router, + struct ospf6_lsa **lsa) +{ + struct route_node *node, *end; + + *lsa = NULL; + + if (argmode > 0) { + struct prefix_ipv6 key = {.family = AF_INET6, .prefixlen = 0}; + + ospf6_lsdb_set_key(&key, &type, sizeof(type)); + if (argmode > 1) + ospf6_lsdb_set_key(&key, &adv_router, + sizeof(adv_router)); + + node = route_table_get_next(lsdb->table, &key); + if (!node || !prefix_match((struct prefix *)&key, &node->p)) + return NULL; + + for (end = node; end && end->parent + && end->parent->p.prefixlen >= key.prefixlen; + end = end->parent) + ; + } else { + node = route_top(lsdb->table); + end = NULL; + } + + while (node && !node->info) + node = route_next_until(node, end); + + if (!node) + return NULL; + if (!node->info) { + route_unlock_node(node); + return NULL; + } + + *lsa = node->info; + ospf6_lsa_lock(*lsa); + + return end; +} + +struct ospf6_lsa *ospf6_lsdb_next(const struct route_node *iterend, + struct ospf6_lsa *lsa) +{ + struct route_node *node = lsa->rn; + + ospf6_lsa_unlock(&lsa); + + do + node = route_next_until(node, iterend); + while (node && !node->info); + + if (node && node->info) { + struct ospf6_lsa *next = node->info; + ospf6_lsa_lock(next); + return next; + } + + if (node) + route_unlock_node(node); + return NULL; +} + +void ospf6_lsdb_remove_all(struct ospf6_lsdb *lsdb) +{ + struct ospf6_lsa *lsa, *lsanext; + + if (lsdb == NULL) + return; + + for (ALL_LSDB(lsdb, lsa, lsanext)) + ospf6_lsdb_remove(lsa, lsdb); +} + +void ospf6_lsdb_lsa_unlock(struct ospf6_lsa *lsa) +{ + if (lsa != NULL) { + if (lsa->rn != NULL) + route_unlock_node(lsa->rn); + ospf6_lsa_unlock(&lsa); + } +} + +int ospf6_lsdb_maxage_remover(struct ospf6_lsdb *lsdb) +{ + int reschedule = 0; + struct ospf6_lsa *lsa, *lsanext; + + for (ALL_LSDB(lsdb, lsa, lsanext)) { + if (!OSPF6_LSA_IS_MAXAGE(lsa)) { + if (IS_OSPF6_DEBUG_LSA_TYPE(lsa->header->type)) + zlog_debug("Not MaxAge %s", lsa->name); + continue; + } + + if (lsa->retrans_count != 0) { + if (IS_OSPF6_DEBUG_LSA_TYPE(lsa->header->type)) + zlog_debug("Remove MaxAge %s retrans_count %d", + lsa->name, lsa->retrans_count); + + reschedule = 1; + continue; + } + if (IS_OSPF6_DEBUG_LSA_TYPE(lsa->header->type)) + zlog_debug("Remove MaxAge %s", lsa->name); + + if (CHECK_FLAG(lsa->flag, OSPF6_LSA_SEQWRAPPED)) { + UNSET_FLAG(lsa->flag, OSPF6_LSA_SEQWRAPPED); + /* + * lsa->header->age = 0; + */ + lsa->header->seqnum = + htonl(OSPF_MAX_SEQUENCE_NUMBER + 1); + ospf6_lsa_checksum(lsa->header); + + EVENT_OFF(lsa->refresh); + event_execute(master, ospf6_lsa_refresh, lsa, 0, NULL); + } else { + zlog_debug("calling ospf6_lsdb_remove %s", lsa->name); + ospf6_lsdb_remove(lsa, lsdb); + } + } + + return (reschedule); +} + +uint32_t ospf6_new_ls_id(uint16_t type, uint32_t adv_router, + struct ospf6_lsdb *lsdb) +{ + struct ospf6_lsa *lsa; + uint32_t id = 1, tmp_id; + + /* This routine is curently invoked only for Inter-Prefix LSAs for + * non-summarized routes (no area/range). + */ + for (ALL_LSDB_TYPED_ADVRTR(lsdb, type, adv_router, lsa)) { + tmp_id = ntohl(lsa->header->id); + if (tmp_id < id) + continue; + + if (tmp_id > id) { + ospf6_lsdb_lsa_unlock(lsa); + break; + } + id++; + } + + return ((uint32_t)htonl(id)); +} + +/* Decide new LS sequence number to originate. + note return value is network byte order */ +uint32_t ospf6_new_ls_seqnum(uint16_t type, uint32_t id, uint32_t adv_router, + struct ospf6_lsdb *lsdb) +{ + struct ospf6_lsa *lsa; + signed long seqnum = 0; + + /* if current database copy not found, return InitialSequenceNumber */ + lsa = ospf6_lsdb_lookup(type, id, adv_router, lsdb); + if (lsa == NULL) + seqnum = OSPF_INITIAL_SEQUENCE_NUMBER; + else + seqnum = (signed long)ntohl(lsa->header->seqnum) + 1; + + return ((uint32_t)htonl(seqnum)); +} diff --git a/ospf6d/ospf6_lsdb.h b/ospf6d/ospf6_lsdb.h new file mode 100644 index 0000000..604406d --- /dev/null +++ b/ospf6d/ospf6_lsdb.h @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2003 Yasuhiro Ohara + */ + +#ifndef OSPF6_LSDB_H +#define OSPF6_LSDB_H + +#include "prefix.h" +#include "table.h" +#include "ospf6_route.h" + +struct ospf6_lsdb { + void *data; /* data structure that holds this lsdb */ + struct route_table *table; + uint32_t count; + uint32_t stats[OSPF6_LSTYPE_SIZE]; + void (*hook_add)(struct ospf6_lsa *); + void (*hook_remove)(struct ospf6_lsa *); +}; + +/* Function Prototypes */ +extern struct ospf6_lsdb *ospf6_lsdb_create(void *data); +extern void ospf6_lsdb_delete(struct ospf6_lsdb *lsdb); + +extern struct ospf6_lsa *ospf6_lsdb_lookup(uint16_t type, uint32_t id, + uint32_t adv_router, + struct ospf6_lsdb *lsdb); +extern struct ospf6_lsa *ospf6_lsdb_lookup_next(uint16_t type, uint32_t id, + uint32_t adv_router, + struct ospf6_lsdb *lsdb); +extern struct ospf6_lsa *ospf6_find_inter_prefix_lsa(struct ospf6 *ospf6, + struct ospf6_area *area, + struct prefix *p); + +extern void ospf6_lsdb_add(struct ospf6_lsa *lsa, struct ospf6_lsdb *lsdb); +extern void ospf6_lsdb_remove(struct ospf6_lsa *lsa, struct ospf6_lsdb *lsdb); + +extern const struct route_node *ospf6_lsdb_head(struct ospf6_lsdb *lsdb, + int argmode, uint16_t type, + uint32_t adv_router, + struct ospf6_lsa **lsa); +extern struct ospf6_lsa *ospf6_lsdb_next(const struct route_node *iterend, + struct ospf6_lsa *lsa); + +#define ALL_LSDB_TYPED_ADVRTR(lsdb, type, adv_router, lsa) \ + const struct route_node *iterend = \ + ospf6_lsdb_head(lsdb, 2, type, adv_router, &lsa); \ + lsa; \ + lsa = ospf6_lsdb_next(iterend, lsa) + +#define ALL_LSDB_TYPED(lsdb, type, lsa) \ + const struct route_node *iterend = \ + ospf6_lsdb_head(lsdb, 1, type, 0, &lsa); \ + lsa; \ + lsa = ospf6_lsdb_next(iterend, lsa) + +/* + * Since we are locking the lsa in ospf6_lsdb_head + * and then unlocking it in ospf6_lsa_unlock, when + * we cache the next pointer we need to increment + * the lock for the lsa so we don't accidentally free + * it really early. + */ +#define ALL_LSDB(lsdb, lsa, lsanext) \ + const struct route_node *iterend = ospf6_lsdb_head(lsdb, 0, 0, 0, \ + &lsa); \ + (lsa) != NULL && ospf6_lsa_lock(lsa) && \ + ((lsanext) = ospf6_lsdb_next(iterend, (lsa)), 1); \ + ospf6_lsa_unlock(&lsa), (lsa) = (lsanext) + +extern void ospf6_lsdb_remove_all(struct ospf6_lsdb *lsdb); +extern void ospf6_lsdb_lsa_unlock(struct ospf6_lsa *lsa); + +enum ospf_lsdb_show_level { + OSPF6_LSDB_SHOW_LEVEL_NORMAL = 0, + OSPF6_LSDB_SHOW_LEVEL_DETAIL, + OSPF6_LSDB_SHOW_LEVEL_INTERNAL, + OSPF6_LSDB_SHOW_LEVEL_DUMP, +}; + +extern void ospf6_lsdb_show(struct vty *vty, enum ospf_lsdb_show_level level, + uint16_t *type, uint32_t *id, uint32_t *adv_router, + struct ospf6_lsdb *lsdb, json_object *json, + bool use_json); + +extern uint32_t ospf6_new_ls_id(uint16_t type, uint32_t adv_router, + struct ospf6_lsdb *lsdb); +extern uint32_t ospf6_new_ls_seqnum(uint16_t type, uint32_t id, + uint32_t adv_router, + struct ospf6_lsdb *lsdb); +extern int ospf6_lsdb_maxage_remover(struct ospf6_lsdb *lsdb); + +#endif /* OSPF6_LSDB_H */ diff --git a/ospf6d/ospf6_main.c b/ospf6d/ospf6_main.c new file mode 100644 index 0000000..9323045 --- /dev/null +++ b/ospf6d/ospf6_main.c @@ -0,0 +1,254 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 1999 Yasuhiro Ohara + */ + +#include <zebra.h> +#include <lib/version.h> +#include <lib/keychain.h> +#include <stdlib.h> + +#include "getopt.h" +#include "frrevent.h" +#include "log.h" +#include "command.h" +#include "vty.h" +#include "memory.h" +#include "if.h" +#include "filter.h" +#include "prefix.h" +#include "plist.h" +#include "privs.h" +#include "sigevent.h" +#include "zclient.h" +#include "vrf.h" +#include "bfd.h" +#include "libfrr.h" + +#include "ospf6d.h" +#include "ospf6_top.h" +#include "ospf6_message.h" +#include "ospf6_network.h" +#include "ospf6_asbr.h" +#include "ospf6_lsa.h" +#include "ospf6_interface.h" +#include "ospf6_zebra.h" +#include "ospf6_routemap_nb.h" + +/* Default configuration file name for ospf6d. */ +#define OSPF6_DEFAULT_CONFIG "ospf6d.conf" + +/* Default port values. */ +#define OSPF6_VTY_PORT 2606 + +/* ospf6d privileges */ +zebra_capabilities_t _caps_p[] = {ZCAP_NET_RAW, ZCAP_BIND, ZCAP_SYS_ADMIN}; + +struct zebra_privs_t ospf6d_privs = { +#if defined(FRR_USER) + .user = FRR_USER, +#endif +#if defined FRR_GROUP + .group = FRR_GROUP, +#endif +#ifdef VTY_GROUP + .vty_group = VTY_GROUP, +#endif + .caps_p = _caps_p, + .cap_num_p = array_size(_caps_p), + .cap_num_i = 0}; + +/* ospf6d options, we use GNU getopt library. */ +struct option longopts[] = {{0}}; + +/* Master of threads. */ +struct event_loop *master; + +static void __attribute__((noreturn)) ospf6_exit(int status) +{ + struct vrf *vrf; + struct interface *ifp; + struct ospf6 *ospf6; + struct listnode *node, *nnode; + + frr_early_fini(); + + bfd_protocol_integration_set_shutdown(true); + + for (ALL_LIST_ELEMENTS(om6->ospf6, node, nnode, ospf6)) { + vrf = vrf_lookup_by_id(ospf6->vrf_id); + ospf6_delete(ospf6); + ospf6 = NULL; + FOR_ALL_INTERFACES (vrf, ifp) + if (ifp->info != NULL) + ospf6_interface_delete(ifp->info); + } + + ospf6_message_terminate(); + ospf6_asbr_terminate(); + ospf6_lsa_terminate(); + + /* reverse access_list_init */ + access_list_reset(); + + /* reverse prefix_list_init */ + prefix_list_add_hook(NULL); + prefix_list_delete_hook(NULL); + prefix_list_reset(); + + vrf_terminate(); + + if (zclient) { + zclient_stop(zclient); + zclient_free(zclient); + } + + frr_fini(); + exit(status); +} + +/* SIGHUP handler. */ +static void sighup(void) +{ + zlog_info("SIGHUP received"); +} + +/* SIGINT handler. */ +static void sigint(void) +{ + zlog_notice("Terminating on signal SIGINT"); + ospf6_exit(0); +} + +/* SIGTERM handler. */ +static void sigterm(void) +{ + zlog_notice("Terminating on signal SIGTERM"); + ospf6_exit(0); +} + +/* SIGUSR1 handler. */ +static void sigusr1(void) +{ + zlog_info("SIGUSR1 received"); + zlog_rotate(); +} + +struct frr_signal_t ospf6_signals[] = { + { + .signal = SIGHUP, + .handler = &sighup, + }, + { + .signal = SIGINT, + .handler = &sigint, + }, + { + .signal = SIGTERM, + .handler = &sigterm, + }, + { + .signal = SIGUSR1, + .handler = &sigusr1, + }, +}; + +static const struct frr_yang_module_info *const ospf6d_yang_modules[] = { + &frr_filter_info, + &frr_interface_info, + &frr_route_map_info, + &frr_vrf_info, + &frr_ospf_route_map_info, + &frr_ospf6_route_map_info, +}; + +FRR_DAEMON_INFO(ospf6d, OSPF6, .vty_port = OSPF6_VTY_PORT, + + .proghelp = "Implementation of the OSPFv3 routing protocol.", + + .signals = ospf6_signals, + .n_signals = array_size(ospf6_signals), + + .privs = &ospf6d_privs, .yang_modules = ospf6d_yang_modules, + .n_yang_modules = array_size(ospf6d_yang_modules), +); + +/* Max wait time for config to load before accepting hellos */ +#define OSPF6_PRE_CONFIG_MAX_WAIT_SECONDS 600 + +static void ospf6_config_finish(struct event *t) +{ + zlog_err("OSPF6 configuration end timer expired after %d seconds.", + OSPF6_PRE_CONFIG_MAX_WAIT_SECONDS); +} + +static void ospf6_config_start(void) +{ + if (IS_OSPF6_DEBUG_EVENT) + zlog_debug("ospf6d config start received"); + EVENT_OFF(t_ospf6_cfg); + event_add_timer(master, ospf6_config_finish, NULL, + OSPF6_PRE_CONFIG_MAX_WAIT_SECONDS, &t_ospf6_cfg); +} + +static void ospf6_config_end(void) +{ + if (IS_OSPF6_DEBUG_EVENT) + zlog_debug("ospf6d config end received"); + + EVENT_OFF(t_ospf6_cfg); +} + +/* Main routine of ospf6d. Treatment of argument and starting ospf finite + state machine is handled here. */ +int main(int argc, char *argv[], char *envp[]) +{ + int opt; + + frr_preinit(&ospf6d_di, argc, argv); + frr_opt_add("", longopts, ""); + + /* Command line argument treatment. */ + while (1) { + opt = frr_getopt(argc, argv, NULL); + + if (opt == EOF) + break; + + switch (opt) { + case 0: + break; + default: + frr_help_exit(1); + } + } + + if (geteuid() != 0) { + errno = EPERM; + perror(ospf6d_di.progname); + exit(1); + } + + /* OSPF6 master init. */ + ospf6_master_init(frr_init()); + + /* thread master */ + master = om6->master; + + keychain_init(); + ospf6_vrf_init(); + access_list_init(); + prefix_list_init(); + + /* initialize ospf6 */ + ospf6_init(master); + + /* Configuration processing callback initialization. */ + cmd_init_config_callbacks(ospf6_config_start, ospf6_config_end); + + frr_config_fork(); + frr_run(master); + + /* Not reached. */ + ospf6_exit(0); +} diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c new file mode 100644 index 0000000..07da9a5 --- /dev/null +++ b/ospf6d/ospf6_message.c @@ -0,0 +1,3215 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2003 Yasuhiro Ohara + */ + +#include <zebra.h> + +#include "memory.h" +#include "log.h" +#include "vty.h" +#include "command.h" +#include "frrevent.h" +#include "linklist.h" +#include "lib_errors.h" +#include "checksum.h" +#include "network.h" + +#include "ospf6_proto.h" +#include "ospf6_lsa.h" +#include "ospf6_lsdb.h" +#include "ospf6_top.h" +#include "ospf6_network.h" +#include "ospf6_message.h" + +#include "ospf6_area.h" +#include "ospf6_neighbor.h" +#include "ospf6_interface.h" + +/* for structures and macros ospf6_lsa_examin() needs */ +#include "ospf6_abr.h" +#include "ospf6_asbr.h" +#include "ospf6_intra.h" + +#include "ospf6_flood.h" +#include "ospf6d.h" +#include "ospf6_gr.h" +#include <netinet/ip6.h> +#include "lib/libospf.h" +#include "lib/keychain.h" +#include "ospf6_auth_trailer.h" + +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_MESSAGE, "OSPF6 message"); +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_PACKET, "OSPF6 packet"); +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_FIFO, "OSPF6 FIFO queue"); + +unsigned char conf_debug_ospf6_message[6] = {0x03, 0, 0, 0, 0, 0}; + +const char *ospf6_message_type(int type) +{ + switch (type) { + case OSPF6_MESSAGE_TYPE_HELLO: + return "Hello"; + case OSPF6_MESSAGE_TYPE_DBDESC: + return "DbDesc"; + case OSPF6_MESSAGE_TYPE_LSREQ: + return "LSReq"; + case OSPF6_MESSAGE_TYPE_LSUPDATE: + return "LSUpdate"; + case OSPF6_MESSAGE_TYPE_LSACK: + return "LSAck"; + case OSPF6_MESSAGE_TYPE_UNKNOWN: + default: + return "unknown"; + } +} + +/* Minimum (besides the standard OSPF packet header) lengths for OSPF + packets of particular types, offset is the "type" field. */ +const uint16_t ospf6_packet_minlen[OSPF6_MESSAGE_TYPE_ALL] = { + 0, + OSPF6_HELLO_MIN_SIZE, + OSPF6_DB_DESC_MIN_SIZE, + OSPF6_LS_REQ_MIN_SIZE, + OSPF6_LS_UPD_MIN_SIZE, + OSPF6_LS_ACK_MIN_SIZE}; + +/* Minimum (besides the standard LSA header) lengths for LSAs of particular + types, offset is the "LSA function code" portion of "LSA type" field. */ +const uint16_t ospf6_lsa_minlen[OSPF6_LSTYPE_SIZE] = { + 0, + /* 0x2001 */ OSPF6_ROUTER_LSA_MIN_SIZE, + /* 0x2002 */ OSPF6_NETWORK_LSA_MIN_SIZE, + /* 0x2003 */ OSPF6_INTER_PREFIX_LSA_MIN_SIZE, + /* 0x2004 */ OSPF6_INTER_ROUTER_LSA_FIX_SIZE, + /* 0x4005 */ OSPF6_AS_EXTERNAL_LSA_MIN_SIZE, + /* 0x2006 */ 0, + /* 0x2007 */ OSPF6_AS_EXTERNAL_LSA_MIN_SIZE, + /* 0x0008 */ OSPF6_LINK_LSA_MIN_SIZE, + /* 0x2009 */ OSPF6_INTRA_PREFIX_LSA_MIN_SIZE, + /* 0x200a */ 0, + /* 0x000b */ OSPF6_GRACE_LSA_MIN_SIZE}; + +/* print functions */ + +static void ospf6_header_print(struct ospf6_header *oh) +{ + zlog_debug(" OSPFv%d Type:%d Len:%hu Router-ID:%pI4", oh->version, + oh->type, ntohs(oh->length), &oh->router_id); + zlog_debug(" Area-ID:%pI4 Cksum:%hx Instance-ID:%d", &oh->area_id, + ntohs(oh->checksum), oh->instance_id); +} + +void ospf6_hello_print(struct ospf6_header *oh, int action) +{ + struct ospf6_hello *hello; + char options[32]; + char *p; + + ospf6_header_print(oh); + assert(oh->type == OSPF6_MESSAGE_TYPE_HELLO); + + hello = (struct ospf6_hello *)((caddr_t)oh + + sizeof(struct ospf6_header)); + + ospf6_options_printbuf(hello->options, options, sizeof(options)); + + zlog_debug(" I/F-Id:%ld Priority:%d Option:%s", + (unsigned long)ntohl(hello->interface_id), hello->priority, + options); + zlog_debug(" HelloInterval:%hu DeadInterval:%hu", + ntohs(hello->hello_interval), ntohs(hello->dead_interval)); + zlog_debug(" DR:%pI4 BDR:%pI4", &hello->drouter, &hello->bdrouter); + + if ((IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV) + && action == OSPF6_ACTION_RECV) + || (IS_OSPF6_DEBUG_MESSAGE(oh->type, SEND) + && action == OSPF6_ACTION_SEND)) { + + for (p = (char *)((caddr_t)hello + sizeof(struct ospf6_hello)); + p + sizeof(uint32_t) <= OSPF6_MESSAGE_END(oh); + p += sizeof(uint32_t)) + zlog_debug(" Neighbor: %pI4", (in_addr_t *)p); + + assert(p == OSPF6_MESSAGE_END(oh)); + } +} + +void ospf6_dbdesc_print(struct ospf6_header *oh, int action) +{ + struct ospf6_dbdesc *dbdesc; + char options[32]; + char *p; + + ospf6_header_print(oh); + assert(oh->type == OSPF6_MESSAGE_TYPE_DBDESC); + + dbdesc = (struct ospf6_dbdesc *)((caddr_t)oh + + sizeof(struct ospf6_header)); + + ospf6_options_printbuf(dbdesc->options, options, sizeof(options)); + + zlog_debug(" MBZ: %#x Option: %s IfMTU: %hu", dbdesc->reserved1, + options, ntohs(dbdesc->ifmtu)); + zlog_debug(" MBZ: %#x Bits: %s%s%s SeqNum: %#lx", dbdesc->reserved2, + (CHECK_FLAG(dbdesc->bits, OSPF6_DBDESC_IBIT) ? "I" : "-"), + (CHECK_FLAG(dbdesc->bits, OSPF6_DBDESC_MBIT) ? "M" : "-"), + (CHECK_FLAG(dbdesc->bits, OSPF6_DBDESC_MSBIT) ? "m" : "s"), + (unsigned long)ntohl(dbdesc->seqnum)); + + if ((IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV) + && action == OSPF6_ACTION_RECV) + || (IS_OSPF6_DEBUG_MESSAGE(oh->type, SEND) + && action == OSPF6_ACTION_SEND)) { + + for (p = (char *)((caddr_t)dbdesc + + sizeof(struct ospf6_dbdesc)); + p + sizeof(struct ospf6_lsa_header) + <= OSPF6_MESSAGE_END(oh); + p += sizeof(struct ospf6_lsa_header)) + ospf6_lsa_header_print_raw( + (struct ospf6_lsa_header *)p); + + assert(p == OSPF6_MESSAGE_END(oh)); + } +} + +void ospf6_lsreq_print(struct ospf6_header *oh, int action) +{ + char *p; + + ospf6_header_print(oh); + assert(oh->type == OSPF6_MESSAGE_TYPE_LSREQ); + + if ((IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV) + && action == OSPF6_ACTION_RECV) + || (IS_OSPF6_DEBUG_MESSAGE(oh->type, SEND) + && action == OSPF6_ACTION_SEND)) { + + for (p = (char *)((caddr_t)oh + sizeof(struct ospf6_header)); + p + sizeof(struct ospf6_lsreq_entry) + <= OSPF6_MESSAGE_END(oh); + p += sizeof(struct ospf6_lsreq_entry)) { + struct ospf6_lsreq_entry *e = + (struct ospf6_lsreq_entry *)p; + + zlog_debug(" [%s Id:%pI4 Adv:%pI4]", + ospf6_lstype_name(e->type), &e->id, + &e->adv_router); + } + + assert(p == OSPF6_MESSAGE_END(oh)); + } +} + +void ospf6_lsupdate_print(struct ospf6_header *oh, int action) +{ + struct ospf6_lsupdate *lsupdate; + unsigned long num; + char *p; + + ospf6_header_print(oh); + assert(oh->type == OSPF6_MESSAGE_TYPE_LSUPDATE); + + lsupdate = (struct ospf6_lsupdate *)((caddr_t)oh + + sizeof(struct ospf6_header)); + + num = ntohl(lsupdate->lsa_number); + zlog_debug(" Number of LSA: %ld", num); + + if ((IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV) + && action == OSPF6_ACTION_RECV) + || (IS_OSPF6_DEBUG_MESSAGE(oh->type, SEND) + && action == OSPF6_ACTION_SEND)) { + + for (p = (char *)((caddr_t)lsupdate + + sizeof(struct ospf6_lsupdate)); + p < OSPF6_MESSAGE_END(oh) + && p + OSPF6_LSA_SIZE(p) <= OSPF6_MESSAGE_END(oh); + p += OSPF6_LSA_SIZE(p)) { + ospf6_lsa_header_print_raw( + (struct ospf6_lsa_header *)p); + } + + assert(p == OSPF6_MESSAGE_END(oh)); + } +} + +void ospf6_lsack_print(struct ospf6_header *oh, int action) +{ + char *p; + + ospf6_header_print(oh); + assert(oh->type == OSPF6_MESSAGE_TYPE_LSACK); + + if ((IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV) + && action == OSPF6_ACTION_RECV) + || (IS_OSPF6_DEBUG_MESSAGE(oh->type, SEND) + && action == OSPF6_ACTION_SEND)) { + + for (p = (char *)((caddr_t)oh + sizeof(struct ospf6_header)); + p + sizeof(struct ospf6_lsa_header) + <= OSPF6_MESSAGE_END(oh); + p += sizeof(struct ospf6_lsa_header)) + ospf6_lsa_header_print_raw( + (struct ospf6_lsa_header *)p); + + assert(p == OSPF6_MESSAGE_END(oh)); + } +} + +static struct ospf6_packet *ospf6_packet_new(size_t size) +{ + struct ospf6_packet *new; + + new = XCALLOC(MTYPE_OSPF6_PACKET, sizeof(struct ospf6_packet)); + new->s = stream_new(size); + + return new; +} + +static void ospf6_packet_free(struct ospf6_packet *op) +{ + if (op->s) + stream_free(op->s); + + XFREE(MTYPE_OSPF6_PACKET, op); +} + +struct ospf6_fifo *ospf6_fifo_new(void) +{ + struct ospf6_fifo *new; + + new = XCALLOC(MTYPE_OSPF6_FIFO, sizeof(struct ospf6_fifo)); + return new; +} + +/* Add new packet to fifo. */ +static void ospf6_fifo_push(struct ospf6_fifo *fifo, struct ospf6_packet *op) +{ + if (fifo->tail) + fifo->tail->next = op; + else + fifo->head = op; + + fifo->tail = op; + + fifo->count++; +} + +/* Add new packet to head of fifo. */ +static void ospf6_fifo_push_head(struct ospf6_fifo *fifo, + struct ospf6_packet *op) +{ + op->next = fifo->head; + + if (fifo->tail == NULL) + fifo->tail = op; + + fifo->head = op; + + fifo->count++; +} + +/* Delete first packet from fifo. */ +static struct ospf6_packet *ospf6_fifo_pop(struct ospf6_fifo *fifo) +{ + struct ospf6_packet *op; + + op = fifo->head; + + if (op) { + fifo->head = op->next; + + if (fifo->head == NULL) + fifo->tail = NULL; + + fifo->count--; + } + + return op; +} + +/* Return first fifo entry. */ +static struct ospf6_packet *ospf6_fifo_head(struct ospf6_fifo *fifo) +{ + return fifo->head; +} + +/* Flush ospf packet fifo. */ +void ospf6_fifo_flush(struct ospf6_fifo *fifo) +{ + struct ospf6_packet *op; + struct ospf6_packet *next; + + for (op = fifo->head; op; op = next) { + next = op->next; + ospf6_packet_free(op); + } + fifo->head = fifo->tail = NULL; + fifo->count = 0; +} + +/* Free ospf packet fifo. */ +void ospf6_fifo_free(struct ospf6_fifo *fifo) +{ + ospf6_fifo_flush(fifo); + + XFREE(MTYPE_OSPF6_FIFO, fifo); +} + +static void ospf6_packet_add(struct ospf6_interface *oi, + struct ospf6_packet *op) +{ + /* Add packet to end of queue. */ + ospf6_fifo_push(oi->obuf, op); + + /* Debug of packet fifo*/ + /* ospf_fifo_debug (oi->obuf); */ +} + +static void ospf6_packet_add_top(struct ospf6_interface *oi, + struct ospf6_packet *op) +{ + /* Add packet to head of queue. */ + ospf6_fifo_push_head(oi->obuf, op); + + /* Debug of packet fifo*/ + /* ospf_fifo_debug (oi->obuf); */ +} + +static void ospf6_packet_delete(struct ospf6_interface *oi) +{ + struct ospf6_packet *op; + + op = ospf6_fifo_pop(oi->obuf); + + if (op) + ospf6_packet_free(op); +} + + +static void ospf6_hello_recv(struct in6_addr *src, struct in6_addr *dst, + struct ospf6_interface *oi, + struct ospf6_header *oh) +{ + struct ospf6_hello *hello; + struct ospf6_neighbor *on; + char *p; + int twoway = 0; + int neighborchange = 0; + int neighbor_ifindex_change = 0; + int backupseen = 0; + int64_t latency = 0; + struct timeval timestamp; + + monotime(×tamp); + hello = (struct ospf6_hello *)((caddr_t)oh + + sizeof(struct ospf6_header)); + + /* HelloInterval check */ + if (ntohs(hello->hello_interval) != oi->hello_interval) { + zlog_warn( + "VRF %s: I/F %s HelloInterval mismatch from %pI6 (%pI4): (my %d, rcvd %d)", + oi->interface->vrf->name, oi->interface->name, src, + &oh->router_id, oi->hello_interval, + ntohs(hello->hello_interval)); + return; + } + + /* RouterDeadInterval check */ + if (ntohs(hello->dead_interval) != oi->dead_interval) { + zlog_warn( + "VRF %s: I/F %s DeadInterval mismatch from %pI6 (%pI4): (my %d, rcvd %d)", + oi->interface->vrf->name, oi->interface->name, src, + &oh->router_id, oi->dead_interval, + ntohs(hello->dead_interval)); + return; + } + + /* E-bit check */ + if (OSPF6_OPT_ISSET(hello->options, OSPF6_OPT_E) != + OSPF6_OPT_ISSET(oi->area->options, OSPF6_OPT_E)) { + zlog_warn("VRF %s: IF %s E-bit mismatch from %pI6 (%pI4)", + oi->interface->vrf->name, oi->interface->name, src, + &oh->router_id); + return; + } + + /* N-bit check */ + if (OSPF6_OPT_ISSET(hello->options, OSPF6_OPT_N) + != OSPF6_OPT_ISSET(oi->area->options, OSPF6_OPT_N)) { + zlog_warn("VRF %s: IF %s N-bit mismatch", + oi->interface->vrf->name, oi->interface->name); + return; + } + + if (((OSPF6_OPT_ISSET_EXT(hello->options, OSPF6_OPT_AT) == + OSPF6_OPT_AT) && + (oi->at_data.flags == 0)) || + ((OSPF6_OPT_ISSET_EXT(hello->options, OSPF6_OPT_AT) != + OSPF6_OPT_AT) && + (oi->at_data.flags != 0))) { + if (IS_OSPF6_DEBUG_AUTH_RX) + zlog_warn( + "VRF %s: IF %s AT-bit mismatch in hello packet", + oi->interface->vrf->name, oi->interface->name); + oi->at_data.rx_drop++; + return; + } + + /* Find neighbor, create if not exist */ + on = ospf6_neighbor_lookup(oh->router_id, oi); + if (on == NULL) { + on = ospf6_neighbor_create(oh->router_id, oi); + on->prev_drouter = on->drouter = hello->drouter; + on->prev_bdrouter = on->bdrouter = hello->bdrouter; + on->priority = hello->priority; + } + + /* check latency against hello period */ + if (on->hello_in) + latency = monotime_since(&on->last_hello, NULL) + - ((int64_t)oi->hello_interval * 1000000); + /* log if latency exceeds the hello period */ + if (latency > ((int64_t)oi->hello_interval * 1000000)) + zlog_warn("%s RX %pI4 high latency %" PRId64 "us.", __func__, + &on->router_id, latency); + on->last_hello = timestamp; + on->hello_in++; + + /* Always override neighbor's source address */ + memcpy(&on->linklocal_addr, src, sizeof(struct in6_addr)); + + /* Neighbor ifindex check */ + if (on->ifindex != (ifindex_t)ntohl(hello->interface_id)) { + on->ifindex = ntohl(hello->interface_id); + neighbor_ifindex_change++; + } + + /* TwoWay check */ + for (p = (char *)((caddr_t)hello + sizeof(struct ospf6_hello)); + p + sizeof(uint32_t) <= OSPF6_MESSAGE_END(oh); + p += sizeof(uint32_t)) { + uint32_t *router_id = (uint32_t *)p; + + if (*router_id == oi->area->ospf6->router_id) + twoway++; + } + + assert(p == OSPF6_MESSAGE_END(oh)); + + /* RouterPriority check */ + if (on->priority != hello->priority) { + on->priority = hello->priority; + neighborchange++; + } + + /* DR check */ + if (on->drouter != hello->drouter) { + on->prev_drouter = on->drouter; + on->drouter = hello->drouter; + if (on->prev_drouter == on->router_id + || on->drouter == on->router_id) + neighborchange++; + } + + /* BDR check */ + if (on->bdrouter != hello->bdrouter) { + on->prev_bdrouter = on->bdrouter; + on->bdrouter = hello->bdrouter; + if (on->prev_bdrouter == on->router_id + || on->bdrouter == on->router_id) + neighborchange++; + } + + /* BackupSeen check */ + if (oi->state == OSPF6_INTERFACE_WAITING) { + if (hello->bdrouter == on->router_id) + backupseen++; + else if (hello->drouter == on->router_id + && hello->bdrouter == htonl(0)) + backupseen++; + } + + oi->hello_in++; + + /* Execute neighbor events */ + event_execute(master, hello_received, on, 0, NULL); + if (twoway) + event_execute(master, twoway_received, on, 0, NULL); + else { + if (OSPF6_GR_IS_ACTIVE_HELPER(on)) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "%s, Received oneway hello from RESTARTER so ignore here.", + __PRETTY_FUNCTION__); + } else { + /* If the router is DR_OTHER, RESTARTER will not wait + * until it receives the hello from it if it receives + * from DR and BDR. + * So, helper might receives ONE_WAY hello from + * RESTARTER. So not allowing to change the state if it + * receives one_way hellow when it acts as HELPER for + * that specific neighbor. + */ + event_execute(master, oneway_received, on, 0, NULL); + } + } + + if (OSPF6_GR_IS_ACTIVE_HELPER(on)) { + /* As per the GR Conformance Test Case 7.2. Section 3 + * "Also, if X was the Designated Router on network segment S + * when the helping relationship began, Y maintains X as the + * Designated Router until the helping relationship is + * terminated." + * When it is a helper for this neighbor, It should not trigger + * the ISM Events. Also Intentionally not setting the priority + * and other fields so that when the neighbor exits the Grace + * period, it can handle if there is any change before GR and + * after GR. + */ + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "%s, Neighbor is under GR Restart, hence ignoring the ISM Events", + __PRETTY_FUNCTION__); + + return; + } + + /* + * RFC 3623 - Section 2: + * "If the restarting router determines that it was the Designated + * Router on a given segment prior to the restart, it elects + * itself as the Designated Router again. The restarting router + * knows that it was the Designated Router if, while the + * associated interface is in Waiting state, a Hello packet is + * received from a neighbor listing the router as the Designated + * Router". + */ + if (oi->area->ospf6->gr_info.restart_in_progress + && oi->state == OSPF6_INTERFACE_WAITING + && hello->drouter == oi->area->ospf6->router_id) + oi->drouter = hello->drouter; + + /* Schedule interface events */ + if (backupseen) + event_add_event(master, backup_seen, oi, 0, NULL); + if (neighborchange) + event_add_event(master, neighbor_change, oi, 0, NULL); + + if (neighbor_ifindex_change && on->state == OSPF6_NEIGHBOR_FULL) + OSPF6_ROUTER_LSA_SCHEDULE(oi->area); +} + +static void ospf6_dbdesc_recv_master(struct ospf6_header *oh, + struct ospf6_neighbor *on) +{ + struct ospf6_dbdesc *dbdesc; + char *p; + + dbdesc = (struct ospf6_dbdesc *)((caddr_t)oh + + sizeof(struct ospf6_header)); + + if (on->state < OSPF6_NEIGHBOR_INIT) { + if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR)) + zlog_debug("Neighbor state less than Init, ignore"); + return; + } + + switch (on->state) { + case OSPF6_NEIGHBOR_TWOWAY: + if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR)) + zlog_debug("Neighbor state is 2-Way, ignore"); + return; + + case OSPF6_NEIGHBOR_INIT: + event_execute(master, twoway_received, on, 0, NULL); + if (on->state != OSPF6_NEIGHBOR_EXSTART) { + if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR)) + zlog_debug( + "Neighbor state is not ExStart, ignore"); + return; + } + /* else fall through to ExStart */ + /* fallthru */ + case OSPF6_NEIGHBOR_EXSTART: + /* if neighbor obeys us as our slave, schedule negotiation_done + and process LSA Headers. Otherwise, ignore this message */ + if (!CHECK_FLAG(dbdesc->bits, OSPF6_DBDESC_MSBIT) + && !CHECK_FLAG(dbdesc->bits, OSPF6_DBDESC_IBIT) + && ntohl(dbdesc->seqnum) == on->dbdesc_seqnum) { + /* execute NegotiationDone */ + event_execute(master, negotiation_done, on, 0, NULL); + + /* Record neighbor options */ + memcpy(on->options, dbdesc->options, + sizeof(on->options)); + } else { + zlog_warn("VRF %s: Nbr %s: Negotiation failed", + on->ospf6_if->interface->vrf->name, on->name); + return; + } + /* fall through to exchange */ + + case OSPF6_NEIGHBOR_EXCHANGE: + if (!memcmp(dbdesc, &on->dbdesc_last, + sizeof(struct ospf6_dbdesc))) { + /* Duplicated DatabaseDescription is dropped by master + */ + if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR)) + zlog_debug( + "Duplicated dbdesc discarded by Master, ignore"); + return; + } + + if (CHECK_FLAG(dbdesc->bits, OSPF6_DBDESC_MSBIT)) { + zlog_warn( + "DbDesc recv: Master/Slave bit mismatch Nbr %s", + on->name); + event_add_event(master, seqnumber_mismatch, on, 0, + NULL); + return; + } + + if (CHECK_FLAG(dbdesc->bits, OSPF6_DBDESC_IBIT)) { + zlog_warn("DbDesc recv: Initialize bit mismatch Nbr %s", + on->name); + event_add_event(master, seqnumber_mismatch, on, 0, + NULL); + return; + } + + if (memcmp(on->options, dbdesc->options, sizeof(on->options))) { + zlog_warn("DbDesc recv: Option field mismatch Nbr %s", + on->name); + event_add_event(master, seqnumber_mismatch, on, 0, + NULL); + return; + } + + if (ntohl(dbdesc->seqnum) != on->dbdesc_seqnum) { + zlog_warn( + "DbDesc recv: Sequence number mismatch Nbr %s (received %#lx, %#lx expected)", + on->name, (unsigned long)ntohl(dbdesc->seqnum), + (unsigned long)on->dbdesc_seqnum); + event_add_event(master, seqnumber_mismatch, on, 0, + NULL); + return; + } + break; + + case OSPF6_NEIGHBOR_LOADING: + case OSPF6_NEIGHBOR_FULL: + if (!memcmp(dbdesc, &on->dbdesc_last, + sizeof(struct ospf6_dbdesc))) { + /* Duplicated DatabaseDescription is dropped by master + */ + if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR)) + zlog_debug( + "Duplicated dbdesc discarded by Master, ignore"); + return; + } + + zlog_warn( + "DbDesc recv: Not duplicate dbdesc in state %s Nbr %s", + ospf6_neighbor_state_str[on->state], on->name); + event_add_event(master, seqnumber_mismatch, on, 0, NULL); + return; + + default: + assert(0); + break; + } + + /* Process LSA headers */ + for (p = (char *)((caddr_t)dbdesc + sizeof(struct ospf6_dbdesc)); + p + sizeof(struct ospf6_lsa_header) <= OSPF6_MESSAGE_END(oh); + p += sizeof(struct ospf6_lsa_header)) { + struct ospf6_lsa *his, *mine; + struct ospf6_lsdb *lsdb = NULL; + + his = ospf6_lsa_create_headeronly((struct ospf6_lsa_header *)p); + + if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV)) + zlog_debug("%s", his->name); + + switch (OSPF6_LSA_SCOPE(his->header->type)) { + case OSPF6_SCOPE_LINKLOCAL: + lsdb = on->ospf6_if->lsdb; + break; + case OSPF6_SCOPE_AREA: + lsdb = on->ospf6_if->area->lsdb; + break; + case OSPF6_SCOPE_AS: + lsdb = on->ospf6_if->area->ospf6->lsdb; + break; + case OSPF6_SCOPE_RESERVED: + if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV)) + zlog_debug("Ignoring LSA of reserved scope"); + ospf6_lsa_delete(his); + continue; + } + + if (ntohs(his->header->type) == OSPF6_LSTYPE_AS_EXTERNAL + && (IS_AREA_STUB(on->ospf6_if->area) + || IS_AREA_NSSA(on->ospf6_if->area))) { + if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV)) + zlog_debug( + "SeqNumMismatch (E-bit mismatch), discard"); + ospf6_lsa_delete(his); + event_add_event(master, seqnumber_mismatch, on, 0, + NULL); + return; + } + + mine = ospf6_lsdb_lookup(his->header->type, his->header->id, + his->header->adv_router, lsdb); + if (mine == NULL) { + if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV)) + zlog_debug("Add request (No database copy)"); + ospf6_lsdb_add(ospf6_lsa_copy(his), on->request_list); + } else if (ospf6_lsa_compare(his, mine) < 0) { + if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV)) + zlog_debug("Add request (Received MoreRecent)"); + ospf6_lsdb_add(ospf6_lsa_copy(his), on->request_list); + } else { + if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV)) + zlog_debug("Discard (Existing MoreRecent)"); + } + ospf6_lsa_delete(his); + } + + assert(p == OSPF6_MESSAGE_END(oh)); + + /* Increment sequence number */ + on->dbdesc_seqnum++; + + /* schedule send lsreq */ + if (on->request_list->count) + event_add_event(master, ospf6_lsreq_send, on, 0, + &on->thread_send_lsreq); + + EVENT_OFF(on->thread_send_dbdesc); + + /* More bit check */ + if (!CHECK_FLAG(dbdesc->bits, OSPF6_DBDESC_MBIT) + && !CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MBIT)) + event_add_event(master, exchange_done, on, 0, + &on->thread_exchange_done); + else { + event_add_event(master, ospf6_dbdesc_send_newone, on, 0, + &on->thread_send_dbdesc); + } + + /* save last received dbdesc */ + memcpy(&on->dbdesc_last, dbdesc, sizeof(struct ospf6_dbdesc)); +} + +static void ospf6_dbdesc_recv_slave(struct ospf6_header *oh, + struct ospf6_neighbor *on) +{ + struct ospf6_dbdesc *dbdesc; + char *p; + + dbdesc = (struct ospf6_dbdesc *)((caddr_t)oh + + sizeof(struct ospf6_header)); + + if (on->state < OSPF6_NEIGHBOR_INIT) { + if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR)) + zlog_debug("Neighbor state less than Init, ignore"); + return; + } + + switch (on->state) { + case OSPF6_NEIGHBOR_TWOWAY: + if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR)) + zlog_debug("Neighbor state is 2-Way, ignore"); + return; + + case OSPF6_NEIGHBOR_INIT: + event_execute(master, twoway_received, on, 0, NULL); + if (on->state != OSPF6_NEIGHBOR_EXSTART) { + if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR)) + zlog_debug( + "Neighbor state is not ExStart, ignore"); + return; + } + /* else fall through to ExStart */ + /* fallthru */ + case OSPF6_NEIGHBOR_EXSTART: + /* If the neighbor is Master, act as Slave. Schedule + negotiation_done + and process LSA Headers. Otherwise, ignore this message */ + if (CHECK_FLAG(dbdesc->bits, OSPF6_DBDESC_IBIT) + && CHECK_FLAG(dbdesc->bits, OSPF6_DBDESC_MBIT) + && CHECK_FLAG(dbdesc->bits, OSPF6_DBDESC_MSBIT) + && ntohs(oh->length) + == sizeof(struct ospf6_header) + + sizeof(struct ospf6_dbdesc)) { + /* set the master/slave bit to slave */ + UNSET_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MSBIT); + + /* set the DD sequence number to one specified by master + */ + on->dbdesc_seqnum = ntohl(dbdesc->seqnum); + + /* schedule NegotiationDone */ + event_execute(master, negotiation_done, on, 0, NULL); + + /* Record neighbor options */ + memcpy(on->options, dbdesc->options, + sizeof(on->options)); + } else { + zlog_warn("VRF %s: Nbr %s Negotiation failed", + on->ospf6_if->interface->vrf->name, on->name); + return; + } + break; + + case OSPF6_NEIGHBOR_EXCHANGE: + if (!memcmp(dbdesc, &on->dbdesc_last, + sizeof(struct ospf6_dbdesc))) { + /* Duplicated DatabaseDescription causes slave to + * retransmit */ + if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR)) + zlog_debug( + "Duplicated dbdesc causes retransmit"); + EVENT_OFF(on->thread_send_dbdesc); + event_add_event(master, ospf6_dbdesc_send, on, 0, + &on->thread_send_dbdesc); + return; + } + + if (!CHECK_FLAG(dbdesc->bits, OSPF6_DBDESC_MSBIT)) { + zlog_warn( + "DbDesc slave recv: Master/Slave bit mismatch Nbr %s", + on->name); + event_add_event(master, seqnumber_mismatch, on, 0, + NULL); + return; + } + + if (CHECK_FLAG(dbdesc->bits, OSPF6_DBDESC_IBIT)) { + zlog_warn( + "DbDesc slave recv: Initialize bit mismatch Nbr %s", + on->name); + event_add_event(master, seqnumber_mismatch, on, 0, + NULL); + return; + } + + if (memcmp(on->options, dbdesc->options, sizeof(on->options))) { + zlog_warn( + "DbDesc slave recv: Option field mismatch Nbr %s", + on->name); + event_add_event(master, seqnumber_mismatch, on, 0, + NULL); + return; + } + + if (ntohl(dbdesc->seqnum) != on->dbdesc_seqnum + 1) { + zlog_warn( + "DbDesc slave recv: Sequence number mismatch Nbr %s (received %#lx, %#lx expected)", + on->name, (unsigned long)ntohl(dbdesc->seqnum), + (unsigned long)on->dbdesc_seqnum + 1); + event_add_event(master, seqnumber_mismatch, on, 0, + NULL); + return; + } + break; + + case OSPF6_NEIGHBOR_LOADING: + case OSPF6_NEIGHBOR_FULL: + if (!memcmp(dbdesc, &on->dbdesc_last, + sizeof(struct ospf6_dbdesc))) { + /* Duplicated DatabaseDescription causes slave to + * retransmit */ + if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR)) + zlog_debug( + "Duplicated dbdesc causes retransmit"); + EVENT_OFF(on->thread_send_dbdesc); + event_add_event(master, ospf6_dbdesc_send, on, 0, + &on->thread_send_dbdesc); + return; + } + + zlog_warn( + "DbDesc slave recv: Not duplicate dbdesc in state %s Nbr %s", + ospf6_neighbor_state_str[on->state], on->name); + event_add_event(master, seqnumber_mismatch, on, 0, NULL); + return; + + default: + assert(0); + break; + } + + /* Process LSA headers */ + for (p = (char *)((caddr_t)dbdesc + sizeof(struct ospf6_dbdesc)); + p + sizeof(struct ospf6_lsa_header) <= OSPF6_MESSAGE_END(oh); + p += sizeof(struct ospf6_lsa_header)) { + struct ospf6_lsa *his, *mine; + struct ospf6_lsdb *lsdb = NULL; + + his = ospf6_lsa_create_headeronly((struct ospf6_lsa_header *)p); + + switch (OSPF6_LSA_SCOPE(his->header->type)) { + case OSPF6_SCOPE_LINKLOCAL: + lsdb = on->ospf6_if->lsdb; + break; + case OSPF6_SCOPE_AREA: + lsdb = on->ospf6_if->area->lsdb; + break; + case OSPF6_SCOPE_AS: + lsdb = on->ospf6_if->area->ospf6->lsdb; + break; + case OSPF6_SCOPE_RESERVED: + if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV)) + zlog_debug("Ignoring LSA of reserved scope"); + ospf6_lsa_delete(his); + continue; + } + + if (OSPF6_LSA_SCOPE(his->header->type) == OSPF6_SCOPE_AS + && (IS_AREA_STUB(on->ospf6_if->area) + || IS_AREA_NSSA(on->ospf6_if->area))) { + if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV)) + zlog_debug("E-bit mismatch with LSA Headers"); + ospf6_lsa_delete(his); + event_add_event(master, seqnumber_mismatch, on, 0, + NULL); + return; + } + + mine = ospf6_lsdb_lookup(his->header->type, his->header->id, + his->header->adv_router, lsdb); + if (mine == NULL || ospf6_lsa_compare(his, mine) < 0) { + if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV)) + zlog_debug("Add request-list: %s", his->name); + ospf6_lsdb_add(ospf6_lsa_copy(his), on->request_list); + } + ospf6_lsa_delete(his); + } + + assert(p == OSPF6_MESSAGE_END(oh)); + + /* Set sequence number to Master's */ + on->dbdesc_seqnum = ntohl(dbdesc->seqnum); + + /* schedule send lsreq */ + if (on->request_list->count) + event_add_event(master, ospf6_lsreq_send, on, 0, + &on->thread_send_lsreq); + + EVENT_OFF(on->thread_send_dbdesc); + event_add_event(master, ospf6_dbdesc_send_newone, on, 0, + &on->thread_send_dbdesc); + + /* save last received dbdesc */ + memcpy(&on->dbdesc_last, dbdesc, sizeof(struct ospf6_dbdesc)); +} + +static void ospf6_dbdesc_recv(struct in6_addr *src, struct in6_addr *dst, + struct ospf6_interface *oi, + struct ospf6_header *oh) +{ + struct ospf6_neighbor *on; + struct ospf6_dbdesc *dbdesc; + + on = ospf6_neighbor_lookup(oh->router_id, oi); + if (on == NULL) { + if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR)) + zlog_debug("Neighbor not found, ignore"); + return; + } + + dbdesc = (struct ospf6_dbdesc *)((caddr_t)oh + + sizeof(struct ospf6_header)); + + if (((OSPF6_OPT_ISSET_EXT(dbdesc->options, OSPF6_OPT_AT) == + OSPF6_OPT_AT) && + (oi->at_data.flags == 0)) || + ((OSPF6_OPT_ISSET_EXT(dbdesc->options, OSPF6_OPT_AT) != + OSPF6_OPT_AT) && + (oi->at_data.flags != 0))) { + if (IS_OSPF6_DEBUG_AUTH_RX) + zlog_warn( + "VRF %s: IF %s AT-bit mismatch in dbdesc packet", + oi->interface->vrf->name, oi->interface->name); + oi->at_data.rx_drop++; + return; + } + + /* Interface MTU check */ + if (!oi->mtu_ignore && ntohs(dbdesc->ifmtu) != oi->ifmtu) { + zlog_warn("VRF %s: I/F %s MTU mismatch (my %d rcvd %d)", + oi->interface->vrf->name, oi->interface->name, + oi->ifmtu, ntohs(dbdesc->ifmtu)); + return; + } + + if (dbdesc->reserved1 || dbdesc->reserved2) { + if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR)) + zlog_debug( + "Non-0 reserved field in %s's DbDesc, correct", + on->name); + dbdesc->reserved1 = 0; + dbdesc->reserved2 = 0; + } + + oi->db_desc_in++; + + if (ntohl(oh->router_id) < ntohl(oi->area->ospf6->router_id)) + ospf6_dbdesc_recv_master(oh, on); + else if (ntohl(oi->area->ospf6->router_id) < ntohl(oh->router_id)) + ospf6_dbdesc_recv_slave(oh, on); + else { + if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR)) + zlog_debug("Can't decide which is master, ignore"); + } +} + +static void ospf6_lsreq_recv(struct in6_addr *src, struct in6_addr *dst, + struct ospf6_interface *oi, + struct ospf6_header *oh) +{ + struct ospf6_neighbor *on; + char *p; + struct ospf6_lsreq_entry *e; + struct ospf6_lsdb *lsdb = NULL; + struct ospf6_lsa *lsa; + + on = ospf6_neighbor_lookup(oh->router_id, oi); + if (on == NULL) { + if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR)) + zlog_debug("Neighbor not found, ignore"); + return; + } + + if (on->state != OSPF6_NEIGHBOR_EXCHANGE + && on->state != OSPF6_NEIGHBOR_LOADING + && on->state != OSPF6_NEIGHBOR_FULL) { + if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR)) + zlog_debug("Neighbor state less than Exchange, ignore"); + return; + } + + oi->ls_req_in++; + + /* Process each request */ + for (p = (char *)((caddr_t)oh + sizeof(struct ospf6_header)); + p + sizeof(struct ospf6_lsreq_entry) <= OSPF6_MESSAGE_END(oh); + p += sizeof(struct ospf6_lsreq_entry)) { + e = (struct ospf6_lsreq_entry *)p; + + switch (OSPF6_LSA_SCOPE(e->type)) { + case OSPF6_SCOPE_LINKLOCAL: + lsdb = on->ospf6_if->lsdb; + break; + case OSPF6_SCOPE_AREA: + lsdb = on->ospf6_if->area->lsdb; + break; + case OSPF6_SCOPE_AS: + lsdb = on->ospf6_if->area->ospf6->lsdb; + break; + default: + if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV)) + zlog_debug("Ignoring LSA of reserved scope"); + continue; + } + + /* Find database copy */ + lsa = ospf6_lsdb_lookup(e->type, e->id, e->adv_router, lsdb); + if (lsa == NULL) { + zlog_warn( + "Can't find requested lsa [%s Id:%pI4 Adv:%pI4] send badLSReq", + ospf6_lstype_name(e->type), &e->id, + &e->adv_router); + event_add_event(master, bad_lsreq, on, 0, NULL); + return; + } + + ospf6_lsdb_add(ospf6_lsa_copy(lsa), on->lsupdate_list); + } + + assert(p == OSPF6_MESSAGE_END(oh)); + + /* schedule send lsupdate */ + EVENT_OFF(on->thread_send_lsupdate); + event_add_event(master, ospf6_lsupdate_send_neighbor, on, 0, + &on->thread_send_lsupdate); +} + +/* Verify, that the specified memory area contains exactly N valid IPv6 + prefixes as specified by RFC5340, A.4.1. */ +static unsigned ospf6_prefixes_examin( + struct ospf6_prefix *current, /* start of buffer */ + unsigned length, + const uint32_t req_num_pfxs /* always compared with the actual number + of prefixes */ +) +{ + uint8_t requested_pfx_bytes; + uint32_t real_num_pfxs = 0; + + while (length) { + if (length < OSPF6_PREFIX_MIN_SIZE) { + zlog_warn("%s: undersized IPv6 prefix header", + __func__); + return MSG_NG; + } + /* safe to look deeper */ + if (current->prefix_length > IPV6_MAX_BITLEN) { + zlog_warn("%s: invalid PrefixLength (%u bits)", + __func__, current->prefix_length); + return MSG_NG; + } + /* covers both fixed- and variable-sized fields */ + requested_pfx_bytes = + OSPF6_PREFIX_MIN_SIZE + + OSPF6_PREFIX_SPACE(current->prefix_length); + if (requested_pfx_bytes > length) { + zlog_warn("%s: undersized IPv6 prefix", __func__); + return MSG_NG; + } + /* next prefix */ + length -= requested_pfx_bytes; + current = (struct ospf6_prefix *)((caddr_t)current + + requested_pfx_bytes); + real_num_pfxs++; + } + if (real_num_pfxs != req_num_pfxs) { + zlog_warn( + "%s: IPv6 prefix number mismatch (%u required, %u real)", + __func__, req_num_pfxs, real_num_pfxs); + return MSG_NG; + } + return MSG_OK; +} + +/* Verify an LSA to have a valid length and dispatch further (where + appropriate) to check if the contents, including nested IPv6 prefixes, + is properly sized/aligned within the LSA. Note that this function gets + LSA type in network byte order, uses in host byte order and passes to + ospf6_lstype_name() in network byte order again. */ +static unsigned ospf6_lsa_examin(struct ospf6_lsa_header *lsah, + const uint16_t lsalen, + const uint8_t headeronly) +{ + struct ospf6_intra_prefix_lsa *intra_prefix_lsa; + struct ospf6_as_external_lsa *as_external_lsa; + struct ospf6_link_lsa *link_lsa; + unsigned exp_length; + uint8_t ltindex; + uint16_t lsatype; + + /* In case an additional minimum length constraint is defined for + current + LSA type, make sure that this constraint is met. */ + lsatype = ntohs(lsah->type); + ltindex = lsatype & OSPF6_LSTYPE_FCODE_MASK; + if (ltindex < OSPF6_LSTYPE_SIZE && ospf6_lsa_minlen[ltindex] + && lsalen < ospf6_lsa_minlen[ltindex] + OSPF6_LSA_HEADER_SIZE) { + zlog_warn("%s: undersized (%u B) LSA", __func__, lsalen); + return MSG_NG; + } + switch (lsatype) { + case OSPF6_LSTYPE_ROUTER: + /* RFC5340 A.4.3, LSA header + OSPF6_ROUTER_LSA_MIN_SIZE bytes + followed + by N>=0 interface descriptions. */ + if ((lsalen - OSPF6_LSA_HEADER_SIZE - OSPF6_ROUTER_LSA_MIN_SIZE) + % OSPF6_ROUTER_LSDESC_FIX_SIZE) { + zlog_warn( + "%s: Router LSA interface description alignment error", + __func__); + return MSG_NG; + } + break; + case OSPF6_LSTYPE_NETWORK: + /* RFC5340 A.4.4, LSA header + OSPF6_NETWORK_LSA_MIN_SIZE bytes + followed by N>=0 attached router descriptions. */ + if ((lsalen - OSPF6_LSA_HEADER_SIZE + - OSPF6_NETWORK_LSA_MIN_SIZE) + % OSPF6_NETWORK_LSDESC_FIX_SIZE) { + zlog_warn( + "%s: Network LSA router description alignment error", + __func__); + return MSG_NG; + } + break; + case OSPF6_LSTYPE_INTER_PREFIX: + /* RFC5340 A.4.5, LSA header + OSPF6_INTER_PREFIX_LSA_MIN_SIZE + bytes + followed by 3-4 fields of a single IPv6 prefix. */ + if (headeronly) + break; + return ospf6_prefixes_examin( + (struct ospf6_prefix + *)((caddr_t)lsah + OSPF6_LSA_HEADER_SIZE + + OSPF6_INTER_PREFIX_LSA_MIN_SIZE), + lsalen - OSPF6_LSA_HEADER_SIZE + - OSPF6_INTER_PREFIX_LSA_MIN_SIZE, + 1); + case OSPF6_LSTYPE_INTER_ROUTER: + /* RFC5340 A.4.6, fixed-size LSA. */ + if (lsalen + > OSPF6_LSA_HEADER_SIZE + OSPF6_INTER_ROUTER_LSA_FIX_SIZE) { + zlog_warn("%s: Inter Router LSA oversized (%u B) LSA", + __func__, lsalen); + return MSG_NG; + } + break; + case OSPF6_LSTYPE_AS_EXTERNAL: /* RFC5340 A.4.7, same as A.4.8. */ + case OSPF6_LSTYPE_TYPE_7: + /* RFC5340 A.4.8, LSA header + OSPF6_AS_EXTERNAL_LSA_MIN_SIZE + bytes + followed by 3-4 fields of IPv6 prefix and 3 conditional LSA + fields: + 16 bytes of forwarding address, 4 bytes of external route + tag, + 4 bytes of referenced link state ID. */ + if (headeronly) + break; + as_external_lsa = + (struct ospf6_as_external_lsa + *)((caddr_t)lsah + OSPF6_LSA_HEADER_SIZE); + exp_length = + OSPF6_LSA_HEADER_SIZE + OSPF6_AS_EXTERNAL_LSA_MIN_SIZE; + /* To find out if the last optional field (Referenced Link State + ID) is + assumed in this LSA, we need to access fixed fields of the + IPv6 + prefix before ospf6_prefix_examin() confirms its sizing. */ + if (exp_length + OSPF6_PREFIX_MIN_SIZE > lsalen) { + zlog_warn( + "%s: AS External undersized (%u B) LSA header", + __func__, lsalen); + return MSG_NG; + } + /* forwarding address */ + if (CHECK_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_F)) + exp_length += 16; + /* external route tag */ + if (CHECK_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_T)) + exp_length += 4; + /* referenced link state ID */ + if (as_external_lsa->prefix.u._prefix_referenced_lstype) + exp_length += 4; + /* All the fixed-size fields (mandatory and optional) must fit. + I.e., + this check does not include any IPv6 prefix fields. */ + if (exp_length > lsalen) { + zlog_warn( + "%s: AS External undersized (%u B) LSA header", + __func__, lsalen); + return MSG_NG; + } + /* The last call completely covers the remainder (IPv6 prefix). + */ + return ospf6_prefixes_examin( + (struct ospf6_prefix + *)((caddr_t)as_external_lsa + + OSPF6_AS_EXTERNAL_LSA_MIN_SIZE), + lsalen - exp_length, 1); + case OSPF6_LSTYPE_LINK: + /* RFC5340 A.4.9, LSA header + OSPF6_LINK_LSA_MIN_SIZE bytes + followed + by N>=0 IPv6 prefix blocks (with N declared beforehand). */ + if (headeronly) + break; + link_lsa = (struct ospf6_link_lsa *)((caddr_t)lsah + + OSPF6_LSA_HEADER_SIZE); + return ospf6_prefixes_examin( + (struct ospf6_prefix *)((caddr_t)link_lsa + + OSPF6_LINK_LSA_MIN_SIZE), + lsalen - OSPF6_LSA_HEADER_SIZE + - OSPF6_LINK_LSA_MIN_SIZE, + ntohl(link_lsa->prefix_num) /* 32 bits */ + ); + case OSPF6_LSTYPE_INTRA_PREFIX: + /* RFC5340 A.4.10, LSA header + OSPF6_INTRA_PREFIX_LSA_MIN_SIZE + bytes + followed by N>=0 IPv6 prefixes (with N declared beforehand). + */ + if (headeronly) + break; + intra_prefix_lsa = + (struct ospf6_intra_prefix_lsa + *)((caddr_t)lsah + OSPF6_LSA_HEADER_SIZE); + return ospf6_prefixes_examin( + (struct ospf6_prefix + *)((caddr_t)intra_prefix_lsa + + OSPF6_INTRA_PREFIX_LSA_MIN_SIZE), + lsalen - OSPF6_LSA_HEADER_SIZE + - OSPF6_INTRA_PREFIX_LSA_MIN_SIZE, + ntohs(intra_prefix_lsa->prefix_num) /* 16 bits */ + ); + case OSPF6_LSTYPE_GRACE_LSA: + if (lsalen < OSPF6_LSA_HEADER_SIZE + GRACE_PERIOD_TLV_SIZE + + GRACE_RESTART_REASON_TLV_SIZE) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug("%s: Undersized GraceLSA.", + __func__); + return MSG_NG; + } + } + /* No additional validation is possible for unknown LSA types, which are + themselves valid in OPSFv3, hence the default decision is to accept. + */ + return MSG_OK; +} + +/* Verify if the provided input buffer is a valid sequence of LSAs. This + includes verification of LSA blocks length/alignment and dispatching + of deeper-level checks. */ +static unsigned +ospf6_lsaseq_examin(struct ospf6_lsa_header *lsah, /* start of buffered data */ + size_t length, const uint8_t headeronly, + /* When declared_num_lsas is not 0, compare it to the real + number of LSAs + and treat the difference as an error. */ + const uint32_t declared_num_lsas) +{ + uint32_t counted_lsas = 0; + + while (length) { + uint16_t lsalen; + if (length < OSPF6_LSA_HEADER_SIZE) { + zlog_warn( + "%s: undersized (%zu B) trailing (#%u) LSA header", + __func__, length, counted_lsas); + return MSG_NG; + } + /* save on ntohs() calls here and in the LSA validator */ + lsalen = OSPF6_LSA_SIZE(lsah); + if (lsalen < OSPF6_LSA_HEADER_SIZE) { + zlog_warn( + "%s: malformed LSA header #%u, declared length is %u B", + __func__, counted_lsas, lsalen); + return MSG_NG; + } + if (headeronly) { + /* less checks here and in ospf6_lsa_examin() */ + if (MSG_OK != ospf6_lsa_examin(lsah, lsalen, 1)) { + zlog_warn( + "%s: anomaly in header-only %s LSA #%u", + __func__, ospf6_lstype_name(lsah->type), + counted_lsas); + return MSG_NG; + } + lsah = (struct ospf6_lsa_header + *)((caddr_t)lsah + + OSPF6_LSA_HEADER_SIZE); + length -= OSPF6_LSA_HEADER_SIZE; + } else { + /* make sure the input buffer is deep enough before + * further checks */ + if (lsalen > length) { + zlog_warn( + "%s: anomaly in %s LSA #%u: declared length is %u B, buffered length is %zu B", + __func__, ospf6_lstype_name(lsah->type), + counted_lsas, lsalen, length); + return MSG_NG; + } + if (MSG_OK != ospf6_lsa_examin(lsah, lsalen, 0)) { + zlog_warn("%s: anomaly in %s LSA #%u", __func__, + ospf6_lstype_name(lsah->type), + counted_lsas); + return MSG_NG; + } + lsah = (struct ospf6_lsa_header *)((caddr_t)lsah + + lsalen); + length -= lsalen; + } + counted_lsas++; + } + + if (declared_num_lsas && counted_lsas != declared_num_lsas) { + zlog_warn("%s: #LSAs declared (%u) does not match actual (%u)", + __func__, declared_num_lsas, counted_lsas); + return MSG_NG; + } + return MSG_OK; +} + +/* Verify a complete OSPF packet for proper sizing/alignment. */ +static unsigned ospf6_packet_examin(struct ospf6_header *oh, + const unsigned bytesonwire) +{ + struct ospf6_lsupdate *lsupd; + unsigned test; + + /* length, 1st approximation */ + if (bytesonwire < OSPF6_HEADER_SIZE) { + zlog_warn("%s: undersized (%u B) packet", __func__, + bytesonwire); + return MSG_NG; + } + + /* Now it is safe to access header fields. */ + if (bytesonwire != ntohs(oh->length)) { + zlog_warn("%s: %s packet length error (%u real, %u declared)", + __func__, ospf6_message_type(oh->type), bytesonwire, + ntohs(oh->length)); + return MSG_NG; + } + + /* version check */ + if (oh->version != OSPFV3_VERSION) { + zlog_warn("%s: invalid (%u) protocol version", __func__, + oh->version); + return MSG_NG; + } + /* length, 2nd approximation */ + if (oh->type < OSPF6_MESSAGE_TYPE_ALL && ospf6_packet_minlen[oh->type] + && bytesonwire + < OSPF6_HEADER_SIZE + ospf6_packet_minlen[oh->type]) { + zlog_warn("%s: undersized (%u B) %s packet", __func__, + bytesonwire, ospf6_message_type(oh->type)); + return MSG_NG; + } + /* type-specific deeper validation */ + switch (oh->type) { + case OSPF6_MESSAGE_TYPE_HELLO: + /* RFC5340 A.3.2, packet header + OSPF6_HELLO_MIN_SIZE bytes + followed + by N>=0 router-IDs. */ + if (0 + == (bytesonwire - OSPF6_HEADER_SIZE - OSPF6_HELLO_MIN_SIZE) + % 4) + return MSG_OK; + zlog_warn("%s: alignment error in %s packet", __func__, + ospf6_message_type(oh->type)); + return MSG_NG; + case OSPF6_MESSAGE_TYPE_DBDESC: + /* RFC5340 A.3.3, packet header + OSPF6_DB_DESC_MIN_SIZE bytes + followed + by N>=0 header-only LSAs. */ + test = ospf6_lsaseq_examin( + (struct ospf6_lsa_header *)((caddr_t)oh + + OSPF6_HEADER_SIZE + + OSPF6_DB_DESC_MIN_SIZE), + bytesonwire - OSPF6_HEADER_SIZE + - OSPF6_DB_DESC_MIN_SIZE, + 1, 0); + break; + case OSPF6_MESSAGE_TYPE_LSREQ: + /* RFC5340 A.3.4, packet header + N>=0 LS description blocks. */ + if (0 + == (bytesonwire - OSPF6_HEADER_SIZE - OSPF6_LS_REQ_MIN_SIZE) + % OSPF6_LSREQ_LSDESC_FIX_SIZE) + return MSG_OK; + zlog_warn("%s: alignment error in %s packet", __func__, + ospf6_message_type(oh->type)); + return MSG_NG; + case OSPF6_MESSAGE_TYPE_LSUPDATE: + /* RFC5340 A.3.5, packet header + OSPF6_LS_UPD_MIN_SIZE bytes + followed + by N>=0 full LSAs (with N declared beforehand). */ + lsupd = (struct ospf6_lsupdate *)((caddr_t)oh + + OSPF6_HEADER_SIZE); + test = ospf6_lsaseq_examin( + (struct ospf6_lsa_header *)((caddr_t)lsupd + + OSPF6_LS_UPD_MIN_SIZE), + bytesonwire - OSPF6_HEADER_SIZE - OSPF6_LS_UPD_MIN_SIZE, + 0, ntohl(lsupd->lsa_number) /* 32 bits */ + ); + break; + case OSPF6_MESSAGE_TYPE_LSACK: + /* RFC5340 A.3.6, packet header + N>=0 header-only LSAs. */ + test = ospf6_lsaseq_examin( + (struct ospf6_lsa_header *)((caddr_t)oh + + OSPF6_HEADER_SIZE + + OSPF6_LS_ACK_MIN_SIZE), + bytesonwire - OSPF6_HEADER_SIZE - OSPF6_LS_ACK_MIN_SIZE, + 1, 0); + break; + default: + zlog_warn("%s: invalid (%u) message type", __func__, oh->type); + return MSG_NG; + } + if (test != MSG_OK) + zlog_warn("%s: anomaly in %s packet", __func__, + ospf6_message_type(oh->type)); + return test; +} + +/* Verify particular fields of otherwise correct received OSPF packet to + meet the requirements of RFC. */ +static int ospf6_rxpacket_examin(struct ospf6_interface *oi, + struct ospf6_header *oh, + const unsigned bytesonwire) +{ + struct ospf6_neighbor *on; + + if (MSG_OK != ospf6_packet_examin(oh, bytesonwire)) + return MSG_NG; + + on = ospf6_neighbor_lookup(oh->router_id, oi); + + /* Area-ID check */ + if (oh->area_id != oi->area->area_id) { + if (oh->area_id == OSPF_AREA_BACKBONE) + zlog_warn( + "VRF %s: I/F %s (%s, Router-ID: %pI4) Message may be via Virtual Link: not supported", + oi->interface->vrf->name, oi->interface->name, + on ? on->name : "null", &oh->router_id); + else + zlog_warn( + "VRF %s: I/F %s (%s, Router-ID: %pI4) Area-ID mismatch (my %pI4, rcvd %pI4)", + oi->interface->vrf->name, oi->interface->name, + on ? on->name : "null", &oh->router_id, + &oi->area->area_id, &oh->area_id); + return MSG_NG; + } + + /* Instance-ID check */ + if (oh->instance_id != oi->instance_id) { + zlog_warn( + "VRF %s: I/F %s (%s, Router-ID: %pI4) Instance-ID mismatch (my %u, rcvd %u)", + oi->interface->vrf->name, oi->interface->name, + on ? on->name : "null", &oh->router_id, oi->instance_id, + oh->instance_id); + return MSG_NG; + } + + /* Router-ID check */ + if (oh->router_id == oi->area->ospf6->router_id) { + zlog_warn("VRF %s: I/F %s Duplicate Router-ID (%pI4)", + oi->interface->vrf->name, oi->interface->name, + &oh->router_id); + return MSG_NG; + } + return MSG_OK; +} + +static void ospf6_lsupdate_recv(struct in6_addr *src, struct in6_addr *dst, + struct ospf6_interface *oi, + struct ospf6_header *oh) +{ + struct ospf6_neighbor *on; + struct ospf6_lsupdate *lsupdate; + char *p; + + on = ospf6_neighbor_lookup(oh->router_id, oi); + if (on == NULL) { + if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR)) + zlog_debug("Neighbor not found, ignore"); + return; + } + + if (on->state != OSPF6_NEIGHBOR_EXCHANGE + && on->state != OSPF6_NEIGHBOR_LOADING + && on->state != OSPF6_NEIGHBOR_FULL) { + if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR)) + zlog_debug("Neighbor state less than Exchange, ignore"); + return; + } + + lsupdate = (struct ospf6_lsupdate *)((caddr_t)oh + + sizeof(struct ospf6_header)); + + oi->ls_upd_in++; + + /* Process LSAs */ + for (p = (char *)((caddr_t)lsupdate + sizeof(struct ospf6_lsupdate)); + p < OSPF6_MESSAGE_END(oh) + && p + OSPF6_LSA_SIZE(p) <= OSPF6_MESSAGE_END(oh); + p += OSPF6_LSA_SIZE(p)) { + ospf6_receive_lsa(on, (struct ospf6_lsa_header *)p); + } + + assert(p == OSPF6_MESSAGE_END(oh)); +} + +static void ospf6_lsack_recv(struct in6_addr *src, struct in6_addr *dst, + struct ospf6_interface *oi, + struct ospf6_header *oh) +{ + struct ospf6_neighbor *on; + char *p; + struct ospf6_lsa *his, *mine; + struct ospf6_lsdb *lsdb = NULL; + + assert(oh->type == OSPF6_MESSAGE_TYPE_LSACK); + + on = ospf6_neighbor_lookup(oh->router_id, oi); + if (on == NULL) { + if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR)) + zlog_debug("Neighbor not found, ignore"); + return; + } + + if (on->state != OSPF6_NEIGHBOR_EXCHANGE + && on->state != OSPF6_NEIGHBOR_LOADING + && on->state != OSPF6_NEIGHBOR_FULL) { + if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR)) + zlog_debug("Neighbor state less than Exchange, ignore"); + return; + } + + oi->ls_ack_in++; + + for (p = (char *)((caddr_t)oh + sizeof(struct ospf6_header)); + p + sizeof(struct ospf6_lsa_header) <= OSPF6_MESSAGE_END(oh); + p += sizeof(struct ospf6_lsa_header)) { + his = ospf6_lsa_create_headeronly((struct ospf6_lsa_header *)p); + + switch (OSPF6_LSA_SCOPE(his->header->type)) { + case OSPF6_SCOPE_LINKLOCAL: + lsdb = on->ospf6_if->lsdb; + break; + case OSPF6_SCOPE_AREA: + lsdb = on->ospf6_if->area->lsdb; + break; + case OSPF6_SCOPE_AS: + lsdb = on->ospf6_if->area->ospf6->lsdb; + break; + case OSPF6_SCOPE_RESERVED: + if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV)) + zlog_debug("Ignoring LSA of reserved scope"); + ospf6_lsa_delete(his); + continue; + } + + if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV)) + zlog_debug("%s acknowledged by %s", his->name, + on->name); + + /* Find database copy */ + mine = ospf6_lsdb_lookup(his->header->type, his->header->id, + his->header->adv_router, lsdb); + if (mine == NULL) { + if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV)) + zlog_debug("No database copy"); + ospf6_lsa_delete(his); + continue; + } + + /* Check if the LSA is on his retrans-list */ + mine = ospf6_lsdb_lookup(his->header->type, his->header->id, + his->header->adv_router, + on->retrans_list); + if (mine == NULL) { + if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV)) + zlog_debug("Not on %s's retrans-list", + on->name); + ospf6_lsa_delete(his); + continue; + } + + if (ospf6_lsa_compare(his, mine) != 0) { + /* Log this questionable acknowledgement, + and examine the next one. */ + if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV)) + zlog_debug("Questionable acknowledgement"); + ospf6_lsa_delete(his); + continue; + } + + if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV)) + zlog_debug( + "Acknowledged, remove from %s's retrans-list", + on->name); + + ospf6_decrement_retrans_count(mine); + if (OSPF6_LSA_IS_MAXAGE(mine)) + ospf6_maxage_remove(on->ospf6_if->area->ospf6); + ospf6_lsdb_remove(mine, on->retrans_list); + ospf6_lsa_delete(his); + } + + assert(p == OSPF6_MESSAGE_END(oh)); +} + +static uint8_t *recvbuf = NULL; +static uint8_t *sendbuf = NULL; +static unsigned int iobuflen = 0; + +int ospf6_iobuf_size(unsigned int size) +{ + /* NB: there was previously code here that tried to dynamically size + * the buffer for whatever we see in MTU on interfaces. Which is + * _unconditionally wrong_ - we can always receive fragmented IPv6 + * up to the regular 64k length limit. (No jumbograms, thankfully.) + */ + + if (!iobuflen) { + /* the + 128 is to have some runway at the end */ + size_t alloc_size = 65536 + 128; + + assert(!recvbuf && !sendbuf); + + recvbuf = XMALLOC(MTYPE_OSPF6_MESSAGE, alloc_size); + sendbuf = XMALLOC(MTYPE_OSPF6_MESSAGE, alloc_size); + iobuflen = alloc_size; + } + + return iobuflen; +} + +void ospf6_message_terminate(void) +{ + XFREE(MTYPE_OSPF6_MESSAGE, recvbuf); + XFREE(MTYPE_OSPF6_MESSAGE, sendbuf); + + iobuflen = 0; +} + +enum ospf6_read_return_enum { + OSPF6_READ_ERROR, + OSPF6_READ_CONTINUE, +}; + +static int ospf6_read_helper(int sockfd, struct ospf6 *ospf6) +{ + int len; + struct in6_addr src, dst; + ifindex_t ifindex; + struct iovec iovector[2]; + struct ospf6_interface *oi; + struct ospf6_header *oh; + enum ospf6_auth_err ret = OSPF6_AUTH_PROCESS_NORMAL; + uint32_t at_len = 0; + uint32_t lls_len = 0; + + /* initialize */ + memset(&src, 0, sizeof(src)); + memset(&dst, 0, sizeof(dst)); + ifindex = 0; + iovector[0].iov_base = recvbuf; + iovector[0].iov_len = iobuflen; + iovector[1].iov_base = NULL; + iovector[1].iov_len = 0; + + /* receive message */ + len = ospf6_recvmsg(&src, &dst, &ifindex, iovector, sockfd); + if (len < 0) + return OSPF6_READ_ERROR; + + if ((uint)len > iobuflen) { + flog_err(EC_LIB_DEVELOPMENT, "Excess message read"); + return OSPF6_READ_ERROR; + } + + /* ensure some zeroes past the end, just as a security precaution */ + memset(recvbuf + len, 0, MIN(128, iobuflen - len)); + + oi = ospf6_interface_lookup_by_ifindex(ifindex, ospf6->vrf_id); + if (oi == NULL || oi->area == NULL + || CHECK_FLAG(oi->flag, OSPF6_INTERFACE_DISABLE)) { + if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, + RECV_HDR)) + zlog_debug("Message received on disabled interface"); + return OSPF6_READ_CONTINUE; + } + if (CHECK_FLAG(oi->flag, OSPF6_INTERFACE_PASSIVE)) { + if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, + RECV_HDR)) + zlog_debug("%s: Ignore message on passive interface %s", + __func__, oi->interface->name); + return OSPF6_READ_CONTINUE; + } + + /* + * Drop packet destined to another VRF. + * This happens when raw_l3mdev_accept is set to 1. + */ + if (ospf6->vrf_id != oi->interface->vrf->vrf_id) + return OSPF6_READ_CONTINUE; + + oh = (struct ospf6_header *)recvbuf; + ret = ospf6_auth_validate_pkt(oi, (uint32_t *)&len, oh, &at_len, + &lls_len); + if (ret == OSPF6_AUTH_VALIDATE_SUCCESS) { + ret = ospf6_auth_check_digest(oh, oi, &src, lls_len); + if (ret == OSPF6_AUTH_VALIDATE_FAILURE) { + if (IS_OSPF6_DEBUG_AUTH_RX) + zlog_err( + "RECV[%s]: OSPF packet auth digest miss-match on %s", + oi->interface->name, + ospf6_message_type(oh->type)); + oi->at_data.rx_drop++; + return OSPF6_READ_CONTINUE; + } + } else if (ret == OSPF6_AUTH_VALIDATE_FAILURE) { + oi->at_data.rx_drop++; + return OSPF6_READ_CONTINUE; + } + + if (ospf6_rxpacket_examin(oi, oh, len) != MSG_OK) + return OSPF6_READ_CONTINUE; + + /* Being here means, that no sizing/alignment issues were detected in + the input packet. This renders the additional checks performed below + and also in the type-specific dispatching functions a dead code, + which can be dismissed in a cleanup-focused review round later. */ + + /* Log */ + if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR)) { + zlog_debug("%s received on %s", ospf6_message_type(oh->type), + oi->interface->name); + zlog_debug(" src: %pI6", &src); + zlog_debug(" dst: %pI6", &dst); + + switch (oh->type) { + case OSPF6_MESSAGE_TYPE_HELLO: + ospf6_hello_print(oh, OSPF6_ACTION_RECV); + break; + case OSPF6_MESSAGE_TYPE_DBDESC: + ospf6_dbdesc_print(oh, OSPF6_ACTION_RECV); + break; + case OSPF6_MESSAGE_TYPE_LSREQ: + ospf6_lsreq_print(oh, OSPF6_ACTION_RECV); + break; + case OSPF6_MESSAGE_TYPE_LSUPDATE: + ospf6_lsupdate_print(oh, OSPF6_ACTION_RECV); + break; + case OSPF6_MESSAGE_TYPE_LSACK: + ospf6_lsack_print(oh, OSPF6_ACTION_RECV); + break; + default: + assert(0); + } + + if ((at_len != 0) && IS_OSPF6_DEBUG_AUTH_RX) + ospf6_auth_hdr_dump_recv(oh, (len + at_len + lls_len), + lls_len); + } + + switch (oh->type) { + case OSPF6_MESSAGE_TYPE_HELLO: + ospf6_hello_recv(&src, &dst, oi, oh); + break; + + case OSPF6_MESSAGE_TYPE_DBDESC: + ospf6_dbdesc_recv(&src, &dst, oi, oh); + break; + + case OSPF6_MESSAGE_TYPE_LSREQ: + ospf6_lsreq_recv(&src, &dst, oi, oh); + break; + + case OSPF6_MESSAGE_TYPE_LSUPDATE: + ospf6_lsupdate_recv(&src, &dst, oi, oh); + break; + + case OSPF6_MESSAGE_TYPE_LSACK: + ospf6_lsack_recv(&src, &dst, oi, oh); + break; + + default: + assert(0); + } + + return OSPF6_READ_CONTINUE; +} + +void ospf6_receive(struct event *thread) +{ + int sockfd; + struct ospf6 *ospf6; + int count = 0; + + /* add next read thread */ + ospf6 = EVENT_ARG(thread); + sockfd = EVENT_FD(thread); + + event_add_read(master, ospf6_receive, ospf6, ospf6->fd, + &ospf6->t_ospf6_receive); + + while (count < ospf6->write_oi_count) { + count++; + switch (ospf6_read_helper(sockfd, ospf6)) { + case OSPF6_READ_ERROR: + return; + case OSPF6_READ_CONTINUE: + break; + } + } +} + +static void ospf6_fill_hdr_checksum(struct ospf6_interface *oi, + struct ospf6_packet *op) +{ + struct ipv6_ph ph = {}; + struct ospf6_header *oh; + void *offset = NULL; + + if (oi->at_data.flags != 0) + return; + + memcpy(&ph.src, oi->linklocal_addr, sizeof(struct in6_addr)); + memcpy(&ph.dst, &op->dst, sizeof(struct in6_addr)); + ph.ulpl = htonl(op->length); + ph.next_hdr = IPPROTO_OSPFIGP; + + /* Suppress static analysis warnings about accessing icmp6 oob */ + oh = (struct ospf6_header *)STREAM_DATA(op->s); + offset = oh; + oh->checksum = in_cksum_with_ph6(&ph, offset, op->length); +} + +static void ospf6_make_header(uint8_t type, struct ospf6_interface *oi, + struct stream *s) +{ + struct ospf6_header *oh; + + oh = (struct ospf6_header *)STREAM_DATA(s); + + oh->version = (uint8_t)OSPFV3_VERSION; + oh->type = type; + oh->length = 0; + + oh->router_id = oi->area->ospf6->router_id; + oh->area_id = oi->area->area_id; + oh->checksum = 0; + oh->instance_id = oi->instance_id; + oh->reserved = 0; + + stream_forward_endp(s, OSPF6_HEADER_SIZE); +} + +static void ospf6_fill_header(struct ospf6_interface *oi, struct stream *s, + uint16_t length) +{ + struct ospf6_header *oh; + + oh = (struct ospf6_header *)STREAM_DATA(s); + + oh->length = htons(length); +} + +static void ospf6_fill_lsupdate_header(struct stream *s, uint32_t lsa_num) +{ + struct ospf6_header *oh; + struct ospf6_lsupdate *lsu; + + oh = (struct ospf6_header *)STREAM_DATA(s); + + lsu = (struct ospf6_lsupdate *)((caddr_t)oh + + sizeof(struct ospf6_header)); + lsu->lsa_number = htonl(lsa_num); +} + +static void ospf6_auth_trailer_copy_keychain_key(struct ospf6_interface *oi) +{ + char *keychain_name = NULL; + struct keychain *keychain = NULL; + struct key *key = NULL; + + keychain_name = oi->at_data.keychain; + keychain = keychain_lookup(keychain_name); + if (keychain) { + key = key_lookup_for_send(keychain); + if (key && key->string && + key->hash_algo != KEYCHAIN_ALGO_NULL) { + /* storing the values so that further + * lookup can be avoided. after + * processing the digest need to reset + * these values + */ + oi->at_data.hash_algo = key->hash_algo; + if (oi->at_data.auth_key) + XFREE(MTYPE_OSPF6_AUTH_MANUAL_KEY, + oi->at_data.auth_key); + oi->at_data.auth_key = XSTRDUP( + MTYPE_OSPF6_AUTH_MANUAL_KEY, key->string); + oi->at_data.key_id = key->index; + SET_FLAG(oi->at_data.flags, + OSPF6_AUTH_TRAILER_KEYCHAIN_VALID); + } + } +} + +static uint16_t ospf6_packet_max(struct ospf6_interface *oi) +{ + uint16_t at_len = 0; + + assert(oi->ifmtu > sizeof(struct ip6_hdr)); + + if (oi->at_data.flags != 0) { + if (CHECK_FLAG(oi->at_data.flags, OSPF6_AUTH_TRAILER_KEYCHAIN)) + ospf6_auth_trailer_copy_keychain_key(oi); + + at_len += OSPF6_AUTH_HDR_MIN_SIZE; + at_len += keychain_get_hash_len(oi->at_data.hash_algo); + return oi->ifmtu - (sizeof(struct ip6_hdr)) - at_len; + } + + return oi->ifmtu - (sizeof(struct ip6_hdr)); +} + +static uint16_t ospf6_make_hello(struct ospf6_interface *oi, struct stream *s) +{ + struct listnode *node, *nnode; + struct ospf6_neighbor *on; + uint16_t length = OSPF6_HELLO_MIN_SIZE; + uint8_t options1 = oi->area->options[1]; + + if (oi->at_data.flags != 0) + options1 |= OSPF6_OPT_AT; + + stream_putl(s, oi->interface->ifindex); + stream_putc(s, oi->priority); + stream_putc(s, oi->area->options[0]); + stream_putc(s, options1); + stream_putc(s, oi->area->options[2]); + stream_putw(s, oi->hello_interval); + stream_putw(s, oi->dead_interval); + stream_put_ipv4(s, oi->drouter); + stream_put_ipv4(s, oi->bdrouter); + + for (ALL_LIST_ELEMENTS(oi->neighbor_list, node, nnode, on)) { + if (on->state < OSPF6_NEIGHBOR_INIT) + continue; + + if ((length + sizeof(uint32_t) + OSPF6_HEADER_SIZE) + > ospf6_packet_max(oi)) { + if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_HELLO, + SEND)) + zlog_debug( + "sending Hello message: exceeds I/F MTU"); + break; + } + + stream_put_ipv4(s, on->router_id); + length += sizeof(uint32_t); + } + + return length; +} + +static void ospf6_write(struct event *thread) +{ + struct ospf6 *ospf6 = EVENT_ARG(thread); + struct ospf6_interface *oi; + struct ospf6_header *oh; + struct ospf6_packet *op; + struct listnode *node; + struct iovec iovector[2]; + int pkt_count = 0; + int len; + int64_t latency = 0; + struct timeval timestamp; + uint16_t at_len = 0; + + if (ospf6->fd < 0) { + zlog_warn("ospf6_write failed to send, fd %d", ospf6->fd); + return; + } + + node = listhead(ospf6->oi_write_q); + assert(node); + oi = listgetdata(node); + + while ((pkt_count < ospf6->write_oi_count) && oi) { + op = ospf6_fifo_head(oi->obuf); + assert(op); + assert(op->length >= OSPF6_HEADER_SIZE); + + iovector[0].iov_base = (caddr_t)stream_pnt(op->s); + iovector[0].iov_len = op->length; + iovector[1].iov_base = NULL; + iovector[1].iov_len = 0; + + oh = (struct ospf6_header *)STREAM_DATA(op->s); + + if (oi->at_data.flags != 0) { + at_len = ospf6_auth_len_get(oi); + if (at_len) { + iovector[0].iov_len = + ntohs(oh->length) + at_len; + ospf6_auth_digest_send(oi->linklocal_addr, oi, + oh, at_len, + iovector[0].iov_len); + } else { + iovector[0].iov_len = ntohs(oh->length); + } + } else { + iovector[0].iov_len = ntohs(oh->length); + } + + len = ospf6_sendmsg(oi->linklocal_addr, &op->dst, + oi->interface->ifindex, iovector, + ospf6->fd); + + if (len != (op->length + (int)at_len)) + flog_err(EC_LIB_DEVELOPMENT, + "Could not send entire message"); + + if (IS_OSPF6_DEBUG_MESSAGE(oh->type, SEND_HDR)) { + zlog_debug("%s send on %s", + ospf6_message_type(oh->type), + oi->interface->name); + zlog_debug(" src: %pI6", oi->linklocal_addr); + zlog_debug(" dst: %pI6", &op->dst); + switch (oh->type) { + case OSPF6_MESSAGE_TYPE_HELLO: + ospf6_hello_print(oh, OSPF6_ACTION_SEND); + break; + case OSPF6_MESSAGE_TYPE_DBDESC: + ospf6_dbdesc_print(oh, OSPF6_ACTION_SEND); + break; + case OSPF6_MESSAGE_TYPE_LSREQ: + ospf6_lsreq_print(oh, OSPF6_ACTION_SEND); + break; + case OSPF6_MESSAGE_TYPE_LSUPDATE: + ospf6_lsupdate_print(oh, OSPF6_ACTION_SEND); + break; + case OSPF6_MESSAGE_TYPE_LSACK: + ospf6_lsack_print(oh, OSPF6_ACTION_SEND); + break; + default: + zlog_debug("Unknown message"); + assert(0); + break; + } + } + switch (oh->type) { + case OSPF6_MESSAGE_TYPE_HELLO: + monotime(×tamp); + if (oi->hello_out) + latency = monotime_since(&oi->last_hello, NULL) + - ((int64_t)oi->hello_interval + * 1000000); + + /* log if latency exceeds the hello period */ + if (latency > ((int64_t)oi->hello_interval * 1000000)) + zlog_warn("%s hello TX high latency %" PRId64 + "us.", + __func__, latency); + oi->last_hello = timestamp; + oi->hello_out++; + break; + case OSPF6_MESSAGE_TYPE_DBDESC: + oi->db_desc_out++; + break; + case OSPF6_MESSAGE_TYPE_LSREQ: + oi->ls_req_out++; + break; + case OSPF6_MESSAGE_TYPE_LSUPDATE: + oi->ls_upd_out++; + break; + case OSPF6_MESSAGE_TYPE_LSACK: + oi->ls_ack_out++; + break; + default: + zlog_debug("Unknown message"); + assert(0); + break; + } + + if ((oi->at_data.flags != 0) && + (IS_OSPF6_DEBUG_MESSAGE(oh->type, SEND_HDR)) && + (IS_OSPF6_DEBUG_AUTH_TX)) + ospf6_auth_hdr_dump_send(oh, iovector[0].iov_len); + + /* initialize at_len to 0 for next packet */ + at_len = 0; + + /* Now delete packet from queue. */ + ospf6_packet_delete(oi); + + /* Move this interface to the tail of write_q to + serve everyone in a round robin fashion */ + list_delete_node(ospf6->oi_write_q, node); + if (ospf6_fifo_head(oi->obuf) == NULL) { + oi->on_write_q = 0; + oi = NULL; + } else { + listnode_add(ospf6->oi_write_q, oi); + } + + /* Setup to service from the head of the queue again */ + if (!list_isempty(ospf6->oi_write_q)) { + node = listhead(ospf6->oi_write_q); + oi = listgetdata(node); + } + } + + /* If packets still remain in queue, call write thread. */ + if (!list_isempty(ospf6->oi_write_q)) + event_add_write(master, ospf6_write, ospf6, ospf6->fd, + &ospf6->t_write); +} + +void ospf6_hello_send(struct event *thread) +{ + struct ospf6_interface *oi; + struct ospf6_packet *op; + uint16_t length = OSPF6_HEADER_SIZE; + + oi = (struct ospf6_interface *)EVENT_ARG(thread); + + /* Check if the GR hello-delay is active. */ + if (oi->gr.hello_delay.t_grace_send) + return; + + /* Check if config is still being processed */ + if (event_is_scheduled(t_ospf6_cfg)) { + if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_HELLO, SEND)) + zlog_debug( + "Suppressing Hello on interface %s during config load", + oi->interface->name); + event_add_timer(master, ospf6_hello_send, oi, + oi->hello_interval, &oi->thread_send_hello); + return; + } + + if (oi->state <= OSPF6_INTERFACE_DOWN) { + if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_HELLO, SEND_HDR)) + zlog_debug("Unable to send Hello on down interface %s", + oi->interface->name); + return; + } + + op = ospf6_packet_new(oi->ifmtu); + + ospf6_make_header(OSPF6_MESSAGE_TYPE_HELLO, oi, op->s); + + /* Prepare OSPF Hello body */ + length += ospf6_make_hello(oi, op->s); + if (length == OSPF6_HEADER_SIZE) { + /* Hello overshooting MTU */ + ospf6_packet_free(op); + return; + } + + /* Fill OSPF header. */ + ospf6_fill_header(oi, op->s, length); + + /* Set packet length. */ + op->length = length; + + op->dst = allspfrouters6; + + ospf6_fill_hdr_checksum(oi, op); + + /* Add packet to the top of the interface output queue, so that they + * can't get delayed by things like long queues of LS Update packets + */ + ospf6_packet_add_top(oi, op); + + /* set next thread */ + event_add_timer(master, ospf6_hello_send, oi, oi->hello_interval, + &oi->thread_send_hello); + + OSPF6_MESSAGE_WRITE_ON(oi); +} + +static uint16_t ospf6_make_dbdesc(struct ospf6_neighbor *on, struct stream *s) +{ + uint16_t length = OSPF6_DB_DESC_MIN_SIZE; + struct ospf6_lsa *lsa, *lsanext; + uint8_t options1 = on->ospf6_if->area->options[1]; + + if (on->ospf6_if->at_data.flags != 0) + options1 |= OSPF6_OPT_AT; + + /* if this is initial one, initialize sequence number for DbDesc */ + if (CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_IBIT) + && (on->dbdesc_seqnum == 0)) { + on->dbdesc_seqnum = frr_sequence32_next(); + } + + /* reserved */ + stream_putc(s, 0); /* reserved 1 */ + stream_putc(s, on->ospf6_if->area->options[0]); + stream_putc(s, options1); + stream_putc(s, on->ospf6_if->area->options[2]); + stream_putw(s, on->ospf6_if->ifmtu); + stream_putc(s, 0); /* reserved 2 */ + stream_putc(s, on->dbdesc_bits); + stream_putl(s, on->dbdesc_seqnum); + + /* if this is not initial one, set LSA headers in dbdesc */ + if (!CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_IBIT)) { + for (ALL_LSDB(on->dbdesc_list, lsa, lsanext)) { + ospf6_lsa_age_update_to_send(lsa, + on->ospf6_if->transdelay); + + /* MTU check */ + if ((length + sizeof(struct ospf6_lsa_header) + + OSPF6_HEADER_SIZE) + > ospf6_packet_max(on->ospf6_if)) { + ospf6_lsa_unlock(&lsa); + if (lsanext) + ospf6_lsa_unlock(&lsanext); + break; + } + stream_put(s, lsa->header, + sizeof(struct ospf6_lsa_header)); + length += sizeof(struct ospf6_lsa_header); + } + } + return length; +} + +void ospf6_dbdesc_send(struct event *thread) +{ + struct ospf6_neighbor *on; + uint16_t length = OSPF6_HEADER_SIZE; + struct ospf6_packet *op; + + on = (struct ospf6_neighbor *)EVENT_ARG(thread); + + if (on->state < OSPF6_NEIGHBOR_EXSTART) { + if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_DBDESC, SEND)) + zlog_debug( + "Quit to send DbDesc to neighbor %s state %s", + on->name, ospf6_neighbor_state_str[on->state]); + return; + } + + /* set next thread if master */ + if (CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MSBIT)) + event_add_timer(master, ospf6_dbdesc_send, on, + on->ospf6_if->rxmt_interval, + &on->thread_send_dbdesc); + + op = ospf6_packet_new(on->ospf6_if->ifmtu); + ospf6_make_header(OSPF6_MESSAGE_TYPE_DBDESC, on->ospf6_if, op->s); + + length += ospf6_make_dbdesc(on, op->s); + ospf6_fill_header(on->ospf6_if, op->s, length); + + /* Set packet length. */ + op->length = length; + + if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT) + op->dst = allspfrouters6; + else + op->dst = on->linklocal_addr; + + ospf6_fill_hdr_checksum(on->ospf6_if, op); + + ospf6_packet_add(on->ospf6_if, op); + + OSPF6_MESSAGE_WRITE_ON(on->ospf6_if); +} + +void ospf6_dbdesc_send_newone(struct event *thread) +{ + struct ospf6_neighbor *on; + struct ospf6_lsa *lsa, *lsanext; + unsigned int size = 0; + + on = (struct ospf6_neighbor *)EVENT_ARG(thread); + ospf6_lsdb_remove_all(on->dbdesc_list); + + /* move LSAs from summary_list to dbdesc_list (within neighbor + structure) + so that ospf6_send_dbdesc () can send those LSAs */ + size = sizeof(struct ospf6_lsa_header) + sizeof(struct ospf6_dbdesc); + for (ALL_LSDB(on->summary_list, lsa, lsanext)) { + /* if stub area then don't advertise AS-External LSAs */ + if ((IS_AREA_STUB(on->ospf6_if->area) + || IS_AREA_NSSA(on->ospf6_if->area)) + && ntohs(lsa->header->type) == OSPF6_LSTYPE_AS_EXTERNAL) { + ospf6_lsdb_remove(lsa, on->summary_list); + continue; + } + + if (size + sizeof(struct ospf6_lsa_header) + > ospf6_packet_max(on->ospf6_if)) { + ospf6_lsa_unlock(&lsa); + if (lsanext) + ospf6_lsa_unlock(&lsanext); + break; + } + + ospf6_lsdb_add(ospf6_lsa_copy(lsa), on->dbdesc_list); + ospf6_lsdb_remove(lsa, on->summary_list); + size += sizeof(struct ospf6_lsa_header); + } + + if (on->summary_list->count == 0) + UNSET_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MBIT); + + /* If slave, More bit check must be done here */ + if (!CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MSBIT) && /* Slave */ + !CHECK_FLAG(on->dbdesc_last.bits, OSPF6_DBDESC_MBIT) + && !CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MBIT)) + event_add_event(master, exchange_done, on, 0, + &on->thread_exchange_done); + + event_execute(master, ospf6_dbdesc_send, on, 0, NULL); +} + +static uint16_t ospf6_make_lsreq(struct ospf6_neighbor *on, struct stream *s) +{ + uint16_t length = 0; + struct ospf6_lsa *lsa, *lsanext, *last_req = NULL; + + for (ALL_LSDB(on->request_list, lsa, lsanext)) { + if ((length + OSPF6_HEADER_SIZE) + > ospf6_packet_max(on->ospf6_if)) { + ospf6_lsa_unlock(&lsa); + if (lsanext) + ospf6_lsa_unlock(&lsanext); + break; + } + stream_putw(s, 0); /* reserved */ + stream_putw(s, ntohs(lsa->header->type)); + stream_putl(s, ntohl(lsa->header->id)); + stream_putl(s, ntohl(lsa->header->adv_router)); + length += sizeof(struct ospf6_lsreq_entry); + last_req = lsa; + } + + if (last_req != NULL) { + if (on->last_ls_req != NULL) + ospf6_lsa_unlock(&on->last_ls_req); + + ospf6_lsa_lock(last_req); + on->last_ls_req = last_req; + } + + return length; +} + +static uint16_t ospf6_make_lsack_neighbor(struct ospf6_neighbor *on, + struct ospf6_packet **op) +{ + uint16_t length = 0; + struct ospf6_lsa *lsa, *lsanext; + int lsa_cnt = 0; + + for (ALL_LSDB(on->lsack_list, lsa, lsanext)) { + if ((length + sizeof(struct ospf6_lsa_header) + + OSPF6_HEADER_SIZE) + > ospf6_packet_max(on->ospf6_if)) { + /* if we run out of packet size/space here, + better to try again soon. */ + if (lsa_cnt) { + ospf6_fill_header(on->ospf6_if, (*op)->s, + length + OSPF6_HEADER_SIZE); + + (*op)->length = length + OSPF6_HEADER_SIZE; + (*op)->dst = on->linklocal_addr; + ospf6_fill_hdr_checksum(on->ospf6_if, *op); + ospf6_packet_add(on->ospf6_if, *op); + OSPF6_MESSAGE_WRITE_ON(on->ospf6_if); + /* new packet */ + *op = ospf6_packet_new(on->ospf6_if->ifmtu); + ospf6_make_header(OSPF6_MESSAGE_TYPE_LSACK, + on->ospf6_if, (*op)->s); + length = 0; + lsa_cnt = 0; + } + } + ospf6_lsa_age_update_to_send(lsa, on->ospf6_if->transdelay); + stream_put((*op)->s, lsa->header, + sizeof(struct ospf6_lsa_header)); + length += sizeof(struct ospf6_lsa_header); + + assert(lsa->lock == 2); + ospf6_lsdb_remove(lsa, on->lsack_list); + lsa_cnt++; + } + return length; +} + +void ospf6_lsreq_send(struct event *thread) +{ + struct ospf6_neighbor *on; + struct ospf6_packet *op; + uint16_t length = OSPF6_HEADER_SIZE; + + on = (struct ospf6_neighbor *)EVENT_ARG(thread); + + /* LSReq will be sent only in ExStart or Loading */ + if (on->state != OSPF6_NEIGHBOR_EXCHANGE + && on->state != OSPF6_NEIGHBOR_LOADING) { + if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_LSREQ, SEND_HDR)) + zlog_debug("Quit to send LSReq to neighbor %s state %s", + on->name, + ospf6_neighbor_state_str[on->state]); + return; + } + + /* schedule loading_done if request list is empty */ + if (on->request_list->count == 0) { + event_add_event(master, loading_done, on, 0, + &on->event_loading_done); + return; + } + + op = ospf6_packet_new(on->ospf6_if->ifmtu); + ospf6_make_header(OSPF6_MESSAGE_TYPE_LSREQ, on->ospf6_if, op->s); + + length += ospf6_make_lsreq(on, op->s); + + if (length == OSPF6_HEADER_SIZE) { + /* Hello overshooting MTU */ + ospf6_packet_free(op); + return; + } + + /* Fill OSPF header. */ + ospf6_fill_header(on->ospf6_if, op->s, length); + + /* Set packet length */ + op->length = length; + + if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT) + op->dst = allspfrouters6; + else + op->dst = on->linklocal_addr; + + ospf6_fill_hdr_checksum(on->ospf6_if, op); + ospf6_packet_add(on->ospf6_if, op); + + OSPF6_MESSAGE_WRITE_ON(on->ospf6_if); + + /* set next thread */ + if (on->request_list->count != 0) { + event_add_timer(master, ospf6_lsreq_send, on, + on->ospf6_if->rxmt_interval, + &on->thread_send_lsreq); + } +} + +static void ospf6_send_lsupdate(struct ospf6_neighbor *on, + struct ospf6_interface *oi, + struct ospf6_packet *op) +{ + if (on) { + if ((on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT) + || (on->ospf6_if->state == OSPF6_INTERFACE_DR) + || (on->ospf6_if->state == OSPF6_INTERFACE_BDR)) + op->dst = allspfrouters6; + else + op->dst = on->linklocal_addr; + oi = on->ospf6_if; + } else if (oi) { + if ((oi->state == OSPF6_INTERFACE_POINTTOPOINT) + || (oi->state == OSPF6_INTERFACE_DR) + || (oi->state == OSPF6_INTERFACE_BDR)) + op->dst = allspfrouters6; + else + op->dst = alldrouters6; + } + if (oi) { + struct ospf6 *ospf6; + + ospf6_fill_hdr_checksum(oi, op); + ospf6_packet_add(oi, op); + /* If ospf instance is being deleted, send the packet + * immediately + */ + if ((oi->area == NULL) || (oi->area->ospf6 == NULL)) + return; + + ospf6 = oi->area->ospf6; + if (ospf6->inst_shutdown) { + if (oi->on_write_q == 0) { + listnode_add(ospf6->oi_write_q, oi); + oi->on_write_q = 1; + } + /* + * When ospf6d immediately calls event_execute + * for items in the oi_write_q. The event_execute + * will call ospf6_write and cause the oi_write_q + * to be emptied. *IF* there is already an event + * scheduled for the oi_write_q by something else + * then when it wakes up in the future and attempts + * to cycle through items in the queue it will + * assert. Let's stop the t_write event and + * if ospf6_write doesn't finish up the work + * it will schedule itself again. + */ + event_cancel(&ospf6->t_write); + event_execute(master, ospf6_write, ospf6, 0, NULL); + } else + OSPF6_MESSAGE_WRITE_ON(oi); + } +} + +static uint16_t ospf6_make_lsupdate_list(struct ospf6_neighbor *on, + struct ospf6_packet **op, int *lsa_cnt) +{ + uint16_t length = OSPF6_LS_UPD_MIN_SIZE; + struct ospf6_lsa *lsa, *lsanext; + + /* skip over fixed header */ + stream_forward_endp((*op)->s, OSPF6_LS_UPD_MIN_SIZE); + + for (ALL_LSDB(on->lsupdate_list, lsa, lsanext)) { + if ((length + OSPF6_LSA_SIZE(lsa->header) + OSPF6_HEADER_SIZE) > + ospf6_packet_max(on->ospf6_if)) { + ospf6_fill_header(on->ospf6_if, (*op)->s, + length + OSPF6_HEADER_SIZE); + (*op)->length = length + OSPF6_HEADER_SIZE; + ospf6_fill_lsupdate_header((*op)->s, *lsa_cnt); + ospf6_send_lsupdate(on, NULL, *op); + + /* refresh packet */ + *op = ospf6_packet_new(on->ospf6_if->ifmtu); + length = OSPF6_LS_UPD_MIN_SIZE; + *lsa_cnt = 0; + ospf6_make_header(OSPF6_MESSAGE_TYPE_LSUPDATE, + on->ospf6_if, (*op)->s); + stream_forward_endp((*op)->s, OSPF6_LS_UPD_MIN_SIZE); + } + ospf6_lsa_age_update_to_send(lsa, on->ospf6_if->transdelay); + stream_put((*op)->s, lsa->header, OSPF6_LSA_SIZE(lsa->header)); + (*lsa_cnt)++; + length += OSPF6_LSA_SIZE(lsa->header); + assert(lsa->lock == 2); + ospf6_lsdb_remove(lsa, on->lsupdate_list); + } + return length; +} + +static uint16_t ospf6_make_ls_retrans_list(struct ospf6_neighbor *on, + struct ospf6_packet **op, + int *lsa_cnt) +{ + uint16_t length = OSPF6_LS_UPD_MIN_SIZE; + struct ospf6_lsa *lsa, *lsanext; + + /* skip over fixed header */ + stream_forward_endp((*op)->s, OSPF6_LS_UPD_MIN_SIZE); + + for (ALL_LSDB(on->retrans_list, lsa, lsanext)) { + if ((length + OSPF6_LSA_SIZE(lsa->header) + OSPF6_HEADER_SIZE) > + ospf6_packet_max(on->ospf6_if)) { + ospf6_fill_header(on->ospf6_if, (*op)->s, + length + OSPF6_HEADER_SIZE); + (*op)->length = length + OSPF6_HEADER_SIZE; + ospf6_fill_lsupdate_header((*op)->s, *lsa_cnt); + if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT) + (*op)->dst = allspfrouters6; + else + (*op)->dst = on->linklocal_addr; + + ospf6_fill_hdr_checksum(on->ospf6_if, *op); + ospf6_packet_add(on->ospf6_if, *op); + OSPF6_MESSAGE_WRITE_ON(on->ospf6_if); + + /* refresh packet */ + *op = ospf6_packet_new(on->ospf6_if->ifmtu); + length = OSPF6_LS_UPD_MIN_SIZE; + *lsa_cnt = 0; + ospf6_make_header(OSPF6_MESSAGE_TYPE_LSUPDATE, + on->ospf6_if, (*op)->s); + stream_forward_endp((*op)->s, OSPF6_LS_UPD_MIN_SIZE); + } + ospf6_lsa_age_update_to_send(lsa, on->ospf6_if->transdelay); + stream_put((*op)->s, lsa->header, OSPF6_LSA_SIZE(lsa->header)); + (*lsa_cnt)++; + length += OSPF6_LSA_SIZE(lsa->header); + } + return length; +} + +void ospf6_lsupdate_send_neighbor(struct event *thread) +{ + struct ospf6_neighbor *on; + struct ospf6_packet *op; + uint16_t length = OSPF6_HEADER_SIZE; + int lsa_cnt = 0; + + on = (struct ospf6_neighbor *)EVENT_ARG(thread); + + if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_LSUPDATE, SEND_HDR)) + zlog_debug("LSUpdate to neighbor %s", on->name); + + if (on->state < OSPF6_NEIGHBOR_EXCHANGE) { + if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_LSUPDATE, + SEND_HDR)) + zlog_debug("Quit to send (neighbor state %s)", + ospf6_neighbor_state_str[on->state]); + return; + } + + /* first do lsupdate_list */ + op = ospf6_packet_new(on->ospf6_if->ifmtu); + ospf6_make_header(OSPF6_MESSAGE_TYPE_LSUPDATE, on->ospf6_if, op->s); + length += ospf6_make_lsupdate_list(on, &op, &lsa_cnt); + if (lsa_cnt) { + /* Fill OSPF header. */ + ospf6_fill_header(on->ospf6_if, op->s, length); + ospf6_fill_lsupdate_header(op->s, lsa_cnt); + op->length = length; + ospf6_send_lsupdate(on, NULL, op); + + /* prepare new packet */ + op = ospf6_packet_new(on->ospf6_if->ifmtu); + length = OSPF6_HEADER_SIZE; + lsa_cnt = 0; + } else { + stream_reset(op->s); + length = OSPF6_HEADER_SIZE; + } + + ospf6_make_header(OSPF6_MESSAGE_TYPE_LSUPDATE, on->ospf6_if, op->s); + /* now do retransmit list */ + length += ospf6_make_ls_retrans_list(on, &op, &lsa_cnt); + if (lsa_cnt) { + ospf6_fill_header(on->ospf6_if, op->s, length); + ospf6_fill_lsupdate_header(op->s, lsa_cnt); + op->length = length; + if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT) + op->dst = allspfrouters6; + else + op->dst = on->linklocal_addr; + ospf6_fill_hdr_checksum(on->ospf6_if, op); + ospf6_packet_add(on->ospf6_if, op); + OSPF6_MESSAGE_WRITE_ON(on->ospf6_if); + } else + ospf6_packet_free(op); + + if (on->lsupdate_list->count != 0) { + event_add_event(master, ospf6_lsupdate_send_neighbor, on, 0, + &on->thread_send_lsupdate); + } else if (on->retrans_list->count != 0) { + event_add_timer(master, ospf6_lsupdate_send_neighbor, on, + on->ospf6_if->rxmt_interval, + &on->thread_send_lsupdate); + } +} + +int ospf6_lsupdate_send_neighbor_now(struct ospf6_neighbor *on, + struct ospf6_lsa *lsa) +{ + struct ospf6_packet *op; + uint16_t length = OSPF6_HEADER_SIZE; + + op = ospf6_packet_new(on->ospf6_if->ifmtu); + ospf6_make_header(OSPF6_MESSAGE_TYPE_LSUPDATE, on->ospf6_if, op->s); + + /* skip over fixed header */ + stream_forward_endp(op->s, OSPF6_LS_UPD_MIN_SIZE); + ospf6_lsa_age_update_to_send(lsa, on->ospf6_if->transdelay); + stream_put(op->s, lsa->header, OSPF6_LSA_SIZE(lsa->header)); + length = OSPF6_HEADER_SIZE + OSPF6_LS_UPD_MIN_SIZE + + OSPF6_LSA_SIZE(lsa->header); + ospf6_fill_header(on->ospf6_if, op->s, length); + ospf6_fill_lsupdate_header(op->s, 1); + op->length = length; + + if (IS_OSPF6_DEBUG_FLOODING + || IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_LSUPDATE, SEND_HDR)) + zlog_debug("%s: Send lsupdate with lsa %s (age %u)", __func__, + lsa->name, ntohs(lsa->header->age)); + + ospf6_send_lsupdate(on, NULL, op); + + return 0; +} + +static uint16_t ospf6_make_lsupdate_interface(struct ospf6_interface *oi, + struct ospf6_packet **op, + int *lsa_cnt) +{ + uint16_t length = OSPF6_LS_UPD_MIN_SIZE; + struct ospf6_lsa *lsa, *lsanext; + + /* skip over fixed header */ + stream_forward_endp((*op)->s, OSPF6_LS_UPD_MIN_SIZE); + + for (ALL_LSDB(oi->lsupdate_list, lsa, lsanext)) { + if (length + OSPF6_LSA_SIZE(lsa->header) + OSPF6_HEADER_SIZE > + ospf6_packet_max(oi)) { + ospf6_fill_header(oi, (*op)->s, + length + OSPF6_HEADER_SIZE); + (*op)->length = length + OSPF6_HEADER_SIZE; + ospf6_fill_lsupdate_header((*op)->s, *lsa_cnt); + ospf6_send_lsupdate(NULL, oi, *op); + + /* refresh packet */ + *op = ospf6_packet_new(oi->ifmtu); + length = OSPF6_LS_UPD_MIN_SIZE; + *lsa_cnt = 0; + ospf6_make_header(OSPF6_MESSAGE_TYPE_LSUPDATE, oi, + (*op)->s); + stream_forward_endp((*op)->s, OSPF6_LS_UPD_MIN_SIZE); + } + + ospf6_lsa_age_update_to_send(lsa, oi->transdelay); + stream_put((*op)->s, lsa->header, OSPF6_LSA_SIZE(lsa->header)); + (*lsa_cnt)++; + length += OSPF6_LSA_SIZE(lsa->header); + + assert(lsa->lock == 2); + ospf6_lsdb_remove(lsa, oi->lsupdate_list); + } + return length; +} + +void ospf6_lsupdate_send_interface(struct event *thread) +{ + struct ospf6_interface *oi; + struct ospf6_packet *op; + uint16_t length = OSPF6_HEADER_SIZE; + int lsa_cnt = 0; + + oi = (struct ospf6_interface *)EVENT_ARG(thread); + + if (oi->state <= OSPF6_INTERFACE_WAITING) { + if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_LSUPDATE, + SEND_HDR)) + zlog_debug( + "Quit to send LSUpdate to interface %s state %s", + oi->interface->name, + ospf6_interface_state_str[oi->state]); + return; + } + + /* if we have nothing to send, return */ + if (oi->lsupdate_list->count == 0) + return; + + op = ospf6_packet_new(oi->ifmtu); + ospf6_make_header(OSPF6_MESSAGE_TYPE_LSUPDATE, oi, op->s); + length += ospf6_make_lsupdate_interface(oi, &op, &lsa_cnt); + if (lsa_cnt) { + /* Fill OSPF header. */ + ospf6_fill_header(oi, op->s, length); + ospf6_fill_lsupdate_header(op->s, lsa_cnt); + op->length = length; + ospf6_send_lsupdate(NULL, oi, op); + } else + ospf6_packet_free(op); + + if (oi->lsupdate_list->count > 0) { + event_add_event(master, ospf6_lsupdate_send_interface, oi, 0, + &oi->thread_send_lsupdate); + } +} + +void ospf6_lsack_send_neighbor(struct event *thread) +{ + struct ospf6_neighbor *on; + struct ospf6_packet *op; + uint16_t length = OSPF6_HEADER_SIZE; + + on = (struct ospf6_neighbor *)EVENT_ARG(thread); + + if (on->state < OSPF6_NEIGHBOR_EXCHANGE) { + if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_LSACK, SEND_HDR)) + zlog_debug("Quit to send LSAck to neighbor %s state %s", + on->name, + ospf6_neighbor_state_str[on->state]); + return; + } + + /* if we have nothing to send, return */ + if (on->lsack_list->count == 0) + return; + + op = ospf6_packet_new(on->ospf6_if->ifmtu); + ospf6_make_header(OSPF6_MESSAGE_TYPE_LSACK, on->ospf6_if, op->s); + + length += ospf6_make_lsack_neighbor(on, &op); + + if (length == OSPF6_HEADER_SIZE) { + ospf6_packet_free(op); + return; + } + + /* Fill OSPF header. */ + ospf6_fill_header(on->ospf6_if, op->s, length); + + /* Set packet length, dst and queue to FIFO. */ + op->length = length; + op->dst = on->linklocal_addr; + ospf6_fill_hdr_checksum(on->ospf6_if, op); + ospf6_packet_add(on->ospf6_if, op); + OSPF6_MESSAGE_WRITE_ON(on->ospf6_if); + + if (on->lsack_list->count > 0) + event_add_event(master, ospf6_lsack_send_neighbor, on, 0, + &on->thread_send_lsack); +} + +static uint16_t ospf6_make_lsack_interface(struct ospf6_interface *oi, + struct ospf6_packet *op) +{ + uint16_t length = 0; + struct ospf6_lsa *lsa, *lsanext; + + for (ALL_LSDB(oi->lsack_list, lsa, lsanext)) { + if ((length + sizeof(struct ospf6_lsa_header) + + OSPF6_HEADER_SIZE) + > ospf6_packet_max(oi)) { + /* if we run out of packet size/space here, + better to try again soon. */ + EVENT_OFF(oi->thread_send_lsack); + event_add_event(master, ospf6_lsack_send_interface, oi, + 0, &oi->thread_send_lsack); + + ospf6_lsa_unlock(&lsa); + if (lsanext) + ospf6_lsa_unlock(&lsanext); + break; + } + ospf6_lsa_age_update_to_send(lsa, oi->transdelay); + stream_put(op->s, lsa->header, sizeof(struct ospf6_lsa_header)); + length += sizeof(struct ospf6_lsa_header); + + assert(lsa->lock == 2); + ospf6_lsdb_remove(lsa, oi->lsack_list); + } + return length; +} + +void ospf6_lsack_send_interface(struct event *thread) +{ + struct ospf6_interface *oi; + struct ospf6_packet *op; + uint16_t length = OSPF6_HEADER_SIZE; + + oi = (struct ospf6_interface *)EVENT_ARG(thread); + + if (oi->state <= OSPF6_INTERFACE_WAITING) { + if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_LSACK, SEND_HDR)) + zlog_debug( + "Quit to send LSAck to interface %s state %s", + oi->interface->name, + ospf6_interface_state_str[oi->state]); + return; + } + + /* if we have nothing to send, return */ + if (oi->lsack_list->count == 0) + return; + + op = ospf6_packet_new(oi->ifmtu); + ospf6_make_header(OSPF6_MESSAGE_TYPE_LSACK, oi, op->s); + + length += ospf6_make_lsack_interface(oi, op); + + if (length == OSPF6_HEADER_SIZE) { + ospf6_packet_free(op); + return; + } + /* Fill OSPF header. */ + ospf6_fill_header(oi, op->s, length); + + /* Set packet length, dst and queue to FIFO. */ + op->length = length; + if ((oi->state == OSPF6_INTERFACE_POINTTOPOINT) + || (oi->state == OSPF6_INTERFACE_DR) + || (oi->state == OSPF6_INTERFACE_BDR)) + op->dst = allspfrouters6; + else + op->dst = alldrouters6; + + ospf6_fill_hdr_checksum(oi, op); + ospf6_packet_add(oi, op); + OSPF6_MESSAGE_WRITE_ON(oi); + + if (oi->lsack_list->count > 0) + event_add_event(master, ospf6_lsack_send_interface, oi, 0, + &oi->thread_send_lsack); +} + +/* Commands */ +DEFUN(debug_ospf6_message, debug_ospf6_message_cmd, + "debug ospf6 message <unknown|hello|dbdesc|lsreq|lsupdate|lsack|all> [<send|recv|send-hdr|recv-hdr>]", + DEBUG_STR OSPF6_STR + "Debug OSPFv3 message\n" + "Debug Unknown message\n" + "Debug Hello message\n" + "Debug Database Description message\n" + "Debug Link State Request message\n" + "Debug Link State Update message\n" + "Debug Link State Acknowledgement message\n" + "Debug All message\n" + "Debug only sending message, entire packet\n" + "Debug only receiving message, entire packet\n" + "Debug only sending message, header only\n" + "Debug only receiving message, header only\n") +{ + int idx_packet = 3; + int idx_send_recv = 4; + unsigned char level = 0; + int type = 0; + int i; + + /* check type */ + if (!strncmp(argv[idx_packet]->arg, "u", 1)) + type = OSPF6_MESSAGE_TYPE_UNKNOWN; + else if (!strncmp(argv[idx_packet]->arg, "h", 1)) + type = OSPF6_MESSAGE_TYPE_HELLO; + else if (!strncmp(argv[idx_packet]->arg, "d", 1)) + type = OSPF6_MESSAGE_TYPE_DBDESC; + else if (!strncmp(argv[idx_packet]->arg, "lsr", 3)) + type = OSPF6_MESSAGE_TYPE_LSREQ; + else if (!strncmp(argv[idx_packet]->arg, "lsu", 3)) + type = OSPF6_MESSAGE_TYPE_LSUPDATE; + else if (!strncmp(argv[idx_packet]->arg, "lsa", 3)) + type = OSPF6_MESSAGE_TYPE_LSACK; + else if (!strncmp(argv[idx_packet]->arg, "a", 1)) + type = OSPF6_MESSAGE_TYPE_ALL; + + if (argc == 4) + level = OSPF6_DEBUG_MESSAGE_SEND | OSPF6_DEBUG_MESSAGE_RECV; + else if (!strncmp(argv[idx_send_recv]->arg, "send-h", 6)) + level = OSPF6_DEBUG_MESSAGE_SEND_HDR; + else if (!strncmp(argv[idx_send_recv]->arg, "s", 1)) + level = OSPF6_DEBUG_MESSAGE_SEND; + else if (!strncmp(argv[idx_send_recv]->arg, "recv-h", 6)) + level = OSPF6_DEBUG_MESSAGE_RECV_HDR; + else if (!strncmp(argv[idx_send_recv]->arg, "r", 1)) + level = OSPF6_DEBUG_MESSAGE_RECV; + + if (type == OSPF6_MESSAGE_TYPE_ALL) { + for (i = 0; i < 6; i++) + OSPF6_DEBUG_MESSAGE_ON(i, level); + } else + OSPF6_DEBUG_MESSAGE_ON(type, level); + + return CMD_SUCCESS; +} + +DEFUN(no_debug_ospf6_message, no_debug_ospf6_message_cmd, + "no debug ospf6 message <unknown|hello|dbdesc|lsreq|lsupdate|lsack|all> [<send|recv|send-hdr|recv-hdr>]", + NO_STR DEBUG_STR OSPF6_STR + "Debug OSPFv3 message\n" + "Debug Unknown message\n" + "Debug Hello message\n" + "Debug Database Description message\n" + "Debug Link State Request message\n" + "Debug Link State Update message\n" + "Debug Link State Acknowledgement message\n" + "Debug All message\n" + "Debug only sending message, entire pkt\n" + "Debug only receiving message, entire pkt\n" + "Debug only sending message, header only\n" + "Debug only receiving message, header only\n") +{ + int idx_packet = 4; + int idx_send_recv = 5; + unsigned char level = 0; + int type = 0; + int i; + + /* check type */ + if (!strncmp(argv[idx_packet]->arg, "u", 1)) + type = OSPF6_MESSAGE_TYPE_UNKNOWN; + else if (!strncmp(argv[idx_packet]->arg, "h", 1)) + type = OSPF6_MESSAGE_TYPE_HELLO; + else if (!strncmp(argv[idx_packet]->arg, "d", 1)) + type = OSPF6_MESSAGE_TYPE_DBDESC; + else if (!strncmp(argv[idx_packet]->arg, "lsr", 3)) + type = OSPF6_MESSAGE_TYPE_LSREQ; + else if (!strncmp(argv[idx_packet]->arg, "lsu", 3)) + type = OSPF6_MESSAGE_TYPE_LSUPDATE; + else if (!strncmp(argv[idx_packet]->arg, "lsa", 3)) + type = OSPF6_MESSAGE_TYPE_LSACK; + else if (!strncmp(argv[idx_packet]->arg, "a", 1)) + type = OSPF6_MESSAGE_TYPE_ALL; + + if (argc == 5) + level = OSPF6_DEBUG_MESSAGE_SEND | OSPF6_DEBUG_MESSAGE_RECV + | OSPF6_DEBUG_MESSAGE_SEND_HDR + | OSPF6_DEBUG_MESSAGE_RECV_HDR; + else if (!strncmp(argv[idx_send_recv]->arg, "send-h", 6)) + level = OSPF6_DEBUG_MESSAGE_SEND_HDR; + else if (!strncmp(argv[idx_send_recv]->arg, "s", 1)) + level = OSPF6_DEBUG_MESSAGE_SEND; + else if (!strncmp(argv[idx_send_recv]->arg, "recv-h", 6)) + level = OSPF6_DEBUG_MESSAGE_RECV_HDR; + else if (!strncmp(argv[idx_send_recv]->arg, "r", 1)) + level = OSPF6_DEBUG_MESSAGE_RECV; + + if (type == OSPF6_MESSAGE_TYPE_ALL) { + for (i = 0; i < 6; i++) + OSPF6_DEBUG_MESSAGE_OFF(i, level); + } else + OSPF6_DEBUG_MESSAGE_OFF(type, level); + + return CMD_SUCCESS; +} + + +int config_write_ospf6_debug_message(struct vty *vty) +{ + const char *type_str[] = {"unknown", "hello", "dbdesc", + "lsreq", "lsupdate", "lsack"}; + unsigned char s = 0, r = 0, sh = 0, rh = 0; + int i; + + for (i = 0; i < 6; i++) { + if (IS_OSPF6_DEBUG_MESSAGE_ENABLED(i, SEND)) + s |= 1 << i; + if (IS_OSPF6_DEBUG_MESSAGE_ENABLED(i, RECV)) + r |= 1 << i; + } + + for (i = 0; i < 6; i++) { + if (IS_OSPF6_DEBUG_MESSAGE_ENABLED(i, SEND_HDR)) + sh |= 1 << i; + if (IS_OSPF6_DEBUG_MESSAGE_ENABLED(i, RECV_HDR)) + rh |= 1 << i; + } + + if (s == 0x3f && r == 0x3f) { + vty_out(vty, "debug ospf6 message all\n"); + return 0; + } + + if (s == 0x3f && r == 0) { + vty_out(vty, "debug ospf6 message all send\n"); + return 0; + } else if (s == 0 && r == 0x3f) { + vty_out(vty, "debug ospf6 message all recv\n"); + return 0; + } + + if (sh == 0x3f && rh == 0) { + vty_out(vty, "debug ospf6 message all send-hdr\n"); + return 0; + } else if (sh == 0 && rh == 0x3f) { + vty_out(vty, "debug ospf6 message all recv-hdr\n"); + return 0; + } + + /* Unknown message is logged by default */ + if (!IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, SEND) + && !IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) + vty_out(vty, "no debug ospf6 message unknown\n"); + else if (!IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, SEND)) + vty_out(vty, "no debug ospf6 message unknown send\n"); + else if (!IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN, RECV)) + vty_out(vty, "no debug ospf6 message unknown recv\n"); + + for (i = 1; i < 6; i++) { + if (IS_OSPF6_DEBUG_MESSAGE_ENABLED(i, SEND) + && IS_OSPF6_DEBUG_MESSAGE_ENABLED(i, RECV)) { + vty_out(vty, "debug ospf6 message %s\n", type_str[i]); + continue; + } + + if (IS_OSPF6_DEBUG_MESSAGE_ENABLED(i, SEND)) + vty_out(vty, "debug ospf6 message %s send\n", + type_str[i]); + else if (IS_OSPF6_DEBUG_MESSAGE_ENABLED(i, SEND_HDR)) + vty_out(vty, "debug ospf6 message %s send-hdr\n", + type_str[i]); + + if (IS_OSPF6_DEBUG_MESSAGE_ENABLED(i, RECV)) + vty_out(vty, "debug ospf6 message %s recv\n", + type_str[i]); + else if (IS_OSPF6_DEBUG_MESSAGE_ENABLED(i, RECV_HDR)) + vty_out(vty, "debug ospf6 message %s recv-hdr\n", + type_str[i]); + } + + return 0; +} + +void install_element_ospf6_debug_message(void) +{ + install_element(ENABLE_NODE, &debug_ospf6_message_cmd); + install_element(ENABLE_NODE, &no_debug_ospf6_message_cmd); + install_element(CONFIG_NODE, &debug_ospf6_message_cmd); + install_element(CONFIG_NODE, &no_debug_ospf6_message_cmd); +} diff --git a/ospf6d/ospf6_message.h b/ospf6d/ospf6_message.h new file mode 100644 index 0000000..2b25b07 --- /dev/null +++ b/ospf6d/ospf6_message.h @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 1999-2003 Yasuhiro Ohara + */ + +#ifndef OSPF6_MESSAGE_H +#define OSPF6_MESSAGE_H + +#define OSPF6_MESSAGE_BUFSIZ 4096 + +/* Debug option */ +extern unsigned char conf_debug_ospf6_message[]; + +#define OSPF6_ACTION_SEND 0x01 +#define OSPF6_ACTION_RECV 0x02 +#define OSPF6_DEBUG_MESSAGE_SEND 0x01 +#define OSPF6_DEBUG_MESSAGE_RECV 0x02 +#define OSPF6_DEBUG_MESSAGE_SEND_HDR 0x04 +#define OSPF6_DEBUG_MESSAGE_RECV_HDR 0x08 +#define OSPF6_DEBUG_MESSAGE_SEND_BOTH \ + OSPF6_DEBUG_MESSAGE_SEND | OSPF6_DEBUG_MESSAGE_SEND_HDR +#define OSPF6_DEBUG_MESSAGE_RECV_BOTH \ + OSPF6_DEBUG_MESSAGE_RECV | OSPF6_DEBUG_MESSAGE_RECV_HDR + +#define OSPF6_DEBUG_MESSAGE_ON(type, level) \ + (conf_debug_ospf6_message[type] |= (level)) +#define OSPF6_DEBUG_MESSAGE_OFF(type, level) \ + (conf_debug_ospf6_message[type] &= ~(level)) + +#define IS_OSPF6_DEBUG_MESSAGE(t, e) \ + (((OSPF6_DEBUG_MESSAGE_##e) == OSPF6_DEBUG_MESSAGE_RECV_HDR) \ + ? (conf_debug_ospf6_message[t] \ + & (OSPF6_DEBUG_MESSAGE_RECV_BOTH)) \ + : (((OSPF6_DEBUG_MESSAGE_##e) == OSPF6_DEBUG_MESSAGE_SEND_HDR) \ + ? (conf_debug_ospf6_message[t] \ + & (OSPF6_DEBUG_MESSAGE_SEND_BOTH)) \ + : (conf_debug_ospf6_message[t] \ + & (OSPF6_DEBUG_MESSAGE_##e)))) + +#define IS_OSPF6_DEBUG_MESSAGE_ENABLED(type, e) \ + (conf_debug_ospf6_message[type] & (OSPF6_DEBUG_MESSAGE_##e)) + +/* Type */ +#define OSPF6_MESSAGE_TYPE_UNKNOWN 0x0 +#define OSPF6_MESSAGE_TYPE_HELLO 0x1 /* Discover/maintain neighbors */ +#define OSPF6_MESSAGE_TYPE_DBDESC 0x2 /* Summarize database contents */ +#define OSPF6_MESSAGE_TYPE_LSREQ 0x3 /* Database download request */ +#define OSPF6_MESSAGE_TYPE_LSUPDATE 0x4 /* Database update */ +#define OSPF6_MESSAGE_TYPE_LSACK 0x5 /* Flooding acknowledgment */ +#define OSPF6_MESSAGE_TYPE_ALL 0x6 /* For debug option */ +#define OSPF6_MESSAGE_TYPE_MAX 0x6 /* same as OSPF6_MESSAGE_TYPE_ALL */ + +struct ospf6_packet { + struct ospf6_packet *next; + + /* Pointer to data stream. */ + struct stream *s; + + /* IP destination address. */ + struct in6_addr dst; + + /* OSPF6 packet length. */ + uint16_t length; +}; + +/* OSPF packet queue structure. */ +struct ospf6_fifo { + unsigned long count; + + struct ospf6_packet *head; + struct ospf6_packet *tail; +}; + +/* OSPFv3 packet header */ +#define OSPF6_HEADER_SIZE 16U +struct ospf6_header { + uint8_t version; + uint8_t type; + uint16_t length; + in_addr_t router_id; + in_addr_t area_id; + uint16_t checksum; + uint8_t instance_id; + uint8_t reserved; +}; + +#define OSPF6_MESSAGE_END(H) ((caddr_t) (H) + ntohs ((H)->length)) + +/* Hello */ +#define OSPF6_HELLO_MIN_SIZE 20U +struct ospf6_hello { + ifindex_t interface_id; + uint8_t priority; + uint8_t options[3]; + uint16_t hello_interval; + uint16_t dead_interval; + in_addr_t drouter; + in_addr_t bdrouter; + /* Followed by Router-IDs */ +}; + +/* Database Description */ +#define OSPF6_DB_DESC_MIN_SIZE 12U +struct ospf6_dbdesc { + uint8_t reserved1; + uint8_t options[3]; + uint16_t ifmtu; + uint8_t reserved2; + uint8_t bits; + uint32_t seqnum; + /* Followed by LSA Headers */ +}; + +#define OSPF6_DBDESC_MSBIT (0x01) /* master/slave bit */ +#define OSPF6_DBDESC_MBIT (0x02) /* more bit */ +#define OSPF6_DBDESC_IBIT (0x04) /* initial bit */ + +/* Link State Request */ +#define OSPF6_LS_REQ_MIN_SIZE 0U +/* It is just a sequence of entries below */ +#define OSPF6_LSREQ_LSDESC_FIX_SIZE 12U +struct ospf6_lsreq_entry { + uint16_t reserved; /* Must Be Zero */ + uint16_t type; /* LS type */ + in_addr_t id; /* Link State ID */ + in_addr_t adv_router; /* Advertising Router */ +}; + +/* Link State Update */ +#define OSPF6_LS_UPD_MIN_SIZE 4U +struct ospf6_lsupdate { + uint32_t lsa_number; + /* Followed by LSAs */ +}; + +/* LLS is not supported, but used to derive + * offset of Auth_trailer + */ +struct ospf6_lls_hdr { + uint16_t checksum; + uint16_t length; +}; + +/* Link State Acknowledgement */ +#define OSPF6_LS_ACK_MIN_SIZE 0U +/* It is just a sequence of LSA Headers */ + +/* Function definition */ +extern void ospf6_hello_print(struct ospf6_header *, int action); +extern void ospf6_dbdesc_print(struct ospf6_header *, int action); +extern void ospf6_lsreq_print(struct ospf6_header *, int action); +extern void ospf6_lsupdate_print(struct ospf6_header *, int action); +extern void ospf6_lsack_print(struct ospf6_header *, int action); + +extern struct ospf6_fifo *ospf6_fifo_new(void); +extern void ospf6_fifo_flush(struct ospf6_fifo *fifo); +extern void ospf6_fifo_free(struct ospf6_fifo *fifo); + +extern int ospf6_iobuf_size(unsigned int size); +extern void ospf6_message_terminate(void); +extern void ospf6_receive(struct event *thread); + +extern void ospf6_hello_send(struct event *thread); +extern void ospf6_dbdesc_send(struct event *thread); +extern void ospf6_dbdesc_send_newone(struct event *thread); +extern void ospf6_lsreq_send(struct event *thread); +extern void ospf6_lsupdate_send_interface(struct event *thread); +extern void ospf6_lsupdate_send_neighbor(struct event *thread); +extern void ospf6_lsack_send_interface(struct event *thread); +extern void ospf6_lsack_send_neighbor(struct event *thread); + +extern int config_write_ospf6_debug_message(struct vty *); +extern void install_element_ospf6_debug_message(void); +extern const char *ospf6_message_type(int type); +#endif /* OSPF6_MESSAGE_H */ diff --git a/ospf6d/ospf6_neighbor.c b/ospf6d/ospf6_neighbor.c new file mode 100644 index 0000000..e1aec06 --- /dev/null +++ b/ospf6d/ospf6_neighbor.c @@ -0,0 +1,1327 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2003 Yasuhiro Ohara + */ + +#include <zebra.h> + +#include "log.h" +#include "memory.h" +#include "frrevent.h" +#include "linklist.h" +#include "vty.h" +#include "command.h" +#include "lib/bfd.h" + +#include "ospf6_proto.h" +#include "ospf6_lsa.h" +#include "ospf6_lsdb.h" +#include "ospf6_message.h" +#include "ospf6_top.h" +#include "ospf6_area.h" +#include "ospf6_interface.h" +#include "ospf6_neighbor.h" +#include "ospf6_intra.h" +#include "ospf6_flood.h" +#include "ospf6d.h" +#include "ospf6_bfd.h" +#include "ospf6_abr.h" +#include "ospf6_asbr.h" +#include "ospf6_lsa.h" +#include "ospf6_spf.h" +#include "ospf6_zebra.h" +#include "ospf6_gr.h" +#include "lib/json.h" + +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_NEIGHBOR, "OSPF6 neighbor"); + +DEFINE_HOOK(ospf6_neighbor_change, + (struct ospf6_neighbor * on, int state, int next_state), + (on, state, next_state)); + +unsigned char conf_debug_ospf6_neighbor = 0; + +const char *const ospf6_neighbor_state_str[] = { + "None", "Down", "Attempt", "Init", "Twoway", + "ExStart", "ExChange", "Loading", "Full", NULL}; + +const char *const ospf6_neighbor_event_str[] = { + "NoEvent", "HelloReceived", "2-WayReceived", "NegotiationDone", + "ExchangeDone", "LoadingDone", "AdjOK?", "SeqNumberMismatch", + "BadLSReq", "1-WayReceived", "InactivityTimer", +}; + +int ospf6_neighbor_cmp(void *va, void *vb) +{ + struct ospf6_neighbor *ona = (struct ospf6_neighbor *)va; + struct ospf6_neighbor *onb = (struct ospf6_neighbor *)vb; + + if (ona->router_id == onb->router_id) + return 0; + + return (ntohl(ona->router_id) < ntohl(onb->router_id)) ? -1 : 1; +} + +struct ospf6_neighbor *ospf6_neighbor_lookup(uint32_t router_id, + struct ospf6_interface *oi) +{ + struct listnode *n; + struct ospf6_neighbor *on; + + for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, n, on)) + if (on->router_id == router_id) + return on; + + return (struct ospf6_neighbor *)NULL; +} + +struct ospf6_neighbor *ospf6_area_neighbor_lookup(struct ospf6_area *area, + uint32_t router_id) +{ + struct ospf6_interface *oi; + struct ospf6_neighbor *nbr; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(area->if_list, node, oi)) { + nbr = ospf6_neighbor_lookup(router_id, oi); + if (nbr) + return nbr; + } + + return NULL; +} + +static void ospf6_neighbor_clear_ls_lists(struct ospf6_neighbor *on) +{ + struct ospf6_lsa *lsa; + struct ospf6_lsa *lsanext; + + ospf6_lsdb_remove_all(on->summary_list); + if (on->last_ls_req) { + ospf6_lsa_unlock(&on->last_ls_req); + on->last_ls_req = NULL; + } + + ospf6_lsdb_remove_all(on->request_list); + for (ALL_LSDB(on->retrans_list, lsa, lsanext)) { + ospf6_decrement_retrans_count(lsa); + ospf6_lsdb_remove(lsa, on->retrans_list); + } +} + +/* create ospf6_neighbor */ +struct ospf6_neighbor *ospf6_neighbor_create(uint32_t router_id, + struct ospf6_interface *oi) +{ + struct ospf6_neighbor *on; + char buf[16]; + int type; + + on = XCALLOC(MTYPE_OSPF6_NEIGHBOR, sizeof(struct ospf6_neighbor)); + inet_ntop(AF_INET, &router_id, buf, sizeof(buf)); + snprintf(on->name, sizeof(on->name), "%s%%%s", buf, + oi->interface->name); + on->ospf6_if = oi; + on->state = OSPF6_NEIGHBOR_DOWN; + on->state_change = 0; + monotime(&on->last_changed); + on->router_id = router_id; + + on->summary_list = ospf6_lsdb_create(on); + on->request_list = ospf6_lsdb_create(on); + on->retrans_list = ospf6_lsdb_create(on); + + on->dbdesc_list = ospf6_lsdb_create(on); + on->lsupdate_list = ospf6_lsdb_create(on); + on->lsack_list = ospf6_lsdb_create(on); + + for (type = 0; type < OSPF6_MESSAGE_TYPE_MAX; type++) { + on->seqnum_l[type] = 0; + on->seqnum_h[type] = 0; + } + + on->auth_present = false; + + listnode_add_sort(oi->neighbor_list, on); + + ospf6_bfd_info_nbr_create(oi, on); + return on; +} + +void ospf6_neighbor_delete(struct ospf6_neighbor *on) +{ + ospf6_neighbor_clear_ls_lists(on); + + ospf6_lsdb_remove_all(on->dbdesc_list); + ospf6_lsdb_remove_all(on->lsupdate_list); + ospf6_lsdb_remove_all(on->lsack_list); + + ospf6_lsdb_delete(on->summary_list); + ospf6_lsdb_delete(on->request_list); + ospf6_lsdb_delete(on->retrans_list); + + ospf6_lsdb_delete(on->dbdesc_list); + ospf6_lsdb_delete(on->lsupdate_list); + ospf6_lsdb_delete(on->lsack_list); + + EVENT_OFF(on->inactivity_timer); + + EVENT_OFF(on->last_dbdesc_release_timer); + + EVENT_OFF(on->thread_send_dbdesc); + EVENT_OFF(on->thread_send_lsreq); + EVENT_OFF(on->thread_send_lsupdate); + EVENT_OFF(on->thread_send_lsack); + EVENT_OFF(on->thread_exchange_done); + EVENT_OFF(on->thread_adj_ok); + EVENT_OFF(on->event_loading_done); + + EVENT_OFF(on->gr_helper_info.t_grace_timer); + + bfd_sess_free(&on->bfd_session); + XFREE(MTYPE_OSPF6_NEIGHBOR, on); +} + +static void ospf6_neighbor_state_change(uint8_t next_state, + struct ospf6_neighbor *on, int event) +{ + uint8_t prev_state; + + prev_state = on->state; + on->state = next_state; + + if (prev_state == next_state) + return; + + on->state_change++; + monotime(&on->last_changed); + + /* log */ + if (IS_OSPF6_DEBUG_NEIGHBOR(STATE)) { + zlog_debug( + "Neighbor state change %s (Router-ID: %pI4): [%s]->[%s] (%s)", + on->name, &on->router_id, + ospf6_neighbor_state_str[prev_state], + ospf6_neighbor_state_str[next_state], + ospf6_neighbor_event_string(event)); + } + + /* Optionally notify about adjacency changes */ + if (CHECK_FLAG(on->ospf6_if->area->ospf6->config_flags, + OSPF6_LOG_ADJACENCY_CHANGES) + && (CHECK_FLAG(on->ospf6_if->area->ospf6->config_flags, + OSPF6_LOG_ADJACENCY_DETAIL) + || (next_state == OSPF6_NEIGHBOR_FULL) + || (next_state < prev_state))) + zlog_notice( + "AdjChg: Nbr %pI4(%s) on %s: %s -> %s (%s)", + &on->router_id, + vrf_id_to_name(on->ospf6_if->interface->vrf->vrf_id), + on->name, ospf6_neighbor_state_str[prev_state], + ospf6_neighbor_state_str[next_state], + ospf6_neighbor_event_string(event)); + + if (prev_state == OSPF6_NEIGHBOR_FULL + || next_state == OSPF6_NEIGHBOR_FULL) { + if (!OSPF6_GR_IS_ACTIVE_HELPER(on)) { + OSPF6_ROUTER_LSA_SCHEDULE(on->ospf6_if->area); + if (on->ospf6_if->state == OSPF6_INTERFACE_DR) { + OSPF6_NETWORK_LSA_SCHEDULE(on->ospf6_if); + OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT( + on->ospf6_if); + } + } + if (next_state == OSPF6_NEIGHBOR_FULL) + on->ospf6_if->area->intra_prefix_originate = 1; + + if (!OSPF6_GR_IS_ACTIVE_HELPER(on)) + OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB( + on->ospf6_if->area); + + if ((prev_state == OSPF6_NEIGHBOR_LOADING + || prev_state == OSPF6_NEIGHBOR_EXCHANGE) + && next_state == OSPF6_NEIGHBOR_FULL) { + OSPF6_AS_EXTERN_LSA_SCHEDULE(on->ospf6_if); + on->ospf6_if->area->full_nbrs++; + } + + if (prev_state == OSPF6_NEIGHBOR_FULL) + on->ospf6_if->area->full_nbrs--; + } + + if ((prev_state == OSPF6_NEIGHBOR_EXCHANGE + || prev_state == OSPF6_NEIGHBOR_LOADING) + && (next_state != OSPF6_NEIGHBOR_EXCHANGE + && next_state != OSPF6_NEIGHBOR_LOADING)) + ospf6_maxage_remove(on->ospf6_if->area->ospf6); + + hook_call(ospf6_neighbor_change, on, next_state, prev_state); + ospf6_bfd_trigger_event(on, prev_state, next_state); +} + +/* RFC2328 section 10.4 */ +static int need_adjacency(struct ospf6_neighbor *on) +{ + if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT + || on->ospf6_if->state == OSPF6_INTERFACE_DR + || on->ospf6_if->state == OSPF6_INTERFACE_BDR) + return 1; + + if (on->ospf6_if->drouter == on->router_id + || on->ospf6_if->bdrouter == on->router_id) + return 1; + + return 0; +} + +void hello_received(struct event *thread) +{ + struct ospf6_neighbor *on; + + on = (struct ospf6_neighbor *)EVENT_ARG(thread); + assert(on); + + if (IS_OSPF6_DEBUG_NEIGHBOR(EVENT)) + zlog_debug("Neighbor Event %s: *HelloReceived*", on->name); + + /* reset Inactivity Timer */ + EVENT_OFF(on->inactivity_timer); + event_add_timer(master, inactivity_timer, on, + on->ospf6_if->dead_interval, &on->inactivity_timer); + + if (on->state <= OSPF6_NEIGHBOR_DOWN) + ospf6_neighbor_state_change(OSPF6_NEIGHBOR_INIT, on, + OSPF6_NEIGHBOR_EVENT_HELLO_RCVD); +} + +void twoway_received(struct event *thread) +{ + struct ospf6_neighbor *on; + + on = (struct ospf6_neighbor *)EVENT_ARG(thread); + assert(on); + + if (on->state > OSPF6_NEIGHBOR_INIT) + return; + + if (IS_OSPF6_DEBUG_NEIGHBOR(EVENT)) + zlog_debug("Neighbor Event %s: *2Way-Received*", on->name); + + event_add_event(master, neighbor_change, on->ospf6_if, 0, NULL); + + if (!need_adjacency(on)) { + ospf6_neighbor_state_change(OSPF6_NEIGHBOR_TWOWAY, on, + OSPF6_NEIGHBOR_EVENT_TWOWAY_RCVD); + return; + } + + ospf6_neighbor_state_change(OSPF6_NEIGHBOR_EXSTART, on, + OSPF6_NEIGHBOR_EVENT_TWOWAY_RCVD); + SET_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MSBIT); + SET_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MBIT); + SET_FLAG(on->dbdesc_bits, OSPF6_DBDESC_IBIT); + + EVENT_OFF(on->thread_send_dbdesc); + event_add_event(master, ospf6_dbdesc_send, on, 0, + &on->thread_send_dbdesc); +} + +void negotiation_done(struct event *thread) +{ + struct ospf6_neighbor *on; + struct ospf6_lsa *lsa, *lsanext; + + on = (struct ospf6_neighbor *)EVENT_ARG(thread); + assert(on); + + if (on->state != OSPF6_NEIGHBOR_EXSTART) + return; + + if (IS_OSPF6_DEBUG_NEIGHBOR(EVENT)) + zlog_debug("Neighbor Event %s: *NegotiationDone*", on->name); + + /* clear ls-list */ + ospf6_neighbor_clear_ls_lists(on); + + /* Interface scoped LSAs */ + for (ALL_LSDB(on->ospf6_if->lsdb, lsa, lsanext)) { + if (OSPF6_LSA_IS_MAXAGE(lsa)) { + ospf6_increment_retrans_count(lsa); + ospf6_lsdb_add(ospf6_lsa_copy(lsa), on->retrans_list); + } else + ospf6_lsdb_add(ospf6_lsa_copy(lsa), on->summary_list); + } + + /* Area scoped LSAs */ + for (ALL_LSDB(on->ospf6_if->area->lsdb, lsa, lsanext)) { + if (OSPF6_LSA_IS_MAXAGE(lsa)) { + ospf6_increment_retrans_count(lsa); + ospf6_lsdb_add(ospf6_lsa_copy(lsa), on->retrans_list); + } else + ospf6_lsdb_add(ospf6_lsa_copy(lsa), on->summary_list); + } + + /* AS scoped LSAs */ + for (ALL_LSDB(on->ospf6_if->area->ospf6->lsdb, lsa, lsanext)) { + if (OSPF6_LSA_IS_MAXAGE(lsa)) { + ospf6_increment_retrans_count(lsa); + ospf6_lsdb_add(ospf6_lsa_copy(lsa), on->retrans_list); + } else + ospf6_lsdb_add(ospf6_lsa_copy(lsa), on->summary_list); + } + + UNSET_FLAG(on->dbdesc_bits, OSPF6_DBDESC_IBIT); + ospf6_neighbor_state_change(OSPF6_NEIGHBOR_EXCHANGE, on, + OSPF6_NEIGHBOR_EVENT_NEGOTIATION_DONE); +} + +static void ospf6_neighbor_last_dbdesc_release(struct event *thread) +{ + struct ospf6_neighbor *on = EVENT_ARG(thread); + + assert(on); + memset(&on->dbdesc_last, 0, sizeof(struct ospf6_dbdesc)); +} + +void exchange_done(struct event *thread) +{ + struct ospf6_neighbor *on; + + on = (struct ospf6_neighbor *)EVENT_ARG(thread); + assert(on); + + if (on->state != OSPF6_NEIGHBOR_EXCHANGE) + return; + + if (IS_OSPF6_DEBUG_NEIGHBOR(EVENT)) + zlog_debug("Neighbor Event %s: *ExchangeDone*", on->name); + + EVENT_OFF(on->thread_send_dbdesc); + ospf6_lsdb_remove_all(on->dbdesc_list); + + /* RFC 2328 (10.8): Release the last dbdesc after dead_interval */ + if (!CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MSBIT)) { + EVENT_OFF(on->last_dbdesc_release_timer); + event_add_timer(master, ospf6_neighbor_last_dbdesc_release, on, + on->ospf6_if->dead_interval, + &on->last_dbdesc_release_timer); + } + + if (on->request_list->count == 0) + ospf6_neighbor_state_change(OSPF6_NEIGHBOR_FULL, on, + OSPF6_NEIGHBOR_EVENT_EXCHANGE_DONE); + else { + ospf6_neighbor_state_change(OSPF6_NEIGHBOR_LOADING, on, + OSPF6_NEIGHBOR_EVENT_EXCHANGE_DONE); + + event_add_event(master, ospf6_lsreq_send, on, 0, + &on->thread_send_lsreq); + } +} + +/* Check loading state. */ +void ospf6_check_nbr_loading(struct ospf6_neighbor *on) +{ + + /* RFC2328 Section 10.9: When the neighbor responds to these requests + with the proper Link State Update packet(s), the Link state request + list is truncated and a new Link State Request packet is sent. + */ + if ((on->state == OSPF6_NEIGHBOR_LOADING) + || (on->state == OSPF6_NEIGHBOR_EXCHANGE)) { + if (on->request_list->count == 0) + event_add_event(master, loading_done, on, 0, + &on->event_loading_done); + else if (on->last_ls_req == NULL) { + EVENT_OFF(on->thread_send_lsreq); + event_add_event(master, ospf6_lsreq_send, on, 0, + &on->thread_send_lsreq); + } + } +} + +void loading_done(struct event *thread) +{ + struct ospf6_neighbor *on; + + on = (struct ospf6_neighbor *)EVENT_ARG(thread); + assert(on); + + if (on->state != OSPF6_NEIGHBOR_LOADING) + return; + + if (IS_OSPF6_DEBUG_NEIGHBOR(EVENT)) + zlog_debug("Neighbor Event %s: *LoadingDone*", on->name); + + assert(on->request_list->count == 0); + + ospf6_neighbor_state_change(OSPF6_NEIGHBOR_FULL, on, + OSPF6_NEIGHBOR_EVENT_LOADING_DONE); +} + +void adj_ok(struct event *thread) +{ + struct ospf6_neighbor *on; + + on = (struct ospf6_neighbor *)EVENT_ARG(thread); + assert(on); + + if (IS_OSPF6_DEBUG_NEIGHBOR(EVENT)) + zlog_debug("Neighbor Event %s: *AdjOK?*", on->name); + + if (on->state == OSPF6_NEIGHBOR_TWOWAY && need_adjacency(on)) { + ospf6_neighbor_state_change(OSPF6_NEIGHBOR_EXSTART, on, + OSPF6_NEIGHBOR_EVENT_ADJ_OK); + SET_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MSBIT); + SET_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MBIT); + SET_FLAG(on->dbdesc_bits, OSPF6_DBDESC_IBIT); + + EVENT_OFF(on->thread_send_dbdesc); + event_add_event(master, ospf6_dbdesc_send, on, 0, + &on->thread_send_dbdesc); + + } else if (on->state >= OSPF6_NEIGHBOR_EXSTART && !need_adjacency(on)) { + ospf6_neighbor_state_change(OSPF6_NEIGHBOR_TWOWAY, on, + OSPF6_NEIGHBOR_EVENT_ADJ_OK); + ospf6_neighbor_clear_ls_lists(on); + } +} + +void seqnumber_mismatch(struct event *thread) +{ + struct ospf6_neighbor *on; + + on = (struct ospf6_neighbor *)EVENT_ARG(thread); + assert(on); + + if (on->state < OSPF6_NEIGHBOR_EXCHANGE) + return; + + if (IS_OSPF6_DEBUG_NEIGHBOR(EVENT)) + zlog_debug("Neighbor Event %s: *SeqNumberMismatch*", on->name); + + ospf6_neighbor_state_change(OSPF6_NEIGHBOR_EXSTART, on, + OSPF6_NEIGHBOR_EVENT_SEQNUMBER_MISMATCH); + SET_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MSBIT); + SET_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MBIT); + SET_FLAG(on->dbdesc_bits, OSPF6_DBDESC_IBIT); + + ospf6_neighbor_clear_ls_lists(on); + + EVENT_OFF(on->thread_send_dbdesc); + on->dbdesc_seqnum++; /* Incr seqnum as per RFC2328, sec 10.3 */ + + event_add_event(master, ospf6_dbdesc_send, on, 0, + &on->thread_send_dbdesc); +} + +void bad_lsreq(struct event *thread) +{ + struct ospf6_neighbor *on; + + on = (struct ospf6_neighbor *)EVENT_ARG(thread); + assert(on); + + if (on->state < OSPF6_NEIGHBOR_EXCHANGE) + return; + + if (IS_OSPF6_DEBUG_NEIGHBOR(EVENT)) + zlog_debug("Neighbor Event %s: *BadLSReq*", on->name); + + ospf6_neighbor_state_change(OSPF6_NEIGHBOR_EXSTART, on, + OSPF6_NEIGHBOR_EVENT_BAD_LSREQ); + SET_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MSBIT); + SET_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MBIT); + SET_FLAG(on->dbdesc_bits, OSPF6_DBDESC_IBIT); + + ospf6_neighbor_clear_ls_lists(on); + + EVENT_OFF(on->thread_send_dbdesc); + on->dbdesc_seqnum++; /* Incr seqnum as per RFC2328, sec 10.3 */ + + event_add_event(master, ospf6_dbdesc_send, on, 0, + &on->thread_send_dbdesc); +} + +void oneway_received(struct event *thread) +{ + struct ospf6_neighbor *on; + + on = (struct ospf6_neighbor *)EVENT_ARG(thread); + assert(on); + + if (on->state < OSPF6_NEIGHBOR_TWOWAY) + return; + + if (IS_OSPF6_DEBUG_NEIGHBOR(EVENT)) + zlog_debug("Neighbor Event %s: *1Way-Received*", on->name); + + ospf6_neighbor_state_change(OSPF6_NEIGHBOR_INIT, on, + OSPF6_NEIGHBOR_EVENT_ONEWAY_RCVD); + event_add_event(master, neighbor_change, on->ospf6_if, 0, NULL); + + ospf6_neighbor_clear_ls_lists(on); + + EVENT_OFF(on->thread_send_dbdesc); + EVENT_OFF(on->thread_send_lsreq); + EVENT_OFF(on->thread_send_lsupdate); + EVENT_OFF(on->thread_send_lsack); + EVENT_OFF(on->thread_exchange_done); + EVENT_OFF(on->thread_adj_ok); +} + +void inactivity_timer(struct event *thread) +{ + struct ospf6_neighbor *on; + + on = (struct ospf6_neighbor *)EVENT_ARG(thread); + assert(on); + + if (IS_OSPF6_DEBUG_NEIGHBOR(EVENT)) + zlog_debug("Neighbor Event %s: *InactivityTimer*", on->name); + + on->drouter = on->prev_drouter = 0; + on->bdrouter = on->prev_bdrouter = 0; + + if (!OSPF6_GR_IS_ACTIVE_HELPER(on)) { + on->drouter = on->prev_drouter = 0; + on->bdrouter = on->prev_bdrouter = 0; + + ospf6_neighbor_state_change( + OSPF6_NEIGHBOR_DOWN, on, + OSPF6_NEIGHBOR_EVENT_INACTIVITY_TIMER); + event_add_event(master, neighbor_change, on->ospf6_if, 0, NULL); + + listnode_delete(on->ospf6_if->neighbor_list, on); + ospf6_neighbor_delete(on); + + } else { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "%s, Acting as HELPER for this neighbour, So restart the dead timer.", + __PRETTY_FUNCTION__); + + event_add_timer(master, inactivity_timer, on, + on->ospf6_if->dead_interval, + &on->inactivity_timer); + } +} + + +/* vty functions */ +/* show neighbor structure */ +static void ospf6_neighbor_show(struct vty *vty, struct ospf6_neighbor *on, + json_object *json_array, bool use_json) +{ + char router_id[16]; + char duration[64]; + struct timeval res; + char nstate[16]; + char deadtime[64]; + long h, m, s; + json_object *json_route; + + /* Router-ID (Name) */ + inet_ntop(AF_INET, &on->router_id, router_id, sizeof(router_id)); +#ifdef HAVE_GETNAMEINFO + { + } +#endif /*HAVE_GETNAMEINFO*/ + + /* Dead time */ + h = m = s = 0; + if (on->inactivity_timer) { + s = monotime_until(&on->inactivity_timer->u.sands, NULL) + / 1000000LL; + h = s / 3600; + s -= h * 3600; + m = s / 60; + s -= m * 60; + } + snprintf(deadtime, sizeof(deadtime), "%02ld:%02ld:%02ld", h, m, s); + + /* Neighbor State */ + if (on->ospf6_if->type == OSPF_IFTYPE_POINTOPOINT) + snprintf(nstate, sizeof(nstate), "PointToPoint"); + else { + if (on->router_id == on->drouter) + snprintf(nstate, sizeof(nstate), "DR"); + else if (on->router_id == on->bdrouter) + snprintf(nstate, sizeof(nstate), "BDR"); + else + snprintf(nstate, sizeof(nstate), "DROther"); + } + + /* Duration */ + monotime_since(&on->last_changed, &res); + timerstring(&res, duration, sizeof(duration)); + + /* + vty_out (vty, "%-15s %3d %11s %6s/%-12s %11s %s[%s]\n", + "Neighbor ID", "Pri", "DeadTime", "State", "IfState", + "Duration", "I/F", "State"); + */ + if (use_json) { + json_route = json_object_new_object(); + + json_object_string_add(json_route, "neighborId", router_id); + json_object_int_add(json_route, "priority", on->priority); + json_object_string_add(json_route, "deadTime", deadtime); + json_object_string_add(json_route, "state", + ospf6_neighbor_state_str[on->state]); + json_object_string_add(json_route, "ifState", nstate); + json_object_string_add(json_route, "duration", duration); + json_object_string_add(json_route, "interfaceName", + on->ospf6_if->interface->name); + json_object_string_add( + json_route, "interfaceState", + ospf6_interface_state_str[on->ospf6_if->state]); + + json_object_array_add(json_array, json_route); + } else + vty_out(vty, "%-15s %3d %11s %8s/%-12s %11s %s[%s]\n", + router_id, on->priority, deadtime, + ospf6_neighbor_state_str[on->state], nstate, duration, + on->ospf6_if->interface->name, + ospf6_interface_state_str[on->ospf6_if->state]); +} + +static void ospf6_neighbor_show_drchoice(struct vty *vty, + struct ospf6_neighbor *on, + json_object *json_array, bool use_json) +{ + char router_id[16]; + char drouter[16], bdrouter[16]; + char duration[64]; + struct timeval now, res; + json_object *json_route; + + /* + vty_out (vty, "%-15s %6s/%-11s %-15s %-15s %s[%s]\n", + "RouterID", "State", "Duration", "DR", "BDR", "I/F", + "State"); + */ + + inet_ntop(AF_INET, &on->router_id, router_id, sizeof(router_id)); + inet_ntop(AF_INET, &on->drouter, drouter, sizeof(drouter)); + inet_ntop(AF_INET, &on->bdrouter, bdrouter, sizeof(bdrouter)); + + monotime(&now); + timersub(&now, &on->last_changed, &res); + timerstring(&res, duration, sizeof(duration)); + + if (use_json) { + json_route = json_object_new_object(); + json_object_string_add(json_route, "routerId", router_id); + json_object_string_add(json_route, "state", + ospf6_neighbor_state_str[on->state]); + json_object_string_add(json_route, "duration", duration); + json_object_string_add(json_route, "dRouter", drouter); + json_object_string_add(json_route, "bdRouter", bdrouter); + json_object_string_add(json_route, "interfaceName", + on->ospf6_if->interface->name); + json_object_string_add( + json_route, "interfaceState", + ospf6_interface_state_str[on->ospf6_if->state]); + + json_object_array_add(json_array, json_route); + } else + vty_out(vty, "%-15s %8s/%-11s %-15s %-15s %s[%s]\n", router_id, + ospf6_neighbor_state_str[on->state], duration, drouter, + bdrouter, on->ospf6_if->interface->name, + ospf6_interface_state_str[on->ospf6_if->state]); +} + +static void ospf6_neighbor_show_detail(struct vty *vty, + struct ospf6_neighbor *on, + json_object *json, bool use_json) +{ + char drouter[16], bdrouter[16]; + char linklocal_addr[64], duration[32]; + struct timeval now, res; + struct ospf6_lsa *lsa, *lsanext; + json_object *json_neighbor; + json_object *json_array; + char db_desc_str[20]; + + inet_ntop(AF_INET6, &on->linklocal_addr, linklocal_addr, + sizeof(linklocal_addr)); + inet_ntop(AF_INET, &on->drouter, drouter, sizeof(drouter)); + inet_ntop(AF_INET, &on->bdrouter, bdrouter, sizeof(bdrouter)); + + monotime(&now); + timersub(&now, &on->last_changed, &res); + timerstring(&res, duration, sizeof(duration)); + + if (use_json) { + json_neighbor = json_object_new_object(); + json_object_string_add(json_neighbor, "area", + on->ospf6_if->area->name); + json_object_string_add(json_neighbor, "interface", + on->ospf6_if->interface->name); + json_object_int_add(json_neighbor, "interfaceIndex", + on->ospf6_if->interface->ifindex); + json_object_int_add(json_neighbor, "neighborInterfaceIndex", + on->ifindex); + json_object_string_add(json_neighbor, "linkLocalAddress", + linklocal_addr); + json_object_string_add(json_neighbor, "neighborState", + ospf6_neighbor_state_str[on->state]); + json_object_string_add(json_neighbor, "neighborStateDuration", + duration); + json_object_string_add(json_neighbor, "neighborDRouter", + drouter); + json_object_string_add(json_neighbor, "neighborBdRouter", + bdrouter); + snprintf(db_desc_str, sizeof(db_desc_str), "%s%s%s", + (CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_IBIT) + ? "Initial " + : ""), + (CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MBIT) + ? "More" + : ""), + (CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MSBIT) + ? "Master" + : "Slave")); + json_object_string_add(json_neighbor, "dbDescStatus", + db_desc_str); + + json_object_int_add(json_neighbor, "dbDescSeqNumber", + (unsigned long)ntohl(on->dbdesc_seqnum)); + + json_array = json_object_new_array(); + json_object_int_add(json_neighbor, "summaryListCount", + on->summary_list->count); + for (ALL_LSDB(on->summary_list, lsa, lsanext)) + json_object_array_add( + json_array, json_object_new_string(lsa->name)); + json_object_object_add(json_neighbor, "summaryListLsa", + json_array); + + json_array = json_object_new_array(); + json_object_int_add(json_neighbor, "requestListCount", + on->request_list->count); + for (ALL_LSDB(on->request_list, lsa, lsanext)) + json_object_array_add( + json_array, json_object_new_string(lsa->name)); + json_object_object_add(json_neighbor, "requestListLsa", + json_array); + + json_array = json_object_new_array(); + json_object_int_add(json_neighbor, "reTransListCount", + on->retrans_list->count); + for (ALL_LSDB(on->retrans_list, lsa, lsanext)) + json_object_array_add( + json_array, json_object_new_string(lsa->name)); + json_object_object_add(json_neighbor, "reTransListLsa", + json_array); + + + timerclear(&res); + if (event_is_scheduled(on->thread_send_dbdesc)) + timersub(&on->thread_send_dbdesc->u.sands, &now, &res); + timerstring(&res, duration, sizeof(duration)); + json_object_int_add(json_neighbor, "pendingLsaDbDescCount", + on->dbdesc_list->count); + json_object_string_add(json_neighbor, "pendingLsaDbDescTime", + duration); + json_object_string_add( + json_neighbor, "dbDescSendThread", + (event_is_scheduled(on->thread_send_dbdesc) ? "on" + : "off")); + json_array = json_object_new_array(); + for (ALL_LSDB(on->dbdesc_list, lsa, lsanext)) + json_object_array_add( + json_array, json_object_new_string(lsa->name)); + json_object_object_add(json_neighbor, "pendingLsaDbDesc", + json_array); + + timerclear(&res); + if (event_is_scheduled(on->thread_send_lsreq)) + timersub(&on->thread_send_lsreq->u.sands, &now, &res); + timerstring(&res, duration, sizeof(duration)); + json_object_int_add(json_neighbor, "pendingLsaLsReqCount", + on->request_list->count); + json_object_string_add(json_neighbor, "pendingLsaLsReqTime", + duration); + json_object_string_add( + json_neighbor, "lsReqSendThread", + (event_is_scheduled(on->thread_send_lsreq) ? "on" + : "off")); + json_array = json_object_new_array(); + for (ALL_LSDB(on->request_list, lsa, lsanext)) + json_object_array_add( + json_array, json_object_new_string(lsa->name)); + json_object_object_add(json_neighbor, "pendingLsaLsReq", + json_array); + + + timerclear(&res); + if (event_is_scheduled(on->thread_send_lsupdate)) + timersub(&on->thread_send_lsupdate->u.sands, &now, + &res); + timerstring(&res, duration, sizeof(duration)); + json_object_int_add(json_neighbor, "pendingLsaLsUpdateCount", + on->lsupdate_list->count); + json_object_string_add(json_neighbor, "pendingLsaLsUpdateTime", + duration); + json_object_string_add( + json_neighbor, "lsUpdateSendThread", + (event_is_scheduled(on->thread_send_lsupdate) ? "on" + : "off")); + json_array = json_object_new_array(); + for (ALL_LSDB(on->lsupdate_list, lsa, lsanext)) + json_object_array_add( + json_array, json_object_new_string(lsa->name)); + json_object_object_add(json_neighbor, "pendingLsaLsUpdate", + json_array); + + timerclear(&res); + if (event_is_scheduled(on->thread_send_lsack)) + timersub(&on->thread_send_lsack->u.sands, &now, &res); + timerstring(&res, duration, sizeof(duration)); + json_object_int_add(json_neighbor, "pendingLsaLsAckCount", + on->lsack_list->count); + json_object_string_add(json_neighbor, "pendingLsaLsAckTime", + duration); + json_object_string_add( + json_neighbor, "lsAckSendThread", + (event_is_scheduled(on->thread_send_lsack) ? "on" + : "off")); + json_array = json_object_new_array(); + for (ALL_LSDB(on->lsack_list, lsa, lsanext)) + json_object_array_add( + json_array, json_object_new_string(lsa->name)); + json_object_object_add(json_neighbor, "pendingLsaLsAck", + json_array); + + bfd_sess_show(vty, json_neighbor, on->bfd_session); + + if (on->auth_present == true) { + json_object_string_add(json_neighbor, "authStatus", + "enabled"); + json_object_int_add( + json_neighbor, "recvdHelloHigherSeqNo", + on->seqnum_h[OSPF6_MESSAGE_TYPE_HELLO]); + json_object_int_add( + json_neighbor, "recvdHelloLowerSeqNo", + on->seqnum_l[OSPF6_MESSAGE_TYPE_HELLO]); + json_object_int_add( + json_neighbor, "recvdDBDescHigherSeqNo", + on->seqnum_h[OSPF6_MESSAGE_TYPE_DBDESC]); + json_object_int_add( + json_neighbor, "recvdDBDescLowerSeqNo", + on->seqnum_l[OSPF6_MESSAGE_TYPE_DBDESC]); + json_object_int_add( + json_neighbor, "recvdLSReqHigherSeqNo", + on->seqnum_h[OSPF6_MESSAGE_TYPE_LSREQ]); + json_object_int_add( + json_neighbor, "recvdLSReqLowerSeqNo", + on->seqnum_l[OSPF6_MESSAGE_TYPE_LSREQ]); + json_object_int_add( + json_neighbor, "recvdLSUpdHigherSeqNo", + on->seqnum_h[OSPF6_MESSAGE_TYPE_LSUPDATE]); + json_object_int_add( + json_neighbor, "recvdLSUpdLowerSeqNo", + on->seqnum_l[OSPF6_MESSAGE_TYPE_LSUPDATE]); + json_object_int_add( + json_neighbor, "recvdLSAckHigherSeqNo", + on->seqnum_h[OSPF6_MESSAGE_TYPE_LSACK]); + json_object_int_add( + json_neighbor, "recvdLSAckLowerSeqNo", + on->seqnum_l[OSPF6_MESSAGE_TYPE_LSACK]); + } else + json_object_string_add(json_neighbor, "authStatus", + "disabled"); + + json_object_object_add(json, on->name, json_neighbor); + + } else { + vty_out(vty, " Neighbor %s\n", on->name); + vty_out(vty, " Area %s via interface %s (ifindex %d)\n", + on->ospf6_if->area->name, on->ospf6_if->interface->name, + on->ospf6_if->interface->ifindex); + vty_out(vty, " His IfIndex: %d Link-local address: %s\n", + on->ifindex, linklocal_addr); + vty_out(vty, " State %s for a duration of %s\n", + ospf6_neighbor_state_str[on->state], duration); + vty_out(vty, " His choice of DR/BDR %s/%s, Priority %d\n", + drouter, bdrouter, on->priority); + vty_out(vty, " DbDesc status: %s%s%s SeqNum: %#lx\n", + (CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_IBIT) + ? "Initial " + : ""), + (CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MBIT) + ? "More " + : ""), + (CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MSBIT) + ? "Master" + : "Slave"), + (unsigned long)ntohl(on->dbdesc_seqnum)); + + vty_out(vty, " Summary-List: %d LSAs\n", + on->summary_list->count); + for (ALL_LSDB(on->summary_list, lsa, lsanext)) + vty_out(vty, " %s\n", lsa->name); + + vty_out(vty, " Request-List: %d LSAs\n", + on->request_list->count); + for (ALL_LSDB(on->request_list, lsa, lsanext)) + vty_out(vty, " %s\n", lsa->name); + + vty_out(vty, " Retrans-List: %d LSAs\n", + on->retrans_list->count); + for (ALL_LSDB(on->retrans_list, lsa, lsanext)) + vty_out(vty, " %s\n", lsa->name); + + timerclear(&res); + if (event_is_scheduled(on->thread_send_dbdesc)) + timersub(&on->thread_send_dbdesc->u.sands, &now, &res); + timerstring(&res, duration, sizeof(duration)); + vty_out(vty, + " %d Pending LSAs for DbDesc in Time %s [thread %s]\n", + on->dbdesc_list->count, duration, + (event_is_scheduled(on->thread_send_dbdesc) ? "on" + : "off")); + for (ALL_LSDB(on->dbdesc_list, lsa, lsanext)) + vty_out(vty, " %s\n", lsa->name); + + timerclear(&res); + if (event_is_scheduled(on->thread_send_lsreq)) + timersub(&on->thread_send_lsreq->u.sands, &now, &res); + timerstring(&res, duration, sizeof(duration)); + vty_out(vty, + " %d Pending LSAs for LSReq in Time %s [thread %s]\n", + on->request_list->count, duration, + (event_is_scheduled(on->thread_send_lsreq) ? "on" + : "off")); + for (ALL_LSDB(on->request_list, lsa, lsanext)) + vty_out(vty, " %s\n", lsa->name); + + timerclear(&res); + if (event_is_scheduled(on->thread_send_lsupdate)) + timersub(&on->thread_send_lsupdate->u.sands, &now, + &res); + timerstring(&res, duration, sizeof(duration)); + vty_out(vty, + " %d Pending LSAs for LSUpdate in Time %s [thread %s]\n", + on->lsupdate_list->count, duration, + (event_is_scheduled(on->thread_send_lsupdate) ? "on" + : "off")); + for (ALL_LSDB(on->lsupdate_list, lsa, lsanext)) + vty_out(vty, " %s\n", lsa->name); + + timerclear(&res); + if (event_is_scheduled(on->thread_send_lsack)) + timersub(&on->thread_send_lsack->u.sands, &now, &res); + timerstring(&res, duration, sizeof(duration)); + vty_out(vty, + " %d Pending LSAs for LSAck in Time %s [thread %s]\n", + on->lsack_list->count, duration, + (event_is_scheduled(on->thread_send_lsack) ? "on" + : "off")); + for (ALL_LSDB(on->lsack_list, lsa, lsanext)) + vty_out(vty, " %s\n", lsa->name); + + bfd_sess_show(vty, NULL, on->bfd_session); + + if (on->auth_present == true) { + vty_out(vty, " Authentication header present\n"); + vty_out(vty, + "\t\t\t hello DBDesc LSReq LSUpd LSAck\n"); + vty_out(vty, + " Higher sequence no 0x%-10X 0x%-10X 0x%-10X 0x%-10X 0x%-10X\n", + on->seqnum_h[OSPF6_MESSAGE_TYPE_HELLO], + on->seqnum_h[OSPF6_MESSAGE_TYPE_DBDESC], + on->seqnum_h[OSPF6_MESSAGE_TYPE_LSREQ], + on->seqnum_h[OSPF6_MESSAGE_TYPE_LSUPDATE], + on->seqnum_h[OSPF6_MESSAGE_TYPE_LSACK]); + vty_out(vty, + " Lower sequence no 0x%-10X 0x%-10X 0x%-10X 0x%-10X 0x%-10X\n", + on->seqnum_l[OSPF6_MESSAGE_TYPE_HELLO], + on->seqnum_l[OSPF6_MESSAGE_TYPE_DBDESC], + on->seqnum_l[OSPF6_MESSAGE_TYPE_LSREQ], + on->seqnum_l[OSPF6_MESSAGE_TYPE_LSUPDATE], + on->seqnum_l[OSPF6_MESSAGE_TYPE_LSACK]); + } else + vty_out(vty, " Authentication header not present\n"); + } +} + +static void ospf6_neighbor_show_detail_common(struct vty *vty, + struct ospf6 *ospf6, bool uj, + bool detail, bool drchoice) +{ + struct ospf6_neighbor *on; + struct ospf6_interface *oi; + struct ospf6_area *oa; + struct listnode *i, *j, *k; + json_object *json = NULL; + json_object *json_array = NULL; + void (*showfunc)(struct vty *, struct ospf6_neighbor *, + json_object *json, bool use_json); + + if (detail) + showfunc = ospf6_neighbor_show_detail; + else if (drchoice) + showfunc = ospf6_neighbor_show_drchoice; + else + showfunc = ospf6_neighbor_show; + + if (uj) { + json = json_object_new_object(); + json_array = json_object_new_array(); + } else { + if (showfunc == ospf6_neighbor_show) + vty_out(vty, "%-15s %3s %11s %8s/%-12s %11s %s[%s]\n", + "Neighbor ID", "Pri", "DeadTime", "State", + "IfState", "Duration", "I/F", "State"); + else if (showfunc == ospf6_neighbor_show_drchoice) + vty_out(vty, "%-15s %8s/%-11s %-15s %-15s %s[%s]\n", + "RouterID", "State", "Duration", "DR", "BDR", + "I/F", "State"); + } + + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, i, oa)) + for (ALL_LIST_ELEMENTS_RO(oa->if_list, j, oi)) + for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, k, on)) { + if (showfunc == ospf6_neighbor_show_detail) + (*showfunc)(vty, on, json, uj); + else + (*showfunc)(vty, on, json_array, uj); + } + + if (uj) { + if (showfunc != ospf6_neighbor_show_detail) + json_object_object_add(json, "neighbors", json_array); + else + json_object_free(json_array); + vty_json(vty, json); + } +} + +DEFUN(show_ipv6_ospf6_neighbor, show_ipv6_ospf6_neighbor_cmd, + "show ipv6 ospf6 [vrf <NAME|all>] neighbor [<detail|drchoice>] [json]", + SHOW_STR IP6_STR OSPF6_STR VRF_CMD_HELP_STR + "All VRFs\n" + "Neighbor list\n" + "Display details\n" + "Display DR choices\n" JSON_STR) +{ + struct ospf6 *ospf6; + struct listnode *node; + const char *vrf_name = NULL; + bool all_vrf = false; + int idx_vrf = 0; + int idx_type = 4; + bool uj = use_json(argc, argv); + bool detail = false; + bool drchoice = false; + + OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + + if (argv_find(argv, argc, "detail", &idx_type)) + detail = true; + else if (argv_find(argv, argc, "drchoice", &idx_type)) + drchoice = true; + + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { + if (all_vrf || strcmp(ospf6->name, vrf_name) == 0) { + ospf6_neighbor_show_detail_common(vty, ospf6, uj, + detail, drchoice); + if (!all_vrf) + break; + } + } + + OSPF6_CMD_CHECK_VRF(uj, all_vrf, ospf6); + + return CMD_SUCCESS; +} + +static int ospf6_neighbor_show_common(struct vty *vty, int argc, + struct cmd_token **argv, + struct ospf6 *ospf6, int idx_ipv4, + bool uj) +{ + struct ospf6_neighbor *on; + struct ospf6_interface *oi; + struct ospf6_area *oa; + struct listnode *i, *j, *k; + void (*showfunc)(struct vty *, struct ospf6_neighbor *, + json_object *json, bool use_json); + uint32_t router_id; + json_object *json = NULL; + + showfunc = ospf6_neighbor_show_detail; + if (uj) + json = json_object_new_object(); + + if ((inet_pton(AF_INET, argv[idx_ipv4]->arg, &router_id)) != 1) { + vty_out(vty, "Router-ID is not parsable: %s\n", + argv[idx_ipv4]->arg); + return CMD_SUCCESS; + } + + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, i, oa)) + for (ALL_LIST_ELEMENTS_RO(oa->if_list, j, oi)) + for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, k, on)) { + if (router_id == on->router_id) + (*showfunc)(vty, on, json, uj); + } + + if (uj) + vty_json(vty, json); + + return CMD_SUCCESS; +} + +DEFUN(show_ipv6_ospf6_neighbor_one, show_ipv6_ospf6_neighbor_one_cmd, + "show ipv6 ospf6 [vrf <NAME|all>] neighbor A.B.C.D [json]", + SHOW_STR IP6_STR OSPF6_STR VRF_CMD_HELP_STR + "All VRFs\n" + "Neighbor list\n" + "Specify Router-ID as IPv4 address notation\n" JSON_STR) +{ + int idx_ipv4 = 4; + struct ospf6 *ospf6; + struct listnode *node; + const char *vrf_name = NULL; + bool all_vrf = false; + int idx_vrf = 0; + bool uj = use_json(argc, argv); + + OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + if (idx_vrf > 0) + idx_ipv4 += 2; + + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { + if (all_vrf || strcmp(ospf6->name, vrf_name) == 0) { + ospf6_neighbor_show_common(vty, argc, argv, ospf6, + idx_ipv4, uj); + + if (!all_vrf) + break; + } + } + + OSPF6_CMD_CHECK_VRF(uj, all_vrf, ospf6); + + return CMD_SUCCESS; +} + +void ospf6_neighbor_init(void) +{ + install_element(VIEW_NODE, &show_ipv6_ospf6_neighbor_cmd); + install_element(VIEW_NODE, &show_ipv6_ospf6_neighbor_one_cmd); +} + +DEFUN (debug_ospf6_neighbor, + debug_ospf6_neighbor_cmd, + "debug ospf6 neighbor [<state|event>]", + DEBUG_STR + OSPF6_STR + "Debug OSPFv3 Neighbor\n" + "Debug OSPFv3 Neighbor State Change\n" + "Debug OSPFv3 Neighbor Event\n") +{ + int idx_type = 3; + unsigned char level = 0; + + if (argc == 4) { + if (!strncmp(argv[idx_type]->arg, "s", 1)) + level = OSPF6_DEBUG_NEIGHBOR_STATE; + else if (!strncmp(argv[idx_type]->arg, "e", 1)) + level = OSPF6_DEBUG_NEIGHBOR_EVENT; + } else + level = OSPF6_DEBUG_NEIGHBOR_STATE | OSPF6_DEBUG_NEIGHBOR_EVENT; + + OSPF6_DEBUG_NEIGHBOR_ON(level); + return CMD_SUCCESS; +} + + +DEFUN (no_debug_ospf6_neighbor, + no_debug_ospf6_neighbor_cmd, + "no debug ospf6 neighbor [<state|event>]", + NO_STR + DEBUG_STR + OSPF6_STR + "Debug OSPFv3 Neighbor\n" + "Debug OSPFv3 Neighbor State Change\n" + "Debug OSPFv3 Neighbor Event\n") +{ + int idx_type = 4; + unsigned char level = 0; + + if (argc == 5) { + if (!strncmp(argv[idx_type]->arg, "s", 1)) + level = OSPF6_DEBUG_NEIGHBOR_STATE; + if (!strncmp(argv[idx_type]->arg, "e", 1)) + level = OSPF6_DEBUG_NEIGHBOR_EVENT; + } else + level = OSPF6_DEBUG_NEIGHBOR_STATE | OSPF6_DEBUG_NEIGHBOR_EVENT; + + OSPF6_DEBUG_NEIGHBOR_OFF(level); + return CMD_SUCCESS; +} + + +DEFUN (no_debug_ospf6, + no_debug_ospf6_cmd, + "no debug ospf6", + NO_STR + DEBUG_STR + OSPF6_STR) +{ + unsigned int i; + + OSPF6_DEBUG_ABR_OFF(); + OSPF6_DEBUG_ASBR_OFF(); + OSPF6_DEBUG_BROUTER_OFF(); + OSPF6_DEBUG_BROUTER_SPECIFIC_ROUTER_OFF(); + OSPF6_DEBUG_BROUTER_SPECIFIC_AREA_OFF(); + OSPF6_DEBUG_FLOODING_OFF(); + OSPF6_DEBUG_INTERFACE_OFF(); + + ospf6_lsa_debug_set_all(false); + + for (i = 0; i < 6; i++) + OSPF6_DEBUG_MESSAGE_OFF(i, + OSPF6_DEBUG_NEIGHBOR_STATE + | OSPF6_DEBUG_NEIGHBOR_EVENT); + + OSPF6_DEBUG_NEIGHBOR_OFF(OSPF6_DEBUG_NEIGHBOR_STATE + | OSPF6_DEBUG_NEIGHBOR_EVENT); + OSPF6_DEBUG_ROUTE_OFF(OSPF6_DEBUG_ROUTE_TABLE); + OSPF6_DEBUG_ROUTE_OFF(OSPF6_DEBUG_ROUTE_INTRA); + OSPF6_DEBUG_ROUTE_OFF(OSPF6_DEBUG_ROUTE_INTER); + OSPF6_DEBUG_ROUTE_OFF(OSPF6_DEBUG_ROUTE_MEMORY); + OSPF6_DEBUG_SPF_OFF(OSPF6_DEBUG_SPF_PROCESS); + OSPF6_DEBUG_SPF_OFF(OSPF6_DEBUG_SPF_TIME); + OSPF6_DEBUG_SPF_OFF(OSPF6_DEBUG_SPF_DATABASE); + OSPF6_DEBUG_ZEBRA_OFF(OSPF6_DEBUG_ZEBRA_SEND | OSPF6_DEBUG_ZEBRA_RECV); + + return CMD_SUCCESS; +} + +int config_write_ospf6_debug_neighbor(struct vty *vty) +{ + if (IS_OSPF6_DEBUG_NEIGHBOR(STATE) && IS_OSPF6_DEBUG_NEIGHBOR(EVENT)) + vty_out(vty, "debug ospf6 neighbor\n"); + else if (IS_OSPF6_DEBUG_NEIGHBOR(STATE)) + vty_out(vty, "debug ospf6 neighbor state\n"); + else if (IS_OSPF6_DEBUG_NEIGHBOR(EVENT)) + vty_out(vty, "debug ospf6 neighbor event\n"); + return 0; +} + +void install_element_ospf6_debug_neighbor(void) +{ + install_element(ENABLE_NODE, &debug_ospf6_neighbor_cmd); + install_element(ENABLE_NODE, &no_debug_ospf6_neighbor_cmd); + install_element(ENABLE_NODE, &no_debug_ospf6_cmd); + install_element(CONFIG_NODE, &debug_ospf6_neighbor_cmd); + install_element(CONFIG_NODE, &no_debug_ospf6_neighbor_cmd); + install_element(CONFIG_NODE, &no_debug_ospf6_cmd); +} diff --git a/ospf6d/ospf6_neighbor.h b/ospf6d/ospf6_neighbor.h new file mode 100644 index 0000000..226f4c1 --- /dev/null +++ b/ospf6d/ospf6_neighbor.h @@ -0,0 +1,214 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2003 Yasuhiro Ohara + */ + +#ifndef OSPF6_NEIGHBOR_H +#define OSPF6_NEIGHBOR_H + +#include "hook.h" + +/* Forward declaration(s). */ +struct ospf6_area; + +/* Debug option */ +extern unsigned char conf_debug_ospf6_neighbor; +#define OSPF6_DEBUG_NEIGHBOR_STATE 0x01 +#define OSPF6_DEBUG_NEIGHBOR_EVENT 0x02 +#define OSPF6_DEBUG_NEIGHBOR_ON(level) (conf_debug_ospf6_neighbor |= (level)) +#define OSPF6_DEBUG_NEIGHBOR_OFF(level) (conf_debug_ospf6_neighbor &= ~(level)) +#define IS_OSPF6_DEBUG_NEIGHBOR(level) \ + (conf_debug_ospf6_neighbor & OSPF6_DEBUG_NEIGHBOR_##level) + +struct ospf6_helper_info { + + /* Grace interval received from + * Restarting Router. + */ + uint32_t recvd_grace_period; + + /* Grace interval used for grace + * gracetimer. + */ + uint32_t actual_grace_period; + + /* Grace timer,This Router acts as + * helper until this timer until + * this timer expires. + */ + struct event *t_grace_timer; + + /* Helper status */ + uint32_t gr_helper_status; + + /* Helper exit reason*/ + uint32_t helper_exit_reason; + + /* Planned/Unplanned restart*/ + uint32_t gr_restart_reason; + + + /* Helper rejected reason */ + uint32_t rejected_reason; +}; + +/* Neighbor structure */ +struct ospf6_neighbor { + /* Neighbor Router ID String */ + char name[36]; + + /* OSPFv3 Interface this neighbor belongs to */ + struct ospf6_interface *ospf6_if; + + /* Neighbor state */ + uint8_t state; + + /* timestamp of last changing state */ + uint32_t state_change; + struct timeval last_changed; + + /* last received hello */ + struct timeval last_hello; + uint32_t hello_in; + + /* Neighbor Router ID */ + in_addr_t router_id; + + /* Neighbor Interface ID */ + ifindex_t ifindex; + + /* Router Priority of this neighbor */ + uint8_t priority; + + in_addr_t drouter; + in_addr_t bdrouter; + in_addr_t prev_drouter; + in_addr_t prev_bdrouter; + + /* Options field (Capability) */ + char options[3]; + + /* IPaddr of I/F on neighbour's link */ + struct in6_addr linklocal_addr; + + /* For Database Exchange */ + uint8_t dbdesc_bits; + uint32_t dbdesc_seqnum; + /* Last received Database Description packet */ + struct ospf6_dbdesc dbdesc_last; + + /* LS-list */ + struct ospf6_lsdb *summary_list; + struct ospf6_lsdb *request_list; + struct ospf6_lsdb *retrans_list; + + /* LSA list for message transmission */ + struct ospf6_lsdb *dbdesc_list; + struct ospf6_lsdb *lsreq_list; + struct ospf6_lsdb *lsupdate_list; + struct ospf6_lsdb *lsack_list; + + struct ospf6_lsa *last_ls_req; + + /* Inactivity timer */ + struct event *inactivity_timer; + + /* Timer to release the last dbdesc packet */ + struct event *last_dbdesc_release_timer; + + /* Thread for sending message */ + struct event *thread_send_dbdesc; + struct event *thread_send_lsreq; + struct event *thread_send_lsupdate; + struct event *thread_send_lsack; + struct event *thread_exchange_done; + struct event *thread_adj_ok; + struct event *event_loading_done; + + /* BFD information */ + struct bfd_session_params *bfd_session; + + /* ospf6 graceful restart HELPER info */ + struct ospf6_helper_info gr_helper_info; + + /* seqnum_h/l is used to compare sequence + * number in received packet Auth header + */ + uint32_t seqnum_h[OSPF6_MESSAGE_TYPE_MAX]; + uint32_t seqnum_l[OSPF6_MESSAGE_TYPE_MAX]; + bool auth_present; + bool lls_present; +}; + +/* Neighbor state */ +#define OSPF6_NEIGHBOR_DOWN 1 +#define OSPF6_NEIGHBOR_ATTEMPT 2 +#define OSPF6_NEIGHBOR_INIT 3 +#define OSPF6_NEIGHBOR_TWOWAY 4 +#define OSPF6_NEIGHBOR_EXSTART 5 +#define OSPF6_NEIGHBOR_EXCHANGE 6 +#define OSPF6_NEIGHBOR_LOADING 7 +#define OSPF6_NEIGHBOR_FULL 8 + +/* Neighbor Events */ +#define OSPF6_NEIGHBOR_EVENT_NO_EVENT 0 +#define OSPF6_NEIGHBOR_EVENT_HELLO_RCVD 1 +#define OSPF6_NEIGHBOR_EVENT_TWOWAY_RCVD 2 +#define OSPF6_NEIGHBOR_EVENT_NEGOTIATION_DONE 3 +#define OSPF6_NEIGHBOR_EVENT_EXCHANGE_DONE 4 +#define OSPF6_NEIGHBOR_EVENT_LOADING_DONE 5 +#define OSPF6_NEIGHBOR_EVENT_ADJ_OK 6 +#define OSPF6_NEIGHBOR_EVENT_SEQNUMBER_MISMATCH 7 +#define OSPF6_NEIGHBOR_EVENT_BAD_LSREQ 8 +#define OSPF6_NEIGHBOR_EVENT_ONEWAY_RCVD 9 +#define OSPF6_NEIGHBOR_EVENT_INACTIVITY_TIMER 10 +#define OSPF6_NEIGHBOR_EVENT_MAX_EVENT 11 + +extern const char *const ospf6_neighbor_event_str[]; + +static inline const char *ospf6_neighbor_event_string(int event) +{ +#define OSPF6_NEIGHBOR_UNKNOWN_EVENT_STRING "UnknownEvent" + + if (event < OSPF6_NEIGHBOR_EVENT_MAX_EVENT) + return ospf6_neighbor_event_str[event]; + return OSPF6_NEIGHBOR_UNKNOWN_EVENT_STRING; +} + +extern const char *const ospf6_neighbor_state_str[]; + + +/* Function Prototypes */ +int ospf6_neighbor_cmp(void *va, void *vb); +void ospf6_neighbor_dbex_init(struct ospf6_neighbor *on); + +struct ospf6_neighbor *ospf6_neighbor_lookup(uint32_t router_id, + struct ospf6_interface *oi); +struct ospf6_neighbor *ospf6_area_neighbor_lookup(struct ospf6_area *area, + uint32_t router_id); +struct ospf6_neighbor *ospf6_neighbor_create(uint32_t router_id, + struct ospf6_interface *oi); +void ospf6_neighbor_delete(struct ospf6_neighbor *on); + +/* Neighbor event */ +extern void hello_received(struct event *thread); +extern void twoway_received(struct event *thread); +extern void negotiation_done(struct event *thread); +extern void exchange_done(struct event *thread); +extern void loading_done(struct event *thread); +extern void adj_ok(struct event *thread); +extern void seqnumber_mismatch(struct event *thread); +extern void bad_lsreq(struct event *thread); +extern void oneway_received(struct event *thread); +extern void inactivity_timer(struct event *thread); +extern void ospf6_check_nbr_loading(struct ospf6_neighbor *on); + +extern void ospf6_neighbor_init(void); +extern int config_write_ospf6_debug_neighbor(struct vty *vty); +extern void install_element_ospf6_debug_neighbor(void); + +DECLARE_HOOK(ospf6_neighbor_change, + (struct ospf6_neighbor * on, int state, int next_state), + (on, state, next_state)); + +#endif /* OSPF6_NEIGHBOR_H */ diff --git a/ospf6d/ospf6_network.c b/ospf6d/ospf6_network.c new file mode 100644 index 0000000..eddf877 --- /dev/null +++ b/ospf6d/ospf6_network.c @@ -0,0 +1,251 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2003 Yasuhiro Ohara + */ + +#include <zebra.h> + +#include "log.h" +#include "memory.h" +#include "sockunion.h" +#include "sockopt.h" +#include "privs.h" +#include "lib_errors.h" +#include "vrf.h" + +#include "libospf.h" +#include "ospf6_proto.h" +#include "ospf6_top.h" +#include "ospf6_network.h" +#include "ospf6d.h" +#include "ospf6_message.h" + +struct in6_addr allspfrouters6; +struct in6_addr alldrouters6; + +/* setsockopt MulticastLoop to off */ +static void ospf6_reset_mcastloop(int ospf6_sock) +{ + unsigned int off = 0; + if (setsockopt(ospf6_sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off, + sizeof(unsigned int)) + < 0) + zlog_warn("Network: reset IPV6_MULTICAST_LOOP failed: %s", + safe_strerror(errno)); +} + +static void ospf6_set_pktinfo(int ospf6_sock) +{ + setsockopt_ipv6_pktinfo(ospf6_sock, 1); +} + +static void ospf6_set_transport_class(int ospf6_sock) +{ +#ifdef IPTOS_PREC_INTERNETCONTROL + setsockopt_ipv6_tclass(ospf6_sock, IPTOS_PREC_INTERNETCONTROL); +#endif +} + +void ospf6_serv_close(int *ospf6_sock) +{ + if (*ospf6_sock != -1) { + close(*ospf6_sock); + *ospf6_sock = -1; + return; + } +} + +/* Make ospf6d's server socket. */ +int ospf6_serv_sock(struct ospf6 *ospf6) +{ + int ospf6_sock; + + if (ospf6->fd != -1) + return -1; + + if (ospf6->vrf_id == VRF_UNKNOWN) + return -1; + + frr_with_privs(&ospf6d_privs) { + + ospf6_sock = vrf_socket(AF_INET6, SOCK_RAW, IPPROTO_OSPFIGP, + ospf6->vrf_id, ospf6->name); + if (ospf6_sock < 0) { + zlog_warn("Network: can't create OSPF6 socket."); + return -1; + } + } + +/* set socket options */ +#if 1 + sockopt_reuseaddr(ospf6_sock); +#else + ospf6_set_reuseaddr(); +#endif /*1*/ + ospf6_reset_mcastloop(ospf6_sock); + ospf6_set_pktinfo(ospf6_sock); + ospf6_set_transport_class(ospf6_sock); + + ospf6->fd = ospf6_sock; + /* setup global in6_addr, allspf6 and alldr6 for later use */ + inet_pton(AF_INET6, ALLSPFROUTERS6, &allspfrouters6); + inet_pton(AF_INET6, ALLDROUTERS6, &alldrouters6); + + return 0; +} + +/* ospf6 set socket option */ +int ospf6_sso(ifindex_t ifindex, struct in6_addr *group, int option, int sockfd) +{ + struct ipv6_mreq mreq6; + int ret; + int bufsize = (8 * 1024 * 1024); + + if (sockfd == -1) + return -1; + + assert(ifindex); + mreq6.ipv6mr_interface = ifindex; + memcpy(&mreq6.ipv6mr_multiaddr, group, sizeof(struct in6_addr)); + + ret = setsockopt(sockfd, IPPROTO_IPV6, option, &mreq6, sizeof(mreq6)); + if (ret < 0) { + flog_err_sys( + EC_LIB_SOCKET, + "Network: setsockopt (%d) on ifindex %d failed: %s", + option, ifindex, safe_strerror(errno)); + return ret; + } + + setsockopt_so_sendbuf(sockfd, bufsize); + setsockopt_so_recvbuf(sockfd, bufsize); + + return 0; +} + +static int iov_count(struct iovec *iov) +{ + int i; + for (i = 0; iov[i].iov_base; i++) + ; + return i; +} + +static int iov_totallen(struct iovec *iov) +{ + int i; + int totallen = 0; + for (i = 0; iov[i].iov_base; i++) + totallen += iov[i].iov_len; + return totallen; +} + +int ospf6_sendmsg(struct in6_addr *src, struct in6_addr *dst, + ifindex_t ifindex, struct iovec *message, int ospf6_sock) +{ + int retval; + struct msghdr smsghdr; + struct cmsghdr *scmsgp; + union { + struct cmsghdr hdr; + uint8_t buf[CMSG_SPACE(sizeof(struct in6_pktinfo))]; + } cmsgbuf; + struct in6_pktinfo *pktinfo; + struct sockaddr_in6 dst_sin6; + + assert(dst); + + memset(&cmsgbuf, 0, sizeof(cmsgbuf)); + scmsgp = (struct cmsghdr *)&cmsgbuf; + pktinfo = (struct in6_pktinfo *)(CMSG_DATA(scmsgp)); + memset(&dst_sin6, 0, sizeof(dst_sin6)); + + /* source address */ + pktinfo->ipi6_ifindex = ifindex; + if (src) + memcpy(&pktinfo->ipi6_addr, src, sizeof(struct in6_addr)); + else + memset(&pktinfo->ipi6_addr, 0, sizeof(struct in6_addr)); + + /* destination address */ + dst_sin6.sin6_family = AF_INET6; +#ifdef SIN6_LEN + dst_sin6.sin6_len = sizeof(struct sockaddr_in6); +#endif /*SIN6_LEN*/ + memcpy(&dst_sin6.sin6_addr, dst, sizeof(struct in6_addr)); + dst_sin6.sin6_scope_id = ifindex; + + /* send control msg */ + scmsgp->cmsg_level = IPPROTO_IPV6; + scmsgp->cmsg_type = IPV6_PKTINFO; + scmsgp->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); + /* scmsgp = CMSG_NXTHDR (&smsghdr, scmsgp); */ + + /* send msg hdr */ + memset(&smsghdr, 0, sizeof(smsghdr)); + smsghdr.msg_iov = message; + smsghdr.msg_iovlen = iov_count(message); + smsghdr.msg_name = (caddr_t)&dst_sin6; + smsghdr.msg_namelen = sizeof(struct sockaddr_in6); + smsghdr.msg_control = (caddr_t)&cmsgbuf.buf; + smsghdr.msg_controllen = sizeof(cmsgbuf.buf); + + retval = sendmsg(ospf6_sock, &smsghdr, 0); + if (retval != iov_totallen(message)) + zlog_warn("sendmsg failed: source: %pI6 Dest: %pI6 ifindex: %d: %s (%d)", + src, dst, ifindex, + safe_strerror(errno), errno); + + return retval; +} + +int ospf6_recvmsg(struct in6_addr *src, struct in6_addr *dst, + ifindex_t *ifindex, struct iovec *message, int ospf6_sock) +{ + int retval; + struct msghdr rmsghdr; + struct cmsghdr *rcmsgp; + uint8_t cmsgbuf[CMSG_SPACE(sizeof(struct in6_pktinfo))]; + struct in6_pktinfo *pktinfo; + struct sockaddr_in6 src_sin6; + + rcmsgp = (struct cmsghdr *)cmsgbuf; + pktinfo = (struct in6_pktinfo *)(CMSG_DATA(rcmsgp)); + memset(&src_sin6, 0, sizeof(src_sin6)); + + /* receive control msg */ + rcmsgp->cmsg_level = IPPROTO_IPV6; + rcmsgp->cmsg_type = IPV6_PKTINFO; + rcmsgp->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); + /* rcmsgp = CMSG_NXTHDR (&rmsghdr, rcmsgp); */ + + /* receive msg hdr */ + memset(&rmsghdr, 0, sizeof(rmsghdr)); + rmsghdr.msg_iov = message; + rmsghdr.msg_iovlen = iov_count(message); + rmsghdr.msg_name = (caddr_t)&src_sin6; + rmsghdr.msg_namelen = sizeof(struct sockaddr_in6); + rmsghdr.msg_control = (caddr_t)cmsgbuf; + rmsghdr.msg_controllen = sizeof(cmsgbuf); + + retval = recvmsg(ospf6_sock, &rmsghdr, MSG_DONTWAIT); + if (retval < 0) { + if (errno != EAGAIN && errno != EWOULDBLOCK) + zlog_warn("stream_recvmsg failed: %s", + safe_strerror(errno)); + return retval; + } else if (retval == iov_totallen(message)) + zlog_warn("recvmsg read full buffer size: %d", retval); + + /* source address */ + assert(src); + memcpy(src, &src_sin6.sin6_addr, sizeof(struct in6_addr)); + + /* destination address */ + if (ifindex) + *ifindex = pktinfo->ipi6_ifindex; + if (dst) + memcpy(dst, &pktinfo->ipi6_addr, sizeof(struct in6_addr)); + + return retval; +} diff --git a/ospf6d/ospf6_network.h b/ospf6d/ospf6_network.h new file mode 100644 index 0000000..60b7cc5 --- /dev/null +++ b/ospf6d/ospf6_network.h @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2003 Yasuhiro Ohara + */ + +#ifndef OSPF6_NETWORK_H +#define OSPF6_NETWORK_H + +extern struct in6_addr allspfrouters6; +extern struct in6_addr alldrouters6; + +extern int ospf6_serv_sock(struct ospf6 *ospf6); +extern void ospf6_serv_close(int *ospf6_sock); +extern int ospf6_sso(ifindex_t ifindex, struct in6_addr *group, int option, + int sockfd); + +extern int ospf6_sendmsg(struct in6_addr *src, struct in6_addr *dst, + ifindex_t ifindex, struct iovec *message, + int ospf6_sock); +extern int ospf6_recvmsg(struct in6_addr *src, struct in6_addr *dst, + ifindex_t *ifindex, struct iovec *message, + int ospf6_sock); + +#define OSPF6_MESSAGE_WRITE_ON(oi) \ + do { \ + bool list_was_empty = \ + list_isempty(oi->area->ospf6->oi_write_q); \ + if ((oi)->on_write_q == 0) { \ + listnode_add(oi->area->ospf6->oi_write_q, (oi)); \ + (oi)->on_write_q = 1; \ + } \ + if (list_was_empty && \ + !list_isempty(oi->area->ospf6->oi_write_q)) \ + event_add_write(master, ospf6_write, oi->area->ospf6, \ + oi->area->ospf6->fd, \ + &oi->area->ospf6->t_write); \ + } while (0) + +#endif /* OSPF6_NETWORK_H */ diff --git a/ospf6d/ospf6_nssa.c b/ospf6d/ospf6_nssa.c new file mode 100644 index 0000000..405ae90 --- /dev/null +++ b/ospf6d/ospf6_nssa.c @@ -0,0 +1,1483 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPFv3 Not So Stubby Area implementation. + * + * Copyright (C) 2021 Kaushik Nath + * Copyright (C) 2021 Soman K.S + */ + +#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_abr.h" +#include "ospf6_asbr.h" +#include "ospf6d.h" +#include "ospf6_nssa.h" +#include "ospf6d/ospf6_nssa_clippy.c" + +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_LSA, "OSPF6 LSA"); +unsigned char config_debug_ospf6_nssa = 0; +/* Determine whether this router is elected translator or not for area */ +static int ospf6_abr_nssa_am_elected(struct ospf6_area *oa) +{ + struct ospf6_lsa *lsa; + struct ospf6_router_lsa *router_lsa; + in_addr_t *best = NULL; + uint16_t type; + + type = htons(OSPF6_LSTYPE_ROUTER); + + /* Verify all the router LSA to compare the router ID */ + for (ALL_LSDB_TYPED(oa->lsdb, type, lsa)) { + + router_lsa = (struct ospf6_router_lsa + *)((caddr_t)lsa->header + + sizeof(struct ospf6_lsa_header)); + + /* ignore non-ABR routers */ + if (!CHECK_FLAG(router_lsa->bits, OSPF6_ROUTER_BIT_B)) + continue; + + /* Router has Nt flag - always translate */ + if (CHECK_FLAG(router_lsa->bits, OSPF6_ROUTER_BIT_NT)) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: router %pI4 asserts Nt", + __func__, &lsa->header->id); + return 1; + } + + if (best == NULL) + best = &lsa->header->adv_router; + else if (IPV4_ADDR_CMP(best, &lsa->header->adv_router) < 0) + best = &lsa->header->adv_router; + } + + if (best == NULL) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: best electable ABR not found", + __func__); + return 0; + } + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: best electable ABR is: %pI4", __func__, best); + + if (IPV4_ADDR_CMP(best, &oa->ospf6->router_id) <= 0) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: elected ABR is: %pI4", __func__, best); + return 1; + } else { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: not elected best %pI4, router ID %pI4", + __func__, best, &oa->ospf6->router_id); + return 0; + } +} + +/* Flush the translated LSA when translation is disabled */ +static void ospf6_flush_translated_lsa(struct ospf6_area *area) +{ + uint16_t type; + struct ospf6_lsa *type7; + struct ospf6_lsa *type5; + struct ospf6 *ospf6 = area->ospf6; + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: start area %s", __func__, area->name); + + type = htons(OSPF6_LSTYPE_TYPE_7); + for (ALL_LSDB_TYPED_ADVRTR(area->lsdb, type, ospf6->router_id, type7)) { + type5 = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL), + type7->external_lsa_id, + ospf6->router_id, ospf6->lsdb); + if (type5 && CHECK_FLAG(type5->flag, OSPF6_LSA_LOCAL_XLT)) + ospf6_lsa_premature_aging(type5); + } + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: finish area %s", __func__, area->name); +} + +/* Check NSSA status for all nssa areas */ +void ospf6_abr_nssa_check_status(struct ospf6 *ospf6) +{ + struct ospf6_area *area; + struct listnode *lnode, *nnode; + + for (ALL_LIST_ELEMENTS(ospf6->area_list, lnode, nnode, area)) { + uint8_t old_state = area->NSSATranslatorState; + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: checking area %s flag %x", __func__, + area->name, area->flag); + + if (!IS_AREA_NSSA(area)) + continue; + + if (!CHECK_FLAG(area->ospf6->flag, OSPF6_FLAG_ABR)) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: not ABR", __func__); + area->NSSATranslatorState = + OSPF6_NSSA_TRANSLATE_DISABLED; + ospf6_flush_translated_lsa(area); + } else { + /* Router is ABR */ + if (area->NSSATranslatorRole == OSPF6_NSSA_ROLE_ALWAYS) + area->NSSATranslatorState = + OSPF6_NSSA_TRANSLATE_ENABLED; + else { + /* We are a candidate for Translation */ + if (ospf6_abr_nssa_am_elected(area) > 0) { + area->NSSATranslatorState = + OSPF6_NSSA_TRANSLATE_ENABLED; + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug( + "%s: elected translator", + __func__); + } else { + area->NSSATranslatorState = + OSPF6_NSSA_TRANSLATE_DISABLED; + ospf6_flush_translated_lsa(area); + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: not elected", + __func__); + } + } + } + + /* RFC3101, 3.1: + * All NSSA border routers must set the E-bit in the Type-1 + * router-LSAs of their directly attached non-stub areas, even + * when they are not translating. + */ + if (old_state != area->NSSATranslatorState) { + if (old_state == OSPF6_NSSA_TRANSLATE_DISABLED) { + ++ospf6->redist_count; + ospf6_asbr_status_update(ospf6, + ospf6->redist_count); + } else { + --ospf6->redist_count; + ospf6_asbr_status_update(ospf6, + ospf6->redist_count); + } + } + } +} + +/* Mark the summary LSA's as unapproved, when ABR status changes.*/ +static void ospf6_abr_unapprove_summaries(struct ospf6 *ospf6) +{ + struct listnode *node, *nnode; + struct ospf6_area *area; + struct ospf6_lsa *lsa; + uint16_t type; + + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s : Start", __func__); + + for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, area)) { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s : considering area %pI4", __func__, + &area->area_id); + /* Inter area router LSA */ + type = htons(OSPF6_LSTYPE_INTER_ROUTER); + for (ALL_LSDB_TYPED_ADVRTR(area->lsdb, type, ospf6->router_id, + lsa)) { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug( + "%s : approved unset on summary link id %pI4", + __func__, &lsa->header->id); + SET_FLAG(lsa->flag, OSPF6_LSA_UNAPPROVED); + } + /* Inter area prefix LSA */ + type = htons(OSPF6_LSTYPE_INTER_PREFIX); + for (ALL_LSDB_TYPED_ADVRTR(area->lsdb, type, ospf6->router_id, + lsa)) { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug( + "%s : approved unset on asbr-summary link id %pI4", + __func__, &lsa->header->id); + SET_FLAG(lsa->flag, OSPF6_LSA_UNAPPROVED); + } + } + + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s : Stop", __func__); +} + +/* Re-advertise inter-area router LSA's */ +void ospf6_asbr_prefix_readvertise(struct ospf6 *ospf6) +{ + struct ospf6_route *brouter; + struct listnode *node, *nnode; + struct ospf6_area *oa; + + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("Re-examining Inter-Router prefixes"); + + + for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, oa)) { + for (brouter = ospf6_route_head(oa->ospf6->brouter_table); + brouter; brouter = ospf6_route_next(brouter)) + ospf6_abr_originate_summary_to_area(brouter, oa); + } + + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("Finished re-examining Inter-Router prefixes"); +} + +/* Advertise prefixes configured using area <area-id> range command */ +static void ospf6_abr_announce_aggregates(struct ospf6 *ospf6) +{ + struct ospf6_area *area; + struct ospf6_route *range; + struct listnode *node; + + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s: Start", __func__); + + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, area)) { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug( + "ospf_abr_announce_aggregates(): looking at area %pI4", + &area->area_id); + + for (range = ospf6_route_head(area->range_table); range; + range = ospf6_route_next(range)) + ospf6_abr_range_update(range, ospf6); + } + + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s: Stop", __func__); +} + +/* Flush the summary LSA's which are not approved.*/ +void ospf6_abr_remove_unapproved_summaries(struct ospf6 *ospf6) +{ + struct listnode *node, *nnode; + struct ospf6_area *area; + struct ospf6_lsa *lsa; + uint16_t type; + + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s : Start", __func__); + + for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, area)) { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s : looking at area %pI4", __func__, + &area->area_id); + + /* Inter area router LSA */ + type = htons(OSPF6_LSTYPE_INTER_ROUTER); + for (ALL_LSDB_TYPED_ADVRTR(area->lsdb, type, ospf6->router_id, + lsa)) { + if (CHECK_FLAG(lsa->flag, OSPF6_LSA_UNAPPROVED)) + ospf6_lsa_premature_aging(lsa); + } + + /* Inter area prefix LSA */ + type = htons(OSPF6_LSTYPE_INTER_PREFIX); + for (ALL_LSDB_TYPED_ADVRTR(area->lsdb, type, ospf6->router_id, + lsa)) { + if (CHECK_FLAG(lsa->flag, OSPF6_LSA_UNAPPROVED)) + ospf6_lsa_premature_aging(lsa); + } + } + + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s : Stop", __func__); +} + +/* + * This is the function taking care about ABR stuff, i.e. + * summary-LSA origination and flooding. + */ +static void ospf6_abr_task(struct ospf6 *ospf6) +{ + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s : Start", __func__); + + if (ospf6->route_table == NULL || ospf6->brouter_table == NULL) { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s : Routing tables are not yet ready", + __func__); + return; + } + + ospf6_abr_unapprove_summaries(ospf6); + + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s : prepare aggregates", __func__); + + ospf6_abr_range_reset_cost(ospf6); + + if (IS_OSPF6_ABR(ospf6)) { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s : process network RT", __func__); + ospf6_abr_prefix_resummarize(ospf6); + + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s : process router RT", __func__); + ospf6_asbr_prefix_readvertise(ospf6); + + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s : announce aggregates", __func__); + ospf6_abr_announce_aggregates(ospf6); + + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s : announce stub defaults", __func__); + ospf6_abr_defaults_to_stub(ospf6); + + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s : announce NSSA Type-7 defaults", + __func__); + ospf6_abr_nssa_type_7_defaults(ospf6); + } + + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s : remove unapproved summaries", __func__); + ospf6_abr_remove_unapproved_summaries(ospf6); + + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("%s : Stop", __func__); +} + +/* For NSSA Translations + * Mark the translated LSA's as unapproved. */ +static void ospf6_abr_unapprove_translates(struct ospf6 *ospf6) +{ + struct ospf6_lsa *lsa; + uint16_t type; + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: Start", __func__); + + type = htons(OSPF6_LSTYPE_AS_EXTERNAL); + for (ALL_LSDB_TYPED(ospf6->lsdb, type, lsa)) { + if (CHECK_FLAG(lsa->flag, OSPF6_LSA_LOCAL_XLT)) { + SET_FLAG(lsa->flag, OSPF6_LSA_UNAPPROVED); + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug( + "%s : approved unset on link id %pI4", + __func__, &lsa->header->id); + } + } + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: Stop", __func__); +} + +/* Generate the translated external lsa from NSSA lsa */ +static struct ospf6_lsa *ospf6_lsa_translated_nssa_new(struct ospf6_area *area, + struct ospf6_lsa *type7) +{ + char buffer[OSPF6_MAX_LSASIZE]; + struct ospf6_lsa *lsa; + struct ospf6_as_external_lsa *ext, *extnew; + struct ospf6_lsa_header *lsa_header; + caddr_t old_ptr, new_ptr; + struct ospf6_as_external_lsa *nssa; + struct prefix prefix; + struct ospf6 *ospf6 = area->ospf6; + ptrdiff_t tag_offset = 0; + route_tag_t network_order; + struct ospf6_route *range; + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s : Start", __func__); + + if (area->NSSATranslatorState == OSPF6_NSSA_TRANSLATE_DISABLED) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: Translation disabled for area %s", + __func__, area->name); + return NULL; + } + + /* find the translated Type-5 for this Type-7 */ + nssa = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END( + type7->header); + prefix.family = AF_INET6; + prefix.prefixlen = nssa->prefix.prefix_length; + ospf6_prefix_in6_addr(&prefix.u.prefix6, nssa, &nssa->prefix); + + /* Check if the Type-7 LSA should be suppressed by aggregation. */ + range = ospf6_route_lookup_bestmatch(&prefix, area->nssa_range_table); + if (range && !prefix_same(&prefix, &range->prefix) + && !CHECK_FLAG(range->flag, OSPF6_ROUTE_REMOVE)) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug( + "%s: LSA %s suppressed by range %pFX of area %s", + __func__, type7->name, &range->prefix, + area->name); + return NULL; + } + + /* prepare buffer */ + memset(buffer, 0, sizeof(buffer)); + lsa_header = (struct ospf6_lsa_header *)buffer; + extnew = (struct ospf6_as_external_lsa + *)((caddr_t)lsa_header + + sizeof(struct ospf6_lsa_header)); + ext = (struct ospf6_as_external_lsa + *)((caddr_t)(type7->header) + + sizeof(struct ospf6_lsa_header)); + old_ptr = + (caddr_t)((caddr_t)ext + sizeof(struct ospf6_as_external_lsa)); + new_ptr = (caddr_t)((caddr_t)extnew + + sizeof(struct ospf6_as_external_lsa)); + + memcpy(extnew, ext, sizeof(struct ospf6_as_external_lsa)); + + /* set Prefix */ + memcpy(new_ptr, old_ptr, OSPF6_PREFIX_SPACE(ext->prefix.prefix_length)); + ospf6_prefix_apply_mask(&extnew->prefix); + new_ptr += OSPF6_PREFIX_SPACE(extnew->prefix.prefix_length); + + tag_offset = + sizeof(*ext) + OSPF6_PREFIX_SPACE(ext->prefix.prefix_length); + + /* Forwarding address */ + if (CHECK_FLAG(ext->bits_metric, OSPF6_ASBR_BIT_F)) { + memcpy(new_ptr, (caddr_t)ext + tag_offset, + sizeof(struct in6_addr)); + new_ptr += sizeof(struct in6_addr); + tag_offset += sizeof(struct in6_addr); + } + /* External Route Tag */ + if (CHECK_FLAG(ext->bits_metric, OSPF6_ASBR_BIT_T)) { + memcpy(&network_order, (caddr_t)ext + tag_offset, + sizeof(network_order)); + network_order = htonl(network_order); + memcpy(new_ptr, &network_order, sizeof(network_order)); + new_ptr += sizeof(network_order); + } + + /* Fill LSA Header */ + lsa_header->age = 0; + lsa_header->type = htons(OSPF6_LSTYPE_AS_EXTERNAL); + lsa_header->id = htonl(ospf6->external_id); + ospf6->external_id++; + lsa_header->adv_router = ospf6->router_id; + lsa_header->seqnum = + ospf6_new_ls_seqnum(lsa_header->type, lsa_header->id, + lsa_header->adv_router, ospf6->lsdb); + lsa_header->length = htons((caddr_t)new_ptr - (caddr_t)lsa_header); + type7->external_lsa_id = lsa_header->id; + + /* LSA checksum */ + ospf6_lsa_checksum(lsa_header); + + /* create LSA */ + lsa = ospf6_lsa_create(lsa_header); + + SET_FLAG(lsa->flag, OSPF6_LSA_LOCAL_XLT); + UNSET_FLAG(lsa->flag, OSPF6_LSA_UNAPPROVED); + + /* Originate */ + ospf6_lsa_originate_process(lsa, ospf6); + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: Originated type5 LSA id %pI4", __func__, + &lsa_header->id); + return lsa; +} + +/* Delete LSA from retransmission list */ +static void ospf6_ls_retransmit_delete_nbr_as(struct ospf6 *ospf6, + struct ospf6_lsa *lsa) +{ + struct listnode *node, *nnode; + struct ospf6_area *area; + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s : start lsa %s", __func__, lsa->name); + + /*The function ospf6_flood_clear_area removes LSA from + * retransmit list. + */ + for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, area)) + ospf6_flood_clear_area(lsa, area); + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s : finish lsa %s", __func__, lsa->name); +} + +/* Refresh translated AS-external-LSA. */ +struct ospf6_lsa *ospf6_translated_nssa_refresh(struct ospf6_area *area, + struct ospf6_lsa *type7, + struct ospf6_lsa *type5) +{ + struct ospf6_lsa *new = NULL; + struct prefix prefix; + struct ospf6 *ospf6 = area->ospf6; + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s : start area %s", __func__, area->name); + + /* Sanity checks. */ + assert(type7); + + /* Find the AS external LSA */ + if (type5 == NULL) { + struct ospf6_as_external_lsa *ext_lsa; + struct ospf6_route *match; + + /* Find the AS external LSA from Type-7 LSA */ + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug( + "%s: try to find translated Type-5 LSA for %s", + __func__, type7->name); + + ext_lsa = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END( + type7->header); + prefix.family = AF_INET6; + prefix.prefixlen = ext_lsa->prefix.prefix_length; + ospf6_prefix_in6_addr(&prefix.u.prefix6, ext_lsa, + &ext_lsa->prefix); + + match = ospf6_route_lookup(&prefix, ospf6->external_table); + if (match) + type5 = ospf6_lsdb_lookup( + htons(OSPF6_LSTYPE_AS_EXTERNAL), + match->path.origin.id, ospf6->router_id, + ospf6->lsdb); + } + + if (type5) { + if (CHECK_FLAG(type5->flag, OSPF6_LSA_LOCAL_XLT)) { + /* Delete LSA from neighbor retransmit-list. */ + ospf6_ls_retransmit_delete_nbr_as(ospf6, type5); + + /* Flush the LSA */ + ospf6_lsa_premature_aging(type5); + } else { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: Invalid translated LSA %s", + __func__, type5->name); + return NULL; + } + } + + /* create new translated LSA */ + if (ospf6_lsa_age_current(type7) != OSPF_LSA_MAXAGE) { + if ((new = ospf6_lsa_translated_nssa_new(area, type7)) + == NULL) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug( + "%s: Could not translate Type-7 for %pI4", + __func__, &type7->header->id); + return NULL; + } + } + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: finish", __func__); + + return new; +} + +static void ospf6_abr_translate_nssa(struct ospf6_area *area, + struct ospf6_lsa *lsa) +{ + /* Incoming Type-7 or aggregated Type-7 + * + * LSA is skipped if P-bit is off. + * + * The Type-7 is translated, Installed/Approved as a Type-5 into + * global LSDB, then Flooded through AS + * + * Later, any Unapproved Translated Type-5's are flushed/discarded + */ + + struct ospf6_lsa *old = NULL; + struct ospf6_as_external_lsa *nssa_lsa; + struct prefix prefix; + struct ospf6_route *match; + struct ospf6 *ospf6; + + ospf6 = area->ospf6; + nssa_lsa = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END( + lsa->header); + + if (!CHECK_FLAG(nssa_lsa->prefix.prefix_options, + OSPF6_PREFIX_OPTION_P)) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug( + "%s : LSA Id %pI4, P-bit off, NO Translation", + __func__, &lsa->header->id); + return; + } + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug( + "%s : LSA Id %pI4 external ID %pI4, Translating type 7 to 5", + __func__, &lsa->header->id, &lsa->external_lsa_id); + + prefix.family = AF_INET6; + prefix.prefixlen = nssa_lsa->prefix.prefix_length; + ospf6_prefix_in6_addr(&prefix.u.prefix6, nssa_lsa, &nssa_lsa->prefix); + + if (!CHECK_FLAG(nssa_lsa->bits_metric, OSPF6_ASBR_BIT_F)) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug( + "%s : LSA Id %pI4, Forward address is 0, NO Translation", + __func__, &lsa->header->id); + return; + } + + /* Find the type-5 LSA in the area-range table */ + match = ospf6_route_lookup_bestmatch(&prefix, area->nssa_range_table); + if (match && CHECK_FLAG(match->flag, OSPF6_ROUTE_NSSA_RANGE)) { + if (prefix_same(&prefix, &match->prefix)) { + /* The prefix range is being removed, + * no need to refresh + */ + if + CHECK_FLAG(match->flag, OSPF6_ROUTE_REMOVE) + return; + } else { + if (!CHECK_FLAG(match->flag, OSPF6_ROUTE_REMOVE)) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug( + "%s: LSA Id %pI4 suppressed by range %pFX of area %s", + __func__, &lsa->header->id, + &match->prefix, area->name); + /* LSA will be suppressed by area-range command, + * no need to refresh + */ + return; + } + } + } + + /* Find the existing AS-External LSA for this prefix */ + match = ospf6_route_lookup(&prefix, ospf6->route_table); + if (match) { + old = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL), + lsa->external_lsa_id, ospf6->router_id, + ospf6->lsdb); + } + + if (OSPF6_LSA_IS_MAXAGE(lsa)) { + if (old) + ospf6_lsa_premature_aging(old); + return; + } + + if (old && !OSPF6_LSA_IS_MAXAGE(old)) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug( + "%s : found old translated LSA Id %pI4, skip", + __func__, &old->header->id); + + UNSET_FLAG(old->flag, OSPF6_LSA_UNAPPROVED); + return; + + } else { + /* no existing external route for this LSA Id + * originate translated LSA + */ + + if (ospf6_lsa_translated_nssa_new(area, lsa) == NULL) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug( + "%s : Could not translate Type-7 for %pI4 to Type-5", + __func__, &lsa->header->id); + return; + } + } +} + +static void ospf6_abr_process_nssa_translates(struct ospf6 *ospf6) +{ + /* Scan through all NSSA_LSDB records for all areas; + * If P-bit is on, translate all Type-7's to 5's and aggregate or + * flood install as approved in Type-5 LSDB with XLATE Flag on + * later, do same for all aggregates... At end, DISCARD all + * remaining UNAPPROVED Type-5's (Aggregate is for future ) */ + + struct listnode *node; + struct ospf6_area *oa; + struct ospf6_lsa *lsa; + int type; + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s : Start", __func__); + + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, oa)) { + if (!IS_AREA_NSSA(oa)) + continue; + + /* skip if not translator */ + if (oa->NSSATranslatorState == OSPF6_NSSA_TRANSLATE_DISABLED) { + zlog_debug("%s area %pI4 NSSATranslatorState %d", + __func__, &oa->area_id, + oa->NSSATranslatorState); + continue; + } + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s : looking at area %pI4", __func__, + &oa->area_id); + + type = htons(OSPF6_LSTYPE_TYPE_7); + for (ALL_LSDB_TYPED(oa->lsdb, type, lsa)) { + zlog_debug("%s : lsa %s , id %pI4 , adv router %pI4", + __func__, lsa->name, &lsa->header->id, + &lsa->header->adv_router); + ospf6_abr_translate_nssa(oa, lsa); + } + } + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s : Stop", __func__); +} + +static void ospf6_abr_send_nssa_aggregates(struct ospf6 *ospf6) +{ + struct listnode *node; + struct ospf6_area *area; + struct ospf6_route *range; + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: Start", __func__); + + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, area)) { + if (area->NSSATranslatorState == OSPF6_NSSA_TRANSLATE_DISABLED) + continue; + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: looking at area %pI4", __func__, + &area->area_id); + + for (range = ospf6_route_head(area->nssa_range_table); range; + range = ospf6_route_next(range)) + ospf6_abr_range_update(range, ospf6); + } + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: Stop", __func__); +} + +static void ospf6_abr_remove_unapproved_translates(struct ospf6 *ospf6) +{ + struct ospf6_lsa *lsa; + uint16_t type; + + /* All AREA PROCESS should have APPROVED necessary LSAs */ + /* Remove any left over and not APPROVED */ + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: Start", __func__); + + type = htons(OSPF6_LSTYPE_AS_EXTERNAL); + for (ALL_LSDB_TYPED(ospf6->lsdb, type, lsa)) { + if (CHECK_FLAG(lsa->flag, OSPF6_LSA_LOCAL_XLT) + && CHECK_FLAG(lsa->flag, OSPF6_LSA_UNAPPROVED)) { + zlog_debug( + "%s : removing unapproved translates, lsa : %s", + __func__, lsa->name); + + ospf6_lsa_premature_aging(lsa); + } + } + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: Stop", __func__); +} + +static void ospf6_abr_nssa_type_7_default_create(struct ospf6 *ospf6, + struct ospf6_area *oa) +{ + struct ospf6_route *def; + int metric; + int metric_type; + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("Announcing Type-7 default route into NSSA area %s", + oa->name); + + def = ospf6_route_create(ospf6); + 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.subtype = OSPF6_PATH_SUBTYPE_DEFAULT_RT; + if (CHECK_FLAG(ospf6->flag, OSPF6_FLAG_ABR)) + def->path.area_id = ospf6->backbone->area_id; + else + def->path.area_id = oa->area_id; + + /* Compute default route type and metric. */ + if (oa->nssa_default_originate.metric_value != -1) + metric = oa->nssa_default_originate.metric_value; + else + metric = DEFAULT_DEFAULT_ALWAYS_METRIC; + if (oa->nssa_default_originate.metric_type != -1) + metric_type = oa->nssa_default_originate.metric_type; + else + metric_type = DEFAULT_METRIC_TYPE; + def->path.metric_type = metric_type; + def->path.cost = metric; + if (metric_type == 1) + def->path.type = OSPF6_PATH_TYPE_EXTERNAL1; + else + def->path.type = OSPF6_PATH_TYPE_EXTERNAL2; + + ospf6_nssa_lsa_originate(def, oa, false); + ospf6_route_delete(def); +} + +static void ospf6_abr_nssa_type_7_default_delete(struct ospf6 *ospf6, + struct ospf6_area *oa) +{ + struct ospf6_lsa *lsa; + + lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_TYPE_7), 0, + oa->ospf6->router_id, oa->lsdb); + if (lsa && !OSPF6_LSA_IS_MAXAGE(lsa)) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug( + "Withdrawing Type-7 default route from area %s", + oa->name); + + ospf6_lsa_purge(lsa); + } +} + +/* NSSA Type-7 default route. */ +void ospf6_abr_nssa_type_7_defaults(struct ospf6 *ospf6) +{ + struct listnode *node; + struct ospf6_area *oa; + + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, oa)) { + if (IS_AREA_NSSA(oa) && oa->nssa_default_originate.enabled + && (IS_OSPF6_ABR(ospf6) + || (IS_OSPF6_ASBR(ospf6) + && ospf6->nssa_default_import_check.status))) + ospf6_abr_nssa_type_7_default_create(ospf6, oa); + else + ospf6_abr_nssa_type_7_default_delete(ospf6, oa); + } +} + +static void ospf6_abr_nssa_task(struct ospf6 *ospf6) +{ + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("Check for NSSA-ABR Tasks():"); + + if (!IS_OSPF6_ABR(ospf6)) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s Not ABR", __func__); + return; + } + + if (!ospf6->anyNSSA) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s Not NSSA", __func__); + return; + } + + /* Each area must confirm TranslatorRole */ + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: Start", __func__); + + /* For all Global Entries flagged "local-translate", unset APPROVED */ + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: unapprove translates", __func__); + + ospf6_abr_unapprove_translates(ospf6); + + /* Originate Type-7 aggregates */ + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: send NSSA aggregates", __func__); + ospf6_abr_send_nssa_aggregates(ospf6); + + /* For all NSSAs, Type-7s, translate to 5's, INSTALL/FLOOD, or + * Aggregate as Type-7 + * Install or Approve in Type-5 Global LSDB + */ + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: process translates", __func__); + ospf6_abr_process_nssa_translates(ospf6); + + /* Flush any unapproved previous translates from Global Data Base */ + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: remove unapproved translates", __func__); + ospf6_abr_remove_unapproved_translates(ospf6); + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: Stop", __func__); +} + +int ospf6_redistribute_check(struct ospf6 *ospf6, struct ospf6_route *route, + int type) +{ + route_map_result_t ret; + struct prefix *prefix; + struct ospf6_redist *red; + + if (!ospf6_zebra_is_redistribute(type, ospf6->vrf_id)) + return 0; + + prefix = &route->prefix; + + red = ospf6_redist_lookup(ospf6, type, 0); + if (!red) + return 0; + + /* Change to new redist structure */ + if (ROUTEMAP_NAME(red)) { + if (ROUTEMAP(red) == NULL) + ospf6_asbr_routemap_update(NULL); + if (ROUTEMAP(red) == NULL) { + zlog_warn( + "route-map \"%s\" not found, suppress redistributing", + ROUTEMAP_NAME(red)); + return 0; + } + } + + /* Change to new redist structure */ + if (ROUTEMAP(red)) { + ret = route_map_apply(ROUTEMAP(red), prefix, route); + if (ret == RMAP_DENYMATCH) { + if (IS_OSPF6_DEBUG_ASBR) + zlog_debug("Denied by route-map \"%s\"", + ROUTEMAP_NAME(red)); + return 0; + } + } + + return 1; +} + +/* This function performs ABR related processing */ +static void ospf6_abr_task_timer(struct event *thread) +{ + struct ospf6 *ospf6 = EVENT_ARG(thread); + + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("Running ABR task on timer"); + + (void)ospf6_check_and_set_router_abr(ospf6); + ospf6_abr_nssa_check_status(ospf6); + ospf6_abr_task(ospf6); + /* if nssa-abr, then scan Type-7 LSDB */ + ospf6_abr_nssa_task(ospf6); +} + +void ospf6_schedule_abr_task(struct ospf6 *ospf6) +{ + if (event_is_scheduled(ospf6->t_abr_task)) { + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("ABR task already scheduled"); + return; + } + + if (IS_OSPF6_DEBUG_ABR) + zlog_debug("Scheduling ABR task"); + + event_add_timer(master, ospf6_abr_task_timer, ospf6, + OSPF6_ABR_TASK_DELAY, &ospf6->t_abr_task); +} + +/* Flush the NSSA LSAs from the area */ +static void ospf6_nssa_flush_area(struct ospf6_area *area) +{ + uint16_t type; + struct ospf6_lsa *lsa = NULL, *type5 = NULL; + struct ospf6 *ospf6 = area->ospf6; + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s: area %s", __func__, area->name); + + /* Flush the NSSA LSA */ + type = htons(OSPF6_LSTYPE_TYPE_7); + for (ALL_LSDB_TYPED_ADVRTR(area->lsdb, type, ospf6->router_id, lsa)) { + lsa->header->age = htons(OSPF_LSA_MAXAGE); + SET_FLAG(lsa->flag, OSPF6_LSA_FLUSH); + ospf6_flood(NULL, lsa); + + /* Flush the translated LSA */ + if (ospf6_check_and_set_router_abr(ospf6)) { + type5 = ospf6_lsdb_lookup( + htons(OSPF6_LSTYPE_AS_EXTERNAL), + lsa->external_lsa_id, ospf6->router_id, + ospf6->lsdb); + if (type5 + && CHECK_FLAG(type5->flag, OSPF6_LSA_LOCAL_XLT)) { + type5->header->age = htons(OSPF_LSA_MAXAGE); + SET_FLAG(type5->flag, OSPF6_LSA_FLUSH); + ospf6_flood(NULL, type5); + } + } + } +} + +static void ospf6_check_and_originate_type7_lsa(struct ospf6_area *area) +{ + struct ospf6_route *route; + struct route_node *rn = NULL; + struct ospf6_external_aggr_rt *aggr; + + /* Loop through the external_table to find the LSAs originated + * without aggregation and originate type-7 LSAs for them. + */ + for (route = ospf6_route_head( + area->ospf6->external_table); + route; route = ospf6_route_next(route)) { + struct ospf6_external_info *info = route->route_option; + + /* This means the Type-5 LSA was originated for this route */ + if (route->path.origin.id != 0 && info->type != DEFAULT_ROUTE) + ospf6_nssa_lsa_originate(route, area, true); + } + + /* Loop through the aggregation table to originate type-7 LSAs + * for the aggregated type-5 LSAs + */ + for (rn = route_top(area->ospf6->rt_aggr_tbl); rn; + rn = route_next(rn)) { + if (!rn->info) + continue; + + aggr = rn->info; + + if (CHECK_FLAG(aggr->aggrflags, + OSPF6_EXTERNAL_AGGRT_ORIGINATED)) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug( + "Originating Type-7 LSAs for area %s", + area->name); + + ospf6_nssa_lsa_originate(aggr->route, area, true); + } + } +} + +static void ospf6_ase_lsa_refresh(struct ospf6 *o) +{ + struct ospf6_lsa *old; + + for (struct ospf6_route *route = ospf6_route_head(o->external_table); + route; route = ospf6_route_next(route)) { + old = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL), + route->path.origin.id, o->router_id, + o->lsdb); + if (old) { + EVENT_OFF(old->refresh); + event_add_event(master, ospf6_lsa_refresh, old, 0, + &old->refresh); + } else { + ospf6_as_external_lsa_originate(route, o); + } + } +} + +void ospf6_area_nssa_update(struct ospf6_area *area) +{ + if (IS_AREA_NSSA(area)) { + OSPF6_OPT_CLEAR(area->options, OSPF6_OPT_E); + area->ospf6->anyNSSA++; + OSPF6_OPT_SET(area->options, OSPF6_OPT_N); + area->NSSATranslatorRole = OSPF6_NSSA_ROLE_CANDIDATE; + } else if (IS_AREA_ENABLED(area)) { + if (IS_OSPF6_DEBUG_ORIGINATE(ROUTER)) + zlog_debug("Normal area for if %s", area->name); + OSPF6_OPT_CLEAR(area->options, OSPF6_OPT_N); + OSPF6_OPT_SET(area->options, OSPF6_OPT_E); + area->ospf6->anyNSSA--; + area->NSSATranslatorState = OSPF6_NSSA_TRANSLATE_DISABLED; + } + + /* Refresh router LSA */ + if (IS_AREA_NSSA(area)) { + OSPF6_ROUTER_LSA_SCHEDULE(area); + + /* Flush external LSAs. */ + ospf6_asbr_remove_externals_from_area(area); + + /* Check if router is ABR */ + if (ospf6_check_and_set_router_abr(area->ospf6)) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("Router is ABR area %s", area->name); + ospf6_schedule_abr_task(area->ospf6); + } else { + /* Router is not ABR */ + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("NSSA area %s", area->name); + + /* Originate NSSA LSA */ + ospf6_check_and_originate_type7_lsa(area); + } + } else { + /* Disable NSSA */ + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("Normal area %s", area->name); + ospf6_nssa_flush_area(area); + + /* Check if router is ABR */ + if (ospf6_check_and_set_router_abr(area->ospf6)) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("Router is ABR area %s", area->name); + ospf6_schedule_abr_task(area->ospf6); + ospf6_ase_lsa_refresh(area->ospf6); + } else { + uint16_t type; + struct ospf6_lsa *lsa = NULL; + + /* + * Refresh all type-5 LSAs so they get installed + * in the converted ares + */ + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("Refresh type-5 LSAs, area %s", + area->name); + + type = htons(OSPF6_LSTYPE_AS_EXTERNAL); + for (ALL_LSDB_TYPED_ADVRTR(area->ospf6->lsdb, type, + area->ospf6->router_id, + lsa)) { + if (IS_OSPF6_DEBUG_NSSA) + ospf6_lsa_header_print(lsa); + EVENT_OFF(lsa->refresh); + event_add_event(master, ospf6_lsa_refresh, lsa, + 0, &lsa->refresh); + } + } + } +} + +int ospf6_area_nssa_set(struct ospf6 *ospf6, struct ospf6_area *area) +{ + + if (!IS_AREA_NSSA(area)) { + /* Disable stub first. */ + ospf6_area_stub_unset(ospf6, area); + + SET_FLAG(area->flag, OSPF6_AREA_NSSA); + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("area %s nssa set", area->name); + ospf6_area_nssa_update(area); + } + + return 1; +} + +int ospf6_area_nssa_unset(struct ospf6 *ospf6, struct ospf6_area *area) +{ + if (IS_AREA_NSSA(area)) { + UNSET_FLAG(area->flag, OSPF6_AREA_NSSA); + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("area %s nssa reset", area->name); + + /* Clear the table of NSSA ranges. */ + ospf6_route_table_delete(area->nssa_range_table); + area->nssa_range_table = + OSPF6_ROUTE_TABLE_CREATE(AREA, PREFIX_RANGES); + area->nssa_range_table->scope = area; + + ospf6_area_nssa_update(area); + } + + return 1; +} + +/* Find the NSSA forwarding address */ +static struct in6_addr *ospf6_get_nssa_fwd_addr(struct ospf6_area *oa) +{ + struct listnode *node, *nnode; + struct ospf6_interface *oi; + + for (ALL_LIST_ELEMENTS(oa->if_list, node, nnode, oi)) { + struct in6_addr *addr; + + if (!if_is_operative(oi->interface)) + continue; + + addr = ospf6_interface_get_global_address(oi->interface); + if (addr) + return addr; + } + return NULL; +} + +void ospf6_nssa_lsa_originate(struct ospf6_route *route, + struct ospf6_area *area, bool p_bit) +{ + char buffer[OSPF6_MAX_LSASIZE]; + struct ospf6_lsa_header *lsa_header; + struct ospf6_lsa *lsa; + struct ospf6_external_info *info = route->route_option; + struct in6_addr *fwd_addr; + + struct ospf6_as_external_lsa *as_external_lsa; + caddr_t p; + + if (IS_OSPF6_DEBUG_ASBR || IS_OSPF6_DEBUG_ORIGINATE(AS_EXTERNAL)) + zlog_debug("Originate NSSA-LSA for %pFX", &route->prefix); + + /* prepare buffer */ + memset(buffer, 0, sizeof(buffer)); + lsa_header = (struct ospf6_lsa_header *)buffer; + as_external_lsa = (struct ospf6_as_external_lsa + *)((caddr_t)lsa_header + + sizeof(struct ospf6_lsa_header)); + p = (caddr_t)((caddr_t)as_external_lsa + + sizeof(struct ospf6_as_external_lsa)); + + /* Fill AS-External-LSA */ + /* Metric type */ + if (route->path.metric_type == 2) + SET_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_E); + else + UNSET_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_E); + + /* external route tag */ + if (info && info->tag) + SET_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_T); + else + UNSET_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_T); + + /* Set metric */ + OSPF6_ASBR_METRIC_SET(as_external_lsa, route->path.cost); + + /* prefixlen */ + as_external_lsa->prefix.prefix_length = route->prefix.prefixlen; + + /* PrefixOptions */ + as_external_lsa->prefix.prefix_options = route->prefix_options; + + /* Set the P bit */ + if (p_bit) + as_external_lsa->prefix.prefix_options |= OSPF6_PREFIX_OPTION_P; + + /* don't use refer LS-type */ + as_external_lsa->prefix.prefix_refer_lstype = htons(0); + + /* set Prefix */ + memcpy(p, &route->prefix.u.prefix6, + OSPF6_PREFIX_SPACE(route->prefix.prefixlen)); + ospf6_prefix_apply_mask(&as_external_lsa->prefix); + p += OSPF6_PREFIX_SPACE(route->prefix.prefixlen); + + /* Forwarding address */ + fwd_addr = ospf6_get_nssa_fwd_addr(area); + if (fwd_addr) { + memcpy(p, fwd_addr, sizeof(struct in6_addr)); + p += sizeof(struct in6_addr); + SET_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_F); + } else + UNSET_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_F); + + /* External Route Tag */ + if (info + && CHECK_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_T)) { + route_tag_t network_order = htonl(info->tag); + + memcpy(p, &network_order, sizeof(network_order)); + p += sizeof(network_order); + } + + /* Fill LSA Header */ + lsa_header->age = 0; + lsa_header->type = htons(OSPF6_LSTYPE_TYPE_7); + lsa_header->id = route->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); + + /* Originate */ + ospf6_lsa_originate_area(lsa, area); +} + +void ospf6_abr_check_translate_nssa(struct ospf6_area *area, + struct ospf6_lsa *lsa) +{ + struct ospf6_lsa *type5 = NULL; + struct ospf6 *ospf6 = area->ospf6; + + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s : start", __func__); + + if (!ospf6_check_and_set_router_abr(ospf6)) + return; + + type5 = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_AS_EXTERNAL), + lsa->external_lsa_id, ospf6->router_id, + ospf6->lsdb); + if (!type5) { + if (IS_OSPF6_DEBUG_NSSA) + zlog_debug("%s : Originating type5 LSA", __func__); + ospf6_lsa_translated_nssa_new(area, lsa); + } +} + +DEFPY (area_nssa_range, + area_nssa_range_cmd, + "area <A.B.C.D|(0-4294967295)>$area nssa range X:X::X:X/M$prefix [<not-advertise$not_adv|cost (0-16777215)$cost>]", + "OSPF6 area parameters\n" + "OSPF6 area ID in IP address format\n" + "OSPF6 area ID as a decimal value\n" + "Configure OSPF6 area as nssa\n" + "Configured address range\n" + "Specify IPv6 prefix\n" + "Do not advertise\n" + "User specified metric for this range\n" + "Advertised metric for this range\n") +{ + struct ospf6_area *oa; + struct ospf6_route *range; + + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + OSPF6_CMD_AREA_GET(area, oa, ospf6); + + if (!IS_AREA_NSSA(oa)) { + vty_out(vty, "%% First configure %s as an NSSA area\n", area); + return CMD_WARNING; + } + + range = ospf6_route_lookup((struct prefix *)prefix, + oa->nssa_range_table); + if (range == NULL) { + range = ospf6_route_create(ospf6); + range->type = OSPF6_DEST_TYPE_RANGE; + SET_FLAG(range->flag, OSPF6_ROUTE_NSSA_RANGE); + prefix_copy(&range->prefix, prefix); + range->path.area_id = oa->area_id; + range->path.metric_type = 2; + range->path.cost = OSPF_AREA_RANGE_COST_UNSPEC; + range->path.origin.type = htons(OSPF6_LSTYPE_TYPE_7); + range->path.origin.id = htonl(ospf6->external_id++); + range->path.origin.adv_router = ospf6->router_id; + ospf6_route_add(range, oa->nssa_range_table); + } + + /* process "not-advertise" */ + if (not_adv) + SET_FLAG(range->flag, OSPF6_ROUTE_DO_NOT_ADVERTISE); + else + UNSET_FLAG(range->flag, OSPF6_ROUTE_DO_NOT_ADVERTISE); + + /* process "cost" */ + if (!cost_str) + cost = OSPF_AREA_RANGE_COST_UNSPEC; + range->path.u.cost_config = cost; + + /* Redo summaries if required */ + if (ospf6_check_and_set_router_abr(ospf6)) + ospf6_schedule_abr_task(ospf6); + + return CMD_SUCCESS; +} + +DEFPY (no_area_nssa_range, + no_area_nssa_range_cmd, + "no area <A.B.C.D|(0-4294967295)>$area nssa range X:X::X:X/M$prefix [<not-advertise|cost (0-16777215)>]", + NO_STR + "OSPF6 area parameters\n" + "OSPF6 area ID in IP address format\n" + "OSPF6 area ID as a decimal value\n" + "Configure OSPF6 area as nssa\n" + "Configured address range\n" + "Specify IPv6 prefix\n" + "Do not advertise\n" + "User specified metric for this range\n" + "Advertised metric for this range\n") +{ + struct ospf6_area *oa; + struct ospf6_route *range; + + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + OSPF6_CMD_AREA_GET(area, oa, ospf6); + + range = ospf6_route_lookup((struct prefix *)prefix, + oa->nssa_range_table); + if (range == NULL) { + vty_out(vty, "%% range %s does not exist.\n", prefix_str); + return CMD_SUCCESS; + } + + if (ospf6_check_and_set_router_abr(oa->ospf6)) { + /* Blow away the aggregated LSA and route */ + SET_FLAG(range->flag, OSPF6_ROUTE_REMOVE); + + /* Redo summaries if required */ + event_execute(master, ospf6_abr_task_timer, ospf6, 0, NULL); + } + + ospf6_route_remove(range, oa->nssa_range_table); + + return CMD_SUCCESS; +} + +DEFUN(debug_ospf6_nssa, debug_ospf6_nssa_cmd, + "debug ospf6 nssa", + DEBUG_STR + OSPF6_STR + "Debug OSPFv3 NSSA function\n") +{ + OSPF6_DEBUG_NSSA_ON(); + return CMD_SUCCESS; +} + +DEFUN(no_debug_ospf6_nssa, no_debug_ospf6_nssa_cmd, + "no debug ospf6 nssa", + NO_STR + DEBUG_STR + OSPF6_STR + "Debug OSPFv3 NSSA function\n") +{ + OSPF6_DEBUG_NSSA_OFF(); + return CMD_SUCCESS; +} + +void config_write_ospf6_debug_nssa(struct vty *vty) +{ + if (IS_OSPF6_DEBUG_NSSA) + vty_out(vty, "debug ospf6 nssa\n"); +} + +void install_element_ospf6_debug_nssa(void) +{ + install_element(OSPF6_NODE, &area_nssa_range_cmd); + install_element(OSPF6_NODE, &no_area_nssa_range_cmd); + + install_element(ENABLE_NODE, &debug_ospf6_nssa_cmd); + install_element(ENABLE_NODE, &no_debug_ospf6_nssa_cmd); + install_element(CONFIG_NODE, &debug_ospf6_nssa_cmd); + install_element(CONFIG_NODE, &no_debug_ospf6_nssa_cmd); +} diff --git a/ospf6d/ospf6_nssa.h b/ospf6d/ospf6_nssa.h new file mode 100644 index 0000000..a6bafe5 --- /dev/null +++ b/ospf6d/ospf6_nssa.h @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * OSPFv3 Not So Stubby Area implementation. + * + * Copyright (C) 2021 Kaushik Nath + * Copyright (C) 2021 Soman K.S + */ + +#ifndef OSPF6_NSSA_H +#define OSPF6_NSSA_H + +#define OSPF6_OPTION_NP 0x08 +#define OSPF6_LS_INFINITY 0xffffff + +#define OSPF6_OPT_N (1 << 3) /* Handling Type-7 LSA Capability */ + +#define OSPF6_DEBUG_NSSA 0x09 +/* Debug option */ +extern unsigned char config_debug_ospf6_nssa; + +#define OSPF6_DEBUG_NSSA_ON() (config_debug_ospf6_nssa = 1) +#define OSPF6_DEBUG_NSSA_OFF() (config_debug_ospf6_nssa = 0) +#define IS_OSPF6_DEBUG_NSSA config_debug_ospf6_nssa + +#define CHECK_LSA_TYPE_1_TO_5_OR_7(type) \ + ((type == OSPF6_ROUTER_LSA_MIN_SIZE) \ + || (type == OSPF6_NETWORK_LSA_MIN_SIZE) \ + || (type == OSPF6_LINK_LSA_MIN_SIZE) \ + || (type == OSPF6_INTRA_PREFIX_LSA_MIN_SIZE) \ + || (type == OSPF6_AS_NSSA_LSA)) + +#define OSPF6_LSA_APPROVED 0x08 +#define OSPF6_LSA_LOCAL_XLT 0x40 + +#define OSPF6_ABR_TASK_DELAY 5 + +int ospf6_area_nssa_no_summary_set(struct ospf6 *ospf6, struct in_addr area_id); +int ospf6_area_nssa_unset(struct ospf6 *ospf6, struct ospf6_area *area); +int ospf6_area_nssa_set(struct ospf6 *ospf6, struct ospf6_area *area); + +extern void ospf6_nssa_lsa_flush(struct ospf6 *ospf6, struct prefix_ipv6 *p); +extern struct ospf6_lsa *ospf6_translated_nssa_refresh(struct ospf6_area *oa, + struct ospf6_lsa *type7, + struct ospf6_lsa *type5); + +extern void ospf6_asbr_nssa_redist_task(struct ospf6 *ospf6); + +extern void ospf6_schedule_abr_task(struct ospf6 *ospf6); +extern void ospf6_area_nssa_update(struct ospf6_area *area); +void ospf6_asbr_prefix_readvertise(struct ospf6 *ospf6); +extern void ospf6_nssa_lsa_originate(struct ospf6_route *route, + struct ospf6_area *area, bool p_bit); +extern void install_element_ospf6_debug_nssa(void); +extern void ospf6_abr_nssa_type_7_defaults(struct ospf6 *osof6); +int ospf6_redistribute_check(struct ospf6 *ospf6, struct ospf6_route *route, + int type); +extern void ospf6_abr_check_translate_nssa(struct ospf6_area *area, + struct ospf6_lsa *lsa); +extern void ospf6_abr_nssa_check_status(struct ospf6 *ospf6); +extern void config_write_ospf6_debug_nssa(struct vty *vty); +#endif /* OSPF6_NSSA_H */ diff --git a/ospf6d/ospf6_proto.c b/ospf6d/ospf6_proto.c new file mode 100644 index 0000000..09b7b21 --- /dev/null +++ b/ospf6d/ospf6_proto.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2003 Yasuhiro Ohara + */ + +#include <zebra.h> + +#include "log.h" + +#include "ospf6_proto.h" + +void ospf6_prefix_in6_addr(struct in6_addr *in6, const void *prefix_buf, + const struct ospf6_prefix *p) +{ + ptrdiff_t in6_off = (caddr_t)p->addr - (caddr_t)prefix_buf; + + memset(in6, 0, sizeof(struct in6_addr)); + memcpy(in6, (uint8_t *)prefix_buf + in6_off, + OSPF6_PREFIX_SPACE(p->prefix_length)); +} + +void ospf6_prefix_apply_mask(struct ospf6_prefix *op) +{ + uint8_t *pnt, mask; + int index, offset; + + pnt = (uint8_t *)((caddr_t)op + sizeof(struct ospf6_prefix)); + index = op->prefix_length / 8; + offset = op->prefix_length % 8; + mask = 0xff << (8 - offset); + + if (index > 16) { + zlog_warn("Prefix length too long: %d", op->prefix_length); + return; + } + + /* nonzero mask means no check for this byte because if it contains + * prefix bits it must be there for us to write */ + if (mask) + pnt[index++] &= mask; + + while (index < OSPF6_PREFIX_SPACE(op->prefix_length)) + pnt[index++] = 0; +} + +void ospf6_prefix_options_printbuf(uint8_t prefix_options, char *buf, int size) +{ + const char *dn, *p, *mc, *la, *nu; + + dn = (CHECK_FLAG(prefix_options, OSPF6_PREFIX_OPTION_DN) ? "DN" : "--"); + p = (CHECK_FLAG(prefix_options, OSPF6_PREFIX_OPTION_P) ? "P" : "--"); + mc = (CHECK_FLAG(prefix_options, OSPF6_PREFIX_OPTION_MC) ? "MC" : "--"); + la = (CHECK_FLAG(prefix_options, OSPF6_PREFIX_OPTION_LA) ? "LA" : "--"); + nu = (CHECK_FLAG(prefix_options, OSPF6_PREFIX_OPTION_NU) ? "NU" : "--"); + snprintf(buf, size, "%s|%s|%s|%s|%s", dn, p, mc, la, nu); +} + +void ospf6_capability_printbuf(char capability, char *buf, int size) +{ + char w, v, e, b; + w = (capability & OSPF6_ROUTER_BIT_W ? 'W' : '-'); + v = (capability & OSPF6_ROUTER_BIT_V ? 'V' : '-'); + e = (capability & OSPF6_ROUTER_BIT_E ? 'E' : '-'); + b = (capability & OSPF6_ROUTER_BIT_B ? 'B' : '-'); + snprintf(buf, size, "----%c%c%c%c", w, v, e, b); +} + +void ospf6_options_printbuf(uint8_t *options, char *buf, int size) +{ + const char *dc, *r, *n, *mc, *e, *v6, *af, *at, *l; + dc = (OSPF6_OPT_ISSET(options, OSPF6_OPT_DC) ? "DC" : "--"); + r = (OSPF6_OPT_ISSET(options, OSPF6_OPT_R) ? "R" : "-"); + n = (OSPF6_OPT_ISSET(options, OSPF6_OPT_N) ? "N" : "-"); + mc = (OSPF6_OPT_ISSET(options, OSPF6_OPT_MC) ? "MC" : "--"); + e = (OSPF6_OPT_ISSET(options, OSPF6_OPT_E) ? "E" : "-"); + v6 = (OSPF6_OPT_ISSET(options, OSPF6_OPT_V6) ? "V6" : "--"); + af = (OSPF6_OPT_ISSET_EXT(options, OSPF6_OPT_AF) ? "AF" : "--"); + at = (OSPF6_OPT_ISSET_EXT(options, OSPF6_OPT_AT) ? "AT" : "--"); + l = (OSPF6_OPT_ISSET_EXT(options, OSPF6_OPT_L) ? "L" : "-"); + snprintf(buf, size, "%s|%s|%s|-|-|%s|%s|%s|%s|%s|%s", at, l, af, dc, r, + n, mc, e, v6); +} diff --git a/ospf6d/ospf6_proto.h b/ospf6d/ospf6_proto.h new file mode 100644 index 0000000..4307ee3 --- /dev/null +++ b/ospf6d/ospf6_proto.h @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Common protocol data and data structures. + * Copyright (C) 2003 Yasuhiro Ohara + */ + +#ifndef OSPF6_PROTO_H +#define OSPF6_PROTO_H + +/* OSPF protocol version */ +#define OSPFV3_VERSION 3 + +/* TOS field normaly null */ +#define DEFAULT_TOS_VALUE 0x0 + +#define ALLSPFROUTERS6 "ff02::5" +#define ALLDROUTERS6 "ff02::6" + +#define OSPF6_ROUTER_BIT_W (1 << 3) +#define OSPF6_ROUTER_BIT_V (1 << 2) +#define OSPF6_ROUTER_BIT_E (1 << 1) +#define OSPF6_ROUTER_BIT_B (1 << 0) +#define OSPF6_ROUTER_BIT_NT (1 << 4) + + +/* OSPF options */ +/* present in HELLO, DD, LSA */ +#define OSPF6_OPT_SET(x, opt) ((x)[2] |= (opt)) +#define OSPF6_OPT_ISSET(x, opt) ((x)[2] & (opt)) +#define OSPF6_OPT_CLEAR(x, opt) ((x)[2] &= ~(opt)) +#define OSPF6_OPT_SET_EXT(x, opt) ((x)[1] |= (opt)) +#define OSPF6_OPT_ISSET_EXT(x, opt) ((x)[1] & (opt)) +#define OSPF6_OPT_CLEAR_EXT(x, opt) ((x)[1] &= ~(opt)) +#define OSPF6_OPT_CLEAR_ALL(x) ((x)[0] = (x)[1] = (x)[2] = 0) + +#define OSPF6_OPT_AT (1 << 2) /* Authentication trailer Capability */ +#define OSPF6_OPT_L (1 << 1) /* Link local signalling Capability */ +#define OSPF6_OPT_AF (1 << 0) /* Address family Capability */ +/* 2 bits reserved for OSPFv2 migrated options */ +#define OSPF6_OPT_DC (1 << 5) /* Demand Circuit handling Capability */ +#define OSPF6_OPT_R (1 << 4) /* Forwarding Capability (Any Protocol) */ +#define OSPF6_OPT_N (1 << 3) /* Handling Type-7 LSA Capability */ +#define OSPF6_OPT_MC (1 << 2) /* Multicasting Capability */ +#define OSPF6_OPT_E (1 << 1) /* AS External Capability */ +#define OSPF6_OPT_V6 (1 << 0) /* IPv6 forwarding Capability */ + +/* OSPF6 Prefix */ +#define OSPF6_PREFIX_MIN_SIZE 4U /* .length == 0 */ +struct ospf6_prefix { + uint8_t prefix_length; + uint8_t prefix_options; + union { + uint16_t _prefix_metric; + uint16_t _prefix_referenced_lstype; + } u; +#define prefix_metric u._prefix_metric +#define prefix_refer_lstype u._prefix_referenced_lstype + /* followed by one address_prefix */ + struct in6_addr addr[]; +}; + +#define OSPF6_PREFIX_OPTION_NU (1 << 0) /* No Unicast */ +#define OSPF6_PREFIX_OPTION_LA (1 << 1) /* Local Address */ +#define OSPF6_PREFIX_OPTION_MC (1 << 2) /* MultiCast */ +#define OSPF6_PREFIX_OPTION_P (1 << 3) /* Propagate (NSSA) */ +#define OSPF6_PREFIX_OPTION_DN \ + (1 << 4) /* DN bit to prevent loops in VPN environment */ + +/* caddr_t OSPF6_PREFIX_BODY (struct ospf6_prefix *); */ +#define OSPF6_PREFIX_BODY(x) ((caddr_t)(x) + sizeof(struct ospf6_prefix)) + +/* size_t OSPF6_PREFIX_SPACE (int prefixlength); */ +#define OSPF6_PREFIX_SPACE(x) ((((x) + 31) / 32) * 4) + +/* size_t OSPF6_PREFIX_SIZE (struct ospf6_prefix *); */ +#define OSPF6_PREFIX_SIZE(x) \ + (OSPF6_PREFIX_SPACE((x)->prefix_length) + sizeof(struct ospf6_prefix)) + +/* struct ospf6_prefix *OSPF6_PREFIX_NEXT (struct ospf6_prefix *); */ +#define OSPF6_PREFIX_NEXT(x) \ + ((struct ospf6_prefix *)((caddr_t)(x) + OSPF6_PREFIX_SIZE(x))) + +extern void ospf6_prefix_in6_addr(struct in6_addr *in6, const void *prefix_buf, + const struct ospf6_prefix *p); +extern void ospf6_prefix_apply_mask(struct ospf6_prefix *op); +extern void ospf6_prefix_options_printbuf(uint8_t prefix_options, char *buf, + int size); +extern void ospf6_capability_printbuf(char capability, char *buf, int size); +extern void ospf6_options_printbuf(uint8_t *options, char *buf, int size); + +#endif /* OSPF6_PROTO_H */ diff --git a/ospf6d/ospf6_route.c b/ospf6d/ospf6_route.c new file mode 100644 index 0000000..4430329 --- /dev/null +++ b/ospf6d/ospf6_route.c @@ -0,0 +1,1856 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2003 Yasuhiro Ohara + */ + +#include <zebra.h> + +#include "log.h" +#include "memory.h" +#include "prefix.h" +#include "table.h" +#include "vty.h" +#include "command.h" +#include "linklist.h" + +#include "ospf6_proto.h" +#include "ospf6_lsa.h" +#include "ospf6_lsdb.h" +#include "ospf6_route.h" +#include "ospf6_top.h" +#include "ospf6_area.h" +#include "ospf6_interface.h" +#include "ospf6d.h" +#include "ospf6_zebra.h" +#include "ospf6d/ospf6_route_clippy.c" + +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_ROUTE, "OSPF6 route"); +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_ROUTE_TABLE, "OSPF6 route table"); +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_NEXTHOP, "OSPF6 nexthop"); +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_PATH, "OSPF6 Path"); + +unsigned char conf_debug_ospf6_route = 0; + +static char *ospf6_route_table_name(struct ospf6_route_table *table) +{ + static char name[64]; + switch (table->scope_type) { + case OSPF6_SCOPE_TYPE_GLOBAL: { + switch (table->table_type) { + case OSPF6_TABLE_TYPE_ROUTES: + snprintf(name, sizeof(name), "global route table"); + break; + case OSPF6_TABLE_TYPE_BORDER_ROUTERS: + snprintf(name, sizeof(name), "global brouter table"); + break; + case OSPF6_TABLE_TYPE_EXTERNAL_ROUTES: + snprintf(name, sizeof(name), "global external table"); + break; + default: + snprintf(name, sizeof(name), "global unknown table"); + break; + } + } break; + + case OSPF6_SCOPE_TYPE_AREA: { + struct ospf6_area *oa = (struct ospf6_area *)table->scope; + switch (table->table_type) { + case OSPF6_TABLE_TYPE_SPF_RESULTS: + snprintf(name, sizeof(name), "area %s spf table", + oa->name); + break; + case OSPF6_TABLE_TYPE_ROUTES: + snprintf(name, sizeof(name), "area %s route table", + oa->name); + break; + case OSPF6_TABLE_TYPE_PREFIX_RANGES: + snprintf(name, sizeof(name), "area %s range table", + oa->name); + break; + case OSPF6_TABLE_TYPE_SUMMARY_PREFIXES: + snprintf(name, sizeof(name), + "area %s summary prefix table", oa->name); + break; + case OSPF6_TABLE_TYPE_SUMMARY_ROUTERS: + snprintf(name, sizeof(name), + "area %s summary router table", oa->name); + break; + default: + snprintf(name, sizeof(name), "area %s unknown table", + oa->name); + break; + } + } break; + + case OSPF6_SCOPE_TYPE_INTERFACE: { + struct ospf6_interface *oi = + (struct ospf6_interface *)table->scope; + switch (table->table_type) { + case OSPF6_TABLE_TYPE_CONNECTED_ROUTES: + snprintf(name, sizeof(name), + "interface %s connected table", + oi->interface->name); + break; + default: + snprintf(name, sizeof(name), + "interface %s unknown table", + oi->interface->name); + break; + } + } break; + + default: { + switch (table->table_type) { + case OSPF6_TABLE_TYPE_SPF_RESULTS: + snprintf(name, sizeof(name), "temporary spf table"); + break; + default: + snprintf(name, sizeof(name), "temporary unknown table"); + break; + } + } break; + } + return name; +} + +void ospf6_linkstate_prefix(uint32_t adv_router, uint32_t id, + struct prefix *prefix) +{ + memset(prefix, 0, sizeof(struct prefix)); + prefix->family = AF_INET6; + prefix->prefixlen = 64; + memcpy(&prefix->u.prefix6.s6_addr[0], &adv_router, 4); + memcpy(&prefix->u.prefix6.s6_addr[4], &id, 4); +} + +void ospf6_linkstate_prefix2str(struct prefix *prefix, char *buf, int size) +{ + uint32_t adv_router, id; + char adv_router_str[16], id_str[16]; + memcpy(&adv_router, &prefix->u.prefix6.s6_addr[0], 4); + memcpy(&id, &prefix->u.prefix6.s6_addr[4], 4); + inet_ntop(AF_INET, &adv_router, adv_router_str, sizeof(adv_router_str)); + inet_ntop(AF_INET, &id, id_str, sizeof(id_str)); + if (ntohl(id)) + snprintf(buf, size, "%s Net-ID: %s", adv_router_str, id_str); + else + snprintf(buf, size, "%s", adv_router_str); +} + +/* Global strings for logging */ +const char *const ospf6_dest_type_str[OSPF6_DEST_TYPE_MAX] = { + "Unknown", "Router", "Network", "Discard", "Linkstate", "AddressRange", +}; + +const char *const ospf6_dest_type_substr[OSPF6_DEST_TYPE_MAX] = { + "?", "R", "N", "D", "L", "A", +}; + +const char *const ospf6_path_type_str[OSPF6_PATH_TYPE_MAX] = { + "Unknown", "Intra-Area", "Inter-Area", "External-1", "External-2", +}; + +const char *const ospf6_path_type_substr[OSPF6_PATH_TYPE_MAX] = { + "??", "IA", "IE", "E1", "E2", +}; + +const char *ospf6_path_type_json[OSPF6_PATH_TYPE_MAX] = { + "UnknownRoute", "IntraArea", "InterArea", "External1", "External2", +}; + + +struct ospf6_nexthop *ospf6_nexthop_create(void) +{ + struct ospf6_nexthop *nh; + + nh = XCALLOC(MTYPE_OSPF6_NEXTHOP, sizeof(struct ospf6_nexthop)); + return nh; +} + +void ospf6_nexthop_delete(struct ospf6_nexthop *nh) +{ + XFREE(MTYPE_OSPF6_NEXTHOP, nh); +} + +void ospf6_clear_nexthops(struct list *nh_list) +{ + struct listnode *node; + struct ospf6_nexthop *nh; + + if (nh_list) { + for (ALL_LIST_ELEMENTS_RO(nh_list, node, nh)) + ospf6_nexthop_clear(nh); + } +} + +static struct ospf6_nexthop * +ospf6_route_find_nexthop(struct list *nh_list, struct ospf6_nexthop *nh_match) +{ + struct listnode *node; + struct ospf6_nexthop *nh; + + if (nh_list && nh_match) { + for (ALL_LIST_ELEMENTS_RO(nh_list, node, nh)) { + if (ospf6_nexthop_is_same(nh, nh_match)) + return (nh); + } + } + + return (NULL); +} + +void ospf6_copy_nexthops(struct list *dst, struct list *src) +{ + struct ospf6_nexthop *nh_new, *nh; + struct listnode *node; + + if (dst && src) { + for (ALL_LIST_ELEMENTS_RO(src, node, nh)) { + if (ospf6_nexthop_is_set(nh)) { + nh_new = ospf6_nexthop_create(); + ospf6_nexthop_copy(nh_new, nh); + listnode_add_sort(dst, nh_new); + } + } + } +} + +void ospf6_merge_nexthops(struct list *dst, struct list *src) +{ + struct listnode *node; + struct ospf6_nexthop *nh, *nh_new; + + if (src && dst) { + for (ALL_LIST_ELEMENTS_RO(src, node, nh)) { + if (!ospf6_route_find_nexthop(dst, nh)) { + nh_new = ospf6_nexthop_create(); + ospf6_nexthop_copy(nh_new, nh); + listnode_add_sort(dst, nh_new); + } + } + } +} + +/* + * If the nexthops are the same return true + */ +bool ospf6_route_cmp_nexthops(struct ospf6_route *a, struct ospf6_route *b) +{ + struct listnode *anode, *bnode; + struct ospf6_nexthop *anh, *bnh; + bool identical = false; + + if (a && b) { + if (listcount(a->nh_list) == listcount(b->nh_list)) { + for (ALL_LIST_ELEMENTS_RO(a->nh_list, anode, anh)) { + identical = false; + for (ALL_LIST_ELEMENTS_RO(b->nh_list, bnode, + bnh)) { + if (ospf6_nexthop_is_same(anh, bnh)) + identical = true; + } + /* Currnet List A element not found List B + * Non-Identical lists return */ + if (identical == false) + return false; + } + return true; + } else + return false; + } + /* One of the routes doesn't exist ? */ + return false; +} + +int ospf6_num_nexthops(struct list *nh_list) +{ + return (listcount(nh_list)); +} + +void ospf6_add_nexthop(struct list *nh_list, int ifindex, + const struct in6_addr *addr) +{ + struct ospf6_nexthop *nh; + struct ospf6_nexthop nh_match; + + if (nh_list) { + if (addr) { + if (ifindex) + nh_match.type = NEXTHOP_TYPE_IPV6_IFINDEX; + else + nh_match.type = NEXTHOP_TYPE_IPV6; + + memcpy(&nh_match.address, addr, + sizeof(struct in6_addr)); + } else { + nh_match.type = NEXTHOP_TYPE_IFINDEX; + + memset(&nh_match.address, 0, sizeof(struct in6_addr)); + } + + nh_match.ifindex = ifindex; + + if (!ospf6_route_find_nexthop(nh_list, &nh_match)) { + nh = ospf6_nexthop_create(); + ospf6_nexthop_copy(nh, &nh_match); + listnode_add(nh_list, nh); + } + } +} + +void ospf6_add_route_nexthop_blackhole(struct ospf6_route *route) +{ + struct ospf6_nexthop *nh; + struct ospf6_nexthop nh_match = {}; + + /* List not allocated. */ + if (route->nh_list == NULL) + return; + + /* Entry already exists. */ + nh_match.type = NEXTHOP_TYPE_BLACKHOLE; + if (ospf6_route_find_nexthop(route->nh_list, &nh_match)) + return; + + nh = ospf6_nexthop_create(); + ospf6_nexthop_copy(nh, &nh_match); + listnode_add(route->nh_list, nh); +} + +void ospf6_route_zebra_copy_nexthops(struct ospf6_route *route, + struct zapi_nexthop nexthops[], + int entries, vrf_id_t vrf_id) +{ + struct ospf6_nexthop *nh; + struct listnode *node; + int i; + + if (route) { + i = 0; + for (ALL_LIST_ELEMENTS_RO(route->nh_list, node, nh)) { + if (IS_OSPF6_DEBUG_ZEBRA(SEND)) { + zlog_debug(" nexthop: %s %pI6%%%.*s(%d)", + nexthop_type_to_str(nh->type), + &nh->address, IFNAMSIZ, + ifindex2ifname(nh->ifindex, vrf_id), + nh->ifindex); + } + + if (i >= entries) + return; + + nexthops[i].vrf_id = vrf_id; + nexthops[i].type = nh->type; + + switch (nh->type) { + case NEXTHOP_TYPE_BLACKHOLE: + /* NOTHING */ + break; + + case NEXTHOP_TYPE_IFINDEX: + nexthops[i].ifindex = nh->ifindex; + break; + + case NEXTHOP_TYPE_IPV4_IFINDEX: + case NEXTHOP_TYPE_IPV4: + /* + * OSPFv3 with IPv4 routes is not supported + * yet. Skip this next hop. + */ + if (IS_OSPF6_DEBUG_ZEBRA(SEND)) + zlog_debug(" Skipping IPv4 next hop"); + continue; + + case NEXTHOP_TYPE_IPV6_IFINDEX: + nexthops[i].ifindex = nh->ifindex; + /* FALLTHROUGH */ + case NEXTHOP_TYPE_IPV6: + nexthops[i].gate.ipv6 = nh->address; + break; + } + i++; + } + } +} + +int ospf6_route_get_first_nh_index(struct ospf6_route *route) +{ + struct ospf6_nexthop *nh; + + if (route) { + nh = listnode_head(route->nh_list); + if (nh) + return nh->ifindex; + } + + return -1; +} + +int ospf6_nexthop_cmp(struct ospf6_nexthop *a, struct ospf6_nexthop *b) +{ + if (a->ifindex < b->ifindex) + return -1; + else if (a->ifindex > b->ifindex) + return 1; + else + return memcmp(&a->address, &b->address, + sizeof(struct in6_addr)); +} + +static int ospf6_path_cmp(struct ospf6_path *a, struct ospf6_path *b) +{ + if (a->origin.adv_router < b->origin.adv_router) + return -1; + else if (a->origin.adv_router > b->origin.adv_router) + return 1; + else + return 0; +} + +void ospf6_path_free(struct ospf6_path *op) +{ + if (op->nh_list) + list_delete(&op->nh_list); + XFREE(MTYPE_OSPF6_PATH, op); +} + +struct ospf6_path *ospf6_path_dup(struct ospf6_path *path) +{ + struct ospf6_path *new; + + new = XCALLOC(MTYPE_OSPF6_PATH, sizeof(struct ospf6_path)); + memcpy(new, path, sizeof(struct ospf6_path)); + new->nh_list = list_new(); + new->nh_list->cmp = (int (*)(void *, void *))ospf6_nexthop_cmp; + new->nh_list->del = (void (*)(void *))ospf6_nexthop_delete; + + return new; +} + +void ospf6_copy_paths(struct list *dst, struct list *src) +{ + struct ospf6_path *path_new, *path; + struct listnode *node; + + if (dst && src) { + for (ALL_LIST_ELEMENTS_RO(src, node, path)) { + path_new = ospf6_path_dup(path); + ospf6_copy_nexthops(path_new->nh_list, path->nh_list); + listnode_add_sort(dst, path_new); + } + } +} + +struct ospf6_route *ospf6_route_create(struct ospf6 *ospf6) +{ + struct ospf6_route *route; + + route = XCALLOC(MTYPE_OSPF6_ROUTE, sizeof(struct ospf6_route)); + route->nh_list = list_new(); + route->nh_list->cmp = (int (*)(void *, void *))ospf6_nexthop_cmp; + route->nh_list->del = (void (*)(void *))ospf6_nexthop_delete; + route->paths = list_new(); + route->paths->cmp = (int (*)(void *, void *))ospf6_path_cmp; + route->paths->del = (void (*)(void *))ospf6_path_free; + route->ospf6 = ospf6; + + return route; +} + +void ospf6_route_delete(struct ospf6_route *route) +{ + if (route) { + if (route->nh_list) + list_delete(&route->nh_list); + if (route->paths) + list_delete(&route->paths); + XFREE(MTYPE_OSPF6_ROUTE, route); + } +} + +struct ospf6_route *ospf6_route_copy(struct ospf6_route *route) +{ + struct ospf6_route *new; + + new = ospf6_route_create(route->ospf6); + new->type = route->type; + memcpy(&new->prefix, &route->prefix, sizeof(struct prefix)); + new->prefix_options = route->prefix_options; + new->installed = route->installed; + new->changed = route->changed; + new->flag = route->flag; + new->route_option = route->route_option; + new->linkstate_id = route->linkstate_id; + new->path = route->path; + ospf6_copy_nexthops(new->nh_list, route->nh_list); + ospf6_copy_paths(new->paths, route->paths); + new->rnode = NULL; + new->prev = NULL; + new->next = NULL; + new->table = NULL; + new->lock = 0; + return new; +} + +void ospf6_route_lock(struct ospf6_route *route) +{ + route->lock++; +} + +void ospf6_route_unlock(struct ospf6_route *route) +{ + assert(route->lock > 0); + route->lock--; + if (route->lock == 0) { + /* Can't detach from the table until here + because ospf6_route_next () will use + the 'route->table' pointer for logging */ + route->table = NULL; + ospf6_route_delete(route); + } +} + +/* Route compare function. If ra is more preferred, it returns + less than 0. If rb is more preferred returns greater than 0. + Otherwise (neither one is preferred), returns 0 */ +int ospf6_route_cmp(struct ospf6_route *ra, struct ospf6_route *rb) +{ + assert(ospf6_route_is_same(ra, rb)); + assert(OSPF6_PATH_TYPE_NONE < ra->path.type + && ra->path.type < OSPF6_PATH_TYPE_MAX); + assert(OSPF6_PATH_TYPE_NONE < rb->path.type + && rb->path.type < OSPF6_PATH_TYPE_MAX); + + if (ra->type != rb->type) + return (ra->type - rb->type); + + if (ra->path.type != rb->path.type) + return (ra->path.type - rb->path.type); + + if (ra->path.type == OSPF6_PATH_TYPE_EXTERNAL2) { + if (ra->path.u.cost_e2 != rb->path.u.cost_e2) + return (ra->path.u.cost_e2 - rb->path.u.cost_e2); + else + return (ra->path.cost - rb->path.cost); + } else { + if (ra->path.cost != rb->path.cost) + return (ra->path.cost - rb->path.cost); + } + + if (ra->path.area_id != rb->path.area_id) + return (ntohl(ra->path.area_id) - ntohl(rb->path.area_id)); + + return 0; +} + +struct ospf6_route *ospf6_route_lookup(struct prefix *prefix, + struct ospf6_route_table *table) +{ + struct route_node *node; + struct ospf6_route *route; + + node = route_node_lookup(table->table, prefix); + if (node == NULL) + return NULL; + + route = (struct ospf6_route *)node->info; + route_unlock_node(node); /* to free the lookup lock */ + return route; +} + +struct ospf6_route * +ospf6_route_lookup_identical(struct ospf6_route *route, + struct ospf6_route_table *table) +{ + struct ospf6_route *target; + + for (target = ospf6_route_lookup(&route->prefix, table); target; + target = target->next) { + if (ospf6_route_is_identical(target, route)) + return target; + } + return NULL; +} + +struct ospf6_route * +ospf6_route_lookup_bestmatch(struct prefix *prefix, + struct ospf6_route_table *table) +{ + struct route_node *node; + struct ospf6_route *route; + + node = route_node_match(table->table, prefix); + if (node == NULL) + return NULL; + route_unlock_node(node); + + route = (struct ospf6_route *)node->info; + return route; +} + +#ifdef DEBUG +static void route_table_assert(struct ospf6_route_table *table) +{ + struct ospf6_route *prev, *r, *next; + unsigned int link_error = 0, num = 0; + + r = ospf6_route_head(table); + prev = NULL; + while (r) { + if (r->prev != prev) + link_error++; + + next = ospf6_route_next(r); + + if (r->next != next) + link_error++; + + prev = r; + r = next; + } + + for (r = ospf6_route_head(table); r; r = ospf6_route_next(r)) + num++; + + if (link_error == 0 && num == table->count) + return; + + flog_err(EC_LIB_DEVELOPMENT, "PANIC !!"); + flog_err(EC_LIB_DEVELOPMENT, + "Something has gone wrong with ospf6_route_table[%p]", table); + zlog_debug("table count = %d, real number = %d", table->count, num); + zlog_debug("DUMP START"); + for (r = ospf6_route_head(table); r; r = ospf6_route_next(r)) + zlog_info("%p<-[%p]->%p : %pFX", r->prev, r, r->next, + &r->prefix); + zlog_debug("DUMP END"); + + assert(link_error == 0 && num == table->count); +} +#define ospf6_route_table_assert(t) (route_table_assert (t)) +#else +#define ospf6_route_table_assert(t) ((void) 0) +#endif /*DEBUG*/ + +struct ospf6_route *ospf6_route_add(struct ospf6_route *route, + struct ospf6_route_table *table) +{ + struct route_node *node, *nextnode, *prevnode; + struct ospf6_route *current = NULL; + struct ospf6_route *prev = NULL, *old = NULL, *next = NULL; + char buf[PREFIX2STR_BUFFER]; + struct timeval now; + + assert(route->rnode == NULL); + assert(route->lock == 0); + assert(route->next == NULL); + assert(route->prev == NULL); + + if (route->type == OSPF6_DEST_TYPE_LINKSTATE) + ospf6_linkstate_prefix2str(&route->prefix, buf, sizeof(buf)); + else 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)); + + if (IS_OSPF6_DEBUG_ROUTE(MEMORY)) + zlog_debug("%s %p: route add %p: %s paths %u nh %u", + ospf6_route_table_name(table), (void *)table, + (void *)route, buf, listcount(route->paths), + listcount(route->nh_list)); + else if (IS_OSPF6_DEBUG_ROUTE(TABLE)) + zlog_debug("%s: route add: %s", ospf6_route_table_name(table), + buf); + + monotime(&now); + + node = route_node_get(table->table, &route->prefix); + route->rnode = node; + + /* find place to insert */ + for (current = node->info; current; current = current->next) { + if (!ospf6_route_is_same(current, route)) + next = current; + else if (current->type != route->type) + prev = current; + else if (ospf6_route_is_same_origin(current, route)) + old = current; + else if (ospf6_route_cmp(current, route) > 0) + next = current; + else + prev = current; + + if (old || next) + break; + } + + if (old) { + /* if route does not actually change, return unchanged */ + if (ospf6_route_is_identical(old, route)) { + if (IS_OSPF6_DEBUG_ROUTE(MEMORY)) + zlog_debug( + "%s %p: route add %p: needless update of %p old cost %u", + ospf6_route_table_name(table), + (void *)table, (void *)route, + (void *)old, old->path.cost); + else if (IS_OSPF6_DEBUG_ROUTE(TABLE)) + zlog_debug("%s: route add: needless update", + ospf6_route_table_name(table)); + + ospf6_route_delete(route); + SET_FLAG(old->flag, OSPF6_ROUTE_ADD); + ospf6_route_table_assert(table); + + /* to free the lookup lock */ + route_unlock_node(node); + return old; + } + + if (IS_OSPF6_DEBUG_ROUTE(MEMORY)) + zlog_debug( + "%s %p: route add %p cost %u paths %u nh %u: update of %p cost %u paths %u nh %u", + ospf6_route_table_name(table), (void *)table, + (void *)route, route->path.cost, + listcount(route->paths), + listcount(route->nh_list), (void *)old, + old->path.cost, listcount(old->paths), + listcount(old->nh_list)); + else if (IS_OSPF6_DEBUG_ROUTE(TABLE)) + zlog_debug("%s: route add: update", + ospf6_route_table_name(table)); + + /* replace old one if exists */ + if (node->info == old) { + node->info = route; + SET_FLAG(route->flag, OSPF6_ROUTE_BEST); + if (IS_OSPF6_DEBUG_ROUTE(MEMORY)) + zlog_debug("%s: replace old route %s", + __func__, buf); + } + + if (old->prev) + old->prev->next = route; + route->prev = old->prev; + if (old->next) + old->next->prev = route; + route->next = old->next; + + route->installed = old->installed; + route->changed = now; + assert(route->table == NULL); + route->table = table; + + ospf6_route_unlock(old); /* will be deleted later */ + ospf6_route_lock(route); + + SET_FLAG(route->flag, OSPF6_ROUTE_CHANGE); + ospf6_route_table_assert(table); + + if (table->hook_add) + (*table->hook_add)(route); + + return route; + } + + /* insert if previous or next node found */ + if (prev || next) { + if (IS_OSPF6_DEBUG_ROUTE(MEMORY)) + zlog_debug( + "%s %p: route add %p cost %u: another path: prev %p, next %p node ref %u", + ospf6_route_table_name(table), (void *)table, + (void *)route, route->path.cost, (void *)prev, + (void *)next, route_node_get_lock_count(node)); + else if (IS_OSPF6_DEBUG_ROUTE(TABLE)) + zlog_debug("%s: route add cost %u: another path found", + ospf6_route_table_name(table), + route->path.cost); + + if (prev == NULL) + prev = next->prev; + if (next == NULL) + next = prev->next; + + if (prev) + prev->next = route; + route->prev = prev; + if (next) + next->prev = route; + route->next = next; + + if (node->info == next) { + assert(next && next->rnode == node); + node->info = route; + UNSET_FLAG(next->flag, OSPF6_ROUTE_BEST); + SET_FLAG(route->flag, OSPF6_ROUTE_BEST); + if (IS_OSPF6_DEBUG_ROUTE(MEMORY)) + zlog_debug( + "%s %p: route add %p cost %u: replacing previous best: %p cost %u", + ospf6_route_table_name(table), + (void *)table, (void *)route, + route->path.cost, (void *)next, + next->path.cost); + } + + route->installed = now; + route->changed = now; + assert(route->table == NULL); + route->table = table; + + ospf6_route_lock(route); + table->count++; + ospf6_route_table_assert(table); + + SET_FLAG(route->flag, OSPF6_ROUTE_ADD); + if (table->hook_add) + (*table->hook_add)(route); + + return route; + } + + /* Else, this is the brand new route regarding to the prefix */ + if (IS_OSPF6_DEBUG_ROUTE(MEMORY)) + zlog_debug("%s %p: route add %p %s cost %u: brand new route", + ospf6_route_table_name(table), (void *)table, + (void *)route, buf, route->path.cost); + else if (IS_OSPF6_DEBUG_ROUTE(TABLE)) + zlog_debug("%s: route add: brand new route", + ospf6_route_table_name(table)); + + assert(node->info == NULL); + node->info = route; + SET_FLAG(route->flag, OSPF6_ROUTE_BEST); + ospf6_route_lock(route); + route->installed = now; + route->changed = now; + assert(route->table == NULL); + route->table = table; + + /* lookup real existing next route */ + nextnode = node; + route_lock_node(nextnode); + do { + nextnode = route_next(nextnode); + } while (nextnode && nextnode->info == NULL); + + /* set next link */ + if (nextnode == NULL) + route->next = NULL; + else { + route_unlock_node(nextnode); + + next = nextnode->info; + route->next = next; + next->prev = route; + } + + /* lookup real existing prev route */ + prevnode = node; + route_lock_node(prevnode); + do { + prevnode = route_prev(prevnode); + } while (prevnode && prevnode->info == NULL); + + /* set prev link */ + if (prevnode == NULL) + route->prev = NULL; + else { + route_unlock_node(prevnode); + + prev = prevnode->info; + while (prev->next && ospf6_route_is_same(prev, prev->next)) + prev = prev->next; + route->prev = prev; + prev->next = route; + } + + table->count++; + ospf6_route_table_assert(table); + + SET_FLAG(route->flag, OSPF6_ROUTE_ADD); + if (table->hook_add) + (*table->hook_add)(route); + + return route; +} + +void ospf6_route_remove(struct ospf6_route *route, + struct ospf6_route_table *table) +{ + struct route_node *node; + struct ospf6_route *current; + char buf[PREFIX2STR_BUFFER]; + + if (route->type == OSPF6_DEST_TYPE_LINKSTATE) + ospf6_linkstate_prefix2str(&route->prefix, buf, sizeof(buf)); + else 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)); + + if (IS_OSPF6_DEBUG_ROUTE(MEMORY)) + zlog_debug("%s %p: route remove %p: %s cost %u refcount %u", + ospf6_route_table_name(table), (void *)table, + (void *)route, buf, route->path.cost, route->lock); + else if (IS_OSPF6_DEBUG_ROUTE(TABLE)) + zlog_debug("%s: route remove: %s", + ospf6_route_table_name(table), buf); + + node = route_node_lookup(table->table, &route->prefix); + assert(node); + + /* find the route to remove, making sure that the route pointer + is from the route table. */ + current = node->info; + while (current && current != route) + current = current->next; + + assert(current == route); + + /* adjust doubly linked list */ + if (route->prev) + route->prev->next = route->next; + if (route->next) + route->next->prev = route->prev; + + if (node->info == route) { + if (route->next && route->next->rnode == node) { + node->info = route->next; + SET_FLAG(route->next->flag, OSPF6_ROUTE_BEST); + if (IS_OSPF6_DEBUG_ROUTE(MEMORY)) + zlog_debug("%s: remove route %s", __func__, + buf); + } else { + node->info = NULL; + route->rnode = NULL; + route_unlock_node(node); /* to free the original lock */ + } + } + + route_unlock_node(node); /* to free the lookup lock */ + table->count--; + ospf6_route_table_assert(table); + + SET_FLAG(route->flag, OSPF6_ROUTE_WAS_REMOVED); + + /* Note hook_remove may call ospf6_route_remove */ + if (table->hook_remove) + (*table->hook_remove)(route); + + ospf6_route_unlock(route); +} + +struct ospf6_route *ospf6_route_head(struct ospf6_route_table *table) +{ + struct route_node *node; + struct ospf6_route *route; + + node = route_top(table->table); + if (node == NULL) + return NULL; + + /* skip to the real existing entry */ + while (node && node->info == NULL) + node = route_next(node); + if (node == NULL) + return NULL; + + route_unlock_node(node); + assert(node->info); + + route = (struct ospf6_route *)node->info; + assert(route->prev == NULL); + assert(route->table == table); + ospf6_route_lock(route); + + if (IS_OSPF6_DEBUG_ROUTE(MEMORY)) + zlog_info("%s %p: route head: %p<-[%p]->%p", + ospf6_route_table_name(table), (void *)table, + (void *)route->prev, (void *)route, + (void *)route->next); + + return route; +} + +struct ospf6_route *ospf6_route_next(struct ospf6_route *route) +{ + struct ospf6_route *next = route->next; + + if (IS_OSPF6_DEBUG_ROUTE(MEMORY)) + zlog_info("%s %p: route next: %p<-[%p]->%p , route ref count %u", + ospf6_route_table_name(route->table), + (void *)route->table, (void *)route->prev, + (void *)route, (void *)route->next, + route->lock); + + ospf6_route_unlock(route); + if (next) + ospf6_route_lock(next); + + return next; +} + +struct ospf6_route *ospf6_route_best_next(struct ospf6_route *route) +{ + struct route_node *rnode; + struct ospf6_route *next; + + ospf6_route_unlock(route); + + rnode = route->rnode; + route_lock_node(rnode); + rnode = route_next(rnode); + while (rnode && rnode->info == NULL) + rnode = route_next(rnode); + if (rnode == NULL) + return NULL; + route_unlock_node(rnode); + + assert(rnode->info); + next = (struct ospf6_route *)rnode->info; + ospf6_route_lock(next); + return next; +} + +struct ospf6_route *ospf6_route_match_head(struct prefix *prefix, + struct ospf6_route_table *table) +{ + struct route_node *node; + struct ospf6_route *route; + + /* Walk down tree. */ + node = table->table->top; + while (node && node->p.prefixlen < prefix->prefixlen + && prefix_match(&node->p, prefix)) + node = node->link[prefix_bit(&prefix->u.prefix, + node->p.prefixlen)]; + + if (node) + route_lock_node(node); + while (node && node->info == NULL) + node = route_next(node); + if (node == NULL) + return NULL; + route_unlock_node(node); + + if (!prefix_match(prefix, &node->p)) + return NULL; + + route = node->info; + ospf6_route_lock(route); + return route; +} + +struct ospf6_route *ospf6_route_match_next(struct prefix *prefix, + struct ospf6_route *route) +{ + struct ospf6_route *next; + + next = ospf6_route_next(route); + if (next && !prefix_match(prefix, &next->prefix)) { + ospf6_route_unlock(next); + next = NULL; + } + + return next; +} + +void ospf6_route_remove_all(struct ospf6_route_table *table) +{ + struct ospf6_route *route; + for (route = ospf6_route_head(table); route; + route = ospf6_route_next(route)) + ospf6_route_remove(route, table); +} + +struct ospf6_route_table *ospf6_route_table_create(int s, int t) +{ + struct ospf6_route_table *new; + new = XCALLOC(MTYPE_OSPF6_ROUTE_TABLE, + sizeof(struct ospf6_route_table)); + new->table = route_table_init(); + new->scope_type = s; + new->table_type = t; + return new; +} + +void ospf6_route_table_delete(struct ospf6_route_table *table) +{ + ospf6_route_remove_all(table); + route_table_finish(table->table); + XFREE(MTYPE_OSPF6_ROUTE_TABLE, table); +} + + +/* VTY commands */ +void ospf6_route_show(struct vty *vty, struct ospf6_route *route, + json_object *json_routes, bool use_json) +{ + int i; + char destination[PREFIX2STR_BUFFER], nexthop[64]; + char duration[64]; + struct timeval now, res; + struct listnode *node; + struct ospf6_nexthop *nh; + json_object *json_route = NULL; + json_object *json_array_next_hops = NULL; + json_object *json_next_hop; + vrf_id_t vrf_id = route->ospf6->vrf_id; + + monotime(&now); + timersub(&now, &route->changed, &res); + timerstring(&res, duration, sizeof(duration)); + + /* destination */ + if (route->type == OSPF6_DEST_TYPE_LINKSTATE) + ospf6_linkstate_prefix2str(&route->prefix, destination, + sizeof(destination)); + else if (route->type == OSPF6_DEST_TYPE_ROUTER) + inet_ntop(route->prefix.family, &route->prefix.u.prefix, + destination, sizeof(destination)); + else + prefix2str(&route->prefix, destination, sizeof(destination)); + + if (use_json) { + json_route = json_object_new_object(); + json_object_boolean_add(json_route, "isBestRoute", + ospf6_route_is_best(route)); + json_object_string_add(json_route, "destinationType", + OSPF6_DEST_TYPE_SUBSTR(route->type)); + json_object_string_add( + json_route, "pathType", + OSPF6_PATH_TYPE_SUBSTR(route->path.type)); + json_object_string_add(json_route, "duration", duration); + } + + /* Nexthops */ + if (use_json) + json_array_next_hops = json_object_new_array(); + else + i = 0; + for (ALL_LIST_ELEMENTS_RO(route->nh_list, node, nh)) { + /* nexthop */ + inet_ntop(AF_INET6, &nh->address, nexthop, sizeof(nexthop)); + if (use_json) { + json_next_hop = json_object_new_object(); + json_object_string_add(json_next_hop, "nextHop", + nexthop); + json_object_string_add( + json_next_hop, "interfaceName", + ifindex2ifname(nh->ifindex, vrf_id)); + json_object_array_add(json_array_next_hops, + json_next_hop); + } else { + if (!i) { + vty_out(vty, "%c%1s %2s %-30s %-25s %6.*s %s\n", + (ospf6_route_is_best(route) ? '*' + : ' '), + OSPF6_DEST_TYPE_SUBSTR(route->type), + OSPF6_PATH_TYPE_SUBSTR( + route->path.type), + destination, nexthop, IFNAMSIZ, + ifindex2ifname(nh->ifindex, vrf_id), + duration); + i++; + } else + vty_out(vty, "%c%1s %2s %-30s %-25s %6.*s %s\n", + ' ', "", "", "", nexthop, IFNAMSIZ, + ifindex2ifname(nh->ifindex, vrf_id), + ""); + } + } + if (use_json) { + json_object_object_add(json_route, "nextHops", + json_array_next_hops); + json_object_object_add(json_routes, destination, json_route); + } +} + +void ospf6_route_show_detail(struct vty *vty, struct ospf6_route *route, + json_object *json_routes, bool use_json) +{ + char destination[PREFIX2STR_BUFFER], nexthop[64]; + char area_id[16], id[16], adv_router[16], capa[16], options[32]; + char pfx_options[16]; + struct timeval now, res; + char duration[64]; + struct listnode *node; + struct ospf6_nexthop *nh; + char flag[6]; + json_object *json_route = NULL; + json_object *json_array_next_hops = NULL; + json_object *json_next_hop; + vrf_id_t vrf_id = route->ospf6->vrf_id; + + monotime(&now); + + /* destination */ + if (route->type == OSPF6_DEST_TYPE_LINKSTATE) + ospf6_linkstate_prefix2str(&route->prefix, destination, + sizeof(destination)); + else if (route->type == OSPF6_DEST_TYPE_ROUTER) + inet_ntop(route->prefix.family, &route->prefix.u.prefix, + destination, sizeof(destination)); + else + prefix2str(&route->prefix, destination, sizeof(destination)); + + if (use_json) { + json_route = json_object_new_object(); + json_object_string_add(json_route, "destinationType", + OSPF6_DEST_TYPE_NAME(route->type)); + } else { + vty_out(vty, "Destination: %s\n", destination); + vty_out(vty, "Destination type: %s\n", + OSPF6_DEST_TYPE_NAME(route->type)); + } + + /* Time */ + timersub(&now, &route->installed, &res); + timerstring(&res, duration, sizeof(duration)); + if (use_json) + json_object_string_add(json_route, "installedTimeSince", + duration); + else + vty_out(vty, "Installed Time: %s ago\n", duration); + + timersub(&now, &route->changed, &res); + timerstring(&res, duration, sizeof(duration)); + if (use_json) + json_object_string_add(json_route, "changedTimeSince", + duration); + else + vty_out(vty, "Changed Time: %s ago\n", duration); + + /* Debugging info */ + if (use_json) { + json_object_int_add(json_route, "numberOfLock", route->lock); + snprintf( + flag, sizeof(flag), "%s%s%s%s", + (CHECK_FLAG(route->flag, OSPF6_ROUTE_BEST) ? "B" : "-"), + (CHECK_FLAG(route->flag, OSPF6_ROUTE_ADD) ? "A" : "-"), + (CHECK_FLAG(route->flag, OSPF6_ROUTE_REMOVE) ? "R" + : "-"), + (CHECK_FLAG(route->flag, OSPF6_ROUTE_CHANGE) ? "C" + : "-")); + json_object_string_add(json_route, "flags", flag); + } else { + vty_out(vty, "Lock: %d Flags: %s%s%s%s\n", route->lock, + (CHECK_FLAG(route->flag, OSPF6_ROUTE_BEST) ? "B" : "-"), + (CHECK_FLAG(route->flag, OSPF6_ROUTE_ADD) ? "A" : "-"), + (CHECK_FLAG(route->flag, OSPF6_ROUTE_REMOVE) ? "R" + : "-"), + (CHECK_FLAG(route->flag, OSPF6_ROUTE_CHANGE) ? "C" + : "-")); + vty_out(vty, "Memory: prev: %p this: %p next: %p\n", + (void *)route->prev, (void *)route, + (void *)route->next); + } + + /* Path section */ + + /* Area-ID */ + inet_ntop(AF_INET, &route->path.area_id, area_id, sizeof(area_id)); + if (use_json) + json_object_string_add(json_route, "associatedArea", area_id); + else + vty_out(vty, "Associated Area: %s\n", area_id); + + /* Path type */ + if (use_json) + json_object_string_add(json_route, "pathType", + OSPF6_PATH_TYPE_NAME(route->path.type)); + else + vty_out(vty, "Path Type: %s\n", + OSPF6_PATH_TYPE_NAME(route->path.type)); + + /* LS Origin */ + inet_ntop(AF_INET, &route->path.origin.id, id, sizeof(id)); + inet_ntop(AF_INET, &route->path.origin.adv_router, adv_router, + sizeof(adv_router)); + if (use_json) { + json_object_string_add( + json_route, "lsOriginRoutePathType", + ospf6_lstype_name(route->path.origin.type)); + json_object_string_add(json_route, "lsId", id); + json_object_string_add(json_route, "lsAdvertisingRouter", + adv_router); + } else { + vty_out(vty, "LS Origin: %s Id: %s Adv: %s\n", + ospf6_lstype_name(route->path.origin.type), id, + adv_router); + } + + /* Options */ + ospf6_options_printbuf(route->path.options, options, sizeof(options)); + if (use_json) + json_object_string_add(json_route, "options", options); + else + vty_out(vty, "Options: %s\n", options); + + /* Router Bits */ + ospf6_capability_printbuf(route->path.router_bits, capa, sizeof(capa)); + if (use_json) + json_object_string_add(json_route, "routerBits", capa); + else + vty_out(vty, "Router Bits: %s\n", capa); + + /* Prefix Options */ + ospf6_prefix_options_printbuf(route->prefix_options, pfx_options, + sizeof(pfx_options)); + if (use_json) + json_object_string_add(json_route, "prefixOptions", + pfx_options); + else + vty_out(vty, "Prefix Options: %s\n", pfx_options); + + /* Metrics */ + if (use_json) { + json_object_int_add(json_route, "metricType", + route->path.metric_type); + json_object_int_add(json_route, "metricCost", route->path.cost); + json_object_int_add(json_route, "metricCostE2", + route->path.u.cost_e2); + + json_object_int_add(json_route, "pathsCount", + route->paths->count); + json_object_int_add(json_route, "nextHopCount", + route->nh_list->count); + } else { + vty_out(vty, "Metric Type: %d\n", route->path.metric_type); + vty_out(vty, "Metric: %d (%d)\n", route->path.cost, + route->path.u.cost_e2); + + vty_out(vty, "Paths count: %u\n", route->paths->count); + vty_out(vty, "Nexthop count: %u\n", route->nh_list->count); + } + + /* Nexthops */ + if (use_json) + json_array_next_hops = json_object_new_array(); + else + vty_out(vty, "Nexthop:\n"); + + for (ALL_LIST_ELEMENTS_RO(route->nh_list, node, nh)) { + /* nexthop */ + if (use_json) { + inet_ntop(AF_INET6, &nh->address, nexthop, + sizeof(nexthop)); + json_next_hop = json_object_new_object(); + json_object_string_add(json_next_hop, "nextHop", + nexthop); + json_object_string_add( + json_next_hop, "interfaceName", + ifindex2ifname(nh->ifindex, vrf_id)); + json_object_array_add(json_array_next_hops, + json_next_hop); + } else + vty_out(vty, " %pI6 %.*s\n", &nh->address, IFNAMSIZ, + ifindex2ifname(nh->ifindex, vrf_id)); + } + if (use_json) { + json_object_object_add(json_route, "nextHops", + json_array_next_hops); + json_object_object_add(json_routes, destination, json_route); + } else + vty_out(vty, "\n"); +} + +static void ospf6_route_show_table_summary(struct vty *vty, + struct ospf6_route_table *table, + json_object *json, bool use_json) +{ + struct ospf6_route *route, *prev = NULL; + int i, pathtype[OSPF6_PATH_TYPE_MAX]; + unsigned int number = 0; + int nh_count = 0, ecmp = 0; + int alternative = 0, destination = 0; + char path_str[30]; + + for (i = 0; i < OSPF6_PATH_TYPE_MAX; i++) + pathtype[i] = 0; + + for (route = ospf6_route_head(table); route; + route = ospf6_route_next(route)) { + if (prev == NULL || !ospf6_route_is_same(prev, route)) + destination++; + else + alternative++; + nh_count = ospf6_num_nexthops(route->nh_list); + if (nh_count > 1) + ecmp++; + pathtype[route->path.type]++; + number++; + + prev = route; + } + + assert(number == table->count); + if (use_json) { + json_object_int_add(json, "numberOfOspfv3Routes", number); + json_object_int_add(json, "numberOfDestination", destination); + json_object_int_add(json, "numberOfAlternativeRoutes", + alternative); + json_object_int_add(json, "numberOfEcmp", ecmp); + } else { + vty_out(vty, "Number of OSPFv3 routes: %d\n", number); + vty_out(vty, "Number of Destination: %d\n", destination); + vty_out(vty, "Number of Alternative routes: %d\n", alternative); + vty_out(vty, "Number of Equal Cost Multi Path: %d\n", ecmp); + } + for (i = OSPF6_PATH_TYPE_INTRA; i <= OSPF6_PATH_TYPE_EXTERNAL2; i++) { + if (use_json) { + snprintf(path_str, sizeof(path_str), "numberOf%sRoutes", + OSPF6_PATH_TYPE_JSON(i)); + json_object_int_add(json, path_str, pathtype[i]); + } else + vty_out(vty, "Number of %s routes: %d\n", + OSPF6_PATH_TYPE_NAME(i), pathtype[i]); + } +} + +static void ospf6_route_show_table_prefix(struct vty *vty, + struct prefix *prefix, + struct ospf6_route_table *table, + json_object *json, bool use_json) +{ + struct ospf6_route *route; + json_object *json_routes = NULL; + + route = ospf6_route_lookup(prefix, table); + if (route == NULL) + return; + + if (use_json) + json_routes = json_object_new_object(); + ospf6_route_lock(route); + while (route && ospf6_route_is_prefix(prefix, route)) { + /* Specifying a prefix will always display details */ + ospf6_route_show_detail(vty, route, json_routes, use_json); + route = ospf6_route_next(route); + } + + if (use_json) + json_object_object_add(json, "routes", json_routes); + if (route) + ospf6_route_unlock(route); +} + +static void ospf6_route_show_table_address(struct vty *vty, + struct prefix *prefix, + struct ospf6_route_table *table, + json_object *json, bool use_json) +{ + struct ospf6_route *route; + json_object *json_routes = NULL; + + route = ospf6_route_lookup_bestmatch(prefix, table); + if (route == NULL) + return; + + if (use_json) + json_routes = json_object_new_object(); + prefix = &route->prefix; + ospf6_route_lock(route); + while (route && ospf6_route_is_prefix(prefix, route)) { + /* Specifying a prefix will always display details */ + ospf6_route_show_detail(vty, route, json_routes, use_json); + route = ospf6_route_next(route); + } + if (use_json) + json_object_object_add(json, "routes", json_routes); + if (route) + ospf6_route_unlock(route); +} + +static void ospf6_route_show_table_match(struct vty *vty, int detail, + struct prefix *prefix, + struct ospf6_route_table *table, + json_object *json, bool use_json) +{ + struct ospf6_route *route; + json_object *json_routes = NULL; + + assert(prefix->family); + + route = ospf6_route_match_head(prefix, table); + if (use_json) + json_routes = json_object_new_object(); + while (route) { + if (detail) + ospf6_route_show_detail(vty, route, json_routes, + use_json); + else + ospf6_route_show(vty, route, json_routes, use_json); + route = ospf6_route_match_next(prefix, route); + } + if (use_json) + json_object_object_add(json, "routes", json_routes); +} + +static void ospf6_route_show_table_type(struct vty *vty, int detail, + uint8_t type, + struct ospf6_route_table *table, + json_object *json, bool use_json) +{ + struct ospf6_route *route; + json_object *json_routes = NULL; + + route = ospf6_route_head(table); + if (use_json) + json_routes = json_object_new_object(); + while (route) { + if (route->path.type == type) { + if (detail) + ospf6_route_show_detail(vty, route, json_routes, + use_json); + else + ospf6_route_show(vty, route, json_routes, + use_json); + } + route = ospf6_route_next(route); + } + if (use_json) + json_object_object_add(json, "routes", json_routes); +} + +static void ospf6_route_show_table(struct vty *vty, int detail, + struct ospf6_route_table *table, + json_object *json, bool use_json) +{ + struct ospf6_route *route; + json_object *json_routes = NULL; + + route = ospf6_route_head(table); + if (use_json) + json_routes = json_object_new_object(); + while (route) { + if (detail) + ospf6_route_show_detail(vty, route, json_routes, + use_json); + else + ospf6_route_show(vty, route, json_routes, use_json); + route = ospf6_route_next(route); + } + if (use_json) + json_object_object_add(json, "routes", json_routes); +} + +int ospf6_route_table_show(struct vty *vty, int argc_start, int argc, + struct cmd_token **argv, + struct ospf6_route_table *table, bool use_json) +{ + int summary = 0; + int match = 0; + int detail = 0; + int slash = 0; + int isprefix = 0; + int i, ret; + struct prefix prefix; + uint8_t type = 0; + int arg_end = use_json ? (argc - 1) : argc; + json_object *json = NULL; + + memset(&prefix, 0, sizeof(prefix)); + + if (use_json) + json = json_object_new_object(); + + for (i = argc_start; i < arg_end; i++) { + if (strmatch(argv[i]->text, "summary")) { + summary++; + continue; + } + + if (strmatch(argv[i]->text, "intra-area")) { + type = OSPF6_PATH_TYPE_INTRA; + continue; + } + + if (strmatch(argv[i]->text, "inter-area")) { + type = OSPF6_PATH_TYPE_INTER; + continue; + } + + if (strmatch(argv[i]->text, "external-1")) { + type = OSPF6_PATH_TYPE_EXTERNAL1; + continue; + } + + if (strmatch(argv[i]->text, "external-2")) { + type = OSPF6_PATH_TYPE_EXTERNAL2; + continue; + } + + if (strmatch(argv[i]->text, "detail")) { + detail++; + continue; + } + + if (strmatch(argv[i]->text, "match")) { + match++; + continue; + } + + ret = str2prefix(argv[i]->arg, &prefix); + if (ret == 1 && prefix.family == AF_INET6) { + isprefix++; + if (strchr(argv[i]->arg, '/')) + slash++; + continue; + } + if (use_json) + json_object_string_add(json, "malformedArgument", + argv[i]->arg); + else + vty_out(vty, "Malformed argument: %s\n", argv[i]->arg); + + return CMD_SUCCESS; + } + + /* Give summary of this route table */ + if (summary) { + ospf6_route_show_table_summary(vty, table, json, use_json); + if (use_json) + vty_json(vty, json); + return CMD_SUCCESS; + } + + /* Give exact prefix-match route */ + if (isprefix && !match) { + /* If exact address, give best matching route */ + if (!slash) + ospf6_route_show_table_address(vty, &prefix, table, + json, use_json); + else + ospf6_route_show_table_prefix(vty, &prefix, table, json, + use_json); + + if (use_json) + vty_json(vty, json); + return CMD_SUCCESS; + } + + if (match) + ospf6_route_show_table_match(vty, detail, &prefix, table, json, + use_json); + else if (type) + ospf6_route_show_table_type(vty, detail, type, table, json, + use_json); + else + ospf6_route_show_table(vty, detail, table, json, use_json); + + if (use_json) + vty_json(vty, json); + return CMD_SUCCESS; +} + +static void ospf6_linkstate_show_header(struct vty *vty) +{ + vty_out(vty, "%-7s %-15s %-15s %-8s %-14s %s\n", "Type", "Router-ID", + "Net-ID", "Rtr-Bits", "Options", "Cost"); +} + +static void ospf6_linkstate_show(struct vty *vty, struct ospf6_route *route) +{ + uint32_t router, id; + char routername[16], idname[16], rbits[16], options[32]; + + router = ospf6_linkstate_prefix_adv_router(&route->prefix); + inet_ntop(AF_INET, &router, routername, sizeof(routername)); + id = ospf6_linkstate_prefix_id(&route->prefix); + inet_ntop(AF_INET, &id, idname, sizeof(idname)); + + ospf6_capability_printbuf(route->path.router_bits, rbits, + sizeof(rbits)); + ospf6_options_printbuf(route->path.options, options, sizeof(options)); + + if (ntohl(id)) + vty_out(vty, "%-7s %-15s %-15s %-8s %-14s %lu\n", "Network", + routername, idname, rbits, options, + (unsigned long)route->path.cost); + else + vty_out(vty, "%-7s %-15s %-15s %-8s %-14s %lu\n", "Router", + routername, idname, rbits, options, + (unsigned long)route->path.cost); +} + + +static void ospf6_linkstate_show_table_exact(struct vty *vty, + struct prefix *prefix, + struct ospf6_route_table *table) +{ + struct ospf6_route *route; + + route = ospf6_route_lookup(prefix, table); + if (route == NULL) + return; + + ospf6_route_lock(route); + while (route && ospf6_route_is_prefix(prefix, route)) { + /* Specifying a prefix will always display details */ + ospf6_route_show_detail(vty, route, NULL, false); + route = ospf6_route_next(route); + } + if (route) + ospf6_route_unlock(route); +} + +static void ospf6_linkstate_show_table(struct vty *vty, int detail, + struct ospf6_route_table *table) +{ + struct ospf6_route *route; + + if (!detail) + ospf6_linkstate_show_header(vty); + + route = ospf6_route_head(table); + while (route) { + if (detail) + ospf6_route_show_detail(vty, route, NULL, false); + else + ospf6_linkstate_show(vty, route); + route = ospf6_route_next(route); + } +} + +int ospf6_linkstate_table_show(struct vty *vty, int idx_ipv4, int argc, + struct cmd_token **argv, + struct ospf6_route_table *table) +{ + int detail = 0; + int is_id = 0; + int is_router = 0; + int i, ret; + struct prefix router, id, prefix; + + memset(&router, 0, sizeof(router)); + memset(&id, 0, sizeof(id)); + memset(&prefix, 0, sizeof(prefix)); + + for (i = idx_ipv4; i < argc; i++) { + if (strmatch(argv[i]->text, "detail")) { + detail++; + continue; + } + + if (!is_router) { + ret = str2prefix(argv[i]->arg, &router); + if (ret == 1 && router.family == AF_INET) { + is_router++; + continue; + } + vty_out(vty, "Malformed argument: %s\n", argv[i]->arg); + return CMD_SUCCESS; + } + + if (!is_id) { + ret = str2prefix(argv[i]->arg, &id); + if (ret == 1 && id.family == AF_INET) { + is_id++; + continue; + } + vty_out(vty, "Malformed argument: %s\n", argv[i]->arg); + return CMD_SUCCESS; + } + + vty_out(vty, "Malformed argument: %s\n", argv[i]->arg); + return CMD_SUCCESS; + } + + if (is_router) + ospf6_linkstate_prefix(router.u.prefix4.s_addr, + id.u.prefix4.s_addr, &prefix); + + if (prefix.family) + ospf6_linkstate_show_table_exact(vty, &prefix, table); + else + ospf6_linkstate_show_table(vty, detail, table); + + return CMD_SUCCESS; +} + + +void ospf6_brouter_show_header(struct vty *vty) +{ + vty_out(vty, "%-15s %-8s %-14s %-10s %-15s\n", "Router-ID", "Rtr-Bits", + "Options", "Path-Type", "Area"); +} + +void ospf6_brouter_show(struct vty *vty, struct ospf6_route *route) +{ + uint32_t adv_router; + char adv[16], rbits[16], options[32], area[16]; + + adv_router = ospf6_linkstate_prefix_adv_router(&route->prefix); + inet_ntop(AF_INET, &adv_router, adv, sizeof(adv)); + ospf6_capability_printbuf(route->path.router_bits, rbits, + sizeof(rbits)); + ospf6_options_printbuf(route->path.options, options, sizeof(options)); + inet_ntop(AF_INET, &route->path.area_id, area, sizeof(area)); + + /* vty_out (vty, "%-15s %-8s %-14s %-10s %-15s\n", + "Router-ID", "Rtr-Bits", "Options", "Path-Type", "Area"); */ + vty_out(vty, "%-15s %-8s %-14s %-10s %-15s\n", adv, rbits, options, + OSPF6_PATH_TYPE_NAME(route->path.type), area); +} + +DEFPY(debug_ospf6_route, + debug_ospf6_route_cmd, + "[no$no] debug ospf6 route <all|table|intra-area|inter-area|memory>", + NO_STR + DEBUG_STR + OSPF6_STR + "Debug routes\n" + "Debug for all types of route calculation\n" + "Debug route table calculation\n" + "Debug intra-area route calculation\n" + "Debug inter-area route calculation\n" + "Debug route memory use\n") +{ + int idx_type; + unsigned char level = 0; + + idx_type = ((no) ? 4 : 3); + + if (!strcmp(argv[idx_type]->text, "all")) + level = OSPF6_DEBUG_ROUTE_ALL; + else if (!strcmp(argv[idx_type]->text, "table")) + level = OSPF6_DEBUG_ROUTE_TABLE; + else if (!strcmp(argv[idx_type]->text, "intra-area")) + level = OSPF6_DEBUG_ROUTE_INTRA; + else if (!strcmp(argv[idx_type]->text, "inter-area")) + level = OSPF6_DEBUG_ROUTE_INTER; + else if (!strcmp(argv[idx_type]->text, "memory")) + level = OSPF6_DEBUG_ROUTE_MEMORY; + + if (no) + OSPF6_DEBUG_ROUTE_OFF(level); + else + OSPF6_DEBUG_ROUTE_ON(level); + return CMD_SUCCESS; +} + +int config_write_ospf6_debug_route(struct vty *vty) +{ + if (IS_OSPF6_DEBUG_ROUTE(ALL) == OSPF6_DEBUG_ROUTE_ALL) { + vty_out(vty, "debug ospf6 route all\n"); + return 0; + } + if (IS_OSPF6_DEBUG_ROUTE(TABLE)) + vty_out(vty, "debug ospf6 route table\n"); + if (IS_OSPF6_DEBUG_ROUTE(INTRA)) + vty_out(vty, "debug ospf6 route intra-area\n"); + if (IS_OSPF6_DEBUG_ROUTE(INTER)) + vty_out(vty, "debug ospf6 route inter-area\n"); + if (IS_OSPF6_DEBUG_ROUTE(MEMORY)) + vty_out(vty, "debug ospf6 route memory\n"); + + return 0; +} + +void install_element_ospf6_debug_route(void) +{ + install_element(ENABLE_NODE, &debug_ospf6_route_cmd); + install_element(CONFIG_NODE, &debug_ospf6_route_cmd); +} diff --git a/ospf6d/ospf6_route.h b/ospf6d/ospf6_route.h new file mode 100644 index 0000000..c212595 --- /dev/null +++ b/ospf6d/ospf6_route.h @@ -0,0 +1,392 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2003 Yasuhiro Ohara + */ + +#ifndef OSPF6_ROUTE_H +#define OSPF6_ROUTE_H + +#include "command.h" +#include "zclient.h" +#include "lib/json.h" +#include "lib/nexthop.h" + +#define OSPF6_MULTI_PATH_LIMIT 4 + +/* Debug option */ +extern unsigned char conf_debug_ospf6_route; +#define OSPF6_DEBUG_ROUTE_TABLE 0x01 +#define OSPF6_DEBUG_ROUTE_INTRA 0x02 +#define OSPF6_DEBUG_ROUTE_INTER 0x04 +#define OSPF6_DEBUG_ROUTE_MEMORY 0x08 +#define OSPF6_DEBUG_ROUTE_ALL \ + (OSPF6_DEBUG_ROUTE_TABLE | OSPF6_DEBUG_ROUTE_INTRA \ + | OSPF6_DEBUG_ROUTE_INTER | OSPF6_DEBUG_ROUTE_MEMORY) +#define OSPF6_DEBUG_ROUTE_ON(level) (conf_debug_ospf6_route |= (level)) +#define OSPF6_DEBUG_ROUTE_OFF(level) (conf_debug_ospf6_route &= ~(level)) +#define IS_OSPF6_DEBUG_ROUTE(e) (conf_debug_ospf6_route & OSPF6_DEBUG_ROUTE_##e) + +/* Nexthop */ +struct ospf6_nexthop { + /* Interface index */ + ifindex_t ifindex; + + /* IP address, if any */ + struct in6_addr address; + + /** Next-hop type information. */ + enum nexthop_types_t type; +}; + +static inline bool ospf6_nexthop_is_set(const struct ospf6_nexthop *nh) +{ + return nh->type != 0; +} + +static inline bool ospf6_nexthop_is_same(const struct ospf6_nexthop *nha, + const struct ospf6_nexthop *nhb) +{ + if (nha->type != nhb->type) + return false; + + switch (nha->type) { + case NEXTHOP_TYPE_BLACKHOLE: + /* NOTHING */ + break; + + case NEXTHOP_TYPE_IFINDEX: + if (nha->ifindex != nhb->ifindex) + return false; + break; + + case NEXTHOP_TYPE_IPV4_IFINDEX: + case NEXTHOP_TYPE_IPV4: + /* OSPFv3 does not support IPv4 next hops. */ + return false; + + case NEXTHOP_TYPE_IPV6_IFINDEX: + if (nha->ifindex != nhb->ifindex) + return false; + /* FALLTHROUGH */ + case NEXTHOP_TYPE_IPV6: + if (!IN6_ARE_ADDR_EQUAL(&nha->address, &nhb->address)) + return false; + break; + } + + return true; +} + +static inline void ospf6_nexthop_clear(struct ospf6_nexthop *nh) +{ + memset(nh, 0, sizeof(*nh)); +} + +static inline void ospf6_nexthop_copy(struct ospf6_nexthop *nha, + const struct ospf6_nexthop *nhb) +{ + memcpy(nha, nhb, sizeof(*nha)); +} + +/* Path */ +struct ospf6_ls_origin { + uint16_t type; + in_addr_t id; + in_addr_t adv_router; +}; + +struct ospf6_path { + /* Link State Origin */ + struct ospf6_ls_origin origin; + + /* Router bits */ + uint8_t router_bits; + + /* Optional Capabilities */ + uint8_t options[3]; + + /* Associated Area */ + in_addr_t area_id; + + /* Path-type */ + uint8_t type; + uint8_t subtype; /* only used for redistribute i.e ZEBRA_ROUTE_XXX */ + + /* Cost */ + uint8_t metric_type; + uint32_t cost; + + struct prefix ls_prefix; + + union { + uint32_t cost_e2; + uint32_t cost_config; + } u; + uint32_t tag; + + /* nh list for this path */ + struct list *nh_list; +}; + +#define OSPF6_PATH_TYPE_NONE 0 +#define OSPF6_PATH_TYPE_INTRA 1 +#define OSPF6_PATH_TYPE_INTER 2 +#define OSPF6_PATH_TYPE_EXTERNAL1 3 +#define OSPF6_PATH_TYPE_EXTERNAL2 4 +#define OSPF6_PATH_TYPE_MAX 5 + +#define OSPF6_PATH_SUBTYPE_DEFAULT_RT 1 + +#define OSPF6_PATH_COST_IS_CONFIGURED(path) (path.u.cost_config != OSPF_AREA_RANGE_COST_UNSPEC) + +#include "prefix.h" +#include "table.h" +#include "bitfield.h" + +struct ospf6_route { + struct route_node *rnode; + struct ospf6_route_table *table; + struct ospf6_route *prev; + struct ospf6_route *next; + + /* Back pointer to ospf6 */ + struct ospf6 *ospf6; + + unsigned int lock; + + /* Destination Type */ + uint8_t type; + + /* XXX: It would likely be better to use separate struct in_addr's + * for the advertising router-ID and prefix IDs, instead of stuffing + * them + * into one. See also XXX below. + */ + /* Destination ID */ + struct prefix prefix; + + /* Time */ + struct timeval installed; + struct timeval changed; + + /* flag */ + uint16_t flag; + + /* Prefix Options */ + uint8_t prefix_options; + + /* route option */ + void *route_option; + + /* link state id for advertising */ + uint32_t linkstate_id; + + /* path */ + struct ospf6_path path; + + /* List of Paths. */ + struct list *paths; + + /* nexthop */ + struct list *nh_list; + + /* points to the summarised route */ + struct ospf6_external_aggr_rt *aggr_route; + + /* For Aggr routes */ + bool to_be_processed; +}; + +#define OSPF6_DEST_TYPE_NONE 0 +#define OSPF6_DEST_TYPE_ROUTER 1 +#define OSPF6_DEST_TYPE_NETWORK 2 +#define OSPF6_DEST_TYPE_DISCARD 3 +#define OSPF6_DEST_TYPE_LINKSTATE 4 +#define OSPF6_DEST_TYPE_RANGE 5 +#define OSPF6_DEST_TYPE_MAX 6 + +#define OSPF6_ROUTE_CHANGE 0x0001 +#define OSPF6_ROUTE_ADD 0x0002 +#define OSPF6_ROUTE_REMOVE 0x0004 +#define OSPF6_ROUTE_BEST 0x0008 +#define OSPF6_ROUTE_ACTIVE_SUMMARY 0x0010 +#define OSPF6_ROUTE_DO_NOT_ADVERTISE 0x0020 +#define OSPF6_ROUTE_WAS_REMOVED 0x0040 +#define OSPF6_ROUTE_BLACKHOLE_ADDED 0x0080 +#define OSPF6_ROUTE_NSSA_RANGE 0x0100 +struct ospf6; + +struct ospf6_route_table { + int scope_type; + int table_type; + void *scope; + + /* patricia tree */ + struct route_table *table; + + uint32_t count; + + /* hooks */ + void (*hook_add)(struct ospf6_route *); + void (*hook_change)(struct ospf6_route *); + void (*hook_remove)(struct ospf6_route *); +}; + +#define OSPF6_SCOPE_TYPE_NONE 0 +#define OSPF6_SCOPE_TYPE_GLOBAL 1 +#define OSPF6_SCOPE_TYPE_AREA 2 +#define OSPF6_SCOPE_TYPE_INTERFACE 3 + +#define OSPF6_TABLE_TYPE_NONE 0 +#define OSPF6_TABLE_TYPE_ROUTES 1 +#define OSPF6_TABLE_TYPE_BORDER_ROUTERS 2 +#define OSPF6_TABLE_TYPE_CONNECTED_ROUTES 3 +#define OSPF6_TABLE_TYPE_EXTERNAL_ROUTES 4 +#define OSPF6_TABLE_TYPE_SPF_RESULTS 5 +#define OSPF6_TABLE_TYPE_PREFIX_RANGES 6 +#define OSPF6_TABLE_TYPE_SUMMARY_PREFIXES 7 +#define OSPF6_TABLE_TYPE_SUMMARY_ROUTERS 8 + +#define OSPF6_ROUTE_TABLE_CREATE(s, t) \ + ospf6_route_table_create(OSPF6_SCOPE_TYPE_##s, OSPF6_TABLE_TYPE_##t) + +extern const char *const ospf6_dest_type_str[OSPF6_DEST_TYPE_MAX]; +extern const char *const ospf6_dest_type_substr[OSPF6_DEST_TYPE_MAX]; +#define OSPF6_DEST_TYPE_NAME(x) \ + (0 < (x) && (x) < OSPF6_DEST_TYPE_MAX ? ospf6_dest_type_str[(x)] \ + : ospf6_dest_type_str[0]) +#define OSPF6_DEST_TYPE_SUBSTR(x) \ + (0 < (x) && (x) < OSPF6_DEST_TYPE_MAX ? ospf6_dest_type_substr[(x)] \ + : ospf6_dest_type_substr[0]) + +extern const char *const ospf6_path_type_str[OSPF6_PATH_TYPE_MAX]; +extern const char *const ospf6_path_type_substr[OSPF6_PATH_TYPE_MAX]; +#define OSPF6_PATH_TYPE_NAME(x) \ + (0 < (x) && (x) < OSPF6_PATH_TYPE_MAX ? ospf6_path_type_str[(x)] \ + : ospf6_path_type_str[0]) +#define OSPF6_PATH_TYPE_SUBSTR(x) \ + (0 < (x) && (x) < OSPF6_PATH_TYPE_MAX ? ospf6_path_type_substr[(x)] \ + : ospf6_path_type_substr[0]) +#define OSPF6_PATH_TYPE_JSON(x) \ + (0 < (x) && (x) < OSPF6_PATH_TYPE_MAX ? ospf6_path_type_json[(x)] \ + : ospf6_path_type_json[0]) + +#define OSPF6_ROUTE_ADDRESS_STR "Display the route bestmatches the address\n" +#define OSPF6_ROUTE_PREFIX_STR "Display the route\n" +#define OSPF6_ROUTE_MATCH_STR "Display the route matches the prefix\n" + +#define ospf6_route_is_prefix(p, r) (prefix_same(p, &(r)->prefix)) +#define ospf6_route_is_same(ra, rb) (prefix_same(&(ra)->prefix, &(rb)->prefix)) +#define ospf6_route_is_same_origin(ra, rb) \ + ((ra)->path.area_id == (rb)->path.area_id \ + && (ra)->path.origin.type == (rb)->path.origin.type \ + && (ra)->path.origin.id == (rb)->path.origin.id \ + && (ra)->path.origin.adv_router == (rb)->path.origin.adv_router) +#define ospf6_route_is_identical(ra, rb) \ + ((ra)->type == (rb)->type && \ + prefix_same(&(ra)->prefix, &(rb)->prefix) && \ + (ra)->path.type == (rb)->path.type && \ + (ra)->path.cost == (rb)->path.cost && \ + (ra)->path.u.cost_e2 == (rb)->path.u.cost_e2 && \ + listcount(ra->paths) == listcount(rb->paths) && \ + ospf6_route_cmp_nexthops(ra, rb)) + +#define ospf6_route_is_best(r) (CHECK_FLAG ((r)->flag, OSPF6_ROUTE_BEST)) + +#define ospf6_linkstate_prefix_adv_router(x) ((x)->u.lp.id.s_addr) +#define ospf6_linkstate_prefix_id(x) ((x)->u.lp.adv_router.s_addr) + +#define ADV_ROUTER_IN_PREFIX(x) ((x)->u.lp.id.s_addr) + +/* Function prototype */ +extern void ospf6_linkstate_prefix(uint32_t adv_router, uint32_t id, + struct prefix *prefix); +extern void ospf6_linkstate_prefix2str(struct prefix *prefix, char *buf, + int size); + +extern struct ospf6_nexthop *ospf6_nexthop_create(void); +extern int ospf6_nexthop_cmp(struct ospf6_nexthop *a, struct ospf6_nexthop *b); +extern void ospf6_nexthop_delete(struct ospf6_nexthop *nh); +extern void ospf6_clear_nexthops(struct list *nh_list); +extern int ospf6_num_nexthops(struct list *nh_list); +extern void ospf6_copy_nexthops(struct list *dst, struct list *src); +extern void ospf6_merge_nexthops(struct list *dst, struct list *src); +extern void ospf6_add_nexthop(struct list *nh_list, int ifindex, + const struct in6_addr *addr); +extern void ospf6_add_route_nexthop_blackhole(struct ospf6_route *route); +extern int ospf6_num_nexthops(struct list *nh_list); +extern bool ospf6_route_cmp_nexthops(struct ospf6_route *a, + struct ospf6_route *b); +extern void ospf6_route_zebra_copy_nexthops(struct ospf6_route *route, + struct zapi_nexthop nexthops[], + int entries, vrf_id_t vrf_id); +extern int ospf6_route_get_first_nh_index(struct ospf6_route *route); + +/* Hide abstraction of nexthop implementation in route from outsiders */ +#define ospf6_route_copy_nexthops(dst, src) ospf6_copy_nexthops(dst->nh_list, src->nh_list) +#define ospf6_route_merge_nexthops(dst, src) ospf6_merge_nexthops(dst->nh_list, src->nh_list) +#define ospf6_route_num_nexthops(route) ospf6_num_nexthops(route->nh_list) +#define ospf6_route_add_nexthop(route, ifindex, addr) \ + ospf6_add_nexthop(route->nh_list, ifindex, addr) + +extern struct ospf6_route *ospf6_route_create(struct ospf6 *ospf6); +extern void ospf6_route_delete(struct ospf6_route *route); +extern struct ospf6_route *ospf6_route_copy(struct ospf6_route *route); +extern int ospf6_route_cmp(struct ospf6_route *ra, struct ospf6_route *rb); + +extern void ospf6_route_lock(struct ospf6_route *route); +extern void ospf6_route_unlock(struct ospf6_route *route); +extern struct ospf6_route *ospf6_route_lookup(struct prefix *prefix, + struct ospf6_route_table *table); +extern struct ospf6_route * +ospf6_route_lookup_identical(struct ospf6_route *route, + struct ospf6_route_table *table); +extern struct ospf6_route * +ospf6_route_lookup_bestmatch(struct prefix *prefix, + struct ospf6_route_table *table); + +extern struct ospf6_route *ospf6_route_add(struct ospf6_route *route, + struct ospf6_route_table *table); +extern void ospf6_route_remove(struct ospf6_route *route, + struct ospf6_route_table *table); + +extern struct ospf6_route *ospf6_route_head(struct ospf6_route_table *table); +extern struct ospf6_route *ospf6_route_next(struct ospf6_route *route); +extern struct ospf6_route *ospf6_route_best_next(struct ospf6_route *route); + +extern struct ospf6_route * +ospf6_route_match_head(struct prefix *prefix, struct ospf6_route_table *table); +extern struct ospf6_route *ospf6_route_match_next(struct prefix *prefix, + struct ospf6_route *route); + +extern void ospf6_route_remove_all(struct ospf6_route_table *table); +extern struct ospf6_route_table *ospf6_route_table_create(int s, int t); +extern void ospf6_route_table_delete(struct ospf6_route_table *table); +extern void ospf6_route_dump(struct ospf6_route_table *table); + + +extern void ospf6_route_show(struct vty *vty, struct ospf6_route *route, + json_object *json, bool use_json); +extern void ospf6_route_show_detail(struct vty *vty, struct ospf6_route *route, + json_object *json, bool use_json); + + +extern int ospf6_route_table_show(struct vty *vty, int argc_start, int argc, + struct cmd_token **argv, + struct ospf6_route_table *table, + bool use_json); +extern int ospf6_linkstate_table_show(struct vty *vty, int idx_ipv4, int argc, + struct cmd_token **argv, + struct ospf6_route_table *table); + +extern void ospf6_brouter_show_header(struct vty *vty); +extern void ospf6_brouter_show(struct vty *vty, struct ospf6_route *route); + +extern int config_write_ospf6_debug_route(struct vty *vty); +extern void install_element_ospf6_debug_route(void); +extern void ospf6_route_init(void); +extern void ospf6_path_free(struct ospf6_path *op); +extern struct ospf6_path *ospf6_path_dup(struct ospf6_path *path); +extern void ospf6_copy_paths(struct list *dst, struct list *src); + +#endif /* OSPF6_ROUTE_H */ diff --git a/ospf6d/ospf6_routemap_nb.c b/ospf6d/ospf6_routemap_nb.c new file mode 100644 index 0000000..4ccec4f --- /dev/null +++ b/ospf6d/ospf6_routemap_nb.c @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2020 Vmware + * Sarita Patra + */ + +#include <zebra.h> + +#include "lib/northbound.h" +#include "lib/routemap.h" +#include "ospf6_routemap_nb.h" + +/* clang-format off */ +const struct frr_yang_module_info frr_ospf_route_map_info = { + .name = "frr-ospf-route-map", + .nodes = { + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-ospf-route-map:metric-type", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_metric_type_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_metric_type_destroy, + } + }, + { + .xpath = NULL, + }, + } +}; + +const struct frr_yang_module_info frr_ospf6_route_map_info = { + .name = "frr-ospf6-route-map", + .nodes = { + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-ospf6-route-map:ipv6-address", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_ipv6_address_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_ipv6_address_destroy, + } + }, + { + .xpath = NULL, + }, + } +}; diff --git a/ospf6d/ospf6_routemap_nb.h b/ospf6d/ospf6_routemap_nb.h new file mode 100644 index 0000000..74c1827 --- /dev/null +++ b/ospf6d/ospf6_routemap_nb.h @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2020 Vmware + * Sarita Patra + */ + +#ifndef _FRR_OSPF6_ROUTEMAP_NB_H_ +#define _FRR_OSPF6_ROUTEMAP_NB_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +extern const struct frr_yang_module_info frr_ospf_route_map_info; +extern const struct frr_yang_module_info frr_ospf6_route_map_info; + +/* prototypes */ +int lib_route_map_entry_set_action_rmap_set_action_ipv6_address_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_ipv6_address_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_metric_type_modify(struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_metric_type_destroy(struct nb_cb_destroy_args *args); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/ospf6d/ospf6_routemap_nb_config.c b/ospf6d/ospf6_routemap_nb_config.c new file mode 100644 index 0000000..ad2d571 --- /dev/null +++ b/ospf6d/ospf6_routemap_nb_config.c @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2020 Vmware + * Sarita Patra + */ + +#include <zebra.h> + +#include "lib/command.h" +#include "lib/log.h" +#include "lib/northbound.h" +#include "lib/routemap.h" +#include "ospf6_routemap_nb.h" + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-ospf-route-map:metric-type + */ +int lib_route_map_entry_set_action_rmap_set_action_metric_type_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *type; + int rv; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = generic_set_delete; + rhc->rhc_rule = "metric-type"; + rhc->rhc_event = RMAP_EVENT_SET_DELETED; + + rv = generic_set_add(rhc->rhc_rmi, "metric-type", type, + args->errmsg, args->errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int lib_route_map_entry_set_action_rmap_set_action_metric_type_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_set_destroy(args); + } + + return NB_OK; +} + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-ospf6-route-map:ipv6-address + */ +int lib_route_map_entry_set_action_rmap_set_action_ipv6_address_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *ipv6_addr; + int rv; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + ipv6_addr = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = generic_set_delete; + rhc->rhc_rule = "forwarding-address"; + rhc->rhc_event = RMAP_EVENT_SET_DELETED; + + rv = generic_set_add(rhc->rhc_rmi, "forwarding-address", + ipv6_addr, + args->errmsg, args->errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int lib_route_map_entry_set_action_rmap_set_action_ipv6_address_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_set_destroy(args); + } + + return NB_OK; +} diff --git a/ospf6d/ospf6_snmp.c b/ospf6d/ospf6_snmp.c new file mode 100644 index 0000000..f88667b --- /dev/null +++ b/ospf6d/ospf6_snmp.c @@ -0,0 +1,1404 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* OSPFv3 SNMP support + * Copyright (C) 2004 Yasuhiro Ohara + */ + +#include <zebra.h> + +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-includes.h> + +#include "log.h" +#include "vty.h" +#include "linklist.h" +#include "vector.h" +#include "vrf.h" +#include "smux.h" +#include "libfrr.h" +#include "lib/version.h" + +#include "ospf6_proto.h" +#include "ospf6_lsa.h" +#include "ospf6_lsdb.h" +#include "ospf6_route.h" +#include "ospf6_top.h" +#include "ospf6_area.h" +#include "ospf6_interface.h" +#include "ospf6_message.h" +#include "ospf6_neighbor.h" +#include "ospf6_abr.h" +#include "ospf6_asbr.h" +#include "ospf6d.h" + +/* OSPFv3-MIB */ +#define OSPFv3MIB 1,3,6,1,2,1,191 + +/* OSPFv3 MIB General Group values. */ +#define OSPFv3ROUTERID 1 +#define OSPFv3ADMINSTAT 2 +#define OSPFv3VERSIONNUMBER 3 +#define OSPFv3AREABDRRTRSTATUS 4 +#define OSPFv3ASBDRRTRSTATUS 5 +#define OSPFv3ASSCOPELSACOUNT 6 +#define OSPFv3ASSCOPELSACHECKSUMSUM 7 +#define OSPFv3ORIGINATENEWLSAS 8 +#define OSPFv3RXNEWLSAS 9 +#define OSPFv3EXTLSACOUNT 10 +#define OSPFv3EXTAREALSDBLIMIT 11 +#define OSPFv3EXITOVERFLOWINTERVAL 12 +#define OSPFv3DEMANDEXTENSIONS 13 +#define OSPFv3REFERENCEBANDWIDTH 14 +#define OSPFv3RESTARTSUPPORT 15 +#define OSPFv3RESTARTINTERVAL 16 +#define OSPFv3RESTARTSTRICTLSACHECKING 17 +#define OSPFv3RESTARTSTATUS 18 +#define OSPFv3RESTARTAGE 19 +#define OSPFv3RESTARTEXITREASON 20 +#define OSPFv3NOTIFICATIONENABLE 21 +#define OSPFv3STUBROUTERSUPPORT 22 +#define OSPFv3STUBROUTERADVERTISEMENT 23 +#define OSPFv3DISCONTINUITYTIME 24 +#define OSPFv3RESTARTTIME 25 + +/* OSPFv3 MIB Area Table values: ospfv3AreaTable */ +#define OSPFv3IMPORTASEXTERN 2 +#define OSPFv3AREASPFRUNS 3 +#define OSPFv3AREABDRRTRCOUNT 4 +#define OSPFv3AREAASBDRRTRCOUNT 5 +#define OSPFv3AREASCOPELSACOUNT 6 +#define OSPFv3AREASCOPELSACKSUMSUM 7 +#define OSPFv3AREASUMMARY 8 +#define OSPFv3AREAROWSTATUS 9 +#define OSPFv3AREASTUBMETRIC 10 +#define OSPFv3AREANSSATRANSLATORROLE 11 +#define OSPFv3AREANSSATRANSLATORSTATE 12 +#define OSPFv3AREANSSATRANSLATORSTABINTERVAL 13 +#define OSPFv3AREANSSATRANSLATOREVENTS 14 +#define OSPFv3AREASTUBMETRICTYPE 15 +#define OSPFv3AREATEENABLED 16 + +/* OSPFv3 MIB * Lsdb Table values: ospfv3*LsdbTable */ +#define OSPFv3WWLSDBSEQUENCE 1 +#define OSPFv3WWLSDBAGE 2 +#define OSPFv3WWLSDBCHECKSUM 3 +#define OSPFv3WWLSDBADVERTISEMENT 4 +#define OSPFv3WWLSDBTYPEKNOWN 5 + +/* Three first bits are to identify column */ +#define OSPFv3WWCOLUMN 0x7 +/* Then we use other bits to identify table */ +#define OSPFv3WWASTABLE (1 << 3) +#define OSPFv3WWAREATABLE (1 << 4) +#define OSPFv3WWLINKTABLE (1 << 5) +#define OSPFv3WWVIRTLINKTABLE (1 << 6) + +/* OSPFv3 MIB Host Table values: ospfv3HostTable */ +#define OSPFv3HOSTMETRIC 3 +#define OSPFv3HOSTROWSTATUS 4 +#define OSPFv3HOSTAREAID 5 + +/* OSPFv3 MIB Interface Table values: ospfv3IfTable */ +#define OSPFv3IFAREAID 3 +#define OSPFv3IFTYPE 4 +#define OSPFv3IFADMINSTATUS 5 +#define OSPFv3IFRTRPRIORITY 6 +#define OSPFv3IFTRANSITDELAY 7 +#define OSPFv3IFRETRANSINTERVAL 8 +#define OSPFv3IFHELLOINTERVAL 9 +#define OSPFv3IFRTRDEADINTERVAL 10 +#define OSPFv3IFPOLLINTERVAL 11 +#define OSPFv3IFSTATE 12 +#define OSPFv3IFDESIGNATEDROUTER 13 +#define OSPFv3IFBACKUPDESIGNATEDROUTER 14 +#define OSPFv3IFEVENTS 15 +#define OSPFv3IFROWSTATUS 16 +#define OSPFv3IFDEMAND 17 +#define OSPFv3IFMETRICVALUE 18 +#define OSPFv3IFLINKSCOPELSACOUNT 19 +#define OSPFv3IFLINKLSACKSUMSUM 20 +#define OSPFv3IFDEMANDNBRPROBE 21 +#define OSPFv3IFDEMANDNBRPROBERETRANSLIMIT 22 +#define OSPFv3IFDEMANDNBRPROBEINTERVAL 23 +#define OSPFv3IFTEDISABLED 24 +#define OSPFv3IFLINKLSASUPPRESSION 25 + +/* OSPFv3 MIB Virtual Interface Table values: ospfv3VirtIfTable */ +#define OSPFv3VIRTIFINDEX 3 +#define OSPFv3VIRTIFINSTID 4 +#define OSPFv3VIRTIFTRANSITDELAY 5 +#define OSPFv3VIRTIFRETRANSINTERVAL 6 +#define OSPFv3VIRTIFHELLOINTERVAL 7 +#define OSPFv3VIRTIFRTRDEADINTERVAL 8 +#define OSPFv3VIRTIFSTATE 9 +#define OSPFv3VIRTIFEVENTS 10 +#define OSPFv3VIRTIFROWSTATUS 11 +#define OSPFv3VIRTIFLINKSCOPELSACOUNT 12 +#define OSPFv3VIRTIFLINKLSACKSUMSUM 13 + +/* OSPFv3 MIB Neighbors Table values: ospfv3NbrTable */ +#define OSPFv3NBRADDRESSTYPE 4 +#define OSPFv3NBRADDRESS 5 +#define OSPFv3NBROPTIONS 6 +#define OSPFv3NBRPRIORITY 7 +#define OSPFv3NBRSTATE 8 +#define OSPFv3NBREVENTS 9 +#define OSPFv3NBRLSRETRANSQLEN 10 +#define OSPFv3NBRHELLOSUPPRESSED 11 +#define OSPFv3NBRIFID 12 +#define OSPFv3NBRRESTARTHELPERSTATUS 13 +#define OSPFv3NBRRESTARTHELPERAGE 14 +#define OSPFv3NBRRESTARTHELPEREXITREASON 15 + +/* OSPFv3 MIB Configured Neighbors Table values: ospfv3CfgNbrTable */ +#define OSPFv3CFGNBRPRIORITY 5 +#define OSPFv3CFGNBRROWSTATUS 6 + +/* OSPFv3 MIB Virtual Neighbors Table values: ospfv3VirtNbrTable */ +#define OSPFv3VIRTNBRIFINDEX 3 +#define OSPFv3VIRTNBRIFINSTID 4 +#define OSPFv3VIRTNBRADDRESSTYPE 5 +#define OSPFv3VIRTNBRADDRESS 6 +#define OSPFv3VIRTNBROPTIONS 7 +#define OSPFv3VIRTNBRSTATE 8 +#define OSPFv3VIRTNBREVENTS 9 +#define OSPFv3VIRTNBRLSRETRANSQLEN 10 +#define OSPFv3VIRTNBRHELLOSUPPRESSED 11 +#define OSPFv3VIRTNBRIFID 12 +#define OSPFv3VIRTNBRRESTARTHELPERSTATUS 13 +#define OSPFv3VIRTNBRRESTARTHELPERAGE 14 +#define OSPFv3VIRTNBRRESTARTHELPEREXITREASON 15 + +/* OSPFv3 MIB Area Aggregate Table values: ospfv3AreaAggregateTable */ +#define OSPFv3AREAAGGREGATEROWSTATUS 6 +#define OSPFv3AREAAGGREGATEEFFECT 7 +#define OSPFv3AREAAGGREGATEROUTETAG 8 + +/* SYNTAX Status from OSPF-MIB. */ +#define OSPF_STATUS_ENABLED 1 +#define OSPF_STATUS_DISABLED 2 + +/* SNMP value hack. */ +#define COUNTER ASN_COUNTER +#define INTEGER ASN_INTEGER +#define GAUGE ASN_GAUGE +#define UNSIGNED ASN_UNSIGNED +#define TIMETICKS ASN_TIMETICKS +#define IPADDRESS ASN_IPADDRESS +#define STRING ASN_OCTET_STR + +/* For return values e.g. SNMP_INTEGER macro */ +SNMP_LOCAL_VARIABLES + +/* OSPFv3-MIB instances. */ +static oid ospfv3_oid[] = {OSPFv3MIB}; +static oid ospfv3_trap_oid[] = {OSPFv3MIB, 0}; + +/* Hook functions. */ +static uint8_t *ospfv3GeneralGroup(struct variable *, oid *, size_t *, int, + size_t *, WriteMethod **); +static uint8_t *ospfv3AreaEntry(struct variable *, oid *, size_t *, int, + size_t *, WriteMethod **); +static uint8_t *ospfv3WwLsdbEntry(struct variable *, oid *, size_t *, int, + size_t *, WriteMethod **); +static uint8_t *ospfv3NbrEntry(struct variable *, oid *, size_t *, int, + size_t *, WriteMethod **); +static uint8_t *ospfv3IfEntry(struct variable *, oid *, size_t *, int, size_t *, + WriteMethod **); + +static struct variable ospfv3_variables[] = { + /* OSPF general variables */ + {OSPFv3ROUTERID, UNSIGNED, RWRITE, ospfv3GeneralGroup, 3, {1, 1, 1}}, + {OSPFv3ADMINSTAT, INTEGER, RWRITE, ospfv3GeneralGroup, 3, {1, 1, 2}}, + {OSPFv3VERSIONNUMBER, INTEGER, RONLY, ospfv3GeneralGroup, 3, {1, 1, 3}}, + {OSPFv3AREABDRRTRSTATUS, + INTEGER, + RONLY, + ospfv3GeneralGroup, + 3, + {1, 1, 4}}, + {OSPFv3ASBDRRTRSTATUS, + INTEGER, + RWRITE, + ospfv3GeneralGroup, + 3, + {1, 1, 5}}, + {OSPFv3ASSCOPELSACOUNT, GAUGE, RONLY, ospfv3GeneralGroup, 3, {1, 1, 6}}, + {OSPFv3ASSCOPELSACHECKSUMSUM, + UNSIGNED, + RONLY, + ospfv3GeneralGroup, + 3, + {1, 1, 7}}, + {OSPFv3ORIGINATENEWLSAS, + COUNTER, + RONLY, + ospfv3GeneralGroup, + 3, + {1, 1, 8}}, + {OSPFv3RXNEWLSAS, COUNTER, RONLY, ospfv3GeneralGroup, 3, {1, 1, 9}}, + {OSPFv3EXTLSACOUNT, GAUGE, RONLY, ospfv3GeneralGroup, 3, {1, 1, 10}}, + {OSPFv3EXTAREALSDBLIMIT, + INTEGER, + RWRITE, + ospfv3GeneralGroup, + 3, + {1, 1, 11}}, + {OSPFv3EXITOVERFLOWINTERVAL, + UNSIGNED, + RWRITE, + ospfv3GeneralGroup, + 3, + {1, 1, 12}}, + {OSPFv3DEMANDEXTENSIONS, + INTEGER, + RWRITE, + ospfv3GeneralGroup, + 3, + {1, 1, 13}}, + {OSPFv3REFERENCEBANDWIDTH, + UNSIGNED, + RWRITE, + ospfv3GeneralGroup, + 3, + {1, 1, 14}}, + {OSPFv3RESTARTSUPPORT, + INTEGER, + RWRITE, + ospfv3GeneralGroup, + 3, + {1, 1, 15}}, + {OSPFv3RESTARTINTERVAL, + UNSIGNED, + RWRITE, + ospfv3GeneralGroup, + 3, + {1, 1, 16}}, + {OSPFv3RESTARTSTRICTLSACHECKING, + INTEGER, + RWRITE, + ospfv3GeneralGroup, + 3, + {1, 1, 17}}, + {OSPFv3RESTARTSTATUS, + INTEGER, + RONLY, + ospfv3GeneralGroup, + 3, + {1, 1, 18}}, + {OSPFv3RESTARTAGE, UNSIGNED, RONLY, ospfv3GeneralGroup, 3, {1, 1, 19}}, + {OSPFv3RESTARTEXITREASON, + INTEGER, + RONLY, + ospfv3GeneralGroup, + 3, + {1, 1, 20}}, + {OSPFv3NOTIFICATIONENABLE, + INTEGER, + RWRITE, + ospfv3GeneralGroup, + 3, + {1, 1, 21}}, + {OSPFv3STUBROUTERSUPPORT, + INTEGER, + RONLY, + ospfv3GeneralGroup, + 3, + {1, 1, 22}}, + {OSPFv3STUBROUTERADVERTISEMENT, + INTEGER, + RWRITE, + ospfv3GeneralGroup, + 3, + {1, 1, 23}}, + {OSPFv3DISCONTINUITYTIME, + TIMETICKS, + RONLY, + ospfv3GeneralGroup, + 3, + {1, 1, 24}}, + {OSPFv3RESTARTTIME, + TIMETICKS, + RONLY, + ospfv3GeneralGroup, + 3, + {1, 1, 25}}, + + /* OSPFv3 Area Data Structure */ + {OSPFv3IMPORTASEXTERN, + INTEGER, + RWRITE, + ospfv3AreaEntry, + 4, + {1, 2, 1, 2}}, + {OSPFv3AREASPFRUNS, COUNTER, RONLY, ospfv3AreaEntry, 4, {1, 2, 1, 3}}, + {OSPFv3AREABDRRTRCOUNT, GAUGE, RONLY, ospfv3AreaEntry, 4, {1, 2, 1, 4}}, + {OSPFv3AREAASBDRRTRCOUNT, + GAUGE, + RONLY, + ospfv3AreaEntry, + 4, + {1, 2, 1, 5}}, + {OSPFv3AREASCOPELSACOUNT, + GAUGE, + RONLY, + ospfv3AreaEntry, + 4, + {1, 2, 1, 6}}, + {OSPFv3AREASCOPELSACKSUMSUM, + UNSIGNED, + RONLY, + ospfv3AreaEntry, + 4, + {1, 2, 1, 7}}, + {OSPFv3AREASUMMARY, INTEGER, RWRITE, ospfv3AreaEntry, 4, {1, 2, 1, 8}}, + {OSPFv3AREAROWSTATUS, + INTEGER, + RWRITE, + ospfv3AreaEntry, + 4, + {1, 2, 1, 9}}, + {OSPFv3AREASTUBMETRIC, + INTEGER, + RWRITE, + ospfv3AreaEntry, + 4, + {1, 2, 1, 10}}, + {OSPFv3AREANSSATRANSLATORROLE, + INTEGER, + RWRITE, + ospfv3AreaEntry, + 4, + {1, 2, 1, 11}}, + {OSPFv3AREANSSATRANSLATORSTATE, + INTEGER, + RONLY, + ospfv3AreaEntry, + 4, + {1, 2, 1, 12}}, + {OSPFv3AREANSSATRANSLATORSTABINTERVAL, + UNSIGNED, + RWRITE, + ospfv3AreaEntry, + 4, + {1, 2, 1, 13}}, + {OSPFv3AREANSSATRANSLATOREVENTS, + COUNTER, + RONLY, + ospfv3AreaEntry, + 4, + {1, 2, 1, 14}}, + {OSPFv3AREASTUBMETRICTYPE, + INTEGER, + RWRITE, + ospfv3AreaEntry, + 4, + {1, 2, 1, 15}}, + {OSPFv3AREATEENABLED, + INTEGER, + RWRITE, + ospfv3AreaEntry, + 4, + {1, 2, 1, 16}}, + + /* OSPFv3 AS LSDB */ + {OSPFv3WWLSDBSEQUENCE | OSPFv3WWASTABLE, + INTEGER, + RONLY, + ospfv3WwLsdbEntry, + 4, + {1, 3, 1, 4}}, + {OSPFv3WWLSDBAGE | OSPFv3WWASTABLE, + UNSIGNED, + RONLY, + ospfv3WwLsdbEntry, + 4, + {1, 3, 1, 5}}, + {OSPFv3WWLSDBCHECKSUM | OSPFv3WWASTABLE, + INTEGER, + RONLY, + ospfv3WwLsdbEntry, + 4, + {1, 3, 1, 6}}, + {OSPFv3WWLSDBADVERTISEMENT | OSPFv3WWASTABLE, + STRING, + RONLY, + ospfv3WwLsdbEntry, + 4, + {1, 3, 1, 7}}, + {OSPFv3WWLSDBTYPEKNOWN | OSPFv3WWASTABLE, + INTEGER, + RONLY, + ospfv3WwLsdbEntry, + 4, + {1, 3, 1, 8}}, + + /* OSPFv3 Area LSDB */ + {OSPFv3WWLSDBSEQUENCE | OSPFv3WWAREATABLE, + INTEGER, + RONLY, + ospfv3WwLsdbEntry, + 4, + {1, 4, 1, 5}}, + {OSPFv3WWLSDBAGE | OSPFv3WWAREATABLE, + UNSIGNED, + RONLY, + ospfv3WwLsdbEntry, + 4, + {1, 4, 1, 6}}, + {OSPFv3WWLSDBCHECKSUM | OSPFv3WWAREATABLE, + INTEGER, + RONLY, + ospfv3WwLsdbEntry, + 4, + {1, 4, 1, 7}}, + {OSPFv3WWLSDBADVERTISEMENT | OSPFv3WWAREATABLE, + STRING, + RONLY, + ospfv3WwLsdbEntry, + 4, + {1, 4, 1, 8}}, + {OSPFv3WWLSDBTYPEKNOWN | OSPFv3WWAREATABLE, + INTEGER, + RONLY, + ospfv3WwLsdbEntry, + 4, + {1, 4, 1, 9}}, + + /* OSPFv3 Link LSDB */ + {OSPFv3WWLSDBSEQUENCE | OSPFv3WWLINKTABLE, + INTEGER, + RONLY, + ospfv3WwLsdbEntry, + 4, + {1, 5, 1, 6}}, + {OSPFv3WWLSDBAGE | OSPFv3WWLINKTABLE, + UNSIGNED, + RONLY, + ospfv3WwLsdbEntry, + 4, + {1, 5, 1, 7}}, + {OSPFv3WWLSDBCHECKSUM | OSPFv3WWLINKTABLE, + INTEGER, + RONLY, + ospfv3WwLsdbEntry, + 4, + {1, 5, 1, 8}}, + {OSPFv3WWLSDBADVERTISEMENT | OSPFv3WWLINKTABLE, + STRING, + RONLY, + ospfv3WwLsdbEntry, + 4, + {1, 5, 1, 9}}, + {OSPFv3WWLSDBTYPEKNOWN | OSPFv3WWLINKTABLE, + INTEGER, + RONLY, + ospfv3WwLsdbEntry, + 4, + {1, 5, 1, 10}}, + + /* OSPFv3 interfaces */ + {OSPFv3IFAREAID, UNSIGNED, RONLY, ospfv3IfEntry, 4, {1, 7, 1, 3}}, + {OSPFv3IFTYPE, INTEGER, RONLY, ospfv3IfEntry, 4, {1, 7, 1, 4}}, + {OSPFv3IFADMINSTATUS, INTEGER, RONLY, ospfv3IfEntry, 4, {1, 7, 1, 5}}, + {OSPFv3IFRTRPRIORITY, INTEGER, RONLY, ospfv3IfEntry, 4, {1, 7, 1, 6}}, + {OSPFv3IFTRANSITDELAY, UNSIGNED, RONLY, ospfv3IfEntry, 4, {1, 7, 1, 7}}, + {OSPFv3IFRETRANSINTERVAL, + UNSIGNED, + RONLY, + ospfv3IfEntry, + 4, + {1, 7, 1, 8}}, + {OSPFv3IFHELLOINTERVAL, INTEGER, RONLY, ospfv3IfEntry, 4, {1, 7, 1, 9}}, + {OSPFv3IFRTRDEADINTERVAL, + UNSIGNED, + RONLY, + ospfv3IfEntry, + 4, + {1, 7, 1, 10}}, + {OSPFv3IFPOLLINTERVAL, + UNSIGNED, + RONLY, + ospfv3IfEntry, + 4, + {1, 7, 1, 11}}, + {OSPFv3IFSTATE, INTEGER, RONLY, ospfv3IfEntry, 4, {1, 7, 1, 12}}, + {OSPFv3IFDESIGNATEDROUTER, + UNSIGNED, + RONLY, + ospfv3IfEntry, + 4, + {1, 7, 1, 13}}, + {OSPFv3IFBACKUPDESIGNATEDROUTER, + UNSIGNED, + RONLY, + ospfv3IfEntry, + 4, + {1, 7, 1, 14}}, + {OSPFv3IFEVENTS, COUNTER, RONLY, ospfv3IfEntry, 4, {1, 7, 1, 15}}, + {OSPFv3IFROWSTATUS, INTEGER, RONLY, ospfv3IfEntry, 4, {1, 7, 1, 16}}, + {OSPFv3IFDEMAND, INTEGER, RONLY, ospfv3IfEntry, 4, {1, 7, 1, 17}}, + {OSPFv3IFMETRICVALUE, INTEGER, RONLY, ospfv3IfEntry, 4, {1, 7, 1, 18}}, + {OSPFv3IFLINKSCOPELSACOUNT, + GAUGE, + RONLY, + ospfv3IfEntry, + 4, + {1, 7, 1, 19}}, + {OSPFv3IFLINKLSACKSUMSUM, + UNSIGNED, + RONLY, + ospfv3IfEntry, + 4, + {1, 7, 1, 20}}, + {OSPFv3IFDEMANDNBRPROBE, + INTEGER, + RONLY, + ospfv3IfEntry, + 4, + {1, 7, 1, 21}}, + {OSPFv3IFDEMANDNBRPROBERETRANSLIMIT, + UNSIGNED, + RONLY, + ospfv3IfEntry, + 4, + {1, 7, 1, 22}}, + {OSPFv3IFDEMANDNBRPROBEINTERVAL, + UNSIGNED, + RONLY, + ospfv3IfEntry, + 4, + {1, 7, 1, 23}}, + {OSPFv3IFTEDISABLED, INTEGER, RONLY, ospfv3IfEntry, 4, {1, 7, 1, 24}}, + {OSPFv3IFLINKLSASUPPRESSION, + INTEGER, + RONLY, + ospfv3IfEntry, + 4, + {1, 7, 1, 25}}, + + /* OSPFv3 neighbors */ + {OSPFv3NBRADDRESSTYPE, INTEGER, RONLY, ospfv3NbrEntry, 4, {1, 9, 1, 4}}, + {OSPFv3NBRADDRESS, STRING, RONLY, ospfv3NbrEntry, 4, {1, 9, 1, 5}}, + {OSPFv3NBROPTIONS, INTEGER, RONLY, ospfv3NbrEntry, 4, {1, 9, 1, 6}}, + {OSPFv3NBRPRIORITY, INTEGER, RONLY, ospfv3NbrEntry, 4, {1, 9, 1, 7}}, + {OSPFv3NBRSTATE, INTEGER, RONLY, ospfv3NbrEntry, 4, {1, 9, 1, 8}}, + {OSPFv3NBREVENTS, COUNTER, RONLY, ospfv3NbrEntry, 4, {1, 9, 1, 9}}, + {OSPFv3NBRLSRETRANSQLEN, + GAUGE, + RONLY, + ospfv3NbrEntry, + 4, + {1, 9, 1, 10}}, + {OSPFv3NBRHELLOSUPPRESSED, + INTEGER, + RONLY, + ospfv3NbrEntry, + 4, + {1, 9, 1, 11}}, + {OSPFv3NBRIFID, INTEGER, RONLY, ospfv3NbrEntry, 4, {1, 9, 1, 12}}, + {OSPFv3NBRRESTARTHELPERSTATUS, + INTEGER, + RONLY, + ospfv3NbrEntry, + 4, + {1, 9, 1, 13}}, + {OSPFv3NBRRESTARTHELPERAGE, + UNSIGNED, + RONLY, + ospfv3NbrEntry, + 4, + {1, 9, 1, 14}}, + {OSPFv3NBRRESTARTHELPEREXITREASON, + INTEGER, + RONLY, + ospfv3NbrEntry, + 4, + {1, 9, 1, 15}}, +}; + +static uint8_t *ospfv3GeneralGroup(struct variable *v, oid *name, + size_t *length, int exact, size_t *var_len, + WriteMethod **write_method) +{ + uint16_t sum; + uint32_t count; + struct ospf6_lsa *lsa = NULL, *lsanext; + struct ospf6 *ospf6; + + ospf6 = ospf6_lookup_by_vrf_id(VRF_DEFAULT); + /* Check whether the instance identifier is valid */ + if (smux_header_generic(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + + /* Return the current value of the variable */ + switch (v->magic) { + case OSPFv3ROUTERID: + /* Router-ID of this OSPF instance. */ + if (ospf6) + return SNMP_INTEGER(ntohl(ospf6->router_id)); + return SNMP_INTEGER(0); + case OSPFv3ADMINSTAT: + if (ospf6) + return SNMP_INTEGER( + CHECK_FLAG(ospf6->flag, OSPF6_DISABLED) + ? OSPF_STATUS_DISABLED + : OSPF_STATUS_ENABLED); + return SNMP_INTEGER(OSPF_STATUS_DISABLED); + case OSPFv3VERSIONNUMBER: + return SNMP_INTEGER(3); + case OSPFv3AREABDRRTRSTATUS: + if (ospf6) + return SNMP_INTEGER( + ospf6_check_and_set_router_abr(ospf6) + ? SNMP_TRUE + : SNMP_FALSE); + return SNMP_INTEGER(SNMP_FALSE); + case OSPFv3ASBDRRTRSTATUS: + if (ospf6) + return SNMP_INTEGER(ospf6_asbr_is_asbr(ospf6) + ? SNMP_TRUE + : SNMP_FALSE); + return SNMP_INTEGER(SNMP_FALSE); + case OSPFv3ASSCOPELSACOUNT: + if (ospf6) + return SNMP_INTEGER(ospf6->lsdb->count); + return SNMP_INTEGER(0); + case OSPFv3ASSCOPELSACHECKSUMSUM: + if (ospf6) { + sum = 0; + for (ALL_LSDB(ospf6->lsdb, lsa, lsanext)) + sum += ntohs(lsa->header->checksum); + return SNMP_INTEGER(sum); + } + return SNMP_INTEGER(0); + case OSPFv3ORIGINATENEWLSAS: + return SNMP_INTEGER( + 0); /* Don't know where to get this value... */ + case OSPFv3RXNEWLSAS: + return SNMP_INTEGER( + 0); /* Don't know where to get this value... */ + case OSPFv3EXTLSACOUNT: + if (ospf6) { + count = 0; + for (ALL_LSDB_TYPED(ospf6->lsdb, + htons(OSPF6_LSTYPE_AS_EXTERNAL), + lsa)) + count += 1; + return SNMP_INTEGER(count); + } + return SNMP_INTEGER(0); + case OSPFv3EXTAREALSDBLIMIT: + return SNMP_INTEGER(-1); + case OSPFv3EXITOVERFLOWINTERVAL: + return SNMP_INTEGER(0); /* Not supported */ + case OSPFv3DEMANDEXTENSIONS: + return SNMP_INTEGER(0); /* Not supported */ + case OSPFv3REFERENCEBANDWIDTH: + if (ospf6) + return SNMP_INTEGER(ospf6->ref_bandwidth); + /* Otherwise, like for "not implemented". */ + /* fallthru */ + case OSPFv3RESTARTSUPPORT: + case OSPFv3RESTARTINTERVAL: + case OSPFv3RESTARTSTRICTLSACHECKING: + case OSPFv3RESTARTSTATUS: + case OSPFv3RESTARTAGE: + case OSPFv3RESTARTEXITREASON: + case OSPFv3NOTIFICATIONENABLE: + case OSPFv3STUBROUTERSUPPORT: + case OSPFv3STUBROUTERADVERTISEMENT: + case OSPFv3DISCONTINUITYTIME: + case OSPFv3RESTARTTIME: + /* TODO: Not implemented */ + return NULL; + } + return NULL; +} + +static uint8_t *ospfv3AreaEntry(struct variable *v, oid *name, size_t *length, + int exact, size_t *var_len, + WriteMethod **write_method) +{ + struct ospf6_area *oa, *area = NULL; + struct ospf6_lsa *lsa = NULL, *lsanext; + uint32_t area_id = 0; + uint32_t count; + uint16_t sum; + struct listnode *node; + unsigned int len; + char a[16]; + struct ospf6_route *ro; + struct ospf6 *ospf6; + + ospf6 = ospf6_lookup_by_vrf_id(VRF_DEFAULT); + + if (ospf6 == NULL) + return NULL; + + if (smux_header_table(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + + len = *length - v->namelen; + len = (len >= 1 ? 1 : 0); + if (exact && len != 1) + return NULL; + if (len) + area_id = htonl(name[v->namelen]); + + inet_ntop(AF_INET, &area_id, a, sizeof(a)); + zlog_debug("SNMP access by area: %s, exact=%d len=%d length=%lu", a, + exact, len, (unsigned long)*length); + + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, oa)) { + if (area == NULL) { + if (len == 0) /* return first area entry */ + area = oa; + else if (exact && ntohl(oa->area_id) == ntohl(area_id)) + area = oa; + else if (ntohl(oa->area_id) > ntohl(area_id)) + area = oa; + } + } + + if (area == NULL) + return NULL; + + *length = v->namelen + 1; + name[v->namelen] = ntohl(area->area_id); + + inet_ntop(AF_INET, &area->area_id, a, sizeof(a)); + zlog_debug("SNMP found area: %s, exact=%d len=%d length=%lu", a, exact, + len, (unsigned long)*length); + + switch (v->magic) { + case OSPFv3IMPORTASEXTERN: + /* No NSSA support */ + return SNMP_INTEGER(IS_AREA_STUB(area) ? 2 : 1); + case OSPFv3AREASPFRUNS: + return SNMP_INTEGER(area->spf_calculation); + case OSPFv3AREABDRRTRCOUNT: + case OSPFv3AREAASBDRRTRCOUNT: + count = 0; + for (ro = ospf6_route_head(ospf6->brouter_table); ro; + ro = ospf6_route_next(ro)) { + if (ntohl(ro->path.area_id) != ntohl(area->area_id)) + continue; + if (v->magic == OSPFv3AREABDRRTRCOUNT + && CHECK_FLAG(ro->path.router_bits, + OSPF6_ROUTER_BIT_B)) + count++; + if (v->magic == OSPFv3AREAASBDRRTRCOUNT + && CHECK_FLAG(ro->path.router_bits, + OSPF6_ROUTER_BIT_E)) + count++; + } + return SNMP_INTEGER(count); + case OSPFv3AREASCOPELSACOUNT: + return SNMP_INTEGER(area->lsdb->count); + case OSPFv3AREASCOPELSACKSUMSUM: + sum = 0; + for (ALL_LSDB(area->lsdb, lsa, lsanext)) + sum += ntohs(lsa->header->checksum); + return SNMP_INTEGER(sum); + case OSPFv3AREASUMMARY: + return SNMP_INTEGER(2); /* sendAreaSummary */ + case OSPFv3AREAROWSTATUS: + return SNMP_INTEGER(1); /* Active */ + case OSPFv3AREASTUBMETRIC: + case OSPFv3AREANSSATRANSLATORROLE: + case OSPFv3AREANSSATRANSLATORSTATE: + case OSPFv3AREANSSATRANSLATORSTABINTERVAL: + case OSPFv3AREANSSATRANSLATOREVENTS: + case OSPFv3AREASTUBMETRICTYPE: + case OSPFv3AREATEENABLED: + /* Not implemented. */ + return NULL; + } + return NULL; +} + +static int if_icmp_func(struct interface *ifp1, struct interface *ifp2) +{ + return (ifp1->ifindex - ifp2->ifindex); +} + +static uint8_t *ospfv3WwLsdbEntry(struct variable *v, oid *name, size_t *length, + int exact, size_t *var_len, + WriteMethod **write_method) +{ + struct vrf *vrf; + struct ospf6_lsa *lsa = NULL; + ifindex_t ifindex; + uint32_t area_id, id, instid, adv_router; + uint16_t type; + int len; + oid *offset; + int offsetlen; + struct ospf6_area *oa = NULL; + struct listnode *node; + struct interface *iif; + struct ospf6_interface *oi = NULL; + struct list *ifslist; + struct ospf6 *ospf6; + + ospf6 = ospf6_lookup_by_vrf_id(VRF_DEFAULT); + + if (smux_header_table(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + + instid = ifindex = area_id = type = id = adv_router = 0; + + /* Check OSPFv3 instance. */ + if (ospf6 == NULL) + return NULL; + + vrf = vrf_lookup_by_id(ospf6->vrf_id); + /* Get variable length. */ + offset = name + v->namelen; + offsetlen = *length - v->namelen; + + if (exact && (v->magic & OSPFv3WWASTABLE) && offsetlen != 3) + return NULL; + if (exact && (v->magic & OSPFv3WWAREATABLE) && offsetlen != 4) + return NULL; + if (exact && (v->magic & OSPFv3WWLINKTABLE) && offsetlen != 5) + return NULL; + + if (v->magic & OSPFv3WWLINKTABLE) { + /* Parse ifindex */ + len = (offsetlen < 1 ? 0 : 1); + if (len) + ifindex = *offset; + offset += len; + offsetlen -= len; + + /* Parse instance ID */ + len = (offsetlen < 1 ? 0 : 1); + if (len) + instid = *offset; + offset += len; + offsetlen -= len; + } else if (v->magic & OSPFv3WWAREATABLE) { + /* Parse area-id */ + len = (offsetlen < 1 ? 0 : 1); + if (len) + area_id = htonl(*offset); + offset += len; + offsetlen -= len; + } + + /* Parse type */ + len = (offsetlen < 1 ? 0 : 1); + if (len) + type = htons(*offset); + offset += len; + offsetlen -= len; + + /* Parse Router-ID */ + len = (offsetlen < 1 ? 0 : 1); + if (len) + adv_router = htonl(*offset); + offset += len; + offsetlen -= len; + + /* Parse LS-ID */ + len = (offsetlen < 1 ? 0 : 1); + if (len) + id = htonl(*offset); + offset += len; + // offsetlen -= len; // Add back in if we need it again + + if (exact) { + if (v->magic & OSPFv3WWASTABLE) { + lsa = ospf6_lsdb_lookup(type, id, adv_router, + ospf6->lsdb); + } else if (v->magic & OSPFv3WWAREATABLE) { + oa = ospf6_area_lookup(area_id, ospf6); + if (!oa) + return NULL; + lsa = ospf6_lsdb_lookup(type, id, adv_router, oa->lsdb); + } else if (v->magic & OSPFv3WWLINKTABLE) { + oi = ospf6_interface_lookup_by_ifindex(ifindex, + ospf6->vrf_id); + if (!oi || oi->instance_id != instid) + return NULL; + lsa = ospf6_lsdb_lookup(type, id, adv_router, oi->lsdb); + } + } else { + if (v->magic & OSPFv3WWASTABLE) { + if (ospf6->lsdb->count) + lsa = ospf6_lsdb_lookup_next( + type, id, adv_router, ospf6->lsdb); + } else if (v->magic & OSPFv3WWAREATABLE) + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, oa)) { + if (oa->area_id < area_id) + continue; + + if (oa->lsdb->count) + lsa = ospf6_lsdb_lookup_next( + type, id, adv_router, oa->lsdb); + if (lsa) + break; + type = 0; + id = 0; + adv_router = 0; + } + else if (v->magic & OSPFv3WWLINKTABLE) { + /* We build a sorted list of interfaces */ + ifslist = list_new(); + ifslist->cmp = (int (*)(void *, void *))if_icmp_func; + FOR_ALL_INTERFACES (vrf, iif) + listnode_add_sort(ifslist, iif); + + for (ALL_LIST_ELEMENTS_RO(ifslist, node, iif)) { + if (!iif->ifindex) + continue; + oi = iif->info; + if (!oi) + continue; + if (iif->ifindex < ifindex) + continue; + if (oi->instance_id < instid) + continue; + + if (oi->lsdb->count) + lsa = ospf6_lsdb_lookup_next( + type, id, adv_router, oi->lsdb); + if (lsa) + break; + type = 0; + id = 0; + adv_router = 0; + oi = NULL; + } + + list_delete_all_node(ifslist); + list_delete(&ifslist); + } + } + + if (!lsa) + return NULL; + + /* Add indexes */ + if (v->magic & OSPFv3WWASTABLE) { + *length = v->namelen + 3; + offset = name + v->namelen; + } else if (v->magic & OSPFv3WWAREATABLE) { + *length = v->namelen + 4; + offset = name + v->namelen; + *offset = ntohl(oa->area_id); + offset++; + } else if (v->magic & OSPFv3WWLINKTABLE) { + *length = v->namelen + 5; + offset = name + v->namelen; + *offset = oi->interface->ifindex; + offset++; + *offset = oi->instance_id; + offset++; + } + *offset = ntohs(lsa->header->type); + offset++; + *offset = ntohl(lsa->header->adv_router); + offset++; + *offset = ntohl(lsa->header->id); + offset++; + + /* Return the current value of the variable */ + switch (v->magic & OSPFv3WWCOLUMN) { + case OSPFv3WWLSDBSEQUENCE: + return SNMP_INTEGER(ntohl(lsa->header->seqnum)); + case OSPFv3WWLSDBAGE: + ospf6_lsa_age_current(lsa); + return SNMP_INTEGER(ntohs(lsa->header->age)); + case OSPFv3WWLSDBCHECKSUM: + return SNMP_INTEGER(ntohs(lsa->header->checksum)); + case OSPFv3WWLSDBADVERTISEMENT: + *var_len = ntohs(lsa->header->length); + return (uint8_t *)lsa->header; + case OSPFv3WWLSDBTYPEKNOWN: + return SNMP_INTEGER(OSPF6_LSA_IS_KNOWN(lsa->header->type) + ? SNMP_TRUE + : SNMP_FALSE); + } + return NULL; +} + +static uint8_t *ospfv3IfEntry(struct variable *v, oid *name, size_t *length, + int exact, size_t *var_len, + WriteMethod **write_method) +{ + struct vrf *vrf; + ifindex_t ifindex = 0; + unsigned int instid = 0; + struct ospf6_interface *oi = NULL; + struct ospf6_lsa *lsa = NULL, *lsanext; + struct interface *iif; + struct listnode *i; + struct list *ifslist; + oid *offset; + int offsetlen, len; + uint32_t sum; + struct ospf6 *ospf6; + + ospf6 = ospf6_lookup_by_vrf_id(VRF_DEFAULT); + + if (smux_header_table(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + + /* Check OSPFv3 instance. */ + if (ospf6 == NULL) + return NULL; + + vrf = vrf_lookup_by_id(ospf6->vrf_id); + /* Get variable length. */ + offset = name + v->namelen; + offsetlen = *length - v->namelen; + + if (exact && offsetlen != 2) + return NULL; + + /* Parse if index */ + len = (offsetlen < 1 ? 0 : 1); + if (len) + ifindex = *offset; + offset += len; + offsetlen -= len; + + /* Parse instance ID */ + len = (offsetlen < 1 ? 0 : 1); + if (len) + instid = *offset; + // offset += len; // Add back in if we ever start using again + // offsetlen -= len; + + if (exact) { + oi = ospf6_interface_lookup_by_ifindex(ifindex, ospf6->vrf_id); + if (!oi || oi->instance_id != instid) + return NULL; + } else { + /* We build a sorted list of interfaces */ + ifslist = list_new(); + ifslist->cmp = (int (*)(void *, void *))if_icmp_func; + FOR_ALL_INTERFACES (vrf, iif) + listnode_add_sort(ifslist, iif); + + for (ALL_LIST_ELEMENTS_RO(ifslist, i, iif)) { + if (!iif->ifindex) + continue; + oi = iif->info; + if (!oi) + continue; + if (iif->ifindex > ifindex + || (iif->ifindex == ifindex + && (oi->instance_id > instid))) + break; + oi = NULL; + } + + list_delete_all_node(ifslist); + list_delete(&ifslist); + } + + if (!oi) + return NULL; + + /* Add Index (IfIndex, IfInstId) */ + *length = v->namelen + 2; + offset = name + v->namelen; + *offset = oi->interface->ifindex; + offset++; + *offset = oi->instance_id; + offset++; + + /* Return the current value of the variable */ + switch (v->magic) { + case OSPFv3IFAREAID: + if (oi->area) + return SNMP_INTEGER(ntohl(oi->area->area_id)); + break; + case OSPFv3IFTYPE: + if (oi->type == OSPF_IFTYPE_BROADCAST) + return SNMP_INTEGER(1); + else if (oi->type == OSPF_IFTYPE_POINTOPOINT) + return SNMP_INTEGER(3); + else + break; /* Unknown, don't put anything */ + case OSPFv3IFADMINSTATUS: + if (oi->area) + return SNMP_INTEGER(OSPF_STATUS_ENABLED); + return SNMP_INTEGER(OSPF_STATUS_DISABLED); + case OSPFv3IFRTRPRIORITY: + return SNMP_INTEGER(oi->priority); + case OSPFv3IFTRANSITDELAY: + return SNMP_INTEGER(oi->transdelay); + case OSPFv3IFRETRANSINTERVAL: + return SNMP_INTEGER(oi->rxmt_interval); + case OSPFv3IFHELLOINTERVAL: + return SNMP_INTEGER(oi->hello_interval); + case OSPFv3IFRTRDEADINTERVAL: + return SNMP_INTEGER(oi->dead_interval); + case OSPFv3IFPOLLINTERVAL: + /* No support for NBMA */ + break; + case OSPFv3IFSTATE: + return SNMP_INTEGER(oi->state); + case OSPFv3IFDESIGNATEDROUTER: + return SNMP_INTEGER(ntohl(oi->drouter)); + case OSPFv3IFBACKUPDESIGNATEDROUTER: + return SNMP_INTEGER(ntohl(oi->bdrouter)); + case OSPFv3IFEVENTS: + return SNMP_INTEGER(oi->state_change); + case OSPFv3IFROWSTATUS: + return SNMP_INTEGER(1); + case OSPFv3IFDEMAND: + return SNMP_INTEGER(SNMP_FALSE); + case OSPFv3IFMETRICVALUE: + return SNMP_INTEGER(oi->cost); + case OSPFv3IFLINKSCOPELSACOUNT: + return SNMP_INTEGER(oi->lsdb->count); + case OSPFv3IFLINKLSACKSUMSUM: + sum = 0; + for (ALL_LSDB(oi->lsdb, lsa, lsanext)) + sum += ntohs(lsa->header->checksum); + return SNMP_INTEGER(sum); + case OSPFv3IFDEMANDNBRPROBE: + case OSPFv3IFDEMANDNBRPROBERETRANSLIMIT: + case OSPFv3IFDEMANDNBRPROBEINTERVAL: + case OSPFv3IFTEDISABLED: + case OSPFv3IFLINKLSASUPPRESSION: + /* Not implemented. Only works if all the last ones are not + implemented! */ + return NULL; + } + + /* Try an internal getnext. Some columns are missing in this table. */ + if (!exact && (name[*length - 1] < MAX_SUBID)) + return ospfv3IfEntry(v, name, length, exact, var_len, + write_method); + return NULL; +} + +static uint8_t *ospfv3NbrEntry(struct variable *v, oid *name, size_t *length, + int exact, size_t *var_len, + WriteMethod **write_method) +{ + struct vrf *vrf; + ifindex_t ifindex = 0; + unsigned int instid, rtrid; + struct ospf6_interface *oi = NULL; + struct ospf6_neighbor *on = NULL; + struct interface *iif; + struct listnode *i, *j; + struct list *ifslist; + oid *offset; + int offsetlen, len; + struct ospf6 *ospf6; + + ospf6 = ospf6_lookup_by_vrf_id(VRF_DEFAULT); + + if (smux_header_table(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + + instid = rtrid = 0; + + /* Check OSPFv3 instance. */ + if (ospf6 == NULL) + return NULL; + + vrf = vrf_lookup_by_id(ospf6->vrf_id); + /* Get variable length. */ + offset = name + v->namelen; + offsetlen = *length - v->namelen; + + if (exact && offsetlen != 3) + return NULL; + + /* Parse if index */ + len = (offsetlen < 1 ? 0 : 1); + if (len) + ifindex = *offset; + offset += len; + offsetlen -= len; + + /* Parse instance ID */ + len = (offsetlen < 1 ? 0 : 1); + if (len) + instid = *offset; + offset += len; + offsetlen -= len; + + /* Parse router ID */ + len = (offsetlen < 1 ? 0 : 1); + if (len) + rtrid = htonl(*offset); + // offset += len; // Add back in if we ever start looking at data + // offsetlen -= len; + + if (exact) { + oi = ospf6_interface_lookup_by_ifindex(ifindex, ospf6->vrf_id); + if (!oi || oi->instance_id != instid) + return NULL; + on = ospf6_neighbor_lookup(rtrid, oi); + } else { + /* We build a sorted list of interfaces */ + ifslist = list_new(); + ifslist->cmp = (int (*)(void *, void *))if_icmp_func; + FOR_ALL_INTERFACES (vrf, iif) + listnode_add_sort(ifslist, iif); + + for (ALL_LIST_ELEMENTS_RO(ifslist, i, iif)) { + if (!iif->ifindex) + continue; + oi = iif->info; + if (!oi) + continue; + for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, j, on)) { + if (iif->ifindex > ifindex + || (iif->ifindex == ifindex + && (oi->instance_id > instid + || (oi->instance_id == instid + && ntohl(on->router_id) + > ntohl(rtrid))))) + break; + } + if (on) + break; + oi = NULL; + on = NULL; + } + + list_delete_all_node(ifslist); + list_delete(&ifslist); + } + + if (!oi || !on) + return NULL; + + /* Add Index (IfIndex, IfInstId, RtrId) */ + *length = v->namelen + 3; + offset = name + v->namelen; + *offset = oi->interface->ifindex; + offset++; + *offset = oi->instance_id; + offset++; + *offset = ntohl(on->router_id); + offset++; + + /* Return the current value of the variable */ + switch (v->magic) { + case OSPFv3NBRADDRESSTYPE: + return SNMP_INTEGER(2); /* IPv6 only */ + case OSPFv3NBRADDRESS: + *var_len = sizeof(struct in6_addr); + return (uint8_t *)&on->linklocal_addr; + case OSPFv3NBROPTIONS: + return SNMP_INTEGER(on->options[2]); + case OSPFv3NBRPRIORITY: + return SNMP_INTEGER(on->priority); + case OSPFv3NBRSTATE: + return SNMP_INTEGER(on->state); + case OSPFv3NBREVENTS: + return SNMP_INTEGER(on->state_change); + case OSPFv3NBRLSRETRANSQLEN: + return SNMP_INTEGER(on->retrans_list->count); + case OSPFv3NBRHELLOSUPPRESSED: + return SNMP_INTEGER(SNMP_FALSE); + case OSPFv3NBRIFID: + return SNMP_INTEGER(on->ifindex); + case OSPFv3NBRRESTARTHELPERSTATUS: + case OSPFv3NBRRESTARTHELPERAGE: + case OSPFv3NBRRESTARTHELPEREXITREASON: + /* Not implemented. Only works if all the last ones are not + implemented! */ + return NULL; + } + + return NULL; +} + +/* OSPF Traps. */ +#define NBRSTATECHANGE 2 +#define IFSTATECHANGE 10 + +static struct trap_object ospf6NbrTrapList[] = { + {-3, {1, 1, OSPFv3ROUTERID}}, + {4, {1, 9, 1, OSPFv3NBRADDRESSTYPE}}, + {4, {1, 9, 1, OSPFv3NBRADDRESS}}, + {4, {1, 9, 1, OSPFv3NBRSTATE}}}; + +static struct trap_object ospf6IfTrapList[] = { + {-3, {1, 1, OSPFv3ROUTERID}}, + {4, {1, 7, 1, OSPFv3IFSTATE}}, + {4, {1, 7, 1, OSPFv3IFADMINSTATUS}}, + {4, {1, 7, 1, OSPFv3IFAREAID}}}; + +static int ospf6TrapNbrStateChange(struct ospf6_neighbor *on, int next_state, + int prev_state) +{ + oid index[3]; + + /* Terminal state or regression */ + if ((next_state != OSPF6_NEIGHBOR_FULL) + && (next_state != OSPF6_NEIGHBOR_TWOWAY) + && (next_state >= prev_state)) + return 0; + + index[0] = on->ospf6_if->interface->ifindex; + index[1] = on->ospf6_if->instance_id; + index[2] = ntohl(on->router_id); + + smux_trap(ospfv3_variables, array_size(ospfv3_variables), + ospfv3_trap_oid, array_size(ospfv3_trap_oid), ospfv3_oid, + sizeof(ospfv3_oid) / sizeof(oid), index, 3, ospf6NbrTrapList, + array_size(ospf6NbrTrapList), NBRSTATECHANGE); + return 0; +} + +static int ospf6TrapIfStateChange(struct ospf6_interface *oi, int next_state, + int prev_state) +{ + oid index[2]; + + /* Terminal state or regression */ + if ((next_state != OSPF6_INTERFACE_POINTTOPOINT) + && (next_state != OSPF6_INTERFACE_DROTHER) + && (next_state != OSPF6_INTERFACE_BDR) + && (next_state != OSPF6_INTERFACE_DR) && (next_state >= prev_state)) + return 0; + + index[0] = oi->interface->ifindex; + index[1] = oi->instance_id; + + smux_trap(ospfv3_variables, array_size(ospfv3_variables), + ospfv3_trap_oid, array_size(ospfv3_trap_oid), ospfv3_oid, + sizeof(ospfv3_oid) / sizeof(oid), index, 2, ospf6IfTrapList, + array_size(ospf6IfTrapList), IFSTATECHANGE); + return 0; +} + +/* Register OSPFv3-MIB. */ +static int ospf6_snmp_init(struct event_loop *master) +{ + smux_init(master); + REGISTER_MIB("OSPFv3MIB", ospfv3_variables, variable, ospfv3_oid); + return 0; +} + +static int ospf6_snmp_module_init(void) +{ + hook_register(ospf6_interface_change, ospf6TrapIfStateChange); + hook_register(ospf6_neighbor_change, ospf6TrapNbrStateChange); + hook_register(frr_late_init, ospf6_snmp_init); + return 0; +} + +FRR_MODULE_SETUP(.name = "ospf6d_snmp", .version = FRR_VERSION, + .description = "ospf6d AgentX SNMP module", + .init = ospf6_snmp_module_init, +); diff --git a/ospf6d/ospf6_spf.c b/ospf6d/ospf6_spf.c new file mode 100644 index 0000000..e39ae50 --- /dev/null +++ b/ospf6d/ospf6_spf.c @@ -0,0 +1,1294 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2003 Yasuhiro Ohara + */ + +/* Shortest Path First calculation for OSPFv3 */ + +#include <zebra.h> + +#include "log.h" +#include "memory.h" +#include "command.h" +#include "vty.h" +#include "prefix.h" +#include "linklist.h" +#include "frrevent.h" +#include "lib_errors.h" + +#include "ospf6_lsa.h" +#include "ospf6_lsdb.h" +#include "ospf6_route.h" +#include "ospf6_area.h" +#include "ospf6_proto.h" +#include "ospf6_abr.h" +#include "ospf6_asbr.h" +#include "ospf6_spf.h" +#include "ospf6_intra.h" +#include "ospf6_interface.h" +#include "ospf6d.h" +#include "ospf6_abr.h" +#include "ospf6_nssa.h" +#include "ospf6_zebra.h" + +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_VERTEX, "OSPF6 vertex"); + +unsigned char conf_debug_ospf6_spf = 0; + +static void ospf6_spf_copy_nexthops_to_route(struct ospf6_route *rt, + struct ospf6_vertex *v) +{ + if (rt && v) + ospf6_copy_nexthops(rt->nh_list, v->nh_list); +} + +static void ospf6_spf_merge_nexthops_to_route(struct ospf6_route *rt, + struct ospf6_vertex *v) +{ + if (rt && v) + ospf6_merge_nexthops(rt->nh_list, v->nh_list); +} + +static unsigned int ospf6_spf_get_ifindex_from_nh(struct ospf6_vertex *v) +{ + struct ospf6_nexthop *nh; + struct listnode *node; + + if (v) { + node = listhead(v->nh_list); + if (node) { + nh = listgetdata(node); + if (nh) + return (nh->ifindex); + } + } + return 0; +} + +static int ospf6_vertex_cmp(const struct ospf6_vertex *va, + const struct ospf6_vertex *vb) +{ + /* ascending order */ + if (va->cost != vb->cost) + return (va->cost - vb->cost); + if (va->hops != vb->hops) + return (va->hops - vb->hops); + return 0; +} +DECLARE_SKIPLIST_NONUNIQ(vertex_pqueue, struct ospf6_vertex, pqi, + ospf6_vertex_cmp); + +static int ospf6_vertex_id_cmp(void *a, void *b) +{ + struct ospf6_vertex *va = (struct ospf6_vertex *)a; + struct ospf6_vertex *vb = (struct ospf6_vertex *)b; + int ret = 0; + + ret = ntohl(ospf6_linkstate_prefix_adv_router(&va->vertex_id)) + - ntohl(ospf6_linkstate_prefix_adv_router(&vb->vertex_id)); + if (ret) + return ret; + + ret = ntohl(ospf6_linkstate_prefix_id(&va->vertex_id)) + - ntohl(ospf6_linkstate_prefix_id(&vb->vertex_id)); + return ret; +} + +static struct ospf6_vertex *ospf6_vertex_create(struct ospf6_lsa *lsa) +{ + struct ospf6_vertex *v; + + v = XMALLOC(MTYPE_OSPF6_VERTEX, sizeof(struct ospf6_vertex)); + + /* type */ + if (ntohs(lsa->header->type) == OSPF6_LSTYPE_ROUTER) { + v->type = OSPF6_VERTEX_TYPE_ROUTER; + /* Router LSA use Link ID 0 as base in vertex_id */ + ospf6_linkstate_prefix(lsa->header->adv_router, htonl(0), + &v->vertex_id); + } else if (ntohs(lsa->header->type) == OSPF6_LSTYPE_NETWORK) { + v->type = OSPF6_VERTEX_TYPE_NETWORK; + /* vertex_id */ + ospf6_linkstate_prefix(lsa->header->adv_router, lsa->header->id, + &v->vertex_id); + } else + assert(0); + + /* name */ + ospf6_linkstate_prefix2str(&v->vertex_id, v->name, sizeof(v->name)); + + if (IS_OSPF6_DEBUG_SPF(PROCESS)) + zlog_debug("%s: Creating vertex %s of type %s (0x%04hx) lsa %s", + __func__, v->name, + ((ntohs(lsa->header->type) == OSPF6_LSTYPE_ROUTER) + ? "Router" + : "N/W"), + ntohs(lsa->header->type), lsa->name); + + + /* Associated LSA */ + v->lsa = lsa; + + /* capability bits + options */ + v->capability = *(uint8_t *)(OSPF6_LSA_HEADER_END(lsa->header)); + v->options[0] = *(uint8_t *)(OSPF6_LSA_HEADER_END(lsa->header) + 1); + v->options[1] = *(uint8_t *)(OSPF6_LSA_HEADER_END(lsa->header) + 2); + v->options[2] = *(uint8_t *)(OSPF6_LSA_HEADER_END(lsa->header) + 3); + + v->nh_list = list_new(); + v->nh_list->cmp = (int (*)(void *, void *))ospf6_nexthop_cmp; + v->nh_list->del = (void (*)(void *))ospf6_nexthop_delete; + + v->parent = NULL; + v->child_list = list_new(); + v->child_list->cmp = ospf6_vertex_id_cmp; + + return v; +} + +static void ospf6_vertex_delete(struct ospf6_vertex *v) +{ + list_delete(&v->nh_list); + list_delete(&v->child_list); + XFREE(MTYPE_OSPF6_VERTEX, v); +} + +static struct ospf6_lsa *ospf6_lsdesc_lsa(caddr_t lsdesc, + struct ospf6_vertex *v) +{ + struct ospf6_lsa *lsa = NULL; + uint16_t type = 0; + uint32_t id = 0, adv_router = 0; + + if (VERTEX_IS_TYPE(NETWORK, v)) { + type = htons(OSPF6_LSTYPE_ROUTER); + id = htonl(0); + adv_router = NETWORK_LSDESC_GET_NBR_ROUTERID(lsdesc); + } else { + if (ROUTER_LSDESC_IS_TYPE(POINTTOPOINT, lsdesc)) { + type = htons(OSPF6_LSTYPE_ROUTER); + id = htonl(0); + adv_router = ROUTER_LSDESC_GET_NBR_ROUTERID(lsdesc); + } else if (ROUTER_LSDESC_IS_TYPE(TRANSIT_NETWORK, lsdesc)) { + type = htons(OSPF6_LSTYPE_NETWORK); + id = htonl(ROUTER_LSDESC_GET_NBR_IFID(lsdesc)); + adv_router = ROUTER_LSDESC_GET_NBR_ROUTERID(lsdesc); + } + } + + if (type == htons(OSPF6_LSTYPE_NETWORK)) + lsa = ospf6_lsdb_lookup(type, id, adv_router, v->area->lsdb); + else + lsa = ospf6_create_single_router_lsa(v->area, v->area->lsdb, + adv_router); + if (IS_OSPF6_DEBUG_SPF(PROCESS)) { + char ibuf[16], abuf[16]; + inet_ntop(AF_INET, &id, ibuf, sizeof(ibuf)); + inet_ntop(AF_INET, &adv_router, abuf, sizeof(abuf)); + if (lsa) + zlog_debug(" Link to: %s len %u, V %s", lsa->name, + ntohs(lsa->header->length), v->name); + else + zlog_debug(" Link to: [%s Id:%s Adv:%s] No LSA , V %s", + ospf6_lstype_name(type), ibuf, abuf, + v->name); + } + + return lsa; +} + +static char *ospf6_lsdesc_backlink(struct ospf6_lsa *lsa, caddr_t lsdesc, + struct ospf6_vertex *v) +{ + caddr_t backlink, found = NULL; + int size; + + size = (OSPF6_LSA_IS_TYPE(ROUTER, lsa) + ? sizeof(struct ospf6_router_lsdesc) + : sizeof(struct ospf6_network_lsdesc)); + for (backlink = OSPF6_LSA_HEADER_END(lsa->header) + 4; + backlink + size <= OSPF6_LSA_END(lsa->header); backlink += size) { + assert(!(OSPF6_LSA_IS_TYPE(NETWORK, lsa) + && VERTEX_IS_TYPE(NETWORK, v))); + + if (OSPF6_LSA_IS_TYPE(NETWORK, lsa)) { + if (NETWORK_LSDESC_GET_NBR_ROUTERID(backlink) + == v->lsa->header->adv_router) + found = backlink; + } else if (VERTEX_IS_TYPE(NETWORK, v)) { + if (ROUTER_LSDESC_IS_TYPE(TRANSIT_NETWORK, backlink) + && ROUTER_LSDESC_GET_NBR_ROUTERID(backlink) + == v->lsa->header->adv_router + && ROUTER_LSDESC_GET_NBR_IFID(backlink) + == ntohl(v->lsa->header->id)) + found = backlink; + } else { + assert(OSPF6_LSA_IS_TYPE(ROUTER, lsa) + && VERTEX_IS_TYPE(ROUTER, v)); + + if (!ROUTER_LSDESC_IS_TYPE(POINTTOPOINT, backlink) + || !ROUTER_LSDESC_IS_TYPE(POINTTOPOINT, lsdesc)) + continue; + + if (ROUTER_LSDESC_GET_NBR_IFID(backlink) + != ROUTER_LSDESC_GET_IFID(lsdesc) + || ROUTER_LSDESC_GET_NBR_IFID(lsdesc) + != ROUTER_LSDESC_GET_IFID(backlink)) + continue; + if (ROUTER_LSDESC_GET_NBR_ROUTERID(backlink) + != v->lsa->header->adv_router + || ROUTER_LSDESC_GET_NBR_ROUTERID(lsdesc) + != lsa->header->adv_router) + continue; + found = backlink; + } + } + + if (IS_OSPF6_DEBUG_SPF(PROCESS)) + zlog_debug("Vertex %s Lsa %s Backlink %s", v->name, lsa->name, + (found ? "OK" : "FAIL")); + + return found; +} + +static void ospf6_nexthop_calc(struct ospf6_vertex *w, struct ospf6_vertex *v, + caddr_t lsdesc, struct ospf6 *ospf6) +{ + int i; + ifindex_t ifindex; + struct ospf6_interface *oi; + uint16_t type; + uint32_t adv_router; + struct ospf6_lsa *lsa; + struct ospf6_link_lsa *link_lsa; + char buf[64]; + + assert(VERTEX_IS_TYPE(ROUTER, w)); + ifindex = (VERTEX_IS_TYPE(NETWORK, v) ? ospf6_spf_get_ifindex_from_nh(v) + : ROUTER_LSDESC_GET_IFID(lsdesc)); + if (ifindex == 0) { + flog_err(EC_LIB_DEVELOPMENT, "No nexthop ifindex at vertex %s", + v->name); + return; + } + + oi = ospf6_interface_lookup_by_ifindex(ifindex, ospf6->vrf_id); + if (oi == NULL) { + zlog_warn("Can't find interface in SPF: ifindex %d", ifindex); + return; + } + + type = htons(OSPF6_LSTYPE_LINK); + adv_router = (VERTEX_IS_TYPE(NETWORK, v) + ? NETWORK_LSDESC_GET_NBR_ROUTERID(lsdesc) + : ROUTER_LSDESC_GET_NBR_ROUTERID(lsdesc)); + + i = 0; + for (ALL_LSDB_TYPED_ADVRTR(oi->lsdb, type, adv_router, lsa)) { + if (VERTEX_IS_TYPE(ROUTER, v) + && htonl(ROUTER_LSDESC_GET_NBR_IFID(lsdesc)) + != lsa->header->id) + continue; + + link_lsa = (struct ospf6_link_lsa *)OSPF6_LSA_HEADER_END( + lsa->header); + if (IS_OSPF6_DEBUG_SPF(PROCESS)) { + inet_ntop(AF_INET6, &link_lsa->linklocal_addr, buf, + sizeof(buf)); + zlog_debug(" nexthop %s from %s", buf, lsa->name); + } + + ospf6_add_nexthop(w->nh_list, ifindex, + &link_lsa->linklocal_addr); + i++; + } + + if (i == 0 && IS_OSPF6_DEBUG_SPF(PROCESS)) + zlog_debug("No nexthop for %s found", w->name); +} + +static int ospf6_spf_install(struct ospf6_vertex *v, + struct ospf6_route_table *result_table) +{ + struct ospf6_route *route; + struct ospf6_vertex *prev; + + if (IS_OSPF6_DEBUG_SPF(PROCESS)) + zlog_debug("SPF install %s (lsa %s) hops %d cost %d", v->name, + v->lsa->name, v->hops, v->cost); + + route = ospf6_route_lookup(&v->vertex_id, result_table); + if (route && route->path.cost < v->cost) { + if (IS_OSPF6_DEBUG_SPF(PROCESS)) + zlog_debug( + " already installed with lower cost (%d), ignore", + route->path.cost); + ospf6_vertex_delete(v); + return -1; + } else if (route && route->path.cost == v->cost) { + if (IS_OSPF6_DEBUG_SPF(PROCESS)) + zlog_debug( + " another path found to route %pFX lsa %s, merge", + &route->prefix, v->lsa->name); + + /* merging the parent's nexthop information to the child's + * if the parent is not the root of the tree. + */ + if (!ospf6_merge_parents_nh_to_child(v, route, result_table)) + ospf6_spf_merge_nexthops_to_route(route, v); + + prev = (struct ospf6_vertex *)route->route_option; + assert(prev->hops <= v->hops); + + if ((VERTEX_IS_TYPE(ROUTER, v) + && route->path.origin.id != v->lsa->header->id)) { + if (IS_OSPF6_DEBUG_SPF(PROCESS)) { + zlog_debug( + "%s: V lsa %s id %u, route id %u are different", + __func__, v->lsa->name, + ntohl(v->lsa->header->id), + ntohl(route->path.origin.id)); + } + return 0; + } + + ospf6_vertex_delete(v); + return -1; + } + + /* There should be no case where candidate being installed (variable + "v") is closer than the one in the SPF tree (variable "route"). + In the case something has gone wrong with the behavior of + Priority-Queue. */ + + /* the case where the route exists already is handled and returned + up to here. */ + assert(route == NULL); + + route = ospf6_route_create(v->area->ospf6); + memcpy(&route->prefix, &v->vertex_id, sizeof(struct prefix)); + route->type = OSPF6_DEST_TYPE_LINKSTATE; + route->path.type = OSPF6_PATH_TYPE_INTRA; + route->path.origin.type = v->lsa->header->type; + route->path.origin.id = v->lsa->header->id; + route->path.origin.adv_router = v->lsa->header->adv_router; + route->path.metric_type = 1; + route->path.cost = v->cost; + route->path.u.cost_e2 = v->hops; + route->path.router_bits = v->capability; + route->path.options[0] = v->options[0]; + route->path.options[1] = v->options[1]; + route->path.options[2] = v->options[2]; + + ospf6_spf_copy_nexthops_to_route(route, v); + + /* + * The SPF logic implementation does not transfer the multipathing + * properties + * of a parent to a child node. Thus if there was a 3-way multipath to a + * node's parent and a single hop from the parent to the child, the + * logic of + * creating new vertices and computing next hops prevents there from + * being 3 + * paths to the child node. This is primarily because the resolution of + * multipath is done in this routine, not in the main spf loop. + * + * The following logic addresses that problem by merging the parent's + * nexthop + * information with the child's, if the parent is not the root of the + * tree. + * This is based on the assumption that before a node's route is + * installed, + * its parent's route's nexthops have already been installed. + */ + ospf6_merge_parents_nh_to_child(v, route, result_table); + + if (v->parent) + listnode_add_sort(v->parent->child_list, v); + route->route_option = v; + + ospf6_route_add(route, result_table); + return 0; +} + +void ospf6_spf_table_finish(struct ospf6_route_table *result_table) +{ + struct ospf6_route *route, *nroute; + struct ospf6_vertex *v; + for (route = ospf6_route_head(result_table); route; route = nroute) { + nroute = ospf6_route_next(route); + v = (struct ospf6_vertex *)route->route_option; + ospf6_vertex_delete(v); + ospf6_route_remove(route, result_table); + } +} + +static const char *const ospf6_spf_reason_str[] = { + "R+", /* OSPF6_SPF_FLAGS_ROUTER_LSA_ADDED */ + "R-", /* OSPF6_SPF_FLAGS_ROUTER_LSA_REMOVED */ + "N+", /* OSPF6_SPF_FLAGS_NETWORK_LSA_ADDED */ + "N-", /* OSPF6_SPF_FLAGS_NETWORK_LSA_REMOVED */ + "L+", /* OSPF6_SPF_FLAGS_NETWORK_LINK_LSA_ADDED */ + "L-", /* OSPF6_SPF_FLAGS_NETWORK_LINK_LSA_REMOVED */ + "R*", /* OSPF6_SPF_FLAGS_ROUTER_LSA_ORIGINATED */ + "N*", /* OSPF6_SPF_FLAGS_NETWORK_LSA_ORIGINATED */ + "C", /* OSPF6_SPF_FLAGS_CONFIG_CHANGE */ + "A", /* OSPF6_SPF_FLAGS_ASBR_STATUS_CHANGE */ + "GR", /* OSPF6_SPF_FLAGS_GR_FINISH */ +}; + +void ospf6_spf_reason_string(uint32_t reason, char *buf, int size) +{ + uint32_t bit; + int len = 0; + + if (!buf) + return; + + if (!reason) { + buf[0] = '\0'; + return; + } + for (bit = 0; bit < array_size(ospf6_spf_reason_str); bit++) { + if ((reason & (1 << bit)) && (len < size)) { + len += snprintf((buf + len), (size - len), "%s%s", + (len > 0) ? ", " : "", + ospf6_spf_reason_str[bit]); + } + } +} + +/* RFC2328 16.1. Calculating the shortest-path tree for an area */ +/* RFC2740 3.8.1. Calculating the shortest path tree for an area */ +void ospf6_spf_calculation(uint32_t router_id, + struct ospf6_route_table *result_table, + struct ospf6_area *oa) +{ + struct vertex_pqueue_head candidate_list; + struct ospf6_vertex *root, *v, *w; + int size; + caddr_t lsdesc; + struct ospf6_lsa *lsa; + struct in6_addr address; + + ospf6_spf_table_finish(result_table); + + /* Install the calculating router itself as the root of the SPF tree */ + /* construct root vertex */ + lsa = ospf6_create_single_router_lsa(oa, oa->lsdb_self, router_id); + if (lsa == NULL) { + zlog_warn("%s: No router LSA for area %s", __func__, oa->name); + return; + } + + /* initialize */ + vertex_pqueue_init(&candidate_list); + + root = ospf6_vertex_create(lsa); + root->area = oa; + root->cost = 0; + root->hops = 0; + root->link_id = lsa->header->id; + inet_pton(AF_INET6, "::1", &address); + + /* Actually insert root to the candidate-list as the only candidate */ + vertex_pqueue_add(&candidate_list, root); + + /* Iterate until candidate-list becomes empty */ + while ((v = vertex_pqueue_pop(&candidate_list))) { + /* installing may result in merging or rejecting of the vertex + */ + if (ospf6_spf_install(v, result_table) < 0) + continue; + + /* Skip overloaded routers */ + if ((OSPF6_LSA_IS_TYPE(ROUTER, v->lsa) + && ospf6_router_is_stub_router(v->lsa))) + continue; + + /* For each LS description in the just-added vertex V's LSA */ + size = (VERTEX_IS_TYPE(ROUTER, v) + ? sizeof(struct ospf6_router_lsdesc) + : sizeof(struct ospf6_network_lsdesc)); + for (lsdesc = OSPF6_LSA_HEADER_END(v->lsa->header) + 4; + lsdesc + size <= OSPF6_LSA_END(v->lsa->header); + lsdesc += size) { + lsa = ospf6_lsdesc_lsa(lsdesc, v); + if (lsa == NULL) + continue; + + if (OSPF6_LSA_IS_MAXAGE(lsa)) + continue; + + if (!ospf6_lsdesc_backlink(lsa, lsdesc, v)) + continue; + + w = ospf6_vertex_create(lsa); + w->area = oa; + w->parent = v; + if (VERTEX_IS_TYPE(ROUTER, v)) { + w->cost = v->cost + + ROUTER_LSDESC_GET_METRIC(lsdesc); + w->hops = + v->hops + + (VERTEX_IS_TYPE(NETWORK, w) ? 0 : 1); + } else { + /* NETWORK */ + w->cost = v->cost; + w->hops = v->hops + 1; + } + + /* nexthop calculation */ + if (w->hops == 0) + ospf6_add_nexthop( + w->nh_list, + ROUTER_LSDESC_GET_IFID(lsdesc), NULL); + else if (w->hops == 1 && v->hops == 0) + ospf6_nexthop_calc(w, v, lsdesc, oa->ospf6); + else + ospf6_copy_nexthops(w->nh_list, v->nh_list); + + + /* add new candidate to the candidate_list */ + if (IS_OSPF6_DEBUG_SPF(PROCESS)) + zlog_debug( + " New candidate: %s hops %d cost %d", + w->name, w->hops, w->cost); + vertex_pqueue_add(&candidate_list, w); + } + } + + //vertex_pqueue_fini(&candidate_list); + + ospf6_remove_temp_router_lsa(oa); + + oa->spf_calculation++; +} + +static void ospf6_spf_log_database(struct ospf6_area *oa) +{ + char *p, *end, buffer[256]; + struct listnode *node; + struct ospf6_interface *oi; + + p = buffer; + end = buffer + sizeof(buffer); + + snprintf(p, end - p, "SPF on DB (#LSAs):"); + p = (buffer + strlen(buffer) < end ? buffer + strlen(buffer) : end); + snprintf(p, end - p, " Area %s: %d", oa->name, oa->lsdb->count); + p = (buffer + strlen(buffer) < end ? buffer + strlen(buffer) : end); + + for (ALL_LIST_ELEMENTS_RO(oa->if_list, node, oi)) { + snprintf(p, end - p, " I/F %s: %d", oi->interface->name, + oi->lsdb->count); + p = (buffer + strlen(buffer) < end ? buffer + strlen(buffer) + : end); + } + + zlog_debug("%s", buffer); +} + +static void ospf6_spf_calculation_thread(struct event *t) +{ + struct ospf6_area *oa; + struct ospf6 *ospf6; + struct timeval start, end, runtime; + struct listnode *node; + int areas_processed = 0; + char rbuf[32]; + + ospf6 = (struct ospf6 *)EVENT_ARG(t); + + /* execute SPF calculation */ + monotime(&start); + ospf6->ts_spf = start; + + if (ospf6_check_and_set_router_abr(ospf6)) + ospf6_abr_range_reset_cost(ospf6); + + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, oa)) { + + if (oa == ospf6->backbone) + continue; + + monotime(&oa->ts_spf); + if (IS_OSPF6_DEBUG_SPF(PROCESS)) + zlog_debug("SPF calculation for Area %s", oa->name); + if (IS_OSPF6_DEBUG_SPF(DATABASE)) + ospf6_spf_log_database(oa); + + ospf6_spf_calculation(ospf6->router_id, oa->spf_table, oa); + ospf6_intra_route_calculation(oa); + ospf6_intra_brouter_calculation(oa); + + areas_processed++; + } + + if (ospf6->backbone) { + monotime(&ospf6->backbone->ts_spf); + if (IS_OSPF6_DEBUG_SPF(PROCESS)) + zlog_debug("SPF calculation for Backbone area %s", + ospf6->backbone->name); + if (IS_OSPF6_DEBUG_SPF(DATABASE)) + ospf6_spf_log_database(ospf6->backbone); + + ospf6_spf_calculation(ospf6->router_id, + ospf6->backbone->spf_table, + ospf6->backbone); + ospf6_intra_route_calculation(ospf6->backbone); + ospf6_intra_brouter_calculation(ospf6->backbone); + areas_processed++; + } + + /* External LSA calculation */ + ospf6_ase_calculate_timer_add(ospf6); + + if (ospf6_check_and_set_router_abr(ospf6)) { + ospf6_abr_defaults_to_stub(ospf6); + ospf6_abr_nssa_type_7_defaults(ospf6); + } + + monotime(&end); + timersub(&end, &start, &runtime); + + ospf6->ts_spf_duration = runtime; + + ospf6_spf_reason_string(ospf6->spf_reason, rbuf, sizeof(rbuf)); + + if (IS_OSPF6_DEBUG_SPF(PROCESS) || IS_OSPF6_DEBUG_SPF(TIME)) + zlog_debug( + "SPF processing: # Areas: %d, SPF runtime: %lld sec %lld usec, Reason: %s", + areas_processed, (long long)runtime.tv_sec, + (long long)runtime.tv_usec, rbuf); + + ospf6->last_spf_reason = ospf6->spf_reason; + ospf6_reset_spf_reason(ospf6); +} + +/* Add schedule for SPF calculation. To avoid frequenst SPF calc, we + set timer for SPF calc. */ +void ospf6_spf_schedule(struct ospf6 *ospf6, unsigned int reason) +{ + unsigned long delay, elapsed, ht; + + /* OSPF instance does not exist. */ + if (ospf6 == NULL) + return; + + ospf6_set_spf_reason(ospf6, reason); + + if (IS_OSPF6_DEBUG_SPF(PROCESS) || IS_OSPF6_DEBUG_SPF(TIME)) { + char rbuf[32]; + ospf6_spf_reason_string(reason, rbuf, sizeof(rbuf)); + zlog_debug("SPF: calculation timer scheduled (reason %s)", + rbuf); + } + + /* SPF calculation timer is already scheduled. */ + if (event_is_scheduled(ospf6->t_spf_calc)) { + if (IS_OSPF6_DEBUG_SPF(PROCESS) || IS_OSPF6_DEBUG_SPF(TIME)) + zlog_debug( + "SPF: calculation timer is already scheduled: %p", + (void *)ospf6->t_spf_calc); + return; + } + + elapsed = monotime_since(&ospf6->ts_spf, NULL) / 1000LL; + ht = ospf6->spf_holdtime * ospf6->spf_hold_multiplier; + + if (ht > ospf6->spf_max_holdtime) + ht = ospf6->spf_max_holdtime; + + /* Get SPF calculation delay time. */ + if (elapsed < ht) { + /* Got an event within the hold time of last SPF. We need to + * increase the hold_multiplier, if it's not already at/past + * maximum value, and wasn't already increased.. + */ + if (ht < ospf6->spf_max_holdtime) + ospf6->spf_hold_multiplier++; + + /* always honour the SPF initial delay */ + if ((ht - elapsed) < ospf6->spf_delay) + delay = ospf6->spf_delay; + else + delay = ht - elapsed; + } else { + /* Event is past required hold-time of last SPF */ + delay = ospf6->spf_delay; + ospf6->spf_hold_multiplier = 1; + } + + if (IS_OSPF6_DEBUG_SPF(PROCESS) || IS_OSPF6_DEBUG_SPF(TIME)) + zlog_debug("SPF: Rescheduling in %ld msec", delay); + + EVENT_OFF(ospf6->t_spf_calc); + event_add_timer_msec(master, ospf6_spf_calculation_thread, ospf6, delay, + &ospf6->t_spf_calc); +} + +void ospf6_spf_display_subtree(struct vty *vty, const char *prefix, int rest, + struct ospf6_vertex *v, json_object *json_obj, + bool use_json) +{ + struct listnode *node, *nnode; + struct ospf6_vertex *c; + char *next_prefix; + int len; + int restnum; + json_object *json_childs = NULL; + json_object *json_child = NULL; + + if (use_json) { + json_childs = json_object_new_object(); + json_object_int_add(json_obj, "cost", v->cost); + } else { + /* "prefix" is the space prefix of the display line */ + vty_out(vty, "%s+-%s [%d]\n", prefix, v->name, v->cost); + } + + len = strlen(prefix) + 4; + next_prefix = (char *)malloc(len); + if (next_prefix == NULL) { + vty_out(vty, "malloc failed\n"); + return; + } + snprintf(next_prefix, len, "%s%s", prefix, (rest ? "| " : " ")); + + restnum = listcount(v->child_list); + for (ALL_LIST_ELEMENTS(v->child_list, node, nnode, c)) { + if (use_json) + json_child = json_object_new_object(); + else + restnum--; + + ospf6_spf_display_subtree(vty, next_prefix, restnum, c, + json_child, use_json); + + if (use_json) + json_object_object_add(json_childs, c->name, + json_child); + } + if (use_json) { + json_object_boolean_add(json_obj, "isLeafNode", + !listcount(v->child_list)); + if (listcount(v->child_list)) + json_object_object_add(json_obj, "children", + json_childs); + else + json_object_free(json_childs); + } + free(next_prefix); +} + +DEFUN (debug_ospf6_spf_process, + debug_ospf6_spf_process_cmd, + "debug ospf6 spf process", + DEBUG_STR + OSPF6_STR + "Debug SPF Calculation\n" + "Debug Detailed SPF Process\n" + ) +{ + unsigned char level = 0; + level = OSPF6_DEBUG_SPF_PROCESS; + OSPF6_DEBUG_SPF_ON(level); + return CMD_SUCCESS; +} + +DEFUN (debug_ospf6_spf_time, + debug_ospf6_spf_time_cmd, + "debug ospf6 spf time", + DEBUG_STR + OSPF6_STR + "Debug SPF Calculation\n" + "Measure time taken by SPF Calculation\n" + ) +{ + unsigned char level = 0; + level = OSPF6_DEBUG_SPF_TIME; + OSPF6_DEBUG_SPF_ON(level); + return CMD_SUCCESS; +} + +DEFUN (debug_ospf6_spf_database, + debug_ospf6_spf_database_cmd, + "debug ospf6 spf database", + DEBUG_STR + OSPF6_STR + "Debug SPF Calculation\n" + "Log number of LSAs at SPF Calculation time\n" + ) +{ + unsigned char level = 0; + level = OSPF6_DEBUG_SPF_DATABASE; + OSPF6_DEBUG_SPF_ON(level); + return CMD_SUCCESS; +} + +DEFUN (no_debug_ospf6_spf_process, + no_debug_ospf6_spf_process_cmd, + "no debug ospf6 spf process", + NO_STR + DEBUG_STR + OSPF6_STR + "Quit Debugging SPF Calculation\n" + "Quit Debugging Detailed SPF Process\n" + ) +{ + unsigned char level = 0; + level = OSPF6_DEBUG_SPF_PROCESS; + OSPF6_DEBUG_SPF_OFF(level); + return CMD_SUCCESS; +} + +DEFUN (no_debug_ospf6_spf_time, + no_debug_ospf6_spf_time_cmd, + "no debug ospf6 spf time", + NO_STR + DEBUG_STR + OSPF6_STR + "Quit Debugging SPF Calculation\n" + "Quit Measuring time taken by SPF Calculation\n" + ) +{ + unsigned char level = 0; + level = OSPF6_DEBUG_SPF_TIME; + OSPF6_DEBUG_SPF_OFF(level); + return CMD_SUCCESS; +} + +DEFUN (no_debug_ospf6_spf_database, + no_debug_ospf6_spf_database_cmd, + "no debug ospf6 spf database", + NO_STR + DEBUG_STR + OSPF6_STR + "Debug SPF Calculation\n" + "Quit Logging number of LSAs at SPF Calculation time\n" + ) +{ + unsigned char level = 0; + level = OSPF6_DEBUG_SPF_DATABASE; + OSPF6_DEBUG_SPF_OFF(level); + return CMD_SUCCESS; +} + +static int ospf6_timers_spf_set(struct vty *vty, unsigned int delay, + unsigned int hold, unsigned int max) +{ + VTY_DECLVAR_CONTEXT(ospf6, ospf); + + ospf->spf_delay = delay; + ospf->spf_holdtime = hold; + ospf->spf_max_holdtime = max; + + return CMD_SUCCESS; +} + +DEFUN (ospf6_timers_throttle_spf, + ospf6_timers_throttle_spf_cmd, + "timers throttle spf (0-600000) (0-600000) (0-600000)", + "Adjust routing timers\n" + "Throttling adaptive timer\n" + "OSPF6 SPF timers\n" + "Delay (msec) from first change received till SPF calculation\n" + "Initial hold time (msec) between consecutive SPF calculations\n" + "Maximum hold time (msec)\n") +{ + int idx_number = 3; + int idx_number_2 = 4; + int idx_number_3 = 5; + unsigned int delay, hold, max; + + delay = strtoul(argv[idx_number]->arg, NULL, 10); + hold = strtoul(argv[idx_number_2]->arg, NULL, 10); + max = strtoul(argv[idx_number_3]->arg, NULL, 10); + + return ospf6_timers_spf_set(vty, delay, hold, max); +} + +DEFUN (no_ospf6_timers_throttle_spf, + no_ospf6_timers_throttle_spf_cmd, + "no timers throttle spf [(0-600000) (0-600000) (0-600000)]", + NO_STR + "Adjust routing timers\n" + "Throttling adaptive timer\n" + "OSPF6 SPF timers\n" + "Delay (msec) from first change received till SPF calculation\n" + "Initial hold time (msec) between consecutive SPF calculations\n" + "Maximum hold time (msec)\n") +{ + return ospf6_timers_spf_set(vty, OSPF_SPF_DELAY_DEFAULT, + OSPF_SPF_HOLDTIME_DEFAULT, + OSPF_SPF_MAX_HOLDTIME_DEFAULT); +} + + +int config_write_ospf6_debug_spf(struct vty *vty) +{ + if (IS_OSPF6_DEBUG_SPF(PROCESS)) + vty_out(vty, "debug ospf6 spf process\n"); + if (IS_OSPF6_DEBUG_SPF(TIME)) + vty_out(vty, "debug ospf6 spf time\n"); + if (IS_OSPF6_DEBUG_SPF(DATABASE)) + vty_out(vty, "debug ospf6 spf database\n"); + return 0; +} + +void ospf6_spf_config_write(struct vty *vty, struct ospf6 *ospf6) +{ + + if (ospf6->spf_delay != OSPF_SPF_DELAY_DEFAULT + || ospf6->spf_holdtime != OSPF_SPF_HOLDTIME_DEFAULT + || ospf6->spf_max_holdtime != OSPF_SPF_MAX_HOLDTIME_DEFAULT) + vty_out(vty, " timers throttle spf %d %d %d\n", + ospf6->spf_delay, ospf6->spf_holdtime, + ospf6->spf_max_holdtime); +} + +void install_element_ospf6_debug_spf(void) +{ + install_element(ENABLE_NODE, &debug_ospf6_spf_process_cmd); + install_element(ENABLE_NODE, &debug_ospf6_spf_time_cmd); + install_element(ENABLE_NODE, &debug_ospf6_spf_database_cmd); + install_element(ENABLE_NODE, &no_debug_ospf6_spf_process_cmd); + install_element(ENABLE_NODE, &no_debug_ospf6_spf_time_cmd); + install_element(ENABLE_NODE, &no_debug_ospf6_spf_database_cmd); + install_element(CONFIG_NODE, &debug_ospf6_spf_process_cmd); + install_element(CONFIG_NODE, &debug_ospf6_spf_time_cmd); + install_element(CONFIG_NODE, &debug_ospf6_spf_database_cmd); + install_element(CONFIG_NODE, &no_debug_ospf6_spf_process_cmd); + install_element(CONFIG_NODE, &no_debug_ospf6_spf_time_cmd); + install_element(CONFIG_NODE, &no_debug_ospf6_spf_database_cmd); +} + +void ospf6_spf_init(void) +{ + install_element(OSPF6_NODE, &ospf6_timers_throttle_spf_cmd); + install_element(OSPF6_NODE, &no_ospf6_timers_throttle_spf_cmd); +} + +/* Create Aggregated Large Router-LSA from multiple Link-State IDs + * RFC 5340 A 4.3: + * When more than one router-LSA is received from a single router, + * the links are processed as if concatenated into a single LSA.*/ +struct ospf6_lsa *ospf6_create_single_router_lsa(struct ospf6_area *area, + struct ospf6_lsdb *lsdb, + uint32_t adv_router) +{ + struct ospf6_lsa *lsa = NULL; + struct ospf6_lsa *rtr_lsa = NULL; + struct ospf6_lsa_header *lsa_header = NULL; + uint8_t *new_header = NULL; + const struct route_node *end = NULL; + uint16_t lsa_length, total_lsa_length = 0, num_lsa = 0; + uint16_t type = 0; + char ifbuf[16]; + uint32_t interface_id; + caddr_t lsd; + + lsa_length = sizeof(struct ospf6_lsa_header) + + sizeof(struct ospf6_router_lsa); + total_lsa_length = lsa_length; + type = htons(OSPF6_LSTYPE_ROUTER); + + /* First check Aggregated LSA formed earlier in Cache */ + lsa = ospf6_lsdb_lookup(type, htonl(0), adv_router, + area->temp_router_lsa_lsdb); + if (lsa) + return lsa; + + inet_ntop(AF_INET, &adv_router, ifbuf, sizeof(ifbuf)); + + /* Determine total LSA length from all link state ids */ + end = ospf6_lsdb_head(lsdb, 2, type, adv_router, &rtr_lsa); + while (rtr_lsa) { + lsa = rtr_lsa; + if (OSPF6_LSA_IS_MAXAGE(rtr_lsa)) { + rtr_lsa = ospf6_lsdb_next(end, rtr_lsa); + continue; + } + lsa_header = rtr_lsa->header; + total_lsa_length += (ntohs(lsa_header->length) - lsa_length); + num_lsa++; + rtr_lsa = ospf6_lsdb_next(end, rtr_lsa); + } + if (IS_OSPF6_DEBUG_SPF(PROCESS)) + zlog_debug("%s: adv_router %s num_lsa %u to convert.", __func__, + ifbuf, num_lsa); + if (num_lsa == 1) + return lsa; + + if (num_lsa == 0) { + if (IS_OSPF6_DEBUG_SPF(PROCESS)) + zlog_debug("%s: adv_router %s not found in LSDB.", + __func__, ifbuf); + return NULL; + } + + lsa = ospf6_lsa_alloc(total_lsa_length); + new_header = (uint8_t *)lsa->header; + + lsa->lsdb = area->temp_router_lsa_lsdb; + + /* Fill Larger LSA Payload */ + end = ospf6_lsdb_head(lsdb, 2, type, adv_router, &rtr_lsa); + + /* + * We assume at this point in time that rtr_lsa is + * a valid pointer. + */ + assert(rtr_lsa); + if (!OSPF6_LSA_IS_MAXAGE(rtr_lsa)) { + /* Append first Link State ID LSA */ + lsa_header = rtr_lsa->header; + memcpy(new_header, lsa_header, ntohs(lsa_header->length)); + /* Assign new lsa length as aggregated length. */ + ((struct ospf6_lsa_header *)new_header)->length = + htons(total_lsa_length); + new_header += ntohs(lsa_header->length); + num_lsa--; + } + + /* Print LSA Name */ + ospf6_lsa_printbuf(lsa, lsa->name, sizeof(lsa->name)); + + rtr_lsa = ospf6_lsdb_next(end, rtr_lsa); + while (rtr_lsa) { + if (OSPF6_LSA_IS_MAXAGE(rtr_lsa)) { + rtr_lsa = ospf6_lsdb_next(end, rtr_lsa); + continue; + } + + if (IS_OSPF6_DEBUG_SPF(PROCESS)) { + lsd = OSPF6_LSA_HEADER_END(rtr_lsa->header) + 4; + interface_id = ROUTER_LSDESC_GET_IFID(lsd); + inet_ntop(AF_INET, &interface_id, ifbuf, sizeof(ifbuf)); + zlog_debug( + "%s: Next Router LSA %s to aggreat with len %u interface_id %s", + __func__, rtr_lsa->name, + ntohs(lsa_header->length), ifbuf); + } + + /* Append Next Link State ID LSA */ + lsa_header = rtr_lsa->header; + memcpy(new_header, (OSPF6_LSA_HEADER_END(rtr_lsa->header) + 4), + (ntohs(lsa_header->length) - lsa_length)); + new_header += (ntohs(lsa_header->length) - lsa_length); + num_lsa--; + + rtr_lsa = ospf6_lsdb_next(end, rtr_lsa); + } + + /* Calculate birth of this lsa */ + ospf6_lsa_age_set(lsa); + + /* Store Aggregated LSA into area temp lsdb */ + ospf6_lsdb_add(lsa, area->temp_router_lsa_lsdb); + + if (IS_OSPF6_DEBUG_SPF(PROCESS)) + zlog_debug("%s: LSA %s id %u type 0%x len %u num_lsa %u", + __func__, lsa->name, ntohl(lsa->header->id), + ntohs(lsa->header->type), ntohs(lsa->header->length), + num_lsa); + + return lsa; +} + +void ospf6_remove_temp_router_lsa(struct ospf6_area *area) +{ + struct ospf6_lsa *lsa = NULL, *lsanext; + + for (ALL_LSDB(area->temp_router_lsa_lsdb, lsa, lsanext)) { + if (IS_OSPF6_DEBUG_SPF(PROCESS)) + zlog_debug( + "%s Remove LSA %s lsa->lock %u lsdb count %u", + __func__, lsa->name, lsa->lock, + area->temp_router_lsa_lsdb->count); + ospf6_lsdb_remove(lsa, area->temp_router_lsa_lsdb); + } +} + +int ospf6_ase_calculate_route(struct ospf6 *ospf6, struct ospf6_lsa *lsa, + struct ospf6_area *area) +{ + struct ospf6_route *route; + struct ospf6_as_external_lsa *external; + struct prefix prefix; + void (*hook_add)(struct ospf6_route *) = NULL; + void (*hook_remove)(struct ospf6_route *) = NULL; + + assert(lsa); + + if (IS_OSPF6_DEBUG_SPF(PROCESS)) + zlog_debug("%s : start", __func__); + + if (ntohs(lsa->header->type) == OSPF6_LSTYPE_TYPE_7) + if (IS_OSPF6_DEBUG_SPF(PROCESS)) + zlog_debug("%s: Processing Type-7", __func__); + + /* Stay away from any Local Translated Type-7 LSAs */ + if (CHECK_FLAG(lsa->flag, OSPF6_LSA_LOCAL_XLT)) { + if (IS_OSPF6_DEBUG_SPF(PROCESS)) + zlog_debug("%s: Rejecting Local translated LSA", + __func__); + return 0; + } + + external = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END( + lsa->header); + prefix.family = AF_INET6; + prefix.prefixlen = external->prefix.prefix_length; + ospf6_prefix_in6_addr(&prefix.u.prefix6, external, &external->prefix); + + if (ntohs(lsa->header->type) == OSPF6_LSTYPE_AS_EXTERNAL) { + hook_add = ospf6->route_table->hook_add; + hook_remove = ospf6->route_table->hook_remove; + ospf6->route_table->hook_add = NULL; + ospf6->route_table->hook_remove = NULL; + + if (!OSPF6_LSA_IS_MAXAGE(lsa)) + ospf6_asbr_lsa_add(lsa); + + ospf6->route_table->hook_add = hook_add; + ospf6->route_table->hook_remove = hook_remove; + + route = ospf6_route_lookup(&prefix, ospf6->route_table); + if (route == NULL) { + if (IS_OSPF6_DEBUG_SPF(PROCESS)) + zlog_debug("%s: no external route %pFX", + __func__, &prefix); + return 0; + } + + if (CHECK_FLAG(route->flag, OSPF6_ROUTE_REMOVE) + && CHECK_FLAG(route->flag, OSPF6_ROUTE_ADD)) { + UNSET_FLAG(route->flag, OSPF6_ROUTE_REMOVE); + UNSET_FLAG(route->flag, OSPF6_ROUTE_ADD); + } + + if (CHECK_FLAG(route->flag, OSPF6_ROUTE_REMOVE)) + ospf6_route_remove(route, ospf6->route_table); + else if (CHECK_FLAG(route->flag, OSPF6_ROUTE_ADD) + || CHECK_FLAG(route->flag, OSPF6_ROUTE_CHANGE)) { + if (hook_add) { + if (IS_OSPF6_DEBUG_SPF(PROCESS)) + zlog_debug( + "%s: add external route %pFX", + __func__, &prefix); + (*hook_add)(route); + } + } + } else if (ntohs(lsa->header->type) == OSPF6_LSTYPE_TYPE_7) { + hook_add = area->route_table->hook_add; + hook_remove = area->route_table->hook_remove; + area->route_table->hook_add = NULL; + area->route_table->hook_remove = NULL; + + if (!OSPF6_LSA_IS_MAXAGE(lsa)) + ospf6_asbr_lsa_add(lsa); + + area->route_table->hook_add = hook_add; + area->route_table->hook_remove = hook_remove; + + route = ospf6_route_lookup(&prefix, area->route_table); + if (route == NULL) { + if (IS_OSPF6_DEBUG_SPF(PROCESS)) + zlog_debug("%s: no route %pFX, area %s", + __func__, &prefix, area->name); + return 0; + } + + if (CHECK_FLAG(route->flag, OSPF6_ROUTE_REMOVE) + && CHECK_FLAG(route->flag, OSPF6_ROUTE_ADD)) { + UNSET_FLAG(route->flag, OSPF6_ROUTE_REMOVE); + UNSET_FLAG(route->flag, OSPF6_ROUTE_ADD); + } + + if (CHECK_FLAG(route->flag, OSPF6_ROUTE_REMOVE)) { + if (IS_OSPF6_DEBUG_SPF(PROCESS)) + zlog_debug("%s : remove route %pFX, area %s", + __func__, &prefix, area->name); + ospf6_route_remove(route, area->route_table); + } else if (CHECK_FLAG(route->flag, OSPF6_ROUTE_ADD) + || CHECK_FLAG(route->flag, OSPF6_ROUTE_CHANGE)) { + if (hook_add) { + if (IS_OSPF6_DEBUG_SPF(PROCESS)) + zlog_debug( + "%s: add nssa route %pFX, area %s", + __func__, &prefix, area->name); + (*hook_add)(route); + } + ospf6_abr_check_translate_nssa(area, lsa); + } + } + return 0; +} + +static void ospf6_ase_calculate_timer(struct event *t) +{ + struct ospf6 *ospf6; + struct ospf6_lsa *lsa; + struct listnode *node, *nnode; + struct ospf6_area *area; + uint16_t type; + + ospf6 = EVENT_ARG(t); + + /* Calculate external route for each AS-external-LSA */ + type = htons(OSPF6_LSTYPE_AS_EXTERNAL); + for (ALL_LSDB_TYPED(ospf6->lsdb, type, lsa)) + ospf6_ase_calculate_route(ospf6, lsa, NULL); + + /* This version simple adds to the table all NSSA areas */ + if (ospf6->anyNSSA) { + for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, area)) { + if (IS_OSPF6_DEBUG_SPF(PROCESS)) + zlog_debug("%s : looking at area %s", __func__, + area->name); + + type = htons(OSPF6_LSTYPE_TYPE_7); + for (ALL_LSDB_TYPED(area->lsdb, type, lsa)) + ospf6_ase_calculate_route(ospf6, lsa, area); + } + } + + if (ospf6->gr_info.finishing_restart) { + /* + * The routing table computation is complete. Uninstall remnant + * routes that were installed before the restart, but that are + * no longer valid. + */ + ospf6_zebra_gr_disable(ospf6); + ospf6_zebra_gr_enable(ospf6, ospf6->gr_info.grace_period); + ospf6->gr_info.finishing_restart = false; + } +} + +void ospf6_ase_calculate_timer_add(struct ospf6 *ospf6) +{ + if (ospf6 == NULL) + return; + + event_add_timer(master, ospf6_ase_calculate_timer, ospf6, + OSPF6_ASE_CALC_INTERVAL, &ospf6->t_ase_calc); +} + +bool ospf6_merge_parents_nh_to_child(struct ospf6_vertex *v, + struct ospf6_route *route, + struct ospf6_route_table *result_table) +{ + struct ospf6_route *parent_route; + + if (v->parent && v->parent->hops) { + parent_route = + ospf6_route_lookup(&v->parent->vertex_id, result_table); + if (parent_route) { + ospf6_route_merge_nexthops(route, parent_route); + return true; + } + } + return false; +} diff --git a/ospf6d/ospf6_spf.h b/ospf6d/ospf6_spf.h new file mode 100644 index 0000000..c2fd5d9 --- /dev/null +++ b/ospf6d/ospf6_spf.h @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2003 Yasuhiro Ohara + */ + +#ifndef OSPF6_SPF_H +#define OSPF6_SPF_H + +#include "typesafe.h" +#include "ospf6_top.h" +#include "lib/json.h" + +/* Debug option */ +extern unsigned char conf_debug_ospf6_spf; +#define OSPF6_DEBUG_SPF_PROCESS 0x01 +#define OSPF6_DEBUG_SPF_TIME 0x02 +#define OSPF6_DEBUG_SPF_DATABASE 0x04 +#define OSPF6_DEBUG_SPF_ON(level) (conf_debug_ospf6_spf |= (level)) +#define OSPF6_DEBUG_SPF_OFF(level) (conf_debug_ospf6_spf &= ~(level)) +#define IS_OSPF6_DEBUG_SPF(level) \ + (conf_debug_ospf6_spf & OSPF6_DEBUG_SPF_##level) + +#define OSPF6_ASE_CALC_INTERVAL 1 + +PREDECL_SKIPLIST_NONUNIQ(vertex_pqueue); +/* Transit Vertex */ +struct ospf6_vertex { + /* type of this vertex */ + uint8_t type; + + /* Vertex Identifier */ + struct prefix vertex_id; + + struct vertex_pqueue_item pqi; + + /* Identifier String */ + char name[128]; + + /* Associated Area */ + struct ospf6_area *area; + + /* Associated LSA */ + struct ospf6_lsa *lsa; + + /* Distance from Root (i.e. Cost) */ + uint32_t cost; + + /* Router hops to this node */ + uint8_t hops; + + /* capability bits */ + uint8_t capability; + + /* Optional capabilities */ + uint8_t options[3]; + + /* For tree display */ + struct ospf6_vertex *parent; + struct list *child_list; + + /* nexthops to this node */ + struct list *nh_list; + uint32_t link_id; +}; + +#define OSPF6_VERTEX_TYPE_ROUTER 0x01 +#define OSPF6_VERTEX_TYPE_NETWORK 0x02 +#define VERTEX_IS_TYPE(t, v) ((v)->type == OSPF6_VERTEX_TYPE_##t ? 1 : 0) + +/* What triggered the SPF? */ +#define OSPF6_SPF_FLAGS_ROUTER_LSA_ADDED (1 << 0) +#define OSPF6_SPF_FLAGS_ROUTER_LSA_REMOVED (1 << 1) +#define OSPF6_SPF_FLAGS_NETWORK_LSA_ADDED (1 << 2) +#define OSPF6_SPF_FLAGS_NETWORK_LSA_REMOVED (1 << 3) +#define OSPF6_SPF_FLAGS_LINK_LSA_ADDED (1 << 4) +#define OSPF6_SPF_FLAGS_LINK_LSA_REMOVED (1 << 5) +#define OSPF6_SPF_FLAGS_ROUTER_LSA_ORIGINATED (1 << 6) +#define OSPF6_SPF_FLAGS_NETWORK_LSA_ORIGINATED (1 << 7) +#define OSPF6_SPF_FLAGS_CONFIG_CHANGE (1 << 8) +#define OSPF6_SPF_FLAGS_ASBR_STATUS_CHANGE (1 << 9) +#define OSPF6_SPF_FLAGS_GR_FINISH (1 << 10) + +static inline void ospf6_set_spf_reason(struct ospf6 *ospf, unsigned int reason) +{ + ospf->spf_reason |= reason; +} + +static inline void ospf6_reset_spf_reason(struct ospf6 *ospf) +{ + ospf->spf_reason = 0; +} + +static inline unsigned int ospf6_lsadd_to_spf_reason(struct ospf6_lsa *lsa) +{ + unsigned int reason = 0; + + switch (ntohs(lsa->header->type)) { + case OSPF6_LSTYPE_ROUTER: + reason = OSPF6_SPF_FLAGS_ROUTER_LSA_ADDED; + break; + case OSPF6_LSTYPE_NETWORK: + reason = OSPF6_SPF_FLAGS_NETWORK_LSA_ADDED; + break; + case OSPF6_LSTYPE_LINK: + reason = OSPF6_SPF_FLAGS_LINK_LSA_ADDED; + break; + default: + break; + } + return (reason); +} + +static inline unsigned int ospf6_lsremove_to_spf_reason(struct ospf6_lsa *lsa) +{ + unsigned int reason = 0; + + switch (ntohs(lsa->header->type)) { + case OSPF6_LSTYPE_ROUTER: + reason = OSPF6_SPF_FLAGS_ROUTER_LSA_REMOVED; + break; + case OSPF6_LSTYPE_NETWORK: + reason = OSPF6_SPF_FLAGS_NETWORK_LSA_REMOVED; + break; + case OSPF6_LSTYPE_LINK: + reason = OSPF6_SPF_FLAGS_LINK_LSA_REMOVED; + break; + default: + break; + } + return (reason); +} + +extern void ospf6_spf_table_finish(struct ospf6_route_table *result_table); +extern void ospf6_spf_calculation(uint32_t router_id, + struct ospf6_route_table *result_table, + struct ospf6_area *oa); +extern void ospf6_spf_schedule(struct ospf6 *ospf, unsigned int reason); + +extern void ospf6_spf_display_subtree(struct vty *vty, const char *prefix, + int rest, struct ospf6_vertex *v, + json_object *json_obj, bool use_json); + +extern void ospf6_spf_config_write(struct vty *vty, struct ospf6 *ospf6); +extern int config_write_ospf6_debug_spf(struct vty *vty); +extern void install_element_ospf6_debug_spf(void); +extern void ospf6_spf_init(void); +extern void ospf6_spf_reason_string(unsigned int reason, char *buf, int size); +extern struct ospf6_lsa *ospf6_create_single_router_lsa(struct ospf6_area *area, + struct ospf6_lsdb *lsdb, + uint32_t adv_router); +extern void ospf6_remove_temp_router_lsa(struct ospf6_area *area); +extern void ospf6_ase_calculate_timer_add(struct ospf6 *ospf6); +extern int ospf6_ase_calculate_route(struct ospf6 *ospf6, struct ospf6_lsa *lsa, + struct ospf6_area *area); +extern bool +ospf6_merge_parents_nh_to_child(struct ospf6_vertex *v, + struct ospf6_route *route, + struct ospf6_route_table *result_table); +#endif /* OSPF6_SPF_H */ diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c new file mode 100644 index 0000000..216837d --- /dev/null +++ b/ospf6d/ospf6_top.c @@ -0,0 +1,2373 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2003 Yasuhiro Ohara + */ + +#include <zebra.h> + +#include "log.h" +#include "memory.h" +#include "vty.h" +#include "linklist.h" +#include "prefix.h" +#include "table.h" +#include "frrevent.h" +#include "command.h" +#include "defaults.h" +#include "lib/json.h" +#include "lib_errors.h" + +#include "ospf6_proto.h" +#include "ospf6_message.h" +#include "ospf6_lsa.h" +#include "ospf6_lsdb.h" +#include "ospf6_route.h" +#include "ospf6_zebra.h" + +#include "ospf6_top.h" +#include "ospf6_area.h" +#include "ospf6_interface.h" +#include "ospf6_neighbor.h" +#include "ospf6_network.h" + +#include "ospf6_flood.h" +#include "ospf6_asbr.h" +#include "ospf6_abr.h" +#include "ospf6_intra.h" +#include "ospf6_spf.h" +#include "ospf6d.h" +#include "ospf6_gr.h" +#include "lib/json.h" +#include "ospf6_nssa.h" +#include "ospf6_auth_trailer.h" + +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_TOP, "OSPF6 top"); + +DEFINE_QOBJ_TYPE(ospf6); + +FRR_CFG_DEFAULT_BOOL(OSPF6_LOG_ADJACENCY_CHANGES, + { .val_bool = true, .match_profile = "datacenter", }, + { .val_bool = false }, +); + +#include "ospf6d/ospf6_top_clippy.c" + +/* global ospf6d variable */ +static struct ospf6_master ospf6_master; +struct ospf6_master *om6; + +static void ospf6_disable(struct ospf6 *o); + +static void ospf6_add(struct ospf6 *ospf6) +{ + listnode_add(om6->ospf6, ospf6); +} + +static void ospf6_del(struct ospf6 *ospf6) +{ + listnode_delete(om6->ospf6, ospf6); +} + +const char *ospf6_vrf_id_to_name(vrf_id_t vrf_id) +{ + struct vrf *vrf = vrf_lookup_by_id(vrf_id); + + return vrf ? vrf->name : "NIL"; +} + +/* Link OSPF instance to VRF. */ +void ospf6_vrf_link(struct ospf6 *ospf6, struct vrf *vrf) +{ + ospf6->vrf_id = vrf->vrf_id; + if (vrf->info != (void *)ospf6) + vrf->info = (void *)ospf6; +} + +/* Unlink OSPF instance from VRF. */ +void ospf6_vrf_unlink(struct ospf6 *ospf6, struct vrf *vrf) +{ + if (vrf->info == (void *)ospf6) + vrf->info = NULL; + ospf6->vrf_id = VRF_UNKNOWN; +} + +struct ospf6 *ospf6_lookup_by_vrf_id(vrf_id_t vrf_id) +{ + struct vrf *vrf = NULL; + + vrf = vrf_lookup_by_id(vrf_id); + if (!vrf) + return NULL; + return (vrf->info) ? (struct ospf6 *)vrf->info : NULL; +} + +struct ospf6 *ospf6_lookup_by_vrf_name(const char *name) +{ + struct ospf6 *o = NULL; + struct listnode *node, *nnode; + + for (ALL_LIST_ELEMENTS(om6->ospf6, node, nnode, o)) { + if (((o->name == NULL && name == NULL) + || (o->name && name && strcmp(o->name, name) == 0))) + return o; + } + return NULL; +} + +/* This is hook function for vrf create called as part of vrf_init */ +static int ospf6_vrf_new(struct vrf *vrf) +{ + return 0; +} + +/* This is hook function for vrf delete call as part of vrf_init */ +static int ospf6_vrf_delete(struct vrf *vrf) +{ + return 0; +} + +static void ospf6_set_redist_vrf_bitmaps(struct ospf6 *ospf6, bool set) +{ + int type; + struct list *red_list; + + for (type = 0; type < ZEBRA_ROUTE_MAX; type++) { + red_list = ospf6->redist[type]; + if (!red_list) + continue; + if (IS_OSPF6_DEBUG_ZEBRA(RECV)) + zlog_debug( + "%s: setting redist vrf %d bitmap for type %d", + __func__, ospf6->vrf_id, type); + if (set) + vrf_bitmap_set(&zclient->redist[AFI_IP6][type], + ospf6->vrf_id); + else + vrf_bitmap_unset(&zclient->redist[AFI_IP6][type], + ospf6->vrf_id); + } + + red_list = ospf6->redist[DEFAULT_ROUTE]; + if (red_list) { + if (set) + vrf_bitmap_set(&zclient->default_information[AFI_IP6], + ospf6->vrf_id); + else + vrf_bitmap_unset(&zclient->default_information[AFI_IP6], + ospf6->vrf_id); + } +} + +/* Disable OSPF6 VRF instance */ +static int ospf6_vrf_disable(struct vrf *vrf) +{ + struct ospf6 *ospf6 = NULL; + + if (vrf->vrf_id == VRF_DEFAULT) + return 0; + + ospf6 = ospf6_lookup_by_vrf_name(vrf->name); + if (ospf6) { + ospf6_zebra_vrf_deregister(ospf6); + + ospf6_set_redist_vrf_bitmaps(ospf6, false); + + /* We have instance configured, unlink + * from VRF and make it "down". + */ + ospf6_vrf_unlink(ospf6, vrf); + event_cancel(&ospf6->t_ospf6_receive); + close(ospf6->fd); + ospf6->fd = -1; + } + + /* Note: This is a callback, the VRF will be deleted by the caller. */ + return 0; +} + +/* Enable OSPF6 VRF instance */ +static int ospf6_vrf_enable(struct vrf *vrf) +{ + struct ospf6 *ospf6 = NULL; + vrf_id_t old_vrf_id; + int ret = 0; + + ospf6 = ospf6_lookup_by_vrf_name(vrf->name); + if (ospf6) { + old_vrf_id = ospf6->vrf_id; + /* We have instance configured, link to VRF and make it "up". */ + ospf6_vrf_link(ospf6, vrf); + + if (old_vrf_id != ospf6->vrf_id) { + ospf6_set_redist_vrf_bitmaps(ospf6, true); + + /* start zebra redist to us for new vrf */ + ospf6_zebra_vrf_register(ospf6); + + ret = ospf6_serv_sock(ospf6); + if (ret < 0 || ospf6->fd <= 0) + return 0; + event_add_read(master, ospf6_receive, ospf6, ospf6->fd, + &ospf6->t_ospf6_receive); + + ospf6_router_id_update(ospf6, true); + } + } + + return 0; +} + +void ospf6_vrf_init(void) +{ + vrf_init(ospf6_vrf_new, ospf6_vrf_enable, ospf6_vrf_disable, + ospf6_vrf_delete); + + vrf_cmd_init(NULL); +} + +static void ospf6_top_lsdb_hook_add(struct ospf6_lsa *lsa) +{ + switch (ntohs(lsa->header->type)) { + case OSPF6_LSTYPE_AS_EXTERNAL: + ospf6_asbr_lsa_add(lsa); + break; + + default: + break; + } +} + +static void ospf6_top_lsdb_hook_remove(struct ospf6_lsa *lsa) +{ + switch (ntohs(lsa->header->type)) { + case OSPF6_LSTYPE_AS_EXTERNAL: + ospf6_asbr_lsa_remove(lsa, NULL); + break; + + default: + break; + } +} + +static void ospf6_top_route_hook_add(struct ospf6_route *route) +{ + struct ospf6 *ospf6 = NULL; + struct ospf6_area *oa = NULL; + + if (route->table->scope_type == OSPF6_SCOPE_TYPE_GLOBAL) + ospf6 = route->table->scope; + else if (route->table->scope_type == OSPF6_SCOPE_TYPE_AREA) { + oa = (struct ospf6_area *)route->table->scope; + ospf6 = oa->ospf6; + } else { + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL) + || IS_OSPF6_DEBUG_BROUTER) + zlog_debug( + "%s: Route is not GLOBAL or scope is not of TYPE_AREA: %pFX", + __func__, &route->prefix); + return; + } + + ospf6_abr_originate_summary(route, ospf6); + ospf6_zebra_route_update_add(route, ospf6); +} + +static void ospf6_top_route_hook_remove(struct ospf6_route *route) +{ + struct ospf6 *ospf6 = NULL; + struct ospf6_area *oa = NULL; + + if (route->table->scope_type == OSPF6_SCOPE_TYPE_GLOBAL) + ospf6 = route->table->scope; + else if (route->table->scope_type == OSPF6_SCOPE_TYPE_AREA) { + oa = (struct ospf6_area *)route->table->scope; + ospf6 = oa->ospf6; + } else { + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL) + || IS_OSPF6_DEBUG_BROUTER) + zlog_debug( + "%s: Route is not GLOBAL or scope is not of TYPE_AREA: %pFX", + __func__, &route->prefix); + return; + } + + route->flag |= OSPF6_ROUTE_REMOVE; + ospf6_abr_originate_summary(route, ospf6); + ospf6_zebra_route_update_remove(route, ospf6); +} + +static void ospf6_top_brouter_hook_add(struct ospf6_route *route) +{ + struct ospf6 *ospf6 = route->table->scope; + + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL) || + IS_OSPF6_DEBUG_BROUTER) { + uint32_t brouter_id; + char brouter_name[16]; + + brouter_id = ADV_ROUTER_IN_PREFIX(&route->prefix); + inet_ntop(AF_INET, &brouter_id, brouter_name, + sizeof(brouter_name)); + zlog_debug("%s: brouter %s add with adv router %x nh count %u", + __func__, brouter_name, + route->path.origin.adv_router, + listcount(route->nh_list)); + } + ospf6_abr_examin_brouter(ADV_ROUTER_IN_PREFIX(&route->prefix), route, + ospf6); + ospf6_asbr_lsentry_add(route, ospf6); + ospf6_abr_originate_summary(route, ospf6); +} + +static void ospf6_top_brouter_hook_remove(struct ospf6_route *route) +{ + struct ospf6 *ospf6 = route->table->scope; + + if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL) || + IS_OSPF6_DEBUG_BROUTER) { + uint32_t brouter_id; + char brouter_name[16]; + + brouter_id = ADV_ROUTER_IN_PREFIX(&route->prefix); + inet_ntop(AF_INET, &brouter_id, brouter_name, + sizeof(brouter_name)); + zlog_debug("%s: brouter %p %s del with adv router %x nh %u", + __func__, (void *)route, brouter_name, + route->path.origin.adv_router, + listcount(route->nh_list)); + } + route->flag |= OSPF6_ROUTE_REMOVE; + ospf6_abr_examin_brouter(ADV_ROUTER_IN_PREFIX(&route->prefix), route, + ospf6); + ospf6_asbr_lsentry_remove(route, ospf6); + ospf6_abr_originate_summary(route, ospf6); +} + +static struct ospf6 *ospf6_create(const char *name) +{ + struct ospf6 *o; + struct vrf *vrf = NULL; + + o = XCALLOC(MTYPE_OSPF6_TOP, sizeof(struct ospf6)); + + vrf = vrf_lookup_by_name(name); + if (vrf) { + o->vrf_id = vrf->vrf_id; + } else + o->vrf_id = VRF_UNKNOWN; + + /* Freed in ospf6_delete */ + o->name = XSTRDUP(MTYPE_OSPF6_TOP, name); + if (vrf) + ospf6_vrf_link(o, vrf); + + ospf6_zebra_vrf_register(o); + + /* initialize */ + monotime(&o->starttime); + o->area_list = list_new(); + o->area_list->cmp = ospf6_area_cmp; + o->lsdb = ospf6_lsdb_create(o); + o->lsdb_self = ospf6_lsdb_create(o); + o->lsdb->hook_add = ospf6_top_lsdb_hook_add; + o->lsdb->hook_remove = ospf6_top_lsdb_hook_remove; + + o->spf_delay = OSPF_SPF_DELAY_DEFAULT; + o->spf_holdtime = OSPF_SPF_HOLDTIME_DEFAULT; + o->spf_max_holdtime = OSPF_SPF_MAX_HOLDTIME_DEFAULT; + o->spf_hold_multiplier = 1; + + o->default_originate = DEFAULT_ORIGINATE_NONE; + o->redistribute = 0; + /* LSA timers value init */ + o->lsa_minarrival = OSPF_MIN_LS_ARRIVAL; + + o->route_table = OSPF6_ROUTE_TABLE_CREATE(GLOBAL, ROUTES); + o->route_table->scope = o; + o->route_table->hook_add = ospf6_top_route_hook_add; + o->route_table->hook_remove = ospf6_top_route_hook_remove; + + o->brouter_table = OSPF6_ROUTE_TABLE_CREATE(GLOBAL, BORDER_ROUTERS); + o->brouter_table->scope = o; + o->brouter_table->hook_add = ospf6_top_brouter_hook_add; + o->brouter_table->hook_remove = ospf6_top_brouter_hook_remove; + + o->external_table = OSPF6_ROUTE_TABLE_CREATE(GLOBAL, EXTERNAL_ROUTES); + o->external_table->scope = o; + /* Setting this to 1, so that the LS ID 0 can be considered as invalid + * for self originated external LSAs. This helps in differentiating if + * an LSA is originated for any route or not in the route data. + * rt->route_option->id is by default 0 + * Consider a route having id as 0 and prefix as 1::1, an external LSA + * is originated with ID 0.0.0.0. Now consider another route 2::2 + * and for this LSA was not originated because of some configuration + * but the ID field rt->route_option->id is still 0.Consider now this + * 2::2 is being deleted, it will search LSA with LS ID as 0 and it + * will find the LSA and hence delete it but the LSA belonged to prefix + * 1::1, this happened because of LS ID 0. + */ + o->external_id = OSPF6_EXT_INIT_LS_ID; + + o->write_oi_count = OSPF6_WRITE_INTERFACE_COUNT_DEFAULT; + o->ref_bandwidth = OSPF6_REFERENCE_BANDWIDTH; + + o->distance_table = route_table_init(); + + o->rt_aggr_tbl = route_table_init(); + o->aggr_delay_interval = OSPF6_EXTL_AGGR_DEFAULT_DELAY; + o->aggr_action = OSPF6_ROUTE_AGGR_NONE; + + o->fd = -1; + + o->max_multipath = MULTIPATH_NUM; + + o->oi_write_q = list_new(); + + ospf6_gr_helper_init(o); + QOBJ_REG(o, ospf6); + + /* Make ospf protocol socket. */ + ospf6_serv_sock(o); + + /* If sequence number is stored in persistent storage, read it. + */ + if (ospf6_auth_nvm_file_exist() == OSPF6_AUTH_FILE_EXIST) { + ospf6_auth_seqno_nvm_read(o); + o->seqnum_h = o->seqnum_h + 1; + ospf6_auth_seqno_nvm_update(o); + } else { + o->seqnum_l = o->seqnum_h = 0; + ospf6_auth_seqno_nvm_update(o); + } + + return o; +} + +struct ospf6 *ospf6_instance_create(const char *name) +{ + struct ospf6 *ospf6; + struct vrf *vrf; + struct interface *ifp; + + ospf6 = ospf6_create(name); + if (DFLT_OSPF6_LOG_ADJACENCY_CHANGES) + SET_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_CHANGES); + if (ospf6->router_id == 0) + ospf6_router_id_update(ospf6, true); + ospf6_add(ospf6); + + /* + * Read from non-volatile memory whether this instance is performing a + * graceful restart or not. + */ + ospf6_gr_nvm_read(ospf6); + + if (ospf6->vrf_id != VRF_UNKNOWN) { + vrf = vrf_lookup_by_id(ospf6->vrf_id); + FOR_ALL_INTERFACES (vrf, ifp) { + if (ifp->info) + ospf6_interface_start(ifp->info); + } + } + if (ospf6->fd < 0) + return ospf6; + + event_add_read(master, ospf6_receive, ospf6, ospf6->fd, + &ospf6->t_ospf6_receive); + + return ospf6; +} + +void ospf6_delete(struct ospf6 *o) +{ + struct listnode *node, *nnode; + struct route_node *rn = NULL; + struct ospf6_area *oa; + struct vrf *vrf; + struct ospf6_external_aggr_rt *aggr; + + QOBJ_UNREG(o); + + ospf6_gr_helper_deinit(o); + if (!o->gr_info.prepare_in_progress) + ospf6_flush_self_originated_lsas_now(o); + XFREE(MTYPE_TMP, o->gr_info.exit_reason); + ospf6_disable(o); + ospf6_del(o); + + ospf6_zebra_vrf_deregister(o); + + ospf6_serv_close(&o->fd); + + for (ALL_LIST_ELEMENTS(o->area_list, node, nnode, oa)) + ospf6_area_delete(oa); + + + list_delete(&o->area_list); + + ospf6_lsdb_delete(o->lsdb); + ospf6_lsdb_delete(o->lsdb_self); + + ospf6_route_table_delete(o->route_table); + ospf6_route_table_delete(o->brouter_table); + + ospf6_route_table_delete(o->external_table); + + ospf6_distance_reset(o); + route_table_finish(o->distance_table); + list_delete(&o->oi_write_q); + + if (o->vrf_id != VRF_UNKNOWN) { + vrf = vrf_lookup_by_id(o->vrf_id); + if (vrf) + ospf6_vrf_unlink(o, vrf); + } + + for (rn = route_top(o->rt_aggr_tbl); rn; rn = route_next(rn)) + if (rn->info) { + aggr = rn->info; + ospf6_asbr_summary_config_delete(o, rn); + ospf6_external_aggregator_free(aggr); + } + route_table_finish(o->rt_aggr_tbl); + + XFREE(MTYPE_OSPF6_TOP, o->name); + XFREE(MTYPE_OSPF6_TOP, o); +} + +static void ospf6_disable(struct ospf6 *o) +{ + struct listnode *node, *nnode; + struct ospf6_area *oa; + + if (!CHECK_FLAG(o->flag, OSPF6_DISABLED)) { + SET_FLAG(o->flag, OSPF6_DISABLED); + + for (ALL_LIST_ELEMENTS(o->area_list, node, nnode, oa)) + ospf6_area_disable(oa); + + /* XXX: This also changes persistent settings */ + /* Unregister redistribution */ + ospf6_asbr_redistribute_disable(o); + + ospf6_lsdb_remove_all(o->lsdb); + ospf6_route_remove_all(o->route_table); + ospf6_route_remove_all(o->brouter_table); + + EVENT_OFF(o->maxage_remover); + EVENT_OFF(o->t_spf_calc); + EVENT_OFF(o->t_ase_calc); + EVENT_OFF(o->t_distribute_update); + EVENT_OFF(o->t_ospf6_receive); + EVENT_OFF(o->t_external_aggr); + EVENT_OFF(o->gr_info.t_grace_period); + EVENT_OFF(o->t_write); + EVENT_OFF(o->t_abr_task); + } +} + +void ospf6_master_init(struct event_loop *master) +{ + memset(&ospf6_master, 0, sizeof(ospf6_master)); + + om6 = &ospf6_master; + om6->ospf6 = list_new(); + om6->master = master; +} + +static void ospf6_maxage_remover(struct event *thread) +{ + struct ospf6 *o = (struct ospf6 *)EVENT_ARG(thread); + struct ospf6_area *oa; + struct ospf6_interface *oi; + struct ospf6_neighbor *on; + struct listnode *i, *j, *k; + int reschedule = 0; + + for (ALL_LIST_ELEMENTS_RO(o->area_list, i, oa)) { + for (ALL_LIST_ELEMENTS_RO(oa->if_list, j, oi)) { + for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, k, on)) { + if (on->state != OSPF6_NEIGHBOR_EXCHANGE + && on->state != OSPF6_NEIGHBOR_LOADING) + continue; + + ospf6_maxage_remove(o); + return; + } + } + } + + for (ALL_LIST_ELEMENTS_RO(o->area_list, i, oa)) { + for (ALL_LIST_ELEMENTS_RO(oa->if_list, j, oi)) { + if (ospf6_lsdb_maxage_remover(oi->lsdb)) { + reschedule = 1; + } + } + + if (ospf6_lsdb_maxage_remover(oa->lsdb)) { + reschedule = 1; + } + } + + if (ospf6_lsdb_maxage_remover(o->lsdb)) { + reschedule = 1; + } + + if (reschedule) { + ospf6_maxage_remove(o); + } +} + +void ospf6_maxage_remove(struct ospf6 *o) +{ + if (o) + event_add_timer(master, ospf6_maxage_remover, o, + OSPF_LSA_MAXAGE_REMOVE_DELAY_DEFAULT, + &o->maxage_remover); +} + +bool ospf6_router_id_update(struct ospf6 *ospf6, bool init) +{ + in_addr_t new_router_id; + struct listnode *node; + struct ospf6_area *oa; + + if (!ospf6) + return true; + + if (ospf6->router_id_static != 0) + new_router_id = ospf6->router_id_static; + else + new_router_id = ospf6->router_id_zebra; + + if (ospf6->router_id == new_router_id) + return true; + + if (!init) + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, oa)) { + if (oa->full_nbrs) { + zlog_err( + "%s: cannot update router-id. Run the \"clear ipv6 ospf6 process\" command", + __func__); + return false; + } + } + + ospf6->router_id = new_router_id; + return true; +} + +/* start ospf6 */ +DEFUN_NOSH(router_ospf6, router_ospf6_cmd, "router ospf6 [vrf NAME]", + ROUTER_STR OSPF6_STR VRF_CMD_HELP_STR) +{ + struct ospf6 *ospf6; + const char *vrf_name = VRF_DEFAULT_NAME; + int idx_vrf = 0; + + if (argv_find(argv, argc, "vrf", &idx_vrf)) { + vrf_name = argv[idx_vrf + 1]->arg; + } + + ospf6 = ospf6_lookup_by_vrf_name(vrf_name); + if (ospf6 == NULL) + ospf6 = ospf6_instance_create(vrf_name); + + /* set current ospf point. */ + VTY_PUSH_CONTEXT(OSPF6_NODE, ospf6); + + return CMD_SUCCESS; +} + +/* stop ospf6 */ +DEFUN(no_router_ospf6, no_router_ospf6_cmd, "no router ospf6 [vrf NAME]", + NO_STR ROUTER_STR OSPF6_STR VRF_CMD_HELP_STR) +{ + struct ospf6 *ospf6; + const char *vrf_name = VRF_DEFAULT_NAME; + int idx_vrf = 0; + + if (argv_find(argv, argc, "vrf", &idx_vrf)) { + vrf_name = argv[idx_vrf + 1]->arg; + } + + ospf6 = ospf6_lookup_by_vrf_name(vrf_name); + if (ospf6 == NULL) + vty_out(vty, "OSPFv3 is not configured\n"); + else { + if (ospf6->gr_info.restart_support) + ospf6_gr_nvm_delete(ospf6); + + ospf6_delete(ospf6); + ospf6 = NULL; + } + + /* return to config node . */ + VTY_PUSH_CONTEXT_NULL(CONFIG_NODE); + + return CMD_SUCCESS; +} + +static void ospf6_db_clear(struct ospf6 *ospf6) +{ + struct ospf6_interface *oi; + struct interface *ifp; + struct vrf *vrf = vrf_lookup_by_id(ospf6->vrf_id); + struct listnode *node, *nnode; + struct ospf6_area *oa; + + FOR_ALL_INTERFACES (vrf, ifp) { + if (if_is_operative(ifp) && ifp->info != NULL) { + oi = (struct ospf6_interface *)ifp->info; + ospf6_lsdb_remove_all(oi->lsdb); + ospf6_lsdb_remove_all(oi->lsdb_self); + ospf6_lsdb_remove_all(oi->lsupdate_list); + ospf6_lsdb_remove_all(oi->lsack_list); + } + } + + for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, oa)) { + ospf6_lsdb_remove_all(oa->lsdb); + ospf6_lsdb_remove_all(oa->lsdb_self); + + ospf6_spf_table_finish(oa->spf_table); + ospf6_route_remove_all(oa->route_table); + } + + ospf6_lsdb_remove_all(ospf6->lsdb); + ospf6_lsdb_remove_all(ospf6->lsdb_self); + ospf6_route_remove_all(ospf6->route_table); + ospf6_route_remove_all(ospf6->brouter_table); +} + +static void ospf6_process_reset(struct ospf6 *ospf6) +{ + struct interface *ifp; + struct vrf *vrf = vrf_lookup_by_id(ospf6->vrf_id); + + ospf6_unset_all_aggr_flag(ospf6); + ospf6_flush_self_originated_lsas_now(ospf6); + ospf6->inst_shutdown = 0; + ospf6_db_clear(ospf6); + + ospf6_asbr_redistribute_reset(ospf6); + FOR_ALL_INTERFACES (vrf, ifp) + ospf6_interface_clear(ifp); +} + +DEFPY (clear_router_ospf6, + clear_router_ospf6_cmd, + "clear ipv6 ospf6 process [vrf NAME$name]", + CLEAR_STR + IP6_STR + OSPF6_STR + "Reset OSPF Process\n" + VRF_CMD_HELP_STR) +{ + struct ospf6 *ospf6; + const char *vrf_name = VRF_DEFAULT_NAME; + + if (name != NULL) + vrf_name = name; + + ospf6 = ospf6_lookup_by_vrf_name(vrf_name); + if (ospf6 == NULL) { + vty_out(vty, "OSPFv3 is not configured\n"); + } else { + ospf6_router_id_update(ospf6, true); + ospf6_process_reset(ospf6); + } + + return CMD_SUCCESS; +} + +/* change Router_ID commands. */ +DEFUN(ospf6_router_id, + ospf6_router_id_cmd, + "ospf6 router-id A.B.C.D", + OSPF6_STR + "Configure OSPF6 Router-ID\n" + V4NOTATION_STR) +{ + VTY_DECLVAR_CONTEXT(ospf6, o); + int idx = 0; + int ret; + const char *router_id_str; + uint32_t router_id; + + argv_find(argv, argc, "A.B.C.D", &idx); + router_id_str = argv[idx]->arg; + + ret = inet_pton(AF_INET, router_id_str, &router_id); + if (ret == 0) { + vty_out(vty, "malformed OSPF Router-ID: %s\n", router_id_str); + return CMD_SUCCESS; + } + + o->router_id_static = router_id; + + if (ospf6_router_id_update(o, false)) + ospf6_process_reset(o); + else + vty_out(vty, + "For this router-id change to take effect run the \"clear ipv6 ospf6 process\" command\n"); + + return CMD_SUCCESS; +} + +DEFUN(no_ospf6_router_id, + no_ospf6_router_id_cmd, + "no ospf6 router-id [A.B.C.D]", + NO_STR OSPF6_STR + "Configure OSPF6 Router-ID\n" + V4NOTATION_STR) +{ + VTY_DECLVAR_CONTEXT(ospf6, o); + + o->router_id_static = 0; + + + if (ospf6_router_id_update(o, false)) + ospf6_process_reset(o); + else + vty_out(vty, + "For this router-id change to take effect run the \"clear ipv6 ospf6 process\" command\n"); + + return CMD_SUCCESS; +} + +DEFUN (ospf6_log_adjacency_changes, + ospf6_log_adjacency_changes_cmd, + "log-adjacency-changes", + "Log changes in adjacency state\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + SET_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_CHANGES); + UNSET_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_DETAIL); + return CMD_SUCCESS; +} + +DEFUN (ospf6_log_adjacency_changes_detail, + ospf6_log_adjacency_changes_detail_cmd, + "log-adjacency-changes detail", + "Log changes in adjacency state\n" + "Log all state changes\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + SET_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_CHANGES); + SET_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_DETAIL); + return CMD_SUCCESS; +} + +DEFUN (no_ospf6_log_adjacency_changes, + no_ospf6_log_adjacency_changes_cmd, + "no log-adjacency-changes", + NO_STR + "Log changes in adjacency state\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + UNSET_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_DETAIL); + UNSET_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_CHANGES); + return CMD_SUCCESS; +} + +DEFUN (no_ospf6_log_adjacency_changes_detail, + no_ospf6_log_adjacency_changes_detail_cmd, + "no log-adjacency-changes detail", + NO_STR + "Log changes in adjacency state\n" + "Log all state changes\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + UNSET_FLAG(ospf6->config_flags, OSPF6_LOG_ADJACENCY_DETAIL); + return CMD_SUCCESS; +} + +static void ospf6_reinstall_routes(struct ospf6 *ospf6) +{ + struct ospf6_route *route; + + for (route = ospf6_route_head(ospf6->route_table); route; + route = ospf6_route_next(route)) + ospf6_zebra_route_update_add(route, ospf6); +} + +DEFPY (ospf6_send_extra_data, + ospf6_send_extra_data_cmd, + "[no] ospf6 send-extra-data zebra", + NO_STR + OSPF6_STR + "Extra data to Zebra for display/use\n" + "To zebra\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + if (no + && CHECK_FLAG(ospf6->config_flags, + OSPF6_SEND_EXTRA_DATA_TO_ZEBRA)) { + UNSET_FLAG(ospf6->config_flags, OSPF6_SEND_EXTRA_DATA_TO_ZEBRA); + ospf6_reinstall_routes(ospf6); + } else if (!CHECK_FLAG(ospf6->config_flags, + OSPF6_SEND_EXTRA_DATA_TO_ZEBRA)) { + SET_FLAG(ospf6->config_flags, OSPF6_SEND_EXTRA_DATA_TO_ZEBRA); + ospf6_reinstall_routes(ospf6); + } + + return CMD_SUCCESS; +} + +DEFUN (ospf6_timers_lsa, + ospf6_timers_lsa_cmd, + "timers lsa min-arrival (0-600000)", + "Adjust routing timers\n" + "OSPF6 LSA timers\n" + "Minimum delay in receiving new version of a LSA\n" + "Delay in milliseconds\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, ospf); + int idx_number = 3; + unsigned int minarrival; + + minarrival = strtoul(argv[idx_number]->arg, NULL, 10); + ospf->lsa_minarrival = minarrival; + + return CMD_SUCCESS; +} + +DEFUN (no_ospf6_timers_lsa, + no_ospf6_timers_lsa_cmd, + "no timers lsa min-arrival [(0-600000)]", + NO_STR + "Adjust routing timers\n" + "OSPF6 LSA timers\n" + "Minimum delay in receiving new version of a LSA\n" + "Delay in milliseconds\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, ospf); + int idx_number = 4; + unsigned int minarrival; + + if (argc == 5) { + minarrival = strtoul(argv[idx_number]->arg, NULL, 10); + + if (ospf->lsa_minarrival != minarrival + || minarrival == OSPF_MIN_LS_ARRIVAL) + return CMD_SUCCESS; + } + + ospf->lsa_minarrival = OSPF_MIN_LS_ARRIVAL; + + return CMD_SUCCESS; +} + + +DEFUN (ospf6_distance, + ospf6_distance_cmd, + "distance (1-255)", + "Administrative distance\n" + "OSPF6 Administrative distance\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, o); + uint8_t distance; + + distance = atoi(argv[1]->arg); + if (o->distance_all != distance) { + o->distance_all = distance; + ospf6_restart_spf(o); + } + + return CMD_SUCCESS; +} + +DEFUN (no_ospf6_distance, + no_ospf6_distance_cmd, + "no distance (1-255)", + NO_STR + "Administrative distance\n" + "OSPF6 Administrative distance\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, o); + + if (o->distance_all) { + o->distance_all = 0; + ospf6_restart_spf(o); + } + return CMD_SUCCESS; +} + +DEFUN (ospf6_distance_ospf6, + ospf6_distance_ospf6_cmd, + "distance ospf6 {intra-area (1-255)|inter-area (1-255)|external (1-255)}", + "Administrative distance\n" + "OSPF6 administrative distance\n" + "Intra-area routes\n" + "Distance for intra-area routes\n" + "Inter-area routes\n" + "Distance for inter-area routes\n" + "External routes\n" + "Distance for external routes\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, o); + int idx = 0; + + o->distance_intra = 0; + o->distance_inter = 0; + o->distance_external = 0; + + if (argv_find(argv, argc, "intra-area", &idx)) + o->distance_intra = atoi(argv[idx + 1]->arg); + idx = 0; + if (argv_find(argv, argc, "inter-area", &idx)) + o->distance_inter = atoi(argv[idx + 1]->arg); + idx = 0; + if (argv_find(argv, argc, "external", &idx)) + o->distance_external = atoi(argv[idx + 1]->arg); + + return CMD_SUCCESS; +} + +DEFUN (no_ospf6_distance_ospf6, + no_ospf6_distance_ospf6_cmd, + "no distance ospf6 [{intra-area [(1-255)]|inter-area [(1-255)]|external [(1-255)]}]", + NO_STR + "Administrative distance\n" + "OSPF6 distance\n" + "Intra-area routes\n" + "Distance for intra-area routes\n" + "Inter-area routes\n" + "Distance for inter-area routes\n" + "External routes\n" + "Distance for external routes\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, o); + int idx = 0; + + if (argv_find(argv, argc, "intra-area", &idx) || argc == 3) + idx = o->distance_intra = 0; + if (argv_find(argv, argc, "inter-area", &idx) || argc == 3) + idx = o->distance_inter = 0; + if (argv_find(argv, argc, "external", &idx) || argc == 3) + o->distance_external = 0; + + return CMD_SUCCESS; +} + +DEFUN_HIDDEN (ospf6_interface_area, + ospf6_interface_area_cmd, + "interface IFNAME area <A.B.C.D|(0-4294967295)>", + "Enable routing on an IPv6 interface\n" + IFNAME_STR + "Specify the OSPF6 area ID\n" + "OSPF6 area ID in IPv4 address notation\n" + "OSPF6 area ID in decimal notation\n" + ) +{ + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + int idx_ifname = 1; + int idx_ipv4 = 3; + struct ospf6_area *oa; + struct ospf6_interface *oi; + struct interface *ifp; + uint32_t area_id; + int format; + + vty_out(vty, + "This command is deprecated, because it is not VRF-aware.\n"); + vty_out(vty, + "Please, use \"ipv6 ospf6 area\" on an interface instead.\n"); + + /* find/create ospf6 interface */ + ifp = if_get_by_name(argv[idx_ifname]->arg, ospf6->vrf_id, ospf6->name); + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL) + oi = ospf6_interface_create(ifp); + if (oi->area) { + vty_out(vty, "%s already attached to Area %s\n", + oi->interface->name, oi->area->name); + return CMD_SUCCESS; + } + + if (str2area_id(argv[idx_ipv4]->arg, &area_id, &format)) { + vty_out(vty, "Malformed Area-ID: %s\n", argv[idx_ipv4]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + + oi->area_id = area_id; + oi->area_id_format = format; + + oa = ospf6_area_lookup(area_id, ospf6); + if (oa == NULL) + oa = ospf6_area_create(area_id, ospf6, format); + + /* attach interface to area */ + listnode_add(oa->if_list, oi); /* sort ?? */ + oi->area = oa; + + SET_FLAG(oa->flag, OSPF6_AREA_ENABLE); + + /* ospf6 process is currently disabled, not much more to do */ + if (CHECK_FLAG(ospf6->flag, OSPF6_DISABLED)) + return CMD_SUCCESS; + + /* start up */ + ospf6_interface_enable(oi); + + /* If the router is ABR, originate summary routes */ + if (ospf6_check_and_set_router_abr(ospf6)) { + ospf6_abr_enable_area(oa); + ospf6_schedule_abr_task(oa->ospf6); + } + + return CMD_SUCCESS; +} + +DEFUN_HIDDEN (no_ospf6_interface_area, + no_ospf6_interface_area_cmd, + "no interface IFNAME area <A.B.C.D|(0-4294967295)>", + NO_STR + "Disable routing on an IPv6 interface\n" + IFNAME_STR + "Specify the OSPF6 area ID\n" + "OSPF6 area ID in IPv4 address notation\n" + "OSPF6 area ID in decimal notation\n" + ) +{ + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + int idx_ifname = 2; + int idx_ipv4 = 4; + struct ospf6_interface *oi; + struct ospf6_area *oa; + struct interface *ifp; + uint32_t area_id; + + vty_out(vty, + "This command is deprecated, because it is not VRF-aware.\n"); + vty_out(vty, + "Please, use \"no ipv6 ospf6 area\" on an interface instead.\n"); + + /* find/create ospf6 interface */ + ifp = if_get_by_name(argv[idx_ifname]->arg, ospf6->vrf_id, ospf6->name); + + if (ifp == NULL) { + vty_out(vty, "No such interface %s\n", argv[idx_ifname]->arg); + return CMD_SUCCESS; + } + + oi = (struct ospf6_interface *)ifp->info; + if (oi == NULL) { + vty_out(vty, "Interface %s not enabled\n", ifp->name); + return CMD_SUCCESS; + } + + /* parse Area-ID */ + if (inet_pton(AF_INET, argv[idx_ipv4]->arg, &area_id) != 1) + area_id = htonl(strtoul(argv[idx_ipv4]->arg, NULL, 10)); + + /* Verify Area */ + if (oi->area == NULL) { + vty_out(vty, "%s not attached to area %s\n", + oi->interface->name, argv[idx_ipv4]->arg); + return CMD_SUCCESS; + } + + if (oi->area->area_id != area_id) { + vty_out(vty, "Wrong Area-ID: %s is attached to area %s\n", + oi->interface->name, oi->area->name); + return CMD_SUCCESS; + } + + ospf6_interface_disable(oi); + + oa = oi->area; + listnode_delete(oi->area->if_list, oi); + oi->area = (struct ospf6_area *)NULL; + + /* Withdraw inter-area routes from this area, if necessary */ + if (oa->if_list->count == 0) { + UNSET_FLAG(oa->flag, OSPF6_AREA_ENABLE); + ospf6_abr_disable_area(oa); + } + + oi->area_id = 0; + oi->area_id_format = OSPF6_AREA_FMT_UNSET; + + return CMD_SUCCESS; +} + +DEFUN (ospf6_stub_router_admin, + ospf6_stub_router_admin_cmd, + "stub-router administrative", + "Make router a stub router\n" + "Administratively applied, for an indefinite period\n") +{ + struct listnode *node; + struct ospf6_area *oa; + + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + if (!CHECK_FLAG(ospf6->flag, OSPF6_STUB_ROUTER)) { + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, oa)) { + OSPF6_OPT_CLEAR(oa->options, OSPF6_OPT_V6); + OSPF6_OPT_CLEAR(oa->options, OSPF6_OPT_R); + OSPF6_ROUTER_LSA_SCHEDULE(oa); + } + SET_FLAG(ospf6->flag, OSPF6_STUB_ROUTER); + } + + return CMD_SUCCESS; +} + +DEFUN (no_ospf6_stub_router_admin, + no_ospf6_stub_router_admin_cmd, + "no stub-router administrative", + NO_STR + "Make router a stub router\n" + "Administratively applied, for an indefinite period\n") +{ + struct listnode *node; + struct ospf6_area *oa; + + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + if (CHECK_FLAG(ospf6->flag, OSPF6_STUB_ROUTER)) { + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, oa)) { + OSPF6_OPT_SET(oa->options, OSPF6_OPT_V6); + OSPF6_OPT_SET(oa->options, OSPF6_OPT_R); + OSPF6_ROUTER_LSA_SCHEDULE(oa); + } + UNSET_FLAG(ospf6->flag, OSPF6_STUB_ROUTER); + } + + return CMD_SUCCESS; +} + +/* Restart OSPF SPF algorithm*/ +void ospf6_restart_spf(struct ospf6 *ospf6) +{ + ospf6_route_remove_all(ospf6->route_table); + ospf6_route_remove_all(ospf6->brouter_table); + + /* Trigger SPF */ + ospf6_spf_schedule(ospf6, OSPF6_SPF_FLAGS_CONFIG_CHANGE); +} + +/* Set the max paths */ +static void ospf6_maxpath_set(struct ospf6 *ospf6, uint16_t paths) +{ + if (ospf6->max_multipath == paths) + return; + + ospf6->max_multipath = paths; + + /* Send deletion to zebra to delete all + * ospf specific routes and reinitiate + * SPF to reflect the new max multipath. + */ + ospf6_restart_spf(ospf6); +} + +/* Ospf Maximum-paths config support */ +DEFUN(ospf6_max_multipath, + ospf6_max_multipath_cmd, + "maximum-paths " CMD_RANGE_STR(1, MULTIPATH_NUM), + "Max no of multiple paths for ECMP support\n" + "Number of paths\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + int idx_number = 1; + int maximum_paths = strtol(argv[idx_number]->arg, NULL, 10); + + ospf6_maxpath_set(ospf6, maximum_paths); + + return CMD_SUCCESS; +} + +DEFUN(no_ospf6_max_multipath, + no_ospf6_max_multipath_cmd, + "no maximum-paths [" CMD_RANGE_STR(1, MULTIPATH_NUM)"]", + NO_STR + "Max no of multiple paths for ECMP support\n" + "Number of paths\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + ospf6_maxpath_set(ospf6, MULTIPATH_NUM); + + return CMD_SUCCESS; +} + +static void ospf6_show(struct vty *vty, struct ospf6 *o, json_object *json, + bool use_json) +{ + struct listnode *n; + struct ospf6_area *oa; + char router_id[16], duration[32]; + struct timeval now, running, result; + char buf[32], rbuf[32]; + json_object *json_areas = NULL; + const char *adjacency; + + if (use_json) { + json_areas = json_object_new_object(); + + /* process id, router id */ + inet_ntop(AF_INET, &o->router_id, router_id, sizeof(router_id)); + json_object_string_add(json, "routerId", router_id); + + /* running time */ + monotime(&now); + timersub(&now, &o->starttime, &running); + timerstring(&running, duration, sizeof(duration)); + json_object_string_add(json, "running", duration); + + /* Redistribute configuration */ + /* XXX */ + json_object_int_add(json, "lsaMinimumArrivalMsecs", + o->lsa_minarrival); + + /* Show SPF parameters */ + json_object_int_add(json, "spfScheduleDelayMsecs", + o->spf_delay); + json_object_int_add(json, "holdTimeMinMsecs", o->spf_holdtime); + json_object_int_add(json, "holdTimeMaxMsecs", + o->spf_max_holdtime); + json_object_int_add(json, "holdTimeMultiplier", + o->spf_hold_multiplier); + + json_object_int_add(json, "maximumPaths", o->max_multipath); + json_object_int_add(json, "preference", + o->distance_all + ? o->distance_all + : ZEBRA_OSPF6_DISTANCE_DEFAULT); + + if (o->ts_spf.tv_sec || o->ts_spf.tv_usec) { + timersub(&now, &o->ts_spf, &result); + timerstring(&result, buf, sizeof(buf)); + ospf6_spf_reason_string(o->last_spf_reason, rbuf, + sizeof(rbuf)); + json_object_boolean_true_add(json, "spfHasRun"); + json_object_string_add(json, "spfLastExecutedMsecs", + buf); + json_object_string_add(json, "spfLastExecutedReason", + rbuf); + + json_object_int_add( + json, "spfLastDurationSecs", + (long long)o->ts_spf_duration.tv_sec); + + json_object_int_add( + json, "spfLastDurationMsecs", + (long long)o->ts_spf_duration.tv_usec); + } else + json_object_boolean_false_add(json, "spfHasRun"); + + if (event_is_scheduled(o->t_spf_calc)) { + long time_store; + + json_object_boolean_true_add(json, "spfTimerActive"); + time_store = + monotime_until(&o->t_spf_calc->u.sands, NULL) + / 1000LL; + json_object_int_add(json, "spfTimerDueInMsecs", + time_store); + } else + json_object_boolean_false_add(json, "spfTimerActive"); + + json_object_boolean_add(json, "routerIsStubRouter", + CHECK_FLAG(o->flag, OSPF6_STUB_ROUTER)); + + /* LSAs */ + json_object_int_add(json, "numberOfAsScopedLsa", + o->lsdb->count); + /* Areas */ + json_object_int_add(json, "numberOfAreaInRouter", + listcount(o->area_list)); + + json_object_int_add(json, "AuthTrailerHigherSeqNo", + o->seqnum_h); + json_object_int_add(json, "AuthTrailerLowerSeqNo", o->seqnum_l); + + if (CHECK_FLAG(o->config_flags, OSPF6_LOG_ADJACENCY_CHANGES)) { + if (CHECK_FLAG(o->config_flags, + OSPF6_LOG_ADJACENCY_DETAIL)) + adjacency = "LoggedAll"; + else + adjacency = "Logged"; + } else + adjacency = "NotLogged"; + json_object_string_add(json, "adjacencyChanges", adjacency); + + for (ALL_LIST_ELEMENTS_RO(o->area_list, n, oa)) + ospf6_area_show(vty, oa, json_areas, use_json); + + json_object_object_add(json, "areas", json_areas); + + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + + } else { + /* process id, router id */ + inet_ntop(AF_INET, &o->router_id, router_id, sizeof(router_id)); + vty_out(vty, " OSPFv3 Routing Process (0) with Router-ID %s\n", + router_id); + + /* running time */ + monotime(&now); + timersub(&now, &o->starttime, &running); + timerstring(&running, duration, sizeof(duration)); + vty_out(vty, " Running %s\n", duration); + + /* Redistribute configuration */ + /* XXX */ + vty_out(vty, " LSA minimum arrival %d msecs\n", + o->lsa_minarrival); + + vty_out(vty, " Maximum-paths %u\n", o->max_multipath); + vty_out(vty, " Administrative distance %u\n", + o->distance_all ? o->distance_all + : ZEBRA_OSPF6_DISTANCE_DEFAULT); + + /* Show SPF parameters */ + vty_out(vty, + " Initial SPF scheduling delay %d millisec(s)\n" + " Minimum hold time between consecutive SPFs %d millsecond(s)\n" + " Maximum hold time between consecutive SPFs %d millsecond(s)\n" + " Hold time multiplier is currently %d\n", + o->spf_delay, o->spf_holdtime, o->spf_max_holdtime, + o->spf_hold_multiplier); + + + vty_out(vty, " SPF algorithm "); + if (o->ts_spf.tv_sec || o->ts_spf.tv_usec) { + timersub(&now, &o->ts_spf, &result); + timerstring(&result, buf, sizeof(buf)); + ospf6_spf_reason_string(o->last_spf_reason, rbuf, + sizeof(rbuf)); + vty_out(vty, "last executed %s ago, reason %s\n", buf, + rbuf); + vty_out(vty, " Last SPF duration %lld sec %lld usec\n", + (long long)o->ts_spf_duration.tv_sec, + (long long)o->ts_spf_duration.tv_usec); + } else + vty_out(vty, "has not been run\n"); + + threadtimer_string(now, o->t_spf_calc, buf, sizeof(buf)); + vty_out(vty, " SPF timer %s%s\n", + (event_is_scheduled(o->t_spf_calc) ? "due in " : "is "), + buf); + + if (CHECK_FLAG(o->flag, OSPF6_STUB_ROUTER)) + vty_out(vty, " Router Is Stub Router\n"); + + /* LSAs */ + vty_out(vty, " Number of AS scoped LSAs is %u\n", + o->lsdb->count); + + /* Areas */ + vty_out(vty, " Number of areas in this router is %u\n", + listcount(o->area_list)); + + vty_out(vty, " Authentication Sequence number info\n"); + vty_out(vty, " Higher sequence no %u, Lower sequence no %u\n", + o->seqnum_h, o->seqnum_l); + + if (CHECK_FLAG(o->config_flags, OSPF6_LOG_ADJACENCY_CHANGES)) { + if (CHECK_FLAG(o->config_flags, + OSPF6_LOG_ADJACENCY_DETAIL)) + vty_out(vty, + " All adjacency changes are logged\n"); + else + vty_out(vty, " Adjacency changes are logged\n"); + } + + + vty_out(vty, "\n"); + + for (ALL_LIST_ELEMENTS_RO(o->area_list, n, oa)) + ospf6_area_show(vty, oa, json_areas, use_json); + } +} + +DEFUN(show_ipv6_ospf6_vrfs, show_ipv6_ospf6_vrfs_cmd, + "show ipv6 ospf6 vrfs [json]", + SHOW_STR IP6_STR OSPF6_STR "Show OSPF6 VRFs \n" JSON_STR) +{ + bool uj = use_json(argc, argv); + json_object *json = NULL; + json_object *json_vrfs = NULL; + struct ospf6 *ospf6 = NULL; + struct listnode *node = NULL; + int count = 0; + char buf[PREFIX_STRLEN]; + static const char header[] = + "Name Id RouterId "; + + if (uj) { + json = json_object_new_object(); + json_vrfs = json_object_new_object(); + } + + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { + json_object *json_vrf = NULL; + const char *name = NULL; + int64_t vrf_id_ui = 0; + struct in_addr router_id; + + router_id.s_addr = ospf6->router_id; + count++; + + if (!uj && count == 1) + vty_out(vty, "%s\n", header); + if (uj) + json_vrf = json_object_new_object(); + + if (ospf6->vrf_id == VRF_DEFAULT) + name = VRF_DEFAULT_NAME; + else + name = ospf6->name; + + vrf_id_ui = (ospf6->vrf_id == VRF_UNKNOWN) + ? -1 + : (int64_t)ospf6->vrf_id; + + if (uj) { + json_object_int_add(json_vrf, "vrfId", vrf_id_ui); + json_object_string_addf(json_vrf, "routerId", "%pI4", + &router_id); + json_object_object_add(json_vrfs, name, json_vrf); + + } else { + vty_out(vty, "%-25s %-5d %-16s \n", name, + ospf6->vrf_id, + inet_ntop(AF_INET, &router_id, buf, + sizeof(buf))); + } + } + + if (uj) { + json_object_object_add(json, "vrfs", json_vrfs); + json_object_int_add(json, "totalVrfs", count); + + vty_json(vty, json); + } else { + if (count) + vty_out(vty, "\nTotal number of OSPF VRFs: %d\n", + count); + } + + return CMD_SUCCESS; +} + +/* show top level structures */ +DEFUN(show_ipv6_ospf6, show_ipv6_ospf6_cmd, + "show ipv6 ospf6 [vrf <NAME|all>] [json]", + SHOW_STR IP6_STR OSPF6_STR VRF_CMD_HELP_STR "All VRFs\n" JSON_STR) +{ + struct ospf6 *ospf6; + struct listnode *node; + const char *vrf_name = NULL; + bool all_vrf = false; + int idx_vrf = 0; + + bool uj = use_json(argc, argv); + json_object *json = NULL; + + OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { + if (all_vrf || strcmp(ospf6->name, vrf_name) == 0) { + if (uj) + json = json_object_new_object(); + ospf6_show(vty, ospf6, json, uj); + + if (!all_vrf) + break; + } + } + + if (uj) + json_object_free(json); + + OSPF6_CMD_CHECK_VRF(uj, all_vrf, ospf6); + + return CMD_SUCCESS; +} + +DEFUN(show_ipv6_ospf6_route, show_ipv6_ospf6_route_cmd, + "show ipv6 ospf6 [vrf <NAME|all>] route [<intra-area|inter-area|external-1|external-2|X:X::X:X|X:X::X:X/M|detail|summary>] [json]", + SHOW_STR IP6_STR OSPF6_STR VRF_CMD_HELP_STR + "All VRFs\n" ROUTE_STR + "Display Intra-Area routes\n" + "Display Inter-Area routes\n" + "Display Type-1 External routes\n" + "Display Type-2 External routes\n" + "Specify IPv6 address\n" + "Specify IPv6 prefix\n" + "Detailed information\n" + "Summary of route table\n" JSON_STR) +{ + struct ospf6 *ospf6; + struct listnode *node; + const char *vrf_name = NULL; + bool all_vrf = false; + int idx_vrf = 0; + int idx_arg_start = 4; + bool uj = use_json(argc, argv); + + OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + if (idx_vrf > 0) + idx_arg_start += 2; + + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { + if (all_vrf || strcmp(ospf6->name, vrf_name) == 0) { + ospf6_route_table_show(vty, idx_arg_start, argc, argv, + ospf6->route_table, uj); + + if (!all_vrf) + break; + } + } + + OSPF6_CMD_CHECK_VRF(uj, all_vrf, ospf6); + + return CMD_SUCCESS; +} + +DEFUN(show_ipv6_ospf6_route_match, show_ipv6_ospf6_route_match_cmd, + "show ipv6 ospf6 [vrf <NAME|all>] route X:X::X:X/M <match|longer> [json]", + SHOW_STR IP6_STR OSPF6_STR VRF_CMD_HELP_STR + "All VRFs\n" ROUTE_STR + "Specify IPv6 prefix\n" + "Display routes which match the specified route\n" + "Display routes longer than the specified route\n" JSON_STR) +{ + struct ospf6 *ospf6; + struct listnode *node; + const char *vrf_name = NULL; + bool all_vrf = false; + int idx_vrf = 0; + int idx_start_arg = 4; + bool uj = use_json(argc, argv); + + OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + if (idx_vrf > 0) + idx_start_arg += 2; + + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { + if (all_vrf || strcmp(ospf6->name, vrf_name) == 0) { + ospf6_route_table_show(vty, idx_start_arg, argc, argv, + ospf6->route_table, uj); + + if (!all_vrf) + break; + } + } + + OSPF6_CMD_CHECK_VRF(uj, all_vrf, ospf6); + + return CMD_SUCCESS; +} + +DEFUN(show_ipv6_ospf6_route_match_detail, + show_ipv6_ospf6_route_match_detail_cmd, + "show ipv6 ospf6 [vrf <NAME|all>] route X:X::X:X/M match detail [json]", + SHOW_STR IP6_STR OSPF6_STR VRF_CMD_HELP_STR + "All VRFs\n" ROUTE_STR + "Specify IPv6 prefix\n" + "Display routes which match the specified route\n" + "Detailed information\n" JSON_STR) +{ + struct ospf6 *ospf6; + struct listnode *node; + const char *vrf_name = NULL; + bool all_vrf = false; + int idx_vrf = 0; + int idx_start_arg = 4; + bool uj = use_json(argc, argv); + + OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + if (idx_vrf > 0) + idx_start_arg += 2; + + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { + if (all_vrf || strcmp(ospf6->name, vrf_name) == 0) { + ospf6_route_table_show(vty, idx_start_arg, argc, argv, + ospf6->route_table, uj); + + if (!all_vrf) + break; + } + } + + OSPF6_CMD_CHECK_VRF(uj, all_vrf, ospf6); + + return CMD_SUCCESS; +} + +DEFUN(show_ipv6_ospf6_route_type_detail, show_ipv6_ospf6_route_type_detail_cmd, + "show ipv6 ospf6 [vrf <NAME|all>] route <intra-area|inter-area|external-1|external-2> detail [json]", + SHOW_STR IP6_STR OSPF6_STR VRF_CMD_HELP_STR + "All VRFs\n" ROUTE_STR + "Display Intra-Area routes\n" + "Display Inter-Area routes\n" + "Display Type-1 External routes\n" + "Display Type-2 External routes\n" + "Detailed information\n" JSON_STR) +{ + struct ospf6 *ospf6; + struct listnode *node; + const char *vrf_name = NULL; + bool all_vrf = false; + int idx_vrf = 0; + int idx_start_arg = 4; + bool uj = use_json(argc, argv); + + OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + if (idx_vrf > 0) + idx_start_arg += 2; + + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { + if (all_vrf || strcmp(ospf6->name, vrf_name) == 0) { + ospf6_route_table_show(vty, idx_start_arg, argc, argv, + ospf6->route_table, uj); + + if (!all_vrf) + break; + } + } + + OSPF6_CMD_CHECK_VRF(uj, all_vrf, ospf6); + + return CMD_SUCCESS; +} + +bool ospf6_is_valid_summary_addr(struct vty *vty, struct prefix *p) +{ + /* Default prefix validation*/ + if (is_default_prefix(p)) { + vty_out(vty, + "Default address should not be configured as summary address.\n"); + return false; + } + + /* Host route should not be configured as summary address */ + if (p->prefixlen == IPV6_MAX_BITLEN) { + vty_out(vty, "Host route should not be configured as summary address.\n"); + return false; + } + + return true; +} + +/* External Route Aggregation */ +DEFPY (ospf6_external_route_aggregation, + ospf6_external_route_aggregation_cmd, + "summary-address X:X::X:X/M$prefix [tag (1-4294967295)] [{metric (0-16777215) | metric-type (1-2)$mtype}]", + "External summary address\n" + "Specify IPv6 prefix\n" + "Router tag \n" + "Router tag value\n" + "Metric \n" + "Advertised metric for this route\n" + "OSPFv3 exterior metric type for summarised routes\n" + "Set OSPFv3 External Type 1/2 metrics\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + struct prefix p; + int ret = CMD_SUCCESS; + + p.family = AF_INET6; + ret = str2prefix(prefix_str, &p); + if (ret == 0) { + vty_out(vty, "Malformed prefix\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + /* Apply mask for given prefix. */ + apply_mask(&p); + + if (!ospf6_is_valid_summary_addr(vty, &p)) + return CMD_WARNING_CONFIG_FAILED; + + if (!tag_str) + tag = 0; + + if (!metric_str) + metric = -1; + + if (!mtype_str) + mtype = DEFAULT_METRIC_TYPE; + + ret = ospf6_external_aggr_config_set(ospf6, &p, tag, metric, mtype); + if (ret == OSPF6_FAILURE) { + vty_out(vty, "Invalid configuration!!\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + return CMD_SUCCESS; +} + +DEFPY(no_ospf6_external_route_aggregation, + no_ospf6_external_route_aggregation_cmd, + "no summary-address X:X::X:X/M$prefix [tag (1-4294967295)] [{metric (0-16777215) | metric-type (1-2)}]", + NO_STR + "External summary address\n" + "Specify IPv6 prefix\n" + "Router tag\n" + "Router tag value\n" + "Metric \n" + "Advertised metric for this route\n" + "OSPFv3 exterior metric type for summarised routes\n" + "Set OSPFv3 External Type 1/2 metrics\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + struct prefix p; + int ret = CMD_SUCCESS; + + ret = str2prefix(prefix_str, &p); + if (ret == 0) { + vty_out(vty, "Malformed prefix\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + /* Apply mask for given prefix. */ + apply_mask(&p); + + if (!ospf6_is_valid_summary_addr(vty, &p)) + return CMD_WARNING_CONFIG_FAILED; + + ret = ospf6_external_aggr_config_unset(ospf6, &p); + if (ret == OSPF6_INVALID) + vty_out(vty, "Invalid configuration!!\n"); + + return CMD_SUCCESS; +} + +DEFPY (ospf6_external_route_aggregation_no_advertise, + ospf6_external_route_aggregation_no_advertise_cmd, + "summary-address X:X::X:X/M$prefix no-advertise", + "External summary address\n" + "Specify IPv6 prefix\n" + "Don't advertise summary route \n") +{ + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + struct prefix p; + int ret = CMD_SUCCESS; + + ret = str2prefix(prefix_str, &p); + if (ret == 0) { + vty_out(vty, "Malformed prefix\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + /* Apply mask for given prefix. */ + apply_mask(&p); + + if (!ospf6_is_valid_summary_addr(vty, &p)) + return CMD_WARNING_CONFIG_FAILED; + + ret = ospf6_asbr_external_rt_no_advertise(ospf6, &p); + if (ret == OSPF6_INVALID) + vty_out(vty, "!!Invalid configuration\n"); + + return CMD_SUCCESS; +} + +DEFPY (no_ospf6_external_route_aggregation_no_advertise, + no_ospf6_external_route_aggregation_no_advertise_cmd, + "no summary-address X:X::X:X/M$prefix no-advertise", + NO_STR + "External summary address\n" + "Specify IPv6 prefix\n" + "Adverise summary route to the AS \n") +{ + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + struct prefix p; + int ret = CMD_SUCCESS; + + ret = str2prefix(prefix_str, &p); + if (ret == 0) { + vty_out(vty, "Malformed prefix\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + /* Apply mask for given prefix. */ + apply_mask(&p); + + if (!ospf6_is_valid_summary_addr(vty, &p)) + return CMD_WARNING_CONFIG_FAILED; + + ret = ospf6_asbr_external_rt_advertise(ospf6, &p); + if (ret == OSPF6_INVALID) + vty_out(vty, "!!Invalid configuration\n"); + + return CMD_SUCCESS; +} + +DEFPY (ospf6_route_aggregation_timer, + ospf6_route_aggregation_timer_cmd, + "aggregation timer (5-1800)", + "External route aggregation\n" + "Delay timer (in seconds)\n" + "Timer interval(in seconds)\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + ospf6_external_aggr_delay_timer_set(ospf6, timer); + + return CMD_SUCCESS; +} + +DEFPY (no_ospf6_route_aggregation_timer, + no_ospf6_route_aggregation_timer_cmd, + "no aggregation timer [5-1800]", + NO_STR + "External route aggregation\n" + "Delay timer\n" + "Timer interval(in seconds)\n") +{ + VTY_DECLVAR_CONTEXT(ospf6, ospf6); + + ospf6_external_aggr_delay_timer_set(ospf6, + OSPF6_EXTL_AGGR_DEFAULT_DELAY); + return CMD_SUCCESS; +} + +static int +ospf6_print_vty_external_routes_walkcb(struct hash_bucket *bucket, void *arg) +{ + struct ospf6_route *rt = bucket->data; + struct vty *vty = (struct vty *)arg; + static unsigned int count; + + vty_out(vty, "%pFX ", &rt->prefix); + + count++; + + if (count%5 == 0) + vty_out(vty, "\n"); + + if (OSPF6_EXTERNAL_RT_COUNT(rt->aggr_route) == count) + count = 0; + + return HASHWALK_CONTINUE; +} + +static int +ospf6_print_json_external_routes_walkcb(struct hash_bucket *bucket, void *arg) +{ + struct ospf6_route *rt = bucket->data; + struct json_object *json = (struct json_object *)arg; + char buf[PREFIX2STR_BUFFER]; + char exnalbuf[20]; + static unsigned int count; + + prefix2str(&rt->prefix, buf, sizeof(buf)); + + snprintf(exnalbuf, sizeof(exnalbuf), "Exnl Addr-%d", count); + + json_object_string_add(json, exnalbuf, buf); + + count++; + + if (OSPF6_EXTERNAL_RT_COUNT(rt->aggr_route) == count) + count = 0; + + return HASHWALK_CONTINUE; +} + +static void +ospf6_show_vrf_name(struct vty *vty, struct ospf6 *ospf6, + json_object *json) +{ + if (json) { + if (ospf6->vrf_id == VRF_DEFAULT) + json_object_string_add(json, "vrfName", + "default"); + else + json_object_string_add(json, "vrfName", + ospf6->name); + json_object_int_add(json, "vrfId", ospf6->vrf_id); + } else { + if (ospf6->vrf_id == VRF_DEFAULT) + vty_out(vty, "VRF Name: %s\n", "default"); + else if (ospf6->name) + vty_out(vty, "VRF Name: %s\n", ospf6->name); + } +} + +static int +ospf6_show_summary_address(struct vty *vty, struct ospf6 *ospf6, + json_object *json, + bool uj, const char *detail) +{ + struct route_node *rn; + static const char header[] = "Summary-address Metric-type Metric Tag External_Rt_count\n"; + json_object *json_vrf = NULL; + + if (!uj) { + ospf6_show_vrf_name(vty, ospf6, json_vrf); + vty_out(vty, "aggregation delay interval: %u(in seconds)\n\n", + ospf6->aggr_delay_interval); + vty_out(vty, "%s\n", header); + } else { + json_vrf = json_object_new_object(); + + ospf6_show_vrf_name(vty, ospf6, json_vrf); + + json_object_int_add(json_vrf, "aggregationDelayInterval", + ospf6->aggr_delay_interval); + } + + + for (rn = route_top(ospf6->rt_aggr_tbl); rn; rn = route_next(rn)) { + if (!rn->info) + continue; + + struct ospf6_external_aggr_rt *aggr = rn->info; + json_object *json_aggr = NULL; + char buf[PREFIX2STR_BUFFER]; + + prefix2str(&aggr->p, buf, sizeof(buf)); + + if (uj) { + + json_aggr = json_object_new_object(); + + json_object_object_add(json_vrf, + buf, + json_aggr); + + json_object_string_add(json_aggr, "summaryAddress", + buf); + + json_object_string_add( + json_aggr, "metricType", + (aggr->mtype == DEFAULT_METRIC_TYPE) ? "E2" + : "E1"); + + json_object_int_add(json_aggr, "Metric", + (aggr->metric != -1) + ? aggr->metric + : DEFAULT_DEFAULT_METRIC); + + json_object_int_add(json_aggr, "Tag", + aggr->tag); + + json_object_int_add(json_aggr, "externalRouteCount", + OSPF6_EXTERNAL_RT_COUNT(aggr)); + + if (OSPF6_EXTERNAL_RT_COUNT(aggr) && detail) { + json_object_int_add(json_aggr, "ID", + aggr->id); + json_object_int_add(json_aggr, "Flags", + aggr->aggrflags); + hash_walk(aggr->match_extnl_hash, + ospf6_print_json_external_routes_walkcb, + json_aggr); + } + + } else { + vty_out(vty, "%-22s", buf); + + (aggr->mtype == DEFAULT_METRIC_TYPE) + ? vty_out(vty, "%-16s", "E2") + : vty_out(vty, "%-16s", "E1"); + vty_out(vty, "%-11d", (aggr->metric != -1) + ? aggr->metric + : DEFAULT_DEFAULT_METRIC); + + vty_out(vty, "%-12u", aggr->tag); + + vty_out(vty, "%-5ld\n", + OSPF6_EXTERNAL_RT_COUNT(aggr)); + + if (OSPF6_EXTERNAL_RT_COUNT(aggr) && detail) { + vty_out(vty, + "Matched External routes:\n"); + hash_walk(aggr->match_extnl_hash, + ospf6_print_vty_external_routes_walkcb, + vty); + vty_out(vty, "\n"); + } + + vty_out(vty, "\n"); + } + } + + if (uj) + json_object_object_add(json, ospf6->name, + json_vrf); + + return CMD_SUCCESS; +} + +DEFPY (show_ipv6_ospf6_external_aggregator, + show_ipv6_ospf6_external_aggregator_cmd, + "show ipv6 ospf6 [vrf <NAME|all>] summary-address [detail$detail] [json]", + SHOW_STR + IP6_STR + OSPF6_STR + VRF_CMD_HELP_STR + "All VRFs\n" + "Show external summary addresses\n" + "detailed information\n" + JSON_STR) +{ + bool uj = use_json(argc, argv); + struct ospf6 *ospf6 = NULL; + json_object *json = NULL; + const char *vrf_name = NULL; + struct listnode *node; + bool all_vrf = false; + int idx_vrf = 0; + + if (uj) + json = json_object_new_object(); + + OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { + if (all_vrf || strcmp(ospf6->name, vrf_name) == 0) { + + ospf6_show_summary_address(vty, ospf6, json, uj, + detail); + + if (!all_vrf) + break; + } + } + + if (uj) { + vty_json(vty, json); + } + + OSPF6_CMD_CHECK_VRF(uj, all_vrf, ospf6); + + return CMD_SUCCESS; +} + +static void ospf6_stub_router_config_write(struct vty *vty, struct ospf6 *ospf6) +{ + if (CHECK_FLAG(ospf6->flag, OSPF6_STUB_ROUTER)) + vty_out(vty, " stub-router administrative\n"); + return; +} + +static int ospf6_distance_config_write(struct vty *vty, struct ospf6 *ospf6) +{ + struct route_node *rn; + struct ospf6_distance *odistance; + + if (ospf6->distance_all) + vty_out(vty, " distance %u\n", ospf6->distance_all); + + if (ospf6->distance_intra || ospf6->distance_inter + || ospf6->distance_external) { + vty_out(vty, " distance ospf6"); + + if (ospf6->distance_intra) + vty_out(vty, " intra-area %u", ospf6->distance_intra); + if (ospf6->distance_inter) + vty_out(vty, " inter-area %u", ospf6->distance_inter); + if (ospf6->distance_external) + vty_out(vty, " external %u", ospf6->distance_external); + + vty_out(vty, "\n"); + } + + for (rn = route_top(ospf6->distance_table); rn; rn = route_next(rn)) + if ((odistance = rn->info) != NULL) + vty_out(vty, " distance %u %pFX %s\n", + odistance->distance, &rn->p, + odistance->access_list ? odistance->access_list + : ""); + return 0; +} + +static int ospf6_asbr_summary_config_write(struct vty *vty, struct ospf6 *ospf6) +{ + struct route_node *rn; + struct ospf6_external_aggr_rt *aggr; + char buf[PREFIX2STR_BUFFER]; + + if (ospf6->aggr_delay_interval != OSPF6_EXTL_AGGR_DEFAULT_DELAY) + vty_out(vty, " aggregation timer %u\n", + ospf6->aggr_delay_interval); + + /* print 'summary-address A:B::C:D/M' */ + for (rn = route_top(ospf6->rt_aggr_tbl); rn; rn = route_next(rn)) { + if (!rn->info) + continue; + + aggr = rn->info; + + prefix2str(&aggr->p, buf, sizeof(buf)); + vty_out(vty, " summary-address %s", buf); + if (aggr->tag) + vty_out(vty, " tag %u", aggr->tag); + + if (aggr->metric != -1) + vty_out(vty, " metric %d", aggr->metric); + + if (aggr->mtype != DEFAULT_METRIC_TYPE) + vty_out(vty, " metric-type %d", aggr->mtype); + + if (CHECK_FLAG(aggr->aggrflags, + OSPF6_EXTERNAL_AGGRT_NO_ADVERTISE)) + vty_out(vty, " no-advertise"); + + vty_out(vty, "\n"); + } + + return 0; +} + +/* OSPF configuration write function. */ +static int config_write_ospf6(struct vty *vty) +{ + struct ospf6 *ospf6; + struct listnode *node, *nnode; + + /* OSPFv3 configuration. */ + if (om6 == NULL) + return CMD_SUCCESS; + + for (ALL_LIST_ELEMENTS(om6->ospf6, node, nnode, ospf6)) { + if (ospf6->name && strcmp(ospf6->name, VRF_DEFAULT_NAME)) + vty_out(vty, "router ospf6 vrf %s\n", ospf6->name); + else + vty_out(vty, "router ospf6\n"); + + if (ospf6->router_id_static != 0) + vty_out(vty, " ospf6 router-id %pI4\n", + &ospf6->router_id_static); + + if (CHECK_FLAG(ospf6->config_flags, + OSPF6_SEND_EXTRA_DATA_TO_ZEBRA)) + vty_out(vty, " ospf6 send-extra-data zebra\n"); + + /* log-adjacency-changes flag print. */ + if (CHECK_FLAG(ospf6->config_flags, + OSPF6_LOG_ADJACENCY_CHANGES)) { + if (CHECK_FLAG(ospf6->config_flags, + OSPF6_LOG_ADJACENCY_DETAIL)) + vty_out(vty, " log-adjacency-changes detail\n"); + else if (!SAVE_OSPF6_LOG_ADJACENCY_CHANGES) + vty_out(vty, " log-adjacency-changes\n"); + } else if (SAVE_OSPF6_LOG_ADJACENCY_CHANGES) { + vty_out(vty, " no log-adjacency-changes\n"); + } + + if (ospf6->ref_bandwidth != OSPF6_REFERENCE_BANDWIDTH) + vty_out(vty, " auto-cost reference-bandwidth %d\n", + ospf6->ref_bandwidth); + + if (ospf6->write_oi_count + != OSPF6_WRITE_INTERFACE_COUNT_DEFAULT) + vty_out(vty, " write-multiplier %d\n", + ospf6->write_oi_count); + + /* LSA timers print. */ + if (ospf6->lsa_minarrival != OSPF_MIN_LS_ARRIVAL) + vty_out(vty, " timers lsa min-arrival %d\n", + ospf6->lsa_minarrival); + + /* ECMP max path config */ + if (ospf6->max_multipath != MULTIPATH_NUM) + vty_out(vty, " maximum-paths %d\n", + ospf6->max_multipath); + + ospf6_stub_router_config_write(vty, ospf6); + ospf6_redistribute_config_write(vty, ospf6); + ospf6_area_config_write(vty, ospf6); + ospf6_spf_config_write(vty, ospf6); + ospf6_distance_config_write(vty, ospf6); + ospf6_distribute_config_write(vty, ospf6); + ospf6_asbr_summary_config_write(vty, ospf6); + config_write_ospf6_gr(vty, ospf6); + config_write_ospf6_gr_helper(vty, ospf6); + + vty_out(vty, "exit\n"); + vty_out(vty, "!\n"); + } + return 0; +} + +static int config_write_ospf6(struct vty *vty); +/* OSPF6 node structure. */ +static struct cmd_node ospf6_node = { + .name = "ospf6", + .node = OSPF6_NODE, + .parent_node = CONFIG_NODE, + .prompt = "%s(config-ospf6)# ", + .config_write = config_write_ospf6, +}; + +void install_element_ospf6_clear_process(void) +{ + install_element(ENABLE_NODE, &clear_router_ospf6_cmd); +} + +/* Install ospf related commands. */ +void ospf6_top_init(void) +{ + /* Install ospf6 top node. */ + install_node(&ospf6_node); + + install_element(VIEW_NODE, &show_ipv6_ospf6_cmd); + install_element(VIEW_NODE, &show_ipv6_ospf6_vrfs_cmd); + install_element(CONFIG_NODE, &router_ospf6_cmd); + install_element(CONFIG_NODE, &no_router_ospf6_cmd); + + install_element(VIEW_NODE, &show_ipv6_ospf6_route_cmd); + install_element(VIEW_NODE, &show_ipv6_ospf6_route_match_cmd); + install_element(VIEW_NODE, &show_ipv6_ospf6_route_match_detail_cmd); + install_element(VIEW_NODE, &show_ipv6_ospf6_route_type_detail_cmd); + + install_default(OSPF6_NODE); + install_element(OSPF6_NODE, &ospf6_router_id_cmd); + install_element(OSPF6_NODE, &no_ospf6_router_id_cmd); + install_element(OSPF6_NODE, &ospf6_log_adjacency_changes_cmd); + install_element(OSPF6_NODE, &ospf6_log_adjacency_changes_detail_cmd); + install_element(OSPF6_NODE, &no_ospf6_log_adjacency_changes_cmd); + install_element(OSPF6_NODE, &no_ospf6_log_adjacency_changes_detail_cmd); + install_element(OSPF6_NODE, &ospf6_send_extra_data_cmd); + + /* LSA timers commands */ + install_element(OSPF6_NODE, &ospf6_timers_lsa_cmd); + install_element(OSPF6_NODE, &no_ospf6_timers_lsa_cmd); + + install_element(OSPF6_NODE, &ospf6_interface_area_cmd); + install_element(OSPF6_NODE, &no_ospf6_interface_area_cmd); + install_element(OSPF6_NODE, &ospf6_stub_router_admin_cmd); + install_element(OSPF6_NODE, &no_ospf6_stub_router_admin_cmd); + + /* maximum-paths command */ + install_element(OSPF6_NODE, &ospf6_max_multipath_cmd); + install_element(OSPF6_NODE, &no_ospf6_max_multipath_cmd); + + /* ASBR Summarisation */ + install_element(OSPF6_NODE, &ospf6_external_route_aggregation_cmd); + install_element(OSPF6_NODE, &no_ospf6_external_route_aggregation_cmd); + install_element(OSPF6_NODE, + &ospf6_external_route_aggregation_no_advertise_cmd); + install_element(OSPF6_NODE, + &no_ospf6_external_route_aggregation_no_advertise_cmd); + install_element(OSPF6_NODE, &ospf6_route_aggregation_timer_cmd); + install_element(OSPF6_NODE, &no_ospf6_route_aggregation_timer_cmd); + install_element(VIEW_NODE, &show_ipv6_ospf6_external_aggregator_cmd); + + install_element(OSPF6_NODE, &ospf6_distance_cmd); + install_element(OSPF6_NODE, &no_ospf6_distance_cmd); + install_element(OSPF6_NODE, &ospf6_distance_ospf6_cmd); + install_element(OSPF6_NODE, &no_ospf6_distance_ospf6_cmd); +} diff --git a/ospf6d/ospf6_top.h b/ospf6d/ospf6_top.h new file mode 100644 index 0000000..a38dad8 --- /dev/null +++ b/ospf6d/ospf6_top.h @@ -0,0 +1,254 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2003 Yasuhiro Ohara + */ + +#ifndef OSPF6_TOP_H +#define OSPF6_TOP_H + +#include "qobj.h" +#include "routemap.h" +struct ospf6_master { + + /* OSPFv3 instance. */ + struct list *ospf6; + /* OSPFv3 thread master. */ + struct event_loop *master; +}; + +/* ospf6->config_flags */ +enum { OSPF6_LOG_ADJACENCY_CHANGES = (1 << 0), + OSPF6_LOG_ADJACENCY_DETAIL = (1 << 1), + OSPF6_SEND_EXTRA_DATA_TO_ZEBRA = (1 << 2), +}; + +/* For processing route-map change update in the callback */ +#define OSPF6_IS_RMAP_CHANGED 0x01 +struct ospf6_redist { + uint8_t instance; + + uint8_t flag; + /* Redistribute metric info. */ + struct { + int type; /* External metric type (E1 or E2). */ + int value; /* Value for static metric (24-bit). + * -1 means metric value is not set. + */ + } dmetric; + + /* For redistribute route map. */ + struct { + char *name; + struct route_map *map; + } route_map; +#define ROUTEMAP_NAME(R) (R->route_map.name) +#define ROUTEMAP(R) (R->route_map.map) +}; + +struct ospf6_gr_info { + bool restart_support; + bool restart_in_progress; + bool prepare_in_progress; + bool finishing_restart; + uint32_t grace_period; + int reason; + char *exit_reason; + struct event *t_grace_period; +}; + +struct ospf6_gr_helper { + /* Graceful restart Helper supported configs*/ + /* Supported grace interval*/ + uint32_t supported_grace_time; + + /* Helper support + * Supported : True + * Not Supported : False. + */ + bool is_helper_supported; + + /* Support for strict LSA check. + * if it is set,Helper aborted + * upon a TOPO change. + */ + bool strict_lsa_check; + + /* Support as HELPER only for + * planned restarts. + */ + bool only_planned_restart; + + /* This list contains the advertisement + * routerids for which Helper support is + * enabled. + */ + struct hash *enable_rtr_list; + + /* HELPER for number of active + * RESTARTERs. + */ + int active_restarter_cnt; + + /* last HELPER exit reason */ + uint32_t last_exit_reason; +}; + +/* OSPFv3 top level data structure */ +struct ospf6 { + /* The relevant vrf_id */ + vrf_id_t vrf_id; + + char *name; /* VRF name */ + + /* my router id */ + in_addr_t router_id; + + /* static router id */ + in_addr_t router_id_static; + + in_addr_t router_id_zebra; + + /* start time */ + struct timeval starttime; + + /* list of areas */ + struct list *area_list; + struct ospf6_area *backbone; + + /* AS scope link state database */ + struct ospf6_lsdb *lsdb; + struct ospf6_lsdb *lsdb_self; + + struct ospf6_route_table *route_table; + struct ospf6_route_table *brouter_table; + + struct ospf6_route_table *external_table; +#define OSPF6_EXT_INIT_LS_ID 1 + uint32_t external_id; + + /* OSPF6 redistribute configuration */ + struct list *redist[ZEBRA_ROUTE_MAX + 1]; + + /* NSSA default-information-originate */ + struct { + /* # of NSSA areas requesting default information */ + uint16_t refcnt; + + /* + * Whether a default route known through non-OSPF protocol is + * present in the RIB. + */ + bool status; + } nssa_default_import_check; + + uint8_t flag; +#define OSPF6_FLAG_ABR 0x04 +#define OSPF6_FLAG_ASBR 0x08 + + int redistribute; /* Num of redistributed protocols. */ + + /* Configuration bitmask, refer to enum above */ + uint8_t config_flags; + int default_originate; /* Default information originate. */ +#define DEFAULT_ORIGINATE_NONE 0 +#define DEFAULT_ORIGINATE_ZEBRA 1 +#define DEFAULT_ORIGINATE_ALWAYS 2 + /* LSA timer parameters */ + unsigned int lsa_minarrival; /* LSA minimum arrival in milliseconds. */ + + /* SPF parameters */ + unsigned int spf_delay; /* SPF delay time. */ + unsigned int spf_holdtime; /* SPF hold time. */ + unsigned int spf_max_holdtime; /* SPF maximum-holdtime */ + unsigned int + spf_hold_multiplier; /* Adaptive multiplier for hold time */ + unsigned int spf_reason; /* reason bits while scheduling SPF */ + + struct timeval ts_spf; /* SPF calculation time stamp. */ + struct timeval ts_spf_duration; /* Execution time of last SPF */ + unsigned int last_spf_reason; /* Last SPF reason */ + + int fd; + /* Threads */ + struct event *t_spf_calc; /* SPF calculation timer. */ + struct event *t_ase_calc; /* ASE calculation timer. */ + struct event *maxage_remover; + struct event *t_distribute_update; /* Distirbute update timer. */ + struct event *t_ospf6_receive; /* OSPF6 receive timer */ + struct event *t_external_aggr; /* OSPF6 aggregation timer */ +#define OSPF6_WRITE_INTERFACE_COUNT_DEFAULT 20 + struct event *t_write; + + int write_oi_count; /* Num of packets sent per thread invocation */ + uint32_t ref_bandwidth; + + /* Distance parameters */ + uint8_t distance_all; + uint8_t distance_intra; + uint8_t distance_inter; + uint8_t distance_external; + + struct route_table *distance_table; + + /* Used during ospf instance going down send LSDB + * update to neighbors immediatly */ + uint8_t inst_shutdown; + + /* Max number of multiple paths + * to support ECMP. + */ + uint16_t max_multipath; + + /* OSPF Graceful Restart info (restarting mode) */ + struct ospf6_gr_info gr_info; + + /*ospf6 Graceful restart helper info */ + struct ospf6_gr_helper ospf6_helper_cfg; + + /* Count of NSSA areas */ + uint8_t anyNSSA; + struct event *t_abr_task; /* ABR task timer. */ + struct list *oi_write_q; + + uint32_t redist_count; + + /* Action for aggregation of external LSAs */ + int aggr_action; + + uint32_t seqnum_l; /* lower order Sequence Number */ + uint32_t seqnum_h; /* higher order Sequence Number */ +#define OSPF6_EXTL_AGGR_DEFAULT_DELAY 5 + /* For ASBR summary delay timer */ + uint16_t aggr_delay_interval; + /* Table of configured Aggregate addresses */ + struct route_table *rt_aggr_tbl; + + QOBJ_FIELDS; +}; +DECLARE_QOBJ_TYPE(ospf6); + +#define OSPF6_DISABLED 0x01 +#define OSPF6_STUB_ROUTER 0x02 + +/* global pointer for OSPF top data structure */ +extern struct ospf6 *ospf6; +extern struct ospf6_master *om6; + +/* prototypes */ +extern void ospf6_master_init(struct event_loop *master); +extern void install_element_ospf6_clear_process(void); +extern void ospf6_top_init(void); +extern void ospf6_delete(struct ospf6 *o); +extern bool ospf6_router_id_update(struct ospf6 *ospf6, bool init); +void ospf6_restart_spf(struct ospf6 *ospf6); + +extern void ospf6_maxage_remove(struct ospf6 *o); +extern struct ospf6 *ospf6_instance_create(const char *name); +void ospf6_vrf_link(struct ospf6 *ospf6, struct vrf *vrf); +void ospf6_vrf_unlink(struct ospf6 *ospf6, struct vrf *vrf); +struct ospf6 *ospf6_lookup_by_vrf_id(vrf_id_t vrf_id); +struct ospf6 *ospf6_lookup_by_vrf_name(const char *name); +const char *ospf6_vrf_id_to_name(vrf_id_t vrf_id); +void ospf6_vrf_init(void); +bool ospf6_is_valid_summary_addr(struct vty *vty, struct prefix *p); +#endif /* OSPF6_TOP_H */ diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c new file mode 100644 index 0000000..8bd0d8f --- /dev/null +++ b/ospf6d/ospf6_zebra.c @@ -0,0 +1,854 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2003 Yasuhiro Ohara + */ + +#include <zebra.h> + +#include "log.h" +#include "vty.h" +#include "command.h" +#include "prefix.h" +#include "stream.h" +#include "zclient.h" +#include "memory.h" +#include "route_opaque.h" +#include "lib/bfd.h" +#include "lib_errors.h" + +#include "ospf6_proto.h" +#include "ospf6_top.h" +#include "ospf6_interface.h" +#include "ospf6_route.h" +#include "ospf6_lsa.h" +#include "ospf6_lsdb.h" +#include "ospf6_asbr.h" +#include "ospf6_nssa.h" +#include "ospf6_zebra.h" +#include "ospf6d.h" +#include "ospf6_area.h" +#include "ospf6_gr.h" +#include "lib/json.h" + +DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_DISTANCE, "OSPF6 distance"); + +unsigned char conf_debug_ospf6_zebra = 0; + +/* information about zebra. */ +struct zclient *zclient = NULL; + +void ospf6_zebra_vrf_register(struct ospf6 *ospf6) +{ + if (!zclient || zclient->sock < 0 || !ospf6) + return; + + if (ospf6->vrf_id != VRF_UNKNOWN) { + if (IS_OSPF6_DEBUG_ZEBRA(RECV)) { + zlog_debug("%s: Register VRF %s id %u", __func__, + ospf6_vrf_id_to_name(ospf6->vrf_id), + ospf6->vrf_id); + } + zclient_send_reg_requests(zclient, ospf6->vrf_id); + } +} + +void ospf6_zebra_vrf_deregister(struct ospf6 *ospf6) +{ + if (!zclient || zclient->sock < 0 || !ospf6) + return; + + if (ospf6->vrf_id != VRF_DEFAULT && ospf6->vrf_id != VRF_UNKNOWN) { + if (IS_OSPF6_DEBUG_ZEBRA(RECV)) { + zlog_debug("%s: De-Register VRF %s id %u to Zebra.", + __func__, + ospf6_vrf_id_to_name(ospf6->vrf_id), + ospf6->vrf_id); + } + /* Deregister for router-id, interfaces, + * redistributed routes. */ + zclient_send_dereg_requests(zclient, ospf6->vrf_id); + } +} + +/* Router-id update message from zebra. */ +static int ospf6_router_id_update_zebra(ZAPI_CALLBACK_ARGS) +{ + struct prefix router_id; + struct ospf6 *o; + + zebra_router_id_update_read(zclient->ibuf, &router_id); + + if (IS_OSPF6_DEBUG_ZEBRA(RECV)) + zlog_debug("Zebra router-id update %pI4 vrf %s id %u", + &router_id.u.prefix4, ospf6_vrf_id_to_name(vrf_id), + vrf_id); + + o = ospf6_lookup_by_vrf_id(vrf_id); + if (o == NULL) + return 0; + + o->router_id_zebra = router_id.u.prefix4.s_addr; + + ospf6_router_id_update(o, false); + + return 0; +} + +/* redistribute function */ +void ospf6_zebra_redistribute(int type, vrf_id_t vrf_id) +{ + if (vrf_bitmap_check(&zclient->redist[AFI_IP6][type], vrf_id)) + return; + vrf_bitmap_set(&zclient->redist[AFI_IP6][type], vrf_id); + + if (zclient->sock > 0) + zebra_redistribute_send(ZEBRA_REDISTRIBUTE_ADD, zclient, + AFI_IP6, type, 0, vrf_id); +} + +void ospf6_zebra_no_redistribute(int type, vrf_id_t vrf_id) +{ + if (!vrf_bitmap_check(&zclient->redist[AFI_IP6][type], vrf_id)) + return; + vrf_bitmap_unset(&zclient->redist[AFI_IP6][type], vrf_id); + if (zclient->sock > 0) + zebra_redistribute_send(ZEBRA_REDISTRIBUTE_DELETE, zclient, + AFI_IP6, type, 0, vrf_id); +} + +void ospf6_zebra_import_default_route(struct ospf6 *ospf6, bool unreg) +{ + struct prefix prefix = {}; + int command; + + if (zclient->sock < 0) { + if (IS_OSPF6_DEBUG_ZEBRA(SEND)) + zlog_debug(" Not connected to Zebra"); + return; + } + + prefix.family = AF_INET6; + prefix.prefixlen = 0; + + if (unreg) + command = ZEBRA_NEXTHOP_UNREGISTER; + else + command = ZEBRA_NEXTHOP_REGISTER; + + if (IS_OSPF6_DEBUG_ZEBRA(SEND)) + zlog_debug("%s: sending cmd %s for %pFX (vrf %u)", __func__, + zserv_command_string(command), &prefix, + ospf6->vrf_id); + + if (zclient_send_rnh(zclient, command, &prefix, SAFI_UNICAST, false, + true, ospf6->vrf_id) + == ZCLIENT_SEND_FAILURE) + flog_err(EC_LIB_ZAPI_SOCKET, "%s: zclient_send_rnh() failed", + __func__); +} + +static int ospf6_zebra_import_check_update(ZAPI_CALLBACK_ARGS) +{ + struct ospf6 *ospf6; + struct zapi_route nhr; + struct prefix matched; + + ospf6 = ospf6_lookup_by_vrf_id(vrf_id); + if (ospf6 == NULL || !IS_OSPF6_ASBR(ospf6)) + return 0; + + if (!zapi_nexthop_update_decode(zclient->ibuf, &matched, &nhr)) { + zlog_err("%s[%u]: Failure to decode route", __func__, + ospf6->vrf_id); + return -1; + } + + if (matched.family != AF_INET6 || matched.prefixlen != 0 || + nhr.type == ZEBRA_ROUTE_OSPF6) + return 0; + + ospf6->nssa_default_import_check.status = !!nhr.nexthop_num; + ospf6_abr_nssa_type_7_defaults(ospf6); + + return 0; +} + +static int ospf6_zebra_if_address_update_add(ZAPI_CALLBACK_ARGS) +{ + struct connected *c; + + c = zebra_interface_address_read(ZEBRA_INTERFACE_ADDRESS_ADD, + zclient->ibuf, vrf_id); + if (c == NULL) + return 0; + + if (IS_OSPF6_DEBUG_ZEBRA(RECV)) + zlog_debug("Zebra Interface address add: %s %5s %pFX", + c->ifp->name, prefix_family_str(c->address), + c->address); + + if (c->address->family == AF_INET6) { + ospf6_interface_state_update(c->ifp); + ospf6_interface_connected_route_update(c->ifp); + } + return 0; +} + +static int ospf6_zebra_if_address_update_delete(ZAPI_CALLBACK_ARGS) +{ + struct connected *c; + + c = zebra_interface_address_read(ZEBRA_INTERFACE_ADDRESS_DELETE, + zclient->ibuf, vrf_id); + if (c == NULL) + return 0; + + if (IS_OSPF6_DEBUG_ZEBRA(RECV)) + zlog_debug("Zebra Interface address delete: %s %5s %pFX", + c->ifp->name, prefix_family_str(c->address), + c->address); + + if (c->address->family == AF_INET6) { + ospf6_interface_connected_route_update(c->ifp); + ospf6_interface_state_update(c->ifp); + } + + connected_free(&c); + + return 0; +} + +static int ospf6_zebra_gr_update(struct ospf6 *ospf6, int command, + uint32_t stale_time) +{ + struct zapi_cap api; + + if (!zclient || zclient->sock < 0 || !ospf6) + return 1; + + memset(&api, 0, sizeof(api)); + api.cap = command; + api.stale_removal_time = stale_time; + api.vrf_id = ospf6->vrf_id; + + (void)zclient_capabilities_send(ZEBRA_CLIENT_CAPABILITIES, zclient, + &api); + + return 0; +} + +int ospf6_zebra_gr_enable(struct ospf6 *ospf6, uint32_t stale_time) +{ + if (IS_DEBUG_OSPF6_GR) + zlog_debug("Zebra enable GR [stale time %u]", stale_time); + + return ospf6_zebra_gr_update(ospf6, ZEBRA_CLIENT_GR_CAPABILITIES, + stale_time); +} + +int ospf6_zebra_gr_disable(struct ospf6 *ospf6) +{ + if (IS_DEBUG_OSPF6_GR) + zlog_debug("Zebra disable GR"); + + return ospf6_zebra_gr_update(ospf6, ZEBRA_CLIENT_GR_DISABLE, 0); +} + +static int ospf6_zebra_read_route(ZAPI_CALLBACK_ARGS) +{ + struct zapi_route api; + unsigned long ifindex; + const struct in6_addr *nexthop = &in6addr_any; + struct ospf6 *ospf6; + struct prefix_ipv6 p; + + ospf6 = ospf6_lookup_by_vrf_id(vrf_id); + + if (ospf6 == NULL) + return 0; + + if (zapi_route_decode(zclient->ibuf, &api) < 0) + return -1; + + /* we completely ignore srcdest routes for now. */ + if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRCPFX)) + return 0; + + if (IN6_IS_ADDR_LINKLOCAL(&api.prefix.u.prefix6)) + return 0; + + ifindex = api.nexthops[0].ifindex; + if (api.nexthops[0].type == NEXTHOP_TYPE_IPV6 + || api.nexthops[0].type == NEXTHOP_TYPE_IPV6_IFINDEX) + nexthop = &api.nexthops[0].gate.ipv6; + + if (IS_OSPF6_DEBUG_ZEBRA(RECV)) + zlog_debug( + "Zebra Receive route %s: %s %pFX nexthop %pI6 ifindex %ld tag %" ROUTE_TAG_PRI, + (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD ? "add" + : "delete"), + zebra_route_string(api.type), &api.prefix, nexthop, + ifindex, api.tag); + + memcpy(&p, &api.prefix, sizeof(p)); + if (is_default_prefix6(&p)) + api.type = DEFAULT_ROUTE; + + if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD) + ospf6_asbr_redistribute_add(api.type, ifindex, &api.prefix, + api.nexthop_num, nexthop, api.tag, + ospf6); + else + ospf6_asbr_redistribute_remove(api.type, ifindex, &api.prefix, + ospf6); + + return 0; +} + +DEFUN(show_zebra, + show_ospf6_zebra_cmd, + "show ipv6 ospf6 zebra [json]", + SHOW_STR + IPV6_STR + OSPF6_STR + ZEBRA_STR + JSON_STR) +{ + int i; + bool uj = use_json(argc, argv); + json_object *json; + json_object *json_zebra; + json_object *json_array; + + if (zclient == NULL) { + vty_out(vty, "Not connected to zebra\n"); + return CMD_SUCCESS; + } + + if (uj) { + json = json_object_new_object(); + json_zebra = json_object_new_object(); + json_array = json_object_new_array(); + + json_object_int_add(json_zebra, "fail", zclient->fail); + json_object_int_add( + json_zebra, "redistributeDefault", + vrf_bitmap_check(&zclient->default_information[AFI_IP6], + VRF_DEFAULT)); + for (i = 0; i < ZEBRA_ROUTE_MAX; i++) { + if (vrf_bitmap_check(&zclient->redist[AFI_IP6][i], + VRF_DEFAULT)) + json_object_array_add( + json_array, + json_object_new_string( + zebra_route_string(i))); + } + json_object_object_add(json_zebra, "redistribute", json_array); + json_object_object_add(json, "zebraInformation", json_zebra); + + vty_json(vty, json); + } else { + vty_out(vty, "Zebra Information\n"); + vty_out(vty, " fail: %d\n", zclient->fail); + vty_out(vty, " redistribute default: %d\n", + vrf_bitmap_check(&zclient->default_information[AFI_IP6], + VRF_DEFAULT)); + vty_out(vty, " redistribute:"); + for (i = 0; i < ZEBRA_ROUTE_MAX; i++) { + if (vrf_bitmap_check(&zclient->redist[AFI_IP6][i], + VRF_DEFAULT)) + vty_out(vty, " %s", zebra_route_string(i)); + } + vty_out(vty, "\n"); + } + return CMD_SUCCESS; +} + +static void ospf6_zebra_append_opaque_attr(struct ospf6_route *request, + struct zapi_route *api) +{ + struct ospf_zebra_opaque ospf_opaque = {}; + + /* OSPF path type */ + snprintf(ospf_opaque.path_type, sizeof(ospf_opaque.path_type), "%s", + OSPF6_PATH_TYPE_NAME(request->path.type)); + + switch (request->path.type) { + case OSPF6_PATH_TYPE_INTRA: + case OSPF6_PATH_TYPE_INTER: + /* OSPF area ID */ + (void)inet_ntop(AF_INET, &request->path.area_id, + ospf_opaque.area_id, + sizeof(ospf_opaque.area_id)); + break; + case OSPF6_PATH_TYPE_EXTERNAL1: + case OSPF6_PATH_TYPE_EXTERNAL2: + /* OSPF route tag */ + snprintf(ospf_opaque.tag, sizeof(ospf_opaque.tag), "%u", + request->path.tag); + break; + default: + break; + } + + SET_FLAG(api->message, ZAPI_MESSAGE_OPAQUE); + api->opaque.length = sizeof(struct ospf_zebra_opaque); + memcpy(api->opaque.data, &ospf_opaque, api->opaque.length); +} + +#define ADD 0 +#define REM 1 +static void ospf6_zebra_route_update(int type, struct ospf6_route *request, + struct ospf6 *ospf6) +{ + struct zapi_route api; + int nhcount; + int ret = 0; + struct prefix *dest; + + if (IS_OSPF6_DEBUG_ZEBRA(SEND)) + zlog_debug("Zebra Send %s route: %pFX", + (type == REM ? "remove" : "add"), &request->prefix); + + if (zclient->sock < 0) { + if (IS_OSPF6_DEBUG_ZEBRA(SEND)) + zlog_debug(" Not connected to Zebra"); + return; + } + + if (request->path.origin.adv_router == ospf6->router_id + && (request->path.type == OSPF6_PATH_TYPE_EXTERNAL1 + || request->path.type == OSPF6_PATH_TYPE_EXTERNAL2)) { + if (IS_OSPF6_DEBUG_ZEBRA(SEND)) + zlog_debug(" Ignore self-originated external route"); + return; + } + + /* If removing is the best path and if there's another path, + * treat this request as add the secondary path - if there are + * nexthops. + */ + if (type == REM && ospf6_route_is_best(request) && request->next && + ospf6_route_is_same(request, request->next) && + ospf6_route_num_nexthops(request->next) > 0) { + if (IS_OSPF6_DEBUG_ZEBRA(SEND)) + zlog_debug( + " Best-path removal resulted Secondary addition"); + type = ADD; + request = request->next; + } + + /* Only the best path will be sent to zebra. */ + if (!ospf6_route_is_best(request)) { + /* this is not preferred best route, ignore */ + if (IS_OSPF6_DEBUG_ZEBRA(SEND)) + zlog_debug(" Ignore non-best route"); + return; + } + + nhcount = ospf6_route_num_nexthops(request); + if (nhcount == 0) { + if (type == ADD) { + if (IS_OSPF6_DEBUG_ZEBRA(SEND)) + zlog_debug(" No nexthop, ignore"); + return; + } else if (IS_OSPF6_DEBUG_ZEBRA(SEND)) + zlog_debug(" No nexthop, rem ok"); + } + + dest = &request->prefix; + + memset(&api, 0, sizeof(api)); + api.vrf_id = ospf6->vrf_id; + api.type = ZEBRA_ROUTE_OSPF6; + api.safi = SAFI_UNICAST; + api.prefix = *dest; + + if (nhcount > ospf6->max_multipath) { + if (IS_OSPF6_DEBUG_ZEBRA(SEND)) + zlog_debug( + " Nexthop count is greater than configured maximum-path, hence ignore the extra nexthops"); + } + + api.nexthop_num = MIN(nhcount, ospf6->max_multipath); + if (api.nexthop_num > 0) { + SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP); + ospf6_route_zebra_copy_nexthops(request, api.nexthops, + api.nexthop_num, api.vrf_id); + } + + SET_FLAG(api.message, ZAPI_MESSAGE_METRIC); + api.metric = (request->path.metric_type == 2 ? request->path.u.cost_e2 + : request->path.cost); + if (request->path.tag) { + SET_FLAG(api.message, ZAPI_MESSAGE_TAG); + api.tag = request->path.tag; + } + + SET_FLAG(api.message, ZAPI_MESSAGE_DISTANCE); + api.distance = ospf6_distance_apply((struct prefix_ipv6 *)dest, request, + ospf6); + + if (type == ADD + && CHECK_FLAG(ospf6->config_flags, OSPF6_SEND_EXTRA_DATA_TO_ZEBRA)) + ospf6_zebra_append_opaque_attr(request, &api); + + if (type == REM) + ret = zclient_route_send(ZEBRA_ROUTE_DELETE, zclient, &api); + else + ret = zclient_route_send(ZEBRA_ROUTE_ADD, zclient, &api); + + if (ret == ZCLIENT_SEND_FAILURE) + flog_err(EC_LIB_ZAPI_SOCKET, + "zclient_route_send() %s failed: %s", + (type == REM ? "delete" : "add"), + safe_strerror(errno)); + + return; +} + +void ospf6_zebra_route_update_add(struct ospf6_route *request, + struct ospf6 *ospf6) +{ + if (ospf6->gr_info.restart_in_progress + || ospf6->gr_info.prepare_in_progress) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "Zebra: Graceful Restart in progress -- not installing %pFX", + &request->prefix); + return; + } + + ospf6_zebra_route_update(ADD, request, ospf6); +} + +void ospf6_zebra_route_update_remove(struct ospf6_route *request, + struct ospf6 *ospf6) +{ + if (ospf6->gr_info.restart_in_progress + || ospf6->gr_info.prepare_in_progress) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "Zebra: Graceful Restart in progress -- not uninstalling %pFX", + &request->prefix); + return; + } + + ospf6_zebra_route_update(REM, request, ospf6); +} + +void ospf6_zebra_add_discard(struct ospf6_route *request, struct ospf6 *ospf6) +{ + struct zapi_route api; + struct prefix *dest = &request->prefix; + + if (ospf6->gr_info.restart_in_progress + || ospf6->gr_info.prepare_in_progress) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "Zebra: Graceful Restart in progress -- not installing %pFX", + &request->prefix); + return; + } + + if (!CHECK_FLAG(request->flag, OSPF6_ROUTE_BLACKHOLE_ADDED)) { + memset(&api, 0, sizeof(api)); + api.vrf_id = ospf6->vrf_id; + api.type = ZEBRA_ROUTE_OSPF6; + api.safi = SAFI_UNICAST; + api.prefix = *dest; + zapi_route_set_blackhole(&api, BLACKHOLE_NULL); + + zclient_route_send(ZEBRA_ROUTE_ADD, zclient, &api); + + if (IS_OSPF6_DEBUG_ZEBRA(SEND)) + zlog_debug("Zebra: Route add discard %pFX", dest); + + SET_FLAG(request->flag, OSPF6_ROUTE_BLACKHOLE_ADDED); + } else { + if (IS_OSPF6_DEBUG_ZEBRA(SEND)) + zlog_debug( + "Zebra: Blackhole route present already %pFX", + dest); + } +} + +void ospf6_zebra_delete_discard(struct ospf6_route *request, + struct ospf6 *ospf6) +{ + struct zapi_route api; + struct prefix *dest = &request->prefix; + + if (ospf6->gr_info.restart_in_progress + || ospf6->gr_info.prepare_in_progress) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "Zebra: Graceful Restart in progress -- not uninstalling %pFX", + &request->prefix); + return; + } + + if (CHECK_FLAG(request->flag, OSPF6_ROUTE_BLACKHOLE_ADDED)) { + memset(&api, 0, sizeof(api)); + api.vrf_id = ospf6->vrf_id; + api.type = ZEBRA_ROUTE_OSPF6; + api.safi = SAFI_UNICAST; + api.prefix = *dest; + zapi_route_set_blackhole(&api, BLACKHOLE_NULL); + + zclient_route_send(ZEBRA_ROUTE_DELETE, zclient, &api); + + if (IS_OSPF6_DEBUG_ZEBRA(SEND)) + zlog_debug("Zebra: Route delete discard %pFX", dest); + + UNSET_FLAG(request->flag, OSPF6_ROUTE_BLACKHOLE_ADDED); + } else { + if (IS_OSPF6_DEBUG_ZEBRA(SEND)) + zlog_debug( + "Zebra: Blackhole route already deleted %pFX", + dest); + } +} + +static struct ospf6_distance *ospf6_distance_new(void) +{ + return XCALLOC(MTYPE_OSPF6_DISTANCE, sizeof(struct ospf6_distance)); +} + +static void ospf6_distance_free(struct ospf6_distance *odistance) +{ + XFREE(MTYPE_OSPF6_DISTANCE, odistance); +} + +int ospf6_distance_set(struct vty *vty, struct ospf6 *o, + const char *distance_str, const char *ip_str, + const char *access_list_str) +{ + int ret; + struct prefix_ipv6 p; + uint8_t distance; + struct route_node *rn; + struct ospf6_distance *odistance; + + ret = str2prefix_ipv6(ip_str, &p); + if (ret == 0) { + vty_out(vty, "Malformed prefix\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + distance = atoi(distance_str); + + /* Get OSPF6 distance node. */ + rn = route_node_get(o->distance_table, (struct prefix *)&p); + if (rn->info) { + odistance = rn->info; + route_unlock_node(rn); + } else { + odistance = ospf6_distance_new(); + rn->info = odistance; + } + + /* Set distance value. */ + odistance->distance = distance; + + /* Reset access-list configuration. */ + if (odistance->access_list) { + free(odistance->access_list); + odistance->access_list = NULL; + } + if (access_list_str) + odistance->access_list = strdup(access_list_str); + + return CMD_SUCCESS; +} + +int ospf6_distance_unset(struct vty *vty, struct ospf6 *o, + const char *distance_str, const char *ip_str, + const char *access_list_str) +{ + int ret; + struct prefix_ipv6 p; + struct route_node *rn; + struct ospf6_distance *odistance; + + ret = str2prefix_ipv6(ip_str, &p); + if (ret == 0) { + vty_out(vty, "Malformed prefix\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + rn = route_node_lookup(o->distance_table, (struct prefix *)&p); + if (!rn) { + vty_out(vty, "Cant't find specified prefix\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + odistance = rn->info; + + if (odistance->access_list) + free(odistance->access_list); + ospf6_distance_free(odistance); + + rn->info = NULL; + route_unlock_node(rn); + route_unlock_node(rn); + + return CMD_SUCCESS; +} + +void ospf6_distance_reset(struct ospf6 *o) +{ + struct route_node *rn; + struct ospf6_distance *odistance; + + for (rn = route_top(o->distance_table); rn; rn = route_next(rn)) + if ((odistance = rn->info) != NULL) { + if (odistance->access_list) + free(odistance->access_list); + ospf6_distance_free(odistance); + rn->info = NULL; + route_unlock_node(rn); + } +} + +uint8_t ospf6_distance_apply(struct prefix_ipv6 *p, struct ospf6_route * or, + struct ospf6 *ospf6) +{ + struct ospf6 *o; + + o = ospf6; + if (o == NULL) + return 0; + + if (o->distance_intra) + if (or->path.type == OSPF6_PATH_TYPE_INTRA) + return o->distance_intra; + + if (o->distance_inter) + if (or->path.type == OSPF6_PATH_TYPE_INTER) + return o->distance_inter; + + if (o->distance_external) + if (or->path.type == OSPF6_PATH_TYPE_EXTERNAL1 || + or->path.type == OSPF6_PATH_TYPE_EXTERNAL2) + return o->distance_external; + + if (o->distance_all) + return o->distance_all; + + return 0; +} + +static void ospf6_zebra_connected(struct zclient *zclient) +{ + struct ospf6 *ospf6; + struct listnode *node; + + /* Send the client registration */ + bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT); + + zclient_send_reg_requests(zclient, VRF_DEFAULT); + + /* Activate graceful restart if configured. */ + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { + if (!ospf6->gr_info.restart_support) + continue; + (void)ospf6_zebra_gr_enable(ospf6, ospf6->gr_info.grace_period); + } +} + +static zclient_handler *const ospf6_handlers[] = { + [ZEBRA_ROUTER_ID_UPDATE] = ospf6_router_id_update_zebra, + [ZEBRA_INTERFACE_ADDRESS_ADD] = ospf6_zebra_if_address_update_add, + [ZEBRA_INTERFACE_ADDRESS_DELETE] = ospf6_zebra_if_address_update_delete, + [ZEBRA_REDISTRIBUTE_ROUTE_ADD] = ospf6_zebra_read_route, + [ZEBRA_REDISTRIBUTE_ROUTE_DEL] = ospf6_zebra_read_route, + [ZEBRA_NEXTHOP_UPDATE] = ospf6_zebra_import_check_update, +}; + +void ospf6_zebra_init(struct event_loop *master) +{ + /* Allocate zebra structure. */ + zclient = zclient_new(master, &zclient_options_default, ospf6_handlers, + array_size(ospf6_handlers)); + zclient_init(zclient, ZEBRA_ROUTE_OSPF6, 0, &ospf6d_privs); + zclient->zebra_connected = ospf6_zebra_connected; + + /* Install command element for zebra node. */ + install_element(VIEW_NODE, &show_ospf6_zebra_cmd); +} + +/* Debug */ + +DEFUN (debug_ospf6_zebra_sendrecv, + debug_ospf6_zebra_sendrecv_cmd, + "debug ospf6 zebra [<send|recv>]", + DEBUG_STR + OSPF6_STR + "Debug connection between zebra\n" + "Debug Sending zebra\n" + "Debug Receiving zebra\n" + ) +{ + int idx_send_recv = 3; + unsigned char level = 0; + + if (argc == 4) { + if (strmatch(argv[idx_send_recv]->text, "send")) + level = OSPF6_DEBUG_ZEBRA_SEND; + else if (strmatch(argv[idx_send_recv]->text, "recv")) + level = OSPF6_DEBUG_ZEBRA_RECV; + } else + level = OSPF6_DEBUG_ZEBRA_SEND | OSPF6_DEBUG_ZEBRA_RECV; + + OSPF6_DEBUG_ZEBRA_ON(level); + return CMD_SUCCESS; +} + +DEFUN (no_debug_ospf6_zebra_sendrecv, + no_debug_ospf6_zebra_sendrecv_cmd, + "no debug ospf6 zebra [<send|recv>]", + NO_STR + DEBUG_STR + OSPF6_STR + "Debug connection between zebra\n" + "Debug Sending zebra\n" + "Debug Receiving zebra\n" + ) +{ + int idx_send_recv = 4; + unsigned char level = 0; + + if (argc == 5) { + if (strmatch(argv[idx_send_recv]->text, "send")) + level = OSPF6_DEBUG_ZEBRA_SEND; + else if (strmatch(argv[idx_send_recv]->text, "recv")) + level = OSPF6_DEBUG_ZEBRA_RECV; + } else + level = OSPF6_DEBUG_ZEBRA_SEND | OSPF6_DEBUG_ZEBRA_RECV; + + OSPF6_DEBUG_ZEBRA_OFF(level); + return CMD_SUCCESS; +} + + +int config_write_ospf6_debug_zebra(struct vty *vty) +{ + if (IS_OSPF6_DEBUG_ZEBRA(SEND) && IS_OSPF6_DEBUG_ZEBRA(RECV)) + vty_out(vty, "debug ospf6 zebra\n"); + else { + if (IS_OSPF6_DEBUG_ZEBRA(SEND)) + vty_out(vty, "debug ospf6 zebra send\n"); + if (IS_OSPF6_DEBUG_ZEBRA(RECV)) + vty_out(vty, "debug ospf6 zebra recv\n"); + } + return 0; +} + +void install_element_ospf6_debug_zebra(void) +{ + install_element(ENABLE_NODE, &debug_ospf6_zebra_sendrecv_cmd); + install_element(ENABLE_NODE, &no_debug_ospf6_zebra_sendrecv_cmd); + install_element(CONFIG_NODE, &debug_ospf6_zebra_sendrecv_cmd); + install_element(CONFIG_NODE, &no_debug_ospf6_zebra_sendrecv_cmd); +} diff --git a/ospf6d/ospf6_zebra.h b/ospf6d/ospf6_zebra.h new file mode 100644 index 0000000..7669b5e --- /dev/null +++ b/ospf6d/ospf6_zebra.h @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2003 Yasuhiro Ohara + */ + +#ifndef OSPF6_ZEBRA_H +#define OSPF6_ZEBRA_H + +#include "zclient.h" + +#define DEFAULT_ROUTE ZEBRA_ROUTE_MAX + +/* Debug option */ +extern unsigned char conf_debug_ospf6_zebra; +#define OSPF6_DEBUG_ZEBRA_SEND 0x01 +#define OSPF6_DEBUG_ZEBRA_RECV 0x02 +#define OSPF6_DEBUG_ZEBRA_ON(level) (conf_debug_ospf6_zebra |= level) +#define OSPF6_DEBUG_ZEBRA_OFF(level) (conf_debug_ospf6_zebra &= ~(level)) +#define IS_OSPF6_DEBUG_ZEBRA(e) (conf_debug_ospf6_zebra & OSPF6_DEBUG_ZEBRA_##e) + +/* OSPF6 distance */ +struct ospf6_distance { + /* Distance value for the IP source prefix */ + uint8_t distance; + + /* Name of the access-list to be matched */ + char *access_list; +}; + +extern struct zclient *zclient; +struct ospf6; + +extern void ospf6_zebra_route_update_add(struct ospf6_route *request, + struct ospf6 *ospf6); +extern void ospf6_zebra_route_update_remove(struct ospf6_route *request, + struct ospf6 *ospf6); + +extern void ospf6_zebra_redistribute(int, vrf_id_t vrf_id); +extern void ospf6_zebra_no_redistribute(int, vrf_id_t vrf_id); +#define ospf6_zebra_is_redistribute(type, vrf_id) \ + vrf_bitmap_check(&zclient->redist[AFI_IP6][type], vrf_id) +extern void ospf6_zebra_init(struct event_loop *tm); +extern void ospf6_zebra_import_default_route(struct ospf6 *ospf6, bool unreg); +extern void ospf6_zebra_add_discard(struct ospf6_route *request, + struct ospf6 *ospf6); +extern void ospf6_zebra_delete_discard(struct ospf6_route *request, + struct ospf6 *ospf6); + +extern void ospf6_distance_reset(struct ospf6 *ospf6); +extern uint8_t ospf6_distance_apply(struct prefix_ipv6 *p, + struct ospf6_route * or, + struct ospf6 *ospf6); + +extern int ospf6_zebra_gr_enable(struct ospf6 *ospf6, uint32_t stale_time); +extern int ospf6_zebra_gr_disable(struct ospf6 *ospf6); +extern int ospf6_distance_set(struct vty *vty, struct ospf6 *ospf6, + const char *distance_str, const char *ip_str, + const char *access_list_str); +extern int ospf6_distance_unset(struct vty *vty, struct ospf6 *ospf6, + const char *distance_str, const char *ip_str, + const char *access_list_str); + +extern int config_write_ospf6_debug_zebra(struct vty *vty); +extern void install_element_ospf6_debug_zebra(void); +extern void ospf6_zebra_vrf_register(struct ospf6 *ospf6); +extern void ospf6_zebra_vrf_deregister(struct ospf6 *ospf6); +#endif /*OSPF6_ZEBRA_H*/ diff --git a/ospf6d/ospf6d.c b/ospf6d/ospf6d.c new file mode 100644 index 0000000..d90a950 --- /dev/null +++ b/ospf6d/ospf6d.c @@ -0,0 +1,1486 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2003 Yasuhiro Ohara + */ + +#include <zebra.h> + +#include "frrevent.h" +#include "linklist.h" +#include "vty.h" +#include "command.h" +#include "plist.h" +#include "filter.h" + +#include "ospf6_proto.h" +#include "ospf6_top.h" +#include "ospf6_network.h" +#include "ospf6_lsa.h" +#include "ospf6_lsdb.h" +#include "ospf6_message.h" +#include "ospf6_route.h" +#include "ospf6_zebra.h" +#include "ospf6_spf.h" +#include "ospf6_area.h" +#include "ospf6_interface.h" +#include "ospf6_neighbor.h" +#include "ospf6_intra.h" +#include "ospf6_asbr.h" +#include "ospf6_abr.h" +#include "ospf6_flood.h" +#include "ospf6d.h" +#include "ospf6_bfd.h" +#include "ospf6_gr.h" +#include "lib/json.h" +#include "ospf6_nssa.h" +#include "ospf6_auth_trailer.h" +#include "ospf6d/ospf6d_clippy.c" + +DEFINE_MGROUP(OSPF6D, "ospf6d"); + +/* OSPF6 config processing timer thread */ +struct event *t_ospf6_cfg; + +/* OSPF6 debug event state */ +unsigned char conf_debug_ospf6_event; + +struct route_node *route_prev(struct route_node *node) +{ + struct route_node *end; + struct route_node *prev = NULL; + + end = node; + node = node->parent; + if (node) + route_lock_node(node); + while (node) { + prev = node; + node = route_next(node); + if (node == end) { + route_unlock_node(node); + node = NULL; + } + } + route_unlock_node(end); + if (prev) + route_lock_node(prev); + + return prev; +} + +static int config_write_ospf6_debug(struct vty *vty); +static int config_write_ospf6_debug_event(struct vty *vty); +static struct cmd_node debug_node = { + .name = "debug", + .node = DEBUG_NODE, + .prompt = "", + .config_write = config_write_ospf6_debug, +}; + +static int config_write_ospf6_debug(struct vty *vty) +{ + config_write_ospf6_debug_message(vty); + config_write_ospf6_debug_lsa(vty); + config_write_ospf6_debug_zebra(vty); + config_write_ospf6_debug_interface(vty); + config_write_ospf6_debug_neighbor(vty); + config_write_ospf6_debug_spf(vty); + config_write_ospf6_debug_route(vty); + config_write_ospf6_debug_brouter(vty); + config_write_ospf6_debug_asbr(vty); + config_write_ospf6_debug_abr(vty); + config_write_ospf6_debug_flood(vty); + config_write_ospf6_debug_nssa(vty); + config_write_ospf6_debug_gr_helper(vty); + config_write_ospf6_debug_auth(vty); + config_write_ospf6_debug_event(vty); + + return 0; +} + +DEFUN_NOSH (show_debugging_ospf6, + show_debugging_ospf6_cmd, + "show debugging [ospf6]", + SHOW_STR + DEBUG_STR + OSPF6_STR) +{ + vty_out(vty, "OSPF6 debugging status:\n"); + + config_write_ospf6_debug(vty); + + cmd_show_lib_debugs(vty); + + return CMD_SUCCESS; +} + +#define AREA_LSDB_TITLE_FORMAT \ + "\n Area Scoped Link State Database (Area %s)\n\n" +#define IF_LSDB_TITLE_FORMAT \ + "\n I/F Scoped Link State Database (I/F %s in Area %s)\n\n" +#define AS_LSDB_TITLE_FORMAT "\n AS Scoped Link State Database\n\n" + +static int parse_show_level(int idx_level, int argc, struct cmd_token **argv) +{ + int level = OSPF6_LSDB_SHOW_LEVEL_NORMAL; + + if (argc > idx_level) { + if (strmatch(argv[idx_level]->text, "detail")) + level = OSPF6_LSDB_SHOW_LEVEL_DETAIL; + else if (strmatch(argv[idx_level]->text, "dump")) + level = OSPF6_LSDB_SHOW_LEVEL_DUMP; + else if (strmatch(argv[idx_level]->text, "internal")) + level = OSPF6_LSDB_SHOW_LEVEL_INTERNAL; + } + + return level; +} + +static uint16_t parse_type_spec(int idx_lsa, int argc, struct cmd_token **argv) +{ + uint16_t type = 0; + + if (argc > idx_lsa) { + if (strmatch(argv[idx_lsa]->text, "router")) + type = htons(OSPF6_LSTYPE_ROUTER); + else if (strmatch(argv[idx_lsa]->text, "network")) + type = htons(OSPF6_LSTYPE_NETWORK); + else if (strmatch(argv[idx_lsa]->text, "as-external")) + type = htons(OSPF6_LSTYPE_AS_EXTERNAL); + else if (strmatch(argv[idx_lsa]->text, "intra-prefix")) + type = htons(OSPF6_LSTYPE_INTRA_PREFIX); + else if (strmatch(argv[idx_lsa]->text, "inter-router")) + type = htons(OSPF6_LSTYPE_INTER_ROUTER); + else if (strmatch(argv[idx_lsa]->text, "inter-prefix")) + type = htons(OSPF6_LSTYPE_INTER_PREFIX); + else if (strmatch(argv[idx_lsa]->text, "link")) + type = htons(OSPF6_LSTYPE_LINK); + else if (strmatch(argv[idx_lsa]->text, "type-7")) + type = htons(OSPF6_LSTYPE_TYPE_7); + } + + return type; +} + +void ospf6_lsdb_show(struct vty *vty, enum ospf_lsdb_show_level level, + uint16_t *type, uint32_t *id, uint32_t *adv_router, + struct ospf6_lsdb *lsdb, json_object *json_obj, + bool use_json) +{ + struct ospf6_lsa *lsa; + const struct route_node *end = NULL; + void (*showfunc)(struct vty *, struct ospf6_lsa *, json_object *, + bool) = NULL; + json_object *json_array = NULL; + + switch (level) { + case OSPF6_LSDB_SHOW_LEVEL_DETAIL: + showfunc = ospf6_lsa_show; + break; + case OSPF6_LSDB_SHOW_LEVEL_INTERNAL: + showfunc = ospf6_lsa_show_internal; + break; + case OSPF6_LSDB_SHOW_LEVEL_DUMP: + showfunc = ospf6_lsa_show_dump; + break; + case OSPF6_LSDB_SHOW_LEVEL_NORMAL: + default: + showfunc = ospf6_lsa_show_summary; + } + + if (use_json) + json_array = json_object_new_array(); + + if (type && id && adv_router) { + lsa = ospf6_lsdb_lookup(*type, *id, *adv_router, lsdb); + if (lsa) { + if (level == OSPF6_LSDB_SHOW_LEVEL_NORMAL) + ospf6_lsa_show(vty, lsa, json_array, use_json); + else + (*showfunc)(vty, lsa, json_array, use_json); + } + + if (use_json) + json_object_object_add(json_obj, "lsa", json_array); + return; + } + + if ((level == OSPF6_LSDB_SHOW_LEVEL_NORMAL) && !use_json) + ospf6_lsa_show_summary_header(vty); + + end = ospf6_lsdb_head(lsdb, !!type + !!(type && adv_router), + type ? *type : 0, adv_router ? *adv_router : 0, + &lsa); + while (lsa) { + if ((!adv_router || lsa->header->adv_router == *adv_router) + && (!id || lsa->header->id == *id)) + (*showfunc)(vty, lsa, json_array, use_json); + lsa = ospf6_lsdb_next(end, lsa); + } + + if (use_json) + json_object_object_add(json_obj, "lsa", json_array); +} + +static void ospf6_lsdb_show_wrapper(struct vty *vty, + enum ospf_lsdb_show_level level, + uint16_t *type, uint32_t *id, + uint32_t *adv_router, bool uj, + struct ospf6 *ospf6) +{ + struct listnode *i, *j; + struct ospf6 *o = ospf6; + struct ospf6_area *oa; + struct ospf6_interface *oi; + json_object *json = NULL; + json_object *json_array = NULL; + json_object *json_obj = NULL; + + if (uj) { + json = json_object_new_object(); + json_array = json_object_new_array(); + } + for (ALL_LIST_ELEMENTS_RO(o->area_list, i, oa)) { + if (uj) { + json_obj = json_object_new_object(); + json_object_string_add(json_obj, "areaId", oa->name); + } else + vty_out(vty, AREA_LSDB_TITLE_FORMAT, oa->name); + ospf6_lsdb_show(vty, level, type, id, adv_router, oa->lsdb, + json_obj, uj); + if (uj) + json_object_array_add(json_array, json_obj); + } + if (uj) + json_object_object_add(json, "areaScopedLinkStateDb", + json_array); + + if (uj) + json_array = json_object_new_array(); + for (ALL_LIST_ELEMENTS_RO(o->area_list, i, oa)) { + for (ALL_LIST_ELEMENTS_RO(oa->if_list, j, oi)) { + if (uj) { + json_obj = json_object_new_object(); + json_object_string_add(json_obj, "areaId", + oa->name); + json_object_string_add(json_obj, "interface", + oi->interface->name); + } else + vty_out(vty, IF_LSDB_TITLE_FORMAT, + oi->interface->name, oa->name); + ospf6_lsdb_show(vty, level, type, id, adv_router, + oi->lsdb, json_obj, uj); + if (uj) + json_object_array_add(json_array, json_obj); + } + } + if (uj) + json_object_object_add(json, "interfaceScopedLinkStateDb", + json_array); + if (uj) { + json_array = json_object_new_array(); + json_obj = json_object_new_object(); + } else + vty_out(vty, AS_LSDB_TITLE_FORMAT); + + ospf6_lsdb_show(vty, level, type, id, adv_router, o->lsdb, json_obj, + uj); + + if (uj) { + json_object_array_add(json_array, json_obj); + json_object_object_add(json, "asScopedLinkStateDb", json_array); + + vty_json(vty, json); + } else + vty_out(vty, "\n"); +} + +static void ospf6_lsdb_type_show_wrapper(struct vty *vty, + enum ospf_lsdb_show_level level, + uint16_t *type, uint32_t *id, + uint32_t *adv_router, bool uj, + struct ospf6 *ospf6) +{ + struct listnode *i, *j; + struct ospf6 *o = ospf6; + struct ospf6_area *oa; + struct ospf6_interface *oi; + json_object *json = NULL; + json_object *json_array = NULL; + json_object *json_obj = NULL; + + if (uj) { + json = json_object_new_object(); + json_array = json_object_new_array(); + } + + switch (OSPF6_LSA_SCOPE(*type)) { + case OSPF6_SCOPE_AREA: + for (ALL_LIST_ELEMENTS_RO(o->area_list, i, oa)) { + if (uj) { + json_obj = json_object_new_object(); + json_object_string_add(json_obj, "areaId", + oa->name); + } else + vty_out(vty, AREA_LSDB_TITLE_FORMAT, oa->name); + + ospf6_lsdb_show(vty, level, type, id, adv_router, + oa->lsdb, json_obj, uj); + if (uj) + json_object_array_add(json_array, json_obj); + } + if (uj) + json_object_object_add(json, "areaScopedLinkStateDb", + json_array); + break; + + case OSPF6_SCOPE_LINKLOCAL: + for (ALL_LIST_ELEMENTS_RO(o->area_list, i, oa)) { + for (ALL_LIST_ELEMENTS_RO(oa->if_list, j, oi)) { + if (uj) { + json_obj = json_object_new_object(); + json_object_string_add( + json_obj, "areaId", oa->name); + json_object_string_add( + json_obj, "interface", + oi->interface->name); + } else + vty_out(vty, IF_LSDB_TITLE_FORMAT, + oi->interface->name, oa->name); + + ospf6_lsdb_show(vty, level, type, id, + adv_router, oi->lsdb, json_obj, + uj); + + if (uj) + json_object_array_add(json_array, + json_obj); + } + } + if (uj) + json_object_object_add( + json, "interfaceScopedLinkStateDb", json_array); + break; + + case OSPF6_SCOPE_AS: + if (uj) + json_obj = json_object_new_object(); + else + vty_out(vty, AS_LSDB_TITLE_FORMAT); + + ospf6_lsdb_show(vty, level, type, id, adv_router, o->lsdb, + json_obj, uj); + if (uj) { + json_object_array_add(json_array, json_obj); + json_object_object_add(json, "asScopedLinkStateDb", + json_array); + } + break; + + default: + assert(0); + break; + } + if (uj) + vty_json(vty, json); + else + vty_out(vty, "\n"); +} + +DEFUN(show_ipv6_ospf6_database, show_ipv6_ospf6_database_cmd, + "show ipv6 ospf6 [vrf <NAME|all>] database [<detail|dump|internal>] [json]", + SHOW_STR IPV6_STR OSPF6_STR VRF_CMD_HELP_STR + "All VRFs\n" + "Display Link state database\n" + "Display details of LSAs\n" + "Dump LSAs\n" + "Display LSA's internal information\n" JSON_STR) +{ + int level; + int idx_level = 4; + struct listnode *node; + struct ospf6 *ospf6; + const char *vrf_name = NULL; + bool all_vrf = false; + int idx_vrf = 0; + bool uj = use_json(argc, argv); + + OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + if (idx_vrf > 0) + idx_level += 2; + + level = parse_show_level(idx_level, argc, argv); + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { + if (all_vrf || strcmp(ospf6->name, vrf_name) == 0) { + ospf6_lsdb_show_wrapper(vty, level, NULL, NULL, NULL, + uj, ospf6); + if (!all_vrf) + break; + } + } + + OSPF6_CMD_CHECK_VRF(uj, all_vrf, ospf6); + + return CMD_SUCCESS; +} + +DEFUN(show_ipv6_ospf6_database_type, show_ipv6_ospf6_database_type_cmd, + "show ipv6 ospf6 [vrf <NAME|all>] database <router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix> [<detail|dump|internal>] [json]", + SHOW_STR IPV6_STR OSPF6_STR VRF_CMD_HELP_STR + "All VRFs\n" + "Display Link state database\n" + "Display Router LSAs\n" + "Display Network LSAs\n" + "Display Inter-Area-Prefix LSAs\n" + "Display Inter-Area-Router LSAs\n" + "Display As-External LSAs\n" + "Display Group-Membership LSAs\n" + "Display Type-7 LSAs\n" + "Display Link LSAs\n" + "Display Intra-Area-Prefix LSAs\n" + "Display details of LSAs\n" + "Dump LSAs\n" + "Display LSA's internal information\n" JSON_STR) +{ + int idx_lsa = 4; + int idx_level = 5; + int level; + bool uj = use_json(argc, argv); + struct listnode *node; + struct ospf6 *ospf6; + uint16_t type = 0; + const char *vrf_name = NULL; + bool all_vrf = false; + int idx_vrf = 0; + + OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + if (idx_vrf > 0) { + idx_lsa += 2; + idx_level += 2; + } + + type = parse_type_spec(idx_lsa, argc, argv); + level = parse_show_level(idx_level, argc, argv); + + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { + if (all_vrf || strcmp(ospf6->name, vrf_name) == 0) { + ospf6_lsdb_type_show_wrapper(vty, level, &type, NULL, + NULL, uj, ospf6); + if (!all_vrf) + break; + } + } + + OSPF6_CMD_CHECK_VRF(uj, all_vrf, ospf6); + + return CMD_SUCCESS; +} + +DEFUN(show_ipv6_ospf6_database_id, show_ipv6_ospf6_database_id_cmd, + "show ipv6 ospf6 [vrf <NAME|all>] database <*|linkstate-id> A.B.C.D [<detail|dump|internal>] [json]", + SHOW_STR IPV6_STR OSPF6_STR VRF_CMD_HELP_STR + "All VRFs\n" + "Display Link state database\n" + "Any Link state Type\n" + "Search by Link state ID\n" + "Specify Link state ID as IPv4 address notation\n" + "Display details of LSAs\n" + "Dump LSAs\n" + "Display LSA's internal information\n" JSON_STR) +{ + int idx_ipv4 = 5; + int idx_level = 6; + int level; + bool uj = use_json(argc, argv); + struct listnode *node; + struct ospf6 *ospf6; + uint32_t id = 0; + const char *vrf_name = NULL; + bool all_vrf = false; + int idx_vrf = 0; + + OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + if (argv[idx_ipv4]->type == IPV4_TKN) + inet_pton(AF_INET, argv[idx_ipv4]->arg, &id); + + level = parse_show_level(idx_level, argc, argv); + + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { + if (all_vrf || strcmp(ospf6->name, vrf_name) == 0) { + ospf6_lsdb_show_wrapper(vty, level, NULL, &id, NULL, uj, + ospf6); + if (!all_vrf) + break; + } + } + + OSPF6_CMD_CHECK_VRF(uj, all_vrf, ospf6); + + return CMD_SUCCESS; +} + +DEFUN(show_ipv6_ospf6_database_router, show_ipv6_ospf6_database_router_cmd, + "show ipv6 ospf6 [vrf <NAME|all>] database <*|adv-router> * A.B.C.D <detail|dump|internal> [json]", + SHOW_STR IPV6_STR OSPF6_STR VRF_CMD_HELP_STR + "All VRFs\n" + "Display Link state database\n" + "Any Link state Type\n" + "Search by Advertising Router\n" + "Any Link state ID\n" + "Specify Advertising Router as IPv4 address notation\n" + "Display details of LSAs\n" + "Dump LSAs\n" + "Display LSA's internal information\n" JSON_STR) +{ + int idx_ipv4 = 6; + int idx_level = 7; + int level; + struct listnode *node; + struct ospf6 *ospf6; + uint32_t adv_router = 0; + const char *vrf_name = NULL; + bool all_vrf = false; + int idx_vrf = 0; + bool uj = use_json(argc, argv); + + OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + if (idx_vrf > 0) { + idx_ipv4 += 2; + idx_level += 2; + } + + inet_pton(AF_INET, argv[idx_ipv4]->arg, &adv_router); + level = parse_show_level(idx_level, argc, argv); + + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { + if (all_vrf || strcmp(ospf6->name, vrf_name) == 0) { + ospf6_lsdb_show_wrapper(vty, level, NULL, NULL, + &adv_router, uj, ospf6); + if (!all_vrf) + break; + } + } + + OSPF6_CMD_CHECK_VRF(uj, all_vrf, ospf6); + + return CMD_SUCCESS; +} + +static int ipv6_ospf6_database_aggr_router_common(struct vty *vty, + uint32_t adv_router, + struct ospf6 *ospf6) +{ + int level = OSPF6_LSDB_SHOW_LEVEL_DETAIL; + uint16_t type = htons(OSPF6_LSTYPE_ROUTER); + struct listnode *i; + struct ospf6_area *oa; + struct ospf6_lsdb *lsdb; + + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, i, oa)) { + if (adv_router == ospf6->router_id) + lsdb = oa->lsdb_self; + else + lsdb = oa->lsdb; + if (ospf6_create_single_router_lsa(oa, lsdb, adv_router) + == NULL) { + vty_out(vty, "Adv router is not found in LSDB."); + return CMD_SUCCESS; + } + ospf6_lsdb_show(vty, level, &type, NULL, NULL, + oa->temp_router_lsa_lsdb, NULL, false); + /* Remove the temp cache */ + ospf6_remove_temp_router_lsa(oa); + } + + vty_out(vty, "\n"); + return CMD_SUCCESS; +} + +DEFUN_HIDDEN( + show_ipv6_ospf6_database_aggr_router, + show_ipv6_ospf6_database_aggr_router_cmd, + "show ipv6 ospf6 [vrf <NAME|all>] database aggr adv-router A.B.C.D", + SHOW_STR IPV6_STR OSPF6_STR VRF_CMD_HELP_STR + "All VRFs\n" + "Display Link state database\n" + "Aggregated Router LSA\n" + "Search by Advertising Router\n" + "Specify Advertising Router as IPv4 address notation\n") +{ + int idx_ipv4 = 6; + struct listnode *node; + struct ospf6 *ospf6; + uint32_t adv_router = 0; + const char *vrf_name = NULL; + bool all_vrf = false; + int idx_vrf = 0; + + OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + if (idx_vrf > 0) + idx_ipv4 += 2; + + inet_pton(AF_INET, argv[idx_ipv4]->arg, &adv_router); + + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { + if (all_vrf || strcmp(ospf6->name, vrf_name) == 0) { + ipv6_ospf6_database_aggr_router_common(vty, adv_router, + ospf6); + + if (!all_vrf) + break; + } + } + + OSPF6_CMD_CHECK_VRF(false, all_vrf, ospf6); + + return CMD_SUCCESS; +} + +DEFUN(show_ipv6_ospf6_database_type_id, show_ipv6_ospf6_database_type_id_cmd, + "show ipv6 ospf6 [vrf <NAME|all>] database <router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix> linkstate-id A.B.C.D [<detail|dump|internal>] [json]", + SHOW_STR IPV6_STR OSPF6_STR VRF_CMD_HELP_STR + "All VRFs\n" + "Display Link state database\n" + "Display Router LSAs\n" + "Display Network LSAs\n" + "Display Inter-Area-Prefix LSAs\n" + "Display Inter-Area-Router LSAs\n" + "Display As-External LSAs\n" + "Display Group-Membership LSAs\n" + "Display Type-7 LSAs\n" + "Display Link LSAs\n" + "Display Intra-Area-Prefix LSAs\n" + "Search by Link state ID\n" + "Specify Link state ID as IPv4 address notation\n" + "Display details of LSAs\n" + "Dump LSAs\n" + "Display LSA's internal information\n" JSON_STR) +{ + int idx_lsa = 4; + int idx_ipv4 = 6; + int idx_level = 7; + int level; + bool uj = use_json(argc, argv); + struct listnode *node; + struct ospf6 *ospf6; + uint16_t type = 0; + uint32_t id = 0; + const char *vrf_name = NULL; + bool all_vrf = false; + int idx_vrf = 0; + + OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + if (idx_vrf > 0) { + idx_lsa += 2; + idx_ipv4 += 2; + idx_level += 2; + } + + type = parse_type_spec(idx_lsa, argc, argv); + inet_pton(AF_INET, argv[idx_ipv4]->arg, &id); + level = parse_show_level(idx_level, argc, argv); + + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { + if (all_vrf || strcmp(ospf6->name, vrf_name) == 0) { + ospf6_lsdb_type_show_wrapper(vty, level, &type, &id, + NULL, uj, ospf6); + if (!all_vrf) + break; + } + } + + OSPF6_CMD_CHECK_VRF(uj, all_vrf, ospf6); + + return CMD_SUCCESS; +} + +DEFUN(show_ipv6_ospf6_database_type_router, + show_ipv6_ospf6_database_type_router_cmd, + "show ipv6 ospf6 [vrf <NAME|all>] database <router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix> <*|adv-router> A.B.C.D [<detail|dump|internal>] [json]", + SHOW_STR IPV6_STR OSPF6_STR VRF_CMD_HELP_STR + "All VRFs\n" + "Display Link state database\n" + "Display Router LSAs\n" + "Display Network LSAs\n" + "Display Inter-Area-Prefix LSAs\n" + "Display Inter-Area-Router LSAs\n" + "Display As-External LSAs\n" + "Display Group-Membership LSAs\n" + "Display Type-7 LSAs\n" + "Display Link LSAs\n" + "Display Intra-Area-Prefix LSAs\n" + "Any Link state ID\n" + "Search by Advertising Router\n" + "Specify Advertising Router as IPv4 address notation\n" + "Display details of LSAs\n" + "Dump LSAs\n" + "Display LSA's internal information\n" JSON_STR) +{ + int idx_lsa = 4; + int idx_ipv4 = 6; + int idx_level = 7; + int level; + bool uj = use_json(argc, argv); + struct listnode *node; + struct ospf6 *ospf6; + uint16_t type = 0; + uint32_t adv_router = 0; + const char *vrf_name = NULL; + bool all_vrf = false; + int idx_vrf = 0; + + OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + if (idx_vrf > 0) { + idx_lsa += 2; + idx_ipv4 += 2; + idx_level += 2; + } + + type = parse_type_spec(idx_lsa, argc, argv); + inet_pton(AF_INET, argv[idx_ipv4]->arg, &adv_router); + level = parse_show_level(idx_level, argc, argv); + + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { + if (all_vrf || strcmp(ospf6->name, vrf_name) == 0) { + ospf6_lsdb_type_show_wrapper(vty, level, &type, NULL, + &adv_router, uj, ospf6); + + if (!all_vrf) + break; + } + } + + OSPF6_CMD_CHECK_VRF(uj, all_vrf, ospf6); + + return CMD_SUCCESS; +} + +DEFUN(show_ipv6_ospf6_database_id_router, + show_ipv6_ospf6_database_id_router_cmd, + "show ipv6 ospf6 [vrf <NAME|all>] database * A.B.C.D A.B.C.D [<detail|dump|internal>] [json]", + SHOW_STR IPV6_STR OSPF6_STR VRF_CMD_HELP_STR + "All VRFs\n" + "Display Link state database\n" + "Any Link state Type\n" + "Specify Link state ID as IPv4 address notation\n" + "Specify Advertising Router as IPv4 address notation\n" + "Display details of LSAs\n" + "Dump LSAs\n" + "Display LSA's internal information\n" JSON_STR) +{ + int idx_ls_id = 5; + int idx_adv_rtr = 6; + int idx_level = 7; + int level; + bool uj = use_json(argc, argv); + struct listnode *node; + struct ospf6 *ospf6; + uint32_t id = 0; + uint32_t adv_router = 0; + const char *vrf_name = NULL; + bool all_vrf = false; + int idx_vrf = 0; + + OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + if (idx_vrf > 0) { + idx_ls_id += 2; + idx_adv_rtr += 2; + idx_level += 2; + } + + inet_pton(AF_INET, argv[idx_ls_id]->arg, &id); + inet_pton(AF_INET, argv[idx_adv_rtr]->arg, &adv_router); + level = parse_show_level(idx_level, argc, argv); + + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { + if (all_vrf || strcmp(ospf6->name, vrf_name) == 0) { + ospf6_lsdb_show_wrapper(vty, level, NULL, &id, + &adv_router, uj, ospf6); + if (!all_vrf) + break; + } + } + + OSPF6_CMD_CHECK_VRF(uj, all_vrf, ospf6); + + return CMD_SUCCESS; +} + +DEFUN(show_ipv6_ospf6_database_adv_router_linkstate_id, + show_ipv6_ospf6_database_adv_router_linkstate_id_cmd, + "show ipv6 ospf6 [vrf <NAME|all>] database adv-router A.B.C.D linkstate-id A.B.C.D [<detail|dump|internal>] [json]", + SHOW_STR IPV6_STR OSPF6_STR VRF_CMD_HELP_STR + "All VRFs\n" + "Display Link state database\n" + "Search by Advertising Router\n" + "Specify Advertising Router as IPv4 address notation\n" + "Search by Link state ID\n" + "Specify Link state ID as IPv4 address notation\n" + "Display details of LSAs\n" + "Dump LSAs\n" + "Display LSA's internal information\n" JSON_STR) +{ + int idx_adv_rtr = 5; + int idx_ls_id = 7; + int idx_level = 8; + int level; + bool uj = use_json(argc, argv); + struct listnode *node; + struct ospf6 *ospf6; + uint32_t id = 0; + uint32_t adv_router = 0; + const char *vrf_name = NULL; + bool all_vrf = false; + int idx_vrf = 0; + + OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + if (idx_vrf > 0) { + idx_adv_rtr += 2; + idx_ls_id += 2; + idx_level += 2; + } + inet_pton(AF_INET, argv[idx_adv_rtr]->arg, &adv_router); + inet_pton(AF_INET, argv[idx_ls_id]->arg, &id); + level = parse_show_level(idx_level, argc, argv); + + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { + if (all_vrf || strcmp(ospf6->name, vrf_name) == 0) { + ospf6_lsdb_show_wrapper(vty, level, NULL, &id, + &adv_router, uj, ospf6); + if (!all_vrf) + break; + } + } + + OSPF6_CMD_CHECK_VRF(uj, all_vrf, ospf6); + + return CMD_SUCCESS; +} + +DEFUN(show_ipv6_ospf6_database_type_id_router, + show_ipv6_ospf6_database_type_id_router_cmd, + "show ipv6 ospf6 [vrf <NAME|all>] database <router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix> A.B.C.D A.B.C.D [<dump|internal>] [json]", + SHOW_STR IPV6_STR OSPF6_STR VRF_CMD_HELP_STR + "All VRFs\n" + "Display Link state database\n" + "Display Router LSAs\n" + "Display Network LSAs\n" + "Display Inter-Area-Prefix LSAs\n" + "Display Inter-Area-Router LSAs\n" + "Display As-External LSAs\n" + "Display Group-Membership LSAs\n" + "Display Type-7 LSAs\n" + "Display Link LSAs\n" + "Display Intra-Area-Prefix LSAs\n" + "Specify Link state ID as IPv4 address notation\n" + "Specify Advertising Router as IPv4 address notation\n" + "Dump LSAs\n" + "Display LSA's internal information\n" JSON_STR) +{ + int idx_lsa = 4; + int idx_ls_id = 5; + int idx_adv_rtr = 6; + int idx_level = 7; + int level; + bool uj = use_json(argc, argv); + struct listnode *node; + struct ospf6 *ospf6; + uint16_t type = 0; + uint32_t id = 0; + uint32_t adv_router = 0; + const char *vrf_name = NULL; + bool all_vrf = false; + int idx_vrf = 0; + + OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + if (idx_vrf > 0) { + idx_lsa += 2; + idx_ls_id += 2; + idx_adv_rtr += 2; + idx_level += 2; + } + + type = parse_type_spec(idx_lsa, argc, argv); + inet_pton(AF_INET, argv[idx_ls_id]->arg, &id); + inet_pton(AF_INET, argv[idx_adv_rtr]->arg, &adv_router); + level = parse_show_level(idx_level, argc, argv); + + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { + if (all_vrf || strcmp(ospf6->name, vrf_name) == 0) { + ospf6_lsdb_type_show_wrapper(vty, level, &type, &id, + &adv_router, uj, ospf6); + + if (!all_vrf) + break; + } + } + + OSPF6_CMD_CHECK_VRF(uj, all_vrf, ospf6); + + return CMD_SUCCESS; +} + + +DEFUN (show_ipv6_ospf6_database_type_adv_router_linkstate_id, + show_ipv6_ospf6_database_type_adv_router_linkstate_id_cmd, + "show ipv6 ospf6 [vrf <NAME|all>] database <router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix> adv-router A.B.C.D linkstate-id A.B.C.D [<dump|internal>] [json]", + SHOW_STR + IPV6_STR + OSPF6_STR + VRF_CMD_HELP_STR + "All VRFs\n" + "Display Link state database\n" + "Display Router LSAs\n" + "Display Network LSAs\n" + "Display Inter-Area-Prefix LSAs\n" + "Display Inter-Area-Router LSAs\n" + "Display As-External LSAs\n" + "Display Group-Membership LSAs\n" + "Display Type-7 LSAs\n" + "Display Link LSAs\n" + "Display Intra-Area-Prefix LSAs\n" + "Search by Advertising Router\n" + "Specify Advertising Router as IPv4 address notation\n" + "Search by Link state ID\n" + "Specify Link state ID as IPv4 address notation\n" + "Dump LSAs\n" + "Display LSA's internal information\n" + JSON_STR) +{ + int idx_lsa = 4; + int idx_adv_rtr = 6; + int idx_ls_id = 8; + int idx_level = 9; + int level; + bool uj = use_json(argc, argv); + struct listnode *node; + struct ospf6 *ospf6; + uint16_t type = 0; + uint32_t id = 0; + uint32_t adv_router = 0; + const char *vrf_name = NULL; + bool all_vrf = false; + int idx_vrf = 0; + + OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + if (idx_vrf > 0) { + idx_lsa += 2; + idx_adv_rtr += 2; + idx_ls_id += 2; + idx_level += 2; + } + + type = parse_type_spec(idx_lsa, argc, argv); + inet_pton(AF_INET, argv[idx_adv_rtr]->arg, &adv_router); + inet_pton(AF_INET, argv[idx_ls_id]->arg, &id); + level = parse_show_level(idx_level, argc, argv); + + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { + if (all_vrf || strcmp(ospf6->name, vrf_name) == 0) { + ospf6_lsdb_type_show_wrapper(vty, level, &type, &id, + &adv_router, uj, ospf6); + + if (!all_vrf) + break; + } + } + + OSPF6_CMD_CHECK_VRF(uj, all_vrf, ospf6); + + return CMD_SUCCESS; +} + +DEFUN(show_ipv6_ospf6_database_self_originated, + show_ipv6_ospf6_database_self_originated_cmd, + "show ipv6 ospf6 [vrf <NAME|all>] database self-originated [<detail|dump|internal>] [json]", + SHOW_STR IPV6_STR OSPF6_STR VRF_CMD_HELP_STR + "All VRFs\n" + "Display Link state database\n" + "Display Self-originated LSAs\n" + "Display details of LSAs\n" + "Dump LSAs\n" + "Display LSA's internal information\n" JSON_STR) +{ + int idx_level = 5; + int level; + struct listnode *node; + struct ospf6 *ospf6; + const char *vrf_name = NULL; + bool all_vrf = false; + int idx_vrf = 0; + uint32_t adv_router = 0; + bool uj = use_json(argc, argv); + + OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + if (idx_vrf > 0) + idx_level += 2; + + level = parse_show_level(idx_level, argc, argv); + + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { + adv_router = ospf6->router_id; + if (all_vrf || strcmp(ospf6->name, vrf_name) == 0) { + ospf6_lsdb_show_wrapper(vty, level, NULL, NULL, + &adv_router, uj, ospf6); + + if (!all_vrf) + break; + } + } + + OSPF6_CMD_CHECK_VRF(uj, all_vrf, ospf6); + + return CMD_SUCCESS; +} + + +DEFUN(show_ipv6_ospf6_database_type_self_originated, + show_ipv6_ospf6_database_type_self_originated_cmd, + "show ipv6 ospf6 [vrf <NAME|all>] database <router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix> self-originated [<detail|dump|internal>] [json]", + SHOW_STR IPV6_STR OSPF6_STR VRF_CMD_HELP_STR + "All VRFs\n" + "Display Link state database\n" + "Display Router LSAs\n" + "Display Network LSAs\n" + "Display Inter-Area-Prefix LSAs\n" + "Display Inter-Area-Router LSAs\n" + "Display As-External LSAs\n" + "Display Group-Membership LSAs\n" + "Display Type-7 LSAs\n" + "Display Link LSAs\n" + "Display Intra-Area-Prefix LSAs\n" + "Display Self-originated LSAs\n" + "Display details of LSAs\n" + "Dump LSAs\n" + "Display LSA's internal information\n" JSON_STR) +{ + int idx_lsa = 4; + int idx_level = 6; + int level; + struct listnode *node; + struct ospf6 *ospf6; + uint16_t type = 0; + uint32_t adv_router = 0; + bool uj = use_json(argc, argv); + + const char *vrf_name = NULL; + bool all_vrf = false; + int idx_vrf = 0; + + OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + if (idx_vrf > 0) { + idx_lsa += 2; + idx_level += 2; + } + + type = parse_type_spec(idx_lsa, argc, argv); + level = parse_show_level(idx_level, argc, argv); + + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { + if (all_vrf || strcmp(ospf6->name, vrf_name) == 0) { + adv_router = ospf6->router_id; + ospf6_lsdb_type_show_wrapper(vty, level, &type, NULL, + &adv_router, uj, ospf6); + + if (!all_vrf) + break; + } + } + + OSPF6_CMD_CHECK_VRF(uj, all_vrf, ospf6); + + return CMD_SUCCESS; +} + +DEFUN(show_ipv6_ospf6_database_type_self_originated_linkstate_id, + show_ipv6_ospf6_database_type_self_originated_linkstate_id_cmd, + "show ipv6 ospf6 [vrf <NAME|all>] database <router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix> self-originated linkstate-id A.B.C.D [<detail|dump|internal>] [json]", + SHOW_STR IPV6_STR OSPF6_STR VRF_CMD_HELP_STR + "All VRFs\n" + "Display Link state database\n" + "Display Router LSAs\n" + "Display Network LSAs\n" + "Display Inter-Area-Prefix LSAs\n" + "Display Inter-Area-Router LSAs\n" + "Display As-External LSAs\n" + "Display Group-Membership LSAs\n" + "Display Type-7 LSAs\n" + "Display Link LSAs\n" + "Display Intra-Area-Prefix LSAs\n" + "Display Self-originated LSAs\n" + "Search by Link state ID\n" + "Specify Link state ID as IPv4 address notation\n" + "Display details of LSAs\n" + "Dump LSAs\n" + "Display LSA's internal information\n" JSON_STR) +{ + int idx_lsa = 4; + int idx_ls_id = 7; + int idx_level = 8; + int level; + bool uj = use_json(argc, argv); + struct listnode *node; + struct ospf6 *ospf6; + uint16_t type = 0; + uint32_t adv_router = 0; + uint32_t id = 0; + const char *vrf_name = NULL; + bool all_vrf = false; + int idx_vrf = 0; + + OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + if (idx_vrf > 0) { + idx_lsa += 2; + idx_ls_id += 2; + idx_level += 2; + } + + + type = parse_type_spec(idx_lsa, argc, argv); + inet_pton(AF_INET, argv[idx_ls_id]->arg, &id); + level = parse_show_level(idx_level, argc, argv); + + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { + if (all_vrf || strcmp(ospf6->name, vrf_name) == 0) { + adv_router = ospf6->router_id; + ospf6_lsdb_type_show_wrapper(vty, level, &type, &id, + &adv_router, uj, ospf6); + + if (!all_vrf) + break; + } + } + + OSPF6_CMD_CHECK_VRF(uj, all_vrf, ospf6); + + return CMD_SUCCESS; +} + +DEFUN(show_ipv6_ospf6_database_type_id_self_originated, + show_ipv6_ospf6_database_type_id_self_originated_cmd, + "show ipv6 ospf6 [vrf <NAME|all>] database <router|network|inter-prefix|inter-router|as-external|group-membership|type-7|link|intra-prefix> A.B.C.D self-originated [<detail|dump|internal>] [json]", + SHOW_STR IPV6_STR OSPF6_STR VRF_CMD_HELP_STR + "All VRFs\n" + "Display Link state database\n" + "Display Router LSAs\n" + "Display Network LSAs\n" + "Display Inter-Area-Prefix LSAs\n" + "Display Inter-Area-Router LSAs\n" + "Display As-External LSAs\n" + "Display Group-Membership LSAs\n" + "Display Type-7 LSAs\n" + "Display Link LSAs\n" + "Display Intra-Area-Prefix LSAs\n" + "Specify Link state ID as IPv4 address notation\n" + "Display Self-originated LSAs\n" + "Display details of LSAs\n" + "Dump LSAs\n" + "Display LSA's internal information\n" JSON_STR) +{ + int idx_lsa = 4; + int idx_ls_id = 5; + int idx_level = 7; + int level; + bool uj = use_json(argc, argv); + struct listnode *node; + struct ospf6 *ospf6; + uint16_t type = 0; + uint32_t adv_router = 0; + uint32_t id = 0; + const char *vrf_name = NULL; + bool all_vrf = false; + int idx_vrf = 0; + + OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + if (idx_vrf > 0) { + idx_lsa += 2; + idx_ls_id += 2; + idx_level += 2; + } + + type = parse_type_spec(idx_lsa, argc, argv); + inet_pton(AF_INET, argv[idx_ls_id]->arg, &id); + level = parse_show_level(idx_level, argc, argv); + + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { + if (all_vrf || strcmp(ospf6->name, vrf_name) == 0) { + adv_router = ospf6->router_id; + ospf6_lsdb_type_show_wrapper(vty, level, &type, &id, + &adv_router, uj, ospf6); + + if (!all_vrf) + break; + } + } + + OSPF6_CMD_CHECK_VRF(uj, all_vrf, ospf6); + + return CMD_SUCCESS; +} + +static int show_ospf6_border_routers_common(struct vty *vty, int argc, + struct cmd_token **argv, + struct ospf6 *ospf6, int idx_ipv4, + int idx_argc) +{ + uint32_t adv_router; + struct ospf6_route *ro; + struct prefix prefix; + + + if (argc == idx_argc) { + if (strmatch(argv[idx_ipv4]->text, "detail")) { + for (ro = ospf6_route_head(ospf6->brouter_table); ro; + ro = ospf6_route_next(ro)) + ospf6_route_show_detail(vty, ro, NULL, false); + } else { + inet_pton(AF_INET, argv[idx_ipv4]->arg, &adv_router); + + ospf6_linkstate_prefix(adv_router, 0, &prefix); + ro = ospf6_route_lookup(&prefix, ospf6->brouter_table); + if (!ro) { + vty_out(vty, + "No Route found for Router ID: %s\n", + argv[idx_ipv4]->arg); + return CMD_SUCCESS; + } + + ospf6_route_show_detail(vty, ro, NULL, false); + return CMD_SUCCESS; + } + } else { + ospf6_brouter_show_header(vty); + + for (ro = ospf6_route_head(ospf6->brouter_table); ro; + ro = ospf6_route_next(ro)) + ospf6_brouter_show(vty, ro); + } + + return CMD_SUCCESS; +} + +DEFUN(show_ipv6_ospf6_border_routers, show_ipv6_ospf6_border_routers_cmd, + "show ipv6 ospf6 [vrf <NAME|all>] border-routers [<A.B.C.D|detail>]", + SHOW_STR IP6_STR OSPF6_STR VRF_CMD_HELP_STR + "All VRFs\n" + "Display routing table for ABR and ASBR\n" + "Router ID\n" + "Show detailed output\n") +{ + int idx_ipv4 = 4; + struct ospf6 *ospf6 = NULL; + struct listnode *node; + const char *vrf_name = NULL; + bool all_vrf = false; + int idx_vrf = 0; + int idx_argc = 5; + + OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + if (idx_vrf > 0) { + idx_argc += 2; + idx_ipv4 += 2; + } + + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { + if (all_vrf || strcmp(ospf6->name, vrf_name) == 0) { + show_ospf6_border_routers_common(vty, argc, argv, ospf6, + idx_ipv4, idx_argc); + + if (!all_vrf) + break; + } + } + + OSPF6_CMD_CHECK_VRF(false, all_vrf, ospf6); + + return CMD_SUCCESS; +} + + +DEFUN(show_ipv6_ospf6_linkstate, show_ipv6_ospf6_linkstate_cmd, + "show ipv6 ospf6 [vrf <NAME|all>] linkstate <router A.B.C.D|network A.B.C.D A.B.C.D>", + SHOW_STR IP6_STR OSPF6_STR VRF_CMD_HELP_STR + "All VRFs\n" + "Display linkstate routing table\n" + "Display Router Entry\n" + "Specify Router ID as IPv4 address notation\n" + "Display Network Entry\n" + "Specify Router ID as IPv4 address notation\n" + "Specify Link state ID as IPv4 address notation\n") +{ + int idx_ipv4 = 5; + struct listnode *node, *nnode; + struct ospf6_area *oa; + struct ospf6 *ospf6 = NULL; + const char *vrf_name = NULL; + bool all_vrf = false; + int idx_vrf = 0; + + OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + if (idx_vrf > 0) + idx_ipv4 += 2; + + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, nnode, ospf6)) { + if (all_vrf || strcmp(ospf6->name, vrf_name) == 0) { + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, oa)) { + vty_out(vty, + "\n SPF Result in Area %s\n\n", + oa->name); + ospf6_linkstate_table_show(vty, idx_ipv4, argc, + argv, oa->spf_table); + } + vty_out(vty, "\n"); + + if (!all_vrf) + break; + } + } + + OSPF6_CMD_CHECK_VRF(false, all_vrf, ospf6); + + return CMD_SUCCESS; +} + + +DEFUN(show_ipv6_ospf6_linkstate_detail, show_ipv6_ospf6_linkstate_detail_cmd, + "show ipv6 ospf6 [vrf <NAME|all>] linkstate detail", + SHOW_STR IP6_STR OSPF6_STR VRF_CMD_HELP_STR + "All VRFs\n" + "Display linkstate routing table\n" + "Display detailed information\n") +{ + int idx_detail = 4; + struct listnode *node; + struct ospf6_area *oa; + struct ospf6 *ospf6 = NULL; + const char *vrf_name = NULL; + bool all_vrf = false; + int idx_vrf = 0; + + OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + if (idx_vrf > 0) + idx_detail += 2; + + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { + if (all_vrf || strcmp(ospf6->name, vrf_name) == 0) { + for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, oa)) { + vty_out(vty, + "\n SPF Result in Area %s\n\n", + oa->name); + ospf6_linkstate_table_show(vty, idx_detail, + argc, argv, + oa->spf_table); + } + vty_out(vty, "\n"); + + if (!all_vrf) + break; + } + } + + OSPF6_CMD_CHECK_VRF(false, all_vrf, ospf6); + + return CMD_SUCCESS; +} + +DEFPY(debug_ospf6_event, debug_ospf6_event_cmd, "[no] debug ospf6 event", + NO_STR DEBUG_STR OSPF6_STR "Debug OSPFv3 event function\n") +{ + if (!no) + OSPF6_DEBUG_EVENT_ON(); + else + OSPF6_DEBUG_EVENT_OFF(); + return CMD_SUCCESS; +} + +static int config_write_ospf6_debug_event(struct vty *vty) +{ + if (IS_OSPF6_DEBUG_EVENT) + vty_out(vty, "debug ospf6 event\n"); + return 0; +} + +static void install_element_ospf6_debug_event(void) +{ + install_element(ENABLE_NODE, &debug_ospf6_event_cmd); + install_element(CONFIG_NODE, &debug_ospf6_event_cmd); +} + +/* Install ospf related commands. */ +void ospf6_init(struct event_loop *master) +{ + ospf6_top_init(); + ospf6_area_init(); + ospf6_interface_init(); + ospf6_neighbor_init(); + ospf6_zebra_init(master); + + ospf6_lsa_init(); + ospf6_spf_init(); + ospf6_intra_init(); + ospf6_asbr_init(); + ospf6_abr_init(); + ospf6_gr_init(); + ospf6_gr_helper_config_init(); + + /* initialize hooks for modifying filter rules */ + prefix_list_add_hook(ospf6_plist_update); + prefix_list_delete_hook(ospf6_plist_update); + access_list_add_hook(ospf6_filter_update); + access_list_delete_hook(ospf6_filter_update); + + ospf6_bfd_init(); + install_node(&debug_node); + + install_element_ospf6_debug_message(); + install_element_ospf6_debug_lsa(); + install_element_ospf6_debug_interface(); + install_element_ospf6_debug_neighbor(); + install_element_ospf6_debug_zebra(); + install_element_ospf6_debug_spf(); + install_element_ospf6_debug_route(); + install_element_ospf6_debug_brouter(); + install_element_ospf6_debug_asbr(); + install_element_ospf6_debug_abr(); + install_element_ospf6_debug_flood(); + install_element_ospf6_debug_nssa(); + + install_element_ospf6_clear_process(); + install_element_ospf6_clear_interface(); + + install_element(ENABLE_NODE, &show_debugging_ospf6_cmd); + + install_element(VIEW_NODE, &show_ipv6_ospf6_border_routers_cmd); + + install_element(VIEW_NODE, &show_ipv6_ospf6_linkstate_cmd); + install_element(VIEW_NODE, &show_ipv6_ospf6_linkstate_detail_cmd); + + install_element(VIEW_NODE, &show_ipv6_ospf6_database_cmd); + install_element(VIEW_NODE, &show_ipv6_ospf6_database_type_cmd); + install_element(VIEW_NODE, &show_ipv6_ospf6_database_id_cmd); + install_element(VIEW_NODE, &show_ipv6_ospf6_database_router_cmd); + install_element(VIEW_NODE, &show_ipv6_ospf6_database_type_id_cmd); + install_element(VIEW_NODE, &show_ipv6_ospf6_database_type_router_cmd); + install_element(VIEW_NODE, + &show_ipv6_ospf6_database_adv_router_linkstate_id_cmd); + install_element(VIEW_NODE, &show_ipv6_ospf6_database_id_router_cmd); + install_element(VIEW_NODE, + &show_ipv6_ospf6_database_type_id_router_cmd); + install_element( + VIEW_NODE, + &show_ipv6_ospf6_database_type_adv_router_linkstate_id_cmd); + install_element(VIEW_NODE, + &show_ipv6_ospf6_database_self_originated_cmd); + install_element(VIEW_NODE, + &show_ipv6_ospf6_database_type_self_originated_cmd); + install_element(VIEW_NODE, + &show_ipv6_ospf6_database_type_id_self_originated_cmd); + install_element( + VIEW_NODE, + &show_ipv6_ospf6_database_type_self_originated_linkstate_id_cmd); + install_element(VIEW_NODE, &show_ipv6_ospf6_database_aggr_router_cmd); + install_element_ospf6_debug_event(); + install_element_ospf6_debug_auth(); + ospf6_interface_auth_trailer_cmd_init(); + install_element_ospf6_clear_intf_auth(); +} diff --git a/ospf6d/ospf6d.h b/ospf6d/ospf6d.h new file mode 100644 index 0000000..c927ee7 --- /dev/null +++ b/ospf6d/ospf6d.h @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2003 Yasuhiro Ohara + */ + +#ifndef OSPF6D_H +#define OSPF6D_H + +#include "libospf.h" +#include "frrevent.h" +#include "memory.h" + +DECLARE_MGROUP(OSPF6D); + +/* global variables */ +extern struct event_loop *master; + +/* OSPF config processing timer thread */ +extern struct event *t_ospf6_cfg; + +/* Historical for KAME. */ +#ifndef IPV6_JOIN_GROUP +#ifdef IPV6_ADD_MEMBERSHIP +#define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP +#endif /* IPV6_ADD_MEMBERSHIP. */ +#ifdef IPV6_JOIN_MEMBERSHIP +#define IPV6_JOIN_GROUP IPV6_JOIN_MEMBERSHIP +#endif /* IPV6_JOIN_MEMBERSHIP. */ +#endif /* ! IPV6_JOIN_GROUP*/ + +#ifndef IPV6_LEAVE_GROUP +#ifdef IPV6_DROP_MEMBERSHIP +#define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP +#endif /* IPV6_DROP_MEMBERSHIP */ +#endif /* ! IPV6_LEAVE_GROUP */ + +#define MSG_OK 0 +#define MSG_NG 1 + +#define OSPF6_SUCCESS 1 +#define OSPF6_FAILURE 0 +#define OSPF6_INVALID -1 + +/* cast macro: XXX - these *must* die, ick ick. */ +#define OSPF6_PROCESS(x) ((struct ospf6 *) (x)) +#define OSPF6_AREA(x) ((struct ospf6_area *) (x)) +#define OSPF6_INTERFACE(x) ((struct ospf6_interface *) (x)) +#define OSPF6_NEIGHBOR(x) ((struct ospf6_neighbor *) (x)) + +/* operation on timeval structure */ +#define timerstring(tv, buf, size) \ + do { \ + if ((tv)->tv_sec / 60 / 60 / 24) \ + snprintf(buf, size, "%lldd%02lld:%02lld:%02lld", \ + (tv)->tv_sec / 60LL / 60 / 24, \ + (tv)->tv_sec / 60LL / 60 % 24, \ + (tv)->tv_sec / 60LL % 60, \ + (tv)->tv_sec % 60LL); \ + else \ + snprintf(buf, size, "%02lld:%02lld:%02lld", \ + (tv)->tv_sec / 60LL / 60 % 24, \ + (tv)->tv_sec / 60LL % 60, \ + (tv)->tv_sec % 60LL); \ + } while (0) + +#define threadtimer_string(now, t, buf, size) \ + do { \ + struct timeval _result; \ + if (!t) \ + snprintf(buf, size, "inactive"); \ + else { \ + timersub(&t->u.sands, &now, &_result); \ + timerstring(&_result, buf, size); \ + } \ + } while (0) + +/* for commands */ +#define OSPF6_AREA_STR "Area information\n" +#define OSPF6_AREA_ID_STR "Area ID (as an IPv4 notation)\n" +#define OSPF6_SPF_STR "Shortest Path First tree information\n" +#define OSPF6_ROUTER_ID_STR "Specify Router-ID\n" +#define OSPF6_LS_ID_STR "Specify Link State ID\n" + +#define OSPF6_CMD_CHECK_VRF(uj, all_vrf, ospf6) \ + do { \ + if (uj == false && all_vrf == false && ospf6 == NULL) { \ + vty_out(vty, "%% OSPFv3 instance not found\n"); \ + return CMD_SUCCESS; \ + } \ + } while (0) + +#define IS_OSPF6_ASBR(O) ((O)->flag & OSPF6_FLAG_ASBR) +#define OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf) \ + do { \ + if (argv_find(argv, argc, "vrf", &idx_vrf)) { \ + vrf_name = argv[idx_vrf + 1]->arg; \ + all_vrf = strmatch(vrf_name, "all"); \ + } else { \ + vrf_name = VRF_DEFAULT_NAME; \ + } \ + } while (0) + +#define OSPF6_FALSE false +#define OSPF6_TRUE true +#define OSPF6_SUCCESS 1 +#define OSPF6_FAILURE 0 +#define OSPF6_INVALID -1 + +extern struct zebra_privs_t ospf6d_privs; + +/* Event Debug option */ +extern unsigned char conf_debug_ospf6_event; +#define OSPF6_DEBUG_EVENT_ON() (conf_debug_ospf6_event = 1) +#define OSPF6_DEBUG_EVENT_OFF() (conf_debug_ospf6_event = 0) +#define IS_OSPF6_DEBUG_EVENT (conf_debug_ospf6_event) + +/* Function Prototypes */ +extern struct route_node *route_prev(struct route_node *node); + +extern void ospf6_debug(void); +extern void ospf6_init(struct event_loop *master); + +#endif /* OSPF6D_H */ diff --git a/ospf6d/subdir.am b/ospf6d/subdir.am new file mode 100644 index 0000000..f6d27c8 --- /dev/null +++ b/ospf6d/subdir.am @@ -0,0 +1,93 @@ +# +# ospf6d +# + +if OSPF6D +noinst_LIBRARIES += ospf6d/libospf6.a +sbin_PROGRAMS += ospf6d/ospf6d +vtysh_daemons += ospf6d +if SNMP +module_LTLIBRARIES += ospf6d/ospf6d_snmp.la +endif +man8 += $(MANBUILD)/frr-ospf6d.8 +endif + +ospf6d_libospf6_a_SOURCES = \ + ospf6d/ospf6_nssa.c \ + ospf6d/ospf6_abr.c \ + ospf6d/ospf6_area.c \ + ospf6d/ospf6_asbr.c \ + ospf6d/ospf6_routemap_nb.c \ + ospf6d/ospf6_routemap_nb_config.c \ + ospf6d/ospf6_bfd.c \ + ospf6d/ospf6_flood.c \ + ospf6d/ospf6_gr.c \ + ospf6d/ospf6_gr_helper.c \ + ospf6d/ospf6_interface.c \ + ospf6d/ospf6_intra.c \ + ospf6d/ospf6_lsa.c \ + ospf6d/ospf6_lsdb.c \ + ospf6d/ospf6_message.c \ + ospf6d/ospf6_neighbor.c \ + ospf6d/ospf6_network.c \ + ospf6d/ospf6_proto.c \ + ospf6d/ospf6_route.c \ + ospf6d/ospf6_spf.c \ + ospf6d/ospf6_top.c \ + ospf6d/ospf6_zebra.c \ + ospf6d/ospf6d.c \ + ospf6d/ospf6_auth_trailer.c \ + # end + +noinst_HEADERS += \ + ospf6d/ospf6_nssa.h \ + ospf6d/ospf6_abr.h \ + ospf6d/ospf6_area.h \ + ospf6d/ospf6_asbr.h \ + ospf6d/ospf6_bfd.h \ + ospf6d/ospf6_flood.h \ + ospf6d/ospf6_gr.h \ + ospf6d/ospf6_interface.h \ + ospf6d/ospf6_intra.h \ + ospf6d/ospf6_lsa.h \ + ospf6d/ospf6_lsdb.h \ + ospf6d/ospf6_message.h \ + ospf6d/ospf6_neighbor.h \ + ospf6d/ospf6_network.h \ + ospf6d/ospf6_proto.h \ + ospf6d/ospf6_route.h \ + ospf6d/ospf6_routemap_nb.h \ + ospf6d/ospf6_spf.h \ + ospf6d/ospf6_top.h \ + ospf6d/ospf6_zebra.h \ + ospf6d/ospf6d.h \ + ospf6d/ospf6_auth_trailer.h \ + # end + +ospf6d_ospf6d_LDADD = ospf6d/libospf6.a lib/libfrr.la $(LIBCAP) +ospf6d_ospf6d_SOURCES = \ + ospf6d/ospf6_main.c \ + # end + +ospf6d_ospf6d_snmp_la_SOURCES = ospf6d/ospf6_snmp.c +ospf6d_ospf6d_snmp_la_CFLAGS = $(AM_CFLAGS) $(SNMP_CFLAGS) -std=gnu11 +ospf6d_ospf6d_snmp_la_LDFLAGS = $(MODULE_LDFLAGS) +ospf6d_ospf6d_snmp_la_LIBADD = lib/libfrrsnmp.la + +clippy_scan += \ + ospf6d/ospf6d.c \ + ospf6d/ospf6_top.c \ + ospf6d/ospf6_area.c \ + ospf6d/ospf6_asbr.c \ + ospf6d/ospf6_interface.c \ + ospf6d/ospf6_lsa.c \ + ospf6d/ospf6_gr_helper.c \ + ospf6d/ospf6_gr.c \ + ospf6d/ospf6_nssa.c \ + ospf6d/ospf6_route.c \ + # end + +nodist_ospf6d_ospf6d_SOURCES = \ + yang/frr-ospf-route-map.yang.c \ + yang/frr-ospf6-route-map.yang.c \ + # end |