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