diff options
Diffstat (limited to 'src/lib/dhcpsrv/free_lease_queue.cc')
-rw-r--r-- | src/lib/dhcpsrv/free_lease_queue.cc | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/src/lib/dhcpsrv/free_lease_queue.cc b/src/lib/dhcpsrv/free_lease_queue.cc new file mode 100644 index 0000000..b9069a8 --- /dev/null +++ b/src/lib/dhcpsrv/free_lease_queue.cc @@ -0,0 +1,249 @@ +// Copyright (C) 2020-2021 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 <asiolink/addr_utilities.h> +#include <dhcpsrv/free_lease_queue.h> + +#include <boost/make_shared.hpp> + +#include <iostream> +#include <tuple> +#include <utility> + +using namespace isc::asiolink; + +namespace isc { +namespace dhcp { + +FreeLeaseQueue::FreeLeaseQueue() + : ranges_() { +} + +void +FreeLeaseQueue::addRange(const AddressRange& range) { + // If the container with ranges is empty, there are is no need for + // doing any checks. Let's just add the new range. + if (!ranges_.empty()) { + checkRangeOverlaps(range.start_, range.end_); + } + ranges_.insert(RangeDescriptor{range.start_, range.end_, 128, boost::make_shared<Leases>()}); +} + +void +FreeLeaseQueue::addRange(const IOAddress& start, const IOAddress& end) { + addRange(AddressRange(start, end)); +} + +void +FreeLeaseQueue::addRange(const PrefixRange& range) { + if (!ranges_.empty()) { + auto last_addr = offsetAddress(range.end_, range.delegated_length_ - 1); + checkRangeOverlaps(range.start_, last_addr); + } + ranges_.insert(RangeDescriptor{range.start_, range.end_, range.delegated_length_, + boost::make_shared<Leases>()}); +} + +void +FreeLeaseQueue::addRange(const asiolink::IOAddress& prefix, const uint8_t prefix_length, + const uint8_t delegated_length) { + addRange(PrefixRange(prefix, prefix_length, delegated_length)); +} + +bool +FreeLeaseQueue::append(const IOAddress& address) { + // If there are no ranges defined, there is nothing to do. + if (ranges_.empty()) { + return (false); + } + // Find the beginning of the range which has the start address + // greater than the address we're appending. + auto lb = ranges_.upper_bound(address); + // If the range we found is the first one in the container + // there is no range matching our address because all existing + // ranges include higher addresses. + if (lb == ranges_.begin()) { + return (false); + } + --lb; + // Go one range back and see if our address is within its boundaries. + if ((lb->range_end_ < address) || (address < lb->range_start_)) { + return (false); + } + // Use the range we found and append the address to it. + AddressRange range(lb->range_start_, lb->range_end_); + append(range, address); + + // Everything is fine. + return (true); +} + +bool +FreeLeaseQueue::append(const IOAddress& prefix, const uint8_t delegated_length) { + // If there are no ranges defined, there is nothing to do. + if (ranges_.empty()) { + return (false); + } + // Find the beginning of the range which has the start address + // greater than the address we're appending. + auto lb = ranges_.upper_bound(prefix); + // If the range we found is the first one in the container + // there is no range matching our prefix because all existing + // ranges include higher addresses. + if (lb == ranges_.begin()) { + return (false); + } + --lb; + // Go one range back and see if our prefix is within its boundaries. + if ((lb->range_end_ < prefix) || (prefix < lb->range_start_) || + (delegated_length != lb->delegated_length_)) { + return (false); + } + // Use the range we found and append the prefix to it. + PrefixRange range(lb->range_start_, lb->range_end_, lb->delegated_length_); + append(range, prefix); + + // Everything is fine. + return (true); +} + +void +FreeLeaseQueue::append(const AddressRange& range, const IOAddress& address) { + // Make sure the address is within the range boundaries. + checkRangeBoundaries(range, address); + auto cont = getLeases(range); + cont->insert(address); +} + +void +FreeLeaseQueue::append(const uint64_t range_index, const IOAddress& ip) { + auto desc = getRangeDescriptor(range_index); + if ((ip < desc.range_start_) || (desc.range_end_ < ip)) { + isc_throw(BadValue, ip << " is not within the range of " << desc.range_start_ + << ":" << desc.range_end_); + } + desc.leases_->insert(ip); +} + +void +FreeLeaseQueue::append(const PrefixRange& range, const asiolink::IOAddress& prefix) { + checkRangeBoundaries(range, prefix, true); + auto cont = getLeases(range); + cont->insert(prefix); +} + +bool +FreeLeaseQueue::use(const AddressRange& range, const IOAddress& address) { + checkRangeBoundaries(range, address); + auto cont = getLeases(range); + auto found = cont->find(address); + if (found != cont->end()) { + static_cast<void>(cont->erase(found)); + return (true); + } + return (false); +} + +bool +FreeLeaseQueue::use(const PrefixRange& range, const IOAddress& prefix) { + checkRangeBoundaries(range, prefix, true); + auto cont = getLeases(range); + auto found = cont->find(prefix); + if (found != cont->end()) { + static_cast<void>(cont->erase(found)); + return (true); + } + return (false); +} + +template<typename RangeType> +void +FreeLeaseQueue::checkRangeBoundaries(const RangeType& range, const IOAddress& ip, + const bool prefix) const { + if ((ip < range.start_) || (range.end_ < ip)) { + isc_throw(BadValue, (prefix ? "prefix " : "address ") << ip << " is not within the range of " + << range.start_ << ":" << range.end_); + } +} + +void +FreeLeaseQueue::checkRangeOverlaps(const IOAddress& start, const IOAddress& end) const { + // Get the next range in the container relative to the start of the new + // range. The upper_bound returns the range which starts after the start + // of the new range. + auto next_range = ranges_.lower_bound(start); + // Get the range the range that is before that one. It is also possible that + // there is no previous range in which case we default to end(). + auto previous_range = ranges_.end(); + // If the next range is at the beginning of the container there is no + // previous range. + if (next_range != ranges_.begin()) { + // This should work fine even if the next range is set to end(). We + // will get the range that is one position before end() and that + // should be the range that goes before the new one. + auto it = next_range; + --it; + previous_range = it; + } + + // Now that we have next and previous ranges set we should check that the + // new range we're adding does not overlap with them. + + // If the previous range exists, let's check that the start of the new + // range is neither within that range nor lower. Assuming that the ranges + // are constructed such that the end must be greater or equal the start + // it is sufficient to check that the start of the new range is not lower + // or equal the end of the previous range. + if ((previous_range != ranges_.end()) && + (start <= previous_range->range_end_)) { + isc_throw(BadValue, "new address range " << start << ":" << end + << " overlaps with the existing range"); + } + + // If the next range exists, let's check that the end of the new range + // is neither within that range nor higher. + if ((next_range != ranges_.end()) && + (next_range->range_start_ <= end)) { + isc_throw(BadValue, "new address range " << start << ":" << end + << " overlaps with the existing range"); + } +} + + +FreeLeaseQueue::LeasesPtr +FreeLeaseQueue::getLeases(const AddressRange& range) const { + auto cont = ranges_.find(range.start_); + if (cont == ranges_.end()) { + isc_throw(BadValue, "container for the specified address range " << range.start_ + << ":" << range.end_ << " does not exist"); + } + return (cont->leases_); +} + +FreeLeaseQueue::LeasesPtr +FreeLeaseQueue::getLeases(const PrefixRange& range) const { + auto cont = ranges_.find(range.start_); + if (cont == ranges_.end()) { + isc_throw(BadValue, "container for the specified prefix " << range.start_ + << " and delegated length of " << static_cast<int>(range.delegated_length_) + << " does not exist"); + } + return (cont->leases_); +} + +FreeLeaseQueue::RangeDescriptor +FreeLeaseQueue::getRangeDescriptor(const uint64_t range_index) const { + if (ranges_.get<2>().size() <= range_index) { + isc_throw(BadValue, "container for the specified range index " << range_index + << " does not exist"); + } + auto cont = ranges_.get<2>().at(range_index); + return (cont); +} + +} // end of namespace isc::dhcp +} // end of namespace isc |