LCOV - code coverage report
Current view: top level - bgp - peer_close_manager.cc (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 450 479 93.9 %
Date: 2026-08-17 02:09:53 Functions: 41 41 100.0 %
Legend: Lines: hit not hit

          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       20413 : PeerCloseManager::PeerCloseManager(IPeerClose *peer_close) :
      64       20413 :         peer_close_(peer_close), gr_timer_(NULL),
      65       40826 :         event_queue_(new WorkQueue<Event *>(
      66       61239 :                      TaskScheduler::GetInstance()->GetTaskId(
      67       20413 :                          peer_close_->GetTaskName()),
      68       20413 :                      peer_close_->GetTaskInstance(),
      69       20413 :                      boost::bind(&PeerCloseManager::EventCallback, this, _1))),
      70       20413 :         state_(NONE), close_again_(false), graceful_(true), gr_elapsed_(0),
      71       40826 :         llgr_elapsed_(0), membership_state_(MEMBERSHIP_NONE) {
      72       20413 :     stats_.init++;
      73       20413 :     membership_req_pending_ = 0;
      74       20413 :     if (peer_close->peer() && peer_close->peer()->server()) {
      75       16598 :         gr_timer_ =
      76       16598 :            TimerManager::CreateTimer(*peer_close->peer()->server()->ioservice(),
      77             :                                      "Graceful Restart Timer");
      78             :     }
      79       20413 : }
      80             : 
      81       45865 : PeerCloseManager::~PeerCloseManager() {
      82       28477 :     event_queue_->Shutdown();
      83       28477 :     TimerManager::DeleteTimer(gr_timer_);
      84       45865 : }
      85             : 
      86      228607 : std::string PeerCloseManager::GetStateName(State state) const {
      87      228607 :     switch (state) {
      88       32784 :     case NONE:
      89       32784 :         return "NONE";
      90       68739 :     case GR_TIMER:
      91       68739 :         return "GR_TIMER";
      92       20143 :     case STALE:
      93       20143 :         return "STALE";
      94        6102 :     case LLGR_STALE:
      95        6102 :         return "LLGR_STALE";
      96        7687 :     case LLGR_TIMER:
      97        7687 :         return "LLGR_TIMER";
      98       10913 :     case SWEEP:
      99       10913 :         return "SWEEP";
     100       82239 :     case DELETE:
     101       82239 :         return "DELETE";
     102             :     }
     103           0 :     assert(false);
     104             :     return "";
     105             : }
     106             : 
     107      206438 : std::string PeerCloseManager::GetMembershipStateName(
     108             :         MembershipState state) const {
     109      206438 :     switch (state) {
     110       93118 :     case MEMBERSHIP_NONE:
     111       93118 :         return "NONE";
     112      109296 :     case MEMBERSHIP_IN_USE:
     113      109296 :         return "IN_USE";
     114        4025 :     case MEMBERSHIP_IN_WAIT:
     115        4025 :         return "IN_WAIT";
     116             :     }
     117           0 :     assert(false);
     118             :     return "";
     119             : }
     120             : 
     121       54141 : std::string PeerCloseManager::GetEventName(EventType eventType) const {
     122       54141 :     switch (eventType) {
     123           0 :     case EVENT_NONE:
     124           0 :         return "NONE";
     125       10237 :     case CLOSE:
     126       10237 :         return "CLOSE";
     127        5626 :     case EOR_RECEIVED:
     128        5626 :         return "EOR_RECEIVED";
     129        9671 :     case MEMBERSHIP_REQUEST:
     130        9671 :         return "MEMBERSHIP_REQUEST";
     131       25896 :     case MEMBERSHIP_REQUEST_COMPLETE_CALLBACK:
     132       25896 :         return "MEMBERSHIP_REQUEST_COMPLETE_CALLBACK";
     133        2711 :     case TIMER_CALLBACK:
     134        2711 :         return "TIMER_CALLBACK";
     135             :     }
     136             : 
     137           0 :     return "";
     138             : }
     139             : 
     140      143980 : void PeerCloseManager::EnqueueEvent(Event *event) {
     141      164564 :     PEER_CLOSE_MANAGER_LOG("Enqueued event " <<
     142             :             GetEventName(event->event_type) <<
     143             :             ", graceful " << event->graceful <<
     144             :             ", family " << Address::FamilyToString(event->family));
     145      143998 :     event_queue_->Enqueue(event);
     146      144116 : }
     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       21035 : void PeerCloseManager::Close(bool graceful) {
     186       21035 :     EnqueueEvent(new Event(CLOSE, graceful));
     187       21048 : }
     188             : 
     189       21048 : 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       21048 :     graceful_ &= event->graceful;
     194       21048 :     CloseInternal();
     195       21048 : }
     196             : 
     197       21584 : void PeerCloseManager::CloseInternal() {
     198       21584 :     stats_.close++;
     199             : 
     200             :     // Ignore nested closures
     201       21584 :     if (close_again_) {
     202           4 :         PEER_CLOSE_MANAGER_LOG("Nested close calls ignored");
     203           4 :         return;
     204             :     }
     205             : 
     206       21580 :     switch (state_) {
     207       20029 :     case NONE:
     208       20029 :         stats_.ResetRouteStats();
     209       20029 :         ProcessClosure();
     210       20029 :         break;
     211             : 
     212         340 :     case GR_TIMER:
     213         466 :         PEER_CLOSE_MANAGER_LOG("Nested close: Restart GR");
     214         340 :         close_again_ = true;
     215         340 :         stats_.nested++;
     216         340 :         gr_elapsed_ += gr_timer_->GetElapsedTime();
     217         340 :         CloseComplete();
     218         340 :         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        1015 :     case STALE:
     229             :     case LLGR_STALE:
     230             :     case SWEEP:
     231             :     case DELETE:
     232        1015 :         PEER_CLOSE_MANAGER_LOG("Nested close");
     233        1015 :         close_again_ = true;
     234        1015 :         stats_.nested++;
     235        1015 :         break;
     236             :     }
     237             : }
     238             : 
     239       15148 : void PeerCloseManager::ProcessEORMarkerReceived(Address::Family family) {
     240       15148 :     EnqueueEvent(new Event(EOR_RECEIVED, family));
     241       15149 : }
     242             : 
     243       15150 : void PeerCloseManager::ProcessEORMarkerReceived(Event *event) {
     244       15150 :     if ((state_ == GR_TIMER || state_ == LLGR_TIMER) && !families_.empty()) {
     245        1911 :         if (event->family == Address::UNSPEC) {
     246          40 :             families_.clear();
     247             :         } else {
     248        1871 :             families_.erase(event->family);
     249             :         }
     250             : 
     251             :         // Start the timer if all EORs have been received.
     252        1911 :         if (families_.empty())
     253        1289 :             StartRestartTimer(0);
     254             :     }
     255       15150 : }
     256             : 
     257             : // Process RibIn during peer closure.
     258        2810 : void PeerCloseManager::StartRestartTimer(int time) {
     259        2810 :     gr_timer_->Cancel();
     260        3074 :     PEER_CLOSE_MANAGER_LOG("GR Timer started to fire after " << time/1000 <<
     261             :                            " seconds");
     262        2810 :     gr_timer_->Start(time,
     263             :         boost::bind(&PeerCloseManager::RestartTimerCallback, this));
     264        2810 : }
     265             : 
     266        2713 : bool PeerCloseManager::RestartTimerCallback() {
     267        2713 :     CHECK_CONCURRENCY("timer::TimerTask");
     268        2715 :     EnqueueEvent(new Event(TIMER_CALLBACK));
     269        2715 :     return false;
     270             : }
     271             : 
     272        2715 : void PeerCloseManager::RestartTimerCallback(Event *event) {
     273        2715 :     CHECK_CONCURRENCY(peer_close_->GetTaskName());
     274             : 
     275        2825 :     PEER_CLOSE_MANAGER_LOG("GR Timer callback started");
     276        2715 :     if (state_ != GR_TIMER && state_ != LLGR_TIMER)
     277         960 :         return;
     278             : 
     279        1755 :     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        1551 :         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       21580 : 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       21580 :     switch (state_) {
     298       20029 :         case NONE:
     299       20029 :             if (!graceful_ || !peer_close_->IsCloseGraceful()) {
     300       19737 :                 MOVE_TO_STATE(DELETE);
     301       18937 :                 stats_.deletes++;
     302             :             } else {
     303        1287 :                 MOVE_TO_STATE(STALE);
     304        1092 :                 stats_.stale++;
     305        1092 :                 StaleNotify();
     306        1092 :                 return;
     307             :             }
     308       18937 :             break;
     309        1092 :         case GR_TIMER:
     310        1092 :             if (peer_close_->IsReady()) {
     311         768 :                 MOVE_TO_STATE(SWEEP);
     312         728 :                 gr_elapsed_ = 0;
     313         728 :                 llgr_elapsed_ = 0;
     314         728 :                 stats_.sweep++;
     315         728 :                 break;
     316             :             }
     317         364 :             if (peer_close_->IsCloseLongLivedGraceful()) {
     318         291 :                 MOVE_TO_STATE(LLGR_STALE);
     319         262 :                 stats_.llgr_stale++;
     320         262 :                 peer_close_->LongLivedGracefulRestartStale();
     321         262 :                 break;
     322             :             }
     323         102 :             MOVE_TO_STATE(DELETE);
     324         102 :             stats_.deletes++;
     325         102 :             break;
     326             : 
     327         459 :         case LLGR_TIMER:
     328         459 :             if (peer_close_->IsReady()) {
     329         262 :                 MOVE_TO_STATE(SWEEP);
     330         262 :                 gr_elapsed_ = 0;
     331         262 :                 llgr_elapsed_ = 0;
     332         262 :                 stats_.sweep++;
     333         262 :                 break;
     334             :             }
     335         226 :             MOVE_TO_STATE(DELETE);
     336         197 :             stats_.deletes++;
     337         197 :             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       20488 :     if (state_ == DELETE)
     348       19236 :         peer_close_->CustomClose();
     349       20488 :     MembershipRequest();
     350             : }
     351             : 
     352        1439 : void PeerCloseManager::CloseComplete() {
     353        1605 :     MOVE_TO_STATE(NONE);
     354        1439 :     gr_timer_->Cancel();
     355        1439 :     families_.clear();
     356        1439 :     stats_.init++;
     357             : 
     358             :     // Nested closures trigger fresh GR
     359        1439 :     if (close_again_) {
     360         536 :         close_again_ = false;
     361         536 :         CloseInternal();
     362             :     }
     363        1439 : }
     364             : 
     365        1479 : bool PeerCloseManager::AssertSweepState(bool do_assert) {
     366        1479 :     bool check = (state_ == SWEEP);
     367        1479 :     if (do_assert)
     368         711 :         assert(check);
     369        1479 :     return check;
     370             : }
     371             : 
     372       95428 : bool PeerCloseManager::AssertMembershipManagerInUse(bool do_assert) {
     373       95428 :     bool check = false;
     374      181951 :     check |= (state_ == STALE || state_ == LLGR_STALE || state_ == SWEEP ||
     375       86523 :               state_ == DELETE);
     376       95428 :     check |= (membership_state_ == MEMBERSHIP_IN_USE);
     377       95428 :     check |= (membership_req_pending_ > 0);
     378       95428 :     if (do_assert)
     379       94084 :         assert(check);
     380       95428 :     return check;
     381             : }
     382             : 
     383       22988 : bool PeerCloseManager::AssertMembershipState(bool do_assert) {
     384       22988 :     bool check = (membership_state_ != MEMBERSHIP_IN_USE);
     385       22988 :     if (do_assert)
     386       20502 :         assert(check);
     387       22988 :     return check;
     388             : }
     389             : 
     390       21283 : bool PeerCloseManager::AssertMembershipReqCount(bool do_assert) {
     391       21283 :     bool check = !membership_req_pending_;
     392       21283 :     if (do_assert)
     393       20438 :         assert(check);
     394       21283 :     return check;
     395             : }
     396             : 
     397        1479 : void PeerCloseManager::TriggerSweepStateActions() {
     398        1479 :     CHECK_CONCURRENCY(peer_close_->GetTaskName());
     399        1479 :     if (!AssertSweepState())
     400         576 :         return;
     401             : 
     402             :     // Notify clients to trigger sweep as appropriate.
     403         903 :     peer_close_->GracefulRestartSweep();
     404             : 
     405             :     // Reset MembershipUse state after client has been notified above.
     406         903 :     set_membership_state(MEMBERSHIP_NONE);
     407         903 :     CloseComplete();
     408             : }
     409             : 
     410             : // Notify clients about entering Stale event.
     411        1092 : void PeerCloseManager::StaleNotify() {
     412        1092 :     CHECK_CONCURRENCY(peer_close_->GetTaskName());
     413             : 
     414        1092 :     peer_close_->GracefulRestartStale();
     415        1092 :     if (!AssertMembershipState())
     416          48 :         return;
     417        1044 :     MembershipRequest(NULL);
     418             : }
     419             : 
     420       19554 : bool PeerCloseManager::CanUseMembershipManager() const {
     421       19554 :     return peer_close_->peer()->CanUseMembershipManager();
     422             : }
     423             : 
     424       20438 : void PeerCloseManager::GetRegisteredRibs(std::list<BgpTable *> *tables) {
     425       20438 :     BgpMembershipManager *mgr = peer_close_->peer()->server()->membership_mgr();
     426       20438 :     mgr->GetRegisteredRibs(peer_close_->peer(), tables);
     427       20438 : }
     428             : 
     429       83633 : bool PeerCloseManager::IsRegistered(BgpTable *table) const {
     430       83633 :     BgpMembershipManager *mgr = peer_close_->peer()->server()->membership_mgr();
     431       83633 :     return mgr->IsRegistered(peer_close_->peer(), table);
     432             : }
     433             : 
     434       73491 : void PeerCloseManager::Unregister(BgpTable *table) {
     435       73491 :     BgpMembershipManager *mgr = peer_close_->peer()->server()->membership_mgr();
     436       73491 :     mgr->Unregister(peer_close_->peer(), table);
     437       73491 : }
     438             : 
     439        3889 : void PeerCloseManager::WalkRibIn(BgpTable *table) {
     440        3889 :     BgpMembershipManager *mgr = peer_close_->peer()->server()->membership_mgr();
     441        3889 :     mgr->WalkRibIn(peer_close_->peer(), table);
     442        3889 : }
     443             : 
     444        4440 : void PeerCloseManager::UnregisterRibOut(BgpTable *table) {
     445        4440 :     BgpMembershipManager *mgr = peer_close_->peer()->server()->membership_mgr();
     446        4440 :     mgr->UnregisterRibOut(peer_close_->peer(), table);
     447        4440 : }
     448             : 
     449        4031 : bool PeerCloseManager::IsRibInRegistered(BgpTable *table) const {
     450        4031 :     BgpMembershipManager *mgr = peer_close_->peer()->server()->membership_mgr();
     451        4031 :     return mgr->IsRibInRegistered(peer_close_->peer(), table);
     452             : }
     453             : 
     454        1813 : void PeerCloseManager::UnregisterRibIn(BgpTable *table) {
     455        1813 :     BgpMembershipManager *mgr = peer_close_->peer()->server()->membership_mgr();
     456        1813 :     mgr->UnregisterRibIn(peer_close_->peer(), table);
     457        1813 : }
     458             : 
     459       21896 : void PeerCloseManager::MembershipRequest() {
     460       21896 :     if (!AssertMembershipState())
     461         848 :         return;
     462             : 
     463             :     // Pause if membership manager is not ready for usage.
     464       21048 :     if (!CanUseMembershipManager()) {
     465         809 :         set_membership_state(MEMBERSHIP_IN_WAIT);
     466         844 :         PEER_CLOSE_MANAGER_LOG("Wait for membership manager availability");
     467         809 :         return;
     468             :     }
     469       20239 :     set_membership_state(MEMBERSHIP_IN_USE);
     470       20239 :     EnqueueEvent(new Event(MEMBERSHIP_REQUEST));
     471             : }
     472             : 
     473       21283 : void PeerCloseManager::MembershipRequest(Event *evnet) {
     474       21283 :     CHECK_CONCURRENCY(peer_close_->GetTaskName());
     475             : 
     476       21283 :     set_membership_state(MEMBERSHIP_IN_USE);
     477       21283 :     if (!AssertMembershipReqCount())
     478       10899 :         return;
     479       20835 :     membership_req_pending_++;
     480       20835 :     std::list<BgpTable *> tables;
     481       20835 :     GetRegisteredRibs(&tables);
     482             : 
     483       20835 :     if (tables.empty()) {
     484       10451 :         assert(MembershipRequestCallback(NULL));
     485       10451 :         return;
     486             :     }
     487             : 
     488             :     // Account for extra increment above.
     489       10384 :     membership_req_pending_--;
     490      178444 :     BOOST_FOREACH(BgpTable *table, tables) {
     491       84030 :         membership_req_pending_++;
     492       84030 :         if (IsRegistered(table)) {
     493       79803 :             if (state_ == PeerCloseManager::DELETE) {
     494      100256 :                 PEER_CLOSE_MANAGER_TABLE_LOG(
     495             :                     "MembershipManager::Unregister");
     496       73594 :                 Unregister(table);
     497        6209 :             } else if (state_ == PeerCloseManager::SWEEP) {
     498        3797 :                 PEER_CLOSE_MANAGER_TABLE_LOG("MembershipManager::WalkRibIn");
     499        1710 :                 WalkRibIn(table);
     500             :             } else {
     501       11812 :                 PEER_CLOSE_MANAGER_TABLE_LOG(
     502             :                     "MembershipManager::UnregisterRibOut");
     503        4499 :                 UnregisterRibOut(table);
     504             :             }
     505             :         } else {
     506        4227 :             assert(IsRibInRegistered(table));
     507        4227 :             if (state_ == PeerCloseManager::DELETE) {
     508        5024 :                 PEER_CLOSE_MANAGER_TABLE_LOG(
     509             :                     "MembershipManager::UnregisterRibIn");
     510        1913 :                 UnregisterRibIn(table);
     511             :             } else {
     512        6118 :                 PEER_CLOSE_MANAGER_TABLE_LOG("MembershipManager::WalkRibIn");
     513        2314 :                 WalkRibIn(table);
     514             :             }
     515             :         }
     516             :     }
     517       20835 : }
     518             : 
     519       84852 : void PeerCloseManager::MembershipRequestCallback() {
     520       84852 :     EnqueueEvent(new Event(MEMBERSHIP_REQUEST_COMPLETE_CALLBACK));
     521       84964 : }
     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       95428 : bool PeerCloseManager::MembershipRequestCallback(Event *event) {
     528       95428 :     CHECK_CONCURRENCY(peer_close_->GetTaskName());
     529             : 
     530       95428 :     bool result = false;
     531      114038 :     PEER_CLOSE_MANAGER_LOG("MembershipRequestCallback");
     532             : 
     533       95428 :     if (!AssertMembershipManagerInUse())
     534           0 :         return result;
     535       95428 :     if (--membership_req_pending_)
     536       73646 :         return result;
     537             : 
     538             :     // Indicate to the caller that we are done using the membership manager.
     539       21782 :     result = true;
     540             : 
     541       21782 :     if (state_ == DELETE) {
     542       19611 :         MOVE_TO_STATE(NONE);
     543       18782 :         peer_close_->Delete();
     544       18782 :         gr_elapsed_ = 0;
     545       18782 :         llgr_elapsed_ = 0;
     546       18782 :         stats_.init++;
     547       18782 :         close_again_ = false;
     548       18782 :         graceful_ = true;
     549       18782 :         set_membership_state(MEMBERSHIP_NONE);
     550       18782 :         return result;
     551             :     }
     552             : 
     553             :     // Process nested closures.
     554        3000 :     if (close_again_) {
     555           0 :         set_membership_state(MEMBERSHIP_NONE);
     556           0 :         CloseComplete();
     557             : 
     558             :         // Nested closure can make membership manager to be in use again.
     559           0 :         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        3000 :     if (state_ == STALE) {
     565        1140 :         peer_close_->CloseComplete();
     566        1335 :         MOVE_TO_STATE(GR_TIMER);
     567        1140 :         peer_close_->GetGracefulRestartFamilies(&families_);
     568             : 
     569             :         // Offset restart time with elapsed time during nested closures.
     570        1140 :         int time = peer_close_->GetGracefulRestartTime() * 1000;
     571        1140 :         time -= gr_elapsed_;
     572        1140 :         if (time < 0)
     573           0 :             time = 0;
     574        1140 :         StartRestartTimer(time);
     575        1140 :         stats_.gr_timer++;
     576        1140 :         set_membership_state(MEMBERSHIP_NONE);
     577        1140 :         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        1860 :     if (state_ == LLGR_STALE) {
     586         410 :         MOVE_TO_STATE(LLGR_TIMER);
     587         381 :         peer_close_->CloseComplete();
     588         381 :         peer_close_->GetLongLivedGracefulRestartFamilies(&families_);
     589             : 
     590             :         // Offset restart time with elapsed time during nested closures.
     591         381 :         int time = peer_close_->GetLongLivedGracefulRestartTime() * 1000;
     592         381 :         time -= llgr_elapsed_;
     593         381 :         if (time < 0)
     594           0 :             time = 0;
     595         381 :         StartRestartTimer(time);
     596         381 :         stats_.llgr_timer++;
     597         381 :         set_membership_state(MEMBERSHIP_NONE);
     598         381 :         return result;
     599             :     }
     600             : 
     601        1479 :     TriggerSweepStateActions();
     602        1479 :     return result;
     603             : }
     604             : 
     605       64924 : void PeerCloseManager::FillRouteCloseInfo(PeerCloseInfo *close_info) const {
     606       64924 :     std::map<std::string, PeerCloseRouteInfo> route_stats;
     607             : 
     608      714164 :     for (int i = 0; i < Address::NUM_FAMILIES; i++) {
     609      649240 :         if (!stats_.route_stats[i].IsSet())
     610      489637 :             continue;
     611      159603 :         PeerCloseRouteInfo route_info;
     612      159603 :         route_info.set_staled(stats_.route_stats[i].staled);
     613      159603 :         route_info.set_llgr_staled(stats_.route_stats[i].llgr_staled);
     614      159603 :         route_info.set_refreshed(stats_.route_stats[i].refreshed);
     615      159603 :         route_info.set_fresh(stats_.route_stats[i].fresh);
     616      159603 :         route_info.set_deleted(stats_.route_stats[i].deleted);
     617      159603 :         route_stats[Address::FamilyToString(static_cast<Address::Family>(i))] = route_info;
     618      159603 :     }
     619             : 
     620       64924 :     if (!route_stats.empty())
     621       63995 :         close_info->set_route_stats(route_stats);
     622       64924 : }
     623             : 
     624       64924 : BgpNeighborResp *PeerCloseManager::FillCloseInfo(BgpNeighborResp *resp) const {
     625       64924 :     PeerCloseInfo peer_close_info;
     626       64924 :     peer_close_info.set_state(GetStateName(state_));
     627       64924 :     peer_close_info.set_membership_state(
     628      129848 :         GetMembershipStateName(membership_state_));
     629       64924 :     peer_close_info.set_close_again(close_again_);
     630       64924 :     peer_close_info.set_graceful(graceful_);
     631       64924 :     peer_close_info.set_init(stats_.init);
     632       64924 :     peer_close_info.set_close(stats_.close);
     633       64924 :     peer_close_info.set_nested(stats_.nested);
     634       64924 :     peer_close_info.set_deletes(stats_.deletes);
     635       64924 :     peer_close_info.set_stale(stats_.stale);
     636       64924 :     peer_close_info.set_llgr_stale(stats_.llgr_stale);
     637       64924 :     peer_close_info.set_sweep(stats_.sweep);
     638       64924 :     peer_close_info.set_gr_timer(stats_.gr_timer);
     639       64924 :     peer_close_info.set_llgr_timer(stats_.llgr_timer);
     640       64924 :     FillRouteCloseInfo(&peer_close_info);
     641             : 
     642       64924 :     resp->set_peer_close_info(peer_close_info);
     643             : 
     644       64924 :     return resp;
     645       64924 : }
     646             : 
     647      216932 : void PeerCloseManager::UpdateRouteStats(Address::Family family,
     648             :         const BgpPath *old_path, uint32_t path_flags) const {
     649      216932 :     if (state_ == NONE)
     650      196793 :         return;
     651             : 
     652       20139 :     if (!old_path)
     653          88 :         stats_.route_stats[family].fresh++;
     654       20051 :     else if (old_path->IsStale() && !(path_flags & BgpPath::Stale))
     655        3519 :         stats_.route_stats[family].refreshed++;
     656             : }
     657             : 
     658      100272 : bool PeerCloseManager::MembershipPathCallback(DBTablePartBase *root,
     659             :                                               BgpRoute *rt, BgpPath *path) {
     660      100272 :     CHECK_CONCURRENCY("db::DBTable");
     661      100276 :     DBRequest::DBOperation oper = DBRequest::DB_ENTRY_INVALID;
     662      100276 :     BgpAttrPtr attrs;
     663             : 
     664      100276 :     BgpTable *table = static_cast<BgpTable *>(root->parent());
     665      100274 :     assert(table);
     666             : 
     667      100274 :     uint32_t stale = 0;
     668             : 
     669      100274 :     switch (state_) {
     670           0 :         case NONE:
     671             :         case GR_TIMER:
     672             :         case LLGR_TIMER:
     673           0 :             return false;
     674             : 
     675        6181 :         case SWEEP:
     676             : 
     677             :             // Stale paths must be deleted.
     678        6181 :             if (!path->IsStale() && !path->IsLlgrStale())
     679        3084 :                 return false;
     680        3097 :             if (path->IsStale()) {
     681        3097 :                 path->ResetStale();
     682        3098 :                 table->UpdateStalePathCount(-1);
     683             :             }
     684        3100 :             if (path->IsLlgrStale()) {
     685           0 :                 path->ResetLlgrStale();
     686           0 :                 table->UpdateLlgrStalePathCount(-1);
     687             :             }
     688        3100 :             oper = DBRequest::DB_ENTRY_DELETE;
     689        3100 :             attrs = NULL;
     690        3099 :             stats_.route_stats[table->family()].deleted++;
     691        3101 :             break;
     692             : 
     693       75973 :         case DELETE:
     694             : 
     695             :             // This path must be deleted. Hence attr is not required.
     696       75973 :             oper = DBRequest::DB_ENTRY_DELETE;
     697       75973 :             attrs = NULL;
     698       75975 :             stats_.route_stats[table->family()].deleted++;
     699       76008 :             break;
     700             : 
     701       14893 :         case STALE:
     702             : 
     703             :             // We do not support GR for multicast routes (yet).
     704       29786 :             if ((table->family() == Address::ERMVPN) ||
     705       14893 :                     (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       14893 :             if (path->IsStale())
     716        3342 :                 return false;
     717             : 
     718             :             // This path must be marked for staling. Update the local
     719             :             // preference and update the route accordingly.
     720       11552 :             oper = DBRequest::DB_ENTRY_ADD_CHANGE;
     721       11552 :             attrs = path->GetAttr();
     722       11552 :             stale = BgpPath::Stale;
     723       11552 :             stats_.route_stats[table->family()].staled++;
     724       11554 :             break;
     725             : 
     726        3228 :         case LLGR_STALE:
     727             : 
     728             :             // If the path has NO_LLGR community, DELETE it.
     729        3228 :             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        3231 :             if (path->IsLlgrStale())
     742           0 :                 return false;
     743             : 
     744        3230 :             attrs = path->GetAttr();
     745        3235 :             stale = BgpPath::LlgrStale;
     746        3235 :             oper = DBRequest::DB_ENTRY_ADD_CHANGE;
     747        3235 :             stats_.route_stats[table->family()].llgr_staled++;
     748        3236 :             break;
     749             :     }
     750             : 
     751             :     // Feed the route modify/delete request to the table input process.
     752      281664 :     return table->InputCommon(root, rt, path, peer_close_->peer(), NULL, oper,
     753       93884 :         attrs, path->GetPathId(), path->GetFlags() | stale, path->GetLabel(),
     754       93885 :         path->GetL3Label());
     755      100308 : }
     756             : 
     757             : //
     758             : // Handler for an Event.
     759             : //
     760      144129 : bool PeerCloseManager::EventCallback(Event *event) {
     761      144129 :     CHECK_CONCURRENCY(peer_close_->GetTaskName());
     762             :     bool result;
     763             : 
     764      144129 :     switch (event->event_type) {
     765           0 :     case EVENT_NONE:
     766           0 :         break;
     767       21048 :     case CLOSE:
     768       21048 :         Close(event);
     769       21048 :         break;
     770       15150 :     case EOR_RECEIVED:
     771       15150 :         ProcessEORMarkerReceived(event);
     772       15150 :         break;
     773       20239 :     case MEMBERSHIP_REQUEST:
     774       20239 :         MembershipRequest(event);
     775       20239 :         break;
     776       84977 :     case MEMBERSHIP_REQUEST_COMPLETE_CALLBACK:
     777       84977 :         result = MembershipRequestCallback(event);
     778             : 
     779             :         // Notify clients if we are no longer using the membership mgr.
     780       84977 :         if (result)
     781       11331 :             peer_close_->MembershipRequestCallbackComplete();
     782       84977 :         break;
     783        2715 :     case TIMER_CALLBACK:
     784        2715 :         RestartTimerCallback(event);
     785        2715 :         break;
     786             :     }
     787             : 
     788      144129 :     delete event;
     789      144129 :     return true;
     790             : }

Generated by: LCOV version 1.14