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