Browse Source

[master] Merge branch 'trac2238'

Conflicts:
	ChangeLog
Tomek Mrugalski 12 years ago
parent
commit
09e793e3c7

+ 7 - 0
ChangeLog

@@ -1,3 +1,10 @@
+484.	[func]		tomek
+	A new library (libb10-dhcpsrv) has been created. At present, it
+	only holds the code for the DHCP Configuration Manager. Currently
+	this object only supports basic configuration storage for the DHCPv6
+	server,	but that capability will be expanded.
+	(Trac #2238, git 6f29861b92742da34be9ae76968e82222b5bfd7d)
+
 bind10-devel-20120927 released on September 27, 2012
 
 483.	[func]		marcin

+ 53 - 2
src/lib/asiolink/io_address.h

@@ -131,7 +131,7 @@ public:
         return equals(other);
     }
 
-    // \brief Compare addresses for inequality
+    /// \brief Compare addresses for inequality
     ///
     /// \param other Address to compare against.
     ///
@@ -140,7 +140,58 @@ public:
         return (!equals(other));
     }
 
-    // \brief Compare addresses for inequality
+    /// \brief Checks if one address is smaller than the other
+    ///
+    /// \param other Address to compare against.
+    ///
+    /// \return true if this address is smaller than the other address.
+    ///
+    /// It is useful for comparing which address is bigger.
+    /// Operations within one protocol family are obvious.
+    /// Comparisons between v4 and v6 will allways return v4
+    /// being smaller. This follows boost::asio::ip implementation
+    bool lessThan(const IOAddress& other) const {
+        if (this->getFamily() == other.getFamily()) {
+            if (this->getFamily() == AF_INET6) {
+                return (this->asio_address_.to_v6() < other.asio_address_.to_v6());
+            } else {
+                return (this->asio_address_.to_v4() < other.asio_address_.to_v4());
+            }
+        }
+        return (this->getFamily() < other.getFamily());
+    }
+
+    /// \brief Checks if one address is smaller or equal than the other
+    ///
+    /// \param other Address to compare against.
+    ///
+    /// \return true if this address is smaller than the other address.
+    bool smallerEqual(const IOAddress& other) const {
+        if (equals(other)) {
+            return (true);
+        }
+        return (lessThan(other));
+    }
+
+    /// \brief Checks if one address is smaller than the other
+    ///
+    /// \param other Address to compare against.
+    ///
+    /// See \ref smaller_than method for details.
+    bool operator<(const IOAddress& other) const {
+        return (lessThan(other));
+    }
+
+    /// \brief Checks if one address is smaller or equal than the other
+    ///
+    /// \param other Address to compare against.
+    ///
+    /// See \ref smaller_equal method for details.
+    bool operator<=(const IOAddress& other) const {
+        return (smallerEqual(other));
+    }
+
+    /// \brief Compare addresses for inequality
     ///
     /// \param other Address to compare against.
     ///

+ 32 - 0
src/lib/asiolink/tests/io_address_unittest.cc

@@ -99,3 +99,35 @@ TEST(IOAddressTest, uint32) {
 
     EXPECT_EQ(addr3.toText(), "192.0.2.5");
 }
+
+TEST(IOAddressTest, lessThanEqual) {
+    IOAddress addr1("192.0.2.5");
+    IOAddress addr2("192.0.2.6");
+    IOAddress addr3("0.0.0.0");
+
+    IOAddress addr4("::");
+    IOAddress addr5("2001:db8::1");
+    IOAddress addr6("2001:db8::1:0");
+    IOAddress addr7("2001:db8::1:0"); // the same as 6
+
+    // v4 comparisons
+    EXPECT_TRUE(addr1 < addr2);
+    EXPECT_FALSE(addr2 < addr1);
+    EXPECT_FALSE(addr2 <= addr1);
+    EXPECT_TRUE(addr3 < addr1);
+    EXPECT_TRUE(addr3 < addr2);
+    EXPECT_TRUE(addr3 <= addr2);
+
+    // v6 comparisons
+    EXPECT_TRUE(addr4 < addr5);
+    EXPECT_TRUE(addr5 < addr6);
+    EXPECT_FALSE(addr6 < addr5);
+    EXPECT_FALSE(addr6 <= addr5);
+
+    // v4 to v6 - v4 should always be smaller
+    EXPECT_TRUE(addr1 < addr4);
+    EXPECT_TRUE(addr3 < addr4);
+    EXPECT_TRUE(addr2 < addr5);
+
+    EXPECT_TRUE(addr6 <= addr7);
+}

