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: 795 842 94.4 %
Date: 2026-06-22 02:21:21 Functions: 184 191 96.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       15497 :     EvStart() {
      76       15497 :     }
      77             :     static const char *Name() {
      78             :         return "EvStart";
      79             :     }
      80             : };
      81             : 
      82             : struct EvStop : sc::event<EvStop> {
      83       11149 :     explicit EvStop(int subcode) : subcode(subcode) {
      84       11149 :     }
      85             :     static const char *Name() {
      86             :         return "EvStop";
      87             :     }
      88             :     int subcode;
      89             : };
      90             : 
      91             : struct EvIdleHoldTimerExpired : sc::event<EvIdleHoldTimerExpired> {
      92        4522 :     explicit EvIdleHoldTimerExpired(Timer *timer)  : timer_(timer) {
      93        4521 :     }
      94             :     static const char *Name() {
      95             :         return "EvIdleHoldTimerExpired";
      96             :     }
      97        4522 :     bool validate(StateMachine *state_machine) const {
      98        4522 :         return !timer_->cancelled();
      99             :     }
     100             : 
     101             :     Timer *timer_;
     102             : };
     103             : 
     104             : struct EvConnectTimerExpired : sc::event<EvConnectTimerExpired> {
     105        7459 :     explicit EvConnectTimerExpired(Timer *timer) : timer_(timer) {
     106        7449 :     }
     107             :     static const char *Name() {
     108             :         return "EvConnectTimerExpired";
     109             :     }
     110        7458 :     bool validate(StateMachine *state_machine) const {
     111        7458 :         if (timer_->cancelled()) {
     112         175 :             return false;
     113        7286 :         } else if (state_machine->get_state() == StateMachine::ACTIVE) {
     114        6996 :             return (state_machine->passive_session() == NULL);
     115         290 :         } else if (state_machine->get_state() == StateMachine::CONNECT) {
     116         394 :             return (!state_machine->active_session() ||
     117         394 :                     !state_machine->active_session()->IsEstablished());
     118             :         }
     119           3 :         return false;
     120             :     }
     121             : 
     122             :     Timer *timer_;
     123             : };
     124             : 
     125             : struct EvOpenTimerExpired : sc::event<EvOpenTimerExpired> {
     126          36 :     explicit EvOpenTimerExpired(Timer *timer) : timer_(timer) {
     127          36 :     }
     128             :     static const char *Name() {
     129             :         return "EvOpenTimerExpired";
     130             :     }
     131          36 :     bool validate(StateMachine *state_machine) const {
     132          36 :         if (timer_->cancelled()) {
     133           0 :             return false;
     134             :         } else {
     135          36 :             return (state_machine->passive_session() != NULL);
     136             :         }
     137             :     }
     138             : 
     139             :     Timer *timer_;
     140             : };
     141             : 
     142             : struct EvHoldTimerExpired : sc::event<EvHoldTimerExpired> {
     143          74 :     explicit EvHoldTimerExpired(Timer *timer) : timer_(timer) {
     144          74 :     }
     145             :     static const char *Name() {
     146             :         return "EvHoldTimerExpired";
     147             :     }
     148          74 :     bool validate(StateMachine *state_machine) const {
     149          74 :         if (timer_->cancelled()) {
     150           2 :             return false;
     151          72 :         } else if (state_machine->get_state() == StateMachine::OPENSENT) {
     152           5 :             return true;
     153             :         } else {
     154          67 :             return (state_machine->peer()->session() != NULL);
     155             :         }
     156             :     }
     157             : 
     158             :     Timer *timer_;
     159             : };
     160             : 
     161             : struct EvTcpConnected : sc::event<EvTcpConnected> {
     162        6537 :     explicit EvTcpConnected(BgpSession *session) : session(session) {
     163        6537 :     }
     164             :     static const char *Name() {
     165             :         return "EvTcpConnected";
     166             :     }
     167        6537 :     bool validate(StateMachine *state_machine) const {
     168        6537 :         return (state_machine->active_session() == session);
     169             :     }
     170             : 
     171             :     BgpSession *session;
     172             : };
     173             : 
     174             : struct EvTcpConnectFail : sc::event<EvTcpConnectFail> {
     175         129 :     explicit EvTcpConnectFail(BgpSession *session) : session(session) {
     176         129 :     }
     177             :     static const char *Name() {
     178             :         return "EvTcpConnectFail";
     179             :     }
     180         129 :     bool validate(StateMachine *state_machine) const {
     181         129 :         return (state_machine->active_session() == session);
     182             :     }
     183             : 
     184             :     BgpSession *session;
     185             : };
     186             : 
     187             : struct EvTcpPassiveOpen : sc::event<EvTcpPassiveOpen> {
     188        4251 :     explicit EvTcpPassiveOpen(BgpSession *session) : session(session) {
     189        4251 :     }
     190             :     static const char *Name() {
     191             :         return "EvTcpPassiveOpen";
     192             :     }
     193             : 
     194             :     BgpSession *session;
     195             : };
     196             : 
     197             : struct EvTcpClose : sc::event<EvTcpClose> {
     198        3803 :     explicit EvTcpClose(BgpSession *session) : session(session) {
     199        3803 :     }
     200             :     static const char *Name() {
     201             :         return "EvTcpClose";
     202             :     }
     203        3802 :     bool validate(StateMachine *state_machine) const {
     204        3802 :         return ((state_machine->peer()->session() == session) ||
     205        6956 :                 (state_machine->active_session() == session) ||
     206        6956 :                 (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       11054 :     explicit EvTcpDeleteSession(BgpSession *session) : session(session) {
     215       11054 :     }
     216             :     static const char *Name() {
     217             :         return "EvTcpDeleteSession";
     218             :     }
     219             : 
     220             :     BgpSession *session;
     221             : };
     222             : 
     223             : struct EvBgpHeaderError : sc::event<EvBgpHeaderError> {
     224           5 :     EvBgpHeaderError(BgpSession *session, int subcode, const uint8_t *_data,
     225             :         size_t data_size)
     226           5 :         : session(session), subcode(subcode) {
     227           5 :         if (_data)
     228           0 :             data = std::string((const char *)_data, data_size);
     229           5 :     }
     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        6138 :     EvBgpOpen(BgpSession *session, const BgpProto::OpenMessage *msg)
     241        6138 :         : session(session), msg(msg) {
     242        6138 :         BGP_LOG_PEER(Message, session->peer(), SandeshLevel::SYS_INFO,
     243             :             BGP_LOG_FLAG_SYSLOG, BGP_PEER_DIR_IN,
     244             :             "Open " << msg->ToString());
     245        6138 :     }
     246             :     static const char *Name() {
     247             :         return "EvBgpOpen";
     248             :     }
     249        6141 :     bool validate(StateMachine *state_machine) const {
     250        6141 :         return ((state_machine->peer()->session() == session) ||
     251        9572 :                 (state_machine->active_session() == session) ||
     252        9572 :                 (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         112 :     EvBgpOpenError(BgpSession *session, int subcode,
     261             :         const uint8_t *_data = NULL, size_t data_size = 0)
     262         112 :         : session(session), subcode(subcode) {
     263         112 :         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         112 :         } else if (_data) {
     269           0 :             data = std::string((const char *)_data, data_size);
     270             :         }
     271         112 :     }
     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        6824 :     explicit EvBgpKeepalive(BgpSession *session) : session(session) {
     283        6824 :         const StateMachine *state_machine = session->peer()->state_machine();
     284             :         SandeshLevel::type log_level;
     285        6824 :         if (state_machine->get_state() == StateMachine::ESTABLISHED) {
     286        1491 :             log_level = Sandesh::LoggingUtLevel();
     287             :         } else {
     288        5333 :             log_level = SandeshLevel::SYS_INFO;
     289             :         }
     290        6824 :         BGP_LOG_PEER(Message, session->peer(), log_level,
     291             :                      BGP_LOG_FLAG_SYSLOG, BGP_PEER_DIR_IN, "Keepalive");
     292        6824 :     }
     293             :     static const char *Name() {
     294             :         return "EvBgpKeepalive";
     295             :     }
     296        6822 :     bool validate(StateMachine *state_machine) const {
     297        6822 :         return !session->IsClosed();
     298             :     }
     299             : 
     300             :     BgpSession *session;
     301             : };
     302             : 
     303             : struct EvBgpNotification : sc::event<EvBgpNotification> {
     304        4708 :     EvBgpNotification(BgpSession *session, const BgpProto::Notification *msg)
     305        4708 :         : session(session), msg(msg) {
     306             :         string peer_key =
     307        4709 :             session->peer() ? session->peer()->ToUVEKey() : session->ToString();
     308        4709 :         session->LogNotification(msg->error, msg->subcode, BGP_PEER_DIR_IN,
     309             :                                  peer_key, *msg);
     310        4709 :     }
     311        1989 :     static const char *Name() {
     312        1989 :         return "EvBgpNotification";
     313             :     }
     314        4709 :     bool validate(StateMachine *state_machine) const {
     315        4709 :         return ((state_machine->peer()->session() == session) ||
     316        5009 :                 (state_machine->active_session() == session) ||
     317        5009 :                 (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      158478 :     EvBgpUpdate(BgpSession *session, const BgpProto::Update *msg,
     326      158478 :         size_t msgsize) : session(session), msg(msg), msgsize(msgsize) {
     327      158478 :     }
     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           5 :     EvBgpUpdateError(BgpSession *session, int subcode, std::string data)
     339           5 :         : session(session), subcode(subcode), data(data) {
     340           5 :     }
     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       26116 :     explicit Idle(my_context ctx) : my_base(ctx) {
     407       26116 :         StateMachine *state_machine = &context<StateMachine>();
     408       26116 :         BgpPeer *peer = state_machine->peer();
     409       26116 :         BgpSession *session = peer->session();
     410       26116 :         peer->clear_session();
     411       26115 :         state_machine->set_active_session(NULL);
     412       26116 :         state_machine->set_passive_session(NULL);
     413       26116 :         state_machine->DeleteSession(session);
     414       26116 :         state_machine->CancelOpenTimer();
     415       26115 :         state_machine->CancelIdleHoldTimer();
     416       26115 :         state_machine->set_state(StateMachine::IDLE);
     417       26114 :     }
     418             : 
     419       52218 :     ~Idle() {
     420       26109 :         StateMachine *state_machine = &context<StateMachine>();
     421       26108 :         state_machine->CancelIdleHoldTimer();
     422       52225 :     }
     423             : 
     424             :     // Start idle hold timer if it's enabled, else go to Active right away.
     425       15371 :     sc::result react(const EvStart &event) {
     426       15371 :         StateMachine *state_machine = &context<StateMachine>();
     427       15372 :         if (state_machine->idle_hold_time()) {
     428        5602 :             state_machine->StartIdleHoldTimer();
     429             :         } else {
     430        9770 :             return transit<Active>();
     431             :         }
     432        5603 :         return discard_event();
     433             :     }
     434             : 
     435             :     // The idle hold timer expired, go to Active.
     436        4488 :     sc::result react(const EvIdleHoldTimerExpired &event) {
     437        4488 :         return transit<Active>();
     438             :     }
     439             : 
     440             :     // Delete the session and ignore event.
     441         496 :     sc::result react(const EvTcpPassiveOpen &event) {
     442         496 :         StateMachine *state_machine = &context<StateMachine>();
     443         496 :         BgpSession *session = event.session;
     444         496 :         state_machine->DeleteSession(session);
     445         496 :         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       15323 :     explicit Active(my_context ctx) : my_base(ctx) {
     477       15317 :         StateMachine *state_machine = &context<StateMachine>();
     478       15320 :         BgpPeer *peer = state_machine->peer();
     479       15319 :         if (!peer->IsPassive() && !state_machine->passive_session())
     480       14965 :             state_machine->StartConnectTimer(state_machine->GetConnectTime());
     481       15329 :         state_machine->set_state(StateMachine::ACTIVE);
     482       15330 :     }
     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       30660 :     ~Active() {
     487       15330 :         StateMachine *state_machine = &context<StateMachine>();
     488       15330 :         state_machine->CancelConnectTimer();
     489       30659 :     }
     490             : 
     491             :     // The connect timer expired, go to Connect if the peer is not passive.
     492        6996 :     sc::result react(const EvConnectTimerExpired &event) {
     493        6996 :         StateMachine *state_machine = &context<StateMachine>();
     494        6996 :         BgpPeer *peer = state_machine->peer();
     495        6996 :         if (peer->IsPassive()) {
     496           1 :             return discard_event();
     497             :         } else {
     498        6995 :             return transit<Connect>();
     499             :         }
     500             :     }
     501             : 
     502             :     // Send an OPEN message on the passive session and go to OpenSent.
     503          12 :     sc::result react(const EvOpenTimerExpired &event) {
     504          12 :         StateMachine *state_machine = &context<StateMachine>();
     505          12 :         BgpSession *session = state_machine->passive_session();
     506          12 :         if (session) {
     507          12 :             BgpPeer *peer = state_machine->peer();
     508          12 :             peer->SendOpen(session);
     509          12 :             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        2051 :     sc::result react(const EvTcpPassiveOpen &event) {
     519        2051 :         StateMachine *state_machine = &context<StateMachine>();
     520        2051 :         state_machine->set_passive_session(event.session);
     521        2051 :         state_machine->CancelConnectTimer();
     522        2051 :         state_machine->StartOpenTimer(StateMachine::kOpenTime);
     523        2051 :         return discard_event();
     524             :     }
     525             : 
     526             :     // Start the connect timer since we don't have a passive session anymore.
     527           2 :     sc::result react(const EvTcpClose &event) {
     528           2 :         StateMachine *state_machine = &context<StateMachine>();
     529           2 :         if (event.session == state_machine->passive_session()) {
     530           2 :             state_machine->set_passive_session(NULL);
     531           2 :             state_machine->CancelOpenTimer();
     532           2 :             state_machine->StartConnectTimer(state_machine->GetConnectTime());
     533             :         }
     534           2 :         return discard_event();
     535             :     }
     536             : 
     537             :     // We received an OPEN message on the passive session. Send OPEN message
     538             :     // and go to OpenConfirm.
     539        1958 :     sc::result react(const EvBgpOpen &event) {
     540        1958 :         StateMachine *state_machine = &context<StateMachine>();
     541        1958 :         BgpPeer *peer = state_machine->peer();
     542        1958 :         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        1958 :         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        1958 :         if (session != event.session)
     555           0 :             return discard_event();
     556             : 
     557             :         // Send OPEN and go to OpenConfirm.
     558        1958 :         int local_holdtime = state_machine->GetConfiguredHoldTime();
     559        1958 :         state_machine->set_hold_time(min(event.msg->holdtime, local_holdtime));
     560        1958 :         state_machine->AssignSession(false);
     561        1958 :         peer->SendOpen(session);
     562        1958 :         if (!peer->SetCapabilities(event.msg.get()))
     563           0 :             return discard_event();
     564        1958 :         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        6994 :     explicit Connect(my_context ctx) : my_base(ctx) {
     594        6994 :         StateMachine *state_machine = &context<StateMachine>();
     595        6994 :         state_machine->connect_attempts_inc();
     596        6994 :         state_machine->StartConnectTimer(state_machine->GetConnectTime());
     597        6995 :         state_machine->StartSession();
     598        6995 :         state_machine->set_state(StateMachine::CONNECT);
     599        6994 :     }
     600             : 
     601       13990 :     ~Connect() {
     602        6995 :         StateMachine *state_machine = &context<StateMachine>();
     603        6994 :         state_machine->CancelConnectTimer();
     604       13990 :     }
     605             : 
     606             :     // Get rid of the active session and go back to Active.
     607         287 :     sc::result react(const EvConnectTimerExpired &event) {
     608         287 :         StateMachine *state_machine = &context<StateMachine>();
     609         287 :         BgpPeer *peer = state_machine->peer();
     610         287 :         state_machine->set_active_session(NULL);
     611         287 :         peer->inc_connect_timer_expired();
     612         287 :         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           2 :     sc::result react(const EvOpenTimerExpired &event) {
     620           2 :         StateMachine *state_machine = &context<StateMachine>();
     621           2 :         BgpPeer *peer = state_machine->peer();
     622           2 :         peer->SendOpen(state_machine->passive_session());
     623           2 :         state_machine->set_active_session(NULL);
     624           2 :         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        6531 :     sc::result react(const EvTcpConnected &event) {
     631        6531 :         StateMachine *state_machine = &context<StateMachine>();
     632        6531 :         BgpPeer *peer = state_machine->peer();
     633        6531 :         BgpSession *session = state_machine->active_session();
     634        6531 :         peer->SendOpen(session);
     635        6529 :         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         126 :     sc::result react(const EvTcpConnectFail &event) {
     641         126 :         StateMachine *state_machine = &context<StateMachine>();
     642         126 :         state_machine->set_active_session(NULL);
     643         126 :         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         281 :     sc::result react(const EvTcpPassiveOpen &event) {
     650         281 :         StateMachine *state_machine = &context<StateMachine>();
     651         281 :         state_machine->set_passive_session(event.session);
     652         281 :         state_machine->StartOpenTimer(StateMachine::kOpenTime);
     653         281 :         return discard_event();
     654             :     }
     655             : 
     656             :     // Either the active or passive session got closed.
     657           3 :     sc::result react(const EvTcpClose &event) {
     658           3 :         StateMachine *state_machine = &context<StateMachine>();
     659           3 :         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           1 :             state_machine->set_passive_session(NULL);
     663           1 :             state_machine->CancelOpenTimer();
     664           1 :             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           2 :             assert(event.session == state_machine->active_session());
     669           2 :             state_machine->set_active_session(NULL);
     670           2 :             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          31 :     sc::result react(const EvBgpOpen &event) {
     677          31 :         StateMachine *state_machine = &context<StateMachine>();
     678          31 :         BgpPeer *peer = state_machine->peer();
     679          31 :         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          31 :         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          31 :         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          31 :         int local_holdtime = state_machine->GetConfiguredHoldTime();
     697          31 :         state_machine->set_hold_time(min(event.msg->holdtime, local_holdtime));
     698          31 :         state_machine->set_active_session(NULL);
     699          31 :         state_machine->AssignSession(false);
     700          31 :         peer->SendOpen(session);
     701          31 :         if (!peer->SetCapabilities(event.msg.get()))
     702           0 :             return discard_event();
     703          31 :         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        6542 :     explicit OpenSent(my_context ctx) : my_base(ctx) {
     737        6542 :         StateMachine *state_machine = &context<StateMachine>();
     738        6545 :         state_machine->set_hold_time(StateMachine::kOpenSentHoldTime);
     739        6544 :         state_machine->StartHoldTimer();
     740        6545 :         state_machine->set_state(StateMachine::OPENSENT);
     741        6545 :     }
     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       13090 :     ~OpenSent() {
     746        6545 :         StateMachine *state_machine = &context<StateMachine>();
     747        6545 :         state_machine->CancelHoldTimer();
     748       13090 :     }
     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          19 :     sc::result react(const EvOpenTimerExpired &event) {
     754          19 :         StateMachine *state_machine = &context<StateMachine>();
     755          19 :         BgpPeer *peer = state_machine->peer();
     756          19 :         peer->SendOpen(state_machine->passive_session());
     757          19 :         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        1264 :     sc::result react(const EvTcpPassiveOpen &event) {
     763        1264 :         StateMachine *state_machine = &context<StateMachine>();
     764        1264 :         state_machine->set_passive_session(event.session);
     765        1264 :         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        1264 :         if (!state_machine->active_session()) {
     772           1 :             return transit<Active>();
     773             :         } else {
     774        1263 :             return discard_event();
     775             :         }
     776             :     }
     777             : 
     778             :     // Either the passive or the active session closed.
     779         645 :     sc::result react(const EvTcpClose &event) {
     780         645 :         StateMachine *state_machine = &context<StateMachine>();
     781         645 :         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         637 :             state_machine->set_active_session(NULL);
     786         639 :             if (state_machine->passive_session() == NULL ||
     787           2 :                 state_machine->OpenTimerRunning()) {
     788         636 :                 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           8 :             state_machine->set_passive_session(NULL);
     794           8 :             state_machine->CancelOpenTimer();
     795           8 :             if (state_machine->active_session() == NULL)
     796           1 :                 return transit<Active>();
     797             :         }
     798             : 
     799           8 :         return discard_event();
     800             :     }
     801             : 
     802             :     // This one is pretty involved.
     803        4126 :     sc::result react(const EvBgpOpen &event) {
     804        4126 :         StateMachine *state_machine = &context<StateMachine>();
     805        4126 :         BgpPeer *peer = state_machine->peer();
     806        4126 :         BgpSession *session = NULL;
     807             : 
     808        5557 :         if (state_machine->passive_session() &&
     809        1431 :             state_machine->active_session()) {
     810             :             // Need to resolve connection collision.
     811        1425 :             uint32_t local_bgp_id = peer->server()->bgp_identifier();
     812        1425 :             if (event.msg->identifier > local_bgp_id) {
     813             :                 // Passive connection wins, close the active session.
     814         701 :                 peer->SendNotification(state_machine->active_session(),
     815             :                     BgpProto::Notification::Cease,
     816             :                     BgpProto::Notification::ConnectionCollision,
     817             :                     "Connection collision - closing active session");
     818         701 :                 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         701 :                 session = state_machine->passive_session();
     823         701 :                 if (state_machine->OpenTimerRunning()) {
     824         697 :                     state_machine->CancelOpenTimer();
     825         697 :                     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         701 :                 if (event.session != session) {
     835           3 :                     return discard_event();
     836             :                 } else {
     837         698 :                     state_machine->AssignSession(false);
     838             :                 }
     839             :             } else {
     840             :                 // Active connection wins, close the passive session.
     841         724 :                 peer->SendNotification(state_machine->passive_session(),
     842             :                     BgpProto::Notification::Cease,
     843             :                     BgpProto::Notification::ConnectionCollision,
     844             :                     "Connection collision - closing passive session");
     845         724 :                 state_machine->set_passive_session(NULL);
     846         724 :                 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         724 :                 session = state_machine->active_session();
     855         724 :                 if (event.session != session) {
     856         715 :                     return discard_event();
     857             :                 } else {
     858           9 :                     state_machine->AssignSession(true);
     859             :                 }
     860             :             }
     861        2701 :         } 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           6 :             session = state_machine->passive_session();
     869           6 :             if (event.session != session) {
     870           0 :                 return discard_event();
     871             :             } else {
     872           6 :                 state_machine->AssignSession(false);
     873             :             }
     874        2695 :         } 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        2695 :             session = state_machine->active_session();
     882        2695 :             if (event.session != session) {
     883           0 :                 return discard_event();
     884             :             } else {
     885        2695 :                 state_machine->AssignSession(true);
     886             :             }
     887             :         }
     888             : 
     889        3408 :         int local_holdtime = state_machine->GetConfiguredHoldTime();
     890        3408 :         state_machine->set_hold_time(min(event.msg->holdtime, local_holdtime));
     891        3408 :         if (!peer->SetCapabilities(event.msg.get()))
     892           0 :             return discard_event();
     893        3408 :         return transit<OpenConfirm>();
     894             :     }
     895             : 
     896             :     // Notification received on one of the sessions for this state machine.
     897        2421 :     sc::result react(const EvBgpNotification &event) {
     898        2421 :         StateMachine *state_machine = &context<StateMachine>();
     899             : 
     900             :         // Ignore if the NOTIFICATION came in on a stale session.
     901        2421 :         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        2421 :         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           4 :             state_machine->CancelOpenTimer();
     912           4 :             return discard_event();
     913        2417 :         } 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          16 :             if (state_machine->OpenTimerRunning()) {
     920          15 :                 return transit<Active>();
     921             :             } else {
     922           1 :                 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        2401 :                 &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        5397 :     explicit OpenConfirm(my_context ctx) : my_base(ctx) {
     958        5397 :         StateMachine *state_machine = &context<StateMachine>();
     959        5397 :         BgpPeer *peer = state_machine->peer();
     960        5397 :         peer->SendKeepalive(false);
     961        5394 :         peer->StartKeepaliveTimer();
     962        5397 :         state_machine->CancelOpenTimer();
     963        5397 :         state_machine->StartHoldTimer();
     964        5397 :         state_machine->set_state(StateMachine::OPENCONFIRM);
     965        5396 :     }
     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       10794 :     ~OpenConfirm() {
     970        5397 :         StateMachine *state_machine = &context<StateMachine>();
     971        5397 :         state_machine->CancelHoldTimer();
     972       10794 :     }
     973             : 
     974             :     // Send a notification, delete the new session and stay in OpenConfirm.
     975          10 :     sc::result react(const EvTcpPassiveOpen &event) {
     976          10 :         StateMachine *state_machine = &context<StateMachine>();
     977          10 :         BgpPeer *peer = state_machine->peer();
     978          10 :         BgpSession *session = event.session;
     979          10 :         peer->SendNotification(session,
     980             :             BgpProto::Notification::Cease,
     981             :             BgpProto::Notification::ConnectionRejected,
     982             :             "Connection rejected - unexpected passive session");
     983          10 :         state_machine->DeleteSession(session);
     984          10 :         return discard_event();
     985             :     }
     986             : 
     987             :     // Ignore the notification if it's for a stale session, else go to Idle.
     988           6 :     sc::result react(const EvBgpNotification &event) {
     989           6 :         StateMachine *state_machine = &context<StateMachine>();
     990           6 :         if (!state_machine->ProcessNotificationEvent(event.session))
     991           0 :             return discard_event();
     992             : 
     993             :         return transit<Idle, StateMachine, EvBgpNotification>(
     994           6 :             &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        5328 :     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        5328 :         StateMachine *state_machine = &context<StateMachine>();
    1003        5328 :         if (state_machine->IsPeerCloseInProgress()) {
    1004             :             return transit<Idle, StateMachine, EvBgpKeepalive>(
    1005           2 :                 &StateMachine::OnIdle<EvBgpKeepalive, 0>, event);
    1006             :         }
    1007        5325 :         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        5326 :     explicit Established(my_context ctx) : my_base(ctx) {
    1034        5326 :         StateMachine *state_machine = &context<StateMachine>();
    1035        5326 :         BgpPeer *peer = state_machine->peer();
    1036        5326 :         state_machine->connect_attempts_clear();
    1037        5325 :         state_machine->StartHoldTimer();
    1038        5326 :         state_machine->set_state(StateMachine::ESTABLISHED);
    1039        5326 :         peer->NotifyEstablished(true);
    1040        5326 :         peer->RegisterAllTables();
    1041        5326 :     }
    1042             : 
    1043       10650 :     ~Established() {
    1044        5325 :         StateMachine *state_machine = &context<StateMachine>();
    1045        5324 :         BgpPeer *peer = state_machine->peer();
    1046        5324 :         peer->NotifyEstablished(false);
    1047        5326 :         state_machine->CancelHoldTimer();
    1048       10650 :     }
    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         149 :     sc::result react(const EvTcpPassiveOpen &event) {
    1053         149 :         StateMachine *state_machine = &context<StateMachine>();
    1054         149 :         BgpSession *session = event.session;
    1055         149 :         state_machine->DeleteSession(session);
    1056         158 :         if (state_machine->IsCloseGraceful() ||
    1057           9 :                 state_machine->IsRouterTypeBGPaaS()) {
    1058         142 :             state_machine->Shutdown(BgpProto::Notification::Unknown);
    1059             :         }
    1060         149 :         return discard_event();
    1061             :     }
    1062             : 
    1063             :     // Restart the hold timer.
    1064        1491 :     sc::result react(const EvBgpKeepalive &event) {
    1065        1491 :         StateMachine *state_machine = &context<StateMachine>();
    1066        1491 :         state_machine->StartHoldTimer();
    1067        1491 :         return discard_event();
    1068             :     }
    1069             : 
    1070             :     // Restart the hold timer and process the update.
    1071      158390 :     sc::result react(const EvBgpUpdate &event) {
    1072      158390 :         StateMachine *state_machine = &context<StateMachine>();
    1073      158390 :         state_machine->StartHoldTimer();
    1074      158394 :         state_machine->peer()->ProcessUpdate(event.msg.get(), event.msgsize);
    1075      158389 :         return discard_event();
    1076             :     }
    1077             : };
    1078             : 
    1079             : }  // namespace fsm
    1080             : 
    1081       10358 : StateMachine::StateMachine(BgpPeer *peer)
    1082       10358 :     : work_queue_(TaskScheduler::GetInstance()->GetTaskId("bgp::StateMachine"),
    1083             :         peer->GetTaskInstance(),
    1084             :         boost::bind(&StateMachine::DequeueEvent, this, _1)),
    1085       10358 :       peer_(peer),
    1086       10358 :       active_session_(NULL),
    1087       10358 :       passive_session_(NULL),
    1088       10358 :       connect_timer_(TimerManager::CreateTimer(*peer->server()->ioservice(),
    1089             :           "Connect timer",
    1090             :           TaskScheduler::GetInstance()->GetTaskId("bgp::StateMachine"),
    1091             :           peer->GetTaskInstance())),
    1092       10358 :       open_timer_(TimerManager::CreateTimer(*peer->server()->ioservice(),
    1093             :           "Open timer",
    1094             :           TaskScheduler::GetInstance()->GetTaskId("bgp::StateMachine"),
    1095             :           peer->GetTaskInstance())),
    1096       10358 :       hold_timer_(TimerManager::CreateTimer(*peer->server()->ioservice(),
    1097             :           "Hold timer",
    1098             :           TaskScheduler::GetInstance()->GetTaskId("bgp::StateMachine"),
    1099             :           peer->GetTaskInstance())),
    1100       10358 :       idle_hold_timer_(TimerManager::CreateTimer(*peer->server()->ioservice(),
    1101             :           "Idle hold timer",
    1102             :           TaskScheduler::GetInstance()->GetTaskId("bgp::StateMachine"),
    1103             :           peer->GetTaskInstance())),
    1104       10358 :       hold_time_(GetConfiguredHoldTime()),
    1105       10358 :       idle_hold_time_(0),
    1106       10358 :       attempts_(0),
    1107       10358 :       deleted_(false),
    1108       10358 :       state_(IDLE),
    1109       20716 :       last_state_(IDLE) {
    1110       10358 :     seed_ = peer_->bgp_identifier();
    1111       10358 :     initiate();
    1112       10358 : }
    1113             : 
    1114       10358 : void StateMachine::DeleteAllTimers() {
    1115       10358 :     TimerManager::DeleteTimer(connect_timer_);
    1116       10358 :     TimerManager::DeleteTimer(open_timer_);
    1117       10358 :     TimerManager::DeleteTimer(hold_timer_);
    1118       10358 :     TimerManager::DeleteTimer(idle_hold_timer_);
    1119       10358 : }
    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       15705 : StateMachine::~StateMachine() {
    1126       10358 :     work_queue_.Shutdown();
    1127       10358 :     terminate();
    1128       10358 :     DeleteAllTimers();
    1129       15705 : }
    1130             : 
    1131       15093 : void StateMachine::Initialize() {
    1132       15093 :     Enqueue(fsm::EvStart());
    1133       15093 : }
    1134             : 
    1135        5730 : bool StateMachine::IsPeerCloseInProgress() const {
    1136        5730 :     return peer_->IsCloseInProgress();
    1137             : }
    1138             : 
    1139       10674 : void StateMachine::Shutdown(int subcode) {
    1140       10674 :     if (peer_->IsDeleted()) {
    1141        9567 :         work_queue_.SetExitCallback(
    1142             :             boost::bind(&StateMachine::DequeueEventDone, this, _1));
    1143             :     }
    1144       10674 :     Enqueue(fsm::EvStop(subcode));
    1145       10674 : }
    1146             : 
    1147         879 : void StateMachine::SetAdminState(bool down, int subcode) {
    1148         879 :     if (down) {
    1149         475 :         Enqueue(fsm::EvStop(subcode));
    1150             :     } else {
    1151             :         // Reset all previous state.
    1152         404 :         reset_idle_hold_time();
    1153         404 :         reset_last_info();
    1154         404 :         peer_->reset_flap_count();
    1155         404 :         if (!IsPeerCloseInProgress())
    1156         404 :             Enqueue(fsm::EvStart());
    1157             :     }
    1158         879 : }
    1159             : 
    1160       17856 : bool StateMachine::IsQueueEmpty() const {
    1161       17856 :     return work_queue_.IsQueueEmpty();
    1162             : }
    1163             : 
    1164       15753 : void StateMachine::UpdateFlapCount() {
    1165       15753 :     if (get_state() == StateMachine::ESTABLISHED) {
    1166        5324 :         peer_->increment_flap_count();
    1167        5325 :         peer_->peer_stats()->Clear();
    1168             :     }
    1169       15755 : }
    1170             : 
    1171       15754 : void StateMachine::PeerClose(int code, int subcode) {
    1172       15754 :     UpdateFlapCount();
    1173       15755 :     peer_->Close(peer_->AttemptGRHelperMode(code, subcode));
    1174       25353 :     set_idle_hold_time(idle_hold_time() ? idle_hold_time() :
    1175        9595 :                                           GetIdleHoldTimeMSecs());
    1176       15758 :     reset_hold_time();
    1177       15756 : }
    1178             : 
    1179             : template <typename Ev, int code>
    1180        2499 : void StateMachine::OnIdle(const Ev &event) {
    1181        2499 :     SendNotification(peer_->session(), code);
    1182        2499 :     PeerClose(code, 0);
    1183        2499 : }
    1184             : 
    1185             : template <typename Ev>
    1186       11146 : void StateMachine::OnIdleCease(const Ev &event) {
    1187       22291 :     SendNotification(peer_->session(), BgpProto::Notification::Cease,
    1188       11146 :                      event.subcode);
    1189       11145 :     PeerClose(BgpProto::Notification::Cease, event.subcode);
    1190       11147 : }
    1191             : 
    1192             : //
    1193             : // The template below must only be called for EvBgpHeaderError, EvBgpOpenError
    1194             : // or EvBgpUpdateError.
    1195             : //
    1196             : template <typename Ev, int code>
    1197         121 : void StateMachine::OnIdleError(const Ev &event) {
    1198         121 :     SendNotification(event.session, code, event.subcode, event.data);
    1199         121 :     PeerClose(code, event.subcode);
    1200         121 : }
    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        1989 : void StateMachine::OnIdleNotification(const fsm::EvBgpNotification &event) {
    1205        1989 :     PeerClose(event.msg->error, event.msg->subcode);
    1206        1989 :     set_last_notification_in(event.msg->error, event.msg->subcode,
    1207             :                              event.Name());
    1208        1989 : }
    1209             : 
    1210       21970 : int StateMachine::GetConnectTime() const {
    1211       21970 :     int backoff = min(attempts_, 6);
    1212       21969 :     return std::min(backoff ? 1 << (backoff - 1) : 0, kConnectInterval);
    1213             : }
    1214             : 
    1215        5296 : void StateMachine::StartConnectTimer(int seconds) {
    1216        5296 :     connect_timer_->Cancel();
    1217             : 
    1218             :     // Add up to +/- kJitter percentage to reduce connection collisions.
    1219        5302 :     int ms = seconds ? seconds * 1000 : 50;
    1220        5302 :     ms = (ms * (100 - kJitter)) / 100;
    1221        5302 :     ms += (ms * (rand_r(&seed_) % (kJitter * 2))) / 100;
    1222        5302 :     connect_timer_->Start(ms,
    1223             :         boost::bind(&StateMachine::ConnectTimerExpired, this),
    1224             :         boost::bind(&StateMachine::TimerErrorHanlder, this, _1, _2));
    1225        5303 : }
    1226             : 
    1227       24375 : void StateMachine::CancelConnectTimer() {
    1228       24375 :     connect_timer_->Cancel();
    1229       24375 : }
    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       32949 : void StateMachine::CancelOpenTimer() {
    1243       32949 :     open_timer_->Cancel();
    1244       32948 : }
    1245             : 
    1246         719 : bool StateMachine::OpenTimerRunning() {
    1247         719 :     return open_timer_->running();
    1248             : }
    1249             : 
    1250          77 : void StateMachine::StartIdleHoldTimer() {
    1251          77 :     if (idle_hold_time_ <= 0)
    1252           0 :         return;
    1253             : 
    1254          77 :     idle_hold_timer_->Cancel();
    1255          77 :     idle_hold_timer_->Start(idle_hold_time_,
    1256             :         boost::bind(&StateMachine::IdleHoldTimerExpired, this),
    1257             :         boost::bind(&StateMachine::TimerErrorHanlder, this, _1, _2));
    1258             : }
    1259             : 
    1260       52223 : void StateMachine::CancelIdleHoldTimer() {
    1261       52223 :     idle_hold_timer_->Cancel();
    1262       52231 : }
    1263             : 
    1264           0 : bool StateMachine::IdleHoldTimerRunning() {
    1265           0 :     return idle_hold_timer_->running();
    1266             : }
    1267             : 
    1268      176606 : void StateMachine::StartHoldTimer() {
    1269      176606 :     if (hold_time_ <= 0)
    1270           0 :         return;
    1271             : 
    1272      176606 :     hold_timer_->Cancel();
    1273      176607 :     hold_timer_->Start(hold_time_ * 1000,
    1274             :         boost::bind(&StateMachine::HoldTimerExpired, this),
    1275             :         boost::bind(&StateMachine::TimerErrorHanlder, this, _1, _2));
    1276             : }
    1277             : 
    1278       17268 : void StateMachine::CancelHoldTimer() {
    1279       17268 :     hold_timer_->Cancel();
    1280       17268 : }
    1281             : 
    1282           0 : bool StateMachine::HoldTimerRunning() {
    1283           0 :     return hold_timer_->running();
    1284             : }
    1285             : 
    1286             : // Test Only APIs : Start
    1287             : 
    1288         191 : void StateMachine::FireConnectTimer() {
    1289         191 :     connect_timer_->Fire();
    1290         191 : }
    1291             : 
    1292          39 : void StateMachine::FireOpenTimer() {
    1293          39 :     open_timer_->Fire();
    1294          39 : }
    1295             : 
    1296          11 : void StateMachine::FireHoldTimer() {
    1297          11 :     hold_timer_->Fire();
    1298          11 : }
    1299             : 
    1300         205 : void StateMachine::FireIdleHoldTimer() {
    1301         205 :     idle_hold_timer_->Fire();
    1302         205 : }
    1303             : 
    1304             : // Test Only APIs : END
    1305             : 
    1306             : //
    1307             : // Create an active session.
    1308             : //
    1309        6995 : void StateMachine::StartSession() {
    1310        6995 :     BgpSession *session = peer_->CreateSession();
    1311        6994 :     if (!session)
    1312         192 :         return;
    1313        6802 :     set_active_session(session);
    1314        6803 :     session->set_observer(
    1315             :         boost::bind(&StateMachine::OnSessionEvent, this, _1, _2));
    1316       13604 :     peer_->server()->session_manager()->Connect(session,
    1317        6802 :         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       94326 : void StateMachine::DeleteSession(BgpSession *session) {
    1329       94326 :     if (!session)
    1330       83274 :         return;
    1331       11052 :     session->set_observer(NULL);
    1332       11053 :     session->Close();
    1333       11054 :     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        5397 : void StateMachine::AssignSession(bool active) {
    1342        5397 :     if (active) {
    1343        2704 :         peer_->set_session(active_session_);
    1344        2704 :         active_session_ = NULL;
    1345             :     } else {
    1346        2693 :         peer_->set_session(passive_session_);
    1347        2693 :         passive_session_ = NULL;
    1348             :     }
    1349        5397 : }
    1350             : 
    1351       37120 : void StateMachine::set_active_session(BgpSession *session) {
    1352       37120 :     DeleteSession(active_session_);
    1353       37121 :     active_session_ = session;
    1354       37121 : }
    1355             : 
    1356       30458 : void StateMachine::set_passive_session(BgpSession *session) {
    1357       30458 :     DeleteSession(passive_session_);
    1358       30458 :     passive_session_ = session;
    1359       30458 : }
    1360             : 
    1361       42357 : BgpSession *StateMachine::active_session() {
    1362       42357 :     return active_session_;
    1363             : }
    1364             : 
    1365       43377 : BgpSession *StateMachine::passive_session() {
    1366       43377 :     return passive_session_;
    1367             : }
    1368             : 
    1369       13766 : 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       13766 :     if (!session)
    1373       10242 :         session = passive_session_;
    1374       13766 :     if (!session)
    1375       10201 :         session = active_session_;
    1376       13766 :     if (session && code != 0)
    1377        3586 :         peer_->SendNotification(session, code, subcode, data);
    1378       13766 : }
    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        2427 : 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        2427 :     if (session && peer_->session() && peer_->session() != session) {
    1393           0 :         return false;
    1394             :     }
    1395             : 
    1396             :     // TransitToIdle<EvBgpNotification>::reaction,
    1397        2427 :     if (active_session() == session) {
    1398        2416 :         set_active_session(NULL);
    1399             :     } else {
    1400          11 :         set_passive_session(NULL);
    1401             :     }
    1402             : 
    1403        2427 :     return true;
    1404             : }
    1405             : 
    1406        7460 : bool StateMachine::ConnectTimerExpired() {
    1407        7460 :     Enqueue(fsm::EvConnectTimerExpired(connect_timer_));
    1408        7461 :     return false;
    1409             : }
    1410             : 
    1411          36 : bool StateMachine::OpenTimerExpired() {
    1412          36 :     Enqueue(fsm::EvOpenTimerExpired(open_timer_));
    1413          36 :     return false;
    1414             : }
    1415             : 
    1416          74 : bool StateMachine::HoldTimerExpired() {
    1417          74 :     boost::system::error_code error;
    1418             : 
    1419             :     // Reset hold timer if there is data already present in the socket.
    1420         138 :     if (peer() && peer()->session() && peer()->session()->socket() &&
    1421          64 :         peer()->session()->socket()->available(error) > 0) {
    1422           0 :         return true;
    1423             :     }
    1424          74 :     Enqueue(fsm::EvHoldTimerExpired(hold_timer_));
    1425          74 :     peer_->inc_hold_timer_expired();
    1426          74 :     return false;
    1427             : }
    1428             : 
    1429        4522 : bool StateMachine::IdleHoldTimerExpired() {
    1430        4522 :     Enqueue(fsm::EvIdleHoldTimerExpired(idle_hold_timer_));
    1431        4523 :     return false;
    1432             : }
    1433             : 
    1434         147 : bool StateMachine::IsCloseGraceful() const {
    1435         147 :     return peer_->IsCloseGraceful();
    1436             : }
    1437             : 
    1438           7 : bool StateMachine::IsRouterTypeBGPaaS() const {
    1439           7 :     return peer_->IsRouterTypeBGPaaS();
    1440             : }
    1441             : 
    1442             : //
    1443             : // Concurrency: ASIO thread.
    1444             : // Feed TCP session events into the state machine.
    1445             : //
    1446       10746 : void StateMachine::OnSessionEvent(
    1447             :         TcpSession *session, TcpSession::Event event) {
    1448       10746 :     BgpSession *bgp_session = static_cast<BgpSession *>(session);
    1449       10746 :     switch (event) {
    1450        6537 :     case TcpSession::CONNECT_COMPLETE:
    1451        6537 :         Enqueue(fsm::EvTcpConnected(bgp_session));
    1452        6537 :         break;
    1453         129 :     case TcpSession::CONNECT_FAILED:
    1454         129 :         Enqueue(fsm::EvTcpConnectFail(bgp_session));
    1455         129 :         peer_->inc_connect_error();
    1456         129 :         break;
    1457        3803 :     case TcpSession::CLOSE:
    1458        3803 :         Enqueue(fsm::EvTcpClose(bgp_session));
    1459        3803 :         break;
    1460         277 :     default:
    1461         277 :         break;
    1462             :     }
    1463       10746 : }
    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        4251 : bool StateMachine::PassiveOpen(BgpSession *session) {
    1471        4251 :     CHECK_CONCURRENCY("bgp::Config");
    1472        4251 :     Enqueue(fsm::EvTcpPassiveOpen(session));
    1473        4251 :     session->set_observer(boost::bind(&StateMachine::OnSessionEvent,
    1474             :         this, _1, _2));
    1475        4251 :     session->AsyncReadStart();
    1476        4251 :     return true;
    1477             : }
    1478             : 
    1479        4708 : void StateMachine::OnNotificationMessage(BgpSession *session,
    1480             :                                          BgpProto::BgpMessage *msg) {
    1481        4708 :     BgpPeer *peer = session->peer();
    1482        4708 :     if (peer)
    1483        4708 :         peer->inc_rx_notification();
    1484        4708 :     Enqueue(fsm::EvBgpNotification(session,
    1485             :             static_cast<BgpProto::Notification *>(msg)));
    1486        4709 : }
    1487             : 
    1488             : //
    1489             : // Handle incoming message on the session.
    1490             : //
    1491      176257 : void StateMachine::OnMessage(BgpSession *session, BgpProto::BgpMessage *msg,
    1492             :     size_t msgsize) {
    1493      176257 :     switch (msg->type) {
    1494        6247 :     case BgpProto::OPEN: {
    1495        6247 :         BgpProto::OpenMessage *open_msg =
    1496             :             static_cast<BgpProto::OpenMessage *>(msg);
    1497        6247 :         BgpPeer *peer = session->peer();
    1498        6247 :         peer->inc_rx_open();
    1499        6247 :         if (int subcode = open_msg->Validate(peer)) {
    1500         106 :             Enqueue(fsm::EvBgpOpenError(session, subcode));
    1501         106 :             peer->inc_open_error();
    1502             :         } else {
    1503        6138 :             Enqueue(fsm::EvBgpOpen(session, open_msg));
    1504        6141 :             msg = NULL;
    1505             :         }
    1506        6247 :         break;
    1507             :     }
    1508        6824 :     case BgpProto::KEEPALIVE: {
    1509        6824 :         BgpPeer *peer = session->peer();
    1510        6824 :         Enqueue(fsm::EvBgpKeepalive(session));
    1511        6825 :         if (peer) peer->inc_rx_keepalive();
    1512        6824 :         break;
    1513             :     }
    1514        4709 :     case BgpProto::NOTIFICATION: {
    1515        4709 :         OnNotificationMessage(session, msg);
    1516        4709 :         msg = NULL;
    1517        4709 :         break;
    1518             :     }
    1519      158479 :     case BgpProto::UPDATE: {
    1520      158479 :         BgpProto::Update *update = static_cast<BgpProto::Update *>(msg);
    1521      158479 :         BgpPeer *peer = NULL;
    1522      158479 :         if (session)
    1523      158480 :             peer = session->peer();
    1524      158478 :         if (peer)
    1525      158478 :             peer->inc_rx_update();
    1526             : 
    1527      158477 :         std::string data;
    1528             :         int subcode;
    1529      158477 :         if (peer && (subcode = update->Validate(peer, &data))) {
    1530           0 :             Enqueue(fsm::EvBgpUpdateError(session, subcode, data));
    1531           0 :             peer->inc_update_error();
    1532             :         } else {
    1533      158478 :             Enqueue(fsm::EvBgpUpdate(session, update, msgsize));
    1534      158483 :             msg = NULL;
    1535             :         }
    1536      158483 :         break;
    1537      158483 :     }
    1538           0 :     default:
    1539           0 :         SM_LOG_NOTICE("Unknown message type " << msg->type);
    1540           0 :         break;
    1541             :     }
    1542             : 
    1543      176263 :     delete msg;
    1544      176263 : }
    1545             : 
    1546             : //
    1547             : // Handle errors in incoming message on the session.
    1548             : //
    1549          16 : void StateMachine::OnMessageError(BgpSession *session,
    1550             :         const ParseErrorContext *context) {
    1551          16 :     switch (context->error_code) {
    1552           5 :     case BgpProto::Notification::MsgHdrErr: {
    1553           5 :         Enqueue(fsm::EvBgpHeaderError(session, context->error_subcode,
    1554           5 :                 context->data, context->data_size));
    1555           5 :         break;
    1556             :     }
    1557           6 :     case BgpProto::Notification::OpenMsgErr:
    1558           6 :         Enqueue(fsm::EvBgpOpenError(session, context->error_subcode,
    1559           6 :                 context->data, context->data_size));
    1560           6 :         break;
    1561           5 :     case BgpProto::Notification::UpdateMsgErr:
    1562           5 :         Enqueue(fsm::EvBgpUpdateError(session, context->error_subcode,
    1563          10 :                 std::string((const char *)context->data, context->data_size)));
    1564           5 :         break;
    1565           0 :     default:
    1566           0 :         break;
    1567             :     }
    1568          16 : }
    1569             : 
    1570             : static const std::string state_names[] = {
    1571             :     "Idle",
    1572             :     "Active",
    1573             :     "Connect",
    1574             :     "OpenSent",
    1575             :     "OpenConfirm",
    1576             :     "Established"
    1577             : };
    1578             : 
    1579      535925 : const string &StateMachine::StateName() const {
    1580      535925 :     return state_names[state_];
    1581             : }
    1582             : 
    1583       54304 : const string &StateMachine::LastStateName() const {
    1584       54304 :     return state_names[last_state_];
    1585             : }
    1586             : 
    1587          56 : const std::string StateMachine::last_state_change_at() const {
    1588         112 :     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        1216 : ostream &operator<<(ostream &out, const StateMachine::State &state) {
    1596        1216 :     out << state_names[state];
    1597        1216 :     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      200542 :     EvValidate operator()(const Ev *event) {
    1612      200542 :         return NULL;
    1613             :     }
    1614             : };
    1615             : 
    1616             : template <typename Ev>
    1617             : struct ValidateFn<Ev, true> {
    1618       40199 :     EvValidate operator()(const Ev *event) {
    1619       40199 :         return boost::bind(&Ev::validate, event, _1);
    1620             :     }
    1621             : };
    1622             : 
    1623             : template <typename Ev>
    1624      240760 : bool StateMachine::Enqueue(const Ev &event) {
    1625      240760 :     LogEvent(TYPE_NAME(event), "Enqueue");
    1626      240793 :     EventContainer ec;
    1627      240783 :     ec.event = event.intrusive_from_this();
    1628      240743 :     ec.validate = ValidateFn<Ev, HasValidate<Ev>::Has>()(
    1629      240746 :         static_cast<const Ev *>(ec.event.get()));
    1630      240682 :     work_queue_.Enqueue(ec);
    1631             : 
    1632      240794 :     return true;
    1633      240793 : }
    1634             : 
    1635      481562 : void StateMachine::LogEvent(string event_name, string msg,
    1636             :                             SandeshLevel::type log_level) {
    1637             :     // Reduce log level for keepalive and update messages.
    1638     1154471 :     if (get_state() == ESTABLISHED &&
    1639      672910 :         (event_name == "fsm::EvBgpKeepalive" ||
    1640      334964 :          event_name == "fsm::EvBgpUpdate")) {
    1641      319843 :         log_level = Sandesh::LoggingUtLevel();
    1642             :     }
    1643      481558 :     SM_LOG(log_level, msg << " " << event_name << " in state " << StateName());
    1644      481566 : }
    1645             : 
    1646      240712 : bool StateMachine::DequeueEvent(StateMachine::EventContainer ec) {
    1647             :     const fsm::EvTcpDeleteSession *deferred_delete =
    1648      240712 :         dynamic_cast<const fsm::EvTcpDeleteSession *>(ec.event.get());
    1649      240704 :     if (deferred_delete != NULL) {
    1650       11053 :         LogEvent(TYPE_NAME(*ec.event), "Dequeue");
    1651       11053 :         peer_->server()->session_manager()->DeleteSession(
    1652       11053 :             deferred_delete->session);
    1653       11054 :         return true;
    1654             :     }
    1655             : 
    1656      229651 :     set_last_event(TYPE_NAME(*ec.event));
    1657      229717 :     if (ec.validate.empty() || ec.validate(this)) {
    1658      226035 :         LogEvent(TYPE_NAME(*ec.event), "Dequeue");
    1659      226047 :         process_event(*ec.event);
    1660             :     } else {
    1661        3691 :         LogEvent(TYPE_NAME(*ec.event), "Discard", SandeshLevel::SYS_INFO);
    1662             :     }
    1663      229731 :     ec.event.reset();
    1664             : 
    1665      229739 :     return true;
    1666             : }
    1667             : 
    1668        9562 : void StateMachine::DequeueEventDone(bool done) {
    1669        9562 :     peer_->RetryDelete();
    1670        9567 : }
    1671             : 
    1672       32356 : void StateMachine::SetDataCollectionKey(BgpPeerInfo *peer_info) const {
    1673       32356 :     peer_->SetDataCollectionKey(peer_info);
    1674       32355 : }
    1675             : 
    1676          56 : const std::string StateMachine::last_notification_in_error() const {
    1677             :     return (BgpProto::Notification::toString(
    1678          56 :         static_cast<BgpProto::Notification::Code>(last_notification_in_.first),
    1679          56 :         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       40822 : 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       40822 :     if (!env_checked) {
    1697          65 :         char *keepalive_time_str = getenv("BGP_KEEPALIVE_SECONDS");
    1698          65 :         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          65 :             env_checked = true;
    1704             :         }
    1705       40758 :     } else if (env_hold_time) {
    1706           0 :         return env_hold_time;
    1707             :     }
    1708             : 
    1709             :     // Use the configured hold-time from peer if available.
    1710       40823 :     if (peer_ && peer_->hold_time())
    1711        4987 :         return peer_->hold_time();
    1712             : 
    1713             :     // Use the configured hold-time from server if available.
    1714       35836 :     if (peer_ && peer_->server()->hold_time())
    1715           2 :         return peer_->server()->hold_time();
    1716             : 
    1717             :     // Use hard coded default.
    1718       35834 :     return kHoldTime;
    1719             : }
    1720             : 
    1721      158772 : void StateMachine::BGPPeerInfoSend(const BgpPeerInfoData &peer_info) {
    1722      158772 :     assert(!peer_info.get_name().empty());
    1723      158773 :     BGP_UVE_SEND(BGPPeerInfo, peer_info);
    1724      158803 : }
    1725             : 
    1726      229701 : void StateMachine::set_last_event(const std::string &event) {
    1727      229701 :     last_event_ = event;
    1728      229697 :     last_event_at_ = UTCTimestampUsec();
    1729             : 
    1730             :     // Skip keepalive and update events after we've reached established state.
    1731      559010 :     if (state_ == ESTABLISHED &&
    1732      329276 :         (event == "fsm::EvBgpKeepalive" || event == "fsm::EvBgpUpdate")) {
    1733      159885 :         return;
    1734             :     }
    1735             : 
    1736      139654 :     BgpPeerInfoData peer_info;
    1737       69802 :     peer_info.set_name(peer()->ToUVEKey());
    1738       69810 :     PeerEventInfo event_info;
    1739       69788 :     event_info.set_last_event(last_event_);
    1740       69789 :     event_info.set_last_event_at(last_event_at_);
    1741       69786 :     peer_info.set_event_info(event_info);
    1742       69825 :     BGPPeerInfoSend(peer_info);
    1743       69840 : }
    1744             : 
    1745        5023 : void StateMachine::set_last_notification_out(int code, int subcode,
    1746             :     const string &reason) {
    1747        5023 :     last_notification_out_ = std::make_pair(code, subcode);
    1748        5023 :     last_notification_out_at_ = UTCTimestampUsec();
    1749        5023 :     last_notification_out_error_ = reason;
    1750             : 
    1751       10045 :     BgpPeerInfoData peer_info;
    1752        5022 :     peer_info.set_name(peer()->ToUVEKey());
    1753        5022 :     peer_info.set_notification_out_at(last_notification_out_at_);
    1754        5022 :     peer_info.set_notification_out(BgpProto::Notification::toString(
    1755             :         static_cast<BgpProto::Notification::Code>(code), subcode));
    1756        5023 :     BGPPeerInfoSend(peer_info);
    1757        5023 : }
    1758             : 
    1759        1989 : void StateMachine::set_last_notification_in(int code, int subcode,
    1760             :     const string &reason) {
    1761        1989 :     last_notification_in_ = std::make_pair(code, subcode);
    1762        1989 :     last_notification_in_at_ = UTCTimestampUsec();
    1763        1989 :     last_notification_in_error_ = reason;
    1764             : 
    1765        3978 :     BgpPeerInfoData peer_info;
    1766        1989 :     peer_info.set_name(peer()->ToUVEKey());
    1767        1989 :     peer_info.set_notification_in_at(last_notification_in_at_);
    1768        1989 :     peer_info.set_notification_in(BgpProto::Notification::toString(
    1769             :         static_cast<BgpProto::Notification::Code>(code), subcode));
    1770        1989 :     BGPPeerInfoSend(peer_info);
    1771        1989 : }
    1772             : 
    1773       65708 : void StateMachine::set_state(State state) {
    1774       65708 :     if (state == state_)
    1775       11854 :         return;
    1776       53854 :     last_state_ = state_; state_ = state;
    1777       53854 :     last_state_change_at_ = UTCTimestampUsec();
    1778             : 
    1779      107702 :     BgpPeerInfoData peer_info;
    1780       53849 :     peer_info.set_name(peer()->ToUVEKey());
    1781       53849 :     PeerStateInfo state_info;
    1782       53840 :     state_info.set_state(StateName());
    1783       53844 :     state_info.set_last_state(LastStateName());
    1784       53845 :     state_info.set_last_state_at(last_state_change_at_);
    1785       53844 :     peer_info.set_state_info(state_info);
    1786       53847 :     BGPPeerInfoSend(peer_info);
    1787       53853 : }
    1788             : 
    1789       11942 : void StateMachine::set_hold_time(int hold_time) {
    1790       11942 :     hold_time_ = hold_time;
    1791             : 
    1792       23883 :     BgpPeerInfoData peer_info;
    1793       11941 :     peer_info.set_name(peer()->ToUVEKey());
    1794       11940 :     peer_info.set_hold_time(hold_time_);
    1795       11940 :     BGPPeerInfoSend(peer_info);
    1796       11940 : }
    1797             : 
    1798       15758 : void StateMachine::reset_hold_time() {
    1799       15758 :     hold_time_ = GetConfiguredHoldTime();
    1800             : 
    1801       31515 :     BgpPeerInfoData peer_info;
    1802       15757 :     peer_info.set_name(peer()->ToUVEKey());
    1803       15757 :     peer_info.set_hold_time(hold_time_);
    1804       15756 :     BGPPeerInfoSend(peer_info);
    1805       15758 : }
    1806             : 
    1807         404 : void StateMachine::reset_last_info() {
    1808         404 :     last_notification_in_ = std::make_pair(0, 0);
    1809         404 :     last_notification_in_at_ = 0;
    1810         404 :     last_notification_in_error_ = std::string();
    1811         404 :     last_notification_out_ = std::make_pair(0, 0);
    1812         404 :     last_notification_out_at_ = 0;
    1813         404 :     last_notification_out_error_ = std::string();
    1814         404 :     last_state_ = IDLE;
    1815         404 :     last_event_ = "";
    1816         404 :     last_state_change_at_ = 0;
    1817         404 :     last_event_at_ = 0;
    1818             : 
    1819         808 :     BgpPeerInfoData peer_info;
    1820         404 :     peer_info.set_name(peer()->ToUVEKey());
    1821         404 :     PeerStateInfo state_info;
    1822         404 :     state_info.set_state(StateName());
    1823         404 :     state_info.set_last_state(LastStateName());
    1824         404 :     state_info.set_last_state_at(last_state_change_at_);
    1825         404 :     peer_info.set_state_info(state_info);
    1826             : 
    1827         404 :     PeerEventInfo event_info;
    1828         404 :     event_info.set_last_event(last_event_);
    1829         404 :     event_info.set_last_event_at(last_event_at_);
    1830         404 :     peer_info.set_event_info(event_info);
    1831         404 :     BGPPeerInfoSend(peer_info);
    1832         404 : }

Generated by: LCOV version 1.14