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-04 02:06:09 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       15508 :     EvStart() {
      76       15508 :     }
      77             :     static const char *Name() {
      78             :         return "EvStart";
      79             :     }
      80             : };
      81             : 
      82             : struct EvStop : sc::event<EvStop> {
      83       11144 :     explicit EvStop(int subcode) : subcode(subcode) {
      84       11144 :     }
      85             :     static const char *Name() {
      86             :         return "EvStop";
      87             :     }
      88             :     int subcode;
      89             : };
      90             : 
      91             : struct EvIdleHoldTimerExpired : sc::event<EvIdleHoldTimerExpired> {
      92        4478 :     explicit EvIdleHoldTimerExpired(Timer *timer)  : timer_(timer) {
      93        4477 :     }
      94             :     static const char *Name() {
      95             :         return "EvIdleHoldTimerExpired";
      96             :     }
      97        4479 :     bool validate(StateMachine *state_machine) const {
      98        4479 :         return !timer_->cancelled();
      99             :     }
     100             : 
     101             :     Timer *timer_;
     102             : };
     103             : 
     104             : struct EvConnectTimerExpired : sc::event<EvConnectTimerExpired> {
     105        7492 :     explicit EvConnectTimerExpired(Timer *timer) : timer_(timer) {
     106        7477 :     }
     107             :     static const char *Name() {
     108             :         return "EvConnectTimerExpired";
     109             :     }
     110        7490 :     bool validate(StateMachine *state_machine) const {
     111        7490 :         if (timer_->cancelled()) {
     112         203 :             return false;
     113        7288 :         } else if (state_machine->get_state() == StateMachine::ACTIVE) {
     114        6998 :             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        6548 :     explicit EvTcpConnected(BgpSession *session) : session(session) {
     163        6548 :     }
     164             :     static const char *Name() {
     165             :         return "EvTcpConnected";
     166             :     }
     167        6547 :     bool validate(StateMachine *state_machine) const {
     168        6547 :         return (state_machine->active_session() == session);
     169             :     }
     170             : 
     171             :     BgpSession *session;
     172             : };
     173             : 
     174             : struct EvTcpConnectFail : sc::event<EvTcpConnectFail> {
     175         124 :     explicit EvTcpConnectFail(BgpSession *session) : session(session) {
     176         124 :     }
     177             :     static const char *Name() {
     178             :         return "EvTcpConnectFail";
     179             :     }
     180         124 :     bool validate(StateMachine *state_machine) const {
     181         124 :         return (state_machine->active_session() == session);
     182             :     }
     183             : 
     184             :     BgpSession *session;
     185             : };
     186             : 
     187             : struct EvTcpPassiveOpen : sc::event<EvTcpPassiveOpen> {
     188        4277 :     explicit EvTcpPassiveOpen(BgpSession *session) : session(session) {
     189        4277 :     }
     190             :     static const char *Name() {
     191             :         return "EvTcpPassiveOpen";
     192             :     }
     193             : 
     194             :     BgpSession *session;
     195             : };
     196             : 
     197             : struct EvTcpClose : sc::event<EvTcpClose> {
     198        3765 :     explicit EvTcpClose(BgpSession *session) : session(session) {
     199        3765 :     }
     200             :     static const char *Name() {
     201             :         return "EvTcpClose";
     202             :     }
     203        3765 :     bool validate(StateMachine *state_machine) const {
     204        3765 :         return ((state_machine->peer()->session() == session) ||
     205        6848 :                 (state_machine->active_session() == session) ||
     206        6848 :                 (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       11084 :     explicit EvTcpDeleteSession(BgpSession *session) : session(session) {
     215       11084 :     }
     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        6174 :     EvBgpOpen(BgpSession *session, const BgpProto::OpenMessage *msg)
     241        6174 :         : session(session), msg(msg) {
     242        6174 :         BGP_LOG_PEER(Message, session->peer(), SandeshLevel::SYS_INFO,
     243             :             BGP_LOG_FLAG_SYSLOG, BGP_PEER_DIR_IN,
     244             :             "Open " << msg->ToString());
     245        6174 :     }
     246             :     static const char *Name() {
     247             :         return "EvBgpOpen";
     248             :     }
     249        6175 :     bool validate(StateMachine *state_machine) const {
     250        6175 :         return ((state_machine->peer()->session() == session) ||
     251        9628 :                 (state_machine->active_session() == session) ||
     252        9628 :                 (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         103 :     EvBgpOpenError(BgpSession *session, int subcode,
     261             :         const uint8_t *_data = NULL, size_t data_size = 0)
     262         103 :         : session(session), subcode(subcode) {
     263         103 :         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         103 :         } else if (_data) {
     269           0 :             data = std::string((const char *)_data, data_size);
     270             :         }
     271         103 :     }
     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        6846 :     explicit EvBgpKeepalive(BgpSession *session) : session(session) {
     283        6846 :         const StateMachine *state_machine = session->peer()->state_machine();
     284             :         SandeshLevel::type log_level;
     285        6845 :         if (state_machine->get_state() == StateMachine::ESTABLISHED) {
     286        1484 :             log_level = Sandesh::LoggingUtLevel();
     287             :         } else {
     288        5361 :             log_level = SandeshLevel::SYS_INFO;
     289             :         }
     290        6845 :         BGP_LOG_PEER(Message, session->peer(), log_level,
     291             :                      BGP_LOG_FLAG_SYSLOG, BGP_PEER_DIR_IN, "Keepalive");
     292        6845 :     }
     293             :     static const char *Name() {
     294             :         return "EvBgpKeepalive";
     295             :     }
     296        6848 :     bool validate(StateMachine *state_machine) const {
     297        6848 :         return !session->IsClosed();
     298             :     }
     299             : 
     300             :     BgpSession *session;
     301             : };
     302             : 
     303             : struct EvBgpNotification : sc::event<EvBgpNotification> {
     304        4703 :     EvBgpNotification(BgpSession *session, const BgpProto::Notification *msg)
     305        4703 :         : session(session), msg(msg) {
     306             :         string peer_key =
     307        4702 :             session->peer() ? session->peer()->ToUVEKey() : session->ToString();
     308        4702 :         session->LogNotification(msg->error, msg->subcode, BGP_PEER_DIR_IN,
     309             :                                  peer_key, *msg);
     310        4703 :     }
     311        1998 :     static const char *Name() {
     312        1998 :         return "EvBgpNotification";
     313             :     }
     314        4702 :     bool validate(StateMachine *state_machine) const {
     315        4702 :         return ((state_machine->peer()->session() == session) ||
     316        5006 :                 (state_machine->active_session() == session) ||
     317        5006 :                 (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      158013 :     EvBgpUpdate(BgpSession *session, const BgpProto::Update *msg,
     326      158013 :         size_t msgsize) : session(session), msg(msg), msgsize(msgsize) {
     327      158010 :     }
     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       26111 :     explicit Idle(my_context ctx) : my_base(ctx) {
     407       26110 :         StateMachine *state_machine = &context<StateMachine>();
     408       26113 :         BgpPeer *peer = state_machine->peer();
     409       26113 :         BgpSession *session = peer->session();
     410       26113 :         peer->clear_session();
     411       26114 :         state_machine->set_active_session(NULL);
     412       26111 :         state_machine->set_passive_session(NULL);
     413       26111 :         state_machine->DeleteSession(session);
     414       26111 :         state_machine->CancelOpenTimer();
     415       26112 :         state_machine->CancelIdleHoldTimer();
     416       26114 :         state_machine->set_state(StateMachine::IDLE);
     417       26114 :     }
     418             : 
     419       52216 :     ~Idle() {
     420       26108 :         StateMachine *state_machine = &context<StateMachine>();
     421       26109 :         state_machine->CancelIdleHoldTimer();
     422       52220 :     }
     423             : 
     424             :     // Start idle hold timer if it's enabled, else go to Active right away.
     425       15384 :     sc::result react(const EvStart &event) {
     426       15384 :         StateMachine *state_machine = &context<StateMachine>();
     427       15383 :         if (state_machine->idle_hold_time()) {
     428        5593 :             state_machine->StartIdleHoldTimer();
     429             :         } else {
     430        9790 :             return transit<Active>();
     431             :         }
     432        5595 :         return discard_event();
     433             :     }
     434             : 
     435             :     // The idle hold timer expired, go to Active.
     436        4456 :     sc::result react(const EvIdleHoldTimerExpired &event) {
     437        4456 :         return transit<Active>();
     438             :     }
     439             : 
     440             :     // Delete the session and ignore event.
     441         514 :     sc::result react(const EvTcpPassiveOpen &event) {
     442         514 :         StateMachine *state_machine = &context<StateMachine>();
     443         514 :         BgpSession *session = event.session;
     444         514 :         state_machine->DeleteSession(session);
     445         514 :         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       15327 :     explicit Active(my_context ctx) : my_base(ctx) {
     477       15324 :         StateMachine *state_machine = &context<StateMachine>();
     478       15325 :         BgpPeer *peer = state_machine->peer();
     479       15325 :         if (!peer->IsPassive() && !state_machine->passive_session())
     480       14973 :             state_machine->StartConnectTimer(state_machine->GetConnectTime());
     481       15329 :         state_machine->set_state(StateMachine::ACTIVE);
     482       15329 :     }
     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       30658 :     ~Active() {
     487       15329 :         StateMachine *state_machine = &context<StateMachine>();
     488       15329 :         state_machine->CancelConnectTimer();
     489       30659 :     }
     490             : 
     491             :     // The connect timer expired, go to Connect if the peer is not passive.
     492        6998 :     sc::result react(const EvConnectTimerExpired &event) {
     493        6998 :         StateMachine *state_machine = &context<StateMachine>();
     494        6999 :         BgpPeer *peer = state_machine->peer();
     495        7000 :         if (peer->IsPassive()) {
     496           1 :             return discard_event();
     497             :         } else {
     498        6999 :             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        2069 :     sc::result react(const EvTcpPassiveOpen &event) {
     519        2069 :         StateMachine *state_machine = &context<StateMachine>();
     520        2069 :         state_machine->set_passive_session(event.session);
     521        2069 :         state_machine->CancelConnectTimer();
     522        2069 :         state_machine->StartOpenTimer(StateMachine::kOpenTime);
     523        2069 :         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        1989 :     sc::result react(const EvBgpOpen &event) {
     540        1989 :         StateMachine *state_machine = &context<StateMachine>();
     541        1989 :         BgpPeer *peer = state_machine->peer();
     542        1989 :         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        1989 :         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        1989 :         if (session != event.session)
     555           0 :             return discard_event();
     556             : 
     557             :         // Send OPEN and go to OpenConfirm.
     558        1989 :         int local_holdtime = state_machine->GetConfiguredHoldTime();
     559        1989 :         state_machine->set_hold_time(min(event.msg->holdtime, local_holdtime));
     560        1989 :         state_machine->AssignSession(false);
     561        1989 :         peer->SendOpen(session);
     562        1989 :         if (!peer->SetCapabilities(event.msg.get()))
     563           0 :             return discard_event();
     564        1989 :         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        6997 :     explicit Connect(my_context ctx) : my_base(ctx) {
     594        6993 :         StateMachine *state_machine = &context<StateMachine>();
     595        6994 :         state_machine->connect_attempts_inc();
     596        6993 :         state_machine->StartConnectTimer(state_machine->GetConnectTime());
     597        6999 :         state_machine->StartSession();
     598        6999 :         state_machine->set_state(StateMachine::CONNECT);
     599        6993 :     }
     600             : 
     601       13994 :     ~Connect() {
     602        6997 :         StateMachine *state_machine = &context<StateMachine>();
     603        6998 :         state_machine->CancelConnectTimer();
     604       13996 :     }
     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        6542 :     sc::result react(const EvTcpConnected &event) {
     631        6542 :         StateMachine *state_machine = &context<StateMachine>();
     632        6542 :         BgpPeer *peer = state_machine->peer();
     633        6542 :         BgpSession *session = state_machine->active_session();
     634        6542 :         peer->SendOpen(session);
     635        6541 :         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         121 :     sc::result react(const EvTcpConnectFail &event) {
     641         121 :         StateMachine *state_machine = &context<StateMachine>();
     642         121 :         state_machine->set_active_session(NULL);
     643         121 :         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         284 :     sc::result react(const EvTcpPassiveOpen &event) {
     650         284 :         StateMachine *state_machine = &context<StateMachine>();
     651         284 :         state_machine->set_passive_session(event.session);
     652         284 :         state_machine->StartOpenTimer(StateMachine::kOpenTime);
     653         284 :         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          29 :     sc::result react(const EvBgpOpen &event) {
     677          29 :         StateMachine *state_machine = &context<StateMachine>();
     678          29 :         BgpPeer *peer = state_machine->peer();
     679          29 :         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          29 :         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          29 :         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          29 :         int local_holdtime = state_machine->GetConfiguredHoldTime();
     697          29 :         state_machine->set_hold_time(min(event.msg->holdtime, local_holdtime));
     698          29 :         state_machine->set_active_session(NULL);
     699          29 :         state_machine->AssignSession(false);
     700          29 :         peer->SendOpen(session);
     701          29 :         if (!peer->SetCapabilities(event.msg.get()))
     702           0 :             return discard_event();
     703          29 :         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        6555 :     explicit OpenSent(my_context ctx) : my_base(ctx) {
     737        6554 :         StateMachine *state_machine = &context<StateMachine>();
     738        6553 :         state_machine->set_hold_time(StateMachine::kOpenSentHoldTime);
     739        6555 :         state_machine->StartHoldTimer();
     740        6556 :         state_machine->set_state(StateMachine::OPENSENT);
     741        6556 :     }
     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       13110 :     ~OpenSent() {
     746        6555 :         StateMachine *state_machine = &context<StateMachine>();
     747        6556 :         state_machine->CancelHoldTimer();
     748       13110 :     }
     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        1248 :     sc::result react(const EvTcpPassiveOpen &event) {
     763        1248 :         StateMachine *state_machine = &context<StateMachine>();
     764        1248 :         state_machine->set_passive_session(event.session);
     765        1248 :         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        1249 :         if (!state_machine->active_session()) {
     772           1 :             return transit<Active>();
     773             :         } else {
     774        1248 :             return discard_event();
     775             :         }
     776             :     }
     777             : 
     778             :     // Either the passive or the active session closed.
     779         664 :     sc::result react(const EvTcpClose &event) {
     780         664 :         StateMachine *state_machine = &context<StateMachine>();
     781         664 :         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         660 :             state_machine->set_active_session(NULL);
     786         664 :             if (state_machine->passive_session() == NULL ||
     787           4 :                 state_machine->OpenTimerRunning()) {
     788         659 :                 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           4 :             state_machine->set_passive_session(NULL);
     794           4 :             state_machine->CancelOpenTimer();
     795           4 :             if (state_machine->active_session() == NULL)
     796           1 :                 return transit<Active>();
     797             :         }
     798             : 
     799           4 :         return discard_event();
     800             :     }
     801             : 
     802             :     // This one is pretty involved.
     803        4119 :     sc::result react(const EvBgpOpen &event) {
     804        4119 :         StateMachine *state_machine = &context<StateMachine>();
     805        4118 :         BgpPeer *peer = state_machine->peer();
     806        4118 :         BgpSession *session = NULL;
     807             : 
     808        5537 :         if (state_machine->passive_session() &&
     809        1419 :             state_machine->active_session()) {
     810             :             // Need to resolve connection collision.
     811        1413 :             uint32_t local_bgp_id = peer->server()->bgp_identifier();
     812        1413 :             if (event.msg->identifier > local_bgp_id) {
     813             :                 // Passive connection wins, close the active session.
     814         695 :                 peer->SendNotification(state_machine->active_session(),
     815             :                     BgpProto::Notification::Cease,
     816             :                     BgpProto::Notification::ConnectionCollision,
     817             :                     "Connection collision - closing active session");
     818         695 :                 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         695 :                 session = state_machine->passive_session();
     823         695 :                 if (state_machine->OpenTimerRunning()) {
     824         691 :                     state_machine->CancelOpenTimer();
     825         691 :                     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         695 :                 if (event.session != session) {
     835           7 :                     return discard_event();
     836             :                 } else {
     837         688 :                     state_machine->AssignSession(false);
     838             :                 }
     839             :             } else {
     840             :                 // Active connection wins, close the passive session.
     841         718 :                 peer->SendNotification(state_machine->passive_session(),
     842             :                     BgpProto::Notification::Cease,
     843             :                     BgpProto::Notification::ConnectionCollision,
     844             :                     "Connection collision - closing passive session");
     845         718 :                 state_machine->set_passive_session(NULL);
     846         718 :                 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         718 :                 session = state_machine->active_session();
     855         718 :                 if (event.session != session) {
     856         706 :                     return discard_event();
     857             :                 } else {
     858          12 :                     state_machine->AssignSession(true);
     859             :                 }
     860             :             }
     861        2705 :         } 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        2699 :         } 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        2699 :             session = state_machine->active_session();
     882        2699 :             if (event.session != session) {
     883           0 :                 return discard_event();
     884             :             } else {
     885        2699 :                 state_machine->AssignSession(true);
     886             :             }
     887             :         }
     888             : 
     889        3406 :         int local_holdtime = state_machine->GetConfiguredHoldTime();
     890        3406 :         state_machine->set_hold_time(min(event.msg->holdtime, local_holdtime));
     891        3406 :         if (!peer->SetCapabilities(event.msg.get()))
     892           0 :             return discard_event();
     893        3406 :         return transit<OpenConfirm>();
     894             :     }
     895             : 
     896             :     // Notification received on one of the sessions for this state machine.
     897        2400 :     sc::result react(const EvBgpNotification &event) {
     898        2400 :         StateMachine *state_machine = &context<StateMachine>();
     899             : 
     900             :         // Ignore if the NOTIFICATION came in on a stale session.
     901        2400 :         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        2400 :         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        2396 :         } 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          12 :             if (state_machine->OpenTimerRunning()) {
     920          11 :                 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        2384 :                 &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        5423 :     explicit OpenConfirm(my_context ctx) : my_base(ctx) {
     958        5423 :         StateMachine *state_machine = &context<StateMachine>();
     959        5424 :         BgpPeer *peer = state_machine->peer();
     960        5424 :         peer->SendKeepalive(false);
     961        5424 :         peer->StartKeepaliveTimer();
     962        5424 :         state_machine->CancelOpenTimer();
     963        5423 :         state_machine->StartHoldTimer();
     964        5424 :         state_machine->set_state(StateMachine::OPENCONFIRM);
     965        5423 :     }
     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       10842 :     ~OpenConfirm() {
     970        5421 :         StateMachine *state_machine = &context<StateMachine>();
     971        5421 :         state_machine->CancelHoldTimer();
     972       10842 :     }
     973             : 
     974             :     // Send a notification, delete the new session and stay in OpenConfirm.
     975           8 :     sc::result react(const EvTcpPassiveOpen &event) {
     976           8 :         StateMachine *state_machine = &context<StateMachine>();
     977           8 :         BgpPeer *peer = state_machine->peer();
     978           8 :         BgpSession *session = event.session;
     979           8 :         peer->SendNotification(session,
     980             :             BgpProto::Notification::Cease,
     981             :             BgpProto::Notification::ConnectionRejected,
     982             :             "Connection rejected - unexpected passive session");
     983           8 :         state_machine->DeleteSession(session);
     984           8 :         return discard_event();
     985             :     }
     986             : 
     987             :     // Ignore the notification if it's for a stale session, else go to Idle.
     988           9 :     sc::result react(const EvBgpNotification &event) {
     989           9 :         StateMachine *state_machine = &context<StateMachine>();
     990           9 :         if (!state_machine->ProcessNotificationEvent(event.session))
     991           0 :             return discard_event();
     992             : 
     993             :         return transit<Idle, StateMachine, EvBgpNotification>(
     994           9 :             &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        5344 :     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        5344 :         StateMachine *state_machine = &context<StateMachine>();
    1003        5343 :         if (state_machine->IsPeerCloseInProgress()) {
    1004             :             return transit<Idle, StateMachine, EvBgpKeepalive>(
    1005           2 :                 &StateMachine::OnIdle<EvBgpKeepalive, 0>, event);
    1006             :         }
    1007        5338 :         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        5340 :     explicit Established(my_context ctx) : my_base(ctx) {
    1034        5340 :         StateMachine *state_machine = &context<StateMachine>();
    1035        5341 :         BgpPeer *peer = state_machine->peer();
    1036        5341 :         state_machine->connect_attempts_clear();
    1037        5341 :         state_machine->StartHoldTimer();
    1038        5342 :         state_machine->set_state(StateMachine::ESTABLISHED);
    1039        5341 :         peer->NotifyEstablished(true);
    1040        5342 :         peer->RegisterAllTables();
    1041        5342 :     }
    1042             : 
    1043       10682 :     ~Established() {
    1044        5341 :         StateMachine *state_machine = &context<StateMachine>();
    1045        5342 :         BgpPeer *peer = state_machine->peer();
    1046        5342 :         peer->NotifyEstablished(false);
    1047        5342 :         state_machine->CancelHoldTimer();
    1048       10683 :     }
    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         153 :     sc::result react(const EvTcpPassiveOpen &event) {
    1053         153 :         StateMachine *state_machine = &context<StateMachine>();
    1054         153 :         BgpSession *session = event.session;
    1055         153 :         state_machine->DeleteSession(session);
    1056         166 :         if (state_machine->IsCloseGraceful() ||
    1057          13 :                 state_machine->IsRouterTypeBGPaaS()) {
    1058         142 :             state_machine->Shutdown(BgpProto::Notification::Unknown);
    1059             :         }
    1060         153 :         return discard_event();
    1061             :     }
    1062             : 
    1063             :     // Restart the hold timer.
    1064        1484 :     sc::result react(const EvBgpKeepalive &event) {
    1065        1484 :         StateMachine *state_machine = &context<StateMachine>();
    1066        1483 :         state_machine->StartHoldTimer();
    1067        1485 :         return discard_event();
    1068             :     }
    1069             : 
    1070             :     // Restart the hold timer and process the update.
    1071      157914 :     sc::result react(const EvBgpUpdate &event) {
    1072      157914 :         StateMachine *state_machine = &context<StateMachine>();
    1073      157918 :         state_machine->StartHoldTimer();
    1074      157923 :         state_machine->peer()->ProcessUpdate(event.msg.get(), event.msgsize);
    1075      157922 :         return discard_event();
    1076             :     }
    1077             : };
    1078             : 
    1079             : }  // namespace fsm
    1080             : 
    1081       10360 : StateMachine::StateMachine(BgpPeer *peer)
    1082       10360 :     : work_queue_(TaskScheduler::GetInstance()->GetTaskId("bgp::StateMachine"),
    1083             :         peer->GetTaskInstance(),
    1084             :         boost::bind(&StateMachine::DequeueEvent, this, _1)),
    1085       10360 :       peer_(peer),
    1086       10360 :       active_session_(NULL),
    1087       10360 :       passive_session_(NULL),
    1088       10360 :       connect_timer_(TimerManager::CreateTimer(*peer->server()->ioservice(),
    1089             :           "Connect timer",
    1090             :           TaskScheduler::GetInstance()->GetTaskId("bgp::StateMachine"),
    1091             :           peer->GetTaskInstance())),
    1092       10360 :       open_timer_(TimerManager::CreateTimer(*peer->server()->ioservice(),
    1093             :           "Open timer",
    1094             :           TaskScheduler::GetInstance()->GetTaskId("bgp::StateMachine"),
    1095             :           peer->GetTaskInstance())),
    1096       10360 :       hold_timer_(TimerManager::CreateTimer(*peer->server()->ioservice(),
    1097             :           "Hold timer",
    1098             :           TaskScheduler::GetInstance()->GetTaskId("bgp::StateMachine"),
    1099             :           peer->GetTaskInstance())),
    1100       10360 :       idle_hold_timer_(TimerManager::CreateTimer(*peer->server()->ioservice(),
    1101             :           "Idle hold timer",
    1102             :           TaskScheduler::GetInstance()->GetTaskId("bgp::StateMachine"),
    1103             :           peer->GetTaskInstance())),
    1104       10360 :       hold_time_(GetConfiguredHoldTime()),
    1105       10360 :       idle_hold_time_(0),
    1106       10360 :       attempts_(0),
    1107       10360 :       deleted_(false),
    1108       10360 :       state_(IDLE),
    1109       20720 :       last_state_(IDLE) {
    1110       10360 :     seed_ = peer_->bgp_identifier();
    1111       10360 :     initiate();
    1112       10360 : }
    1113             : 
    1114       10360 : void StateMachine::DeleteAllTimers() {
    1115       10360 :     TimerManager::DeleteTimer(connect_timer_);
    1116       10360 :     TimerManager::DeleteTimer(open_timer_);
    1117       10360 :     TimerManager::DeleteTimer(hold_timer_);
    1118       10360 :     TimerManager::DeleteTimer(idle_hold_timer_);
    1119       10360 : }
    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       15707 : StateMachine::~StateMachine() {
    1126       10360 :     work_queue_.Shutdown();
    1127       10360 :     terminate();
    1128       10360 :     DeleteAllTimers();
    1129       15707 : }
    1130             : 
    1131       15104 : void StateMachine::Initialize() {
    1132       15104 :     Enqueue(fsm::EvStart());
    1133       15104 : }
    1134             : 
    1135        5744 : bool StateMachine::IsPeerCloseInProgress() const {
    1136        5744 :     return peer_->IsCloseInProgress();
    1137             : }
    1138             : 
    1139       10669 : void StateMachine::Shutdown(int subcode) {
    1140       10669 :     if (peer_->IsDeleted()) {
    1141        9569 :         work_queue_.SetExitCallback(
    1142             :             boost::bind(&StateMachine::DequeueEventDone, this, _1));
    1143             :     }
    1144       10669 :     Enqueue(fsm::EvStop(subcode));
    1145       10669 : }
    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       17878 : bool StateMachine::IsQueueEmpty() const {
    1161       17878 :     return work_queue_.IsQueueEmpty();
    1162             : }
    1163             : 
    1164       15752 : void StateMachine::UpdateFlapCount() {
    1165       15752 :     if (get_state() == StateMachine::ESTABLISHED) {
    1166        5342 :         peer_->increment_flap_count();
    1167        5341 :         peer_->peer_stats()->Clear();
    1168             :     }
    1169       15749 : }
    1170             : 
    1171       15748 : void StateMachine::PeerClose(int code, int subcode) {
    1172       15748 :     UpdateFlapCount();
    1173       15749 :     peer_->Close(peer_->AttemptGRHelperMode(code, subcode));
    1174       25351 :     set_idle_hold_time(idle_hold_time() ? idle_hold_time() :
    1175        9597 :                                           GetIdleHoldTimeMSecs());
    1176       15754 :     reset_hold_time();
    1177       15754 : }
    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        2500 : }
    1184             : 
    1185             : template <typename Ev>
    1186       11139 : void StateMachine::OnIdleCease(const Ev &event) {
    1187       22280 :     SendNotification(peer_->session(), BgpProto::Notification::Cease,
    1188       11140 :                      event.subcode);
    1189       11141 :     PeerClose(BgpProto::Notification::Cease, event.subcode);
    1190       11143 : }
    1191             : 
    1192             : //
    1193             : // The template below must only be called for EvBgpHeaderError, EvBgpOpenError
    1194             : // or EvBgpUpdateError.
    1195             : //
    1196             : template <typename Ev, int code>
    1197         112 : void StateMachine::OnIdleError(const Ev &event) {
    1198         112 :     SendNotification(event.session, code, event.subcode, event.data);
    1199         112 :     PeerClose(code, event.subcode);
    1200         112 : }
    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        1998 : void StateMachine::OnIdleNotification(const fsm::EvBgpNotification &event) {
    1205        1998 :     PeerClose(event.msg->error, event.msg->subcode);
    1206        1998 :     set_last_notification_in(event.msg->error, event.msg->subcode,
    1207             :                              event.Name());
    1208        1998 : }
    1209             : 
    1210       21978 : int StateMachine::GetConnectTime() const {
    1211       21978 :     int backoff = min(attempts_, 6);
    1212       21976 :     return std::min(backoff ? 1 << (backoff - 1) : 0, kConnectInterval);
    1213             : }
    1214             : 
    1215        5302 : void StateMachine::StartConnectTimer(int seconds) {
    1216        5302 :     connect_timer_->Cancel();
    1217             : 
    1218             :     // Add up to +/- kJitter percentage to reduce connection collisions.
    1219        5303 :     int ms = seconds ? seconds * 1000 : 50;
    1220        5303 :     ms = (ms * (100 - kJitter)) / 100;
    1221        5303 :     ms += (ms * (rand_r(&seed_) % (kJitter * 2))) / 100;
    1222        5303 :     connect_timer_->Start(ms,
    1223             :         boost::bind(&StateMachine::ConnectTimerExpired, this),
    1224             :         boost::bind(&StateMachine::TimerErrorHanlder, this, _1, _2));
    1225        5303 : }
    1226             : 
    1227       24395 : void StateMachine::CancelConnectTimer() {
    1228       24395 :     connect_timer_->Cancel();
    1229       24398 : }
    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       32955 : void StateMachine::CancelOpenTimer() {
    1243       32955 :     open_timer_->Cancel();
    1244       32955 : }
    1245             : 
    1246         711 : bool StateMachine::OpenTimerRunning() {
    1247         711 :     return open_timer_->running();
    1248             : }
    1249             : 
    1250          72 : void StateMachine::StartIdleHoldTimer() {
    1251          72 :     if (idle_hold_time_ <= 0)
    1252           0 :         return;
    1253             : 
    1254          72 :     idle_hold_timer_->Cancel();
    1255          72 :     idle_hold_timer_->Start(idle_hold_time_,
    1256             :         boost::bind(&StateMachine::IdleHoldTimerExpired, this),
    1257             :         boost::bind(&StateMachine::TimerErrorHanlder, this, _1, _2));
    1258             : }
    1259             : 
    1260       52216 : void StateMachine::CancelIdleHoldTimer() {
    1261       52216 :     idle_hold_timer_->Cancel();
    1262       52226 : }
    1263             : 
    1264           0 : bool StateMachine::IdleHoldTimerRunning() {
    1265           0 :     return idle_hold_timer_->running();
    1266             : }
    1267             : 
    1268      176178 : void StateMachine::StartHoldTimer() {
    1269      176178 :     if (hold_time_ <= 0)
    1270           0 :         return;
    1271             : 
    1272      176178 :     hold_timer_->Cancel();
    1273      176187 :     hold_timer_->Start(hold_time_ * 1000,
    1274             :         boost::bind(&StateMachine::HoldTimerExpired, this),
    1275             :         boost::bind(&StateMachine::TimerErrorHanlder, this, _1, _2));
    1276             : }
    1277             : 
    1278       17319 : void StateMachine::CancelHoldTimer() {
    1279       17319 :     hold_timer_->Cancel();
    1280       17318 : }
    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        6999 : void StateMachine::StartSession() {
    1310        6999 :     BgpSession *session = peer_->CreateSession();
    1311        6999 :     if (!session)
    1312         192 :         return;
    1313        6807 :     set_active_session(session);
    1314        6807 :     session->set_observer(
    1315             :         boost::bind(&StateMachine::OnSessionEvent, this, _1, _2));
    1316       13614 :     peer_->server()->session_manager()->Connect(session,
    1317        6807 :         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       94327 : void StateMachine::DeleteSession(BgpSession *session) {
    1329       94327 :     if (!session)
    1330       83243 :         return;
    1331       11084 :     session->set_observer(NULL);
    1332       11083 :     session->Close();
    1333       11084 :     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        5423 : void StateMachine::AssignSession(bool active) {
    1342        5423 :     if (active) {
    1343        2711 :         peer_->set_session(active_session_);
    1344        2712 :         active_session_ = NULL;
    1345             :     } else {
    1346        2712 :         peer_->set_session(passive_session_);
    1347        2712 :         passive_session_ = NULL;
    1348             :     }
    1349        5424 : }
    1350             : 
    1351       37112 : void StateMachine::set_active_session(BgpSession *session) {
    1352       37112 :     DeleteSession(active_session_);
    1353       37110 :     active_session_ = session;
    1354       37110 : }
    1355             : 
    1356       30451 : void StateMachine::set_passive_session(BgpSession *session) {
    1357       30451 :     DeleteSession(passive_session_);
    1358       30451 :     passive_session_ = session;
    1359       30451 : }
    1360             : 
    1361       42287 : BgpSession *StateMachine::active_session() {
    1362       42287 :     return active_session_;
    1363             : }
    1364             : 
    1365       43357 : BgpSession *StateMachine::passive_session() {
    1366       43357 :     return passive_session_;
    1367             : }
    1368             : 
    1369       13752 : 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       13752 :     if (!session)
    1373       10217 :         session = passive_session_;
    1374       13752 :     if (!session)
    1375       10172 :         session = active_session_;
    1376       13752 :     if (session && code != 0)
    1377        3585 :         peer_->SendNotification(session, code, subcode, data);
    1378       13752 : }
    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        2409 : 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        2409 :     if (session && peer_->session() && peer_->session() != session) {
    1393           0 :         return false;
    1394             :     }
    1395             : 
    1396             :     // TransitToIdle<EvBgpNotification>::reaction,
    1397        2409 :     if (active_session() == session) {
    1398        2395 :         set_active_session(NULL);
    1399             :     } else {
    1400          14 :         set_passive_session(NULL);
    1401             :     }
    1402             : 
    1403        2409 :     return true;
    1404             : }
    1405             : 
    1406        7492 : bool StateMachine::ConnectTimerExpired() {
    1407        7492 :     Enqueue(fsm::EvConnectTimerExpired(connect_timer_));
    1408        7493 :     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        4479 : bool StateMachine::IdleHoldTimerExpired() {
    1430        4479 :     Enqueue(fsm::EvIdleHoldTimerExpired(idle_hold_timer_));
    1431        4481 :     return false;
    1432             : }
    1433             : 
    1434         151 : bool StateMachine::IsCloseGraceful() const {
    1435         151 :     return peer_->IsCloseGraceful();
    1436             : }
    1437             : 
    1438          11 : bool StateMachine::IsRouterTypeBGPaaS() const {
    1439          11 :     return peer_->IsRouterTypeBGPaaS();
    1440             : }
    1441             : 
    1442             : //
    1443             : // Concurrency: ASIO thread.
    1444             : // Feed TCP session events into the state machine.
    1445             : //
    1446       10687 : void StateMachine::OnSessionEvent(
    1447             :         TcpSession *session, TcpSession::Event event) {
    1448       10687 :     BgpSession *bgp_session = static_cast<BgpSession *>(session);
    1449       10687 :     switch (event) {
    1450        6548 :     case TcpSession::CONNECT_COMPLETE:
    1451        6548 :         Enqueue(fsm::EvTcpConnected(bgp_session));
    1452        6548 :         break;
    1453         124 :     case TcpSession::CONNECT_FAILED:
    1454         124 :         Enqueue(fsm::EvTcpConnectFail(bgp_session));
    1455         124 :         peer_->inc_connect_error();
    1456         124 :         break;
    1457        3765 :     case TcpSession::CLOSE:
    1458        3765 :         Enqueue(fsm::EvTcpClose(bgp_session));
    1459        3765 :         break;
    1460         250 :     default:
    1461         250 :         break;
    1462             :     }
    1463       10687 : }
    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        4277 : bool StateMachine::PassiveOpen(BgpSession *session) {
    1471        4277 :     CHECK_CONCURRENCY("bgp::Config");
    1472        4277 :     Enqueue(fsm::EvTcpPassiveOpen(session));
    1473        4277 :     session->set_observer(boost::bind(&StateMachine::OnSessionEvent,
    1474             :         this, _1, _2));
    1475        4277 :     session->AsyncReadStart();
    1476        4277 :     return true;
    1477             : }
    1478             : 
    1479        4703 : void StateMachine::OnNotificationMessage(BgpSession *session,
    1480             :                                          BgpProto::BgpMessage *msg) {
    1481        4703 :     BgpPeer *peer = session->peer();
    1482        4703 :     if (peer)
    1483        4703 :         peer->inc_rx_notification();
    1484        4703 :     Enqueue(fsm::EvBgpNotification(session,
    1485             :             static_cast<BgpProto::Notification *>(msg)));
    1486        4703 : }
    1487             : 
    1488             : //
    1489             : // Handle incoming message on the session.
    1490             : //
    1491      175838 : void StateMachine::OnMessage(BgpSession *session, BgpProto::BgpMessage *msg,
    1492             :     size_t msgsize) {
    1493      175838 :     switch (msg->type) {
    1494        6272 :     case BgpProto::OPEN: {
    1495        6272 :         BgpProto::OpenMessage *open_msg =
    1496             :             static_cast<BgpProto::OpenMessage *>(msg);
    1497        6272 :         BgpPeer *peer = session->peer();
    1498        6272 :         peer->inc_rx_open();
    1499        6272 :         if (int subcode = open_msg->Validate(peer)) {
    1500          97 :             Enqueue(fsm::EvBgpOpenError(session, subcode));
    1501          97 :             peer->inc_open_error();
    1502             :         } else {
    1503        6174 :             Enqueue(fsm::EvBgpOpen(session, open_msg));
    1504        6175 :             msg = NULL;
    1505             :         }
    1506        6272 :         break;
    1507             :     }
    1508        6845 :     case BgpProto::KEEPALIVE: {
    1509        6845 :         BgpPeer *peer = session->peer();
    1510        6845 :         Enqueue(fsm::EvBgpKeepalive(session));
    1511        6848 :         if (peer) peer->inc_rx_keepalive();
    1512        6848 :         break;
    1513             :     }
    1514        4703 :     case BgpProto::NOTIFICATION: {
    1515        4703 :         OnNotificationMessage(session, msg);
    1516        4703 :         msg = NULL;
    1517        4703 :         break;
    1518             :     }
    1519      158018 :     case BgpProto::UPDATE: {
    1520      158018 :         BgpProto::Update *update = static_cast<BgpProto::Update *>(msg);
    1521      158018 :         BgpPeer *peer = NULL;
    1522      158018 :         if (session)
    1523      158020 :             peer = session->peer();
    1524      158018 :         if (peer)
    1525      158020 :             peer->inc_rx_update();
    1526             : 
    1527      158017 :         std::string data;
    1528             :         int subcode;
    1529      158018 :         if (peer && (subcode = update->Validate(peer, &data))) {
    1530           0 :             Enqueue(fsm::EvBgpUpdateError(session, subcode, data));
    1531           0 :             peer->inc_update_error();
    1532             :         } else {
    1533      158012 :             Enqueue(fsm::EvBgpUpdate(session, update, msgsize));
    1534      158026 :             msg = NULL;
    1535             :         }
    1536      158026 :         break;
    1537      158026 :     }
    1538           0 :     default:
    1539           0 :         SM_LOG_NOTICE("Unknown message type " << msg->type);
    1540           0 :         break;
    1541             :     }
    1542             : 
    1543      175849 :     delete msg;
    1544      175849 : }
    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      535180 : const string &StateMachine::StateName() const {
    1580      535180 :     return state_names[state_];
    1581             : }
    1582             : 
    1583       54353 : const string &StateMachine::LastStateName() const {
    1584       54353 :     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      200133 :     EvValidate operator()(const Ev *event) {
    1612      200133 :         return NULL;
    1613             :     }
    1614             : };
    1615             : 
    1616             : template <typename Ev>
    1617             : struct ValidateFn<Ev, true> {
    1618       40200 :     EvValidate operator()(const Ev *event) {
    1619       40200 :         return boost::bind(&Ev::validate, event, _1);
    1620             :     }
    1621             : };
    1622             : 
    1623             : template <typename Ev>
    1624      240346 : bool StateMachine::Enqueue(const Ev &event) {
    1625      240346 :     LogEvent(TYPE_NAME(event), "Enqueue");
    1626      240394 :     EventContainer ec;
    1627      240380 :     ec.event = event.intrusive_from_this();
    1628      240334 :     ec.validate = ValidateFn<Ev, HasValidate<Ev>::Has>()(
    1629      240340 :         static_cast<const Ev *>(ec.event.get()));
    1630      240259 :     work_queue_.Enqueue(ec);
    1631             : 
    1632      240399 :     return true;
    1633      240399 : }
    1634             : 
    1635      480779 : void StateMachine::LogEvent(string event_name, string msg,
    1636             :                             SandeshLevel::type log_level) {
    1637             :     // Reduce log level for keepalive and update messages.
    1638     1151844 :     if (get_state() == ESTABLISHED &&
    1639      671068 :         (event_name == "fsm::EvBgpKeepalive" ||
    1640      334043 :          event_name == "fsm::EvBgpUpdate")) {
    1641      318906 :         log_level = Sandesh::LoggingUtLevel();
    1642             :     }
    1643      480778 :     SM_LOG(log_level, msg << " " << event_name << " in state " << StateName());
    1644      480778 : }
    1645             : 
    1646      240345 : bool StateMachine::DequeueEvent(StateMachine::EventContainer ec) {
    1647             :     const fsm::EvTcpDeleteSession *deferred_delete =
    1648      240345 :         dynamic_cast<const fsm::EvTcpDeleteSession *>(ec.event.get());
    1649      240347 :     if (deferred_delete != NULL) {
    1650       11081 :         LogEvent(TYPE_NAME(*ec.event), "Dequeue");
    1651       11084 :         peer_->server()->session_manager()->DeleteSession(
    1652       11084 :             deferred_delete->session);
    1653       11084 :         return true;
    1654             :     }
    1655             : 
    1656      229266 :     set_last_event(TYPE_NAME(*ec.event));
    1657      229283 :     if (ec.validate.empty() || ec.validate(this)) {
    1658      225627 :         LogEvent(TYPE_NAME(*ec.event), "Dequeue");
    1659      225647 :         process_event(*ec.event);
    1660             :     } else {
    1661        3664 :         LogEvent(TYPE_NAME(*ec.event), "Discard", SandeshLevel::SYS_INFO);
    1662             :     }
    1663      229306 :     ec.event.reset();
    1664             : 
    1665      229315 :     return true;
    1666             : }
    1667             : 
    1668        9565 : void StateMachine::DequeueEventDone(bool done) {
    1669        9565 :     peer_->RetryDelete();
    1670        9569 : }
    1671             : 
    1672       31980 : void StateMachine::SetDataCollectionKey(BgpPeerInfo *peer_info) const {
    1673       31980 :     peer_->SetDataCollectionKey(peer_info);
    1674       31980 : }
    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       40882 : 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       40882 :     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       40817 :     } else if (env_hold_time) {
    1706           0 :         return env_hold_time;
    1707             :     }
    1708             : 
    1709             :     // Use the configured hold-time from peer if available.
    1710       40882 :     if (peer_ && peer_->hold_time())
    1711        5020 :         return peer_->hold_time();
    1712             : 
    1713             :     // Use the configured hold-time from server if available.
    1714       35862 :     if (peer_ && peer_->server()->hold_time())
    1715           2 :         return peer_->server()->hold_time();
    1716             : 
    1717             :     // Use hard coded default.
    1718       35860 :     return kHoldTime;
    1719             : }
    1720             : 
    1721      158909 : void StateMachine::BGPPeerInfoSend(const BgpPeerInfoData &peer_info) {
    1722      158909 :     assert(!peer_info.get_name().empty());
    1723      158911 :     BGP_UVE_SEND(BGPPeerInfo, peer_info);
    1724      158922 : }
    1725             : 
    1726      229288 : void StateMachine::set_last_event(const std::string &event) {
    1727      229288 :     last_event_ = event;
    1728      229278 :     last_event_at_ = UTCTimestampUsec();
    1729             : 
    1730             :     // Skip keepalive and update events after we've reached established state.
    1731      557671 :     if (state_ == ESTABLISHED &&
    1732      328372 :         (event == "fsm::EvBgpKeepalive" || event == "fsm::EvBgpUpdate")) {
    1733      159407 :         return;
    1734             :     }
    1735             : 
    1736      139760 :     BgpPeerInfoData peer_info;
    1737       69869 :     peer_info.set_name(peer()->ToUVEKey());
    1738       69871 :     PeerEventInfo event_info;
    1739       69845 :     event_info.set_last_event(last_event_);
    1740       69847 :     event_info.set_last_event_at(last_event_at_);
    1741       69845 :     peer_info.set_event_info(event_info);
    1742       69887 :     BGPPeerInfoSend(peer_info);
    1743       69894 : }
    1744             : 
    1745        5006 : void StateMachine::set_last_notification_out(int code, int subcode,
    1746             :     const string &reason) {
    1747        5006 :     last_notification_out_ = std::make_pair(code, subcode);
    1748        5006 :     last_notification_out_at_ = UTCTimestampUsec();
    1749        5006 :     last_notification_out_error_ = reason;
    1750             : 
    1751       10012 :     BgpPeerInfoData peer_info;
    1752        5006 :     peer_info.set_name(peer()->ToUVEKey());
    1753        5006 :     peer_info.set_notification_out_at(last_notification_out_at_);
    1754        5006 :     peer_info.set_notification_out(BgpProto::Notification::toString(
    1755             :         static_cast<BgpProto::Notification::Code>(code), subcode));
    1756        5006 :     BGPPeerInfoSend(peer_info);
    1757        5006 : }
    1758             : 
    1759        1998 : void StateMachine::set_last_notification_in(int code, int subcode,
    1760             :     const string &reason) {
    1761        1998 :     last_notification_in_ = std::make_pair(code, subcode);
    1762        1998 :     last_notification_in_at_ = UTCTimestampUsec();
    1763        1998 :     last_notification_in_error_ = reason;
    1764             : 
    1765        3995 :     BgpPeerInfoData peer_info;
    1766        1998 :     peer_info.set_name(peer()->ToUVEKey());
    1767        1998 :     peer_info.set_notification_in_at(last_notification_in_at_);
    1768        1997 :     peer_info.set_notification_in(BgpProto::Notification::toString(
    1769             :         static_cast<BgpProto::Notification::Code>(code), subcode));
    1770        1998 :     BGPPeerInfoSend(peer_info);
    1771        1998 : }
    1772             : 
    1773       65764 : void StateMachine::set_state(State state) {
    1774       65764 :     if (state == state_)
    1775       11865 :         return;
    1776       53899 :     last_state_ = state_; state_ = state;
    1777       53899 :     last_state_change_at_ = UTCTimestampUsec();
    1778             : 
    1779      107794 :     BgpPeerInfoData peer_info;
    1780       53894 :     peer_info.set_name(peer()->ToUVEKey());
    1781       53896 :     PeerStateInfo state_info;
    1782       53894 :     state_info.set_state(StateName());
    1783       53893 :     state_info.set_last_state(LastStateName());
    1784       53894 :     state_info.set_last_state_at(last_state_change_at_);
    1785       53894 :     peer_info.set_state_info(state_info);
    1786       53897 :     BGPPeerInfoSend(peer_info);
    1787       53898 : }
    1788             : 
    1789       11978 : void StateMachine::set_hold_time(int hold_time) {
    1790       11978 :     hold_time_ = hold_time;
    1791             : 
    1792       23957 :     BgpPeerInfoData peer_info;
    1793       11979 :     peer_info.set_name(peer()->ToUVEKey());
    1794       11978 :     peer_info.set_hold_time(hold_time_);
    1795       11977 :     BGPPeerInfoSend(peer_info);
    1796       11978 : }
    1797             : 
    1798       15754 : void StateMachine::reset_hold_time() {
    1799       15754 :     hold_time_ = GetConfiguredHoldTime();
    1800             : 
    1801       31508 :     BgpPeerInfoData peer_info;
    1802       15754 :     peer_info.set_name(peer()->ToUVEKey());
    1803       15754 :     peer_info.set_hold_time(hold_time_);
    1804       15754 :     BGPPeerInfoSend(peer_info);
    1805       15753 : }
    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