Commit 69f66e61 authored by Marcin Siodelski's avatar Marcin Siodelski

[3587] Created CfgSubnets4 class to hold subnets configuration.

parent 1da855a8
......@@ -48,6 +48,7 @@ libkea_dhcpsrv_la_SOURCES += callout_handle_store.h
libkea_dhcpsrv_la_SOURCES += cfg_iface.cc cfg_iface.h
libkea_dhcpsrv_la_SOURCES += cfg_option.cc cfg_option.h
libkea_dhcpsrv_la_SOURCES += cfg_option_def.cc cfg_option_def.h
libkea_dhcpsrv_la_SOURCES += cfg_subnets4.cc cfg_subnets4.h
libkea_dhcpsrv_la_SOURCES += cfgmgr.cc cfgmgr.h
libkea_dhcpsrv_la_SOURCES += csv_lease_file4.cc csv_lease_file4.h
libkea_dhcpsrv_la_SOURCES += csv_lease_file6.cc csv_lease_file6.h
......
// Copyright (C) 2014 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/iface_mgr.h>
#include <dhcpsrv/cfg_subnets4.h>
#include <dhcpsrv/dhcpsrv_log.h>
using namespace isc::asiolink;
namespace {
/// @brief Returns @c IOAddress object set to "0.0.0.0".
const IOAddress& ZERO_ADDRESS() {
static IOAddress address("0.0.0.0");
return (address);
}
} // end of anonymous namespace
namespace isc {
namespace dhcp {
CfgSubnets4::Selector::Selector()
: ciaddr_(ZERO_ADDRESS()), giaddr_(ZERO_ADDRESS()),
local_address_(ZERO_ADDRESS()), remote_address_(ZERO_ADDRESS()),
client_classes_(ClientClasses()), iface_name_(std::string()) {
}
void
CfgSubnets4::add(const Subnet4Ptr& subnet) {
/// @todo: Check that this new subnet does not cross boundaries of any
/// other already defined subnet.
if (isDuplicate(*subnet)) {
isc_throw(isc::dhcp::DuplicateSubnet4ID, "ID of the new IPv4 subnet '"
<< subnet->getID() << "' is already in use");
}
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_ADD_SUBNET4)
.arg(subnet->toText());
subnets_.push_back(subnet);
}
Subnet4Ptr
CfgSubnets4::get(const Selector& selector) const {
// If relayed message has been received, try to match the giaddr with the
// relay address specified for a subnet. It is also possible that the relay
// address will not match with any of the relay addresses accross all
// subnets, but we need to verify that for all subnets before we can try
// to use the giaddr to match with the subnet prefix.
if (selector.giaddr_.isSpecified() && selector.giaddr_ != ZERO_ADDRESS()) {
for (Subnet4Collection::const_iterator subnet = subnets_.begin();
subnet != subnets_.end(); ++subnet) {
// Eliminate those subnets that do not meet client class criteria.
if (!(*subnet)->clientSupported(selector.client_classes_)) {
continue;
}
// Check if the giaddr is equal to the one defined for the subnet.
if (selector.giaddr_ == (*subnet)->getRelayInfo().addr_) {
return (*subnet);
}
}
}
// If we got to this point it means that we were not able to match the
// giaddr with any of the addresses specified for subnets. Let's determine
// what address from the client's packet to use to match with the
// subnets' prefixes.
IOAddress address = ZERO_ADDRESS();
// If there is a giaddr, use it for subnet selection.
if (selector.giaddr_.isSpecified() &&
(selector.giaddr_ != ZERO_ADDRESS())) {
address = selector.giaddr_;
// If it is a Renew or Rebind, use the ciaddr.
} else if (selector.ciaddr_.isSpecified() &&
selector.ciaddr_ != ZERO_ADDRESS()) {
address = selector.ciaddr_;
// If ciaddr is not specified, use the source address.
} else if (selector.remote_address_.isSpecified() &&
selector.remote_address_ != ZERO_ADDRESS()) {
address = selector.remote_address_;
// If local interface name is known, use the local address on this
// interface.
} else if (selector.iface_name_.isSpecified()) {
Iface* iface = IfaceMgr::instance().getIface(selector.iface_name_);
// This should never happen in the real life. Hence we throw an
// exception.
if (iface == NULL) {
isc_throw(isc::BadValue, "interface " << selector.iface_name_.get()
<< " doesn't exist and therefore it is impossible"
" to find a suitable subnet for its IPv4 address");
}
iface->getAddress4(address);
} else {
isc_throw(isc::BadValue, "subnet selector structure does not contain"
" sufficient information");
}
// Unable to find a suitable address to use for subnet selection.
if (address == ZERO_ADDRESS()) {
return (Subnet4Ptr());
}
// We have identified an address in the client's packet that can be
// used for subnet selection. Match this packet with the subnets.
for (Subnet4Collection::const_iterator subnet = subnets_.begin();
subnet != subnets_.end(); ++subnet) {
// Eliminate those subnets that do not meet client class criteria.
if (!(*subnet)->clientSupported(selector.client_classes_)) {
continue;
}
// Address is in range for the subnet prefix, so return it.
if ((*subnet)->inRange(address)) {
return (*subnet);
}
}
// Failed to find a subnet.
return (Subnet4Ptr());
}
bool
CfgSubnets4::isDuplicate(const Subnet4& subnet) const {
for (Subnet4Collection::const_iterator subnet_it = subnets_.begin();
subnet_it != subnets_.end(); ++subnet_it) {
if ((*subnet_it)->getID() == subnet.getID()) {
return (true);
}
}
return (false);
}
} // end of namespace isc::dhcp
} // end of namespace isc
// Copyright (C) 2014 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 CFG_SUBNETS4_H
#define CFG_SUBNETS4_H
#include <asiolink/io_address.h>
#include <exceptions/exceptions.h>
#include <dhcpsrv/subnet.h>
#include <util/optional_value.h>
namespace isc {
namespace dhcp {
/// @brief Exception thrown upon attempt to add subnet with an ID that belongs
/// to the subnet that already exists.
class DuplicateSubnet4ID : public Exception {
public:
DuplicateSubnet4ID(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) { };
};
/// @brief Holds subnets configured for the DHCPv4 server.
///
/// This class holds a collection of subnets configured for the DHCPv4 server.
/// It allows for retrieving a subnet for the particular client using various
/// parameters extracted from the DHCPv4 message. These parameters must be
/// assigned to the appropriate members of the @c CfgSubnets4::Selector
/// structure.
///
/// See @c CfgSubnets4::get documentation for more details on how the subnet
/// is selected for the client.
class CfgSubnets4 {
public:
/// @brief Subnet selector used in @c CfgSubnets4::getSubnet4.
///
/// This structure holds various parameters extracted from a packet sent
/// by a DHCP client used to select the subnet for the client. Note that
/// data members are optional which means that they may be left unspecified
/// by the caller, if the caller doesn't have access to the relevant
/// information.
struct Selector {
/// @brief ciaddr from the client's message.
util::OptionalValue<asiolink::IOAddress> ciaddr_;
/// @brief giaddr from the client's message.
util::OptionalValue<asiolink::IOAddress> giaddr_;
/// @brief Address on which the message was received.
util::OptionalValue<asiolink::IOAddress> local_address_;
/// @brief Source address of the message.
util::OptionalValue<asiolink::IOAddress> remote_address_;
/// @brief Classes that the client belongs to.
util::OptionalValue<ClientClasses> client_classes_;
/// @brief Name of the interface on which the message was received.
util::OptionalValue<std::string> iface_name_;
/// @brief Default constructor.
///
/// Sets the default values for the @c Selector.
Selector();
};
/// @brief Adds new subnet to the configuration.
///
/// @param subnet Pointer to the subnet being added.
///
/// @throw isc::DuplicateSubnet4ID If the subnet id for the new subnet
/// duplicates id of an existing subnet.
void add(const Subnet4Ptr& subnet);
/// @brief Returns pointer to the collection of all IPv4 subnets.
///
/// This is used in a hook (subnet4_select), where the hook is able
/// to choose a different subnet. Server code has to offer a list
/// of possible choices (i.e. all subnets).
///
/// @return A pointer to const Subnet4 collection
const Subnet4Collection* getAll() const {
return (&subnets_);
}
/// @brief Returns pointer to the selected subnet.
///
/// This method tries to retrieve the subnet for the client using various
/// parameters extracted from the client's message using the following
/// logic.
///
/// If the giaddr value is found it means that the client's message was
/// relayed. The subnet configuration allows for setting the relay address
/// for each subnet to indicate that the subnet must be assigned when the
/// packet was transmitted over the particular relay. This method first
/// tries to match the giaddr with the relay addresses specified for
/// all subnets. If the relay address for the subnet is equal to the address
/// of the relay through which the message was transmitted, the particular
/// subnet is returned.
///
/// If the giaddr is not matched with any of the relay addresses in any
/// subnet or the message was not relayed, the method will need to try to
/// match one of the addresses in the client's message with the prefixes
/// of the existing subnets. Depending whether it is a relayed message,
/// message from the renewing client or a new allocation, the server will
/// pick one of the following addresses for this matching:
/// - giaddr - for relayed message
/// - ciaddr - for renewing or rebinding client
/// - source address - for the renewing client which didn't provide ciaddr
/// - address on the local server's interface if this is a new allocation
/// requested by the directly connected client
///
/// If the address matches with a subnet, the subnet is returned.
///
/// @param selector Const reference to the selector structure which holds
/// various information extracted from the client's packet which are used
/// to find appropriate subnet.
///
/// @return Pointer to the selected subnet or NULL if no subnet found.
/// @throw isc::BadValue if the values in the subnet selector are invalid
/// or they are insufficient to select a subnet.
Subnet4Ptr get(const Selector& selector) const;
private:
/// @brief Checks that the IPv4 subnet with the given id already exists.
///
/// @param subnet Subnet for which this function will check if the other
/// subnet with equal id already exists.
///
/// @return true if the duplicate subnet exists.
bool isDuplicate(const Subnet4& subnet) const;
/// @brief A container for IPv4 subnets.
Subnet4Collection subnets_;
};
}
}
#endif // CFG_SUBNETS4_H
......@@ -484,7 +484,7 @@ protected:
/// a match is found.
Subnet6Collection subnets6_;
/// @brief a container for IPv4 subnets.
/// @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
......
......@@ -493,13 +493,17 @@ protected:
isc::asiolink::IOAddress siaddr_;
};
/// @brief A pointer to a Subnet4 object
/// @brief A pointer to a @c Subnet4 object
typedef boost::shared_ptr<Subnet4> Subnet4Ptr;
/// @brief A collection of Subnet6 objects
/// @brief A collection of @c Subnet4 objects
///
/// 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.
typedef std::vector<Subnet4Ptr> Subnet4Collection;
/// @brief A configuration holder for IPv6 subnet.
///
/// This class represents an IPv6 subnet.
......
......@@ -58,6 +58,7 @@ libdhcpsrv_unittests_SOURCES += callout_handle_store_unittest.cc
libdhcpsrv_unittests_SOURCES += cfg_iface_unittest.cc
libdhcpsrv_unittests_SOURCES += cfg_option_unittest.cc
libdhcpsrv_unittests_SOURCES += cfg_option_def_unittest.cc
libdhcpsrv_unittests_SOURCES += cfg_subnets4_unittest.cc
libdhcpsrv_unittests_SOURCES += cfgmgr_unittest.cc
libdhcpsrv_unittests_SOURCES += csv_lease_file4_unittest.cc
libdhcpsrv_unittests_SOURCES += csv_lease_file6_unittest.cc
......
// Copyright (C) 2014 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/classify.h>
#include <dhcp/tests/iface_mgr_test_config.h>
#include <dhcpsrv/cfg_subnets4.h>
#include <dhcpsrv/subnet.h>
#include <gtest/gtest.h>
using namespace isc;
using namespace isc::asiolink;
using namespace isc::dhcp;
using namespace isc::dhcp::test;
namespace {
// This test verifies that it is possible to retrieve a subnet using an
// IP address.
TEST(CfgSubnets4Test, getSubnetByCiaddr) {
CfgSubnets4 cfg;
// Create 3 subnets.
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));
// Make sure that initially the subnets don't exist.
CfgSubnets4::Selector selector;
selector.ciaddr_ = IOAddress("192.0.2.0");
// Set some unicast local address to simulate a Renew.
selector.local_address_ = IOAddress("10.0.0.100");
ASSERT_FALSE(cfg.get(selector));
// Add one subnet and make sure it is returned.
cfg.add(subnet1);
selector.ciaddr_ = IOAddress("192.0.2.63");
EXPECT_EQ(subnet1, cfg.get(selector));
// Add all other subnets.
cfg.add(subnet2);
cfg.add(subnet3);
// Make sure they are returned for the appropriate addresses.
selector.ciaddr_ = IOAddress("192.0.2.15");
EXPECT_EQ(subnet1, cfg.get(selector));
selector.ciaddr_ = IOAddress("192.0.2.85");
EXPECT_EQ(subnet2, cfg.get(selector));
selector.ciaddr_ = IOAddress("192.0.2.191");
EXPECT_EQ(subnet3, cfg.get(selector));
// Also, make sure that the NULL pointer is returned if the subnet
// cannot be found.
selector.ciaddr_ = IOAddress("192.0.2.192");
EXPECT_FALSE(cfg.get(selector));
}
// This test verifies that when the classification information is specified for
// subnets, the proper subnets are returned by the subnet configuration.
TEST(CfgSubnets4Test, getSubnetByClasses) {
CfgSubnets4 cfg;
// Create 3 subnets.
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));
// Add them to the configuration.
cfg.add(subnet1);
cfg.add(subnet2);
cfg.add(subnet3);
CfgSubnets4::Selector selector;
selector.local_address_ = IOAddress("10.0.0.10");
selector.ciaddr_ = IOAddress("192.0.2.5");
EXPECT_EQ(subnet1, cfg.get(selector));
selector.ciaddr_ = IOAddress("192.0.2.70");
EXPECT_EQ(subnet2, cfg.get(selector));
selector.ciaddr_ = IOAddress("192.0.2.130");
EXPECT_EQ(subnet3, cfg.get(selector));
ClientClasses client_classes;
client_classes.insert("bar");
selector.client_classes_ = client_classes;
// There are no class restrictions defined, so everything should work
// as before
selector.ciaddr_ = IOAddress("192.0.2.5");
EXPECT_EQ(subnet1, cfg.get(selector));
selector.ciaddr_ = IOAddress("192.0.2.70");
EXPECT_EQ(subnet2, cfg.get(selector));
selector.ciaddr_ = IOAddress("192.0.2.130");
EXPECT_EQ(subnet3, cfg.get(selector));
// Now let's add client class restrictions.
subnet1->allowClientClass("foo"); // Serve here only clients from foo class
subnet2->allowClientClass("bar"); // Serve here only clients from bar class
subnet3->allowClientClass("baz"); // Serve here only clients from baz class
// The same check as above should result in client being served only in
// bar class, i.e. subnet2.
selector.ciaddr_ = IOAddress("192.0.2.5");
EXPECT_FALSE(cfg.get(selector));
selector.ciaddr_ = IOAddress("192.0.2.70");
EXPECT_EQ(subnet2, cfg.get(selector));
selector.ciaddr_ = IOAddress("192.0.2.130");
EXPECT_FALSE(cfg.get(selector));
// Now let's check that client with wrong class is not supported.
client_classes.clear();
client_classes.insert("some_other_class");
selector.client_classes_ = client_classes;
selector.ciaddr_ = IOAddress("192.0.2.5");
EXPECT_FALSE(cfg.get(selector));
selector.ciaddr_ = IOAddress("192.0.2.70");
EXPECT_FALSE(cfg.get(selector));
selector.ciaddr_ = IOAddress("192.0.2.130");
EXPECT_FALSE(cfg.get(selector));
// Finally, let's check that client without any classes is not supported.
client_classes.clear();
selector.ciaddr_ = IOAddress("192.0.2.5");
EXPECT_FALSE(cfg.get(selector));
selector.ciaddr_ = IOAddress("192.0.2.70");
EXPECT_FALSE(cfg.get(selector));
selector.ciaddr_ = IOAddress("192.0.2.130");
EXPECT_FALSE(cfg.get(selector));
}
// This test verifies that the relay information can be used to retrieve the
// subnet.
TEST(CfgSubnetsTest, getSubnetByRelayAddress) {
CfgSubnets4 cfg;
// Create 3 subnets.
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));
// Add them to the configuration.
cfg.add(subnet1);
cfg.add(subnet2);
cfg.add(subnet3);
CfgSubnets4::Selector selector;
// Check that without relay-info specified, subnets are not selected
selector.giaddr_ = IOAddress("10.0.0.1");
EXPECT_FALSE(cfg.get(selector));
selector.giaddr_ = IOAddress("10.0.0.2");
EXPECT_FALSE(cfg.get(selector));
selector.giaddr_ = IOAddress("10.0.0.3");
EXPECT_FALSE(cfg.get(selector));
// Now specify relay info
subnet1->setRelayInfo(IOAddress("10.0.0.1"));
subnet2->setRelayInfo(IOAddress("10.0.0.2"));
subnet3->setRelayInfo(IOAddress("10.0.0.3"));
// And try again. This time relay-info is there and should match.
selector.giaddr_ = IOAddress("10.0.0.1");
EXPECT_EQ(subnet1, cfg.get(selector));
selector.giaddr_ = IOAddress("10.0.0.2");
EXPECT_EQ(subnet2, cfg.get(selector));
selector.giaddr_ = IOAddress("10.0.0.3");
EXPECT_EQ(subnet3, cfg.get(selector));
}
// This test verifies that the subnet can be selected for the client
// using a source address if the client hasn't set the ciaddr.
TEST(CfgSubnetsTest, getSubnetNoCiaddr) {
CfgSubnets4 cfg;
// Create 3 subnets.
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));
// Make sure that initially the subnets don't exist.
CfgSubnets4::Selector selector;
selector.remote_address_ = IOAddress("192.0.2.0");
// Set some unicast local address to simulate a Renew.
selector.local_address_ = IOAddress("10.0.0.100");
ASSERT_FALSE(cfg.get(selector));
// Add one subnet and make sure it is returned.
cfg.add(subnet1);
selector.remote_address_ = IOAddress("192.0.2.63");
EXPECT_EQ(subnet1, cfg.get(selector));
// Add all other subnets.
cfg.add(subnet2);
cfg.add(subnet3);
// Make sure they are returned for the appropriate addresses.
selector.remote_address_ = IOAddress("192.0.2.15");
EXPECT_EQ(subnet1, cfg.get(selector));
selector.remote_address_ = IOAddress("192.0.2.85");
EXPECT_EQ(subnet2, cfg.get(selector));
selector.remote_address_ = IOAddress("192.0.2.191");
EXPECT_EQ(subnet3, cfg.get(selector));
// Also, make sure that the NULL pointer is returned if the subnet
// cannot be found.
selector.remote_address_ = IOAddress("192.0.2.192");
EXPECT_FALSE(cfg.get(selector));
}
// This test verifies that the subnet can be selected using an address
// set on the local interface.
TEST(CfgSubnetsTest, getSubnetInterface) {
IfaceMgrTestConfig config(true);
CfgSubnets4 cfg;
CfgSubnets4::Selector selector;
// Initially, there are no subnets configured, so none of the IPv4
// addresses assigned to eth0 and eth1 can match with any subnet.
selector.iface_name_ = "eth0";
EXPECT_FALSE(cfg.get(selector));
selector.iface_name_ = "eth1";
EXPECT_FALSE(cfg.get(selector));
// Configure first subnet which address on eth0 corresponds to.
Subnet4Ptr subnet1(new Subnet4(IOAddress("10.0.0.1"), 24, 1, 2, 3));
cfg.add(subnet1);
// The address on eth0 should match the existing subnet.
selector.iface_name_ = "eth0";
Subnet4Ptr subnet1_ret = cfg.get(selector);
ASSERT_TRUE(subnet1_ret);
EXPECT_EQ(subnet1->get().first, subnet1_ret->get().first);
// There should still be no match for eth1.
selector.iface_name_ = "eth1";
EXPECT_FALSE(cfg.get(selector));
// Configure a second subnet.
Subnet4Ptr subnet2(new Subnet4(IOAddress("192.0.2.1"), 24, 1, 2, 3));
cfg.add(subnet2);
// There should be match between eth0 and subnet1 and between eth1 and
// subnet 2.
selector.iface_name_ = "eth0";
subnet1_ret = cfg.get(selector);
ASSERT_TRUE(subnet1_ret);
EXPECT_EQ(subnet1->get().first, subnet1_ret->get().first);
selector.iface_name_ = "eth1";
Subnet4Ptr subnet2_ret = cfg.get(selector);
ASSERT_TRUE(subnet2_ret);
EXPECT_EQ(subnet2->get().first, subnet2_ret->get().first);
// This function throws an exception if the name of the interface is wrong.
selector.iface_name_ = "bogus-interface";
EXPECT_THROW(cfg.get(selector), isc::BadValue);
}
// Checks that detection of duplicated subnet IDs works as expected. It should
// not be possible to add two IPv4 subnets holding the same ID.
TEST(CfgSubnets4, duplication) {
CfgSubnets4 cfg;
Subnet4Ptr subnet1(new Subnet4(IOAddress("192.0.2.0"), 26, 1, 2, 3, 123));
Subnet4Ptr subnet2(new Subnet4(IOAddress("192.0.2.64"), 26, 1, 2, 3, 124));
Subnet4Ptr subnet3(new Subnet4(IOAddress("192.0.2.128"), 26, 1, 2, 3, 123));
ASSERT_NO_THROW(cfg.add(subnet1));
EXPECT_NO_THROW(cfg.add(subnet2));