diff options
author | paul <paul> | 2003-01-17 23:48:42 +0000 |
---|---|---|
committer | paul <paul> | 2003-01-17 23:48:42 +0000 |
commit | 2d59836a4fb16e2471560a47da7e5f75779c5807 (patch) | |
tree | 619901a67086c8eca741eee4e8b6b9810a3d86b9 | |
parent | e04ab74d17b4f3456bd3eeefe2043584431b4ea2 (diff) |
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.
-rw-r--r-- | ospfd/ospf_ism.c | 658 | ||||
-rw-r--r-- | ospfd/ospf_neighbor.c | 329 | ||||
-rw-r--r-- | ospfd/ospf_nsm.c | 879 |
3 files changed, 1866 insertions, 0 deletions
diff --git a/ospfd/ospf_ism.c b/ospfd/ospf_ism.c new file mode 100644 index 00000000..b47487f0 --- /dev/null +++ b/ospfd/ospf_ism.c @@ -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; +} + diff --git a/ospfd/ospf_neighbor.c b/ospfd/ospf_neighbor.c new file mode 100644 index 00000000..a302aa91 --- /dev/null +++ b/ospfd/ospf_neighbor.c @@ -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; +} diff --git a/ospfd/ospf_nsm.c b/ospfd/ospf_nsm.c new file mode 100644 index 00000000..cfb18a98 --- /dev/null +++ b/ospfd/ospf_nsm.c @@ -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); + } +} |