]> git.proxmox.com Git - mirror_frr.git/blobdiff - ospf6d/ospf6_gr.c
Merge pull request #13646 from donaldsharp/logically_illogical
[mirror_frr.git] / ospf6d / ospf6_gr.c
index 87407245b3db8a1a1b775fa41226a80c20022c1b..ecaaa038ab5d4dac14e19af718092e44a50e66f0 100644 (file)
@@ -1,21 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
 /*
  * 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>
@@ -27,6 +14,7 @@
 #include "log.h"
 #include "hook.h"
 #include "printfrr.h"
+#include "lib_errors.h"
 
 #include "ospf6d/ospf6_lsa.h"
 #include "ospf6d/ospf6_lsdb.h"
 #include "ospf6d/ospf6_zebra.h"
 #include "ospf6d/ospf6_message.h"
 #include "ospf6d/ospf6_neighbor.h"
+#include "ospf6d/ospf6_network.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);
+static void ospf6_gr_grace_period_expired(struct event *thread);
 
 /* Originate and install Grace-LSA for a given interface. */
-static int ospf6_gr_lsa_originate(struct ospf6_interface *oi)
+static int ospf6_gr_lsa_originate(struct ospf6_interface *oi,
+                                 enum ospf6_gr_restart_reason reason)
 {
-       struct ospf6_gr_info *gr_info = &oi->area->ospf6->gr_info;
+       struct ospf6 *ospf6 = oi->area->ospf6;
+       struct ospf6_gr_info *gr_info = &ospf6->gr_info;
        struct ospf6_lsa_header *lsa_header;
        struct ospf6_grace_lsa *grace_lsa;
        struct ospf6_lsa *lsa;
+       uint16_t lsa_length;
        char buffer[OSPF6_MAX_LSASIZE];
 
        if (IS_OSPF6_DEBUG_ORIGINATE(LINK))
@@ -76,29 +66,59 @@ static int ospf6_gr_lsa_originate(struct ospf6_interface *oi)
        /* 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;
+       grace_lsa->tlv_reason.reason = reason;
 
        /* Fill LSA Header */
+       lsa_length = sizeof(*lsa_header) + sizeof(*grace_lsa);
        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->adv_router = 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_header->length = htons(lsa_length);
 
        /* LSA checksum */
        ospf6_lsa_checksum(lsa_header);
 
-       /* create LSA */
-       lsa = ospf6_lsa_create(lsa_header);
-
-       /* Originate */
-       ospf6_lsa_originate_interface(lsa, oi);
+       if (reason == OSPF6_GR_UNKNOWN_RESTART) {
+               struct ospf6_header *oh;
+               uint32_t *uv32;
+               int n;
+               uint16_t length = OSPF6_HEADER_SIZE + 4 + lsa_length;
+               struct iovec iovector[2] = {};
+
+               /* Reserve space for OSPFv3 header. */
+               memmove(&buffer[OSPF6_HEADER_SIZE + 4], buffer, lsa_length);
+
+               /* Fill in the OSPFv3 header. */
+               oh = (struct ospf6_header *)buffer;
+               oh->version = OSPFV3_VERSION;
+               oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE;
+               oh->router_id = oi->area->ospf6->router_id;
+               oh->area_id = oi->area->area_id;
+               oh->instance_id = oi->instance_id;
+               oh->reserved = 0;
+               oh->length = htons(length);
+
+               /* Fill LSA header. */
+               uv32 = (uint32_t *)&buffer[sizeof(*oh)];
+               *uv32 = htonl(1);
+
+               /* Send packet. */
+               iovector[0].iov_base = lsa_header;
+               iovector[0].iov_len = length;
+               n = ospf6_sendmsg(oi->linklocal_addr, &allspfrouters6,
+                                 oi->interface->ifindex, iovector, ospf6->fd);
+               if (n != length)
+                       flog_err(EC_LIB_DEVELOPMENT,
+                                "%s: could not send entire message", __func__);
+       } else {
+               /* Create and install LSA. */
+               lsa = ospf6_lsa_create(lsa_header);
+               ospf6_lsa_originate_interface(lsa, oi);
+       }
 
        return 0;
 }
@@ -142,16 +162,16 @@ static void ospf6_gr_restart_exit(struct ospf6 *ospf6, const char *reason)
 {
        struct ospf6_area *area;
        struct listnode *onode, *anode;
+       struct ospf6_route *route;
 
        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);
