Commit e2954383 authored by Tomek Mrugalski's avatar Tomek Mrugalski 🛰
Browse files

[3171] AllocEngine work: increasePrefix() added, prefix_len passed around

parent 7759a99d
......@@ -58,7 +58,7 @@ AllocEngine::IterativeAllocator::IterativeAllocator(Lease::Type lease_type)
}
isc::asiolink::IOAddress
AllocEngine::IterativeAllocator::increaseAddress(const isc::asiolink::IOAddress& addr) {
AllocEngine::IterativeAllocator::increaseAddress(const isc::asiolink::IOAddress& addr) const {
// Get a buffer holding an address.
const std::vector<uint8_t>& vec = addr.toBytes();
// Get the address length.
......@@ -85,6 +85,52 @@ AllocEngine::IterativeAllocator::increaseAddress(const isc::asiolink::IOAddress&
return (IOAddress::fromBytes(addr.getFamily(), packed));
}
isc::asiolink::IOAddress
AllocEngine::IterativeAllocator::increasePrefix(const isc::asiolink::IOAddress& prefix,
uint8_t prefix_len) const {
if (!prefix.isV6()) {
isc_throw(BadValue, "Prefix operations are for IPv6 only");
}
// Get a buffer holding an address.
const std::vector<uint8_t>& vec = prefix.toBytes();
if (prefix_len < 1 || prefix_len > 128) {
isc_throw(BadValue, "Cannot increase prefix: invalid prefix length: "
<< prefix_len);
}
// Explanation what happens here: http://www.youtube.com/watch?v=NFQCYpIHLNQ
uint8_t n_bytes = (prefix_len - 1)/8;
uint8_t n_bits = 8 - (prefix_len - n_bytes*8);
uint8_t mask = 1 << n_bits;
uint8_t packed[V6ADDRESS_LEN];
// Copy the address. It must be V6, but we already checked that.
std::memcpy(packed, &vec[0], V6ADDRESS_LEN);
// Increase last byte that is in prefix
if (packed[n_bytes] + uint16_t(mask) < 256u) {
packed[n_bytes] += mask;
return (IOAddress::fromBytes(AF_INET6, packed));
}
// Overflow (done on uint8_t, but the sum is greater than 255)
packed[n_bytes] += mask;
// Start increasing the least significant byte
for (int i = n_bytes - 1; i >= 0; --i) {
++packed[i];
// If we haven't overflowed (0xff->0x0) the next byte, then we are done
if (packed[i] != 0) {
break;
}
}
return (IOAddress::fromBytes(AF_INET6, packed));
}
isc::asiolink::IOAddress
AllocEngine::IterativeAllocator::pickAddress(const SubnetPtr& subnet,
......@@ -261,8 +307,10 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
}
// check if the hint is in pool and is available
if (subnet->inPool(hint)) {
// This is equivalent of subnet->inPool(hint), but returns the pool
Pool6Ptr pool = boost::dynamic_pointer_cast<Pool6>(subnet->getPool(type, hint, false));
if (pool) {
/// @todo: We support only one hint for now
Lease6Ptr lease = LeaseMgrFactory::instance().getLease6(type, hint);
if (!lease) {
......@@ -270,10 +318,9 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
/// implemented
// the hint is valid and not currently used, let's create a lease for it
/// @todo: We support only one lease per ia for now
lease = createLease6(subnet, duid, iaid, hint, type, fwd_dns_update,
rev_dns_update, hostname, callout_handle,
fake_allocation);
lease = createLease6(subnet, duid, iaid, hint, pool->getLength(),
type, fwd_dns_update, rev_dns_update,
hostname, callout_handle, fake_allocation);
// It can happen that the lease allocation failed (we could have lost
// the race condition. That means that the hint is lo longer usable and
......@@ -288,6 +335,7 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
if (lease->expired()) {
/// We found a lease and it is expired, so we can reuse it
lease = reuseExpiredLease(lease, subnet, duid, iaid,
pool->getLength(),
fwd_dns_update, rev_dns_update,
hostname, callout_handle,
fake_allocation);
......@@ -324,13 +372,24 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
/// @todo: check if the address is reserved once we have host support
/// implemented
// The first step is to find out prefix length. It is 128 for
// non-PD leases.
uint8_t prefix_len = 128;
if (type == Lease::TYPE_PD) {
Pool6Ptr pool = boost::dynamic_pointer_cast<Pool6>(
subnet->getPool(type, candidate, false));
prefix_len = pool->getLength();
}
Lease6Ptr existing = LeaseMgrFactory::instance().getLease6(type,
candidate);
if (!existing) {
// there's no existing lease for selected candidate, so it is
// free. Let's allocate it.
Lease6Ptr lease = createLease6(subnet, duid, iaid, candidate,
type, fwd_dns_update,
prefix_len, type, fwd_dns_update,
rev_dns_update, hostname,
callout_handle, fake_allocation);
if (lease) {
......@@ -345,9 +404,9 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
} else {
if (existing->expired()) {
existing = reuseExpiredLease(existing, subnet, duid, iaid,
fwd_dns_update, rev_dns_update,
hostname, callout_handle,
fake_allocation);
prefix_len, fwd_dns_update,
rev_dns_update, hostname,
callout_handle, fake_allocation);
Lease6Collection collection;
collection.push_back(existing);
return (collection);
......@@ -621,6 +680,7 @@ Lease6Ptr AllocEngine::reuseExpiredLease(Lease6Ptr& expired,
const Subnet6Ptr& subnet,
const DuidPtr& duid,
uint32_t iaid,
uint8_t prefix_len,
const bool fwd_dns_update,
const bool rev_dns_update,
const std::string& hostname,
......@@ -631,6 +691,10 @@ Lease6Ptr AllocEngine::reuseExpiredLease(Lease6Ptr& expired,
isc_throw(BadValue, "Attempt to recycle lease that is still valid");
}
if (expired->type_ != Lease::TYPE_PD) {
prefix_len = 128; // non-PD lease types must be always /128
}
// address, lease type and prefixlen (0) stay the same
expired->iaid_ = iaid;
expired->duid_ = duid;
......@@ -644,6 +708,7 @@ Lease6Ptr AllocEngine::reuseExpiredLease(Lease6Ptr& expired,
expired->hostname_ = hostname;
expired->fqdn_fwd_ = fwd_dns_update;
expired->fqdn_rev_ = rev_dns_update;
expired->prefixlen_ = prefix_len;
/// @todo: log here that the lease was reused (there's ticket #2524 for
/// logging in libdhcpsrv)
......@@ -779,6 +844,7 @@ Lease6Ptr AllocEngine::createLease6(const Subnet6Ptr& subnet,
const DuidPtr& duid,
uint32_t iaid,
const IOAddress& addr,
uint8_t prefix_len,
Lease::Type type,
const bool fwd_dns_update,
const bool rev_dns_update,
......@@ -788,7 +854,8 @@ Lease6Ptr AllocEngine::createLease6(const Subnet6Ptr& subnet,
Lease6Ptr lease(new Lease6(type, addr, duid, iaid,
subnet->getPreferred(), subnet->getValid(),
subnet->getT1(), subnet->getT2(), subnet->getID()));
subnet->getT1(), subnet->getT2(), subnet->getID(),
prefix_len));
lease->fqdn_fwd_ = fwd_dns_update;
lease->fqdn_rev_ = rev_dns_update;
......
......@@ -124,13 +124,30 @@ protected:
pickAddress(const SubnetPtr& subnet,
const DuidPtr& duid,
const isc::asiolink::IOAddress& hint);
private:
protected:
/// @brief returns an address by one
/// @brief returns an address increased by one
///
/// This method works for both IPv4 and IPv6 addresses.
///
/// @param addr address to be increased
/// @return address increased by one
isc::asiolink::IOAddress increaseAddress(const isc::asiolink::IOAddress& addr);
isc::asiolink::IOAddress
increaseAddress(const isc::asiolink::IOAddress& addr) const;
/// @brief returns the next prefix
///
/// This method works for IPv6 addresses only. It increase
/// specified prefix by a given prefix_len. For example, 2001:db8::
/// increased by prefix length /32 will become 2001:db9::. This method
/// is used to iterate over IPv6 prefix pools
///
/// @param prefix prefix to be increased
/// @param prefix_len length of the prefix to be increased
/// @return result prefix
isc::asiolink::IOAddress
increasePrefix(const isc::asiolink::IOAddress& prefix,
uint8_t prefix_len) const;
};
/// @brief Address/prefix allocator that gets an address based on a hash
......@@ -381,6 +398,7 @@ private:
/// @param iaid IAID from the IA_NA container the client sent to us
/// @param addr an address that was selected and is confirmed to be
/// available
/// @param prefix_len lenght of the prefix (for PD only)
/// @param type lease type (IA, TA or PD)
/// @param fwd_dns_update A boolean value which indicates that server takes
/// responsibility for the forward DNS Update for this lease
......@@ -398,8 +416,8 @@ private:
/// became unavailable)
Lease6Ptr createLease6(const Subnet6Ptr& subnet, const DuidPtr& duid,
uint32_t iaid, const isc::asiolink::IOAddress& addr,
Lease::Type type, const bool fwd_dns_update,
const bool rev_dns_update,
uint8_t prefix_len, Lease::Type type,
const bool fwd_dns_update, const bool rev_dns_update,
const std::string& hostname,
const isc::hooks::CalloutHandlePtr& callout_handle,
bool fake_allocation = false);
......@@ -445,6 +463,7 @@ private:
/// @param subnet subnet the lease is allocated from
/// @param duid client's DUID
/// @param iaid IAID from the IA_NA container the client sent to us
/// @param prefix_len prefix length (for PD leases)
/// @param fwd_dns_update A boolean value which indicates that server takes
/// responsibility for the forward DNS Update for this lease
/// (if true).
......@@ -460,7 +479,7 @@ private:
/// @throw BadValue if trying to recycle lease that is still valid
Lease6Ptr reuseExpiredLease(Lease6Ptr& expired, const Subnet6Ptr& subnet,
const DuidPtr& duid, uint32_t iaid,
const bool fwd_dns_update,
uint8_t prefix_len, const bool fwd_dns_update,
const bool rev_dns_update,
const std::string& hostname,
const isc::hooks::CalloutHandlePtr& callout_handle,
......
......@@ -289,7 +289,7 @@ struct Lease6 : public Lease {
/// @param prefixlen An address prefix length.
Lease6(Type type, const isc::asiolink::IOAddress& addr, DuidPtr duid,
uint32_t iaid, uint32_t preferred, uint32_t valid, uint32_t t1,
uint32_t t2, SubnetID subnet_id, uint8_t prefixlen = 0);
uint32_t t2, SubnetID subnet_id, uint8_t prefixlen = 128);
/// @brief Constructor, including FQDN data.
///
......
......@@ -168,7 +168,8 @@ const PoolCollection& Subnet::getPools(Lease::Type type) const {
}
}
PoolPtr Subnet::getPool(Lease::Type type, isc::asiolink::IOAddress hint) {
PoolPtr Subnet::getPool(Lease::Type type, isc::asiolink::IOAddress hint,
bool anypool /* true */) {
// check if the type is valid (and throw if it isn't)
checkType(type);
......@@ -195,7 +196,7 @@ PoolPtr Subnet::getPool(Lease::Type type, isc::asiolink::IOAddress hint) {
pool != pools->end(); ++pool) {
// if we won't find anything better, then let's just use the first pool
if (!candidate) {
if (anypool && !candidate) {
candidate = *pool;
}
......
......@@ -277,10 +277,17 @@ public:
/// If there is no pool that the address belongs to (hint is invalid), other
/// pool of specified type will be returned.
///
/// With anypool set to true, this is means give me a pool, preferably
/// the one that addr belongs to. With anypool set to false, it means
/// give me a pool that addr belongs to (or NULL if here is no such pool)
///
/// @param type pool type that the pool is looked for
/// @param addr address that the returned pool should cover (optional)
/// @param anypool other pool may be returned as well, not only the one
/// that addr belongs to
/// @return found pool (or NULL)
PoolPtr getPool(Lease::Type type, isc::asiolink::IOAddress addr);
PoolPtr getPool(Lease::Type type, isc::asiolink::IOAddress addr,
bool anypool = true);
/// @brief Returns a pool without any address specified
///
......
......@@ -65,6 +65,16 @@ public:
using AllocEngine::Allocator;
using AllocEngine::IterativeAllocator;
using AllocEngine::getAllocator;
class NakedIterativeAllocator: public AllocEngine::IterativeAllocator {
public:
NakedIterativeAllocator(Lease::Type type)
:IterativeAllocator(type) {
}
using AllocEngine::IterativeAllocator::increaseAddress;
using AllocEngine::IterativeAllocator::increasePrefix;
};
};
/// @brief Used in Allocation Engine tests for IPv6
......@@ -125,12 +135,30 @@ public:
EXPECT_EQ(subnet_->getPreferred(), lease->preferred_lft_);
EXPECT_EQ(subnet_->getT1(), lease->t1_);
EXPECT_EQ(subnet_->getT2(), lease->t2_);
EXPECT_EQ(0, lease->prefixlen_); // this is IA_NA, not IA_PD
EXPECT_EQ(128, lease->prefixlen_); // this is IA_NA, not IA_PD
EXPECT_TRUE(false == lease->fqdn_fwd_);
EXPECT_TRUE(false == lease->fqdn_rev_);
EXPECT_TRUE(*lease->duid_ == *duid_);
// @todo: check cltt
}
}
/// @brief checks if specified address is increased properly
/// @param alloc IterativeAllocator that is tested
/// @param input address to be increased
/// @param exp_output expected address after increase
void
checkAddrIncrease(NakedAllocEngine::NakedIterativeAllocator& alloc,
std::string input, std::string exp_output) {
EXPECT_EQ(exp_output, alloc.increaseAddress(IOAddress(input)).toText());
}
void
checkPrefixIncrease(NakedAllocEngine::NakedIterativeAllocator& alloc,
std::string input, uint8_t prefix_len,
std::string exp_output) {
EXPECT_EQ(exp_output, alloc.increasePrefix(IOAddress(input), prefix_len)
.toText());
}
virtual ~AllocEngine6Test() {
factory_.destroy();
......@@ -423,6 +451,71 @@ TEST_F(AllocEngine6Test, IterativeAllocator) {
}
}
// This test verifies that the allocator iterates over addresses properly
TEST_F(AllocEngine6Test, IterativeAllocatorAddrStep) {
NakedAllocEngine::NakedIterativeAllocator alloc(Lease::TYPE_NA);
// Let's pick the first address
IOAddress addr1 = alloc.pickAddress(subnet_, duid_, IOAddress("2001:db8:1::10"));
// Check that we can indeed pick the first address from the pool
EXPECT_EQ("2001:db8:1::10", addr1.toText());
// Check that addresses can be increased properly
checkAddrIncrease(alloc, "2001:db8::9", "2001:db8::a");
checkAddrIncrease(alloc, "2001:db8::f", "2001:db8::10");
checkAddrIncrease(alloc, "2001:db8::10", "2001:db8::11");
checkAddrIncrease(alloc, "2001:db8::ff", "2001:db8::100");
checkAddrIncrease(alloc, "2001:db8::ffff", "2001:db8::1:0");
checkAddrIncrease(alloc, "::", "::1");
checkAddrIncrease(alloc, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "::");
// Check that prefixes can be increased properly
// For /128 prefix, increasePrefix should work the same as addressIncrease
checkPrefixIncrease(alloc, "2001:db8::9", 128, "2001:db8::a");
checkPrefixIncrease(alloc, "2001:db8::f", 128, "2001:db8::10");
checkPrefixIncrease(alloc, "2001:db8::10", 128, "2001:db8::11");
checkPrefixIncrease(alloc, "2001:db8::ff", 128, "2001:db8::100");
checkPrefixIncrease(alloc, "2001:db8::ffff", 128, "2001:db8::1:0");
checkPrefixIncrease(alloc, "::", 128, "::1");
checkPrefixIncrease(alloc, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 128, "::");
// Check that /64 prefixes can be generated
checkPrefixIncrease(alloc, "2001:db8::", 64, "2001:db8:0:1::");
// Check that prefix length not divisible by 8 are working
checkPrefixIncrease(alloc, "2001:db8::", 128, "2001:db8::1");
checkPrefixIncrease(alloc, "2001:db8::", 127, "2001:db8::2");
checkPrefixIncrease(alloc, "2001:db8::", 126, "2001:db8::4");
checkPrefixIncrease(alloc, "2001:db8::", 125, "2001:db8::8");
checkPrefixIncrease(alloc, "2001:db8::", 124, "2001:db8::10");
checkPrefixIncrease(alloc, "2001:db8::", 123, "2001:db8::20");
checkPrefixIncrease(alloc, "2001:db8::", 122, "2001:db8::40");
checkPrefixIncrease(alloc, "2001:db8::", 121, "2001:db8::80");
checkPrefixIncrease(alloc, "2001:db8::", 120, "2001:db8::100");
// These are not really useful cases, because there are bits set
// int the last (128 - prefix_len) bits. Nevertheless, it shows
// that the algorithm is working even in such cases
checkPrefixIncrease(alloc, "2001:db8::1", 128, "2001:db8::2");
checkPrefixIncrease(alloc, "2001:db8::1", 127, "2001:db8::3");
checkPrefixIncrease(alloc, "2001:db8::1", 126, "2001:db8::5");
checkPrefixIncrease(alloc, "2001:db8::1", 125, "2001:db8::9");
checkPrefixIncrease(alloc, "2001:db8::1", 124, "2001:db8::11");
checkPrefixIncrease(alloc, "2001:db8::1", 123, "2001:db8::21");
checkPrefixIncrease(alloc, "2001:db8::1", 122, "2001:db8::41");
checkPrefixIncrease(alloc, "2001:db8::1", 121, "2001:db8::81");
checkPrefixIncrease(alloc, "2001:db8::1", 120, "2001:db8::101");
// Let's try out couple real life scenarios
checkPrefixIncrease(alloc, "2001:db8:1:abcd::", 64, "2001:db8:1:abce::");
checkPrefixIncrease(alloc, "2001:db8:1:abcd::", 60, "2001:db8:1:abdd::");
checkPrefixIncrease(alloc, "2001:db8:1:abcd::", 56, "2001:db8:1:accd::");
checkPrefixIncrease(alloc, "2001:db8:1:abcd::", 52, "2001:db8:1:bbcd::");
}
// This test verifies that the iterative allocator really walks over all addresses
// in all pools in specified subnet. It also must not pick the same address twice
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment