]> git.puffer.fish Git - mirror/frr.git/commitdiff
ospf6d: introduce support for Graceful Restart (restarting mode)
authorRenato Westphal <renato@opensourcerouting.org>
Fri, 2 Jul 2021 16:01:32 +0000 (13:01 -0300)
committerRenato Westphal <renato@opensourcerouting.org>
Thu, 16 Sep 2021 15:26:48 +0000 (12:26 -0300)
RFC 5187 specifies the Graceful Restart enhancement to the OSPFv3
routing protocol. This commit implements support for the GR
restarting mode.

Here's a quick summary of how the GR restarting mode works:
* GR can be enabled on a per-instance basis using the `graceful-restart
  [grace-period (1-1800)]` command;
* To perform a graceful shutdown, the `graceful-restart prepare ipv6
  ospf` EXEC-level command needs to be issued before restarting the
  ospf6d daemon (there's no specific requirement on how the daemon
  should be restarted);
* `graceful-restart prepare ospf` will initiate the graceful restart
  for all GR-enabled instances by taking the following actions:
  o Flooding Grace-LSAs over all interfaces
  o Freezing the OSPF routes in the RIB
  o Saving the end of the grace period in non-volatile memory (a JSON
    file stored in `$frr_statedir`)
* Once ospf6d is started again, it will follow the procedures
  described in RFC 3623 until it detects it's time to exit the graceful
  restart (either successfully or unsuccessfully).

Testing done:
* New topotest featuring a multi-area OSPF topology (including stub
  and NSSA areas);
* Successful interop tests against IOS-XR routers acting as helpers.

Signed-off-by: Renato Westphal <renato@opensourcerouting.org>
21 files changed:
configure.ac
doc/user/ospf6d.rst
ospf6d/ospf6_asbr.c
ospf6d/ospf6_flood.c
ospf6d/ospf6_flood.h
ospf6d/ospf6_gr.c [new file with mode: 0644]
ospf6d/ospf6_gr.h
ospf6d/ospf6_gr_helper.c
ospf6d/ospf6_intra.c
ospf6d/ospf6_intra.h
ospf6d/ospf6_message.c
ospf6d/ospf6_neighbor.c
ospf6d/ospf6_neighbor.h
ospf6d/ospf6_spf.c
ospf6d/ospf6_spf.h
ospf6d/ospf6_top.c
ospf6d/ospf6_top.h
ospf6d/ospf6_zebra.c
ospf6d/ospf6_zebra.h
ospf6d/ospf6d.c
ospf6d/subdir.am