+       XFREE(MTYPE_TMP, ospf6->gr_info.exit_reason);
+       ospf6->gr_info.exit_reason = XSTRDUP(MTYPE_TMP, reason);
+       EVENT_OFF(ospf6->gr_info.t_grace_period);
 
        for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, onode, area)) {
                struct ospf6_interface *oi;
@@ -163,8 +183,24 @@ static void ospf6_gr_restart_exit(struct ospf6 *ospf6, const char *reason)
                 */
                OSPF6_ROUTER_LSA_EXECUTE(area);
 
+               /*
+                * Force reorigination of intra-area-prefix-LSAs to handle
+                * areas without any full adjacency.
+                */
+               OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB(area);
+
                for (ALL_LIST_ELEMENTS_RO(area->if_list, anode, oi)) {
-                       OSPF6_LINK_LSA_EXECUTE(oi);
+                       /* Disable hello delay. */
+                       if (oi->gr.hello_delay.t_grace_send) {
+                               oi->gr.hello_delay.elapsed_seconds = 0;
+                               EVENT_OFF(oi->gr.hello_delay.t_grace_send);
+                               event_add_event(master, ospf6_hello_send, oi, 0,
+                                               &oi->thread_send_hello);
+                       }
+
+                       /* Reoriginate Link-LSA. */
+                       if (oi->type != OSPF_IFTYPE_VIRTUALLINK)
+                               OSPF6_LINK_LSA_EXECUTE(oi);
 
                        /*
                         * 2) The router should reoriginate network-LSAs on all
@@ -175,6 +211,16 @@ static void ospf6_gr_restart_exit(struct ospf6 *ospf6, const char *reason)
                }
        }
 
+       /*
+        * While all self-originated NSSA and AS-external LSAs were already
+        * learned from the helping neighbors, we need to reoriginate them in
+        * order to ensure they will be refreshed periodically.
+        */
+       for (route = ospf6_route_head(ospf6->external_table); route;
+            route = ospf6_route_next(route))
+               ospf6_handle_external_lsa_origination(ospf6, route,
+                                                     &route->prefix);
+
        /*
         * 3) The router reruns its OSPF routing calculations, this time
         *    installing the results into the system forwarding table, and
@@ -191,6 +237,27 @@ static void ospf6_gr_restart_exit(struct ospf6 *ospf6, const char *reason)
        ospf6_gr_flush_grace_lsas(ospf6);
 }
 
+/* Enter the Graceful Restart mode. */
+void ospf6_gr_restart_enter(struct ospf6 *ospf6,
+                           enum ospf6_gr_restart_reason reason,
+                           time_t timestamp)
+{
+       unsigned long remaining_time;
+
+       ospf6->gr_info.restart_in_progress = true;
+       ospf6->gr_info.reason = reason;
+
+       /* Schedule grace period timeout. */
+       remaining_time = timestamp - time(NULL);
+       if (IS_DEBUG_OSPF6_GR)
+               zlog_debug(
+                       "GR: remaining time until grace period expires: %lu(s)",
+                       remaining_time);
+
+       event_add_timer(master, ospf6_gr_grace_period_expired, ospf6,
+                       remaining_time, &ospf6->gr_info.t_grace_period);
+}
+
 #define RTR_LSA_MISSING 0
 #define RTR_LSA_ADJ_FOUND 1
 #define RTR_LSA_ADJ_NOT_FOUND 2
@@ -455,19 +522,33 @@ static bool ospf6_gr_check_adjs(struct ospf6 *ospf6)
 }
 
 /* Handling of grace period expiry. */
-static void ospf6_gr_grace_period_expired(struct thread *thread)
+static void ospf6_gr_grace_period_expired(struct event *thread)
 {
-       struct ospf6 *ospf6 = THREAD_ARG(thread);
+       struct ospf6 *ospf6 = EVENT_ARG(thread);
 
-       ospf6->gr_info.t_grace_period = NULL;
        ospf6_gr_restart_exit(ospf6, "grace period has expired");
 }
 
+/* Send extra Grace-LSA out the interface (unplanned outages only). */
+void ospf6_gr_iface_send_grace_lsa(struct event *thread)
+{
+       struct ospf6_interface *oi = EVENT_ARG(thread);
+
+       ospf6_gr_lsa_originate(oi, oi->area->ospf6->gr_info.reason);
+
+       if (++oi->gr.hello_delay.elapsed_seconds < oi->gr.hello_delay.interval)
+               event_add_timer(master, ospf6_gr_iface_send_grace_lsa, oi, 1,
+                               &oi->gr.hello_delay.t_grace_send);
+       else
+               event_add_event(master, ospf6_hello_send, oi, 0,
+                               &oi->thread_send_hello);
+}
+
 /*
  * 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)
+static void ospf6_gr_nvm_update(struct ospf6 *ospf6, bool prepare)
 {
        const char *inst_name;
        json_object *json;
@@ -493,16 +574,18 @@ static void ospf6_gr_nvm_update(struct ospf6 *ospf6)
                                       json_instance);
        }
 
+       json_object_int_add(json_instance, "gracePeriod",
+                           ospf6->gr_info.grace_period);
+
        /*
         * Record not only the grace period, but also a UNIX timestamp
         * corresponding to the end of that period. That way, once 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);
+       if (prepare)
+               json_object_int_add(json_instance, "timestamp",
+                                   time(NULL) + ospf6->gr_info.grace_period);
 
        json_object_to_file_ext((char *)OSPF6D_GR_STATE, json,
                                JSON_C_TO_STRING_PRETTY);
@@ -513,7 +596,7 @@ static void ospf6_gr_nvm_update(struct ospf6 *ospf6)
  * Delete GR status information about the given OSPF instance from non-volatile
  * memory.
  */
-static void ospf6_gr_nvm_delete(struct ospf6 *ospf6)
+void ospf6_gr_nvm_delete(struct ospf6 *ospf6)
 {
        const char *inst_name;
        json_object *json;
@@ -549,6 +632,7 @@ void ospf6_gr_nvm_read(struct ospf6 *ospf6)
        json_object *json_instances;
        json_object *json_instance;
        json_object *json_timestamp;
+       json_object *json_grace_period;
        time_t timestamp = 0;
 
        inst_name = ospf6->name ? ospf6->name : VRF_DEFAULT_NAME;
@@ -570,29 +654,33 @@ void ospf6_gr_nvm_read(struct ospf6 *ospf6)
                                       json_instance);
        }
 
