BIND 10 trac2238, updated. 68621232d6840227c4309c1fd1ee9e1a8b26120f [2238] Comments in CfgMgr updated.

BIND 10 source code commits bind10-changes at lists.isc.org
Tue Sep 18 16:35:43 UTC 2012


The branch, trac2238 has been updated
       via  68621232d6840227c4309c1fd1ee9e1a8b26120f (commit)
       via  c9bae4745f58fc361ebeb0f91bb15b288e96e5cf (commit)
       via  daf7bbcc05e24e2e6894ac2cf59fa738cfb199bc (commit)
      from  d1c7c9057bb24595f9dcefe226dc7f326f390486 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 68621232d6840227c4309c1fd1ee9e1a8b26120f
Author: Tomek Mrugalski <tomasz at isc.org>
Date:   Tue Sep 18 18:35:18 2012 +0200

    [2238] Comments in CfgMgr updated.

commit c9bae4745f58fc361ebeb0f91bb15b288e96e5cf
Author: Tomek Mrugalski <tomasz at isc.org>
Date:   Mon Sep 17 19:26:03 2012 +0200

    [2238] lifetimes are now property of Subnet6, not Pool6

commit daf7bbcc05e24e2e6894ac2cf59fa738cfb199bc
Author: Tomek Mrugalski <tomasz at isc.org>
Date:   Mon Sep 17 18:28:09 2012 +0200

    [2238] CfgMgr (+tests) implemented.

-----------------------------------------------------------------------

Summary of changes:
 ChangeLog                             |    8 +
 src/lib/dhcp/cfgmgr.cc                |  152 ++++++++++++-----
 src/lib/dhcp/cfgmgr.h                 |  291 +++++++++++++++++++++++++++------
 src/lib/dhcp/tests/cfgmgr_unittest.cc |  145 ++++++++++++----
 4 files changed, 471 insertions(+), 125 deletions(-)