index 917e791182adcb20ec44db91bdb7ce2e817b4094..b00fdb23fc98e6c1103eb178201dafd452db25d1 100644 (file)
@@ -2519,6 +2519,7 @@ AC_DEFINE_UNQUOTED([LDPD_SOCKET], ["$frr_statedir%s%s/ldpd.sock"], [ldpd control
 AC_DEFINE_UNQUOTED([ZEBRA_SERV_PATH], ["$frr_statedir%s%s/zserv.api"], [zebra api socket])
 AC_DEFINE_UNQUOTED([BFDD_CONTROL_SOCKET], ["$frr_statedir%s%s/bfdd.sock"], [bfdd control socket])
 AC_DEFINE_UNQUOTED([OSPFD_GR_STATE], ["$frr_statedir%s/ospfd-gr.json"], [ospfd GR state information])
+AC_DEFINE_UNQUOTED([OSPF6D_GR_STATE], ["$frr_statedir/ospf6d-gr.json"], [ospf6d GR state information])
 AC_DEFINE_UNQUOTED([DAEMON_VTY_DIR], ["$frr_statedir%s%s"], [daemon vty directory])
 AC_DEFINE_UNQUOTED([DAEMON_DB_DIR], ["$frr_statedir"], [daemon database directory])
 
index 4fd86ffb139e9f64224e524ee86e4a2699cd22c4..ede2144107e42378c8998af2d9ff90fc507f6116 100644 (file)
@@ -264,8 +264,17 @@ Redistribute routes to OSPF6
    argument injects the default route regardless of it being present in the
    router. Metric values and route-map can also be specified optionally.
 
-Graceful Restart Helper
-=======================
+Graceful Restart
+================
+
+.. clicmd:: graceful-restart [grace-period (1-1800)]
+
+
+   Configure Graceful Restart (RFC 5187) restarting support.
+   When enabled, the default grace period is 120 seconds.
+
+   To perform a graceful shutdown, the "graceful-restart prepare ipv6 ospf"
+   EXEC-level command needs to be issued before restarting the ospf6d daemon.
 
 .. clicmd:: graceful-restart helper-only [A.B.C.D]
 
@@ -297,6 +306,16 @@ Graceful Restart Helper
    restarts. By default, it supports both planned and
    unplanned outages.
 
+.. clicmd:: graceful-restart prepare ipv6 ospf
+
+
+   Initiate a graceful restart for all OSPFv3 instances configured with the
+   "graceful-restart" command. The ospf6d daemon should be restarted during
+   the instance-specific grace period, otherwise the graceful restart will fail.
+
+   This is an EXEC-level command.
+
+
 .. _showing-ospf6-information:
 
 Showing OSPF6 information
index ff651d674b0d3219e1d947e4a64fbf7278280a6c..5a7023126dc80680b6a3e4584cae2b0f665450ee 100644 (file)
@@ -52,6 +52,7 @@
 #include "ospf6d.h"
 #include "ospf6_spf.h"
 #include "ospf6_nssa.h"
+#include "ospf6_gr.h"
 #include "lib/json.h"
 
 DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_EXTERNAL_INFO, "OSPF6 ext. info");
@@ -102,6 +103,13 @@ struct ospf6_lsa *ospf6_as_external_lsa_originate(struct ospf6_route *route,
        struct ospf6_as_external_lsa *as_external_lsa;
        caddr_t p;
 
+       if (ospf6->gr_info.restart_in_progress) {
+               if (IS_DEBUG_OSPF6_GR)
+                       zlog_debug(
+                               "Graceful Restart in progress, don't originate LSA");
+               return NULL;
+       }
+
        if (IS_OSPF6_DEBUG_ASBR || IS_OSPF6_DEBUG_ORIGINATE(AS_EXTERNAL))
                zlog_debug("Originate AS-External-LSA for %pFX",
                           &route->prefix);
index f13ed3e3bbf2fed816008c873c8f58f519fdbb24..186eac35a505d2a30c16a24360bc1add5d7b5666 100644 (file)
@@ -85,7 +85,7 @@ struct ospf6_lsdb *ospf6_get_scoped_lsdb_self(struct ospf6_lsa *lsa)
        return lsdb_self;
 }
 
-void ospf6_lsa_originate(struct ospf6_lsa *lsa)
+void ospf6_lsa_originate(struct ospf6 *ospf6, struct ospf6_lsa *lsa)
 {
        struct ospf6_lsa *old;
        struct ospf6_lsdb *lsdb_self;
@@ -106,7 +106,8 @@ void ospf6_lsa_originate(struct ospf6_lsa *lsa)
 
        /* if the new LSA does not differ from previous,
           suppress this update of the LSA */
-       if (old && !OSPF6_LSA_IS_DIFFER(lsa, old)) {
+       if (old && !OSPF6_LSA_IS_DIFFER(lsa, old)
+           && !ospf6->gr_info.finishing_restart) {
                if (IS_OSPF6_DEBUG_ORIGINATE_TYPE(lsa->header->type))
                        zlog_debug("Suppress updating LSA: %s", lsa->name);
                ospf6_lsa_delete(lsa);
@@ -134,20 +135,20 @@ void ospf6_lsa_originate(struct ospf6_lsa *lsa)
 void ospf6_lsa_originate_process(struct ospf6_lsa *lsa, struct ospf6 *process)
 {
        lsa->lsdb = process->lsdb;
-       ospf6_lsa_originate(lsa);
+       ospf6_lsa_originate(process, lsa);
 }
 
 void ospf6_lsa_originate_area(struct ospf6_lsa *lsa, struct ospf6_area *oa)
 {
        lsa->lsdb = oa->lsdb;
-       ospf6_lsa_originate(lsa);
+       ospf6_lsa_originate(oa->ospf6, lsa);
 }
 
 void ospf6_lsa_originate_interface(struct ospf6_lsa *lsa,
                                   struct ospf6_interface *oi)
 {
        lsa->lsdb = oi->lsdb;
-       ospf6_lsa_originate(lsa);
+       ospf6_lsa_originate(oi->area->ospf6, lsa);
 }
 
 void ospf6_remove_id_from_external_id_table(struct ospf6 *ospf6,
@@ -326,7 +327,8 @@ void ospf6_install_lsa(struct ospf6_lsa *lsa)
        lsa->installed = now;
 
        /* Topo change handling */
-       if (CHECK_LSA_TOPO_CHG_ELIGIBLE(ntohs(lsa->header->type))) {
+       if (CHECK_LSA_TOPO_CHG_ELIGIBLE(ntohs(lsa->header->type))
+           && !CHECK_FLAG(lsa->flag, OSPF6_LSA_DUPLICATE)) {
 
                /* check if it is new lsa ? or existing lsa got modified ?*/
                if (!old || OSPF6_LSA_IS_CHANGED(old, lsa)) {
@@ -991,6 +993,8 @@ void ospf6_receive_lsa(struct ospf6_neighbor *from,
 
        /* if no database copy or received is more recent */
        if (old == NULL || ismore_recent < 0) {
+               bool self_originated;
+
                /* in case we have no database copy */
                ismore_recent = -1;
 
@@ -1029,12 +1033,13 @@ void ospf6_receive_lsa(struct ospf6_neighbor *from,
                reoriginated instance of the LSA not to be rejected by other
                routers
                due to MinLSArrival. */
-               if (new->header->adv_router
-                   != from->ospf6_if->area->ospf6->router_id)
+               self_originated = (new->header->adv_router
+                                  == from->ospf6_if->area->ospf6->router_id);
+               if (!self_originated)
                        ospf6_flood(from, new);
 
-               /* Received Grace-LSA */
-               if (IS_GRACE_LSA(new)) {
+               /* Received non-self-originated Grace LSA. */
+               if (IS_GRACE_LSA(new) && !self_originated) {
                        struct ospf6 *ospf6;
 
                        ospf6 = ospf6_get_by_lsdb(new);
@@ -1088,8 +1093,16 @@ void ospf6_receive_lsa(struct ospf6_neighbor *from,
                ospf6_acknowledge_lsa(new, ismore_recent, from);
 
                /* (f) Self Originated LSA, section 13.4 */
-               if (new->header->adv_router
-                   == from->ospf6_if->area->ospf6->router_id) {
+               if (self_originated) {
+                       if (from->ospf6_if->area->ospf6->gr_info
+                                   .restart_in_progress) {
+                               if (IS_DEBUG_OSPF6_GR)
+                                       zlog_debug(
+                                               "Graceful Restart in progress -- not flushing self-originated LSA: %s",
+                                               new->name);
+                               return;
+                       }
+
                        /* Self-originated LSA (newer than ours) is received
                           from
                           another router. We have to make a new instance of the
@@ -1105,6 +1118,11 @@ void ospf6_receive_lsa(struct ospf6_neighbor *from,
                                         &new->refresh);
                }
 
+               struct ospf6 *ospf6 = from->ospf6_if->area->ospf6;
+               struct ospf6_area *area = from->ospf6_if->area;
+               if (ospf6->gr_info.restart_in_progress)
+                       ospf6_gr_check_lsdb_consistency(ospf6, area);
+
                return;
        }
 
index 4e4fc55ed4a501efa797c518c948d1b4706aae92..775d0d289d34ed0e3a5b54fcab355c1e6cbffe9b 100644 (file)
@@ -32,7 +32,7 @@ extern struct ospf6_lsdb *ospf6_get_scoped_lsdb(struct ospf6_lsa *lsa);
 extern struct ospf6_lsdb *ospf6_get_scoped_lsdb_self(struct ospf6_lsa *lsa);
 
 /* origination & purging */
-extern void ospf6_lsa_originate(struct ospf6_lsa *lsa);
+extern void ospf6_lsa_originate(struct ospf6 *ospf6, struct ospf6_lsa *lsa);
 extern void ospf6_lsa_originate_process(struct ospf6_lsa *lsa,
                                        struct ospf6 *process);
 extern void ospf6_lsa_originate_area(struct ospf6_lsa *lsa,
diff --git a/ospf6d/ospf6_gr.c b/ospf6d/ospf6_gr.c
new file mode 100644 (file)
index 0000000..c1d345d
--- /dev/null
@@ -0,0 +1,746 @@
+/*
+ * This is an implementation of RFC 5187 Graceful Restart.
+ *
+ * Copyright 2021 NetDEF (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 "hook.h"
+#include "printfrr.h"
+
+#include "ospf6d/ospf6_lsa.h"
+#include "ospf6d/ospf6_lsdb.h"
+#include "ospf6d/ospf6_route.h"
+#include "ospf6d/ospf6_area.h"
+#include "ospf6d/ospf6_interface.h"
+#include "ospf6d/ospf6d.h"
+#include "ospf6d/ospf6_asbr.h"
+#include "ospf6d/ospf6_zebra.h"
+#include "ospf6d/ospf6_message.h"
+#include "ospf6d/ospf6_neighbor.h"
+#include "ospf6d/ospf6_flood.h"
+#include "ospf6d/ospf6_intra.h"
+#include "ospf6d/ospf6_spf.h"
+#include "ospf6d/ospf6_gr.h"
+#ifndef VTYSH_EXTRACT_PL
+#include "ospf6d/ospf6_gr_clippy.c"
+#endif
+
+static void ospf6_gr_nvm_delete(struct ospf6 *ospf6);
+
+/* Originate and install Grace-LSA for a given interface. */
+static int ospf6_gr_lsa_originate(struct ospf6_interface *oi)
+{
+       struct ospf6_gr_info *gr_info = &oi->area->ospf6->gr_info;
+       struct ospf6_lsa_header *lsa_header;
+       struct ospf6_grace_lsa *grace_lsa;
+       struct ospf6_lsa *lsa;
+       char buffer[OSPF6_MAX_LSASIZE];
+
+       if (IS_OSPF6_DEBUG_ORIGINATE(LINK))
+               zlog_debug("Originate Link-LSA for Interface %s",
+                          oi->interface->name);
+
+       /* prepare buffer */
+       memset(buffer, 0, sizeof(buffer));
+       lsa_header = (struct ospf6_lsa_header *)buffer;
+       grace_lsa =
+               (struct ospf6_grace_lsa *)((caddr_t)lsa_header
+                                          + sizeof(struct ospf6_lsa_header));
+
+       /* Put grace period. */
+       grace_lsa->tlv_period.header.type = htons(GRACE_PERIOD_TYPE);
+       grace_lsa->tlv_period.header.length = htons(GRACE_PERIOD_LENGTH);
+       grace_lsa->tlv_period.interval = htonl(gr_info->grace_period);
+
+       /* Put restart reason. */
+       grace_lsa->tlv_reason.header.type = htons(RESTART_REASON_TYPE);
+       grace_lsa->tlv_reason.header.length = htons(RESTART_REASON_LENGTH);
+       if (gr_info->restart_support)
+               grace_lsa->tlv_reason.reason = OSPF6_GR_SW_RESTART;
+       else
+               grace_lsa->tlv_reason.reason = OSPF6_GR_UNKNOWN_RESTART;
+
+       /* Fill LSA Header */
+       lsa_header->age = 0;
+       lsa_header->type = htons(OSPF6_LSTYPE_GRACE_LSA);
+       lsa_header->id = htonl(oi->interface->ifindex);
+       lsa_header->adv_router = oi->area->ospf6->router_id;
+       lsa_header->seqnum =
+               ospf6_new_ls_seqnum(lsa_header->type, lsa_header->id,
+                                   lsa_header->adv_router, oi->lsdb);
+       lsa_header->length = htons(sizeof(*lsa_header) + sizeof(*grace_lsa));
+
+       /* LSA checksum */
+       ospf6_lsa_checksum(lsa_header);
+
+       /* create LSA */
+       lsa = ospf6_lsa_create(lsa_header);
+
+       /* Originate */
+       ospf6_lsa_originate_interface(lsa, oi);
+
+       return 0;
+}
+
+/* Flush all self-originated Grace-LSAs. */
+static void ospf6_gr_flush_grace_lsas(struct ospf6 *ospf6)
+{
+       struct ospf6_area *area;
+       struct listnode *anode;
+
+       for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, anode, area)) {
+               struct ospf6_lsa *lsa;
+               struct ospf6_interface *oi;
+               struct listnode *inode;
+
+               if (IS_DEBUG_OSPF6_GR)
+                       zlog_debug(
+                               "GR: flushing self-originated Grace-LSAs [area %pI4]",
+                               &area->area_id);
+
+               for (ALL_LIST_ELEMENTS_RO(area->if_list, inode, oi)) {
+                       lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_GRACE_LSA),
+                                               htonl(oi->interface->ifindex),
+                                               oi->area->ospf6->router_id,
+                                               oi->lsdb);
+                       if (!lsa) {
+                               zlog_warn(
+                                       "%s: Grace-LSA not found [interface %s] [area %pI4]",
+                                       __func__, oi->interface->name,
+                                       &area->area_id);
+                               continue;
+                       }
+
+                       ospf6_lsa_purge(lsa);
+               }
+       }
+}
+
+/* Exit from the Graceful Restart mode. */
+static void ospf6_gr_restart_exit(struct ospf6 *ospf6, const char *reason)
+{
+       struct ospf6_area *area;
+       struct listnode *onode, *anode;
+
+       if (IS_DEBUG_OSPF6_GR)
+               zlog_debug("GR: exiting graceful restart: %s", reason);
+
+       ospf6->gr_info.restart_in_progress = false;
+       ospf6->gr_info.finishing_restart = true;
+       THREAD_OFF(ospf6->gr_info.t_grace_period);
+
+       /* Record in non-volatile memory that the restart is complete. */
+       ospf6_gr_nvm_delete(ospf6);
+
+       for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, onode, area)) {
+               struct ospf6_interface *oi;
+
+               /*
+                * 1) The router should reoriginate its router-LSAs for all
+                *    attached areas in order to make sure they have the correct
+                *    contents.
+                */
+               OSPF6_ROUTER_LSA_EXECUTE(area);
+
+               for (ALL_LIST_ELEMENTS_RO(area->if_list, anode, oi)) {
+                       OSPF6_LINK_LSA_EXECUTE(oi);
+
+                       /*
+                        * 2) The router should reoriginate network-LSAs on all
+                        * segments where it is the Designated Router.
+                        */
+                       if (oi->state == OSPF6_INTERFACE_DR)
+                               OSPF6_NETWORK_LSA_EXECUTE(oi);
+               }
+       }
+
+       /*
+        * 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.
+        */
+       ospf6_spf_schedule(ospf6, OSPF6_SPF_FLAGS_GR_FINISH);
+
+       /* 6) Any grace-LSAs that the router originated should be flushed. */
+       ospf6_gr_flush_grace_lsas(ospf6);
+}
+
+#define RTR_LSA_MISSING 0
+#define RTR_LSA_ADJ_FOUND 1
+#define RTR_LSA_ADJ_NOT_FOUND 2
+
+/* Check if a Router-LSA exists and if it contains a given link. */
+static int ospf6_router_lsa_contains_adj(struct ospf6_area *area,
+                                        in_addr_t adv_router,
+                                        in_addr_t neighbor_router_id)
+{
+       uint16_t type;
+       struct ospf6_lsa *lsa;
+       bool empty = true;
+
+       type = ntohs(OSPF6_LSTYPE_ROUTER);
+       for (ALL_LSDB_TYPED_ADVRTR(area->lsdb, type, adv_router, lsa)) {
+               struct ospf6_router_lsa *router_lsa;
+               char *start, *end, *current;
+
+               empty = false;
+               router_lsa = (struct ospf6_router_lsa
+                                     *)((char *)lsa->header
+                                        + sizeof(struct ospf6_lsa_header));
+
+               /* Iterate over all interfaces in the Router-LSA. */
+               start = (char *)router_lsa + sizeof(struct ospf6_router_lsa);
+               end = (char *)lsa->header + ntohs(lsa->header->length);
+               for (current = start;
+                    current + sizeof(struct ospf6_router_lsdesc) <= end;
+                    current += sizeof(struct ospf6_router_lsdesc)) {
+                       struct ospf6_router_lsdesc *lsdesc;
+
+                       lsdesc = (struct ospf6_router_lsdesc *)current;
+                       if (lsdesc->type != OSPF6_ROUTER_LSDESC_POINTTOPOINT)
+                               continue;
+
+                       if (lsdesc->neighbor_router_id == neighbor_router_id)
+                               return RTR_LSA_ADJ_FOUND;
+               }
+       }
+
+       if (empty)
+               return RTR_LSA_MISSING;
+
+       return RTR_LSA_ADJ_NOT_FOUND;
+}
+
+static bool ospf6_gr_check_router_lsa_consistency(struct ospf6 *ospf6,
+                                                 struct ospf6_area *area,
+                                                 struct ospf6_lsa *lsa)
+{
+       if (lsa->header->adv_router == ospf6->router_id) {
+               struct ospf6_router_lsa *router_lsa;
+               char *start, *end, *current;
+
+               router_lsa = (struct ospf6_router_lsa
+                                     *)((char *)lsa->header
+                                        + sizeof(struct ospf6_lsa_header));
+
+               /* Iterate over all interfaces in the Router-LSA. */
+               start = (char *)router_lsa + sizeof(struct ospf6_router_lsa);
+               end = (char *)lsa->header + ntohs(lsa->header->length);
+               for (current = start;
+                    current + sizeof(struct ospf6_router_lsdesc) <= end;
+                    current += sizeof(struct ospf6_router_lsdesc)) {
+                       struct ospf6_router_lsdesc *lsdesc;
+
+                       lsdesc = (struct ospf6_router_lsdesc *)current;
+                       if (lsdesc->type != OSPF6_ROUTER_LSDESC_POINTTOPOINT)
+                               continue;
+
+                       if (ospf6_router_lsa_contains_adj(
+                                   area, lsdesc->neighbor_router_id,
+                                   ospf6->router_id)
+                           == RTR_LSA_ADJ_NOT_FOUND)
+                               return false;
+               }
+       } else {
+               int adj1, adj2;
+
+               adj1 = ospf6_router_lsa_contains_adj(area, ospf6->router_id,
+                                                    lsa->header->adv_router);
+               adj2 = ospf6_router_lsa_contains_adj(
+                       area, lsa->header->adv_router, ospf6->router_id);
+               if ((adj1 == RTR_LSA_ADJ_FOUND && adj2 == RTR_LSA_ADJ_NOT_FOUND)
+                   || (adj1 == RTR_LSA_ADJ_NOT_FOUND
+                       && adj2 == RTR_LSA_ADJ_FOUND))
+                       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 ospf6_gr_check_lsdb_consistency(struct ospf6 *ospf6,
+                                    struct ospf6_area *area)
+{
+       uint16_t type;
+       struct ospf6_lsa *lsa;
+
+       type = ntohs(OSPF6_LSTYPE_ROUTER);
+       for (ALL_LSDB_TYPED(area->lsdb, type, lsa)) {
+               if (!ospf6_gr_check_router_lsa_consistency(ospf6, area, lsa)) {
+                       char reason[256];
+
+                       snprintfrr(reason, sizeof(reason),
+                                  "detected inconsistent LSA %s [area %pI4]",
+                                  lsa->name, &area->area_id);
+                       ospf6_gr_restart_exit(ospf6, reason);
+                       return;
+               }
+       }
+}
+
+/* Check if there's a fully formed adjacency with the given neighbor ID. */
+static bool ospf6_gr_check_adj_id(struct ospf6_area *area,
+                                 in_addr_t neighbor_router_id)
+{
+       struct ospf6_neighbor *nbr;
+
+       nbr = ospf6_area_neighbor_lookup(area, neighbor_router_id);
+       if (!nbr || nbr->state < OSPF6_NEIGHBOR_FULL) {
+               if (IS_DEBUG_OSPF6_GR)
+                       zlog_debug("GR: missing adjacency to router %pI4",
+                                  &neighbor_router_id);
+               return false;
+       }
+
+       return true;
+}
+
+static bool ospf6_gr_check_adjs_lsa_transit(struct ospf6_area *area,
+                                           in_addr_t neighbor_router_id,
+                                           uint32_t neighbor_interface_id)
+{
+       struct ospf6 *ospf6 = area->ospf6;
+
+       /* Check if we are the DR. */
+       if (neighbor_router_id == ospf6->router_id) {
+               struct ospf6_lsa *lsa;
+               char *start, *end, *current;
+               struct ospf6_network_lsa *network_lsa;
+               struct ospf6_network_lsdesc *lsdesc;
+
+               /* Lookup Network LSA corresponding to this interface. */
+               lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_NETWORK),
+                                       neighbor_interface_id,
+                                       neighbor_router_id, area->lsdb);
+               if (!lsa)
+                       return false;
+
+               /* Iterate over all routers present in the network. */
+               network_lsa = (struct ospf6_network_lsa
+                                      *)((char *)lsa->header
+                                         + sizeof(struct ospf6_lsa_header));
+               start = (char *)network_lsa + sizeof(struct ospf6_network_lsa);
+               end = (char *)lsa->header + ntohs(lsa->header->length);
+               for (current = start;
+                    current + sizeof(struct ospf6_network_lsdesc) <= end;
+                    current += sizeof(struct ospf6_network_lsdesc)) {
+                       lsdesc = (struct ospf6_network_lsdesc *)current;
+
+                       /* Skip self in the pseudonode. */
+                       if (lsdesc->router_id == ospf6->router_id)
+                               continue;
+
+                       /*
+                        * Check if there's a fully formed adjacency with this
+                        * router.
+                        */
+                       if (!ospf6_gr_check_adj_id(area, lsdesc->router_id))
+                               return false;
+               }
+       } else {
+               struct ospf6_neighbor *nbr;
+
+               /* Check if there's a fully formed adjacency with the DR. */
+               nbr = ospf6_area_neighbor_lookup(area, neighbor_router_id);
+               if (!nbr || nbr->state < OSPF6_NEIGHBOR_FULL) {
+                       if (IS_DEBUG_OSPF6_GR)
+                               zlog_debug(
+                                       "GR: missing adjacency to DR router %pI4",
+                                       &neighbor_router_id);
+                       return false;
+               }
+       }
+
+       return true;
+}
+
+static bool ospf6_gr_check_adjs_lsa(struct ospf6_area *area,
+                                   struct ospf6_lsa *lsa)
+{
+       struct ospf6_router_lsa *router_lsa;
+       char *start, *end, *current;
+
+       router_lsa =
+               (struct ospf6_router_lsa *)((char *)lsa->header
+                                           + sizeof(struct ospf6_lsa_header));
+
+       /* Iterate over all interfaces in the Router-LSA. */
+       start = (char *)router_lsa + sizeof(struct ospf6_router_lsa);
+       end = (char *)lsa->header + ntohs(lsa->header->length);
+       for (current = start;
+            current + sizeof(struct ospf6_router_lsdesc) <= end;
+            current += sizeof(struct ospf6_router_lsdesc)) {
+               struct ospf6_router_lsdesc *lsdesc;
+
+               lsdesc = (struct ospf6_router_lsdesc *)current;
+               switch (lsdesc->type) {
+               case OSPF6_ROUTER_LSDESC_POINTTOPOINT:
+                       if (!ospf6_gr_check_adj_id(area,
+                                                  lsdesc->neighbor_router_id))
+                               return false;
+                       break;
+               case OSPF6_ROUTER_LSDESC_TRANSIT_NETWORK:
+                       if (!ospf6_gr_check_adjs_lsa_transit(
+                                   area, lsdesc->neighbor_router_id,
+                                   lsdesc->neighbor_interface_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.
+ */
+static bool ospf6_gr_check_adjs(struct ospf6 *ospf6)
+{
+       struct ospf6_area *area;
+       struct listnode *node;
+
+       for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, area)) {
+               uint16_t type;
+               uint32_t router;
+               struct ospf6_lsa *lsa_self;
+               bool found = false;
+
+               type = ntohs(OSPF6_LSTYPE_ROUTER);
+               router = ospf6->router_id;
+               for (ALL_LSDB_TYPED_ADVRTR(area->lsdb, type, router,
+                                          lsa_self)) {
+                       found = true;
+                       if (!ospf6_gr_check_adjs_lsa(area, lsa_self))
+                               return false;
+               }
+               if (!found)
+                       return false;
+       }
+
+       return true;
+}
+
+/* Handling of grace period expiry. */
+static int ospf6_gr_grace_period_expired(struct thread *thread)
+{
+       struct ospf6 *ospf6 = THREAD_ARG(thread);
+
+       ospf6->gr_info.t_grace_period = NULL;
+       ospf6_gr_restart_exit(ospf6, "grace period has expired");
+
+       return 0;
+}
+
+/*
+ * Record in non-volatile memory that the given OSPF instance is attempting to
+ * perform a graceful restart.
+ */
+static void ospf6_gr_nvm_update(struct ospf6 *ospf6)
+{
+       const char *inst_name;
+       json_object *json;
+       json_object *json_instances;
+       json_object *json_instance;
+
+       inst_name = ospf6->name ? ospf6->name : VRF_DEFAULT_NAME;
+
+       json = json_object_from_file(OSPF6D_GR_STATE);
+       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 ospf6d is
+        * restarted, it will be possible to take into account the time that
+        * passed while ospf6d wasn't running.
+        */
+       json_object_int_add(json_instance, "gracePeriod",
+                           ospf6->gr_info.grace_period);
+       json_object_int_add(json_instance, "timestamp",
+                           time(NULL) + ospf6->gr_info.grace_period);
+
+       json_object_to_file_ext(OSPF6D_GR_STATE, 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 ospf6_gr_nvm_delete(struct ospf6 *ospf6)
+{
+       const char *inst_name;
+       json_object *json;
+       json_object *json_instances;
+
+       inst_name = ospf6->name ? ospf6->name : VRF_DEFAULT_NAME;
+
+       json = json_object_from_file(OSPF6D_GR_STATE);
+       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(OSPF6D_GR_STATE, 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 ospf6_gr_nvm_read(struct ospf6 *ospf6)
+{
+       const char *inst_name;
+       json_object *json;
+       json_object *json_instances;
+       json_object *json_instance;
+       json_object *json_timestamp;
+       time_t timestamp = 0;
+
+       inst_name = ospf6->name ? ospf6->name : VRF_DEFAULT_NAME;
+
+       json = json_object_from_file(OSPF6D_GR_STATE);
+       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) {
+                       ospf6_gr_restart_exit(
+                               ospf6, "grace period has expired already");
+               } else {
+                       /* Schedule grace period timeout. */
+                       ospf6->gr_info.restart_in_progress = true;
+                       remaining_time = timestamp - time(NULL);
+                       if (IS_DEBUG_OSPF6_GR)
+                               zlog_debug(
+                                       "GR: remaining time until grace period expires: %lu(s)",
+                                       remaining_time);
+                       thread_add_timer(master, ospf6_gr_grace_period_expired,
+                                        ospf6, remaining_time,
+                                        &ospf6->gr_info.t_grace_period);
+               }
+       }
+
+       json_object_object_del(json_instances, inst_name);
+
+       json_object_to_file_ext(OSPF6D_GR_STATE, json, JSON_C_TO_STRING_PRETTY);
+       json_object_free(json);
+}
+
+/* Prepare to start a Graceful Restart. */
+static void ospf6_gr_prepare(void)
+{
+       struct ospf6 *ospf6;
+       struct ospf6_interface *oi;
+       struct listnode *onode, *anode, *inode;
+
+       for (ALL_LIST_ELEMENTS_RO(om6->ospf6, onode, ospf6)) {
+               struct ospf6_area *area;
+
+               if (!ospf6->gr_info.restart_support
+                   || ospf6->gr_info.prepare_in_progress)
+                       continue;
+
+               if (IS_DEBUG_OSPF6_GR)
+                       zlog_debug(
+                               "GR: preparing to perform a graceful restart [period %u second(s)] [vrf %s]",
+                               ospf6->gr_info.grace_period,
+                               ospf6_vrf_id_to_name(ospf6->vrf_id));
+
+               /* Freeze OSPF routes in the RIB. */
+               if (ospf6_zebra_gr_enable(ospf6, ospf6->gr_info.grace_period)) {
+                       zlog_warn(
+                               "%s: failed to activate graceful restart: not connected to zebra",
+                               __func__);
+                       continue;
+               }
+
+               /* Send a Grace-LSA to all neighbors. */
+               for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, anode, area)) {
+                       for (ALL_LIST_ELEMENTS_RO(area->if_list, inode, oi)) {
+                               if (oi->state < OSPF6_INTERFACE_POINTTOPOINT)
+                                       continue;
+                               ospf6_gr_lsa_originate(oi);
+                       }
+               }
+
+               /* Record end of the grace period in non-volatile memory. */
+               ospf6_gr_nvm_update(ospf6);
+
+               /*
+                * Mark that a Graceful Restart preparation is in progress, to
+                * prevent ospf6d from flushing its self-originated LSAs on
+                * exit.
+                */
+               ospf6->gr_info.prepare_in_progress = true;
+       }
+}
+
+static int ospf6_gr_neighbor_change(struct ospf6_neighbor *on, int next_state,
+                                   int prev_state)
+{
+       struct ospf6 *ospf6 = on->ospf6_if->area->ospf6;
+
+       if (next_state == OSPF6_NEIGHBOR_FULL
+           && ospf6->gr_info.restart_in_progress) {
+               if (ospf6_gr_check_adjs(ospf6)) {
+                       ospf6_gr_restart_exit(
+                               ospf6, "all adjacencies were reestablished");
+               } else {
+                       if (IS_DEBUG_OSPF6_GR)
+                               zlog_debug(
+                                       "GR: not all adjacencies were reestablished yet");
+               }
+       }
+
+       return 0;
+}
+
+int config_write_ospf6_gr(struct vty *vty, struct ospf6 *ospf6)
+{
+       if (!ospf6->gr_info.restart_support)
+               return 0;
+
+       if (ospf6->gr_info.grace_period == OSPF6_DFLT_GRACE_INTERVAL)
+               vty_out(vty, " graceful-restart\n");
+       else
+               vty_out(vty, " graceful-restart grace-period %u\n",
+                       ospf6->gr_info.grace_period);
+
+       return 0;
+}
+
+DEFPY(ospf6_graceful_restart_prepare, ospf6_graceful_restart_prepare_cmd,
+      "graceful-restart prepare ipv6 ospf",
+      "Graceful Restart commands\n"
+      "Prepare upcoming graceful restart\n" IPV6_STR
+      "Prepare to restart the OSPFv3 process")
+{
+       ospf6_gr_prepare();
+
+       return CMD_SUCCESS;
+}
+
+DEFPY(ospf6_graceful_restart, ospf6_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(ospf6, ospf6);
+
+       /* Check and get restart period if present. */
+       if (!grace_period_str)
+               grace_period = OSPF6_DFLT_GRACE_INTERVAL;
+
+       ospf6->gr_info.restart_support = true;
+       ospf6->gr_info.grace_period = grace_period;
+
+       return CMD_SUCCESS;
+}
+
+DEFPY(ospf6_no_graceful_restart, ospf6_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(ospf6, ospf6);
+
+       if (!ospf6->gr_info.restart_support)
+               return CMD_SUCCESS;
+
+       if (ospf6->gr_info.prepare_in_progress) {
+               vty_out(vty,
+                       "%% Error: Graceful Restart preparation in progress\n");
+               return CMD_WARNING;
+       }
+
+       ospf6->gr_info.restart_support = false;
+       ospf6->gr_info.grace_period = OSPF6_DFLT_GRACE_INTERVAL;
+
+       return CMD_SUCCESS;
+}
+
+void ospf6_gr_init(void)
+{
+       hook_register(ospf6_neighbor_change, ospf6_gr_neighbor_change);
+
+       install_element(ENABLE_NODE, &ospf6_graceful_restart_prepare_cmd);
+       install_element(OSPF6_NODE, &ospf6_graceful_restart_cmd);
+       install_element(OSPF6_NODE, &ospf6_no_graceful_restart_cmd);
+}
index 378b7193cdba041b4d3c3924e6752b739c2fc7ca..6406e8efee15deeb24217317d82dc12032ee0192 100644 (file)
 
 #define OSPF6_MAX_GRACE_INTERVAL 1800
 #define OSPF6_MIN_GRACE_INTERVAL 1
+#define OSPF6_DFLT_GRACE_INTERVAL 120
+
+/* Forward declaration(s). */
+struct ospf6_neighbor;
 
 /* Debug option */
 extern unsigned char conf_debug_ospf6_gr;
@@ -67,7 +71,8 @@ enum ospf6_gr_helper_rejected_reason {
        OSPF6_HELPER_NOT_A_VALID_NEIGHBOUR,
        OSPF6_HELPER_PLANNED_ONLY_RESTART,
        OSPF6_HELPER_TOPO_CHANGE_RTXMT_LIST,
-       OSPF6_HELPER_LSA_AGE_MORE
+       OSPF6_HELPER_LSA_AGE_MORE,
+       OSPF6_HELPER_RESTARTING,
 };
 
 #ifdef roundup
@@ -119,6 +124,11 @@ struct grace_tlv_restart_reason {
 #define OSPF6_GRACE_LSA_MIN_SIZE                                               \
        GRACE_PERIOD_TLV_SIZE + GRACE_RESTART_REASON_TLV_SIZE
 
+struct ospf6_grace_lsa {
+       struct grace_tlv_graceperiod tlv_period;
+       struct grace_tlv_restart_reason tlv_reason;
+};
+
 struct advRtr {
        in_addr_t advRtrAddr;
 };
@@ -156,6 +166,13 @@ extern void ospf6_process_maxage_grace_lsa(struct ospf6 *ospf,
                                           struct ospf6_neighbor *nbr);
 extern void ospf6_helper_handle_topo_chg(struct ospf6 *ospf6,
                                         struct ospf6_lsa *lsa);
+extern int config_write_ospf6_gr(struct vty *vty, struct ospf6 *ospf6);
 extern int config_write_ospf6_gr_helper(struct vty *vty, struct ospf6 *ospf6);
 extern int config_write_ospf6_debug_gr_helper(struct vty *vty);
+
+extern void ospf6_gr_check_lsdb_consistency(struct ospf6 *ospf,
+                                           struct ospf6_area *area);
+extern void ospf6_gr_nvm_read(struct ospf6 *ospf);
+extern void ospf6_gr_init(void);
+
 #endif /* OSPF6_GR_H */
index 77eb9e8b3996870381149483caeb8eb8c302030b..4522bd2619ff32e095954b8957a48c6716f10740 100644 (file)
@@ -360,6 +360,16 @@ int ospf6_process_grace_lsa(struct ospf6 *ospf6, struct ospf6_lsa *lsa,
                return OSPF6_GR_NOT_HELPER;
        }
 
+       if (ospf6->gr_info.restart_in_progress) {
+               if (IS_DEBUG_OSPF6_GR)
+                       zlog_debug(
+                               "%s: router is in the process of graceful restart",
+                               __func__);
+               restarter->gr_helper_info.rejected_reason =
+                       OSPF6_HELPER_RESTARTING;
+               return OSPF6_GR_NOT_HELPER;
+       }
+
        /* check supported grace period configured
         * if configured, use this to start the grace
         * timer otherwise use the interval received
index 06a950156bf4a60d3fd618169b4d09cdd362290d..bea8cf0edd3ccd330f038621ef233d89e2a1f939 100644 (file)
@@ -47,6 +47,7 @@
 #include "ospf6_flood.h"
 #include "ospf6d.h"
 #include "ospf6_spf.h"
+#include "ospf6_gr.h"
 
 unsigned char conf_debug_ospf6_brouter = 0;
 uint32_t conf_debug_ospf6_brouter_specific_router_id;
@@ -249,6 +250,13 @@ int ospf6_router_lsa_originate(struct thread *thread)
        oa = (struct ospf6_area *)THREAD_ARG(thread);
        oa->thread_router_lsa = NULL;
 
+       if (oa->ospf6->gr_info.restart_in_progress) {
+               if (IS_DEBUG_OSPF6_GR)
+                       zlog_debug(
+                               "Graceful Restart in progress, don't originate LSA");
+               return 0;
+       }
+
        if (IS_OSPF6_DEBUG_ORIGINATE(ROUTER))
                zlog_debug("Originate Router-LSA for Area %s", oa->name);
 
@@ -532,6 +540,13 @@ int ospf6_network_lsa_originate(struct thread *thread)
           by ospf6_lsa_refresh (), and does not come here. */
        assert(oi->area);
 
+       if (oi->area->ospf6->gr_info.restart_in_progress) {
+               if (IS_DEBUG_OSPF6_GR)
+                       zlog_debug(
+                               "Graceful Restart in progress, don't originate LSA");
+               return 0;
+       }
+
        old = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_NETWORK),
                                htonl(oi->interface->ifindex),
                                oi->area->ospf6->router_id, oi->area->lsdb);
@@ -773,6 +788,14 @@ int ospf6_link_lsa_originate(struct thread *thread)
 
        assert(oi->area);
 
+       if (oi->area->ospf6->gr_info.restart_in_progress) {
+               if (IS_DEBUG_OSPF6_GR)
+                       zlog_debug(
+                               "Graceful Restart in progress, don't originate LSA");
+               return 0;
+       }
+
+
        /* find previous LSA */
        old = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_LINK),
                                htonl(oi->interface->ifindex),
@@ -1009,6 +1032,13 @@ int ospf6_intra_prefix_lsa_originate_stub(struct thread *thread)
        oa = (struct ospf6_area *)THREAD_ARG(thread);
        oa->thread_intra_prefix_lsa = NULL;
 
+       if (oa->ospf6->gr_info.restart_in_progress) {
+               if (IS_DEBUG_OSPF6_GR)
+                       zlog_debug(
+                               "Graceful Restart in progress, don't originate LSA");
+               return 0;
+       }
+
        /* find previous LSA */
        old = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_INTRA_PREFIX), htonl(0),
                                oa->ospf6->router_id, oa->lsdb);
@@ -1243,6 +1273,13 @@ int ospf6_intra_prefix_lsa_originate_transit(struct thread *thread)
 
        assert(oi->area);
 
+       if (oi->area->ospf6->gr_info.restart_in_progress) {
+               if (IS_DEBUG_OSPF6_GR)
+                       zlog_debug(
+                               "Graceful Restart in progress, don't originate LSA");
+               return 0;
+       }
+
        /* find previous LSA */
        old = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_INTRA_PREFIX),
                                htonl(oi->interface->ifindex),
index 4031ffe6896c29e146d63526bc8c84e5513c5e26..f15bf0b9b4fa6d9ef6379e10f9745587932f8e8d 100644 (file)
@@ -192,12 +192,26 @@ struct ospf6_intra_prefix_lsa {
                                         oi, 0, &(oi)->thread_as_extern_lsa);  \
        } while (0)
 
+#define OSPF6_ROUTER_LSA_EXECUTE(oa)                                           \
+       do {                                                                   \
+               if (CHECK_FLAG((oa)->flag, OSPF6_AREA_ENABLE))                 \
+                       thread_execute(master, ospf6_router_lsa_originate, oa, \
+                                      0);                                     \
+       } while (0)
+
 #define OSPF6_NETWORK_LSA_EXECUTE(oi)                                          \
        do {                                                                   \
                THREAD_OFF((oi)->thread_network_lsa);                          \
                thread_execute(master, ospf6_network_lsa_originate, oi, 0);    \
        } while (0)
 
+#define OSPF6_LINK_LSA_EXECUTE(oi)                                             \
+       do {                                                                   \
+               if (!CHECK_FLAG((oi)->flag, OSPF6_INTERFACE_DISABLE))          \
+                       thread_execute(master, ospf6_link_lsa_originate, oi,   \
+                                      0);                                     \
+       } while (0)
+
 #define OSPF6_INTRA_PREFIX_LSA_EXECUTE_TRANSIT(oi)                             \
        do {                                                                   \
                THREAD_OFF((oi)->thread_intra_prefix_lsa);                     \
index 3dcc74589adddffa32859352127857dc3d228bca..53f3c3468ac171922ad6f1e5d1e3b000c440a087 100644 (file)
@@ -553,6 +553,21 @@ static void ospf6_hello_recv(struct in6_addr *src, struct in6_addr *dst,
                return;
        }
 
+       /*
+        * RFC 3623 - Section 2:
+        * "If the restarting router determines that it was the Designated
+        * Router on a given segment prior to the restart, it elects
+        * itself as the Designated Router again.  The restarting router
+        * knows that it was the Designated Router if, while the
+        * associated interface is in Waiting state, a Hello packet is
+        * received from a neighbor listing the router as the Designated
+        * Router".
+        */
+       if (oi->area->ospf6->gr_info.restart_in_progress
+           && oi->state == OSPF6_INTERFACE_WAITING
+           && hello->drouter == oi->area->ospf6->router_id)
+               oi->drouter = hello->drouter;
+
        /* Schedule interface events */
        if (backupseen)
                thread_add_event(master, backup_seen, oi, 0, NULL);
index 05ef40be4773fd12275f0cbee41b9b37d1487980..35fbd3991cdd8febaa1892becc1fcef5198f1e65 100644 (file)
@@ -90,6 +90,22 @@ struct ospf6_neighbor *ospf6_neighbor_lookup(uint32_t router_id,
        return (struct ospf6_neighbor *)NULL;
 }
 
+struct ospf6_neighbor *ospf6_area_neighbor_lookup(struct ospf6_area *area,
+                                                 uint32_t router_id)
+{
+       struct ospf6_interface *oi;
+       struct ospf6_neighbor *nbr;
+       struct listnode *node;
+
+       for (ALL_LIST_ELEMENTS_RO(area->if_list, node, oi)) {
+               nbr = ospf6_neighbor_lookup(router_id, oi);
+               if (nbr)
+                       return nbr;
+       }
+
+       return NULL;
+}
+
 /* create ospf6_neighbor */
 struct ospf6_neighbor *ospf6_neighbor_create(uint32_t router_id,
                                             struct ospf6_interface *oi)
index 30e044da4b8f07c58f2f4a14047754ce83873ae0..f7735b87b9b3cdcd770c580518c656715e870ca2 100644 (file)
@@ -23,6 +23,9 @@
 
 #include "hook.h"
 
+/* Forward declaration(s). */
+struct ospf6_area;
+
 /* Debug option */
 extern unsigned char conf_debug_ospf6_neighbor;
 #define OSPF6_DEBUG_NEIGHBOR_STATE   0x01
@@ -185,6 +188,8 @@ void ospf6_neighbor_dbex_init(struct ospf6_neighbor *on);
 
 struct ospf6_neighbor *ospf6_neighbor_lookup(uint32_t router_id,
                                             struct ospf6_interface *oi);
+struct ospf6_neighbor *ospf6_area_neighbor_lookup(struct ospf6_area *area,
+                                                 uint32_t router_id);
 struct ospf6_neighbor *ospf6_neighbor_create(uint32_t router_id,
                                             struct ospf6_interface *oi);
 void ospf6_neighbor_delete(struct ospf6_neighbor *on);
index a27ca092b8586f54b7fd0c9db7e58bc00c762cb2..e4de6ccf910b1125f71b7e2dcab6e4bfaa814038 100644 (file)
@@ -44,6 +44,7 @@
 #include "ospf6d.h"
 #include "ospf6_abr.h"
 #include "ospf6_nssa.h"
+#include "ospf6_zebra.h"
 
 DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_VERTEX, "OSPF6 vertex");
 
@@ -438,8 +439,8 @@ void ospf6_spf_table_finish(struct ospf6_route_table *result_table)
        }
 }
 
-static const char *const ospf6_spf_reason_str[] = {"R+", "R-", "N+", "N-", "L+",
-                                                  "L-", "R*", "N*", "C",  "A"};
+static const char *const ospf6_spf_reason_str[] = {
+       "R+", "R-", "N+", "N-", "L+", "L-", "R*", "N*", "C", "A", "GR"};
 
 void ospf6_spf_reason_string(unsigned int reason, char *buf, int size)
 {
@@ -1255,6 +1256,17 @@ static int ospf6_ase_calculate_timer(struct thread *t)
                                ospf6_ase_calculate_route(ospf6, lsa, area);
                }
        }
+
+       if (ospf6->gr_info.finishing_restart) {
+               /*
+                * The routing table computation is complete. Uninstall remnant
+                * routes that were installed before the restart, but that are
+                * no longer valid.
+                */
+               ospf6_zebra_gr_disable(ospf6);
+               ospf6->gr_info.finishing_restart = false;
+       }
+
        return 0;
 }
 
index d6fbc5c13b74ed6201091ead73daa2fab9bc8e45..cc52d168614e8086dda7c53a3c5ababe01101b37 100644 (file)
@@ -93,6 +93,7 @@ struct ospf6_vertex {
 #define OSPF6_SPF_FLAGS_NETWORK_LSA_ORIGINATED   (1 << 7)
 #define OSPF6_SPF_FLAGS_CONFIG_CHANGE            (1 << 8)
 #define OSPF6_SPF_FLAGS_ASBR_STATUS_CHANGE       (1 << 9)
+#define OSPF6_SPF_FLAGS_GR_FINISH                (1 << 10)
 
 static inline void ospf6_set_spf_reason(struct ospf6 *ospf, unsigned int reason)
 {
index d07d36c3191c4fdde04f3b0245d3f129c9697b95..3122d616cb04bf5d065df3b78bf3194cf11e0177 100644 (file)
@@ -472,6 +472,12 @@ struct ospf6 *ospf6_instance_create(const char *name)
        if (ospf6->fd < 0)
                return ospf6;
 
+       /*
+        * Read from non-volatile memory whether this instance is performing a
+        * graceful restart or not.
+        */
+       ospf6_gr_nvm_read(ospf6);
+
        thread_add_read(master, ospf6_receive, ospf6, ospf6->fd,
                        &ospf6->t_ospf6_receive);
 
@@ -488,7 +494,8 @@ void ospf6_delete(struct ospf6 *o)
        QOBJ_UNREG(o);
 
        ospf6_gr_helper_deinit(o);
-       ospf6_flush_self_originated_lsas_now(o);
+       if (!o->gr_info.prepare_in_progress)
+               ospf6_flush_self_originated_lsas_now(o);
        ospf6_disable(o);
        ospf6_del(o);
 
@@ -555,6 +562,7 @@ static void ospf6_disable(struct ospf6 *o)
                THREAD_OFF(o->t_distribute_update);
                THREAD_OFF(o->t_ospf6_receive);
                THREAD_OFF(o->t_external_aggr);
+               THREAD_OFF(o->gr_info.t_grace_period);
        }
 }
 
@@ -2230,6 +2238,7 @@ static int config_write_ospf6(struct vty *vty)
                ospf6_distance_config_write(vty, ospf6);
                ospf6_distribute_config_write(vty, ospf6);
                ospf6_asbr_summary_config_write(vty, ospf6);
+               config_write_ospf6_gr(vty, ospf6);
                config_write_ospf6_gr_helper(vty, ospf6);
 
                vty_out(vty, "exit\n");
index 58ecf08495b9413ff9e36bb8b97b76587fae7a48..3188b1f58febd68d33d0a02699d324a4c41bf8f6 100644 (file)
@@ -60,6 +60,15 @@ struct ospf6_redist {
 #define ROUTEMAP(R) (R->route_map.map)
 };
 
+struct ospf6_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;
+};
+
 struct ospf6_gr_helper {
        /* Gracefull restart Helper supported configs*/
        /* Supported grace interval*/
@@ -192,6 +201,9 @@ struct ospf6 {
         */
        uint16_t max_multipath;
 
+       /* OSPF Graceful Restart info (restarting mode) */
+       struct ospf6_gr_info gr_info;
+
        /*ospf6 Graceful restart helper info */
        struct ospf6_gr_helper ospf6_helper_cfg;
 
index 5403e643dc664b542d7ed1b13a71695355f576f0..c2e91d09bb66eeb4b4cc111a6002e7d16ac3945a 100644 (file)
@@ -40,6 +40,7 @@
 #include "ospf6_zebra.h"
 #include "ospf6d.h"
 #include "ospf6_area.h"
+#include "ospf6_gr.h"
 #include "lib/json.h"
 
 DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_DISTANCE, "OSPF6 distance");
@@ -173,6 +174,36 @@ static int ospf6_zebra_if_address_update_delete(ZAPI_CALLBACK_ARGS)
        return 0;
 }
 
+static int ospf6_zebra_gr_update(struct ospf6 *ospf6, int command,
+                                uint32_t stale_time)
+{
+       struct zapi_cap api;
+
+       if (!zclient || zclient->sock < 0 || !ospf6)
+               return 1;
+
+       memset(&api, 0, sizeof(struct zapi_cap));
+       api.cap = command;
+       api.stale_removal_time = stale_time;
+       api.vrf_id = ospf6->vrf_id;
+
+       (void)zclient_capabilities_send(ZEBRA_CLIENT_CAPABILITIES, zclient,
+                                       &api);
+
+       return 0;
+}
+
+int ospf6_zebra_gr_enable(struct ospf6 *ospf6, uint32_t stale_time)
+{
+       return ospf6_zebra_gr_update(ospf6, ZEBRA_CLIENT_GR_CAPABILITIES,
+                                    stale_time);
+}
+
+int ospf6_zebra_gr_disable(struct ospf6 *ospf6)
+{
+       return ospf6_zebra_gr_update(ospf6, ZEBRA_CLIENT_GR_DISABLE, 0);
+}
+
 static int ospf6_zebra_read_route(ZAPI_CALLBACK_ARGS)
 {
        struct zapi_route api;
@@ -384,12 +415,30 @@ static void ospf6_zebra_route_update(int type, struct ospf6_route *request,
 void ospf6_zebra_route_update_add(struct ospf6_route *request,
                                  struct ospf6 *ospf6)
 {
+       if (ospf6->gr_info.restart_in_progress
+           || ospf6->gr_info.prepare_in_progress) {
+               if (IS_DEBUG_OSPF6_GR)
+                       zlog_debug(
+                               "Zebra: Graceful Restart in progress -- not installing %pFX",
+                               &request->prefix);
+               return;
+       }
+
        ospf6_zebra_route_update(ADD, request, ospf6);
 }
 
 void ospf6_zebra_route_update_remove(struct ospf6_route *request,
                                     struct ospf6 *ospf6)
 {
+       if (ospf6->gr_info.restart_in_progress
+           || ospf6->gr_info.prepare_in_progress) {
+               if (IS_DEBUG_OSPF6_GR)
+                       zlog_debug(
+                               "Zebra: Graceful Restart in progress -- not uninstalling %pFX",
+                               &request->prefix);
+               return;
+       }
+
        ospf6_zebra_route_update(REM, request, ospf6);
 }
 
@@ -398,6 +447,15 @@ void ospf6_zebra_add_discard(struct ospf6_route *request, struct ospf6 *ospf6)
        struct zapi_route api;
        struct prefix *dest = &request->prefix;
 
+       if (ospf6->gr_info.restart_in_progress
+           || ospf6->gr_info.prepare_in_progress) {
+               if (IS_DEBUG_OSPF6_GR)
+                       zlog_debug(
+                               "Zebra: Graceful Restart in progress -- not installing %pFX",
+                               &request->prefix);
+               return;
+       }
+
        if (!CHECK_FLAG(request->flag, OSPF6_ROUTE_BLACKHOLE_ADDED)) {
                memset(&api, 0, sizeof(api));
                api.vrf_id = ospf6->vrf_id;
@@ -426,6 +484,15 @@ void ospf6_zebra_delete_discard(struct ospf6_route *request,
        struct zapi_route api;
        struct prefix *dest = &request->prefix;
 
+       if (ospf6->gr_info.restart_in_progress
+           || ospf6->gr_info.prepare_in_progress) {
+               if (IS_DEBUG_OSPF6_GR)
+                       zlog_debug(
+                               "Zebra: Graceful Restart in progress -- not uninstalling %pFX",
+                               &request->prefix);
+               return;
+       }
+
        if (CHECK_FLAG(request->flag, OSPF6_ROUTE_BLACKHOLE_ADDED)) {
                memset(&api, 0, sizeof(api));
                api.vrf_id = ospf6->vrf_id;
index e25f6bf80d092e71f7d0cb0bb92d536229c4fbdc..77e48673c872c4ed64324d1e21116bd31247cc32 100644 (file)
@@ -65,6 +65,8 @@ extern uint8_t ospf6_distance_apply(struct prefix_ipv6 *p,
                                    struct ospf6_route * or,
                                    struct ospf6 *ospf6);
 
+extern int ospf6_zebra_gr_enable(struct ospf6 *ospf6, uint32_t stale_time);
+extern int ospf6_zebra_gr_disable(struct ospf6 *ospf6);
 extern int ospf6_distance_set(struct vty *vty, struct ospf6 *ospf6,
                              const char *distance_str, const char *ip_str,
                              const char *access_list_str);
index e85f4c1b61caafaf47e438ff4e09bad8560f3683..2c8c9b9d454c1736e369a70e12c2b8520241a87f 100644 (file)
@@ -1383,6 +1383,7 @@ void ospf6_init(struct thread_master *master)
        ospf6_intra_init();
        ospf6_asbr_init();
        ospf6_abr_init();
+       ospf6_gr_init();
        ospf6_gr_helper_config_init();
 
        /* initialize hooks for modifying filter rules */
index 910c26b791ef9e6849fb3a67bfd0fecb26455dfc..be626646a000ece7b415e8d31955518f2c8276c8 100644 (file)
@@ -12,6 +12,7 @@ vtysh_scan += \
        ospf6d/ospf6_area.c \
        ospf6d/ospf6_bfd.c \
        ospf6d/ospf6_flood.c \
+       ospf6d/ospf6_gr.c \
        ospf6d/ospf6_gr_helper.c \
        ospf6d/ospf6_interface.c \
        ospf6d/ospf6_intra.c \
@@ -40,6 +41,7 @@ ospf6d_libospf6_a_SOURCES = \
        ospf6d/ospf6_routemap_nb_config.c \
        ospf6d/ospf6_bfd.c \
        ospf6d/ospf6_flood.c \
+       ospf6d/ospf6_gr.c \
        ospf6d/ospf6_gr_helper.c \
        ospf6d/ospf6_interface.c \
        ospf6d/ospf6_intra.c \
@@ -96,6 +98,7 @@ clippy_scan += \
        ospf6d/ospf6_asbr.c \
        ospf6d/ospf6_lsa.c \
        ospf6d/ospf6_gr_helper.c \
+       ospf6d/ospf6_gr.c \
        ospf6d/ospf6_route.c \
        # end