+ 12 - 2
src/lib/dhcp/Makefile.am

@@ -13,7 +13,7 @@ AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
 
 CLEANFILES = *.gcno *.gcda
 
-lib_LTLIBRARIES = libb10-dhcp++.la
+lib_LTLIBRARIES = libb10-dhcp++.la libb10-dhcpsrv.la
 libb10_dhcp___la_SOURCES  =
 libb10_dhcp___la_SOURCES += libdhcp++.cc libdhcp++.h
 libb10_dhcp___la_SOURCES += iface_mgr.cc iface_mgr.h
@@ -29,8 +29,18 @@ libb10_dhcp___la_SOURCES += dhcp6.h dhcp4.h
 libb10_dhcp___la_SOURCES += pkt6.cc pkt6.h
 libb10_dhcp___la_SOURCES += pkt4.cc pkt4.h
 
+libb10_dhcpsrv_la_SOURCES  = cfgmgr.cc cfgmgr.h
+libb10_dhcpsrv_la_SOURCES += pool.cc pool.h
+libb10_dhcpsrv_la_SOURCES += subnet.cc subnet.h
+libb10_dhcpsrv_la_SOURCES += triplet.h
+libb10_dhcpsrv_la_SOURCES += addr_utilities.cc addr_utilities.h
+libb10_dhcpsrv_la_CXXFLAGS = $(AM_CXXFLAGS)
+libb10_dhcpsrv_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
+libb10_dhcpsrv_la_LIBADD   = $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
+libb10_dhcpsrv_la_LIBADD  += $(top_builddir)/src/lib/util/libb10-util.la
+libb10_dhcpsrv_la_LDFLAGS  = -no-undefined -version-info 2:0:0
+
 EXTRA_DIST  = README
-#EXTRA_DIST += log_messages.mes
 
 libb10_dhcp___la_CXXFLAGS = $(AM_CXXFLAGS)
 libb10_dhcp___la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)

+ 6 - 8
src/lib/dhcp/README

@@ -1,11 +1,9 @@
-This directory holds implementation for libdhcp++.
+This directory holds implementation for DHCP libraries:
 
+libdhcp++ - this is a generic purpose DHCP library. Please be careful
+what is put here. It is going to be shared by various servers (the "regular"
+one and the embedded as well), clients, relays and performance tools.
 
-Basic Ideas
-===========
+libdhcpsrv - Server specific code goes in here. It will be used by
+dhcp4 and dhcp6 server.
 
-
-Notes
-=====
-This work just begun. Don't expect to see much useful code here.
-We are working on it.

+ 95 - 0
src/lib/dhcp/addr_utilities.cc