-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index da4a826..46faabf 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+4XX.	[func]		tomek
+	A new library (libb10-dhcpsrv) has been create. Currently its
+	functionality is limited to a Configuration Manager. CfgMgr
+	currently only supports basic configuration storage for DHCPv6
+	server,	but that capability is expected to be expanded in a near
+	future.
+	(Trac #2238, git TBD)
+
 473.	[bug]		jelte
 	TCP connections now time out in b10-auth if no (or not all) query
 	data is sent by the client. The timeout value defaults to 5000
diff --git a/src/lib/dhcp/cfgmgr.cc b/src/lib/dhcp/cfgmgr.cc
index a4a4b5f..22b99de 100644
--- a/src/lib/dhcp/cfgmgr.cc
+++ b/src/lib/dhcp/cfgmgr.cc
@@ -23,12 +23,8 @@ namespace isc {
 namespace dhcp {
 
 Pool::Pool(const isc::asiolink::IOAddress& first,
-           const isc::asiolink::IOAddress& last,
-           const Triplet<uint32_t>& t1,
-           const Triplet<uint32_t>& t2,
-           const Triplet<uint32_t>& valid_lifetime)
-    :id_(getNextID()), first_(first), last_(last), t1_(t1), t2_(t2), valid_(valid_lifetime) {
-
+           const isc::asiolink::IOAddress& last)
+    :id_(getNextID()), first_(first), last_(last) {
 }
 
 bool Pool::inRange(const isc::asiolink::IOAddress& addr) {
@@ -36,27 +32,26 @@ bool Pool::inRange(const isc::asiolink::IOAddress& addr) {
 }
 
 Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& first,
-             const isc::asiolink::IOAddress& last,
-             const Triplet<uint32_t>& t1,
-             const Triplet<uint32_t>& t2,
-             const Triplet<uint32_t>& preferred_lifetime,
-             const Triplet<uint32_t>& valid_lifetime)
-    :Pool(first, last, t1, t2, valid_lifetime),
-     type_(type), prefix_len_(0), preferred_(preferred_lifetime) {
+             const isc::asiolink::IOAddress& last)
+    :Pool(first, last), type_(type), prefix_len_(0) {
+
+    // check if specified address boundaries are sane
+    if (first.getFamily() != AF_INET6 || last.getFamily() != AF_INET6) {
+        isc_throw(BadValue, "Invalid Pool6 address boundaries: not IPv6");
+    }
 
     if (last < first) {
         isc_throw(BadValue, "Upper boundary is smaller than lower boundary.");
-        // This check is strict. If we decide that it is too strict,
+        // This check is a bit strict. If we decide that it is too strict,
         // we need to comment it and uncomment lines below.
+        // On one hand, letting the user specify 2001::f - 2001::1 is nice, but
+        // on the other hand, 2001::1 may be a typo and the user really meant
+        // 2001::1:0 (or 1something), so a at least a warning would be useful.
 
         // first_  = last;
         // last_ = first;
     }
 
-    if (first.getFamily() != AF_INET6 || last.getFamily() != AF_INET6) {
-        isc_throw(BadValue, "Invalid Pool6 address boundaries: not IPv6");
-    }
-
     // TYPE_PD is not supported by this constructor. first-last style
     // parameters are for IA and TA only. There is another dedicated
     // constructor for that (it uses prefix/length)
@@ -65,30 +60,31 @@ Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& first,
     }
 }
 
-Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& addr,
-             uint8_t prefix_len,
-             const Triplet<uint32_t>& t1,
-             const Triplet<uint32_t>& t2,
-             const Triplet<uint32_t>& preferred_lifetime,
-             const Triplet<uint32_t>& valid_lifetime)
-    :Pool(addr, IOAddress("::"), t1, t2, valid_lifetime),
-     type_(type), prefix_len_(prefix_len), preferred_(preferred_lifetime) {
+Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& prefix,
+             uint8_t prefix_len)
+    :Pool(prefix, IOAddress("::")),
+     type_(type), prefix_len_(prefix_len) {
 
-    if (prefix_len == 0 || prefix_len > 128) {
-        isc_throw(BadValue, "Invalid prefix length");
+    // check if the prefix is sane
+    if (prefix.getFamily() != AF_INET6) {
+        isc_throw(BadValue, "Invalid Pool6 address boundaries: not IPv6");
     }
 
-    if (addr.getFamily() != AF_INET6) {
-        isc_throw(BadValue, "Invalid Pool6 address boundaries: not IPv6");
+    // check if the prefix length is sane
+    if (prefix_len == 0 || prefix_len > 128) {
+        isc_throw(BadValue, "Invalid prefix length");
     }
 
     // Let's now calculate the last address in defined pool
-    last_ = lastAddrInPrefix(addr, prefix_len);
+    last_ = lastAddrInPrefix(prefix, prefix_len);
 }
 
-
-Subnet::Subnet(const isc::asiolink::IOAddress& prefix, uint8_t len)
-    :id_(getNextID()), prefix_(prefix), len_(len) {
+Subnet::Subnet(const isc::asiolink::IOAddress& prefix, uint8_t len,
+               const Triplet<uint32_t>& t1,
+               const Triplet<uint32_t>& t2,
+               const Triplet<uint32_t>& valid_lifetime)
+    :id_(getNextID()), prefix_(prefix), prefix_len_(len), t1_(t1),
+     t2_(t2), valid_(valid_lifetime) {
     if ( (prefix.getFamily() == AF_INET6 && len > 128) ||
          (prefix.getFamily() == AF_INET && len > 32) ) {
         isc_throw(BadValue, "Invalid prefix length specified for subnet: " << len);
@@ -96,21 +92,99 @@ Subnet::Subnet(const isc::asiolink::IOAddress& prefix, uint8_t len)
 }
 
 bool Subnet::inRange(const isc::asiolink::IOAddress& addr) {
-    IOAddress first = firstAddrInPrefix(prefix_, len_);
-    IOAddress last = lastAddrInPrefix(prefix_, len_);
+    IOAddress first = firstAddrInPrefix(prefix_, prefix_len_);
+    IOAddress last = lastAddrInPrefix(prefix_, prefix_len_);
 
     return ( (first <= addr) && (addr <= last) );
 }
 
-Subnet6::Subnet6(const isc::asiolink::IOAddress& prefix, uint8_t length)
-    :Subnet(prefix, length) {
+Subnet6::Subnet6(const isc::asiolink::IOAddress& prefix, uint8_t length,
+                 const Triplet<uint32_t>& t1,
+                 const Triplet<uint32_t>& t2,
+                 const Triplet<uint32_t>& preferred_lifetime,
+                 const Triplet<uint32_t>& valid_lifetime)
+    :Subnet(prefix, length, t1, t2, valid_lifetime),
+     preferred_(preferred_lifetime){
     if (prefix.getFamily() != AF_INET6) {
-        isc_throw(BadValue, "Invalid prefix " << prefix.toText() << " specified in subnet6");
+        isc_throw(BadValue, "Invalid prefix " << prefix.toText()
+                  << " specified in subnet6");
+    }
+}
+
+void Subnet6::addPool6(const Pool6Ptr& pool) {
+    IOAddress first_addr = pool->getFirstAddress();
+    IOAddress last_addr = pool->getLastAddress();
+
+    if (!inRange(first_addr) || !inRange(last_addr)) {
+        isc_throw(BadValue, "Pool6 (" << first_addr.toText() << "-" << last_addr.toText()
+                  << " does not belong in this (" << prefix_ << "/" << prefix_len_
+                  << ") subnet6");
     }
+
+    pools_.push_back(pool);
 }
 
+Pool6Ptr Subnet6::getPool6(const isc::asiolink::IOAddress& hint /* = IOAddress("::")*/ ) {
+    Pool6Ptr candidate;
+    for (Pool6Collection::iterator pool = pools_.begin(); pool != pools_.end(); ++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 in,
+        // then let's use that pool
+        if ((*pool)->inRange(hint)) {
+            return (*pool);
+        }
+    }
+    return (candidate);
+}
 
 
+CfgMgr&
+CfgMgr::instance() {
+    static CfgMgr cfg_mgr;
+    return (cfg_mgr);
+}
+
+Subnet6Ptr
+CfgMgr::getSubnet6(const isc::asiolink::IOAddress& hint) {
+
+    // If there's only one subnet configured, let's just use it
+    if (subnets6_.size() == 1) {
+        return (subnets6_[0]);
+    }
+
+    // If there is more than one, we need to choose the proper one
+    for (Subnet6Collection::iterator subnet = subnets6_.begin();
+         subnet != subnets6_.end(); ++subnet) {
+        if ((*subnet)->inRange(hint)) {
+            return (*subnet);
+        }
+    }
+
+    // sorry, we don't support that subnet
+    return (Subnet6Ptr());
+}
+
+Subnet6Ptr CfgMgr::getSubnet6(OptionPtr /*interfaceId*/) {
+    /// @todo: Implement get subnet6 by interface-id (for relayed traffic)
+    isc_throw(NotImplemented, "Relayed DHCPv6 traffic is not supported yet.");
+}
+
+void CfgMgr::addSubnet6(const Subnet6Ptr& subnet) {
+    /// @todo: Check that this new subnet does not cross boundaries of any
+    /// other already defined subnet.
+    subnets6_.push_back(subnet);
+}
+
+CfgMgr::CfgMgr() {
+}
+
+CfgMgr::~CfgMgr() {
+}
 
 }; // end of isc::dhcp namespace
 }; // end of isc namespace
diff --git a/src/lib/dhcp/cfgmgr.h b/src/lib/dhcp/cfgmgr.h
index 989801f..f3ffe98 100644
--- a/src/lib/dhcp/cfgmgr.h
+++ b/src/lib/dhcp/cfgmgr.h
@@ -19,21 +19,27 @@
 #include <map>
 #include <vector>
 #include <boost/shared_ptr.hpp>
+#include <boost/noncopyable.hpp>
 #include <asiolink/io_address.h>
 #include <util/buffer.h>
+#include <dhcp/option.h>
 
 namespace isc {
 namespace dhcp {
 
 class Pool6;
+
 class Subnet6;
 
-/// @brief this class specifes parameter value
+/// @brief this template specifes a parameter value
 ///
-/// This class is used to store configuration parameters, like lifetime or T1.
-/// It defines 3 parameters: min/default/max values. There are 2 constructors:
+/// This template class is used to store configuration parameters, like lifetime or T1.
+/// It defines 3 parameters: min, default, and max value. There are 2 constructors:
 /// - simple (just one value that sets all parameters)
 /// - extended (that sets default value and two thresholds)
+/// It will be used with integer types. It provides necessary operators, so
+/// it can be assigned to a plain integer or integer assigned to a Triplet.
+/// See TripletTest.operator test for details on an easy Triplet usage.
 template <class T>
 class Triplet {
 public:
@@ -55,6 +61,9 @@ public:
     }
 
     /// @brief sets a fixed value
+    ///
+    /// This constructor assigns a fixed (i.e. no range, just a single value)
+    /// value.
     Triplet(T value)
         :min_(value), default_(value), max_(value) {
     }
@@ -107,45 +116,53 @@ protected:
     T max_;
 };
 
+
+/// @brief base class for Pool4 and Pool6
+///
+/// Stores information about pool of IPv4 or IPv6 addresses.
+/// That is a basic component of a configuration.
 class Pool {
 
 public:
+
+    /// @brief returns Pool-id
+    ///
+    /// Pool-id is an unique value that can be used to identify a pool.
     uint32_t getId() const {
         return (id_);
     }
 
-    Triplet<uint32_t> getValid() const {
-        return (valid_);
-    }
-
+    /// @brief Returns the first address in a pool.
+    ///
+    /// @return first address in a pool
     const isc::asiolink::IOAddress& getFirstAddress() const {
         return (first_);
     }
 
+    /// @brief Returns the last address in a pool.
+    /// @return last address in a pool
     const isc::asiolink::IOAddress& getLastAddress() const {
         return (last_);
     }
 
-    Triplet<uint32_t> getT1() const {
-        return (t1_);
-    }
-
-    Triplet<uint32_t> getT2() const {
-        return (t2_);
-    }
-
-    /// @brief checks if specified address is in range
+    /// @brief Checks if a given address is in the range.
+    ///
+    /// @return true, if the address is in pool
     bool inRange(const isc::asiolink::IOAddress& addr);
 
 protected:
 
     /// @brief protected constructor
+    ///
+    /// 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,
-         const isc::asiolink::IOAddress& last,
-         const Triplet<uint32_t>& t1,
-         const Triplet<uint32_t>& t2,
-         const Triplet<uint32_t>& valid_lifetime);
+         const isc::asiolink::IOAddress& last);
 
+    /// @brief returns the next unique Pool-ID
+    ///
+    /// @return the next unique Pool-ID
     static uint32_t getNextID() {
         static uint32_t id = 0;
         return (id++);
@@ -156,102 +173,270 @@ protected:
     /// This ID is used to indentify this specific pool.
     uint32_t id_;
 
+    /// @brief The first address in a pool
     isc::asiolink::IOAddress first_;
 
+    /// @brief The last address in a pool
     isc::asiolink::IOAddress last_;
 
-    Triplet<uint32_t> t1_;
-
-    Triplet<uint32_t> t2_;
-
-    Triplet<uint32_t> valid_;
-
+    /// @brief Comments field
+    ///
+    /// @todo: This field is currently not used.
     std::string comments_;
-
-    ///uint128_t available_leases_;
-
-    ///uint128_t total_leases_;
 };
 
+/// @brief Pool information for IPv6 addresses and prefixes
+///
+/// It holds information about pool6, i.e. a range of IPv6 address space that
+/// is configured for DHCP allocation.
 class Pool6 : public Pool {
 public:
+
+    /// @brief specifies Pool type
+    ///
+    /// Currently there are 3 pool types defined in DHCPv6:
+    /// - Non-temporary addresses (conveyed in IA_NA)
+    /// - Temporary addresses (conveyed in IA_TA)
+    /// - Delegated Prefixes (conveyed in IA_PD)
+    /// There is a new one being worked on (IA_PA, see draft-ietf-dhc-host-gen-id), but
+    /// support for it is not planned for now.
     typedef enum {
         TYPE_IA,
         TYPE_TA,
         TYPE_PD
     }  Pool6Type;
 
+    /// @brief the constructor for Pool6 "min-max" style definition
+    ///
+    /// @param first the first address in a pool
+    /// @param last the last address in a pool
     Pool6(Pool6Type type, const isc::asiolink::IOAddress& first,
-          const isc::asiolink::IOAddress& last,
-          const Triplet<uint32_t>& t1,
-          const Triplet<uint32_t>& t2,
-          const Triplet<uint32_t>& preferred_lifetime,
-          const Triplet<uint32_t>& valid_lifetime);
-
-    Pool6(Pool6Type type, const isc::asiolink::IOAddress& addr,
-          uint8_t prefix_len,
-          const Triplet<uint32_t>& t1,
-          const Triplet<uint32_t>& t2,
-          const Triplet<uint32_t>& preferred_lifetime,
-          const Triplet<uint32_t>& valid_lifetime);
+          const isc::asiolink::IOAddress& last);
+
+    /// @brief the constructor for Pool6 "prefix/len" style definition
+    ///
+    /// @param prefix specifies prefix of the pool
+    /// @param prefix_len specifies length of the prefix of the pool
+    Pool6(Pool6Type type, const isc::asiolink::IOAddress& prefix,
+          uint8_t prefix_len);
 
+    /// @brief returns pool type
+    ///
+    /// @return pool type
     Pool6Type getType() const {
         return (type_);
     }
 
-    Triplet<uint32_t> getPreferred() const {
-        return (preferred_);
-    }
-
 protected:
-
+    /// @brief defines a pool type
     Pool6Type type_;
 
     /// @brief prefix length
     /// used by TYPE_PD only (zeroed for other types)
     uint8_t prefix_len_;
-
-    Triplet<uint32_t> preferred_;
 };
 
-typedef boost::shared_ptr<Pool> PoolPtr;
+/// @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 base class for Subnet4 and Subnet6
+///
+/// This class presents a common base for IPv4 and IPv6 subnets.
+/// In a physical sense, a subnet defines a single network link with all devices
+/// attached to it. In most cases all devices attached to a single link can
+/// share the same parameters. Therefore Subnet holds several values that are
+/// typically shared by all hosts: renew timer (T1), rebind timer (T2) and
+/// leased addresses lifetime (valid-lifetime).
+///
+/// @todo: Implement support for options here
 class Subnet {
 public:
     /// @brief checks if specified address is in range
     bool inRange(const isc::asiolink::IOAddress& addr);
 
+    /// @brief return valid-lifetime for addresses in that prefix
+    Triplet<uint32_t> getValid() const {
+        return (valid_);
+    }
+
+    /// @brief returns T1 (renew timer), expressed in seconds
+    Triplet<uint32_t> getT1() const {
+        return (t1_);
+    }
+
+    /// @brief returns T2 (rebind timer), expressed in seconds
+    Triplet<uint32_t> getT2() const {
+        return (t2_);
+    }
+
 protected:
     /// @brief protected constructor
     //
     /// By making the constructor protected, we make sure that noone will
     /// ever instantiate that class. Pool4 and Pool6 should be used instead.
-    Subnet(const isc::asiolink::IOAddress& prefix, uint8_t len);
+    Subnet(const isc::asiolink::IOAddress& prefix, uint8_t len,
+           const Triplet<uint32_t>& t1,
+           const Triplet<uint32_t>& t2,
+           const Triplet<uint32_t>& valid_lifetime);
 
+    /// @brief returns the next unique Subnet-ID
+    ///
+    /// @return the next unique Subnet-ID
     static uint32_t getNextID() {
         static uint32_t id = 0;
         return (id++);
     }
 
     /// @brief subnet-id
+    ///
+    /// Subnet-id is a unique value that can be used to find or identify
+    /// a Subnet4 or Subnet6.
     uint32_t id_;
 
+    /// @brief a prefix of the subnet
     isc::asiolink::IOAddress prefix_;
 
-    uint8_t len_;
+    /// @brief a prefix length of the subnet
+    uint8_t prefix_len_;
+
+    /// @brief a tripet (min/default/max) holding allowed renew timer values
+    Triplet<uint32_t> t1_;
+
+    /// @brief a tripet (min/default/max) holding allowed rebind timer values
+    Triplet<uint32_t> t2_;
+
+    /// @brief a tripet (min/default/max) holding allowed valid lifetime values
+    Triplet<uint32_t> valid_;
 };
 
+/// @brief A configuration holder for IPv6 subnet.
+///
+/// This class represents an IPv6 subnet.
 class Subnet6 : public Subnet {
 public:
-    Subnet6(const isc::asiolink::IOAddress& prefix, uint8_t length);
+    Subnet6(const isc::asiolink::IOAddress& prefix, uint8_t length,
+            const Triplet<uint32_t>& t1,
+            const Triplet<uint32_t>& t2,
+            const Triplet<uint32_t>& preferred_lifetime,
+            const Triplet<uint32_t>& valid_lifetime);
+
+    Triplet<uint32_t> getPreferred() const {
+        return (preferred_);
+    }
+
+    Pool6Ptr getPool6(const isc::asiolink::IOAddress& hint =
+                      isc::asiolink::IOAddress("::"));
+
+    void addPool6(const Pool6Ptr& pool);
+
+    const Pool6Collection& getPools() const {
+        return pools_;
+    }
 
 protected:
     /// collection of pools in that list
     Pool6Collection pools_;
 
+    Triplet<uint32_t> preferred_;
+};
+
+/// @brief A pointer to a Subnet6 object
+typedef boost::shared_ptr<Subnet6> Subnet6Ptr;
+
+/// @brief A collection of Subnet6 objects
+typedef std::vector<Subnet6Ptr> Subnet6Collection;
+
+/// @brief Configuration Manager
+///
+/// This singleton class holds the whole configuration for DHCPv4 and DHCPv6
+/// servers. It currently holds information about zero or more subnets6.
+/// Each subnet may contain zero or more pools. Pool4 and Pool6 is the most
+/// basic "chunk" of configuration. It contains a range of assigneable
+/// addresses.
+///
+/// Below is a sketch of configuration inheritance (not implemented yet).
+/// Let's investigate the following configuration:
+///
+/// preferred-lifetime 500;
+/// valid-lifetime 1000;
+/// subnet6 2001:db8:1::/48 {
+///     pool6 2001::db8:1::1 - 2001::db8:1::ff;
+/// };
+/// subnet6 2001:db8:2::/48 {
+///     valid-lifetime 2000;
+///     pool6 2001::db8:2::1 - 2001::db8:2::ff;
+/// };
+/// Parameters defined in a global scope are applicable to everything until
+/// they are overwritten in a smaller scope, in this case subnet6.
+/// In the example above, the first subnet6 has preferred lifetime of 500s
+/// and a valid lifetime of 1000s. The second subnet has preferred lifetime
+/// of 500s, but valid lifetime of 2000s.
+///
+/// Parameter inheritance is likely to be implemented in configuration handling
+/// routines, so there is no storage capability in a global scope for
+/// subnet-specific parameters.
+///
+/// @todo: Implement Subnet4 support (ticket #2237)
+/// @todo: Implement option definition support
+/// @todo: Implement parameter inheritance
+class CfgMgr : public boost::noncopyable {
+public:
+
+    /// @brief returns a single instance of Configuration Manager
+    ///
+    /// CfgMgr is a singleton and this method is the only way of
+    /// accessing it.
+    static CfgMgr& instance();
+
+    /// @brief get subnet by address
+    ///
+    /// Finds a matching subnet, based on an address. This can be used
+    /// in two cases: when trying to find an appropriate lease based on
+    /// a) relay link address (that must be the address that is on link)
+    /// b) our global address on the interface the message was received on
+    ///    (for directly connected clients)
+    ///
+    /// @param hint an address that belongs to a searched subnet
+    Subnet6Ptr getSubnet6(const isc::asiolink::IOAddress& hint);
+
+    /// @brief get subnet by interface-id
+    ///
+    /// Another possibility to find a subnet is based on interface-id.
+    ///
+    /// @param interface_id content of interface-id option returned by a relay
+    /// @todo This method is not currently supported.
+    Subnet6Ptr getSubnet6(OptionPtr interface_id);
+
+    /// @brief adds a subnet6
+    void addSubnet6(const Subnet6Ptr& subnet);
+
+    /// @todo: Add subnet6 removal routines. Currently it is not possible
+    /// to remove subnets. The only case where subnet6 removal would be
+    /// needed is a dynamic server reconfiguration - a use case that is not
+    /// planned to be supported any time soon.
+protected:
+
+    /// @brief Protected constructor.
+    ///
+    /// This constructor is protected for 2 reasons. First, it forbids any
+    /// instantiations of this class (CfgMgr is a singleton). Second, it
+    /// allows derived class to instantiate it. That is useful for testing
+    /// purposes.
+    CfgMgr();
+
+    /// @brief virtual desctructor
+    virtual ~CfgMgr();
+
+    /// @brief a container for Subnet6
+    ///
+    /// That is a simple vector of pointers. It does not make much sense to
+    /// optimize access time (e.g. using a map), because typical search
+    /// pattern will use calling inRange() method on each subnet until
+    /// a match is found.
+    Subnet6Collection subnets6_;
 };
 
 } // namespace isc::dhcp
diff --git a/src/lib/dhcp/tests/cfgmgr_unittest.cc b/src/lib/dhcp/tests/cfgmgr_unittest.cc
index 3b08e66..6592803 100644
--- a/src/lib/dhcp/tests/cfgmgr_unittest.cc
+++ b/src/lib/dhcp/tests/cfgmgr_unittest.cc
@@ -103,58 +103,42 @@ TEST(Pool6Test, constructor_first_last) {
 
     // let's construct 2001:db8:1:: - 2001:db8:1::ffff:ffff:ffff:ffff pool
     Pool6 pool1(Pool6::TYPE_IA, IOAddress("2001:db8:1::"),
-                IOAddress("2001:db8:1::ffff:ffff:ffff:ffff"),
-                1000, 2000, 3000, 4000);
+                IOAddress("2001:db8:1::ffff:ffff:ffff:ffff"));
 
     EXPECT_EQ(Pool6::TYPE_IA, pool1.getType());
     EXPECT_EQ(IOAddress("2001:db8:1::"), pool1.getFirstAddress());
     EXPECT_EQ(IOAddress("2001:db8:1::ffff:ffff:ffff:ffff"),
               pool1.getLastAddress());
-    EXPECT_EQ(1000, pool1.getT1());
-    EXPECT_EQ(2000, pool1.getT2());
-    EXPECT_EQ(3000, pool1.getPreferred());
-    EXPECT_EQ(4000, pool1.getValid());
 
     // This is Pool6, IPv4 addresses do not belong here
     EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("2001:db8::1"),
-                       IOAddress("192.168.0.5"),
-                       1000, 2000, 3000, 4000), BadValue);
+                       IOAddress("192.168.0.5")), BadValue);
     EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("192.168.0.2"),
-                       IOAddress("2001:db8::1"),
-                       1000, 2000, 3000, 4000), BadValue);
-
+                       IOAddress("2001:db8::1")), BadValue);
 
     // Should throw. Range should be 2001:db8::1 - 2001:db8::2, not
     // the other way around.
     EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("2001:db8::2"),
-                       IOAddress("2001:db8::1"),
-                       1000, 2000, 3000, 4000), BadValue);
+                       IOAddress("2001:db8::1")), BadValue);
 }
 
 TEST(Pool6Test, constructor_prefix_len) {
 
     // let's construct 2001:db8:1::/96 pool
-    Pool6 pool1(Pool6::TYPE_IA, IOAddress("2001:db8:1::"),
-                96, 1000, 2000, 3000, 4000);
+    Pool6 pool1(Pool6::TYPE_IA, IOAddress("2001:db8:1::"), 96);
 
     EXPECT_EQ(Pool6::TYPE_IA, pool1.getType());
     EXPECT_EQ("2001:db8:1::", pool1.getFirstAddress().toText());
     EXPECT_EQ("2001:db8:1::ffff:ffff", pool1.getLastAddress().toText());
-    EXPECT_EQ(1000, pool1.getT1());
-    EXPECT_EQ(2000, pool1.getT2());
-    EXPECT_EQ(3000, pool1.getPreferred());
-    EXPECT_EQ(4000, pool1.getValid());
 
     // This is Pool6, IPv4 addresses do not belong here
-    EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("192.168.0.2"),
-                       96, 1000, 2000, 3000, 4000),
+    EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("192.168.0.2"), 96),
                  BadValue);
 }
 
 TEST(Pool6Test, in_range) {
    Pool6 pool1(Pool6::TYPE_IA, IOAddress("2001:db8:1::1"),
-               IOAddress("2001:db8:1::f"),
-               1000, 2000, 3000, 4000);
+               IOAddress("2001:db8:1::f"));
 
    EXPECT_FALSE(pool1.inRange(IOAddress("2001:db8:1::")));
    EXPECT_TRUE(pool1.inRange(IOAddress("2001:db8:1::1")));
@@ -172,8 +156,7 @@ TEST(Pool6Test, unique_id) {
 
     for (int i = 0; i < num_pools; ++i) {
         pools.push_back(Pool6Ptr(new Pool6(Pool6::TYPE_IA, IOAddress("2001:db8:1::"),
-                                           IOAddress("2001:db8:1::ffff:ffff:ffff:ffff"),
-                                           1000, 2000, 3000, 4000)));
+                                           IOAddress("2001:db8:1::ffff:ffff:ffff:ffff"))));
     }
 
     for (int i = 0; i < num_pools; ++i) {
@@ -187,15 +170,111 @@ TEST(Pool6Test, unique_id) {
 }
 
 
+TEST(Subnet6Test, constructor) {
+
+    EXPECT_NO_THROW(Subnet6 subnet1(IOAddress("2001:db8:1::"), 64,
+                                    1, 2, 3, 4));
+
+    EXPECT_THROW(Subnet6 subnet2(IOAddress("2001:db8:1::"), 129, 1, 2, 3, 4),
+                BadValue); // invalid prefix length
+    EXPECT_THROW(Subnet6 subnet3(IOAddress("192.168.0.0"), 32, 1, 2, 3, 4),
+                BadValue); // IPv4 addresses are not allowed in Subnet6
+}
+
 TEST(Subnet6Test, in_range) {
-    Subnet6 subnet(IOAddress("2001:db8:1::"), 64);
-
-   EXPECT_FALSE(subnet.inRange(IOAddress("2001:db8:0:ffff:ffff:ffff:ffff:ffff")));
-   EXPECT_TRUE(subnet.inRange(IOAddress("2001:db8:1::0")));
-   EXPECT_TRUE(subnet.inRange(IOAddress("2001:db8:1::1")));
-   EXPECT_TRUE(subnet.inRange(IOAddress("2001:db8:1::ffff:ffff:ffff:ffff")));
-   EXPECT_FALSE(subnet.inRange(IOAddress("2001:db8:1:1::")));
-   EXPECT_FALSE(subnet.inRange(IOAddress("::")));
+    Subnet6 subnet(IOAddress("2001:db8:1::"), 64, 1000, 2000, 3000, 4000);
+
+    EXPECT_EQ(1000, subnet.getT1());
+    EXPECT_EQ(2000, subnet.getT2());
+    EXPECT_EQ(3000, subnet.getPreferred());
+    EXPECT_EQ(4000, subnet.getValid());
+
+
+    EXPECT_FALSE(subnet.inRange(IOAddress("2001:db8:0:ffff:ffff:ffff:ffff:ffff")));
+    EXPECT_TRUE(subnet.inRange(IOAddress("2001:db8:1::0")));
+    EXPECT_TRUE(subnet.inRange(IOAddress("2001:db8:1::1")));
+    EXPECT_TRUE(subnet.inRange(IOAddress("2001:db8:1::ffff:ffff:ffff:ffff")));
+    EXPECT_FALSE(subnet.inRange(IOAddress("2001:db8:1:1::")));
+    EXPECT_FALSE(subnet.inRange(IOAddress("::")));
+}
+
+TEST(Subnet6Test, Pool6InSubnet6) {
+
+    Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));
+
+    Pool6Ptr pool1(new Pool6(Pool6::TYPE_IA, IOAddress("2001:db8:1:1::"), 64));
+    Pool6Ptr pool2(new Pool6(Pool6::TYPE_IA, IOAddress("2001:db8:1:2::"), 64));
+    Pool6Ptr pool3(new Pool6(Pool6::TYPE_IA, IOAddress("2001:db8:1:3::"), 64));
+
+    subnet->addPool6(pool1);
+
+    // If there's only one pool, get that pool
+    Pool6Ptr mypool = subnet->getPool6();
+    EXPECT_EQ(mypool, pool1);
+
+
+    subnet->addPool6(pool2);
+    subnet->addPool6(pool3);
+
+    // If there are more than one pool and we didn't provide hint, we
+    // should get the first pool
+    mypool = subnet->getPool6();
+
+    EXPECT_EQ(mypool, pool1);
+
+    // If we provide a hint, we should get a pool that this hint belongs to
+    mypool = subnet->getPool6(IOAddress("2001:db8:1:3::dead:beef"));
+
+    EXPECT_EQ(mypool, pool3);
+
+}
+
+TEST(Subnet6Test, Subnet6_Pool6_checks) {
+
+    Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4));
+
+    // this one is in subnet
+    Pool6Ptr pool1(new Pool6(Pool6::TYPE_IA, IOAddress("2001:db8:1:1::"), 64));
+    subnet->addPool6(pool1);
+
+    // this one is larger than the subnet!
+    Pool6Ptr pool2(new Pool6(Pool6::TYPE_IA, IOAddress("2001:db8::"), 48));
+
+    EXPECT_THROW(subnet->addPool6(pool2), BadValue);
+
+
+    // this one is totally out of blue
+    Pool6Ptr pool3(new Pool6(Pool6::TYPE_IA, IOAddress("3000::"), 16));
+    EXPECT_THROW(subnet->addPool6(pool3), BadValue);
+
+}
+
+// This test verifies if the configuration manager is able to hold and return
+// valid leases
+TEST(CfgMgrTest, subnet6) {
+    CfgMgr& cfg_mgr = CfgMgr::instance();
+
+    ASSERT_TRUE(&cfg_mgr != 0);
+
+    Subnet6Ptr subnet1(new Subnet6(IOAddress("2000::"), 48, 1, 2, 3, 4));
+    Subnet6Ptr subnet2(new Subnet6(IOAddress("3000::"), 48, 1, 2, 3, 4));
+    Subnet6Ptr subnet3(new Subnet6(IOAddress("4000::"), 48, 1, 2, 3, 4));
+
+    // there shouldn't be any subnet configured at this stage
+    EXPECT_EQ( Subnet6Ptr(), cfg_mgr.getSubnet6(IOAddress("2000::1")));
+
+    cfg_mgr.addSubnet6(subnet1);
+
+    // Now we have only one subnet, any request will be served from it
+    EXPECT_EQ(subnet1, cfg_mgr.getSubnet6(IOAddress("2001:db8::1")));
+
+    cfg_mgr.addSubnet6(subnet2);
+    cfg_mgr.addSubnet6(subnet3);
+
+    EXPECT_EQ(subnet3, cfg_mgr.getSubnet6(IOAddress("4000::123")));
+    EXPECT_EQ(subnet2, cfg_mgr.getSubnet6(IOAddress("3000::dead:beef")));
+    EXPECT_EQ(Subnet6Ptr(), cfg_mgr.getSubnet6(IOAddress("5000::1")));
+
 }
 
 } // end of anonymous namespace



More information about the bind10-changes mailing list