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 15531 : EvStart() {
76 15531 : }
77 : static const char *Name() {
78 : return "EvStart";
79 : }
80 : };
81 :
82 : struct EvStop : sc::event<EvStop> {
83 11145 : explicit EvStop(int subcode) : subcode(subcode) {
84 11145 : }
85 : static const char *Name() {
86 : return "EvStop";
87 : }
88 : int subcode;
89 : };
90 :
91 : struct EvIdleHoldTimerExpired : sc::event<EvIdleHoldTimerExpired> {
92 4538 : explicit EvIdleHoldTimerExpired(Timer *timer) : timer_(timer) {
93 4537 : }
94 : static const char *Name() {
95 : return "EvIdleHoldTimerExpired";
96 : }
97 4538 : bool validate(StateMachine *state_machine) const {
98 4538 : return !timer_->cancelled();
99 : }
100 :
101 : Timer *timer_;
102 : };
103 :
104 : struct EvConnectTimerExpired : sc::event<EvConnectTimerExpired> {
105 7506 : explicit EvConnectTimerExpired(Timer *timer) : timer_(timer) {
106 7491 : }
107 : static const char *Name() {
108 : return "EvConnectTimerExpired";
109 : }
110 7508 : bool validate(StateMachine *state_machine) const {
111 7508 : if (timer_->cancelled()) {
112 202 : return false;
113 7306 : } else if (state_machine->get_state() == StateMachine::ACTIVE) {
114 7016 : 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 6557 : explicit EvTcpConnected(BgpSession *session) : session(session) {
163 6557 : }
164 : static const char *Name() {
165 : return "EvTcpConnected";
166 : }
167 6556 : bool validate(StateMachine *state_machine) const {
168 6556 : return (state_machine->active_session() == session);
169 : }
170 :
171 : BgpSession *session;
172 : };
173 :
174 : struct EvTcpConnectFail : sc::event<EvTcpConnectFail> {
175 131 : explicit EvTcpConnectFail(BgpSession *session) : session(session) {
176 131 : }
177 : static const char *Name() {
178 : return "EvTcpConnectFail";
179 : }
180 131 : bool validate(StateMachine *state_machine) const {
181 131 : return (state_machine->active_session() == session);
182 : }
183 :
184 : BgpSession *session;
185 : };
186 :
187 : struct EvTcpPassiveOpen : sc::event<EvTcpPassiveOpen> {
188 4265 : explicit EvTcpPassiveOpen(BgpSession *session) : session(session) {
189 4265 : }
190 : static const char *Name() {
191 : return "EvTcpPassiveOpen";
192 : }
193 :
194 : BgpSession *session;
195 : };
196 :
197 : struct EvTcpClose : sc::event<EvTcpClose> {
198 3901 : explicit EvTcpClose(BgpSession *session) : session(session) {
199 3901 : }
200 : static const char *Name() {
201 : return "EvTcpClose";
202 : }
203 3900 : bool validate(StateMachine *state_machine) const {
204 3900 : return ((state_machine->peer()->session() == session) ||
205 7141 : (state_machine->active_session() == session) ||
206 7141 : (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 11089 : explicit EvTcpDeleteSession(BgpSession *session) : session(session) {
215 11089 : }
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 6145 : EvBgpOpen(BgpSession *session, const BgpProto::OpenMessage *msg)
241 6145 : : session(session), msg(msg) {
242 6145 : BGP_LOG_PEER(Message, session->peer(), SandeshLevel::SYS_INFO,
243 : BGP_LOG_FLAG_SYSLOG, BGP_PEER_DIR_IN,
244 : "Open " << msg->ToString());
245 6145 : }
246 : static const char *Name() {
247 : return "EvBgpOpen";
248 : }
249 6145 : bool validate(StateMachine *state_machine) const {
250 6145 : return ((state_machine->peer()->session() == session) ||
251 9575 : (state_machine->active_session() == session) ||
252 9575 : (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 114 : EvBgpOpenError(BgpSession *session, int subcode,
261 : const uint8_t *_data = NULL, size_t data_size = 0)
262 114 : : session(session), subcode(subcode) {
263 114 : 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 114 : } else if (_data) {
269 0 : data = std::string((const char *)_data, data_size);
270 : }
271 114 : }
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 6832 : explicit EvBgpKeepalive(BgpSession *session) : session(session) {
283 6832 : const StateMachine *state_machine = session->peer()->state_machine();
284 : SandeshLevel::type log_level;
285 6831 : if (state_machine->get_state() == StateMachine::ESTABLISHED) {
286 1491 : log_level = Sandesh::LoggingUtLevel();
287 : } else {
288 5340 : log_level = SandeshLevel::SYS_INFO;
289 : }
290 6831 : BGP_LOG_PEER(Message, session->peer(), log_level,
291 : BGP_LOG_FLAG_SYSLOG, BGP_PEER_DIR_IN, "Keepalive");
292 6831 : }
293 : static const char *Name() {
294 : return "EvBgpKeepalive";
295 : }
296 6831 : bool validate(StateMachine *state_machine) const {
297 6831 : return !session->IsClosed();
298 : }
299 :
300 : BgpSession *session;
301 : };
302 :
303 : struct EvBgpNotification : sc::event<EvBgpNotification> {
304 4743 : EvBgpNotification(BgpSession *session, const BgpProto::Notification *msg)
305 4743 : : session(session), msg(msg) {
306 : string peer_key =
307 4743 : session->peer() ? session->peer()->ToUVEKey() : session->ToString();
308 4745 : session->LogNotification(msg->error, msg->subcode, BGP_PEER_DIR_IN,
309 : peer_key, *msg);
310 4745 : }
311 1992 : static const char *Name() {
312 1992 : return "EvBgpNotification";
313 : }
314 4745 : bool validate(StateMachine *state_machine) const {
315 4745 : return ((state_machine->peer()->session() == session) ||
316 5058 : (state_machine->active_session() == session) ||
317 5058 : (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 158250 : EvBgpUpdate(BgpSession *session, const BgpProto::Update *msg,
326 158250 : size_t msgsize) : session(session), msg(msg), msgsize(msgsize) {
327 158249 : }
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 26137 : explicit Idle(my_context ctx) : my_base(ctx) {
407 26136 : StateMachine *state_machine = &context<StateMachine>();
408 26137 : BgpPeer *peer = state_machine->peer();
409 26137 : BgpSession *session = peer->session();
410 26137 : peer->clear_session();
411 26138 : state_machine->set_active_session(NULL);
412 26138 : state_machine->set_passive_session(NULL);
413 26138 : state_machine->DeleteSession(session);
414 26138 : state_machine->CancelOpenTimer();
415 26138 : state_machine->CancelIdleHoldTimer();
416 26138 : state_machine->set_state(StateMachine::IDLE);
417 26138 : }
418 :
419 52264 : ~Idle() {
420 26132 : StateMachine *state_machine = &context<StateMachine>();
421 26136 : state_machine->CancelIdleHoldTimer();
422 52264 : }
423 :
424 : // Start idle hold timer if it's enabled, else go to Active right away.
425 15399 : sc::result react(const EvStart &event) {
426 15399 : StateMachine *state_machine = &context<StateMachine>();
427 15400 : if (state_machine->idle_hold_time()) {
428 5638 : state_machine->StartIdleHoldTimer();
429 : } else {
430 9761 : return transit<Active>();
431 : }
432 5643 : return discard_event();
433 : }
434 :
435 : // The idle hold timer expired, go to Active.
436 4520 : sc::result react(const EvIdleHoldTimerExpired &event) {
437 4520 : return transit<Active>();
438 : }
439 :
440 : // Delete the session and ignore event.
441 505 : sc::result react(const EvTcpPassiveOpen &event) {
442 505 : StateMachine *state_machine = &context<StateMachine>();
443 505 : BgpSession *session = event.session;
444 505 : state_machine->DeleteSession(session);
445 505 : 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 15363 : explicit Active(my_context ctx) : my_base(ctx) {
477 15363 : StateMachine *state_machine = &context<StateMachine>();
478 15366 : BgpPeer *peer = state_machine->peer();
479 15366 : if (!peer->IsPassive() && !state_machine->passive_session())
480 15007 : state_machine->StartConnectTimer(state_machine->GetConnectTime());
481 15371 : state_machine->set_state(StateMachine::ACTIVE);
482 15371 : }
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 30735 : ~Active() {
487 15369 : StateMachine *state_machine = &context<StateMachine>();
488 15370 : state_machine->CancelConnectTimer();
489 30736 : }
490 :
491 : // The connect timer expired, go to Connect if the peer is not passive.
492 7016 : sc::result react(const EvConnectTimerExpired &event) {
493 7016 : StateMachine *state_machine = &context<StateMachine>();
494 7017 : BgpPeer *peer = state_machine->peer();
495 7016 : if (peer->IsPassive()) {
496 1 : return discard_event();
497 : } else {
498 7015 : 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 2065 : sc::result react(const EvTcpPassiveOpen &event) {
519 2065 : StateMachine *state_machine = &context<StateMachine>();
520 2065 : state_machine->set_passive_session(event.session);
521 2065 : state_machine->CancelConnectTimer();
522 2065 : state_machine->StartOpenTimer(StateMachine::kOpenTime);
523 2065 : 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 1975 : sc::result react(const EvBgpOpen &event) {
540 1975 : StateMachine *state_machine = &context<StateMachine>();
541 1975 : BgpPeer *peer = state_machine->peer();
542 1975 : 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 1975 : 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 1975 : if (session != event.session)
555 0 : return discard_event();
556 :
557 : // Send OPEN and go to OpenConfirm.
558 1975 : int local_holdtime = state_machine->GetConfiguredHoldTime();
559 1975 : state_machine->set_hold_time(min(event.msg->holdtime, local_holdtime));
560 1975 : state_machine->AssignSession(false);
561 1975 : peer->SendOpen(session);
562 1975 : if (!peer->SetCapabilities(event.msg.get()))
563 0 : return discard_event();
564 1975 : 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 7014 : explicit Connect(my_context ctx) : my_base(ctx) {
594 7014 : StateMachine *state_machine = &context<StateMachine>();
595 7016 : state_machine->connect_attempts_inc();
596 7016 : state_machine->StartConnectTimer(state_machine->GetConnectTime());
597 7016 : state_machine->StartSession();
598 7014 : state_machine->set_state(StateMachine::CONNECT);
599 7016 : }
600 :
601 14032 : ~Connect() {
602 7016 : StateMachine *state_machine = &context<StateMachine>();
603 7016 : state_machine->CancelConnectTimer();
604 14032 : }
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 6548 : sc::result react(const EvTcpConnected &event) {
631 6548 : StateMachine *state_machine = &context<StateMachine>();
632 6548 : BgpPeer *peer = state_machine->peer();
633 6548 : BgpSession *session = state_machine->active_session();
634 6548 : peer->SendOpen(session);
635 6548 : 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 128 : sc::result react(const EvTcpConnectFail &event) {
641 128 : StateMachine *state_machine = &context<StateMachine>();
642 128 : state_machine->set_active_session(NULL);
643 128 : 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 315 : sc::result react(const EvTcpPassiveOpen &event) {
650 315 : StateMachine *state_machine = &context<StateMachine>();
651 315 : state_machine->set_passive_session(event.session);
652 315 : state_machine->StartOpenTimer(StateMachine::kOpenTime);
653 315 : 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 30 : sc::result react(const EvBgpOpen &event) {
677 30 : StateMachine *state_machine = &context<StateMachine>();
678 30 : BgpPeer *peer = state_machine->peer();
679 30 : 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 30 : 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 30 : 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 30 : int local_holdtime = state_machine->GetConfiguredHoldTime();
697 30 : state_machine->set_hold_time(min(event.msg->holdtime, local_holdtime));
698 30 : state_machine->set_active_session(NULL);
699 30 : state_machine->AssignSession(false);
700 30 : peer->SendOpen(session);
701 30 : if (!peer->SetCapabilities(event.msg.get()))
702 0 : return discard_event();
703 30 : 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 6564 : explicit OpenSent(my_context ctx) : my_base(ctx) {
737 6564 : StateMachine *state_machine = &context<StateMachine>();
738 6564 : state_machine->set_hold_time(StateMachine::kOpenSentHoldTime);
739 6562 : state_machine->StartHoldTimer();
740 6564 : state_machine->set_state(StateMachine::OPENSENT);
741 6564 : }
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 13124 : ~OpenSent() {
746 6562 : StateMachine *state_machine = &context<StateMachine>();
747 6563 : state_machine->CancelHoldTimer();
748 13126 : }
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 1221 : sc::result react(const EvTcpPassiveOpen &event) {
763 1221 : StateMachine *state_machine = &context<StateMachine>();
764 1221 : state_machine->set_passive_session(event.session);
765 1221 : 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 1221 : if (!state_machine->active_session()) {
772 1 : return transit<Active>();
773 : } else {
774 1220 : return discard_event();
775 : }
776 : }
777 :
778 : // Either the passive or the active session closed.
779 653 : sc::result react(const EvTcpClose &event) {
780 653 : StateMachine *state_machine = &context<StateMachine>();
781 653 : 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 647 : state_machine->set_active_session(NULL);
786 649 : if (state_machine->passive_session() == NULL ||
787 2 : state_machine->OpenTimerRunning()) {
788 646 : 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 6 : state_machine->set_passive_session(NULL);
794 6 : state_machine->CancelOpenTimer();
795 6 : if (state_machine->active_session() == NULL)
796 1 : return transit<Active>();
797 : }
798 :
799 6 : return discard_event();
800 : }
801 :
802 : // This one is pretty involved.
803 4119 : sc::result react(const EvBgpOpen &event) {
804 4119 : StateMachine *state_machine = &context<StateMachine>();
805 4119 : BgpPeer *peer = state_machine->peer();
806 4119 : BgpSession *session = NULL;
807 :
808 5543 : if (state_machine->passive_session() &&
809 1424 : state_machine->active_session()) {
810 : // Need to resolve connection collision.
811 1418 : uint32_t local_bgp_id = peer->server()->bgp_identifier();
812 1418 : if (event.msg->identifier > local_bgp_id) {
813 : // Passive connection wins, close the active session.
814 694 : peer->SendNotification(state_machine->active_session(),
815 : BgpProto::Notification::Cease,
816 : BgpProto::Notification::ConnectionCollision,
817 : "Connection collision - closing active session");
818 694 : 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 694 : session = state_machine->passive_session();
823 694 : if (state_machine->OpenTimerRunning()) {
824 690 : state_machine->CancelOpenTimer();
825 690 : 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 694 : if (event.session != session) {
835 7 : return discard_event();
836 : } else {
837 687 : 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 713 : return discard_event();
857 : } else {
858 11 : 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 2694 : session = state_machine->active_session();
882 2694 : if (event.session != session) {
883 0 : return discard_event();
884 : } else {
885 2694 : state_machine->AssignSession(true);
886 : }
887 : }
888 :
889 3398 : int local_holdtime = state_machine->GetConfiguredHoldTime();
890 3398 : state_machine->set_hold_time(min(event.msg->holdtime, local_holdtime));
891 3399 : if (!peer->SetCapabilities(event.msg.get()))
892 0 : return discard_event();
893 3398 : return transit<OpenConfirm>();
894 : }
895 :
896 : // Notification received on one of the sessions for this state machine.
897 2439 : sc::result react(const EvBgpNotification &event) {
898 2439 : StateMachine *state_machine = &context<StateMachine>();
899 :
900 : // Ignore if the NOTIFICATION came in on a stale session.
901 2439 : 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 2439 : 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 2435 : } 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 20 : if (state_machine->OpenTimerRunning()) {
920 19 : 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 2415 : &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 5403 : explicit OpenConfirm(my_context ctx) : my_base(ctx) {
958 5402 : StateMachine *state_machine = &context<StateMachine>();
959 5401 : BgpPeer *peer = state_machine->peer();
960 5401 : peer->SendKeepalive(false);
961 5404 : peer->StartKeepaliveTimer();
962 5404 : state_machine->CancelOpenTimer();
963 5404 : state_machine->StartHoldTimer();
964 5404 : state_machine->set_state(StateMachine::OPENCONFIRM);
965 5404 : }
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 10804 : ~OpenConfirm() {
970 5402 : StateMachine *state_machine = &context<StateMachine>();
971 5402 : state_machine->CancelHoldTimer();
972 10806 : }
973 :
974 : // Send a notification, delete the new session and stay in OpenConfirm.
975 6 : sc::result react(const EvTcpPassiveOpen &event) {
976 6 : StateMachine *state_machine = &context<StateMachine>();
977 6 : BgpPeer *peer = state_machine->peer();
978 6 : BgpSession *session = event.session;
979 6 : peer->SendNotification(session,
980 : BgpProto::Notification::Cease,
981 : BgpProto::Notification::ConnectionRejected,
982 : "Connection rejected - unexpected passive session");
983 6 : state_machine->DeleteSession(session);
984 6 : return discard_event();
985 : }
986 :
987 : // Ignore the notification if it's for a stale session, else go to Idle.
988 9 : sc::result react(const EvBgpNotification &event) {
989 9 : StateMachine *state_machine = &context<StateMachine>();
990 9 : if (!state_machine->ProcessNotificationEvent(event.session))
991 0 : return discard_event();
992 :
993 : return transit<Idle, StateMachine, EvBgpNotification>(
994 9 : &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 5331 : 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 5331 : StateMachine *state_machine = &context<StateMachine>();
1003 5331 : if (state_machine->IsPeerCloseInProgress()) {
1004 : return transit<Idle, StateMachine, EvBgpKeepalive>(
1005 2 : &StateMachine::OnIdle<EvBgpKeepalive, 0>, event);
1006 : }
1007 5328 : 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 5328 : explicit Established(my_context ctx) : my_base(ctx) {
1034 5327 : StateMachine *state_machine = &context<StateMachine>();
1035 5328 : BgpPeer *peer = state_machine->peer();
1036 5328 : state_machine->connect_attempts_clear();
1037 5328 : state_machine->StartHoldTimer();
1038 5330 : state_machine->set_state(StateMachine::ESTABLISHED);
1039 5330 : peer->NotifyEstablished(true);
1040 5330 : peer->RegisterAllTables();
1041 5330 : }
1042 :
1043 10660 : ~Established() {
1044 5330 : StateMachine *state_machine = &context<StateMachine>();
1045 5329 : BgpPeer *peer = state_machine->peer();
1046 5329 : peer->NotifyEstablished(false);
1047 5329 : state_machine->CancelHoldTimer();
1048 10659 : }
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 153 : sc::result react(const EvTcpPassiveOpen &event) {
1053 153 : StateMachine *state_machine = &context<StateMachine>();
1054 153 : BgpSession *session = event.session;
1055 153 : state_machine->DeleteSession(session);
1056 162 : if (state_machine->IsCloseGraceful() ||
1057 9 : state_machine->IsRouterTypeBGPaaS()) {
1058 146 : state_machine->Shutdown(BgpProto::Notification::Unknown);
1059 : }
1060 153 : 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 158168 : sc::result react(const EvBgpUpdate &event) {
1072 158168 : StateMachine *state_machine = &context<StateMachine>();
1073 158168 : state_machine->StartHoldTimer();
1074 158172 : state_machine->peer()->ProcessUpdate(event.msg.get(), event.msgsize);
1075 158169 : 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 15129 : void StateMachine::Initialize() {
1132 15129 : Enqueue(fsm::EvStart());
1133 15129 : }
1134 :
1135 5732 : bool StateMachine::IsPeerCloseInProgress() const {
1136 5732 : return peer_->IsCloseInProgress();
1137 : }
1138 :
1139 10671 : void StateMachine::Shutdown(int subcode) {
1140 10671 : if (peer_->IsDeleted()) {
1141 9567 : work_queue_.SetExitCallback(
1142 : boost::bind(&StateMachine::DequeueEventDone, this, _1));
1143 : }
1144 10671 : Enqueue(fsm::EvStop(subcode));
1145 10671 : }
1146 :
1147 877 : void StateMachine::SetAdminState(bool down, int subcode) {
1148 877 : if (down) {
1149 474 : Enqueue(fsm::EvStop(subcode));
1150 : } else {
1151 : // Reset all previous state.
1152 403 : reset_idle_hold_time();
1153 403 : reset_last_info();
1154 403 : peer_->reset_flap_count();
1155 403 : if (!IsPeerCloseInProgress())
1156 402 : Enqueue(fsm::EvStart());
1157 : }
1158 877 : }
1159 :
1160 17858 : bool StateMachine::IsQueueEmpty() const {
1161 17858 : return work_queue_.IsQueueEmpty();
1162 : }
1163 :
1164 15780 : void StateMachine::UpdateFlapCount() {
1165 15780 : if (get_state() == StateMachine::ESTABLISHED) {
1166 5330 : peer_->increment_flap_count();
1167 5328 : peer_->peer_stats()->Clear();
1168 : }
1169 15779 : }
1170 :
1171 15780 : void StateMachine::PeerClose(int code, int subcode) {
1172 15780 : UpdateFlapCount();
1173 15779 : peer_->Close(peer_->AttemptGRHelperMode(code, subcode));
1174 25374 : set_idle_hold_time(idle_hold_time() ? idle_hold_time() :
1175 9594 : GetIdleHoldTimeMSecs());
1176 15780 : reset_hold_time();
1177 15779 : }
1178 :
1179 : template <typename Ev, int code>
1180 2521 : void StateMachine::OnIdle(const Ev &event) {
1181 2521 : SendNotification(peer_->session(), code);
1182 2521 : PeerClose(code, 0);
1183 2521 : }
1184 :
1185 : template <typename Ev>
1186 11142 : void StateMachine::OnIdleCease(const Ev &event) {
1187 22284 : SendNotification(peer_->session(), BgpProto::Notification::Cease,
1188 11143 : event.subcode);
1189 11145 : PeerClose(BgpProto::Notification::Cease, event.subcode);
1190 11144 : }
1191 :
1192 : //
1193 : // The template below must only be called for EvBgpHeaderError, EvBgpOpenError
1194 : // or EvBgpUpdateError.
1195 : //
1196 : template <typename Ev, int code>
1197 122 : void StateMachine::OnIdleError(const Ev &event) {
1198 122 : SendNotification(event.session, code, event.subcode, event.data);
1199 122 : PeerClose(code, event.subcode);
1200 122 : }
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 1992 : void StateMachine::OnIdleNotification(const fsm::EvBgpNotification &event) {
1205 1992 : PeerClose(event.msg->error, event.msg->subcode);
1206 1992 : set_last_notification_in(event.msg->error, event.msg->subcode,
1207 : event.Name());
1208 1992 : }
1209 :
1210 22035 : int StateMachine::GetConnectTime() const {
1211 22035 : int backoff = min(attempts_, 6);
1212 22035 : return std::min(backoff ? 1 << (backoff - 1) : 0, kConnectInterval);
1213 : }
1214 :
1215 5301 : void StateMachine::StartConnectTimer(int seconds) {
1216 5301 : connect_timer_->Cancel();
1217 :
1218 : // Add up to +/- kJitter percentage to reduce connection collisions.
1219 5303 : int ms = seconds ? seconds * 1000 : 50;
1220 5303 : ms = (ms * (100 - kJitter)) / 100;
1221 5303 : ms += (ms * (rand_r(&seed_) % (kJitter * 2))) / 100;
1222 5303 : connect_timer_->Start(ms,
1223 : boost::bind(&StateMachine::ConnectTimerExpired, this),
1224 : boost::bind(&StateMachine::TimerErrorHanlder, this, _1, _2));
1225 5303 : }
1226 :
1227 24451 : void StateMachine::CancelConnectTimer() {
1228 24451 : connect_timer_->Cancel();
1229 24451 : }
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 32969 : void StateMachine::CancelOpenTimer() {
1243 32969 : open_timer_->Cancel();
1244 32969 : }
1245 :
1246 716 : bool StateMachine::OpenTimerRunning() {
1247 716 : return open_timer_->running();
1248 : }
1249 :
1250 72 : void StateMachine::StartIdleHoldTimer() {
1251 72 : if (idle_hold_time_ <= 0)
1252 0 : return;
1253 :
1254 72 : idle_hold_timer_->Cancel();
1255 72 : idle_hold_timer_->Start(idle_hold_time_,
1256 : boost::bind(&StateMachine::IdleHoldTimerExpired, this),
1257 : boost::bind(&StateMachine::TimerErrorHanlder, this, _1, _2));
1258 : }
1259 :
1260 52273 : void StateMachine::CancelIdleHoldTimer() {
1261 52273 : idle_hold_timer_->Cancel();
1262 52272 : }
1263 :
1264 0 : bool StateMachine::IdleHoldTimerRunning() {
1265 0 : return idle_hold_timer_->running();
1266 : }
1267 :
1268 176410 : void StateMachine::StartHoldTimer() {
1269 176410 : if (hold_time_ <= 0)
1270 0 : return;
1271 :
1272 176410 : hold_timer_->Cancel();
1273 176415 : hold_timer_->Start(hold_time_ * 1000,
1274 : boost::bind(&StateMachine::HoldTimerExpired, this),
1275 : boost::bind(&StateMachine::TimerErrorHanlder, this, _1, _2));
1276 : }
1277 :
1278 17294 : void StateMachine::CancelHoldTimer() {
1279 17294 : hold_timer_->Cancel();
1280 17297 : }
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 7016 : void StateMachine::StartSession() {
1310 7016 : BgpSession *session = peer_->CreateSession();
1311 7016 : if (!session)
1312 192 : return;
1313 6824 : set_active_session(session);
1314 6824 : session->set_observer(
1315 : boost::bind(&StateMachine::OnSessionEvent, this, _1, _2));
1316 13648 : peer_->server()->session_manager()->Connect(session,
1317 6824 : 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 94456 : void StateMachine::DeleteSession(BgpSession *session) {
1329 94456 : if (!session)
1330 83367 : return;
1331 11089 : session->set_observer(NULL);
1332 11089 : session->Close();
1333 11089 : 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 5403 : void StateMachine::AssignSession(bool active) {
1342 5403 : if (active) {
1343 2705 : peer_->set_session(active_session_);
1344 2705 : active_session_ = NULL;
1345 : } else {
1346 2698 : peer_->set_session(passive_session_);
1347 2698 : passive_session_ = NULL;
1348 : }
1349 5403 : }
1350 :
1351 37186 : void StateMachine::set_active_session(BgpSession *session) {
1352 37186 : DeleteSession(active_session_);
1353 37186 : active_session_ = session;
1354 37186 : }
1355 :
1356 30486 : void StateMachine::set_passive_session(BgpSession *session) {
1357 30486 : DeleteSession(passive_session_);
1358 30486 : passive_session_ = session;
1359 30486 : }
1360 :
1361 42516 : BgpSession *StateMachine::active_session() {
1362 42516 : return active_session_;
1363 : }
1364 :
1365 43576 : BgpSession *StateMachine::passive_session() {
1366 43576 : return passive_session_;
1367 : }
1368 :
1369 13786 : 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 13786 : if (!session)
1373 10256 : session = passive_session_;
1374 13786 : if (!session)
1375 10213 : session = active_session_;
1376 13786 : if (session && code != 0)
1377 3587 : peer_->SendNotification(session, code, subcode, data);
1378 13788 : }
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 2448 : 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 2448 : if (session && peer_->session() && peer_->session() != session) {
1393 0 : return false;
1394 : }
1395 :
1396 : // TransitToIdle<EvBgpNotification>::reaction,
1397 2448 : if (active_session() == session) {
1398 2434 : set_active_session(NULL);
1399 : } else {
1400 14 : set_passive_session(NULL);
1401 : }
1402 :
1403 2448 : return true;
1404 : }
1405 :
1406 7506 : bool StateMachine::ConnectTimerExpired() {
1407 7506 : Enqueue(fsm::EvConnectTimerExpired(connect_timer_));
1408 7509 : 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 4538 : bool StateMachine::IdleHoldTimerExpired() {
1430 4538 : Enqueue(fsm::EvIdleHoldTimerExpired(idle_hold_timer_));
1431 4540 : return false;
1432 : }
1433 :
1434 151 : bool StateMachine::IsCloseGraceful() const {
1435 151 : 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 10799 : void StateMachine::OnSessionEvent(
1447 : TcpSession *session, TcpSession::Event event) {
1448 10799 : BgpSession *bgp_session = static_cast<BgpSession *>(session);
1449 10799 : switch (event) {
1450 6557 : case TcpSession::CONNECT_COMPLETE:
1451 6557 : Enqueue(fsm::EvTcpConnected(bgp_session));
1452 6557 : break;
1453 131 : case TcpSession::CONNECT_FAILED:
1454 131 : Enqueue(fsm::EvTcpConnectFail(bgp_session));
1455 131 : peer_->inc_connect_error();
1456 131 : break;
1457 3901 : case TcpSession::CLOSE:
1458 3901 : Enqueue(fsm::EvTcpClose(bgp_session));
1459 3901 : break;
1460 210 : default:
1461 210 : break;
1462 : }
1463 10799 : }
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 4265 : bool StateMachine::PassiveOpen(BgpSession *session) {
1471 4265 : CHECK_CONCURRENCY("bgp::Config");
1472 4265 : Enqueue(fsm::EvTcpPassiveOpen(session));
1473 4265 : session->set_observer(boost::bind(&StateMachine::OnSessionEvent,
1474 : this, _1, _2));
1475 4265 : session->AsyncReadStart();
1476 4265 : return true;
1477 : }
1478 :
1479 4742 : void StateMachine::OnNotificationMessage(BgpSession *session,
1480 : BgpProto::BgpMessage *msg) {
1481 4742 : BgpPeer *peer = session->peer();
1482 4743 : if (peer)
1483 4743 : peer->inc_rx_notification();
1484 4743 : Enqueue(fsm::EvBgpNotification(session,
1485 : static_cast<BgpProto::Notification *>(msg)));
1486 4745 : }
1487 :
1488 : //
1489 : // Handle incoming message on the session.
1490 : //
1491 176088 : void StateMachine::OnMessage(BgpSession *session, BgpProto::BgpMessage *msg,
1492 : size_t msgsize) {
1493 176088 : switch (msg->type) {
1494 6253 : case BgpProto::OPEN: {
1495 6253 : BgpProto::OpenMessage *open_msg =
1496 : static_cast<BgpProto::OpenMessage *>(msg);
1497 6253 : BgpPeer *peer = session->peer();
1498 6253 : peer->inc_rx_open();
1499 6253 : if (int subcode = open_msg->Validate(peer)) {
1500 108 : Enqueue(fsm::EvBgpOpenError(session, subcode));
1501 108 : peer->inc_open_error();
1502 : } else {
1503 6145 : Enqueue(fsm::EvBgpOpen(session, open_msg));
1504 6146 : msg = NULL;
1505 : }
1506 6254 : break;
1507 : }
1508 6833 : case BgpProto::KEEPALIVE: {
1509 6833 : BgpPeer *peer = session->peer();
1510 6833 : Enqueue(fsm::EvBgpKeepalive(session));
1511 6833 : if (peer) peer->inc_rx_keepalive();
1512 6833 : break;
1513 : }
1514 4743 : case BgpProto::NOTIFICATION: {
1515 4743 : OnNotificationMessage(session, msg);
1516 4745 : msg = NULL;
1517 4745 : break;
1518 : }
1519 158260 : case BgpProto::UPDATE: {
1520 158260 : BgpProto::Update *update = static_cast<BgpProto::Update *>(msg);
1521 158260 : BgpPeer *peer = NULL;
1522 158260 : if (session)
1523 158260 : peer = session->peer();
1524 158260 : if (peer)
1525 158260 : peer->inc_rx_update();
1526 :
1527 158260 : std::string data;
1528 : int subcode;
1529 158260 : if (peer && (subcode = update->Validate(peer, &data))) {
1530 0 : Enqueue(fsm::EvBgpUpdateError(session, subcode, data));
1531 0 : peer->inc_update_error();
1532 : } else {
1533 158250 : Enqueue(fsm::EvBgpUpdate(session, update, msgsize));
1534 158262 : msg = NULL;
1535 : }
1536 158262 : break;
1537 158262 : }
1538 0 : default:
1539 0 : SM_LOG_NOTICE("Unknown message type " << msg->type);
1540 0 : break;
1541 : }
1542 :
1543 176094 : delete msg;
1544 176094 : }
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 536235 : const string &StateMachine::StateName() const {
1580 536235 : return state_names[state_];
1581 : }
1582 :
1583 54430 : const string &StateMachine::LastStateName() const {
1584 54430 : 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 200398 : EvValidate operator()(const Ev *event) {
1612 200398 : return NULL;
1613 : }
1614 : };
1615 :
1616 : template <typename Ev>
1617 : struct ValidateFn<Ev, true> {
1618 40429 : EvValidate operator()(const Ev *event) {
1619 40429 : return boost::bind(&Ev::validate, event, _1);
1620 : }
1621 : };
1622 :
1623 : template <typename Ev>
1624 240846 : bool StateMachine::Enqueue(const Ev &event) {
1625 240846 : LogEvent(TYPE_NAME(event), "Enqueue");
1626 240880 : EventContainer ec;
1627 240869 : ec.event = event.intrusive_from_this();
1628 240829 : ec.validate = ValidateFn<Ev, HasValidate<Ev>::Has>()(
1629 240830 : static_cast<const Ev *>(ec.event.get()));
1630 240790 : work_queue_.Enqueue(ec);
1631 :
1632 240888 : return true;
1633 240886 : }
1634 :
1635 481746 : void StateMachine::LogEvent(string event_name, string msg,
1636 : SandeshLevel::type log_level) {
1637 : // Reduce log level for keepalive and update messages.
1638 1153821 : if (get_state() == ESTABLISHED &&
1639 672075 : (event_name == "fsm::EvBgpKeepalive" ||
1640 334547 : event_name == "fsm::EvBgpUpdate")) {
1641 319399 : log_level = Sandesh::LoggingUtLevel();
1642 : }
1643 481746 : SM_LOG(log_level, msg << " " << event_name << " in state " << StateName());
1644 481753 : }
1645 :
1646 240792 : bool StateMachine::DequeueEvent(StateMachine::EventContainer ec) {
1647 : const fsm::EvTcpDeleteSession *deferred_delete =
1648 240792 : dynamic_cast<const fsm::EvTcpDeleteSession *>(ec.event.get());
1649 240787 : if (deferred_delete != NULL) {
1650 11089 : LogEvent(TYPE_NAME(*ec.event), "Dequeue");
1651 11089 : peer_->server()->session_manager()->DeleteSession(
1652 11089 : deferred_delete->session);
1653 11089 : return true;
1654 : }
1655 :
1656 229698 : set_last_event(TYPE_NAME(*ec.event));
1657 229759 : if (ec.validate.empty() || ec.validate(this)) {
1658 225968 : LogEvent(TYPE_NAME(*ec.event), "Dequeue");
1659 225989 : process_event(*ec.event);
1660 : } else {
1661 3802 : LogEvent(TYPE_NAME(*ec.event), "Discard", SandeshLevel::SYS_INFO);
1662 : }
1663 229796 : ec.event.reset();
1664 :
1665 229799 : return true;
1666 : }
1667 :
1668 9566 : void StateMachine::DequeueEventDone(bool done) {
1669 9566 : peer_->RetryDelete();
1670 9567 : }
1671 :
1672 32668 : void StateMachine::SetDataCollectionKey(BgpPeerInfo *peer_info) const {
1673 32668 : peer_->SetDataCollectionKey(peer_info);
1674 32668 : }
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 40876 : 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 40876 : 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 40812 : } else if (env_hold_time) {
1706 0 : return env_hold_time;
1707 : }
1708 :
1709 : // Use the configured hold-time from peer if available.
1710 40875 : if (peer_ && peer_->hold_time())
1711 4983 : return peer_->hold_time();
1712 :
1713 : // Use the configured hold-time from server if available.
1714 35892 : if (peer_ && peer_->server()->hold_time())
1715 2 : return peer_->server()->hold_time();
1716 :
1717 : // Use hard coded default.
1718 35889 : return kHoldTime;
1719 : }
1720 :
1721 159211 : void StateMachine::BGPPeerInfoSend(const BgpPeerInfoData &peer_info) {
1722 159211 : assert(!peer_info.get_name().empty());
1723 159201 : BGP_UVE_SEND(BGPPeerInfo, peer_info);
1724 159243 : }
1725 :
1726 229756 : void StateMachine::set_last_event(const std::string &event) {
1727 229756 : last_event_ = event;
1728 229752 : last_event_at_ = UTCTimestampUsec();
1729 :
1730 : // Skip keepalive and update events after we've reached established state.
1731 558628 : if (state_ == ESTABLISHED &&
1732 328844 : (event == "fsm::EvBgpKeepalive" || event == "fsm::EvBgpUpdate")) {
1733 159661 : return;
1734 : }
1735 :
1736 140160 : BgpPeerInfoData peer_info;
1737 70035 : peer_info.set_name(peer()->ToUVEKey());
1738 70055 : PeerEventInfo event_info;
1739 70028 : event_info.set_last_event(last_event_);
1740 70032 : event_info.set_last_event_at(last_event_at_);
1741 70032 : peer_info.set_event_info(event_info);
1742 70099 : BGPPeerInfoSend(peer_info);
1743 70121 : }
1744 :
1745 5013 : void StateMachine::set_last_notification_out(int code, int subcode,
1746 : const string &reason) {
1747 5013 : last_notification_out_ = std::make_pair(code, subcode);
1748 5012 : last_notification_out_at_ = UTCTimestampUsec();
1749 5013 : last_notification_out_error_ = reason;
1750 :
1751 10024 : BgpPeerInfoData peer_info;
1752 5011 : peer_info.set_name(peer()->ToUVEKey());
1753 5011 : peer_info.set_notification_out_at(last_notification_out_at_);
1754 5011 : peer_info.set_notification_out(BgpProto::Notification::toString(
1755 : static_cast<BgpProto::Notification::Code>(code), subcode));
1756 5013 : BGPPeerInfoSend(peer_info);
1757 5013 : }
1758 :
1759 1992 : void StateMachine::set_last_notification_in(int code, int subcode,
1760 : const string &reason) {
1761 1992 : last_notification_in_ = std::make_pair(code, subcode);
1762 1992 : last_notification_in_at_ = UTCTimestampUsec();
1763 1992 : last_notification_in_error_ = reason;
1764 :
1765 3984 : BgpPeerInfoData peer_info;
1766 1992 : peer_info.set_name(peer()->ToUVEKey());
1767 1992 : peer_info.set_notification_in_at(last_notification_in_at_);
1768 1992 : peer_info.set_notification_in(BgpProto::Notification::toString(
1769 : static_cast<BgpProto::Notification::Code>(code), subcode));
1770 1992 : BGPPeerInfoSend(peer_info);
1771 1992 : }
1772 :
1773 65823 : void StateMachine::set_state(State state) {
1774 65823 : if (state == state_)
1775 11851 : return;
1776 53972 : last_state_ = state_; state_ = state;
1777 53972 : last_state_change_at_ = UTCTimestampUsec();
1778 :
1779 107942 : BgpPeerInfoData peer_info;
1780 53970 : peer_info.set_name(peer()->ToUVEKey());
1781 53970 : PeerStateInfo state_info;
1782 53971 : state_info.set_state(StateName());
1783 53971 : state_info.set_last_state(LastStateName());
1784 53971 : state_info.set_last_state_at(last_state_change_at_);
1785 53971 : peer_info.set_state_info(state_info);
1786 53970 : BGPPeerInfoSend(peer_info);
1787 53971 : }
1788 :
1789 11967 : void StateMachine::set_hold_time(int hold_time) {
1790 11967 : hold_time_ = hold_time;
1791 :
1792 23933 : BgpPeerInfoData peer_info;
1793 11966 : peer_info.set_name(peer()->ToUVEKey());
1794 11966 : peer_info.set_hold_time(hold_time_);
1795 11966 : BGPPeerInfoSend(peer_info);
1796 11966 : }
1797 :
1798 15780 : void StateMachine::reset_hold_time() {
1799 15780 : hold_time_ = GetConfiguredHoldTime();
1800 :
1801 31555 : BgpPeerInfoData peer_info;
1802 15777 : peer_info.set_name(peer()->ToUVEKey());
1803 15778 : peer_info.set_hold_time(hold_time_);
1804 15778 : BGPPeerInfoSend(peer_info);
1805 15778 : }
1806 :
1807 403 : void StateMachine::reset_last_info() {
1808 403 : last_notification_in_ = std::make_pair(0, 0);
1809 403 : last_notification_in_at_ = 0;
1810 403 : last_notification_in_error_ = std::string();
1811 403 : last_notification_out_ = std::make_pair(0, 0);
1812 403 : last_notification_out_at_ = 0;
1813 403 : last_notification_out_error_ = std::string();
1814 403 : last_state_ = IDLE;
1815 403 : last_event_ = "";
1816 403 : last_state_change_at_ = 0;
1817 403 : last_event_at_ = 0;
1818 :
1819 806 : BgpPeerInfoData peer_info;
1820 403 : peer_info.set_name(peer()->ToUVEKey());
1821 403 : PeerStateInfo state_info;
1822 403 : state_info.set_state(StateName());
1823 403 : state_info.set_last_state(LastStateName());
1824 403 : state_info.set_last_state_at(last_state_change_at_);
1825 403 : peer_info.set_state_info(state_info);
1826 :
1827 403 : PeerEventInfo event_info;
1828 403 : event_info.set_last_event(last_event_);
1829 403 : event_info.set_last_event_at(last_event_at_);
1830 403 : peer_info.set_event_info(event_info);
1831 403 : BGPPeerInfoSend(peer_info);
1832 403 : }
|