summaryrefslogtreecommitdiffstats
path: root/ospfd
diff options
context:
space:
mode:
authorRuss White <russ@riw.us>2021-07-13 12:39:53 +0200
committerGitHub <noreply@github.com>2021-07-13 12:39:53 +0200
commit000df71ccdc2e81c4d6e7c123ad0db2cbcc4f05d (patch)
tree6fb9713c0171fa1dbaa2d327cba0d087399e42e8 /ospfd
parentMerge pull request #8734 from imzyxwvu/paf-deactivate (diff)
parenttests: add OSPF graceful restart topotest (diff)
downloadfrr-000df71ccdc2e81c4d6e7c123ad0db2cbcc4f05d.tar.xz
frr-000df71ccdc2e81c4d6e7c123ad0db2cbcc4f05d.zip
Merge pull request #8767 from opensourcerouting/ospfd-gr
ospfd: introduce support for Graceful Restart (restarting mode)
Diffstat (limited to 'ospfd')
-rw-r--r--ospfd/ospf_abr.c6
-rw-r--r--ospfd/ospf_ase.c10
-rw-r--r--ospfd/ospf_dump.c27
-rw-r--r--ospfd/ospf_dump.h5
-rw-r--r--ospfd/ospf_flood.c49
-rw-r--r--ospfd/ospf_flood.h3
-rw-r--r--ospfd/ospf_gr.c824
-rw-r--r--ospfd/ospf_gr.h (renamed from ospfd/ospf_gr_helper.h)20
-rw-r--r--ospfd/ospf_gr_helper.c69
-rw-r--r--ospfd/ospf_lsa.c70
-rw-r--r--ospfd/ospf_main.c2
-rw-r--r--ospfd/ospf_neighbor.c2
-rw-r--r--ospfd/ospf_neighbor.h2
-rw-r--r--ospfd/ospf_nsm.c6
-rw-r--r--ospfd/ospf_opaque.c5
-rw-r--r--ospfd/ospf_packet.c24
-rw-r--r--ospfd/ospf_spf.c2
-rw-r--r--ospfd/ospf_spf.h1
-rw-r--r--ospfd/ospf_vty.c15
-rw-r--r--ospfd/ospf_zebra.c62
-rw-r--r--ospfd/ospf_zebra.h2
-rw-r--r--ospfd/ospfd.c21
-rw-r--r--ospfd/ospfd.h13
-rw-r--r--ospfd/subdir.am5
24 files changed, 1145 insertions, 100 deletions
diff --git a/ospfd/ospf_abr.c b/ospfd/ospf_abr.c
index a6027ee9d..6a009d214 100644
--- a/ospfd/ospf_abr.c
+++ b/ospfd/ospf_abr.c
@@ -1694,6 +1694,9 @@ static void ospf_abr_manage_discard_routes(struct ospf *ospf)
static void ospf_abr_nssa_task(struct ospf *ospf) /* called only if any_nssa */
{
+ if (ospf->gr_info.restart_in_progress)
+ return;
+
if (IS_DEBUG_OSPF_NSSA)
zlog_debug("Check for NSSA-ABR Tasks():");
@@ -1758,6 +1761,9 @@ static void ospf_abr_nssa_task(struct ospf *ospf) /* called only if any_nssa */
summary-LSA origination and flooding. */
void ospf_abr_task(struct ospf *ospf)
{
+ if (ospf->gr_info.restart_in_progress)
+ return;
+
if (IS_DEBUG_OSPF_EVENT)
zlog_debug("ospf_abr_task(): Start");
diff --git a/ospfd/ospf_ase.c b/ospfd/ospf_ase.c
index 51da4a55a..aaacebca1 100644
--- a/ospfd/ospf_ase.c
+++ b/ospfd/ospf_ase.c
@@ -609,6 +609,16 @@ static int ospf_ase_calculate_timer(struct thread *t)
+ (stop_time.tv_usec
- start_time.tv_usec));
}
+
+ /*
+ * Uninstall remnant routes that were installed before the restart, but
+ * that are no longer valid.
+ */
+ if (ospf->gr_info.finishing_restart) {
+ ospf_zebra_gr_disable(ospf);
+ ospf->gr_info.finishing_restart = false;
+ }
+
return 0;
}
diff --git a/ospfd/ospf_dump.c b/ospfd/ospf_dump.c
index e490070d0..f11c84b09 100644
--- a/ospfd/ospf_dump.c
+++ b/ospfd/ospf_dump.c
@@ -1552,21 +1552,16 @@ DEFUN(no_debug_ospf_ldp_sync,
return CMD_SUCCESS;
}
-DEFPY (debug_ospf_gr,
- debug_ospf_gr_cmd,
- "[no$no] debug ospf graceful-restart helper",
- NO_STR
- DEBUG_STR OSPF_STR
- "Gracefull restart\n"
- "Helper Information\n")
+DEFPY(debug_ospf_gr, debug_ospf_gr_cmd, "[no$no] debug ospf graceful-restart",
+ NO_STR DEBUG_STR OSPF_STR "OSPF Graceful Restart\n")
{
if (vty->node == CONFIG_NODE)
- CONF_DEBUG_ON(gr, GR_HELPER);
+ CONF_DEBUG_ON(gr, GR);
if (!no)
- TERM_DEBUG_ON(gr, GR_HELPER);
+ TERM_DEBUG_ON(gr, GR);
else
- TERM_DEBUG_OFF(gr, GR_HELPER);
+ TERM_DEBUG_OFF(gr, GR);
return CMD_SUCCESS;
}
@@ -1764,9 +1759,9 @@ static int show_debugging_ospf_common(struct vty *vty)
if (IS_DEBUG_OSPF(ldp_sync, LDP_SYNC) == OSPF_DEBUG_LDP_SYNC)
vty_out(vty, " OSPF ldp-sync debugging is on\n");
- /* Show debug status for GR helper. */
- if (IS_DEBUG_OSPF(gr, GR_HELPER) == OSPF_DEBUG_GR_HELPER)
- vty_out(vty, " OSPF Graceful Restart Helper debugging is on\n");
+ /* Show debug status for GR. */
+ if (IS_DEBUG_OSPF(gr, GR) == OSPF_DEBUG_GR)
+ vty_out(vty, " OSPF Graceful Restart debugging is on\n");
if (IS_DEBUG_OSPF(bfd, BFD_LIB) == OSPF_DEBUG_BFD_LIB)
vty_out(vty,
@@ -1953,9 +1948,9 @@ static int config_write_debug(struct vty *vty)
write = 1;
}
- /* debug ospf gr helper */
- if (IS_CONF_DEBUG_OSPF(gr, GR_HELPER) == OSPF_DEBUG_GR_HELPER) {
- vty_out(vty, "debug ospf%s graceful-restart helper\n", str);
+ /* debug ospf gr */
+ if (IS_CONF_DEBUG_OSPF(gr, GR) == OSPF_DEBUG_GR) {
+ vty_out(vty, "debug ospf%s graceful-restart\n", str);
write = 1;
}
diff --git a/ospfd/ospf_dump.h b/ospfd/ospf_dump.h
index b1c1d02a5..a1f55dd0a 100644
--- a/ospfd/ospf_dump.h
+++ b/ospfd/ospf_dump.h
@@ -64,8 +64,7 @@
#define OSPF_DEBUG_DEFAULTINFO 0x20
#define OSPF_DEBUG_LDP_SYNC 0x40
-#define OSPF_DEBUG_GR_HELPER 0x01
-#define OSPF_DEBUG_GR 0x03
+#define OSPF_DEBUG_GR 0x01
#define OSPF_DEBUG_BFD_LIB 0x01
@@ -118,7 +117,7 @@
#define IS_DEBUG_OSPF_DEFAULT_INFO IS_DEBUG_OSPF(defaultinfo, DEFAULTINFO)
#define IS_DEBUG_OSPF_LDP_SYNC IS_DEBUG_OSPF(ldp_sync, LDP_SYNC)
-#define IS_DEBUG_OSPF_GR_HELPER IS_DEBUG_OSPF(gr, GR_HELPER)
+#define IS_DEBUG_OSPF_GR IS_DEBUG_OSPF(gr, GR)
#define IS_CONF_DEBUG_OSPF_PACKET(a, b) \
(conf_debug_ospf_packet[a] & OSPF_DEBUG_##b)
diff --git a/ospfd/ospf_flood.c b/ospfd/ospf_flood.c
index 55bcaebd6..7fddb65a8 100644
--- a/ospfd/ospf_flood.c
+++ b/ospfd/ospf_flood.c
@@ -379,13 +379,13 @@ int ospf_flood(struct ospf *ospf, struct ospf_neighbor *nbr,
SET_FLAG(new->flags, OSPF_LSA_RECEIVED);
(void)ospf_lsa_is_self_originated(ospf, new); /* Let it set the flag */
- /* Received Grace LSA */
- if (IS_GRACE_LSA(new)) {
+ /* Received non-self-originated Grace LSA */
+ if (IS_GRACE_LSA(new) && !IS_LSA_SELF(new)) {
if (IS_LSA_MAXAGE(new)) {
/* Handling Max age grace LSA.*/
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug(
"%s, Received a maxage GRACE-LSA from router %pI4",
__func__, &new->data->adv_router);
@@ -393,21 +393,21 @@ int ospf_flood(struct ospf *ospf, struct ospf_neighbor *nbr,
if (current) {
ospf_process_maxage_grace_lsa(ospf, new, nbr);
} else {
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug(
"%s, Grace LSA doesn't exist in lsdb, so discarding grace lsa",
__func__);
return -1;
}
} else {
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug(
"%s, Received a GRACE-LSA from router %pI4",
__func__, &new->data->adv_router);
if (ospf_process_grace_lsa(ospf, new, nbr)
== OSPF_GR_NOT_HELPER) {
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug(
"%s, Not moving to HELPER role, So discarding grace LSA",
__func__);
@@ -445,9 +445,9 @@ int ospf_flood(struct ospf *ospf, struct ospf_neighbor *nbr,
}
/* OSPF LSA flooding -- RFC2328 Section 13.3. */
-static int ospf_flood_through_interface(struct ospf_interface *oi,
- struct ospf_neighbor *inbr,
- struct ospf_lsa *lsa)
+int ospf_flood_through_interface(struct ospf_interface *oi,
+ struct ospf_neighbor *inbr,
+ struct ospf_lsa *lsa)
{
struct ospf_neighbor *onbr;
struct route_node *rn;
@@ -1031,25 +1031,50 @@ void ospf_ls_retransmit_delete_nbr_as(struct ospf *ospf, struct ospf_lsa *lsa)
flushing an LSA from the whole domain. */
void ospf_lsa_flush_area(struct ospf_lsa *lsa, struct ospf_area *area)
{
+ struct ospf *ospf = area->ospf;
+
+ if (ospf_lsa_is_self_originated(ospf, lsa)
+ && ospf->gr_info.restart_in_progress) {
+ if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
+ zlog_debug(
+ "%s:LSA[Type%d:%pI4]: Graceful Restart in progress -- not flushing self-originated LSA",
+ ospf_get_name(ospf), lsa->data->type,
+ &lsa->data->id);
+ return;
+ }
+
/* Reset the lsa origination time such that it gives
more time for the ACK to be received and avoid
retransmissions */
lsa->data->ls_age = htons(OSPF_LSA_MAXAGE);
if (IS_DEBUG_OSPF_EVENT)
- zlog_debug("%s: MAXAGE set to LSA %pI4", __func__,
- &lsa->data->id);
+ zlog_debug("%s: MaxAge set to LSA[%s]", __func__,
+ dump_lsa_key(lsa));
monotime(&lsa->tv_recv);
lsa->tv_orig = lsa->tv_recv;
ospf_flood_through_area(area, NULL, lsa);
- ospf_lsa_maxage(area->ospf, lsa);
+ ospf_lsa_maxage(ospf, lsa);
}
void ospf_lsa_flush_as(struct ospf *ospf, struct ospf_lsa *lsa)
{
+ if (ospf_lsa_is_self_originated(ospf, lsa)
+ && ospf->gr_info.restart_in_progress) {
+ if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
+ zlog_debug(
+ "%s:LSA[Type%d:%pI4]: Graceful Restart in progress -- not flushing self-originated LSA",
+ ospf_get_name(ospf), lsa->data->type,
+ &lsa->data->id);
+ return;
+ }
+
/* Reset the lsa origination time such that it gives
more time for the ACK to be received and avoid
retransmissions */
lsa->data->ls_age = htons(OSPF_LSA_MAXAGE);
+ if (IS_DEBUG_OSPF_EVENT)
+ zlog_debug("%s: MaxAge set to LSA[%s]", __func__,
+ dump_lsa_key(lsa));
monotime(&lsa->tv_recv);
lsa->tv_orig = lsa->tv_recv;
ospf_flood_through_as(ospf, NULL, lsa);
diff --git a/ospfd/ospf_flood.h b/ospfd/ospf_flood.h
index 6f7ecfccf..95a5b358c 100644
--- a/ospfd/ospf_flood.h
+++ b/ospfd/ospf_flood.h
@@ -30,6 +30,9 @@ extern int ospf_flood_through_area(struct ospf_area *, struct ospf_neighbor *,
struct ospf_lsa *);
extern int ospf_flood_through_as(struct ospf *, struct ospf_neighbor *,
struct ospf_lsa *);
+extern int ospf_flood_through_interface(struct ospf_interface *oi,
+ struct ospf_neighbor *inbr,
+ struct ospf_lsa *lsa);
extern unsigned long ospf_ls_request_count(struct ospf_neighbor *);
extern int ospf_ls_request_isempty(struct ospf_neighbor *);
diff --git a/ospfd/ospf_gr.c b/ospfd/ospf_gr.c
new file mode 100644
index 000000000..c10804030
--- /dev/null
+++ b/ospfd/ospf_gr.c
@@ -0,0 +1,824 @@
+/*
+ * This is an implementation of RFC 3623 Graceful OSPF Restart.
+ *
+ * Copyright 2021 NetDEF (c), All rights reserved.
+ * Copyright 2020 6WIND (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 "memory.h"
+#include "command.h"
+#include "table.h"
+#include "vty.h"
+#include "log.h"
+#include "printfrr.h"
+
+#include "ospfd/ospfd.h"
+#include "ospfd/ospf_abr.h"
+#include "ospfd/ospf_flood.h"
+#include "ospfd/ospf_ism.h"
+#include "ospfd/ospf_interface.h"
+#include "ospfd/ospf_asbr.h"
+#include "ospfd/ospf_lsa.h"
+#include "ospfd/ospf_route.h"
+#include "ospfd/ospf_zebra.h"
+#include "ospfd/ospf_lsdb.h"
+#include "ospfd/ospf_neighbor.h"
+#include "ospfd/ospf_opaque.h"
+#include "ospfd/ospf_nsm.h"
+#include "ospfd/ospf_gr.h"
+#include "ospfd/ospf_errors.h"
+#include "ospfd/ospf_dump.h"
+#ifndef VTYSH_EXTRACT_PL
+#include "ospfd/ospf_gr_clippy.c"
+#endif
+
+static void ospf_gr_nvm_delete(struct ospf *ospf);
+
+/* Lookup self-originated Grace-LSA in the LSDB. */
+static struct ospf_lsa *ospf_gr_lsa_lookup(struct ospf *ospf,
+ struct ospf_area *area)
+{
+ struct ospf_lsa *lsa;
+ struct in_addr lsa_id;
+ uint32_t lsa_id_host_byte_order;
+
+ lsa_id_host_byte_order = SET_OPAQUE_LSID(OPAQUE_TYPE_GRACE_LSA, 0);
+ lsa_id.s_addr = htonl(lsa_id_host_byte_order);
+ lsa = ospf_lsa_lookup(ospf, area, OSPF_OPAQUE_LINK_LSA, lsa_id,
+ ospf->router_id);
+
+ return lsa;
+}
+
+/* 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 grace_tlv_graceperiod tlv_period = {};
+ struct grace_tlv_restart_reason tlv_reason = {};
+ struct grace_tlv_restart_addr tlv_address = {};
+
+ /* Put grace period. */
+ tlv_period.header.type = htons(GRACE_PERIOD_TYPE);
+ tlv_period.header.length = htons(GRACE_PERIOD_LENGTH);
+ tlv_period.interval = htonl(gr_info->grace_period);
+ stream_put(s, &tlv_period, sizeof(tlv_period));
+
+ /* 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;
+ stream_put(s, &tlv_reason, sizeof(tlv_reason));
+
+ /* Put IP address. */
+ if (oi->type == OSPF_IFTYPE_BROADCAST || oi->type == OSPF_IFTYPE_NBMA
+ || oi->type == OSPF_IFTYPE_POINTOMULTIPOINT) {
+ tlv_address.header.type = htons(RESTARTER_IP_ADDR_TYPE);
+ tlv_address.header.length = htons(RESTARTER_IP_ADDR_LEN);
+ tlv_address.addr = oi->address->u.prefix4;
+ stream_put(s, &tlv_address, sizeof(tlv_address));
+ }
+}
+
+/* Generate Grace-LSA for a given interface. */
+static struct ospf_lsa *ospf_gr_lsa_new(struct ospf_interface *oi)
+{
+ struct stream *s;
+ struct lsa_header *lsah;
+ struct ospf_lsa *new;
+ uint8_t options, lsa_type;
+ struct in_addr lsa_id;
+ uint32_t lsa_id_host_byte_order;
+ uint16_t length;
+
+ /* Create a stream for LSA. */
+ s = stream_new(OSPF_MAX_LSA_SIZE);
+
+ lsah = (struct lsa_header *)STREAM_DATA(s);
+
+ options = LSA_OPTIONS_GET(oi->area);
+ options |= LSA_OPTIONS_NSSA_GET(oi->area);
+ options |= OSPF_OPTION_O;
+
+ lsa_type = OSPF_OPAQUE_LINK_LSA;
+ lsa_id_host_byte_order = SET_OPAQUE_LSID(OPAQUE_TYPE_GRACE_LSA, 0);
+ lsa_id.s_addr = htonl(lsa_id_host_byte_order);
+
+ /* Set opaque-LSA header fields. */
+ 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);
+
+ /* Set length. */
+ length = stream_get_endp(s);
+ lsah->length = htons(length);
+
+ /* Now, create an OSPF LSA instance. */
+ new = ospf_lsa_new_and_data(length);
+
+ if (IS_DEBUG_OSPF_GR)
+ zlog_debug("LSA[Type%d:%pI4]: Create an Opaque-LSA/GR instance",
+ lsa_type, &lsa_id);
+
+ new->area = oi->area;
+ new->oi = oi;
+ SET_FLAG(new->flags, OSPF_LSA_SELF);
+ memcpy(new->data, lsah, length);
+ stream_free(s);
+
+ return new;
+}
+
+/* Originate and install Grace-LSA for a given interface. */
+static void ospf_gr_lsa_originate(struct ospf_interface *oi)
+{
+ struct ospf_lsa *lsa, *old;
+
+ if (ospf_interface_neighbor_count(oi) == 0)
+ return;
+
+ /* Create new Grace-LSA. */
+ lsa = ospf_gr_lsa_new(oi);
+ if (!lsa) {
+ zlog_warn("%s: ospf_gr_lsa_new() failed", __func__);
+ return;
+ }
+
+ /* Find the old LSA and increase the seqno. */
+ old = ospf_gr_lsa_lookup(oi->ospf, oi->area);
+ 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;
+ }
+
+ /* 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 a given self-originated Grace-LSA. */
+static struct ospf_lsa *ospf_gr_flush_grace_lsa(struct ospf_interface *oi,
+ struct ospf_lsa *old)
+{
+ struct ospf_lsa *lsa;
+
+ if (ospf_interface_neighbor_count(oi) == 0)
+ return NULL;
+
+ if (IS_DEBUG_OSPF_GR)
+ zlog_debug(
+ "GR: flushing self-originated Grace-LSAs [interface %s]",
+ oi->ifp->name);
+
+ lsa = ospf_lsa_dup(old);
+ lsa->data->ls_age = htons(OSPF_LSA_MAXAGE);
+ lsa->data->ls_seqnum = lsa_seqnum_increment(lsa);
+
+ /* Install updated 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 NULL;
+ }
+
+ /* Flood the LSA through out the interface */
+ ospf_flood_through_interface(oi, NULL, lsa);
+
+ return lsa;
+}
+
+/* Flush all self-originated Grace-LSAs. */
+static void ospf_gr_flush_grace_lsas(struct ospf *ospf)
+{
+ struct ospf_area *area;
+ struct listnode *anode;
+
+ for (ALL_LIST_ELEMENTS_RO(ospf->areas, anode, area)) {
+ struct ospf_lsa *lsa;
+ 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);
+
+ lsa = ospf_gr_lsa_lookup(ospf, area);
+ if (!lsa) {
+ zlog_warn("%s: Grace-LSA not found [area %pI4]",
+ __func__, &area->area_id);
+ continue;
+ }
+
+ for (ALL_LIST_ELEMENTS_RO(area->oiflist, inode, oi))
+ ospf_gr_flush_grace_lsa(oi, lsa);
+ }
+}
+
+/* Exit from the Graceful Restart mode. */
+static void ospf_gr_restart_exit(struct ospf *ospf, const char *reason)
+{
+ struct ospf_area *area;
+ struct listnode *onode, *anode;
+
+ if (IS_DEBUG_OSPF_GR)
+ zlog_debug("GR: exiting graceful restart: %s", reason);
+
+ ospf->gr_info.restart_in_progress = false;
+ OSPF_TIMER_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;
+
+ /*
+ * 1) The router should reoriginate its router-LSAs for all
+ * attached areas in order to make sure they have the correct
+ * contents.
+ */
+ ospf_router_lsa_update_area(area);
+
+ /*
+ * 2) The router should reoriginate network-LSAs on all segments
+ * where it is the Designated Router.
+ */
+ for (ALL_LIST_ELEMENTS_RO(area->oiflist, anode, oi))
+ if (oi->state == ISM_DR)
+ ospf_network_lsa_update(oi);
+ }
+
+ /*
+ * 5) Any received self-originated LSAs that are no longer valid should
+ * be flushed.
+ */
+ ospf_schedule_abr_task(ospf);
+
+ /*
+ * 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.
+ */
+ ospf->gr_info.finishing_restart = true;
+ 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);
+}
+
+/* Check if a Router-LSA contains a given link. */
+static bool ospf_router_lsa_contains_adj(struct ospf_lsa *lsa,
+ struct in_addr *id)
+{
+ struct router_lsa *rl;
+
+ rl = (struct router_lsa *)lsa->data;
+ for (int i = 0; i < ntohs(rl->links); i++) {
+ struct in_addr *link_id = &rl->link[i].link_id;
+
+ if (rl->link[i].type != LSA_LINK_TYPE_POINTOPOINT)
+ continue;
+
+ if (IPV4_ADDR_SAME(id, link_id))
+ return true;
+ }
+
+ return false;
+}
+
+static bool ospf_gr_check_router_lsa_consistency(struct ospf *ospf,
+ struct ospf_area *area,
+ struct ospf_lsa *lsa)
+{
+ if (CHECK_FLAG(lsa->flags, OSPF_LSA_SELF)) {
+ struct ospf_lsa *lsa_self = lsa;
+ struct router_lsa *rl = (struct router_lsa *)lsa->data;
+
+ for (int i = 0; i < ntohs(rl->links); i++) {
+ struct in_addr *link_id = &rl->link[i].link_id;
+ struct ospf_lsa *lsa_adj;
+
+ if (rl->link[i].type != LSA_LINK_TYPE_POINTOPOINT)
+ continue;
+
+ lsa_adj = ospf_lsa_lookup_by_id(area, OSPF_ROUTER_LSA,
+ *link_id);
+ if (!lsa_adj)
+ continue;
+
+ if (!ospf_router_lsa_contains_adj(lsa_adj,
+ &lsa_self->data->id))
+ return false;
+ }
+ } else {
+ struct ospf_lsa *lsa_self;
+
+ lsa_self = ospf_lsa_lookup_by_id(area, OSPF_ROUTER_LSA,
+ ospf->router_id);
+ if (!lsa_self
+ || !CHECK_FLAG(lsa_self->flags, OSPF_LSA_RECEIVED))
+ return true;
+
+ if (ospf_router_lsa_contains_adj(lsa, &ospf->router_id)
+ != ospf_router_lsa_contains_adj(lsa_self, &lsa->data->id))
+ 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 ospf_gr_check_lsdb_consistency(struct ospf *ospf, struct ospf_area *area)
+{
+ struct route_node *rn;
+ struct ospf_lsa *lsa;
+
+ for (rn = route_top(ROUTER_LSDB(area)); rn; rn = route_next(rn)) {
+ lsa = rn->info;
+ if (!lsa)
+ continue;
+
+ if (!ospf_gr_check_router_lsa_consistency(ospf, area, lsa)) {
+ char reason[256];
+
+ snprintfrr(reason, sizeof(reason),
+ "detected inconsistent LSA[%s] [area %pI4]",
+ dump_lsa_key(lsa), &area->area_id);
+ ospf_gr_restart_exit(ospf, reason);
+ route_unlock_node(rn);
+ return;
+ }
+ }
+}
+
+/* Lookup neighbor by address in a given OSPF area. */
+static struct ospf_neighbor *
+ospf_area_nbr_lookup_by_addr(struct ospf_area *area, struct in_addr *addr)
+{
+ struct ospf_interface *oi;
+ struct ospf_neighbor *nbr;
+ struct listnode *node;
+
+ for (ALL_LIST_ELEMENTS_RO(area->oiflist, node, oi)) {
+ nbr = ospf_nbr_lookup_by_addr(oi->nbrs, addr);
+ if (nbr)
+ return nbr;
+ }
+
+ return NULL;
+}
+
+/* Lookup neighbor by Router ID in a given OSPF area. */
+static struct ospf_neighbor *
+ospf_area_nbr_lookup_by_routerid(struct ospf_area *area, struct in_addr *id)
+{
+ struct ospf_interface *oi;
+ struct ospf_neighbor *nbr;
+ struct listnode *node;
+
+ for (ALL_LIST_ELEMENTS_RO(area->oiflist, node, oi)) {
+ nbr = ospf_nbr_lookup_by_routerid(oi->nbrs, id);
+ if (nbr)
+ return nbr;
+ }
+
+ return NULL;
+}
+
+/* Check if there's a fully formed adjacency with the given neighbor ID. */
+static bool ospf_gr_check_adj_id(struct ospf_area *area,
+ struct in_addr *nbr_id)
+{
+ struct ospf_neighbor *nbr;
+
+ nbr = ospf_area_nbr_lookup_by_routerid(area, nbr_id);
+ if (!nbr || nbr->state < NSM_Full) {
+ if (IS_DEBUG_OSPF_GR)
+ zlog_debug("GR: missing adjacency to router %pI4",
+ nbr_id);
+ return false;
+ }
+
+ return true;
+}
+
+static bool ospf_gr_check_adjs_lsa_transit(struct ospf_area *area,
+ struct in_addr *link_id)
+{
+ struct ospf *ospf = area->ospf;
+ struct ospf_interface *oi;
+
+ /*
+ * Check if the transit network refers to a local interface (in which
+ * case it must be a DR for that network).
+ */
+ oi = ospf_if_lookup_by_local_addr(ospf, NULL, *link_id);
+ if (oi) {
+ struct ospf_lsa *lsa;
+ struct network_lsa *nlsa;
+ size_t cnt;
+
+ /* Lookup Network LSA corresponding to this interface. */
+ lsa = ospf_lsa_lookup_by_id(area, OSPF_NETWORK_LSA, *link_id);
+ if (!lsa)
+ return false;
+
+ /* Iterate over all routers present in the network. */
+ nlsa = (struct network_lsa *)lsa->data;
+ cnt = (lsa->size - (OSPF_LSA_HEADER_SIZE + 4)) / 4;
+ for (size_t i = 0; i < cnt; i++) {
+ struct in_addr *nbr_id = &nlsa->routers[i];
+
+ /* Skip self in the pseudonode. */
+ if (IPV4_ADDR_SAME(nbr_id, &ospf->router_id))
+ continue;
+
+ /*
+ * Check if there's a fully formed adjacency with this
+ * router.
+ */
+ if (!ospf_gr_check_adj_id(area, nbr_id))
+ return false;
+ }
+ } else {
+ struct ospf_neighbor *nbr;
+
+ /* Check if there's a fully formed adjacency with the DR. */
+ nbr = ospf_area_nbr_lookup_by_addr(area, link_id);
+ if (!nbr || nbr->state < NSM_Full) {
+ if (IS_DEBUG_OSPF_GR)
+ zlog_debug(
+ "GR: missing adjacency to DR router %pI4",
+ link_id);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool ospf_gr_check_adjs_lsa(struct ospf_area *area, struct ospf_lsa *lsa)
+{
+ struct router_lsa *rl = (struct router_lsa *)lsa->data;
+
+ for (int i = 0; i < ntohs(rl->links); i++) {
+ struct in_addr *link_id = &rl->link[i].link_id;
+
+ switch (rl->link[i].type) {
+ case LSA_LINK_TYPE_POINTOPOINT:
+ if (!ospf_gr_check_adj_id(area, link_id))
+ return false;
+ break;
+ case LSA_LINK_TYPE_TRANSIT:
+ if (!ospf_gr_check_adjs_lsa_transit(area, link_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.
+ */
+void ospf_gr_check_adjs(struct ospf *ospf)
+{
+ struct ospf_area *area;
+ struct listnode *node;
+
+ for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) {
+ struct ospf_lsa *lsa_self;
+
+ lsa_self = ospf_lsa_lookup_by_id(area, OSPF_ROUTER_LSA,
+ ospf->router_id);
+ if (!lsa_self || !ospf_gr_check_adjs_lsa(area, lsa_self)) {
+ if (IS_DEBUG_OSPF_GR)
+ zlog_debug(
+ "GR: not all adjacencies were reestablished yet [area %pI4]",
+ &area->area_id);
+ return;
+ }
+ }
+
+ ospf_gr_restart_exit(ospf, "all adjacencies were reestablished");
+}
+
+/* Handling of grace period expiry. */
+static int ospf_gr_grace_period_expired(struct thread *thread)
+{
+ struct ospf *ospf = THREAD_ARG(thread);
+
+ ospf->gr_info.t_grace_period = NULL;
+ ospf_gr_restart_exit(ospf, "grace period has expired");
+
+ return 0;
+}
+
+/*
+ * Returns the path of the file (non-volatile memory) that contains GR status
+ * information.
+ */
+static char *ospf_gr_nvm_filepath(struct ospf *ospf)
+{
+ static char filepath[MAXPATHLEN];
+ char instance[16] = "";
+
+ if (ospf->instance)
+ snprintf(instance, sizeof(instance), "-%d", ospf->instance);
+ snprintf(filepath, sizeof(filepath), OSPFD_GR_STATE, instance);
+ return filepath;
+}
+
+/*
+ * 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)
+{
+ char *filepath;
+ const char *inst_name;
+ json_object *json;
+ json_object *json_instances;
+ json_object *json_instance;
+
+ filepath = ospf_gr_nvm_filepath(ospf);
+ inst_name = ospf->name ? ospf->name : VRF_DEFAULT_NAME;
+
+ json = json_object_from_file(filepath);
+ 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);
+ }
+
+ /*
+ * 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);
+
+ json_object_to_file_ext(filepath, json, JSON_C_TO_STRING_PRETTY);
+ json_object_free(json);
+}
+
+/*
+ * Delete GR status information about the given OSPF instance from non-volatile
+ * memory.
+ */
+static void ospf_gr_nvm_delete(struct ospf *ospf)
+{
+ char *filepath;
+ const char *inst_name;
+ json_object *json;
+ json_object *json_instances;
+
+ filepath = ospf_gr_nvm_filepath(ospf);
+ inst_name = ospf->name ? ospf->name : VRF_DEFAULT_NAME;
+
+ json = json_object_from_file(filepath);
+ 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(filepath, 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 ospf_gr_nvm_read(struct ospf *ospf)
+{
+ char *filepath;
+ const char *inst_name;
+ json_object *json;
+ json_object *json_instances;
+ json_object *json_instance;
+ json_object *json_timestamp;
+ time_t timestamp = 0;
+
+ filepath = ospf_gr_nvm_filepath(ospf);
+ inst_name = ospf->name ? ospf->name : VRF_DEFAULT_NAME;
+
+ json = json_object_from_file(filepath);
+ 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, "timestamp", &json_timestamp);
+ if (json_timestamp) {
+ time_t now;
+ unsigned long remaining_time;
+
+ /* 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);
+ thread_add_timer(master, ospf_gr_grace_period_expired,
+ ospf, remaining_time,
+ &ospf->gr_info.t_grace_period);
+ }
+ }
+
+ json_object_object_del(json_instances, inst_name);
+
+ json_object_to_file_ext(filepath, json, JSON_C_TO_STRING_PRETTY);
+ json_object_free(json);
+}
+
+/* Prepare to start a Graceful Restart. */
+static void ospf_gr_prepare(void)
+{
+ struct ospf *ospf;
+ struct ospf_interface *oi;
+ struct listnode *onode;
+
+ for (ALL_LIST_ELEMENTS_RO(om->ospf, onode, ospf)) {
+ struct listnode *inode;
+
+ if (!ospf->gr_info.restart_support
+ || ospf->gr_info.prepare_in_progress)
+ continue;
+
+ if (IS_DEBUG_OSPF_GR)
+ zlog_debug(
+ "GR: preparing to perform a graceful restart [period %u second(s)] [vrf %s]",
+ ospf->gr_info.grace_period,
+ ospf_vrf_id_to_name(ospf->vrf_id));
+
+ if (!CHECK_FLAG(ospf->config, OSPF_OPAQUE_CAPABLE)) {
+ zlog_warn(
+ "%s: failed to activate graceful restart: opaque capability not enabled",
+ __func__);
+ 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);
+
+ /* Record end of the grace period in non-volatile memory. */
+ ospf_gr_nvm_update(ospf);
+
+ /*
+ * Mark that a Graceful Restart preparation is in progress, to
+ * prevent ospfd from flushing its self-originated LSAs on exit.
+ */
+ ospf->gr_info.prepare_in_progress = true;
+ }
+}
+
+DEFPY(graceful_restart_prepare, graceful_restart_prepare_cmd,
+ "graceful-restart prepare ip ospf",
+ "Graceful Restart commands\n"
+ "Prepare upcoming graceful restart\n"
+ IP_STR
+ "Prepare to restart the OSPF process")
+{
+ ospf_gr_prepare();
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(graceful_restart, 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(ospf, ospf);
+
+ /* Check and get restart period if present. */
+ if (!grace_period_str)
+ grace_period = OSPF_DFLT_GRACE_INTERVAL;
+
+ ospf->gr_info.restart_support = true;
+ ospf->gr_info.grace_period = grace_period;
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(no_graceful_restart, 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(ospf, ospf);
+
+ if (!ospf->gr_info.restart_support)
+ return CMD_SUCCESS;
+
+ if (ospf->gr_info.prepare_in_progress) {
+ vty_out(vty,
+ "%% Error: Graceful Restart preparation in progress\n");
+ return CMD_WARNING;
+ }
+
+ ospf->gr_info.restart_support = false;
+ ospf->gr_info.grace_period = OSPF_DFLT_GRACE_INTERVAL;
+
+ return CMD_SUCCESS;
+}
+
+void ospf_gr_init(void)
+{
+ install_element(ENABLE_NODE, &graceful_restart_prepare_cmd);
+ install_element(OSPF_NODE, &graceful_restart_cmd);
+ install_element(OSPF_NODE, &no_graceful_restart_cmd);
+}
diff --git a/ospfd/ospf_gr_helper.h b/ospfd/ospf_gr.h
index bd6d1d746..496ba3c59 100644
--- a/ospfd/ospf_gr_helper.h
+++ b/ospfd/ospf_gr.h
@@ -21,8 +21,8 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#ifndef _ZEBRA_OSPF_GR_HELPER_H
-#define _ZEBRA_OSPF_GR_HELPER_H
+#ifndef _ZEBRA_OSPF_GR_H
+#define _ZEBRA_OSPF_GR_H
#define OSPF_GR_NOT_HELPER 0
#define OSPF_GR_ACTIVE_HELPER 1
@@ -32,6 +32,7 @@
#define OSPF_MAX_GRACE_INTERVAL 1800
#define OSPF_MIN_GRACE_INTERVAL 1
+#define OSPF_DFLT_GRACE_INTERVAL 120
enum ospf_helper_exit_reason {
OSPF_GR_HELPER_EXIT_NONE = 0,
@@ -55,7 +56,8 @@ enum ospf_gr_helper_rejected_reason {
OSPF_HELPER_NOT_A_VALID_NEIGHBOUR,
OSPF_HELPER_PLANNED_ONLY_RESTART,
OSPF_HELPER_TOPO_CHANGE_RTXMT_LIST,
- OSPF_HELPER_LSA_AGE_MORE
+ OSPF_HELPER_LSA_AGE_MORE,
+ OSPF_HELPER_RESTARTING,
};
/* Ref RFC3623 appendex-A */
@@ -101,7 +103,8 @@ struct ospf_helper_info {
/* Grace timer,This Router acts as
* helper until this timer until
- * this timer expires*/
+ * this timer expires.
+ */
struct thread *t_grace_timer;
/* Helper status */
@@ -178,4 +181,11 @@ 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);
-#endif /* _ZEBRA_OSPF_HELPER_H */
+
+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_init(void);
+
+#endif /* _ZEBRA_OSPF_GR_H */
diff --git a/ospfd/ospf_gr_helper.c b/ospfd/ospf_gr_helper.c
index b32318b83..11ad45d30 100644
--- a/ospfd/ospf_gr_helper.c
+++ b/ospfd/ospf_gr_helper.c
@@ -48,7 +48,7 @@
#include "ospfd/ospf_errors.h"
#include "ospfd/ospf_nsm.h"
#include "ospfd/ospf_ism.h"
-#include "ospfd/ospf_gr_helper.h"
+#include "ospfd/ospf_gr.h"
static const char * const ospf_exit_reason_desc[] = {
"Unknown reason",
@@ -72,6 +72,7 @@ static const char * const ospf_rejected_reason_desc[] = {
"Supports only planned restart but received unplanned",
"Topo change due to change in lsa rxmt list",
"LSA age is more than Grace interval",
+ "Router is in the process of graceful restart",
};
static void show_ospf_grace_lsa_info(struct vty *vty, struct ospf_lsa *lsa);
@@ -161,7 +162,7 @@ const char *ospf_rejected_reason2str(unsigned int reason)
*/
void ospf_gr_helper_instance_init(struct ospf *ospf)
{
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug("%s, GR Helper init.", __func__);
ospf->is_helper_supported = OSPF_GR_FALSE;
@@ -187,7 +188,7 @@ void ospf_gr_helper_instance_init(struct ospf *ospf)
*/
void ospf_gr_helper_instance_stop(struct ospf *ospf)
{
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug("%s, GR helper deinit.", __func__);
ospf_enable_rtr_hash_destroy(ospf);
@@ -203,7 +204,7 @@ void ospf_gr_helper_init(void)
{
int rc;
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug("%s, GR Helper init.", __func__);
rc = ospf_register_opaque_functab(
@@ -225,8 +226,7 @@ void ospf_gr_helper_init(void)
*/
void ospf_gr_helper_stop(void)
{
-
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug("%s, GR helper deinit.", __func__);
ospf_delete_opaque_functab(OSPF_OPAQUE_LINK_LSA, OPAQUE_TYPE_GRACE_LSA);
@@ -259,7 +259,7 @@ static int ospf_extract_grace_lsa_fields(struct ospf_lsa *lsa,
/* Check LSA len */
if (lsa->size <= OSPF_LSA_HEADER_SIZE) {
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug("%s: Malformed packet: Invalid LSA len:%d",
__func__, length);
return OSPF_GR_FAILURE;
@@ -272,7 +272,7 @@ static int ospf_extract_grace_lsa_fields(struct ospf_lsa *lsa,
/* Check TLV len against overall LSA */
if (sum + TLV_SIZE(tlvh) > length) {
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug("%s: Malformed packet: Invalid TLV len:%u",
__func__, TLV_SIZE(tlvh));
return OSPF_GR_FAILURE;
@@ -324,7 +324,7 @@ static int ospf_extract_grace_lsa_fields(struct ospf_lsa *lsa,
sum += TLV_SIZE(tlvh);
break;
default:
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug(
"%s, Malformed packet.Invalid TLV type:%d",
__func__, ntohs(tlvh->type));
@@ -391,12 +391,12 @@ int ospf_process_grace_lsa(struct ospf *ospf, struct ospf_lsa *lsa,
ret = ospf_extract_grace_lsa_fields(lsa, &grace_interval, &restart_addr,
&restart_reason);
if (ret != OSPF_GR_SUCCESS) {
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug("%s, Wrong Grace LSA packet.", __func__);
return OSPF_GR_NOT_HELPER;
}
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug(
"%s, Grace LSA received from %pI4, grace interval:%u, restart reason:%s",
__func__, &restart_addr, grace_interval,
@@ -410,7 +410,7 @@ int ospf_process_grace_lsa(struct ospf *ospf, struct ospf_lsa *lsa,
restarter = ospf_nbr_lookup_by_addr(oi->nbrs, &restart_addr);
if (!restarter) {
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug(
"%s, Restarter is not a nbr(%pI4) for this router.",
__func__, &restart_addr);
@@ -427,7 +427,7 @@ int ospf_process_grace_lsa(struct ospf *ospf, struct ospf_lsa *lsa,
lookup.advRtrAddr.s_addr = restarter->router_id.s_addr;
if (!hash_lookup(ospf->enable_rtr_list, &lookup)) {
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug(
"%s, HELPER support is disabled, So not a HELPER",
__func__);
@@ -442,7 +442,7 @@ int ospf_process_grace_lsa(struct ospf *ospf, struct ospf_lsa *lsa,
* became a adjacency.
*/
if (!IS_NBR_STATE_FULL(restarter)) {
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug(
"%s, This Neighbour %pI4 is not in FULL state.",
__func__, &restarter->src);
@@ -456,7 +456,7 @@ int ospf_process_grace_lsa(struct ospf *ospf, struct ospf_lsa *lsa,
*/
if (ospf->only_planned_restart
&& !OSPF_GR_IS_PLANNED_RESTART(restart_reason)) {
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug(
"%s, Router supports only planned restarts but received the GRACE LSA for an unplanned restart.",
__func__);
@@ -470,7 +470,7 @@ int ospf_process_grace_lsa(struct ospf *ospf, struct ospf_lsa *lsa,
*/
if (ospf->strict_lsa_check && !ospf_ls_retransmit_isempty(restarter)
&& ospf_check_change_in_rxmt_list(restarter)) {
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug(
"%s, Changed LSA in Rxmt list. So not Helper.",
__func__);
@@ -481,7 +481,7 @@ int ospf_process_grace_lsa(struct ospf *ospf, struct ospf_lsa *lsa,
/*LSA age must be less than the grace period */
if (ntohs(lsa->data->ls_age) >= grace_interval) {
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug(
"%s, Grace LSA age(%d) is more than the grace interval(%d)",
__func__, lsa->data->ls_age, grace_interval);
@@ -490,6 +490,16 @@ int ospf_process_grace_lsa(struct ospf *ospf, struct ospf_lsa *lsa,
return OSPF_GR_NOT_HELPER;
}
+ if (ospf->gr_info.restart_in_progress) {
+ if (IS_DEBUG_OSPF_GR)
+ zlog_debug(
+ "%s: router is in the process of graceful restart",
+ __func__);
+ restarter->gr_helper_info.rejected_reason =
+ OSPF_HELPER_RESTARTING;
+ return OSPF_GR_NOT_HELPER;
+ }
+
/* check supported grace period configured
* if configured, use this to start the grace
* timer otherwise use the interval received
@@ -497,7 +507,7 @@ int ospf_process_grace_lsa(struct ospf *ospf, struct ospf_lsa *lsa,
*/
actual_grace_interval = grace_interval;
if (grace_interval > ospf->supported_grace_time) {
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug(
"%s, Received grace period %d is larger than supported grace %d",
__func__, grace_interval,
@@ -512,12 +522,12 @@ int ospf_process_grace_lsa(struct ospf *ospf, struct ospf_lsa *lsa,
if (ospf->active_restarter_cnt > 0)
ospf->active_restarter_cnt--;
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug(
"%s, Router is already acting as a HELPER for this nbr,so restart the grace timer",
__func__);
} else {
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug(
"%s, This Router becomes a HELPER for the neighbour %pI4",
__func__, &restarter->src);
@@ -535,7 +545,7 @@ int ospf_process_grace_lsa(struct ospf *ospf, struct ospf_lsa *lsa,
/* Increment the active restarter count */
ospf->active_restarter_cnt++;
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug("%s, Grace timer started.interval:%d", __func__,
actual_grace_interval);
@@ -622,10 +632,9 @@ void ospf_helper_handle_topo_chg(struct ospf *ospf, struct ospf_lsa *lsa)
if (!ospf->strict_lsa_check)
return;
- if (IS_DEBUG_OSPF_GR_HELPER)
- zlog_debug(
- "%s, Topo change detected due to lsa LSID:%pI4 type:%d",
- __func__, &lsa->data->id, lsa->data->type);
+ if (IS_DEBUG_OSPF_GR)
+ zlog_debug("%s: Topo change detected due to LSA[%s]", __func__,
+ dump_lsa_key(lsa));
lsa->to_be_acknowledged = OSPF_GR_TRUE;
@@ -686,7 +695,7 @@ void ospf_gr_helper_exit(struct ospf_neighbor *nbr,
if (!OSPF_GR_IS_ACTIVE_HELPER(nbr))
return;
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug("%s, Exiting from HELPER support to %pI4, due to %s",
__func__, &nbr->src, ospf_exit_reason2str(reason));
@@ -717,7 +726,7 @@ void ospf_gr_helper_exit(struct ospf_neighbor *nbr,
* If no, bring down the neighbour.
*/
if (reason != OSPF_GR_HELPER_COMPLETED) {
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug(
"%s, Failed GR exit, so bringing down the neighbour",
__func__);
@@ -768,12 +777,12 @@ void ospf_process_maxage_grace_lsa(struct ospf *ospf, struct ospf_lsa *lsa,
ret = ospf_extract_grace_lsa_fields(lsa, &graceInterval, &restartAddr,
&restartReason);
if (ret != OSPF_GR_SUCCESS) {
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug("%s, Wrong Grace LSA packet.", __func__);
return;
}
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug("%s, GraceLSA received for neighbour %pI4", __func__,
&restartAddr);
@@ -785,7 +794,7 @@ void ospf_process_maxage_grace_lsa(struct ospf *ospf, struct ospf_lsa *lsa,
restarter = ospf_nbr_lookup_by_addr(oi->nbrs, &restartAddr);
if (!restarter) {
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug(
"%s, Restarter is not a neighbour for this router.",
__func__);
diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c
index c7c9fa2d6..9ef2a6520 100644
--- a/ospfd/ospf_lsa.c
+++ b/ospfd/ospf_lsa.c
@@ -819,6 +819,14 @@ static struct ospf_lsa *ospf_router_lsa_originate(struct ospf_area *area)
{
struct ospf_lsa *new;
+ if (area->ospf->gr_info.restart_in_progress) {
+ if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
+ zlog_debug(
+ "LSA[Type%d]: Graceful Restart in progress, don't originate",
+ OSPF_ROUTER_LSA);
+ return NULL;
+ }
+
/* Create new router-LSA instance. */
if ((new = ospf_router_lsa_new(area)) == NULL) {
zlog_err("%s: ospf_router_lsa_new returned NULL", __func__);
@@ -1045,6 +1053,14 @@ void ospf_network_lsa_update(struct ospf_interface *oi)
{
struct ospf_lsa *new;
+ if (oi->area->ospf->gr_info.restart_in_progress) {
+ if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
+ zlog_debug(
+ "LSA[Type%d]: Graceful Restart in progress, don't originate",
+ OSPF_NETWORK_LSA);
+ return;
+ }
+
if (oi->network_lsa_self != NULL) {
ospf_lsa_refresh(oi->ospf, oi->network_lsa_self);
return;
@@ -1212,6 +1228,14 @@ struct ospf_lsa *ospf_summary_lsa_originate(struct prefix_ipv4 *p,
struct ospf_lsa *new;
struct in_addr id;
+ if (area->ospf->gr_info.restart_in_progress) {
+ if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
+ zlog_debug(
+ "LSA[Type%d]: Graceful Restart in progress, don't originate",
+ OSPF_SUMMARY_LSA);
+ return NULL;
+ }
+
id = ospf_lsa_unique_id(area->ospf, area->lsdb, OSPF_SUMMARY_LSA, p);
if (id.s_addr == 0xffffffff) {
@@ -1353,6 +1377,14 @@ struct ospf_lsa *ospf_summary_asbr_lsa_originate(struct prefix_ipv4 *p,
struct ospf_lsa *new;
struct in_addr id;
+ if (area->ospf->gr_info.restart_in_progress) {
+ if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
+ zlog_debug(
+ "LSA[Type%d]: Graceful Restart in progress, don't originate",
+ OSPF_ASBR_SUMMARY_LSA);
+ return NULL;
+ }
+
id = ospf_lsa_unique_id(area->ospf, area->lsdb, OSPF_ASBR_SUMMARY_LSA,
p);
@@ -1799,6 +1831,13 @@ struct ospf_lsa *ospf_translated_nssa_originate(struct ospf *ospf,
struct ospf_lsa *new;
struct as_external_lsa *extnew;
+ if (ospf->gr_info.restart_in_progress) {
+ if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
+ zlog_debug(
+ "LSA[Translated Type5]: Graceful Restart in progress, don't originate");
+ return NULL;
+ }
+
/* we cant use ospf_external_lsa_originate() as we need to set
* the OSPF_LSA_LOCAL_XLT flag, must originate by hand
*/
@@ -1953,6 +1992,13 @@ struct ospf_lsa *ospf_external_lsa_originate(struct ospf *ospf,
{
struct ospf_lsa *new;
+ if (ospf->gr_info.restart_in_progress) {
+ if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
+ zlog_debug(
+ "LSA[Type5]: Graceful Restart in progress, don't originate");
+ return NULL;
+ }
+
/* Added for NSSA project....
External LSAs are originated in ASBRs as usual, but for NSSA
@@ -2779,8 +2825,8 @@ struct ospf_lsa *ospf_lsa_install(struct ospf *ospf, struct ospf_interface *oi,
*/
if (IS_LSA_MAXAGE(new)) {
if (IS_DEBUG_OSPF(lsa, LSA_INSTALL))
- zlog_debug("LSA[Type%d:%pI4]: Install LSA %p, MaxAge",
- new->data->type, &new->data->id, lsa);
+ zlog_debug("LSA[%s]: Install LSA %p, MaxAge",
+ dump_lsa_key(new), lsa);
ospf_lsa_maxage(ospf, lsa);
}
@@ -2862,9 +2908,8 @@ static int ospf_maxage_lsa_remover(struct thread *thread)
if (IS_DEBUG_OSPF(lsa, LSA_FLOODING))
zlog_debug(
- "LSA[Type%d:%pI4]: MaxAge LSA removed from list",
- lsa->data->type,
- &lsa->data->id);
+ "LSA[%s]: MaxAge LSA removed from list",
+ dump_lsa_key(lsa));
if (CHECK_FLAG(lsa->flags, OSPF_LSA_PREMATURE_AGE)) {
if (IS_DEBUG_OSPF(lsa, LSA_FLOODING))
@@ -2882,9 +2927,8 @@ static int ospf_maxage_lsa_remover(struct thread *thread)
*/
if (old != lsa) {
flog_err(EC_OSPF_LSA_MISSING,
- "%s: LSA[Type%d:%pI4]: LSA not in LSDB",
- __func__, lsa->data->type,
- &lsa->data->id);
+ "%s: LSA[%s]: LSA not in LSDB",
+ __func__, dump_lsa_key(lsa));
continue;
}
@@ -2893,9 +2937,8 @@ static int ospf_maxage_lsa_remover(struct thread *thread)
} else {
if (IS_DEBUG_OSPF(lsa, LSA_FLOODING))
zlog_debug(
- "%s: LSA[Type%d:%pI4]: No associated LSDB!",
- __func__, lsa->data->type,
- &lsa->data->id);
+ "%s: LSA[%s]: No associated LSDB!",
+ __func__, dump_lsa_key(lsa));
}
}
@@ -2952,9 +2995,8 @@ void ospf_lsa_maxage(struct ospf *ospf, struct ospf_lsa *lsa)
if (CHECK_FLAG(lsa->flags, OSPF_LSA_IN_MAXAGE)) {
if (IS_DEBUG_OSPF(lsa, LSA_FLOODING))
zlog_debug(
- "LSA[Type%d:%pI4]: %p already exists on MaxAge LSA list",
- lsa->data->type, &lsa->data->id,
- (void *)lsa);
+ "LSA[%s]: %p already exists on MaxAge LSA list",
+ dump_lsa_key(lsa), lsa);
return;
}
diff --git a/ospfd/ospf_main.c b/ospfd/ospf_main.c
index d94de1299..73d596b03 100644
--- a/ospfd/ospf_main.c
+++ b/ospfd/ospf_main.c
@@ -54,6 +54,7 @@
#include "ospfd/ospf_zebra.h"
#include "ospfd/ospf_vty.h"
#include "ospfd/ospf_bfd.h"
+#include "ospfd/ospf_gr.h"
#include "ospfd/ospf_errors.h"
#include "ospfd/ospf_ldp_sync.h"
#include "ospfd/ospf_routemap_nb.h"
@@ -225,6 +226,7 @@ int main(int argc, char **argv)
ospf_route_map_init();
ospf_opaque_init();
+ ospf_gr_init();
ospf_gr_helper_init();
/* OSPF errors init */
diff --git a/ospfd/ospf_neighbor.c b/ospfd/ospf_neighbor.c
index 8725497f2..98fb54d82 100644
--- a/ospfd/ospf_neighbor.c
+++ b/ospfd/ospf_neighbor.c
@@ -44,7 +44,7 @@
#include "ospfd/ospf_flood.h"
#include "ospfd/ospf_dump.h"
#include "ospfd/ospf_bfd.h"
-#include "ospfd/ospf_gr_helper.h"
+#include "ospfd/ospf_gr.h"
/* Fill in the the 'key' as appropriate to retrieve the entry for nbr
* from the ospf_interface's nbrs table. Indexed by interface address
diff --git a/ospfd/ospf_neighbor.h b/ospfd/ospf_neighbor.h
index 2ce6d6755..433b13615 100644
--- a/ospfd/ospf_neighbor.h
+++ b/ospfd/ospf_neighbor.h
@@ -22,7 +22,7 @@
#ifndef _ZEBRA_OSPF_NEIGHBOR_H
#define _ZEBRA_OSPF_NEIGHBOR_H
-#include <ospfd/ospf_gr_helper.h>
+#include <ospfd/ospf_gr.h>
#include <ospfd/ospf_packet.h>
/* Neighbor Data Structure */
diff --git a/ospfd/ospf_nsm.c b/ospfd/ospf_nsm.c
index b3b9244b2..892d264a2 100644
--- a/ospfd/ospf_nsm.c
+++ b/ospfd/ospf_nsm.c
@@ -49,6 +49,7 @@
#include "ospfd/ospf_flood.h"
#include "ospfd/ospf_abr.h"
#include "ospfd/ospf_bfd.h"
+#include "ospfd/ospf_gr.h"
#include "ospfd/ospf_errors.h"
DEFINE_HOOK(ospf_nsm_change,
@@ -75,7 +76,7 @@ static int ospf_inactivity_timer(struct thread *thread)
*/
if (!OSPF_GR_IS_ACTIVE_HELPER(nbr))
OSPF_NSM_EVENT_SCHEDULE(nbr, NSM_InactivityTimer);
- else if (IS_DEBUG_OSPF_GR_HELPER)
+ else if (IS_DEBUG_OSPF_GR)
zlog_debug(
"%s, Acting as HELPER for this neighbour, So inactivitytimer event will not be fired.",
__func__);
@@ -727,6 +728,9 @@ static void nsm_change_state(struct ospf_neighbor *nbr, int state)
ospf_network_lsa_update(oi);
}
}
+
+ if (state == NSM_Full && oi->ospf->gr_info.restart_in_progress)
+ ospf_gr_check_adjs(oi->ospf);
}
ospf_opaque_nsm_change(nbr, old_state);
diff --git a/ospfd/ospf_opaque.c b/ospfd/ospf_opaque.c
index fac2f9714..9ec2ed0aa 100644
--- a/ospfd/ospf_opaque.c
+++ b/ospfd/ospf_opaque.c
@@ -2147,6 +2147,11 @@ void ospf_opaque_self_originated_lsa_received(struct ospf_neighbor *nbr,
if ((top = oi_to_top(nbr->oi)) == NULL)
return;
+ if (IS_DEBUG_OSPF_EVENT)
+ zlog_debug(
+ "LSA[Type%d:%pI4]: processing self-originated Opaque-LSA",
+ lsa->data->type, &lsa->data->id);
+
/*
* Since these LSA entries are not yet installed into corresponding
* LSDB, just flush them without calling ospf_ls_maxage() afterward.
diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c
index 580eee13c..9930b0bd4 100644
--- a/ospfd/ospf_packet.c
+++ b/ospfd/ospf_packet.c
@@ -54,7 +54,7 @@
#include "ospfd/ospf_dump.h"
#include "ospfd/ospf_errors.h"
#include "ospfd/ospf_zebra.h"
-#include "ospfd/ospf_gr_helper.h"
+#include "ospfd/ospf_gr.h"
/*
* OSPF Fragmentation / fragmented writes
@@ -2025,9 +2025,11 @@ static void ospf_ls_upd(struct ospf *ospf, struct ip *iph,
ospf_ls_ack_send(nbr, lsa);
- ospf_opaque_self_originated_lsa_received(nbr,
- lsa);
- continue;
+ if (!ospf->gr_info.restart_in_progress) {
+ ospf_opaque_self_originated_lsa_received(
+ nbr, lsa);
+ continue;
+ }
}
}
@@ -2213,6 +2215,9 @@ static void ospf_ls_upd(struct ospf *ospf, struct ip *iph,
assert(listcount(lsas) == 0);
list_delete(&lsas);
+
+ if (ospf->gr_info.restart_in_progress)
+ ospf_gr_check_lsdb_consistency(oi->ospf, oi->area);
}
/* OSPF Link State Acknowledgment message read -- RFC2328 Section 13.7. */
@@ -3575,14 +3580,13 @@ static int ospf_make_ls_upd(struct ospf_interface *oi, struct list *update,
struct lsa_header *lsah;
uint16_t ls_age;
- if (IS_DEBUG_OSPF_EVENT)
- zlog_debug("ospf_make_ls_upd: List Iteration %d",
- count);
-
lsa = listgetdata(node);
-
assert(lsa->data);
+ if (IS_DEBUG_OSPF_EVENT)
+ zlog_debug("%s: List Iteration %d LSA[%s]", __func__,
+ count, dump_lsa_key(lsa));
+
/* Will it fit? Minimum it has to fit atleast one */
if ((length + delta + ntohs(lsa->data->length) > size_noauth) &&
(count > 0))
@@ -4264,7 +4268,7 @@ void ospf_ls_ack_send(struct ospf_neighbor *nbr, struct ospf_lsa *lsa)
struct ospf_interface *oi = nbr->oi;
if (IS_GRACE_LSA(lsa)) {
- if (IS_DEBUG_OSPF_GR_HELPER)
+ if (IS_DEBUG_OSPF_GR)
zlog_debug("%s, Sending GRACE ACK to Restarter.",
__func__);
}
diff --git a/ospfd/ospf_spf.c b/ospfd/ospf_spf.c
index 43d6ff44b..6a5144026 100644
--- a/ospfd/ospf_spf.c
+++ b/ospfd/ospf_spf.c
@@ -1903,6 +1903,8 @@ static int ospf_spf_calculate_schedule_worker(struct thread *thread)
strlcat(rbuf, "ASBR, ", sizeof(rbuf));
if (spf_reason_flags & (1 << SPF_FLAG_MAXAGE))
strlcat(rbuf, "M, ", sizeof(rbuf));
+ if (spf_reason_flags & (1 << SPF_FLAG_GR_FINISH))
+ strlcat(rbuf, "GR, ", sizeof(rbuf));
size_t rbuflen = strlen(rbuf);
if (rbuflen >= 2)
diff --git a/ospfd/ospf_spf.h b/ospfd/ospf_spf.h
index 4ff4a6d12..20f38440a 100644
--- a/ospfd/ospf_spf.h
+++ b/ospfd/ospf_spf.h
@@ -69,6 +69,7 @@ typedef enum {
SPF_FLAG_ABR_STATUS_CHANGE,
SPF_FLAG_ASBR_STATUS_CHANGE,
SPF_FLAG_CONFIG_CHANGE,
+ SPF_FLAG_GR_FINISH,
} ospf_spf_reason_t;
extern void ospf_spf_calculate_schedule(struct ospf *, ospf_spf_reason_t);
diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c
index 946d19cfd..2cb3ec089 100644
--- a/ospfd/ospf_vty.c
+++ b/ospfd/ospf_vty.c
@@ -12250,6 +12250,18 @@ static int ospf_cfg_write_helper_dis_rtr_walkcb(struct hash_bucket *bucket,
return HASHWALK_CONTINUE;
}
+static void config_write_ospf_gr(struct vty *vty, struct ospf *ospf)
+{
+ if (!ospf->gr_info.restart_support)
+ return;
+
+ if (ospf->gr_info.grace_period == OSPF_DFLT_GRACE_INTERVAL)
+ vty_out(vty, " graceful-restart\n");
+ else
+ vty_out(vty, " graceful-restart grace-period %u\n",
+ ospf->gr_info.grace_period);
+}
+
static int config_write_ospf_gr_helper(struct vty *vty, struct ospf *ospf)
{
if (ospf->is_helper_supported)
@@ -12464,7 +12476,8 @@ static int ospf_config_write_one(struct vty *vty, struct ospf *ospf)
/* Redistribute information print. */
config_write_ospf_redistribute(vty, ospf);
- /* Print gr helper configs */
+ /* Graceful Restart print */
+ config_write_ospf_gr(vty, ospf);
config_write_ospf_gr_helper(vty, ospf);
/* Print external route aggregation. */
diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c
index 387bbc0ce..1298a17f5 100644
--- a/ospfd/ospf_zebra.c
+++ b/ospfd/ospf_zebra.c
@@ -264,6 +264,14 @@ void ospf_zebra_add(struct ospf *ospf, struct prefix_ipv4 *p,
struct ospf_path *path;
struct listnode *node;
+ if (ospf->gr_info.restart_in_progress) {
+ if (IS_DEBUG_OSPF_GR)
+ zlog_debug(
+ "Zebra: Graceful Restart in progress -- not installing %pFX",
+ p);
+ return;
+ }
+
memset(&api, 0, sizeof(api));
api.vrf_id = ospf->vrf_id;
api.type = ZEBRA_ROUTE_OSPF;
@@ -323,6 +331,14 @@ void ospf_zebra_delete(struct ospf *ospf, struct prefix_ipv4 *p,
{
struct zapi_route api;
+ if (ospf->gr_info.restart_in_progress) {
+ if (IS_DEBUG_OSPF_GR)
+ zlog_debug(
+ "Zebra: Graceful Restart in progress -- not uninstalling %pFX",
+ p);
+ return;
+ }
+
memset(&api, 0, sizeof(api));
api.vrf_id = ospf->vrf_id;
api.type = ZEBRA_ROUTE_OSPF;
@@ -340,6 +356,14 @@ void ospf_zebra_add_discard(struct ospf *ospf, struct prefix_ipv4 *p)
{
struct zapi_route api;
+ if (ospf->gr_info.restart_in_progress) {
+ if (IS_DEBUG_OSPF_GR)
+ zlog_debug(
+ "Zebra: Graceful Restart in progress -- not installing %pFX",
+ p);
+ return;
+ }
+
memset(&api, 0, sizeof(api));
api.vrf_id = ospf->vrf_id;
api.type = ZEBRA_ROUTE_OSPF;
@@ -358,6 +382,14 @@ void ospf_zebra_delete_discard(struct ospf *ospf, struct prefix_ipv4 *p)
{
struct zapi_route api;
+ if (ospf->gr_info.restart_in_progress) {
+ if (IS_DEBUG_OSPF_GR)
+ zlog_debug(
+ "Zebra: Graceful Restart in progress -- not uninstalling %pFX",
+ p);
+ return;
+ }
+
memset(&api, 0, sizeof(api));
api.vrf_id = ospf->vrf_id;
api.type = ZEBRA_ROUTE_OSPF;
@@ -1180,6 +1212,36 @@ void ospf_routemap_unset(struct ospf_redist *red)
ROUTEMAP(red) = NULL;
}
+static int ospf_zebra_gr_update(struct ospf *ospf, int command,
+ uint32_t stale_time)
+{
+ struct zapi_cap api;
+
+ if (!zclient || zclient->sock < 0 || !ospf)
+ return 1;
+
+ memset(&api, 0, sizeof(struct zapi_cap));
+ api.cap = command;
+ api.stale_removal_time = stale_time;
+ api.vrf_id = ospf->vrf_id;
+
+ (void)zclient_capabilities_send(ZEBRA_CLIENT_CAPABILITIES, zclient,
+ &api);
+
+ return 0;
+}
+
+int ospf_zebra_gr_enable(struct ospf *ospf, uint32_t stale_time)
+{
+ return ospf_zebra_gr_update(ospf, ZEBRA_CLIENT_GR_CAPABILITIES,
+ stale_time);
+}
+
+int ospf_zebra_gr_disable(struct ospf *ospf)
+{
+ return ospf_zebra_gr_update(ospf, ZEBRA_CLIENT_GR_DISABLE, 0);
+}
+
/* Zebra route add and delete treatment. */
static int ospf_zebra_read_route(ZAPI_CALLBACK_ARGS)
{
diff --git a/ospfd/ospf_zebra.h b/ospfd/ospf_zebra.h
index 3f4edfa29..3bd8a7680 100644
--- a/ospfd/ospf_zebra.h
+++ b/ospfd/ospf_zebra.h
@@ -88,6 +88,8 @@ extern int ospf_distribute_list_out_set(struct ospf *, int, const char *);
extern int ospf_distribute_list_out_unset(struct ospf *, int, const char *);
extern void ospf_routemap_set(struct ospf_redist *, const char *);
extern void ospf_routemap_unset(struct ospf_redist *);
+extern int ospf_zebra_gr_enable(struct ospf *ospf, uint32_t stale_time);
+extern int ospf_zebra_gr_disable(struct ospf *ospf);
extern int ospf_distance_set(struct vty *, struct ospf *, const char *,
const char *, const char *);
extern int ospf_distance_unset(struct vty *, struct ospf *, const char *,
diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c
index e95ee55e6..3226d6644 100644
--- a/ospfd/ospfd.c
+++ b/ospfd/ospfd.c
@@ -60,7 +60,7 @@
#include "ospfd/ospf_flood.h"
#include "ospfd/ospf_ase.h"
#include "ospfd/ospf_ldp_sync.h"
-#include "ospfd/ospf_gr_helper.h"
+#include "ospfd/ospf_gr.h"
DEFINE_QOBJ_TYPE(ospf);
@@ -420,6 +420,12 @@ static struct ospf *ospf_new(unsigned short instance, const char *name)
thread_add_read(master, ospf_read, new, new->fd, &new->t_read);
+ /*
+ * Read from non-volatile memory whether this instance is performing a
+ * graceful restart or not.
+ */
+ ospf_gr_nvm_read(new);
+
return new;
}
@@ -708,7 +714,8 @@ static void ospf_finish_final(struct ospf *ospf)
ospf_opaque_finish();
- ospf_flush_self_originated_lsas_now(ospf);
+ if (!ospf->gr_info.prepare_in_progress)
+ ospf_flush_self_originated_lsas_now(ospf);
/* Unregister redistribution */
for (i = 0; i < ZEBRA_ROUTE_MAX; i++) {
@@ -805,6 +812,7 @@ static void ospf_finish_final(struct ospf *ospf)
OSPF_TIMER_OFF(ospf->t_sr_update);
OSPF_TIMER_OFF(ospf->t_default_routemap_timer);
OSPF_TIMER_OFF(ospf->t_external_aggr);
+ OSPF_TIMER_OFF(ospf->gr_info.t_grace_period);
LSDB_LOOP (OPAQUE_AS_LSDB(ospf), rn, lsa)
ospf_discard_from_db(ospf, ospf->lsdb, lsa);
@@ -826,7 +834,8 @@ static void ospf_finish_final(struct ospf *ospf)
if (ospf->old_table)
ospf_route_table_free(ospf->old_table);
if (ospf->new_table) {
- ospf_route_delete(ospf, ospf->new_table);
+ if (!ospf->gr_info.prepare_in_progress)
+ ospf_route_delete(ospf, ospf->new_table);
ospf_route_table_free(ospf->new_table);
}
if (ospf->old_rtrs)
@@ -834,11 +843,13 @@ static void ospf_finish_final(struct ospf *ospf)
if (ospf->new_rtrs)
ospf_rtrs_free(ospf->new_rtrs);
if (ospf->new_external_route) {
- ospf_route_delete(ospf, ospf->new_external_route);
+ if (!ospf->gr_info.prepare_in_progress)
+ ospf_route_delete(ospf, ospf->new_external_route);
ospf_route_table_free(ospf->new_external_route);
}
if (ospf->old_external_route) {
- ospf_route_delete(ospf, ospf->old_external_route);
+ if (!ospf->gr_info.prepare_in_progress)
+ ospf_route_delete(ospf, ospf->old_external_route);
ospf_route_table_free(ospf->old_external_route);
}
if (ospf->external_lsas) {
diff --git a/ospfd/ospfd.h b/ospfd/ospfd.h
index 318400e96..d64a044e1 100644
--- a/ospfd/ospfd.h
+++ b/ospfd/ospfd.h
@@ -134,6 +134,16 @@ enum protection_type {
OSPF_TI_LFA_NODE_PROTECTION,
};
+/* OSPF nonstop forwarding aka Graceful Restart */
+struct ospf_gr_info {
+ bool restart_support;
+ bool restart_in_progress;
+ bool prepare_in_progress;
+ bool finishing_restart;
+ uint32_t grace_period;
+ struct thread *t_grace_period;
+};
+
/* OSPF instance structure. */
struct ospf {
/* OSPF's running state based on the '[no] router ospf [<instance>]'
@@ -384,6 +394,9 @@ struct ospf {
/* MPLS LDP-IGP Sync */
struct ldp_sync_info_cmd ldp_sync_cmd;
+ /* OSPF Graceful Restart info */
+ struct ospf_gr_info gr_info;
+
/* TI-LFA support for all interfaces. */
bool ti_lfa_enabled;
enum protection_type ti_lfa_protection_type;
diff --git a/ospfd/subdir.am b/ospfd/subdir.am
index 574e0e3bd..2c4cc262c 100644
--- a/ospfd/subdir.am
+++ b/ospfd/subdir.am
@@ -8,6 +8,7 @@ sbin_PROGRAMS += ospfd/ospfd
vtysh_scan += \
ospfd/ospf_bfd.c \
ospfd/ospf_dump.c \
+ ospfd/ospf_gr.c \
ospfd/ospf_ldp_sync.c \
ospfd/ospf_opaque.c \
ospfd/ospf_ri.c \
@@ -35,6 +36,7 @@ ospfd_libfrrospf_a_SOURCES = \
ospfd/ospf_errors.c \
ospfd/ospf_ext.c \
ospfd/ospf_flood.c \
+ ospfd/ospf_gr.c \
ospfd/ospf_ia.c \
ospfd/ospf_interface.c \
ospfd/ospf_ism.c \
@@ -82,6 +84,7 @@ clippy_scan += \
ospfd/ospf_vty.c \
ospfd/ospf_ldp_sync.c \
ospfd/ospf_dump.c \
+ ospfd/ospf_gr.c \
# end
noinst_HEADERS += \
@@ -100,6 +103,7 @@ noinst_HEADERS += \
ospfd/ospf_network.h \
ospfd/ospf_packet.h \
ospfd/ospf_ri.h \
+ ospfd/ospf_gr.h \
ospfd/ospf_route.h \
ospfd/ospf_routemap_nb.h \
ospfd/ospf_spf.h \
@@ -108,7 +112,6 @@ noinst_HEADERS += \
ospfd/ospf_te.h \
ospfd/ospf_vty.h \
ospfd/ospf_zebra.h \
- ospfd/ospf_gr_helper.h \
# end
ospfd_ospfd_LDADD = ospfd/libfrrospf.a lib/libfrr.la $(LIBCAP) $(LIBM)