summaryrefslogtreecommitdiffstats
path: root/ospf6d
diff options
context:
space:
mode:
Diffstat (limited to 'ospf6d')
-rw-r--r--ospf6d/.gitignore2
-rw-r--r--ospf6d/Makefile10
-rw-r--r--ospf6d/README102
-rw-r--r--ospf6d/ospf6_abr.c1595
-rw-r--r--ospf6d/ospf6_abr.h80
-rw-r--r--ospf6d/ospf6_area.c1463
-rw-r--r--ospf6d/ospf6_area.h165
-rw-r--r--ospf6d/ospf6_asbr.c3723
-rw-r--r--ospf6d/ospf6_asbr.h172
-rw-r--r--ospf6d/ospf6_auth_trailer.c988
-rw-r--r--ospf6d/ospf6_auth_trailer.h80
-rw-r--r--ospf6d/ospf6_bfd.c290
-rw-r--r--ospf6d/ospf6_bfd.h25
-rw-r--r--ospf6d/ospf6_flood.c1282
-rw-r--r--ospf6d/ospf6_flood.h58
-rw-r--r--ospf6d/ospf6_gr.c857
-rw-r--r--ospf6d/ospf6_gr.h169
-rw-r--r--ospf6d/ospf6_gr_helper.c1416
-rw-r--r--ospf6d/ospf6_interface.c3142
-rw-r--r--ospf6d/ospf6_interface.h246
-rw-r--r--ospf6d/ospf6_intra.c2520
-rw-r--r--ospf6d/ospf6_intra.h243
-rw-r--r--ospf6d/ospf6_lsa.c1219
-rw-r--r--ospf6d/ospf6_lsa.h266
-rw-r--r--ospf6d/ospf6_lsdb.c451
-rw-r--r--ospf6d/ospf6_lsdb.h94
-rw-r--r--ospf6d/ospf6_main.c254
-rw-r--r--ospf6d/ospf6_message.c3215
-rw-r--r--ospf6d/ospf6_message.h175
-rw-r--r--ospf6d/ospf6_neighbor.c1327
-rw-r--r--ospf6d/ospf6_neighbor.h214
-rw-r--r--ospf6d/ospf6_network.c251
-rw-r--r--ospf6d/ospf6_network.h39
-rw-r--r--ospf6d/ospf6_nssa.c1483
-rw-r--r--ospf6d/ospf6_nssa.h61
-rw-r--r--ospf6d/ospf6_proto.c82
-rw-r--r--ospf6d/ospf6_proto.h91
-rw-r--r--ospf6d/ospf6_route.c1856
-rw-r--r--ospf6d/ospf6_route.h392
-rw-r--r--ospf6d/ospf6_routemap_nb.c44
-rw-r--r--ospf6d/ospf6_routemap_nb.h27
-rw-r--r--ospf6d/ospf6_routemap_nb_config.c118
-rw-r--r--ospf6d/ospf6_snmp.c1404
-rw-r--r--ospf6d/ospf6_spf.c1294
-rw-r--r--ospf6d/ospf6_spf.h159
-rw-r--r--ospf6d/ospf6_top.c2373
-rw-r--r--ospf6d/ospf6_top.h254
-rw-r--r--ospf6d/ospf6_zebra.c854
-rw-r--r--ospf6d/ospf6_zebra.h67
-rw-r--r--ospf6d/ospf6d.c1486
-rw-r--r--ospf6d/ospf6d.h123
-rw-r--r--ospf6d/subdir.am93
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(&timestamp);
+ 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(&timestamp);
+ 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