@@ -0,0 +1,95 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dhcp/addr_utilities.h>
+
+using namespace isc::asiolink;
+
+namespace isc {
+namespace dhcp {
+
+isc::asiolink::IOAddress firstAddrInPrefix(const isc::asiolink::IOAddress& prefix,
+                                            uint8_t len) {
+
+    static char bitMask[]= { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
+    uint8_t packed[V6ADDRESS_LEN];
+
+    // First we copy the whole address as 16 bytes.
+    memcpy(packed, prefix.getAddress().to_v6().to_bytes().data(), 16);
+
+    // If the length is divisible by 8, it is simple. We just zero out the host
+    // part. Otherwise we need to handle the byte that has to be partially
+    // zeroed.
+    if (len % 8 != 0) {
+
+        // Get the appropriate mask. It has relevant bits (those that should
+        // stay) set and irrelevant (those that should be wiped) cleared.
+        uint8_t mask = bitMask[len % 8];
+
+        // Let's leave only whatever the mask says should not be cleared.
+        packed[len / 8] = packed[len / 8] & mask;
+
+        // Since we have just dealt with this byte, let's move the prefix length
+        // to the beginning of the next byte (len is expressed in bits).
+        len = (len / 8 + 1) * 8;
+    }
+
+    // Clear out the remaining bits.
+    for (int i = len / 8; i < sizeof(packed); ++i) {
+        packed[i] = 0x0;
+    }
+
+    // Finally, let's wrap this into nice and easy IOAddress object.
+    return (isc::asiolink::IOAddress::from_bytes(AF_INET6, packed));
+}
+
+isc::asiolink::IOAddress lastAddrInPrefix(const isc::asiolink::IOAddress& prefix,
+                                           uint8_t len) {
+
+    static char bitMask[]= { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
+    uint8_t packed[V6ADDRESS_LEN];
+
+    // First we copy the whole address as 16 bytes.
+    memcpy(packed, prefix.getAddress().to_v6().to_bytes().data(), 16);
+
+    // if the length is divisible by 8, it is simple. We just fill the host part
+    // with ones. Otherwise we need to handle the byte that has to be partially
+    // zeroed.
+    if (len % 8 != 0) {
+        // Get the appropriate mask. It has relevant bits (those that should
+        // stay) set and irrelevant (those that should be set to 1) cleared.
+        uint8_t mask = bitMask[len % 8];
+
+        // Let's set those irrelevant bits with 1. It would be perhaps
+        // easier to not use negation here and invert bitMask content. However,
+        // with this approach, we can use the same mask in first and last
+        // address calculations.
+        packed[len / 8] = packed[len / 8] | ~mask;
+
+        // Since we have just dealt with this byte, let's move the prefix length
+        // to the beginning of the next byte (len is expressed in bits).
+        len = (len / 8 + 1) * 8;
+    }
+
+    // Finally set remaining bits to 1.
+    for (int i = len / 8; i < sizeof(packed); ++i) {
+        packed[i] = 0xff;
+    }
+
+    // Finally, let's wrap this into nice and easy IOAddress object.
+    return (isc::asiolink::IOAddress::from_bytes(AF_INET6, packed));
+}
+
+};
+};

+ 53 - 0
src/lib/dhcp/addr_utilities.h

@@ -0,0 +1,53 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <asiolink/io_address.h>
+
+namespace isc {
+namespace dhcp {
+
+/// This code is based on similar code from the Dibbler project. I, Tomasz Mrugalski,
+/// as a sole creator of that code hereby release it under BSD license for the benefit
+/// of the BIND10 project.
+
+/// @brief returns a first address in a given prefix
+///
+/// Example: For 2001:db8:1::deaf:beef and length /120 the function will return
+/// 2001:db8:1::dead:be00. See also @ref lastAddrInPrefix.
+///
+/// @todo It currently works for v6 only and will throw if v4 address is passed.
+///
+/// @param prefix and address that belongs to a prefix
+/// @param len prefix length
+///
+/// @return first address from a prefix
+isc::asiolink::IOAddress firstAddrInPrefix(const isc::asiolink::IOAddress& prefix,
+                                            uint8_t len);
+
+/// @brief returns a last address in a given prefix
+///
+/// Example: For 2001:db8:1::deaf:beef and length /112 the function will return
+/// 2001:db8:1::dead:ffff. See also @ref firstAddrInPrefix.
+///
+/// @todo It currently works for v6 only and will throw if v4 address is passed.
+///
+/// @param prefix and address that belongs to a prefix
+/// @param len prefix length
+///
+/// @return first address from a prefix
+isc::asiolink::IOAddress lastAddrInPrefix(const isc::asiolink::IOAddress& prefix,
+                                           uint8_t len);
+
+};
+};

+ 78 - 0
src/lib/dhcp/cfgmgr.cc

@@ -0,0 +1,78 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <asiolink/io_address.h>
+#include <dhcp/cfgmgr.h>
+
+using namespace isc::asiolink;
+using namespace isc::util;
+
+namespace isc {
+namespace dhcp {
+
+
+
+
+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
+    // The idea is to keep small deployments easy. In a small network - one
+    // router that also runs DHCPv6 server. Users specifies a single pool and
+    // expects it to just work. Without this, the server would complain that it
+    // doesn't have IP address on its interfaces that matches that
+    // configuration. Such requirement makes sense in IPv4, but not in IPv6.
+    // The server does not need to have a global address (using just link-local
+    // is ok for DHCPv6 server) from the pool it serves.
+    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

+ 126 - 0
src/lib/dhcp/cfgmgr.h

@@ -0,0 +1,126 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef CFGMGR_H
+#define CFGMGR_H
+
+#include <string>
+#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>
+#include <dhcp/pool.h>
+#include <dhcp/subnet.h>
+
+namespace isc {
+namespace dhcp {
+
+
+/// @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
+} // namespace isc
+
+#endif

+ 87 - 0
src/lib/dhcp/pool.cc

@@ -0,0 +1,87 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <asiolink/io_address.h>
+#include <dhcp/addr_utilities.h>
+#include <dhcp/pool.h>
+
+using namespace isc::asiolink;
+
+namespace isc {
+namespace dhcp {
+
+Pool::Pool(const isc::asiolink::IOAddress& first,
+           const isc::asiolink::IOAddress& last)
+    :id_(getNextID()), first_(first), last_(last) {
+}
+
+bool Pool::inRange(const isc::asiolink::IOAddress& addr) const {
+    return (first_.smallerEqual(addr) && addr.smallerEqual(last_));
+}
+
+Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& first,
+             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 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 1 followed by some hex digit), so a at least a warning
+        // would be useful.
+
+        // first_  = last;
+        // last_ = first;
+    }
+
+
+    // 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)
+    if ((type != TYPE_IA) && (type != TYPE_TA)) {
+        isc_throw(BadValue, "Invalid Pool6 type specified");
+    }
+}
+
+Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& prefix,
+             uint8_t prefix_len)
+    :Pool(prefix, IOAddress("::")),
+     type_(type), prefix_len_(prefix_len) {
+
+    // check if the prefix is sane
+    if (prefix.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");
+    }
+
+    /// @todo: We should probably implement checks against weird addresses
+    /// here, like ::, starting with fe80, starting with ff etc. .
+
+    // Let's now calculate the last address in defined pool
+    last_ = lastAddrInPrefix(prefix, prefix_len);
+}
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace

+ 155 - 0
src/lib/dhcp/pool.h

@@ -0,0 +1,155 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef POOL_H
+#define POOL_H
+
+#include <vector>
+#include <asiolink/io_address.h>
+#include <boost/shared_ptr.hpp>
+
+namespace isc {
+namespace dhcp {
+
+/// @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
+    ///
+    /// @return pool-id value
+    /// Pool-id is an unique value that can be used to identify a pool.
+    uint32_t getId() const {
+        return (id_);
+    }
+
+    /// @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_);
+    }
+
+    /// @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) const;
+
+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);
+
+    /// @brief returns the next unique Pool-ID
+    ///
+    /// @return the next unique Pool-ID
+    static uint32_t getNextID() {
+        static uint32_t id = 0;
+        return (id++);
+    }
+
+    /// @brief pool-id
+    ///
+    /// This ID is used to identify 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_;
+
+    /// @brief Comments field
+    ///
+    /// @todo: This field is currently not used.
+    std::string comments_;
+};
+
+/// @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);
+
+    /// @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_);
+    }
+
+private:
+    /// @brief defines a pool type
+    Pool6Type type_;
+
+    /// @brief prefix length
+    /// used by TYPE_PD only (zeroed for other types)
+    uint8_t prefix_len_;
+};
+
+/// @brief a pointer an IPv6 Pool
+typedef boost::shared_ptr<Pool6> Pool6Ptr;
+
+/// @brief a container for IPv6 Pools
+typedef std::vector<Pool6Ptr> Pool6Collection;
+
+} // end of isc::dhcp namespace
+} // end of isc namespace
+
+
+#endif // POOL_H