+       json_object_object_get_ex(json_instance, "gracePeriod",
+                                 &json_grace_period);
        json_object_object_get_ex(json_instance, "timestamp", &json_timestamp);
        if (json_timestamp) {
                time_t now;
-               unsigned long remaining_time;
 
-               /* Check if the grace period has already expired. */
+               /* Planned GR: check if the grace period has already expired. */
                now = time(NULL);
                timestamp = json_object_get_int(json_timestamp);
                if (now > timestamp) {
                        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);
-               }
+               } else
+                       ospf6_gr_restart_enter(ospf6, OSPF6_GR_SW_RESTART,
+                                              timestamp);
+       } else if (json_grace_period) {
+               uint32_t grace_period;
+
+               /*
+                * Unplanned GR: the Grace-LSAs will be sent later as soon as
+                * the interfaces are operational.
+                */
+               grace_period = json_object_get_int(json_grace_period);
+               ospf6->gr_info.grace_period = grace_period;
+               ospf6_gr_restart_enter(ospf6, OSPF6_GR_UNKNOWN_RESTART,
+                                      time(NULL) +
+                                              ospf6->gr_info.grace_period);
        }
 
        json_object_object_del(json_instances, inst_name);
@@ -602,6 +690,24 @@ void ospf6_gr_nvm_read(struct ospf6 *ospf6)
        json_object_free(json);
 }
 
+void ospf6_gr_unplanned_start_interface(struct ospf6_interface *oi)
+{
+       /*
+        * Can't check OSPF interface state as the OSPF instance might not be
+        * enabled yet.
+        */
+       if (!if_is_operative(oi->interface) || if_is_loopback(oi->interface))
+               return;
+
+       /* Send Grace-LSA. */
+       ospf6_gr_lsa_originate(oi, oi->area->ospf6->gr_info.reason);
+
+       /* Start GR hello-delay interval. */
+       oi->gr.hello_delay.elapsed_seconds = 0;
+       event_add_timer(master, ospf6_gr_iface_send_grace_lsa, oi, 1,
+                       &oi->gr.hello_delay.t_grace_send);
+}
+
 /* Prepare to start a Graceful Restart. */
 static void ospf6_gr_prepare(void)
 {
@@ -622,25 +728,17 @@ static void ospf6_gr_prepare(void)
                                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);
+                               ospf6_gr_lsa_originate(oi, OSPF6_GR_SW_RESTART);
                        }
                }
 
                /* Record end of the grace period in non-volatile memory. */
-               ospf6_gr_nvm_update(ospf6);
+               ospf6_gr_nvm_update(ospf6, true);
 
                /*
                 * Mark that a Graceful Restart preparation is in progress, to
@@ -711,6 +809,12 @@ DEFPY(ospf6_graceful_restart, ospf6_graceful_restart_cmd,
        ospf6->gr_info.restart_support = true;
        ospf6->gr_info.grace_period = grace_period;
 
+       /* Freeze OSPF routes in the RIB. */
+       (void)ospf6_zebra_gr_enable(ospf6, ospf6->gr_info.grace_period);
+
+       /* Record that GR is enabled in non-volatile memory. */
+       ospf6_gr_nvm_update(ospf6, false);
+
        return CMD_SUCCESS;
 }
 
@@ -733,6 +837,8 @@ DEFPY(ospf6_no_graceful_restart, ospf6_no_graceful_restart_cmd,
 
        ospf6->gr_info.restart_support = false;
        ospf6->gr_info.grace_period = OSPF6_DFLT_GRACE_INTERVAL;
+       ospf6_gr_nvm_delete(ospf6);
+       ospf6_zebra_gr_disable(ospf6);
 
        return CMD_SUCCESS;
 }