summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/user/ospfd.rst4
-rw-r--r--ospfd/ospf_ase.c1
-rw-r--r--ospfd/ospf_gr.c172
-rw-r--r--ospfd/ospf_gr.h6
-rw-r--r--ospfd/ospf_packet.c5
-rw-r--r--ospfd/ospf_packet.h3
-rw-r--r--ospfd/ospf_vty.c7
-rw-r--r--ospfd/ospf_zebra.c10
-rw-r--r--ospfd/ospfd.c12
-rw-r--r--ospfd/ospfd.h2
10 files changed, 158 insertions, 64 deletions
diff --git a/doc/user/ospfd.rst b/doc/user/ospfd.rst
index 5171832604..e076fae6c1 100644
--- a/doc/user/ospfd.rst
+++ b/doc/user/ospfd.rst
@@ -770,6 +770,10 @@ Graceful Restart
To perform a graceful shutdown, the "graceful-restart prepare ip ospf"
EXEC-level command needs to be issued before restarting the ospfd daemon.
+ When Graceful Restart is enabled and the ospfd daemon crashes or is killed
+ abruptely (e.g. SIGKILL), it will attempt an unplanned Graceful Restart once
+ it restarts.
+
.. clicmd:: graceful-restart helper enable [A.B.C.D]
diff --git a/ospfd/ospf_ase.c b/ospfd/ospf_ase.c
index cc2110d433..610b5fc08e 100644
--- a/ospfd/ospf_ase.c
+++ b/ospfd/ospf_ase.c
@@ -615,6 +615,7 @@ static void ospf_ase_calculate_timer(struct event *t)
*/
if (ospf->gr_info.finishing_restart) {
ospf_zebra_gr_disable(ospf);
+ ospf_zebra_gr_enable(ospf, ospf->gr_info.grace_period);
ospf->gr_info.finishing_restart = false;
}
}
diff --git a/ospfd/ospf_gr.c b/ospfd/ospf_gr.c
index f60f0e863e..ab188809ed 100644
--- a/ospfd/ospf_gr.c
+++ b/ospfd/ospf_gr.c
@@ -33,7 +33,7 @@
#include "ospfd/ospf_dump.h"
#include "ospfd/ospf_gr_clippy.c"
-static void ospf_gr_nvm_delete(struct ospf *ospf);
+static void ospf_gr_grace_period_expired(struct event *thread);
/* Lookup self-originated Grace-LSA in the LSDB. */
static struct ospf_lsa *ospf_gr_lsa_lookup(struct ospf *ospf,
@@ -53,7 +53,9 @@ static struct ospf_lsa *ospf_gr_lsa_lookup(struct ospf *ospf,
/* Fill in fields of the Grace-LSA that is being originated. */
static void ospf_gr_lsa_body_set(struct ospf_gr_info *gr_info,
- struct ospf_interface *oi, struct stream *s)
+ struct ospf_interface *oi,
+ enum ospf_gr_restart_reason reason,
+ struct stream *s)
{
struct grace_tlv_graceperiod tlv_period = {};
struct grace_tlv_restart_reason tlv_reason = {};
@@ -68,10 +70,7 @@ static void ospf_gr_lsa_body_set(struct ospf_gr_info *gr_info,
/* Put restart reason. */
tlv_reason.header.type = htons(RESTART_REASON_TYPE);
tlv_reason.header.length = htons(RESTART_REASON_LENGTH);
- if (gr_info->restart_support)
- tlv_reason.reason = OSPF_GR_SW_RESTART;
- else
- tlv_reason.reason = OSPF_GR_UNKNOWN_RESTART;
+ tlv_reason.reason = reason;
stream_put(s, &tlv_reason, sizeof(tlv_reason));
/* Put IP address. */
@@ -85,7 +84,8 @@ static void ospf_gr_lsa_body_set(struct ospf_gr_info *gr_info,
}
/* Generate Grace-LSA for a given interface. */
-static struct ospf_lsa *ospf_gr_lsa_new(struct ospf_interface *oi)
+static struct ospf_lsa *ospf_gr_lsa_new(struct ospf_interface *oi,
+ enum ospf_gr_restart_reason reason)
{
struct stream *s;
struct lsa_header *lsah;
@@ -112,7 +112,7 @@ static struct ospf_lsa *ospf_gr_lsa_new(struct ospf_interface *oi)
lsa_header_set(s, options, lsa_type, lsa_id, oi->ospf->router_id);
/* Set opaque-LSA body fields. */
- ospf_gr_lsa_body_set(&oi->ospf->gr_info, oi, s);
+ ospf_gr_lsa_body_set(&oi->ospf->gr_info, oi, reason, s);
/* Set length. */
length = stream_get_endp(s);
@@ -135,15 +135,20 @@ static struct ospf_lsa *ospf_gr_lsa_new(struct ospf_interface *oi)
}
/* Originate and install Grace-LSA for a given interface. */
-static void ospf_gr_lsa_originate(struct ospf_interface *oi, bool maxage)
+static void ospf_gr_lsa_originate(struct ospf_interface *oi,
+ enum ospf_gr_restart_reason reason,
+ bool maxage)
{
struct ospf_lsa *lsa, *old;
- if (ospf_interface_neighbor_count(oi) == 0)
+ /* Skip originating a Grace-LSA when not necessary. */
+ if (!if_is_operative(oi->ifp) || if_is_loopback(oi->ifp) ||
+ (reason != OSPF_GR_UNKNOWN_RESTART &&
+ ospf_interface_neighbor_count(oi) == 0))
return;
/* Create new Grace-LSA. */
- lsa = ospf_gr_lsa_new(oi);
+ lsa = ospf_gr_lsa_new(oi, reason);
if (!lsa) {
zlog_warn("%s: ospf_gr_lsa_new() failed", __func__);
return;
@@ -157,18 +162,36 @@ static void ospf_gr_lsa_originate(struct ospf_interface *oi, bool maxage)
if (old)
lsa->data->ls_seqnum = lsa_seqnum_increment(old);
- /* Install this LSA into LSDB. */
- if (ospf_lsa_install(oi->ospf, oi, lsa) == NULL) {
- zlog_warn("%s: ospf_lsa_install() failed", __func__);
- ospf_lsa_unlock(&lsa);
- return;
+ if (!maxage && reason == OSPF_GR_UNKNOWN_RESTART) {
+ struct list *update;
+ struct in_addr addr;
+
+ /*
+ * When performing an unplanned restart, send a handcrafted
+ * Grace-LSA since the interface isn't fully initialized yet.
+ */
+ ospf_lsa_checksum(lsa->data);
+ ospf_lsa_lock(lsa);
+ update = list_new();
+ listnode_add(update, lsa);
+ addr.s_addr = htonl(OSPF_ALLSPFROUTERS);
+ ospf_ls_upd_queue_send(oi, update, addr, true);
+ list_delete(&update);
+ ospf_lsa_discard(lsa);
+ } else {
+ /* Install this LSA into LSDB. */
+ if (ospf_lsa_install(oi->ospf, oi, lsa) == NULL) {
+ zlog_warn("%s: ospf_lsa_install() failed", __func__);
+ ospf_lsa_unlock(&lsa);
+ return;
+ }
+
+ /* Flood the LSA through out the interface */
+ ospf_flood_through_interface(oi, NULL, lsa);
}
/* Update new LSA origination count. */
oi->ospf->lsa_originate_count++;
-
- /* Flood the LSA through out the interface */
- ospf_flood_through_interface(oi, NULL, lsa);
}
/* Flush all self-originated Grace-LSAs. */
@@ -181,13 +204,14 @@ static void ospf_gr_flush_grace_lsas(struct ospf *ospf)
struct ospf_interface *oi;
struct listnode *inode;
- if (IS_DEBUG_OSPF_GR)
- zlog_debug(
- "GR: flushing self-originated Grace-LSAs [area %pI4]",
- &area->area_id);
+ for (ALL_LIST_ELEMENTS_RO(area->oiflist, inode, oi)) {
+ if (IS_DEBUG_OSPF_GR)
+ zlog_debug(
+ "GR: flushing self-originated Grace-LSA [area %pI4] [interface %s]",
+ &area->area_id, oi->ifp->name);
- for (ALL_LIST_ELEMENTS_RO(area->oiflist, inode, oi))
- ospf_gr_lsa_originate(oi, true);
+ ospf_gr_lsa_originate(oi, ospf->gr_info.reason, true);
+ }
}
}
@@ -203,9 +227,6 @@ static void ospf_gr_restart_exit(struct ospf *ospf, const char *reason)
ospf->gr_info.restart_in_progress = false;
EVENT_OFF(ospf->gr_info.t_grace_period);
- /* Record in non-volatile memory that the restart is complete. */
- ospf_gr_nvm_delete(ospf);
-
for (ALL_LIST_ELEMENTS_RO(ospf->areas, onode, area)) {
struct ospf_interface *oi;
@@ -242,12 +263,34 @@ static void ospf_gr_restart_exit(struct ospf *ospf, const char *reason)
* should be removed.
*/
ospf->gr_info.finishing_restart = true;
+ XFREE(MTYPE_TMP, ospf->gr_info.exit_reason);
+ ospf->gr_info.exit_reason = XSTRDUP(MTYPE_TMP, reason);
ospf_spf_calculate_schedule(ospf, SPF_FLAG_GR_FINISH);
/* 6) Any grace-LSAs that the router originated should be flushed. */
ospf_gr_flush_grace_lsas(ospf);
}
+/* Enter the Graceful Restart mode. */
+void ospf_gr_restart_enter(struct ospf *ospf,
+ enum ospf_gr_restart_reason reason, int timestamp)
+{
+ unsigned long remaining_time;
+
+ ospf->gr_info.restart_in_progress = true;
+ ospf->gr_info.reason = reason;
+
+ /* Schedule grace period timeout. */
+ remaining_time = timestamp - time(NULL);
+ if (IS_DEBUG_OSPF_GR)
+ zlog_debug(
+ "GR: remaining time until grace period expires: %lu(s)",
+ remaining_time);
+
+ event_add_timer(master, ospf_gr_grace_period_expired, ospf,
+ remaining_time, &ospf->gr_info.t_grace_period);
+}
+
/* Check if a Router-LSA contains a given link. */
static bool ospf_router_lsa_contains_adj(struct ospf_lsa *lsa,
struct in_addr *id)
@@ -522,7 +565,7 @@ static char *ospf_gr_nvm_filepath(struct ospf *ospf)
* Record in non-volatile memory that the given OSPF instance is attempting to
* perform a graceful restart.
*/
-static void ospf_gr_nvm_update(struct ospf *ospf)
+static void ospf_gr_nvm_update(struct ospf *ospf, bool prepare)
{
char *filepath;
const char *inst_name;
@@ -550,16 +593,18 @@ static void ospf_gr_nvm_update(struct ospf *ospf)
json_instance);
}
+ json_object_int_add(json_instance, "gracePeriod",
+ ospf->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 ospfd is
* restarted, it will be possible to take into account the time that
* passed while ospfd wasn't running.
*/
- json_object_int_add(json_instance, "gracePeriod",
- ospf->gr_info.grace_period);
- json_object_int_add(json_instance, "timestamp",
- time(NULL) + ospf->gr_info.grace_period);
+ if (prepare)
+ json_object_int_add(json_instance, "timestamp",
+ time(NULL) + ospf->gr_info.grace_period);
json_object_to_file_ext(filepath, json, JSON_C_TO_STRING_PRETTY);
json_object_free(json);
@@ -569,7 +614,7 @@ static void ospf_gr_nvm_update(struct ospf *ospf)
* Delete GR status information about the given OSPF instance from non-volatile
* memory.
*/
-static void ospf_gr_nvm_delete(struct ospf *ospf)
+void ospf_gr_nvm_delete(struct ospf *ospf)
{
char *filepath;
const char *inst_name;
@@ -607,6 +652,7 @@ void ospf_gr_nvm_read(struct ospf *ospf)
json_object *json_instances;
json_object *json_instance;
json_object *json_timestamp;
+ json_object *json_grace_period;
time_t timestamp = 0;
filepath = ospf_gr_nvm_filepath(ospf);
@@ -629,29 +675,33 @@ void ospf_gr_nvm_read(struct ospf *ospf)
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) {
ospf_gr_restart_exit(
ospf, "grace period has expired already");
- } else {
- /* Schedule grace period timeout. */
- ospf->gr_info.restart_in_progress = true;
- remaining_time = timestamp - time(NULL);
- if (IS_DEBUG_OSPF_GR)
- zlog_debug(
- "GR: remaining time until grace period expires: %lu(s)",
- remaining_time);
- event_add_timer(master, ospf_gr_grace_period_expired,
- ospf, remaining_time,
- &ospf->gr_info.t_grace_period);
- }
+ } else
+ ospf_gr_restart_enter(ospf, OSPF_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);
+ ospf->gr_info.grace_period = grace_period;
+ ospf_gr_restart_enter(ospf, OSPF_GR_UNKNOWN_RESTART,
+ time(NULL) + ospf->gr_info.grace_period);
}
json_object_object_del(json_instances, inst_name);
@@ -660,6 +710,12 @@ void ospf_gr_nvm_read(struct ospf *ospf)
json_object_free(json);
}
+void ospf_gr_unplanned_start_interface(struct ospf_interface *oi)
+{
+ /* Send Grace-LSA. */
+ ospf_gr_lsa_originate(oi, oi->ospf->gr_info.reason, false);
+}
+
/* Prepare to start a Graceful Restart. */
static void ospf_gr_prepare(void)
{
@@ -687,20 +743,12 @@ static void ospf_gr_prepare(void)
continue;
}
- /* Freeze OSPF routes in the RIB. */
- if (ospf_zebra_gr_enable(ospf, ospf->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(ospf->oiflist, inode, oi))
- ospf_gr_lsa_originate(oi, false);
+ ospf_gr_lsa_originate(oi, OSPF_GR_SW_RESTART, false);
/* Record end of the grace period in non-volatile memory. */
- ospf_gr_nvm_update(ospf);
+ ospf_gr_nvm_update(ospf, true);
/*
* Mark that a Graceful Restart preparation is in progress, to
@@ -749,6 +797,12 @@ DEFPY(graceful_restart, graceful_restart_cmd,
ospf->gr_info.restart_support = true;
ospf->gr_info.grace_period = grace_period;
+ /* Freeze OSPF routes in the RIB. */
+ (void)ospf_zebra_gr_enable(ospf, ospf->gr_info.grace_period);
+
+ /* Record that GR is enabled in non-volatile memory. */
+ ospf_gr_nvm_update(ospf, false);
+
return CMD_SUCCESS;
}
@@ -771,6 +825,8 @@ DEFPY(no_graceful_restart, no_graceful_restart_cmd,
ospf->gr_info.restart_support = false;
ospf->gr_info.grace_period = OSPF_DFLT_GRACE_INTERVAL;
+ ospf_gr_nvm_delete(ospf);
+ ospf_zebra_gr_disable(ospf);
return CMD_SUCCESS;
}
diff --git a/ospfd/ospf_gr.h b/ospfd/ospf_gr.h
index 9760bb1728..0f6809e0bd 100644
--- a/ospfd/ospf_gr.h
+++ b/ospfd/ospf_gr.h
@@ -166,11 +166,15 @@ extern void ospf_gr_helper_supported_gracetime_set(struct ospf *ospf,
uint32_t interval);
extern void ospf_gr_helper_set_supported_planned_only_restart(struct ospf *ospf,
bool planned_only);
-
+extern void ospf_gr_restart_enter(struct ospf *ospf,
+ enum ospf_gr_restart_reason reason,
+ int timestamp);
extern void ospf_gr_check_lsdb_consistency(struct ospf *ospf,
struct ospf_area *area);
extern void ospf_gr_check_adjs(struct ospf *ospf);
extern void ospf_gr_nvm_read(struct ospf *ospf);
+extern void ospf_gr_nvm_delete(struct ospf *ospf);
+extern void ospf_gr_unplanned_start_interface(struct ospf_interface *oi);
extern void ospf_gr_init(void);
#endif /* _ZEBRA_OSPF_GR_H */
diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c
index 552acfd6d3..66d3df2f97 100644
--- a/ospfd/ospf_packet.c
+++ b/ospfd/ospf_packet.c
@@ -4040,9 +4040,8 @@ static struct ospf_packet *ospf_ls_upd_packet_new(struct list *update,
return ospf_packet_new(size - sizeof(struct ip));
}
-static void ospf_ls_upd_queue_send(struct ospf_interface *oi,
- struct list *update, struct in_addr addr,
- int send_lsupd_now)
+void ospf_ls_upd_queue_send(struct ospf_interface *oi, struct list *update,
+ struct in_addr addr, int send_lsupd_now)
{
struct ospf_packet *op;
uint16_t length = OSPF_HEADER_SIZE;
diff --git a/ospfd/ospf_packet.h b/ospfd/ospf_packet.h
index 4003e2add6..234738979e 100644
--- a/ospfd/ospf_packet.h
+++ b/ospfd/ospf_packet.h
@@ -132,6 +132,9 @@ extern void ospf_ls_req_send(struct ospf_neighbor *);
extern void ospf_ls_upd_send_lsa(struct ospf_neighbor *, struct ospf_lsa *,
int);
extern void ospf_ls_upd_send(struct ospf_neighbor *, struct list *, int, int);
+extern void ospf_ls_upd_queue_send(struct ospf_interface *oi,
+ struct list *update, struct in_addr addr,
+ int send_lsupd_now);
extern void ospf_ls_ack_send(struct ospf_neighbor *, struct ospf_lsa *);
extern void ospf_ls_ack_send_delayed(struct ospf_interface *);
extern void ospf_ls_retransmit(struct ospf_interface *, struct ospf_lsa *);
diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c
index 1b4d26ef3d..751ad6e1af 100644
--- a/ospfd/ospf_vty.c
+++ b/ospfd/ospf_vty.c
@@ -232,9 +232,12 @@ DEFUN (no_router_ospf,
return CMD_NOT_MY_INSTANCE;
ospf = ospf_lookup(instance, vrf_name);
- if (ospf)
+ if (ospf) {
+ if (ospf->gr_info.restart_support)
+ ospf_gr_nvm_delete(ospf);
+
ospf_finish(ospf);
- else
+ } else
ret = CMD_WARNING_CONFIG_FAILED;
return ret;
diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c
index 0b770a8364..e7a98188ab 100644
--- a/ospfd/ospf_zebra.c
+++ b/ospfd/ospf_zebra.c
@@ -2120,10 +2120,20 @@ int ospf_zebra_label_manager_connect(void)
static void ospf_zebra_connected(struct zclient *zclient)
{
+ struct ospf *ospf;
+ 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(om->ospf, node, ospf)) {
+ if (!ospf->gr_info.restart_support)
+ continue;
+ (void)ospf_zebra_gr_enable(ospf, ospf->gr_info.grace_period);
+ }
}
/*
diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c
index 7e83714c0a..eae1f301a2 100644
--- a/ospfd/ospfd.c
+++ b/ospfd/ospfd.c
@@ -720,6 +720,7 @@ static void ospf_finish_final(struct ospf *ospf)
if (!ospf->gr_info.prepare_in_progress)
ospf_flush_self_originated_lsas_now(ospf);
+ XFREE(MTYPE_TMP, ospf->gr_info.exit_reason);
/* Unregister redistribution */
for (i = 0; i < ZEBRA_ROUTE_MAX; i++) {
@@ -1131,6 +1132,17 @@ struct ospf_interface *add_ospf_interface(struct connected *co,
&& if_is_operative(co->ifp))
ospf_if_up(oi);
+ /*
+ * 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->ospf->gr_info.restart_in_progress &&
+ oi->ospf->gr_info.reason == OSPF_GR_UNKNOWN_RESTART)
+ ospf_gr_unplanned_start_interface(oi);
+
return oi;
}
diff --git a/ospfd/ospfd.h b/ospfd/ospfd.h
index 1f8d1a32e6..36936b16f4 100644
--- a/ospfd/ospfd.h
+++ b/ospfd/ospfd.h
@@ -148,6 +148,8 @@ struct ospf_gr_info {
bool prepare_in_progress;
bool finishing_restart;
uint32_t grace_period;
+ int reason;
+ char *exit_reason;
struct event *t_grace_period;
};