From: Tomek Mrugalski Date: Tue, 17 Feb 2015 14:59:57 +0000 (+0100) Subject: [3711] addrInRange, prefixesInRange implemented. X-Git-Tag: trac3723_base~18^2~7 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=0bdd2911c3f091149a0d2b8bddbf4c865e58a2cc;p=thirdparty%2Fkea.git [3711] addrInRange, prefixesInRange implemented. --- diff --git a/src/lib/dhcpsrv/addr_utilities.cc b/src/lib/dhcpsrv/addr_utilities.cc index d32c209792..1c96010296 100644 --- a/src/lib/dhcpsrv/addr_utilities.cc +++ b/src/lib/dhcpsrv/addr_utilities.cc @@ -15,6 +15,8 @@ #include #include +#include +#include #include 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::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::max. Let's do it the manual way. + std::vector 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::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::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)); + } +} + }; }; diff --git a/src/lib/dhcpsrv/addr_utilities.h b/src/lib/dhcpsrv/addr_utilities.h index 2cc6ea51fe..84262cd874 100644 --- a/src/lib/dhcpsrv/addr_utilities.h +++ b/src/lib/dhcpsrv/addr_utilities.h @@ -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); }; }; diff --git a/src/lib/dhcpsrv/tests/addr_utilities_unittest.cc b/src/lib/dhcpsrv/tests/addr_utilities_unittest.cc index cdd59dfa80..bbb7d71eaf 100644 --- a/src/lib/dhcpsrv/tests/addr_utilities_unittest.cc +++ b/src/lib/dhcpsrv/tests/addr_utilities_unittest.cc @@ -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::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::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::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::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::max(), + prefixesInRange(64, 128)); + + // Let's go overboard again. How many IPv6 addresses are there? + EXPECT_EQ(std::numeric_limits::max(), + prefixesInRange(0, 128)); + +} + }; // end of anonymous namespace