+ 91 - 0
src/lib/dhcp/subnet.cc

@@ -0,0 +1,91 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dhcp/addr_utilities.h>
+#include <asiolink/io_address.h>
+#include <dhcp/subnet.h>
+#include <dhcp/pool.h>
+
+using namespace isc::asiolink;
+
+namespace isc {
+namespace dhcp {
+
+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);
+    }
+}
+
+bool Subnet::inRange(const isc::asiolink::IOAddress& addr) const {
+    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,
+                 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, "Non IPv6 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");
+    }
+
+    /// @todo: Check that pools do not overlap
+
+    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);
+}
+
+} // end of isc::dhcp namespace
+} // end of isc namespace

+ 161 - 0
src/lib/dhcp/subnet.h

@@ -0,0 +1,161 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef SUBNET_H
+#define SUBNET_H
+
+#include <boost/shared_ptr.hpp>
+#include <asiolink/io_address.h>
+#include <dhcp/pool.h>
+#include <dhcp/triplet.h>
+
+namespace isc {
+namespace dhcp {
+
+/// @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) const;
+
+    /// @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,
+           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_;
+
+    /// @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:
+
+    /// @brief Constructor with all parameters
+    ///
+    /// @param prefix Subnet6 prefix
+    /// @param length prefix length
+    /// @param t1 renewal timer (in seconds)
+    /// @param t2 rebind timer (in seconds)
+    /// @param preferred_lifetime preferred lifetime of leases (in seconds)
+    /// @param valid_lifetime preferred lifetime of leases (in seconds)
+    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);
+
+    /// @brief Returns preverred lifetime (in seconds)
+    ///
+    /// @return a triplet with preferred lifetime
+    Triplet<uint32_t> getPreferred() const {
+        return (preferred_);
+    }
+
+    /// @brief Returns a pool that specified address belongs to
+    ///
+    /// @param hint address that the returned pool should cover (optional)
+    /// @return Pointer to found pool6 (or NULL)
+    Pool6Ptr getPool6(const isc::asiolink::IOAddress& hint =
+                      isc::asiolink::IOAddress("::"));
+
+    /// @brief Adds a new pool.
+    /// @param pool pool to be added
+    void addPool6(const Pool6Ptr& pool);
+
+    /// @brief returns all pools
+    ///
+    /// The reference is only valid as long as the object that
+    /// returned it.
+    ///
+    /// @return a collection of all pools
+    const Pool6Collection& getPools() const {
+        return pools_;
+    }
+
+protected:
+    /// @brief collection of pools in that list
+    Pool6Collection pools_;
+
+    /// @brief a triplet with preferred lifetime (in seconds)
+    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;
+
+} // end of isc::dhcp namespace
+} // end of isc namespace
+
+#endif // SUBNET_T

