Commit 452efc32 authored by Marcin Siodelski's avatar Marcin Siodelski

[3625] Implemented the CfgSubnets6 class to hold IPv6 subnets configuration

parent 8744ddae
......@@ -49,6 +49,7 @@ 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 += cfg_subnets6.cc cfg_subnets6.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
......@@ -79,6 +80,7 @@ libkea_dhcpsrv_la_SOURCES += pool.cc pool.h
libkea_dhcpsrv_la_SOURCES += srv_config.cc srv_config.h
libkea_dhcpsrv_la_SOURCES += subnet.cc subnet.h
libkea_dhcpsrv_la_SOURCES += subnet_id.h
libkea_dhcpsrv_la_SOURCES += subnet_selector.h
libkea_dhcpsrv_la_SOURCES += triplet.h
libkea_dhcpsrv_la_SOURCES += utils.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 <dhcpsrv/cfg_subnets6.h>
#include <dhcpsrv/dhcpsrv_log.h>
#include <dhcpsrv/subnet_id.h>
using namespace isc::asiolink;
namespace isc {
namespace dhcp {
void
CfgSubnets6::add(const Subnet6Ptr& subnet) {
/// @todo: Check that this new subnet does not cross boundaries of any
/// other already defined subnet.
if (isDuplicate(*subnet)) {
isc_throw(isc::dhcp::DuplicateSubnetID, "ID of the new IPv6 subnet '"
<< subnet->getID() << "' is already in use");
}
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_ADD_SUBNET6)
.arg(subnet->toText());
subnets_.push_back(subnet);
}
Subnet6Ptr
CfgSubnets6::selectSubnet(const SubnetSelector& selector) const {
Subnet6Ptr subnet;
// If relay agent link address is set to zero it means that we're dealing
// with a directly connected client.
if (selector.first_relay_linkaddr_ == IOAddress("::")) {
// If interface name is known try to match it with interface names
// specified for configured subnets.
if (!selector.iface_name_.empty()) {
subnet = selectSubnet(selector.iface_name_,
selector.client_classes_);
}
// If interface name didn't match, try the client's address.
if (!subnet && selector.remote_address_.isSpecified()) {
subnet = selectSubnet(selector.remote_address_,
selector.client_classes_);
}
// If relay agent link address is set, we're dealing with a relayed message.
} else {
// Find the subnet using the Interface Id option, if present.
subnet = selectSubnet(selector.interface_id_, selector.client_classes_);
// If Interface ID option could not be matched for any subnet, try
// the relay agent link address.
if (!subnet) {
subnet = selectSubnet(selector.first_relay_linkaddr_,
selector.client_classes_,
true);
}
}
// Return subnet found, or NULL if not found.
return (subnet);
}
Subnet6Ptr
CfgSubnets6::selectSubnet(const asiolink::IOAddress& address,
const ClientClasses& client_classes,
const bool is_relay_address) const {
// If the specified address is a relay address we first need to match
// it with the relay addresses specified for all subnets.
if (is_relay_address) {
for (Subnet6Collection::const_iterator subnet = subnets_.begin();
subnet != subnets_.end(); ++subnet) {
// If the specified address matches the relay address, return this
// subnet.
if (is_relay_address &&
((*subnet)->getRelayInfo().addr_ == address) &&
(*subnet)->clientSupported(client_classes)) {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
DHCPSRV_CFGMGR_SUBNET6_RELAY)
.arg((*subnet)->toText()).arg(address.toText());
return (*subnet);
}
}
}
// No success so far. Check if the specified address is in range
// with any subnet.
for (Subnet6Collection::const_iterator subnet = subnets_.begin();
subnet != subnets_.end(); ++subnet) {
if ((*subnet)->inRange(address) &&
(*subnet)->clientSupported(client_classes)) {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_SUBNET6)
.arg((*subnet)->toText()).arg(address.toText());
return (*subnet);
}
}
// Nothing found.
return (Subnet6Ptr());
}
Subnet6Ptr
CfgSubnets6::selectSubnet(const std::string& iface_name,
const ClientClasses& client_classes) const {
// If empty interface specified, we can't select subnet by interface.
if (!iface_name.empty()) {
for (Subnet6Collection::const_iterator subnet = subnets_.begin();
subnet != subnets_.end(); ++subnet) {
// If interface name matches with the one specified for the subnet
// and the client is not rejected based on the classification,
// return the subnet.
if ((iface_name == (*subnet)->getIface()) &&
(*subnet)->clientSupported(client_classes)) {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
DHCPSRV_CFGMGR_SUBNET6_IFACE)
.arg((*subnet)->toText()).arg(iface_name);
return (*subnet);
}
}
}
// No subnet found for this interface name.
return (Subnet6Ptr());
}
Subnet6Ptr
CfgSubnets6::selectSubnet(const OptionPtr& interface_id,
const ClientClasses& client_classes) const {
// We can only select subnet using an interface id, if the interface
// id is known.
if (interface_id) {
for (Subnet6Collection::const_iterator subnet = subnets_.begin();
subnet != subnets_.end(); ++subnet) {
// If interface id matches for the subnet and the subnet is not
// rejected based on the classification.
if ((*subnet)->getInterfaceId() &&
(*subnet)->getInterfaceId()->equals(interface_id) &&
(*subnet)->clientSupported(client_classes)) {
LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
DHCPSRV_CFGMGR_SUBNET6_IFACE_ID)
.arg((*subnet)->toText());
return (*subnet);
}
}
}
// No subnet found.
return (Subnet6Ptr());
}
bool
CfgSubnets6::isDuplicate(const Subnet6& subnet) const {
for (Subnet6Collection::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_SUBNETS6_H
#define CFG_SUBNETS6_H
#include <asiolink/io_address.h>
#include <dhcp/option.h>
#include <dhcpsrv/subnet.h>
#include <dhcpsrv/subnet_selector.h>
#include <util/optional_value.h>
#include <boost/shared_ptr.hpp>
namespace isc {
namespace dhcp {
/// @brief Holds subnets configured for the DHCPv6 server.
///
/// This class holds a collection of subnets configured for the DHCPv6 server.
/// It allows for retrieving a subnet for the particular client using various
/// parameters extracted from the DHCPv6 message. These parameters must be
/// assigned to the appropriate members of the @c SubnetSelector structure.
///
/// See @c CfgSubnets6::selectSubnet documentation for more details on how the subnet
/// is selected for the client.
class CfgSubnets6 {
public:
/// @brief Adds new subnet to the configuration.
///
/// @param subnet Pointer to the subnet being added.
///
/// @throw isc::DuplicateSubnetID If the subnet id for the new subnet
/// duplicates id of an existing subnet.
void add(const Subnet6Ptr& subnet);
/// @brief Returns pointer to the collection of all IPv6 subnets.
///
/// This is used in a hook (subnet6_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 Subnet6 collection
const Subnet6Collection* getAll() const {
return (&subnets_);
}
/// @brief Selects a subnet using parameters specified in the selector.
///
/// 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 relay agent link address is set to zero it is assumed that
/// the subnet is selected for the directly connected client.
/// In this case it is checked if there is any subnet associated with the
/// interface over which the message has been received. If there is no
/// subnet explicitly associated with this interface the client's address
/// will be used to check if the address is in range with any of the
/// subnets.
///
/// If the message was relayed it is possible that the relay agent has
/// appended an Interface ID option. If this option is present, the method
/// will check if it matches with any explicitly specified interface id
/// for any subnet. If it does, the subnet is returned. Otherwise, the
/// relay agents link address is used to select the subnet. In this case,
/// the method will first check if this link address is explicitly
/// associated with any subnet. If not, it is checked if the link address
/// is in range with any of the subnets.
///
/// @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.
Subnet6Ptr selectSubnet(const SubnetSelector& selector) const;
/// @brief Selects the subnet using a specified address.
///
/// This method searches for the subnet using the specified address. If
/// the specified address is a link address on the relay agent (which is
/// indicated by the 3rd argument) the method will first try to match the
/// specified address with the relay addresses explicitly specified for
/// existing subnets. If no match is found, the method will check if the
/// address is in range with any of the subnets.
///
/// If the address is not a relay agent link address (@c is_relay_address
/// is set to false), the method will simply check if the address is in
/// range with any of the subnets.
///
/// @param address Address for which the subnet is searched.
/// @param client_classes Optional parameter specifying the classes that
/// the client belongs to.
/// @param is_relay_address Specifies if the provided address is an
/// address of the relay agent (true) or not (false).
///
/// @return Pointer to the selected subnet or NULL if no subnet found.
Subnet6Ptr
selectSubnet(const asiolink::IOAddress& address,
const ClientClasses& client_classes = ClientClasses(),
const bool is_relay_address = false) const;
private:
/// @brief Selects a subnet using the interface name.
///
/// This method searches for the subnet using the name of the interface.
/// If any of the subnets is explicitly associated with the interface
/// name, the subnet is returned.
///
/// @param iface_name Interface name.
/// @param client_classes Optional parameter specifying the classes that
/// the client belongs to.
///
/// @return Pointer to the selected subnet or NULL if no subnet found.
Subnet6Ptr
selectSubnet(const std::string& iface_name,
const ClientClasses& client_classes) const;
/// @brief Selects a subnet using Interface ID option.
///
/// This method searches for the subnet using the Interface ID option
/// inserted by the relay agent to the message from a client. If any
/// of the subnets is explicitly associated with that interface id, the
/// subnet is returned.
///
/// @param interface_id An instance of the Interface ID option received
/// from the client.
/// @param client_classes Optional parameter specifying the classes that
/// the client belongs to.
///
/// @return Pointer to the selected subnet or NULL if no subnet found.
Subnet6Ptr
selectSubnet(const OptionPtr& interface_id,
const ClientClasses& client_classes) const;
/// @brief Checks that the IPv6 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 Subnet6& subnet) const;
/// @brief A container for IPv6 subnets.
Subnet6Collection subnets_;
};
/// @name Pointer to the @c CfgSubnets6 objects.
//@{
/// @brief Non-const pointer.
typedef boost::shared_ptr<CfgSubnets6> CfgSubnets6Ptr;
/// @brief Const pointer.
typedef boost::shared_ptr<const CfgSubnets6> ConstCfgSubnets6Ptr;
//@}
}
}
#endif // CFG_SUBNETS6_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.
#ifndef SUBNET_SELECTOR_H
#define SUBNET_SELECTOR_H
#include <asiolink/io_address.h>
#include <dhcp/classify.h>
#include <dhcp/option.h>
#include <util/optional_value.h>
#include <string>
namespace isc {
namespace dhcp {
/// @brief Subnet selector used to specify parameters used to select a subnet.
///
/// This structure holds various parameters extracted from a packet sent
/// by a DHCP client used to select the subnet for the client. This selector
/// is common for IPv4 and IPv6 subnets.
struct SubnetSelector {
/// @name DHCPv4 specific parameters.
//@{
/// @brief ciaddr from the client's message.
asiolink::IOAddress ciaddr_;
/// @brief giaddr from the client's message.
asiolink::IOAddress giaddr_;
//@}
/// @name DHCPv6 specific parameters.
//@{
/// @brief Interface id option.
OptionPtr interface_id_;
/// @brief First relay link address.
asiolink::IOAddress first_relay_linkaddr_;
//@}
/// @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.
ClientClasses client_classes_;
/// @brief Name of the interface on which the message was received.
std::string iface_name_;
/// @brief Default constructor.
///
/// Sets the default values for the @c Selector.
SubnetSelector()
: ciaddr_(asiolink::IOAddress("0.0.0.0")),
giaddr_(asiolink::IOAddress("0.0.0.0")),
interface_id_(),
first_relay_linkaddr_(asiolink::IOAddress("::")),
local_address_(asiolink::IOAddress("0.0.0.0"), false),
remote_address_(asiolink::IOAddress("0.0.0.0"), false),
client_classes_(), iface_name_(std::string()) {
}
};
}
}
#endif // SUBNET_SELECTOR_H
......@@ -59,6 +59,7 @@ 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 += cfg_subnets6_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/dhcp6.h>
#include <dhcp/option.h>
#include <dhcpsrv/cfg_subnets6.h>
#include <dhcpsrv/subnet.h>
#include <dhcpsrv/subnet_id.h>
#include <dhcpsrv/subnet_selector.h>
#include <gtest/gtest.h>
#include <string>
using namespace isc;
using namespace isc::asiolink;
using namespace isc::dhcp;
namespace {
/// @brief Generates interface id option.
///
/// @param text Interface id in a textual format.
OptionPtr
generateInterfaceId(const std::string& text) {
OptionBuffer buffer(text.begin(), text.end());
return OptionPtr(new Option(Option::V6, D6O_INTERFACE_ID, buffer));
}
// This test checks that the subnet can be selected using a relay agent's
// link address.
TEST(CfgSubnets6Test, selectSubnetByRelayAddress) {
CfgSubnets6 cfg;
// Let's configure 3 subnets
Subnet6Ptr subnet1(new Subnet6(IOAddress("2001:db8:1::"), 48, 1, 2, 3, 4));
Subnet6Ptr subnet2(new Subnet6(IOAddress("2001:db8:2::"), 48, 1, 2, 3, 4));
Subnet6Ptr subnet3(new Subnet6(IOAddress("2001:db8:3::"), 48, 1, 2, 3, 4));
cfg.add(subnet1);
cfg.add(subnet2);
cfg.add(subnet3);
// Make sure that none of the subnets is selected when there is no relay
// information configured for them.
SubnetSelector selector;
selector.first_relay_linkaddr_ = IOAddress("2001:db8:ff::1");
EXPECT_FALSE(cfg.selectSubnet(selector));
selector.first_relay_linkaddr_ = IOAddress("2001:db8:ff::2");
EXPECT_FALSE(cfg.selectSubnet(selector));
selector.first_relay_linkaddr_ = IOAddress("2001:db8:ff::3");
EXPECT_FALSE(cfg.selectSubnet(selector));
// Now specify relay information.
subnet1->setRelayInfo(IOAddress("2001:db8:ff::1"));
subnet2->setRelayInfo(IOAddress("2001:db8:ff::2"));
subnet3->setRelayInfo(IOAddress("2001:db8:ff::3"));
// And try again. This time relay-info is there and should match.
selector.first_relay_linkaddr_ = IOAddress("2001:db8:ff::1");
EXPECT_EQ(subnet1, cfg.selectSubnet(selector));
selector.first_relay_linkaddr_ = IOAddress("2001:db8:ff::2");
EXPECT_EQ(subnet2, cfg.selectSubnet(selector));
selector.first_relay_linkaddr_ = IOAddress("2001:db8:ff::3");
EXPECT_EQ(subnet3, cfg.selectSubnet(selector));
}
// This test checks that the subnet can be selected using an interface
// name associated with a asubnet.
TEST(CfgSubnets6Test, selectSubnetByInterfaceName) {
CfgSubnets6 cfg;
// Let's create 3 subnets.
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));
subnet1->setIface("foo");
subnet2->setIface("bar");
subnet3->setIface("foobar");
// Until subnets are added to the configuration, there should be nothing
// returned.
SubnetSelector selector;
selector.iface_name_ = "foo";
EXPECT_FALSE(cfg.selectSubnet(selector));
// Add one of the subnets.
cfg.add(subnet1);
// The subnet should be now selected for the interface name "foo".
EXPECT_EQ(subnet1, cfg.selectSubnet(selector));
// Check that the interface name is checked even when there is
// only one subnet defined: there should be nothing returned when
// other interface name is specified.
selector.iface_name_ = "bar";
EXPECT_FALSE(cfg.selectSubnet(selector));
// Add other subnets.
cfg.add(subnet2);
cfg.add(subnet3);
// When we specify correct interface names, the subnets should be returned.
selector.iface_name_ = "foobar";
EXPECT_EQ(subnet3, cfg.selectSubnet(selector));
selector.iface_name_ = "bar";
EXPECT_EQ(subnet2, cfg.selectSubnet(selector));
// When specifying a non-existing interface the subnet should not be
// returned.
selector.iface_name_ = "xyzzy";
EXPECT_FALSE(cfg.selectSubnet(selector));
}
// This test checks that the subnet can be selected using an Interface ID
// option inserted by a relay.
TEST(CfgSubnets6Test, selectSubnetByInterfaceId) {
CfgSubnets6 cfg;
// Create 3 subnets.
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));
// Create Interface-id options used in subnets 1,2, and 3
OptionPtr ifaceid1 = generateInterfaceId("relay1.eth0");
OptionPtr ifaceid2 = generateInterfaceId("VL32");
// That's a strange interface-id, but this is a real life example
OptionPtr ifaceid3 = generateInterfaceId("ISAM144|299|ipv6|nt:vp:1:110");
// Bogus interface-id.
OptionPtr ifaceid_bogus = generateInterfaceId("non-existent");
// Assign interface ids to the respective subnets.
subnet1->setInterfaceId(ifaceid1);
subnet2->setInterfaceId(ifaceid2);
subnet3->setInterfaceId(ifaceid3);
// There shouldn't be any subnet configured at this stage.
SubnetSelector selector;
selector.interface_id_ = ifaceid1;
// Note that some link address must be specified to indicate that it is
// a relayed message!
selector.first_relay_linkaddr_ = IOAddress("5000::1");
EXPECT_FALSE(cfg.selectSubnet(selector));
// Add one of the subnets.
cfg.add(subnet1);
// If only one subnet has been specified, it should be returned when the
// interface id matches. But, for a different interface id there should be
// no match.
EXPECT_EQ(subnet1, cfg.selectSubnet(selector));
selector.interface_id_ = ifaceid2;
EXPECT_FALSE(cfg.selectSubnet(selector));
// Add other subnets.
cfg.add(subnet2);
cfg.add(subnet3);
// Now that we have all subnets added. we should be able to retrieve them
// using appropriate interface ids.
selector.interface_id_ = ifaceid3;
EXPECT_EQ(subnet3, cfg.selectSubnet(selector));
selector.interface_id_ = ifaceid2;
EXPECT_EQ(subnet2, cfg.selectSubnet(selector));
// For invalid interface id, there should be nothing returned.
selector.interface_id_ = ifaceid_bogus;
EXPECT_FALSE(cfg.selectSubnet(selector));
}
// Test that the client classes are considered when the subnet is selected by
// the relay link address.