OpenSDN source code
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
state_machine.h
Go to the documentation of this file.
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,
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  BgpPeer *peer() { return peer_; }
148  void set_active_session(BgpSession *session);
150  void set_passive_session(BgpSession *session);
151 
152  int connect_attempts() const { return attempts_; }
155 
156  int hold_time() const { return hold_time_; }
157  void reset_hold_time();
158  void set_hold_time(int hold_time);
159  virtual int keepalive_time_msecs() const { return hold_time_ * 1000 / 3; }
160 
161  int idle_hold_time() const { return idle_hold_time_; }
165  }
166 
167  void set_state(State state);
168  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  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  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;
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  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 
231  unsigned int seed_;
232  bool deleted_;
235  std::string last_event_;
236  uint64_t last_event_at_;
238  std::pair<int, int> last_notification_in_;
241  std::pair<int, int> last_notification_out_;
244 
246 };
247 
248 std::ostream &operator<<(std::ostream &out, const StateMachine::State &state);
249 
250 #endif // SRC_BGP_STATE_MACHINE_H_
void OnIdleNotification(const fsm::EvBgpNotification &event)
const std::string & LastStateName() const
void set_last_notification_out(int code, int subcode, const std::string &reason)
BgpPeer * peer_
Timer * open_timer_
const std::string last_notification_in_error() const
virtual void StartConnectTimer(int seconds)
void reset_hold_time()
const std::string & StateName() const
virtual void StartOpenTimer(int seconds)
bool IdleHoldTimerExpired()
bool OpenTimerExpired()
uint64_t last_state_change_at_
static const int kIdleHoldTime
Definition: state_machine.h:83
const uint64_t last_state_change_usecs_at() const
void set_passive_session(BgpSession *session)
std::ostream & operator<<(std::ostream &out, BFDState state)
Definition: bfd_common.cc:23
void reset_idle_hold_time()
BgpSession * passive_session_
void Shutdown(int subcode)
void PeerClose(int code, int subcode)
int connect_attempts() const
bool ConnectTimerExpired()
bool ProcessNotificationEvent(BgpSession *session)
static const int kJitter
Definition: state_machine.h:85
void OnMessageError(BgpSession *session, const ParseErrorContext *context)
bool ConnectTimerRunning()
std::string last_notification_in_error_
void CancelIdleHoldTimer()
boost::intrusive_ptr< const sc::event_base > event
static const int kHoldTime
Definition: state_machine.h:81
void TimerErrorHanlder(std::string name, std::string error)
void FireConnectTimer()
void SetDataCollectionKey(BgpPeerInfo *peer_info) const
bool HoldTimerExpired()
void set_active_session(BgpSession *session)
void FireIdleHoldTimer()
void CancelOpenTimer()
void OnMessage(BgpSession *session, BgpProto::BgpMessage *msg, size_t msgsize=0)
DISALLOW_COPY_AND_ASSIGN(StateMachine)
void set_last_notification_in(int code, int subcode, const std::string &reason)
int GetConfiguredHoldTime() const
State get_state() const
const std::string & last_event() const
boost::function< bool(StateMachine *)> EvValidate
Definition: state_machine.h:33
uint64_t last_event_at_
Timer * hold_timer_
void OnIdleCease(const Ev &event)
StateMachine(BgpPeer *peer)
unsigned int seed_
bool IdleHoldTimerRunning()
const std::string last_state_change_at() const
void connect_attempts_clear()
virtual void StartIdleHoldTimer()
bool IsQueueEmpty() const
uint8_t type
Definition: load_balance.h:109
virtual void StartHoldTimer()
void BGPPeerInfoSend(const BgpPeerInfoData &peer_info)
bool PassiveOpen(BgpSession *session)
void set_hold_time(int hold_time)
static const int kMaxIdleHoldTime
Definition: state_machine.h:84
std::pair< int, int > last_notification_in_
bool Enqueue(const Ev &event)
friend class StateMachineUnitTest
uint64_t last_notification_out_at_
BgpSession * active_session()
void SendNotification(BgpSession *session, int code, int subcode=0, const std::string &data=std::string())
uint64_t last_notification_in_at_
void SetAdminState(bool down, int subcode)
Timer * idle_hold_timer_
friend class StateMachineTest
void FireOpenTimer()
void DequeueEventDone(bool done)
virtual bool IsPeerCloseInProgress() const
virtual void OnSessionEvent(TcpSession *session, TcpSession::Event event)
virtual const int GetIdleHoldTimeMSecs() const
int GetConnectTime() const
static const int kOpenTime
Definition: state_machine.h:79
std::string last_notification_out_error_
void DeleteAllTimers()
void UpdateFlapCount()
virtual void OnNotificationMessage(BgpSession *session, BgpProto::BgpMessage *msg)
BgpSession * passive_session()
bool OpenTimerRunning()
void OnIdleError(const Ev &event)
const std::string last_notification_out_error() const
virtual ~StateMachine()
std::string last_event_
std::pair< int, int > last_notification_out_
bool HoldTimerRunning()
virtual bool IsCloseGraceful() const
void set_state(State state)
void set_last_event(const std::string &event)
static const int kOpenSentHoldTime
Definition: state_machine.h:82
void AssignSession(bool active)
void CancelConnectTimer()
BgpPeer * peer()
boost::function< void(void)> EventCB
Definition: state_machine.h:77
static const int kConnectInterval
Definition: state_machine.h:80
BgpSession * active_session_
void OnIdle(const Ev &event)
void connect_attempts_inc()
virtual int keepalive_time_msecs() const
void FireHoldTimer()
Definition: timer.h:54
int hold_time() const
void CancelHoldTimer()
void LogEvent(std::string event_name, std::string msg, SandeshLevel::type log_level=SandeshLevel::SYS_DEBUG)
virtual bool IsRouterTypeBGPaaS() const
virtual void DeleteSession(BgpSession *session)
void reset_last_info()
void set_idle_hold_time(int idle_hold_time)
bool DequeueEvent(EventContainer ec)
WorkQueue< EventContainer > work_queue_
Timer * connect_timer_
int idle_hold_time() const