Skip to content

Commit 0ef972a

Browse files
committed
[#1415] Added free lease queue impl
Added implementation of the queue holding free leases.
1 parent 7fea76c commit 0ef972a

File tree

5 files changed

+930
-0
lines changed

5 files changed

+930
-0
lines changed

src/lib/dhcpsrv/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ libkea_dhcpsrv_la_SOURCES += dhcp4o6_ipc.cc dhcp4o6_ipc.h
106106
libkea_dhcpsrv_la_SOURCES += dhcpsrv_exceptions.h
107107
libkea_dhcpsrv_la_SOURCES += dhcpsrv_log.cc dhcpsrv_log.h
108108
libkea_dhcpsrv_la_SOURCES += dhcpsrv_messages.h dhcpsrv_messages.cc
109+
libkea_dhcpsrv_la_SOURCES += free_lease_queue.h free_lease_queue.cc
109110
libkea_dhcpsrv_la_SOURCES += host.cc host.h
110111
libkea_dhcpsrv_la_SOURCES += host_container.h
111112
libkea_dhcpsrv_la_SOURCES += host_data_source_factory.cc host_data_source_factory.h
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
// Copyright (C) 2020 Internet Systems Consortium, Inc. ("ISC")
2+
//
3+
// This Source Code Form is subject to the terms of the Mozilla Public
4+
// License, v. 2.0. If a copy of the MPL was not distributed with this
5+
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6+
7+
#include <config.h>
8+
#include <dhcpsrv/free_lease_queue.h>
9+
#include <exceptions/exceptions.h>
10+
11+
#include <boost/make_shared.hpp>
12+
13+
#include <iostream>
14+
#include <iterator>
15+
#include <tuple>
16+
#include <utility>
17+
18+
using namespace isc::asiolink;
19+
20+
namespace isc {
21+
namespace dhcp {
22+
23+
FreeLeaseQueue::Range::Range(const IOAddress& start, const IOAddress& end)
24+
: start_(start), end_(end) {
25+
// The start must be lower or equal the end.
26+
if (end_ < start_) {
27+
isc_throw(BadValue, "invalid address range boundaries " << start_ << ":" << end_);
28+
}
29+
// Two IPv4 or two IPv6 addresses are expected as range boundaries.
30+
if (start_.getFamily() != end_.getFamily()) {
31+
isc_throw(BadValue, "address range boundaries must have the same type: " << start_
32+
<< ":" << end_);
33+
}
34+
}
35+
36+
FreeLeaseQueue::FreeLeaseQueue()
37+
: containers_() {
38+
}
39+
40+
void
41+
FreeLeaseQueue::addRange(const Range& range) {
42+
// If the container with ranges is empty, there are is no need for
43+
// doing any checks. Let's just add the new range.
44+
if (!containers_.empty()) {
45+
// Get the next range in the container relative to the start of the new
46+
// range. The upper_bound returns the range which starts after the start
47+
// of the new range.
48+
auto next_range = containers_.lower_bound(range.start_);
49+
// Get the range the range that is before that one. It is also possible that
50+
// there is no previous range in which case we default to end().
51+
auto previous_range = containers_.end();
52+
// If the next range is at the beginning of the container there is no
53+
// previous range.
54+
if (next_range != containers_.begin()) {
55+
// This should work fine even if the next range is set to end(). We
56+
// will get the range that is one position before end() and that
57+
// should be the range that goes before the new one.
58+
auto it = next_range;
59+
--it;
60+
previous_range = it;
61+
}
62+
63+
// Now that we have next and previous ranges set we should check that the
64+
// new range we're adding does not overlap with them.
65+
66+
// If the previous range exists, let's check that the start of the new
67+
// range is neither within that range nor lower. Assuming that the ranges
68+
// are constructed such that the end must be greater or equal the start
69+
// it is sufficient to check that the start of the new range is not lower
70+
// or equal the end of the previous range.
71+
if ((previous_range != containers_.end()) &&
72+
(range.start_ <= previous_range->range_end_)) {
73+
isc_throw(BadValue, "new address range " << range.start_ << ":" << range.end_
74+
<< " overlaps with the existing range");
75+
}
76+
77+
// If the next range exists, let's check that the end of the new range
78+
// is neither within that range nor higher.
79+
if ((next_range != containers_.end()) &&
80+
(next_range->range_start_ <= range.end_)) {
81+
isc_throw(BadValue, "new address range " << range.start_ << ":" << range.end_
82+
<< " overlaps with the existing range");
83+
}
84+
}
85+
86+
containers_.insert(ContainerDescriptor{range.start_, range.end_,
87+
boost::make_shared<Container>()});
88+
}
89+
90+
void
91+
FreeLeaseQueue::addRange(const IOAddress& start, const IOAddress& end) {
92+
addRange(FreeLeaseQueue::Range(start, end));
93+
}
94+
95+
bool
96+
FreeLeaseQueue::removeRange(const Range& range) {
97+
return (containers_.get<1>().erase(range.start_) > 0);
98+
}
99+
100+
bool
101+
FreeLeaseQueue::append(const IOAddress& address) {
102+
// If there are no ranges defined, there is nothing to do.
103+
if (containers_.empty()) {
104+
return (false);
105+
}
106+
// Find the beginning of the range which has the start address
107+
// greater than the address we're appending.
108+
auto lb = containers_.upper_bound(address);
109+
// If the range we found is the first one in the container
110+
// there is no range matching our address because all existing
111+
// ranges include higher addresses.
112+
if (lb == containers_.begin()) {
113+
return (false);
114+
}
115+
--lb;
116+
// Go one range back and see if our address is within its boundaries.
117+
if ((lb->range_end_ < address) || (address < lb->range_start_)) {
118+
return (false);
119+
}
120+
// Use the range we found and append the address to it.
121+
FreeLeaseQueue::Range range(lb->range_start_, lb->range_end_);
122+
append(range, address);
123+
124+
// Everything is fine.
125+
return (true);
126+
}
127+
128+
void
129+
FreeLeaseQueue::append(const FreeLeaseQueue::Range& range, const IOAddress& address) {
130+
// Make sure the address is within the range boundaries.
131+
if ((address < range.start_) || (range.end_ < address)) {
132+
isc_throw(BadValue, "address " << address << " is not within the range of "
133+
<< range.start_ << ":" << range.end_);
134+
}
135+
auto cont = getContainer(range);
136+
cont->insert(address);
137+
}
138+
139+
void
140+
FreeLeaseQueue::append(const uint64_t range_index, const IOAddress& address) {
141+
auto desc = getContainerDescriptor(range_index);
142+
if ((address < desc.range_start_) || (desc.range_end_ < address)) {
143+
isc_throw(BadValue, "address " << address << " is not within the range of "
144+
<< desc.range_start_ << ":" << desc.range_end_);
145+
}
146+
desc.container_->insert(address);
147+
}
148+
149+
bool
150+
FreeLeaseQueue::use(const FreeLeaseQueue::Range& range, const IOAddress& address) {
151+
if ((address < range.start_) || (range.end_ < address)) {
152+
isc_throw(BadValue, "address " << address << " is outside of the range of "
153+
<< range.start_ << ":" << range.end_);
154+
}
155+
auto cont = getContainer(range);
156+
auto found = cont->find(address);
157+
if (found != cont->end()) {
158+
static_cast<void>(cont->erase(found));
159+
return (true);
160+
}
161+
return (false);
162+
}
163+
164+
IOAddress
165+
FreeLeaseQueue::next(const FreeLeaseQueue::Range& range) {
166+
return (popNextInternal(range, true));
167+
}
168+
169+
IOAddress
170+
FreeLeaseQueue::pop(const FreeLeaseQueue::Range& range) {
171+
return (popNextInternal(range, false));
172+
}
173+
174+
uint64_t
175+
FreeLeaseQueue::getRangeIndex(const FreeLeaseQueue::Range& range) const {
176+
auto cont = containers_.get<1>().find(range.start_);
177+
if (cont == containers_.get<1>().end()) {
178+
isc_throw(BadValue, "conatiner for the specified address range " << range.start_
179+
<< ":" << range.end_ << " does not exist");
180+
}
181+
return (std::distance(containers_.get<2>().begin(), containers_.project<2>(cont)));
182+
}
183+
184+
FreeLeaseQueue::ContainerPtr
185+
FreeLeaseQueue::getContainer(const FreeLeaseQueue::Range& range) {
186+
auto cont = containers_.find(range.start_);
187+
if (cont == containers_.end()) {
188+
isc_throw(BadValue, "conatiner for the specified address range " << range.start_
189+
<< ":" << range.end_ << " does not exist");
190+
}
191+
return (cont->container_);
192+
}
193+
194+
FreeLeaseQueue::ContainerDescriptor
195+
FreeLeaseQueue::getContainerDescriptor(const uint64_t range_index) {
196+
if (containers_.get<2>().size() <= range_index) {
197+
isc_throw(BadValue, "conatiner for the specified range index " << range_index
198+
<< " does not exist");
199+
}
200+
auto cont = containers_.get<2>().at(range_index);
201+
return (cont);
202+
}
203+
204+
IOAddress
205+
FreeLeaseQueue::popNextInternal(const Range& range, const bool push) {
206+
auto cont = getContainer(range);
207+
if (cont->empty()) {
208+
return (IOAddress::IPV4_ZERO_ADDRESS());
209+
}
210+
auto& idx = cont->get<1>();
211+
auto next = idx.front();
212+
idx.pop_front();
213+
if (push) {
214+
idx.push_back(next);
215+
}
216+
return (next);
217+
218+
}
219+
220+
} // end of namespace isc::dhcp
221+
} // end of namespace isc

0 commit comments

Comments
 (0)