diff options
Diffstat (limited to 'ospfd')
-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); + } +} |