Line data Source code
1 : /* 2 : * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved. 3 : */ 4 : 5 : #ifndef SRC_BGP_STATE_MACHINE_H_ 6 : #define SRC_BGP_STATE_MACHINE_H_ 7 : 8 : #include <boost/statechart/state_machine.hpp> 9 : 10 : #include <string> 11 : #include <utility> 12 : 13 : #include "base/queue_task.h" 14 : #include "base/timer.h" 15 : #include "bgp/bgp_proto.h" 16 : #include "io/tcp_session.h" 17 : #include "sandesh/sandesh.h" 18 : 19 : namespace sc = boost::statechart; 20 : 21 : class BgpPeer; 22 : class BgpSession; 23 : class BgpPeerInfo; 24 : class BgpPeerInfoData; 25 : class BgpMessage; 26 : class StateMachine; 27 : 28 : namespace fsm { 29 : struct Idle; 30 : struct EvBgpNotification; 31 : } 32 : 33 : typedef boost::function<bool(StateMachine *)> EvValidate; 34 : 35 : // 36 : // This class implements the state machine for a BgpPeer. Note that a single 37 : // state machine is used for a BgpPeer, instead of using one per TCP session. 38 : // As a consequence, the state machine keeps track of the active and passive 39 : // sessions to/from the peer. Connection collision is resolved using remote 40 : // and local router ids as specified in the RFC. When the state machine has 41 : // determined which session (active or passive) will be used in the steady 42 : // state, ownership of that session is transferred to the peer and the other 43 : // session, if any, is closed. 44 : // 45 : // Events for the state machine can be posted from a few different contexts. 46 : // Administrative events are posted from the bgp::Config task, TCP related 47 : // events from the ASIO thread, Timer events from bgp::StateMachine task and 48 : // BGP Message related events are posted from the io::Reader task. 49 : // 50 : // Timers run in the context of the bgp::StateMachine task instead of running 51 : // directly from the ASIO thread. This avoids race conditions wherein timers 52 : // expire at the same time that the state machine task is attempting to cancel 53 : // them. 54 : // 55 : // TCP session related events are posted directly from the ASIO thread. Race 56 : // conditions wherein the bgp::StateMachine task tries to delete a session at 57 : // the same time that the ASIO thread is attempting to notify an event for the 58 : // session are avoided by have the state machine post a pseudo event to delete 59 : // the session. See comments for the DeleteSession method for more information. 60 : // 61 : // All events on the state machine are processed asynchronously by enqueueing 62 : // them to a WorkQueue. The WorkQueue is serviced by a bgp::StateMachine task. 63 : // The BgpPeer index is used as the instance id for the task. This allows the 64 : // state machines for multiple BgpPeers to run concurrently. 65 : // 66 : // Since events are processed asynchronously, it is possible that an event is 67 : // no longer relevant by the time we get around to processing it. For example, 68 : // we may see a TCP session close event after we've already decided to delete 69 : // the session. The optional validate method in the event is used to determine 70 : // if an event is still valid/relevant before feeding it to the state machine. 71 : // Note that the validate routine needs to be run right before we process the 72 : // event i.e. it's not correct to call it when posting the event. Hence the 73 : // need for the EventContainer structure. 74 : // 75 : class StateMachine : public sc::state_machine<StateMachine, fsm::Idle> { 76 : public: 77 : typedef boost::function<void(void)> EventCB; 78 : 79 : static const int kOpenTime; 80 : static const int kConnectInterval; 81 : static const int kHoldTime; 82 : static const int kOpenSentHoldTime; 83 : static const int kIdleHoldTime; 84 : static const int kMaxIdleHoldTime; 85 : static const int kJitter; 86 : 87 : enum State { 88 : IDLE = 0, 89 : ACTIVE = 1, 90 : CONNECT = 2, 91 : OPENSENT = 3, 92 : OPENCONFIRM = 4, 93 : ESTABLISHED = 5 94 : }; 95 : 96 : explicit StateMachine(BgpPeer *peer); 97 : virtual ~StateMachine(); 98 : 99 : void Initialize(); 100 : void Shutdown(int subcode); 101 : void SetAdminState(bool down, int subcode); 102 : bool IsQueueEmpty() const; 103 : 104 : template <typename Ev, int code> void OnIdle(const Ev &event); 105 : template <typename Ev> void OnIdleCease(const Ev &event); 106 : template <typename Ev, int code> void OnIdleError(const Ev &event); 107 : void OnIdleNotification(const fsm::EvBgpNotification &event); 108 : 109 : int GetConnectTime() const; 110 : virtual void StartConnectTimer(int seconds); 111 : void CancelConnectTimer(); 112 : bool ConnectTimerRunning(); 113 : 114 : virtual void StartOpenTimer(int seconds); 115 : void CancelOpenTimer(); 116 : bool OpenTimerRunning(); 117 : 118 : int GetConfiguredHoldTime() const; 119 : virtual void StartHoldTimer(); 120 : void CancelHoldTimer(); 121 : bool HoldTimerRunning(); 122 : 123 : virtual void StartIdleHoldTimer(); 124 : void CancelIdleHoldTimer(); 125 : bool IdleHoldTimerRunning(); 126 : 127 : void StartSession(); 128 : virtual void DeleteSession(BgpSession *session); 129 : void AssignSession(bool active); 130 : 131 : virtual void OnSessionEvent(TcpSession *session, TcpSession::Event event); 132 : bool PassiveOpen(BgpSession *session); 133 : 134 : void OnMessage(BgpSession *session, BgpProto::BgpMessage *msg, 135 : size_t msgsize = 0); 136 : void OnMessageError(BgpSession *session, const ParseErrorContext *context); 137 : 138 : void SendNotification(BgpSession *session, int code, int subcode = 0, 139 : const std::string &data = std::string()); 140 : bool ProcessNotificationEvent(BgpSession *session); 141 : void SetDataCollectionKey(BgpPeerInfo *peer_info) const; 142 : 143 : const std::string &StateName() const; 144 : const std::string &LastStateName() const; 145 : 146 409663 : BgpPeer *peer() { return peer_; } 147 : BgpSession *active_session(); 148 : void set_active_session(BgpSession *session); 149 : BgpSession *passive_session(); 150 : void set_passive_session(BgpSession *session); 151 : 152 224 : int connect_attempts() const { return attempts_; } 153 6994 : void connect_attempts_inc() { attempts_++; } 154 5325 : void connect_attempts_clear() { attempts_ = 0; } 155 : 156 324 : int hold_time() const { return hold_time_; } 157 : void reset_hold_time(); 158 : void set_hold_time(int hold_time); 159 135247 : virtual int keepalive_time_msecs() const { return hold_time_ * 1000 / 3; } 160 : 161 37293 : int idle_hold_time() const { return idle_hold_time_; } 162 410 : void reset_idle_hold_time() { idle_hold_time_ = 0; } 163 16406 : void set_idle_hold_time(int idle_hold_time) { 164 16406 : idle_hold_time_ = idle_hold_time; 165 16406 : } 166 : 167 : void set_state(State state); 168 1245947 : State get_state() const { return state_; } 169 : const std::string last_state_change_at() const; 170 : const uint64_t last_state_change_usecs_at() const; 171 : void set_last_event(const std::string &event); 172 56 : const std::string &last_event() const { return last_event_; } 173 : 174 : void set_last_notification_in(int code, int subcode, 175 : const std::string &reason); 176 : void set_last_notification_out(int code, int subcode, 177 : const std::string &reason); 178 : const std::string last_notification_out_error() const; 179 : const std::string last_notification_in_error() const; 180 : void reset_last_info(); 181 : void LogEvent(std::string event_name, std::string msg, 182 : SandeshLevel::type log_level = SandeshLevel::SYS_DEBUG); 183 : bool HoldTimerExpired(); 184 : virtual bool IsCloseGraceful() const; 185 : virtual bool IsRouterTypeBGPaaS() const; 186 : virtual bool IsPeerCloseInProgress() const; 187 : 188 : protected: 189 : virtual void OnNotificationMessage(BgpSession *session, 190 : BgpProto::BgpMessage *msg); 191 5348 : virtual const int GetIdleHoldTimeMSecs() const { return kIdleHoldTime; } 192 : 193 : private: 194 : friend class StateMachineTest; 195 : friend class StateMachineUnitTest; 196 : 197 : struct EventContainer { 198 : boost::intrusive_ptr<const sc::event_base> event; 199 : EvValidate validate; 200 : }; 201 : 202 : bool ConnectTimerExpired(); 203 : void FireConnectTimer(); 204 : bool OpenTimerExpired(); 205 : void FireOpenTimer(); 206 : void FireHoldTimer(); 207 : bool IdleHoldTimerExpired(); 208 : void FireIdleHoldTimer(); 209 : 210 0 : void TimerErrorHanlder(std::string name, std::string error) { } 211 : void DeleteAllTimers(); 212 : void BGPPeerInfoSend(const BgpPeerInfoData &peer_info); 213 : 214 : template <typename Ev> bool Enqueue(const Ev &event); 215 : bool DequeueEvent(EventContainer ec); 216 : void DequeueEventDone(bool done); 217 : void UpdateFlapCount(); 218 : void PeerClose(int code, int subcode); 219 : 220 : WorkQueue<EventContainer> work_queue_; 221 : BgpPeer *peer_; 222 : BgpSession *active_session_; 223 : BgpSession *passive_session_; 224 : Timer *connect_timer_; 225 : Timer *open_timer_; 226 : Timer *hold_timer_; 227 : Timer *idle_hold_timer_; 228 : int hold_time_; 229 : int idle_hold_time_; 230 : int attempts_; 231 : unsigned int seed_; 232 : bool deleted_; 233 : State state_; 234 : State last_state_; 235 : std::string last_event_; 236 : uint64_t last_event_at_; 237 : uint64_t last_state_change_at_; 238 : std::pair<int, int> last_notification_in_; 239 : std::string last_notification_in_error_; 240 : uint64_t last_notification_in_at_; 241 : std::pair<int, int> last_notification_out_; 242 : std::string last_notification_out_error_; 243 : uint64_t last_notification_out_at_; 244 : 245 : DISALLOW_COPY_AND_ASSIGN(StateMachine); 246 : }; 247 : 248 : std::ostream &operator<<(std::ostream &out, const StateMachine::State &state); 249 : 250 : #endif // SRC_BGP_STATE_MACHINE_H_