summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.ac1
-rw-r--r--doc/user/ospf6d.rst23
-rw-r--r--ospf6d/ospf6_asbr.c8
-rw-r--r--ospf6d/ospf6_flood.c42
-rw-r--r--ospf6d/ospf6_flood.h2
-rw-r--r--ospf6d/ospf6_gr.c749
-rw-r--r--ospf6d/ospf6_gr.h19
-rw-r--r--ospf6d/ospf6_gr_helper.c10
-rw-r--r--ospf6d/ospf6_intra.c37
-rw-r--r--ospf6d/ospf6_intra.h14
-rw-r--r--ospf6d/ospf6_message.c15
-rw-r--r--ospf6d/ospf6_neighbor.c16
-rw-r--r--ospf6d/ospf6_neighbor.h5
-rw-r--r--ospf6d/ospf6_spf.c16
-rw-r--r--ospf6d/ospf6_spf.h1
-rw-r--r--ospf6d/ospf6_top.c11
-rw-r--r--ospf6d/ospf6_top.h12
-rw-r--r--ospf6d/ospf6_zebra.c67
-rw-r--r--ospf6d/ospf6_zebra.h2
-rw-r--r--ospf6d/ospf6d.c1
-rw-r--r--ospf6d/subdir.am3
-rw-r--r--tests/topotests/ospf6_gr_topo1/__init__.py0
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt1/ospf6d.conf30
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_database.json95
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_neighbor.json12
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_route.json74
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_route.json139
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt1/zebra.conf22
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt2/ospf6d.conf35
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_database.json183
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_neighbor.json20
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_route.json74
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_route.json139
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt2/zebra.conf22
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt3/ospf6d.conf41
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_database.json144
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_neighbor.json28
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_route.json74
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_route.json139
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt3/zebra.conf24
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt4/ospf6d.conf35
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_database.json188
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_neighbor.json20
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_route.json74
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_route.json139
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt4/zebra.conf22
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt5/ospf6d.conf29
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_database.json100
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_neighbor.json12
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_route.json74
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_route.json139
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt5/zebra.conf20
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt6/ospf6d.conf35
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_database.json183
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_neighbor.json20
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_route.json74
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_route.json139
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt6/zebra.conf22
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt7/ospf6d.conf30
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_database.json95
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_neighbor.json12
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_route.json74
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_route.json139
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt7/zebra.conf22
-rwxr-xr-xtests/topotests/ospf6_gr_topo1/test_ospf6_gr_topo1.py381
65 files changed, 4408 insertions, 19 deletions
diff --git a/configure.ac b/configure.ac
index 2897cfef66..609d3c3230 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2519,6 +2519,7 @@ AC_DEFINE_UNQUOTED([LDPD_SOCKET], ["$frr_statedir%s%s/ldpd.sock"], [ldpd control
AC_DEFINE_UNQUOTED([ZEBRA_SERV_PATH], ["$frr_statedir%s%s/zserv.api"], [zebra api socket])
AC_DEFINE_UNQUOTED([BFDD_CONTROL_SOCKET], ["$frr_statedir%s%s/bfdd.sock"], [bfdd control socket])
AC_DEFINE_UNQUOTED([OSPFD_GR_STATE], ["$frr_statedir%s/ospfd-gr.json"], [ospfd GR state information])
+AC_DEFINE_UNQUOTED([OSPF6D_GR_STATE], ["$frr_statedir/ospf6d-gr.json"], [ospf6d GR state information])
AC_DEFINE_UNQUOTED([DAEMON_VTY_DIR], ["$frr_statedir%s%s"], [daemon vty directory])
AC_DEFINE_UNQUOTED([DAEMON_DB_DIR], ["$frr_statedir"], [daemon database directory])
diff --git a/doc/user/ospf6d.rst b/doc/user/ospf6d.rst
index 4fd86ffb13..ede2144107 100644
--- a/doc/user/ospf6d.rst
+++ b/doc/user/ospf6d.rst
@@ -264,8 +264,17 @@ Redistribute routes to OSPF6
argument injects the default route regardless of it being present in the
router. Metric values and route-map can also be specified optionally.
-Graceful Restart Helper
-=======================
+Graceful Restart
+================
+
+.. clicmd:: graceful-restart [grace-period (1-1800)]
+
+
+ Configure Graceful Restart (RFC 5187) restarting support.
+ When enabled, the default grace period is 120 seconds.
+
+ To perform a graceful shutdown, the "graceful-restart prepare ipv6 ospf"
+ EXEC-level command needs to be issued before restarting the ospf6d daemon.
.. clicmd:: graceful-restart helper-only [A.B.C.D]
@@ -297,6 +306,16 @@ Graceful Restart Helper
restarts. By default, it supports both planned and
unplanned outages.
+.. clicmd:: graceful-restart prepare ipv6 ospf
+
+
+ Initiate a graceful restart for all OSPFv3 instances configured with the
+ "graceful-restart" command. The ospf6d daemon should be restarted during
+ the instance-specific grace period, otherwise the graceful restart will fail.
+
+ This is an EXEC-level command.
+
+
.. _showing-ospf6-information:
Showing OSPF6 information
diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c
index ff651d674b..5a7023126d 100644
--- a/ospf6d/ospf6_asbr.c
+++ b/ospf6d/ospf6_asbr.c
@@ -52,6 +52,7 @@
#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");
@@ -102,6 +103,13 @@ struct ospf6_lsa *ospf6_as_external_lsa_originate(struct ospf6_route *route,
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);
diff --git a/ospf6d/ospf6_flood.c b/ospf6d/ospf6_flood.c
index f13ed3e3bb..186eac35a5 100644
--- a/ospf6d/ospf6_flood.c
+++ b/ospf6d/ospf6_flood.c
@@ -85,7 +85,7 @@ struct ospf6_lsdb *ospf6_get_scoped_lsdb_self(struct ospf6_lsa *lsa)
return lsdb_self;
}
-void ospf6_lsa_originate(struct ospf6_lsa *lsa)
+void ospf6_lsa_originate(struct ospf6 *ospf6, struct ospf6_lsa *lsa)
{
struct ospf6_lsa *old;
struct ospf6_lsdb *lsdb_self;
@@ -106,7 +106,8 @@ void ospf6_lsa_originate(struct ospf6_lsa *lsa)
/* if the new LSA does not differ from previous,
suppress this update of the LSA */
- if (old && !OSPF6_LSA_IS_DIFFER(lsa, old)) {
+ 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);
@@ -134,20 +135,20 @@ void ospf6_lsa_originate(struct ospf6_lsa *lsa)
void ospf6_lsa_originate_process(struct ospf6_lsa *lsa, struct ospf6 *process)
{
lsa->lsdb = process->lsdb;
- ospf6_lsa_originate(lsa);
+ 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(lsa);
+ 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(lsa);
+ ospf6_lsa_originate(oi->area->ospf6, lsa);
}
void ospf6_remove_id_from_external_id_table(struct ospf6 *ospf6,
@@ -326,7 +327,8 @@ void ospf6_install_lsa(struct ospf6_lsa *lsa)
lsa->installed = now;
/* Topo change handling */
- if (CHECK_LSA_TOPO_CHG_ELIGIBLE(ntohs(lsa->header->type))) {
+ 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)) {
@@ -991,6 +993,8 @@ void ospf6_receive_lsa(struct ospf6_neighbor *from,
/* 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;
@@ -1029,12 +1033,13 @@ void ospf6_receive_lsa(struct ospf6_neighbor *from,
reoriginated instance of the LSA not to be rejected by other
routers
due to MinLSArrival. */
- if (new->header->adv_router
- != from->ospf6_if->area->ospf6->router_id)
+ self_originated = (new->header->adv_router
+ == from->ospf6_if->area->ospf6->router_id);
+ if (!self_originated)
ospf6_flood(from, new);
- /* Received Grace-LSA */
- if (IS_GRACE_LSA(new)) {
+ /* Received non-self-originated Grace LSA. */
+ if (IS_GRACE_LSA(new) && !self_originated) {
struct ospf6 *ospf6;
ospf6 = ospf6_get_by_lsdb(new);
@@ -1088,8 +1093,16 @@ void ospf6_receive_lsa(struct ospf6_neighbor *from,
ospf6_acknowledge_lsa(new, ismore_recent, from);
/* (f) Self Originated LSA, section 13.4 */
- if (new->header->adv_router
- == from->ospf6_if->area->ospf6->router_id) {
+ 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
@@ -1105,6 +1118,11 @@ void ospf6_receive_lsa(struct ospf6_neighbor *from,
&new->refresh);
}
+ struct ospf6 *ospf6 = from->ospf6_if->area->ospf6;
+ struct ospf6_area *area = from->ospf6_if->area;
+ if (ospf6->gr_info.restart_in_progress)
+ ospf6_gr_check_lsdb_consistency(ospf6, area);
+
return;
}
diff --git a/ospf6d/ospf6_flood.h b/ospf6d/ospf6_flood.h
index 4e4fc55ed4..775d0d289d 100644
--- a/ospf6d/ospf6_flood.h
+++ b/ospf6d/ospf6_flood.h
@@ -32,7 +32,7 @@ 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_lsa *lsa);
+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,
diff --git a/ospf6d/ospf6_gr.c b/ospf6d/ospf6_gr.c
new file mode 100644
index 0000000000..40893ed998
--- /dev/null
+++ b/ospf6d/ospf6_gr.c
@@ -0,0 +1,749 @@
+/*
+ * This is an implementation of RFC 5187 Graceful Restart.
+ *
+ * Copyright 2021 NetDEF (c), All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#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 "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_flood.h"
+#include "ospf6d/ospf6_intra.h"
+#include "ospf6d/ospf6_spf.h"
+#include "ospf6d/ospf6_gr.h"
+#ifndef VTYSH_EXTRACT_PL
+#include "ospf6d/ospf6_gr_clippy.c"
+#endif
+
+static void ospf6_gr_nvm_delete(struct ospf6 *ospf6);
+
+/* Originate and install Grace-LSA for a given interface. */
+static int ospf6_gr_lsa_originate(struct ospf6_interface *oi)
+{
+ struct ospf6_gr_info *gr_info = &oi->area->ospf6->gr_info;
+ struct ospf6_lsa_header *lsa_header;
+ struct ospf6_grace_lsa *grace_lsa;
+ struct ospf6_lsa *lsa;
+ char buffer[OSPF6_MAX_LSASIZE];
+
+ if (IS_OSPF6_DEBUG_ORIGINATE(LINK))
+ zlog_debug("Originate Link-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);
+ if (gr_info->restart_support)
+ grace_lsa->tlv_reason.reason = OSPF6_GR_SW_RESTART;
+ else
+ grace_lsa->tlv_reason.reason = OSPF6_GR_UNKNOWN_RESTART;
+
+ /* Fill LSA Header */
+ lsa_header->age = 0;
+ lsa_header->type = htons(OSPF6_LSTYPE_GRACE_LSA);
+ 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(sizeof(*lsa_header) + sizeof(*grace_lsa));
+
+ /* LSA checksum */
+ ospf6_lsa_checksum(lsa_header);
+
+ /* create LSA */
+ lsa = ospf6_lsa_create(lsa_header);
+
+ /* Originate */
+ 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;
+
+ 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;
+ THREAD_OFF(ospf6->gr_info.t_grace_period);
+
+ /* Record in non-volatile memory that the restart is complete. */
+ ospf6_gr_nvm_delete(ospf6);
+
+ 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);
+
+ for (ALL_LIST_ELEMENTS_RO(area->if_list, anode, oi)) {
+ 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);
+ }
+ }
+
+ /*
+ * 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);
+}
+
+#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)
+ 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))
+ return false;
+ }
+ if (!found)
+ return false;
+ }
+
+ return true;
+}
+
+/* Handling of grace period expiry. */
+static int ospf6_gr_grace_period_expired(struct thread *thread)
+{
+ struct ospf6 *ospf6 = THREAD_ARG(thread);
+
+ ospf6->gr_info.t_grace_period = NULL;
+ ospf6_gr_restart_exit(ospf6, "grace period has expired");
+
+ return 0;
+}
+
+/*
+ * 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)
+{
+ 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);
+ }
+
+ /*
+ * 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.
+ */
+ json_object_int_add(json_instance, "gracePeriod",
+ ospf6->gr_info.grace_period);
+ 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.
+ */
+static 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;
+ 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, "timestamp", &json_timestamp);
+ if (json_timestamp) {
+ time_t now;
+ unsigned long remaining_time;
+
+ /* 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 {
+ /* Schedule grace period timeout. */
+ ospf6->gr_info.restart_in_progress = true;
+ remaining_time = timestamp - time(NULL);
+ if (IS_DEBUG_OSPF6_GR)
+ zlog_debug(
+ "GR: remaining time until grace period expires: %lu(s)",
+ remaining_time);
+ thread_add_timer(master, ospf6_gr_grace_period_expired,
+ ospf6, remaining_time,
+ &ospf6->gr_info.t_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);
+}
+
+/* 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));
+
+ /* Freeze OSPF routes in the RIB. */
+ if (ospf6_zebra_gr_enable(ospf6, ospf6->gr_info.grace_period)) {
+ zlog_warn(
+ "%s: failed to activate graceful restart: not connected to zebra",
+ __func__);
+ continue;
+ }
+
+ /* 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);
+ }
+ }
+
+ /* Record end of the grace period in non-volatile memory. */
+ ospf6_gr_nvm_update(ospf6);
+
+ /*
+ * 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")
+{
+ 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;
+
+ 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;
+
+ 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
index 378b7193cd..6406e8efee 100644
--- a/ospf6d/ospf6_gr.h
+++ b/ospf6d/ospf6_gr.h
@@ -32,6 +32,10 @@
#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;
@@ -67,7 +71,8 @@ enum ospf6_gr_helper_rejected_reason {
OSPF6_HELPER_NOT_A_VALID_NEIGHBOUR,
OSPF6_HELPER_PLANNED_ONLY_RESTART,
OSPF6_HELPER_TOPO_CHANGE_RTXMT_LIST,
- OSPF6_HELPER_LSA_AGE_MORE
+ OSPF6_HELPER_LSA_AGE_MORE,
+ OSPF6_HELPER_RESTARTING,
};
#ifdef roundup
@@ -119,6 +124,11 @@ 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;
};
@@ -156,6 +166,13 @@ extern void ospf6_process_maxage_grace_lsa(struct ospf6 *ospf,
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_check_lsdb_consistency(struct ospf6 *ospf,
+ struct ospf6_area *area);
+extern void ospf6_gr_nvm_read(struct ospf6 *ospf);
+extern void ospf6_gr_init(void);
+
#endif /* OSPF6_GR_H */
diff --git a/ospf6d/ospf6_gr_helper.c b/ospf6d/ospf6_gr_helper.c
index 77eb9e8b39..4522bd2619 100644
--- a/ospf6d/ospf6_gr_helper.c
+++ b/ospf6d/ospf6_gr_helper.c
@@ -360,6 +360,16 @@ int ospf6_process_grace_lsa(struct ospf6 *ospf6, struct ospf6_lsa *lsa,
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
diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c
index 06a950156b..bea8cf0edd 100644
--- a/ospf6d/ospf6_intra.c
+++ b/ospf6d/ospf6_intra.c
@@ -47,6 +47,7 @@
#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;
@@ -249,6 +250,13 @@ int ospf6_router_lsa_originate(struct thread *thread)
oa = (struct ospf6_area *)THREAD_ARG(thread);
oa->thread_router_lsa = NULL;
+ if (oa->ospf6->gr_info.restart_in_progress) {
+ if (IS_DEBUG_OSPF6_GR)
+ zlog_debug(
+ "Graceful Restart in progress, don't originate LSA");
+ return 0;
+ }
+
if (IS_OSPF6_DEBUG_ORIGINATE(ROUTER))
zlog_debug("Originate Router-LSA for Area %s", oa->name);
@@ -532,6 +540,13 @@ int ospf6_network_lsa_originate(struct thread *thread)
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 0;
+ }
+
old = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_NETWORK),
htonl(oi->interface->ifindex),
oi->area->ospf6->router_id, oi->area->lsdb);
@@ -773,6 +788,14 @@ int ospf6_link_lsa_originate(struct thread *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 0;
+ }
+
+
/* find previous LSA */
old = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_LINK),
htonl(oi->interface->ifindex),
@@ -1009,6 +1032,13 @@ int ospf6_intra_prefix_lsa_originate_stub(struct thread *thread)
oa = (struct ospf6_area *)THREAD_ARG(thread);
oa->thread_intra_prefix_lsa = NULL;
+ if (oa->ospf6->gr_info.restart_in_progress) {
+ if (IS_DEBUG_OSPF6_GR)
+ zlog_debug(
+ "Graceful Restart in progress, don't originate LSA");
+ return 0;
+ }
+
/* find previous LSA */
old = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_INTRA_PREFIX), htonl(0),
oa->ospf6->router_id, oa->lsdb);
@@ -1243,6 +1273,13 @@ int ospf6_intra_prefix_lsa_originate_transit(struct thread *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 0;
+ }
+
/* find previous LSA */
old = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_INTRA_PREFIX),
htonl(oi->interface->ifindex),
diff --git a/ospf6d/ospf6_intra.h b/ospf6d/ospf6_intra.h
index 4031ffe689..f15bf0b9b4 100644
--- a/ospf6d/ospf6_intra.h
+++ b/ospf6d/ospf6_intra.h
@@ -192,12 +192,26 @@ struct ospf6_intra_prefix_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)) \
+ thread_execute(master, ospf6_router_lsa_originate, oa, \
+ 0); \
+ } while (0)
+
#define OSPF6_NETWORK_LSA_EXECUTE(oi) \
do { \
THREAD_OFF((oi)->thread_network_lsa); \
thread_execute(master, ospf6_network_lsa_originate, oi, 0); \
} while (0)
+#define OSPF6_LINK_LSA_EXECUTE(oi) \
+ do { \
+ if (!CHECK_FLAG((oi)->flag, OSPF6_INTERFACE_DISABLE)) \
+ thread_execute(master, ospf6_link_lsa_originate, oi, \
+ 0); \
+ } while (0)
+
#define OSPF6_INTRA_PREFIX_LSA_EXECUTE_TRANSIT(oi) \
do { \
THREAD_OFF((oi)->thread_intra_prefix_lsa); \
diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c
index 3dcc74589a..53f3c3468a 100644
--- a/ospf6d/ospf6_message.c
+++ b/ospf6d/ospf6_message.c
@@ -553,6 +553,21 @@ static void ospf6_hello_recv(struct in6_addr *src, struct in6_addr *dst,
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)
thread_add_event(master, backup_seen, oi, 0, NULL);
diff --git a/ospf6d/ospf6_neighbor.c b/ospf6d/ospf6_neighbor.c
index 05ef40be47..35fbd3991c 100644
--- a/ospf6d/ospf6_neighbor.c
+++ b/ospf6d/ospf6_neighbor.c
@@ -90,6 +90,22 @@ struct ospf6_neighbor *ospf6_neighbor_lookup(uint32_t router_id,
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;
+}
+
/* create ospf6_neighbor */
struct ospf6_neighbor *ospf6_neighbor_create(uint32_t router_id,
struct ospf6_interface *oi)
diff --git a/ospf6d/ospf6_neighbor.h b/ospf6d/ospf6_neighbor.h
index 30e044da4b..f7735b87b9 100644
--- a/ospf6d/ospf6_neighbor.h
+++ b/ospf6d/ospf6_neighbor.h
@@ -23,6 +23,9 @@
#include "hook.h"
+/* Forward declaration(s). */
+struct ospf6_area;
+
/* Debug option */
extern unsigned char conf_debug_ospf6_neighbor;
#define OSPF6_DEBUG_NEIGHBOR_STATE 0x01
@@ -185,6 +188,8 @@ 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);
diff --git a/ospf6d/ospf6_spf.c b/ospf6d/ospf6_spf.c
index 1412298802..e4de6ccf91 100644
--- a/ospf6d/ospf6_spf.c
+++ b/ospf6d/ospf6_spf.c
@@ -44,6 +44,7 @@
#include "ospf6d.h"
#include "ospf6_abr.h"
#include "ospf6_nssa.h"
+#include "ospf6_zebra.h"
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_VERTEX, "OSPF6 vertex");
@@ -438,8 +439,8 @@ void ospf6_spf_table_finish(struct ospf6_route_table *result_table)
}
}
-static const char *const ospf6_spf_reason_str[] = {"R+", "R-", "N+", "N-", "L+",
- "L-", "R*", "N*", "C"};
+static const char *const ospf6_spf_reason_str[] = {
+ "R+", "R-", "N+", "N-", "L+", "L-", "R*", "N*", "C", "A", "GR"};
void ospf6_spf_reason_string(unsigned int reason, char *buf, int size)
{
@@ -1255,6 +1256,17 @@ static int ospf6_ase_calculate_timer(struct thread *t)
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->gr_info.finishing_restart = false;
+ }
+
return 0;
}
diff --git a/ospf6d/ospf6_spf.h b/ospf6d/ospf6_spf.h
index d6fbc5c13b..cc52d16861 100644
--- a/ospf6d/ospf6_spf.h
+++ b/ospf6d/ospf6_spf.h
@@ -93,6 +93,7 @@ struct ospf6_vertex {
#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)
{
diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c
index d07d36c319..3122d616cb 100644
--- a/ospf6d/ospf6_top.c
+++ b/ospf6d/ospf6_top.c
@@ -472,6 +472,12 @@ struct ospf6 *ospf6_instance_create(const char *name)
if (ospf6->fd < 0)
return ospf6;
+ /*
+ * Read from non-volatile memory whether this instance is performing a
+ * graceful restart or not.
+ */
+ ospf6_gr_nvm_read(ospf6);
+
thread_add_read(master, ospf6_receive, ospf6, ospf6->fd,
&ospf6->t_ospf6_receive);
@@ -488,7 +494,8 @@ void ospf6_delete(struct ospf6 *o)
QOBJ_UNREG(o);
ospf6_gr_helper_deinit(o);
- ospf6_flush_self_originated_lsas_now(o);
+ if (!o->gr_info.prepare_in_progress)
+ ospf6_flush_self_originated_lsas_now(o);
ospf6_disable(o);
ospf6_del(o);
@@ -555,6 +562,7 @@ static void ospf6_disable(struct ospf6 *o)
THREAD_OFF(o->t_distribute_update);
THREAD_OFF(o->t_ospf6_receive);
THREAD_OFF(o->t_external_aggr);
+ THREAD_OFF(o->gr_info.t_grace_period);
}
}
@@ -2230,6 +2238,7 @@ static int config_write_ospf6(struct vty *vty)
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");
diff --git a/ospf6d/ospf6_top.h b/ospf6d/ospf6_top.h
index 58ecf08495..3188b1f58f 100644
--- a/ospf6d/ospf6_top.h
+++ b/ospf6d/ospf6_top.h
@@ -60,6 +60,15 @@ struct ospf6_redist {
#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;
+ struct thread *t_grace_period;
+};
+
struct ospf6_gr_helper {
/* Gracefull restart Helper supported configs*/
/* Supported grace interval*/
@@ -192,6 +201,9 @@ struct ospf6 {
*/
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;
diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c
index 5403e643dc..c2e91d09bb 100644
--- a/ospf6d/ospf6_zebra.c
+++ b/ospf6d/ospf6_zebra.c
@@ -40,6 +40,7 @@
#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");
@@ -173,6 +174,36 @@ static int ospf6_zebra_if_address_update_delete(ZAPI_CALLBACK_ARGS)
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(struct zapi_cap));
+ 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)
+{
+ return ospf6_zebra_gr_update(ospf6, ZEBRA_CLIENT_GR_CAPABILITIES,
+ stale_time);
+}
+
+int ospf6_zebra_gr_disable(struct ospf6 *ospf6)
+{
+ return ospf6_zebra_gr_update(ospf6, ZEBRA_CLIENT_GR_DISABLE, 0);
+}
+
static int ospf6_zebra_read_route(ZAPI_CALLBACK_ARGS)
{
struct zapi_route api;
@@ -384,12 +415,30 @@ static void ospf6_zebra_route_update(int type, struct ospf6_route *request,
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);
}
@@ -398,6 +447,15 @@ 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;
@@ -426,6 +484,15 @@ void ospf6_zebra_delete_discard(struct ospf6_route *request,
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;
diff --git a/ospf6d/ospf6_zebra.h b/ospf6d/ospf6_zebra.h
index e25f6bf80d..77e48673c8 100644
--- a/ospf6d/ospf6_zebra.h
+++ b/ospf6d/ospf6_zebra.h
@@ -65,6 +65,8 @@ 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);
diff --git a/ospf6d/ospf6d.c b/ospf6d/ospf6d.c
index e85f4c1b61..2c8c9b9d45 100644
--- a/ospf6d/ospf6d.c
+++ b/ospf6d/ospf6d.c
@@ -1383,6 +1383,7 @@ void ospf6_init(struct thread_master *master)
ospf6_intra_init();
ospf6_asbr_init();
ospf6_abr_init();
+ ospf6_gr_init();
ospf6_gr_helper_config_init();
/* initialize hooks for modifying filter rules */
diff --git a/ospf6d/subdir.am b/ospf6d/subdir.am
index 910c26b791..be626646a0 100644
--- a/ospf6d/subdir.am
+++ b/ospf6d/subdir.am
@@ -12,6 +12,7 @@ vtysh_scan += \
ospf6d/ospf6_area.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 \
@@ -40,6 +41,7 @@ ospf6d_libospf6_a_SOURCES = \
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 \
@@ -96,6 +98,7 @@ clippy_scan += \
ospf6d/ospf6_asbr.c \
ospf6d/ospf6_lsa.c \
ospf6d/ospf6_gr_helper.c \
+ ospf6d/ospf6_gr.c \
ospf6d/ospf6_route.c \
# end
diff --git a/tests/topotests/ospf6_gr_topo1/__init__.py b/tests/topotests/ospf6_gr_topo1/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/__init__.py
diff --git a/tests/topotests/ospf6_gr_topo1/rt1/ospf6d.conf b/tests/topotests/ospf6_gr_topo1/rt1/ospf6d.conf
new file mode 100644
index 0000000000..1ee1189766
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt1/ospf6d.conf
@@ -0,0 +1,30 @@
+:assword 1
+hostname rt1
+log file ospf6d.log
+log commands
+!
+debug ospf6 lsa router originate
+debug ospf6 lsa router flooding
+debug ospf6 zebra
+debug ospf6 interface
+debug ospf6 neighbor
+debug ospf6 flooding
+debug ospf6 graceful-restart
+debug ospf6 spf process
+!
+interface lo
+ ipv6 ospf area 1
+ ipv6 ospf network point-to-point
+!
+interface eth-rt2
+ ipv6 ospf network point-to-point
+ ipv6 ospf area 1
+ ipv6 ospf hello-interval 3
+ ipv6 ospf dead-interval 9
+!
+router ospf6
+ ospf6 router-id 1.1.1.1
+ redistribute connected
+ graceful-restart grace-period 120
+ graceful-restart helper-only
+!
diff --git a/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_database.json b/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_database.json
new file mode 100644
index 0000000000..58fc114a44
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_database.json
@@ -0,0 +1,95 @@
+{
+ "areaScopedLinkStateDb":[
+ {
+ "areaId":"1",
+ "lsa":[
+ {
+ "type":"Rtr",
+ "advRouter":"1.1.1.1"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"2.2.2.2"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::2\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::3\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::4\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::6\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::5\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::7\/128"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"2.2.2.2",
+ "payload":"7.7.7.7"
+ },
+ {
+ "type":"INP",
+ "advRouter":"1.1.1.1",
+ "payload":"2001:db8:1000::1\/128"
+ }
+ ]
+ }
+ ],
+ "interfaceScopedLinkStateDb":[
+ {
+ "areaId":"1",
+ "interface":"eth-rt2",
+ "lsa":[
+ {
+ "type":"Lnk",
+ "advRouter":"1.1.1.1"
+ },
+ {
+ "type":"Lnk",
+ "advRouter":"2.2.2.2"
+ }
+ ]
+ },
+ {
+ "areaId":"1",
+ "interface":"lo",
+ "lsa":[
+ ]
+ }
+ ],
+ "asScopedLinkStateDb":[
+ {
+ "lsa":[
+ {
+ "type":"ASE",
+ "advRouter":"1.1.1.1",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"ASE",
+ "advRouter":"7.7.7.7",
+ "payload":"2001:db8:1000::7\/128"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_neighbor.json b/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_neighbor.json
new file mode 100644
index 0000000000..cb88358639
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_neighbor.json
@@ -0,0 +1,12 @@
+{
+ "neighbors":[
+ {
+ "neighborId":"2.2.2.2",
+ "priority":1,
+ "state":"Full",
+ "ifState":"PointToPoint",
+ "interfaceName":"eth-rt2",
+ "interfaceState":"PointToPoint"
+ }
+ ]
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_route.json b/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_route.json
new file mode 100644
index 0000000000..0c69310eb4
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_route.json
@@ -0,0 +1,74 @@
+{
+ "routes":{
+ "2001:db8:1000::1\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"lo"
+ }
+ ]
+ },
+ "2001:db8:1000::2\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ },
+ "2001:db8:1000::3\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ },
+ "2001:db8:1000::4\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ },
+ "2001:db8:1000::5\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ },
+ "2001:db8:1000::6\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ },
+ "2001:db8:1000::7\/128":{
+ "isBestRoute":false,
+ "destinationType":"N",
+ "pathType":"E2",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_route.json b/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_route.json
new file mode 100644
index 0000000000..66ee57ce84
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_route.json
@@ -0,0 +1,139 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":50,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::7\/128":[
+ {
+ "prefix":"2001:db8:1000::7\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":50,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt1/zebra.conf b/tests/topotests/ospf6_gr_topo1/rt1/zebra.conf
new file mode 100644
index 0000000000..f29f5b73fb
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt1/zebra.conf
@@ -0,0 +1,22 @@
+password 1
+hostname rt1
+log file zebra.log
+log commands
+!
+debug zebra event
+debug zebra packet
+debug zebra rib
+debug zebra kernel
+!
+interface lo
+ ip address 1.1.1.1/32
+ ipv6 address 2001:db8:1000::1/128
+!
+interface stub1
+!
+interface eth-rt2
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf6_gr_topo1/rt2/ospf6d.conf b/tests/topotests/ospf6_gr_topo1/rt2/ospf6d.conf
new file mode 100644
index 0000000000..6cd8d1a8e3
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt2/ospf6d.conf
@@ -0,0 +1,35 @@
+password 1
+hostname rt2
+log file ospf6d.log
+log commands
+!
+debug ospf6 lsa router originate
+debug ospf6 lsa router flooding
+debug ospf6 zebra
+debug ospf6 interface
+debug ospf6 neighbor
+debug ospf6 flooding
+debug ospf6 graceful-restart
+debug ospf6 spf process
+!
+interface lo
+ ipv6 ospf area 0
+ ipv6 ospf network point-to-point
+!
+interface eth-rt1
+ ipv6 ospf network point-to-point
+ ipv6 ospf area 1
+ ipv6 ospf hello-interval 3
+ ipv6 ospf dead-interval 9
+!
+interface eth-rt3
+ ipv6 ospf network point-to-point
+ ipv6 ospf area 0
+ ipv6 ospf hello-interval 3
+ ipv6 ospf dead-interval 9
+!
+router ospf6
+ ospf6 router-id 2.2.2.2
+ graceful-restart grace-period 120
+ graceful-restart helper-only
+!
diff --git a/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_database.json b/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_database.json
new file mode 100644
index 0000000000..fb16326196
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_database.json
@@ -0,0 +1,183 @@
+{
+ "areaScopedLinkStateDb":[
+ {
+ "areaId":"0",
+ "lsa":[
+ {
+ "type":"Rtr",
+ "advRouter":"2.2.2.2"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"4.4.4.4"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"6.6.6.6"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::5\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::7\/128"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"2.2.2.2",
+ "payload":"1.1.1.1"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"6.6.6.6",
+ "payload":"7.7.7.7"
+ },
+ {
+ "type":"INP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::2\/128"
+ },
+ {
+ "type":"INP",
+ "advRouter":"3.3.3.3",
+ "payload":"2001:db8:1000::3\/128"
+ },
+ {
+ "type":"INP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::4\/128"
+ },
+ {
+ "type":"INP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::6\/128"
+ }
+ ]
+ },
+ {
+ "areaId":"1",
+ "lsa":[
+ {
+ "type":"Rtr",
+ "advRouter":"1.1.1.1"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"2.2.2.2"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::2\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::3\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::4\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::6\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::5\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::7\/128"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"2.2.2.2",
+ "payload":"7.7.7.7"
+ },
+ {
+ "type":"INP",
+ "advRouter":"1.1.1.1",
+ "payload":"2001:db8:1000::1\/128"
+ }
+ ]
+ }
+ ],
+ "interfaceScopedLinkStateDb":[
+ {
+ "areaId":"0",
+ "interface":"eth-rt3",
+ "lsa":[
+ {
+ "type":"Lnk",
+ "advRouter":"2.2.2.2"
+ },
+ {
+ "type":"Lnk",
+ "advRouter":"3.3.3.3"
+ }
+ ]
+ },
+ {
+ "areaId":"0",
+ "interface":"lo",
+ "lsa":[
+ ]
+ },
+ {
+ "areaId":"1",
+ "interface":"eth-rt1",
+ "lsa":[
+ {
+ "type":"Lnk",
+ "advRouter":"1.1.1.1"
+ },
+ {
+ "type":"Lnk",
+ "advRouter":"2.2.2.2"
+ }
+ ]
+ }
+ ],
+ "asScopedLinkStateDb":[
+ {
+ "lsa":[
+ {
+ "type":"ASE",
+ "advRouter":"1.1.1.1",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"ASE",
+ "advRouter":"7.7.7.7",
+ "payload":"2001:db8:1000::7\/128"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_neighbor.json b/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_neighbor.json
new file mode 100644
index 0000000000..e4f27bf37f
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_neighbor.json
@@ -0,0 +1,20 @@
+{
+ "neighbors":[
+ {
+ "neighborId":"3.3.3.3",
+ "priority":1,
+ "state":"Full",
+ "ifState":"PointToPoint",
+ "interfaceName":"eth-rt3",
+ "interfaceState":"PointToPoint"
+ },
+ {
+ "neighborId":"1.1.1.1",
+ "priority":1,
+ "state":"Full",
+ "ifState":"PointToPoint",
+ "interfaceName":"eth-rt1",
+ "interfaceState":"PointToPoint"
+ }
+ ]
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_route.json b/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_route.json
new file mode 100644
index 0000000000..34013a19de
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_route.json
@@ -0,0 +1,74 @@
+{
+ "routes":{
+ "2001:db8:1000::1\/128":{
+ "isBestRoute":false,
+ "destinationType":"N",
+ "pathType":"E2",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt1"
+ }
+ ]
+ },
+ "2001:db8:1000::2\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"lo"
+ }
+ ]
+ },
+ "2001:db8:1000::3\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ "2001:db8:1000::4\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ "2001:db8:1000::5\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ "2001:db8:1000::6\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ "2001:db8:1000::7\/128":{
+ "isBestRoute":false,
+ "destinationType":"N",
+ "pathType":"E2",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_route.json b/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_route.json
new file mode 100644
index 0000000000..624ff709e3
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_route.json
@@ -0,0 +1,139 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::7\/128":[
+ {
+ "prefix":"2001:db8:1000::7\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt2/zebra.conf b/tests/topotests/ospf6_gr_topo1/rt2/zebra.conf
new file mode 100644
index 0000000000..e4fe7620da
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt2/zebra.conf
@@ -0,0 +1,22 @@
+password 1
+hostname rt2
+log file zebra.log
+log commands
+!
+debug zebra event
+debug zebra packet
+debug zebra rib
+debug zebra kernel
+!
+interface lo
+ ip address 2.2.2.2/32
+ ipv6 address 2001:db8:1000::2/128
+!
+interface eth-rt1
+!
+interface eth-rt3
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf6_gr_topo1/rt3/ospf6d.conf b/tests/topotests/ospf6_gr_topo1/rt3/ospf6d.conf
new file mode 100644
index 0000000000..6a63d8f788
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt3/ospf6d.conf
@@ -0,0 +1,41 @@
+password 1
+hostname rt3
+log file ospf6d.log
+log commands
+!
+debug ospf6 lsa router originate
+debug ospf6 lsa router flooding
+debug ospf6 zebra
+debug ospf6 interface
+debug ospf6 neighbor
+debug ospf6 flooding
+debug ospf6 graceful-restart
+debug ospf6 spf process
+!
+interface lo
+ ipv6 ospf area 0
+ ipv6 ospf network point-to-point
+!
+interface eth-rt2
+ ipv6 ospf network point-to-point
+ ipv6 ospf area 0
+ ipv6 ospf hello-interval 3
+ ipv6 ospf dead-interval 9
+!
+interface eth-rt4
+ ipv6 ospf network point-to-point
+ ipv6 ospf area 0
+ ipv6 ospf hello-interval 3
+ ipv6 ospf dead-interval 9
+!
+interface eth-rt6
+ ipv6 ospf network point-to-point
+ ipv6 ospf area 0
+ ipv6 ospf hello-interval 3
+ ipv6 ospf dead-interval 9
+!
+router ospf6
+ ospf6 router-id 3.3.3.3
+ graceful-restart grace-period 120
+ graceful-restart helper-only
+!
diff --git a/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_database.json b/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_database.json
new file mode 100644
index 0000000000..f8a8f76093
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_database.json
@@ -0,0 +1,144 @@
+{
+ "areaScopedLinkStateDb":[
+ {
+ "areaId":"0",
+ "lsa":[
+ {
+ "type":"Rtr",
+ "advRouter":"2.2.2.2"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"4.4.4.4"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"6.6.6.6"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::5\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::7\/128"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"2.2.2.2",
+ "payload":"1.1.1.1"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"6.6.6.6",
+ "payload":"7.7.7.7"
+ },
+ {
+ "type":"INP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::2\/128"
+ },
+ {
+ "type":"INP",
+ "advRouter":"3.3.3.3",
+ "payload":"2001:db8:1000::3\/128"
+ },
+ {
+ "type":"INP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::4\/128"
+ },
+ {
+ "type":"INP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::6\/128"
+ }
+ ]
+ }
+ ],
+ "interfaceScopedLinkStateDb":[
+ {
+ "areaId":"0",
+ "interface":"eth-rt2",
+ "lsa":[
+ {
+ "type":"Lnk",
+ "advRouter":"2.2.2.2"
+ },
+ {
+ "type":"Lnk",
+ "advRouter":"3.3.3.3"
+ }
+ ]
+ },
+ {
+ "areaId":"0",
+ "interface":"eth-rt4",
+ "lsa":[
+ {
+ "type":"Lnk",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Lnk",
+ "advRouter":"4.4.4.4"
+ }
+ ]
+ },
+ {
+ "areaId":"0",
+ "interface":"eth-rt6",
+ "lsa":[
+ {
+ "type":"Lnk",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Lnk",
+ "advRouter":"6.6.6.6"
+ }
+ ]
+ },
+ {
+ "areaId":"0",
+ "interface":"lo",
+ "lsa":[
+ ]
+ }
+ ],
+ "asScopedLinkStateDb":[
+ {
+ "lsa":[
+ {
+ "type":"ASE",
+ "advRouter":"1.1.1.1",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"ASE",
+ "advRouter":"7.7.7.7",
+ "payload":"2001:db8:1000::7\/128"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_neighbor.json b/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_neighbor.json
new file mode 100644
index 0000000000..d0d7f45b0e
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_neighbor.json
@@ -0,0 +1,28 @@
+{
+ "neighbors":[
+ {
+ "neighborId":"2.2.2.2",
+ "priority":1,
+ "state":"Full",
+ "ifState":"PointToPoint",
+ "interfaceName":"eth-rt2",
+ "interfaceState":"PointToPoint"
+ },
+ {
+ "neighborId":"4.4.4.4",
+ "priority":1,
+ "state":"Full",
+ "ifState":"PointToPoint",
+ "interfaceName":"eth-rt4",
+ "interfaceState":"PointToPoint"
+ },
+ {
+ "neighborId":"6.6.6.6",
+ "priority":1,
+ "state":"Full",
+ "ifState":"PointToPoint",
+ "interfaceName":"eth-rt6",
+ "interfaceState":"PointToPoint"
+ }
+ ]
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_route.json b/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_route.json
new file mode 100644
index 0000000000..ee516b9d66
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_route.json
@@ -0,0 +1,74 @@
+{
+ "routes":{
+ "2001:db8:1000::1\/128":{
+ "isBestRoute":false,
+ "destinationType":"N",
+ "pathType":"E2",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ },
+ "2001:db8:1000::2\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ },
+ "2001:db8:1000::3\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"lo"
+ }
+ ]
+ },
+ "2001:db8:1000::4\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ },
+ "2001:db8:1000::5\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ },
+ "2001:db8:1000::6\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ },
+ "2001:db8:1000::7\/128":{
+ "isBestRoute":false,
+ "destinationType":"N",
+ "pathType":"E2",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_route.json b/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_route.json
new file mode 100644
index 0000000000..f9b43dcdb9
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_route.json
@@ -0,0 +1,139 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::7\/128":[
+ {
+ "prefix":"2001:db8:1000::7\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt3/zebra.conf b/tests/topotests/ospf6_gr_topo1/rt3/zebra.conf
new file mode 100644
index 0000000000..3a9de21d30
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt3/zebra.conf
@@ -0,0 +1,24 @@
+password 1
+hostname rt3
+log file zebra.log
+log commands
+!
+debug zebra event
+debug zebra packet
+debug zebra rib
+debug zebra kernel
+!
+interface lo
+ ip address 3.3.3.3/32
+ ipv6 address 2001:db8:1000::3/128
+!
+interface eth-rt2
+!
+interface eth-rt4
+!
+interface eth-rt6
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf6_gr_topo1/rt4/ospf6d.conf b/tests/topotests/ospf6_gr_topo1/rt4/ospf6d.conf
new file mode 100644
index 0000000000..dff33d4094
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt4/ospf6d.conf
@@ -0,0 +1,35 @@
+password 1
+hostname rt4
+log file ospf6d.log
+log commands
+!
+debug ospf6 lsa router originate
+debug ospf6 lsa router flooding
+debug ospf6 zebra
+debug ospf6 interface
+debug ospf6 neighbor
+debug ospf6 flooding
+debug ospf6 graceful-restart
+debug ospf6 spf process
+!
+interface lo
+ ipv6 ospf area 0
+ ipv6 ospf network point-to-point
+!
+interface eth-rt3
+ ipv6 ospf network point-to-point
+ ipv6 ospf area 0
+ ipv6 ospf hello-interval 3
+ ipv6 ospf dead-interval 9
+!
+interface eth-rt5
+ ipv6 ospf network point-to-point
+ ipv6 ospf area 2
+ ipv6 ospf hello-interval 3
+ ipv6 ospf dead-interval 9
+!
+router ospf6
+ ospf6 router-id 4.4.4.4
+ graceful-restart grace-period 120
+ graceful-restart helper-only
+!
diff --git a/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_database.json b/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_database.json
new file mode 100644
index 0000000000..0954d1b8eb
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_database.json
@@ -0,0 +1,188 @@
+{
+ "areaScopedLinkStateDb":[
+ {
+ "areaId":"0",
+ "lsa":[
+ {
+ "type":"Rtr",
+ "advRouter":"2.2.2.2"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"4.4.4.4"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"6.6.6.6"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::5\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::7\/128"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"2.2.2.2",
+ "payload":"1.1.1.1"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"6.6.6.6",
+ "payload":"7.7.7.7"
+ },
+ {
+ "type":"INP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::2\/128"
+ },
+ {
+ "type":"INP",
+ "advRouter":"3.3.3.3",
+ "payload":"2001:db8:1000::3\/128"
+ },
+ {
+ "type":"INP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::4\/128"
+ },
+ {
+ "type":"INP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::6\/128"
+ }
+ ]
+ },
+ {
+ "areaId":"2",
+ "lsa":[
+ {
+ "type":"Rtr",
+ "advRouter":"4.4.4.4"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"5.5.5.5"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::4\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::2\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::3\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::6\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::7\/128"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"4.4.4.4",
+ "payload":"1.1.1.1"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"4.4.4.4",
+ "payload":"7.7.7.7"
+ },
+ {
+ "type":"INP",
+ "advRouter":"5.5.5.5",
+ "payload":"2001:db8:1000::5\/128"
+ }
+ ]
+ }
+ ],
+ "interfaceScopedLinkStateDb":[
+ {
+ "areaId":"0",
+ "interface":"eth-rt3",
+ "lsa":[
+ {
+ "type":"Lnk",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Lnk",
+ "advRouter":"4.4.4.4"
+ }
+ ]
+ },
+ {
+ "areaId":"0",
+ "interface":"lo",
+ "lsa":[
+ ]
+ },
+ {
+ "areaId":"2",
+ "interface":"eth-rt5",
+ "lsa":[
+ {
+ "type":"Lnk",
+ "advRouter":"4.4.4.4"
+ },
+ {
+ "type":"Lnk",
+ "advRouter":"5.5.5.5"
+ }
+ ]
+ }
+ ],
+ "asScopedLinkStateDb":[
+ {
+ "lsa":[
+ {
+ "type":"ASE",
+ "advRouter":"1.1.1.1",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"ASE",
+ "advRouter":"7.7.7.7",
+ "payload":"2001:db8:1000::7\/128"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_neighbor.json b/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_neighbor.json
new file mode 100644
index 0000000000..36abba4f87
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_neighbor.json
@@ -0,0 +1,20 @@
+{
+ "neighbors":[
+ {
+ "neighborId":"3.3.3.3",
+ "priority":1,
+ "state":"Full",
+ "ifState":"PointToPoint",
+ "interfaceName":"eth-rt3",
+ "interfaceState":"PointToPoint"
+ },
+ {
+ "neighborId":"5.5.5.5",
+ "priority":1,
+ "state":"Full",
+ "ifState":"PointToPoint",
+ "interfaceName":"eth-rt5",
+ "interfaceState":"PointToPoint"
+ }
+ ]
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_route.json b/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_route.json
new file mode 100644
index 0000000000..3e5f17f491
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_route.json
@@ -0,0 +1,74 @@
+{
+ "routes":{
+ "2001:db8:1000::1\/128":{
+ "isBestRoute":false,
+ "destinationType":"N",
+ "pathType":"E2",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ "2001:db8:1000::2\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ "2001:db8:1000::3\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ "2001:db8:1000::4\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"lo"
+ }
+ ]
+ },
+ "2001:db8:1000::5\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt5"
+ }
+ ]
+ },
+ "2001:db8:1000::6\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ "2001:db8:1000::7\/128":{
+ "isBestRoute":false,
+ "destinationType":"N",
+ "pathType":"E2",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_route.json b/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_route.json
new file mode 100644
index 0000000000..f5212da4f6
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_route.json
@@ -0,0 +1,139 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::7\/128":[
+ {
+ "prefix":"2001:db8:1000::7\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt4/zebra.conf b/tests/topotests/ospf6_gr_topo1/rt4/zebra.conf
new file mode 100644
index 0000000000..eeea417b70
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt4/zebra.conf
@@ -0,0 +1,22 @@
+password 1
+hostname rt4
+log file zebra.log
+log commands
+!
+debug zebra event
+debug zebra packet
+debug zebra rib
+debug zebra kernel
+!
+interface lo
+ ip address 4.4.4.4/32
+ ipv6 address 2001:db8:1000::4/128
+!
+interface eth-rt3
+!
+interface eth-rt5
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf6_gr_topo1/rt5/ospf6d.conf b/tests/topotests/ospf6_gr_topo1/rt5/ospf6d.conf
new file mode 100644
index 0000000000..49c3a8b86f
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt5/ospf6d.conf
@@ -0,0 +1,29 @@
+password 1
+hostname rt5
+log file ospf6d.log
+log commands
+!
+debug ospf6 lsa router originate
+debug ospf6 lsa router flooding
+debug ospf6 zebra
+debug ospf6 interface
+debug ospf6 neighbor
+debug ospf6 flooding
+debug ospf6 graceful-restart
+debug ospf6 spf process
+!
+interface lo
+ ipv6 ospf area 2
+ ipv6 ospf network point-to-point
+!
+interface eth-rt4
+ ipv6 ospf network point-to-point
+ ipv6 ospf area 2
+ ipv6 ospf hello-interval 3
+ ipv6 ospf dead-interval 9
+!
+router ospf6
+ ospf6 router-id 5.5.5.5
+ graceful-restart grace-period 120
+ graceful-restart helper-only
+!
diff --git a/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_database.json b/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_database.json
new file mode 100644
index 0000000000..4a163b984e
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_database.json
@@ -0,0 +1,100 @@
+{
+ "areaScopedLinkStateDb":[
+ {
+ "areaId":"2",
+ "lsa":[
+ {
+ "type":"Rtr",
+ "advRouter":"4.4.4.4"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"5.5.5.5"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::4\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::2\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::3\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::6\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::7\/128"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"4.4.4.4",
+ "payload":"1.1.1.1"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"4.4.4.4",
+ "payload":"7.7.7.7"
+ },
+ {
+ "type":"INP",
+ "advRouter":"5.5.5.5",
+ "payload":"2001:db8:1000::5\/128"
+ }
+ ]
+ }
+ ],
+ "interfaceScopedLinkStateDb":[
+ {
+ "areaId":"2",
+ "interface":"eth-rt4",
+ "lsa":[
+ {
+ "type":"Lnk",
+ "advRouter":"4.4.4.4"
+ },
+ {
+ "type":"Lnk",
+ "advRouter":"5.5.5.5"
+ }
+ ]
+ },
+ {
+ "areaId":"2",
+ "interface":"lo",
+ "lsa":[
+ ]
+ }
+ ],
+ "asScopedLinkStateDb":[
+ {
+ "lsa":[
+ {
+ "type":"ASE",
+ "advRouter":"1.1.1.1",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"ASE",
+ "advRouter":"7.7.7.7",
+ "payload":"2001:db8:1000::7\/128"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_neighbor.json b/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_neighbor.json
new file mode 100644
index 0000000000..9b6ac911d1
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_neighbor.json
@@ -0,0 +1,12 @@
+{
+ "neighbors":[
+ {
+ "neighborId":"4.4.4.4",
+ "priority":1,
+ "state":"Full",
+ "ifState":"PointToPoint",
+ "interfaceName":"eth-rt4",
+ "interfaceState":"PointToPoint"
+ }
+ ]
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_route.json b/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_route.json
new file mode 100644
index 0000000000..a56c3262c6
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_route.json
@@ -0,0 +1,74 @@
+{
+ "routes":{
+ "2001:db8:1000::1\/128":{
+ "isBestRoute":false,
+ "destinationType":"N",
+ "pathType":"E2",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ },
+ "2001:db8:1000::2\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ },
+ "2001:db8:1000::3\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ },
+ "2001:db8:1000::4\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ },
+ "2001:db8:1000::5\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"lo"
+ }
+ ]
+ },
+ "2001:db8:1000::6\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ },
+ "2001:db8:1000::7\/128":{
+ "isBestRoute":false,
+ "destinationType":"N",
+ "pathType":"E2",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_route.json b/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_route.json
new file mode 100644
index 0000000000..5ea4f699fe
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_route.json
@@ -0,0 +1,139 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":50,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::7\/128":[
+ {
+ "prefix":"2001:db8:1000::7\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":50,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt5/zebra.conf b/tests/topotests/ospf6_gr_topo1/rt5/zebra.conf
new file mode 100644
index 0000000000..0cdb90b129
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt5/zebra.conf
@@ -0,0 +1,20 @@
+password 1
+hostname rt5
+log file zebra.log
+log commands
+!
+debug zebra event
+debug zebra packet
+debug zebra rib
+debug zebra kernel
+!
+interface lo
+ ip address 5.5.5.5/32
+ ipv6 address 2001:db8:1000::5/128
+!
+interface eth-rt4
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf6_gr_topo1/rt6/ospf6d.conf b/tests/topotests/ospf6_gr_topo1/rt6/ospf6d.conf
new file mode 100644
index 0000000000..5d6d3280b9
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt6/ospf6d.conf
@@ -0,0 +1,35 @@
+password 1
+hostname rt6
+log file ospf6d.log
+log commands
+!
+debug ospf6 lsa router originate
+debug ospf6 lsa router flooding
+debug ospf6 zebra
+debug ospf6 interface
+debug ospf6 neighbor
+debug ospf6 flooding
+debug ospf6 graceful-restart
+debug ospf6 spf process
+!
+interface lo
+ ipv6 ospf area 0
+ ipv6 ospf network point-to-point
+!
+interface eth-rt3
+ ipv6 ospf network point-to-point
+ ipv6 ospf area 0
+ ipv6 ospf hello-interval 3
+ ipv6 ospf dead-interval 9
+!
+interface eth-rt7
+ ipv6 ospf network point-to-point
+ ipv6 ospf area 3
+ ipv6 ospf hello-interval 3
+ ipv6 ospf dead-interval 9
+!
+router ospf6
+ ospf6 router-id 6.6.6.6
+ graceful-restart grace-period 120
+ graceful-restart helper-only
+!
diff --git a/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_database.json b/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_database.json
new file mode 100644
index 0000000000..71872d19d0
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_database.json
@@ -0,0 +1,183 @@
+{
+ "areaScopedLinkStateDb":[
+ {
+ "areaId":"0",
+ "lsa":[
+ {
+ "type":"Rtr",
+ "advRouter":"2.2.2.2"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"4.4.4.4"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"6.6.6.6"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::5\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::7\/128"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"2.2.2.2",
+ "payload":"1.1.1.1"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"6.6.6.6",
+ "payload":"7.7.7.7"
+ },
+ {
+ "type":"INP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::2\/128"
+ },
+ {
+ "type":"INP",
+ "advRouter":"3.3.3.3",
+ "payload":"2001:db8:1000::3\/128"
+ },
+ {
+ "type":"INP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::4\/128"
+ },
+ {
+ "type":"INP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::6\/128"
+ }
+ ]
+ },
+ {
+ "areaId":"3",
+ "lsa":[
+ {
+ "type":"Rtr",
+ "advRouter":"6.6.6.6"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"7.7.7.7"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::6\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::2\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::3\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::4\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::5\/128"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"6.6.6.6",
+ "payload":"1.1.1.1"
+ },
+ {
+ "type":"INP",
+ "advRouter":"7.7.7.7",
+ "payload":"2001:db8:1000::7\/128"
+ }
+ ]
+ }
+ ],
+ "interfaceScopedLinkStateDb":[
+ {
+ "areaId":"0",
+ "interface":"eth-rt3",
+ "lsa":[
+ {
+ "type":"Lnk",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Lnk",
+ "advRouter":"6.6.6.6"
+ }
+ ]
+ },
+ {
+ "areaId":"0",
+ "interface":"lo",
+ "lsa":[
+ ]
+ },
+ {
+ "areaId":"3",
+ "interface":"eth-rt7",
+ "lsa":[
+ {
+ "type":"Lnk",
+ "advRouter":"6.6.6.6"
+ },
+ {
+ "type":"Lnk",
+ "advRouter":"7.7.7.7"
+ }
+ ]
+ }
+ ],
+ "asScopedLinkStateDb":[
+ {
+ "lsa":[
+ {
+ "type":"ASE",
+ "advRouter":"1.1.1.1",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"ASE",
+ "advRouter":"7.7.7.7",
+ "payload":"2001:db8:1000::7\/128"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_neighbor.json b/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_neighbor.json
new file mode 100644
index 0000000000..aba181ba3f
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_neighbor.json
@@ -0,0 +1,20 @@
+{
+ "neighbors":[
+ {
+ "neighborId":"3.3.3.3",
+ "priority":1,
+ "state":"Full",
+ "ifState":"PointToPoint",
+ "interfaceName":"eth-rt3",
+ "interfaceState":"PointToPoint"
+ },
+ {
+ "neighborId":"7.7.7.7",
+ "priority":1,
+ "state":"Full",
+ "ifState":"PointToPoint",
+ "interfaceName":"eth-rt7",
+ "interfaceState":"PointToPoint"
+ }
+ ]
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_route.json b/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_route.json
new file mode 100644
index 0000000000..c9494a9d57
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_route.json
@@ -0,0 +1,74 @@
+{
+ "routes":{
+ "2001:db8:1000::1\/128":{
+ "isBestRoute":false,
+ "destinationType":"N",
+ "pathType":"E2",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ "2001:db8:1000::2\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ "2001:db8:1000::3\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ "2001:db8:1000::4\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ "2001:db8:1000::5\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ "2001:db8:1000::6\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"lo"
+ }
+ ]
+ },
+ "2001:db8:1000::7\/128":{
+ "isBestRoute":false,
+ "destinationType":"N",
+ "pathType":"E2",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt7"
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_route.json b/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_route.json
new file mode 100644
index 0000000000..862f1baffb
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_route.json
@@ -0,0 +1,139 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::7\/128":[
+ {
+ "prefix":"2001:db8:1000::7\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt6/zebra.conf b/tests/topotests/ospf6_gr_topo1/rt6/zebra.conf
new file mode 100644
index 0000000000..3c2312da8a
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt6/zebra.conf
@@ -0,0 +1,22 @@
+password 1
+hostname rt6
+log file zebra.log
+log commands
+!
+debug zebra event
+debug zebra packet
+debug zebra rib
+debug zebra kernel
+!
+interface lo
+ ip address 6.6.6.6/32
+ ipv6 address 2001:db8:1000::6/128
+!
+interface eth-rt3
+!
+interface eth-rt7
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf6_gr_topo1/rt7/ospf6d.conf b/tests/topotests/ospf6_gr_topo1/rt7/ospf6d.conf
new file mode 100644
index 0000000000..f504fba4de
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt7/ospf6d.conf
@@ -0,0 +1,30 @@
+password 1
+hostname rt7
+log file ospf6d.log
+log commands
+!
+debug ospf6 lsa router originate
+debug ospf6 lsa router flooding
+debug ospf6 zebra
+debug ospf6 interface
+debug ospf6 neighbor
+debug ospf6 flooding
+debug ospf6 graceful-restart
+debug ospf6 spf process
+!
+interface lo
+ ipv6 ospf area 3
+ ipv6 ospf network point-to-point
+!
+interface eth-rt6
+ ipv6 ospf network point-to-point
+ ipv6 ospf area 3
+ ipv6 ospf hello-interval 3
+ ipv6 ospf dead-interval 9
+!
+router ospf6
+ ospf6 router-id 7.7.7.7
+ redistribute connected
+ graceful-restart grace-period 120
+ graceful-restart helper-only
+!
diff --git a/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_database.json b/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_database.json
new file mode 100644
index 0000000000..e70eb57b29
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_database.json
@@ -0,0 +1,95 @@
+{
+ "areaScopedLinkStateDb":[
+ {
+ "areaId":"3",
+ "lsa":[
+ {
+ "type":"Rtr",
+ "advRouter":"6.6.6.6"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"7.7.7.7"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::6\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::2\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::3\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::4\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::5\/128"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"6.6.6.6",
+ "payload":"1.1.1.1"
+ },
+ {
+ "type":"INP",
+ "advRouter":"7.7.7.7",
+ "payload":"2001:db8:1000::7\/128"
+ }
+ ]
+ }
+ ],
+ "interfaceScopedLinkStateDb":[
+ {
+ "areaId":"3",
+ "interface":"eth-rt6",
+ "lsa":[
+ {
+ "type":"Lnk",
+ "advRouter":"6.6.6.6"
+ },
+ {
+ "type":"Lnk",
+ "advRouter":"7.7.7.7"
+ }
+ ]
+ },
+ {
+ "areaId":"3",
+ "interface":"lo",
+ "lsa":[
+ ]
+ }
+ ],
+ "asScopedLinkStateDb":[
+ {
+ "lsa":[
+ {
+ "type":"ASE",
+ "advRouter":"1.1.1.1",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"ASE",
+ "advRouter":"7.7.7.7",
+ "payload":"2001:db8:1000::7\/128"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_neighbor.json b/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_neighbor.json
new file mode 100644
index 0000000000..5548691ef3
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_neighbor.json
@@ -0,0 +1,12 @@
+{
+ "neighbors":[
+ {
+ "neighborId":"6.6.6.6",
+ "priority":1,
+ "state":"Full",
+ "ifState":"PointToPoint",
+ "interfaceName":"eth-rt6",
+ "interfaceState":"PointToPoint"
+ }
+ ]
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_route.json b/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_route.json
new file mode 100644
index 0000000000..42ca54fded
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_route.json
@@ -0,0 +1,74 @@
+{
+ "routes":{
+ "2001:db8:1000::1\/128":{
+ "isBestRoute":false,
+ "destinationType":"N",
+ "pathType":"E2",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ },
+ "2001:db8:1000::2\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ },
+ "2001:db8:1000::3\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ },
+ "2001:db8:1000::4\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ },
+ "2001:db8:1000::5\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ },
+ "2001:db8:1000::6\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ },
+ "2001:db8:1000::7\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"lo"
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_route.json b/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_route.json
new file mode 100644
index 0000000000..f5f8f710e5
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_route.json
@@ -0,0 +1,139 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":50,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":50,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::7\/128":[
+ {
+ "prefix":"2001:db8:1000::7\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt7/zebra.conf b/tests/topotests/ospf6_gr_topo1/rt7/zebra.conf
new file mode 100644
index 0000000000..9cc8c29c1e
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt7/zebra.conf
@@ -0,0 +1,22 @@
+password 1
+hostname rt7
+log file zebra.log
+log commands
+!
+debug zebra event
+debug zebra packet
+debug zebra rib
+debug zebra kernel
+!
+interface lo
+ ip address 7.7.7.7/32
+ ipv6 address 2001:db8:1000::7/128
+!
+interface stub1
+!
+interface eth-rt6
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf6_gr_topo1/test_ospf6_gr_topo1.py b/tests/topotests/ospf6_gr_topo1/test_ospf6_gr_topo1.py
new file mode 100755
index 0000000000..ccbcadb8b1
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/test_ospf6_gr_topo1.py
@@ -0,0 +1,381 @@
+#!/usr/bin/env python
+
+#
+# test_ospf6_gr_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2021 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_ospf6_gr_topo1.py:
+
+ +---------+
+ | RT1 |
+ | 1.1.1.1 |
+ +---------+
+ |eth-rt2
+ |
+ |eth-rt1
+ +---------+
+ | RT2 |
+ | 2.2.2.2 |
+ +---------+
+ |eth-rt3
+ |
+ |eth-rt2
+ +---------+
+ | RT3 |
+ | 3.3.3.3 |
+ +---------+
+ eth-rt4| |eth-rt6
+ | |
+ +---------+ +--------+
+ | |
+ |eth-rt3 |eth-rt3
+ +---------+ +---------+
+ | RT4 | | RT6 |
+ | 4.4.4.4 | | 6.6.6.6 |
+ +---------+ +---------+
+ |eth-rt5 |eth-rt7
+ | |
+ |eth-rt4 |eth-rt6
+ +---------+ +---------+
+ | RT5 | | RT7 |
+ | 5.5.5.5 | | 7.7.7.7 |
+ +---------+ +---------+
+"""
+
+import os
+import sys
+import pytest
+import json
+from time import sleep
+from functools import partial
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+from lib.common_config import (
+ kill_router_daemons,
+ start_router_daemons,
+)
+
+pytestmark = [pytest.mark.ospf6d]
+
+# Global multi-dimensional dictionary containing all expected outputs
+outputs = {}
+
+
+def build_topo(tgen):
+ #
+ # Define FRR Routers
+ #
+ for router in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "rt7"]:
+ tgen.add_router(router)
+
+ #
+ # Define connections
+ #
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["rt1"], nodeif="eth-rt2")
+ switch.add_link(tgen.gears["rt2"], nodeif="eth-rt1")
+
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["rt1"], nodeif="stub1")
+
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["rt2"], nodeif="eth-rt3")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-rt2")
+
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-rt4")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt3")
+
+ switch = tgen.add_switch("s5")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-rt6")
+ switch.add_link(tgen.gears["rt6"], nodeif="eth-rt3")
+
+ switch = tgen.add_switch("s6")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt5")
+ switch.add_link(tgen.gears["rt5"], nodeif="eth-rt4")
+
+ switch = tgen.add_switch("s7")
+ switch.add_link(tgen.gears["rt6"], nodeif="eth-rt7")
+ switch.add_link(tgen.gears["rt7"], nodeif="eth-rt6")
+
+ switch = tgen.add_switch("s8")
+ switch.add_link(tgen.gears["rt7"], nodeif="stub1")
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_OSPF6, os.path.join(CWD, "{}/ospf6d.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+
+ # This function tears down the whole topology.
+ tgen.stop_topology()
+
+
+def router_compare_json_output(rname, command, reference, tries):
+ "Compare router JSON output"
+
+ logger.info('Comparing router "%s" "%s" output', rname, command)
+
+ tgen = get_topogen()
+ filename = "{}/{}/{}".format(CWD, rname, reference)
+ expected = json.loads(open(filename).read())
+
+ test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected)
+ _, diff = topotest.run_and_expect(test_func, None, count=tries, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches the expected result'.format(rname)
+ assert diff is None, assertmsg
+
+
+def check_routers(initial_convergence=False, exiting=None, restarting=None):
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "rt7"]:
+ # Check the RIB first, which should be preserved across restarts in
+ # all routers of the routing domain.
+ if initial_convergence == True:
+ tries = 240
+ else:
+ tries = 1
+ router_compare_json_output(
+ rname, "show ipv6 route ospf json", "show_ipv6_route.json", tries
+ )
+
+ # Check that all adjacencies are up and running (except when there's
+ # an OSPF instance that is shutting down).
+ if exiting == None:
+ tries = 240
+ router_compare_json_output(
+ rname,
+ "show ipv6 ospf neighbor json",
+ "show_ipv6_ospf_neighbor.json",
+ tries,
+ )
+
+ # Check the OSPF RIB and LSDB.
+ # In the restarting router, wait up to one minute for the LSDB to converge.
+ if exiting != rname:
+ if initial_convergence == True or restarting == rname:
+ tries = 240
+ else:
+ tries = 1
+ router_compare_json_output(
+ rname,
+ "show ipv6 ospf database json",
+ "show_ipv6_ospf_database.json",
+ tries,
+ )
+ router_compare_json_output(
+ rname, "show ipv6 ospf route json", "show_ipv6_ospf_route.json", tries
+ )
+
+
+#
+# Test initial network convergence
+#
+def test_initial_convergence():
+ logger.info("Test: verify initial network convergence")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ check_routers(initial_convergence=True)
+
+
+#
+# Test rt1 performing a graceful restart
+#
+def test_gr_rt1():
+ logger.info("Test: verify rt1 performing a graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["rt1"].cmd('vtysh -c "graceful-restart prepare ipv6 ospf"')
+ sleep(5)
+ kill_router_daemons(tgen, "rt1", ["ospf6d"], save_config=False)
+ check_routers(exiting="rt1")
+
+ start_router_daemons(tgen, "rt1", ["ospf6d"])
+ check_routers(restarting="rt1")
+
+
+#
+# Test rt2 performing a graceful restart
+#
+def test_gr_rt2():
+ logger.info("Test: verify rt2 performing a graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["rt2"].cmd('vtysh -c "graceful-restart prepare ipv6 ospf"')
+ sleep(5)
+ kill_router_daemons(tgen, "rt2", ["ospf6d"], save_config=False)
+ check_routers(exiting="rt2")
+
+ start_router_daemons(tgen, "rt2", ["ospf6d"])
+ check_routers(restarting="rt2")
+
+
+#
+# Test rt3 performing a graceful restart
+#
+def test_gr_rt3():
+ logger.info("Test: verify rt3 performing a graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["rt3"].cmd('vtysh -c "graceful-restart prepare ipv6 ospf"')
+ sleep(5)
+ kill_router_daemons(tgen, "rt3", ["ospf6d"], save_config=False)
+ check_routers(exiting="rt3")
+
+ start_router_daemons(tgen, "rt3", ["ospf6d"])
+ check_routers(restarting="rt3")
+
+
+#
+# Test rt4 performing a graceful restart
+#
+def test_gr_rt4():
+ logger.info("Test: verify rt4 performing a graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["rt4"].cmd('vtysh -c "graceful-restart prepare ipv6 ospf"')
+ sleep(5)
+ kill_router_daemons(tgen, "rt4", ["ospf6d"], save_config=False)
+ check_routers(exiting="rt4")
+
+ start_router_daemons(tgen, "rt4", ["ospf6d"])
+ check_routers(restarting="rt4")
+
+
+#
+# Test rt5 performing a graceful restart
+#
+def test_gr_rt5():
+ logger.info("Test: verify rt5 performing a graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["rt5"].cmd('vtysh -c "graceful-restart prepare ipv6 ospf"')
+ sleep(5)
+ kill_router_daemons(tgen, "rt5", ["ospf6d"], save_config=False)
+ check_routers(exiting="rt5")
+
+ start_router_daemons(tgen, "rt5", ["ospf6d"])
+ check_routers(restarting="rt5")
+
+
+#
+# Test rt6 performing a graceful restart
+#
+def test_gr_rt6():
+ logger.info("Test: verify rt6 performing a graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["rt6"].cmd('vtysh -c "graceful-restart prepare ipv6 ospf"')
+ sleep(5)
+ kill_router_daemons(tgen, "rt6", ["ospf6d"], save_config=False)
+ check_routers(exiting="rt6")
+
+ start_router_daemons(tgen, "rt6", ["ospf6d"])
+ check_routers(restarting="rt6")
+
+
+#
+# Test rt7 performing a graceful restart
+#
+def test_gr_rt7():
+ logger.info("Test: verify rt7 performing a graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["rt7"].cmd('vtysh -c "graceful-restart prepare ipv6 ospf"')
+ sleep(5)
+ kill_router_daemons(tgen, "rt7", ["ospf6d"], save_config=False)
+ check_routers(exiting="rt7")
+
+ start_router_daemons(tgen, "rt7", ["ospf6d"])
+ check_routers(restarting="rt7")
+
+
+# Memory leak test template
+def test_memory_leak():
+ "Run the memory leak test and report results."
+ tgen = get_topogen()
+ if not tgen.is_memleak_enabled():
+ pytest.skip("Memory leak test/report is disabled")
+
+ tgen.report_memory_leaks()
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))