+ 19 - 2
src/lib/dhcp/tests/Makefile.am

@@ -24,7 +24,7 @@ TESTS_ENVIRONMENT = \
 
 TESTS =
 if HAVE_GTEST
-TESTS += libdhcp++_unittests
+TESTS += libdhcp++_unittests libdhcpsrv_unittests
 libdhcp___unittests_SOURCES  = run_unittests.cc
 libdhcp___unittests_SOURCES += libdhcp++_unittest.cc
 libdhcp___unittests_SOURCES += iface_mgr_unittest.cc
@@ -38,20 +38,37 @@ libdhcp___unittests_SOURCES += pkt4_unittest.cc
 
 libdhcp___unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
 libdhcp___unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
-
 libdhcp___unittests_CXXFLAGS = $(AM_CXXFLAGS)
 
+libdhcpsrv_unittests_SOURCES  = run_unittests.cc
+libdhcpsrv_unittests_SOURCES += cfgmgr_unittest.cc triplet_unittest.cc
+libdhcpsrv_unittests_SOURCES += pool_unittest.cc subnet_unittest.cc
+libdhcpsrv_unittests_SOURCES += addr_utilities_unittest.cc
+
+libdhcpsrv_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
+libdhcpsrv_unittests_LDFLAGS  = $(AM_LDFLAGS)  $(GTEST_LDFLAGS)
+libdhcpsrv_unittests_CXXFLAGS = $(AM_CXXFLAGS)
+libdhcpsrv_unittests_LDADD  = $(GTEST_LDADD)
+libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
+libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
+libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcpsrv.la
+libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
+
+
 if USE_CLANGPP
 # This is to workaround unused variables tcout and tcerr in
 # log4cplus's streams.h and unused parameters from some of the
 # Boost headers.
 libdhcp___unittests_CXXFLAGS += -Wno-unused-variable -Wno-unused-parameter
+libdhcpsrv_unittests_CXXFLAGS += -Wno-unused-variable -Wno-unused-parameter
 endif
+
 libdhcp___unittests_LDADD  = $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
 libdhcp___unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
 libdhcp___unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
 libdhcp___unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
 libdhcp___unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
+libdhcp___unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
 libdhcp___unittests_LDADD += $(GTEST_LDADD)
 endif
 

+ 93 - 0
src/lib/dhcp/tests/addr_utilities_unittest.cc

