diff --git a/src/lib/dhcpsrv/subnet.cc b/src/lib/dhcpsrv/subnet.cc index 44efbeecc1..aa96921711 100644 --- a/src/lib/dhcpsrv/subnet.cc +++ b/src/lib/dhcpsrv/subnet.cc @@ -133,6 +133,40 @@ Subnet::toText() const { return (tmp.str()); } +uint64_t +Subnet::getLeasesCount(Lease::Type type) const { + switch (type) { + case Lease::TYPE_V4: + case Lease::TYPE_NA: + return sumLeasesCount(pools_); + case Lease::TYPE_TA: + return sumLeasesCount(pools_ta_); + case Lease::TYPE_PD: + return sumLeasesCount(pools_pd_); + default: + isc_throw(BadValue, "Unsupported pool type: " + << static_cast(type)); + } +} + +uint64_t +Subnet::sumLeasesCount(const PoolCollection& pools) const { + uint64_t sum = 0; + for (PoolCollection::const_iterator p = pools.begin(); p != pools.end(); ++p) { + uint64_t x = (*p)->getLeasesCount(); + + // Check if we can add it. If sum + x > uint64::max, then we would have + // overflown if we tried to add it. + if (x > std::numeric_limits::max() - sum) { + return (std::numeric_limits::max()); + } + + sum += x; + } + + return (sum); +} + void Subnet4::checkType(Lease::Type type) const { if (type != Lease::TYPE_V4) { isc_throw(BadValue, "Only TYPE_V4 is allowed for Subnet4"); diff --git a/src/lib/dhcpsrv/subnet.h b/src/lib/dhcpsrv/subnet.h index d037fcecaf..9d5f1182b5 100644 --- a/src/lib/dhcpsrv/subnet.h +++ b/src/lib/dhcpsrv/subnet.h @@ -233,6 +233,11 @@ public: /// @return a collection of all pools const PoolCollection& getPools(Lease::Type type) const; + /// @brief Returns the number of possible leases for specified lease type + /// + /// @param type type of the lease + uint64_t getLeasesCount(Lease::Type type) const; + /// @brief Sets name of the network interface for directly attached networks /// /// @param iface_name name of the interface @@ -403,6 +408,12 @@ protected: /// @throw BadValue if invalid value is used virtual void checkType(Lease::Type type) const = 0; + + /// @brief returns a sum of possible leases in all pools + /// @param pools list of pools + /// @return sum of possible leases + uint64_t sumLeasesCount(const PoolCollection& pools) const; + /// @brief subnet-id /// /// Subnet-id is a unique value that can be used to find or identify diff --git a/src/lib/dhcpsrv/tests/pool_unittest.cc b/src/lib/dhcpsrv/tests/pool_unittest.cc index f7d4fdd2b3..0430d3e136 100644 --- a/src/lib/dhcpsrv/tests/pool_unittest.cc +++ b/src/lib/dhcpsrv/tests/pool_unittest.cc @@ -289,5 +289,4 @@ TEST(Pool6Test, leasesCount) { } - }; // end of anonymous namespace diff --git a/src/lib/dhcpsrv/tests/subnet_unittest.cc b/src/lib/dhcpsrv/tests/subnet_unittest.cc index 6a52f9acb1..b88d1460a2 100644 --- a/src/lib/dhcpsrv/tests/subnet_unittest.cc +++ b/src/lib/dhcpsrv/tests/subnet_unittest.cc @@ -22,6 +22,7 @@ #include #include +#include // don't import the entire boost namespace. It will unexpectedly hide uint8_t // for some systems. @@ -147,6 +148,32 @@ TEST(Subnet4Test, Pool4InSubnet4) { } +// Check if it's possible to get specified number of possible leases for +// an IPv4 subnet. +TEST(Subnet4Test, getLeasesCount) { + + // There's one /24 pool. + Subnet4Ptr subnet(new Subnet4(IOAddress("192.1.2.0"), 24, 1, 2, 3)); + + // There are no pools defined, so the total number of available addrs is 0. + EXPECT_EQ(0, subnet->getLeasesCount(Lease::TYPE_V4)); + + // Let's add a /25 pool. That's 128 addresses. + PoolPtr pool1(new Pool4(IOAddress("192.1.2.0"), 25)); + subnet->addPool(pool1); + EXPECT_EQ(128, subnet->getLeasesCount(Lease::TYPE_V4)); + + // Let's add another /26 pool. That's extra 64 addresses. + PoolPtr pool2(new Pool4(IOAddress("192.1.2.128"), 26)); + subnet->addPool(pool2); + EXPECT_EQ(192, subnet->getLeasesCount(Lease::TYPE_V4)); + + // Let's add a third pool /30. This one has 4 addresses. + PoolPtr pool3(new Pool4(IOAddress("192.1.2.192"), 30)); + subnet->addPool(pool3); + EXPECT_EQ(196, subnet->getLeasesCount(Lease::TYPE_V4)); +} + TEST(Subnet4Test, Subnet4_Pool4_checks) { Subnet4Ptr subnet(new Subnet4(IOAddress("192.0.2.0"), 8, 1, 2, 3)); @@ -434,6 +461,84 @@ TEST(Subnet6Test, relay) { EXPECT_EQ("2001:ffff::1", subnet.getRelayInfo().addr_.toText()); } +// Test checks whether the number of addresses available in the pools are +// calculated properly. +TEST(Subnet6Test, Pool6LeasesCount) { + + Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4)); + + // There's 2^16 = 65536 addresses in this one. + PoolPtr pool1(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1:1::"), 112)); + + // There's 2^32 = 4294967296 addresses in each of those. + PoolPtr pool2(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1:2::"), 96)); + PoolPtr pool3(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1:3::"), 96)); + + EXPECT_EQ(0, subnet->getLeasesCount(Lease::TYPE_NA)); + EXPECT_EQ(0, subnet->getLeasesCount(Lease::TYPE_TA)); + EXPECT_EQ(0, subnet->getLeasesCount(Lease::TYPE_PD)); + + subnet->addPool(pool1); + EXPECT_EQ(65536, subnet->getLeasesCount(Lease::TYPE_NA)); + + subnet->addPool(pool2); + EXPECT_EQ(uint64_t(4294967296 + 65536), subnet->getLeasesCount(Lease::TYPE_NA)); + + subnet->addPool(pool3); + EXPECT_EQ(uint64_t(4294967296 + 4294967296 + 65536), + subnet->getLeasesCount(Lease::TYPE_NA)); + + // This is 2^64 prefixes. We're overflown uint64_t. + PoolPtr pool4(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1:4::"), 64)); + subnet->addPool(pool4); + EXPECT_EQ(std::numeric_limits::max(), + subnet->getLeasesCount(Lease::TYPE_NA)); + + PoolPtr pool5(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1:5::"), 64)); + subnet->addPool(pool5); + EXPECT_EQ(std::numeric_limits::max(), + subnet->getLeasesCount(Lease::TYPE_NA)); +} + +// Test checks whether the number of prefixes available in the pools are +// calculated properly. +TEST(Subnet6Test, Pool6PdLeasesCount) { + + Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8::"), 32, 1, 2, 3, 4)); + + // There's 2^16 = 65536 addresses in this one. + PoolPtr pool1(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:1::"), 48, 64)); + + // There's 2^32 = 4294967296 addresses in each of those. + PoolPtr pool2(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:2::"), 48, 80)); + PoolPtr pool3(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:3::"), 48, 80)); + + EXPECT_EQ(0, subnet->getLeasesCount(Lease::TYPE_NA)); + EXPECT_EQ(0, subnet->getLeasesCount(Lease::TYPE_TA)); + EXPECT_EQ(0, subnet->getLeasesCount(Lease::TYPE_PD)); + + subnet->addPool(pool1); + EXPECT_EQ(65536, subnet->getLeasesCount(Lease::TYPE_PD)); + + subnet->addPool(pool2); + EXPECT_EQ(uint64_t(4294967296 + 65536), subnet->getLeasesCount(Lease::TYPE_PD)); + + subnet->addPool(pool3); + EXPECT_EQ(uint64_t(4294967296 + 4294967296 + 65536), + subnet->getLeasesCount(Lease::TYPE_PD)); + + // This is 2^64. + PoolPtr pool4(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:4::"), 48, 112)); + subnet->addPool(pool4); + EXPECT_EQ(std::numeric_limits::max(), + subnet->getLeasesCount(Lease::TYPE_PD)); + + PoolPtr pool5(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:5::"), 48, 112)); + subnet->addPool(pool5); + EXPECT_EQ(std::numeric_limits::max(), + subnet->getLeasesCount(Lease::TYPE_PD)); +} + TEST(Subnet6Test, Pool6InSubnet6) { Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));