Browse Source

[3150] Support for different pool types in subnet implemented

Tomek Mrugalski 11 years ago
parent
commit
f6619e644f

+ 1 - 1
src/lib/dhcpsrv/alloc_engine.cc

@@ -96,7 +96,7 @@ AllocEngine::IterativeAllocator::pickAddress(const SubnetPtr& subnet,
     // perhaps restarting the server).
     IOAddress last = subnet->getLastAllocated(lease_type_);
 
-    const PoolCollection& pools = subnet->getPools();
+    const PoolCollection& pools = subnet->getPools(lease_type_);
 
     if (pools.empty()) {
         isc_throw(AllocFailed, "No pools defined in selected subnet");

+ 13 - 9
src/lib/dhcpsrv/pool.cc

@@ -21,9 +21,9 @@ using namespace isc::asiolink;
 namespace isc {
 namespace dhcp {
 
-Pool::Pool(const isc::asiolink::IOAddress& first,
+Pool::Pool(PoolType type, const isc::asiolink::IOAddress& first,
            const isc::asiolink::IOAddress& last)
-    :id_(getNextID()), first_(first), last_(last) {
+    :id_(getNextID()), first_(first), last_(last), type_(type) {
 }
 
 bool Pool::inRange(const isc::asiolink::IOAddress& addr) const {
@@ -32,7 +32,7 @@ bool Pool::inRange(const isc::asiolink::IOAddress& addr) const {
 
 Pool4::Pool4(const isc::asiolink::IOAddress& first,
              const isc::asiolink::IOAddress& last)
-:Pool(first, last) {
+:Pool(Pool::TYPE_V4, first, last) {
     // check if specified address boundaries are sane
     if (!first.isV4() || !last.isV4()) {
         isc_throw(BadValue, "Invalid Pool4 address boundaries: not IPv4");
@@ -43,9 +43,8 @@ Pool4::Pool4(const isc::asiolink::IOAddress& first,
     }
 }
 
-Pool4::Pool4(const isc::asiolink::IOAddress& prefix,
-             uint8_t prefix_len)
-    :Pool(prefix, IOAddress("0.0.0.0")) {
+Pool4::Pool4( const isc::asiolink::IOAddress& prefix, uint8_t prefix_len)
+:Pool(Pool::TYPE_V4, prefix, IOAddress("0.0.0.0")) {
 
     // check if the prefix is sane
     if (!prefix.isV4()) {
@@ -64,13 +63,19 @@ Pool4::Pool4(const isc::asiolink::IOAddress& prefix,
 
 Pool6::Pool6(PoolType type, const isc::asiolink::IOAddress& first,
              const isc::asiolink::IOAddress& last)
-    :Pool(first, last), type_(type) {
+    :Pool(type, first, last) {
 
     // check if specified address boundaries are sane
     if (!first.isV6() || !last.isV6()) {
         isc_throw(BadValue, "Invalid Pool6 address boundaries: not IPv6");
     }
 
+    if ( (type != Pool::TYPE_IA) && (type != Pool::TYPE_TA) &&
+         (type != Pool::TYPE_PD)) {
+        isc_throw(BadValue, "Invalid Pool6 type: " << static_cast<int>(type)
+                  << ", must be TYPE_IA, TYPE_TA or TYPE_PD");
+    }
+
     if (last < first) {
         isc_throw(BadValue, "Upper boundary is smaller than lower boundary.");
         // This check is a bit strict. If we decide that it is too strict,
@@ -95,8 +100,7 @@ Pool6::Pool6(PoolType type, const isc::asiolink::IOAddress& first,
 
 Pool6::Pool6(PoolType type, const isc::asiolink::IOAddress& prefix,
              uint8_t prefix_len, uint8_t delegated_len /* = 128 */)
-    :Pool(prefix, IOAddress("::")),
-     type_(type), prefix_len_(delegated_len) {
+    :Pool(type, prefix, IOAddress("::")), prefix_len_(delegated_len) {
 
     // check if the prefix is sane
     if (!prefix.isV6()) {

+ 23 - 10
src/lib/dhcpsrv/pool.h

@@ -77,6 +77,20 @@ public:
     /// @return true, if the address is in pool
     bool inRange(const isc::asiolink::IOAddress& addr) const;
 
+    /// @brief Returns pool type (v4, v6 non-temporary, v6 temp, v6 prefix)
+    /// @return returns pool type
+    PoolType getType() const {
+        return (type_);
+    }
+
+    /// @brief virtual destructor
+    ///
+    /// We need Pool to be a polymorphic class, so we could dynamic cast
+    /// from PoolPtr to Pool6Ptr if we need to. A class becomes polymorphic,
+    /// when there is at least one virtual method.
+    virtual ~Pool() {
+    }
+
 protected:
 
     /// @brief protected constructor
@@ -84,7 +98,12 @@ protected:
     /// This constructor is protected to prevent anyone from instantiating
     /// Pool class directly. Instances of Pool4 and Pool6 should be created
     /// instead.
-    Pool(const isc::asiolink::IOAddress& first,
+    ///
+    /// @param type type of the pool
+    /// @param first first address of a range
+    /// @param last last address of a range
+    Pool(PoolType type,
+         const isc::asiolink::IOAddress& first,
          const isc::asiolink::IOAddress& last);
 
     /// @brief returns the next unique Pool-ID
@@ -110,6 +129,9 @@ protected:
     ///
     /// @todo: This field is currently not used.
     std::string comments_;
+
+    /// @brief defines a pool type
+    PoolType type_;
 };
 
 /// @brief Pool information for IPv4 addresses
@@ -136,9 +158,6 @@ public:
 /// @brief a pointer an IPv4 Pool
 typedef boost::shared_ptr<Pool4> Pool4Ptr;
 
-/// @brief a container for IPv4 Pools
-typedef std::vector<Pool4Ptr> Pool4Collection;
-
 /// @brief Pool information for IPv6 addresses and prefixes
 ///
 /// It holds information about pool6, i.e. a range of IPv6 address space that
@@ -197,9 +216,6 @@ public:
     }
 
 private:
-    /// @brief defines a pool type
-    PoolType type_;
-
     /// @brief Defines prefix length (for TYPE_PD only)
     uint8_t prefix_len_;
 };
@@ -207,9 +223,6 @@ private:
 /// @brief a pointer an IPv6 Pool
 typedef boost::shared_ptr<Pool6> Pool6Ptr;
 
-/// @brief a container for IPv6 Pools
-typedef std::vector<Pool6Ptr> Pool6Collection;
-
 /// @brief a pointer to either IPv4 or IPv6 Pool
 typedef boost::shared_ptr<Pool> PoolPtr;
 

+ 75 - 35
src/lib/dhcpsrv/subnet.cc

@@ -35,12 +35,12 @@ Subnet::Subnet(const isc::asiolink::IOAddress& prefix, uint8_t len,
      last_allocated_pd_(lastAddrInPrefix(prefix, len)) {
     if ((prefix.isV6() && len > 128) ||
         (prefix.isV4() && len > 32)) {
-        isc_throw(BadValue, 
+        isc_throw(BadValue,
                   "Invalid prefix length specified for subnet: " << len);
     }
 }
 
-bool 
+bool
 Subnet::inRange(const isc::asiolink::IOAddress& addr) const {
     IOAddress first = firstAddrInPrefix(prefix_, prefix_len_);
     IOAddress last = lastAddrInPrefix(prefix_, prefix_len_);
@@ -120,7 +120,7 @@ void Subnet::setLastAllocated(const isc::asiolink::IOAddress& addr,
     }
 }
 
-std::string 
+std::string
 Subnet::toText() const {
     std::stringstream tmp;
     tmp << prefix_.toText() << "/" << static_cast<unsigned int>(prefix_len_);
@@ -138,39 +138,50 @@ Subnet4::Subnet4(const isc::asiolink::IOAddress& prefix, uint8_t length,
     }
 }
 
-const PoolCollection& Subnet::getPools() const {
-    return pools_;
-}
-
-void 
-Subnet::addPool(const PoolPtr& pool) {
-    IOAddress first_addr = pool->getFirstAddress();
-    IOAddress last_addr = pool->getLastAddress();
-
-    if (!inRange(first_addr) || !inRange(last_addr)) {
-        isc_throw(BadValue, "Pool (" << first_addr.toText() << "-" 
-                  << last_addr.toText()
-                  << " does not belong in this (" << prefix_.toText() << "/"
-                  << static_cast<int>(prefix_len_) << ") subnet4");
+const PoolCollection& Subnet::getPools(Pool::PoolType type) const {
+    switch (type) {
+    case Pool::TYPE_V4:
+    case Pool::TYPE_IA:
+        return (pools_);
+    case Pool::TYPE_TA:
+        return (pools_ta_);
+    case Pool::TYPE_PD:
+        return (pools_pd_);
+    default:
+        isc_throw(BadValue, "Unsupported pool type: " << type);
     }
+}
 
-    /// @todo: Check that pools do not overlap
+PoolPtr Subnet::getPool(Pool::PoolType type, isc::asiolink::IOAddress hint) {
 
-    pools_.push_back(pool);
-}
+    PoolCollection* pools = NULL;
 
-PoolPtr Subnet::getPool(isc::asiolink::IOAddress hint) {
+    switch (type) {
+    case Pool::TYPE_V4:
+    case Pool::TYPE_IA:
+        pools = &pools_;
+        break;
+    case Pool::TYPE_TA:
+        pools = &pools_ta_;
+        break;
+    case Pool::TYPE_PD:
+        pools = &pools_pd_;
+        break;
+    default:
+        isc_throw(BadValue, "Failed to select pools. Unknown pool type: "
+                  << type);
+    }
 
     PoolPtr candidate;
-    for (PoolCollection::iterator pool = pools_.begin(); 
-         pool != pools_.end(); ++pool) {
+    for (PoolCollection::const_iterator pool = pools->begin();
+         pool != pools->end(); ++pool) {
 
-        // If we won't find anything better, then let's just use the first pool
+        // if we won't find anything better, then let's just use the first pool
         if (!candidate) {
             candidate = *pool;
         }
 
-        // If the client provided a pool and there's a pool that hint is valid 
+        // if the client provided a pool and there's a pool that hint is valid
         // in, then let's use that pool
         if ((*pool)->inRange(hint)) {
             return (*pool);
@@ -179,30 +190,59 @@ PoolPtr Subnet::getPool(isc::asiolink::IOAddress hint) {
     return (candidate);
 }
 
-void 
+void
+Subnet::addPool(const PoolPtr& pool) {
+    IOAddress first_addr = pool->getFirstAddress();
+    IOAddress last_addr = pool->getLastAddress();
+
+    if (!inRange(first_addr) || !inRange(last_addr)) {
+        isc_throw(BadValue, "Pool (" << first_addr.toText() << "-"
+                  << last_addr.toText()
+                  << " does not belong in this (" << prefix_.toText() << "/"
+                  << static_cast<int>(prefix_len_) << ") subnet");
+    }
+
+    /// @todo: Check that pools do not overlap
+
+    switch (pool->getType()) {
+    case Pool::TYPE_V4:
+    case Pool::TYPE_IA:
+        pools_.push_back(pool);
+        return;
+    case Pool6::TYPE_TA:
+        pools_ta_.push_back(pool);
+        return;
+    case Pool6::TYPE_PD:
+        pools_pd_.push_back(pool);
+        return;
+    default:
+        isc_throw(BadValue, "Invalid pool type specified: "
+                  << static_cast<int>(pool->getType()));
+    }
+}
+
+void
 Subnet::setIface(const std::string& iface_name) {
     iface_ = iface_name;
 }
 
-std::string 
+std::string
 Subnet::getIface() const {
     return (iface_);
 }
 
-
-
 void
 Subnet4::validateOption(const OptionPtr& option) const {
     if (!option) {
-        isc_throw(isc::BadValue, 
+        isc_throw(isc::BadValue,
                   "option configured for subnet must not be NULL");
     } else if (option->getUniverse() != Option::V4) {
-        isc_throw(isc::BadValue, 
+        isc_throw(isc::BadValue,
                   "expected V4 option to be added to the subnet");
     }
 }
 
-bool 
+bool
 Subnet::inPool(const isc::asiolink::IOAddress& addr) const {
 
     // Let's start with checking if it even belongs to that subnet.
@@ -210,7 +250,7 @@ Subnet::inPool(const isc::asiolink::IOAddress& addr) const {
         return (false);
     }
 
-    for (PoolCollection::const_iterator pool = pools_.begin(); 
+    for (PoolCollection::const_iterator pool = pools_.begin();
          pool != pools_.end(); ++pool) {
         if ((*pool)->inRange(addr)) {
             return (true);
@@ -236,10 +276,10 @@ Subnet6::Subnet6(const isc::asiolink::IOAddress& prefix, uint8_t length,
 void
 Subnet6::validateOption(const OptionPtr& option) const {
     if (!option) {
-        isc_throw(isc::BadValue, 
+        isc_throw(isc::BadValue,
                   "option configured for subnet must not be NULL");
     } else if (option->getUniverse() != Option::V6) {
-        isc_throw(isc::BadValue, 
+        isc_throw(isc::BadValue,
                   "expected V6 option to be added to the subnet");
     }
 }

+ 19 - 9
src/lib/dhcpsrv/subnet.h

@@ -274,14 +274,20 @@ public:
 
     /// @brief Returns a pool that specified address belongs to
     ///
+    /// If there is no pool that the address belongs to (hint is invalid), other
+    /// pool of specified type will be returned.
+    ///
+    /// @param type pool type that the pool is looked for
     /// @param addr address that the returned pool should cover (optional)
-    /// @return Pointer to found Pool4 or Pool6 (or NULL)
-    PoolPtr getPool(isc::asiolink::IOAddress addr);
+    /// @return found pool (or NULL)
+    PoolPtr getPool(Pool::PoolType type, isc::asiolink::IOAddress addr);
 
     /// @brief Returns a pool without any address specified
+    ///
+    /// @param type pool type that the pool is looked for
     /// @return returns one of the pools defined
-    PoolPtr getAnyPool() {
-        return (getPool(default_pool()));
+    PoolPtr getAnyPool(Pool::PoolType type) {
+        return (getPool(type, default_pool()));
     }
 
     /// @brief Returns the default address that will be used for pool selection
@@ -294,8 +300,9 @@ public:
     ///
     /// The reference is only valid as long as the object that returned it.
     ///
+    /// @param type lease type to be set
     /// @return a collection of all pools
-    const PoolCollection& getPools() const;
+    const PoolCollection& getPools(Pool::PoolType type) const;
 
     /// @brief sets name of the network interface for directly attached networks
     ///
@@ -348,9 +355,15 @@ protected:
     /// a Subnet4 or Subnet6.
     SubnetID id_;
 
-    /// @brief collection of pools in that list
+    /// @brief collection of IPv4 or non-temporary IPv6 pools in that subnet
     PoolCollection pools_;
 
+    /// @brief collection of IPv6 temporary address pools in that subnet
+    PoolCollection pools_ta_;
+
+    /// @brief collection of IPv6 prefix pools in that subnet
+    PoolCollection pools_pd_;
+
     /// @brief a prefix of the subnet
     isc::asiolink::IOAddress prefix_;
 
@@ -501,9 +514,6 @@ protected:
     /// @brief specifies optional interface-id
     OptionPtr interface_id_;
 
-    /// @brief collection of pools for non-temporary addresses
-    Pool6Collection pools_;
-
     /// @brief a triplet with preferred lifetime (in seconds)
     Triplet<uint32_t> preferred_;
 };

+ 1 - 1
src/lib/dhcpsrv/tests/alloc_engine_unittest.cc

@@ -1119,7 +1119,7 @@ TEST_F(AllocEngine4Test, renewLease4) {
     // renew it.
     ASSERT_FALSE(lease->expired());
     lease = engine->renewLease4(subnet_, clientid_, hwaddr_, true,
-                                true, "host.example.com.", lease, 
+                                true, "host.example.com.", lease,
                                 callout_handle, false);
     // Check that he got that single lease
     ASSERT_TRUE(lease);

+ 6 - 6
src/lib/dhcpsrv/tests/subnet_unittest.cc

@@ -69,7 +69,7 @@ TEST(Subnet4Test, Pool4InSubnet4) {
     subnet->addPool(pool1);
 
     // If there's only one pool, get that pool
-    PoolPtr mypool = subnet->getAnyPool();
+    PoolPtr mypool = subnet->getAnyPool(Pool::TYPE_V4);
     EXPECT_EQ(mypool, pool1);
 
 
@@ -78,12 +78,12 @@ TEST(Subnet4Test, Pool4InSubnet4) {
 
     // If there are more than one pool and we didn't provide hint, we
     // should get the first pool
-    mypool = subnet->getAnyPool();
+    mypool = subnet->getAnyPool(Pool::TYPE_V4);
 
     EXPECT_EQ(mypool, pool1);
 
     // If we provide a hint, we should get a pool that this hint belongs to
-    mypool = subnet->getPool(IOAddress("192.1.2.195"));
+    mypool = subnet->getPool(Pool::TYPE_V4, IOAddress("192.1.2.195"));
 
     EXPECT_EQ(mypool, pool3);
 
@@ -215,7 +215,7 @@ TEST(Subnet6Test, Pool6InSubnet6) {
     subnet->addPool(pool1);
 
     // If there's only one pool, get that pool
-    PoolPtr mypool = subnet->getAnyPool();
+    PoolPtr mypool = subnet->getAnyPool(Pool::TYPE_IA);
     EXPECT_EQ(mypool, pool1);
 
 
@@ -224,12 +224,12 @@ TEST(Subnet6Test, Pool6InSubnet6) {
 
     // If there are more than one pool and we didn't provide hint, we
     // should get the first pool
-    mypool = subnet->getAnyPool();
+    mypool = subnet->getAnyPool(Pool::TYPE_IA);
 
     EXPECT_EQ(mypool, pool1);
 
     // If we provide a hint, we should get a pool that this hint belongs to
-    mypool = subnet->getPool(IOAddress("2001:db8:1:3::dead:beef"));
+    mypool = subnet->getPool(Pool::TYPE_IA, IOAddress("2001:db8:1:3::dead:beef"));
 
     EXPECT_EQ(mypool, pool3);
 }