@@ -0,0 +1,93 @@
+
+// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <gtest/gtest.h>
+#include <vector>
+
+#include <dhcp/addr_utilities.h>
+
+using namespace std;
+using namespace isc::dhcp;
+using namespace isc::asiolink;
+
+TEST(Pool6Test, lastAddrInPrefix) {
+    IOAddress addr1("2001:db8:1:1234:5678:abcd:1234:beef");
+
+    // Prefixes rounded to nibbles are easy...
+    EXPECT_EQ("2001:db8:1:1234:5678:abcd:1234:ffff",
+              lastAddrInPrefix(addr1, 112).toText());
+    EXPECT_EQ("2001:db8:1:1234:5678:abcd:123f:ffff",
+              lastAddrInPrefix(addr1, 108).toText());
+    EXPECT_EQ("2001:db8:1:1234:5678:abcd:12ff:ffff",
+              lastAddrInPrefix(addr1, 104).toText());
+    EXPECT_EQ("2001:db8:1:1234:ffff:ffff:ffff:ffff",
+              lastAddrInPrefix(addr1, 64).toText());
+
+    IOAddress addr2("2001::");
+
+    // These are tricker, though, as they are done in 1 bit increments
+
+    // the last address in 2001::/127 pool should be 2001::1
+    EXPECT_EQ("2001::1", lastAddrInPrefix(addr2, 127).toText());
+
+    EXPECT_EQ("2001::3", lastAddrInPrefix(addr2, 126).toText());
+    EXPECT_EQ("2001::7", lastAddrInPrefix(addr2, 125).toText());
+    EXPECT_EQ("2001::f", lastAddrInPrefix(addr2, 124).toText());
+    EXPECT_EQ("2001::1f", lastAddrInPrefix(addr2, 123).toText());
+    EXPECT_EQ("2001::3f", lastAddrInPrefix(addr2, 122).toText());
+    EXPECT_EQ("2001::7f", lastAddrInPrefix(addr2, 121).toText());
+    EXPECT_EQ("2001::ff", lastAddrInPrefix(addr2, 120).toText());
+
+    // Let's check extreme cases
+    IOAddress anyAddr("::");
+    EXPECT_EQ("7fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
+              lastAddrInPrefix(anyAddr, 1).toText());
+    EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
+              lastAddrInPrefix(anyAddr, 0).toText());
+    EXPECT_EQ("::", lastAddrInPrefix(anyAddr, 128).toText());
+}
+
+TEST(Pool6Test, firstAddrInPrefix) {
+    IOAddress addr1("2001:db8:1:1234:5678:abcd:1234:beef");
+
+    // Prefixes rounded to nibbles are easy...
+    EXPECT_EQ("2001:db8:1:1234:5678:abcd:1234:0",
+              firstAddrInPrefix(addr1, 112).toText());
+    EXPECT_EQ("2001:db8:1:1234:5678:abcd:1230:0",
+              firstAddrInPrefix(addr1, 108).toText());
+    EXPECT_EQ("2001:db8:1:1234:5678:abcd:1200:0",
+              firstAddrInPrefix(addr1, 104).toText());
+    EXPECT_EQ("2001:db8:1:1234::",
+              firstAddrInPrefix(addr1, 64).toText());
+
+    IOAddress addr2("2001::ffff");
+
+    // These are tricker, though, as they are done in 1 bit increments
+
+    // the first address in 2001::/127 pool should be 2001::1
+    EXPECT_EQ("2001::fffe", firstAddrInPrefix(addr2, 127).toText());
+
+    EXPECT_EQ("2001::fffc", firstAddrInPrefix(addr2, 126).toText());
+    EXPECT_EQ("2001::fff8", firstAddrInPrefix(addr2, 125).toText());
+    EXPECT_EQ("2001::fff0", firstAddrInPrefix(addr2, 124).toText());
+    EXPECT_EQ("2001::ffe0", firstAddrInPrefix(addr2, 123).toText());
+    EXPECT_EQ("2001::ffc0", firstAddrInPrefix(addr2, 122).toText());
+    EXPECT_EQ("2001::ff80", firstAddrInPrefix(addr2, 121).toText());
+    EXPECT_EQ("2001::ff00", firstAddrInPrefix(addr2, 120).toText());
+}

+ 63 - 0
src/lib/dhcp/tests/cfgmgr_unittest.cc

