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-11-09 02:10:49 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             :  * 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             : }

Generated by: LCOV version 1.14