summaryrefslogtreecommitdiffstats
path: root/ospf6d
diff options
context:
space:
mode:
authorRenato Westphal <renato@opensourcerouting.org>2023-03-03 17:09:20 +0100
committerRenato Westphal <renato@opensourcerouting.org>2023-05-09 02:47:44 +0200
commit88b3d5e5144fc6422c600e56e419231ce630f869 (patch)
tree6b43e13096bfa43ab8dcce98a7f6e48e1e2844a3 /ospf6d
parentospfd: add support for unplanned graceful restart (diff)
downloadfrr-88b3d5e5144fc6422c600e56e419231ce630f869.tar.xz
frr-88b3d5e5144fc6422c600e56e419231ce630f869.zip
ospf6d: add support for unplanned graceful restart
In practical terms, unplanned GR refers to the act of recovering from a software crash without affecting the forwarding plane. Unplanned GR and Planned GR work virtually the same, except for the following difference: on planned GR, the router sends the Grace-LSAs *before* restarting, whereas in unplanned GR the router sends the Grace-LSAs immediately *after* restarting. For unplanned GR to work, ospf6d was modified to send a ZEBRA_CLIENT_GR_CAPABILITIES message to zebra as soon as GR is enabled. This causes zebra to freeze the OSPF routes in the RIB as soon as the ospf6d daemon dies, for as long as the configured grace period (the defaults is 120 seconds). Similarly, ospf6d now stores in non-volatile memory that GR is enabled as soon as GR is configured. Those two things are no longer done during the GR preparation phase, which only happens for planned GRs. Unplanned GR will only take effect when the daemon is killed abruptly (e.g. SIGSEGV, SIGKILL), otherwise all OSPF routes will be uninstalled while ospf6d is exiting. Once ospf6d starts, it will check whether GR is enabled and enter in the GR mode if necessary, sending Grace-LSAs out all operational interfaces. One disadvantage of unplanned GR is that the neighboring routers might time out their corresponding adjacencies if ospf6d takes too long to come back up. This is especially the case when short dead intervals are used (or BFD). For this and other reasons, planned GR should be preferred whenever possible. Signed-off-by: Renato Westphal <renato@opensourcerouting.org>
Diffstat (limited to 'ospf6d')
-rw-r--r--ospf6d/ospf6_gr.c168
-rw-r--r--ospf6d/ospf6_gr.h5
-rw-r--r--ospf6d/ospf6_interface.c11
-rw-r--r--ospf6d/ospf6_spf.c1
-rw-r--r--ospf6d/ospf6_top.c17
-rw-r--r--ospf6d/ospf6_top.h2
-rw-r--r--ospf6d/ospf6_zebra.c10
7 files changed, 161 insertions, 53 deletions
diff --git a/ospf6d/ospf6_gr.c b/ospf6d/ospf6_gr.c
index 976eb529d..18f36abda 100644
--- a/ospf6d/ospf6_gr.c
+++ b/ospf6d/ospf6_gr.c
@@ -14,6 +14,7 @@
#include "log.h"
#include "hook.h"
#include "printfrr.h"
+#include "lib_errors.h"
#include "ospf6d/ospf6_lsa.h"
#include "ospf6d/ospf6_lsdb.h"
@@ -25,21 +26,25 @@
#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_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))
@@ -61,29 +66,59 @@ static int ospf6_gr_lsa_originate(struct ospf6_interface *oi)
/* 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;
}
@@ -134,11 +169,10 @@ static void ospf6_gr_restart_exit(struct ospf6 *ospf6, const char *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);
- /* 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;
@@ -195,6 +229,26 @@ static void ospf6_gr_restart_exit(struct ospf6 *ospf6, const char *reason)
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, int 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
@@ -470,7 +524,7 @@ static void ospf6_gr_grace_period_expired(struct event *thread)
* 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;
@@ -496,16 +550,18 @@ static void ospf6_gr_nvm_update(struct ospf6 *ospf6)
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);
@@ -516,7 +572,7 @@ static void ospf6_gr_nvm_update(struct ospf6 *ospf6)
* 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;
@@ -552,6 +608,7 @@ void ospf6_gr_nvm_read(struct ospf6 *ospf6)
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;
@@ -573,29 +630,33 @@ void ospf6_gr_nvm_read(struct ospf6 *ospf6)
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);
- event_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);
@@ -605,6 +666,19 @@ void ospf6_gr_nvm_read(struct ospf6 *ospf6)
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);
+}
+
/* Prepare to start a Graceful Restart. */
static void ospf6_gr_prepare(void)
{
@@ -625,25 +699,17 @@ 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
@@ -714,6 +780,12 @@ DEFPY(ospf6_graceful_restart, ospf6_graceful_restart_cmd,
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;
}
@@ -736,6 +808,8 @@ DEFPY(ospf6_no_graceful_restart, ospf6_no_graceful_restart_cmd,
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;
}
diff --git a/ospf6d/ospf6_gr.h b/ospf6d/ospf6_gr.h
index 2c7e8b341..42c7bab61 100644
--- a/ospf6d/ospf6_gr.h
+++ b/ospf6d/ospf6_gr.h
@@ -155,9 +155,14 @@ 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_restart_enter(struct ospf6 *ospf6,
+ enum ospf6_gr_restart_reason reason,
+ int timestamp);
extern void ospf6_gr_check_lsdb_consistency(struct ospf6 *ospf,
struct ospf6_area *area);
extern void ospf6_gr_nvm_read(struct ospf6 *ospf);
+extern void ospf6_gr_nvm_delete(struct ospf6 *ospf6);
+extern void ospf6_gr_unplanned_start_interface(struct ospf6_interface *oi);
extern void ospf6_gr_init(void);
#endif /* OSPF6_GR_H */
diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c
index e7148d66b..0f6d9e10d 100644
--- a/ospf6d/ospf6_interface.c
+++ b/ospf6d/ospf6_interface.c
@@ -772,6 +772,17 @@ void interface_up(struct event *thread)
return;
}
+ /*
+ * RFC 3623 - Section 5 ("Unplanned Outages"):
+ * "The grace-LSAs are encapsulated in Link State Update Packets
+ * and sent out to all interfaces, even though the restarted
+ * router has no adjacencies and no knowledge of previous
+ * adjacencies".
+ */
+ if (oi->area->ospf6->gr_info.restart_in_progress &&
+ oi->area->ospf6->gr_info.reason == OSPF6_GR_UNKNOWN_RESTART)
+ ospf6_gr_unplanned_start_interface(oi);
+
#ifdef __FreeBSD__
/*
* There's a delay in FreeBSD between issuing a command to leave a
diff --git a/ospf6d/ospf6_spf.c b/ospf6d/ospf6_spf.c
index 0990b1430..e39ae504a 100644
--- a/ospf6d/ospf6_spf.c
+++ b/ospf6d/ospf6_spf.c
@@ -1262,6 +1262,7 @@ static void ospf6_ase_calculate_timer(struct event *t)
* no longer valid.
*/
ospf6_zebra_gr_disable(ospf6);
+ ospf6_zebra_gr_enable(ospf6, ospf6->gr_info.grace_period);
ospf6->gr_info.finishing_restart = false;
}
}
diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c
index c2aa3abee..01c962194 100644
--- a/ospf6d/ospf6_top.c
+++ b/ospf6d/ospf6_top.c
@@ -455,6 +455,13 @@ struct ospf6 *ospf6_instance_create(const char *name)
if (ospf6->router_id == 0)
ospf6_router_id_update(ospf6, true);
ospf6_add(ospf6);
+
+ /*
+ * Read from non-volatile memory whether this instance is performing a
+ * graceful restart or not.
+ */
+ ospf6_gr_nvm_read(ospf6);
+
if (ospf6->vrf_id != VRF_UNKNOWN) {
vrf = vrf_lookup_by_id(ospf6->vrf_id);
FOR_ALL_INTERFACES (vrf, ifp) {
@@ -465,12 +472,6 @@ 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);
-
event_add_read(master, ospf6_receive, ospf6, ospf6->fd,
&ospf6->t_ospf6_receive);
@@ -490,6 +491,7 @@ void ospf6_delete(struct ospf6 *o)
ospf6_gr_helper_deinit(o);
if (!o->gr_info.prepare_in_progress)
ospf6_flush_self_originated_lsas_now(o);
+ XFREE(MTYPE_TMP, o->gr_info.exit_reason);
ospf6_disable(o);
ospf6_del(o);
@@ -693,6 +695,9 @@ DEFUN(no_router_ospf6, no_router_ospf6_cmd, "no router ospf6 [vrf NAME]",
if (ospf6 == NULL)
vty_out(vty, "OSPFv3 is not configured\n");
else {
+ if (ospf6->gr_info.restart_support)
+ ospf6_gr_nvm_delete(ospf6);
+
ospf6_delete(ospf6);
ospf6 = NULL;
}
diff --git a/ospf6d/ospf6_top.h b/ospf6d/ospf6_top.h
index 8fdd29112..a38dad8fc 100644
--- a/ospf6d/ospf6_top.h
+++ b/ospf6d/ospf6_top.h
@@ -51,6 +51,8 @@ struct ospf6_gr_info {
bool prepare_in_progress;
bool finishing_restart;
uint32_t grace_period;
+ int reason;
+ char *exit_reason;
struct event *t_grace_period;
};
diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c
index 6b3d4955d..1132c0a80 100644
--- a/ospf6d/ospf6_zebra.c
+++ b/ospf6d/ospf6_zebra.c
@@ -735,10 +735,20 @@ uint8_t ospf6_distance_apply(struct prefix_ipv6 *p, struct ospf6_route * or,
static void ospf6_zebra_connected(struct zclient *zclient)
{
+ struct ospf6 *ospf6;
+ struct listnode *node;
+
/* Send the client registration */
bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT);
zclient_send_reg_requests(zclient, VRF_DEFAULT);
+
+ /* Activate graceful restart if configured. */
+ for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) {
+ if (!ospf6->gr_info.restart_support)
+ continue;
+ (void)ospf6_zebra_gr_enable(ospf6, ospf6->gr_info.grace_period);
+ }
}
static zclient_handler *const ospf6_handlers[] = {