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