LCOV - code coverage report
Current view: top level - vnsw/agent/services - dhcp_lease_db.cc (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 0 277 0.0 %
Date: 2026-08-03 02:19:58 Functions: 0 23 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : 
       2             : #include <pugixml/pugixml.hpp>
       3             : #include <boost/filesystem.hpp>
       4             : #include "base/address_util.h"
       5             : #include "base/timer.h"
       6             : #include "cmn/agent_cmn.h"
       7             : #include "init/agent_param.h"
       8             : #include "services/services_types.h"
       9             : #include "services/services_init.h"
      10             : #include "services/dhcp_lease_db.h"
      11             : #include "services/dhcp_proto.h"
      12             : 
      13             : using namespace pugi;
      14             : 
      15           0 : DhcpLeaseDb::DhcpLeaseDb(const Ip4Address &subnet, uint8_t plen,
      16             :                          const std::vector<Ip4Address> &reserve_addresses,
      17             :                          const std::string &lease_filename,
      18           0 :                          boost::asio::io_context &io) :
      19           0 :     subnet_(subnet), plen_(plen),
      20           0 :     max_lease_update_count_(0), lease_update_count_(0),
      21           0 :     lease_timeout_(kDhcpLeaseTimer), lease_filename_(lease_filename) {
      22           0 :     ReserveAddresses(reserve_addresses, true);
      23           0 :     LoadLeaseFile();
      24           0 :     timer_ = TimerManager::CreateTimer(io, "DhcpLeaseTimer",
      25             :                                        TaskScheduler::GetInstance()->
      26             :                                        GetTaskId("Agent::Services"),
      27             :                                        PktHandler::DHCP);
      28           0 :     timer_->Start(lease_timeout_,
      29             :                   boost::bind(&DhcpLeaseDb::LeaseTimerExpiry, this));
      30           0 : }
      31             : 
      32           0 : DhcpLeaseDb::~DhcpLeaseDb() {
      33           0 :     lease_bitmap_.clear();
      34           0 :     released_lease_bitmap_.clear();
      35           0 :     timer_->Cancel();
      36           0 :     TimerManager::DeleteTimer(timer_);
      37             :     // remove(lease_filename_.c_str());
      38           0 : }
      39             : 
      40           0 : void DhcpLeaseDb::Update(const Ip4Address &subnet, uint8_t plen,
      41             :                          const std::vector<Ip4Address> &reserve_addresses) {
      42             :     // TODO: in case we update after allocating addresses from earlier subnet,
      43             :     // they will be ignored; no trace of that in the agent after this update;
      44           0 :     bool subnet_change = false;
      45           0 :     if (subnet != subnet_ || plen != plen_) {
      46           0 :         subnet_ = subnet;
      47           0 :         plen_ = plen;
      48           0 :         leases_.clear();
      49           0 :         lease_bitmap_.clear();
      50           0 :         released_lease_bitmap_.clear();
      51           0 :         remove(lease_filename_.c_str());
      52           0 :         subnet_change = true;
      53             :     }
      54           0 :     ReserveAddresses(reserve_addresses, subnet_change);
      55           0 : }
      56             : 
      57           0 : bool DhcpLeaseDb::Allocate(const MacAddress &mac, Ip4Address *ip,
      58             :                            uint64_t lease) {
      59             :     size_t index;
      60           0 :     uint64_t expiry = ClockMonotonicUsec() + (lease * 1000000);
      61             : 
      62             :     std::set<DhcpLease>::iterator it =
      63           0 :         leases_.find(DhcpLease(mac, Ip4Address(), 0, false));
      64           0 :     if (it != leases_.end()) {
      65           0 :         index = AddressToIndex(it->ip_);
      66           0 :         if (!IsReservedAddress(it->ip_) &&
      67           0 :             (!it->released_ || released_lease_bitmap_[index])) {
      68           0 :             *ip = it->ip_;
      69           0 :             lease_update_count_++;
      70           0 :             UpdateLease(mac, *ip, expiry, false);
      71           0 :             PersistLeaseRecord(mac, *ip, expiry, false);
      72           0 :             return true;
      73             :         } else {
      74             :             // A reserved address was leased earlier or the lease has been
      75             :             // given to a different client, cannot give the same
      76             :             // lease now; release it and allocate newly
      77           0 :             leases_.erase(it);
      78             :         }
      79             :     }
      80             : 
      81           0 :     index = lease_bitmap_.find_first();
      82           0 :     if (index == lease_bitmap_.npos) {
      83           0 :         index = released_lease_bitmap_.find_first();
      84           0 :         if (index == released_lease_bitmap_.npos) {
      85           0 :             DHCP_TRACE(Error, "DHCP Lease not available for MAC " <<
      86             :                        mac.ToString() << " in Subnet " <<
      87             :                        subnet_.to_string());
      88           0 :             return false;
      89             :         }
      90             :     }
      91             : 
      92           0 :     IndexToAddress(index, ip);
      93           0 :     UpdateLease(mac, *ip, expiry, false);
      94           0 :     PersistLeaseRecord(mac, *ip, expiry, false);
      95           0 :     return true;
      96             : }
      97             : 
      98           0 : bool DhcpLeaseDb::Release(const MacAddress &mac) {
      99             :     std::set<DhcpLease>::const_iterator it =
     100           0 :         leases_.find(DhcpLease(mac, Ip4Address(), 0, false));
     101           0 :     if (it != leases_.end()) {
     102           0 :         lease_update_count_++;
     103           0 :         UpdateLease(mac, it->ip_, it->lease_expiry_time_, true);
     104           0 :         PersistLeaseRecord(mac, it->ip_, it->lease_expiry_time_, true);
     105           0 :         return true;
     106             :     }
     107             : 
     108           0 :     return false;
     109             : }
     110             : 
     111           0 : bool DhcpLeaseDb::LeaseTimerExpiry() {
     112           0 :     uint64_t current_time = ClockMonotonicUsec();
     113             : 
     114           0 :     std::vector<DhcpLease> changed_leases;
     115           0 :     for (std::set<DhcpLease>::iterator it = leases_.begin();
     116           0 :          it != leases_.end(); ) {
     117           0 :         size_t index = AddressToIndex(it->ip_);
     118           0 :         if (it->released_ && !released_lease_bitmap_[index]) {
     119             :             // lease is not valid and address is re-allocated; remove the lease
     120           0 :             DHCP_TRACE(Trace, "DHCP Lease removed : " << it->mac_.ToString() <<
     121             :                        it->ip_.to_string());
     122           0 :             leases_.erase(it++);
     123           0 :             continue;
     124           0 :         }
     125           0 :         if (current_time > it->lease_expiry_time_) {
     126           0 :             it->released_ = true;
     127           0 :             released_lease_bitmap_[index] = 1;
     128           0 :             changed_leases.push_back(*it);
     129             :         }
     130           0 :         it++;
     131             :     }
     132             : 
     133           0 :     lease_update_count_ += changed_leases.size();
     134           0 :     if (lease_update_count_ >= max_lease_update_count_) {
     135             :         // Re-create the lease file, so that it is compacted
     136           0 :         CreateLeaseFile();
     137           0 :         lease_update_count_ = 0;
     138           0 :     } else if (changed_leases.size()) {
     139           0 :         PersistLeaseRecords(changed_leases);
     140             :     }
     141             : 
     142           0 :     return true;
     143           0 : }
     144             : 
     145           0 : void DhcpLeaseDb::UpdateLease(const MacAddress &mac, const Ip4Address &ip,
     146             :                               uint64_t expiry, bool released) {
     147           0 :     size_t index = AddressToIndex(ip);
     148           0 :     lease_bitmap_[index] = 0;
     149           0 :     released_lease_bitmap_[index] = (released) ? 1 : 0;
     150             :     std::set<DhcpLease>::iterator it =
     151           0 :         leases_.find(DhcpLease(mac, Ip4Address(), 0, false));
     152           0 :     if (it != leases_.end()) {
     153           0 :         it->ip_ = ip;
     154           0 :         it->lease_expiry_time_ = expiry;
     155           0 :         it->released_ = released;
     156             :     } else {
     157           0 :         leases_.insert(DhcpLease(mac, ip, expiry, released));
     158             :     }
     159           0 :     DHCP_TRACE(Trace, "DHCP Lease : " << mac.ToString() << " " <<
     160             :                ip.to_string() << " " << expiry << " " <<
     161             :                (released ? "released" : "valid"));
     162           0 : }
     163             : 
     164             : // block the reserved addresses
     165           0 : void DhcpLeaseDb::ReserveAddresses(const std::vector<Ip4Address> &addresses,
     166             :                                    bool subnet_change) {
     167           0 :     if (subnet_change) {
     168           0 :         uint32_t num_bits = 1 << (32 - plen_);
     169           0 :         if (num_bits < (2 + addresses.size()))
     170           0 :             return;
     171             : 
     172           0 :         lease_bitmap_.resize(num_bits, 1);
     173           0 :         lease_bitmap_[0] = 0;
     174           0 :         lease_bitmap_[(1 << (32 - plen_)) - 1] = 0;
     175           0 :         released_lease_bitmap_.resize(num_bits, 0);
     176           0 :         max_lease_update_count_ = (num_bits < 256) ? 256 :
     177             :                                   ((num_bits > 8192) ? 8192 : num_bits);
     178             :     }
     179           0 :     for (std::vector<Ip4Address>::const_iterator it = addresses.begin();
     180           0 :          it != addresses.end(); ++it) {
     181           0 :         if (IsIp4SubnetMember(*it, subnet_, plen_)) {
     182           0 :             size_t index = AddressToIndex(*it);
     183           0 :             lease_bitmap_[index] = 0;
     184           0 :             released_lease_bitmap_[index] = 0;
     185             :         }
     186             :     }
     187           0 :     reserve_addresses_ = addresses;
     188             : }
     189             : 
     190           0 : void DhcpLeaseDb::IndexToAddress(size_t index, Ip4Address *address) const {
     191           0 :     Ip4Address ip(subnet_.to_ulong() + index);
     192           0 :     *address = ip;
     193           0 : }
     194             : 
     195           0 : size_t DhcpLeaseDb::AddressToIndex(const Ip4Address &address) const {
     196           0 :     return address.to_ulong() & (0xFFFFFFFF >> plen_);
     197             : }
     198             : 
     199           0 : bool DhcpLeaseDb::IsReservedAddress(const Ip4Address &address) const {
     200           0 :     for (std::vector<Ip4Address>::const_iterator it =
     201           0 :          reserve_addresses_.begin(); it != reserve_addresses_.end(); ++it) {
     202           0 :         if (address == *it)
     203           0 :             return true;
     204             :     }
     205           0 :     return false;
     206             : }
     207             : 
     208             : // Added for UT
     209           0 : void DhcpLeaseDb::ClearLeases() {
     210             :     // clear the lease bitmaps and leases
     211           0 :     leases_.clear();
     212           0 :     lease_bitmap_.set();
     213           0 :     released_lease_bitmap_.reset();
     214           0 :     ReserveAddresses(reserve_addresses_, true);
     215           0 : }
     216             : 
     217             : // Added for UT
     218           0 : void DhcpLeaseDb::set_lease_timeout(uint32_t timeout) {
     219           0 :     if (lease_timeout_ != timeout) {
     220           0 :         lease_timeout_ = timeout;
     221           0 :         timer_->Cancel();
     222           0 :         timer_->Start(lease_timeout_,
     223             :                       boost::bind(&DhcpLeaseDb::LeaseTimerExpiry, this));
     224             :     }
     225           0 : }
     226             : 
     227             : // Write the complete lease file
     228           0 : void DhcpLeaseDb::CreateLeaseFile() {
     229           0 :     std::ofstream lease_ofstream;
     230           0 :     lease_ofstream.open(lease_filename_.c_str(),
     231             :                         std::ofstream::out | std::ofstream::trunc);
     232           0 :     if (!lease_ofstream.good()) {
     233           0 :         lease_ofstream.close();
     234           0 :         DHCP_TRACE(Error, "Cannot create DHCP Lease file: " << lease_filename_);
     235           0 :         return;
     236             :     }
     237             : 
     238           0 :     for (std::set<DhcpLease>::const_iterator it = leases_.begin();
     239           0 :          it != leases_.end(); ++it) {
     240           0 :         WriteLeaseRecord(lease_ofstream, it->mac_, it->ip_,
     241           0 :                          it->lease_expiry_time_, it->released_);
     242             :     }
     243             : 
     244           0 :     lease_ofstream.flush();
     245           0 :     lease_ofstream.close();
     246           0 : }
     247             : 
     248             : // Append lease record
     249           0 : void DhcpLeaseDb::PersistLeaseRecord(const MacAddress &mac,
     250             :                                      const Ip4Address &ip,
     251             :                                      const uint64_t &expiry,
     252             :                                      bool released) {
     253           0 :     std::ofstream lease_ofstream;
     254           0 :     lease_ofstream.open(lease_filename_.c_str(), std::ofstream::app);
     255           0 :     if (!lease_ofstream.good()) {
     256           0 :         lease_ofstream.close();
     257           0 :         DHCP_TRACE(Error, "Cannot open DHCP Lease file for writing : " <<
     258             :                    lease_filename_);
     259           0 :         return;
     260             :     }
     261             : 
     262           0 :     WriteLeaseRecord(lease_ofstream, mac, ip, expiry, released);
     263             : 
     264           0 :     lease_ofstream.flush();
     265           0 :     lease_ofstream.close();
     266           0 : }
     267             : 
     268           0 : void DhcpLeaseDb::PersistLeaseRecords(const std::vector<DhcpLease> &leases) {
     269           0 :     std::ofstream lease_ofstream;
     270           0 :     lease_ofstream.open(lease_filename_.c_str(), std::ofstream::app);
     271           0 :     if (!lease_ofstream.good()) {
     272           0 :         lease_ofstream.close();
     273           0 :         DHCP_TRACE(Error, "Cannot open DHCP Lease file for writing : "
     274             :                    << lease_filename_);
     275           0 :         return;
     276             :     }
     277             : 
     278           0 :     for (std::vector<DhcpLease>::const_iterator it = leases.begin();
     279           0 :          it != leases.end(); ++it) {
     280           0 :         WriteLeaseRecord(lease_ofstream, it->mac_, it->ip_,
     281           0 :                          it->lease_expiry_time_, it->released_);
     282             :     }
     283             : 
     284           0 :     lease_ofstream.flush();
     285           0 :     lease_ofstream.close();
     286           0 : }
     287             : 
     288           0 : void DhcpLeaseDb::WriteLeaseRecord(std::ofstream &lease_ofstream,
     289             :                                    const MacAddress &mac, const Ip4Address &ip,
     290             :                                    const uint64_t &expiry, bool released) {
     291           0 :     lease_ofstream << "<lease>";
     292           0 :     lease_ofstream << " <mac>" << mac.ToString() << "</mac>";
     293           0 :     lease_ofstream << " <ip>" << ip.to_string() << "</ip>";
     294           0 :     lease_ofstream << " <expiry>" << expiry << "</expiry>";
     295             :     lease_ofstream << " <released>" <<
     296           0 :         (released ? "true" : "false") << "</released>";
     297           0 :     lease_ofstream << " </lease>" << std::endl;
     298             : 
     299           0 :     if (!lease_ofstream.good()) {
     300           0 :         DHCP_TRACE(Error, "Lease write to " << lease_filename_ <<
     301             :                    " failed : " << mac.ToString() << " " << ip.to_string() <<
     302             :                    (released ? " released" : " valid"));
     303             :     }
     304           0 : }
     305             : 
     306           0 : void DhcpLeaseDb::LoadLeaseFile() {
     307           0 :     std::string leases;
     308           0 :     ReadLeaseFile(leases);
     309           0 :     ParseLeaseFile(leases);
     310           0 : }
     311             : 
     312           0 : void DhcpLeaseDb::ReadLeaseFile(std::string &leases) {
     313           0 :     std::ifstream ifile(lease_filename_.c_str());
     314           0 :     if (!ifile.good()) {
     315           0 :         ifile.close();
     316           0 :         DHCP_TRACE(Error, "Cannot open DHCP Lease file for reading : " <<
     317             :                    lease_filename_);
     318           0 :         return;
     319             :     }
     320             : 
     321           0 :     ifile.seekg(0, std::ios::end);
     322           0 :     leases.reserve(ifile.tellg());
     323           0 :     ifile.seekg(0, std::ios::beg);
     324             : 
     325           0 :     leases.assign((std::istreambuf_iterator<char>(ifile)),
     326             :                    std::istreambuf_iterator<char>());
     327           0 :     ifile.close();
     328           0 : }
     329             : 
     330           0 : void DhcpLeaseDb::ParseLeaseFile(const std::string &leases) {
     331           0 :     if (leases.empty())
     332           0 :         return;
     333             : 
     334           0 :     std::istringstream sstream(leases);
     335           0 :     xml_document xdoc;
     336           0 :     xml_parse_result result = xdoc.load(sstream);
     337           0 :     if (!result) {
     338           0 :         DHCP_TRACE(Error, "Unable to load DHCP leases. status=" <<
     339             :                    result.status << ", offset=" << result.offset);
     340           0 :         return;
     341             :     }
     342             : 
     343           0 :     for (xml_node root = xdoc.first_child(); root; root = root.next_sibling()) {
     344           0 :         if (strcmp(root.name(), "lease") == 0) {
     345           0 :             ParseLease(root);
     346             :         }
     347             :     }
     348           0 : }
     349             : 
     350           0 : void DhcpLeaseDb::ParseLease(const xml_node &lease) {
     351           0 :     MacAddress mac;
     352           0 :     Ip4Address ip;
     353           0 :     uint64_t expiry = 0;
     354           0 :     boost::system::error_code ec;
     355           0 :     bool released = false;
     356           0 :     bool error = false;
     357           0 :     for (xml_node root = lease.first_child(); root; root = root.next_sibling()) {
     358           0 :         if (strcmp(root.name(), "mac") == 0) {
     359           0 :             mac = MacAddress::FromString(root.child_value(), &ec);
     360           0 :             if (ec)
     361           0 :                 error = true;
     362           0 :             continue;
     363             :         }
     364           0 :         if (strcmp(root.name(), "ip") == 0) {
     365           0 :             ip = Ip4Address::from_string(root.child_value(), ec);
     366           0 :             if (ec)
     367           0 :                 error = true;
     368           0 :             continue;
     369             :         }
     370           0 :         if (strcmp(root.name(), "expiry") == 0) {
     371             :             char *endp;
     372           0 :             expiry = strtoull(root.child_value(), &endp, 10);
     373           0 :             while (isspace(*endp)) endp++;
     374           0 :             if (endp[0] != '\0')
     375           0 :                 error = true;
     376           0 :             continue;
     377           0 :         }
     378           0 :         if (strcmp(root.name(), "released") == 0) {
     379           0 :             if (strcmp(root.child_value(), "true") == 0)
     380           0 :                 released = true;
     381             :         }
     382             :     }
     383             : 
     384           0 :     if (mac.IsZero() || ip.is_unspecified() || error) {
     385           0 :         DHCP_TRACE(Error, "Invalid DHCP Lease record : " << mac.ToString() << " " <<
     386             :                    ip.to_string() << " " << expiry);
     387           0 :         return;
     388             :     }
     389             : 
     390           0 :     UpdateLease(mac, ip, expiry, released);
     391             : }
     392             : 
     393           0 : void ShowGwDhcpLeases::HandleRequest() const {
     394           0 :     std::vector<GwDhcpLeases> lease_list;
     395             :     const DhcpProto::LeaseManagerMap &lease_mgr =
     396           0 :         Agent::GetInstance()->GetDhcpProto()->lease_manager();
     397           0 :     for (DhcpProto::LeaseManagerMap::const_iterator it = lease_mgr.begin();
     398           0 :          it != lease_mgr.end(); ++it) {
     399           0 :         GwDhcpLeases gw_leases;
     400           0 :         std::vector<DhcpLeaseData> out_lease_data;
     401             :         const std::set<DhcpLeaseDb::DhcpLease> &lease_data =
     402           0 :             it->second->leases();
     403           0 :         for (std::set<DhcpLeaseDb::DhcpLease>::const_iterator lit =
     404           0 :              lease_data.begin(); lit != lease_data.end(); ++lit) {
     405           0 :             uint64_t current_time = ClockMonotonicUsec();
     406           0 :             DhcpLeaseData data;
     407           0 :             data.mac = lit->mac_.ToString();
     408           0 :             data.ip = lit->ip_.to_string();
     409           0 :             data.expiry_us = (lit->lease_expiry_time_ > current_time) ?
     410           0 :                 (lit->lease_expiry_time_ - current_time) : 0;
     411           0 :             data.released = lit->released_ ? "yes" : "no";
     412           0 :             out_lease_data.push_back(data);
     413           0 :         }
     414           0 :         VmInterface *vmi = static_cast<VmInterface *>(it->first);
     415           0 :         gw_leases.physical_interface = vmi->parent()->name();
     416           0 :         gw_leases.vm_interface = vmi->name();
     417           0 :         gw_leases.leases = out_lease_data;
     418           0 :         lease_list.push_back(gw_leases);
     419           0 :     }
     420             : 
     421           0 :     GwDhcpLeasesResponse *resp = new GwDhcpLeasesResponse();
     422           0 :     resp->set_context(context());
     423           0 :     resp->set_gw_leases(lease_list);
     424           0 :     resp->Response();
     425           0 : }

Generated by: LCOV version 1.14