Browse Source

[3150] checkType in Subnets implemented

Tomek Mrugalski 11 years ago
parent
commit
22b8b335e3

+ 12 - 8
src/lib/dhcpsrv/alloc_engine.cc

@@ -117,7 +117,7 @@ AllocEngine::IterativeAllocator::pickAddress(const SubnetPtr& subnet,
     if (it == pools.end()) {
         // ok to access first element directly. We checked that pools is non-empty
         IOAddress next = pools[0]->getFirstAddress();
-        subnet->setLastAllocated(next, lease_type_);
+        subnet->setLastAllocated(lease_type_, next);
         return (next);
     }
 
@@ -126,7 +126,7 @@ AllocEngine::IterativeAllocator::pickAddress(const SubnetPtr& subnet,
     IOAddress next = increaseAddress(last); // basically addr++
     if ((*it)->inRange(next)) {
         // the next one is in the pool as well, so we haven't hit pool boundary yet
-        subnet->setLastAllocated(next, lease_type_);
+        subnet->setLastAllocated(lease_type_, next);
         return (next);
     }
 
@@ -136,13 +136,13 @@ AllocEngine::IterativeAllocator::pickAddress(const SubnetPtr& subnet,
         // Really out of luck today. That was the last pool. Let's rewind
         // to the beginning.
         next = pools[0]->getFirstAddress();
-        subnet->setLastAllocated(next, lease_type_);
+        subnet->setLastAllocated(lease_type_, next);
         return (next);
     }
 
     // there is a next pool, let's try first address from it
     next = (*it)->getFirstAddress();
-    subnet->setLastAllocated(next, lease_type_);
+    subnet->setLastAllocated(lease_type_, next);
     return (next);
 }
 
@@ -173,17 +173,21 @@ AllocEngine::RandomAllocator::pickAddress(const SubnetPtr&,
 }
 
 
-AllocEngine::AllocEngine(AllocType engine_type, unsigned int attempts)
+AllocEngine::AllocEngine(AllocType engine_type, unsigned int attempts,
+                         bool ipv6)
     :attempts_(attempts) {
+
+    Pool::PoolType pool_type = ipv6?Pool::TYPE_IA:Pool::TYPE_V4;
+
     switch (engine_type) {
     case ALLOC_ITERATIVE:
-        allocator_.reset(new IterativeAllocator(Pool6::TYPE_IA));
+        allocator_.reset(new IterativeAllocator(pool_type));
         break;
     case ALLOC_HASHED:
-        allocator_.reset(new HashedAllocator(Pool6::TYPE_IA));
+        allocator_.reset(new HashedAllocator(pool_type));
         break;
     case ALLOC_RANDOM:
-        allocator_.reset(new RandomAllocator(Pool6::TYPE_IA));
+        allocator_.reset(new RandomAllocator(pool_type));
         break;
 
     default:

+ 2 - 1
src/lib/dhcpsrv/alloc_engine.h

@@ -193,7 +193,8 @@ protected:
     /// @param engine_type selects allocation algorithm
     /// @param attempts number of attempts for each lease allocation before
     ///        we give up (0 means unlimited)
-    AllocEngine(AllocType engine_type, unsigned int attempts);
+    /// @param ipv6 specifies if the engine should work for IPv4 or IPv6
+    AllocEngine(AllocType engine_type, unsigned int attempts, bool ipv6 = true);
 
     /// @brief Returns IPv4 lease.
     ///

+ 20 - 2
src/lib/dhcpsrv/subnet.cc

@@ -102,8 +102,12 @@ isc::asiolink::IOAddress Subnet::getLastAllocated(Pool::PoolType type) const {
     }
 }
 
-void Subnet::setLastAllocated(const isc::asiolink::IOAddress& addr,
-                              Pool::PoolType type) {
+void Subnet::setLastAllocated(Pool::PoolType type,
+                              const isc::asiolink::IOAddress& addr) {
+
+    // check if the type is valid (and throw if it isn't)
+    checkType(type);
+
     switch (type) {
     case Pool::TYPE_V4:
     case Pool::TYPE_IA:
@@ -127,6 +131,12 @@ Subnet::toText() const {
     return (tmp.str());
 }
 
+void Subnet4::checkType(Pool::PoolType type) {
+    if (type != Pool::TYPE_V4) {
+        isc_throw(BadValue, "Only TYPE_V4 is allowed for Subnet4");
+    }
+}
+
 Subnet4::Subnet4(const isc::asiolink::IOAddress& prefix, uint8_t length,
                  const Triplet<uint32_t>& t1,
                  const Triplet<uint32_t>& t2,
@@ -273,6 +283,14 @@ Subnet6::Subnet6(const isc::asiolink::IOAddress& prefix, uint8_t length,
     }
 }
 
+void Subnet6::checkType(Pool::PoolType type) {
+    if ( (type != Pool::TYPE_IA) && (type != Pool::TYPE_TA) &&
+         (type != Pool::TYPE_PD)) {
+        isc_throw(BadValue, "Invalid Pool type: " << static_cast<int>(type)
+                  << ", must be TYPE_IA, TYPE_TA or TYPE_PD for Subnet6");
+    }
+}
+
 void
 Subnet6::validateOption(const OptionPtr& option) const {
     if (!option) {

+ 30 - 4
src/lib/dhcpsrv/subnet.h

@@ -254,8 +254,8 @@ public:
     ///        AllocEngine::IterativeAllocator and keep the data there
     /// @param addr address/prefix to that was tried last
     /// @param type lease type to be set
-    void setLastAllocated(const isc::asiolink::IOAddress& addr,
-                          Pool::PoolType type);
+    void setLastAllocated(Pool::PoolType type,
+                          const isc::asiolink::IOAddress& addr);
 
     /// @brief returns unique ID for that subnet
     /// @return unique ID for that subnet
@@ -309,12 +309,12 @@ public:
     /// @param iface_name name of the interface
     void setIface(const std::string& iface_name);
 
-    /// @brief network interface name used to reach subnet (or "" for remote 
+    /// @brief network interface name used to reach subnet (or "" for remote
     /// subnets)
     /// @return network interface name for directly attached subnets or ""
     std::string getIface() const;
 
-    /// @brief returns textual representation of the subnet (e.g. 
+    /// @brief returns textual representation of the subnet (e.g.
     /// "2001:db8::/64")
     ///
     /// @return textual representation
@@ -344,6 +344,16 @@ protected:
         return (id++);
     }
 
+    /// @brief Checks if used pool type is valid
+    ///
+    /// Allowed type for Subnet4 is Pool::TYPE_V4.
+    /// Allowed types for Subnet6 are Pool::TYPE_{IA,TA,PD}.
+    /// This method is implemented in derived classes.
+    ///
+    /// @param type type to be checked
+    /// @throw BadValue if invalid value is used
+    virtual void checkType(Pool::PoolType type) = 0;
+
     /// @brief Check if option is valid and can be added to a subnet.
     ///
     /// @param option option to be validated.
@@ -447,6 +457,14 @@ protected:
     virtual isc::asiolink::IOAddress default_pool() const {
         return (isc::asiolink::IOAddress("0.0.0.0"));
     }
+
+    /// @brief Checks if used pool type is valid
+    ///
+    /// Allowed type for Subnet4 is Pool::TYPE_V4.
+    ///
+    /// @param type type to be checked
+    /// @throw BadValue if invalid value is used
+    virtual void checkType(Pool::PoolType type);
 };
 
 /// @brief A pointer to a Subnet4 object
@@ -511,6 +529,14 @@ protected:
         return (isc::asiolink::IOAddress("::"));
     }
 
+    /// @brief Checks if used pool type is valid
+    ///
+    /// allowed types for Subnet6 are Pool::TYPE_{IA,TA,PD}.
+    ///
+    /// @param type type to be checked
+    /// @throw BadValue if invalid value is used
+    virtual void checkType(Pool::PoolType type);
+
     /// @brief specifies optional interface-id
     OptionPtr interface_id_;
 

+ 30 - 15
src/lib/dhcpsrv/tests/alloc_engine_unittest.cc

@@ -55,8 +55,10 @@ public:
     /// @brief the sole constructor
     /// @param engine_type specifies engine type (e.g. iterative)
     /// @param attempts number of lease selection attempts before giving up
-    NakedAllocEngine(AllocEngine::AllocType engine_type, unsigned int attempts)
-        :AllocEngine(engine_type, attempts) {
+    /// @param ipv6 specifies if the engine is IPv6 or IPv4
+    NakedAllocEngine(AllocEngine::AllocType engine_type,
+                     unsigned int attempts, bool ipv6 = true)
+        :AllocEngine(engine_type, attempts, ipv6) {
     }
 
     // Expose internal classes for testing purposes
@@ -607,7 +609,8 @@ TEST_F(AllocEngine6Test, requestReuseExpiredLease6) {
 // This test checks if the simple IPv4 allocation can succeed
 TEST_F(AllocEngine4Test, simpleAlloc4) {
     boost::scoped_ptr<AllocEngine> engine;
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
+                                                 100, false)));
     ASSERT_TRUE(engine);
 
     Lease4Ptr lease = engine->allocateAddress4(subnet_, clientid_, hwaddr_,
@@ -636,7 +639,8 @@ TEST_F(AllocEngine4Test, simpleAlloc4) {
 // This test checks if the fake allocation (for DISCOVER) can succeed
 TEST_F(AllocEngine4Test, fakeAlloc4) {
     boost::scoped_ptr<AllocEngine> engine;
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
+                                                 100, false)));
     ASSERT_TRUE(engine);
 
     Lease4Ptr lease = engine->allocateAddress4(subnet_, clientid_, hwaddr_,
@@ -664,7 +668,8 @@ TEST_F(AllocEngine4Test, fakeAlloc4) {
 // in pool and free) can succeed
 TEST_F(AllocEngine4Test, allocWithValidHint4) {
     boost::scoped_ptr<AllocEngine> engine;
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
+                                                 100, false)));
     ASSERT_TRUE(engine);
 
     Lease4Ptr lease = engine->allocateAddress4(subnet_, clientid_, hwaddr_,
@@ -697,7 +702,8 @@ TEST_F(AllocEngine4Test, allocWithValidHint4) {
 // in pool, but is currently used) can succeed
 TEST_F(AllocEngine4Test, allocWithUsedHint4) {
     boost::scoped_ptr<AllocEngine> engine;
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
+                                                 100, false)));
     ASSERT_TRUE(engine);
 
     // Let's create a lease and put it in the LeaseMgr
@@ -745,7 +751,8 @@ TEST_F(AllocEngine4Test, allocWithUsedHint4) {
 // can succeed. The invalid hint should be ignored completely.
 TEST_F(AllocEngine4Test, allocBogusHint4) {
     boost::scoped_ptr<AllocEngine> engine;
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
+                                                 100, false)));
     ASSERT_TRUE(engine);
 
     // Client would like to get a 3000::abc lease, which does not belong to any
@@ -780,7 +787,8 @@ TEST_F(AllocEngine4Test, allocBogusHint4) {
 // This test checks that NULL values are handled properly
 TEST_F(AllocEngine4Test, allocateAddress4Nulls) {
     boost::scoped_ptr<AllocEngine> engine;
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
+                                                 100, false)));
     ASSERT_TRUE(engine);
 
     // Allocations without subnet are not allowed
@@ -898,7 +906,8 @@ TEST_F(AllocEngine4Test, IterativeAllocator_manyPools4) {
 // This test checks if really small pools are working
 TEST_F(AllocEngine4Test, smallPool4) {
     boost::scoped_ptr<AllocEngine> engine;
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
+                                                 100, false)));
     ASSERT_TRUE(engine);
 
     IOAddress addr("192.0.2.17");
@@ -940,7 +949,8 @@ TEST_F(AllocEngine4Test, smallPool4) {
 // to find out a new lease fails.
 TEST_F(AllocEngine4Test, outOfAddresses4) {
     boost::scoped_ptr<AllocEngine> engine;
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
+                                                 100, false)));
     ASSERT_TRUE(engine);
 
     IOAddress addr("192.0.2.17");