@@ -0,0 +1,63 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <iostream>
+#include <sstream>
+#include <arpa/inet.h>
+#include <gtest/gtest.h>
+#include <dhcp/cfgmgr.h>
+#include <exceptions/exceptions.h>
+
+using namespace std;
+using namespace isc::asiolink;
+using namespace isc::dhcp;
+using namespace isc::util;
+using namespace isc;
+
+// don't import the entire boost namespace.  It will unexpectedly hide uint8_t
+// for some systems.
+using boost::scoped_ptr;
+
+namespace {
+
+// 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

+ 109 - 0
src/lib/dhcp/tests/pool_unittest.cc

@@ -0,0 +1,109 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <iostream>
+#include <vector>
+#include <sstream>
+#include <gtest/gtest.h>
+#include <dhcp/pool.h>
+#include <asiolink/io_address.h>
+
+using boost::scoped_ptr;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::asiolink;
+
+namespace {
+
+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"));
+
+    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());
+
+    // This is Pool6, IPv4 addresses do not belong here
+    EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("2001:db8::1"),
+                       IOAddress("192.168.0.5")), BadValue);
+    EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("192.168.0.2"),
+                       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")), BadValue);
+}
+
+TEST(Pool6Test, constructor_prefix_len) {
+
+    // let's construct 2001:db8:1::/96 pool
+    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());
+
+    // No such thing as /130 prefix
+    EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("2001:db8::"), 130),
+                 BadValue);
+
+    // /0 prefix does not make sense
+    EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("2001:db8::"), 0),
+                 BadValue);
+
+    // This is Pool6, IPv4 addresses do not belong here
+    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"));
+
+   EXPECT_FALSE(pool1.inRange(IOAddress("2001:db8:1::")));
+   EXPECT_TRUE(pool1.inRange(IOAddress("2001:db8:1::1")));
+   EXPECT_TRUE(pool1.inRange(IOAddress("2001:db8:1::7")));
+   EXPECT_TRUE(pool1.inRange(IOAddress("2001:db8:1::f")));
+   EXPECT_FALSE(pool1.inRange(IOAddress("2001:db8:1::10")));
+   EXPECT_FALSE(pool1.inRange(IOAddress("::")));
+}
+
+// This test creates 100 pools and verifies that their IDs are unique.
+TEST(Pool6Test, unique_id) {
+
+    const int num_pools = 100;
+    std::vector<Pool6Ptr> pools;
+
+    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"))));
+    }
+
+    for (int i = 0; i < num_pools; ++i) {
+        for (int j = i + 1; j < num_pools; ++j) {
+            if (pools[i]->getId() == pools[j]->getId()) {
+                FAIL() << "Pool-ids must be unique";
+            }
+        }
+    }
+
+}
+
+}; // end of anonymous namespace
+

+ 0 - 1
src/lib/dhcp/tests/run_unittests.cc

@@ -13,7 +13,6 @@
 // PERFORMANCE OF THIS SOFTWARE.
 
 #include <gtest/gtest.h>
-
 #include <log/logger_support.h>
 
 int

+ 112 - 0
src/lib/dhcp/tests/subnet_unittest.cc

@@ -0,0 +1,112 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+
+#include <config.h>
+#include <dhcp/subnet.h>
+#include <exceptions/exceptions.h>
+#include <boost/scoped_ptr.hpp>
+#include <gtest/gtest.h>
+#include <asiolink/io_address.h>
+
+// don't import the entire boost namespace.  It will unexpectedly hide uint8_t
+// for some systems.
+using boost::scoped_ptr;
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::asiolink;
+
+namespace {
+
+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, 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);
+
+}
+
+
+};

+ 104 - 0
src/lib/dhcp/tests/triplet_unittest.cc

