summaryrefslogtreecommitdiffstats
path: root/ospf6d/ospf6_gr.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-09 13:16:35 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-09 13:16:35 +0000
commite2bbf175a2184bd76f6c54ccf8456babeb1a46fc (patch)
treef0b76550d6e6f500ada964a3a4ee933a45e5a6f1 /ospf6d/ospf6_gr.c
parentInitial commit. (diff)
downloadfrr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.tar.xz
frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.zip
Adding upstream version 9.1.upstream/9.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ospf6d/ospf6_gr.c')
-rw-r--r--ospf6d/ospf6_gr.c857
1 files changed, 857 insertions, 0 deletions
diff --git a/ospf6d/ospf6_gr.c b/ospf6d/ospf6_gr.c
new file mode 100644
index 0000000..69230e5
--- /dev/null
+++ b/ospf6d/ospf6_gr.c
@@ -0,0 +1,857 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * This is an implementation of RFC 5187 Graceful Restart.
+ *
+ * Copyright 2021 NetDEF (c), All rights reserved.
+ */
+
+#include <zebra.h>
+
+#include "memory.h"
+#include "command.h"
+#include "table.h"
+#include "vty.h"
+#include "log.h"
+#include "hook.h"
+#include "printfrr.h"
+#include "lib_errors.h"
+
+#include "ospf6d/ospf6_lsa.h"
+#include "ospf6d/ospf6_lsdb.h"
+#include "ospf6d/ospf6_route.h"
+#include "ospf6d/ospf6_area.h"
+#include "ospf6d/ospf6_interface.h"
+#include "ospf6d/ospf6d.h"
+#include "ospf6d/ospf6_asbr.h"
+#include "ospf6d/ospf6_zebra.h"
+#include "ospf6d/ospf6_message.h"
+#include "ospf6d/ospf6_neighbor.h"
+#include "ospf6d/ospf6_network.h"
+#include "ospf6d/ospf6_flood.h"
+#include "ospf6d/ospf6_intra.h"
+#include "ospf6d/ospf6_spf.h"
+#include "ospf6d/ospf6_gr.h"
+#include "ospf6d/ospf6_gr_clippy.c"
+
+static void ospf6_gr_grace_period_expired(struct event *thread);
+
+/* Originate and install Grace-LSA for a given interface. */
+static int ospf6_gr_lsa_originate(struct ospf6_interface *oi,
+ enum ospf6_gr_restart_reason reason)
+{
+ struct ospf6 *ospf6 = oi->area->ospf6;
+ struct ospf6_gr_info *gr_info = &ospf6->gr_info;
+ struct ospf6_lsa_header *lsa_header;
+ struct ospf6_grace_lsa *grace_lsa;
+ struct ospf6_lsa *lsa;
+ uint16_t lsa_length;
+ char buffer[OSPF6_MAX_LSASIZE];
+
+ if (IS_OSPF6_DEBUG_ORIGINATE(LINK))
+ zlog_debug("Originate Grace-LSA for Interface %s",
+ oi->interface->name);
+
+ /* prepare buffer */
+ memset(buffer, 0, sizeof(buffer));
+ lsa_header = (struct ospf6_lsa_header *)buffer;
+ grace_lsa =
+ (struct ospf6_grace_lsa *)((caddr_t)lsa_header
+ + sizeof(struct ospf6_lsa_header));
+
+ /* Put grace period. */
+ grace_lsa->tlv_period.header.type = htons(GRACE_PERIOD_TYPE);
+ grace_lsa->tlv_period.header.length = htons(GRACE_PERIOD_LENGTH);
+ grace_lsa->tlv_period.interval = htonl(gr_info->grace_period);
+
+ /* Put restart reason. */
+ grace_lsa->tlv_reason.header.type = htons(RESTART_REASON_TYPE);
+ grace_lsa->tlv_reason.header.length = htons(RESTART_REASON_LENGTH);
+ grace_lsa->tlv_reason.reason = reason;
+
+ /* Fill LSA Header */
+ lsa_length = sizeof(*lsa_header) + sizeof(*grace_lsa);
+ lsa_header->age = 0;
+ lsa_header->type = htons(OSPF6_LSTYPE_GRACE_LSA);
+ lsa_header->id = htonl(oi->interface->ifindex);
+ lsa_header->adv_router = ospf6->router_id;
+ lsa_header->seqnum =
+ ospf6_new_ls_seqnum(lsa_header->type, lsa_header->id,
+ lsa_header->adv_router, oi->lsdb);
+ lsa_header->length = htons(lsa_length);
+
+ /* LSA checksum */
+ ospf6_lsa_checksum(lsa_header);
+
+ if (reason == OSPF6_GR_UNKNOWN_RESTART) {
+ struct ospf6_header *oh;
+ uint32_t *uv32;
+ int n;
+ uint16_t length = OSPF6_HEADER_SIZE + 4 + lsa_length;
+ struct iovec iovector[2] = {};
+
+ /* Reserve space for OSPFv3 header. */
+ memmove(&buffer[OSPF6_HEADER_SIZE + 4], buffer, lsa_length);
+
+ /* Fill in the OSPFv3 header. */
+ oh = (struct ospf6_header *)buffer;
+ oh->version = OSPFV3_VERSION;
+ oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE;
+ oh->router_id = oi->area->ospf6->router_id;
+ oh->area_id = oi->area->area_id;
+ oh->instance_id = oi->instance_id;
+ oh->reserved = 0;
+ oh->length = htons(length);
+
+ /* Fill LSA header. */
+ uv32 = (uint32_t *)&buffer[sizeof(*oh)];
+ *uv32 = htonl(1);
+
+ /* Send packet. */
+ iovector[0].iov_base = lsa_header;
+ iovector[0].iov_len = length;
+ n = ospf6_sendmsg(oi->linklocal_addr, &allspfrouters6,
+ oi->interface->ifindex, iovector, ospf6->fd);
+ if (n != length)
+ flog_err(EC_LIB_DEVELOPMENT,
+ "%s: could not send entire message", __func__);
+ } else {
+ /* Create and install LSA. */
+ lsa = ospf6_lsa_create(lsa_header);
+ ospf6_lsa_originate_interface(lsa, oi);
+ }
+
+ return 0;
+}
+
+/* Flush all self-originated Grace-LSAs. */
+static void ospf6_gr_flush_grace_lsas(struct ospf6 *ospf6)
+{
+ struct ospf6_area *area;
+ struct listnode *anode;
+
+ for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, anode, area)) {
+ struct ospf6_lsa *lsa;
+ struct ospf6_interface *oi;
+ struct listnode *inode;
+
+ if (IS_DEBUG_OSPF6_GR)
+ zlog_debug(
+ "GR: flushing self-originated Grace-LSAs [area %pI4]",
+ &area->area_id);
+
+ for (ALL_LIST_ELEMENTS_RO(area->if_list, inode, oi)) {
+ lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_GRACE_LSA),
+ htonl(oi->interface->ifindex),
+ oi->area->ospf6->router_id,
+ oi->lsdb);
+ if (!lsa) {
+ zlog_warn(
+ "%s: Grace-LSA not found [interface %s] [area %pI4]",
+ __func__, oi->interface->name,
+ &area->area_id);
+ continue;
+ }
+
+ ospf6_lsa_purge(lsa);
+ }
+ }
+}
+
+/* Exit from the Graceful Restart mode. */
+static void ospf6_gr_restart_exit(struct ospf6 *ospf6, const char *reason)
+{
+ struct ospf6_area *area;
+ struct listnode *onode, *anode;
+ struct ospf6_route *route;
+
+ if (IS_DEBUG_OSPF6_GR)
+ zlog_debug("GR: exiting graceful restart: %s", reason);
+
+ ospf6->gr_info.restart_in_progress = false;
+ ospf6->gr_info.finishing_restart = true;
+ XFREE(MTYPE_TMP, ospf6->gr_info.exit_reason);
+ ospf6->gr_info.exit_reason = XSTRDUP(MTYPE_TMP, reason);
+ EVENT_OFF(ospf6->gr_info.t_grace_period);
+
+ for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, onode, area)) {
+ struct ospf6_interface *oi;
+
+ /*
+ * 1) The router should reoriginate its router-LSAs for all
+ * attached areas in order to make sure they have the correct
+ * contents.
+ */
+ OSPF6_ROUTER_LSA_EXECUTE(area);
+
+ /*
+ * Force reorigination of intra-area-prefix-LSAs to handle
+ * areas without any full adjacency.
+ */
+ OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB(area);
+
+ for (ALL_LIST_ELEMENTS_RO(area->if_list, anode, oi)) {
+ /* Disable hello delay. */
+ if (oi->gr.hello_delay.t_grace_send) {
+ oi->gr.hello_delay.elapsed_seconds = 0;
+ EVENT_OFF(oi->gr.hello_delay.t_grace_send);
+ event_add_event(master, ospf6_hello_send, oi, 0,
+ &oi->thread_send_hello);
+ }
+
+ /* Reoriginate Link-LSA. */
+ if (oi->type != OSPF_IFTYPE_VIRTUALLINK)
+ OSPF6_LINK_LSA_EXECUTE(oi);
+
+ /*
+ * 2) The router should reoriginate network-LSAs on all
+ * segments where it is the Designated Router.
+ */
+ if (oi->state == OSPF6_INTERFACE_DR)
+ OSPF6_NETWORK_LSA_EXECUTE(oi);
+ }
+ }
+
+ /*
+ * While all self-originated NSSA and AS-external LSAs were already
+ * learned from the helping neighbors, we need to reoriginate them in
+ * order to ensure they will be refreshed periodically.
+ */
+ for (route = ospf6_route_head(ospf6->external_table); route;
+ route = ospf6_route_next(route))
+ ospf6_handle_external_lsa_origination(ospf6, route,
+ &route->prefix);
+
+ /*
+ * 3) The router reruns its OSPF routing calculations, this time
+ * installing the results into the system forwarding table, and
+ * originating summary-LSAs, Type-7 LSAs and AS-external-LSAs as
+ * necessary.
+ *
+ * 4) Any remnant entries in the system forwarding table that were
+ * installed before the restart, but that are no longer valid,
+ * should be removed.
+ */
+ ospf6_spf_schedule(ospf6, OSPF6_SPF_FLAGS_GR_FINISH);
+
+ /* 6) Any grace-LSAs that the router originated should be flushed. */
+ ospf6_gr_flush_grace_lsas(ospf6);
+}
+
+/* Enter the Graceful Restart mode. */
+void ospf6_gr_restart_enter(struct ospf6 *ospf6,
+ enum ospf6_gr_restart_reason reason,
+ time_t timestamp)
+{
+ unsigned long remaining_time;
+
+ ospf6->gr_info.restart_in_progress = true;
+ ospf6->gr_info.reason = reason;
+
+ /* Schedule grace period timeout. */
+ remaining_time = timestamp - time(NULL);
+ if (IS_DEBUG_OSPF6_GR)
+ zlog_debug(
+ "GR: remaining time until grace period expires: %lu(s)",
+ remaining_time);
+
+ event_add_timer(master, ospf6_gr_grace_period_expired, ospf6,
+ remaining_time, &ospf6->gr_info.t_grace_period);
+}
+
+#define RTR_LSA_MISSING 0
+#define RTR_LSA_ADJ_FOUND 1
+#define RTR_LSA_ADJ_NOT_FOUND 2
+
+/* Check if a Router-LSA exists and if it contains a given link. */
+static int ospf6_router_lsa_contains_adj(struct ospf6_area *area,
+ in_addr_t adv_router,
+ in_addr_t neighbor_router_id)
+{
+ uint16_t type;
+ struct ospf6_lsa *lsa;
+ bool empty = true;
+
+ type = ntohs(OSPF6_LSTYPE_ROUTER);
+ for (ALL_LSDB_TYPED_ADVRTR(area->lsdb, type, adv_router, lsa)) {
+ struct ospf6_router_lsa *router_lsa;
+ char *start, *end, *current;
+
+ empty = false;
+ router_lsa = (struct ospf6_router_lsa
+ *)((char *)lsa->header
+ + sizeof(struct ospf6_lsa_header));
+
+ /* Iterate over all interfaces in the Router-LSA. */
+ start = (char *)router_lsa + sizeof(struct ospf6_router_lsa);
+ end = (char *)lsa->header + ntohs(lsa->header->length);
+ for (current = start;
+ current + sizeof(struct ospf6_router_lsdesc) <= end;
+ current += sizeof(struct ospf6_router_lsdesc)) {
+ struct ospf6_router_lsdesc *lsdesc;
+
+ lsdesc = (struct ospf6_router_lsdesc *)current;
+ if (lsdesc->type != OSPF6_ROUTER_LSDESC_POINTTOPOINT)
+ continue;
+
+ if (lsdesc->neighbor_router_id == neighbor_router_id) {
+ ospf6_lsa_unlock(&lsa);
+ return RTR_LSA_ADJ_FOUND;
+ }
+ }
+ }
+
+ if (empty)
+ return RTR_LSA_MISSING;
+
+ return RTR_LSA_ADJ_NOT_FOUND;
+}
+
+static bool ospf6_gr_check_router_lsa_consistency(struct ospf6 *ospf6,
+ struct ospf6_area *area,
+ struct ospf6_lsa *lsa)
+{
+ if (lsa->header->adv_router == ospf6->router_id) {
+ struct ospf6_router_lsa *router_lsa;
+ char *start, *end, *current;
+
+ router_lsa = (struct ospf6_router_lsa
+ *)((char *)lsa->header
+ + sizeof(struct ospf6_lsa_header));
+
+ /* Iterate over all interfaces in the Router-LSA. */
+ start = (char *)router_lsa + sizeof(struct ospf6_router_lsa);
+ end = (char *)lsa->header + ntohs(lsa->header->length);
+ for (current = start;
+ current + sizeof(struct ospf6_router_lsdesc) <= end;
+ current += sizeof(struct ospf6_router_lsdesc)) {
+ struct ospf6_router_lsdesc *lsdesc;
+
+ lsdesc = (struct ospf6_router_lsdesc *)current;
+ if (lsdesc->type != OSPF6_ROUTER_LSDESC_POINTTOPOINT)
+ continue;
+
+ if (ospf6_router_lsa_contains_adj(
+ area, lsdesc->neighbor_router_id,
+ ospf6->router_id)
+ == RTR_LSA_ADJ_NOT_FOUND)
+ return false;
+ }
+ } else {
+ int adj1, adj2;
+
+ adj1 = ospf6_router_lsa_contains_adj(area, ospf6->router_id,
+ lsa->header->adv_router);
+ adj2 = ospf6_router_lsa_contains_adj(
+ area, lsa->header->adv_router, ospf6->router_id);
+ if ((adj1 == RTR_LSA_ADJ_FOUND && adj2 == RTR_LSA_ADJ_NOT_FOUND)
+ || (adj1 == RTR_LSA_ADJ_NOT_FOUND
+ && adj2 == RTR_LSA_ADJ_FOUND))
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Check for LSAs that are inconsistent with the pre-restart LSAs, and abort the
+ * ongoing graceful restart when that's the case.
+ */
+void ospf6_gr_check_lsdb_consistency(struct ospf6 *ospf6,
+ struct ospf6_area *area)
+{
+ uint16_t type;
+ struct ospf6_lsa *lsa;
+
+ type = ntohs(OSPF6_LSTYPE_ROUTER);
+ for (ALL_LSDB_TYPED(area->lsdb, type, lsa)) {
+ if (!ospf6_gr_check_router_lsa_consistency(ospf6, area, lsa)) {
+ char reason[256];
+
+ snprintfrr(reason, sizeof(reason),
+ "detected inconsistent LSA %s [area %pI4]",
+ lsa->name, &area->area_id);
+ ospf6_gr_restart_exit(ospf6, reason);
+ return;
+ }
+ }
+}
+
+/* Check if there's a fully formed adjacency with the given neighbor ID. */
+static bool ospf6_gr_check_adj_id(struct ospf6_area *area,
+ in_addr_t neighbor_router_id)
+{
+ struct ospf6_neighbor *nbr;
+
+ nbr = ospf6_area_neighbor_lookup(area, neighbor_router_id);
+ if (!nbr || nbr->state < OSPF6_NEIGHBOR_FULL) {
+ if (IS_DEBUG_OSPF6_GR)
+ zlog_debug("GR: missing adjacency to router %pI4",
+ &neighbor_router_id);
+ return false;
+ }
+
+ return true;
+}
+
+static bool ospf6_gr_check_adjs_lsa_transit(struct ospf6_area *area,
+ in_addr_t neighbor_router_id,
+ uint32_t neighbor_interface_id)
+{
+ struct ospf6 *ospf6 = area->ospf6;
+
+ /* Check if we are the DR. */
+ if (neighbor_router_id == ospf6->router_id) {
+ struct ospf6_lsa *lsa;
+ char *start, *end, *current;
+ struct ospf6_network_lsa *network_lsa;
+ struct ospf6_network_lsdesc *lsdesc;
+
+ /* Lookup Network LSA corresponding to this interface. */
+ lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_NETWORK),
+ neighbor_interface_id,
+ neighbor_router_id, area->lsdb);
+ if (!lsa)
+ return false;
+
+ /* Iterate over all routers present in the network. */
+ network_lsa = (struct ospf6_network_lsa
+ *)((char *)lsa->header
+ + sizeof(struct ospf6_lsa_header));
+ start = (char *)network_lsa + sizeof(struct ospf6_network_lsa);
+ end = (char *)lsa->header + ntohs(lsa->header->length);
+ for (current = start;
+ current + sizeof(struct ospf6_network_lsdesc) <= end;
+ current += sizeof(struct ospf6_network_lsdesc)) {
+ lsdesc = (struct ospf6_network_lsdesc *)current;
+
+ /* Skip self in the pseudonode. */
+ if (lsdesc->router_id == ospf6->router_id)
+ continue;
+
+ /*
+ * Check if there's a fully formed adjacency with this
+ * router.
+ */
+ if (!ospf6_gr_check_adj_id(area, lsdesc->router_id))
+ return false;
+ }
+ } else {
+ struct ospf6_neighbor *nbr;
+
+ /* Check if there's a fully formed adjacency with the DR. */
+ nbr = ospf6_area_neighbor_lookup(area, neighbor_router_id);
+ if (!nbr || nbr->state < OSPF6_NEIGHBOR_FULL) {
+ if (IS_DEBUG_OSPF6_GR)
+ zlog_debug(
+ "GR: missing adjacency to DR router %pI4",
+ &neighbor_router_id);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool ospf6_gr_check_adjs_lsa(struct ospf6_area *area,
+ struct ospf6_lsa *lsa)
+{
+ struct ospf6_router_lsa *router_lsa;
+ char *start, *end, *current;
+
+ router_lsa =
+ (struct ospf6_router_lsa *)((char *)lsa->header
+ + sizeof(struct ospf6_lsa_header));
+
+ /* Iterate over all interfaces in the Router-LSA. */
+ start = (char *)router_lsa + sizeof(struct ospf6_router_lsa);
+ end = (char *)lsa->header + ntohs(lsa->header->length);
+ for (current = start;
+ current + sizeof(struct ospf6_router_lsdesc) <= end;
+ current += sizeof(struct ospf6_router_lsdesc)) {
+ struct ospf6_router_lsdesc *lsdesc;
+
+ lsdesc = (struct ospf6_router_lsdesc *)current;
+ switch (lsdesc->type) {
+ case OSPF6_ROUTER_LSDESC_POINTTOPOINT:
+ if (!ospf6_gr_check_adj_id(area,
+ lsdesc->neighbor_router_id))
+ return false;
+ break;
+ case OSPF6_ROUTER_LSDESC_TRANSIT_NETWORK:
+ if (!ospf6_gr_check_adjs_lsa_transit(
+ area, lsdesc->neighbor_router_id,
+ lsdesc->neighbor_interface_id))
+ return false;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return true;
+}
+
+/*
+ * Check if all adjacencies prior to the restart were reestablished.
+ *
+ * This is done using pre-restart Router LSAs and pre-restart Network LSAs
+ * received from the helping neighbors.
+ */
+static bool ospf6_gr_check_adjs(struct ospf6 *ospf6)
+{
+ struct ospf6_area *area;
+ struct listnode *node;
+
+ for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, area)) {
+ uint16_t type;
+ uint32_t router;
+ struct ospf6_lsa *lsa_self;
+ bool found = false;
+
+ type = ntohs(OSPF6_LSTYPE_ROUTER);
+ router = ospf6->router_id;
+ for (ALL_LSDB_TYPED_ADVRTR(area->lsdb, type, router,
+ lsa_self)) {
+ found = true;
+ if (!ospf6_gr_check_adjs_lsa(area, lsa_self)) {
+ ospf6_lsa_unlock(&lsa_self);
+ return false;
+ }
+ }
+ if (!found)
+ return false;
+ }
+
+ return true;
+}
+
+/* Handling of grace period expiry. */
+static void ospf6_gr_grace_period_expired(struct event *thread)
+{
+ struct ospf6 *ospf6 = EVENT_ARG(thread);
+
+ ospf6_gr_restart_exit(ospf6, "grace period has expired");
+}
+
+/* Send extra Grace-LSA out the interface (unplanned outages only). */
+void ospf6_gr_iface_send_grace_lsa(struct event *thread)
+{
+ struct ospf6_interface *oi = EVENT_ARG(thread);
+
+ ospf6_gr_lsa_originate(oi, oi->area->ospf6->gr_info.reason);
+
+ if (++oi->gr.hello_delay.elapsed_seconds < oi->gr.hello_delay.interval)
+ event_add_timer(master, ospf6_gr_iface_send_grace_lsa, oi, 1,
+ &oi->gr.hello_delay.t_grace_send);
+ else
+ event_add_event(master, ospf6_hello_send, oi, 0,
+ &oi->thread_send_hello);
+}
+
+/*
+ * Record in non-volatile memory that the given OSPF instance is attempting to
+ * perform a graceful restart.
+ */
+static void ospf6_gr_nvm_update(struct ospf6 *ospf6, bool prepare)
+{
+ const char *inst_name;
+ json_object *json;
+ json_object *json_instances;
+ json_object *json_instance;
+
+ inst_name = ospf6->name ? ospf6->name : VRF_DEFAULT_NAME;
+
+ json = json_object_from_file((char *)OSPF6D_GR_STATE);
+ if (json == NULL)
+ json = json_object_new_object();
+
+ json_object_object_get_ex(json, "instances", &json_instances);
+ if (!json_instances) {
+ json_instances = json_object_new_object();
+ json_object_object_add(json, "instances", json_instances);
+ }
+
+ json_object_object_get_ex(json_instances, inst_name, &json_instance);
+ if (!json_instance) {
+ json_instance = json_object_new_object();
+ json_object_object_add(json_instances, inst_name,
+ json_instance);
+ }
+
+ json_object_int_add(json_instance, "gracePeriod",
+ ospf6->gr_info.grace_period);
+
+ /*
+ * Record not only the grace period, but also a UNIX timestamp
+ * corresponding to the end of that period. That way, once ospf6d is
+ * restarted, it will be possible to take into account the time that
+ * passed while ospf6d wasn't running.
+ */
+ if (prepare)
+ json_object_int_add(json_instance, "timestamp",
+ time(NULL) + ospf6->gr_info.grace_period);
+
+ json_object_to_file_ext((char *)OSPF6D_GR_STATE, json,
+ JSON_C_TO_STRING_PRETTY);
+ json_object_free(json);
+}
+
+/*
+ * Delete GR status information about the given OSPF instance from non-volatile
+ * memory.
+ */
+void ospf6_gr_nvm_delete(struct ospf6 *ospf6)
+{
+ const char *inst_name;
+ json_object *json;
+ json_object *json_instances;
+
+ inst_name = ospf6->name ? ospf6->name : VRF_DEFAULT_NAME;
+
+ json = json_object_from_file((char *)OSPF6D_GR_STATE);
+ if (json == NULL)
+ json = json_object_new_object();
+
+ json_object_object_get_ex(json, "instances", &json_instances);
+ if (!json_instances) {
+ json_instances = json_object_new_object();
+ json_object_object_add(json, "instances", json_instances);
+ }
+
+ json_object_object_del(json_instances, inst_name);
+
+ json_object_to_file_ext((char *)OSPF6D_GR_STATE, json,
+ JSON_C_TO_STRING_PRETTY);
+ json_object_free(json);
+}
+
+/*
+ * Fetch from non-volatile memory whether the given OSPF instance is performing
+ * a graceful shutdown or not.
+ */
+void ospf6_gr_nvm_read(struct ospf6 *ospf6)
+{
+ const char *inst_name;
+ json_object *json;
+ json_object *json_instances;
+ json_object *json_instance;
+ json_object *json_timestamp;
+ json_object *json_grace_period;
+ time_t timestamp = 0;
+
+ inst_name = ospf6->name ? ospf6->name : VRF_DEFAULT_NAME;
+
+ json = json_object_from_file((char *)OSPF6D_GR_STATE);
+ if (json == NULL)
+ json = json_object_new_object();
+
+ json_object_object_get_ex(json, "instances", &json_instances);
+ if (!json_instances) {
+ json_instances = json_object_new_object();
+ json_object_object_add(json, "instances", json_instances);
+ }
+
+ json_object_object_get_ex(json_instances, inst_name, &json_instance);
+ if (!json_instance) {
+ json_instance = json_object_new_object();
+ json_object_object_add(json_instances, inst_name,
+ json_instance);
+ }
+
+ json_object_object_get_ex(json_instance, "gracePeriod",
+ &json_grace_period);
+ json_object_object_get_ex(json_instance, "timestamp", &json_timestamp);
+ if (json_timestamp) {
+ time_t now;
+
+ /* Planned GR: check if the grace period has already expired. */
+ now = time(NULL);
+ timestamp = json_object_get_int(json_timestamp);
+ if (now > timestamp) {
+ ospf6_gr_restart_exit(
+ ospf6, "grace period has expired already");
+ } else
+ ospf6_gr_restart_enter(ospf6, OSPF6_GR_SW_RESTART,
+ timestamp);
+ } else if (json_grace_period) {
+ uint32_t grace_period;
+
+ /*
+ * Unplanned GR: the Grace-LSAs will be sent later as soon as
+ * the interfaces are operational.
+ */
+ grace_period = json_object_get_int(json_grace_period);
+ ospf6->gr_info.grace_period = grace_period;
+ ospf6_gr_restart_enter(ospf6, OSPF6_GR_UNKNOWN_RESTART,
+ time(NULL) +
+ ospf6->gr_info.grace_period);
+ }
+
+ json_object_object_del(json_instances, inst_name);
+
+ json_object_to_file_ext((char *)OSPF6D_GR_STATE, json,
+ JSON_C_TO_STRING_PRETTY);
+ json_object_free(json);
+}
+
+void ospf6_gr_unplanned_start_interface(struct ospf6_interface *oi)
+{
+ /*
+ * Can't check OSPF interface state as the OSPF instance might not be
+ * enabled yet.
+ */
+ if (!if_is_operative(oi->interface) || if_is_loopback(oi->interface))
+ return;
+
+ /* Send Grace-LSA. */
+ ospf6_gr_lsa_originate(oi, oi->area->ospf6->gr_info.reason);
+
+ /* Start GR hello-delay interval. */
+ oi->gr.hello_delay.elapsed_seconds = 0;
+ event_add_timer(master, ospf6_gr_iface_send_grace_lsa, oi, 1,
+ &oi->gr.hello_delay.t_grace_send);
+}
+
+/* Prepare to start a Graceful Restart. */
+static void ospf6_gr_prepare(void)
+{
+ struct ospf6 *ospf6;
+ struct ospf6_interface *oi;
+ struct listnode *onode, *anode, *inode;
+
+ for (ALL_LIST_ELEMENTS_RO(om6->ospf6, onode, ospf6)) {
+ struct ospf6_area *area;
+
+ if (!ospf6->gr_info.restart_support
+ || ospf6->gr_info.prepare_in_progress)
+ continue;
+
+ if (IS_DEBUG_OSPF6_GR)
+ zlog_debug(
+ "GR: preparing to perform a graceful restart [period %u second(s)] [vrf %s]",
+ ospf6->gr_info.grace_period,
+ ospf6_vrf_id_to_name(ospf6->vrf_id));
+
+ /* Send a Grace-LSA to all neighbors. */
+ for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, anode, area)) {
+ for (ALL_LIST_ELEMENTS_RO(area->if_list, inode, oi)) {
+ if (oi->state < OSPF6_INTERFACE_POINTTOPOINT)
+ continue;
+ ospf6_gr_lsa_originate(oi, OSPF6_GR_SW_RESTART);
+ }
+ }
+
+ /* Record end of the grace period in non-volatile memory. */
+ ospf6_gr_nvm_update(ospf6, true);
+
+ /*
+ * Mark that a Graceful Restart preparation is in progress, to
+ * prevent ospf6d from flushing its self-originated LSAs on
+ * exit.
+ */
+ ospf6->gr_info.prepare_in_progress = true;
+ }
+}
+
+static int ospf6_gr_neighbor_change(struct ospf6_neighbor *on, int next_state,
+ int prev_state)
+{
+ struct ospf6 *ospf6 = on->ospf6_if->area->ospf6;
+
+ if (next_state == OSPF6_NEIGHBOR_FULL
+ && ospf6->gr_info.restart_in_progress) {
+ if (ospf6_gr_check_adjs(ospf6)) {
+ ospf6_gr_restart_exit(
+ ospf6, "all adjacencies were reestablished");
+ } else {
+ if (IS_DEBUG_OSPF6_GR)
+ zlog_debug(
+ "GR: not all adjacencies were reestablished yet");
+ }
+ }
+
+ return 0;
+}
+
+int config_write_ospf6_gr(struct vty *vty, struct ospf6 *ospf6)
+{
+ if (!ospf6->gr_info.restart_support)
+ return 0;
+
+ if (ospf6->gr_info.grace_period == OSPF6_DFLT_GRACE_INTERVAL)
+ vty_out(vty, " graceful-restart\n");
+ else
+ vty_out(vty, " graceful-restart grace-period %u\n",
+ ospf6->gr_info.grace_period);
+
+ return 0;
+}
+
+DEFPY(ospf6_graceful_restart_prepare, ospf6_graceful_restart_prepare_cmd,
+ "graceful-restart prepare ipv6 ospf",
+ "Graceful Restart commands\n"
+ "Prepare upcoming graceful restart\n" IPV6_STR
+ "Prepare to restart the OSPFv3 process\n")
+{
+ ospf6_gr_prepare();
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(ospf6_graceful_restart, ospf6_graceful_restart_cmd,
+ "graceful-restart [grace-period (1-1800)$grace_period]",
+ OSPF_GR_STR
+ "Maximum length of the 'grace period'\n"
+ "Maximum length of the 'grace period' in seconds\n")
+{
+ VTY_DECLVAR_INSTANCE_CONTEXT(ospf6, ospf6);
+
+ /* Check and get restart period if present. */
+ if (!grace_period_str)
+ grace_period = OSPF6_DFLT_GRACE_INTERVAL;
+
+ ospf6->gr_info.restart_support = true;
+ ospf6->gr_info.grace_period = grace_period;
+
+ /* Freeze OSPF routes in the RIB. */
+ (void)ospf6_zebra_gr_enable(ospf6, ospf6->gr_info.grace_period);
+
+ /* Record that GR is enabled in non-volatile memory. */
+ ospf6_gr_nvm_update(ospf6, false);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(ospf6_no_graceful_restart, ospf6_no_graceful_restart_cmd,
+ "no graceful-restart [period (1-1800)]",
+ NO_STR OSPF_GR_STR
+ "Maximum length of the 'grace period'\n"
+ "Maximum length of the 'grace period' in seconds\n")
+{
+ VTY_DECLVAR_INSTANCE_CONTEXT(ospf6, ospf6);
+
+ if (!ospf6->gr_info.restart_support)
+ return CMD_SUCCESS;
+
+ if (ospf6->gr_info.prepare_in_progress) {
+ vty_out(vty,
+ "%% Error: Graceful Restart preparation in progress\n");
+ return CMD_WARNING;
+ }
+
+ ospf6->gr_info.restart_support = false;
+ ospf6->gr_info.grace_period = OSPF6_DFLT_GRACE_INTERVAL;
+ ospf6_gr_nvm_delete(ospf6);
+ ospf6_zebra_gr_disable(ospf6);
+
+ return CMD_SUCCESS;
+}
+
+void ospf6_gr_init(void)
+{
+ hook_register(ospf6_neighbor_change, ospf6_gr_neighbor_change);
+
+ install_element(ENABLE_NODE, &ospf6_graceful_restart_prepare_cmd);
+ install_element(OSPF6_NODE, &ospf6_graceful_restart_cmd);
+ install_element(OSPF6_NODE, &ospf6_no_graceful_restart_cmd);
+}