@@ -978,7 +988,8 @@ TEST_F(AllocEngine4Test, outOfAddresses4) {
 // This test checks if an expired lease can be reused in DISCOVER (fake allocation)
 TEST_F(AllocEngine4Test, discoverReuseExpiredLease4) {
     boost::scoped_ptr<AllocEngine> engine;
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
+                                                 100, false)));
     ASSERT_TRUE(engine);
 
     IOAddress addr("192.0.2.15");
@@ -1045,7 +1056,8 @@ TEST_F(AllocEngine4Test, discoverReuseExpiredLease4) {
 // This test checks if an expired lease can be reused in REQUEST (actual allocation)
 TEST_F(AllocEngine4Test, requestReuseExpiredLease4) {
     boost::scoped_ptr<AllocEngine> engine;
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
+                                                 100, false)));
     ASSERT_TRUE(engine);
 
     IOAddress addr("192.0.2.105");
@@ -1098,7 +1110,8 @@ TEST_F(AllocEngine4Test, renewLease4) {
     boost::scoped_ptr<AllocEngine> engine;
     CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
 
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
+                                                 100, false)));
     ASSERT_TRUE(engine);
 
     IOAddress addr("192.0.2.102");
@@ -1471,7 +1484,8 @@ TEST_F(HookAllocEngine4Test, lease4_select) {
 
     // Create allocation engine (hook names are registered in its ctor)
     boost::scoped_ptr<AllocEngine> engine;
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
+                                                 100, false)));
     ASSERT_TRUE(engine);
 
     // Initialize Hooks Manager