@@ -0,0 +1,104 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <stdint.h>
+#include <gtest/gtest.h>
+#include <dhcp/triplet.h>
+#include <exceptions/exceptions.h>
+
+using namespace isc::dhcp;
+using namespace isc;
+
+namespace {
+
+// constructor validation
+TEST(TripletTest, constructor) {
+
+    const uint32_t min = 10;
+    const uint32_t value = 20;
+    const uint32_t max = 30;
+
+    Triplet<uint32_t> x(min, value, max);
+
+    EXPECT_EQ(min, x.getMin());
+    EXPECT_EQ(value, x.get());
+    EXPECT_EQ(max, x.getMax());
+
+    // requested values below min should return allowed min value
+    EXPECT_EQ(min, x.get(min - 5));
+
+    EXPECT_EQ(min, x.get(min));
+
+    // requesting a value from within the range (min < x < max) should
+    // return the requested value
+    EXPECT_EQ(17, x.get(17));
+
+    EXPECT_EQ(max, x.get(max));
+
+    EXPECT_EQ(max, x.get(max + 5));
+
+    // this will be boring. It is expected to return 42 no matter what
+    Triplet<uint32_t> y(42);
+
+    EXPECT_EQ(42, y.getMin()); // min, default and max are equal to 42
+    EXPECT_EQ(42, y.get());    // it returns ...
+    EXPECT_EQ(42, y.getMax()); // the exact value...
+
+    // requested values below or above are ignore
+    EXPECT_EQ(42, y.get(5));   // all...
+    EXPECT_EQ(42, y.get(42));  // the...
+    EXPECT_EQ(42, y.get(80));  // time!
+}
+
+// Triplets must be easy to use.
+// Simple to/from int conversions must be done on the fly.
+TEST(TripletTest, operator) {
+
+    uint32_t x = 47;
+
+    Triplet<uint32_t> foo(1,2,3);
+    Triplet<uint32_t> bar(4,5,6);
+
+    foo = bar;
+
+    EXPECT_EQ(4, foo.getMin());
+    EXPECT_EQ(5, foo.get());
+    EXPECT_EQ(6, foo.getMax());
+
+    // assignment operator: uint32_t => triplet
+    Triplet<uint32_t> y(0);
+    y = x;
+
+    EXPECT_EQ(x, y.get());
+
+    // let's try the other way around: triplet => uint32_t
+    uint32_t z = 0;
+    z = y;
+
+    EXPECT_EQ(x, z);
+}
+
+// check if specified values are sane
+TEST(TripletTest, sanity_check) {
+
+    // min is larger than default
+    EXPECT_THROW(Triplet<uint32_t>(6,5,5), BadValue);
+
+    // max is smaller than default
+    EXPECT_THROW(Triplet<uint32_t>(5,5,4), BadValue);
+
+}
+
+}; // end of anonymous namespace

+ 110 - 0
src/lib/dhcp/triplet.h

@@ -0,0 +1,110 @@
+// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <exceptions/exceptions.h>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief this template specifies a parameter value
+///
+/// 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:
+
+    /// @brief base type to Triple conversion
+    ///
+    /// Typically: uint32_t to Triplet assignment. It is very convenient
+    /// to be able to simply write Triplet<uint32_t> x = 7;
+    Triplet<T> operator=(T other) {
+        min_ = other;
+        default_ = other;
+        max_ = other;
+        return *this;
+    }
+
+    /// @brief triplet to base type conversion
+    ///
+    /// Typically: Triplet to uint32_t assignment. It is very convenient
+    /// to be able to simply write uint32_t z = x; (where x is a Triplet)
+    operator T() const {
+        return (default_);
+    }
+
+    /// @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) {
+    }
+
+    /// @brief sets the default value and thresholds
+    ///
+    /// @throw BadValue if min <= def <= max rule is violated
+    Triplet(T min, T def, T max)
+        :min_(min), default_(def), max_(max) {
+        if ( (min_ > def) || (def > max_) ) {
+            isc_throw(BadValue, "Invalid triplet values.");
+        }
+    }
+
+    /// @brief returns a minimum allowed value
+    T getMin() const { return min_;}
+
+    /// @brief returns the default value
+    T get() const { return default_;}
+
+    /// @brief returns value with a hint
+    ///
+    /// DHCP protocol treats any values sent by a client as hints.
+    /// This is a method that implements that. We can assign any value
+    /// from configured range that client asks.
+    T get(T hint) const {
+        if (hint <= min_) {
+            return (min_);
+        }
+
+        if (hint >= max_) {
+            return (max_);
+        }
+
+        return (hint);
+    }
+
+    /// @brief returns a maximum allowed value
+    T getMax() const { return max_; }
+
+protected:
+
+    /// @brief the minimum value
+    T min_;
+
+    /// @brief the default value
+    T default_;
+
+    /// @brief the maximum value
+    T max_;
+};
+
+
+} // namespace isc::dhcp
+} // namespace isc