summaryrefslogtreecommitdiff
path: root/ospf6d/ospf6_gr.c
diff options
context:
space:
mode:
Diffstat (limited to 'ospf6d/ospf6_gr.c')
-rw-r--r--ospf6d/ospf6_gr.c232
1 files changed, 164 insertions, 68 deletions
diff --git a/ospf6d/ospf6_gr.c b/ospf6d/ospf6_gr.c
index 976eb529d7..34cb9706bd 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;
@@ -156,6 +190,14 @@ static void ospf6_gr_restart_exit(struct ospf6 *ospf6, const char *reason)
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);
@@ -195,6 +237,27 @@ 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,
+ 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
@@ -230,8 +293,10 @@ static int ospf6_router_lsa_contains_adj(struct ospf6_area *area,
if (lsdesc->type != OSPF6_ROUTER_LSDESC_POINTTOPOINT)
continue;
- if (lsdesc->neighbor_router_id == neighbor_router_id)
+ if (lsdesc->neighbor_router_id == neighbor_router_id) {
+ ospf6_lsa_unlock(&lsa);
return RTR_LSA_ADJ_FOUND;
+ }
}
}
@@ -448,8 +513,10 @@ static bool ospf6_gr_check_adjs(struct ospf6 *ospf6)
for (ALL_LSDB_TYPED_ADVRTR(area->lsdb, type, router,
lsa_self)) {
found = true;
- if (!ospf6_gr_check_adjs_lsa(area, lsa_self))
+ if (!ospf6_gr_check_adjs_lsa(area, lsa_self)) {
+ ospf6_lsa_unlock(&lsa_self);
return false;
+ }
}
if (!found)
return false;
@@ -466,11 +533,26 @@ static void ospf6_gr_grace_period_expired(struct event *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)
+static void ospf6_gr_nvm_update(struct ospf6 *ospf6, bool prepare)
{
const char *inst_name;
json_object *json;
@@ -479,9 +561,7 @@ static void ospf6_gr_nvm_update(struct ospf6 *ospf6)
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 = frr_daemon_state_load();
json_object_object_get_ex(json, "instances", &json_instances);
if (!json_instances) {
@@ -496,27 +576,27 @@ 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);
- json_object_free(json);
+ frr_daemon_state_save(&json);
}
/*
* 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;
@@ -524,9 +604,7 @@ static void ospf6_gr_nvm_delete(struct ospf6 *ospf6)
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 = frr_daemon_state_load();
json_object_object_get_ex(json, "instances", &json_instances);
if (!json_instances) {
@@ -536,9 +614,7 @@ static void ospf6_gr_nvm_delete(struct ospf6 *ospf6)
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);
+ frr_daemon_state_save(&json);
}
/*
@@ -552,13 +628,12 @@ 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;
- json = json_object_from_file((char *)OSPF6D_GR_STATE);
- if (json == NULL)
- json = json_object_new_object();
+ json = frr_daemon_state_load();
json_object_object_get_ex(json, "instances", &json_instances);
if (!json_instances) {
@@ -573,36 +648,57 @@ 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);
+ json_object_object_del(json_instance, "gracePeriod");
+ json_object_object_del(json_instance, "timestamp");
+
+ frr_daemon_state_save(&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);
- json_object_to_file_ext((char *)OSPF6D_GR_STATE, json,
- JSON_C_TO_STRING_PRETTY);
- json_object_free(json);
+ /* 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. */
@@ -625,25 +721,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 +802,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 +830,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;
}