mirror_frr/ospfd/ospf_nsm.c
rgirada 89eb4727de ospfd: GR helper functionality change in helper exit
Description:
	As per the RFC 3623 section 3.2,
	OSPF nbr shouldn't be deleted even in unsuccessful helper exit.

	1. Made the changes to keep neighbour even after exit.
	2. Restart the dead timer after expiry in helper. Otherwise, Restarter
	   will be in FULL state in helper forever until it receives the 'hello'.

Signed-off-by: Rajesh Girada <rgirada@vmware.com>
2021-10-01 11:59:11 -07:00

864 lines
26 KiB
C

/*
* 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 int 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);
}
return 0;
}
static int 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);
return 0;
}
/* 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:
OSPF_NSM_TIMER_OFF(nbr->t_inactivity);
OSPF_NSM_TIMER_OFF(nbr->t_hello_reply);
/* fallthru */
case NSM_Attempt:
case NSM_Init:
case NSM_TwoWay:
OSPF_NSM_TIMER_OFF(nbr->t_db_desc);
OSPF_NSM_TIMER_OFF(nbr->t_ls_upd);
OSPF_NSM_TIMER_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);
OSPF_NSM_TIMER_OFF(nbr->t_ls_upd);
OSPF_NSM_TIMER_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))
OSPF_NSM_TIMER_OFF(nbr->t_db_desc);
break;
case NSM_Loading:
case NSM_Full:
default:
OSPF_NSM_TIMER_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_packet_received(struct ospf_neighbor *nbr)
{
/* Start or Restart Inactivity Timer. */
OSPF_NSM_TIMER_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)
OSPF_POLL_TIMER_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)
OSPF_POLL_TIMER_OFF(nbr->nbr_nbma->t_poll);
OSPF_NSM_TIMER_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)
{
/* 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));
}
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}, /* PacketReceived */
{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}, /* PacketReceived */
{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_packet_received, NSM_Init}, /* PacketReceived */
{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_packet_received, NSM_Init}, /* PacketReceived */
{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_packet_received, NSM_Init}, /* PacketReceived */
{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_packet_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_packet_received, NSM_ExStart}, /* PacaketReceived */
{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_packet_received, NSM_Exchange}, /* PacketReceived */
{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_packet_received, NSM_Loading}, /* PacketReceived */
{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_packet_received, NSM_Full}, /* PacketReceived */
{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", "PacketReceived", "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(%s) on %s: %s -> %s (%s)",
&nbr->router_id,
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",
(oi->ospf->name) ? oi->ospf->name
: VRF_DEFAULT_NAME,
&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. */
int 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);
return 0;
}
/* 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);
}
}