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