Commit 0bdd2911 authored by Tomek Mrugalski's avatar Tomek Mrugalski 🛰
Browse files

[3711] addrInRange, prefixesInRange implemented.

parent 37d0f589
......@@ -15,6 +15,8 @@
#include <dhcpsrv/addr_utilities.h>
#include <exceptions/exceptions.h>
#include <vector>
#include <limits>
#include <string.h>
using namespace isc;
......@@ -205,5 +207,95 @@ isc::asiolink::IOAddress getNetmask4(uint8_t len) {
return (IOAddress(x));
}
uint64_t
addrsInRange(const isc::asiolink::IOAddress& min,
const isc::asiolink::IOAddress& max) {
if (min.isV4() != max.isV4()) {
isc_throw(BadValue, "Both addresses have to be the same family");
}
if (min.isV4()) {
// Let's explicitly cast last_ and first_ (IOAddress). This conversion is
// automatic, but let's explicitly cast it show that we moved to integer
// domain and addresses are now substractable.
uint64_t max_numeric = uint32_t(max);
uint64_t min_numeric = uint32_t(min);
// We can simply subtract the values. We need to increase the result
// by one, as both min and max are included in the range. So even if
// min == max, there's one address.
return (max_numeric - min_numeric + 1);
} else {
// Calculating the difference in v6 is more involved. Let's subtract
// one from the other. By subtracting min from max, we move the
// [a, b] range to the [0, (b-a)] range. We don't care about the beginning
// of the new range (it's always zero). The upper bound now specifies
// the number of addresses minus one.
IOAddress count = IOAddress::subtract(max, min);
// There's one very special case. Someone is trying to check how many
// IPv6 addresses are in IPv6 address space. He called this method
// with ::, ffff:ffff:ffff:fffff:ffff:ffff:ffff:ffff. The diff is also
// all 1s. Had we increased it by one, the address would flip to all 0s.
// This will not happen in a real world. Apparently, unit-tests are
// sometimes nastier then a real world.
static IOAddress max6("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
if (count == max6) {
return (std::numeric_limits<uint64_t>::max());
}
// Increase it by one (a..a range still contains one address, even though
// a subtracted from a is zero).
count = IOAddress::increaseAddress(count);
// We don't have uint128, so for anything greater than 2^64, we'll just
// assume numeric_limits<uint64_t>::max. Let's do it the manual way.
std::vector<uint8_t> bin = count.toBytes();
// If any of the most significant 64 bits is set, we have more than
// 2^64 addresses and can't represent it even on uint64_t.
for (int i = 0 ; i < 8; i++) {
if (bin[i]) {
return (std::numeric_limits<uint64_t>::max());
}
}
// Ok, we're good. The pool is sanely sized. It may be huge, but at least
// that's something we can represent on uint64_t.
uint64_t numeric = 0;
for (int i = 8; i < 16; i++) {
numeric <<= 8;
numeric += bin[i];
}
return (numeric);
}
}
uint64_t prefixesInRange(uint8_t pool_len, uint8_t delegated_len) {
if (delegated_len < pool_len) {
return (0);
}
uint64_t count = delegated_len - pool_len;
if (count == 0) {
// If we want to delegate /64 out of /64 pool, we have only
// one prefix.
return (1);
} else if (count >= 64) {
// If the difference is greater than or equal 64, e.g. we want to
// delegate /96 out of /16 pool, the number is bigger than we can
// express, so we'll stick with maximum value of uint64_t.
return (std::numeric_limits<uint64_t>::max());
} else {
// Now count specifies the exponent (e.g. if the difference between the
// delegated and pool length is 4, we have 16 prefixes), so we need
// to calculate 2^(count - 1)
return (((uint64_t)2) << (count - 1));
}
}
};
};
......@@ -52,11 +52,37 @@ isc::asiolink::IOAddress firstAddrInPrefix(const isc::asiolink::IOAddress& prefi
isc::asiolink::IOAddress lastAddrInPrefix(const isc::asiolink::IOAddress& prefix,
uint8_t len);
/// @brief generates an IPv4 netmask of specified length
/// @brief Generates an IPv4 netmask of specified length
/// @throw BadValue if len is greater than 32
/// @return netmask
isc::asiolink::IOAddress getNetmask4(uint8_t len);
/// @brief Returns number of available addresses in the specified range (min - max).
///
/// Note that for min equal max case, the number of addresses is one.
///
/// @throw BadValue if min and max are not of the same family (both must be
/// either IPv4 or IPv6) or if max < min.
///
/// @param min the first address in range
/// @param max the last address in range
/// @return number of addresses in range
uint64_t addrsInRange(const isc::asiolink::IOAddress& min,
const isc::asiolink::IOAddress& max);
/// @brief Returns number of available IPv6 prefixes in the specified prefix.
///
/// Note that if the answer is bigger than uint64_t can hold, it will return
/// the max value of uint64_t type.
///
/// Example: prefixesInRange(48, 64) returns 65536, as there are /48 pool
/// can be split into 65536 prefixes, each /64 large.
///
/// @param min the first address in range
/// @param max the last address in range
/// @return number of addresses in range
uint64_t prefixesInRange(uint8_t pool_len, uint8_t delegated_len);
};
};
......
......@@ -200,4 +200,75 @@ TEST(AddrUtilitiesTest, getNetmask4) {
EXPECT_THROW(getNetmask4(33), isc::BadValue);
}
// Checks if the calculation for IPv4 addresses in range are correct.
TEST(AddrUtilitiesTest, addrsInRange4) {
// Let's start with something simple
EXPECT_EQ(1, addrsInRange(IOAddress("192.0.2.0"), IOAddress("192.0.2.0")));
EXPECT_EQ(10, addrsInRange(IOAddress("192.0.2.10"), IOAddress("192.0.2.19")));
EXPECT_EQ(256, addrsInRange(IOAddress("192.0.2.0"), IOAddress("192.0.2.255")));
EXPECT_EQ(65536, addrsInRange(IOAddress("192.0.0.0"), IOAddress("192.0.255.255")));
EXPECT_EQ(16777216, addrsInRange(IOAddress("10.0.0.0"), IOAddress("10.255.255.255")));
// Let's check if the network boundaries are crossed correctly.
EXPECT_EQ(3, addrsInRange(IOAddress("10.0.0.255"), IOAddress("10.0.1.1")));
// Let's go a bit overboard with this! How many addresses are there in
// IPv4 address space? That's a slightly tricku question, as the answer
// cannot be stored in uint32_t.
EXPECT_EQ(uint64_t(std::numeric_limits<uint32_t>::max()) + 1,
addrsInRange(IOAddress("0.0.0.0"), IOAddress("255.255.255.255")));
}
// Checks if the calculation for IPv6 addresses in range are correct.
TEST(AddrUtilitiesTest, addrsInRange6) {
// Let's start with something simple
EXPECT_EQ(1, addrsInRange(IOAddress("::"), IOAddress("::")));
EXPECT_EQ(16, addrsInRange(IOAddress("fe80::1"), IOAddress("fe80::10")));
EXPECT_EQ(65536, addrsInRange(IOAddress("fe80::"), IOAddress("fe80::ffff")));
EXPECT_EQ(uint64_t(std::numeric_limits<uint32_t>::max()) + 1,
addrsInRange(IOAddress("fe80::"), IOAddress("fe80::ffff:ffff")));
// There's 2^80 addresses between those. Due to uint64_t limits, this method is
// capped at 2^64 -1.
EXPECT_EQ(std::numeric_limits<uint64_t>::max(),
addrsInRange(IOAddress("2001:db8:1::"), IOAddress("2001:db8:2::")));
// Let's check if the network boundaries are crossed correctly.
EXPECT_EQ(3, addrsInRange(IOAddress("2001:db8::ffff"), IOAddress("2001:db8::1:1")));
// Let's go a bit overboard with this! How many addresses are there in
// IPv6 address space? That's a really tricky question, as the answer
// wouldn't fit even in uint128_t (if we had it). This method is capped
// at max value of uint64_t.
EXPECT_EQ(std::numeric_limits<uint64_t>::max(), addrsInRange(IOAddress("::"),
IOAddress("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")));
}
// Checks if prefixInRange returns valid number of prefixes in specified range.
TEST(AddrUtilitiesTest, prefixesInRange) {
// How many /64 prefixes are in /64 pool?
EXPECT_EQ(1, prefixesInRange(64, 64));
// How many /63 prefixes are in /64 pool?
EXPECT_EQ(2, prefixesInRange(63, 64));
// How many /64 prefixes are in /48 pool?
EXPECT_EQ(65536, prefixesInRange(48, 64));
// How many /127 prefixes are in /64 pool?
EXPECT_EQ(uint64_t(9223372036854775808u), prefixesInRange(64, 127));
// How many /128 prefixes are in /64 pool?
EXPECT_EQ(std::numeric_limits<uint64_t>::max(),
prefixesInRange(64, 128));
// Let's go overboard again. How many IPv6 addresses are there?
EXPECT_EQ(std::numeric_limits<uint64_t>::max(),
prefixesInRange(0, 128));
}
}; // end of anonymous namespace
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