From endo@suri.co.jp Fri Jan 17 23:36:30 2003

Date: Fri, 20 Dec 2002 17:58:43 +0900
From: Masahiko Endo <endo@suri.co.jp>
Reply-To: zebra@zebra.org
To: zebra@zebra.org
Cc: kunihiro@zebra.org
Subject: [zebra 16824] [PATCH] nsm_kill_neighbor

    [ The following text is in the "ISO-2022-JP" character set. ]
    [ Your display is set for the "ISO-8859-1" character set.  ]
    [ Some characters may be displayed incorrectly. ]

Hi Ishiguro-san,

Here is my problem analysis against the case that the ospfd crashes
when an interface is brought down.

When the ospfd receives a ZEBRA message "ZEBRA_INTERFACE_DOWN" from
zebra daemon, the ospfd performs bunch of ospf-interface cleanup for
the notified zebra-interface.
There are cases that neighbor instance "nbr", which will be removed
afterward, may scheduled in the NSM thread event queue. And when the
NSM event thread is fired, dereference for this already freed "nbr"
pointer causes SIGSEGV.
Please take a look at following timeline of processing sequences.
This commit is contained in:
paul 2003-01-17 23:48:42 +00:00
parent e04ab74d17
commit 2d59836a4f
3 changed files with 1866 additions and 0 deletions

658
ospfd/ospf_ism.c Normal file
View File

@ -0,0 +1,658 @@
/*
* OSPF version 2 Interface 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 GNU Zebra; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#include <zebra.h>
#include "thread.h"
#include "linklist.h"
#include "prefix.h"
#include "if.h"
#include "table.h"
#include "log.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_dump.h"
#include "ospfd/ospf_packet.h"
#include "ospfd/ospf_flood.h"
#include "ospfd/ospf_abr.h"
/* elect DR and BDR. Refer to RFC2319 section 9.4 */
struct ospf_neighbor *
ospf_dr_election_sub (list routers)
{
listnode node;
struct ospf_neighbor *nbr, *max = NULL;
/* Choose highest router priority.
In case of tie, choose highest Router ID. */
for (node = listhead (routers); node; nextnode (node))
{
nbr = getdata (node);
if (max == NULL)
max = nbr;
else
{
if (max->priority < nbr->priority)
max = nbr;
else if (max->priority == nbr->priority)
if (IPV4_ADDR_CMP (&max->router_id, &nbr->router_id) < 0)
max = nbr;
}
}
return max;
}
struct ospf_neighbor *
ospf_elect_dr (struct ospf_interface *oi, list el_list)
{
list dr_list;
listnode node;
struct ospf_neighbor *nbr, *dr = NULL, *bdr = NULL;
dr_list = list_new ();
/* Add neighbors to the list. */
for (node = listhead (el_list); node; nextnode (node))
{
nbr = getdata (node);
/* neighbor declared to be DR. */
if (NBR_IS_DR (nbr))
listnode_add (dr_list, nbr);
/* Preserve neighbor BDR. */
if (IPV4_ADDR_SAME (&BDR (oi), &nbr->address.u.prefix4))
bdr = nbr;
}
/* Elect Designated Router. */
if (listcount (dr_list) > 0)
dr = ospf_dr_election_sub (dr_list);
else
dr = bdr;
/* Set DR to interface. */
if (dr)
{
DR (oi) = dr->address.u.prefix4;
dr->d_router = dr->address.u.prefix4;
}
else
DR (oi).s_addr = 0;
list_delete (dr_list);
return dr;
}
struct ospf_neighbor *
ospf_elect_bdr (struct ospf_interface *oi, list el_list)
{
list bdr_list, no_dr_list;
listnode node;
struct ospf_neighbor *nbr, *bdr = NULL;
bdr_list = list_new ();
no_dr_list = list_new ();
/* Add neighbors to the list. */
for (node = listhead (el_list); node; nextnode (node))
{
nbr = getdata (node);
/* neighbor declared to be DR. */
if (NBR_IS_DR (nbr))
continue;
/* neighbor declared to be BDR. */
if (NBR_IS_BDR (nbr))
listnode_add (bdr_list, nbr);
listnode_add (no_dr_list, nbr);
}
/* Elect Backup Designated Router. */
if (listcount (bdr_list) > 0)
bdr = ospf_dr_election_sub (bdr_list);
else
bdr = ospf_dr_election_sub (no_dr_list);
/* Set BDR to interface. */
if (bdr)
{
BDR (oi) = bdr->address.u.prefix4;
bdr->bd_router = bdr->address.u.prefix4;
}
else
BDR (oi).s_addr = 0;
list_delete (bdr_list);
list_delete (no_dr_list);
return bdr;
}
int
ospf_ism_state (struct ospf_interface *oi)
{
if (IPV4_ADDR_SAME (&DR (oi), &oi->address->u.prefix4))
return ISM_DR;
else if (IPV4_ADDR_SAME (&BDR (oi), &oi->address->u.prefix4))
return ISM_Backup;
else
return ISM_DROther;
}
void
ospf_dr_eligible_routers (struct route_table *nbrs, list el_list)
{
struct route_node *rn;
struct ospf_neighbor *nbr;
for (rn = route_top (nbrs); rn; rn = route_next (rn))
if ((nbr = rn->info) != NULL)
/* Ignore 0.0.0.0 node*/
if (nbr->router_id.s_addr != 0)
/* Is neighbor eligible? */
if (nbr->priority != 0)
/* Is neighbor upper 2-Way? */
if (nbr->state >= NSM_TwoWay)
listnode_add (el_list, nbr);
}
/* Generate AdjOK? NSM event. */
void
ospf_dr_change (struct route_table *nbrs)
{
struct route_node *rn;
struct ospf_neighbor *nbr;
for (rn = route_top (nbrs); rn; rn = route_next (rn))
if ((nbr = rn->info) != NULL)
/* Ignore 0.0.0.0 node*/
if (nbr->router_id.s_addr != 0)
/* Is neighbor upper 2-Way? */
if (nbr->state >= NSM_TwoWay)
/* Ignore myself. */
if (!IPV4_ADDR_SAME (&nbr->router_id, &ospf_top->router_id))
OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_AdjOK);
}
int
ospf_dr_election (struct ospf_interface *oi)
{
struct in_addr old_dr, old_bdr;
int old_state, new_state;
list el_list;
struct ospf_neighbor *dr, *bdr;
/* backup current values. */
old_dr = DR (oi);
old_bdr = BDR (oi);
old_state = oi->state;
el_list = list_new ();
/* List eligible routers. */
ospf_dr_eligible_routers (oi->nbrs, el_list);
/* First election of DR and BDR. */
bdr = ospf_elect_bdr (oi, el_list);
dr = ospf_elect_dr (oi, el_list);
new_state = ospf_ism_state (oi);
zlog_info ("DR-Election[1st]: Backup %s", inet_ntoa (BDR (oi)));
zlog_info ("DR-Election[1st]: DR %s", inet_ntoa (DR (oi)));
if (new_state != old_state &&
!(new_state == ISM_DROther && old_state < ISM_DROther))
{
ospf_elect_bdr (oi, el_list);
ospf_elect_dr (oi, el_list);
new_state = ospf_ism_state (oi);
zlog_info ("DR-Election[2nd]: Backup %s", inet_ntoa (BDR (oi)));
zlog_info ("DR-Election[2nd]: DR %s", inet_ntoa (DR (oi)));
}
list_delete (el_list);
/* if DR or BDR changes, cause AdjOK? neighbor event. */
if (!IPV4_ADDR_SAME (&old_dr, &DR (oi)) ||
!IPV4_ADDR_SAME (&old_bdr, &BDR (oi)))
ospf_dr_change (oi->nbrs);
if (oi->type == OSPF_IFTYPE_BROADCAST || oi->type == OSPF_IFTYPE_POINTOPOINT)
{
/* Multicast group change. */
if ((old_state != ISM_DR && old_state != ISM_Backup) &&
(new_state == ISM_DR || new_state == ISM_Backup))
ospf_if_add_alldrouters (ospf_top, oi->address, oi->ifp->ifindex);
else if ((old_state == ISM_DR || old_state == ISM_Backup) &&
(new_state != ISM_DR && new_state != ISM_Backup))
ospf_if_drop_alldrouters (ospf_top, oi->address, oi->ifp->ifindex);
}
return new_state;
}
int
ospf_hello_timer (struct thread *thread)
{
struct ospf_interface *oi;
oi = THREAD_ARG (thread);
oi->t_hello = NULL;
if (IS_DEBUG_OSPF (ism, ISM_TIMERS))
zlog (NULL, LOG_DEBUG, "ISM[%s]: Timer (Hello timer expire)",
IF_NAME (oi));
/* Sending hello packet. */
ospf_hello_send (oi);
/* Hello timer set. */
OSPF_ISM_TIMER_ON (oi->t_hello, ospf_hello_timer,
OSPF_IF_PARAM (oi, v_hello));
return 0;
}
int
ospf_wait_timer (struct thread *thread)
{
struct ospf_interface *oi;
oi = THREAD_ARG (thread);
oi->t_wait = NULL;
if (IS_DEBUG_OSPF (ism, ISM_TIMERS))
zlog (NULL, LOG_DEBUG, "ISM[%s]: Timer (Wait timer expire)",
IF_NAME (oi));
OSPF_ISM_EVENT_SCHEDULE (oi, ISM_WaitTimer);
return 0;
}
/* Hook function called after ospf ISM event is occured. And vty's
network command invoke this function after making interface
structure. */
void
ism_timer_set (struct ospf_interface *oi)
{
switch (oi->state)
{
case ISM_Down:
/* First entry point of ospf interface state machine. In this state
interface parameters must be set to initial values, and timers are
reset also. */
OSPF_ISM_TIMER_OFF (oi->t_hello);
OSPF_ISM_TIMER_OFF (oi->t_wait);
OSPF_ISM_TIMER_OFF (oi->t_ls_ack);
break;
case ISM_Loopback:
/* In this state, the interface may be looped back and will be
unavailable for regular data traffic. */
OSPF_ISM_TIMER_OFF (oi->t_hello);
OSPF_ISM_TIMER_OFF (oi->t_wait);
OSPF_ISM_TIMER_OFF (oi->t_ls_ack);
break;
case ISM_Waiting:
/* The router is trying to determine the identity of DRouter and
BDRouter. The router begin to receive and send Hello Packets. */
/* send first hello immediately */
OSPF_ISM_TIMER_ON (oi->t_hello, ospf_hello_timer, 1);
OSPF_ISM_TIMER_ON (oi->t_wait, ospf_wait_timer,
OSPF_IF_PARAM (oi, v_wait));
OSPF_ISM_TIMER_OFF (oi->t_ls_ack);
break;
case ISM_PointToPoint:
/* The interface connects to a physical Point-to-point network or
virtual link. The router attempts to form an adjacency with
neighboring router. Hello packets are also sent. */
/* send first hello immediately */
OSPF_ISM_TIMER_ON (oi->t_hello, ospf_hello_timer, 1);
OSPF_ISM_TIMER_OFF (oi->t_wait);
OSPF_ISM_TIMER_ON (oi->t_ls_ack, ospf_ls_ack_timer, oi->v_ls_ack);
break;
case ISM_DROther:
/* The network type of the interface is broadcast or NBMA network,
and the router itself is neither Designated Router nor
Backup Designated Router. */
OSPF_ISM_TIMER_ON (oi->t_hello, ospf_hello_timer,
OSPF_IF_PARAM (oi, v_hello));
OSPF_ISM_TIMER_OFF (oi->t_wait);
OSPF_ISM_TIMER_ON (oi->t_ls_ack, ospf_ls_ack_timer, oi->v_ls_ack);
break;
case ISM_Backup:
/* The network type of the interface is broadcast os NBMA network,
and the router is Backup Designated Router. */
OSPF_ISM_TIMER_ON (oi->t_hello, ospf_hello_timer,
OSPF_IF_PARAM (oi, v_hello));
OSPF_ISM_TIMER_OFF (oi->t_wait);
OSPF_ISM_TIMER_ON (oi->t_ls_ack, ospf_ls_ack_timer, oi->v_ls_ack);
break;
case ISM_DR:
/* The network type of the interface is broadcast or NBMA network,
and the router is Designated Router. */
OSPF_ISM_TIMER_ON (oi->t_hello, ospf_hello_timer,
OSPF_IF_PARAM (oi, v_hello));
OSPF_ISM_TIMER_OFF (oi->t_wait);
OSPF_ISM_TIMER_ON (oi->t_ls_ack, ospf_ls_ack_timer, oi->v_ls_ack);
break;
}
}
int
ism_stop (struct ospf_interface *oi)
{
return 0;
}
int
ism_interface_up (struct ospf_interface *oi)
{
int next_state = 0;
/* if network type is point-to-point, Point-to-MultiPoint or virtual link,
the state transitions to Point-to-Point. */
if (oi->type == OSPF_IFTYPE_POINTOPOINT ||
oi->type == OSPF_IFTYPE_POINTOMULTIPOINT ||
oi->type == OSPF_IFTYPE_VIRTUALLINK)
next_state = ISM_PointToPoint;
/* Else if the router is not eligible to DR, the state transitions to
DROther. */
else if (PRIORITY (oi) == 0) /* router is eligible? */
next_state = ISM_DROther;
else
/* Otherwise, the state transitions to Waiting. */
next_state = ISM_Waiting;
if (oi->type == OSPF_IFTYPE_NBMA)
ospf_nbr_nbma_if_update (oi);
/* ospf_ism_event (t); */
return next_state;
}
int
ism_loop_ind (struct ospf_interface *oi)
{
int ret = 0;
/* call ism_interface_down. */
/* ret = ism_interface_down (oi); */
return ret;
}
/* Interface down event handler. */
int
ism_interface_down (struct ospf_interface *oi)
{
ospf_if_cleanup (oi);
return 0;
}
int
ism_backup_seen (struct ospf_interface *oi)
{
return ospf_dr_election (oi);
}
int
ism_wait_timer (struct ospf_interface *oi)
{
return ospf_dr_election (oi);
}
int
ism_neighbor_change (struct ospf_interface *oi)
{
return ospf_dr_election (oi);
}
int
ism_ignore (struct ospf_interface *oi)
{
if (IS_DEBUG_OSPF (ism, ISM_EVENTS))
zlog (NULL, LOG_INFO, "ISM[%s]: ism_ignore called", IF_NAME (oi));
return 0;
}
/* Interface State Machine */
struct {
int (*func) ();
int next_state;
} ISM [OSPF_ISM_STATE_MAX][OSPF_ISM_EVENT_MAX] =
{
{
/* DependUpon: dummy state. */
{ ism_ignore, ISM_DependUpon }, /* NoEvent */
{ ism_ignore, ISM_DependUpon }, /* InterfaceUp */
{ ism_ignore, ISM_DependUpon }, /* WaitTimer */
{ ism_ignore, ISM_DependUpon }, /* BackupSeen */
{ ism_ignore, ISM_DependUpon }, /* NeighborChange */
{ ism_ignore, ISM_DependUpon }, /* LoopInd */
{ ism_ignore, ISM_DependUpon }, /* UnloopInd */
{ ism_ignore, ISM_DependUpon }, /* InterfaceDown */
},
{
/* Down:*/
{ ism_ignore, ISM_DependUpon }, /* NoEvent */
{ ism_interface_up, ISM_DependUpon }, /* InterfaceUp */
{ ism_ignore, ISM_Down }, /* WaitTimer */
{ ism_ignore, ISM_Down }, /* BackupSeen */
{ ism_ignore, ISM_Down }, /* NeighborChange */
{ ism_loop_ind, ISM_Loopback }, /* LoopInd */
{ ism_ignore, ISM_Down }, /* UnloopInd */
{ ism_interface_down, ISM_Down }, /* InterfaceDown */
},
{
/* Loopback: */
{ ism_ignore, ISM_DependUpon }, /* NoEvent */
{ ism_ignore, ISM_Loopback }, /* InterfaceUp */
{ ism_ignore, ISM_Loopback }, /* WaitTimer */
{ ism_ignore, ISM_Loopback }, /* BackupSeen */
{ ism_ignore, ISM_Loopback }, /* NeighborChange */
{ ism_ignore, ISM_Loopback }, /* LoopInd */
{ ism_ignore, ISM_Down }, /* UnloopInd */
{ ism_interface_down, ISM_Down }, /* InterfaceDown */
},
{
/* Waiting: */
{ ism_ignore, ISM_DependUpon }, /* NoEvent */
{ ism_ignore, ISM_Waiting }, /* InterfaceUp */
{ ism_wait_timer, ISM_DependUpon }, /* WaitTimer */
{ ism_backup_seen, ISM_DependUpon }, /* BackupSeen */
{ ism_ignore, ISM_Waiting }, /* NeighborChange */
{ ism_loop_ind, ISM_Loopback }, /* LoopInd */
{ ism_ignore, ISM_Waiting }, /* UnloopInd */
{ ism_interface_down, ISM_Down }, /* InterfaceDown */
},
{
/* Point-to-Point: */
{ ism_ignore, ISM_DependUpon }, /* NoEvent */
{ ism_ignore, ISM_PointToPoint }, /* InterfaceUp */
{ ism_ignore, ISM_PointToPoint }, /* WaitTimer */
{ ism_ignore, ISM_PointToPoint }, /* BackupSeen */
{ ism_ignore, ISM_PointToPoint }, /* NeighborChange */
{ ism_loop_ind, ISM_Loopback }, /* LoopInd */
{ ism_ignore, ISM_PointToPoint }, /* UnloopInd */
{ ism_interface_down, ISM_Down }, /* InterfaceDown */
},
{
/* DROther: */
{ ism_ignore, ISM_DependUpon }, /* NoEvent */
{ ism_ignore, ISM_DROther }, /* InterfaceUp */
{ ism_ignore, ISM_DROther }, /* WaitTimer */
{ ism_ignore, ISM_DROther }, /* BackupSeen */
{ ism_neighbor_change, ISM_DependUpon }, /* NeighborChange */
{ ism_loop_ind, ISM_Loopback }, /* LoopInd */
{ ism_ignore, ISM_DROther }, /* UnloopInd */
{ ism_interface_down, ISM_Down }, /* InterfaceDown */
},
{
/* Backup: */
{ ism_ignore, ISM_DependUpon }, /* NoEvent */
{ ism_ignore, ISM_Backup }, /* InterfaceUp */
{ ism_ignore, ISM_Backup }, /* WaitTimer */
{ ism_ignore, ISM_Backup }, /* BackupSeen */
{ ism_neighbor_change, ISM_DependUpon }, /* NeighborChange */
{ ism_loop_ind, ISM_Loopback }, /* LoopInd */
{ ism_ignore, ISM_Backup }, /* UnloopInd */
{ ism_interface_down, ISM_Down }, /* InterfaceDown */
},
{
/* DR: */
{ ism_ignore, ISM_DependUpon }, /* NoEvent */
{ ism_ignore, ISM_DR }, /* InterfaceUp */
{ ism_ignore, ISM_DR }, /* WaitTimer */
{ ism_ignore, ISM_DR }, /* BackupSeen */
{ ism_neighbor_change, ISM_DependUpon }, /* NeighborChange */
{ ism_loop_ind, ISM_Loopback }, /* LoopInd */
{ ism_ignore, ISM_DR }, /* UnloopInd */
{ ism_interface_down, ISM_Down }, /* InterfaceDown */
},
};
static char *ospf_ism_event_str[] =
{
"NoEvent",
"InterfaceUp",
"WaitTimer",
"BackupSeen",
"NeighborChange",
"LoopInd",
"UnLoopInd",
"InterfaceDown",
};
void
ism_change_state (struct ospf_interface *oi, int state)
{
int old_state;
struct ospf_lsa *lsa;
/* Logging change of state. */
if (IS_DEBUG_OSPF (ism, ISM_STATUS))
zlog (NULL, LOG_INFO, "ISM[%s]: State change %s -> %s", IF_NAME (oi),
LOOKUP (ospf_ism_state_msg, oi->state),
LOOKUP (ospf_ism_state_msg, state));
old_state = oi->state;
oi->state = state;
oi->state_change++;
if (old_state == ISM_Down || state == ISM_Down)
ospf_check_abr_status();
/* Originate router-LSA. */
if (oi->area)
{
if (state == ISM_Down)
{
if (oi->area->act_ints > 0)
oi->area->act_ints--;
}
else if (old_state == ISM_Down)
oi->area->act_ints++;
/* schedule router-LSA originate. */
ospf_router_lsa_timer_add (oi->area);
}
/* Originate network-LSA. */
if (old_state != ISM_DR && state == ISM_DR)
ospf_network_lsa_timer_add (oi);
else if (old_state == ISM_DR && state != ISM_DR)
{
/* Free self originated network LSA. */
lsa = oi->network_lsa_self;
if (lsa)
{
ospf_lsa_flush_area (lsa, oi->area);
OSPF_TIMER_OFF (oi->t_network_lsa_self);
}
ospf_lsa_unlock (oi->network_lsa_self);
oi->network_lsa_self = NULL;
}
#ifdef HAVE_OPAQUE_LSA
ospf_opaque_ism_change (oi, old_state);
#endif /* HAVE_OPAQUE_LSA */
/* Check area border status. */
ospf_check_abr_status ();
}
/* Execute ISM event process. */
int
ospf_ism_event (struct thread *thread)
{
int event;
int next_state;
struct ospf_interface *oi;
oi = THREAD_ARG (thread);
event = THREAD_VAL (thread);
/* Call function. */
next_state = (*(ISM [oi->state][event].func))(oi);
if (! next_state)
next_state = ISM [oi->state][event].next_state;
if (IS_DEBUG_OSPF (ism, ISM_EVENTS))
zlog (NULL, LOG_INFO, "ISM[%s]: %s (%s)", IF_NAME (oi),
LOOKUP (ospf_ism_state_msg, oi->state),
ospf_ism_event_str[event]);
/* If state is changed. */
if (next_state != oi->state)
ism_change_state (oi, next_state);
/* Make sure timer is set. */
ism_timer_set (oi);
return 0;
}

329
ospfd/ospf_neighbor.c Normal file
View File

@ -0,0 +1,329 @@
/*
* OSPF Neighbor functions.
* 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 GNU Zebra; see the file COPYING. If not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <zebra.h>
#include "linklist.h"
#include "prefix.h"
#include "memory.h"
#include "command.h"
#include "thread.h"
#include "stream.h"
#include "table.h"
#include "log.h"
#include "ospfd/ospfd.h"
#include "ospfd/ospf_interface.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_packet.h"
#include "ospfd/ospf_network.h"
#include "ospfd/ospf_flood.h"
#include "ospfd/ospf_dump.h"
struct ospf_neighbor *
ospf_nbr_new (struct ospf_interface *oi)
{
struct ospf_neighbor *nbr;
/* Allcate new neighbor. */
nbr = XMALLOC (MTYPE_OSPF_NEIGHBOR, sizeof (struct ospf_neighbor));
memset (nbr, 0, sizeof (struct ospf_neighbor));
/* Relate neighbor to the interface. */
nbr->oi = oi;
/* Set default values. */
nbr->state = NSM_Down;
/* Set inheritance values. */
nbr->v_inactivity = OSPF_IF_PARAM (oi, v_wait);
nbr->v_db_desc = OSPF_IF_PARAM (oi, retransmit_interval);
nbr->v_ls_req = OSPF_IF_PARAM (oi, retransmit_interval);
nbr->v_ls_upd = OSPF_IF_PARAM (oi, retransmit_interval);
nbr->priority = -1;
/* DD flags. */
nbr->dd_flags = OSPF_DD_FLAG_MS|OSPF_DD_FLAG_M|OSPF_DD_FLAG_I;
/* Last received and sent DD. */
nbr->last_send = NULL;
nbr->nbr_nbma = NULL;
ospf_lsdb_init (&nbr->db_sum);
ospf_lsdb_init (&nbr->ls_rxmt);
ospf_lsdb_init (&nbr->ls_req);
nbr->crypt_seqnum = 0;
return nbr;
}
void
ospf_nbr_free (struct ospf_neighbor *nbr)
{
/* Free DB summary list. */
if (ospf_db_summary_count (nbr))
ospf_db_summary_clear (nbr);
/* ospf_db_summary_delete_all (nbr); */
/* Free ls request list. */
if (ospf_ls_request_count (nbr))
ospf_ls_request_delete_all (nbr);
/* Free retransmit list. */
if (ospf_ls_retransmit_count (nbr))
ospf_ls_retransmit_clear (nbr);
/* Cleanup LSDBs. */
ospf_lsdb_cleanup (&nbr->db_sum);
ospf_lsdb_cleanup (&nbr->ls_req);
ospf_lsdb_cleanup (&nbr->ls_rxmt);
/* Clear last send packet. */
if (nbr->last_send)
ospf_packet_free (nbr->last_send);
if (nbr->nbr_nbma)
{
nbr->nbr_nbma->nbr = NULL;
nbr->nbr_nbma = NULL;
}
/* Cancel all timers. */
OSPF_NSM_TIMER_OFF (nbr->t_inactivity);
OSPF_NSM_TIMER_OFF (nbr->t_db_desc);
OSPF_NSM_TIMER_OFF (nbr->t_ls_req);
OSPF_NSM_TIMER_OFF (nbr->t_ls_upd);
/* Cancel all events. *//* Thread lookup cost would be negligible. */
thread_cancel_event (master, nbr);
XFREE (MTYPE_OSPF_NEIGHBOR, nbr);
}
/* Delete specified OSPF neighbor from interface. */
void
ospf_nbr_delete (struct ospf_neighbor *nbr)
{
struct ospf_interface *oi;
struct route_node *rn;
struct prefix p;
oi = nbr->oi;
/* Unlink ospf neighbor from the interface. */
p.family = AF_INET;
p.prefixlen = IPV4_MAX_BITLEN;
p.u.prefix4 = nbr->src;
rn = route_node_lookup (oi->nbrs, &p);
if (rn)
{
if (rn->info)
{
rn->info = NULL;
route_unlock_node (rn);
}
else
zlog_info ("Can't find neighbor %s in the interface %s",
inet_ntoa (nbr->src), IF_NAME (oi));
route_unlock_node (rn);
}
/* Free ospf_neighbor structure. */
ospf_nbr_free (nbr);
}
/* Check myself is in the neighbor list. */
int
ospf_nbr_bidirectional (struct in_addr *router_id,
struct in_addr *neighbors, int size)
{
int i;
int max;
max = size / sizeof (struct in_addr);
for (i = 0; i < max; i ++)
if (IPV4_ADDR_SAME (router_id, &neighbors[i]))
return 1;
return 0;
}
/* Add self to nbr list. */
void
ospf_nbr_add_self (struct ospf_interface *oi)
{
struct ospf_neighbor *nbr;
struct prefix p;
struct route_node *rn;
p.family = AF_INET;
p.prefixlen = 32;
p.u.prefix4 = oi->address->u.prefix4;
rn = route_node_get (oi->nbrs, &p);
if (rn->info)
{
/* There is already pseudo neighbor. */
nbr = rn->info;
route_unlock_node (rn);
}
else
rn->info = oi->nbr_self;
}
/* Get neighbor count by status.
Specify status = 0, get all neighbor other than myself. */
int
ospf_nbr_count (struct route_table *nbrs, int state)
{
struct route_node *rn;
struct ospf_neighbor *nbr;
int count = 0;
/* Sanity check. */
if (nbrs == NULL)
return 0;
for (rn = route_top (nbrs); rn; rn = route_next (rn))
if ((nbr = rn->info) != NULL)
/* Ignore myself. */
if (!IPV4_ADDR_SAME (&nbr->router_id, &ospf_top->router_id))
if (state == 0 || nbr->state == state)
count++;
return count;
}
#ifdef HAVE_OPAQUE_LSA
int
ospf_opaque_capable_nbr_count (struct route_table *nbrs, int state)
{
struct route_node *rn;
struct ospf_neighbor *nbr;
int count = 0;
/* Sanity check. */
if (nbrs == NULL)
return 0;
for (rn = route_top (nbrs); rn; rn = route_next (rn))
if ((nbr = rn->info) != NULL)
/* Ignore myself. */
if (!IPV4_ADDR_SAME (&nbr->router_id, &ospf_top->router_id))
if ((state == 0 || nbr->state == state)
&& CHECK_FLAG (nbr->options, OSPF_OPTION_O))
count++;
return count;
}
#endif /* HAVE_OPAQUE_LSA */
struct ospf_neighbor *
ospf_nbr_lookup_by_addr (struct route_table *nbrs,
struct in_addr *addr)
{
struct prefix p;
struct route_node *rn;
struct ospf_neighbor *nbr;
p.family = AF_INET;
p.prefixlen = IPV4_MAX_BITLEN;
p.u.prefix4 = *addr;
rn = route_node_lookup (nbrs, &p);
if (! rn)
return NULL;
if (rn->info == NULL)
{
route_unlock_node (rn);
return NULL;
}
nbr = (struct ospf_neighbor *) rn->info;
route_unlock_node (rn);
return nbr;
}
struct ospf_neighbor *
ospf_nbr_lookup_by_routerid (struct route_table *nbrs,
struct in_addr *id)
{
struct route_node *rn;
struct ospf_neighbor *nbr;
for (rn = route_top (nbrs); rn; rn = route_next (rn))
if ((nbr = rn->info) != NULL)
if (IPV4_ADDR_SAME (&nbr->router_id, id))
{
route_unlock_node(rn);
return nbr;
}
return NULL;
}
void
ospf_renegotiate_optional_capabilities (struct ospf *top)
{
listnode node;
struct ospf_interface *oi;
struct route_table *nbrs;
struct route_node *rn;
struct ospf_neighbor *nbr;
/* At first, flush self-originated LSAs from routing domain. */
ospf_flush_self_originated_lsas_now (top);
/* Revert all neighbor status to ExStart. */
for (node = listhead (top->oiflist); node; nextnode (node))
{
if ((oi = getdata (node)) == NULL || (nbrs = oi->nbrs) == NULL)
continue;
for (rn = route_top (nbrs); rn; rn = route_next (rn))
{
if ((nbr = rn->info) == NULL || nbr == oi->nbr_self)
continue;
if (nbr->state < NSM_ExStart)
continue;
if (IS_DEBUG_OSPF_EVENT)
zlog_info ("Renegotiate optional capabilities with neighbor(%s)", inet_ntoa (nbr->router_id));
OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_SeqNumberMismatch);
}
}
return;
}

879
ospfd/ospf_nsm.c Normal file
View File

@ -0,0 +1,879 @@
/*
* 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 GNU Zebra; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, 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 "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"
void nsm_reset_nbr (struct ospf_neighbor *);
/* OSPF NSM Timer functions. */
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 (NULL, LOG_DEBUG, "NSM[%s:%s]: Timer (Inactivity timer expire)",
IF_NAME (nbr->oi), inet_ntoa (nbr->router_id));
OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_InactivityTimer);
return 0;
}
int
ospf_db_desc_timer (struct thread *thread)
{
struct ospf_interface *oi;
struct ospf_neighbor *nbr;
nbr = THREAD_ARG (thread);
nbr->t_db_desc = NULL;
oi = nbr->oi;
if (IS_DEBUG_OSPF (nsm, NSM_TIMERS))
zlog (NULL, LOG_INFO, "NSM[%s:%s]: Timer (DD Retransmit timer expire)",
IF_NAME (nbr->oi), inet_ntoa (nbr->src));
/* 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 occured. */
void
nsm_timer_set (struct ospf_neighbor *nbr)
{
switch (nbr->state)
{
case NSM_Down:
OSPF_NSM_TIMER_OFF (nbr->t_db_desc);
OSPF_NSM_TIMER_OFF (nbr->t_ls_upd);
break;
case NSM_Attempt:
OSPF_NSM_TIMER_OFF (nbr->t_db_desc);
OSPF_NSM_TIMER_OFF (nbr->t_ls_upd);
break;
case NSM_Init:
OSPF_NSM_TIMER_OFF (nbr->t_db_desc);
OSPF_NSM_TIMER_OFF (nbr->t_ls_upd);
break;
case NSM_TwoWay:
OSPF_NSM_TIMER_OFF (nbr->t_db_desc);
OSPF_NSM_TIMER_OFF (nbr->t_ls_upd);
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);
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:
OSPF_NSM_TIMER_OFF (nbr->t_db_desc);
break;
case NSM_Full:
OSPF_NSM_TIMER_OFF (nbr->t_db_desc);
break;
default:
OSPF_NSM_TIMER_OFF (nbr->t_db_desc);
break;
}
}
/* OSPF NSM functions. */
int
nsm_ignore (struct ospf_neighbor *nbr)
{
if (IS_DEBUG_OSPF (nsm, NSM_EVENTS))
zlog (NULL, LOG_INFO, "NSM[%s:%s]: nsm_ignore called",
IF_NAME (nbr->oi), inet_ntoa (nbr->router_id));
return 0;
}
int
nsm_hello_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);
return 0;
}
int
nsm_start (struct ospf_neighbor *nbr)
{
nsm_reset_nbr (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);
return 0;
}
int
nsm_twoway_received (struct ospf_neighbor *nbr)
{
struct ospf_interface *oi;
int next_state = NSM_TwoWay;
oi = nbr->oi;
/* These netowork types must be adjacency. */
if (oi->type == OSPF_IFTYPE_POINTOPOINT ||
oi->type == OSPF_IFTYPE_POINTOMULTIPOINT ||
oi->type == OSPF_IFTYPE_VIRTUALLINK)
next_state = NSM_ExStart;
/* Router itself is the DRouter or the BDRouter. */
if (IPV4_ADDR_SAME (&oi->address->u.prefix4, &DR (oi)) ||
IPV4_ADDR_SAME (&oi->address->u.prefix4, &BDR (oi)))
next_state = NSM_ExStart;
/* Neighboring Router is the DRouter or the BDRouter. */
if (IPV4_ADDR_SAME (&nbr->address.u.prefix4, &nbr->d_router) ||
IPV4_ADDR_SAME (&nbr->address.u.prefix4, &nbr->bd_router))
next_state = NSM_ExStart;
return next_state;
}
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);
}
int
ospf_db_summary_add (struct ospf_lsa *lsa, void *v, int i)
{
struct ospf_neighbor *nbr = (struct ospf_neighbor *) v;
if (lsa == NULL)
return 0;
#ifdef HAVE_NSSA
/* Stay away from any Local Translated Type-7 LSAs */
if (CHECK_FLAG (lsa->flags, OSPF_LSA_LOCAL_XLT))
return 0;
#endif /* HAVE_NSSA */
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);
}
}
#ifdef HAVE_OPAQUE_LSA
/* The area link state database consists of the router-LSAs,
network-LSAs, summary-LSAs, and type-9/10 opaque-LSAs contained
^^^^^^^^^^^^^^^^^^^^^^^^^^^
in the area structure, along with the AS-external and type-11
^^^^^^^^^^^
opaque LSAs contained in the global structure.
^^^^^^
AS-external and type-11 opaque LSAs are omitted from a virtual
^^^^^^^^^^^^^^^^^^
neighbor's Database summary list. AS-external and type-11 opaque
^^^^^^^^^^^^^^^^^^
LSAs are omitted from the Database summary list if the area has
been configured as a stub. */
#else /* HAVE_OPAQUE_LSA */
/* 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. */
#endif /* HAVE_OPAQUE_LSA */
int
nsm_negotiation_done (struct ospf_neighbor *nbr)
{
struct ospf_area *area;
area = nbr->oi->area;
foreach_lsa (ROUTER_LSDB (area), nbr, 0, ospf_db_summary_add);
foreach_lsa (NETWORK_LSDB (area), nbr, 0, ospf_db_summary_add);
foreach_lsa (SUMMARY_LSDB (area), nbr, 0, ospf_db_summary_add);
foreach_lsa (ASBR_SUMMARY_LSDB (area), nbr, 0, ospf_db_summary_add);
#ifdef HAVE_OPAQUE_LSA
/* Process only if the neighbor is opaque capable. */
if (CHECK_FLAG (nbr->options, OSPF_OPTION_O))
{
foreach_lsa (OPAQUE_LINK_LSDB (area), nbr, 0, ospf_db_summary_add);
foreach_lsa (OPAQUE_AREA_LSDB (area), nbr, 0, ospf_db_summary_add);
}
#endif /* HAVE_OPAQUE_LSA */
if (nbr->oi->type != OSPF_IFTYPE_VIRTUALLINK &&
area->external_routing == OSPF_AREA_DEFAULT)
foreach_lsa (EXTERNAL_LSDB (ospf_top), nbr, 0, ospf_db_summary_add);
#ifdef HAVE_OPAQUE_LSA
if (CHECK_FLAG (nbr->options, OSPF_OPTION_O) &&
(nbr->oi->type != OSPF_IFTYPE_VIRTUALLINK &&
area->external_routing == OSPF_AREA_DEFAULT))
foreach_lsa (OPAQUE_AS_LSDB (ospf_top),
nbr, 0, ospf_db_summary_add);
#endif /* HAVE_OPAQUE_LSA */
/* OSPF_NSM_TIMER_OFF (nbr->t_db_desc); */
return 0;
}
int
nsm_exchange_done (struct ospf_neighbor *nbr)
{
struct ospf_interface *oi;
oi = nbr->oi;
if (ospf_ls_request_isempty (nbr))
return NSM_Full;
/* Cancel dd retransmit timer. */
/* OSPF_NSM_TIMER_OFF (nbr->t_db_desc); */
/* Send Link State Request. */
ospf_ls_req_send (nbr);
return NSM_Loading;
}
int
nsm_bad_ls_req (struct ospf_neighbor *nbr)
{
/* Clear neighbor. */
nsm_reset_nbr (nbr);
return 0;
}
int
nsm_adj_ok (struct ospf_neighbor *nbr)
{
struct ospf_interface *oi;
int next_state;
int flag = 0;
oi = nbr->oi;
next_state = nbr->state;
/* These netowork types must be adjacency. */
if (oi->type == OSPF_IFTYPE_POINTOPOINT ||
oi->type == OSPF_IFTYPE_POINTOMULTIPOINT ||
oi->type == OSPF_IFTYPE_VIRTUALLINK)
flag = 1;
/* Router itself is the DRouter or the BDRouter. */
if (IPV4_ADDR_SAME (&oi->address->u.prefix4, &DR (oi)) ||
IPV4_ADDR_SAME (&oi->address->u.prefix4, &BDR (oi)))
flag = 1;
if (IPV4_ADDR_SAME (&nbr->address.u.prefix4, &DR (oi)) ||
IPV4_ADDR_SAME (&nbr->address.u.prefix4, &BDR (oi)))
flag = 1;
if (nbr->state == NSM_TwoWay && flag == 1)
next_state = NSM_ExStart;
else if (nbr->state >= NSM_ExStart && flag == 0)
next_state = NSM_TwoWay;
return next_state;
}
int
nsm_seq_number_mismatch (struct ospf_neighbor *nbr)
{
/* Clear neighbor. */
nsm_reset_nbr (nbr);
return 0;
}
int
nsm_oneway_received (struct ospf_neighbor *nbr)
{
/* Clear neighbor. */
nsm_reset_nbr (nbr);
return 0;
}
void
nsm_reset_nbr (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);
/* Cancel thread. */
OSPF_NSM_TIMER_OFF (nbr->t_db_desc);
OSPF_NSM_TIMER_OFF (nbr->t_ls_req);
OSPF_NSM_TIMER_OFF (nbr->t_ls_upd);
OSPF_NSM_TIMER_OFF (nbr->t_hello_reply);
#ifdef HAVE_OPAQUE_LSA
if (CHECK_FLAG (nbr->options, OSPF_OPTION_O))
UNSET_FLAG (nbr->options, OSPF_OPTION_O);
#endif /* HAVE_OPAQUE_LSA */
}
int
nsm_kill_nbr (struct ospf_neighbor *nbr)
{
/* call it here because we cannot call it from ospf_nsm_event */
nsm_change_state (nbr, NSM_Down);
/* Reset neighbor. */
nsm_reset_nbr (nbr);
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_info ("NSM[%s:%s]: Down (PollIntervalTimer scheduled)",
IF_NAME (nbr->oi), inet_ntoa (nbr->address.u.prefix4));
}
/* Delete neighbor from interface. */
ospf_nbr_delete (nbr);
return 0;
}
int
nsm_inactivity_timer (struct ospf_neighbor *nbr)
{
/* Kill neighbor. */
nsm_kill_nbr (nbr);
return 0;
}
int
nsm_ll_down (struct ospf_neighbor *nbr)
{
/* Reset neighbor. */
/*nsm_reset_nbr (nbr);*/
/* Kill neighbor. */
nsm_kill_nbr (nbr);
return 0;
}
/* Neighbor State Machine */
struct {
int (*func) ();
int next_state;
} NSM [OSPF_NSM_STATE_MAX][OSPF_NSM_EVENT_MAX] =
{
{
/* DependUpon: dummy state. */
{ nsm_ignore, NSM_DependUpon }, /* NoEvent */
{ nsm_ignore, NSM_DependUpon }, /* HelloReceived */
{ nsm_ignore, NSM_DependUpon }, /* Start */
{ nsm_ignore, NSM_DependUpon }, /* 2-WayReceived */
{ nsm_ignore, NSM_DependUpon }, /* NegotiationDone */
{ nsm_ignore, NSM_DependUpon }, /* ExchangeDone */
{ nsm_ignore, NSM_DependUpon }, /* BadLSReq */
{ nsm_ignore, NSM_DependUpon }, /* LoadingDone */
{ nsm_ignore, NSM_DependUpon }, /* AdjOK? */
{ nsm_ignore, NSM_DependUpon }, /* SeqNumberMismatch */
{ nsm_ignore, NSM_DependUpon }, /* 1-WayReceived */
{ nsm_ignore, NSM_DependUpon }, /* KillNbr */
{ nsm_ignore, NSM_DependUpon }, /* InactivityTimer */
{ nsm_ignore, NSM_DependUpon }, /* LLDown */
},
{
/* Down: */
{ nsm_ignore, NSM_DependUpon }, /* NoEvent */
{ nsm_hello_received, NSM_Init }, /* HelloReceived */
{ nsm_start, NSM_Attempt }, /* Start */
{ nsm_ignore, NSM_Down }, /* 2-WayReceived */
{ nsm_ignore, NSM_Down }, /* NegotiationDone */
{ nsm_ignore, NSM_Down }, /* ExchangeDone */
{ nsm_ignore, NSM_Down }, /* BadLSReq */
{ nsm_ignore, NSM_Down }, /* LoadingDone */
{ nsm_ignore, NSM_Down }, /* AdjOK? */
{ nsm_ignore, NSM_Down }, /* SeqNumberMismatch */
{ nsm_ignore, NSM_Down }, /* 1-WayReceived */
{ nsm_kill_nbr, NSM_Down }, /* KillNbr */
{ nsm_inactivity_timer, NSM_Down }, /* InactivityTimer */
{ nsm_ll_down, NSM_Down }, /* LLDown */
},
{
/* Attempt: */
{ nsm_ignore, NSM_DependUpon }, /* NoEvent */
{ nsm_hello_received, NSM_Init }, /* HelloReceived */
{ nsm_ignore, NSM_Attempt }, /* Start */
{ nsm_ignore, NSM_Attempt }, /* 2-WayReceived */
{ nsm_ignore, NSM_Attempt }, /* NegotiationDone */
{ nsm_ignore, NSM_Attempt }, /* ExchangeDone */
{ nsm_ignore, NSM_Attempt }, /* BadLSReq */
{ nsm_ignore, NSM_Attempt }, /* LoadingDone */
{ nsm_ignore, NSM_Attempt }, /* AdjOK? */
{ nsm_ignore, NSM_Attempt }, /* SeqNumberMismatch */
{ nsm_ignore, NSM_Attempt }, /* 1-WayReceived */
{ nsm_kill_nbr, NSM_Down }, /* KillNbr */
{ nsm_inactivity_timer, NSM_Down }, /* InactivityTimer */
{ nsm_ll_down, NSM_Down }, /* LLDown */
},
{
/* Init: */
{ nsm_ignore, NSM_DependUpon }, /* NoEvent */
{ nsm_hello_received, NSM_Init }, /* HelloReceived */
{ nsm_ignore, NSM_Init }, /* Start */
{ nsm_twoway_received, NSM_DependUpon }, /* 2-WayReceived */
{ nsm_ignore, NSM_Init }, /* NegotiationDone */
{ nsm_ignore, NSM_Init }, /* ExchangeDone */
{ nsm_ignore, NSM_Init }, /* BadLSReq */
{ nsm_ignore, NSM_Init }, /* LoadingDone */
{ nsm_ignore, NSM_Init }, /* AdjOK? */
{ nsm_ignore, NSM_Init }, /* SeqNumberMismatch */
{ nsm_ignore, NSM_Init }, /* 1-WayReceived */
{ nsm_kill_nbr, NSM_Down }, /* KillNbr */
{ nsm_inactivity_timer, NSM_Down }, /* InactivityTimer */
{ nsm_ll_down, NSM_Down }, /* LLDown */
},
{
/* 2-Way: */
{ nsm_ignore, NSM_DependUpon }, /* NoEvent */
{ nsm_hello_received, NSM_TwoWay }, /* HelloReceived */
{ nsm_ignore, NSM_TwoWay }, /* Start */
{ nsm_ignore, NSM_TwoWay }, /* 2-WayReceived */
{ nsm_ignore, NSM_TwoWay }, /* NegotiationDone */
{ nsm_ignore, NSM_TwoWay }, /* ExchangeDone */
{ nsm_ignore, NSM_TwoWay }, /* BadLSReq */
{ nsm_ignore, NSM_TwoWay }, /* LoadingDone */
{ nsm_adj_ok, NSM_DependUpon }, /* AdjOK? */
{ nsm_ignore, NSM_TwoWay }, /* SeqNumberMismatch */
{ nsm_oneway_received, NSM_Init }, /* 1-WayReceived */
{ nsm_kill_nbr, NSM_Down }, /* KillNbr */
{ nsm_inactivity_timer, NSM_Down }, /* InactivityTimer */
{ nsm_ll_down, NSM_Down }, /* LLDown */
},
{
/* ExStart: */
{ nsm_ignore, NSM_DependUpon }, /* NoEvent */
{ nsm_hello_received, NSM_ExStart }, /* HelloReceived */
{ nsm_ignore, NSM_ExStart }, /* Start */
{ nsm_ignore, NSM_ExStart }, /* 2-WayReceived */
{ nsm_negotiation_done, NSM_Exchange }, /* NegotiationDone */
{ nsm_ignore, NSM_ExStart }, /* ExchangeDone */
{ nsm_ignore, NSM_ExStart }, /* BadLSReq */
{ nsm_ignore, NSM_ExStart }, /* LoadingDone */
{ nsm_adj_ok, NSM_DependUpon }, /* AdjOK? */
{ nsm_ignore, NSM_ExStart }, /* SeqNumberMismatch */
{ nsm_oneway_received, NSM_Init }, /* 1-WayReceived */
{ nsm_kill_nbr, NSM_Down }, /* KillNbr */
{ nsm_inactivity_timer, NSM_Down }, /* InactivityTimer */
{ nsm_ll_down, NSM_Down }, /* LLDown */
},
{
/* Exchange: */
{ nsm_ignore, NSM_DependUpon }, /* NoEvent */
{ nsm_hello_received, NSM_Exchange }, /* HelloReceived */
{ nsm_ignore, NSM_Exchange }, /* Start */
{ nsm_ignore, NSM_Exchange }, /* 2-WayReceived */
{ nsm_ignore, NSM_Exchange }, /* NegotiationDone */
{ nsm_exchange_done, NSM_DependUpon }, /* ExchangeDone */
{ nsm_bad_ls_req, NSM_ExStart }, /* BadLSReq */
{ nsm_ignore, NSM_Exchange }, /* LoadingDone */
{ nsm_adj_ok, NSM_DependUpon }, /* AdjOK? */
{ nsm_seq_number_mismatch, NSM_ExStart }, /* SeqNumberMismatch */
{ nsm_oneway_received, NSM_Init }, /* 1-WayReceived */
{ nsm_kill_nbr, NSM_Down }, /* KillNbr */
{ nsm_inactivity_timer, NSM_Down }, /* InactivityTimer */
{ nsm_ll_down, NSM_Down }, /* LLDown */
},
{
/* Loading: */
{ nsm_ignore, NSM_DependUpon }, /* NoEvent */
{ nsm_hello_received, NSM_Loading }, /* HelloReceived */
{ nsm_ignore, NSM_Loading }, /* Start */
{ nsm_ignore, NSM_Loading }, /* 2-WayReceived */
{ nsm_ignore, NSM_Loading }, /* NegotiationDone */
{ nsm_ignore, NSM_Loading }, /* ExchangeDone */
{ nsm_bad_ls_req, NSM_ExStart }, /* BadLSReq */
{ nsm_ignore, NSM_Full }, /* LoadingDone */
{ nsm_adj_ok, NSM_DependUpon }, /* AdjOK? */
{ nsm_seq_number_mismatch, NSM_ExStart }, /* SeqNumberMismatch */
{ nsm_oneway_received, NSM_Init }, /* 1-WayReceived */
{ nsm_kill_nbr, NSM_Down }, /* KillNbr */
{ nsm_inactivity_timer, NSM_Down }, /* InactivityTimer */
{ nsm_ll_down, NSM_Down }, /* LLDown */
},
{ /* Full: */
{ nsm_ignore, NSM_DependUpon }, /* NoEvent */
{ nsm_hello_received, NSM_Full }, /* HelloReceived */
{ nsm_ignore, NSM_Full }, /* Start */
{ nsm_ignore, NSM_Full }, /* 2-WayReceived */
{ nsm_ignore, NSM_Full }, /* NegotiationDone */
{ nsm_ignore, NSM_Full }, /* ExchangeDone */
{ nsm_bad_ls_req, NSM_ExStart }, /* BadLSReq */
{ nsm_ignore, NSM_Full }, /* LoadingDone */
{ nsm_adj_ok, NSM_DependUpon }, /* AdjOK? */
{ nsm_seq_number_mismatch, NSM_ExStart }, /* SeqNumberMismatch */
{ nsm_oneway_received, NSM_Init }, /* 1-WayReceived */
{ nsm_kill_nbr, NSM_Down }, /* KillNbr */
{ nsm_inactivity_timer, NSM_Down }, /* InactivityTimer */
{ nsm_ll_down, NSM_Down }, /* LLDown */
},
};
static char *ospf_nsm_event_str[] =
{
"NoEvent",
"HelloReceived",
"Start",
"2-WayReceived",
"NegotiationDone",
"ExchangeDone",
"BadLSReq",
"LoadingDone",
"AdjOK?",
"SeqNumberMismatch",
"1-WayReceived",
"KillNbr",
"InactivityTimer",
"LLDown",
};
void
nsm_change_state (struct ospf_neighbor *nbr, int state)
{
struct ospf_interface *oi;
struct ospf_area *vl_area = NULL;
u_char old_state;
int x;
int force = 1;
/* Logging change of status. */
if (IS_DEBUG_OSPF (nsm, NSM_STATUS))
zlog_info ("NSM[%s:%s]: State change %s -> %s",
IF_NAME (nbr->oi), inet_ntoa (nbr->router_id),
LOOKUP (ospf_nsm_state_msg, nbr->state),
LOOKUP (ospf_nsm_state_msg, state));
/* Preserve old status. */
old_state = nbr->state;
/* Change to new status. */
nbr->state = state;
/* Statistics. */
nbr->state_change++;
oi = nbr->oi;
if (oi->type == OSPF_IFTYPE_VIRTUALLINK)
vl_area = ospf_area_lookup_by_area_id (oi->vl_data->vl_area_id);
/* 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 ();
if (oi->type == OSPF_IFTYPE_VIRTUALLINK && vl_area)
if (++vl_area->full_vls == 1)
ospf_schedule_abr_task ();
/* kevinm: refresh any redistributions */
for (x = ZEBRA_ROUTE_SYSTEM; x < ZEBRA_ROUTE_MAX; x++) {
if (x == ZEBRA_ROUTE_OSPF || x == ZEBRA_ROUTE_OSPF6)
continue;
ospf_external_lsa_refresh_type(x, force);
}
}
else
{
oi->full_nbrs--;
oi->area->full_nbrs--;
ospf_check_abr_status ();
if (oi->type == OSPF_IFTYPE_VIRTUALLINK && vl_area)
if (vl_area->full_vls > 0)
if (--vl_area->full_vls == 0)
ospf_schedule_abr_task ();
/* clear neighbor retransmit list */
if (!ospf_ls_retransmit_isempty (nbr))
ospf_ls_retransmit_clear (nbr);
}
zlog_info ("nsm_change_state(): "
"scheduling new router-LSA origination");
ospf_router_lsa_timer_add (oi->area);
if (oi->type == OSPF_IFTYPE_VIRTUALLINK)
{
struct ospf_area *vl_area =
ospf_area_lookup_by_area_id (oi->vl_data->vl_area_id);
if (vl_area)
ospf_router_lsa_timer_add (vl_area);
}
/* 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;
OSPF_TIMER_OFF (oi->t_network_lsa_self);
}
else
ospf_network_lsa_timer_add (oi);
}
}
#ifdef HAVE_OPAQUE_LSA
ospf_opaque_nsm_change (nbr, old_state);
#endif /* HAVE_OPAQUE_LSA */
/* Start DD exchange protocol */
if (state == NSM_ExStart)
{
if (nbr->dd_seqnum == 0)
nbr->dd_seqnum = time (NULL);
else
nbr->dd_seqnum++;
nbr->dd_flags = OSPF_DD_FLAG_I|OSPF_DD_FLAG_M|OSPF_DD_FLAG_MS;
ospf_db_desc_send (nbr);
}
/* clear cryptographic sequence number */
if (state == NSM_Down)
nbr->crypt_seqnum = 0;
/* Generete NeighborChange ISM event. */
#ifdef BUGGY_ISM_TRANSITION
if ((old_state < NSM_TwoWay && state >= NSM_TwoWay) ||
(old_state >= NSM_TwoWay && state < NSM_TwoWay))
OSPF_ISM_EVENT_EXECUTE (oi, ISM_NeighborChange);
#else /* BUGGY_ISM_TRANSITION */
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;
}
#endif /* BUGGY_ISM_TRANSITION */
/* Performance hack. Send hello immideately when some neighbor enter
Init state. This whay we decrease neighbor discovery time. Gleb.*/
if (state == NSM_Init)
{
OSPF_ISM_TIMER_OFF (oi->t_hello);
OSPF_ISM_TIMER_ON (oi->t_hello, ospf_hello_timer, 1);
}
/* Preserve old status? */
}
/* Execute NSM event process. */
int
ospf_nsm_event (struct thread *thread)
{
int event;
int next_state;
struct ospf_neighbor *nbr;
struct in_addr router_id;
int old_state;
struct ospf_interface *oi;
nbr = THREAD_ARG (thread);
event = THREAD_VAL (thread);
router_id = nbr->router_id;
old_state = nbr->state;
oi = nbr->oi ;
/* Call function. */
next_state = (*(NSM [nbr->state][event].func))(nbr);
/* When event is NSM_KillNbr or InactivityTimer, the neighbor is
deleted. */
if (event == NSM_KillNbr || event == NSM_InactivityTimer)
{
if (IS_DEBUG_OSPF (nsm, NSM_EVENTS))
zlog_info ("NSM[%s:%s]: neighbor deleted",
IF_NAME (oi), inet_ntoa (router_id));
/* Timers are canceled in ospf_nbr_free, moreover we cannot call
nsm_timer_set here because nbr is freed already!!!*/
/*nsm_timer_set (nbr);*/
return 0;
}
if (! next_state)
next_state = NSM [nbr->state][event].next_state;
if (IS_DEBUG_OSPF (nsm, NSM_EVENTS))
zlog_info ("NSM[%s:%s]: %s (%s)", IF_NAME (oi),
inet_ntoa (nbr->router_id),
LOOKUP (ospf_nsm_state_msg, nbr->state),
ospf_nsm_event_str [event]);
/* If state is changed. */
if (next_state != nbr->state)
nsm_change_state (nbr, next_state);
/* Make sure timer is set. */
nsm_timer_set (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);
}
}