diff options
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_flood.c | 34 | ||||
-rw-r--r-- | ospfd/ospf_flood.h | 3 | ||||
-rw-r--r-- | ospfd/ospf_gr.c | 824 | ||||
-rw-r--r-- | ospfd/ospf_gr.h | 11 | ||||
-rw-r--r-- | ospfd/ospf_gr_helper.c | 11 | ||||
-rw-r--r-- | ospfd/ospf_lsa.c | 46 | ||||
-rw-r--r-- | ospfd/ospf_main.c | 2 | ||||
-rw-r--r-- | ospfd/ospf_nsm.c | 4 | ||||
-rw-r--r-- | ospfd/ospf_packet.c | 11 | ||||
-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 | 19 | ||||
-rw-r--r-- | ospfd/ospfd.h | 13 | ||||
-rw-r--r-- | ospfd/subdir.am | 3 |
19 files changed, 1064 insertions, 15 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_flood.c b/ospfd/ospf_flood.c index a52812b86..d11c441e3 100644 --- a/ospfd/ospf_flood.c +++ b/ospfd/ospf_flood.c @@ -379,8 +379,8 @@ 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)) { @@ -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,6 +1031,18 @@ 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 */ @@ -1041,11 +1053,21 @@ void ospf_lsa_flush_area(struct ospf_lsa *lsa, struct ospf_area *area) 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 */ 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.h b/ospfd/ospf_gr.h index 2106e0f5c..496ba3c59 100644 --- a/ospfd/ospf_gr.h +++ b/ospfd/ospf_gr.h @@ -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 */ @@ -179,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); + +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 7d0c229af..11ad45d30 100644 --- a/ospfd/ospf_gr_helper.c +++ b/ospfd/ospf_gr_helper.c @@ -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); @@ -489,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 diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c index b098581ce..723a54a77 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 */ @@ -1964,6 +2003,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 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_nsm.c b/ospfd/ospf_nsm.c index 0dc150852..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, @@ -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_packet.c b/ospfd/ospf_packet.c index 743db7002..9930b0bd4 100644 --- a/ospfd/ospf_packet.c +++ b/ospfd/ospf_packet.c @@ -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. */ 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 db8e961b8..b230e066c 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 017915e0e..de33c9732 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 a4b50e0c3..3226d6644 100644 --- a/ospfd/ospfd.c +++ b/ospfd/ospfd.c @@ -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 51254391f..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 += \ |