Commit 33526859 authored by Tomek Mrugalski's avatar Tomek Mrugalski 🛰

[master] Merge branch 'trac4112' (subnet4o6 selection based on v6 info)

parents 324d7664 df2b27cd
......@@ -85,6 +85,7 @@ libkea_dhcpsrv_la_SOURCES += alloc_engine.cc alloc_engine.h
libkea_dhcpsrv_la_SOURCES += alloc_engine_log.cc alloc_engine_log.h
libkea_dhcpsrv_la_SOURCES += base_host_data_source.h
libkea_dhcpsrv_la_SOURCES += callout_handle_store.h
libkea_dhcpsrv_la_SOURCES += cfg_4o6.h
libkea_dhcpsrv_la_SOURCES += cfg_db_access.cc cfg_db_access.h
libkea_dhcpsrv_la_SOURCES += cfg_duid.cc cfg_duid.h
libkea_dhcpsrv_la_SOURCES += cfg_hosts.cc cfg_hosts.h
......
// Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef CFG_4OVER6_H
#define CFG_4OVER6_H
#include <string>
#include <asiolink/io_address.h>
namespace isc {
namespace dhcp {
/// @brief This structure contains information about DHCP4o6 (RFC7341)
///
/// DHCP4o6 is completely optional. If it is not enabled, this structure
/// does not contain any information.
struct Cfg4o6 {
/// the default constructor.
///
/// Initializes fields to their default value.
Cfg4o6()
:enabled_(false), subnet4o6_(std::make_pair(asiolink::IOAddress("::"), 128u)) {
}
/// @brief Returns whether the DHCP4o6 is enabled or not.
/// @return true if enabled
bool enabled() const {
return (enabled_);
}
/// @brief Sets the DHCP4o6 enabled status.
/// @param enabled specifies if the DHCP4o6 should be enabled or not
void enabled(bool enabled) {
enabled_ = enabled;
}
/// @brief Returns the DHCP4o6 interface.
/// @return value of the 4o6-interface parameter.
std::string getIface4o6() const {
return (iface4o6_);
}
/// @brief Sets the 4o6-interface.
/// @param iface name of the network interface the 4o6 traffic is received on
void setIface4o6(const std::string& iface) {
iface4o6_ = iface;
enabled_ = true;
}
/// @brief Returns prefix/len for the IPv6 subnet.
/// @return prefix/length pair
std::pair<asiolink::IOAddress, uint8_t> getSubnet4o6() const {
return (subnet4o6_);
}
/// @brief Sets the prefix/length information (content of the 4o6-subnet).
/// @param subnet IOAddress that represents a prefix
/// @param prefix specifies prefix length
void setSubnet4o6(const asiolink::IOAddress& subnet, uint8_t prefix) {
subnet4o6_ = std::make_pair(subnet, prefix);
enabled_ = true;
}
/// @brief Returns the interface-id.
/// @return the option representing interface-id (or NULL)
OptionPtr getInterfaceId() const {
return (interface_id_);
}
/// @brief Sets the interface-id
/// @param opt option to be used as interface-id match
void setInterfaceId(const OptionPtr& opt) {
interface_id_ = opt;
enabled_ = true;
}
private:
/// Specifies if 4o6 is enabled on this subnet.
bool enabled_;
/// Specifies the network interface used as v4 subnet selector.
std::string iface4o6_;
/// Specifies the IPv6 subnet used for v4 subnet selection.
std::pair<asiolink::IOAddress, uint8_t> subnet4o6_;
/// Specifies the v6 interface-id used for v4 subnet selection.
OptionPtr interface_id_;
};
} // end of isc::dhcp namespace
} // end of isc namespace
#endif
......@@ -9,6 +9,8 @@
#include <dhcpsrv/cfg_subnets4.h>
#include <dhcpsrv/dhcpsrv_log.h>
#include <dhcpsrv/subnet_id.h>
#include <dhcpsrv/addr_utilities.h>
#include <asiolink/io_address.h>
#include <stats/stats_mgr.h>
using namespace isc::asiolink;
......@@ -29,8 +31,55 @@ CfgSubnets4::add(const Subnet4Ptr& subnet) {
subnets_.push_back(subnet);
}
Subnet4Ptr
CfgSubnets4::selectSubnet4o6(const SubnetSelector& selector) const {
for (Subnet4Collection::const_iterator subnet = subnets_.begin();
subnet != subnets_.end(); ++subnet) {
Cfg4o6& cfg4o6 = (*subnet)->get4o6();
// Is this an 4o6 subnet at all?
if (!cfg4o6.enabled()) {
continue; // No? Let's try the next one.
}
// First match criteria: check if we have a prefix/len defined.
std::pair<asiolink::IOAddress, uint8_t> pref = cfg4o6.getSubnet4o6();
if (!pref.first.isV6Zero()) {
// Let's check if the IPv6 address is in range
IOAddress first = firstAddrInPrefix(pref.first, pref.second);
IOAddress last = lastAddrInPrefix(pref.first, pref.second);
if ((first <= selector.remote_address_) &&
(selector.remote_address_ <= last)) {
return (*subnet);
}
}
// Second match criteria: check if the interface-id matches
if (cfg4o6.getInterfaceId() && selector.interface_id_ &&
cfg4o6.getInterfaceId()->equals(selector.interface_id_)) {
return (*subnet);
}
// Third match criteria: check if the interface name matches
if (!cfg4o6.getIface4o6().empty() && !selector.iface_name_.empty()
&& cfg4o6.getIface4o6() == selector.iface_name_) {
return (*subnet);
}
}
// Ok, wasn't able to find any matching subnet.
return (Subnet4Ptr());
}
Subnet4Ptr
CfgSubnets4::selectSubnet(const SubnetSelector& selector) const {
if (selector.dhcp4o6_) {
return selectSubnet4o6(selector);
}
// First use RAI link select sub-option or subnet select option
if (!selector.option_select_.isV4Zero()) {
return (selectSubnet(selector.option_select_,
......
......@@ -120,6 +120,28 @@ public:
const ClientClasses& client_classes
= ClientClasses()) const;
/// @brief Attempts to do subnet selection based on DHCP4o6 information
///
/// The algorithm implemented is as follows:
///
/// - First: try to match IPv6 subnet (4o6-subnet parameter) with the
/// remote IPv6 address of the incoming packet
/// - Second: try to match interface-id (4o6-interface-id parameter)
/// with the interface-id option in the incoming 4o6 packet
/// - Third: try to match interface-name (4o6-interface parameter)
/// with the name of the interface the incoming 4o6 packet was
/// received over.
///
/// @todo: Add additional selection criteria. See
/// http://kea.isc.org/wiki/ISC-DHCP4o6-Design for details.
///
/// @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.
Subnet4Ptr
selectSubnet4o6(const SubnetSelector& selector) const;
/// @brief Updates statistics.
///
/// This method updates statistics that are affected by the newly committed
......
......@@ -12,6 +12,7 @@
#include <dhcp/classify.h>
#include <dhcp/option_space_container.h>
#include <dhcpsrv/cfg_option.h>
#include <dhcpsrv/cfg_4o6.h>
#include <dhcpsrv/pool.h>
#include <dhcpsrv/triplet.h>
#include <dhcpsrv/lease.h>
......@@ -499,82 +500,6 @@ private:
/// @brief A generic pointer to either Subnet4 or Subnet6 object
typedef boost::shared_ptr<Subnet> SubnetPtr;
/// @brief This structure contains information about DHCP4o6 (RFC7341)
///
/// DHCP4o6 is completely optional. If it is not enabled, this structure
/// does not contain any information.
struct Cfg4o6 {
/// the default constructor.
///
/// Initializes fields to their default value.
Cfg4o6()
:enabled_(false), subnet4o6_(std::make_pair(asiolink::IOAddress("::"), 128u)) {
}
/// @brief Returns whether the DHCP4o6 is enabled or not.
/// @return true if enabled
bool enabled() const {
return (enabled_);
}
/// @brief Sets the DHCP4o6 enabled status.
/// @param enabled specifies if the DHCP4o6 should be enabled or not
void enabled(bool enabled) {
enabled_ = enabled;
}
/// @brief Returns the DHCP4o6 interface.
/// @return value of the 4o6-interface parameter.
std::string getIface4o6() const {
return (iface4o6_);
}
/// @brief Sets the 4o6-interface.
/// @param iface name of the network interface the 4o6 traffic is received on
void setIface4o6(const std::string& iface) {
iface4o6_ = iface;
}
/// @brief Returns prefix/len for the IPv6 subnet.
/// @return prefix/length pair
std::pair<asiolink::IOAddress, uint8_t> getSubnet4o6() const {
return (subnet4o6_);
}
/// @brief Sets the prefix/length information (content of the 4o6-subnet).
/// @param subnet IOAddress that represents a prefix
/// @param prefix specifies prefix length
void setSubnet4o6(const asiolink::IOAddress& subnet, uint8_t prefix) {
subnet4o6_ = std::make_pair(subnet, prefix);
}
/// @brief Returns the interface-id.
/// @return the option representing interface-id (or NULL)
OptionPtr getInterfaceId() const {
return (interface_id_);
}
/// @brief Sets the interface-id
/// @param opt option to be used as interface-id match
void setInterfaceId(const OptionPtr& opt) {
interface_id_ = opt;
}
private:
/// Specifies if 4o6 is enabled on this subnet.
bool enabled_;
/// Specifies the network interface used as v4 subnet selector.
std::string iface4o6_;
/// Specifies the IPv6 subnet used for v4 subnet selection.
std::pair<asiolink::IOAddress, uint8_t> subnet4o6_;
/// Specifies the v6 interface-id used for v4 subnet selection.
OptionPtr interface_id_;
};
/// @brief A configuration holder for IPv4 subnet.
///
......
......@@ -48,6 +48,9 @@ struct SubnetSelector {
/// @brief Name of the interface on which the message was received.
std::string iface_name_;
/// @brief Specifies if the packet is DHCP4o6
bool dhcp4o6_;
/// @brief Default constructor.
///
/// Sets the default values for the @c Selector.
......@@ -59,7 +62,8 @@ struct SubnetSelector {
first_relay_linkaddr_(asiolink::IOAddress("::")),
local_address_(asiolink::IOAddress("0.0.0.0")),
remote_address_(asiolink::IOAddress("0.0.0.0")),
client_classes_(), iface_name_(std::string()) {
client_classes_(), iface_name_(std::string()),
dhcp4o6_(false) {
}
};
......
// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
......@@ -12,6 +12,7 @@
#include <dhcpsrv/subnet_id.h>
#include <dhcpsrv/subnet_selector.h>
#include <gtest/gtest.h>
#include <vector>
using namespace isc;
using namespace isc::asiolink;
......@@ -318,5 +319,88 @@ TEST(CfgSubnets4Test, duplication) {
EXPECT_THROW(cfg.add(subnet3), isc::dhcp::DuplicateSubnetID);
}
// This test checks if the IPv4 subnet can be selected based on the IPv6 address.
TEST(CfgSubnets4Test, 4o6subnetMatchByAddress) {
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, 125));
subnet2->get4o6().setSubnet4o6(IOAddress("2001:db8:1::"), 48);
subnet3->get4o6().setSubnet4o6(IOAddress("2001:db8:2::"), 48);
cfg.add(subnet1);
cfg.add(subnet2);
cfg.add(subnet3);
SubnetSelector selector;
selector.dhcp4o6_ = true;
selector.remote_address_ = IOAddress("2001:db8:1::dead:beef");
EXPECT_EQ(subnet2, cfg.selectSubnet(selector));
}
// This test checks if the IPv4 subnet can be selected based on the value of
// interface-id option.
TEST(CfgSubnets4Test, 4o6subnetMatchByInterfaceId) {
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, 125));
const uint8_t dummyPayload1[] = { 1, 2, 3, 4};
const uint8_t dummyPayload2[] = { 1, 2, 3, 5};
std::vector<uint8_t> data1(dummyPayload1, dummyPayload1 + sizeof(dummyPayload1));
std::vector<uint8_t> data2(dummyPayload2, dummyPayload2 + sizeof(dummyPayload2));
OptionPtr interfaceId1(new Option(Option::V6, D6O_INTERFACE_ID, data1));
OptionPtr interfaceId2(new Option(Option::V6, D6O_INTERFACE_ID, data2));
subnet2->get4o6().setInterfaceId(interfaceId1);
cfg.add(subnet1);
cfg.add(subnet2);
cfg.add(subnet3);
SubnetSelector selector;
selector.dhcp4o6_ = true;
selector.interface_id_ = interfaceId2;
// We have mismatched interface-id options (data1 vs data2). Should not match.
EXPECT_FALSE(cfg.selectSubnet(selector));
// This time we have correct interface-id. Should match.
selector.interface_id_ = interfaceId1;
EXPECT_EQ(subnet2, cfg.selectSubnet(selector));
}
// This test checks if the IPv4 subnet can be selected based on the value of
// interface name option.
TEST(CfgSubnets4Test, 4o6subnetMatchByInterfaceName) {
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, 125));
subnet2->get4o6().setIface4o6("eth7");
cfg.add(subnet1);
cfg.add(subnet2);
cfg.add(subnet3);
SubnetSelector selector;
selector.dhcp4o6_ = true;
selector.iface_name_ = "eth5";
// We have mismatched interface names. Should not match.
EXPECT_FALSE(cfg.selectSubnet(selector));
// This time we have correct names. Should match.
selector.iface_name_ = "eth7";
EXPECT_EQ(subnet2, cfg.selectSubnet(selector));
}
} // end of anonymous namespace
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment