Line data Source code
1 : /*
2 : * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
3 : * Copyright Contributors to the OpenSDN project
4 : */
5 :
6 : #include "bgp/peer_close_manager.h"
7 :
8 :
9 : #include <list>
10 : #include <map>
11 :
12 : #include <boost/foreach.hpp>
13 :
14 : #include "base/task_annotations.h"
15 : #include "bgp/bgp_log.h"
16 : #include "bgp/bgp_membership.h"
17 : #include "bgp/bgp_peer_types.h"
18 : #include "bgp/bgp_route.h"
19 : #include "bgp/bgp_server.h"
20 : #include "bgp/routing-instance/routing_instance.h"
21 : #include "net/community_type.h"
22 :
23 : #define PEER_CLOSE_MANAGER_LOG(msg) \
24 : BGP_LOG_PEER(Event, peer_close_->peer(), SandeshLevel::SYS_INFO, \
25 : BGP_LOG_FLAG_ALL, BGP_PEER_DIR_NA, \
26 : "PeerCloseManager: State " << GetStateName(state_) << \
27 : ", MembershipState: " << GetMembershipStateName(membership_state_) << \
28 : ", MembershipReqPending: " << membership_req_pending_ << \
29 : ", CloseAgain?: " << (close_again_ ? "Yes" : "No") << ": " << msg);
30 :
31 : #define PEER_CLOSE_MANAGER_TABLE_LOG(msg) \
32 : BGP_LOG_PEER_TABLE(peer_close_->peer(), SandeshLevel::SYS_INFO, \
33 : BGP_LOG_FLAG_ALL, table, \
34 : "PeerCloseManager: State " << GetStateName(state_) << \
35 : ", MembershipState: " << GetMembershipStateName(membership_state_) << \
36 : ", MembershipReqPending: " << membership_req_pending_ << \
37 : ", CloseAgain?: " << (close_again_ ? "Yes" : "No") << ": " << msg);
38 :
39 : #define MOVE_TO_STATE(state) \
40 : do { \
41 : assert(state_ != state); \
42 : PEER_CLOSE_MANAGER_LOG("Move to state " << GetStateName(state)); \
43 : state_ = state; \
44 : } while (false)
45 :
46 : // Create an instance of PeerCloseManager with back reference to parent IPeer
47 8064 : PeerCloseManager::PeerCloseManager(IPeerClose *peer_close,
48 8064 : boost::asio::io_context *io_service) :
49 8064 : peer_close_(peer_close), gr_timer_(NULL),
50 16128 : event_queue_(new WorkQueue<Event *>(
51 24192 : TaskScheduler::GetInstance()->GetTaskId(
52 8064 : peer_close_->GetTaskName()),
53 8064 : peer_close_->GetTaskInstance(),
54 8064 : boost::bind(&PeerCloseManager::EventCallback, this, _1))),
55 8064 : state_(NONE), close_again_(false), graceful_(true), gr_elapsed_(0),
56 16128 : llgr_elapsed_(0), membership_state_(MEMBERSHIP_NONE) {
57 8064 : stats_.init++;
58 8064 : membership_req_pending_ = 0;
59 8064 : gr_timer_ = TimerManager::CreateTimer(*io_service,
60 : "Graceful Restart Timer");
61 8064 : }
62 :
63 : // Create an instance of PeerCloseManager with back reference to parent IPeer
64 20400 : PeerCloseManager::PeerCloseManager(IPeerClose *peer_close) :
65 20400 : peer_close_(peer_close), gr_timer_(NULL),
66 40800 : event_queue_(new WorkQueue<Event *>(
67 61200 : TaskScheduler::GetInstance()->GetTaskId(
68 20400 : peer_close_->GetTaskName()),
69 20400 : peer_close_->GetTaskInstance(),
70 20400 : boost::bind(&PeerCloseManager::EventCallback, this, _1))),
71 20400 : state_(NONE), close_again_(false), graceful_(true), gr_elapsed_(0),
72 40800 : llgr_elapsed_(0), membership_state_(MEMBERSHIP_NONE) {
73 20399 : stats_.init++;
74 20399 : membership_req_pending_ = 0;
75 20400 : if (peer_close->peer() && peer_close->peer()->server()) {
76 16585 : gr_timer_ =
77 16585 : TimerManager::CreateTimer(*peer_close->peer()->server()->ioservice(),
78 : "Graceful Restart Timer");
79 : }
80 20400 : }
81 :
82 45849 : PeerCloseManager::~PeerCloseManager() {
83 28464 : event_queue_->Shutdown();
84 28464 : TimerManager::DeleteTimer(gr_timer_);
85 45849 : }
86 :
87 226543 : std::string PeerCloseManager::GetStateName(State state) const {
88 226543 : switch (state) {
89 32600 : case NONE:
90 32600 : return "NONE";
91 67941 : case GR_TIMER:
92 67941 : return "GR_TIMER";
93 19725 : case STALE:
94 19725 : return "STALE";
95 6032 : case LLGR_STALE:
96 6032 : return "LLGR_STALE";
97 7545 : case LLGR_TIMER:
98 7545 : return "LLGR_TIMER";
99 10571 : case SWEEP:
100 10571 : return "SWEEP";
101 82130 : case DELETE:
102 82130 : return "DELETE";
103 : }
104 0 : assert(false);
105 : return "";
106 : }
107 :
108 204627 : std::string PeerCloseManager::GetMembershipStateName(
109 : MembershipState state) const {
110 204627 : switch (state) {
111 92153 : case MEMBERSHIP_NONE:
112 92153 : return "NONE";
113 108473 : case MEMBERSHIP_IN_USE:
114 108473 : return "IN_USE";
115 4001 : case MEMBERSHIP_IN_WAIT:
116 4001 : return "IN_WAIT";
117 : }
118 0 : assert(false);
119 : return "";
120 : }
121 :
122 53739 : std::string PeerCloseManager::GetEventName(EventType eventType) const {
123 53739 : switch (eventType) {
124 0 : case EVENT_NONE:
125 0 : return "NONE";
126 10173 : case CLOSE:
127 10173 : return "CLOSE";
128 5582 : case EOR_RECEIVED:
129 5582 : return "EOR_RECEIVED";
130 9600 : case MEMBERSHIP_REQUEST:
131 9600 : return "MEMBERSHIP_REQUEST";
132 25741 : case MEMBERSHIP_REQUEST_COMPLETE_CALLBACK:
133 25741 : return "MEMBERSHIP_REQUEST_COMPLETE_CALLBACK";
134 2643 : case TIMER_CALLBACK:
135 2643 : return "TIMER_CALLBACK";
136 : }
137 :
138 0 : return "";
139 : }
140 :
141 143446 : void PeerCloseManager::EnqueueEvent(Event *event) {
142 163988 : PEER_CLOSE_MANAGER_LOG("Enqueued event " <<
143 : GetEventName(event->event_type) <<
144 : ", graceful " << event->graceful <<
145 : ", family " << Address::FamilyToString(event->family));
146 143440 : event_queue_->Enqueue(event);
147 143568 : }
148 :
149 : // Trigger closure of an IPeer
150 : //
151 : // Graceful close_state_: NONE
152 : // RibIn Stale Marking and Ribout deletion close_state_: STALE
153 : // StateMachine restart and GR timer start close_state_: GR_TIMER
154 : //
155 : // Peer IsReady() in GR timer callback (or via reception of all EoRs)
156 : // RibIn Sweep and Ribout Generation close_state_: SWEEP
157 : // MembershipRequestCallback close_state_: NONE
158 : //
159 : // Peer not IsReady() in GR timer callback
160 : // If LLGR supported close_state_: LLGR_STALE
161 : // RibIn Stale marking with LLGR_STALE community close_state_: LLGR_TIMER
162 : //
163 : // Peer not IsReady() in LLGR timer callback
164 : // RibIn Delete close_state_: DELETE
165 : // MembershipRequestCallback close_state_: NONE
166 : //
167 : // Peer IsReady() in LLGR timer callback (or via reception of all EoRs)
168 : // RibIn Sweep close_state_: SWEEP
169 : // MembershipRequestCallback close_state_: NONE
170 : //
171 : // If LLGR is not supported
172 : // RibIn Delete close_state_: DELETE
173 : // MembershipRequestCallback close_state_: NONE
174 : //
175 : // Close() call during any state other than NONE and DELETE
176 : // Cancel GR timer and restart GR Closure all over again
177 : //
178 : // NonGraceful close_state_ = * (except DELETE)
179 : // A. RibIn deletion and Ribout deletion close_state_ = DELETE
180 : // B. MembershipRequestCallback => Peers delete/StateMachine restart
181 : // close_state_ = NONE
182 : //
183 : // If Close is restarted, account for GR timer's elapsed time.
184 : //
185 : // Use graceful as true to close gracefully.
186 20977 : void PeerCloseManager::Close(bool graceful) {
187 20977 : EnqueueEvent(new Event(CLOSE, graceful));
188 20980 : }
189 :
190 20980 : void PeerCloseManager::Close(Event *event) {
191 : // Note down non-graceful close trigger. Once non-graceful closure is
192 : // triggered, it should remain so until close process is complete. Further
193 : // graceful closure calls until then should remain non-graceful.
194 20980 : graceful_ &= event->graceful;
195 20980 : CloseInternal();
196 20980 : }
197 :
198 21514 : void PeerCloseManager::CloseInternal() {
199 21514 : stats_.close++;
200 :
201 : // Ignore nested closures
202 21514 : if (close_again_) {
203 4 : PEER_CLOSE_MANAGER_LOG("Nested close calls ignored");
204 4 : return;
205 : }
206 :
207 21510 : switch (state_) {
208 19946 : case NONE:
209 19946 : stats_.ResetRouteStats();
210 19946 : ProcessClosure();
211 19946 : break;
212 :
213 338 : case GR_TIMER:
214 464 : PEER_CLOSE_MANAGER_LOG("Nested close: Restart GR");
215 338 : close_again_ = true;
216 338 : stats_.nested++;
217 338 : gr_elapsed_ += gr_timer_->GetElapsedTime();
218 338 : CloseComplete();
219 338 : break;
220 :
221 196 : case LLGR_TIMER:
222 196 : PEER_CLOSE_MANAGER_LOG("Nested close: Restart LLGR");
223 196 : close_again_ = true;
224 196 : stats_.nested++;
225 196 : llgr_elapsed_ += gr_timer_->GetElapsedTime();
226 196 : CloseComplete();
227 196 : break;
228 :
229 1030 : case STALE:
230 : case LLGR_STALE:
231 : case SWEEP:
232 : case DELETE:
233 1030 : PEER_CLOSE_MANAGER_LOG("Nested close");
234 1030 : close_again_ = true;
235 1030 : stats_.nested++;
236 1030 : break;
237 : }
238 : }
239 :
240 14976 : void PeerCloseManager::ProcessEORMarkerReceived(Address::Family family) {
241 14976 : EnqueueEvent(new Event(EOR_RECEIVED, family));
242 14982 : }
243 :
244 14982 : void PeerCloseManager::ProcessEORMarkerReceived(Event *event) {
245 14982 : if ((state_ == GR_TIMER || state_ == LLGR_TIMER) && !families_.empty()) {
246 1846 : if (event->family == Address::UNSPEC) {
247 40 : families_.clear();
248 : } else {
249 1806 : families_.erase(event->family);
250 : }
251 :
252 : // Start the timer if all EORs have been received.
253 1846 : if (families_.empty())
254 1236 : StartRestartTimer(0);
255 : }
256 14982 : }
257 :
258 : // Process RibIn during peer closure.
259 2702 : void PeerCloseManager::StartRestartTimer(int time) {
260 2702 : gr_timer_->Cancel();
261 2966 : PEER_CLOSE_MANAGER_LOG("GR Timer started to fire after " << time/1000 <<
262 : " seconds");
263 2702 : gr_timer_->Start(time,
264 : boost::bind(&PeerCloseManager::RestartTimerCallback, this));
265 2702 : }
266 :
267 2646 : bool PeerCloseManager::RestartTimerCallback() {
268 2646 : CHECK_CONCURRENCY("timer::TimerTask");
269 2647 : EnqueueEvent(new Event(TIMER_CALLBACK));
270 2647 : return false;
271 : }
272 :
273 2647 : void PeerCloseManager::RestartTimerCallback(Event *event) {
274 2647 : CHECK_CONCURRENCY(peer_close_->GetTaskName());
275 :
276 2757 : PEER_CLOSE_MANAGER_LOG("GR Timer callback started");
277 2647 : if (state_ != GR_TIMER && state_ != LLGR_TIMER)
278 960 : return;
279 :
280 1687 : if (peer_close_->IsReady() && !families_.empty()) {
281 : // Fake reception of all EORs.
282 204 : for (IPeerClose::Families::iterator i = families_.begin(), next = i;
283 408 : i != families_.end(); i = next) {
284 204 : next++;
285 216 : PEER_CLOSE_MANAGER_LOG("Simulate EoR reception for family " << *i);
286 204 : peer_close_->ReceiveEndOfRIB(*i);
287 : }
288 : } else {
289 1483 : ProcessClosure();
290 : }
291 : }
292 :
293 : // Route stale timer callback. If the peer has come back up, sweep routes for
294 : // those address families that are still active. Delete the rest
295 21429 : void PeerCloseManager::ProcessClosure() {
296 : // If the peer is back up and this address family is still supported,
297 : // sweep old paths which may not have come back in the new session
298 21429 : switch (state_) {
299 19946 : case NONE:
300 19946 : if (!graceful_ || !peer_close_->IsCloseGraceful()) {
301 19708 : MOVE_TO_STATE(DELETE);
302 18909 : stats_.deletes++;
303 : } else {
304 1232 : MOVE_TO_STATE(STALE);
305 1037 : stats_.stale++;
306 1037 : StaleNotify();
307 1037 : return;
308 : }
309 18909 : break;
310 1014 : case GR_TIMER:
311 1014 : if (peer_close_->IsReady()) {
312 704 : MOVE_TO_STATE(SWEEP);
313 664 : gr_elapsed_ = 0;
314 664 : llgr_elapsed_ = 0;
315 664 : stats_.sweep++;
316 664 : break;
317 : }
318 350 : if (peer_close_->IsCloseLongLivedGraceful()) {
319 284 : MOVE_TO_STATE(LLGR_STALE);
320 255 : stats_.llgr_stale++;
321 255 : peer_close_->LongLivedGracefulRestartStale();
322 255 : break;
323 : }
324 95 : MOVE_TO_STATE(DELETE);
325 95 : stats_.deletes++;
326 95 : break;
327 :
328 469 : case LLGR_TIMER:
329 469 : if (peer_close_->IsReady()) {
330 264 : MOVE_TO_STATE(SWEEP);
331 264 : gr_elapsed_ = 0;
332 264 : llgr_elapsed_ = 0;
333 264 : stats_.sweep++;
334 264 : break;
335 : }
336 234 : MOVE_TO_STATE(DELETE);
337 205 : stats_.deletes++;
338 205 : break;
339 :
340 0 : case STALE:
341 : case LLGR_STALE:
342 : case SWEEP:
343 : case DELETE:
344 0 : assert(false);
345 : return;
346 : }
347 :
348 20392 : if (state_ == DELETE)
349 19209 : peer_close_->CustomClose();
350 20392 : MembershipRequest();
351 : }
352 :
353 1384 : void PeerCloseManager::CloseComplete() {
354 1550 : MOVE_TO_STATE(NONE);
355 1384 : gr_timer_->Cancel();
356 1384 : families_.clear();
357 1384 : stats_.init++;
358 :
359 : // Nested closures trigger fresh GR
360 1384 : if (close_again_) {
361 534 : close_again_ = false;
362 534 : CloseInternal();
363 : }
364 1384 : }
365 :
366 1426 : bool PeerCloseManager::AssertSweepState(bool do_assert) {
367 1426 : bool check = (state_ == SWEEP);
368 1426 : if (do_assert)
369 658 : assert(check);
370 1426 : return check;
371 : }
372 :
373 95254 : bool PeerCloseManager::AssertMembershipManagerInUse(bool do_assert) {
374 95254 : bool check = false;
375 181731 : check |= (state_ == STALE || state_ == LLGR_STALE || state_ == SWEEP ||
376 86477 : state_ == DELETE);
377 95254 : check |= (membership_state_ == MEMBERSHIP_IN_USE);
378 95254 : check |= (membership_req_pending_ > 0);
379 95254 : if (do_assert)
380 93910 : assert(check);
381 95254 : return check;
382 : }
383 :
384 22862 : bool PeerCloseManager::AssertMembershipState(bool do_assert) {
385 22862 : bool check = (membership_state_ != MEMBERSHIP_IN_USE);
386 22862 : if (do_assert)
387 20391 : assert(check);
388 22862 : return check;
389 : }
390 :
391 21139 : bool PeerCloseManager::AssertMembershipReqCount(bool do_assert) {
392 21139 : bool check = !membership_req_pending_;
393 21139 : if (do_assert)
394 20302 : assert(check);
395 21139 : return check;
396 : }
397 :
398 1426 : void PeerCloseManager::TriggerSweepStateActions() {
399 1426 : CHECK_CONCURRENCY(peer_close_->GetTaskName());
400 1426 : if (!AssertSweepState())
401 576 : return;
402 :
403 : // Notify clients to trigger sweep as appropriate.
404 850 : peer_close_->GracefulRestartSweep();
405 :
406 : // Reset MembershipUse state after client has been notified above.
407 850 : set_membership_state(MEMBERSHIP_NONE);
408 850 : CloseComplete();
409 : }
410 :
411 : // Notify clients about entering Stale event.
412 1037 : void PeerCloseManager::StaleNotify() {
413 1037 : CHECK_CONCURRENCY(peer_close_->GetTaskName());
414 :
415 1037 : peer_close_->GracefulRestartStale();
416 1037 : if (!AssertMembershipState())
417 48 : return;
418 989 : MembershipRequest(NULL);
419 : }
420 :
421 19498 : bool PeerCloseManager::CanUseMembershipManager() const {
422 19498 : return peer_close_->peer()->CanUseMembershipManager();
423 : }
424 :
425 20302 : void PeerCloseManager::GetRegisteredRibs(std::list<BgpTable *> *tables) {
426 20302 : BgpMembershipManager *mgr = peer_close_->peer()->server()->membership_mgr();
427 20302 : mgr->GetRegisteredRibs(peer_close_->peer(), tables);
428 20302 : }
429 :
430 83481 : bool PeerCloseManager::IsRegistered(BgpTable *table) const {
431 83481 : BgpMembershipManager *mgr = peer_close_->peer()->server()->membership_mgr();
432 83481 : return mgr->IsRegistered(peer_close_->peer(), table);
433 : }
434 :
435 73480 : void PeerCloseManager::Unregister(BgpTable *table) {
436 73480 : BgpMembershipManager *mgr = peer_close_->peer()->server()->membership_mgr();
437 73480 : mgr->Unregister(peer_close_->peer(), table);
438 73480 : }
439 :
440 3816 : void PeerCloseManager::WalkRibIn(BgpTable *table) {
441 3816 : BgpMembershipManager *mgr = peer_close_->peer()->server()->membership_mgr();
442 3816 : mgr->WalkRibIn(peer_close_->peer(), table);
443 3816 : }
444 :
445 4385 : void PeerCloseManager::UnregisterRibOut(BgpTable *table) {
446 4385 : BgpMembershipManager *mgr = peer_close_->peer()->server()->membership_mgr();
447 4385 : mgr->UnregisterRibOut(peer_close_->peer(), table);
448 4385 : }
449 :
450 3983 : bool PeerCloseManager::IsRibInRegistered(BgpTable *table) const {
451 3983 : BgpMembershipManager *mgr = peer_close_->peer()->server()->membership_mgr();
452 3983 : return mgr->IsRibInRegistered(peer_close_->peer(), table);
453 : }
454 :
455 1800 : void PeerCloseManager::UnregisterRibIn(BgpTable *table) {
456 1800 : BgpMembershipManager *mgr = peer_close_->peer()->server()->membership_mgr();
457 1800 : mgr->UnregisterRibIn(peer_close_->peer(), table);
458 1800 : }
459 :
460 21825 : void PeerCloseManager::MembershipRequest() {
461 21825 : if (!AssertMembershipState())
462 848 : return;
463 :
464 : // Pause if membership manager is not ready for usage.
465 20977 : if (!CanUseMembershipManager()) {
466 827 : set_membership_state(MEMBERSHIP_IN_WAIT);
467 892 : PEER_CLOSE_MANAGER_LOG("Wait for membership manager availability");
468 827 : return;
469 : }
470 20150 : set_membership_state(MEMBERSHIP_IN_USE);
471 20150 : EnqueueEvent(new Event(MEMBERSHIP_REQUEST));
472 : }
473 :
474 21139 : void PeerCloseManager::MembershipRequest(Event *evnet) {
475 21139 : CHECK_CONCURRENCY(peer_close_->GetTaskName());
476 :
477 21139 : set_membership_state(MEMBERSHIP_IN_USE);
478 21139 : if (!AssertMembershipReqCount())
479 10877 : return;
480 20691 : membership_req_pending_++;
481 20691 : std::list<BgpTable *> tables;
482 20691 : GetRegisteredRibs(&tables);
483 :
484 20691 : if (tables.empty()) {
485 10429 : assert(MembershipRequestCallback(NULL));
486 10429 : return;
487 : }
488 :
489 : // Account for extra increment above.
490 10262 : membership_req_pending_--;
491 178002 : BOOST_FOREACH(BgpTable *table, tables) {
492 83870 : membership_req_pending_++;
493 83870 : if (IsRegistered(table)) {
494 79692 : if (state_ == PeerCloseManager::DELETE) {
495 100206 : PEER_CLOSE_MANAGER_TABLE_LOG(
496 : "MembershipManager::Unregister");
497 73581 : Unregister(table);
498 6111 : } else if (state_ == PeerCloseManager::SWEEP) {
499 3742 : PEER_CLOSE_MANAGER_TABLE_LOG("MembershipManager::WalkRibIn");
500 1670 : WalkRibIn(table);
501 : } else {
502 11699 : PEER_CLOSE_MANAGER_TABLE_LOG(
503 : "MembershipManager::UnregisterRibOut");
504 4441 : UnregisterRibOut(table);
505 : }
506 : } else {
507 4178 : assert(IsRibInRegistered(table));
508 4178 : if (state_ == PeerCloseManager::DELETE) {
509 5013 : PEER_CLOSE_MANAGER_TABLE_LOG(
510 : "MembershipManager::UnregisterRibIn");
511 1904 : UnregisterRibIn(table);
512 : } else {
513 6020 : PEER_CLOSE_MANAGER_TABLE_LOG("MembershipManager::WalkRibIn");
514 2274 : WalkRibIn(table);
515 : }
516 : }
517 : }
518 20691 : }
519 :
520 84683 : void PeerCloseManager::MembershipRequestCallback() {
521 84683 : EnqueueEvent(new Event(MEMBERSHIP_REQUEST_COMPLETE_CALLBACK));
522 84811 : }
523 :
524 : // Close process for this peer in terms of walking RibIns and RibOuts are
525 : // complete. Do the final cleanups necessary and notify interested party
526 : //
527 : // Retrun true if we are done using membership manager, false otherwise.
528 95254 : bool PeerCloseManager::MembershipRequestCallback(Event *event) {
529 95254 : CHECK_CONCURRENCY(peer_close_->GetTaskName());
530 :
531 95254 : bool result = false;
532 113853 : PEER_CLOSE_MANAGER_LOG("MembershipRequestCallback");
533 :
534 95254 : if (!AssertMembershipManagerInUse())
535 0 : return result;
536 95254 : if (--membership_req_pending_)
537 73608 : return result;
538 :
539 : // Indicate to the caller that we are done using the membership manager.
540 21646 : result = true;
541 :
542 21646 : if (state_ == DELETE) {
543 19582 : MOVE_TO_STATE(NONE);
544 18754 : peer_close_->Delete();
545 18754 : gr_elapsed_ = 0;
546 18754 : llgr_elapsed_ = 0;
547 18754 : stats_.init++;
548 18754 : close_again_ = false;
549 18754 : graceful_ = true;
550 18754 : set_membership_state(MEMBERSHIP_NONE);
551 18754 : return result;
552 : }
553 :
554 : // Process nested closures.
555 2892 : if (close_again_) {
556 0 : set_membership_state(MEMBERSHIP_NONE);
557 0 : CloseComplete();
558 :
559 : // Nested closure can make membership manager to be in use again.
560 0 : return membership_state_ != MEMBERSHIP_IN_USE;
561 : }
562 :
563 : // If any GR stale timer has to be launched, then to wait for some time
564 : // hoping for the peer (and the paths) to come back up.
565 2892 : if (state_ == STALE) {
566 1085 : peer_close_->CloseComplete();
567 1280 : MOVE_TO_STATE(GR_TIMER);
568 1085 : peer_close_->GetGracefulRestartFamilies(&families_);
569 :
570 : // Offset restart time with elapsed time during nested closures.
571 1085 : int time = peer_close_->GetGracefulRestartTime() * 1000;
572 1085 : time -= gr_elapsed_;
573 1085 : if (time < 0)
574 0 : time = 0;
575 1085 : StartRestartTimer(time);
576 1085 : stats_.gr_timer++;
577 1085 : set_membership_state(MEMBERSHIP_NONE);
578 1085 : return result;
579 : }
580 :
581 : // From LLGR_STALE state, switch to LLGR_TIMER state. Typically this would
582 : // be a very long timer, and we expect to receive EORs before this timer
583 : // expires.
584 : // Just like for GRSTALE in the above case, before moving to LLGR_TIMER and
585 : // starting LLGR_TIMER, we can check if the peer can come back up.
586 1807 : if (state_ == LLGR_STALE) {
587 410 : MOVE_TO_STATE(LLGR_TIMER);
588 381 : peer_close_->CloseComplete();
589 381 : peer_close_->GetLongLivedGracefulRestartFamilies(&families_);
590 :
591 : // Offset restart time with elapsed time during nested closures.
592 381 : int time = peer_close_->GetLongLivedGracefulRestartTime() * 1000;
593 381 : time -= llgr_elapsed_;
594 381 : if (time < 0)
595 0 : time = 0;
596 381 : StartRestartTimer(time);
597 381 : stats_.llgr_timer++;
598 381 : set_membership_state(MEMBERSHIP_NONE);
599 381 : return result;
600 : }
601 :
602 1426 : TriggerSweepStateActions();
603 1426 : return result;
604 : }
605 :
606 64251 : void PeerCloseManager::FillRouteCloseInfo(PeerCloseInfo *close_info) const {
607 64251 : std::map<std::string, PeerCloseRouteInfo> route_stats;
608 :
609 706761 : for (int i = 0; i < Address::NUM_FAMILIES; i++) {
610 642510 : if (!stats_.route_stats[i].IsSet())
611 484451 : continue;
612 158059 : PeerCloseRouteInfo route_info;
613 158059 : route_info.set_staled(stats_.route_stats[i].staled);
614 158059 : route_info.set_llgr_staled(stats_.route_stats[i].llgr_staled);
615 158059 : route_info.set_refreshed(stats_.route_stats[i].refreshed);
616 158059 : route_info.set_fresh(stats_.route_stats[i].fresh);
617 158059 : route_info.set_deleted(stats_.route_stats[i].deleted);
618 158059 : route_stats[Address::FamilyToString(static_cast<Address::Family>(i))] = route_info;
619 158059 : }
620 :
621 64251 : if (!route_stats.empty())
622 63333 : close_info->set_route_stats(route_stats);
623 64251 : }
624 :
625 64251 : BgpNeighborResp *PeerCloseManager::FillCloseInfo(BgpNeighborResp *resp) const {
626 64251 : PeerCloseInfo peer_close_info;
627 64251 : peer_close_info.set_state(GetStateName(state_));
628 64251 : peer_close_info.set_membership_state(
629 128502 : GetMembershipStateName(membership_state_));
630 64251 : peer_close_info.set_close_again(close_again_);
631 64251 : peer_close_info.set_graceful(graceful_);
632 64251 : peer_close_info.set_init(stats_.init);
633 64251 : peer_close_info.set_close(stats_.close);
634 64251 : peer_close_info.set_nested(stats_.nested);
635 64251 : peer_close_info.set_deletes(stats_.deletes);
636 64251 : peer_close_info.set_stale(stats_.stale);
637 64251 : peer_close_info.set_llgr_stale(stats_.llgr_stale);
638 64251 : peer_close_info.set_sweep(stats_.sweep);
639 64251 : peer_close_info.set_gr_timer(stats_.gr_timer);
640 64251 : peer_close_info.set_llgr_timer(stats_.llgr_timer);
641 64251 : FillRouteCloseInfo(&peer_close_info);
642 :
643 64251 : resp->set_peer_close_info(peer_close_info);
644 :
645 64251 : return resp;
646 64251 : }
647 :
648 216594 : void PeerCloseManager::UpdateRouteStats(Address::Family family,
649 : const BgpPath *old_path, uint32_t path_flags) const {
650 216594 : if (state_ == NONE)
651 196657 : return;
652 :
653 19937 : if (!old_path)
654 72 : stats_.route_stats[family].fresh++;
655 19865 : else if (old_path->IsStale() && !(path_flags & BgpPath::Stale))
656 3439 : stats_.route_stats[family].refreshed++;
657 : }
658 :
659 100553 : bool PeerCloseManager::MembershipPathCallback(DBTablePartBase *root,
660 : BgpRoute *rt, BgpPath *path) {
661 100553 : CHECK_CONCURRENCY("db::DBTable");
662 100592 : DBRequest::DBOperation oper = DBRequest::DB_ENTRY_INVALID;
663 100592 : BgpAttrPtr attrs;
664 :
665 100592 : BgpTable *table = static_cast<BgpTable *>(root->parent());
666 100587 : assert(table);
667 :
668 100587 : uint32_t stale = 0;
669 :
670 100587 : switch (state_) {
671 0 : case NONE:
672 : case GR_TIMER:
673 : case LLGR_TIMER:
674 0 : return false;
675 :
676 6099 : case SWEEP:
677 :
678 : // Stale paths must be deleted.
679 6099 : if (!path->IsStale() && !path->IsLlgrStale())
680 2989 : return false;
681 3111 : if (path->IsStale()) {
682 3110 : path->ResetStale();
683 3110 : table->UpdateStalePathCount(-1);
684 : }
685 3110 : if (path->IsLlgrStale()) {
686 0 : path->ResetLlgrStale();
687 0 : table->UpdateLlgrStalePathCount(-1);
688 : }
689 3110 : oper = DBRequest::DB_ENTRY_DELETE;
690 3110 : attrs = NULL;
691 3109 : stats_.route_stats[table->family()].deleted++;
692 3110 : break;
693 :
694 76482 : case DELETE:
695 :
696 : // This path must be deleted. Hence attr is not required.
697 76482 : oper = DBRequest::DB_ENTRY_DELETE;
698 76482 : attrs = NULL;
699 76480 : stats_.route_stats[table->family()].deleted++;
700 76516 : break;
701 :
702 14779 : case STALE:
703 :
704 : // We do not support GR for multicast routes (yet).
705 29556 : if ((table->family() == Address::ERMVPN) ||
706 14778 : (table->family() == Address::MVPN)) {
707 0 : oper = DBRequest::DB_ENTRY_DELETE;
708 0 : attrs = NULL;
709 0 : stats_.route_stats[table->family()].deleted++;
710 0 : break;
711 : }
712 :
713 : // If path is already marked as stale, then there is no need to
714 : // process again. This can happen if the session flips while in
715 : // GR_TIMER state.
716 14777 : if (path->IsStale())
717 3298 : return false;
718 :
719 : // This path must be marked for staling. Update the local
720 : // preference and update the route accordingly.
721 11481 : oper = DBRequest::DB_ENTRY_ADD_CHANGE;
722 11481 : attrs = path->GetAttr();
723 11484 : stale = BgpPath::Stale;
724 11484 : stats_.route_stats[table->family()].staled++;
725 11484 : break;
726 :
727 3229 : case LLGR_STALE:
728 :
729 : // If the path has NO_LLGR community, DELETE it.
730 3229 : if (path->GetAttr()->community() &&
731 0 : path->GetAttr()->community()->ContainsValue(
732 : CommunityType::NoLlgr)) {
733 0 : oper = DBRequest::DB_ENTRY_DELETE;
734 0 : attrs = NULL;
735 0 : stats_.route_stats[table->family()].deleted++;
736 0 : break;
737 : }
738 :
739 : // If path is already marked as llgr_stale, then there is no
740 : // need to process again. This can happen if the session flips
741 : // while in LLGR_TIMER state.
742 3232 : if (path->IsLlgrStale())
743 0 : return false;
744 :
745 3230 : attrs = path->GetAttr();
746 3233 : stale = BgpPath::LlgrStale;
747 3233 : oper = DBRequest::DB_ENTRY_ADD_CHANGE;
748 3233 : stats_.route_stats[table->family()].llgr_staled++;
749 3234 : break;
750 : }
751 :
752 : // Feed the route modify/delete request to the table input process.
753 283018 : return table->InputCommon(root, rt, path, peer_close_->peer(), NULL, oper,
754 94334 : attrs, path->GetPathId(), path->GetFlags() | stale, path->GetLabel(),
755 94343 : path->GetL3Label());
756 100623 : }
757 :
758 : //
759 : // Handler for an Event.
760 : //
761 143584 : bool PeerCloseManager::EventCallback(Event *event) {
762 143584 : CHECK_CONCURRENCY(peer_close_->GetTaskName());
763 : bool result;
764 :
765 143584 : switch (event->event_type) {
766 0 : case EVENT_NONE:
767 0 : break;
768 20980 : case CLOSE:
769 20980 : Close(event);
770 20980 : break;
771 14982 : case EOR_RECEIVED:
772 14982 : ProcessEORMarkerReceived(event);
773 14982 : break;
774 20150 : case MEMBERSHIP_REQUEST:
775 20150 : MembershipRequest(event);
776 20150 : break;
777 84825 : case MEMBERSHIP_REQUEST_COMPLETE_CALLBACK:
778 84825 : result = MembershipRequestCallback(event);
779 :
780 : // Notify clients if we are no longer using the membership mgr.
781 84825 : if (result)
782 11217 : peer_close_->MembershipRequestCallbackComplete();
783 84825 : break;
784 2647 : case TIMER_CALLBACK:
785 2647 : RestartTimerCallback(event);
786 2647 : break;
787 : }
788 :
789 143584 : delete event;
790 143584 : return true;
791 : }
|