diff options
Diffstat (limited to 'src/lib/dhcpsrv/memfile_lease_mgr.cc')
-rw-r--r-- | src/lib/dhcpsrv/memfile_lease_mgr.cc | 2357 |
1 files changed, 2357 insertions, 0 deletions
diff --git a/src/lib/dhcpsrv/memfile_lease_mgr.cc b/src/lib/dhcpsrv/memfile_lease_mgr.cc new file mode 100644 index 0000000..afb3259 --- /dev/null +++ b/src/lib/dhcpsrv/memfile_lease_mgr.cc @@ -0,0 +1,2357 @@ +// Copyright (C) 2012-2022 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <database/database_connection.h> +#include <dhcpsrv/cfgmgr.h> +#include <dhcpsrv/dhcpsrv_exceptions.h> +#include <dhcpsrv/dhcpsrv_log.h> +#include <dhcpsrv/lease_file_loader.h> +#include <dhcpsrv/memfile_lease_mgr.h> +#include <dhcpsrv/timer_mgr.h> +#include <exceptions/exceptions.h> +#include <stats/stats_mgr.h> +#include <util/multi_threading_mgr.h> +#include <util/pid_file.h> + +#include <cstdio> +#include <cstring> +#include <errno.h> +#include <iostream> +#include <limits> +#include <sstream> + +namespace { + +/// @brief A name of the environmental variable specifying the kea-lfc +/// program location. +/// +/// This variable can be set by tests to point to the location of the +/// kea-lfc program within a build directory. If this variable is not +/// set, the backend will use the location of the kea-lfc in the +/// Kea installation directory. +const char* KEA_LFC_EXECUTABLE_ENV_NAME = "KEA_LFC_EXECUTABLE"; + +} // namespace + +using namespace isc::asiolink; +using namespace isc::data; +using namespace isc::db; +using namespace isc::util; +using namespace isc::stats; + +namespace isc { +namespace dhcp { + +/// @brief Represents a configuration for Lease File Cleanup. +/// +/// This class is solely used by the @c Memfile_LeaseMgr as a configuration +/// information storage for %Lease File Cleanup. Internally, it creates +/// the interval timer and assigns a callback function (pointer to which is +/// passed in the constructor), which will be called at the specified +/// intervals to perform the cleanup. It is also responsible for creating +/// and maintaining the object which is used to spawn the new process which +/// executes the @c kea-lfc program. +/// +/// This functionality is enclosed in a separate class so as the implementation +/// details are not exposed in the @c Memfile_LeaseMgr header file and +/// to maintain a single place with the LFC configuration, instead of multiple +/// members and functions scattered in the @c Memfile_LeaseMgr class. +class LFCSetup { +public: + + /// @brief Constructor. + /// + /// Assigns a pointer to the function triggered to perform the cleanup. + /// This pointer should point to the appropriate method of the + /// @c Memfile_LeaseMgr class. + /// + /// @param callback A pointer to the callback function. + LFCSetup(asiolink::IntervalTimer::Callback callback); + + /// @brief Destructor. + /// + /// Unregisters LFC timer. + ~LFCSetup(); + + /// @brief Sets the new configuration for the %Lease File Cleanup. + /// + /// @param lfc_interval An interval in seconds at which the cleanup should + /// be performed. + /// @param lease_file4 A pointer to the DHCPv4 lease file to be cleaned up + /// or NULL. If this is NULL, the @c lease_file6 must be non-null. + /// @param lease_file6 A pointer to the DHCPv6 lease file to be cleaned up + /// or NULL. If this is NULL, the @c lease_file4 must be non-null. + /// @param run_once_now A flag that causes LFC to be invoked immediately, + /// regardless of the value of lfc_interval. This is primarily used to + /// cause lease file schema upgrades upon startup. + void setup(const uint32_t lfc_interval, + const boost::shared_ptr<CSVLeaseFile4>& lease_file4, + const boost::shared_ptr<CSVLeaseFile6>& lease_file6, + bool run_once_now = false); + + /// @brief Spawns a new process. + void execute(); + + /// @brief Checks if the lease file cleanup is in progress. + /// + /// @return true if the lease file cleanup is being executed. + bool isRunning() const; + + /// @brief Returns exit code of the last completed cleanup. + int getExitStatus() const; + +private: + + /// @brief A pointer to the @c ProcessSpawn object used to execute + /// the LFC. + boost::scoped_ptr<ProcessSpawn> process_; + + /// @brief A pointer to the callback function executed by the timer. + asiolink::IntervalTimer::Callback callback_; + + /// @brief A PID of the last executed LFC process. + pid_t pid_; + + /// @brief Pointer to the timer manager. + /// + /// We have to hold this pointer here to make sure that the timer + /// manager is not destroyed before the lease manager. + TimerMgrPtr timer_mgr_; +}; + +LFCSetup::LFCSetup(asiolink::IntervalTimer::Callback callback) + : process_(), callback_(callback), pid_(0), + timer_mgr_(TimerMgr::instance()) { +} + +LFCSetup::~LFCSetup() { + try { + // Remove the timer. This will throw an exception if the timer does not + // exist. There are several possible reasons for this: + // a) It hasn't been registered (although if the LFC Setup instance + // exists it means that the timer must have been registered or that + // such registration has been attempted). + // b) The registration may fail if the duplicate timer exists or if the + // TimerMgr's worker thread is running but if this happens it is a + // programming error. + // c) The program is shutting down and the timer has been removed by + // another component. + timer_mgr_->unregisterTimer("memfile-lfc"); + + } catch (const std::exception& ex) { + // We don't want exceptions being thrown from the destructor so we just + // log a message here. The message is logged at debug severity as + // we don't want an error message output during shutdown. + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, + DHCPSRV_MEMFILE_LFC_UNREGISTER_TIMER_FAILED).arg(ex.what()); + } +} + +void +LFCSetup::setup(const uint32_t lfc_interval, + const boost::shared_ptr<CSVLeaseFile4>& lease_file4, + const boost::shared_ptr<CSVLeaseFile6>& lease_file6, + bool run_once_now) { + + // If to nothing to do, punt + if (lfc_interval == 0 && !run_once_now) { + return; + } + + // Start preparing the command line for kea-lfc. + std::string executable; + char* c_executable = getenv(KEA_LFC_EXECUTABLE_ENV_NAME); + if (c_executable == NULL) { + executable = KEA_LFC_EXECUTABLE; + } else { + executable = c_executable; + } + + // Gather the base file name. + std::string lease_file = lease_file4 ? lease_file4->getFilename() : + lease_file6->getFilename(); + + // Create the other names by appending suffixes to the base name. + ProcessArgs args; + // Universe: v4 or v6. + args.push_back(lease_file4 ? "-4" : "-6"); + + // Previous file. + args.push_back("-x"); + args.push_back(Memfile_LeaseMgr::appendSuffix(lease_file, + Memfile_LeaseMgr::FILE_PREVIOUS)); + // Input file. + args.push_back("-i"); + args.push_back(Memfile_LeaseMgr::appendSuffix(lease_file, + Memfile_LeaseMgr::FILE_INPUT)); + // Output file. + args.push_back("-o"); + args.push_back(Memfile_LeaseMgr::appendSuffix(lease_file, + Memfile_LeaseMgr::FILE_OUTPUT)); + // Finish file. + args.push_back("-f"); + args.push_back(Memfile_LeaseMgr::appendSuffix(lease_file, + Memfile_LeaseMgr::FILE_FINISH)); + // PID file. + args.push_back("-p"); + args.push_back(Memfile_LeaseMgr::appendSuffix(lease_file, + Memfile_LeaseMgr::FILE_PID)); + + // The configuration file is currently unused. + args.push_back("-c"); + args.push_back("ignored-path"); + + // Create the process (do not start it yet). + process_.reset(new ProcessSpawn(LeaseMgr::getIOService(), executable, args)); + + // If we've been told to run it once now, invoke the callback directly. + if (run_once_now) { + callback_(); + } + + // If it's supposed to run periodically, setup that now. + if (lfc_interval > 0) { + // Set the timer to call callback function periodically. + LOG_INFO(dhcpsrv_logger, DHCPSRV_MEMFILE_LFC_SETUP).arg(lfc_interval); + + // Multiple the lfc_interval value by 1000 as this value specifies + // a timeout in seconds, whereas the setup() method expects the + // timeout in milliseconds. + timer_mgr_->registerTimer("memfile-lfc", callback_, lfc_interval * 1000, + asiolink::IntervalTimer::REPEATING); + timer_mgr_->setup("memfile-lfc"); + } +} + +void +LFCSetup::execute() { + try { + LOG_INFO(dhcpsrv_logger, DHCPSRV_MEMFILE_LFC_EXECUTE) + .arg(process_->getCommandLine()); + pid_ = process_->spawn(); + + } catch (const ProcessSpawnError&) { + LOG_ERROR(dhcpsrv_logger, DHCPSRV_MEMFILE_LFC_SPAWN_FAIL); + } +} + +bool +LFCSetup::isRunning() const { + return (process_ && process_->isRunning(pid_)); +} + +int +LFCSetup::getExitStatus() const { + if (!process_) { + isc_throw(InvalidOperation, "unable to obtain LFC process exit code: " + " the process is NULL"); + } + return (process_->getExitStatus(pid_)); +} + + +/// @brief Base Memfile derivation of the statistical lease data query +/// +/// This class provides the functionality such as results storage and row +/// fetching common to fulfilling the statistical lease data query. +/// +class MemfileLeaseStatsQuery : public LeaseStatsQuery { +public: + /// @brief Constructor for all subnets query + /// + MemfileLeaseStatsQuery() + : rows_(0), next_pos_(rows_.end()) { + }; + + /// @brief Constructor for single subnet query + /// + /// @param subnet_id ID of the desired subnet + MemfileLeaseStatsQuery(const SubnetID& subnet_id) + : LeaseStatsQuery(subnet_id), rows_(0), next_pos_(rows_.end()) { + }; + + /// @brief Constructor for subnet range query + /// + /// @param first_subnet_id ID of the first subnet in the desired range + /// @param last_subnet_id ID of the last subnet in the desired range + MemfileLeaseStatsQuery(const SubnetID& first_subnet_id, const SubnetID& last_subnet_id) + : LeaseStatsQuery(first_subnet_id, last_subnet_id), rows_(0), next_pos_(rows_.end()) { + }; + + /// @brief Destructor + virtual ~MemfileLeaseStatsQuery() {}; + + /// @brief Fetches the next row in the result set + /// + /// Once the internal result set has been populated by invoking the + /// the start() method, this method is used to iterate over the + /// result set rows. Once the last row has been fetched, subsequent + /// calls will return false. + /// @param row Storage for the fetched row + /// + /// @return True if the fetch succeeded, false if there are no more + /// rows to fetch. + virtual bool getNextRow(LeaseStatsRow& row) { + if (next_pos_ == rows_.end()) { + return (false); + } + + row = *next_pos_; + ++next_pos_; + return (true); + } + + /// @brief Returns the number of rows in the result set + int getRowCount() const { + return (rows_.size()); + } + +protected: + /// @brief A vector containing the "result set" + std::vector<LeaseStatsRow> rows_; + + /// @brief An iterator for accessing the next row within the result set + std::vector<LeaseStatsRow>::iterator next_pos_; +}; + +/// @brief Memfile derivation of the IPv4 statistical lease data query +/// +/// This class is used to recalculate IPv4 lease statistics for Memfile +/// lease storage. It does so by iterating over the given storage, +/// accumulating counts of leases in each of the monitored lease states +/// for each subnet and storing these counts in an internal collection. +/// The populated result set will contain one entry per monitored state +/// per subnet. +/// +class MemfileLeaseStatsQuery4 : public MemfileLeaseStatsQuery { +public: + /// @brief Constructor for an all subnets query + /// + /// @param storage4 A pointer to the v4 lease storage to be counted + MemfileLeaseStatsQuery4(Lease4Storage& storage4) + : MemfileLeaseStatsQuery(), storage4_(storage4) { + }; + + /// @brief Constructor for a single subnet query + /// + /// @param storage4 A pointer to the v4 lease storage to be counted + /// @param subnet_id ID of the desired subnet + MemfileLeaseStatsQuery4(Lease4Storage& storage4, const SubnetID& subnet_id) + : MemfileLeaseStatsQuery(subnet_id), storage4_(storage4) { + }; + + /// @brief Constructor for a subnet range query + /// + /// @param storage4 A pointer to the v4 lease storage to be counted + /// @param first_subnet_id ID of the first subnet in the desired range + /// @param last_subnet_id ID of the last subnet in the desired range + MemfileLeaseStatsQuery4(Lease4Storage& storage4, const SubnetID& first_subnet_id, + const SubnetID& last_subnet_id) + : MemfileLeaseStatsQuery(first_subnet_id, last_subnet_id), storage4_(storage4) { + }; + + /// @brief Destructor + virtual ~MemfileLeaseStatsQuery4() {}; + + /// @brief Creates the IPv4 lease statistical data result set + /// + /// The result set is populated by iterating over the IPv4 leases in + /// storage, in ascending order by address, accumulating the lease state + /// counts per subnet. + /// At the completion of all entries for a given subnet, the counts are + /// used to create LeaseStatsRow instances which are appended to an + /// internal vector. The process results in a vector containing one entry + /// per state per subnet. + /// + /// Currently the states counted are: + /// + /// - Lease::STATE_DEFAULT (i.e. assigned) + /// - Lease::STATE_DECLINED + void start() { + const Lease4StorageSubnetIdIndex& idx + = storage4_.get<SubnetIdIndexTag>(); + + // Set lower and upper bounds based on select mode + Lease4StorageSubnetIdIndex::const_iterator lower; + Lease4StorageSubnetIdIndex::const_iterator upper; + switch (getSelectMode()) { + case ALL_SUBNETS: + lower = idx.begin(); + upper = idx.end(); + break; + + case SINGLE_SUBNET: + lower = idx.lower_bound(getFirstSubnetID()); + upper = idx.upper_bound(getFirstSubnetID()); + break; + + case SUBNET_RANGE: + lower = idx.lower_bound(getFirstSubnetID()); + upper = idx.upper_bound(getLastSubnetID()); + break; + } + + // Return an empty set if there are no rows. + if (lower == upper) { + return; + } + + // Iterate over the leases in order by subnet, accumulating per + // subnet counts for each state of interest. As we finish each + // subnet, add the appropriate rows to our result set. + SubnetID cur_id = 0; + int64_t assigned = 0; + int64_t declined = 0; + for (Lease4StorageSubnetIdIndex::const_iterator lease = lower; + lease != upper; ++lease) { + // If we've hit the next subnet, add rows for the current subnet + // and wipe the accumulators + if ((*lease)->subnet_id_ != cur_id) { + if (cur_id > 0) { + if (assigned > 0) { + rows_.push_back(LeaseStatsRow(cur_id, + Lease::STATE_DEFAULT, + assigned)); + assigned = 0; + } + + if (declined > 0) { + rows_.push_back(LeaseStatsRow(cur_id, + Lease::STATE_DECLINED, + declined)); + declined = 0; + } + } + + // Update current subnet id + cur_id = (*lease)->subnet_id_; + } + + // Bump the appropriate accumulator + if ((*lease)->state_ == Lease::STATE_DEFAULT) { + ++assigned; + } else if ((*lease)->state_ == Lease::STATE_DECLINED) { + ++declined; + } + } + + // Make the rows for last subnet + if (assigned > 0) { + rows_.push_back(LeaseStatsRow(cur_id, Lease::STATE_DEFAULT, + assigned)); + } + + if (declined > 0) { + rows_.push_back(LeaseStatsRow(cur_id, Lease::STATE_DECLINED, + declined)); + } + + // Reset the next row position back to the beginning of the rows. + next_pos_ = rows_.begin(); + } + +private: + /// @brief The Memfile storage containing the IPv4 leases to analyze + Lease4Storage& storage4_; +}; + + +/// @brief Memfile derivation of the IPv6 statistical lease data query +/// +/// This class is used to recalculate IPv6 lease statistics for Memfile +/// lease storage. It does so by iterating over the given storage, +/// accumulating counts of leases in each of the monitored lease states +/// for each subnet and storing these counts in an internal collection. +/// The populated result set will contain one entry per monitored state +/// per subnet. +/// +class MemfileLeaseStatsQuery6 : public MemfileLeaseStatsQuery { +public: + /// @brief Constructor + /// + /// @param storage6 A pointer to the v6 lease storage to be counted + MemfileLeaseStatsQuery6(Lease6Storage& storage6) + : MemfileLeaseStatsQuery(), storage6_(storage6) { + }; + + /// @brief Constructor for a single subnet query + /// + /// @param storage6 A pointer to the v6 lease storage to be counted + /// @param subnet_id ID of the desired subnet + MemfileLeaseStatsQuery6(Lease6Storage& storage6, const SubnetID& subnet_id) + : MemfileLeaseStatsQuery(subnet_id), storage6_(storage6) { + }; + + /// @brief Constructor for a subnet range query + /// + /// @param storage6 A pointer to the v6 lease storage to be counted + /// @param first_subnet_id ID of the first subnet in the desired range + /// @param last_subnet_id ID of the last subnet in the desired range + MemfileLeaseStatsQuery6(Lease6Storage& storage6, const SubnetID& first_subnet_id, + const SubnetID& last_subnet_id) + : MemfileLeaseStatsQuery(first_subnet_id, last_subnet_id), storage6_(storage6) { + }; + + /// @brief Destructor + virtual ~MemfileLeaseStatsQuery6() {}; + + /// @brief Creates the IPv6 lease statistical data result set + /// + /// The result set is populated by iterating over the IPv6 leases in + /// storage, in ascending order by subnet id, accumulating the lease state + /// counts per subnet. At the completion of all entries for a given subnet, + /// the counts are used to create LeaseStatsRow instances which are appended + /// to an internal vector. The process results in a vector containing one + /// entry per state per lease type per subnet. + /// + /// Currently the states counted are: + /// + /// - Lease::STATE_DEFAULT (i.e. assigned) + /// - Lease::STATE_DECLINED + virtual void start() { + // Get the subnet_id index + const Lease6StorageSubnetIdIndex& idx + = storage6_.get<SubnetIdIndexTag>(); + + // Set lower and upper bounds based on select mode + Lease6StorageSubnetIdIndex::const_iterator lower; + Lease6StorageSubnetIdIndex::const_iterator upper; + switch (getSelectMode()) { + case ALL_SUBNETS: + lower = idx.begin(); + upper = idx.end(); + break; + + case SINGLE_SUBNET: + lower = idx.lower_bound(getFirstSubnetID()); + upper = idx.upper_bound(getFirstSubnetID()); + break; + + case SUBNET_RANGE: + lower = idx.lower_bound(getFirstSubnetID()); + upper = idx.upper_bound(getLastSubnetID()); + break; + } + + // Return an empty set if there are no rows. + if (lower == upper) { + return; + } + + // Iterate over the leases in order by subnet, accumulating per + // subnet counts for each state of interest. As we finish each + // subnet, add the appropriate rows to our result set. + SubnetID cur_id = 0; + int64_t assigned = 0; + int64_t declined = 0; + int64_t assigned_pds = 0; + for (Lease6StorageSubnetIdIndex::const_iterator lease = lower; + lease != upper; ++lease) { + // If we've hit the next subnet, add rows for the current subnet + // and wipe the accumulators + if ((*lease)->subnet_id_ != cur_id) { + if (cur_id > 0) { + if (assigned > 0) { + rows_.push_back(LeaseStatsRow(cur_id, Lease::TYPE_NA, + Lease::STATE_DEFAULT, + assigned)); + assigned = 0; + } + + if (declined > 0) { + rows_.push_back(LeaseStatsRow(cur_id, Lease::TYPE_NA, + Lease::STATE_DECLINED, + declined)); + declined = 0; + } + + if (assigned_pds > 0) { + rows_.push_back(LeaseStatsRow(cur_id, Lease::TYPE_PD, + Lease::STATE_DEFAULT, + assigned_pds)); + assigned_pds = 0; + } + } + + // Update current subnet id + cur_id = (*lease)->subnet_id_; + } + + // Bump the appropriate accumulator + if ((*lease)->state_ == Lease::STATE_DEFAULT) { + switch((*lease)->type_) { + case Lease::TYPE_NA: + ++assigned; + break; + case Lease::TYPE_PD: + ++assigned_pds; + break; + default: + break; + } + } else if ((*lease)->state_ == Lease::STATE_DECLINED) { + // In theory only NAs can be declined + if (((*lease)->type_) == Lease::TYPE_NA) { + ++declined; + } + } + } + + // Make the rows for last subnet, unless there were no rows + if (assigned > 0) { + rows_.push_back(LeaseStatsRow(cur_id, Lease::TYPE_NA, + Lease::STATE_DEFAULT, assigned)); + } + + if (declined > 0) { + rows_.push_back(LeaseStatsRow(cur_id, Lease::TYPE_NA, + Lease::STATE_DECLINED, declined)); + } + + if (assigned_pds > 0) { + rows_.push_back(LeaseStatsRow(cur_id, Lease::TYPE_PD, + Lease::STATE_DEFAULT, assigned_pds)); + } + + // Set the next row position to the beginning of the rows. + next_pos_ = rows_.begin(); + } + +private: + /// @brief The Memfile storage containing the IPv6 leases to analyze + Lease6Storage& storage6_; +}; + +// Explicit definition of class static constants. Values are given in the +// declaration so they're not needed here. +const int Memfile_LeaseMgr::MAJOR_VERSION_V4; +const int Memfile_LeaseMgr::MINOR_VERSION_V4; +const int Memfile_LeaseMgr::MAJOR_VERSION_V6; +const int Memfile_LeaseMgr::MINOR_VERSION_V6; + +Memfile_LeaseMgr::Memfile_LeaseMgr(const DatabaseConnection::ParameterMap& parameters) + : LeaseMgr(), lfc_setup_(), conn_(parameters), mutex_(new std::mutex) { + bool conversion_needed = false; + + // Check the universe and use v4 file or v6 file. + std::string universe = conn_.getParameter("universe"); + if (universe == "4") { + std::string file4 = initLeaseFilePath(V4); + if (!file4.empty()) { + conversion_needed = loadLeasesFromFiles<Lease4, + CSVLeaseFile4>(file4, + lease_file4_, + storage4_); + } + } else { + std::string file6 = initLeaseFilePath(V6); + if (!file6.empty()) { + conversion_needed = loadLeasesFromFiles<Lease6, + CSVLeaseFile6>(file6, + lease_file6_, + storage6_); + } + } + + // If lease persistence have been disabled for both v4 and v6, + // issue a warning. It is ok not to write leases to disk when + // doing testing, but it should not be done in normal server + // operation. + if (!persistLeases(V4) && !persistLeases(V6)) { + LOG_WARN(dhcpsrv_logger, DHCPSRV_MEMFILE_NO_STORAGE); + } else { + if (conversion_needed) { + auto const& version(getVersion()); + LOG_WARN(dhcpsrv_logger, DHCPSRV_MEMFILE_CONVERTING_LEASE_FILES) + .arg(version.first).arg(version.second); + } + lfcSetup(conversion_needed); + } +} + +Memfile_LeaseMgr::~Memfile_LeaseMgr() { + if (lease_file4_) { + lease_file4_->close(); + lease_file4_.reset(); + } + if (lease_file6_) { + lease_file6_->close(); + lease_file6_.reset(); + } +} + +std::string +Memfile_LeaseMgr::getDBVersion(Universe const& u) { + std::stringstream tmp; + tmp << "Memfile backend "; + if (u == V4) { + tmp << MAJOR_VERSION_V4 << "." << MINOR_VERSION_V4; + } else if (u == V6) { + tmp << MAJOR_VERSION_V6 << "." << MINOR_VERSION_V6; + } + return tmp.str(); +} + +bool +Memfile_LeaseMgr::addLeaseInternal(const Lease4Ptr& lease) { + if (getLease4Internal(lease->addr_)) { + // there is a lease with specified address already + return (false); + } + + // Try to write a lease to disk first. If this fails, the lease will + // not be inserted to the memory and the disk and in-memory data will + // remain consistent. + if (persistLeases(V4)) { + lease_file4_->append(*lease); + } + + storage4_.insert(lease); + + // Update lease current expiration time (allows update between the creation + // of the Lease up to the point of insertion in the database). + lease->updateCurrentExpirationTime(); + + // Increment class lease counters. + class_lease_counter_.addLease(lease); + + return (true); +} + +bool +Memfile_LeaseMgr::addLease(const Lease4Ptr& lease) { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_MEMFILE_ADD_ADDR4).arg(lease->addr_.toText()); + + if (MultiThreadingMgr::instance().getMode()) { + std::lock_guard<std::mutex> lock(*mutex_); + return (addLeaseInternal(lease)); + } else { + return (addLeaseInternal(lease)); + } +} + +bool +Memfile_LeaseMgr::addLeaseInternal(const Lease6Ptr& lease) { + if (getLease6Internal(lease->type_, lease->addr_)) { + // there is a lease with specified address already + return (false); + } + + // Try to write a lease to disk first. If this fails, the lease will + // not be inserted to the memory and the disk and in-memory data will + // remain consistent. + if (persistLeases(V6)) { + lease_file6_->append(*lease); + } + + storage6_.insert(lease); + + // Update lease current expiration time (allows update between the creation + // of the Lease up to the point of insertion in the database). + lease->updateCurrentExpirationTime(); + + // Increment class lease counters. + class_lease_counter_.addLease(lease); + + return (true); +} + +bool +Memfile_LeaseMgr::addLease(const Lease6Ptr& lease) { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_MEMFILE_ADD_ADDR6).arg(lease->addr_.toText()); + + if (MultiThreadingMgr::instance().getMode()) { + std::lock_guard<std::mutex> lock(*mutex_); + return (addLeaseInternal(lease)); + } else { + return (addLeaseInternal(lease)); + } +} + +Lease4Ptr +Memfile_LeaseMgr::getLease4Internal(const isc::asiolink::IOAddress& addr) const { + const Lease4StorageAddressIndex& idx = storage4_.get<AddressIndexTag>(); + Lease4StorageAddressIndex::iterator l = idx.find(addr); + if (l == idx.end()) { + return (Lease4Ptr()); + } else { + return (Lease4Ptr(new Lease4(**l))); + } +} + +Lease4Ptr +Memfile_LeaseMgr::getLease4(const isc::asiolink::IOAddress& addr) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_MEMFILE_GET_ADDR4).arg(addr.toText()); + + if (MultiThreadingMgr::instance().getMode()) { + std::lock_guard<std::mutex> lock(*mutex_); + return (getLease4Internal(addr)); + } else { + return (getLease4Internal(addr)); + } +} + +void +Memfile_LeaseMgr::getLease4Internal(const HWAddr& hwaddr, + Lease4Collection& collection) const { + // Using composite index by 'hw address' and 'subnet id'. It is + // ok to use it for searching by the 'hw address' only. + const Lease4StorageHWAddressSubnetIdIndex& idx = + storage4_.get<HWAddressSubnetIdIndexTag>(); + std::pair<Lease4StorageHWAddressSubnetIdIndex::const_iterator, + Lease4StorageHWAddressSubnetIdIndex::const_iterator> l + = idx.equal_range(boost::make_tuple(hwaddr.hwaddr_)); + + for (auto lease = l.first; lease != l.second; ++lease) { + collection.push_back(Lease4Ptr(new Lease4(**lease))); + } +} + +Lease4Collection +Memfile_LeaseMgr::getLease4(const HWAddr& hwaddr) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_MEMFILE_GET_HWADDR).arg(hwaddr.toText()); + + Lease4Collection collection; + if (MultiThreadingMgr::instance().getMode()) { + std::lock_guard<std::mutex> lock(*mutex_); + getLease4Internal(hwaddr, collection); + } else { + getLease4Internal(hwaddr, collection); + } + + return (collection); +} + +Lease4Ptr +Memfile_LeaseMgr::getLease4Internal(const HWAddr& hwaddr, + SubnetID subnet_id) const { + // Get the index by HW Address and Subnet Identifier. + const Lease4StorageHWAddressSubnetIdIndex& idx = + storage4_.get<HWAddressSubnetIdIndexTag>(); + // Try to find the lease using HWAddr and subnet id. + Lease4StorageHWAddressSubnetIdIndex::const_iterator lease = + idx.find(boost::make_tuple(hwaddr.hwaddr_, subnet_id)); + // Lease was not found. Return empty pointer to the caller. + if (lease == idx.end()) { + return (Lease4Ptr()); + } + + // Lease was found. Return it to the caller. + return (Lease4Ptr(new Lease4(**lease))); +} + +Lease4Ptr +Memfile_LeaseMgr::getLease4(const HWAddr& hwaddr, + SubnetID subnet_id) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_MEMFILE_GET_SUBID_HWADDR).arg(subnet_id) + .arg(hwaddr.toText()); + + if (MultiThreadingMgr::instance().getMode()) { + std::lock_guard<std::mutex> lock(*mutex_); + return (getLease4Internal(hwaddr, subnet_id)); + } else { + return (getLease4Internal(hwaddr, subnet_id)); + } +} + +void +Memfile_LeaseMgr::getLease4Internal(const ClientId& client_id, + Lease4Collection& collection) const { + // Using composite index by 'client id' and 'subnet id'. It is ok + // to use it to search by 'client id' only. + const Lease4StorageClientIdSubnetIdIndex& idx = + storage4_.get<ClientIdSubnetIdIndexTag>(); + std::pair<Lease4StorageClientIdSubnetIdIndex::const_iterator, + Lease4StorageClientIdSubnetIdIndex::const_iterator> l + = idx.equal_range(boost::make_tuple(client_id.getClientId())); + + for (auto lease = l.first; lease != l.second; ++lease) { + collection.push_back(Lease4Ptr(new Lease4(**lease))); + } +} + +Lease4Collection +Memfile_LeaseMgr::getLease4(const ClientId& client_id) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_MEMFILE_GET_CLIENTID).arg(client_id.toText()); + + Lease4Collection collection; + if (MultiThreadingMgr::instance().getMode()) { + std::lock_guard<std::mutex> lock(*mutex_); + getLease4Internal(client_id, collection); + } else { + getLease4Internal(client_id, collection); + } + + return (collection); +} + +Lease4Ptr +Memfile_LeaseMgr::getLease4Internal(const ClientId& client_id, + SubnetID subnet_id) const { + // Get the index by client and subnet id. + const Lease4StorageClientIdSubnetIdIndex& idx = + storage4_.get<ClientIdSubnetIdIndexTag>(); + // Try to get the lease using client id and subnet id. + Lease4StorageClientIdSubnetIdIndex::const_iterator lease = + idx.find(boost::make_tuple(client_id.getClientId(), subnet_id)); + // Lease was not found. Return empty pointer to the caller. + if (lease == idx.end()) { + return (Lease4Ptr()); + } + // Lease was found. Return it to the caller. + return (Lease4Ptr(new Lease4(**lease))); +} + +Lease4Ptr +Memfile_LeaseMgr::getLease4(const ClientId& client_id, + SubnetID subnet_id) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_MEMFILE_GET_SUBID_CLIENTID).arg(subnet_id) + .arg(client_id.toText()); + + if (MultiThreadingMgr::instance().getMode()) { + std::lock_guard<std::mutex> lock(*mutex_); + return (getLease4Internal(client_id, subnet_id)); + } else { + return (getLease4Internal(client_id, subnet_id)); + } +} + +void +Memfile_LeaseMgr::getLeases4Internal(SubnetID subnet_id, + Lease4Collection& collection) const { + const Lease4StorageSubnetIdIndex& idx = storage4_.get<SubnetIdIndexTag>(); + std::pair<Lease4StorageSubnetIdIndex::const_iterator, + Lease4StorageSubnetIdIndex::const_iterator> l = + idx.equal_range(subnet_id); + + for (auto lease = l.first; lease != l.second; ++lease) { + collection.push_back(Lease4Ptr(new Lease4(**lease))); + } +} + +Lease4Collection +Memfile_LeaseMgr::getLeases4(SubnetID subnet_id) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MEMFILE_GET_SUBID4) + .arg(subnet_id); + + Lease4Collection collection; + if (MultiThreadingMgr::instance().getMode()) { + std::lock_guard<std::mutex> lock(*mutex_); + getLeases4Internal(subnet_id, collection); + } else { + getLeases4Internal(subnet_id, collection); + } + + return (collection); +} + +void +Memfile_LeaseMgr::getLeases4Internal(const std::string& hostname, + Lease4Collection& collection) const { + const Lease4StorageHostnameIndex& idx = storage4_.get<HostnameIndexTag>(); + std::pair<Lease4StorageHostnameIndex::const_iterator, + Lease4StorageHostnameIndex::const_iterator> l = + idx.equal_range(hostname); + + for (auto lease = l.first; lease != l.second; ++lease) { + collection.push_back(Lease4Ptr(new Lease4(**lease))); + } +} + +Lease4Collection +Memfile_LeaseMgr::getLeases4(const std::string& hostname) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MEMFILE_GET_HOSTNAME4) + .arg(hostname); + + Lease4Collection collection; + if (MultiThreadingMgr::instance().getMode()) { + std::lock_guard<std::mutex> lock(*mutex_); + getLeases4Internal(hostname, collection); + } else { + getLeases4Internal(hostname, collection); + } + + return (collection); +} + +void +Memfile_LeaseMgr::getLeases4Internal(Lease4Collection& collection) const { + for (auto lease = storage4_.begin(); lease != storage4_.end(); ++lease) { + collection.push_back(Lease4Ptr(new Lease4(**lease))); + } +} + +Lease4Collection +Memfile_LeaseMgr::getLeases4() const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MEMFILE_GET4); + + Lease4Collection collection; + if (MultiThreadingMgr::instance().getMode()) { + std::lock_guard<std::mutex> lock(*mutex_); + getLeases4Internal(collection); + } else { + getLeases4Internal(collection); + } + + return (collection); +} + +void +Memfile_LeaseMgr::getLeases4Internal(const asiolink::IOAddress& lower_bound_address, + const LeasePageSize& page_size, + Lease4Collection& collection) const { + const Lease4StorageAddressIndex& idx = storage4_.get<AddressIndexTag>(); + Lease4StorageAddressIndex::const_iterator lb = idx.lower_bound(lower_bound_address); + + // Exclude the lower bound address specified by the caller. + if ((lb != idx.end()) && ((*lb)->addr_ == lower_bound_address)) { + ++lb; + } + + // Return all other leases being within the page size. + for (auto lease = lb; + (lease != idx.end()) && (std::distance(lb, lease) < page_size.page_size_); + ++lease) { + collection.push_back(Lease4Ptr(new Lease4(**lease))); + } +} + +Lease4Collection +Memfile_LeaseMgr::getLeases4(const asiolink::IOAddress& lower_bound_address, + const LeasePageSize& page_size) const { + // Expecting IPv4 address. + if (!lower_bound_address.isV4()) { + isc_throw(InvalidAddressFamily, "expected IPv4 address while " + "retrieving leases from the lease database, got " + << lower_bound_address); + } + + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MEMFILE_GET_PAGE4) + .arg(page_size.page_size_) + .arg(lower_bound_address.toText()); + + Lease4Collection collection; + if (MultiThreadingMgr::instance().getMode()) { + std::lock_guard<std::mutex> lock(*mutex_); + getLeases4Internal(lower_bound_address, page_size, collection); + } else { + getLeases4Internal(lower_bound_address, page_size, collection); + } + + return (collection); +} + +Lease6Ptr +Memfile_LeaseMgr::getLease6Internal(Lease::Type type, + const isc::asiolink::IOAddress& addr) const { + Lease6Storage::iterator l = storage6_.find(addr); + if (l == storage6_.end() || !(*l) || ((*l)->type_ != type)) { + return (Lease6Ptr()); + } else { + return (Lease6Ptr(new Lease6(**l))); + } +} + +Lease6Ptr +Memfile_LeaseMgr::getLease6(Lease::Type type, + const isc::asiolink::IOAddress& addr) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_MEMFILE_GET_ADDR6) + .arg(addr.toText()) + .arg(Lease::typeToText(type)); + + if (MultiThreadingMgr::instance().getMode()) { + std::lock_guard<std::mutex> lock(*mutex_); + return (getLease6Internal(type, addr)); + } else { + return (getLease6Internal(type, addr)); + } +} + +void +Memfile_LeaseMgr::getLeases6Internal(Lease::Type type, + const DUID& duid, + uint32_t iaid, + Lease6Collection& collection) const { + // Get the index by DUID, IAID, lease type. + const Lease6StorageDuidIaidTypeIndex& idx = storage6_.get<DuidIaidTypeIndexTag>(); + // Try to get the lease using the DUID, IAID and lease type. + std::pair<Lease6StorageDuidIaidTypeIndex::const_iterator, + Lease6StorageDuidIaidTypeIndex::const_iterator> l = + idx.equal_range(boost::make_tuple(duid.getDuid(), iaid, type)); + + for (Lease6StorageDuidIaidTypeIndex::const_iterator lease = + l.first; lease != l.second; ++lease) { + collection.push_back(Lease6Ptr(new Lease6(**lease))); + } +} + +Lease6Collection +Memfile_LeaseMgr::getLeases6(Lease::Type type, + const DUID& duid, + uint32_t iaid) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_MEMFILE_GET_IAID_DUID) + .arg(iaid) + .arg(duid.toText()) + .arg(Lease::typeToText(type)); + + Lease6Collection collection; + if (MultiThreadingMgr::instance().getMode()) { + std::lock_guard<std::mutex> lock(*mutex_); + getLeases6Internal(type, duid, iaid, collection); + } else { + getLeases6Internal(type, duid, iaid, collection); + } + + return (collection); +} + +void +Memfile_LeaseMgr::getLeases6Internal(Lease::Type type, + const DUID& duid, + uint32_t iaid, + SubnetID subnet_id, + Lease6Collection& collection) const { + // Get the index by DUID, IAID, lease type. + const Lease6StorageDuidIaidTypeIndex& idx = storage6_.get<DuidIaidTypeIndexTag>(); + // Try to get the lease using the DUID, IAID and lease type. + std::pair<Lease6StorageDuidIaidTypeIndex::const_iterator, + Lease6StorageDuidIaidTypeIndex::const_iterator> l = + idx.equal_range(boost::make_tuple(duid.getDuid(), iaid, type)); + + for (Lease6StorageDuidIaidTypeIndex::const_iterator lease = + l.first; lease != l.second; ++lease) { + // Filter out the leases which subnet id doesn't match. + if ((*lease)->subnet_id_ == subnet_id) { + collection.push_back(Lease6Ptr(new Lease6(**lease))); + } + } +} + +Lease6Collection +Memfile_LeaseMgr::getLeases6(Lease::Type type, + const DUID& duid, + uint32_t iaid, + SubnetID subnet_id) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_MEMFILE_GET_IAID_SUBID_DUID) + .arg(iaid) + .arg(subnet_id) + .arg(duid.toText()) + .arg(Lease::typeToText(type)); + + Lease6Collection collection; + if (MultiThreadingMgr::instance().getMode()) { + std::lock_guard<std::mutex> lock(*mutex_); + getLeases6Internal(type, duid, iaid, subnet_id, collection); + } else { + getLeases6Internal(type, duid, iaid, subnet_id, collection); + } + + return (collection); +} + +void +Memfile_LeaseMgr::getLeases6Internal(SubnetID subnet_id, + Lease6Collection& collection) const { + const Lease6StorageSubnetIdIndex& idx = storage6_.get<SubnetIdIndexTag>(); + std::pair<Lease6StorageSubnetIdIndex::const_iterator, + Lease6StorageSubnetIdIndex::const_iterator> l = + idx.equal_range(subnet_id); + + for (auto lease = l.first; lease != l.second; ++lease) { + collection.push_back(Lease6Ptr(new Lease6(**lease))); + } +} + +Lease6Collection +Memfile_LeaseMgr::getLeases6(SubnetID subnet_id) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MEMFILE_GET_SUBID6) + .arg(subnet_id); + + Lease6Collection collection; + if (MultiThreadingMgr::instance().getMode()) { + std::lock_guard<std::mutex> lock(*mutex_); + getLeases6Internal(subnet_id, collection); + } else { + getLeases6Internal(subnet_id, collection); + } + + return (collection); +} + +void +Memfile_LeaseMgr::getLeases6Internal(const std::string& hostname, + Lease6Collection& collection) const { + const Lease6StorageHostnameIndex& idx = storage6_.get<HostnameIndexTag>(); + std::pair<Lease6StorageHostnameIndex::const_iterator, + Lease6StorageHostnameIndex::const_iterator> l = + idx.equal_range(hostname); + + for (auto lease = l.first; lease != l.second; ++lease) { + collection.push_back(Lease6Ptr(new Lease6(**lease))); + } +} + +Lease6Collection +Memfile_LeaseMgr::getLeases6(const std::string& hostname) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MEMFILE_GET_HOSTNAME6) + .arg(hostname); + + Lease6Collection collection; + if (MultiThreadingMgr::instance().getMode()) { + std::lock_guard<std::mutex> lock(*mutex_); + getLeases6Internal(hostname, collection); + } else { + getLeases6Internal(hostname, collection); + } + + return (collection); +} + +void +Memfile_LeaseMgr::getLeases6Internal(Lease6Collection& collection) const { + for (auto lease = storage6_.begin(); lease != storage6_.end(); ++lease) { + collection.push_back(Lease6Ptr(new Lease6(**lease))); + } +} + +Lease6Collection +Memfile_LeaseMgr::getLeases6() const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MEMFILE_GET6); + + Lease6Collection collection; + if (MultiThreadingMgr::instance().getMode()) { + std::lock_guard<std::mutex> lock(*mutex_); + getLeases6Internal(collection); + } else { + getLeases6Internal(collection); + } + + return (collection); +} + +void +Memfile_LeaseMgr::getLeases6Internal(const DUID& duid, + Lease6Collection& collection) const { + const Lease6StorageDuidIndex& idx = storage6_.get<DuidIndexTag>(); + std::pair<Lease6StorageDuidIndex::const_iterator, + Lease6StorageDuidIndex::const_iterator> l = + idx.equal_range(duid.getDuid()); + + for (auto lease = l.first; lease != l.second; ++lease) { + collection.push_back(Lease6Ptr(new Lease6(**lease))); + } +} + +Lease6Collection +Memfile_LeaseMgr::getLeases6(const DUID& duid) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MEMFILE_GET6_DUID) + .arg(duid.toText()); + + Lease6Collection collection; + if (MultiThreadingMgr::instance().getMode()) { + std::lock_guard<std::mutex> lock(*mutex_); + getLeases6Internal(duid, collection); + } else { + getLeases6Internal(duid, collection); + } + + return (collection); +} + +void +Memfile_LeaseMgr::getLeases6Internal(const asiolink::IOAddress& lower_bound_address, + const LeasePageSize& page_size, + Lease6Collection& collection) const { + const Lease6StorageAddressIndex& idx = storage6_.get<AddressIndexTag>(); + Lease6StorageAddressIndex::const_iterator lb = idx.lower_bound(lower_bound_address); + + // Exclude the lower bound address specified by the caller. + if ((lb != idx.end()) && ((*lb)->addr_ == lower_bound_address)) { + ++lb; + } + + // Return all other leases being within the page size. + for (auto lease = lb; + (lease != idx.end()) && (std::distance(lb, lease) < page_size.page_size_); + ++lease) { + collection.push_back(Lease6Ptr(new Lease6(**lease))); + } +} + +Lease6Collection +Memfile_LeaseMgr::getLeases6(const asiolink::IOAddress& lower_bound_address, + const LeasePageSize& page_size) const { + // Expecting IPv6 address. + if (!lower_bound_address.isV6()) { + isc_throw(InvalidAddressFamily, "expected IPv6 address while " + "retrieving leases from the lease database, got " + << lower_bound_address); + } + + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MEMFILE_GET_PAGE6) + .arg(page_size.page_size_) + .arg(lower_bound_address.toText()); + + Lease6Collection collection; + if (MultiThreadingMgr::instance().getMode()) { + std::lock_guard<std::mutex> lock(*mutex_); + getLeases6Internal(lower_bound_address, page_size, collection); + } else { + getLeases6Internal(lower_bound_address, page_size, collection); + } + + return (collection); +} + +void +Memfile_LeaseMgr::getExpiredLeases4Internal(Lease4Collection& expired_leases, + const size_t max_leases) const { + // Obtain the index which segragates leases by state and time. + const Lease4StorageExpirationIndex& index = storage4_.get<ExpirationIndexTag>(); + + // Retrieve leases which are not reclaimed and which haven't expired. The + // 'less-than' operator will be used for both components of the index. So, + // for the 'state' 'false' is less than 'true'. Also the leases with + // expiration time lower than current time will be returned. + Lease4StorageExpirationIndex::const_iterator ub = + index.upper_bound(boost::make_tuple(false, time(NULL))); + + // Copy only the number of leases indicated by the max_leases parameter. + for (Lease4StorageExpirationIndex::const_iterator lease = index.begin(); + (lease != ub) && ((max_leases == 0) || (std::distance(index.begin(), lease) < + max_leases)); + ++lease) { + expired_leases.push_back(Lease4Ptr(new Lease4(**lease))); + } +} + +void +Memfile_LeaseMgr::getExpiredLeases4(Lease4Collection& expired_leases, + const size_t max_leases) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MEMFILE_GET_EXPIRED4) + .arg(max_leases); + + if (MultiThreadingMgr::instance().getMode()) { + std::lock_guard<std::mutex> lock(*mutex_); + getExpiredLeases4Internal(expired_leases, max_leases); + } else { + getExpiredLeases4Internal(expired_leases, max_leases); + } +} + +void +Memfile_LeaseMgr::getExpiredLeases6Internal(Lease6Collection& expired_leases, + const size_t max_leases) const { + // Obtain the index which segragates leases by state and time. + const Lease6StorageExpirationIndex& index = storage6_.get<ExpirationIndexTag>(); + + // Retrieve leases which are not reclaimed and which haven't expired. The + // 'less-than' operator will be used for both components of the index. So, + // for the 'state' 'false' is less than 'true'. Also the leases with + // expiration time lower than current time will be returned. + Lease6StorageExpirationIndex::const_iterator ub = + index.upper_bound(boost::make_tuple(false, time(NULL))); + + // Copy only the number of leases indicated by the max_leases parameter. + for (Lease6StorageExpirationIndex::const_iterator lease = index.begin(); + (lease != ub) && ((max_leases == 0) || (std::distance(index.begin(), lease) < + max_leases)); + ++lease) { + expired_leases.push_back(Lease6Ptr(new Lease6(**lease))); + } +} + +void +Memfile_LeaseMgr::getExpiredLeases6(Lease6Collection& expired_leases, + const size_t max_leases) const { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MEMFILE_GET_EXPIRED6) + .arg(max_leases); + + if (MultiThreadingMgr::instance().getMode()) { + std::lock_guard<std::mutex> lock(*mutex_); + getExpiredLeases6Internal(expired_leases, max_leases); + } else { + getExpiredLeases6Internal(expired_leases, max_leases); + } +} + +void +Memfile_LeaseMgr::updateLease4Internal(const Lease4Ptr& lease) { + // Obtain 'by address' index. + Lease4StorageAddressIndex& index = storage4_.get<AddressIndexTag>(); + + bool persist = persistLeases(V4); + + // Lease must exist if it is to be updated. + Lease4StorageAddressIndex::const_iterator lease_it = index.find(lease->addr_); + if (lease_it == index.end()) { + isc_throw(NoSuchLease, "failed to update the lease with address " + << lease->addr_ << " - no such lease"); + } else if ((!persist) && (((*lease_it)->cltt_ != lease->current_cltt_) || + ((*lease_it)->valid_lft_ != lease->current_valid_lft_))) { + // For test purpose only: check that the lease has not changed in + // the database. + isc_throw(NoSuchLease, "failed to update the lease with address " + << lease->addr_ << " - lease has changed in database"); + } + + // Try to write a lease to disk first. If this fails, the lease will + // not be inserted to the memory and the disk and in-memory data will + // remain consistent. + if (persist) { + lease_file4_->append(*lease); + } + + // Update lease current expiration time. + lease->updateCurrentExpirationTime(); + + // Save a copy of the old lease as lease_it will point to the new + // one after the replacement. + Lease4Ptr old_lease = *lease_it; + + // Use replace() to re-index leases. + index.replace(lease_it, Lease4Ptr(new Lease4(*lease))); + + // Adjust class lease counters. + class_lease_counter_.updateLease(lease, old_lease); +} + +void +Memfile_LeaseMgr::updateLease4(const Lease4Ptr& lease) { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_MEMFILE_UPDATE_ADDR4).arg(lease->addr_.toText()); + + if (MultiThreadingMgr::instance().getMode()) { + std::lock_guard<std::mutex> lock(*mutex_); + updateLease4Internal(lease); + } else { + updateLease4Internal(lease); + } +} + +void +Memfile_LeaseMgr::updateLease6Internal(const Lease6Ptr& lease) { + // Obtain 'by address' index. + Lease6StorageAddressIndex& index = storage6_.get<AddressIndexTag>(); + + bool persist = persistLeases(V6); + + // Lease must exist if it is to be updated. + Lease6StorageAddressIndex::const_iterator lease_it = index.find(lease->addr_); + if (lease_it == index.end()) { + isc_throw(NoSuchLease, "failed to update the lease with address " + << lease->addr_ << " - no such lease"); + } else if ((!persist) && (((*lease_it)->cltt_ != lease->current_cltt_) || + ((*lease_it)->valid_lft_ != lease->current_valid_lft_))) { + // For test purpose only: check that the lease has not changed in + // the database. + isc_throw(NoSuchLease, "failed to update the lease with address " + << lease->addr_ << " - lease has changed in database"); + } + + // Try to write a lease to disk first. If this fails, the lease will + // not be inserted to the memory and the disk and in-memory data will + // remain consistent. + if (persist) { + lease_file6_->append(*lease); + } + + // Update lease current expiration time. + lease->updateCurrentExpirationTime(); + + // Save a copy of the old lease as lease_it will point to the new + // one after the replacement. + Lease6Ptr old_lease = *lease_it; + + // Use replace() to re-index leases. + index.replace(lease_it, Lease6Ptr(new Lease6(*lease))); + + // Adjust class lease counters. + class_lease_counter_.updateLease(lease, old_lease); +} + +void +Memfile_LeaseMgr::updateLease6(const Lease6Ptr& lease) { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_MEMFILE_UPDATE_ADDR6).arg(lease->addr_.toText()); + + if (MultiThreadingMgr::instance().getMode()) { + std::lock_guard<std::mutex> lock(*mutex_); + updateLease6Internal(lease); + } else { + updateLease6Internal(lease); + } +} + +bool +Memfile_LeaseMgr::deleteLeaseInternal(const Lease4Ptr& lease) { + const isc::asiolink::IOAddress& addr = lease->addr_; + Lease4Storage::iterator l = storage4_.find(addr); + if (l == storage4_.end()) { + // No such lease + return (false); + } else { + if (persistLeases(V4)) { + // Copy the lease. The valid lifetime needs to be modified and + // we don't modify the original lease. + Lease4 lease_copy = **l; + // Setting valid lifetime to 0 means that lease is being + // removed. + lease_copy.valid_lft_ = 0; + lease_file4_->append(lease_copy); + } else { + // For test purpose only: check that the lease has not changed in + // the database. + if (((*l)->cltt_ != lease->current_cltt_) || + ((*l)->valid_lft_ != lease->current_valid_lft_)) { + return false; + } + } + + storage4_.erase(l); + + // Decrement class lease counters. + class_lease_counter_.removeLease(lease); + + return (true); + } +} + +bool +Memfile_LeaseMgr::deleteLease(const Lease4Ptr& lease) { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_MEMFILE_DELETE_ADDR).arg(lease->addr_.toText()); + + if (MultiThreadingMgr::instance().getMode()) { + std::lock_guard<std::mutex> lock(*mutex_); + return (deleteLeaseInternal(lease)); + } else { + return (deleteLeaseInternal(lease)); + } +} + +bool +Memfile_LeaseMgr::deleteLeaseInternal(const Lease6Ptr& lease) { + const isc::asiolink::IOAddress& addr = lease->addr_; + Lease6Storage::iterator l = storage6_.find(addr); + if (l == storage6_.end()) { + // No such lease + return (false); + } else { + if (persistLeases(V6)) { + // Copy the lease. The lifetimes need to be modified and we + // don't modify the original lease. + Lease6 lease_copy = **l; + // Setting lifetimes to 0 means that lease is being removed. + lease_copy.valid_lft_ = 0; + lease_copy.preferred_lft_ = 0; + lease_file6_->append(lease_copy); + } else { + // For test purpose only: check that the lease has not changed in + // the database. + if (((*l)->cltt_ != lease->current_cltt_) || + ((*l)->valid_lft_ != lease->current_valid_lft_)) { + return false; + } + } + + storage6_.erase(l); + + // Decrement class lease counters. + class_lease_counter_.removeLease(lease); + + return (true); + } +} + +bool +Memfile_LeaseMgr::deleteLease(const Lease6Ptr& lease) { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_MEMFILE_DELETE_ADDR).arg(lease->addr_.toText()); + + if (MultiThreadingMgr::instance().getMode()) { + std::lock_guard<std::mutex> lock(*mutex_); + return (deleteLeaseInternal(lease)); + } else { + return (deleteLeaseInternal(lease)); + } +} + +uint64_t +Memfile_LeaseMgr::deleteExpiredReclaimedLeases4(const uint32_t secs) { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_MEMFILE_DELETE_EXPIRED_RECLAIMED4) + .arg(secs); + + if (MultiThreadingMgr::instance().getMode()) { + std::lock_guard<std::mutex> lock(*mutex_); + return (deleteExpiredReclaimedLeases< + Lease4StorageExpirationIndex, Lease4 + >(secs, V4, storage4_, lease_file4_)); + } else { + return (deleteExpiredReclaimedLeases< + Lease4StorageExpirationIndex, Lease4 + >(secs, V4, storage4_, lease_file4_)); + } +} + +uint64_t +Memfile_LeaseMgr::deleteExpiredReclaimedLeases6(const uint32_t secs) { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_MEMFILE_DELETE_EXPIRED_RECLAIMED6) + .arg(secs); + + if (MultiThreadingMgr::instance().getMode()) { + std::lock_guard<std::mutex> lock(*mutex_); + return (deleteExpiredReclaimedLeases< + Lease6StorageExpirationIndex, Lease6 + >(secs, V6, storage6_, lease_file6_)); + } else { + return (deleteExpiredReclaimedLeases< + Lease6StorageExpirationIndex, Lease6 + >(secs, V6, storage6_, lease_file6_)); + } +} + +template<typename IndexType, typename LeaseType, typename StorageType, + typename LeaseFileType> +uint64_t +Memfile_LeaseMgr::deleteExpiredReclaimedLeases(const uint32_t secs, + const Universe& universe, + StorageType& storage, + LeaseFileType& lease_file) const { + // Obtain the index which segragates leases by state and time. + IndexType& index = storage.template get<ExpirationIndexTag>(); + + // This returns the first element which is greater than the specified + // tuple (true, time(NULL) - secs). However, the range between the + // beginning of the index and returned element also includes all the + // elements for which the first value is false (lease state is NOT + // reclaimed), because false < true. All elements between the + // beginning of the index and the element returned, for which the + // first value is true, represent the reclaimed leases which should + // be deleted, because their expiration time + secs has occurred earlier + // than current time. + typename IndexType::const_iterator upper_limit = + index.upper_bound(boost::make_tuple(true, time(NULL) - secs)); + + // Now, we have to exclude all elements of the index which represent + // leases in the state other than reclaimed - with the first value + // in the index equal to false. Note that elements in the index are + // ordered from the lower to the higher ones. So, all elements with + // the first value of false are placed before the elements with the + // value of true. Hence, we have to find the first element which + // contains value of true. The time value is the lowest possible. + typename IndexType::const_iterator lower_limit = + index.upper_bound(boost::make_tuple(true, std::numeric_limits<int64_t>::min())); + + // If there are some elements in this range, delete them. + uint64_t num_leases = static_cast<uint64_t>(std::distance(lower_limit, upper_limit)); + if (num_leases > 0) { + + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_MEMFILE_DELETE_EXPIRED_RECLAIMED_START) + .arg(num_leases); + + // If lease persistence is enabled, we also have to mark leases + // as deleted in the lease file. We do this by setting the + // lifetime to 0. + if (persistLeases(universe)) { + for (typename IndexType::const_iterator lease = lower_limit; + lease != upper_limit; ++lease) { + // Copy lease to not affect the lease in the container. + LeaseType lease_copy(**lease); + // Set the valid lifetime to 0 to indicate the removal + // of the lease. + lease_copy.valid_lft_ = 0; + lease_file->append(lease_copy); + } + } + + // Erase leases from memory. + index.erase(lower_limit, upper_limit); + } + // Return number of leases deleted. + return (num_leases); +} + + +std::string +Memfile_LeaseMgr::getDescription() const { + return (std::string("In memory database with leases stored in a CSV file.")); +} + +std::pair<uint32_t, uint32_t> +Memfile_LeaseMgr::getVersion() const { + std::string const& universe(conn_.getParameter("universe")); + if (universe == "4") { + return std::make_pair(MAJOR_VERSION_V4, MINOR_VERSION_V4); + } else if (universe == "6") { + return std::make_pair(MAJOR_VERSION_V6, MINOR_VERSION_V6); + } + isc_throw(BadValue, "cannot determine version for universe " << universe); +} + +void +Memfile_LeaseMgr::commit() { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MEMFILE_COMMIT); +} + +void +Memfile_LeaseMgr::rollback() { + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, + DHCPSRV_MEMFILE_ROLLBACK); +} + +std::string +Memfile_LeaseMgr::appendSuffix(const std::string& file_name, + const LFCFileType& file_type) { + std::string name(file_name); + switch (file_type) { + case FILE_INPUT: + name += ".1"; + break; + case FILE_PREVIOUS: + name += ".2"; + break; + case FILE_OUTPUT: + name += ".output"; + break; + case FILE_FINISH: + name += ".completed"; + break; + case FILE_PID: + name += ".pid"; + break; + default: + // Do not append any suffix for the FILE_CURRENT. + ; + } + + return (name); +} + +std::string +Memfile_LeaseMgr::getDefaultLeaseFilePath(Universe u) const { + std::ostringstream s; + s << CfgMgr::instance().getDataDir() << "/kea-leases"; + s << (u == V4 ? "4" : "6"); + s << ".csv"; + return (s.str()); +} + +std::string +Memfile_LeaseMgr::getLeaseFilePath(Universe u) const { + if (u == V4) { + return (lease_file4_ ? lease_file4_->getFilename() : ""); + } + + return (lease_file6_ ? lease_file6_->getFilename() : ""); +} + +bool +Memfile_LeaseMgr::persistLeases(Universe u) const { + // Currently, if the lease file IO is not created, it means that writes to + // disk have been explicitly disabled by the administrator. At some point, + // there may be a dedicated ON/OFF flag implemented to control this. + if (u == V4 && lease_file4_) { + return (true); + } + + return (u == V6 && lease_file6_); +} + +std::string +Memfile_LeaseMgr::initLeaseFilePath(Universe u) { + std::string persist_val; + try { + persist_val = conn_.getParameter("persist"); + } catch (const Exception&) { + // If parameter persist hasn't been specified, we use a default value + // 'yes'. + persist_val = "true"; + } + // If persist_val is 'false' we will not store leases to disk, so let's + // return empty file name. + if (persist_val == "false") { + return (""); + + } else if (persist_val != "true") { + isc_throw(isc::BadValue, "invalid value 'persist=" + << persist_val << "'"); + } + + std::string lease_file; + try { + lease_file = conn_.getParameter("name"); + } catch (const Exception&) { + lease_file = getDefaultLeaseFilePath(u); + } + return (lease_file); +} + +template<typename LeaseObjectType, typename LeaseFileType, typename StorageType> +bool +Memfile_LeaseMgr::loadLeasesFromFiles(const std::string& filename, + boost::shared_ptr<LeaseFileType>& lease_file, + StorageType& storage) { + // Check if the instance of the LFC is running right now. If it is + // running, we refuse to load leases as the LFC may be writing to the + // lease files right now. When the user retries server configuration + // it should go through. + /// @todo Consider applying a timeout for an LFC and retry when this + /// timeout elapses. + PIDFile pid_file(appendSuffix(filename, FILE_PID)); + if (pid_file.check()) { + isc_throw(DbOpenError, "unable to load leases from files while the " + "lease file cleanup is in progress"); + } + + storage.clear(); + + std::string max_row_errors_str = "0"; + try { + max_row_errors_str = conn_.getParameter("max-row-errors"); + } catch (const std::exception&) { + // Ignore and default to 0. + } + + uint32_t max_row_errors = 0; + try { + max_row_errors = boost::lexical_cast<uint32_t>(max_row_errors_str); + } catch (const boost::bad_lexical_cast&) { + isc_throw(isc::BadValue, "invalid value of the max-row-errors " + << max_row_errors_str << " specified"); + } + + // Load the leasefile.completed, if exists. + bool conversion_needed = false; + lease_file.reset(new LeaseFileType(std::string(filename + ".completed"))); + if (lease_file->exists()) { + LeaseFileLoader::load<LeaseObjectType>(*lease_file, storage, + max_row_errors); + conversion_needed = conversion_needed || lease_file->needsConversion(); + } else { + // If the leasefile.completed doesn't exist, let's load the leases + // from leasefile.2 and leasefile.1, if they exist. + lease_file.reset(new LeaseFileType(appendSuffix(filename, FILE_PREVIOUS))); + if (lease_file->exists()) { + LeaseFileLoader::load<LeaseObjectType>(*lease_file, storage, + max_row_errors); + conversion_needed = conversion_needed || lease_file->needsConversion(); + } + + lease_file.reset(new LeaseFileType(appendSuffix(filename, FILE_INPUT))); + if (lease_file->exists()) { + LeaseFileLoader::load<LeaseObjectType>(*lease_file, storage, + max_row_errors); + conversion_needed = conversion_needed || lease_file->needsConversion(); + } + } + + // Always load leases from the primary lease file. If the lease file + // doesn't exist it will be created by the LeaseFileLoader. Note + // that the false value passed as the last parameter to load + // function causes the function to leave the file open after + // it is parsed. This file will be used by the backend to record + // future lease updates. + lease_file.reset(new LeaseFileType(filename)); + LeaseFileLoader::load<LeaseObjectType>(*lease_file, storage, + max_row_errors, false); + conversion_needed = conversion_needed || lease_file->needsConversion(); + + return (conversion_needed); +} + + +bool +Memfile_LeaseMgr::isLFCRunning() const { + return (lfc_setup_->isRunning()); +} + +int +Memfile_LeaseMgr::getLFCExitStatus() const { + return (lfc_setup_->getExitStatus()); +} + +void +Memfile_LeaseMgr::lfcCallback() { + LOG_INFO(dhcpsrv_logger, DHCPSRV_MEMFILE_LFC_START); + + // Check if we're in the v4 or v6 space and use the appropriate file. + if (lease_file4_) { + MultiThreadingCriticalSection cs; + lfcExecute(lease_file4_); + } else if (lease_file6_) { + MultiThreadingCriticalSection cs; + lfcExecute(lease_file6_); + } +} + +void +Memfile_LeaseMgr::lfcSetup(bool conversion_needed) { + std::string lfc_interval_str = "3600"; + try { + lfc_interval_str = conn_.getParameter("lfc-interval"); + } catch (const std::exception&) { + // Ignore and default to 3600. + } + + uint32_t lfc_interval = 0; + try { + lfc_interval = boost::lexical_cast<uint32_t>(lfc_interval_str); + } catch (const boost::bad_lexical_cast&) { + isc_throw(isc::BadValue, "invalid value of the lfc-interval " + << lfc_interval_str << " specified"); + } + + if (lfc_interval > 0 || conversion_needed) { + lfc_setup_.reset(new LFCSetup(std::bind(&Memfile_LeaseMgr::lfcCallback, this))); + lfc_setup_->setup(lfc_interval, lease_file4_, lease_file6_, conversion_needed); + } +} + +template<typename LeaseFileType> +void +Memfile_LeaseMgr::lfcExecute(boost::shared_ptr<LeaseFileType>& lease_file) { + bool do_lfc = true; + + // Check the status of the LFC instance. + // If the finish file exists or the copy of the lease file exists it + // is an indication that another LFC instance may be in progress or + // may be stalled. In that case we don't want to rotate the current + // lease file to avoid overriding the contents of the existing file. + CSVFile lease_file_finish(appendSuffix(lease_file->getFilename(), FILE_FINISH)); + CSVFile lease_file_copy(appendSuffix(lease_file->getFilename(), FILE_INPUT)); + if (!lease_file_finish.exists() && !lease_file_copy.exists()) { + // Close the current file so as we can move it to the copy file. + lease_file->close(); + // Move the current file to the copy file. Remember the result + // because we don't want to run LFC if the rename failed. + do_lfc = (rename(lease_file->getFilename().c_str(), + lease_file_copy.getFilename().c_str()) == 0); + + if (!do_lfc) { + LOG_ERROR(dhcpsrv_logger, DHCPSRV_MEMFILE_LFC_LEASE_FILE_RENAME_FAIL) + .arg(lease_file->getFilename()) + .arg(lease_file_copy.getFilename()) + .arg(strerror(errno)); + } + + // Regardless if we successfully moved the current file or not, + // we need to re-open the current file for the server to write + // new lease updates. If the file has been successfully moved, + // this will result in creation of the new file. Otherwise, + // an existing file will be opened. + try { + lease_file->open(true); + + } catch (const CSVFileError& ex) { + // If we're unable to open the lease file this is a serious + // error because the server will not be able to persist + // leases. + /// @todo We need to better address this error. It should + /// trigger an alarm (once we have a monitoring system in + /// place) so as an administrator can correct it. In + /// practice it should be very rare that this happens and + /// is most likely related to a human error, e.g. changing + /// file permissions. + LOG_ERROR(dhcpsrv_logger, DHCPSRV_MEMFILE_LFC_LEASE_FILE_REOPEN_FAIL) + .arg(lease_file->getFilename()) + .arg(ex.what()); + // Reset the pointer to the file so as the backend doesn't + // try to write leases to disk. + lease_file.reset(); + do_lfc = false; + } + } + // Once the files have been rotated, or untouched if another LFC had + // not finished, a new process is started. + if (do_lfc) { + lfc_setup_->execute(); + } +} + +LeaseStatsQueryPtr +Memfile_LeaseMgr::startLeaseStatsQuery4() { + LeaseStatsQueryPtr query(new MemfileLeaseStatsQuery4(storage4_)); + query->start(); + return(query); +} + +LeaseStatsQueryPtr +Memfile_LeaseMgr::startSubnetLeaseStatsQuery4(const SubnetID& subnet_id) { + LeaseStatsQueryPtr query(new MemfileLeaseStatsQuery4(storage4_, subnet_id)); + query->start(); + return(query); +} + +LeaseStatsQueryPtr +Memfile_LeaseMgr::startSubnetRangeLeaseStatsQuery4(const SubnetID& first_subnet_id, + const SubnetID& last_subnet_id) { + LeaseStatsQueryPtr query(new MemfileLeaseStatsQuery4(storage4_, first_subnet_id, + last_subnet_id)); + query->start(); + return(query); +} + +LeaseStatsQueryPtr +Memfile_LeaseMgr::startLeaseStatsQuery6() { + LeaseStatsQueryPtr query(new MemfileLeaseStatsQuery6(storage6_)); + query->start(); + return(query); +} + +LeaseStatsQueryPtr +Memfile_LeaseMgr::startSubnetLeaseStatsQuery6(const SubnetID& subnet_id) { + LeaseStatsQueryPtr query(new MemfileLeaseStatsQuery6(storage6_, subnet_id)); + query->start(); + return(query); +} + +LeaseStatsQueryPtr +Memfile_LeaseMgr::startSubnetRangeLeaseStatsQuery6(const SubnetID& first_subnet_id, + const SubnetID& last_subnet_id) { + LeaseStatsQueryPtr query(new MemfileLeaseStatsQuery6(storage6_, first_subnet_id, + last_subnet_id)); + query->start(); + return(query); +} + +size_t +Memfile_LeaseMgr::wipeLeases4(const SubnetID& subnet_id) { + LOG_INFO(dhcpsrv_logger, DHCPSRV_MEMFILE_WIPE_LEASES4) + .arg(subnet_id); + + // Get the index by DUID, IAID, lease type. + const Lease4StorageSubnetIdIndex& idx = storage4_.get<SubnetIdIndexTag>(); + + // Try to get the lease using the DUID, IAID and lease type. + std::pair<Lease4StorageSubnetIdIndex::const_iterator, + Lease4StorageSubnetIdIndex::const_iterator> l = + idx.equal_range(subnet_id); + + // Let's collect all leases. + Lease4Collection leases; + for (auto lease = l.first; lease != l.second; ++lease) { + leases.push_back(*lease); + } + + size_t num = leases.size(); + for (auto l = leases.begin(); l != leases.end(); ++l) { + deleteLease(*l); + } + LOG_INFO(dhcpsrv_logger, DHCPSRV_MEMFILE_WIPE_LEASES4_FINISHED) + .arg(subnet_id).arg(num); + + return (num); +} + +size_t +Memfile_LeaseMgr::wipeLeases6(const SubnetID& subnet_id) { + LOG_INFO(dhcpsrv_logger, DHCPSRV_MEMFILE_WIPE_LEASES6) + .arg(subnet_id); + + // Get the index by DUID, IAID, lease type. + const Lease6StorageSubnetIdIndex& idx = storage6_.get<SubnetIdIndexTag>(); + + // Try to get the lease using the DUID, IAID and lease type. + std::pair<Lease6StorageSubnetIdIndex::const_iterator, + Lease6StorageSubnetIdIndex::const_iterator> l = + idx.equal_range(subnet_id); + + // Let's collect all leases. + Lease6Collection leases; + for (auto lease = l.first; lease != l.second; ++lease) { + leases.push_back(*lease); + } + + size_t num = leases.size(); + for (auto l = leases.begin(); l != leases.end(); ++l) { + deleteLease(*l); + } + LOG_INFO(dhcpsrv_logger, DHCPSRV_MEMFILE_WIPE_LEASES6_FINISHED) + .arg(subnet_id).arg(num); + + return (num); +} + +void +Memfile_LeaseMgr::recountClassLeases4() { + class_lease_counter_.clear(); + for (auto lease = storage4_.begin(); lease != storage4_.end(); ++lease) { + // Bump the appropriate accumulator + if ((*lease)->state_ == Lease::STATE_DEFAULT) { + class_lease_counter_.addLease(*lease); + } + } +} + +void +Memfile_LeaseMgr::recountClassLeases6() { + class_lease_counter_.clear(); + for (auto lease = storage6_.begin(); lease != storage6_.end(); ++lease) { + // Bump the appropriate accumulator + if ((*lease)->state_ == Lease::STATE_DEFAULT) { + class_lease_counter_.addLease(*lease); + } + } +} + +size_t +Memfile_LeaseMgr::getClassLeaseCount(const ClientClass& client_class, + const Lease::Type& ltype /* = Lease::TYPE_V4*/) const { + if (MultiThreadingMgr::instance().getMode()) { + std::lock_guard<std::mutex> lock(*mutex_); + return(class_lease_counter_.getClassCount(client_class, ltype)); + } else { + return(class_lease_counter_.getClassCount(client_class, ltype)); + } +} + +void +Memfile_LeaseMgr::clearClassLeaseCounts() { + return(class_lease_counter_.clear()); +} + +std::string +Memfile_LeaseMgr::checkLimits4(isc::data::ConstElementPtr const& user_context) const { + if (!user_context) { + return (""); + } + + ConstElementPtr limits = user_context->find("ISC/limits"); + if (!limits) { + return (""); + } + + // Iterate of the 'client-classes' list in 'limits'. For each class that specifies + // an "address-limit", check its value against the class's lease count. + ConstElementPtr classes = limits->get("client-classes"); + if (classes) { + for (int i = 0; i < classes->size(); ++i) { + ConstElementPtr class_elem = classes->get(i); + // Get class name. + ConstElementPtr name_elem = class_elem->get("name"); + if (!name_elem) { + isc_throw(BadValue, "checkLimits4 - client-class.name is missing: " + << prettyPrint(limits)); + } + + std::string name = name_elem->stringValue(); + + // Now look for an address-limit + size_t limit; + if (!getLeaseLimit(class_elem, Lease::TYPE_V4, limit)) { + // No limit, go to the next class. + continue; + } + + // If the limit is > 0 look up the class lease count. Limit of 0 always + // denies the lease. + size_t lease_count = 0; + if (limit) { + lease_count = getClassLeaseCount(name); + } + + // If we're over the limit, return the error, no need to evaluate any others. + if (lease_count >= limit) { + std::ostringstream ss; + ss << "address limit " << limit << " for client class \"" + << name << "\", current lease count " << lease_count; + return (ss.str()); + } + } + } + + // If there were class limits we passed them, now look for a subnet limit. + ConstElementPtr subnet_elem = limits->get("subnet"); + if (subnet_elem) { + // Get the subnet id. + ConstElementPtr id_elem = subnet_elem->get("id"); + if (!id_elem) { + isc_throw(BadValue, "checkLimits4 - subnet.id is missing: " + << prettyPrint(limits)); + } + + SubnetID subnet_id = id_elem->intValue(); + + // Now look for an address-limit. + size_t limit; + if (getLeaseLimit(subnet_elem, Lease::TYPE_V4, limit)) { + // If the limit is > 0 look up the subnet lease count. Limit of 0 always + // denies the lease. + int64_t lease_count = 0; + if (limit) { + lease_count = getSubnetStat(subnet_id, "assigned-addresses"); + } + + // If we're over the limit, return the error. + if (lease_count >= limit) { + std::ostringstream ss; + ss << "address limit " << limit << " for subnet ID " << subnet_id + << ", current lease count " << lease_count; + return (ss.str()); + } + } + } + + // No limits exceeded! + return (""); +} + +std::string +Memfile_LeaseMgr::checkLimits6(isc::data::ConstElementPtr const& user_context) const { + if (!user_context) { + return (""); + } + + ConstElementPtr limits = user_context->find("ISC/limits"); + if (!limits) { + return (""); + } + + // Iterate over the 'client-classes' list in 'limits'. For each class that specifies + // limit (either "address-limit" or "prefix-limit", check its value against the appropriate + // class lease count. + ConstElementPtr classes = limits->get("client-classes"); + if (classes) { + for (int i = 0; i < classes->size(); ++i) { + ConstElementPtr class_elem = classes->get(i); + // Get class name. + ConstElementPtr name_elem = class_elem->get("name"); + if (!name_elem) { + isc_throw(BadValue, "checkLimits6 - client-class.name is missing: " + << prettyPrint(limits)); + } + + std::string name = name_elem->stringValue(); + + // Now look for either address-limit or a prefix=limit. + size_t limit = 0; + Lease::Type ltype = Lease::TYPE_NA; + if (!getLeaseLimit(class_elem, ltype, limit)) { + ltype = Lease::TYPE_PD; + if (!getLeaseLimit(class_elem, ltype, limit)) { + // No limits for this class, skip to the next. + continue; + } + } + + // If the limit is > 0 look up the class lease count. Limit of 0 always + // denies the lease. + size_t lease_count = 0; + if (limit) { + lease_count = getClassLeaseCount(name, ltype); + } + + // If we're over the limit, return the error, no need to evaluate any others. + if (lease_count >= limit) { + std::ostringstream ss; + ss << (ltype == Lease::TYPE_NA ? "address" : "prefix") + << " limit " << limit << " for client class \"" + << name << "\", current lease count " << lease_count; + return (ss.str()); + } + } + } + + // If there were class limits we passed them, now look for a subnet limit. + ConstElementPtr subnet_elem = limits->get("subnet"); + if (subnet_elem) { + // Get the subnet id. + ConstElementPtr id_elem = subnet_elem->get("id"); + if (!id_elem) { + isc_throw(BadValue, "checkLimits6 - subnet.id is missing: " + << prettyPrint(limits)); + } + + SubnetID subnet_id = id_elem->intValue(); + + // Now look for either address-limit or a prefix=limit. + size_t limit = 0; + Lease::Type ltype = Lease::TYPE_NA; + if (!getLeaseLimit(subnet_elem, ltype, limit)) { + ltype = Lease::TYPE_PD; + if (!getLeaseLimit(subnet_elem, ltype, limit)) { + // No limits for the subnet so none exceeded! + return (""); + } + } + + // If the limit is > 0 look up the class lease count. Limit of 0 always + // denies the lease. + int64_t lease_count = 0; + if (limit) { + lease_count = getSubnetStat(subnet_id, (ltype == Lease::TYPE_NA ? + "assigned-nas" : "assigned-pds")); + } + + // If we're over the limit, return the error. + if (lease_count >= limit) { + std::ostringstream ss; + ss << (ltype == Lease::TYPE_NA ? "address" : "prefix") + << " limit " << limit << " for subnet ID " << subnet_id + << ", current lease count " << lease_count; + return (ss.str()); + } + } + + // No limits exceeded! + return (""); +} + +bool +Memfile_LeaseMgr::isJsonSupported() const { + return true; +} + +int64_t +Memfile_LeaseMgr::getSubnetStat(const SubnetID& subnet_id, const std::string& stat_label) const { + /// @todo This could be simplified if StatsMgr provided a mechanism to + /// return the most recent sample as an InterSample. + std::string stat_name = StatsMgr::generateName("subnet", subnet_id, stat_label); + ConstElementPtr stat = StatsMgr::instance().get(stat_name); + ConstElementPtr samples = stat->get(stat_name); + if (samples && samples->size()) { + auto sample = samples->get(0); + if (sample->size()) { + auto count_elem = sample->get(0); + return (count_elem->intValue()); + } + } + + return (0); +} + +bool +Memfile_LeaseMgr::getLeaseLimit(ConstElementPtr parent, Lease::Type ltype, size_t& limit) const { + ConstElementPtr limit_elem = parent->get(ltype == Lease::TYPE_PD ? + "prefix-limit" : "address-limit"); + if (limit_elem) { + limit = limit_elem->intValue(); + return (true); + } + + return (false); +} + + + +} // namespace dhcp +} // namespace isc |