LCOV - code coverage report
Current view: top level - bgp - state_machine.cc (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 646 842 76.7 %
Date: 2026-09-28 02:13:17 Functions: 140 191 73.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
       3             :  */
       4             : 
       5             : #include "bgp/state_machine.h"
       6             : 
       7             : #include <boost/statechart/custom_reaction.hpp>
       8             : #include <boost/statechart/state.hpp>
       9             : #include <boost/statechart/state_machine.hpp>
      10             : #include <boost/statechart/transition.hpp>
      11             : 
      12             : #include <algorithm>
      13             : #include <list>
      14             : #include <string>
      15             : #include <typeinfo>
      16             : #include <atomic>
      17             : 
      18             : #include "base/task_annotations.h"
      19             : #include "bgp/bgp_log.h"
      20             : #include "bgp/bgp_peer.h"
      21             : #include "bgp/bgp_peer_close.h"
      22             : #include "bgp/bgp_peer_types.h"
      23             : #include "bgp/bgp_server.h"
      24             : #include "bgp/bgp_session.h"
      25             : #include "bgp/bgp_session_manager.h"
      26             : 
      27             : using std::min;
      28             : using std::ostream;
      29             : using std::ostringstream;
      30             : using std::string;
      31             : 
      32             : namespace mpl = boost::mpl;
      33             : namespace sc = boost::statechart;
      34             : 
      35             : const int StateMachine::kOpenTime = 15;                 // seconds
      36             : const int StateMachine::kConnectInterval = 30;          // seconds
      37             : const int StateMachine::kHoldTime = 90;                 // seconds
      38             : const int StateMachine::kOpenSentHoldTime = 240;        // seconds
      39             : const int StateMachine::kIdleHoldTime =
      40             :     getenv("CONTRAIL_BGP_IDLE_HOLD_TIME_MSECS") ?
      41             :         strtol(getenv("CONTRAIL_BGP_IDLE_HOLD_TIME_MSECS"), NULL, 0) : 5000;
      42             : const int StateMachine::kMaxIdleHoldTime = 100 * 1000;  // milliseconds
      43             : const int StateMachine::kJitter = 10;                   // percentage
      44             : 
      45             : #define SM_LOG(level, _Msg)                                    \
      46             :     do {                                                       \
      47             :         ostringstream out;                                     \
      48             :         out << _Msg;                                           \
      49             :         if (LoggingDisabled()) break;                          \
      50             :         BGP_LOG_SERVER(peer_, (BgpTable *) 0);                 \
      51             :         BGP_LOG(BgpPeerStateMachine, level,                    \
      52             :                 BGP_LOG_FLAG_SYSLOG, BGP_PEER_DIR_NA,          \
      53             :                 peer_ ? peer_->ToUVEKey() : "",                \
      54             :                 out.str());                                    \
      55             :     } while (false)
      56             : 
      57             : #define SM_LOG_NOTICE(_Msg)                                    \
      58             :     do {                                                       \
      59             :         ostringstream out;                                     \
      60             :         out << _Msg;                                           \
      61             :         if (LoggingDisabled()) break;                          \
      62             :         BGP_LOG_SERVER(peer_, (BgpTable *) 0);                 \
      63             :         BGP_LOG_NOTICE(BgpPeerStateMachine,                    \
      64             :                 BGP_LOG_FLAG_SYSLOG, BGP_PEER_DIR_NA,          \
      65             :                 peer_ ? peer_->ToUVEKey() : "",                \
      66             :                 out.str());                                    \
      67             :     } while (false)
      68             : 
      69             : namespace fsm {
      70             : 
      71             : // Events for the state machine.  These are listed in roughly the same order
      72             : // as the RFC - Administrative, Timer, Tcp and Message.
      73             : 
      74             : struct EvStart : sc::event<EvStart> {
      75        1660 :     EvStart() {
      76        1660 :     }
      77             :     static const char *Name() {
      78             :         return "EvStart";
      79             :     }
      80             : };
      81             : 
      82             : struct EvStop : sc::event<EvStop> {
      83        1012 :     explicit EvStop(int subcode) : subcode(subcode) {
      84        1012 :     }
      85             :     static const char *Name() {
      86             :         return "EvStop";
      87             :     }
      88             :     int subcode;
      89             : };
      90             : 
      91             : struct EvIdleHoldTimerExpired : sc::event<EvIdleHoldTimerExpired> {
      92         579 :     explicit EvIdleHoldTimerExpired(Timer *timer)  : timer_(timer) {
      93         572 :     }
      94             :     static const char *Name() {
      95             :         return "EvIdleHoldTimerExpired";
      96             :     }
      97         580 :     bool validate(StateMachine *state_machine) const {
      98         580 :         return !timer_->cancelled();
      99             :     }
     100             : 
     101             :     Timer *timer_;
     102             : };
     103             : 
     104             : struct EvConnectTimerExpired : sc::event<EvConnectTimerExpired> {
     105         996 :     explicit EvConnectTimerExpired(Timer *timer) : timer_(timer) {
     106         982 :     }
     107             :     static const char *Name() {
     108             :         return "EvConnectTimerExpired";
     109             :     }
     110        1004 :     bool validate(StateMachine *state_machine) const {
     111        1004 :         if (timer_->cancelled()) {
     112          10 :             return false;
     113         998 :         } else if (state_machine->get_state() == StateMachine::ACTIVE) {
     114         920 :             return (state_machine->passive_session() == NULL);
     115          78 :         } else if (state_machine->get_state() == StateMachine::CONNECT) {
     116          84 :             return (!state_machine->active_session() ||
     117          84 :                     !state_machine->active_session()->IsEstablished());
     118             :         }
     119           0 :         return false;
     120             :     }
     121             : 
     122             :     Timer *timer_;
     123             : };
     124             : 
     125             : struct EvOpenTimerExpired : sc::event<EvOpenTimerExpired> {
     126           0 :     explicit EvOpenTimerExpired(Timer *timer) : timer_(timer) {
     127           0 :     }
     128             :     static const char *Name() {
     129             :         return "EvOpenTimerExpired";
     130             :     }
     131           0 :     bool validate(StateMachine *state_machine) const {
     132           0 :         if (timer_->cancelled()) {
     133           0 :             return false;
     134             :         } else {
     135           0 :             return (state_machine->passive_session() != NULL);
     136             :         }
     137             :     }
     138             : 
     139             :     Timer *timer_;
     140             : };
     141             : 
     142             : struct EvHoldTimerExpired : sc::event<EvHoldTimerExpired> {
     143           0 :     explicit EvHoldTimerExpired(Timer *timer) : timer_(timer) {
     144           0 :     }
     145             :     static const char *Name() {
     146             :         return "EvHoldTimerExpired";
     147             :     }
     148           0 :     bool validate(StateMachine *state_machine) const {
     149           0 :         if (timer_->cancelled()) {
     150           0 :             return false;
     151           0 :         } else if (state_machine->get_state() == StateMachine::OPENSENT) {
     152           0 :             return true;
     153             :         } else {
     154           0 :             return (state_machine->peer()->session() != NULL);
     155             :         }
     156             :     }
     157             : 
     158             :     Timer *timer_;
     159             : };
     160             : 
     161             : struct EvTcpConnected : sc::event<EvTcpConnected> {
     162         807 :     explicit EvTcpConnected(BgpSession *session) : session(session) {
     163         807 :     }
     164             :     static const char *Name() {
     165             :         return "EvTcpConnected";
     166             :     }
     167         806 :     bool validate(StateMachine *state_machine) const {
     168         806 :         return (state_machine->active_session() == session);
     169             :     }
     170             : 
     171             :     BgpSession *session;
     172             : };
     173             : 
     174             : struct EvTcpConnectFail : sc::event<EvTcpConnectFail> {
     175          36 :     explicit EvTcpConnectFail(BgpSession *session) : session(session) {
     176          36 :     }
     177             :     static const char *Name() {
     178             :         return "EvTcpConnectFail";
     179             :     }
     180          36 :     bool validate(StateMachine *state_machine) const {
     181          36 :         return (state_machine->active_session() == session);
     182             :     }
     183             : 
     184             :     BgpSession *session;
     185             : };
     186             : 
     187             : struct EvTcpPassiveOpen : sc::event<EvTcpPassiveOpen> {
     188         700 :     explicit EvTcpPassiveOpen(BgpSession *session) : session(session) {
     189         700 :     }
     190             :     static const char *Name() {
     191             :         return "EvTcpPassiveOpen";
     192             :     }
     193             : 
     194             :     BgpSession *session;
     195             : };
     196             : 
     197             : struct EvTcpClose : sc::event<EvTcpClose> {
     198         409 :     explicit EvTcpClose(BgpSession *session) : session(session) {
     199         409 :     }
     200             :     static const char *Name() {
     201             :         return "EvTcpClose";
     202             :     }
     203         408 :     bool validate(StateMachine *state_machine) const {
     204         408 :         return ((state_machine->peer()->session() == session) ||
     205         798 :                 (state_machine->active_session() == session) ||
     206         798 :                 (state_machine->passive_session() == session));
     207             :     }
     208             : 
     209             :     BgpSession *session;
     210             : };
     211             : 
     212             : // Used to defer the session delete after all events currently on the queue.
     213             : struct EvTcpDeleteSession : sc::event<EvTcpDeleteSession> {
     214        1550 :     explicit EvTcpDeleteSession(BgpSession *session) : session(session) {
     215        1544 :     }
     216             :     static const char *Name() {
     217             :         return "EvTcpDeleteSession";
     218             :     }
     219             : 
     220             :     BgpSession *session;
     221             : };
     222             : 
     223             : struct EvBgpHeaderError : sc::event<EvBgpHeaderError> {
     224           0 :     EvBgpHeaderError(BgpSession *session, int subcode, const uint8_t *_data,
     225             :         size_t data_size)
     226           0 :         : session(session), subcode(subcode) {
     227           0 :         if (_data)
     228           0 :             data = std::string((const char *)_data, data_size);
     229           0 :     }
     230             :     static const char *Name() {
     231             :         return "EvBgpHeaderError";
     232             :     }
     233             : 
     234             :     BgpSession *session;
     235             :     int subcode;
     236             :     std::string data;
     237             : };
     238             : 
     239             : struct EvBgpOpen : sc::event<EvBgpOpen> {
     240        1140 :     EvBgpOpen(BgpSession *session, const BgpProto::OpenMessage *msg)
     241        1140 :         : session(session), msg(msg) {
     242        1139 :         BGP_LOG_PEER(Message, session->peer(), SandeshLevel::SYS_INFO,
     243             :             BGP_LOG_FLAG_SYSLOG, BGP_PEER_DIR_IN,
     244             :             "Open " << msg->ToString());
     245        1138 :     }
     246             :     static const char *Name() {
     247             :         return "EvBgpOpen";
     248             :     }
     249        1153 :     bool validate(StateMachine *state_machine) const {
     250        1153 :         return ((state_machine->peer()->session() == session) ||
     251        1804 :                 (state_machine->active_session() == session) ||
     252        1804 :                 (state_machine->passive_session() == session));
     253             :     }
     254             : 
     255             :     BgpSession *session;
     256             :     boost::shared_ptr<const BgpProto::OpenMessage> msg;
     257             : };
     258             : 
     259             : struct EvBgpOpenError : sc::event<EvBgpOpenError> {
     260          29 :     EvBgpOpenError(BgpSession *session, int subcode,
     261             :         const uint8_t *_data = NULL, size_t data_size = 0)
     262          29 :         : session(session), subcode(subcode) {
     263          28 :         if (subcode == BgpProto::Notification::UnsupportedVersion) {
     264             :             // For unsupported version, we need to send our version in the
     265             :             // data field.
     266           0 :             char version = 4;
     267           0 :             data.push_back(version);
     268          28 :         } else if (_data) {
     269           0 :             data = std::string((const char *)_data, data_size);
     270             :         }
     271          28 :     }
     272             :     static const char *Name() {
     273             :         return "EvBgpOpenError";
     274             :     }
     275             : 
     276             :     BgpSession *session;
     277             :     int subcode;
     278             :     std::string data;
     279             : };
     280             : 
     281             : struct EvBgpKeepalive : sc::event<EvBgpKeepalive> {
     282        1434 :     explicit EvBgpKeepalive(BgpSession *session) : session(session) {
     283        1434 :         const StateMachine *state_machine = session->peer()->state_machine();
     284             :         SandeshLevel::type log_level;
     285        1432 :         if (state_machine->get_state() == StateMachine::ESTABLISHED) {
     286         427 :             log_level = Sandesh::LoggingUtLevel();
     287             :         } else {
     288        1002 :             log_level = SandeshLevel::SYS_INFO;
     289             :         }
     290        1429 :         BGP_LOG_PEER(Message, session->peer(), log_level,
     291             :                      BGP_LOG_FLAG_SYSLOG, BGP_PEER_DIR_IN, "Keepalive");
     292        1429 :     }
     293             :     static const char *Name() {
     294             :         return "EvBgpKeepalive";
     295             :     }
     296        1432 :     bool validate(StateMachine *state_machine) const {
     297        1432 :         return !session->IsClosed();
     298             :     }
     299             : 
     300             :     BgpSession *session;
     301             : };
     302             : 
     303             : struct EvBgpNotification : sc::event<EvBgpNotification> {
     304         647 :     EvBgpNotification(BgpSession *session, const BgpProto::Notification *msg)
     305         647 :         : session(session), msg(msg) {
     306             :         string peer_key =
     307         646 :             session->peer() ? session->peer()->ToUVEKey() : session->ToString();
     308         649 :         session->LogNotification(msg->error, msg->subcode, BGP_PEER_DIR_IN,
     309             :                                  peer_key, *msg);
     310         647 :     }
     311         491 :     static const char *Name() {
     312         491 :         return "EvBgpNotification";
     313             :     }
     314         656 :     bool validate(StateMachine *state_machine) const {
     315         656 :         return ((state_machine->peer()->session() == session) ||
     316         674 :                 (state_machine->active_session() == session) ||
     317         674 :                 (state_machine->passive_session() == session));
     318             :     }
     319             : 
     320             :     BgpSession *session;
     321             :     boost::shared_ptr<const BgpProto::Notification> msg;
     322             : };
     323             : 
     324             : struct EvBgpUpdate : sc::event<EvBgpUpdate> {
     325        1092 :     EvBgpUpdate(BgpSession *session, const BgpProto::Update *msg,
     326        1092 :         size_t msgsize) : session(session), msg(msg), msgsize(msgsize) {
     327        1088 :     }
     328             :     static const char *Name() {
     329             :         return "EvBgpUpdate";
     330             :     }
     331             : 
     332             :     BgpSession *session;
     333             :     boost::shared_ptr<const BgpProto::Update> msg;
     334             :     size_t msgsize;
     335             : };
     336             : 
     337             : struct EvBgpUpdateError : sc::event<EvBgpUpdateError> {
     338           0 :     EvBgpUpdateError(BgpSession *session, int subcode, std::string data)
     339           0 :         : session(session), subcode(subcode), data(data) {
     340           0 :     }
     341             :     static const char *Name() {
     342             :         return "EvBgpUpdateError";
     343             :     }
     344             : 
     345             :     BgpSession *session;
     346             :     int subcode;
     347             :     std::string data;
     348             : };
     349             : 
     350             : // States for the BGP state machine.
     351             : struct Idle;
     352             : struct Active;
     353             : struct Connect;
     354             : struct OpenSent;
     355             : struct OpenConfirm;
     356             : struct Established;
     357             : 
     358             : template <typename Ev, int code = 0>
     359             : struct TransitToIdle {
     360             :     typedef sc::transition<Ev, Idle, StateMachine,
     361             :         &StateMachine::OnIdle<Ev, code> > reaction;
     362             : };
     363             : 
     364             : template <>
     365             : struct TransitToIdle<EvBgpNotification, 0> {
     366             :     typedef sc::transition<EvBgpNotification, Idle, StateMachine,
     367             :         &StateMachine::OnIdleNotification> reaction;
     368             : };
     369             : 
     370             : template <typename Ev>
     371             : struct IdleCease {
     372             :     typedef sc::transition<Ev, Idle, StateMachine,
     373             :         &StateMachine::OnIdleCease<Ev> > reaction;
     374             : };
     375             : 
     376             : template <typename Ev>
     377             : struct IdleFsmError {
     378             :     typedef sc::transition<Ev, Idle, StateMachine,
     379             :         &StateMachine::OnIdle<Ev, BgpProto::Notification::FSMErr> > reaction;
     380             : };
     381             : 
     382             : template <typename Ev, int code>
     383             : struct IdleError {
     384             :     typedef sc::transition<Ev, Idle, StateMachine,
     385             :             &StateMachine::OnIdleError<Ev, code> > reaction;
     386             : };
     387             : 
     388             : //
     389             : // We start out in Idle and progress when we get EvStart. We also come back
     390             : // to Idle when there's any kind of error that we need to recover from.
     391             : //
     392             : struct Idle : sc::state<Idle, StateMachine> {
     393             :     typedef mpl::list<
     394             :         IdleCease<EvStop>::reaction,
     395             :         sc::custom_reaction<EvStart>,
     396             :         sc::custom_reaction<EvIdleHoldTimerExpired>,
     397             :         sc::custom_reaction<EvTcpPassiveOpen>
     398             :     > reactions;
     399             : 
     400             :     // Increment the flap count after setting the state.  This is friendly to
     401             :     // tests that first wait for the flap count to go up and then wait for the
     402             :     // state to reach ESTABLISHED again.  Incrementing the flap count before
     403             :     // setting the state could cause tests to break if they look at the old
     404             :     // state (which is still ESTABLISHED) and assume that it's the new state.
     405             :     // This could also be solved by using a mutex but it's not really needed.
     406        2290 :     explicit Idle(my_context ctx) : my_base(ctx) {
     407        2285 :         StateMachine *state_machine = &context<StateMachine>();
     408        2292 :         BgpPeer *peer = state_machine->peer();
     409        2290 :         BgpSession *session = peer->session();
     410        2290 :         peer->clear_session();
     411        2297 :         state_machine->set_active_session(NULL);
     412        2290 :         state_machine->set_passive_session(NULL);
     413        2289 :         state_machine->DeleteSession(session);
     414        2284 :         state_machine->CancelOpenTimer();
     415        2297 :         state_machine->CancelIdleHoldTimer();
     416        2298 :         state_machine->set_state(StateMachine::IDLE);
     417        2291 :     }
     418             : 
     419        4526 :     ~Idle() {
     420        2264 :         StateMachine *state_machine = &context<StateMachine>();
     421        2276 :         state_machine->CancelIdleHoldTimer();
     422        4553 :     }
     423             : 
     424             :     // Start idle hold timer if it's enabled, else go to Active right away.
     425        1636 :     sc::result react(const EvStart &event) {
     426        1636 :         StateMachine *state_machine = &context<StateMachine>();
     427        1638 :         if (state_machine->idle_hold_time()) {
     428         970 :             state_machine->StartIdleHoldTimer();
     429             :         } else {
     430         664 :             return transit<Active>();
     431             :         }
     432         983 :         return discard_event();
     433             :     }
     434             : 
     435             :     // The idle hold timer expired, go to Active.
     436         583 :     sc::result react(const EvIdleHoldTimerExpired &event) {
     437         583 :         return transit<Active>();
     438             :     }
     439             : 
     440             :     // Delete the session and ignore event.
     441          18 :     sc::result react(const EvTcpPassiveOpen &event) {
     442          18 :         StateMachine *state_machine = &context<StateMachine>();
     443          18 :         BgpSession *session = event.session;
     444          18 :         state_machine->DeleteSession(session);
     445          18 :         return discard_event();
     446             :     }
     447             : };
     448             : 
     449             : //
     450             : // In Active state, we wait for the connect timer timer to expire before we
     451             : // move to Connect and start the active session. If we get a passive session
     452             : // we accept it and wait for our delayed open timer to expire.
     453             : //
     454             : struct Active : sc::state<Active, StateMachine> {
     455             :     typedef mpl::list<
     456             :         IdleCease<EvStop>::reaction,
     457             :         sc::custom_reaction<EvConnectTimerExpired>,
     458             :         sc::custom_reaction<EvOpenTimerExpired>,
     459             :         sc::custom_reaction<EvTcpPassiveOpen>,
     460             :         sc::custom_reaction<EvTcpClose>,
     461             :         sc::custom_reaction<EvBgpOpen>,
     462             :         TransitToIdle<EvBgpNotification>::reaction,
     463             :         IdleFsmError<EvBgpKeepalive>::reaction,
     464             :         IdleFsmError<EvBgpUpdate>::reaction,
     465             :         IdleError<EvBgpHeaderError,
     466             :             BgpProto::Notification::MsgHdrErr>::reaction,
     467             :         IdleError<EvBgpOpenError,
     468             :             BgpProto::Notification::OpenMsgErr>::reaction,
     469             :         IdleError<EvBgpUpdateError,
     470             :             BgpProto::Notification::UpdateMsgErr>::reaction
     471             :     > reactions;
     472             : 
     473             :     // Start the connect timer if the peer is not passive and we don't have
     474             :     // a passive session. There may a passive session if we got here from
     475             :     // Connect or OpenSent.
     476        1367 :     explicit Active(my_context ctx) : my_base(ctx) {
     477        1359 :         StateMachine *state_machine = &context<StateMachine>();
     478        1366 :         BgpPeer *peer = state_machine->peer();
     479        1364 :         if (!peer->IsPassive() && !state_machine->passive_session())
     480        1327 :             state_machine->StartConnectTimer(state_machine->GetConnectTime());
     481        1385 :         state_machine->set_state(StateMachine::ACTIVE);
     482        1385 :     }
     483             : 
     484             :     // Stop the connect timer.  If we are going to Connect state, the timer
     485             :     // will be started again from the constructor for that state.
     486        2750 :     ~Active() {
     487        1375 :         StateMachine *state_machine = &context<StateMachine>();
     488        1380 :         state_machine->CancelConnectTimer();
     489        2758 :     }
     490             : 
     491             :     // The connect timer expired, go to Connect if the peer is not passive.
     492         917 :     sc::result react(const EvConnectTimerExpired &event) {
     493         917 :         StateMachine *state_machine = &context<StateMachine>();
     494         919 :         BgpPeer *peer = state_machine->peer();
     495         919 :         if (peer->IsPassive()) {
     496           0 :             return discard_event();
     497             :         } else {
     498         917 :             return transit<Connect>();
     499             :         }
     500             :     }
     501             : 
     502             :     // Send an OPEN message on the passive session and go to OpenSent.
     503           0 :     sc::result react(const EvOpenTimerExpired &event) {
     504           0 :         StateMachine *state_machine = &context<StateMachine>();
     505           0 :         BgpSession *session = state_machine->passive_session();
     506           0 :         if (session) {
     507           0 :             BgpPeer *peer = state_machine->peer();
     508           0 :             peer->SendOpen(session);
     509           0 :             return transit<OpenSent>();
     510             :         }
     511           0 :         return discard_event();
     512             :     }
     513             : 
     514             :     // Cancel the connect timer since we now have a passive session. Note
     515             :     // that we get rid of any existing passive session if we get another
     516             :     // one. Also start the open timer in order to implement a delayed open
     517             :     // on the passive session.
     518         380 :     sc::result react(const EvTcpPassiveOpen &event) {
     519         380 :         StateMachine *state_machine = &context<StateMachine>();
     520         381 :         state_machine->set_passive_session(event.session);
     521         379 :         state_machine->CancelConnectTimer();
     522         381 :         state_machine->StartOpenTimer(StateMachine::kOpenTime);
     523         386 :         return discard_event();
     524             :     }
     525             : 
     526             :     // Start the connect timer since we don't have a passive session anymore.
     527           0 :     sc::result react(const EvTcpClose &event) {
     528           0 :         StateMachine *state_machine = &context<StateMachine>();
     529           0 :         if (event.session == state_machine->passive_session()) {
     530           0 :             state_machine->set_passive_session(NULL);
     531           0 :             state_machine->CancelOpenTimer();
     532           0 :             state_machine->StartConnectTimer(state_machine->GetConnectTime());
     533             :         }
     534           0 :         return discard_event();
     535             :     }
     536             : 
     537             :     // We received an OPEN message on the passive session. Send OPEN message
     538             :     // and go to OpenConfirm.
     539         370 :     sc::result react(const EvBgpOpen &event) {
     540         370 :         StateMachine *state_machine = &context<StateMachine>();
     541         370 :         BgpPeer *peer = state_machine->peer();
     542         370 :         BgpSession *session = state_machine->passive_session();
     543             : 
     544             :         // If EvTcpPassiveOpen was received in IDLE state and the following
     545             :         // open message happens to be processed when we are in Active state,
     546             :         // we may not have the passive session for open message. Ignore the
     547             :         // event in that case.
     548         370 :         if (!session)
     549           0 :             return discard_event();
     550             : 
     551             :         // Ignore the OPEN if it was received on a stale passive session.
     552             :         // This can happen if we got another passive session between the
     553             :         // original passive session and the OPEN message on that session.
     554         370 :         if (session != event.session)
     555           0 :             return discard_event();
     556             : 
     557             :         // Send OPEN and go to OpenConfirm.
     558         370 :         int local_holdtime = state_machine->GetConfiguredHoldTime();
     559         370 :         state_machine->set_hold_time(min(event.msg->holdtime, local_holdtime));
     560         371 :         state_machine->AssignSession(false);
     561         371 :         peer->SendOpen(session);
     562         370 :         if (!peer->SetCapabilities(event.msg.get()))
     563           0 :             return discard_event();
     564         368 :         return transit<OpenConfirm>();
     565             :     }
     566             : };
     567             : 
     568             : //
     569             : // In Connect state, we wait for the active session to come up. We also accept
     570             : // a passive session if we get one and start a delayed open timer.
     571             : //
     572             : struct Connect : sc::state<Connect, StateMachine> {
     573             :     typedef mpl::list<
     574             :         IdleCease<EvStop>::reaction,
     575             :         sc::custom_reaction<EvConnectTimerExpired>,
     576             :         sc::custom_reaction<EvOpenTimerExpired>,
     577             :         sc::custom_reaction<EvTcpConnected>,
     578             :         sc::custom_reaction<EvTcpConnectFail>,
     579             :         sc::custom_reaction<EvTcpPassiveOpen>,
     580             :         sc::custom_reaction<EvTcpClose>,
     581             :         sc::custom_reaction<EvBgpOpen>,
     582             :         TransitToIdle<EvBgpNotification>::reaction,
     583             :         IdleFsmError<EvBgpKeepalive>::reaction,
     584             :         IdleFsmError<EvBgpUpdate>::reaction,
     585             :         IdleError<EvBgpHeaderError,
     586             :             BgpProto::Notification::MsgHdrErr>::reaction,
     587             :         IdleError<EvBgpOpenError,
     588             :             BgpProto::Notification::OpenMsgErr>::reaction,
     589             :         IdleError<EvBgpUpdateError,
     590             :             BgpProto::Notification::UpdateMsgErr>::reaction
     591             :     > reactions;
     592             : 
     593         910 :     explicit Connect(my_context ctx) : my_base(ctx) {
     594         904 :         StateMachine *state_machine = &context<StateMachine>();
     595         910 :         state_machine->connect_attempts_inc();
     596         909 :         state_machine->StartConnectTimer(state_machine->GetConnectTime());
     597         922 :         state_machine->StartSession();
     598         921 :         state_machine->set_state(StateMachine::CONNECT);
     599         905 :     }
     600             : 
     601        1826 :     ~Connect() {
     602         913 :         StateMachine *state_machine = &context<StateMachine>();
     603         916 :         state_machine->CancelConnectTimer();
     604        1832 :     }
     605             : 
     606             :     // Get rid of the active session and go back to Active.
     607          77 :     sc::result react(const EvConnectTimerExpired &event) {
     608          77 :         StateMachine *state_machine = &context<StateMachine>();
     609          77 :         BgpPeer *peer = state_machine->peer();
     610          77 :         state_machine->set_active_session(NULL);
     611          77 :         peer->inc_connect_timer_expired();
     612          77 :         return transit<Active>();
     613             :     }
     614             : 
     615             :     // The open timer for the passive session expired.  Since the active
     616             :     // session has not yet come up we get rid of it and decide to use the
     617             :     // passive session.  Send an OPEN on the passive session and move to
     618             :     // OpenSent.
     619           0 :     sc::result react(const EvOpenTimerExpired &event) {
     620           0 :         StateMachine *state_machine = &context<StateMachine>();
     621           0 :         BgpPeer *peer = state_machine->peer();
     622           0 :         peer->SendOpen(state_machine->passive_session());
     623           0 :         state_machine->set_active_session(NULL);
     624           0 :         return transit<OpenSent>();
     625             :     }
     626             : 
     627             :     // The active session is up.  Send an OPEN right away and go to OpenSent.
     628             :     // Note that we may also have the open timer running if we have a passive
     629             :     // session.  Things will eventually get resolved in the OpenSent state.
     630         805 :     sc::result react(const EvTcpConnected &event) {
     631         805 :         StateMachine *state_machine = &context<StateMachine>();
     632         805 :         BgpPeer *peer = state_machine->peer();
     633         805 :         BgpSession *session = state_machine->active_session();
     634         805 :         peer->SendOpen(session);
     635         800 :         return transit<OpenSent>();
     636             :     }
     637             : 
     638             :     // Delete the active session and go to Active.  Note that we may still
     639             :     // have a passive session.
     640          36 :     sc::result react(const EvTcpConnectFail &event) {
     641          36 :         StateMachine *state_machine = &context<StateMachine>();
     642          36 :         state_machine->set_active_session(NULL);
     643          36 :         return transit<Active>();
     644             :     }
     645             : 
     646             :     // Start the open timer in order to implement a delayed open on passive
     647             :     // session.  Note that we get rid of any existing passive session if we
     648             :     // had one.
     649          18 :     sc::result react(const EvTcpPassiveOpen &event) {
     650          18 :         StateMachine *state_machine = &context<StateMachine>();
     651          18 :         state_machine->set_passive_session(event.session);
     652          18 :         state_machine->StartOpenTimer(StateMachine::kOpenTime);
     653          18 :         return discard_event();
     654             :     }
     655             : 
     656             :     // Either the active or passive session got closed.
     657           0 :     sc::result react(const EvTcpClose &event) {
     658           0 :         StateMachine *state_machine = &context<StateMachine>();
     659           0 :         if (event.session == state_machine->passive_session()) {
     660             :             // Get rid of the passive session and cancel the open timer.
     661             :             // Stay in Connect and wait for the active session to come up.
     662           0 :             state_machine->set_passive_session(NULL);
     663           0 :             state_machine->CancelOpenTimer();
     664           0 :             return discard_event();
     665             :         } else {
     666             :             // Get rid of the active session and go to Active.  Note that we
     667             :             // may still have a passive session at this point.
     668           0 :             assert(event.session == state_machine->active_session());
     669           0 :             state_machine->set_active_session(NULL);
     670           0 :             return transit<Active>();
     671             :         }
     672             :     }
     673             : 
     674             :     // We received an OPEN message on the passive session. Send OPEN message
     675             :     // and go to OpenConfirm.
     676           2 :     sc::result react(const EvBgpOpen &event) {
     677           2 :         StateMachine *state_machine = &context<StateMachine>();
     678           2 :         BgpPeer *peer = state_machine->peer();
     679           2 :         BgpSession *session = state_machine->passive_session();
     680             : 
     681             :         // If EvTcpPassiveOpen was received in IDLE state and the following
     682             :         // open message happens to be processed when we are in Connect state,
     683             :         // we may not have the passive session for open message. Ignore the
     684             :         // event in that case.
     685           2 :         if (!session)
     686           0 :             return discard_event();
     687             : 
     688             :         // Ignore the OPEN if it was received on a stale passive session.
     689             :         // This can happen if we got another passive session between the
     690             :         // original passive session and the OPEN message on that session.
     691           2 :         if (session != event.session)
     692           0 :             return discard_event();
     693             : 
     694             :         // Send OPEN and go to OpenConfirm.  Since we've decided to use the
     695             :         // passive session, we get rid of the active one.
     696           2 :         int local_holdtime = state_machine->GetConfiguredHoldTime();
     697           2 :         state_machine->set_hold_time(min(event.msg->holdtime, local_holdtime));
     698           2 :         state_machine->set_active_session(NULL);
     699           2 :         state_machine->AssignSession(false);
     700           2 :         peer->SendOpen(session);
     701           2 :         if (!peer->SetCapabilities(event.msg.get()))
     702           0 :             return discard_event();
     703           2 :         return transit<OpenConfirm>();
     704             :     }
     705             : };
     706             : 
     707             : //
     708             : // In the OpenSent state, we wait for the other end to send an OPEN message.
     709             : // The state machine reaches OpenSent after sending an immediate OPEN message
     710             : // on the active connection or a delayed OPEN on a passive connection. In the
     711             : // former case there may be both a passive and active session. In the latter,
     712             : // there is only a passive connection.
     713             : //
     714             : struct OpenSent : sc::state<OpenSent, StateMachine> {
     715             :     typedef mpl::list<
     716             :         IdleCease<EvStop>::reaction,
     717             :         sc::custom_reaction<EvOpenTimerExpired>,
     718             :         TransitToIdle<EvHoldTimerExpired,
     719             :             BgpProto::Notification::HoldTimerExp>::reaction,
     720             :         sc::custom_reaction<EvTcpPassiveOpen>,
     721             :         sc::custom_reaction<EvTcpClose>,
     722             :         sc::custom_reaction<EvBgpOpen>,
     723             :         sc::custom_reaction<EvBgpNotification>,
     724             :         IdleFsmError<EvBgpKeepalive>::reaction,
     725             :         IdleFsmError<EvBgpUpdate>::reaction,
     726             :         IdleError<EvBgpHeaderError,
     727             :             BgpProto::Notification::MsgHdrErr>::reaction,
     728             :         IdleError<EvBgpOpenError,
     729             :             BgpProto::Notification::OpenMsgErr>::reaction,
     730             :         IdleError<EvBgpUpdateError,
     731             :             BgpProto::Notification::UpdateMsgErr>::reaction
     732             :     > reactions;
     733             : 
     734             :     // Start the hold timer to ensure that we don't get stuck in OpenSent if
     735             :     // the other end never sends an OPEN message.
     736         799 :     explicit OpenSent(my_context ctx) : my_base(ctx) {
     737         801 :         StateMachine *state_machine = &context<StateMachine>();
     738         802 :         state_machine->set_hold_time(StateMachine::kOpenSentHoldTime);
     739         797 :         state_machine->StartHoldTimer();
     740         805 :         state_machine->set_state(StateMachine::OPENSENT);
     741         805 :     }
     742             : 
     743             :     // Cancel the hold timer.  If we go to OpenConfirm, the timer will get
     744             :     // started again from the constructor for that state.
     745        1602 :     ~OpenSent() {
     746         801 :         StateMachine *state_machine = &context<StateMachine>();
     747         804 :         state_machine->CancelHoldTimer();
     748        1606 :     }
     749             : 
     750             :     // Send an OPEN message on the passive session.  This means that we must
     751             :     // have got to OpenSent because we sent an OPEN on the active session.
     752             :     // Stay in OpenSent and wait for the other end to send an OPEN message.
     753           0 :     sc::result react(const EvOpenTimerExpired &event) {
     754           0 :         StateMachine *state_machine = &context<StateMachine>();
     755           0 :         BgpPeer *peer = state_machine->peer();
     756           0 :         peer->SendOpen(state_machine->passive_session());
     757           0 :         return discard_event();
     758             :     }
     759             : 
     760             :     // Update the passive session and start the open timer. Note that any
     761             :     // existing passive session will get deleted.
     762         271 :     sc::result react(const EvTcpPassiveOpen &event) {
     763         271 :         StateMachine *state_machine = &context<StateMachine>();
     764         272 :         state_machine->set_passive_session(event.session);
     765         271 :         state_machine->StartOpenTimer(StateMachine::kOpenTime);
     766             : 
     767             :         // If we don't have an active session, we need to go back to Active
     768             :         // since we haven't sent an OPEN message on the new passive session.
     769             :         // If we have active session, it means that we sent an OPEN message
     770             :         // on it already, so we can stay in OpenSent.
     771         272 :         if (!state_machine->active_session()) {
     772           0 :             return transit<Active>();
     773             :         } else {
     774         273 :             return discard_event();
     775             :         }
     776             :     }
     777             : 
     778             :     // Either the passive or the active session closed.
     779          18 :     sc::result react(const EvTcpClose &event) {
     780          18 :         StateMachine *state_machine = &context<StateMachine>();
     781          18 :         if (event.session == state_machine->active_session()) {
     782             :             // Since the active session was closed, we go back to Active if
     783             :             // don't have a passive session or if we haven't yet sent an OPEN
     784             :             // on the passive session.
     785          18 :             state_machine->set_active_session(NULL);
     786          18 :             if (state_machine->passive_session() == NULL ||
     787           0 :                 state_machine->OpenTimerRunning()) {
     788          18 :                 return transit<Active>();
     789             :             }
     790             :         } else {
     791             :             // Since the passive session was closed, we cancel the open timer.
     792             :             // We need to go back to Active if don't have a active session.
     793           0 :             state_machine->set_passive_session(NULL);
     794           0 :             state_machine->CancelOpenTimer();
     795           0 :             if (state_machine->active_session() == NULL)
     796           0 :                 return transit<Active>();
     797             :         }
     798             : 
     799           0 :         return discard_event();
     800             :     }
     801             : 
     802             :     // This one is pretty involved.
     803         778 :     sc::result react(const EvBgpOpen &event) {
     804         778 :         StateMachine *state_machine = &context<StateMachine>();
     805         778 :         BgpPeer *peer = state_machine->peer();
     806         778 :         BgpSession *session = NULL;
     807             : 
     808        1054 :         if (state_machine->passive_session() &&
     809         276 :             state_machine->active_session()) {
     810             :             // Need to resolve connection collision.
     811         276 :             uint32_t local_bgp_id = peer->server()->bgp_identifier();
     812         276 :             if (event.msg->identifier > local_bgp_id) {
     813             :                 // Passive connection wins, close the active session.
     814         129 :                 peer->SendNotification(state_machine->active_session(),
     815             :                     BgpProto::Notification::Cease,
     816             :                     BgpProto::Notification::ConnectionCollision,
     817             :                     "Connection collision - closing active session");
     818         129 :                 state_machine->set_active_session(NULL);
     819             : 
     820             :                 // If we haven't already sent an OPEN message on the passive
     821             :                 // session, cancel the open timer and send the OPEN message.
     822         129 :                 session = state_machine->passive_session();
     823         129 :                 if (state_machine->OpenTimerRunning()) {
     824         129 :                     state_machine->CancelOpenTimer();
     825         129 :                     peer->SendOpen(session);
     826             :                 }
     827             : 
     828             :                 // If the OPEN was not received on the passive session, stay
     829             :                 // in OpenSent and wait for the other end to send the OPEN on
     830             :                 // on the passive session.
     831             :                 // If the OPEN was received on the passive session, we assign
     832             :                 // the passive session to the peer and fall through to go to
     833             :                 // OpenConfirm.
     834         128 :                 if (event.session != session) {
     835           0 :                     return discard_event();
     836             :                 } else {
     837         128 :                     state_machine->AssignSession(false);
     838             :                 }
     839             :             } else {
     840             :                 // Active connection wins, close the passive session.
     841         147 :                 peer->SendNotification(state_machine->passive_session(),
     842             :                     BgpProto::Notification::Cease,
     843             :                     BgpProto::Notification::ConnectionCollision,
     844             :                     "Connection collision - closing passive session");
     845         147 :                 state_machine->set_passive_session(NULL);
     846         147 :                 state_machine->CancelOpenTimer();
     847             : 
     848             :                 // If the OPEN was not received on the active session, stay
     849             :                 // in OpenSent and wait for the other end to send the OPEN on
     850             :                 // on the active session.
     851             :                 // If the OPEN was received on the active session, we assign
     852             :                 // the active session to the peer and fall through to go to
     853             :                 // OpenConfirm.
     854         147 :                 session = state_machine->active_session();
     855         147 :                 if (event.session != session) {
     856         147 :                     return discard_event();
     857             :                 } else {
     858           0 :                     state_machine->AssignSession(true);
     859             :                 }
     860             :             }
     861         502 :         } else if (state_machine->passive_session()) {
     862             :             // If the OPEN was not received on the passive session, stay
     863             :             // in OpenSent and wait for the other end to send the OPEN on
     864             :             // on the passive session.
     865             :             // If the OPEN was received on the passive session, we assign
     866             :             // the passive session to the peer and fall through to go to
     867             :             // OpenConfirm.
     868           0 :             session = state_machine->passive_session();
     869           0 :             if (event.session != session) {
     870           0 :                 return discard_event();
     871             :             } else {
     872           0 :                 state_machine->AssignSession(false);
     873             :             }
     874         502 :         } else if (state_machine->active_session()) {
     875             :             // If the OPEN was not received on the active session, stay
     876             :             // in OpenSent and wait for the other end to send the OPEN on
     877             :             // on the active session.
     878             :             // If the OPEN was received on the active session, we assign
     879             :             // the active session to the peer and fall through to go to
     880             :             // OpenConfirm.
     881         502 :             session = state_machine->active_session();
     882         502 :             if (event.session != session) {
     883           0 :                 return discard_event();
     884             :             } else {
     885         502 :                 state_machine->AssignSession(true);
     886             :             }
     887             :         }
     888             : 
     889         630 :         int local_holdtime = state_machine->GetConfiguredHoldTime();
     890         629 :         state_machine->set_hold_time(min(event.msg->holdtime, local_holdtime));
     891         631 :         if (!peer->SetCapabilities(event.msg.get()))
     892           0 :             return discard_event();
     893         628 :         return transit<OpenConfirm>();
     894             :     }
     895             : 
     896             :     // Notification received on one of the sessions for this state machine.
     897         144 :     sc::result react(const EvBgpNotification &event) {
     898         144 :         StateMachine *state_machine = &context<StateMachine>();
     899             : 
     900             :         // Ignore if the NOTIFICATION came in on a stale session.
     901         145 :         if (!state_machine->ProcessNotificationEvent(event.session))
     902           0 :             return discard_event();
     903             : 
     904             :         // The call to ProcessNotificationEvent above would have closed
     905             :         // the session on the which the message was received.
     906         145 :         if (state_machine->active_session()) {
     907             :             // Since we still have an active session, the passive session
     908             :             // has been closed, so we cancel the open timer.  We stay in
     909             :             // OpenSent since we still have an active session on which we
     910             :             // have already sent an OPEN message.
     911           0 :             state_machine->CancelOpenTimer();
     912           0 :             return discard_event();
     913         145 :         } else if (state_machine->passive_session()) {
     914             :             // Since we still have the passive session, the active session
     915             :             // has been closed.  If the open timer is still running, we go
     916             :             // back to Active because we don't have an active session now.
     917             :             // If the open timer has already expired, we stay in OpenSent
     918             :             // since we have sent an OPEN on the passive session.
     919           1 :             if (state_machine->OpenTimerRunning()) {
     920           1 :                 return transit<Active>();
     921             :             } else {
     922           0 :                 return discard_event();
     923             :             }
     924             :         } else {
     925             :             // We have neither an active or passive session.  Go to Idle.
     926             :             return transit<Idle, StateMachine, EvBgpNotification>(
     927         144 :                 &StateMachine::OnIdle<EvBgpNotification, 0>, event);
     928             :         }
     929             :     }
     930             : };
     931             : 
     932             : //
     933             : // In OpenConfirm, we wait for the other end to send a KEEPALIVE.
     934             : //
     935             : struct OpenConfirm : sc::state<OpenConfirm, StateMachine> {
     936             :     typedef mpl::list<
     937             :         IdleCease<EvStop>::reaction,
     938             :         IdleFsmError<EvOpenTimerExpired>::reaction,
     939             :         TransitToIdle<EvHoldTimerExpired,
     940             :             BgpProto::Notification::HoldTimerExp>::reaction,
     941             :         sc::custom_reaction<EvTcpPassiveOpen>,
     942             :         TransitToIdle<EvTcpClose>::reaction,
     943             :         IdleFsmError<EvBgpOpen>::reaction,
     944             :         sc::custom_reaction<EvBgpNotification>,
     945             :         sc::custom_reaction<EvBgpKeepalive>,
     946             :         IdleFsmError<EvBgpUpdate>::reaction,
     947             :         IdleError<EvBgpHeaderError,
     948             :             BgpProto::Notification::MsgHdrErr>::reaction,
     949             :         IdleError<EvBgpOpenError,
     950             :             BgpProto::Notification::OpenMsgErr>::reaction,
     951             :         IdleError<EvBgpUpdateError,
     952             :             BgpProto::Notification::UpdateMsgErr>::reaction
     953             :     > reactions;
     954             : 
     955             :     // Send a KEEPALIVE and start the keepalive timer on the peer. Also start
     956             :     // the hold timer based on the negotiated hold time value.
     957         995 :     explicit OpenConfirm(my_context ctx) : my_base(ctx) {
     958         995 :         StateMachine *state_machine = &context<StateMachine>();
     959        1002 :         BgpPeer *peer = state_machine->peer();
     960        1002 :         peer->SendKeepalive(false);
     961        1004 :         peer->StartKeepaliveTimer();
     962        1003 :         state_machine->CancelOpenTimer();
     963        1004 :         state_machine->StartHoldTimer();
     964        1004 :         state_machine->set_state(StateMachine::OPENCONFIRM);
     965        1002 :     }
     966             : 
     967             :     // Cancel the hold timer.  If we go to Established, the timer will get
     968             :     // started again from the constructor for that state.
     969        1990 :     ~OpenConfirm() {
     970         996 :         StateMachine *state_machine = &context<StateMachine>();
     971         997 :         state_machine->CancelHoldTimer();
     972        1996 :     }
     973             : 
     974             :     // Send a notification, delete the new session and stay in OpenConfirm.
     975           0 :     sc::result react(const EvTcpPassiveOpen &event) {
     976           0 :         StateMachine *state_machine = &context<StateMachine>();
     977           0 :         BgpPeer *peer = state_machine->peer();
     978           0 :         BgpSession *session = event.session;
     979           0 :         peer->SendNotification(session,
     980             :             BgpProto::Notification::Cease,
     981             :             BgpProto::Notification::ConnectionRejected,
     982             :             "Connection rejected - unexpected passive session");
     983           0 :         state_machine->DeleteSession(session);
     984           0 :         return discard_event();
     985             :     }
     986             : 
     987             :     // Ignore the notification if it's for a stale session, else go to Idle.
     988           0 :     sc::result react(const EvBgpNotification &event) {
     989           0 :         StateMachine *state_machine = &context<StateMachine>();
     990           0 :         if (!state_machine->ProcessNotificationEvent(event.session))
     991           0 :             return discard_event();
     992             : 
     993             :         return transit<Idle, StateMachine, EvBgpNotification>(
     994           0 :             &StateMachine::OnIdle<EvBgpNotification, 0>, event);
     995             :     }
     996             : 
     997             :     // Go to Established.  The hold timer will be started in the constructor
     998             :     // for that state.
     999        1000 :     sc::result react(const EvBgpKeepalive &event) {
    1000             :         // If GR timers started running just at the same time when the peer
    1001             :         // came back up, then gracefully close the session.
    1002        1000 :         StateMachine *state_machine = &context<StateMachine>();
    1003        1002 :         if (state_machine->IsPeerCloseInProgress()) {
    1004             :             return transit<Idle, StateMachine, EvBgpKeepalive>(
    1005           0 :                 &StateMachine::OnIdle<EvBgpKeepalive, 0>, event);
    1006             :         }
    1007        1000 :         return transit<Established>();
    1008             :     }
    1009             : };
    1010             : 
    1011             : //
    1012             : // Established is the final state for an operation peer.
    1013             : //
    1014             : struct Established : sc::state<Established, StateMachine> {
    1015             :     typedef mpl::list<
    1016             :         IdleCease<EvStop>::reaction,
    1017             :         IdleFsmError<EvOpenTimerExpired>::reaction,
    1018             :         TransitToIdle<EvHoldTimerExpired,
    1019             :             BgpProto::Notification::HoldTimerExp>::reaction,
    1020             :         sc::custom_reaction<EvTcpPassiveOpen>,
    1021             :         TransitToIdle<EvTcpClose>::reaction,
    1022             :         IdleFsmError<EvBgpOpen>::reaction,
    1023             :         TransitToIdle<EvBgpNotification>::reaction,
    1024             :         sc::custom_reaction<EvBgpKeepalive>,
    1025             :         sc::custom_reaction<EvBgpUpdate>,
    1026             :         IdleError<EvBgpHeaderError,
    1027             :             BgpProto::Notification::MsgHdrErr>::reaction,
    1028             :         IdleFsmError<EvBgpOpenError>::reaction,
    1029             :         IdleError<EvBgpUpdateError,
    1030             :             BgpProto::Notification::UpdateMsgErr>::reaction
    1031             :     > reactions;
    1032             : 
    1033         992 :     explicit Established(my_context ctx) : my_base(ctx) {
    1034         992 :         StateMachine *state_machine = &context<StateMachine>();
    1035         998 :         BgpPeer *peer = state_machine->peer();
    1036         996 :         state_machine->connect_attempts_clear();
    1037         997 :         state_machine->StartHoldTimer();
    1038        1001 :         state_machine->set_state(StateMachine::ESTABLISHED);
    1039         999 :         peer->NotifyEstablished(true);
    1040        1004 :         peer->RegisterAllTables();
    1041        1001 :     }
    1042             : 
    1043        1951 :     ~Established() {
    1044         976 :         StateMachine *state_machine = &context<StateMachine>();
    1045         985 :         BgpPeer *peer = state_machine->peer();
    1046         983 :         peer->NotifyEstablished(false);
    1047         998 :         state_machine->CancelHoldTimer();
    1048        1972 :     }
    1049             : 
    1050             :     // A new TCP session request should cause the previous BGP session to be
    1051             :     // closed in case GR Helper mode is active or if peer router type is BGPaaS.
    1052           3 :     sc::result react(const EvTcpPassiveOpen &event) {
    1053           3 :         StateMachine *state_machine = &context<StateMachine>();
    1054           3 :         BgpSession *session = event.session;
    1055           3 :         state_machine->DeleteSession(session);
    1056           6 :         if (state_machine->IsCloseGraceful() ||
    1057           3 :                 state_machine->IsRouterTypeBGPaaS()) {
    1058           0 :             state_machine->Shutdown(BgpProto::Notification::Unknown);
    1059             :         }
    1060           3 :         return discard_event();
    1061             :     }
    1062             : 
    1063             :     // Restart the hold timer.
    1064         431 :     sc::result react(const EvBgpKeepalive &event) {
    1065         431 :         StateMachine *state_machine = &context<StateMachine>();
    1066         431 :         state_machine->StartHoldTimer();
    1067         432 :         return discard_event();
    1068             :     }
    1069             : 
    1070             :     // Restart the hold timer and process the update.
    1071        1090 :     sc::result react(const EvBgpUpdate &event) {
    1072        1090 :         StateMachine *state_machine = &context<StateMachine>();
    1073        1092 :         state_machine->StartHoldTimer();
    1074        1092 :         state_machine->peer()->ProcessUpdate(event.msg.get(), event.msgsize);
    1075        1095 :         return discard_event();
    1076             :     }
    1077             : };
    1078             : 
    1079             : }  // namespace fsm
    1080             : 
    1081         620 : StateMachine::StateMachine(BgpPeer *peer)
    1082         620 :     : work_queue_(TaskScheduler::GetInstance()->GetTaskId("bgp::StateMachine"),
    1083             :         peer->GetTaskInstance(),
    1084             :         boost::bind(&StateMachine::DequeueEvent, this, _1)),
    1085         620 :       peer_(peer),
    1086         620 :       active_session_(NULL),
    1087         620 :       passive_session_(NULL),
    1088         620 :       connect_timer_(TimerManager::CreateTimer(*peer->server()->ioservice(),
    1089             :           "Connect timer",
    1090             :           TaskScheduler::GetInstance()->GetTaskId("bgp::StateMachine"),
    1091             :           peer->GetTaskInstance())),
    1092         620 :       open_timer_(TimerManager::CreateTimer(*peer->server()->ioservice(),
    1093             :           "Open timer",
    1094             :           TaskScheduler::GetInstance()->GetTaskId("bgp::StateMachine"),
    1095             :           peer->GetTaskInstance())),
    1096         620 :       hold_timer_(TimerManager::CreateTimer(*peer->server()->ioservice(),
    1097             :           "Hold timer",
    1098             :           TaskScheduler::GetInstance()->GetTaskId("bgp::StateMachine"),
    1099             :           peer->GetTaskInstance())),
    1100         620 :       idle_hold_timer_(TimerManager::CreateTimer(*peer->server()->ioservice(),
    1101             :           "Idle hold timer",
    1102             :           TaskScheduler::GetInstance()->GetTaskId("bgp::StateMachine"),
    1103             :           peer->GetTaskInstance())),
    1104         620 :       hold_time_(GetConfiguredHoldTime()),
    1105         620 :       idle_hold_time_(0),
    1106         620 :       attempts_(0),
    1107         620 :       deleted_(false),
    1108         620 :       state_(IDLE),
    1109        1240 :       last_state_(IDLE) {
    1110         620 :     seed_ = peer_->bgp_identifier();
    1111         620 :     initiate();
    1112         620 : }
    1113             : 
    1114         620 : void StateMachine::DeleteAllTimers() {
    1115         620 :     TimerManager::DeleteTimer(connect_timer_);
    1116         620 :     TimerManager::DeleteTimer(open_timer_);
    1117         620 :     TimerManager::DeleteTimer(hold_timer_);
    1118         620 :     TimerManager::DeleteTimer(idle_hold_timer_);
    1119         620 : }
    1120             : 
    1121             : //
    1122             : // Delete timers after state machine is terminated so that there is no
    1123             : // possible reference to the timers being deleted any more
    1124             : //
    1125         620 : StateMachine::~StateMachine() {
    1126         620 :     work_queue_.Shutdown();
    1127         620 :     terminate();
    1128         620 :     DeleteAllTimers();
    1129         620 : }
    1130             : 
    1131        1570 : void StateMachine::Initialize() {
    1132        1570 :     Enqueue(fsm::EvStart());
    1133        1570 : }
    1134             : 
    1135        1092 : bool StateMachine::IsPeerCloseInProgress() const {
    1136        1092 :     return peer_->IsCloseInProgress();
    1137             : }
    1138             : 
    1139         918 : void StateMachine::Shutdown(int subcode) {
    1140         918 :     if (peer_->IsDeleted()) {
    1141         620 :         work_queue_.SetExitCallback(
    1142             :             boost::bind(&StateMachine::DequeueEventDone, this, _1));
    1143             :     }
    1144         918 :     Enqueue(fsm::EvStop(subcode));
    1145         918 : }
    1146             : 
    1147         184 : void StateMachine::SetAdminState(bool down, int subcode) {
    1148         184 :     if (down) {
    1149          94 :         Enqueue(fsm::EvStop(subcode));
    1150             :     } else {
    1151             :         // Reset all previous state.
    1152          90 :         reset_idle_hold_time();
    1153          90 :         reset_last_info();
    1154          90 :         peer_->reset_flap_count();
    1155          90 :         if (!IsPeerCloseInProgress())
    1156          90 :             Enqueue(fsm::EvStart());
    1157             :     }
    1158         184 : }
    1159             : 
    1160        1234 : bool StateMachine::IsQueueEmpty() const {
    1161        1234 :     return work_queue_.IsQueueEmpty();
    1162             : }
    1163             : 
    1164        1675 : void StateMachine::UpdateFlapCount() {
    1165        1675 :     if (get_state() == StateMachine::ESTABLISHED) {
    1166        1003 :         peer_->increment_flap_count();
    1167         996 :         peer_->peer_stats()->Clear();
    1168             :     }
    1169        1666 : }
    1170             : 
    1171        1673 : void StateMachine::PeerClose(int code, int subcode) {
    1172        1673 :     UpdateFlapCount();
    1173        1665 :     peer_->Close(peer_->AttemptGRHelperMode(code, subcode));
    1174        2351 :     set_idle_hold_time(idle_hold_time() ? idle_hold_time() :
    1175         671 :                                           GetIdleHoldTimeMSecs());
    1176        1680 :     reset_hold_time();
    1177        1672 : }
    1178             : 
    1179             : template <typename Ev, int code>
    1180         144 : void StateMachine::OnIdle(const Ev &event) {
    1181         144 :     SendNotification(peer_->session(), code);
    1182         144 :     PeerClose(code, 0);
    1183         144 : }
    1184             : 
    1185             : template <typename Ev>
    1186         993 : void StateMachine::OnIdleCease(const Ev &event) {
    1187        1990 :     SendNotification(peer_->session(), BgpProto::Notification::Cease,
    1188         992 :                      event.subcode);
    1189        1003 :     PeerClose(BgpProto::Notification::Cease, event.subcode);
    1190        1007 : }
    1191             : 
    1192             : //
    1193             : // The template below must only be called for EvBgpHeaderError, EvBgpOpenError
    1194             : // or EvBgpUpdateError.
    1195             : //
    1196             : template <typename Ev, int code>
    1197          30 : void StateMachine::OnIdleError(const Ev &event) {
    1198          30 :     SendNotification(event.session, code, event.subcode, event.data);
    1199          30 :     PeerClose(code, event.subcode);
    1200          30 : }
    1201             : 
    1202             : // Close the peer. No need to send a notification as peer has already closed
    1203             : // this session by sending us a notification message.
    1204         495 : void StateMachine::OnIdleNotification(const fsm::EvBgpNotification &event) {
    1205         495 :     PeerClose(event.msg->error, event.msg->subcode);
    1206         491 :     set_last_notification_in(event.msg->error, event.msg->subcode,
    1207             :                              event.Name());
    1208         493 : }
    1209             : 
    1210        2235 : int StateMachine::GetConnectTime() const {
    1211        2235 :     int backoff = min(attempts_, 6);
    1212        2230 :     return std::min(backoff ? 1 << (backoff - 1) : 0, kConnectInterval);
    1213             : }
    1214             : 
    1215           0 : void StateMachine::StartConnectTimer(int seconds) {
    1216           0 :     connect_timer_->Cancel();
    1217             : 
    1218             :     // Add up to +/- kJitter percentage to reduce connection collisions.
    1219           0 :     int ms = seconds ? seconds * 1000 : 50;
    1220           0 :     ms = (ms * (100 - kJitter)) / 100;
    1221           0 :     ms += (ms * (rand_r(&seed_) % (kJitter * 2))) / 100;
    1222           0 :     connect_timer_->Start(ms,
    1223             :         boost::bind(&StateMachine::ConnectTimerExpired, this),
    1224             :         boost::bind(&StateMachine::TimerErrorHanlder, this, _1, _2));
    1225           0 : }
    1226             : 
    1227        2675 : void StateMachine::CancelConnectTimer() {
    1228        2675 :     connect_timer_->Cancel();
    1229        2684 : }
    1230             : 
    1231           0 : bool StateMachine::ConnectTimerRunning() {
    1232           0 :     return connect_timer_->running();
    1233             : }
    1234             : 
    1235           0 : void StateMachine::StartOpenTimer(int seconds) {
    1236           0 :     open_timer_->Cancel();
    1237           0 :     open_timer_->Start(seconds * 1000,
    1238             :         boost::bind(&StateMachine::OpenTimerExpired, this),
    1239             :         boost::bind(&StateMachine::TimerErrorHanlder, this, _1, _2));
    1240           0 : }
    1241             : 
    1242        3563 : void StateMachine::CancelOpenTimer() {
    1243        3563 :     open_timer_->Cancel();
    1244        3576 : }
    1245             : 
    1246         130 : bool StateMachine::OpenTimerRunning() {
    1247         130 :     return open_timer_->running();
    1248             : }
    1249             : 
    1250           0 : void StateMachine::StartIdleHoldTimer() {
    1251           0 :     if (idle_hold_time_ <= 0)
    1252           0 :         return;
    1253             : 
    1254           0 :     idle_hold_timer_->Cancel();
    1255           0 :     idle_hold_timer_->Start(idle_hold_time_,
    1256             :         boost::bind(&StateMachine::IdleHoldTimerExpired, this),
    1257             :         boost::bind(&StateMachine::TimerErrorHanlder, this, _1, _2));
    1258             : }
    1259             : 
    1260        4567 : void StateMachine::CancelIdleHoldTimer() {
    1261        4567 :     idle_hold_timer_->Cancel();
    1262        4589 : }
    1263             : 
    1264           0 : bool StateMachine::IdleHoldTimerRunning() {
    1265           0 :     return idle_hold_timer_->running();
    1266             : }
    1267             : 
    1268        4324 : void StateMachine::StartHoldTimer() {
    1269        4324 :     if (hold_time_ <= 0)
    1270           0 :         return;
    1271             : 
    1272        4324 :     hold_timer_->Cancel();
    1273        4337 :     hold_timer_->Start(hold_time_ * 1000,
    1274             :         boost::bind(&StateMachine::HoldTimerExpired, this),
    1275             :         boost::bind(&StateMachine::TimerErrorHanlder, this, _1, _2));
    1276             : }
    1277             : 
    1278        2796 : void StateMachine::CancelHoldTimer() {
    1279        2796 :     hold_timer_->Cancel();
    1280        2804 : }
    1281             : 
    1282           0 : bool StateMachine::HoldTimerRunning() {
    1283           0 :     return hold_timer_->running();
    1284             : }
    1285             : 
    1286             : // Test Only APIs : Start
    1287             : 
    1288           0 : void StateMachine::FireConnectTimer() {
    1289           0 :     connect_timer_->Fire();
    1290           0 : }
    1291             : 
    1292           0 : void StateMachine::FireOpenTimer() {
    1293           0 :     open_timer_->Fire();
    1294           0 : }
    1295             : 
    1296           0 : void StateMachine::FireHoldTimer() {
    1297           0 :     hold_timer_->Fire();
    1298           0 : }
    1299             : 
    1300           0 : void StateMachine::FireIdleHoldTimer() {
    1301           0 :     idle_hold_timer_->Fire();
    1302           0 : }
    1303             : 
    1304             : // Test Only APIs : END
    1305             : 
    1306             : //
    1307             : // Create an active session.
    1308             : //
    1309         922 : void StateMachine::StartSession() {
    1310         922 :     BgpSession *session = peer_->CreateSession();
    1311         922 :     if (!session)
    1312          72 :         return;
    1313         850 :     set_active_session(session);
    1314         850 :     session->set_observer(
    1315             :         boost::bind(&StateMachine::OnSessionEvent, this, _1, _2));
    1316        1700 :     peer_->server()->session_manager()->Connect(session,
    1317         850 :         peer_->peer_key().endpoint);
    1318             : }
    1319             : 
    1320             : //
    1321             : // Post a pseudo event to delete the underlying TcpSession.
    1322             : //
    1323             : // This ensures that any references to the TcpSession from pending events on
    1324             : // the state machine queue are still valid. Since we remove the TCP observer
    1325             : // before posting the delete event, we are guaranteed that we won't receive
    1326             : // any more events on the TcpSession.
    1327             : //
    1328        8945 : void StateMachine::DeleteSession(BgpSession *session) {
    1329        8945 :     if (!session)
    1330        7404 :         return;
    1331        1541 :     session->set_observer(NULL);
    1332        1549 :     session->Close();
    1333        1550 :     Enqueue(fsm::EvTcpDeleteSession(session));
    1334             : }
    1335             : 
    1336             : //
    1337             : // Transfer the ownership of the session from state machine to the peer.
    1338             : // This is called after we have resolved any connection collision issues
    1339             : // and decided that we want to reach ESTABLISHED state via the session.
    1340             : //
    1341        1003 : void StateMachine::AssignSession(bool active) {
    1342        1003 :     if (active) {
    1343         502 :         peer_->set_session(active_session_);
    1344         502 :         active_session_ = NULL;
    1345             :     } else {
    1346         501 :         peer_->set_session(passive_session_);
    1347         501 :         passive_session_ = NULL;
    1348             :     }
    1349        1003 : }
    1350             : 
    1351        3553 : void StateMachine::set_active_session(BgpSession *session) {
    1352        3553 :     DeleteSession(active_session_);
    1353        3547 :     active_session_ = session;
    1354        3547 : }
    1355             : 
    1356        3104 : void StateMachine::set_passive_session(BgpSession *session) {
    1357        3104 :     DeleteSession(passive_session_);
    1358        3105 :     passive_session_ = session;
    1359        3105 : }
    1360             : 
    1361        5591 : BgpSession *StateMachine::active_session() {
    1362        5591 :     return active_session_;
    1363             : }
    1364             : 
    1365        5399 : BgpSession *StateMachine::passive_session() {
    1366        5399 :     return passive_session_;
    1367             : }
    1368             : 
    1369        1169 : void StateMachine::SendNotification(BgpSession *session, int code, int subcode,
    1370             :                                     const std::string &data) {
    1371             :     // Prefer the passive session if available since it's operational.
    1372        1169 :     if (!session)
    1373         639 :         session = passive_session_;
    1374        1169 :     if (!session)
    1375         639 :         session = active_session_;
    1376        1169 :     if (session && code != 0)
    1377         530 :         peer_->SendNotification(session, code, subcode, data);
    1378        1177 : }
    1379             : 
    1380             : //
    1381             : // Process notification message.
    1382             : //
    1383             : // Typically we close the session. However, during connection collisions, we
    1384             : // could receive notifications on sessions that are not currently assigned to
    1385             : // the peer. In such cases, we discard the event and let the state machine
    1386             : // continue in the other session which is currently assigned to the peer.
    1387             : //
    1388         145 : bool StateMachine::ProcessNotificationEvent(BgpSession *session) {
    1389             :     // If this is a notification event that does not belong to the session,
    1390             :     // ignore. If either session is not present, continue normal processing
    1391             :     // of the notification.
    1392         145 :     if (session && peer_->session() && peer_->session() != session) {
    1393           0 :         return false;
    1394             :     }
    1395             : 
    1396             :     // TransitToIdle<EvBgpNotification>::reaction,
    1397         145 :     if (active_session() == session) {
    1398         145 :         set_active_session(NULL);
    1399             :     } else {
    1400           0 :         set_passive_session(NULL);
    1401             :     }
    1402             : 
    1403         145 :     return true;
    1404             : }
    1405             : 
    1406         998 : bool StateMachine::ConnectTimerExpired() {
    1407         998 :     Enqueue(fsm::EvConnectTimerExpired(connect_timer_));
    1408        1008 :     return false;
    1409             : }
    1410             : 
    1411           0 : bool StateMachine::OpenTimerExpired() {
    1412           0 :     Enqueue(fsm::EvOpenTimerExpired(open_timer_));
    1413           0 :     return false;
    1414             : }
    1415             : 
    1416           0 : bool StateMachine::HoldTimerExpired() {
    1417           0 :     boost::system::error_code error;
    1418             : 
    1419             :     // Reset hold timer if there is data already present in the socket.
    1420           0 :     if (peer() && peer()->session() && peer()->session()->socket() &&
    1421           0 :         peer()->session()->socket()->available(error) > 0) {
    1422           0 :         return true;
    1423             :     }
    1424           0 :     Enqueue(fsm::EvHoldTimerExpired(hold_timer_));
    1425           0 :     peer_->inc_hold_timer_expired();
    1426           0 :     return false;
    1427             : }
    1428             : 
    1429         578 : bool StateMachine::IdleHoldTimerExpired() {
    1430         578 :     Enqueue(fsm::EvIdleHoldTimerExpired(idle_hold_timer_));
    1431         584 :     return false;
    1432             : }
    1433             : 
    1434           3 : bool StateMachine::IsCloseGraceful() const {
    1435           3 :     return peer_->IsCloseGraceful();
    1436             : }
    1437             : 
    1438           3 : bool StateMachine::IsRouterTypeBGPaaS() const {
    1439           3 :     return peer_->IsRouterTypeBGPaaS();
    1440             : }
    1441             : 
    1442             : //
    1443             : // Concurrency: ASIO thread.
    1444             : // Feed TCP session events into the state machine.
    1445             : //
    1446        1252 : void StateMachine::OnSessionEvent(
    1447             :         TcpSession *session, TcpSession::Event event) {
    1448        1252 :     BgpSession *bgp_session = static_cast<BgpSession *>(session);
    1449        1252 :     switch (event) {
    1450         807 :     case TcpSession::CONNECT_COMPLETE:
    1451         807 :         Enqueue(fsm::EvTcpConnected(bgp_session));
    1452         807 :         break;
    1453          36 :     case TcpSession::CONNECT_FAILED:
    1454          36 :         Enqueue(fsm::EvTcpConnectFail(bgp_session));
    1455          36 :         peer_->inc_connect_error();
    1456          36 :         break;
    1457         409 :     case TcpSession::CLOSE:
    1458         409 :         Enqueue(fsm::EvTcpClose(bgp_session));
    1459         409 :         break;
    1460           0 :     default:
    1461           0 :         break;
    1462             :     }
    1463        1252 : }
    1464             : 
    1465             : //
    1466             : // Receive TCP Passive Open.
    1467             : // Set the observer and start async read on the session. Note that we disable
    1468             : // read on connect when we accept passive sessions.
    1469             : //
    1470         700 : bool StateMachine::PassiveOpen(BgpSession *session) {
    1471         700 :     CHECK_CONCURRENCY("bgp::Config");
    1472         700 :     Enqueue(fsm::EvTcpPassiveOpen(session));
    1473         700 :     session->set_observer(boost::bind(&StateMachine::OnSessionEvent,
    1474             :         this, _1, _2));
    1475         700 :     session->AsyncReadStart();
    1476         700 :     return true;
    1477             : }
    1478             : 
    1479         648 : void StateMachine::OnNotificationMessage(BgpSession *session,
    1480             :                                          BgpProto::BgpMessage *msg) {
    1481         648 :     BgpPeer *peer = session->peer();
    1482         647 :     if (peer)
    1483         648 :         peer->inc_rx_notification();
    1484         647 :     Enqueue(fsm::EvBgpNotification(session,
    1485             :             static_cast<BgpProto::Notification *>(msg)));
    1486         658 : }
    1487             : 
    1488             : //
    1489             : // Handle incoming message on the session.
    1490             : //
    1491        4345 : void StateMachine::OnMessage(BgpSession *session, BgpProto::BgpMessage *msg,
    1492             :     size_t msgsize) {
    1493        4345 :     switch (msg->type) {
    1494        1177 :     case BgpProto::OPEN: {
    1495        1177 :         BgpProto::OpenMessage *open_msg =
    1496             :             static_cast<BgpProto::OpenMessage *>(msg);
    1497        1177 :         BgpPeer *peer = session->peer();
    1498        1177 :         peer->inc_rx_open();
    1499        1176 :         if (int subcode = open_msg->Validate(peer)) {
    1500          29 :             Enqueue(fsm::EvBgpOpenError(session, subcode));
    1501          30 :             peer->inc_open_error();
    1502             :         } else {
    1503        1139 :             Enqueue(fsm::EvBgpOpen(session, open_msg));
    1504        1155 :             msg = NULL;
    1505             :         }
    1506        1185 :         break;
    1507             :     }
    1508        1435 :     case BgpProto::KEEPALIVE: {
    1509        1435 :         BgpPeer *peer = session->peer();
    1510        1434 :         Enqueue(fsm::EvBgpKeepalive(session));
    1511        1435 :         if (peer) peer->inc_rx_keepalive();
    1512        1435 :         break;
    1513             :     }
    1514         648 :     case BgpProto::NOTIFICATION: {
    1515         648 :         OnNotificationMessage(session, msg);
    1516         659 :         msg = NULL;
    1517         659 :         break;
    1518             :     }
    1519        1090 :     case BgpProto::UPDATE: {
    1520        1090 :         BgpProto::Update *update = static_cast<BgpProto::Update *>(msg);
    1521        1090 :         BgpPeer *peer = NULL;
    1522        1090 :         if (session)
    1523        1090 :             peer = session->peer();
    1524        1091 :         if (peer)
    1525        1091 :             peer->inc_rx_update();
    1526             : 
    1527        1091 :         std::string data;
    1528             :         int subcode;
    1529        1092 :         if (peer && (subcode = update->Validate(peer, &data))) {
    1530           0 :             Enqueue(fsm::EvBgpUpdateError(session, subcode, data));
    1531           0 :             peer->inc_update_error();
    1532             :         } else {
    1533        1091 :             Enqueue(fsm::EvBgpUpdate(session, update, msgsize));
    1534        1096 :             msg = NULL;
    1535             :         }
    1536        1096 :         break;
    1537        1096 :     }
    1538           0 :     default:
    1539           0 :         SM_LOG_NOTICE("Unknown message type " << msg->type);
    1540           0 :         break;
    1541             :     }
    1542             : 
    1543        4375 :     delete msg;
    1544        4375 : }
    1545             : 
    1546             : //
    1547             : // Handle errors in incoming message on the session.
    1548             : //
    1549           0 : void StateMachine::OnMessageError(BgpSession *session,
    1550             :         const ParseErrorContext *context) {
    1551           0 :     switch (context->error_code) {
    1552           0 :     case BgpProto::Notification::MsgHdrErr: {
    1553           0 :         Enqueue(fsm::EvBgpHeaderError(session, context->error_subcode,
    1554           0 :                 context->data, context->data_size));
    1555           0 :         break;
    1556             :     }
    1557           0 :     case BgpProto::Notification::OpenMsgErr:
    1558           0 :         Enqueue(fsm::EvBgpOpenError(session, context->error_subcode,
    1559           0 :                 context->data, context->data_size));
    1560           0 :         break;
    1561           0 :     case BgpProto::Notification::UpdateMsgErr:
    1562           0 :         Enqueue(fsm::EvBgpUpdateError(session, context->error_subcode,
    1563           0 :                 std::string((const char *)context->data, context->data_size)));
    1564           0 :         break;
    1565           0 :     default:
    1566           0 :         break;
    1567             :     }
    1568           0 : }
    1569             : 
    1570             : static const std::string state_names[] = {
    1571             :     "Idle",
    1572             :     "Active",
    1573             :     "Connect",
    1574             :     "OpenSent",
    1575             :     "OpenConfirm",
    1576             :     "Established"
    1577             : };
    1578             : 
    1579       30680 : const string &StateMachine::StateName() const {
    1580       30680 :     return state_names[state_];
    1581             : }
    1582             : 
    1583        6446 : const string &StateMachine::LastStateName() const {
    1584        6446 :     return state_names[last_state_];
    1585             : }
    1586             : 
    1587           0 : const std::string StateMachine::last_state_change_at() const {
    1588           0 :     return integerToString(UTCUsecToPTime(last_state_change_at_));
    1589             : }
    1590             : 
    1591           0 : const uint64_t StateMachine::last_state_change_usecs_at() const {
    1592           0 :     return last_state_change_at_;
    1593             : }
    1594             : 
    1595           0 : ostream &operator<<(ostream &out, const StateMachine::State &state) {
    1596           0 :     out << state_names[state];
    1597           0 :     return out;
    1598             : }
    1599             : 
    1600             : // This class determines whether a given class has a method called 'validate'.
    1601             : template <typename Ev>
    1602             : struct HasValidate {
    1603             :     template <typename T, bool (T::*)(StateMachine *) const> struct SFINAE {};
    1604             :     template <typename T> static char Test(SFINAE<T, &T::validate>*);
    1605             :     template <typename T> static int Test(...);
    1606             :     static const bool Has = sizeof(Test<Ev>(0)) == sizeof(char);
    1607             : };
    1608             : 
    1609             : template <typename Ev, bool has_validate>
    1610             : struct ValidateFn {
    1611        6038 :     EvValidate operator()(const Ev *event) {
    1612        6038 :         return NULL;
    1613             :     }
    1614             : };
    1615             : 
    1616             : template <typename Ev>
    1617             : struct ValidateFn<Ev, true> {
    1618        5917 :     EvValidate operator()(const Ev *event) {
    1619        5917 :         return boost::bind(&Ev::validate, event, _1);
    1620             :     }
    1621             : };
    1622             : 
    1623             : template <typename Ev>
    1624       12043 : bool StateMachine::Enqueue(const Ev &event) {
    1625       12043 :     LogEvent(TYPE_NAME(event), "Enqueue");
    1626       12117 :     EventContainer ec;
    1627       12097 :     ec.event = event.intrusive_from_this();
    1628       11972 :     ec.validate = ValidateFn<Ev, HasValidate<Ev>::Has>()(
    1629       11980 :         static_cast<const Ev *>(ec.event.get()));
    1630       11864 :     work_queue_.Enqueue(ec);
    1631             : 
    1632       12142 :     return true;
    1633       12141 : }
    1634             : 
    1635       24179 : void StateMachine::LogEvent(string event_name, string msg,
    1636             :                             SandeshLevel::type log_level) {
    1637             :     // Reduce log level for keepalive and update messages.
    1638       36055 :     if (get_state() == ESTABLISHED &&
    1639       11878 :         (event_name == "fsm::EvBgpKeepalive" ||
    1640        5508 :          event_name == "fsm::EvBgpUpdate")) {
    1641        3051 :         log_level = Sandesh::LoggingUtLevel();
    1642             :     }
    1643       24168 :     SM_LOG(log_level, msg << " " << event_name << " in state " << StateName());
    1644       24221 : }
    1645             : 
    1646       12020 : bool StateMachine::DequeueEvent(StateMachine::EventContainer ec) {
    1647             :     const fsm::EvTcpDeleteSession *deferred_delete =
    1648       12020 :         dynamic_cast<const fsm::EvTcpDeleteSession *>(ec.event.get());
    1649       12011 :     if (deferred_delete != NULL) {
    1650        1545 :         LogEvent(TYPE_NAME(*ec.event), "Dequeue");
    1651        1548 :         peer_->server()->session_manager()->DeleteSession(
    1652        1548 :             deferred_delete->session);
    1653        1550 :         return true;
    1654             :     }
    1655             : 
    1656       10466 :     set_last_event(TYPE_NAME(*ec.event));
    1657       10485 :     if (ec.validate.empty() || ec.validate(this)) {
    1658       10063 :         LogEvent(TYPE_NAME(*ec.event), "Dequeue");
    1659       10146 :         process_event(*ec.event);
    1660             :     } else {
    1661         425 :         LogEvent(TYPE_NAME(*ec.event), "Discard", SandeshLevel::SYS_INFO);
    1662             :     }
    1663       10519 :     ec.event.reset();
    1664             : 
    1665       10585 :     return true;
    1666             : }
    1667             : 
    1668         611 : void StateMachine::DequeueEventDone(bool done) {
    1669         611 :     peer_->RetryDelete();
    1670         618 : }
    1671             : 
    1672           0 : void StateMachine::SetDataCollectionKey(BgpPeerInfo *peer_info) const {
    1673           0 :     peer_->SetDataCollectionKey(peer_info);
    1674           0 : }
    1675             : 
    1676           0 : const std::string StateMachine::last_notification_in_error() const {
    1677             :     return (BgpProto::Notification::toString(
    1678           0 :         static_cast<BgpProto::Notification::Code>(last_notification_in_.first),
    1679           0 :         last_notification_in_.second));
    1680             : }
    1681             : 
    1682           0 : const std::string StateMachine::last_notification_out_error() const {
    1683             :     return (BgpProto::Notification::toString(
    1684           0 :         static_cast<BgpProto::Notification::Code>(last_notification_out_.first),
    1685           0 :         last_notification_out_.second));
    1686             : }
    1687             : 
    1688             : //
    1689             : // Return the configured hold time in seconds.
    1690             : //
    1691        4607 : int StateMachine::GetConfiguredHoldTime() const {
    1692             :     static std::atomic<bool> env_checked = std::atomic<bool>();
    1693             :     static std::atomic<int> env_hold_time = std::atomic<int>();
    1694             : 
    1695             :     // For testing only - configure through environment variable.
    1696        4607 :     if (!env_checked) {
    1697           1 :         char *keepalive_time_str = getenv("BGP_KEEPALIVE_SECONDS");
    1698           1 :         if (keepalive_time_str) {
    1699           0 :             env_hold_time = strtoul(keepalive_time_str, NULL, 0) * 3;
    1700           0 :             env_checked = true;
    1701           0 :             return env_hold_time;
    1702             :         } else {
    1703           1 :             env_checked = true;
    1704             :         }
    1705        4604 :     } else if (env_hold_time) {
    1706           0 :         return env_hold_time;
    1707             :     }
    1708             : 
    1709             :     // Use the configured hold-time from peer if available.
    1710        4602 :     if (peer_ && peer_->hold_time())
    1711        4602 :         return peer_->hold_time();
    1712             : 
    1713             :     // Use the configured hold-time from server if available.
    1714           0 :     if (peer_ && peer_->server()->hold_time())
    1715           0 :         return peer_->server()->hold_time();
    1716             : 
    1717             :     // Use hard coded default.
    1718           0 :     return kHoldTime;
    1719             : }
    1720             : 
    1721       20186 : void StateMachine::BGPPeerInfoSend(const BgpPeerInfoData &peer_info) {
    1722       20186 :     assert(!peer_info.get_name().empty());
    1723       20174 :     BGP_UVE_SEND(BGPPeerInfo, peer_info);
    1724       20171 : }
    1725             : 
    1726       10513 : void StateMachine::set_last_event(const std::string &event) {
    1727       10513 :     last_event_ = event;
    1728       10509 :     last_event_at_ = UTCTimestampUsec();
    1729             : 
    1730             :     // Skip keepalive and update events after we've reached established state.
    1731       15219 :     if (state_ == ESTABLISHED &&
    1732        4658 :         (event == "fsm::EvBgpKeepalive" || event == "fsm::EvBgpUpdate")) {
    1733        1526 :         return;
    1734             :     }
    1735             : 
    1736       18001 :     BgpPeerInfoData peer_info;
    1737        8968 :     peer_info.set_name(peer()->ToUVEKey());
    1738        8960 :     PeerEventInfo event_info;
    1739        8919 :     event_info.set_last_event(last_event_);
    1740        8928 :     event_info.set_last_event_at(last_event_at_);
    1741        8925 :     peer_info.set_event_info(event_info);
    1742        8981 :     BGPPeerInfoSend(peer_info);
    1743        8956 : }
    1744             : 
    1745         810 : void StateMachine::set_last_notification_out(int code, int subcode,
    1746             :     const string &reason) {
    1747         810 :     last_notification_out_ = std::make_pair(code, subcode);
    1748         810 :     last_notification_out_at_ = UTCTimestampUsec();
    1749         814 :     last_notification_out_error_ = reason;
    1750             : 
    1751        1622 :     BgpPeerInfoData peer_info;
    1752         808 :     peer_info.set_name(peer()->ToUVEKey());
    1753         808 :     peer_info.set_notification_out_at(last_notification_out_at_);
    1754         807 :     peer_info.set_notification_out(BgpProto::Notification::toString(
    1755             :         static_cast<BgpProto::Notification::Code>(code), subcode));
    1756         814 :     BGPPeerInfoSend(peer_info);
    1757         813 : }
    1758             : 
    1759         491 : void StateMachine::set_last_notification_in(int code, int subcode,
    1760             :     const string &reason) {
    1761         491 :     last_notification_in_ = std::make_pair(code, subcode);
    1762         493 :     last_notification_in_at_ = UTCTimestampUsec();
    1763         496 :     last_notification_in_error_ = reason;
    1764             : 
    1765         986 :     BgpPeerInfoData peer_info;
    1766         492 :     peer_info.set_name(peer()->ToUVEKey());
    1767         493 :     peer_info.set_notification_in_at(last_notification_in_at_);
    1768         492 :     peer_info.set_notification_in(BgpProto::Notification::toString(
    1769             :         static_cast<BgpProto::Notification::Code>(code), subcode));
    1770         496 :     BGPPeerInfoSend(peer_info);
    1771         496 : }
    1772             : 
    1773        7409 : void StateMachine::set_state(State state) {
    1774        7409 :     if (state == state_)
    1775        1043 :         return;
    1776        6366 :     last_state_ = state_; state_ = state;
    1777        6366 :     last_state_change_at_ = UTCTimestampUsec();
    1778             : 
    1779       12735 :     BgpPeerInfoData peer_info;
    1780        6356 :     peer_info.set_name(peer()->ToUVEKey());
    1781        6358 :     PeerStateInfo state_info;
    1782        6350 :     state_info.set_state(StateName());
    1783        6356 :     state_info.set_last_state(LastStateName());
    1784        6358 :     state_info.set_last_state_at(last_state_change_at_);
    1785        6358 :     peer_info.set_state_info(state_info);
    1786        6363 :     BGPPeerInfoSend(peer_info);
    1787        6356 : }
    1788             : 
    1789        1803 : void StateMachine::set_hold_time(int hold_time) {
    1790        1803 :     hold_time_ = hold_time;
    1791             : 
    1792        3602 :     BgpPeerInfoData peer_info;
    1793        1800 :     peer_info.set_name(peer()->ToUVEKey());
    1794        1801 :     peer_info.set_hold_time(hold_time_);
    1795        1802 :     BGPPeerInfoSend(peer_info);
    1796        1801 : }
    1797             : 
    1798        1680 : void StateMachine::reset_hold_time() {
    1799        1680 :     hold_time_ = GetConfiguredHoldTime();
    1800             : 
    1801        3349 :     BgpPeerInfoData peer_info;
    1802        1672 :     peer_info.set_name(peer()->ToUVEKey());
    1803        1676 :     peer_info.set_hold_time(hold_time_);
    1804        1675 :     BGPPeerInfoSend(peer_info);
    1805        1675 : }
    1806             : 
    1807          90 : void StateMachine::reset_last_info() {
    1808          90 :     last_notification_in_ = std::make_pair(0, 0);
    1809          90 :     last_notification_in_at_ = 0;
    1810          90 :     last_notification_in_error_ = std::string();
    1811          90 :     last_notification_out_ = std::make_pair(0, 0);
    1812          90 :     last_notification_out_at_ = 0;
    1813          90 :     last_notification_out_error_ = std::string();
    1814          90 :     last_state_ = IDLE;
    1815          90 :     last_event_ = "";
    1816          90 :     last_state_change_at_ = 0;
    1817          90 :     last_event_at_ = 0;
    1818             : 
    1819         180 :     BgpPeerInfoData peer_info;
    1820          90 :     peer_info.set_name(peer()->ToUVEKey());
    1821          90 :     PeerStateInfo state_info;
    1822          90 :     state_info.set_state(StateName());
    1823          90 :     state_info.set_last_state(LastStateName());
    1824          90 :     state_info.set_last_state_at(last_state_change_at_);
    1825          90 :     peer_info.set_state_info(state_info);
    1826             : 
    1827          90 :     PeerEventInfo event_info;
    1828          90 :     event_info.set_last_event(last_event_);
    1829          90 :     event_info.set_last_event_at(last_event_at_);
    1830          90 :     peer_info.set_event_info(event_info);
    1831          90 :     BGPPeerInfoSend(peer_info);
    1832          90 : }

Generated by: LCOV version 1.14