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

Generated by: LCOV version 1.14