summaryrefslogtreecommitdiff
path: root/bgpd/bgp_fsm.c
diff options
context:
space:
mode:
authorpaul <paul>2002-12-13 20:15:29 +0000
committerpaul <paul>2002-12-13 20:15:29 +0000
commit718e3744195351130f4ce7dbe0613f4b3e23df93 (patch)
treebac2ad39971cd43f31241ef123bd4e470f695ac9 /bgpd/bgp_fsm.c
Initial revision
Diffstat (limited to 'bgpd/bgp_fsm.c')
-rw-r--r--bgpd/bgp_fsm.c864
1 files changed, 864 insertions, 0 deletions
diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c
new file mode 100644
index 00000000..64a4c1bd
--- /dev/null
+++ b/bgpd/bgp_fsm.c
@@ -0,0 +1,864 @@
+/* BGP-4 Finite State Machine
+ From RFC1771 [A Border Gateway Protocol 4 (BGP-4)]
+ Copyright (C) 1996, 97, 98 Kunihiro Ishiguro
+
+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 "vty.h"
+#include "sockunion.h"
+#include "thread.h"
+#include "log.h"
+#include "stream.h"
+#include "memory.h"
+#include "plist.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_fsm.h"
+#include "bgpd/bgp_packet.h"
+#include "bgpd/bgp_network.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_dump.h"
+#include "bgpd/bgp_open.h"
+#ifdef HAVE_SNMP
+#include "bgpd/bgp_snmp.h"
+#endif /* HAVE_SNMP */
+
+/* BGP FSM (finite state machine) has three types of functions. Type
+ one is thread functions. Type two is event functions. Type three
+ is FSM functions. Timer functions are set by bgp_timer_set
+ function. */
+
+/* BGP event function. */
+int bgp_event (struct thread *);
+
+/* BGP thread functions. */
+static int bgp_start_timer (struct thread *);
+static int bgp_connect_timer (struct thread *);
+static int bgp_holdtime_timer (struct thread *);
+static int bgp_keepalive_timer (struct thread *);
+
+/* BGP FSM functions. */
+static int bgp_start (struct peer *);
+
+/* BGP start timer jitter. */
+int
+bgp_start_jitter (int time)
+{
+ return ((rand () % (time + 1)) - (time / 2));
+}
+
+/* Hook function called after bgp event is occered. And vty's
+ neighbor command invoke this function after making neighbor
+ structure. */
+void
+bgp_timer_set (struct peer *peer)
+{
+ int jitter = 0;
+
+ switch (peer->status)
+ {
+ case Idle:
+ /* First entry point of peer's finite state machine. In Idle
+ status start timer is on unless peer is shutdown or peer is
+ inactive. All other timer must be turned off */
+ if (CHECK_FLAG (peer->flags, PEER_FLAG_SHUTDOWN)
+ || CHECK_FLAG (peer->sflags, PEER_STATUS_PREFIX_OVERFLOW)
+ || ! peer_active (peer))
+ {
+ BGP_TIMER_OFF (peer->t_start);
+ }
+ else
+ {
+ jitter = bgp_start_jitter (peer->v_start);
+ BGP_TIMER_ON (peer->t_start, bgp_start_timer,
+ peer->v_start + jitter);
+ }
+ BGP_TIMER_OFF (peer->t_connect);
+ BGP_TIMER_OFF (peer->t_holdtime);
+ BGP_TIMER_OFF (peer->t_keepalive);
+ BGP_TIMER_OFF (peer->t_asorig);
+ BGP_TIMER_OFF (peer->t_routeadv);
+ break;
+
+ case Connect:
+ /* After start timer is expired, the peer moves to Connnect
+ status. Make sure start timer is off and connect timer is
+ on. */
+ BGP_TIMER_OFF (peer->t_start);
+ BGP_TIMER_ON (peer->t_connect, bgp_connect_timer, peer->v_connect);
+ BGP_TIMER_OFF (peer->t_holdtime);
+ BGP_TIMER_OFF (peer->t_keepalive);
+ BGP_TIMER_OFF (peer->t_asorig);
+ BGP_TIMER_OFF (peer->t_routeadv);
+ break;
+
+ case Active:
+ /* Active is waiting connection from remote peer. And if
+ connect timer is expired, change status to Connect. */
+ BGP_TIMER_OFF (peer->t_start);
+ /* If peer is passive mode, do not set connect timer. */
+ if (CHECK_FLAG (peer->flags, PEER_FLAG_PASSIVE))
+ {
+ BGP_TIMER_OFF (peer->t_connect);
+ }
+ else
+ {
+ BGP_TIMER_ON (peer->t_connect, bgp_connect_timer, peer->v_connect);
+ }
+ BGP_TIMER_OFF (peer->t_holdtime);
+ BGP_TIMER_OFF (peer->t_keepalive);
+ BGP_TIMER_OFF (peer->t_asorig);
+ BGP_TIMER_OFF (peer->t_routeadv);
+ break;
+
+ case OpenSent:
+ /* OpenSent status. */
+ BGP_TIMER_OFF (peer->t_start);
+ BGP_TIMER_OFF (peer->t_connect);
+ if (peer->v_holdtime != 0)
+ {
+ BGP_TIMER_ON (peer->t_holdtime, bgp_holdtime_timer,
+ peer->v_holdtime);
+ }
+ else
+ {
+ BGP_TIMER_OFF (peer->t_holdtime);
+ }
+ BGP_TIMER_OFF (peer->t_keepalive);
+ BGP_TIMER_OFF (peer->t_asorig);
+ BGP_TIMER_OFF (peer->t_routeadv);
+ break;
+
+ case OpenConfirm:
+ /* OpenConfirm status. */
+ BGP_TIMER_OFF (peer->t_start);
+ BGP_TIMER_OFF (peer->t_connect);
+
+ /* If the negotiated Hold Time value is zero, then the Hold Time
+ timer and KeepAlive timers are not started. */
+ if (peer->v_holdtime == 0)
+ {
+ BGP_TIMER_OFF (peer->t_holdtime);
+ BGP_TIMER_OFF (peer->t_keepalive);
+ }
+ else
+ {
+ BGP_TIMER_ON (peer->t_holdtime, bgp_holdtime_timer,
+ peer->v_holdtime);
+ BGP_TIMER_ON (peer->t_keepalive, bgp_keepalive_timer,
+ peer->v_keepalive);
+ }
+ BGP_TIMER_OFF (peer->t_asorig);
+ BGP_TIMER_OFF (peer->t_routeadv);
+ break;
+
+ case Established:
+ /* In Established status start and connect timer is turned
+ off. */
+ BGP_TIMER_OFF (peer->t_start);
+ BGP_TIMER_OFF (peer->t_connect);
+
+ /* Same as OpenConfirm, if holdtime is zero then both holdtime
+ and keepalive must be turned off. */
+ if (peer->v_holdtime == 0)
+ {
+ BGP_TIMER_OFF (peer->t_holdtime);
+ BGP_TIMER_OFF (peer->t_keepalive);
+ }
+ else
+ {
+ BGP_TIMER_ON (peer->t_holdtime, bgp_holdtime_timer,
+ peer->v_holdtime);
+ BGP_TIMER_ON (peer->t_keepalive, bgp_keepalive_timer,
+ peer->v_keepalive);
+ }
+ BGP_TIMER_OFF (peer->t_asorig);
+ break;
+ }
+}
+
+/* BGP start timer. This function set BGP_Start event to thread value
+ and process event. */
+static int
+bgp_start_timer (struct thread *thread)
+{
+ struct peer *peer;
+
+ peer = THREAD_ARG (thread);
+ peer->t_start = NULL;
+
+ if (BGP_DEBUG (fsm, FSM))
+ zlog (peer->log, LOG_DEBUG,
+ "%s [FSM] Timer (start timer expire).", peer->host);
+
+ THREAD_VAL (thread) = BGP_Start;
+ bgp_event (thread);
+
+ return 0;
+}
+
+/* BGP connect retry timer. */
+static int
+bgp_connect_timer (struct thread *thread)
+{
+ struct peer *peer;
+
+ peer = THREAD_ARG (thread);
+ peer->t_connect = NULL;
+
+ if (BGP_DEBUG (fsm, FSM))
+ zlog (peer->log, LOG_DEBUG, "%s [FSM] Timer (connect timer expire)",
+ peer->host);
+
+ THREAD_VAL (thread) = ConnectRetry_timer_expired;
+ bgp_event (thread);
+
+ return 0;
+}
+
+/* BGP holdtime timer. */
+static int
+bgp_holdtime_timer (struct thread *thread)
+{
+ struct peer *peer;
+
+ peer = THREAD_ARG (thread);
+ peer->t_holdtime = NULL;
+
+ if (BGP_DEBUG (fsm, FSM))
+ zlog (peer->log, LOG_DEBUG,
+ "%s [FSM] Timer (holdtime timer expire)",
+ peer->host);
+
+ THREAD_VAL (thread) = Hold_Timer_expired;
+ bgp_event (thread);
+
+ return 0;
+}
+
+/* BGP keepalive fire ! */
+static int
+bgp_keepalive_timer (struct thread *thread)
+{
+ struct peer *peer;
+
+ peer = THREAD_ARG (thread);
+ peer->t_keepalive = NULL;
+
+ if (BGP_DEBUG (fsm, FSM))
+ zlog (peer->log, LOG_DEBUG,
+ "%s [FSM] Timer (keepalive timer expire)",
+ peer->host);
+
+ THREAD_VAL (thread) = KeepAlive_timer_expired;
+ bgp_event (thread);
+
+ return 0;
+}
+
+int
+bgp_routeadv_timer (struct thread *thread)
+{
+ struct peer *peer;
+
+ peer = THREAD_ARG (thread);
+ peer->t_routeadv = NULL;
+
+ if (BGP_DEBUG (fsm, FSM))
+ zlog (peer->log, LOG_DEBUG,
+ "%s [FSM] Timer (routeadv timer expire)",
+ peer->host);
+
+ peer->synctime = time (NULL);
+
+ BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd);
+
+ BGP_TIMER_ON (peer->t_routeadv, bgp_routeadv_timer,
+ peer->v_routeadv);
+
+ return 0;
+}
+
+/* Reset bgp update timer */
+static void
+bgp_uptime_reset (struct peer *peer)
+{
+ peer->uptime = time (NULL);
+}
+
+/* Administrative BGP peer stop event. */
+int
+bgp_stop (struct peer *peer)
+{
+ int established = 0;
+ afi_t afi;
+ safi_t safi;
+ char orf_name[BUFSIZ];
+
+ /* Increment Dropped count. */
+ if (peer->status == Established)
+ {
+ established = 1;
+ peer->dropped++;
+ bgp_fsm_change_status (peer, Idle);
+#ifdef HAVE_SNMP
+ bgpTrapBackwardTransition (peer);
+#endif /* HAVE_SNMP */
+ }
+
+ /* Reset uptime. */
+ bgp_uptime_reset (peer);
+
+ /* Need of clear of peer. */
+ if (established)
+ bgp_clear_route_all (peer);
+
+ /* Stop read and write threads when exists. */
+ BGP_READ_OFF (peer->t_read);
+ BGP_WRITE_OFF (peer->t_write);
+
+ /* Stop all timers. */
+ BGP_TIMER_OFF (peer->t_start);
+ BGP_TIMER_OFF (peer->t_connect);
+ BGP_TIMER_OFF (peer->t_holdtime);
+ BGP_TIMER_OFF (peer->t_keepalive);
+ BGP_TIMER_OFF (peer->t_asorig);
+ BGP_TIMER_OFF (peer->t_routeadv);
+
+ /* Delete all existing events of the peer. */
+ BGP_EVENT_DELETE (peer);
+
+ /* Stream reset. */
+ peer->packet_size = 0;
+
+ /* Clear input and output buffer. */
+ if (peer->ibuf)
+ stream_reset (peer->ibuf);
+ if (peer->work)
+ stream_reset (peer->work);
+ stream_fifo_clean (peer->obuf);
+
+ /* Close of file descriptor. */
+ if (peer->fd >= 0)
+ {
+ close (peer->fd);
+ peer->fd = -1;
+ }
+
+ /* Connection information. */
+ if (peer->su_local)
+ {
+ XFREE (MTYPE_SOCKUNION, peer->su_local);
+ peer->su_local = NULL;
+ }
+
+ if (peer->su_remote)
+ {
+ XFREE (MTYPE_SOCKUNION, peer->su_remote);
+ peer->su_remote = NULL;
+ }
+
+ /* Clear remote router-id. */
+ peer->remote_id.s_addr = 0;
+
+ /* Reset all negotiated variables */
+ peer->afc_nego[AFI_IP][SAFI_UNICAST] = 0;
+ peer->afc_nego[AFI_IP][SAFI_MULTICAST] = 0;
+ peer->afc_nego[AFI_IP][SAFI_MPLS_VPN] = 0;
+ peer->afc_nego[AFI_IP6][SAFI_UNICAST] = 0;
+ peer->afc_nego[AFI_IP6][SAFI_MULTICAST] = 0;
+ peer->afc_adv[AFI_IP][SAFI_UNICAST] = 0;
+ peer->afc_adv[AFI_IP][SAFI_MULTICAST] = 0;
+ peer->afc_adv[AFI_IP][SAFI_MPLS_VPN] = 0;
+ peer->afc_adv[AFI_IP6][SAFI_UNICAST] = 0;
+ peer->afc_adv[AFI_IP6][SAFI_MULTICAST] = 0;
+ peer->afc_recv[AFI_IP][SAFI_UNICAST] = 0;
+ peer->afc_recv[AFI_IP][SAFI_MULTICAST] = 0;
+ peer->afc_recv[AFI_IP][SAFI_MPLS_VPN] = 0;
+ peer->afc_recv[AFI_IP6][SAFI_UNICAST] = 0;
+ peer->afc_recv[AFI_IP6][SAFI_MULTICAST] = 0;
+
+ /* Reset route refresh flag. */
+ UNSET_FLAG (peer->cap, PEER_CAP_REFRESH_ADV);
+ UNSET_FLAG (peer->cap, PEER_CAP_REFRESH_OLD_RCV);
+ UNSET_FLAG (peer->cap, PEER_CAP_REFRESH_NEW_RCV);
+ UNSET_FLAG (peer->cap, PEER_CAP_DYNAMIC_ADV);
+ UNSET_FLAG (peer->cap, PEER_CAP_DYNAMIC_RCV);
+
+ for (afi = AFI_IP ; afi < AFI_MAX ; afi++)
+ for (safi = SAFI_UNICAST ; safi < SAFI_MAX ; safi++)
+ {
+ /* peer address family capability flags*/
+ peer->af_cap[afi][safi] = 0;
+ /* peer address family status flags*/
+ peer->af_sflags[afi][safi] = 0;
+ /* Received ORF prefix-filter */
+ peer->orf_plist[afi][safi] = NULL;
+ /* ORF received prefix-filter pnt */
+ sprintf (orf_name, "%s.%d.%d", peer->host, afi, safi);
+ prefix_bgp_orf_remove_all (orf_name);
+ }
+
+ /* Reset keepalive and holdtime */
+ if (CHECK_FLAG (peer->config, PEER_CONFIG_TIMER))
+ {
+ peer->v_keepalive = peer->keepalive;
+ peer->v_holdtime = peer->holdtime;
+ }
+ else
+ {
+ peer->v_keepalive = peer->bgp->default_keepalive;
+ peer->v_holdtime = peer->bgp->default_holdtime;
+ }
+
+ peer->update_time = 0;
+
+ /* Until we are sure that there is no problem about prefix count
+ this should be commented out.*/
+#if 0
+ /* Reset prefix count */
+ peer->pcount[AFI_IP][SAFI_UNICAST] = 0;
+ peer->pcount[AFI_IP][SAFI_MULTICAST] = 0;
+ peer->pcount[AFI_IP][SAFI_MPLS_VPN] = 0;
+ peer->pcount[AFI_IP6][SAFI_UNICAST] = 0;
+ peer->pcount[AFI_IP6][SAFI_MULTICAST] = 0;
+#endif /* 0 */
+
+ return 0;
+}
+
+/* BGP peer is stoped by the error. */
+int
+bgp_stop_with_error (struct peer *peer)
+{
+ /* Double start timer. */
+ peer->v_start *= 2;
+
+ /* Overflow check. */
+ if (peer->v_start >= (60 * 2))
+ peer->v_start = (60 * 2);
+
+ bgp_stop (peer);
+
+ return 0;
+}
+
+/* TCP connection open. Next we send open message to remote peer. And
+ add read thread for reading open message. */
+int
+bgp_connect_success (struct peer *peer)
+{
+ if (peer->fd < 0)
+ {
+ zlog_err ("bgp_connect_success peer's fd is negative value %d",
+ peer->fd);
+ return -1;
+ }
+ BGP_READ_ON (peer->t_read, bgp_read, peer->fd);
+
+ /* bgp_getsockname (peer); */
+
+ if (! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
+ bgp_open_send (peer);
+
+ return 0;
+}
+
+/* TCP connect fail */
+int
+bgp_connect_fail (struct peer *peer)
+{
+ bgp_stop (peer);
+ return 0;
+}
+
+/* This function is the first starting point of all BGP connection. It
+ try to connect to remote peer with non-blocking IO. */
+int
+bgp_start (struct peer *peer)
+{
+ int status;
+
+ /* If the peer is passive mode, force to move to Active mode. */
+ if (CHECK_FLAG (peer->flags, PEER_FLAG_PASSIVE))
+ {
+ BGP_EVENT_ADD (peer, TCP_connection_open_failed);
+ return 0;
+ }
+
+ status = bgp_connect (peer);
+
+ switch (status)
+ {
+ case connect_error:
+ if (BGP_DEBUG (fsm, FSM))
+ plog_info (peer->log, "%s [FSM] Connect error", peer->host);
+ BGP_EVENT_ADD (peer, TCP_connection_open_failed);
+ break;
+ case connect_success:
+ if (BGP_DEBUG (fsm, FSM))
+ plog_info (peer->log, "%s [FSM] Connect immediately success",
+ peer->host);
+ BGP_EVENT_ADD (peer, TCP_connection_open);
+ break;
+ case connect_in_progress:
+ /* To check nonblocking connect, we wait until socket is
+ readable or writable. */
+ if (BGP_DEBUG (fsm, FSM))
+ plog_info (peer->log, "%s [FSM] Non blocking connect waiting result",
+ peer->host);
+ if (peer->fd < 0)
+ {
+ zlog_err ("bgp_start peer's fd is negative value %d",
+ peer->fd);
+ return -1;
+ }
+ BGP_READ_ON (peer->t_read, bgp_read, peer->fd);
+ BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd);
+ break;
+ }
+ return 0;
+}
+
+/* Connect retry timer is expired when the peer status is Connect. */
+int
+bgp_reconnect (struct peer *peer)
+{
+ bgp_stop (peer);
+ bgp_start (peer);
+ return 0;
+}
+
+int
+bgp_fsm_open (struct peer *peer)
+{
+ /* Send keepalive and make keepalive timer */
+ bgp_keepalive_send (peer);
+
+ /* Reset holdtimer value. */
+ BGP_TIMER_OFF (peer->t_holdtime);
+
+ return 0;
+}
+
+/* Called after event occured, this function change status and reset
+ read/write and timer thread. */
+void
+bgp_fsm_change_status (struct peer *peer, int status)
+{
+ bgp_dump_state (peer, peer->status, status);
+
+ /* Preserve old status and change into new status. */
+ peer->ostatus = peer->status;
+ peer->status = status;
+}
+
+/* Keepalive send to peer. */
+int
+bgp_fsm_keepalive_expire (struct peer *peer)
+{
+ bgp_keepalive_send (peer);
+ return 0;
+}
+
+/* Hold timer expire. This is error of BGP connection. So cut the
+ peer and change to Idle status. */
+int
+bgp_fsm_holdtime_expire (struct peer *peer)
+{
+ if (BGP_DEBUG (fsm, FSM))
+ zlog (peer->log, LOG_DEBUG, "%s [FSM] Hold timer expire", peer->host);
+
+ /* Send notify to remote peer. */
+ bgp_notify_send (peer, BGP_NOTIFY_HOLD_ERR, 0);
+
+ /* Sweep if it is temporary peer. */
+ if (CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER))
+ {
+ zlog_info ("%s [Event] Accepting BGP peer is deleted", peer->host);
+ peer_delete (peer);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Status goes to Established. Send keepalive packet then make first
+ update information. */
+int
+bgp_establish (struct peer *peer)
+{
+ struct bgp_notify *notify;
+ afi_t afi;
+ safi_t safi;
+
+ /* Reset capability open status flag. */
+ if (! CHECK_FLAG (peer->sflags, PEER_STATUS_CAPABILITY_OPEN))
+ SET_FLAG (peer->sflags, PEER_STATUS_CAPABILITY_OPEN);
+
+ /* Clear last notification data. */
+ notify = &peer->notify;
+ if (notify->data)
+ XFREE (MTYPE_TMP, notify->data);
+ memset (notify, 0, sizeof (struct bgp_notify));
+
+ /* Clear start timer value to default. */
+ peer->v_start = BGP_INIT_START_TIMER;
+
+ /* Increment established count. */
+ peer->established++;
+ bgp_fsm_change_status (peer, Established);
+#ifdef HAVE_SNMP
+ bgpTrapEstablished (peer);
+#endif /* HAVE_SNMP */
+
+ /* Reset uptime, send keepalive, send current table. */
+ bgp_uptime_reset (peer);
+
+ /* Send route-refresh when ORF is enabled */
+ for (afi = AFI_IP ; afi < AFI_MAX ; afi++)
+ for (safi = SAFI_UNICAST ; safi < SAFI_MAX ; safi++)
+ if (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_ADV))
+ {
+ if (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_RCV))
+ bgp_route_refresh_send (peer, afi, safi, ORF_TYPE_PREFIX,
+ REFRESH_IMMEDIATE, 0);
+ else if (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_OLD_RCV))
+ bgp_route_refresh_send (peer, afi, safi, ORF_TYPE_PREFIX_OLD,
+ REFRESH_IMMEDIATE, 0);
+ }
+
+ if (peer->v_keepalive)
+ bgp_keepalive_send (peer);
+
+ /* First update is deferred until ORF or ROUTE-REFRESH is received */
+ for (afi = AFI_IP ; afi < AFI_MAX ; afi++)
+ for (safi = SAFI_UNICAST ; safi < SAFI_MAX ; safi++)
+ if (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_ADV))
+ if (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_RCV)
+ || CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_OLD_RCV))
+ SET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_WAIT_REFRESH);
+
+ bgp_announce_route_all (peer);
+
+ BGP_TIMER_ON (peer->t_routeadv, bgp_routeadv_timer, 1);
+
+ return 0;
+}
+
+/* Keepalive packet is received. */
+int
+bgp_fsm_keepalive (struct peer *peer)
+{
+ /* peer count update */
+ peer->keepalive_in++;
+
+ BGP_TIMER_OFF (peer->t_holdtime);
+ return 0;
+}
+
+/* Update packet is received. */
+int
+bgp_fsm_update (struct peer *peer)
+{
+ BGP_TIMER_OFF (peer->t_holdtime);
+ return 0;
+}
+
+/* This is empty event. */
+int
+bgp_ignore (struct peer *peer)
+{
+ if (BGP_DEBUG (fsm, FSM))
+ zlog (peer->log, LOG_DEBUG, "%s [FSM] bgp_ignore called", peer->host);
+ return 0;
+}
+
+/* Finite State Machine structure */
+struct {
+ int (*func) ();
+ int next_state;
+} FSM [BGP_STATUS_MAX - 1][BGP_EVENTS_MAX - 1] =
+{
+ {
+ /* Idle state: In Idle state, all events other than BGP_Start is
+ ignored. With BGP_Start event, finite state machine calls
+ bgp_start(). */
+ {bgp_start, Connect}, /* BGP_Start */
+ {bgp_stop, Idle}, /* BGP_Stop */
+ {bgp_stop, Idle}, /* TCP_connection_open */
+ {bgp_stop, Idle}, /* TCP_connection_closed */
+ {bgp_ignore, Idle}, /* TCP_connection_open_failed */
+ {bgp_stop, Idle}, /* TCP_fatal_error */
+ {bgp_ignore, Idle}, /* ConnectRetry_timer_expired */
+ {bgp_ignore, Idle}, /* Hold_Timer_expired */
+ {bgp_ignore, Idle}, /* KeepAlive_timer_expired */
+ {bgp_ignore, Idle}, /* Receive_OPEN_message */
+ {bgp_ignore, Idle}, /* Receive_KEEPALIVE_message */
+ {bgp_ignore, Idle}, /* Receive_UPDATE_message */
+ {bgp_ignore, Idle}, /* Receive_NOTIFICATION_message */
+ },
+ {
+ /* Connect */
+ {bgp_ignore, Connect}, /* BGP_Start */
+ {bgp_stop, Idle}, /* BGP_Stop */
+ {bgp_connect_success, OpenSent}, /* TCP_connection_open */
+ {bgp_stop, Idle}, /* TCP_connection_closed */
+ {bgp_connect_fail, Active}, /* TCP_connection_open_failed */
+ {bgp_connect_fail, Idle}, /* TCP_fatal_error */
+ {bgp_reconnect, Connect}, /* ConnectRetry_timer_expired */
+ {bgp_ignore, Idle}, /* Hold_Timer_expired */
+ {bgp_ignore, Idle}, /* KeepAlive_timer_expired */
+ {bgp_ignore, Idle}, /* Receive_OPEN_message */
+ {bgp_ignore, Idle}, /* Receive_KEEPALIVE_message */
+ {bgp_ignore, Idle}, /* Receive_UPDATE_message */
+ {bgp_stop, Idle}, /* Receive_NOTIFICATION_message */
+ },
+ {
+ /* Active, */
+ {bgp_ignore, Active}, /* BGP_Start */
+ {bgp_stop, Idle}, /* BGP_Stop */
+ {bgp_connect_success, OpenSent}, /* TCP_connection_open */
+ {bgp_stop, Idle}, /* TCP_connection_closed */
+ {bgp_ignore, Active}, /* TCP_connection_open_failed */
+ {bgp_ignore, Idle}, /* TCP_fatal_error */
+ {bgp_start, Connect}, /* ConnectRetry_timer_expired */
+ {bgp_ignore, Idle}, /* Hold_Timer_expired */
+ {bgp_ignore, Idle}, /* KeepAlive_timer_expired */
+ {bgp_ignore, Idle}, /* Receive_OPEN_message */
+ {bgp_ignore, Idle}, /* Receive_KEEPALIVE_message */
+ {bgp_ignore, Idle}, /* Receive_UPDATE_message */
+ {bgp_stop_with_error, Idle}, /* Receive_NOTIFICATION_message */
+ },
+ {
+ /* OpenSent, */
+ {bgp_ignore, OpenSent}, /* BGP_Start */
+ {bgp_stop, Idle}, /* BGP_Stop */
+ {bgp_stop, Idle}, /* TCP_connection_open */
+ {bgp_stop, Active}, /* TCP_connection_closed */
+ {bgp_ignore, Idle}, /* TCP_connection_open_failed */
+ {bgp_stop, Idle}, /* TCP_fatal_error */
+ {bgp_ignore, Idle}, /* ConnectRetry_timer_expired */
+ {bgp_fsm_holdtime_expire, Idle}, /* Hold_Timer_expired */
+ {bgp_ignore, Idle}, /* KeepAlive_timer_expired */
+ {bgp_fsm_open, OpenConfirm}, /* Receive_OPEN_message */
+ {bgp_ignore, Idle}, /* Receive_KEEPALIVE_message */
+ {bgp_ignore, Idle}, /* Receive_UPDATE_message */
+ {bgp_stop_with_error, Idle}, /* Receive_NOTIFICATION_message */
+ },
+ {
+ /* OpenConfirm, */
+ {bgp_ignore, OpenConfirm}, /* BGP_Start */
+ {bgp_stop, Idle}, /* BGP_Stop */
+ {bgp_stop, Idle}, /* TCP_connection_open */
+ {bgp_stop, Idle}, /* TCP_connection_closed */
+ {bgp_stop, Idle}, /* TCP_connection_open_failed */
+ {bgp_stop, Idle}, /* TCP_fatal_error */
+ {bgp_ignore, Idle}, /* ConnectRetry_timer_expired */
+ {bgp_fsm_holdtime_expire, Idle}, /* Hold_Timer_expired */
+ {bgp_ignore, OpenConfirm}, /* KeepAlive_timer_expired */
+ {bgp_ignore, Idle}, /* Receive_OPEN_message */
+ {bgp_establish, Established}, /* Receive_KEEPALIVE_message */
+ {bgp_ignore, Idle}, /* Receive_UPDATE_message */
+ {bgp_stop_with_error, Idle}, /* Receive_NOTIFICATION_message */
+ },
+ {
+ /* Established, */
+ {bgp_ignore, Established}, /* BGP_Start */
+ {bgp_stop, Idle}, /* BGP_Stop */
+ {bgp_stop, Idle}, /* TCP_connection_open */
+ {bgp_stop, Idle}, /* TCP_connection_closed */
+ {bgp_ignore, Idle}, /* TCP_connection_open_failed */
+ {bgp_stop, Idle}, /* TCP_fatal_error */
+ {bgp_ignore, Idle}, /* ConnectRetry_timer_expired */
+ {bgp_fsm_holdtime_expire, Idle}, /* Hold_Timer_expired */
+ {bgp_fsm_keepalive_expire, Established}, /* KeepAlive_timer_expired */
+ {bgp_stop, Idle}, /* Receive_OPEN_message */
+ {bgp_fsm_keepalive, Established}, /* Receive_KEEPALIVE_message */
+ {bgp_fsm_update, Established}, /* Receive_UPDATE_message */
+ {bgp_stop_with_error, Idle}, /* Receive_NOTIFICATION_message */
+ },
+};
+
+static char *bgp_event_str[] =
+{
+ NULL,
+ "BGP_Start",
+ "BGP_Stop",
+ "TCP_connection_open",
+ "TCP_connection_closed",
+ "TCP_connection_open_failed",
+ "TCP_fatal_error",
+ "ConnectRetry_timer_expired",
+ "Hold_Timer_expired",
+ "KeepAlive_timer_expired",
+ "Receive_OPEN_message",
+ "Receive_KEEPALIVE_message",
+ "Receive_UPDATE_message",
+ "Receive_NOTIFICATION_message"
+};
+
+/* Execute event process. */
+int
+bgp_event (struct thread *thread)
+{
+ int ret;
+ int event;
+ int next;
+ struct peer *peer;
+
+ peer = THREAD_ARG (thread);
+ event = THREAD_VAL (thread);
+
+ /* Logging this event. */
+ next = FSM [peer->status -1][event - 1].next_state;
+
+ if (BGP_DEBUG (fsm, FSM))
+ plog_info (peer->log, "%s [FSM] %s (%s->%s)", peer->host,
+ bgp_event_str[event],
+ LOOKUP (bgp_status_msg, peer->status),
+ LOOKUP (bgp_status_msg, next));
+ if (BGP_DEBUG (normal, NORMAL)
+ && strcmp (LOOKUP (bgp_status_msg, peer->status), LOOKUP (bgp_status_msg, next)))
+ zlog_info ("%s went from %s to %s",
+ peer->host,
+ LOOKUP (bgp_status_msg, peer->status),
+ LOOKUP (bgp_status_msg, next));
+
+ /* Call function. */
+ ret = (*(FSM [peer->status - 1][event - 1].func))(peer);
+
+ /* When function do not want proceed next job return -1. */
+ if (ret < 0)
+ return ret;
+
+ /* If status is changed. */
+ if (next != peer->status)
+ bgp_fsm_change_status (peer, next);
+
+ /* Make sure timer is set. */
+ bgp_timer_set (peer);
+
+ return 0;
+}