Commit 7ad6e568 authored by Tomek Mrugalski's avatar Tomek Mrugalski 🛰
Browse files

[master] Merge branch 'trac2237' (Subnet/pool storage for DHCPv4)

Conflicts:
	ChangeLog
	src/lib/dhcp/cfgmgr.h
	src/lib/dhcp/tests/cfgmgr_unittest.cc
parents de5fd0b1 a78e5603
492. [func] tomek
libdhcpsrv: The DHCP Configuration Manager is now able to store
information about IPv4 subnets and pools. It is still not possible
to configure that information. Such capability will be implemented
in a near future.
(Trac #2237, git a78e560343b41f0f692c7903c938b2b2b24bf56b)
491. [func] tomek
b10-dhcp6: Configuration for DHCPv6 has been implemented.
Currently it is possible to configure IPv6 subnets and pools
......@@ -7,7 +14,7 @@
(Trac #2269, git 028bed9014b15facf1a29d3d4a822c9d14fc6411)
490. [func] tomek
libdhcp++: An abstract API for lease database has been
libdhcpsrv: An abstract API for lease database has been
implemented. It offers a common interface to all concrete
database backends.
(Trac #2140, git df196f7609757253c4f2f918cd91012bb3af1163)
......
......@@ -13,17 +13,39 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <string.h>
#include <exceptions/exceptions.h>
#include <dhcp/addr_utilities.h>
using namespace isc::asiolink;
namespace isc {
namespace dhcp {
isc::asiolink::IOAddress firstAddrInPrefix(const isc::asiolink::IOAddress& prefix,
namespace {
/// @brief mask used for first/last address calculation in a IPv4 prefix
///
/// Using a static mask is faster than calculating it dynamically every time.
const uint32_t bitMask4[] = { 0xffffffff, 0x7fffffff, 0x3fffffff, 0x1fffffff,
0x0fffffff, 0x07ffffff, 0x03ffffff, 0x01ffffff,
0x00ffffff, 0x007fffff, 0x003fffff, 0x001fffff,
0x000fffff, 0x0007ffff, 0x0003ffff, 0x0001ffff,
0x0000ffff, 0x00007fff, 0x00003fff, 0x00001fff,
0x00000fff, 0x000007ff, 0x000003ff, 0x000001ff,
0x000000ff, 0x0000007f, 0x0000003f, 0x0000001f,
0x0000000f, 0x00000007, 0x00000003, 0x00000001,
0x00000000 };
/// @brief mask used for first/last address calculation in a IPv6 prefix
const uint8_t bitMask6[]= { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
/// @brief calculates the first IPv6 address in a IPv6 prefix
///
/// Note: This is a private function. Do not use it directly.
/// Please use firstAddrInPrefix() instead.
///
/// @param prefix IPv6 prefix
/// @param len prefix length
isc::asiolink::IOAddress firstAddrInPrefix6(const isc::asiolink::IOAddress& prefix,
uint8_t len) {
const static uint8_t bitMask[]= { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
uint8_t packed[V6ADDRESS_LEN];
// First we copy the whole address as 16 bytes.
......@@ -36,7 +58,7 @@ isc::asiolink::IOAddress firstAddrInPrefix(const isc::asiolink::IOAddress& prefi
// 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];
uint8_t mask = bitMask6[len % 8];
// Let's leave only whatever the mask says should not be cleared.
packed[len / 8] = packed[len / 8] & mask;
......@@ -55,10 +77,50 @@ isc::asiolink::IOAddress firstAddrInPrefix(const isc::asiolink::IOAddress& prefi
return (isc::asiolink::IOAddress::from_bytes(AF_INET6, packed));
}
isc::asiolink::IOAddress lastAddrInPrefix(const isc::asiolink::IOAddress& prefix,
/// @brief calculates the first IPv4 address in a IPv4 prefix
///
/// Note: This is a private function. Do not use it directly.
/// Please use firstAddrInPrefix() instead.
///
/// @param prefix IPv4 prefix
/// @param len netmask length (0-32)
isc::asiolink::IOAddress firstAddrInPrefix4(const isc::asiolink::IOAddress& prefix,
uint8_t len) {
uint32_t addr = prefix;
if (len > 32) {
isc_throw(isc::BadValue, "Too large netmask. 0..32 is allowed in IPv4");
}
return (IOAddress(addr & (~bitMask4[len])));
}
/// @brief calculates the last IPv4 address in a IPv4 prefix
///
/// Note: This is a private function. Do not use it directly.
/// Please use firstAddrInPrefix() instead.
///
/// @param prefix IPv4 prefix that we calculate first address for
/// @param len netmask length (0-32)
isc::asiolink::IOAddress lastAddrInPrefix4(const isc::asiolink::IOAddress& prefix,
uint8_t len) {
uint32_t addr = prefix;
if (len>32) {
isc_throw(isc::BadValue, "Too large netmask. 0..32 is allowed in IPv4");
}
return (IOAddress(addr | bitMask4[len]));
}
/// @brief calculates the last IPv6 address in a IPv6 prefix
///
/// Note: This is a private function. Do not use it directly.
/// Please use lastAddrInPrefix() instead.
///
/// @param prefix IPv6 prefix that we calculate first address for
/// @param len netmask length (0-128)
isc::asiolink::IOAddress lastAddrInPrefix6(const isc::asiolink::IOAddress& prefix,
uint8_t len) {
const static uint8_t bitMask[]= { 0, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
uint8_t packed[V6ADDRESS_LEN];
// First we copy the whole address as 16 bytes.
......@@ -70,10 +132,10 @@ isc::asiolink::IOAddress lastAddrInPrefix(const isc::asiolink::IOAddress& prefix
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];
uint8_t mask = bitMask6[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,
// easier to not use negation here and invert bitMask6 content. However,
// with this approach, we can use the same mask in first and last
// address calculations.
packed[len / 8] = packed[len / 8] | ~mask;
......@@ -92,5 +154,28 @@ isc::asiolink::IOAddress lastAddrInPrefix(const isc::asiolink::IOAddress& prefix
return (isc::asiolink::IOAddress::from_bytes(AF_INET6, packed));
}
}; // end of anonymous namespace
namespace isc {
namespace dhcp {
isc::asiolink::IOAddress firstAddrInPrefix(const isc::asiolink::IOAddress& prefix,
uint8_t len) {
if (prefix.getFamily() == AF_INET) {
return firstAddrInPrefix4(prefix, len);
} else {
return firstAddrInPrefix6(prefix, len);
}
}
isc::asiolink::IOAddress lastAddrInPrefix(const isc::asiolink::IOAddress& prefix,
uint8_t len) {
if (prefix.getFamily() == AF_INET) {
return lastAddrInPrefix4(prefix, len);
} else {
return lastAddrInPrefix6(prefix, len);
}
}
};
};
......@@ -68,6 +68,39 @@ void CfgMgr::addSubnet6(const Subnet6Ptr& subnet) {
subnets6_.push_back(subnet);
}
Subnet4Ptr
CfgMgr::getSubnet4(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 (subnets4_.size() == 1) {
return (subnets4_[0]);
}
// If there is more than one, we need to choose the proper one
for (Subnet4Collection::iterator subnet = subnets4_.begin();
subnet != subnets4_.end(); ++subnet) {
if ((*subnet)->inRange(hint)) {
return (*subnet);
}
}
// sorry, we don't support that subnet
return (Subnet4Ptr());
}
void CfgMgr::addSubnet4(const Subnet4Ptr& subnet) {
/// @todo: Check that this new subnet does not cross boundaries of any
/// other already defined subnet.
subnets4_.push_back(subnet);
}
CfgMgr::CfgMgr() {
}
......
......@@ -72,7 +72,7 @@ public:
/// accessing it.
static CfgMgr& instance();
/// @brief get subnet by address
/// @brief get IPv6 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
......@@ -83,7 +83,7 @@ public:
/// @param hint an address that belongs to a searched subnet
Subnet6Ptr getSubnet6(const isc::asiolink::IOAddress& hint);
/// @brief get subnet by interface-id
/// @brief get IPv6 subnet by interface-id
///
/// Another possibility to find a subnet is based on interface-id.
///
......@@ -91,7 +91,7 @@ public:
/// @todo This method is not currently supported.
Subnet6Ptr getSubnet6(OptionPtr interface_id);
/// @brief adds a subnet6
/// @brief adds an IPv6 subnet
void addSubnet6(const Subnet6Ptr& subnet);
/// @todo: Add subnet6 removal routines. Currently it is not possible
......@@ -113,6 +113,22 @@ public:
subnets6_.clear();
}
/// @brief get IPv4 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
Subnet4Ptr getSubnet4(const isc::asiolink::IOAddress& hint);
/// @brief adds a subnet4
void addSubnet4(const Subnet4Ptr& subnet);
/// @brief removes all IPv4 subnets
void removeSubnets4();
protected:
/// @brief Protected constructor.
......@@ -126,13 +142,21 @@ protected:
/// @brief virtual desctructor
virtual ~CfgMgr();
/// @brief a container for Subnet6
/// @brief a container for IPv6 subnets.
///
/// 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_;
/// @brief a container for IPv4 subnets.
///
/// 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.
Subnet4Collection subnets4_;
};
} // namespace isc::dhcp
......
......@@ -30,6 +30,38 @@ bool Pool::inRange(const isc::asiolink::IOAddress& addr) const {
return (first_.smallerEqual(addr) && addr.smallerEqual(last_));
}
Pool4::Pool4(const isc::asiolink::IOAddress& first,
const isc::asiolink::IOAddress& last)
:Pool(first, last) {
// check if specified address boundaries are sane
if (first.getFamily() != AF_INET || last.getFamily() != AF_INET) {
isc_throw(BadValue, "Invalid Pool4 address boundaries: not IPv4");
}
if (last < first) {
isc_throw(BadValue, "Upper boundary is smaller than lower boundary.");
}
}
Pool4::Pool4(const isc::asiolink::IOAddress& prefix,
uint8_t prefix_len)
:Pool(prefix, IOAddress("0.0.0.0")) {
// check if the prefix is sane
if (prefix.getFamily() != AF_INET) {
isc_throw(BadValue, "Invalid Pool4 address boundaries: not IPv4");
}
// check if the prefix length is sane
if (prefix_len == 0 || prefix_len > 32) {
isc_throw(BadValue, "Invalid prefix length");
}
// Let's now calculate the last address in defined pool
last_ = lastAddrInPrefix(prefix, prefix_len);
}
Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& first,
const isc::asiolink::IOAddress& last)
:Pool(first, last), type_(type), prefix_len_(0) {
......@@ -52,7 +84,6 @@ Pool6::Pool6(Pool6Type type, const isc::asiolink::IOAddress& first,
// 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)
......
......@@ -91,6 +91,33 @@ protected:
std::string comments_;
};
/// @brief Pool information for IPv4 addresses
///
/// It holds information about pool4, i.e. a range of IPv4 address space that
/// is configured for DHCP allocation.
class Pool4 : public Pool {
public:
/// @brief the constructor for Pool4 "min-max" style definition
///
/// @param first the first address in a pool
/// @param last the last address in a pool
Pool4(const isc::asiolink::IOAddress& first,
const isc::asiolink::IOAddress& last);
/// @brief the constructor for Pool4 "prefix/len" style definition
///
/// @param prefix specifies prefix of the pool
/// @param prefix_len specifies length of the prefix of the pool
Pool4(const isc::asiolink::IOAddress& prefix,
uint8_t prefix_len);
};
/// @brief a pointer an IPv4 Pool
typedef boost::shared_ptr<Pool4> Pool4Ptr;
/// @brief a container for IPv4 Pools
typedef std::vector<Pool4Ptr> Pool4Collection;
/// @brief Pool information for IPv6 addresses and prefixes
///
/// It holds information about pool6, i.e. a range of IPv6 address space that
......
......@@ -41,6 +41,50 @@ bool Subnet::inRange(const isc::asiolink::IOAddress& addr) const {
return ((first <= addr) && (addr <= last));
}
Subnet4::Subnet4(const isc::asiolink::IOAddress& prefix, uint8_t length,
const Triplet<uint32_t>& t1,
const Triplet<uint32_t>& t2,
const Triplet<uint32_t>& valid_lifetime)
:Subnet(prefix, length, t1, t2, valid_lifetime) {
if (prefix.getFamily() != AF_INET) {
isc_throw(BadValue, "Non IPv4 prefix " << prefix.toText()
<< " specified in subnet4");
}
}
void Subnet4::addPool4(const Pool4Ptr& pool) {
IOAddress first_addr = pool->getFirstAddress();
IOAddress last_addr = pool->getLastAddress();
if (!inRange(first_addr) || !inRange(last_addr)) {
isc_throw(BadValue, "Pool4 (" << first_addr.toText() << "-" << last_addr.toText()
<< " does not belong in this (" << prefix_ << "/" << prefix_len_
<< ") subnet4");
}
/// @todo: Check that pools do not overlap
pools_.push_back(pool);
}
Pool4Ptr Subnet4::getPool4(const isc::asiolink::IOAddress& hint /* = IOAddress("::")*/ ) {
Pool4Ptr candidate;
for (Pool4Collection::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);
}
Subnet6::Subnet6(const isc::asiolink::IOAddress& prefix, uint8_t length,
const Triplet<uint32_t>& t1,
const Triplet<uint32_t>& t2,
......
......@@ -93,6 +93,57 @@ protected:
Triplet<uint32_t> valid_;
};
/// @brief A configuration holder for IPv4 subnet.
///
/// This class represents an IPv4 subnet.
class Subnet4 : public Subnet {
public:
/// @brief Constructor with all parameters
///
/// @param prefix Subnet4 prefix
/// @param length prefix length
/// @param t1 renewal timer (in seconds)
/// @param t2 rebind timer (in seconds)
/// @param valid_lifetime preferred lifetime of leases (in seconds)
Subnet4(const isc::asiolink::IOAddress& prefix, uint8_t length,
const Triplet<uint32_t>& t1,
const Triplet<uint32_t>& t2,
const Triplet<uint32_t>& valid_lifetime);
/// @brief Returns a pool that specified address belongs to
///
/// @param hint address that the returned pool should cover (optional)
/// @return Pointer to found pool4 (or NULL)
Pool4Ptr getPool4(const isc::asiolink::IOAddress& hint =
isc::asiolink::IOAddress("0.0.0.0"));
/// @brief Adds a new pool.
/// @param pool pool to be added
void addPool4(const Pool4Ptr& 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 Pool4Collection& getPools() const {
return pools_;
}
protected:
/// @brief collection of pools in that list
Pool4Collection pools_;
};
/// @brief A pointer to a Subnet4 object
typedef boost::shared_ptr<Subnet4> Subnet4Ptr;
/// @brief A collection of Subnet6 objects
typedef std::vector<Subnet4Ptr> Subnet4Collection;
/// @brief A configuration holder for IPv6 subnet.
///
/// This class represents an IPv6 subnet.
......
......@@ -26,7 +26,67 @@ using namespace std;
using namespace isc::dhcp;
using namespace isc::asiolink;
TEST(Pool6Test, lastAddrInPrefix) {
// This test verifies that lastAddrInPrefix is able to handle IPv4 operations.
TEST(AddrUtilitiesTest, lastAddrInPrefix4) {
IOAddress addr1("192.0.2.1");
// Prefixes rounded to addresses are easy...
EXPECT_EQ("192.255.255.255", lastAddrInPrefix(addr1, 8).toText());
EXPECT_EQ("192.0.255.255", lastAddrInPrefix(addr1, 16).toText());
EXPECT_EQ("192.0.2.255", lastAddrInPrefix(addr1, 24).toText());
// these are trickier
EXPECT_EQ("192.0.2.127", lastAddrInPrefix(addr1, 25).toText());
EXPECT_EQ("192.0.2.63", lastAddrInPrefix(addr1, 26).toText());
EXPECT_EQ("192.0.2.31", lastAddrInPrefix(addr1, 27).toText());
EXPECT_EQ("192.0.2.15", lastAddrInPrefix(addr1, 28).toText());
EXPECT_EQ("192.0.2.7", lastAddrInPrefix(addr1, 29).toText());
EXPECT_EQ("192.0.2.3", lastAddrInPrefix(addr1, 30).toText());
// that doesn't make much sense as /31 subnet consists of network address
// and a broadcast address, with 0 usable addresses.
EXPECT_EQ("192.0.2.1", lastAddrInPrefix(addr1, 31).toText());
EXPECT_EQ("192.0.2.1", lastAddrInPrefix(addr1, 32).toText());
// Let's check extreme cases
IOAddress anyAddr("0.0.0.0");
EXPECT_EQ("127.255.255.255", lastAddrInPrefix(anyAddr, 1).toText());
EXPECT_EQ("255.255.255.255", lastAddrInPrefix(anyAddr, 0).toText());
EXPECT_EQ("0.0.0.0", lastAddrInPrefix(anyAddr, 32).toText());
}
// This test checks if firstAddrInPrefix is able to handle IPv4 operations.
TEST(AddrUtilitiesTest, firstAddrInPrefix4) {
IOAddress addr1("192.223.2.255");
// Prefixes rounded to addresses are easy...
EXPECT_EQ("192.0.0.0", firstAddrInPrefix(addr1, 8).toText());
EXPECT_EQ("192.223.0.0", firstAddrInPrefix(addr1, 16).toText());
EXPECT_EQ("192.223.2.0", firstAddrInPrefix(addr1, 24).toText());
// these are trickier
EXPECT_EQ("192.223.2.128", firstAddrInPrefix(addr1, 25).toText());
EXPECT_EQ("192.223.2.192", firstAddrInPrefix(addr1, 26).toText());
EXPECT_EQ("192.223.2.224", firstAddrInPrefix(addr1, 27).toText());
EXPECT_EQ("192.223.2.240", firstAddrInPrefix(addr1, 28).toText());
EXPECT_EQ("192.223.2.248", firstAddrInPrefix(addr1, 29).toText());
EXPECT_EQ("192.223.2.252", firstAddrInPrefix(addr1, 30).toText());
// that doesn't make much sense as /31 subnet consists of network address
// and a broadcast address, with 0 usable addresses.
EXPECT_EQ("192.223.2.254", firstAddrInPrefix(addr1, 31).toText());
EXPECT_EQ("192.223.2.255", firstAddrInPrefix(addr1, 32).toText());
// Let's check extreme cases.
IOAddress bcast("255.255.255.255");
EXPECT_EQ("128.0.0.0", firstAddrInPrefix(bcast, 1).toText());
EXPECT_EQ("0.0.0.0", firstAddrInPrefix(bcast, 0).toText());
EXPECT_EQ("255.255.255.255", firstAddrInPrefix(bcast, 32).toText());
}
/// This test checks if lastAddrInPrefix properly supports IPv6 operations
TEST(AddrUtilitiesTest, lastAddrInPrefix6) {
IOAddress addr1("2001:db8:1:1234:5678:abcd:1234:beef");
// Prefixes rounded to nibbles are easy...
......@@ -63,7 +123,8 @@ TEST(Pool6Test, lastAddrInPrefix) {
EXPECT_EQ("::", lastAddrInPrefix(anyAddr, 128).toText());
}
TEST(Pool6Test, firstAddrInPrefix) {
/// This test checks if firstAddrInPrefix properly supports IPv6 operations
TEST(AddrUtilitiesTest, firstAddrInPrefix6) {
IOAddress addr1("2001:db8:1:1234:5678:1234:abcd:beef");
// Prefixes rounded to nibbles are easy...
......
......@@ -32,6 +32,38 @@ using boost::scoped_ptr;
namespace {
// This test verifies if the configuration manager is able to hold and return
// valid leases
TEST(CfgMgrTest, subnet4) {
CfgMgr& cfg_mgr = CfgMgr::instance();
ASSERT_TRUE(&cfg_mgr != 0);
Subnet4Ptr subnet1(new Subnet4(IOAddress("192.0.2.0"), 26, 1, 2, 3));
Subnet4Ptr subnet2(new Subnet4(IOAddress("192.0.2.64"), 26, 1, 2, 3));
Subnet4Ptr subnet3(new Subnet4(IOAddress("192.0.2.128"), 26, 1, 2, 3));
// there shouldn't be any subnet configured at this stage
EXPECT_EQ( Subnet4Ptr(), cfg_mgr.getSubnet4(IOAddress("192.0.2.0")));
cfg_mgr.addSubnet4(subnet1);
// Now we have only one subnet, any request will be served from it
EXPECT_EQ(subnet1, cfg_mgr.getSubnet4(IOAddress("192.0.2.63")));
// Now we add more subnets and check that both old and new subnets
// are accessible.
cfg_mgr.addSubnet4(subnet2);
cfg_mgr.addSubnet4(subnet3);
EXPECT_EQ(subnet3, cfg_mgr.getSubnet4(IOAddress("192.0.2.191")));
EXPECT_EQ(subnet1, cfg_mgr.getSubnet4(IOAddress("192.0.2.15")));
EXPECT_EQ(subnet2, cfg_mgr.getSubnet4(IOAddress("192.0.2.85")));
// Try to find an address that does not belong to any subnet
EXPECT_EQ(Subnet4Ptr(), cfg_mgr.getSubnet4(IOAddress("192.0.2.192")));
}
// This test verifies if the configuration manager is able to hold and return
// valid leases
TEST(CfgMgrTest, subnet6) {
......
......@@ -27,6 +27,79 @@ using namespace isc::asiolink;
namespace {
TEST(Pool4Test, constructor_first_last) {
// let's construct 192.0.2.1-192.0.2.255 pool
Pool4 pool1(IOAddress("192.0.2.1"), IOAddress("192.0.2.255"));
EXPECT_EQ(IOAddress("192.0.2.1"), pool1.getFirstAddress());
EXPECT_EQ(IOAddress("192.0.2.255"), pool1.getLastAddress());
// This is Pool4, IPv6 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 192.0.2.1-192.0.2.2, not
// the other way around.
EXPECT_THROW(Pool6(Pool6::TYPE_IA, IOAddress("192.0.2.2"),
IOAddress("192.0.2.1")), BadValue);
}
TEST(Pool4Test, constructor_prefix_len) {