summaryrefslogtreecommitdiffstats
path: root/ospfd/ospf_nsm.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:53:30 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:53:30 +0000
commit2c7cac91ed6e7db0f6937923d2b57f97dbdbc337 (patch)
treec05dc0f8e6aa3accc84e3e5cffc933ed94941383 /ospfd/ospf_nsm.c
parentInitial commit. (diff)
downloadfrr-2c7cac91ed6e7db0f6937923d2b57f97dbdbc337.tar.xz
frr-2c7cac91ed6e7db0f6937923d2b57f97dbdbc337.zip
Adding upstream version 8.4.4.upstream/8.4.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ospfd/ospf_nsm.c')
-rw-r--r--ospfd/ospf_nsm.c890
1 files changed, 890 insertions, 0 deletions
diff --git a/ospfd/ospf_nsm.c b/ospfd/ospf_nsm.c
new file mode 100644
index 0000000..bbfeecd
--- /dev/null
+++ b/ospfd/ospf_nsm.c
@@ -0,0 +1,890 @@
+/*
+ * OSPF version 2 Neighbor State Machine
+ * From RFC2328 [OSPF Version 2]
+ * Copyright (C) 1999, 2000 Toshiaki Takada
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra 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 "thread.h"
+#include "memory.h"
+#include "hash.h"
+#include "linklist.h"
+#include "prefix.h"
+#include "if.h"
+#include "table.h"
+#include "stream.h"
+#include "table.h"
+#include "log.h"
+#include "command.h"
+#include "network.h"
+
+#include "ospfd/ospfd.h"
+#include "ospfd/ospf_interface.h"
+#include "ospfd/ospf_ism.h"
+#include "ospfd/ospf_asbr.h"
+#include "ospfd/ospf_lsa.h"
+#include "ospfd/ospf_lsdb.h"
+#include "ospfd/ospf_neighbor.h"
+#include "ospfd/ospf_nsm.h"
+#include "ospfd/ospf_network.h"
+#include "ospfd/ospf_packet.h"
+#include "ospfd/ospf_dump.h"
+#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,
+ (struct ospf_neighbor * on, int state, int oldstate),
+ (on, state, oldstate));
+
+static void nsm_clear_adj(struct ospf_neighbor *);
+
+/* OSPF NSM Timer functions. */
+static void ospf_inactivity_timer(struct thread *thread)
+{
+ struct ospf_neighbor *nbr;
+
+ nbr = THREAD_ARG(thread);
+ nbr->t_inactivity = NULL;
+
+ if (IS_DEBUG_OSPF(nsm, NSM_TIMERS))
+ zlog_debug("NSM[%s:%pI4:%s]: Timer (Inactivity timer expire)",
+ IF_NAME(nbr->oi), &nbr->router_id,
+ ospf_get_name(nbr->oi->ospf));
+
+ /* Dont trigger NSM_InactivityTimer event , if the current
+ * router acting as HELPER for this neighbour.
+ */
+ if (!OSPF_GR_IS_ACTIVE_HELPER(nbr))
+ OSPF_NSM_EVENT_SCHEDULE(nbr, NSM_InactivityTimer);
+ else {
+ if (IS_DEBUG_OSPF_GR)
+ zlog_debug(
+ "%s, Acting as HELPER for this neighbour, So restart the dead timer",
+ __func__);
+ OSPF_NSM_TIMER_ON(nbr->t_inactivity, ospf_inactivity_timer,
+ nbr->v_inactivity);
+ }
+}
+
+static void ospf_db_desc_timer(struct thread *thread)
+{
+ struct ospf_neighbor *nbr;
+
+ nbr = THREAD_ARG(thread);
+ nbr->t_db_desc = NULL;
+
+ if (IS_DEBUG_OSPF(nsm, NSM_TIMERS))
+ zlog_debug("NSM[%s:%pI4:%s]: Timer (DD Retransmit timer expire)",
+ IF_NAME(nbr->oi), &nbr->src,
+ ospf_get_name(nbr->oi->ospf));
+
+ /* resent last send DD packet. */
+ assert(nbr->last_send);
+ ospf_db_desc_resend(nbr);
+
+ /* DD Retransmit timer set. */
+ OSPF_NSM_TIMER_ON(nbr->t_db_desc, ospf_db_desc_timer, nbr->v_db_desc);
+}
+
+/* Hook function called after ospf NSM event is occurred.
+ *
+ * Set/clear any timers whose condition is implicit to the neighbour
+ * state. There may be other timers which are set/unset according to other
+ * state.
+ *
+ * We rely on this function to properly clear timers in lower states,
+ * particularly before deleting a neighbour.
+ */
+static void nsm_timer_set(struct ospf_neighbor *nbr)
+{
+ switch (nbr->state) {
+ case NSM_Deleted:
+ case NSM_Down:
+ THREAD_OFF(nbr->t_inactivity);
+ THREAD_OFF(nbr->t_hello_reply);
+ /* fallthru */
+ case NSM_Attempt:
+ case NSM_Init:
+ case NSM_TwoWay:
+ THREAD_OFF(nbr->t_db_desc);
+ THREAD_OFF(nbr->t_ls_upd);
+ THREAD_OFF(nbr->t_ls_req);
+ break;
+ case NSM_ExStart:
+ OSPF_NSM_TIMER_ON(nbr->t_db_desc, ospf_db_desc_timer,
+ nbr->v_db_desc);
+ THREAD_OFF(nbr->t_ls_upd);
+ THREAD_OFF(nbr->t_ls_req);
+ break;
+ case NSM_Exchange:
+ OSPF_NSM_TIMER_ON(nbr->t_ls_upd, ospf_ls_upd_timer,
+ nbr->v_ls_upd);
+ if (!IS_SET_DD_MS(nbr->dd_flags))
+ THREAD_OFF(nbr->t_db_desc);
+ break;
+ case NSM_Loading:
+ case NSM_Full:
+ default:
+ THREAD_OFF(nbr->t_db_desc);
+ break;
+ }
+}
+
+/* 10.4 of RFC2328, indicate whether an adjacency is appropriate with
+ * the given neighbour
+ */
+int nsm_should_adj(struct ospf_neighbor *nbr)
+{
+ struct ospf_interface *oi = nbr->oi;
+
+ /* These network types must always form adjacencies. */
+ if (oi->type == OSPF_IFTYPE_POINTOPOINT
+ || oi->type == OSPF_IFTYPE_POINTOMULTIPOINT
+ || oi->type == OSPF_IFTYPE_VIRTUALLINK
+ /* Router itself is the DRouter or the BDRouter. */
+ || IPV4_ADDR_SAME(&oi->address->u.prefix4, &DR(oi))
+ || IPV4_ADDR_SAME(&oi->address->u.prefix4, &BDR(oi))
+ /* Neighboring Router is the DRouter or the BDRouter. */
+ || IPV4_ADDR_SAME(&nbr->address.u.prefix4, &DR(oi))
+ || IPV4_ADDR_SAME(&nbr->address.u.prefix4, &BDR(oi)))
+ return 1;
+
+ return 0;
+}
+
+/* OSPF NSM functions. */
+static int nsm_hello_received(struct ospf_neighbor *nbr)
+{
+ /* Start or Restart Inactivity Timer. */
+ THREAD_OFF(nbr->t_inactivity);
+
+ OSPF_NSM_TIMER_ON(nbr->t_inactivity, ospf_inactivity_timer,
+ nbr->v_inactivity);
+
+ if (nbr->oi->type == OSPF_IFTYPE_NBMA && nbr->nbr_nbma)
+ THREAD_OFF(nbr->nbr_nbma->t_poll);
+
+ /* Send proactive ARP requests */
+ if (nbr->state < NSM_Exchange)
+ ospf_proactively_arp(nbr);
+
+ return 0;
+}
+
+static int nsm_start(struct ospf_neighbor *nbr)
+{
+ if (nbr->nbr_nbma)
+ THREAD_OFF(nbr->nbr_nbma->t_poll);
+
+ THREAD_OFF(nbr->t_inactivity);
+
+ OSPF_NSM_TIMER_ON(nbr->t_inactivity, ospf_inactivity_timer,
+ nbr->v_inactivity);
+
+ /* Send proactive ARP requests */
+ ospf_proactively_arp(nbr);
+
+ return 0;
+}
+
+static int nsm_twoway_received(struct ospf_neighbor *nbr)
+{
+ int adj = nsm_should_adj(nbr);
+
+ /* Send proactive ARP requests */
+ if (adj)
+ ospf_proactively_arp(nbr);
+
+ return (adj ? NSM_ExStart : NSM_TwoWay);
+}
+
+int ospf_db_summary_count(struct ospf_neighbor *nbr)
+{
+ return ospf_lsdb_count_all(&nbr->db_sum);
+}
+
+int ospf_db_summary_isempty(struct ospf_neighbor *nbr)
+{
+ return ospf_lsdb_isempty(&nbr->db_sum);
+}
+
+static int ospf_db_summary_add(struct ospf_neighbor *nbr, struct ospf_lsa *lsa)
+{
+ switch (lsa->data->type) {
+ case OSPF_OPAQUE_LINK_LSA:
+ /* Exclude type-9 LSAs that does not have the same "oi" with
+ * "nbr". */
+ if (ospf_if_exists(lsa->oi) != nbr->oi)
+ return 0;
+ break;
+ case OSPF_OPAQUE_AREA_LSA:
+ /*
+ * It is assured by the caller function "nsm_negotiation_done()"
+ * that every given LSA belongs to the same area with "nbr".
+ */
+ break;
+ case OSPF_OPAQUE_AS_LSA:
+ default:
+ break;
+ }
+
+ /* Stay away from any Local Translated Type-7 LSAs */
+ if (CHECK_FLAG(lsa->flags, OSPF_LSA_LOCAL_XLT))
+ return 0;
+
+ if (IS_LSA_MAXAGE(lsa))
+ ospf_ls_retransmit_add(nbr, lsa);
+ else
+ ospf_lsdb_add(&nbr->db_sum, lsa);
+
+ return 0;
+}
+
+void ospf_db_summary_clear(struct ospf_neighbor *nbr)
+{
+ struct ospf_lsdb *lsdb;
+ int i;
+
+ lsdb = &nbr->db_sum;
+ for (i = OSPF_MIN_LSA; i < OSPF_MAX_LSA; i++) {
+ struct route_table *table = lsdb->type[i].db;
+ struct route_node *rn;
+
+ for (rn = route_top(table); rn; rn = route_next(rn))
+ if (rn->info)
+ ospf_lsdb_delete(&nbr->db_sum, rn->info);
+ }
+}
+
+
+/* The area link state database consists of the router-LSAs,
+ network-LSAs and summary-LSAs contained in the area structure,
+ along with the AS-external-LSAs contained in the global structure.
+ AS-external-LSAs are omitted from a virtual neighbor's Database
+ summary list. AS-external-LSAs are omitted from the Database
+ summary list if the area has been configured as a stub. */
+static int nsm_negotiation_done(struct ospf_neighbor *nbr)
+{
+ struct ospf_area *area = nbr->oi->area;
+ struct ospf_lsa *lsa;
+ struct route_node *rn;
+
+ /* Send proactive ARP requests */
+ ospf_proactively_arp(nbr);
+
+ LSDB_LOOP (ROUTER_LSDB(area), rn, lsa)
+ ospf_db_summary_add(nbr, lsa);
+ LSDB_LOOP (NETWORK_LSDB(area), rn, lsa)
+ ospf_db_summary_add(nbr, lsa);
+ LSDB_LOOP (SUMMARY_LSDB(area), rn, lsa)
+ ospf_db_summary_add(nbr, lsa);
+
+ /* Process only if the neighbor is opaque capable. */
+ if (CHECK_FLAG(nbr->options, OSPF_OPTION_O)) {
+ LSDB_LOOP (OPAQUE_LINK_LSDB(area), rn, lsa)
+ ospf_db_summary_add(nbr, lsa);
+ LSDB_LOOP (OPAQUE_AREA_LSDB(area), rn, lsa)
+ ospf_db_summary_add(nbr, lsa);
+ }
+
+ if (CHECK_FLAG(nbr->options, OSPF_OPTION_NP)) {
+ LSDB_LOOP (NSSA_LSDB(area), rn, lsa)
+ ospf_db_summary_add(nbr, lsa);
+ }
+
+ /* For Stub/NSSA area, we should not send Type-4 and Type-5 LSAs */
+ if (nbr->oi->type != OSPF_IFTYPE_VIRTUALLINK
+ && area->external_routing == OSPF_AREA_DEFAULT) {
+ LSDB_LOOP (ASBR_SUMMARY_LSDB(area), rn, lsa)
+ ospf_db_summary_add(nbr, lsa);
+ LSDB_LOOP (EXTERNAL_LSDB(nbr->oi->ospf), rn, lsa)
+ ospf_db_summary_add(nbr, lsa);
+ }
+
+ if (CHECK_FLAG(nbr->options, OSPF_OPTION_O)
+ && (nbr->oi->type != OSPF_IFTYPE_VIRTUALLINK
+ && area->external_routing == OSPF_AREA_DEFAULT))
+ LSDB_LOOP (OPAQUE_AS_LSDB(nbr->oi->ospf), rn, lsa)
+ ospf_db_summary_add(nbr, lsa);
+
+ return 0;
+}
+
+static int nsm_exchange_done(struct ospf_neighbor *nbr)
+{
+ if (ospf_ls_request_isempty(nbr))
+ return NSM_Full;
+
+ /* Send Link State Request. */
+ if (nbr->t_ls_req == NULL)
+ ospf_ls_req_send(nbr);
+
+ return NSM_Loading;
+}
+
+static int nsm_adj_ok(struct ospf_neighbor *nbr)
+{
+ int next_state = nbr->state;
+ int adj = nsm_should_adj(nbr);
+
+ if (nbr->state == NSM_TwoWay && adj == 1) {
+ next_state = NSM_ExStart;
+
+ /* Send proactive ARP requests */
+ ospf_proactively_arp(nbr);
+ } else if (nbr->state >= NSM_ExStart && adj == 0)
+ next_state = NSM_TwoWay;
+
+ return next_state;
+}
+
+/* Clear adjacency related state for a neighbour, intended where nbr
+ * transitions from > ExStart (i.e. a Full or forming adjacency)
+ * to <= ExStart.
+ */
+static void nsm_clear_adj(struct ospf_neighbor *nbr)
+{
+ /* Clear Database Summary list. */
+ if (!ospf_db_summary_isempty(nbr))
+ ospf_db_summary_clear(nbr);
+
+ /* Clear Link State Request list. */
+ if (!ospf_ls_request_isempty(nbr))
+ ospf_ls_request_delete_all(nbr);
+
+ /* Clear Link State Retransmission list. */
+ if (!ospf_ls_retransmit_isempty(nbr))
+ ospf_ls_retransmit_clear(nbr);
+
+ if (CHECK_FLAG(nbr->options, OSPF_OPTION_O))
+ UNSET_FLAG(nbr->options, OSPF_OPTION_O);
+}
+
+static int nsm_kill_nbr(struct ospf_neighbor *nbr)
+{
+ struct ospf_interface *oi = nbr->oi;
+ struct ospf_neighbor *on;
+ struct route_node *rn;
+
+ /* killing nbr_self is invalid */
+ if (nbr == nbr->oi->nbr_self) {
+ assert(nbr != nbr->oi->nbr_self);
+ return 0;
+ }
+
+ if (nbr->oi->type == OSPF_IFTYPE_NBMA && nbr->nbr_nbma != NULL) {
+ struct ospf_nbr_nbma *nbr_nbma = nbr->nbr_nbma;
+
+ nbr_nbma->nbr = NULL;
+ nbr_nbma->state_change = nbr->state_change;
+
+ nbr->nbr_nbma = NULL;
+
+ OSPF_POLL_TIMER_ON(nbr_nbma->t_poll, ospf_poll_timer,
+ nbr_nbma->v_poll);
+
+ if (IS_DEBUG_OSPF(nsm, NSM_EVENTS))
+ zlog_debug(
+ "NSM[%s:%pI4:%s]: Down (PollIntervalTimer scheduled)",
+ IF_NAME(nbr->oi),
+ &nbr->address.u.prefix4,
+ ospf_get_name(nbr->oi->ospf));
+ }
+
+ /*
+ * Do we have any neighbors that are also operating
+ * on this interface?
+ */
+ for (rn = route_top(oi->nbrs); rn; rn = route_next(rn)) {
+ on = rn->info;
+
+ if (!on)
+ continue;
+
+ if (on == nbr || on == oi->nbr_self)
+ continue;
+
+ /*
+ * on is in some state where we might be
+ * sending packets on this interface
+ */
+ if (on->state > NSM_Down) {
+ route_unlock_node(rn);
+ return 0;
+ }
+ }
+ /*
+ * If we get here we know that this interface
+ * has no neighbors in a state where we could
+ * be sending packets. Let's flush anything
+ * we got.
+ */
+ ospf_interface_fifo_flush(oi);
+ return 0;
+}
+
+/* Neighbor State Machine */
+const struct {
+ int (*func)(struct ospf_neighbor *);
+ int next_state;
+} NSM[OSPF_NSM_STATE_MAX][OSPF_NSM_EVENT_MAX] = {
+ {
+ /* DependUpon: dummy state. */
+ {NULL, NSM_DependUpon}, /* NoEvent */
+ {NULL, NSM_DependUpon}, /* HelloReceived */
+ {NULL, NSM_DependUpon}, /* Start */
+ {NULL, NSM_DependUpon}, /* 2-WayReceived */
+ {NULL, NSM_DependUpon}, /* NegotiationDone */
+ {NULL, NSM_DependUpon}, /* ExchangeDone */
+ {NULL, NSM_DependUpon}, /* BadLSReq */
+ {NULL, NSM_DependUpon}, /* LoadingDone */
+ {NULL, NSM_DependUpon}, /* AdjOK? */
+ {NULL, NSM_DependUpon}, /* SeqNumberMismatch */
+ {NULL, NSM_DependUpon}, /* 1-WayReceived */
+ {NULL, NSM_DependUpon}, /* KillNbr */
+ {NULL, NSM_DependUpon}, /* InactivityTimer */
+ {NULL, NSM_DependUpon}, /* LLDown */
+ },
+ {
+ /* Deleted: dummy state. */
+ {NULL, NSM_Deleted}, /* NoEvent */
+ {NULL, NSM_Deleted}, /* HelloReceived */
+ {NULL, NSM_Deleted}, /* Start */
+ {NULL, NSM_Deleted}, /* 2-WayReceived */
+ {NULL, NSM_Deleted}, /* NegotiationDone */
+ {NULL, NSM_Deleted}, /* ExchangeDone */
+ {NULL, NSM_Deleted}, /* BadLSReq */
+ {NULL, NSM_Deleted}, /* LoadingDone */
+ {NULL, NSM_Deleted}, /* AdjOK? */
+ {NULL, NSM_Deleted}, /* SeqNumberMismatch */
+ {NULL, NSM_Deleted}, /* 1-WayReceived */
+ {NULL, NSM_Deleted}, /* KillNbr */
+ {NULL, NSM_Deleted}, /* InactivityTimer */
+ {NULL, NSM_Deleted}, /* LLDown */
+ },
+ {
+ /* Down: */
+ {NULL, NSM_DependUpon}, /* NoEvent */
+ {nsm_hello_received, NSM_Init}, /* HelloReceived */
+ {nsm_start, NSM_Attempt}, /* Start */
+ {NULL, NSM_Down}, /* 2-WayReceived */
+ {NULL, NSM_Down}, /* NegotiationDone */
+ {NULL, NSM_Down}, /* ExchangeDone */
+ {NULL, NSM_Down}, /* BadLSReq */
+ {NULL, NSM_Down}, /* LoadingDone */
+ {NULL, NSM_Down}, /* AdjOK? */
+ {NULL, NSM_Down}, /* SeqNumberMismatch */
+ {NULL, NSM_Down}, /* 1-WayReceived */
+ {nsm_kill_nbr, NSM_Deleted}, /* KillNbr */
+ {nsm_kill_nbr, NSM_Deleted}, /* InactivityTimer */
+ {nsm_kill_nbr, NSM_Deleted}, /* LLDown */
+ },
+ {
+ /* Attempt: */
+ {NULL, NSM_DependUpon}, /* NoEvent */
+ {nsm_hello_received, NSM_Init}, /* HelloReceived */
+ {NULL, NSM_Attempt}, /* Start */
+ {NULL, NSM_Attempt}, /* 2-WayReceived */
+ {NULL, NSM_Attempt}, /* NegotiationDone */
+ {NULL, NSM_Attempt}, /* ExchangeDone */
+ {NULL, NSM_Attempt}, /* BadLSReq */
+ {NULL, NSM_Attempt}, /* LoadingDone */
+ {NULL, NSM_Attempt}, /* AdjOK? */
+ {NULL, NSM_Attempt}, /* SeqNumberMismatch */
+ {NULL, NSM_Attempt}, /* 1-WayReceived */
+ {nsm_kill_nbr, NSM_Deleted}, /* KillNbr */
+ {nsm_kill_nbr, NSM_Deleted}, /* InactivityTimer */
+ {nsm_kill_nbr, NSM_Deleted}, /* LLDown */
+ },
+ {
+ /* Init: */
+ {NULL, NSM_DependUpon}, /* NoEvent */
+ {nsm_hello_received, NSM_Init}, /* HelloReceived */
+ {NULL, NSM_Init}, /* Start */
+ {nsm_twoway_received, NSM_DependUpon}, /* 2-WayReceived */
+ {NULL, NSM_Init}, /* NegotiationDone */
+ {NULL, NSM_Init}, /* ExchangeDone */
+ {NULL, NSM_Init}, /* BadLSReq */
+ {NULL, NSM_Init}, /* LoadingDone */
+ {NULL, NSM_Init}, /* AdjOK? */
+ {NULL, NSM_Init}, /* SeqNumberMismatch */
+ {NULL, NSM_Init}, /* 1-WayReceived */
+ {nsm_kill_nbr, NSM_Deleted}, /* KillNbr */
+ {nsm_kill_nbr, NSM_Deleted}, /* InactivityTimer */
+ {nsm_kill_nbr, NSM_Deleted}, /* LLDown */
+ },
+ {
+ /* 2-Way: */
+ {NULL, NSM_DependUpon}, /* NoEvent */
+ {nsm_hello_received, NSM_TwoWay}, /* HelloReceived */
+ {NULL, NSM_TwoWay}, /* Start */
+ {NULL, NSM_TwoWay}, /* 2-WayReceived */
+ {NULL, NSM_TwoWay}, /* NegotiationDone */
+ {NULL, NSM_TwoWay}, /* ExchangeDone */
+ {NULL, NSM_TwoWay}, /* BadLSReq */
+ {NULL, NSM_TwoWay}, /* LoadingDone */
+ {nsm_adj_ok, NSM_DependUpon}, /* AdjOK? */
+ {NULL, NSM_TwoWay}, /* SeqNumberMismatch */
+ {NULL, NSM_Init}, /* 1-WayReceived */
+ {nsm_kill_nbr, NSM_Deleted}, /* KillNbr */
+ {nsm_kill_nbr, NSM_Deleted}, /* InactivityTimer */
+ {nsm_kill_nbr, NSM_Deleted}, /* LLDown */
+ },
+ {
+ /* ExStart: */
+ {NULL, NSM_DependUpon}, /* NoEvent */
+ {nsm_hello_received, NSM_ExStart}, /* HelloReceived */
+ {NULL, NSM_ExStart}, /* Start */
+ {NULL, NSM_ExStart}, /* 2-WayReceived */
+ {nsm_negotiation_done, NSM_Exchange}, /* NegotiationDone */
+ {NULL, NSM_ExStart}, /* ExchangeDone */
+ {NULL, NSM_ExStart}, /* BadLSReq */
+ {NULL, NSM_ExStart}, /* LoadingDone */
+ {nsm_adj_ok, NSM_DependUpon}, /* AdjOK? */
+ {NULL, NSM_ExStart}, /* SeqNumberMismatch */
+ {NULL, NSM_Init}, /* 1-WayReceived */
+ {nsm_kill_nbr, NSM_Deleted}, /* KillNbr */
+ {nsm_kill_nbr, NSM_Deleted}, /* InactivityTimer */
+ {nsm_kill_nbr, NSM_Deleted}, /* LLDown */
+ },
+ {
+ /* Exchange: */
+ {NULL, NSM_DependUpon}, /* NoEvent */
+ {nsm_hello_received, NSM_Exchange}, /* HelloReceived */
+ {NULL, NSM_Exchange}, /* Start */
+ {NULL, NSM_Exchange}, /* 2-WayReceived */
+ {NULL, NSM_Exchange}, /* NegotiationDone */
+ {nsm_exchange_done, NSM_DependUpon}, /* ExchangeDone */
+ {NULL, NSM_ExStart}, /* BadLSReq */
+ {NULL, NSM_Exchange}, /* LoadingDone */
+ {nsm_adj_ok, NSM_DependUpon}, /* AdjOK? */
+ {NULL, NSM_ExStart}, /* SeqNumberMismatch */
+ {NULL, NSM_Init}, /* 1-WayReceived */
+ {nsm_kill_nbr, NSM_Deleted}, /* KillNbr */
+ {nsm_kill_nbr, NSM_Deleted}, /* InactivityTimer */
+ {nsm_kill_nbr, NSM_Deleted}, /* LLDown */
+ },
+ {
+ /* Loading: */
+ {NULL, NSM_DependUpon}, /* NoEvent */
+ {nsm_hello_received, NSM_Loading}, /* HelloReceived */
+ {NULL, NSM_Loading}, /* Start */
+ {NULL, NSM_Loading}, /* 2-WayReceived */
+ {NULL, NSM_Loading}, /* NegotiationDone */
+ {NULL, NSM_Loading}, /* ExchangeDone */
+ {NULL, NSM_ExStart}, /* BadLSReq */
+ {NULL, NSM_Full}, /* LoadingDone */
+ {nsm_adj_ok, NSM_DependUpon}, /* AdjOK? */
+ {NULL, NSM_ExStart}, /* SeqNumberMismatch */
+ {NULL, NSM_Init}, /* 1-WayReceived */
+ {nsm_kill_nbr, NSM_Deleted}, /* KillNbr */
+ {nsm_kill_nbr, NSM_Deleted}, /* InactivityTimer */
+ {nsm_kill_nbr, NSM_Deleted}, /* LLDown */
+ },
+ {
+ /* Full: */
+ {NULL, NSM_DependUpon}, /* NoEvent */
+ {nsm_hello_received, NSM_Full}, /* HelloReceived */
+ {NULL, NSM_Full}, /* Start */
+ {NULL, NSM_Full}, /* 2-WayReceived */
+ {NULL, NSM_Full}, /* NegotiationDone */
+ {NULL, NSM_Full}, /* ExchangeDone */
+ {NULL, NSM_ExStart}, /* BadLSReq */
+ {NULL, NSM_Full}, /* LoadingDone */
+ {nsm_adj_ok, NSM_DependUpon}, /* AdjOK? */
+ {NULL, NSM_ExStart}, /* SeqNumberMismatch */
+ {NULL, NSM_Init}, /* 1-WayReceived */
+ {nsm_kill_nbr, NSM_Deleted}, /* KillNbr */
+ {nsm_kill_nbr, NSM_Deleted}, /* InactivityTimer */
+ {nsm_kill_nbr, NSM_Deleted}, /* LLDown */
+ },
+};
+
+static const char *const ospf_nsm_event_str[] = {
+ "NoEvent", "HelloReceived", "Start",
+ "2-WayReceived", "NegotiationDone", "ExchangeDone",
+ "BadLSReq", "LoadingDone", "AdjOK?",
+ "SeqNumberMismatch", "1-WayReceived", "KillNbr",
+ "InactivityTimer", "LLDown",
+};
+
+static void nsm_notice_state_change(struct ospf_neighbor *nbr, int next_state,
+ int event)
+{
+ /* Logging change of status. */
+ if (IS_DEBUG_OSPF(nsm, NSM_STATUS))
+ zlog_debug("NSM[%s:%pI4:%s]: State change %s -> %s (%s)",
+ IF_NAME(nbr->oi), &nbr->router_id,
+ ospf_get_name(nbr->oi->ospf),
+ lookup_msg(ospf_nsm_state_msg, nbr->state, NULL),
+ lookup_msg(ospf_nsm_state_msg, next_state, NULL),
+ ospf_nsm_event_str[event]);
+
+ /* Optionally notify about adjacency changes */
+ if (CHECK_FLAG(nbr->oi->ospf->config, OSPF_LOG_ADJACENCY_CHANGES)
+ && (CHECK_FLAG(nbr->oi->ospf->config, OSPF_LOG_ADJACENCY_DETAIL)
+ || (next_state == NSM_Full) || (next_state < nbr->state)))
+ zlog_notice(
+ "AdjChg: Nbr %pI4, NbrIP %pI4 (%s) on %s: %s -> %s (%s)",
+ &nbr->router_id, &nbr->src,
+ ospf_get_name(nbr->oi->ospf), IF_NAME(nbr->oi),
+ lookup_msg(ospf_nsm_state_msg, nbr->state, NULL),
+ lookup_msg(ospf_nsm_state_msg, next_state, NULL),
+ ospf_nsm_event_str[event]);
+
+ /* Advance in NSM */
+ if (next_state > nbr->state)
+ monotime(&nbr->ts_last_progress);
+ else /* regression in NSM */
+ {
+ monotime(&nbr->ts_last_regress);
+ nbr->last_regress_str = ospf_nsm_event_str[event];
+ }
+}
+
+static void nsm_change_state(struct ospf_neighbor *nbr, int state)
+{
+ struct ospf_interface *oi = nbr->oi;
+ struct ospf_area *vl_area = NULL;
+ uint8_t old_state;
+
+ /* Preserve old status. */
+ old_state = nbr->state;
+
+ /* Change to new status. */
+ nbr->state = state;
+
+ /* Statistics. */
+ nbr->state_change++;
+
+ if (oi->type == OSPF_IFTYPE_VIRTUALLINK)
+ vl_area = ospf_area_lookup_by_area_id(oi->ospf,
+ oi->vl_data->vl_area_id);
+
+ /* Generate NeighborChange ISM event.
+ *
+ * In response to NeighborChange, DR election is rerun. The information
+ * from the election process is required by the router-lsa construction.
+ *
+ * Therefore, trigger the event prior to refreshing the LSAs. */
+ switch (oi->state) {
+ case ISM_DROther:
+ case ISM_Backup:
+ case ISM_DR:
+ if ((old_state < NSM_TwoWay && state >= NSM_TwoWay)
+ || (old_state >= NSM_TwoWay && state < NSM_TwoWay))
+ OSPF_ISM_EVENT_EXECUTE(oi, ISM_NeighborChange);
+ break;
+ default:
+ /* ISM_PointToPoint -> ISM_Down, ISM_Loopback -> ISM_Down, etc.
+ */
+ break;
+ }
+
+ /* One of the neighboring routers changes to/from the FULL state. */
+ if ((old_state != NSM_Full && state == NSM_Full)
+ || (old_state == NSM_Full && state != NSM_Full)) {
+ if (state == NSM_Full) {
+ oi->full_nbrs++;
+ oi->area->full_nbrs++;
+
+ ospf_check_abr_status(oi->ospf);
+
+ if (oi->type == OSPF_IFTYPE_VIRTUALLINK && vl_area)
+ if (++vl_area->full_vls == 1)
+ ospf_schedule_abr_task(oi->ospf);
+ } else {
+ oi->full_nbrs--;
+ oi->area->full_nbrs--;
+
+ ospf_check_abr_status(oi->ospf);
+
+ if (oi->type == OSPF_IFTYPE_VIRTUALLINK && vl_area)
+ if (vl_area->full_vls > 0)
+ if (--vl_area->full_vls == 0)
+ ospf_schedule_abr_task(
+ oi->ospf);
+ }
+
+ if (CHECK_FLAG(oi->ospf->config, OSPF_LOG_ADJACENCY_DETAIL))
+ zlog_info(
+ "%s:[%pI4:%s], %s -> %s): scheduling new router-LSA origination",
+ __func__, &nbr->router_id,
+ ospf_get_name(oi->ospf),
+ lookup_msg(ospf_nsm_state_msg, old_state, NULL),
+ lookup_msg(ospf_nsm_state_msg, state, NULL));
+
+ /* Dont originate router LSA if the current
+ * router is acting as a HELPER for this neighbour.
+ */
+ if (!OSPF_GR_IS_ACTIVE_HELPER(nbr))
+ ospf_router_lsa_update_area(oi->area);
+
+ if (oi->type == OSPF_IFTYPE_VIRTUALLINK) {
+ vl_area = ospf_area_lookup_by_area_id(
+ oi->ospf, oi->vl_data->vl_area_id);
+
+ if (vl_area)
+ ospf_router_lsa_update_area(vl_area);
+ }
+
+ /* Dont originate/flush network LSA if the current
+ * router is acting as a HELPER for this neighbour.
+ */
+ if (!OSPF_GR_IS_ACTIVE_HELPER(nbr)) {
+ /* Originate network-LSA. */
+ if (oi->state == ISM_DR) {
+ if (oi->network_lsa_self
+ && oi->full_nbrs == 0) {
+ ospf_lsa_flush_area(
+ oi->network_lsa_self, oi->area);
+ ospf_lsa_unlock(&oi->network_lsa_self);
+ oi->network_lsa_self = NULL;
+ } else
+ 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);
+
+ /* State changes from > ExStart to <= ExStart should clear any Exchange
+ * or Full/LSA Update related lists and state.
+ * Potential causal events: BadLSReq, SeqNumberMismatch, AdjOK?
+ */
+ if ((old_state > NSM_ExStart) && (state <= NSM_ExStart))
+ nsm_clear_adj(nbr);
+
+ /* Start DD exchange protocol */
+ if (state == NSM_ExStart) {
+ if (nbr->dd_seqnum == 0)
+ nbr->dd_seqnum = (uint32_t)frr_weak_random();
+ else
+ nbr->dd_seqnum++;
+
+ nbr->dd_flags =
+ OSPF_DD_FLAG_I | OSPF_DD_FLAG_M | OSPF_DD_FLAG_MS;
+ if (CHECK_FLAG(oi->ospf->config, OSPF_LOG_ADJACENCY_DETAIL))
+ zlog_info(
+ "%s: Initializing [DD]: %pI4 with seqnum:%x , flags:%x",
+ ospf_get_name(oi->ospf), &nbr->router_id,
+ nbr->dd_seqnum, nbr->dd_flags);
+ ospf_db_desc_send(nbr);
+ }
+
+ /* clear cryptographic sequence number */
+ if (state == NSM_Down)
+ nbr->crypt_seqnum = 0;
+
+ if (nbr->bfd_session)
+ ospf_bfd_trigger_event(nbr, old_state, state);
+
+ /* Preserve old status? */
+}
+
+/* Execute NSM event process. */
+void ospf_nsm_event(struct thread *thread)
+{
+ int event;
+ int next_state;
+ struct ospf_neighbor *nbr;
+
+ nbr = THREAD_ARG(thread);
+ event = THREAD_VAL(thread);
+
+ if (IS_DEBUG_OSPF(nsm, NSM_EVENTS))
+ zlog_debug("NSM[%s:%pI4:%s]: %s (%s)", IF_NAME(nbr->oi),
+ &nbr->router_id,
+ ospf_get_name(nbr->oi->ospf),
+ lookup_msg(ospf_nsm_state_msg, nbr->state, NULL),
+ ospf_nsm_event_str[event]);
+
+ next_state = NSM[nbr->state][event].next_state;
+
+ /* Call function. */
+ if (NSM[nbr->state][event].func != NULL) {
+ int func_state = (*(NSM[nbr->state][event].func))(nbr);
+
+ if (NSM[nbr->state][event].next_state == NSM_DependUpon)
+ next_state = func_state;
+ else if (func_state) {
+ /* There's a mismatch between the FSM tables and what an
+ * FSM
+ * action/state-change function returned. State changes
+ * which
+ * do not have conditional/DependUpon next-states should
+ * not
+ * try set next_state.
+ */
+ flog_err(
+ EC_OSPF_FSM_INVALID_STATE,
+ "NSM[%s:%pI4:%s]: %s (%s): Warning: action tried to change next_state to %s",
+ IF_NAME(nbr->oi), &nbr->router_id,
+ ospf_get_name(nbr->oi->ospf),
+ lookup_msg(ospf_nsm_state_msg, nbr->state,
+ NULL),
+ ospf_nsm_event_str[event],
+ lookup_msg(ospf_nsm_state_msg, func_state,
+ NULL));
+ }
+ }
+
+ assert(next_state != NSM_DependUpon);
+
+ /* If state is changed. */
+ if (next_state != nbr->state) {
+ int old_state = nbr->state;
+
+ nsm_notice_state_change(nbr, next_state, event);
+ nsm_change_state(nbr, next_state);
+
+ hook_call(ospf_nsm_change, nbr, next_state, old_state);
+ }
+
+ /* Make sure timer is set. */
+ nsm_timer_set(nbr);
+
+ /* When event is NSM_KillNbr, InactivityTimer or LLDown, the neighbor
+ * is deleted.
+ *
+ * Rather than encode knowledge here of which events lead to NBR
+ * delete, we take our cue from the NSM table, via the dummy
+ * 'Deleted' neighbour state.
+ */
+ if (nbr->state == NSM_Deleted)
+ ospf_nbr_delete(nbr);
+}
+
+/* Check loading state. */
+void ospf_check_nbr_loading(struct ospf_neighbor *nbr)
+{
+ if (nbr->state == NSM_Loading) {
+ if (ospf_ls_request_isempty(nbr))
+ OSPF_NSM_EVENT_SCHEDULE(nbr, NSM_LoadingDone);
+ else if (nbr->ls_req_last == NULL)
+ ospf_ls_req_event(nbr);
+ }
+}