LCOV - code coverage report
Current view: top level - bfd - bfd_session.cc (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 186 199 93.5 %
Date: 2026-06-18 01:51:13 Functions: 25 28 89.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2014 CodiLime, Inc. All rights reserved.
       3             :  */
       4             : 
       5             : #include "bfd/bfd_session.h"
       6             : #include "bfd/bfd_control_packet.h"
       7             : #include "bfd/bfd_common.h"
       8             : #include "bfd/bfd_connection.h"
       9             : 
      10             : #include <boost/asio.hpp>
      11             : #include <boost/random.hpp>
      12             : #include <string>
      13             : #include <algorithm>
      14             : 
      15             : #include "base/logging.h"
      16             : 
      17             : namespace BFD {
      18             : 
      19          29 : Session::Session(Discriminator localDiscriminator,
      20             :         const SessionKey &key,
      21             :         EventManager *evm,
      22          29 :         const SessionConfig &config, Connection *communicator) :
      23          29 :         localDiscriminator_(localDiscriminator),
      24          29 :         key_(key),
      25          29 :         sendTimer_(TimerManager::CreateTimer(*evm->io_service(),
      26             :             "BFD TX", TaskScheduler::GetInstance()->GetTaskId("BFD"), 0)),
      27          29 :         recvTimer_(TimerManager::CreateTimer(*evm->io_service(),
      28             :             "BFD RX", TaskScheduler::GetInstance()->GetTaskId("BFD"), 0)),
      29          29 :         currentConfig_(config),
      30          29 :         nextConfig_(config),
      31          29 :         sm_(CreateStateMachine(evm, this)),
      32          29 :         pollSequence_(false),
      33          29 :         communicator_(communicator),
      34          29 :         local_endpoint_(key.local_address, GetRandomLocalPort()),
      35          29 :         remote_endpoint_(key.remote_address, key.remote_port),
      36          29 :         started_(false),
      37          87 :         stopped_(false) {
      38          29 :     ScheduleSendTimer();
      39          29 :     ScheduleRecvDeadlineTimer();
      40          58 :     sm_->SetCallback(boost::optional<ChangeCb>(
      41          29 :         boost::bind(&Session::CallStateChangeCallbacks, this, _1, _2)));
      42          29 : }
      43             : 
      44          29 : uint16_t Session::GetRandomLocalPort() const {
      45          29 :     boost::random::uniform_int_distribution<> dist(kSendPortMin, kSendPortMax);
      46          29 :     return dist(randomGen);
      47             : }
      48             : 
      49          57 : Session::~Session() {
      50          32 :     Stop();
      51          32 :     callbacks_.clear();
      52          57 : }
      53             : 
      54         116 : bool Session::SendTimerExpired() {
      55         116 :     ControlPacket packet;
      56             : 
      57         116 :     stats_.send_timer_expired_count++;
      58         116 :     PreparePacket(nextConfig_, &packet);
      59         116 :     SendPacket(&packet);
      60             : 
      61             :     // Workaround: Timer code isn't re-entrant
      62         116 :     this->sendTimer_->Reschedule(tx_interval().total_milliseconds());
      63         116 :     return true;
      64         116 : }
      65             : 
      66           8 : bool Session::RecvTimerExpired() {
      67             : 
      68           8 :     if (local_state_non_locking() == kUp) {
      69             :         // Bfd state will transition to Down state,
      70             :         // restore to default values
      71           3 :         remoteSession_.minRxInterval = boost::posix_time::seconds(1);
      72           3 :         remoteSession_.minTxInterval = boost::posix_time::seconds(0);
      73             :     }
      74           8 :     sm_->ProcessTimeout();
      75           8 :     stats_.receive_timer_expired_count++;
      76             : 
      77           8 :     return false;
      78             : }
      79             : 
      80           1 : std::string Session::toString() const {
      81           1 :     std::ostringstream out;
      82           1 :     out << "SessionKey: " << key_.to_string() << "\n";
      83           1 :     out << "LocalDiscriminator: 0x" << std::hex << localDiscriminator_ << "\n";
      84           1 :     out << "RemoteDiscriminator: 0x" << std::hex << remoteSession_.discriminator
      85           1 :         << "\n";
      86           1 :     out << "DesiredMinTxInterval: " << currentConfig_.desiredMinTxInterval
      87           1 :         << "\n";
      88           1 :     out << "RequiredMinRxInterval: " << currentConfig_.requiredMinRxInterval
      89           1 :         << "\n";
      90           1 :     out << "RemoteMinRxInterval: " << remoteSession_.minRxInterval << "\n";
      91           1 :     out << "RemoteMinTxInterval: " << remoteSession_.minTxInterval << "\n";
      92           1 :     out << "Local State:" << local_state() << "\n";
      93           1 :     out << "Remote State:" << remote_state().state << "\n";
      94             : 
      95           2 :     return out.str();
      96           1 : }
      97             : 
      98          33 : void Session::ScheduleSendTimer() {
      99             :     int elapsed_time_ms;
     100             :     int remaining_time_ms;
     101          33 :     TimeInterval ti = tx_interval();
     102             : 
     103             :     // get the elapsed time only if the bfd session timer is running,
     104             :     // otherwise program the config send timer value
     105          33 :     if (started_ == true) {
     106           4 :         elapsed_time_ms = sendTimer_->GetElapsedTime();
     107           4 :         sendTimer_->Cancel();
     108           4 :         if (elapsed_time_ms < 0) {
     109           0 :             remaining_time_ms = 0;
     110             :         } else {
     111           4 :             remaining_time_ms = ti.total_milliseconds() - elapsed_time_ms;
     112             :         }
     113             :     } else {
     114             :         // timer not yet started, program with config value
     115          29 :         remaining_time_ms = ti.total_milliseconds();
     116             :     }
     117             : 
     118          33 :     if (remaining_time_ms > 0) {
     119          30 :         sendTimer_->Start(remaining_time_ms,
     120             :                 boost::bind(&Session::SendTimerExpired, this));
     121             :     } else {
     122             :         // fire the timer now!
     123           3 :         sendTimer_->Start(0,
     124             :                 boost::bind(&Session::SendTimerExpired, this));
     125             :     }
     126          33 :     if (started_ != true) {
     127          29 :         started_ = true;
     128             :     }
     129          33 : }
     130             : 
     131         142 : void Session::ScheduleRecvDeadlineTimer() {
     132         142 :     TimeInterval ti = detection_time();
     133             : 
     134         142 :     recvTimer_->Cancel();
     135         142 :     recvTimer_->Start(ti.total_milliseconds(),
     136             :                       boost::bind(&Session::RecvTimerExpired, this));
     137         142 : }
     138             : 
     139       11852 : BFDState Session::local_state_non_locking() const {
     140       11852 :     return sm_->GetState();
     141             : }
     142             : 
     143       11261 : BFDState Session::local_state() const {
     144       11261 :     return local_state_non_locking();
     145             : }
     146             : 
     147             : //  If periodic BFD Control packets are already being sent (the remote
     148             : //  system is not in Demand mode), the Poll Sequence MUST be performed by
     149             : //  setting the Poll (P) bit on those scheduled periodic transmissions;
     150             : //  additional packets MUST NOT be sent.
     151           1 : void Session::InitPollSequence() {
     152           1 :     pollSequence_ = true;
     153           2 :     if (local_state_non_locking() != kUp &&
     154           1 :         local_state_non_locking() != kAdminDown) {
     155           1 :         ControlPacket packet;
     156           1 :         PreparePacket(nextConfig_, &packet);
     157           1 :         SendPacket(&packet);
     158           1 :     }
     159           1 : }
     160             : 
     161         121 : void Session::PreparePacket(const SessionConfig &config,
     162             :                             ControlPacket *packet) {
     163             : 
     164         121 :     packet->state = local_state_non_locking();
     165         121 :     packet->poll = pollSequence_;
     166         121 :     packet->sender_discriminator = localDiscriminator_;
     167         121 :     packet->receiver_discriminator = remoteSession_.discriminator;
     168         121 :     packet->detection_time_multiplier = config.detectionTimeMultiplier;
     169         121 :     packet->desired_min_tx_interval = config.desiredMinTxInterval;
     170         121 :     packet->required_min_rx_interval = config.requiredMinRxInterval;
     171         121 : }
     172             : 
     173         115 : ResultCode Session::ProcessControlPacket(const ControlPacket *packet) {
     174         115 :     TimeInterval oldMinRxInterval = remoteSession_.minRxInterval;
     175         115 :     remoteSession_.discriminator = packet->sender_discriminator;
     176         115 :     remoteSession_.detectionTimeMultiplier = packet->detection_time_multiplier;
     177         115 :     remoteSession_.state = packet->state;
     178         115 :     if ((local_state_non_locking() == kUp) && packet->poll) {
     179           3 :         remoteSession_.minTxInterval = packet->desired_min_tx_interval;
     180           3 :         if (packet->required_min_rx_interval < remoteSession_.minRxInterval) {
     181           2 :             remoteSession_.minRxInterval = packet->required_min_rx_interval;
     182           2 :             ScheduleSendTimer();
     183             :         } else {
     184             :             // After sending the BFD pkt with previous agreed rate, update
     185             :             // the SendTimer() with new remoteSession_.minRxInterval so as to
     186             :             // not impact the remote Session's detection time.
     187           1 :             remoteSession_.minRxInterval = packet->required_min_rx_interval;
     188             :         }
     189         206 :     } else if (local_state_non_locking() == kInit ||
     190          94 :                local_state_non_locking() == kDown) {
     191          51 :         remoteSession_.minRxInterval = packet->required_min_rx_interval;
     192          51 :         remoteSession_.minTxInterval = packet->desired_min_tx_interval;
     193          51 :         remoteSession_.detectionTimeMultiplier = 
     194          51 :                                      packet->detection_time_multiplier;
     195         102 :         if (packet->required_min_rx_interval.total_microseconds() && 
     196         102 :                 oldMinRxInterval >= (packet->required_min_rx_interval * 10)) {
     197             :             // reschedule the sendtimer to the new value
     198           2 :             ScheduleSendTimer();
     199             :         }
     200             :     }
     201             : 
     202         115 :     sm_->ProcessRemoteState(packet->state);
     203             : 
     204             :     // poll sequence
     205         115 :     if (packet->poll) {
     206           4 :         ControlPacket newPacket;
     207           4 :         PreparePacket(nextConfig_, &newPacket);
     208           4 :         newPacket.poll = false;  // poll & final are forbidden in single packet
     209           4 :         newPacket.final = true;
     210           4 :         SendPacket(&newPacket);
     211           4 :     }
     212         115 :     if (packet->final) {
     213           3 :         pollSequence_ = false;
     214           3 :         currentConfig_ = nextConfig_;
     215             :     }
     216             : 
     217         139 :     if (local_state_non_locking() == kUp ||
     218          24 :         local_state_non_locking() == kInit) {
     219         113 :         ScheduleRecvDeadlineTimer();
     220             :     }
     221             : 
     222         115 :     return kResultCode_Ok;
     223             : }
     224             : 
     225         121 : void Session::SendPacket(const ControlPacket *packet) {
     226             :     boost::asio::mutable_buffer buffer =
     227             :         boost::asio::mutable_buffer(new uint8_t[kMinimalPacketLength],
     228         121 :                                     kMinimalPacketLength);
     229         121 :     int pktSize = EncodeControlPacket(packet,
     230             :         boost::asio::buffer_cast<uint8_t *>(buffer), kMinimalPacketLength);
     231         121 :     if (pktSize != kMinimalPacketLength) {
     232           0 :         LOG(ERROR,
     233             :            "Unable to encode packet: pktSize " << pktSize
     234             :            << ", session: " << toString());
     235           0 :         stats_.tx_error_count++;
     236           0 :         const uint8_t *p = boost::asio::buffer_cast<const uint8_t *>(buffer);
     237           0 :         delete[] p;
     238             :     } else {
     239         121 :         communicator_->SendPacket(local_endpoint_, remote_endpoint_,
     240         121 :                                   key_.index, buffer, pktSize);
     241         121 :         stats_.tx_count++;
     242             :     }
     243         121 : }
     244             : 
     245         146 : TimeInterval Session::detection_time() {
     246         292 :     return std::max(currentConfig_.requiredMinRxInterval,
     247         146 :                     remoteSession_.minTxInterval) *
     248         146 :             remoteSession_.detectionTimeMultiplier;
     249             : }
     250             : 
     251         149 : TimeInterval Session::tx_interval() {
     252         149 :     TimeInterval minInterval, maxInterval;
     253             : 
     254             :     TimeInterval negotiatedInterval =
     255         298 :         std::max(currentConfig_.desiredMinTxInterval,
     256         149 :                 remoteSession_.minRxInterval);
     257             : 
     258         149 :     minInterval = negotiatedInterval * 3/4;
     259         149 :     if (currentConfig_.detectionTimeMultiplier == 1) {
     260           0 :         maxInterval = negotiatedInterval * 9/10;
     261             :     } else {
     262         149 :         maxInterval = negotiatedInterval;
     263             :     }
     264             : 
     265             :     boost::random::uniform_int_distribution<>
     266         298 :                 dist(minInterval.total_microseconds(),
     267         149 :                 maxInterval.total_microseconds());
     268         149 :     return boost::posix_time::microseconds(dist(randomGen));
     269             : }
     270             : 
     271          86 : const SessionKey &Session::key() const {
     272          86 :     return key_;
     273             : }
     274             : 
     275          32 : void Session::Stop() {
     276          32 :     if (stopped_ == false) {
     277          29 :         TimerManager::DeleteTimer(sendTimer_);
     278          29 :         TimerManager::DeleteTimer(recvTimer_);
     279          29 :         stopped_ = true;
     280          29 :         started_ = false;
     281          29 :         sm_->SetCallback(boost::optional<ChangeCb>());
     282             :     }
     283          32 : }
     284             : 
     285           0 : SessionConfig Session::config() const {
     286           0 :     return nextConfig_;
     287             : }
     288             : 
     289           3 : BFDRemoteSessionState Session::remote_state() const {
     290           3 :     return remoteSession_;
     291             : }
     292             : 
     293          54 : Discriminator Session::local_discriminator() const {
     294          54 :     return localDiscriminator_;
     295             : }
     296             : 
     297          52 : void Session::CallStateChangeCallbacks(
     298             :     const SessionKey &key, const BFD::BFDState &new_state) {
     299          52 :     for (Callbacks::const_iterator it = callbacks_.begin();
     300          99 :          it != callbacks_.end(); ++it) {
     301          47 :         it->second(key, new_state);
     302             :     }
     303          52 : }
     304             : 
     305          28 : void Session::RegisterChangeCallback(ClientId client_id, ChangeCb cb) {
     306          28 :     callbacks_[client_id] = cb;
     307          28 : }
     308             : 
     309           0 : void Session::UnregisterChangeCallback(ClientId client_id) {
     310           0 :     callbacks_.erase(client_id);
     311           0 : }
     312             : 
     313           3 : void Session::UpdateConfig(const SessionConfig& config) {
     314           3 :     nextConfig_.desiredMinTxInterval = config.desiredMinTxInterval;
     315           3 :     nextConfig_.requiredMinRxInterval = config.requiredMinRxInterval;
     316           3 :     nextConfig_.detectionTimeMultiplier = config.detectionTimeMultiplier;
     317           3 :     pollSequence_ = true;
     318           3 : }
     319             : 
     320           0 : int Session::reference_count() {
     321           0 :     return callbacks_.size();
     322             : }
     323             : 
     324       11217 : bool Session::Up() const {
     325       11217 :     return local_state() == kUp;
     326             : }
     327             : 
     328             : }  // namespace BFD

Generated by: LCOV version 1.14