+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* 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 "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_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"
-#ifndef VTYSH_EXTRACT_PL
#include "ospf6d/ospf6_gr_clippy.c"
-#endif
-static void ospf6_gr_nvm_delete(struct ospf6 *ospf6);
+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)
+static int ospf6_gr_lsa_originate(struct ospf6_interface *oi,
+ enum ospf6_gr_restart_reason reason)
{
- struct ospf6_gr_info *gr_info = &oi->area->ospf6->gr_info;
+ 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))
/* 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;
+ 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 = oi->area->ospf6->router_id;
+ lsa_header->adv_router = ospf6->router_id;
lsa_header->seqnum =
ospf6_new_ls_seqnum(lsa_header->type, lsa_header->id,
lsa_header->adv_router, oi->lsdb);
- lsa_header->length = htons(sizeof(*lsa_header) + sizeof(*grace_lsa));
+ lsa_header->length = htons(lsa_length);
/* LSA checksum */
ospf6_lsa_checksum(lsa_header);
- /* create LSA */
- lsa = ospf6_lsa_create(lsa_header);
-
- /* Originate */
- ospf6_lsa_originate_interface(lsa, oi);
+ 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;
}
{
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;
- THREAD_OFF(ospf6->gr_info.t_grace_period);
-
- /* Record in non-volatile memory that the restart is complete. */
- ospf6_gr_nvm_delete(ospf6);
+ 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;
*/
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)) {
- OSPF6_LINK_LSA_EXECUTE(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
}
}
+ /*
+ * 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
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
}
/* Handling of grace period expiry. */
-static void ospf6_gr_grace_period_expired(struct thread *thread)
+static void ospf6_gr_grace_period_expired(struct event *thread)
{
- struct ospf6 *ospf6 = THREAD_ARG(thread);
+ struct ospf6 *ospf6 = EVENT_ARG(thread);
- ospf6->gr_info.t_grace_period = NULL;
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)
+static void ospf6_gr_nvm_update(struct ospf6 *ospf6, bool prepare)
{
const char *inst_name;
json_object *json;
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.
*/
- 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);
+ 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);
* Delete GR status information about the given OSPF instance from non-volatile
* memory.
*/
-static void ospf6_gr_nvm_delete(struct ospf6 *ospf6)
+void ospf6_gr_nvm_delete(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_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;
- unsigned long remaining_time;
- /* Check if the grace period has already expired. */
+ /* 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 {
- /* 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);
- }
+ } 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_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)
{
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);
+ ospf6_gr_lsa_originate(oi, OSPF6_GR_SW_RESTART);
}
}
/* Record end of the grace period in non-volatile memory. */
- ospf6_gr_nvm_update(ospf6);
+ ospf6_gr_nvm_update(ospf6, true);
/*
* Mark that a Graceful Restart preparation is in progress, to
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;
}
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;
}