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