#include <dhcpsrv/addr_utilities.h>
#include <exceptions/exceptions.h>
+#include <vector>
+#include <limits>
#include <string.h>
using namespace isc;
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));
+ }
+}
+
};
};
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);
};
};
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