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