diff options
author | Russ White <russ@riw.us> | 2021-07-13 12:39:53 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-07-13 12:39:53 +0200 |
commit | 000df71ccdc2e81c4d6e7c123ad0db2cbcc4f05d (patch) | |
tree | 6fb9713c0171fa1dbaa2d327cba0d087399e42e8 /ospfd | |
parent | Merge pull request #8734 from imzyxwvu/paf-deactivate (diff) | |
parent | tests: add OSPF graceful restart topotest (diff) | |
download | frr-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.c | 6 | ||||
-rw-r--r-- | ospfd/ospf_ase.c | 10 | ||||
-rw-r--r-- | ospfd/ospf_dump.c | 27 | ||||
-rw-r--r-- | ospfd/ospf_dump.h | 5 | ||||
-rw-r--r-- | ospfd/ospf_flood.c | 49 | ||||
-rw-r--r-- | ospfd/ospf_flood.h | 3 | ||||
-rw-r--r-- | ospfd/ospf_gr.c | 824 | ||||
-rw-r--r-- | ospfd/ospf_gr.h (renamed from ospfd/ospf_gr_helper.h) | 20 | ||||
-rw-r--r-- | ospfd/ospf_gr_helper.c | 69 | ||||
-rw-r--r-- | ospfd/ospf_lsa.c | 70 | ||||
-rw-r--r-- | ospfd/ospf_main.c | 2 | ||||
-rw-r--r-- | ospfd/ospf_neighbor.c | 2 | ||||
-rw-r--r-- | ospfd/ospf_neighbor.h | 2 | ||||
-rw-r--r-- | ospfd/ospf_nsm.c | 6 | ||||
-rw-r--r-- | ospfd/ospf_opaque.c | 5 | ||||
-rw-r--r-- | ospfd/ospf_packet.c | 24 | ||||
-rw-r--r-- | ospfd/ospf_spf.c | 2 | ||||
-rw-r--r-- | ospfd/ospf_spf.h | 1 | ||||
-rw-r--r-- | ospfd/ospf_vty.c | 15 | ||||
-rw-r--r-- | ospfd/ospf_zebra.c | 62 | ||||
-rw-r--r-- | ospfd/ospf_zebra.h | 2 | ||||
-rw-r--r-- | ospfd/ospfd.c | 21 | ||||
-rw-r--r-- | ospfd/ospfd.h | 13 | ||||
-rw-r--r-- | ospfd/subdir.am | 5 |
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) |