@@ -1534,7 +1548,8 @@ TEST_F(HookAllocEngine4Test, change_lease4_select) {
 
     // Create allocation engine (hook names are registered in its ctor)
     boost::scoped_ptr<AllocEngine> engine;
-    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+    ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE,
+                                                 100, false)));
     ASSERT_TRUE(engine);
 
     // Initialize Hooks Manager

+ 57 - 0
src/lib/dhcpsrv/tests/subnet_unittest.cc

@@ -175,6 +175,28 @@ TEST(Subnet4Test, get) {
     EXPECT_EQ(28, subnet->get().second);
 }
 
+
+// Checks if last allocated address/prefix is stored/retrieved properly
+TEST(Subnet4Test, lastAllocated) {
+    IOAddress addr("192.0.2.17");
+
+    IOAddress last("192.0.2.255");
+
+    Subnet4Ptr subnet(new Subnet4(IOAddress("192.0.2.0"), 24, 1, 2, 3));
+
+    // Check initial conditions (all should be set to the last address in range)
+    EXPECT_EQ(last.toText(), subnet->getLastAllocated(Pool::TYPE_V4).toText());
+
+    // Now set last allocated for IA
+    EXPECT_NO_THROW(subnet->setLastAllocated(Pool::TYPE_V4, addr));
+    EXPECT_EQ(addr.toText(), subnet->getLastAllocated(Pool::TYPE_V4).toText());
+
+    // No, you can't set the last allocated IPv6 address in IPv4 subnet
+    EXPECT_THROW(subnet->setLastAllocated(Pool::TYPE_IA, addr), BadValue);
+    EXPECT_THROW(subnet->setLastAllocated(Pool::TYPE_TA, addr), BadValue);
+    EXPECT_THROW(subnet->setLastAllocated(Pool::TYPE_PD, addr), BadValue);
+}
+
 // Tests for Subnet6
 
 TEST(Subnet6Test, constructor) {
@@ -532,4 +554,39 @@ TEST(Subnet6Test, interfaceId) {
 
 }
 
+// Checks if last allocated address/prefix is stored/retrieved properly
+TEST(Subnet6Test, lastAllocated) {
+    IOAddress ia("2001:db8:1::1");
+    IOAddress ta("2001:db8:1::abcd");
+    IOAddress pd("2001:db8:1::1234:5678");
+
+    IOAddress last("2001:db8:1::ffff:ffff:ffff:ffff");
+
+    Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 64, 1, 2, 3, 4));
+
+    // Check initial conditions (all should be set to the last address in range)
+    EXPECT_EQ(last.toText(), subnet->getLastAllocated(Pool::TYPE_IA).toText());
+    EXPECT_EQ(last.toText(), subnet->getLastAllocated(Pool::TYPE_TA).toText());
+    EXPECT_EQ(last.toText(), subnet->getLastAllocated(Pool::TYPE_PD).toText());
+
+    // Now set last allocated for IA
+    EXPECT_NO_THROW(subnet->setLastAllocated(Pool::TYPE_IA, ia));
+    EXPECT_EQ(ia.toText(), subnet->getLastAllocated(Pool::TYPE_IA).toText());
+
+    // TA and PD should be unchanged
+    EXPECT_EQ(last.toText(), subnet->getLastAllocated(Pool::TYPE_TA).toText());
+    EXPECT_EQ(last.toText(), subnet->getLastAllocated(Pool::TYPE_PD).toText());
+
+    // Now set TA and PD
+    EXPECT_NO_THROW(subnet->setLastAllocated(Pool::TYPE_TA, ta));
+    EXPECT_NO_THROW(subnet->setLastAllocated(Pool::TYPE_PD, pd));
+
+    EXPECT_EQ(ia.toText(), subnet->getLastAllocated(Pool::TYPE_IA).toText());
+    EXPECT_EQ(ta.toText(), subnet->getLastAllocated(Pool::TYPE_TA).toText());
+    EXPECT_EQ(pd.toText(), subnet->getLastAllocated(Pool::TYPE_PD).toText());
+
+    // No, you can't set the last allocated IPv4 address in IPv6 subnet
+    EXPECT_THROW(subnet->setLastAllocated(Pool::TYPE_V4, ia), BadValue);
+}
+
 };