summaryrefslogtreecommitdiff
path: root/ospfd
diff options
context:
space:
mode:
Diffstat (limited to 'ospfd')
-rw-r--r--ospfd/ospf_ism.c658
-rw-r--r--ospfd/ospf_neighbor.c329
-rw-r--r--ospfd/ospf_nsm.c879
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);
+ }
+}