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 15497 : EvStart() {
76 15497 : }
77 : static const char *Name() {
78 : return "EvStart";
79 : }
80 : };
81 :
82 : struct EvStop : sc::event<EvStop> {
83 11149 : explicit EvStop(int subcode) : subcode(subcode) {
84 11149 : }
85 : static const char *Name() {
86 : return "EvStop";
87 : }
88 : int subcode;
89 : };
90 :
91 : struct EvIdleHoldTimerExpired : sc::event<EvIdleHoldTimerExpired> {
92 4522 : explicit EvIdleHoldTimerExpired(Timer *timer) : timer_(timer) {
93 4521 : }
94 : static const char *Name() {
95 : return "EvIdleHoldTimerExpired";
96 : }
97 4522 : bool validate(StateMachine *state_machine) const {
98 4522 : return !timer_->cancelled();
99 : }
100 :
101 : Timer *timer_;
102 : };
103 :
104 : struct EvConnectTimerExpired : sc::event<EvConnectTimerExpired> {
105 7459 : explicit EvConnectTimerExpired(Timer *timer) : timer_(timer) {
106 7449 : }
107 : static const char *Name() {
108 : return "EvConnectTimerExpired";
109 : }
110 7458 : bool validate(StateMachine *state_machine) const {
111 7458 : if (timer_->cancelled()) {
112 175 : return false;
113 7286 : } else if (state_machine->get_state() == StateMachine::ACTIVE) {
114 6996 : return (state_machine->passive_session() == NULL);
115 290 : } else if (state_machine->get_state() == StateMachine::CONNECT) {
116 394 : return (!state_machine->active_session() ||
117 394 : !state_machine->active_session()->IsEstablished());
118 : }
119 3 : return false;
120 : }
121 :
122 : Timer *timer_;
123 : };
124 :
125 : struct EvOpenTimerExpired : sc::event<EvOpenTimerExpired> {
126 36 : explicit EvOpenTimerExpired(Timer *timer) : timer_(timer) {
127 36 : }
128 : static const char *Name() {
129 : return "EvOpenTimerExpired";
130 : }
131 36 : bool validate(StateMachine *state_machine) const {
132 36 : if (timer_->cancelled()) {
133 0 : return false;
134 : } else {
135 36 : return (state_machine->passive_session() != NULL);
136 : }
137 : }
138 :
139 : Timer *timer_;
140 : };
141 :
142 : struct EvHoldTimerExpired : sc::event<EvHoldTimerExpired> {
143 74 : explicit EvHoldTimerExpired(Timer *timer) : timer_(timer) {
144 74 : }
145 : static const char *Name() {
146 : return "EvHoldTimerExpired";
147 : }
148 74 : bool validate(StateMachine *state_machine) const {
149 74 : if (timer_->cancelled()) {
150 2 : return false;
151 72 : } else if (state_machine->get_state() == StateMachine::OPENSENT) {
152 5 : return true;
153 : } else {
154 67 : return (state_machine->peer()->session() != NULL);
155 : }
156 : }
157 :
158 : Timer *timer_;
159 : };
160 :
161 : struct EvTcpConnected : sc::event<EvTcpConnected> {
162 6537 : explicit EvTcpConnected(BgpSession *session) : session(session) {
163 6537 : }
164 : static const char *Name() {
165 : return "EvTcpConnected";
166 : }
167 6537 : bool validate(StateMachine *state_machine) const {
168 6537 : return (state_machine->active_session() == session);
169 : }
170 :
171 : BgpSession *session;
172 : };
173 :
174 : struct EvTcpConnectFail : sc::event<EvTcpConnectFail> {
175 129 : explicit EvTcpConnectFail(BgpSession *session) : session(session) {
176 129 : }
177 : static const char *Name() {
178 : return "EvTcpConnectFail";
179 : }
180 129 : bool validate(StateMachine *state_machine) const {
181 129 : return (state_machine->active_session() == session);
182 : }
183 :
184 : BgpSession *session;
185 : };
186 :
187 : struct EvTcpPassiveOpen : sc::event<EvTcpPassiveOpen> {
188 4251 : explicit EvTcpPassiveOpen(BgpSession *session) : session(session) {
189 4251 : }
190 : static const char *Name() {
191 : return "EvTcpPassiveOpen";
192 : }
193 :
194 : BgpSession *session;
195 : };
196 :
197 : struct EvTcpClose : sc::event<EvTcpClose> {
198 3803 : explicit EvTcpClose(BgpSession *session) : session(session) {
199 3803 : }
200 : static const char *Name() {
201 : return "EvTcpClose";
202 : }
203 3802 : bool validate(StateMachine *state_machine) const {
204 3802 : return ((state_machine->peer()->session() == session) ||
205 6956 : (state_machine->active_session() == session) ||
206 6956 : (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 11054 : explicit EvTcpDeleteSession(BgpSession *session) : session(session) {
215 11054 : }
216 : static const char *Name() {
217 : return "EvTcpDeleteSession";
218 : }
219 :
220 : BgpSession *session;
221 : };
222 :
223 : struct EvBgpHeaderError : sc::event<EvBgpHeaderError> {
224 5 : EvBgpHeaderError(BgpSession *session, int subcode, const uint8_t *_data,
225 : size_t data_size)
226 5 : : session(session), subcode(subcode) {
227 5 : if (_data)
228 0 : data = std::string((const char *)_data, data_size);
229 5 : }
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 6138 : EvBgpOpen(BgpSession *session, const BgpProto::OpenMessage *msg)
241 6138 : : session(session), msg(msg) {
242 6138 : BGP_LOG_PEER(Message, session->peer(), SandeshLevel::SYS_INFO,
243 : BGP_LOG_FLAG_SYSLOG, BGP_PEER_DIR_IN,
244 : "Open " << msg->ToString());
245 6138 : }
246 : static const char *Name() {
247 : return "EvBgpOpen";
248 : }
249 6141 : bool validate(StateMachine *state_machine) const {
250 6141 : return ((state_machine->peer()->session() == session) ||
251 9572 : (state_machine->active_session() == session) ||
252 9572 : (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 112 : EvBgpOpenError(BgpSession *session, int subcode,
261 : const uint8_t *_data = NULL, size_t data_size = 0)
262 112 : : session(session), subcode(subcode) {
263 112 : 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 112 : } else if (_data) {
269 0 : data = std::string((const char *)_data, data_size);
270 : }
271 112 : }
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 6824 : explicit EvBgpKeepalive(BgpSession *session) : session(session) {
283 6824 : const StateMachine *state_machine = session->peer()->state_machine();
284 : SandeshLevel::type log_level;
285 6824 : if (state_machine->get_state() == StateMachine::ESTABLISHED) {
286 1491 : log_level = Sandesh::LoggingUtLevel();
287 : } else {
288 5333 : log_level = SandeshLevel::SYS_INFO;
289 : }
290 6824 : BGP_LOG_PEER(Message, session->peer(), log_level,
291 : BGP_LOG_FLAG_SYSLOG, BGP_PEER_DIR_IN, "Keepalive");
292 6824 : }
293 : static const char *Name() {
294 : return "EvBgpKeepalive";
295 : }
296 6822 : bool validate(StateMachine *state_machine) const {
297 6822 : return !session->IsClosed();
298 : }
299 :
300 : BgpSession *session;
301 : };
302 :
303 : struct EvBgpNotification : sc::event<EvBgpNotification> {
304 4708 : EvBgpNotification(BgpSession *session, const BgpProto::Notification *msg)
305 4708 : : session(session), msg(msg) {
306 : string peer_key =
307 4709 : session->peer() ? session->peer()->ToUVEKey() : session->ToString();
308 4709 : session->LogNotification(msg->error, msg->subcode, BGP_PEER_DIR_IN,
309 : peer_key, *msg);
310 4709 : }
311 1989 : static const char *Name() {
312 1989 : return "EvBgpNotification";
313 : }
314 4709 : bool validate(StateMachine *state_machine) const {
315 4709 : return ((state_machine->peer()->session() == session) ||
316 5009 : (state_machine->active_session() == session) ||
317 5009 : (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 158478 : EvBgpUpdate(BgpSession *session, const BgpProto::Update *msg,
326 158478 : size_t msgsize) : session(session), msg(msg), msgsize(msgsize) {
327 158478 : }
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 5 : EvBgpUpdateError(BgpSession *session, int subcode, std::string data)
339 5 : : session(session), subcode(subcode), data(data) {
340 5 : }
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 26116 : explicit Idle(my_context ctx) : my_base(ctx) {
407 26116 : StateMachine *state_machine = &context<StateMachine>();
408 26116 : BgpPeer *peer = state_machine->peer();
409 26116 : BgpSession *session = peer->session();
410 26116 : peer->clear_session();
411 26115 : state_machine->set_active_session(NULL);
412 26116 : state_machine->set_passive_session(NULL);
413 26116 : state_machine->DeleteSession(session);
414 26116 : state_machine->CancelOpenTimer();
415 26115 : state_machine->CancelIdleHoldTimer();
416 26115 : state_machine->set_state(StateMachine::IDLE);
417 26114 : }
418 :
419 52218 : ~Idle() {
420 26109 : StateMachine *state_machine = &context<StateMachine>();
421 26108 : state_machine->CancelIdleHoldTimer();
422 52225 : }
423 :
424 : // Start idle hold timer if it's enabled, else go to Active right away.
425 15371 : sc::result react(const EvStart &event) {
426 15371 : StateMachine *state_machine = &context<StateMachine>();
427 15372 : if (state_machine->idle_hold_time()) {
428 5602 : state_machine->StartIdleHoldTimer();
429 : } else {
430 9770 : return transit<Active>();
431 : }
432 5603 : return discard_event();
433 : }
434 :
435 : // The idle hold timer expired, go to Active.
436 4488 : sc::result react(const EvIdleHoldTimerExpired &event) {
437 4488 : return transit<Active>();
438 : }
439 :
440 : // Delete the session and ignore event.
441 496 : sc::result react(const EvTcpPassiveOpen &event) {
442 496 : StateMachine *state_machine = &context<StateMachine>();
443 496 : BgpSession *session = event.session;
444 496 : state_machine->DeleteSession(session);
445 496 : 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 15323 : explicit Active(my_context ctx) : my_base(ctx) {
477 15317 : StateMachine *state_machine = &context<StateMachine>();
478 15320 : BgpPeer *peer = state_machine->peer();
479 15319 : if (!peer->IsPassive() && !state_machine->passive_session())
480 14965 : state_machine->StartConnectTimer(state_machine->GetConnectTime());
481 15329 : state_machine->set_state(StateMachine::ACTIVE);
482 15330 : }
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 30660 : ~Active() {
487 15330 : StateMachine *state_machine = &context<StateMachine>();
488 15330 : state_machine->CancelConnectTimer();
489 30659 : }
490 :
491 : // The connect timer expired, go to Connect if the peer is not passive.
492 6996 : sc::result react(const EvConnectTimerExpired &event) {
493 6996 : StateMachine *state_machine = &context<StateMachine>();
494 6996 : BgpPeer *peer = state_machine->peer();
495 6996 : if (peer->IsPassive()) {
496 1 : return discard_event();
497 : } else {
498 6995 : return transit<Connect>();
499 : }
500 : }
501 :
502 : // Send an OPEN message on the passive session and go to OpenSent.
503 12 : sc::result react(const EvOpenTimerExpired &event) {
504 12 : StateMachine *state_machine = &context<StateMachine>();
505 12 : BgpSession *session = state_machine->passive_session();
506 12 : if (session) {
507 12 : BgpPeer *peer = state_machine->peer();
508 12 : peer->SendOpen(session);
509 12 : 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 2051 : sc::result react(const EvTcpPassiveOpen &event) {
519 2051 : StateMachine *state_machine = &context<StateMachine>();
520 2051 : state_machine->set_passive_session(event.session);
521 2051 : state_machine->CancelConnectTimer();
522 2051 : state_machine->StartOpenTimer(StateMachine::kOpenTime);
523 2051 : return discard_event();
524 : }
525 :
526 : // Start the connect timer since we don't have a passive session anymore.
527 2 : sc::result react(const EvTcpClose &event) {
528 2 : StateMachine *state_machine = &context<StateMachine>();
529 2 : if (event.session == state_machine->passive_session()) {
530 2 : state_machine->set_passive_session(NULL);
531 2 : state_machine->CancelOpenTimer();
532 2 : state_machine->StartConnectTimer(state_machine->GetConnectTime());
533 : }
534 2 : return discard_event();
535 : }
536 :
537 : // We received an OPEN message on the passive session. Send OPEN message
538 : // and go to OpenConfirm.
539 1958 : sc::result react(const EvBgpOpen &event) {
540 1958 : StateMachine *state_machine = &context<StateMachine>();
541 1958 : BgpPeer *peer = state_machine->peer();
542 1958 : 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 1958 : 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 1958 : if (session != event.session)
555 0 : return discard_event();
556 :
557 : // Send OPEN and go to OpenConfirm.
558 1958 : int local_holdtime = state_machine->GetConfiguredHoldTime();
559 1958 : state_machine->set_hold_time(min(event.msg->holdtime, local_holdtime));
560 1958 : state_machine->AssignSession(false);
561 1958 : peer->SendOpen(session);
562 1958 : if (!peer->SetCapabilities(event.msg.get()))
563 0 : return discard_event();
564 1958 : 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 6994 : explicit Connect(my_context ctx) : my_base(ctx) {
594 6994 : StateMachine *state_machine = &context<StateMachine>();
595 6994 : state_machine->connect_attempts_inc();
596 6994 : state_machine->StartConnectTimer(state_machine->GetConnectTime());
597 6995 : state_machine->StartSession();
598 6995 : state_machine->set_state(StateMachine::CONNECT);
599 6994 : }
600 :
601 13990 : ~Connect() {
602 6995 : StateMachine *state_machine = &context<StateMachine>();
603 6994 : state_machine->CancelConnectTimer();
604 13990 : }
605 :
606 : // Get rid of the active session and go back to Active.
607 287 : sc::result react(const EvConnectTimerExpired &event) {
608 287 : StateMachine *state_machine = &context<StateMachine>();
609 287 : BgpPeer *peer = state_machine->peer();
610 287 : state_machine->set_active_session(NULL);
611 287 : peer->inc_connect_timer_expired();
612 287 : 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 2 : sc::result react(const EvOpenTimerExpired &event) {
620 2 : StateMachine *state_machine = &context<StateMachine>();
621 2 : BgpPeer *peer = state_machine->peer();
622 2 : peer->SendOpen(state_machine->passive_session());
623 2 : state_machine->set_active_session(NULL);
624 2 : 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 6531 : sc::result react(const EvTcpConnected &event) {
631 6531 : StateMachine *state_machine = &context<StateMachine>();
632 6531 : BgpPeer *peer = state_machine->peer();
633 6531 : BgpSession *session = state_machine->active_session();
634 6531 : peer->SendOpen(session);
635 6529 : 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 126 : sc::result react(const EvTcpConnectFail &event) {
641 126 : StateMachine *state_machine = &context<StateMachine>();
642 126 : state_machine->set_active_session(NULL);
643 126 : 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 281 : sc::result react(const EvTcpPassiveOpen &event) {
650 281 : StateMachine *state_machine = &context<StateMachine>();
651 281 : state_machine->set_passive_session(event.session);
652 281 : state_machine->StartOpenTimer(StateMachine::kOpenTime);
653 281 : return discard_event();
654 : }
655 :
656 : // Either the active or passive session got closed.
657 3 : sc::result react(const EvTcpClose &event) {
658 3 : StateMachine *state_machine = &context<StateMachine>();
659 3 : 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 1 : state_machine->set_passive_session(NULL);
663 1 : state_machine->CancelOpenTimer();
664 1 : 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 2 : assert(event.session == state_machine->active_session());
669 2 : state_machine->set_active_session(NULL);
670 2 : 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 31 : sc::result react(const EvBgpOpen &event) {
677 31 : StateMachine *state_machine = &context<StateMachine>();
678 31 : BgpPeer *peer = state_machine->peer();
679 31 : 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 31 : 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 31 : 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 31 : int local_holdtime = state_machine->GetConfiguredHoldTime();
697 31 : state_machine->set_hold_time(min(event.msg->holdtime, local_holdtime));
698 31 : state_machine->set_active_session(NULL);
699 31 : state_machine->AssignSession(false);
700 31 : peer->SendOpen(session);
701 31 : if (!peer->SetCapabilities(event.msg.get()))
702 0 : return discard_event();
703 31 : 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 6542 : explicit OpenSent(my_context ctx) : my_base(ctx) {
737 6542 : StateMachine *state_machine = &context<StateMachine>();
738 6545 : state_machine->set_hold_time(StateMachine::kOpenSentHoldTime);
739 6544 : state_machine->StartHoldTimer();
740 6545 : state_machine->set_state(StateMachine::OPENSENT);
741 6545 : }
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 13090 : ~OpenSent() {
746 6545 : StateMachine *state_machine = &context<StateMachine>();
747 6545 : state_machine->CancelHoldTimer();
748 13090 : }
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 19 : sc::result react(const EvOpenTimerExpired &event) {
754 19 : StateMachine *state_machine = &context<StateMachine>();
755 19 : BgpPeer *peer = state_machine->peer();
756 19 : peer->SendOpen(state_machine->passive_session());
757 19 : 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 1264 : sc::result react(const EvTcpPassiveOpen &event) {
763 1264 : StateMachine *state_machine = &context<StateMachine>();
764 1264 : state_machine->set_passive_session(event.session);
765 1264 : 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 1264 : if (!state_machine->active_session()) {
772 1 : return transit<Active>();
773 : } else {
774 1263 : return discard_event();
775 : }
776 : }
777 :
778 : // Either the passive or the active session closed.
779 645 : sc::result react(const EvTcpClose &event) {
780 645 : StateMachine *state_machine = &context<StateMachine>();
781 645 : 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 637 : state_machine->set_active_session(NULL);
786 639 : if (state_machine->passive_session() == NULL ||
787 2 : state_machine->OpenTimerRunning()) {
788 636 : 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 8 : state_machine->set_passive_session(NULL);
794 8 : state_machine->CancelOpenTimer();
795 8 : if (state_machine->active_session() == NULL)
796 1 : return transit<Active>();
797 : }
798 :
799 8 : return discard_event();
800 : }
801 :
802 : // This one is pretty involved.
803 4126 : sc::result react(const EvBgpOpen &event) {
804 4126 : StateMachine *state_machine = &context<StateMachine>();
805 4126 : BgpPeer *peer = state_machine->peer();
806 4126 : BgpSession *session = NULL;
807 :
808 5557 : if (state_machine->passive_session() &&
809 1431 : state_machine->active_session()) {
810 : // Need to resolve connection collision.
811 1425 : uint32_t local_bgp_id = peer->server()->bgp_identifier();
812 1425 : if (event.msg->identifier > local_bgp_id) {
813 : // Passive connection wins, close the active session.
814 701 : peer->SendNotification(state_machine->active_session(),
815 : BgpProto::Notification::Cease,
816 : BgpProto::Notification::ConnectionCollision,
817 : "Connection collision - closing active session");
818 701 : 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 701 : session = state_machine->passive_session();
823 701 : if (state_machine->OpenTimerRunning()) {
824 697 : state_machine->CancelOpenTimer();
825 697 : 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 701 : if (event.session != session) {
835 3 : return discard_event();
836 : } else {
837 698 : state_machine->AssignSession(false);
838 : }
839 : } else {
840 : // Active connection wins, close the passive session.
841 724 : peer->SendNotification(state_machine->passive_session(),
842 : BgpProto::Notification::Cease,
843 : BgpProto::Notification::ConnectionCollision,
844 : "Connection collision - closing passive session");
845 724 : state_machine->set_passive_session(NULL);
846 724 : 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 724 : session = state_machine->active_session();
855 724 : if (event.session != session) {
856 715 : return discard_event();
857 : } else {
858 9 : state_machine->AssignSession(true);
859 : }
860 : }
861 2701 : } 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 6 : session = state_machine->passive_session();
869 6 : if (event.session != session) {
870 0 : return discard_event();
871 : } else {
872 6 : state_machine->AssignSession(false);
873 : }
874 2695 : } 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 2695 : session = state_machine->active_session();
882 2695 : if (event.session != session) {
883 0 : return discard_event();
884 : } else {
885 2695 : state_machine->AssignSession(true);
886 : }
887 : }
888 :
889 3408 : int local_holdtime = state_machine->GetConfiguredHoldTime();
890 3408 : state_machine->set_hold_time(min(event.msg->holdtime, local_holdtime));
891 3408 : if (!peer->SetCapabilities(event.msg.get()))
892 0 : return discard_event();
893 3408 : return transit<OpenConfirm>();
894 : }
895 :
896 : // Notification received on one of the sessions for this state machine.
897 2421 : sc::result react(const EvBgpNotification &event) {
898 2421 : StateMachine *state_machine = &context<StateMachine>();
899 :
900 : // Ignore if the NOTIFICATION came in on a stale session.
901 2421 : 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 2421 : 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 4 : state_machine->CancelOpenTimer();
912 4 : return discard_event();
913 2417 : } 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 16 : if (state_machine->OpenTimerRunning()) {
920 15 : return transit<Active>();
921 : } else {
922 1 : 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 2401 : &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 5397 : explicit OpenConfirm(my_context ctx) : my_base(ctx) {
958 5397 : StateMachine *state_machine = &context<StateMachine>();
959 5397 : BgpPeer *peer = state_machine->peer();
960 5397 : peer->SendKeepalive(false);
961 5394 : peer->StartKeepaliveTimer();
962 5397 : state_machine->CancelOpenTimer();
963 5397 : state_machine->StartHoldTimer();
964 5397 : state_machine->set_state(StateMachine::OPENCONFIRM);
965 5396 : }
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 10794 : ~OpenConfirm() {
970 5397 : StateMachine *state_machine = &context<StateMachine>();
971 5397 : state_machine->CancelHoldTimer();
972 10794 : }
973 :
974 : // Send a notification, delete the new session and stay in OpenConfirm.
975 10 : sc::result react(const EvTcpPassiveOpen &event) {
976 10 : StateMachine *state_machine = &context<StateMachine>();
977 10 : BgpPeer *peer = state_machine->peer();
978 10 : BgpSession *session = event.session;
979 10 : peer->SendNotification(session,
980 : BgpProto::Notification::Cease,
981 : BgpProto::Notification::ConnectionRejected,
982 : "Connection rejected - unexpected passive session");
983 10 : state_machine->DeleteSession(session);
984 10 : return discard_event();
985 : }
986 :
987 : // Ignore the notification if it's for a stale session, else go to Idle.
988 6 : sc::result react(const EvBgpNotification &event) {
989 6 : StateMachine *state_machine = &context<StateMachine>();
990 6 : if (!state_machine->ProcessNotificationEvent(event.session))
991 0 : return discard_event();
992 :
993 : return transit<Idle, StateMachine, EvBgpNotification>(
994 6 : &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 5328 : 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 5328 : StateMachine *state_machine = &context<StateMachine>();
1003 5328 : if (state_machine->IsPeerCloseInProgress()) {
1004 : return transit<Idle, StateMachine, EvBgpKeepalive>(
1005 2 : &StateMachine::OnIdle<EvBgpKeepalive, 0>, event);
1006 : }
1007 5325 : 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 5326 : explicit Established(my_context ctx) : my_base(ctx) {
1034 5326 : StateMachine *state_machine = &context<StateMachine>();
1035 5326 : BgpPeer *peer = state_machine->peer();
1036 5326 : state_machine->connect_attempts_clear();
1037 5325 : state_machine->StartHoldTimer();
1038 5326 : state_machine->set_state(StateMachine::ESTABLISHED);
1039 5326 : peer->NotifyEstablished(true);
1040 5326 : peer->RegisterAllTables();
1041 5326 : }
1042 :
1043 10650 : ~Established() {
1044 5325 : StateMachine *state_machine = &context<StateMachine>();
1045 5324 : BgpPeer *peer = state_machine->peer();
1046 5324 : peer->NotifyEstablished(false);
1047 5326 : state_machine->CancelHoldTimer();
1048 10650 : }
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 149 : sc::result react(const EvTcpPassiveOpen &event) {
1053 149 : StateMachine *state_machine = &context<StateMachine>();
1054 149 : BgpSession *session = event.session;
1055 149 : state_machine->DeleteSession(session);
1056 158 : if (state_machine->IsCloseGraceful() ||
1057 9 : state_machine->IsRouterTypeBGPaaS()) {
1058 142 : state_machine->Shutdown(BgpProto::Notification::Unknown);
1059 : }
1060 149 : return discard_event();
1061 : }
1062 :
1063 : // Restart the hold timer.
1064 1491 : sc::result react(const EvBgpKeepalive &event) {
1065 1491 : StateMachine *state_machine = &context<StateMachine>();
1066 1491 : state_machine->StartHoldTimer();
1067 1491 : return discard_event();
1068 : }
1069 :
1070 : // Restart the hold timer and process the update.
1071 158390 : sc::result react(const EvBgpUpdate &event) {
1072 158390 : StateMachine *state_machine = &context<StateMachine>();
1073 158390 : state_machine->StartHoldTimer();
1074 158394 : state_machine->peer()->ProcessUpdate(event.msg.get(), event.msgsize);
1075 158389 : return discard_event();
1076 : }
1077 : };
1078 :
1079 : } // namespace fsm
1080 :
1081 10358 : StateMachine::StateMachine(BgpPeer *peer)
1082 10358 : : work_queue_(TaskScheduler::GetInstance()->GetTaskId("bgp::StateMachine"),
1083 : peer->GetTaskInstance(),
1084 : boost::bind(&StateMachine::DequeueEvent, this, _1)),
1085 10358 : peer_(peer),
1086 10358 : active_session_(NULL),
1087 10358 : passive_session_(NULL),
1088 10358 : connect_timer_(TimerManager::CreateTimer(*peer->server()->ioservice(),
1089 : "Connect timer",
1090 : TaskScheduler::GetInstance()->GetTaskId("bgp::StateMachine"),
1091 : peer->GetTaskInstance())),
1092 10358 : open_timer_(TimerManager::CreateTimer(*peer->server()->ioservice(),
1093 : "Open timer",
1094 : TaskScheduler::GetInstance()->GetTaskId("bgp::StateMachine"),
1095 : peer->GetTaskInstance())),
1096 10358 : hold_timer_(TimerManager::CreateTimer(*peer->server()->ioservice(),
1097 : "Hold timer",
1098 : TaskScheduler::GetInstance()->GetTaskId("bgp::StateMachine"),
1099 : peer->GetTaskInstance())),
1100 10358 : idle_hold_timer_(TimerManager::CreateTimer(*peer->server()->ioservice(),
1101 : "Idle hold timer",
1102 : TaskScheduler::GetInstance()->GetTaskId("bgp::StateMachine"),
1103 : peer->GetTaskInstance())),
1104 10358 : hold_time_(GetConfiguredHoldTime()),
1105 10358 : idle_hold_time_(0),
1106 10358 : attempts_(0),
1107 10358 : deleted_(false),
1108 10358 : state_(IDLE),
1109 20716 : last_state_(IDLE) {
1110 10358 : seed_ = peer_->bgp_identifier();
1111 10358 : initiate();
1112 10358 : }
1113 :
1114 10358 : void StateMachine::DeleteAllTimers() {
1115 10358 : TimerManager::DeleteTimer(connect_timer_);
1116 10358 : TimerManager::DeleteTimer(open_timer_);
1117 10358 : TimerManager::DeleteTimer(hold_timer_);
1118 10358 : TimerManager::DeleteTimer(idle_hold_timer_);
1119 10358 : }
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 15705 : StateMachine::~StateMachine() {
1126 10358 : work_queue_.Shutdown();
1127 10358 : terminate();
1128 10358 : DeleteAllTimers();
1129 15705 : }
1130 :
1131 15093 : void StateMachine::Initialize() {
1132 15093 : Enqueue(fsm::EvStart());
1133 15093 : }
1134 :
1135 5730 : bool StateMachine::IsPeerCloseInProgress() const {
1136 5730 : return peer_->IsCloseInProgress();
1137 : }
1138 :
1139 10674 : void StateMachine::Shutdown(int subcode) {
1140 10674 : if (peer_->IsDeleted()) {
1141 9567 : work_queue_.SetExitCallback(
1142 : boost::bind(&StateMachine::DequeueEventDone, this, _1));
1143 : }
1144 10674 : Enqueue(fsm::EvStop(subcode));
1145 10674 : }
1146 :
1147 879 : void StateMachine::SetAdminState(bool down, int subcode) {
1148 879 : if (down) {
1149 475 : Enqueue(fsm::EvStop(subcode));
1150 : } else {
1151 : // Reset all previous state.
1152 404 : reset_idle_hold_time();
1153 404 : reset_last_info();
1154 404 : peer_->reset_flap_count();
1155 404 : if (!IsPeerCloseInProgress())
1156 404 : Enqueue(fsm::EvStart());
1157 : }
1158 879 : }
1159 :
1160 17856 : bool StateMachine::IsQueueEmpty() const {
1161 17856 : return work_queue_.IsQueueEmpty();
1162 : }
1163 :
1164 15753 : void StateMachine::UpdateFlapCount() {
1165 15753 : if (get_state() == StateMachine::ESTABLISHED) {
1166 5324 : peer_->increment_flap_count();
1167 5325 : peer_->peer_stats()->Clear();
1168 : }
1169 15755 : }
1170 :
1171 15754 : void StateMachine::PeerClose(int code, int subcode) {
1172 15754 : UpdateFlapCount();
1173 15755 : peer_->Close(peer_->AttemptGRHelperMode(code, subcode));
1174 25353 : set_idle_hold_time(idle_hold_time() ? idle_hold_time() :
1175 9595 : GetIdleHoldTimeMSecs());
1176 15758 : reset_hold_time();
1177 15756 : }
1178 :
1179 : template <typename Ev, int code>
1180 2499 : void StateMachine::OnIdle(const Ev &event) {
1181 2499 : SendNotification(peer_->session(), code);
1182 2499 : PeerClose(code, 0);
1183 2499 : }
1184 :
1185 : template <typename Ev>
1186 11146 : void StateMachine::OnIdleCease(const Ev &event) {
1187 22291 : SendNotification(peer_->session(), BgpProto::Notification::Cease,
1188 11146 : event.subcode);
1189 11145 : PeerClose(BgpProto::Notification::Cease, event.subcode);
1190 11147 : }
1191 :
1192 : //
1193 : // The template below must only be called for EvBgpHeaderError, EvBgpOpenError
1194 : // or EvBgpUpdateError.
1195 : //
1196 : template <typename Ev, int code>
1197 121 : void StateMachine::OnIdleError(const Ev &event) {
1198 121 : SendNotification(event.session, code, event.subcode, event.data);
1199 121 : PeerClose(code, event.subcode);
1200 121 : }
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 1989 : void StateMachine::OnIdleNotification(const fsm::EvBgpNotification &event) {
1205 1989 : PeerClose(event.msg->error, event.msg->subcode);
1206 1989 : set_last_notification_in(event.msg->error, event.msg->subcode,
1207 : event.Name());
1208 1989 : }
1209 :
1210 21970 : int StateMachine::GetConnectTime() const {
1211 21970 : int backoff = min(attempts_, 6);
1212 21969 : return std::min(backoff ? 1 << (backoff - 1) : 0, kConnectInterval);
1213 : }
1214 :
1215 5296 : void StateMachine::StartConnectTimer(int seconds) {
1216 5296 : connect_timer_->Cancel();
1217 :
1218 : // Add up to +/- kJitter percentage to reduce connection collisions.
1219 5302 : int ms = seconds ? seconds * 1000 : 50;
1220 5302 : ms = (ms * (100 - kJitter)) / 100;
1221 5302 : ms += (ms * (rand_r(&seed_) % (kJitter * 2))) / 100;
1222 5302 : connect_timer_->Start(ms,
1223 : boost::bind(&StateMachine::ConnectTimerExpired, this),
1224 : boost::bind(&StateMachine::TimerErrorHanlder, this, _1, _2));
1225 5303 : }
1226 :
1227 24375 : void StateMachine::CancelConnectTimer() {
1228 24375 : connect_timer_->Cancel();
1229 24375 : }
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 32949 : void StateMachine::CancelOpenTimer() {
1243 32949 : open_timer_->Cancel();
1244 32948 : }
1245 :
1246 719 : bool StateMachine::OpenTimerRunning() {
1247 719 : return open_timer_->running();
1248 : }
1249 :
1250 77 : void StateMachine::StartIdleHoldTimer() {
1251 77 : if (idle_hold_time_ <= 0)
1252 0 : return;
1253 :
1254 77 : idle_hold_timer_->Cancel();
1255 77 : idle_hold_timer_->Start(idle_hold_time_,
1256 : boost::bind(&StateMachine::IdleHoldTimerExpired, this),
1257 : boost::bind(&StateMachine::TimerErrorHanlder, this, _1, _2));
1258 : }
1259 :
1260 52223 : void StateMachine::CancelIdleHoldTimer() {
1261 52223 : idle_hold_timer_->Cancel();
1262 52231 : }
1263 :
1264 0 : bool StateMachine::IdleHoldTimerRunning() {
1265 0 : return idle_hold_timer_->running();
1266 : }
1267 :
1268 176606 : void StateMachine::StartHoldTimer() {
1269 176606 : if (hold_time_ <= 0)
1270 0 : return;
1271 :
1272 176606 : hold_timer_->Cancel();
1273 176607 : hold_timer_->Start(hold_time_ * 1000,
1274 : boost::bind(&StateMachine::HoldTimerExpired, this),
1275 : boost::bind(&StateMachine::TimerErrorHanlder, this, _1, _2));
1276 : }
1277 :
1278 17268 : void StateMachine::CancelHoldTimer() {
1279 17268 : hold_timer_->Cancel();
1280 17268 : }
1281 :
1282 0 : bool StateMachine::HoldTimerRunning() {
1283 0 : return hold_timer_->running();
1284 : }
1285 :
1286 : // Test Only APIs : Start
1287 :
1288 191 : void StateMachine::FireConnectTimer() {
1289 191 : connect_timer_->Fire();
1290 191 : }
1291 :
1292 39 : void StateMachine::FireOpenTimer() {
1293 39 : open_timer_->Fire();
1294 39 : }
1295 :
1296 11 : void StateMachine::FireHoldTimer() {
1297 11 : hold_timer_->Fire();
1298 11 : }
1299 :
1300 205 : void StateMachine::FireIdleHoldTimer() {
1301 205 : idle_hold_timer_->Fire();
1302 205 : }
1303 :
1304 : // Test Only APIs : END
1305 :
1306 : //
1307 : // Create an active session.
1308 : //
1309 6995 : void StateMachine::StartSession() {
1310 6995 : BgpSession *session = peer_->CreateSession();
1311 6994 : if (!session)
1312 192 : return;
1313 6802 : set_active_session(session);
1314 6803 : session->set_observer(
1315 : boost::bind(&StateMachine::OnSessionEvent, this, _1, _2));
1316 13604 : peer_->server()->session_manager()->Connect(session,
1317 6802 : 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 94326 : void StateMachine::DeleteSession(BgpSession *session) {
1329 94326 : if (!session)
1330 83274 : return;
1331 11052 : session->set_observer(NULL);
1332 11053 : session->Close();
1333 11054 : 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 5397 : void StateMachine::AssignSession(bool active) {
1342 5397 : if (active) {
1343 2704 : peer_->set_session(active_session_);
1344 2704 : active_session_ = NULL;
1345 : } else {
1346 2693 : peer_->set_session(passive_session_);
1347 2693 : passive_session_ = NULL;
1348 : }
1349 5397 : }
1350 :
1351 37120 : void StateMachine::set_active_session(BgpSession *session) {
1352 37120 : DeleteSession(active_session_);
1353 37121 : active_session_ = session;
1354 37121 : }
1355 :
1356 30458 : void StateMachine::set_passive_session(BgpSession *session) {
1357 30458 : DeleteSession(passive_session_);
1358 30458 : passive_session_ = session;
1359 30458 : }
1360 :
1361 42357 : BgpSession *StateMachine::active_session() {
1362 42357 : return active_session_;
1363 : }
1364 :
1365 43377 : BgpSession *StateMachine::passive_session() {
1366 43377 : return passive_session_;
1367 : }
1368 :
1369 13766 : 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 13766 : if (!session)
1373 10242 : session = passive_session_;
1374 13766 : if (!session)
1375 10201 : session = active_session_;
1376 13766 : if (session && code != 0)
1377 3586 : peer_->SendNotification(session, code, subcode, data);
1378 13766 : }
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 2427 : 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 2427 : if (session && peer_->session() && peer_->session() != session) {
1393 0 : return false;
1394 : }
1395 :
1396 : // TransitToIdle<EvBgpNotification>::reaction,
1397 2427 : if (active_session() == session) {
1398 2416 : set_active_session(NULL);
1399 : } else {
1400 11 : set_passive_session(NULL);
1401 : }
1402 :
1403 2427 : return true;
1404 : }
1405 :
1406 7460 : bool StateMachine::ConnectTimerExpired() {
1407 7460 : Enqueue(fsm::EvConnectTimerExpired(connect_timer_));
1408 7461 : return false;
1409 : }
1410 :
1411 36 : bool StateMachine::OpenTimerExpired() {
1412 36 : Enqueue(fsm::EvOpenTimerExpired(open_timer_));
1413 36 : return false;
1414 : }
1415 :
1416 74 : bool StateMachine::HoldTimerExpired() {
1417 74 : boost::system::error_code error;
1418 :
1419 : // Reset hold timer if there is data already present in the socket.
1420 138 : if (peer() && peer()->session() && peer()->session()->socket() &&
1421 64 : peer()->session()->socket()->available(error) > 0) {
1422 0 : return true;
1423 : }
1424 74 : Enqueue(fsm::EvHoldTimerExpired(hold_timer_));
1425 74 : peer_->inc_hold_timer_expired();
1426 74 : return false;
1427 : }
1428 :
1429 4522 : bool StateMachine::IdleHoldTimerExpired() {
1430 4522 : Enqueue(fsm::EvIdleHoldTimerExpired(idle_hold_timer_));
1431 4523 : return false;
1432 : }
1433 :
1434 147 : bool StateMachine::IsCloseGraceful() const {
1435 147 : return peer_->IsCloseGraceful();
1436 : }
1437 :
1438 7 : bool StateMachine::IsRouterTypeBGPaaS() const {
1439 7 : return peer_->IsRouterTypeBGPaaS();
1440 : }
1441 :
1442 : //
1443 : // Concurrency: ASIO thread.
1444 : // Feed TCP session events into the state machine.
1445 : //
1446 10746 : void StateMachine::OnSessionEvent(
1447 : TcpSession *session, TcpSession::Event event) {
1448 10746 : BgpSession *bgp_session = static_cast<BgpSession *>(session);
1449 10746 : switch (event) {
1450 6537 : case TcpSession::CONNECT_COMPLETE:
1451 6537 : Enqueue(fsm::EvTcpConnected(bgp_session));
1452 6537 : break;
1453 129 : case TcpSession::CONNECT_FAILED:
1454 129 : Enqueue(fsm::EvTcpConnectFail(bgp_session));
1455 129 : peer_->inc_connect_error();
1456 129 : break;
1457 3803 : case TcpSession::CLOSE:
1458 3803 : Enqueue(fsm::EvTcpClose(bgp_session));
1459 3803 : break;
1460 277 : default:
1461 277 : break;
1462 : }
1463 10746 : }
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 4251 : bool StateMachine::PassiveOpen(BgpSession *session) {
1471 4251 : CHECK_CONCURRENCY("bgp::Config");
1472 4251 : Enqueue(fsm::EvTcpPassiveOpen(session));
1473 4251 : session->set_observer(boost::bind(&StateMachine::OnSessionEvent,
1474 : this, _1, _2));
1475 4251 : session->AsyncReadStart();
1476 4251 : return true;
1477 : }
1478 :
1479 4708 : void StateMachine::OnNotificationMessage(BgpSession *session,
1480 : BgpProto::BgpMessage *msg) {
1481 4708 : BgpPeer *peer = session->peer();
1482 4708 : if (peer)
1483 4708 : peer->inc_rx_notification();
1484 4708 : Enqueue(fsm::EvBgpNotification(session,
1485 : static_cast<BgpProto::Notification *>(msg)));
1486 4709 : }
1487 :
1488 : //
1489 : // Handle incoming message on the session.
1490 : //
1491 176257 : void StateMachine::OnMessage(BgpSession *session, BgpProto::BgpMessage *msg,
1492 : size_t msgsize) {
1493 176257 : switch (msg->type) {
1494 6247 : case BgpProto::OPEN: {
1495 6247 : BgpProto::OpenMessage *open_msg =
1496 : static_cast<BgpProto::OpenMessage *>(msg);
1497 6247 : BgpPeer *peer = session->peer();
1498 6247 : peer->inc_rx_open();
1499 6247 : if (int subcode = open_msg->Validate(peer)) {
1500 106 : Enqueue(fsm::EvBgpOpenError(session, subcode));
1501 106 : peer->inc_open_error();
1502 : } else {
1503 6138 : Enqueue(fsm::EvBgpOpen(session, open_msg));
1504 6141 : msg = NULL;
1505 : }
1506 6247 : break;
1507 : }
1508 6824 : case BgpProto::KEEPALIVE: {
1509 6824 : BgpPeer *peer = session->peer();
1510 6824 : Enqueue(fsm::EvBgpKeepalive(session));
1511 6825 : if (peer) peer->inc_rx_keepalive();
1512 6824 : break;
1513 : }
1514 4709 : case BgpProto::NOTIFICATION: {
1515 4709 : OnNotificationMessage(session, msg);
1516 4709 : msg = NULL;
1517 4709 : break;
1518 : }
1519 158479 : case BgpProto::UPDATE: {
1520 158479 : BgpProto::Update *update = static_cast<BgpProto::Update *>(msg);
1521 158479 : BgpPeer *peer = NULL;
1522 158479 : if (session)
1523 158480 : peer = session->peer();
1524 158478 : if (peer)
1525 158478 : peer->inc_rx_update();
1526 :
1527 158477 : std::string data;
1528 : int subcode;
1529 158477 : if (peer && (subcode = update->Validate(peer, &data))) {
1530 0 : Enqueue(fsm::EvBgpUpdateError(session, subcode, data));
1531 0 : peer->inc_update_error();
1532 : } else {
1533 158478 : Enqueue(fsm::EvBgpUpdate(session, update, msgsize));
1534 158483 : msg = NULL;
1535 : }
1536 158483 : break;
1537 158483 : }
1538 0 : default:
1539 0 : SM_LOG_NOTICE("Unknown message type " << msg->type);
1540 0 : break;
1541 : }
1542 :
1543 176263 : delete msg;
1544 176263 : }
1545 :
1546 : //
1547 : // Handle errors in incoming message on the session.
1548 : //
1549 16 : void StateMachine::OnMessageError(BgpSession *session,
1550 : const ParseErrorContext *context) {
1551 16 : switch (context->error_code) {
1552 5 : case BgpProto::Notification::MsgHdrErr: {
1553 5 : Enqueue(fsm::EvBgpHeaderError(session, context->error_subcode,
1554 5 : context->data, context->data_size));
1555 5 : break;
1556 : }
1557 6 : case BgpProto::Notification::OpenMsgErr:
1558 6 : Enqueue(fsm::EvBgpOpenError(session, context->error_subcode,
1559 6 : context->data, context->data_size));
1560 6 : break;
1561 5 : case BgpProto::Notification::UpdateMsgErr:
1562 5 : Enqueue(fsm::EvBgpUpdateError(session, context->error_subcode,
1563 10 : std::string((const char *)context->data, context->data_size)));
1564 5 : break;
1565 0 : default:
1566 0 : break;
1567 : }
1568 16 : }
1569 :
1570 : static const std::string state_names[] = {
1571 : "Idle",
1572 : "Active",
1573 : "Connect",
1574 : "OpenSent",
1575 : "OpenConfirm",
1576 : "Established"
1577 : };
1578 :
1579 535925 : const string &StateMachine::StateName() const {
1580 535925 : return state_names[state_];
1581 : }
1582 :
1583 54304 : const string &StateMachine::LastStateName() const {
1584 54304 : return state_names[last_state_];
1585 : }
1586 :
1587 56 : const std::string StateMachine::last_state_change_at() const {
1588 112 : 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 1216 : ostream &operator<<(ostream &out, const StateMachine::State &state) {
1596 1216 : out << state_names[state];
1597 1216 : 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 200542 : EvValidate operator()(const Ev *event) {
1612 200542 : return NULL;
1613 : }
1614 : };
1615 :
1616 : template <typename Ev>
1617 : struct ValidateFn<Ev, true> {
1618 40199 : EvValidate operator()(const Ev *event) {
1619 40199 : return boost::bind(&Ev::validate, event, _1);
1620 : }
1621 : };
1622 :
1623 : template <typename Ev>
1624 240760 : bool StateMachine::Enqueue(const Ev &event) {
1625 240760 : LogEvent(TYPE_NAME(event), "Enqueue");
1626 240793 : EventContainer ec;
1627 240783 : ec.event = event.intrusive_from_this();
1628 240743 : ec.validate = ValidateFn<Ev, HasValidate<Ev>::Has>()(
1629 240746 : static_cast<const Ev *>(ec.event.get()));
1630 240682 : work_queue_.Enqueue(ec);
1631 :
1632 240794 : return true;
1633 240793 : }
1634 :
1635 481562 : void StateMachine::LogEvent(string event_name, string msg,
1636 : SandeshLevel::type log_level) {
1637 : // Reduce log level for keepalive and update messages.
1638 1154471 : if (get_state() == ESTABLISHED &&
1639 672910 : (event_name == "fsm::EvBgpKeepalive" ||
1640 334964 : event_name == "fsm::EvBgpUpdate")) {
1641 319843 : log_level = Sandesh::LoggingUtLevel();
1642 : }
1643 481558 : SM_LOG(log_level, msg << " " << event_name << " in state " << StateName());
1644 481566 : }
1645 :
1646 240712 : bool StateMachine::DequeueEvent(StateMachine::EventContainer ec) {
1647 : const fsm::EvTcpDeleteSession *deferred_delete =
1648 240712 : dynamic_cast<const fsm::EvTcpDeleteSession *>(ec.event.get());
1649 240704 : if (deferred_delete != NULL) {
1650 11053 : LogEvent(TYPE_NAME(*ec.event), "Dequeue");
1651 11053 : peer_->server()->session_manager()->DeleteSession(
1652 11053 : deferred_delete->session);
1653 11054 : return true;
1654 : }
1655 :
1656 229651 : set_last_event(TYPE_NAME(*ec.event));
1657 229717 : if (ec.validate.empty() || ec.validate(this)) {
1658 226035 : LogEvent(TYPE_NAME(*ec.event), "Dequeue");
1659 226047 : process_event(*ec.event);
1660 : } else {
1661 3691 : LogEvent(TYPE_NAME(*ec.event), "Discard", SandeshLevel::SYS_INFO);
1662 : }
1663 229731 : ec.event.reset();
1664 :
1665 229739 : return true;
1666 : }
1667 :
1668 9562 : void StateMachine::DequeueEventDone(bool done) {
1669 9562 : peer_->RetryDelete();
1670 9567 : }
1671 :
1672 32356 : void StateMachine::SetDataCollectionKey(BgpPeerInfo *peer_info) const {
1673 32356 : peer_->SetDataCollectionKey(peer_info);
1674 32355 : }
1675 :
1676 56 : const std::string StateMachine::last_notification_in_error() const {
1677 : return (BgpProto::Notification::toString(
1678 56 : static_cast<BgpProto::Notification::Code>(last_notification_in_.first),
1679 56 : 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 40822 : 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 40822 : if (!env_checked) {
1697 65 : char *keepalive_time_str = getenv("BGP_KEEPALIVE_SECONDS");
1698 65 : 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 65 : env_checked = true;
1704 : }
1705 40758 : } else if (env_hold_time) {
1706 0 : return env_hold_time;
1707 : }
1708 :
1709 : // Use the configured hold-time from peer if available.
1710 40823 : if (peer_ && peer_->hold_time())
1711 4987 : return peer_->hold_time();
1712 :
1713 : // Use the configured hold-time from server if available.
1714 35836 : if (peer_ && peer_->server()->hold_time())
1715 2 : return peer_->server()->hold_time();
1716 :
1717 : // Use hard coded default.
1718 35834 : return kHoldTime;
1719 : }
1720 :
1721 158772 : void StateMachine::BGPPeerInfoSend(const BgpPeerInfoData &peer_info) {
1722 158772 : assert(!peer_info.get_name().empty());
1723 158773 : BGP_UVE_SEND(BGPPeerInfo, peer_info);
1724 158803 : }
1725 :
1726 229701 : void StateMachine::set_last_event(const std::string &event) {
1727 229701 : last_event_ = event;
1728 229697 : last_event_at_ = UTCTimestampUsec();
1729 :
1730 : // Skip keepalive and update events after we've reached established state.
1731 559010 : if (state_ == ESTABLISHED &&
1732 329276 : (event == "fsm::EvBgpKeepalive" || event == "fsm::EvBgpUpdate")) {
1733 159885 : return;
1734 : }
1735 :
1736 139654 : BgpPeerInfoData peer_info;
1737 69802 : peer_info.set_name(peer()->ToUVEKey());
1738 69810 : PeerEventInfo event_info;
1739 69788 : event_info.set_last_event(last_event_);
1740 69789 : event_info.set_last_event_at(last_event_at_);
1741 69786 : peer_info.set_event_info(event_info);
1742 69825 : BGPPeerInfoSend(peer_info);
1743 69840 : }
1744 :
1745 5023 : void StateMachine::set_last_notification_out(int code, int subcode,
1746 : const string &reason) {
1747 5023 : last_notification_out_ = std::make_pair(code, subcode);
1748 5023 : last_notification_out_at_ = UTCTimestampUsec();
1749 5023 : last_notification_out_error_ = reason;
1750 :
1751 10045 : BgpPeerInfoData peer_info;
1752 5022 : peer_info.set_name(peer()->ToUVEKey());
1753 5022 : peer_info.set_notification_out_at(last_notification_out_at_);
1754 5022 : peer_info.set_notification_out(BgpProto::Notification::toString(
1755 : static_cast<BgpProto::Notification::Code>(code), subcode));
1756 5023 : BGPPeerInfoSend(peer_info);
1757 5023 : }
1758 :
1759 1989 : void StateMachine::set_last_notification_in(int code, int subcode,
1760 : const string &reason) {
1761 1989 : last_notification_in_ = std::make_pair(code, subcode);
1762 1989 : last_notification_in_at_ = UTCTimestampUsec();
1763 1989 : last_notification_in_error_ = reason;
1764 :
1765 3978 : BgpPeerInfoData peer_info;
1766 1989 : peer_info.set_name(peer()->ToUVEKey());
1767 1989 : peer_info.set_notification_in_at(last_notification_in_at_);
1768 1989 : peer_info.set_notification_in(BgpProto::Notification::toString(
1769 : static_cast<BgpProto::Notification::Code>(code), subcode));
1770 1989 : BGPPeerInfoSend(peer_info);
1771 1989 : }
1772 :
1773 65708 : void StateMachine::set_state(State state) {
1774 65708 : if (state == state_)
1775 11854 : return;
1776 53854 : last_state_ = state_; state_ = state;
1777 53854 : last_state_change_at_ = UTCTimestampUsec();
1778 :
1779 107702 : BgpPeerInfoData peer_info;
1780 53849 : peer_info.set_name(peer()->ToUVEKey());
1781 53849 : PeerStateInfo state_info;
1782 53840 : state_info.set_state(StateName());
1783 53844 : state_info.set_last_state(LastStateName());
1784 53845 : state_info.set_last_state_at(last_state_change_at_);
1785 53844 : peer_info.set_state_info(state_info);
1786 53847 : BGPPeerInfoSend(peer_info);
1787 53853 : }
1788 :
1789 11942 : void StateMachine::set_hold_time(int hold_time) {
1790 11942 : hold_time_ = hold_time;
1791 :
1792 23883 : BgpPeerInfoData peer_info;
1793 11941 : peer_info.set_name(peer()->ToUVEKey());
1794 11940 : peer_info.set_hold_time(hold_time_);
1795 11940 : BGPPeerInfoSend(peer_info);
1796 11940 : }
1797 :
1798 15758 : void StateMachine::reset_hold_time() {
1799 15758 : hold_time_ = GetConfiguredHoldTime();
1800 :
1801 31515 : BgpPeerInfoData peer_info;
1802 15757 : peer_info.set_name(peer()->ToUVEKey());
1803 15757 : peer_info.set_hold_time(hold_time_);
1804 15756 : BGPPeerInfoSend(peer_info);
1805 15758 : }
1806 :
1807 404 : void StateMachine::reset_last_info() {
1808 404 : last_notification_in_ = std::make_pair(0, 0);
1809 404 : last_notification_in_at_ = 0;
1810 404 : last_notification_in_error_ = std::string();
1811 404 : last_notification_out_ = std::make_pair(0, 0);
1812 404 : last_notification_out_at_ = 0;
1813 404 : last_notification_out_error_ = std::string();
1814 404 : last_state_ = IDLE;
1815 404 : last_event_ = "";
1816 404 : last_state_change_at_ = 0;
1817 404 : last_event_at_ = 0;
1818 :
1819 808 : BgpPeerInfoData peer_info;
1820 404 : peer_info.set_name(peer()->ToUVEKey());
1821 404 : PeerStateInfo state_info;
1822 404 : state_info.set_state(StateName());
1823 404 : state_info.set_last_state(LastStateName());
1824 404 : state_info.set_last_state_at(last_state_change_at_);
1825 404 : peer_info.set_state_info(state_info);
1826 :
1827 404 : PeerEventInfo event_info;
1828 404 : event_info.set_last_event(last_event_);
1829 404 : event_info.set_last_event_at(last_event_at_);
1830 404 : peer_info.set_event_info(event_info);
1831 404 : BGPPeerInfoSend(peer_info);
1832 404 : }
|