From: Tomek Mrugalski Date: Sun, 1 Nov 2015 03:29:34 +0000 (+0900) Subject: [4112] selectSubnet4o6 implemented + one unit-test X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=refs%2Fheads%2Ftrac4112;p=thirdparty%2Fkea.git [4112] selectSubnet4o6 implemented + one unit-test --- diff --git a/src/lib/dhcpsrv/Makefile.am b/src/lib/dhcpsrv/Makefile.am index 56efc8bbb2..fd63884dfc 100644 --- a/src/lib/dhcpsrv/Makefile.am +++ b/src/lib/dhcpsrv/Makefile.am @@ -78,6 +78,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_hosts.cc cfg_hosts.h libkea_dhcpsrv_la_SOURCES += cfg_iface.cc cfg_iface.h libkea_dhcpsrv_la_SOURCES += cfg_expiration.cc cfg_expiration.h diff --git a/src/lib/dhcpsrv/cfg_4o6.h b/src/lib/dhcpsrv/cfg_4o6.h new file mode 100644 index 0000000000..3a6215f8ff --- /dev/null +++ b/src/lib/dhcpsrv/cfg_4o6.h @@ -0,0 +1,107 @@ +// Copyright (C) 2015 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_4OVER6_H +#define CFG_4OVER6_H + +#include +#include + +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 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 subnet4o6_; + + /// Specifies the v6 interface-id used for v4 subnet selection. + OptionPtr interface_id_; +}; + +} // end of isc::dhcp namespace +} // end of isc namespace + +#endif diff --git a/src/lib/dhcpsrv/cfg_subnets4.cc b/src/lib/dhcpsrv/cfg_subnets4.cc index f46dd84f62..919cc13d24 100644 --- a/src/lib/dhcpsrv/cfg_subnets4.cc +++ b/src/lib/dhcpsrv/cfg_subnets4.cc @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include using namespace isc::asiolink; @@ -37,8 +39,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 pref = cfg4o6.getSubnet4o6(); + if (pref.first != IOAddress::IPV6_ZERO_ADDRESS()) { + + // 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_, diff --git a/src/lib/dhcpsrv/cfg_subnets4.h b/src/lib/dhcpsrv/cfg_subnets4.h index af7d725e97..a665ce27d7 100644 --- a/src/lib/dhcpsrv/cfg_subnets4.h +++ b/src/lib/dhcpsrv/cfg_subnets4.h @@ -155,6 +155,27 @@ private: /// @return true if the duplicate subnet exists. bool isDuplicate(const Subnet4& subnet) 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 + /// @return selected IPv4 subnet + Subnet4Ptr + selectSubnet4o6(const SubnetSelector& selector) const; + + /// @brief A container for IPv4 subnets. Subnet4Collection subnets_; diff --git a/src/lib/dhcpsrv/subnet.h b/src/lib/dhcpsrv/subnet.h index 54ce6a6ee0..db74ccd409 100644 --- a/src/lib/dhcpsrv/subnet.h +++ b/src/lib/dhcpsrv/subnet.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -507,82 +508,6 @@ private: /// @brief A generic pointer to either Subnet4 or Subnet6 object typedef boost::shared_ptr 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 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 subnet4o6_; - - /// Specifies the v6 interface-id used for v4 subnet selection. - OptionPtr interface_id_; -}; /// @brief A configuration holder for IPv4 subnet. /// diff --git a/src/lib/dhcpsrv/subnet_selector.h b/src/lib/dhcpsrv/subnet_selector.h index cfcdc5fc36..b848f360ba 100644 --- a/src/lib/dhcpsrv/subnet_selector.h +++ b/src/lib/dhcpsrv/subnet_selector.h @@ -56,6 +56,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. @@ -67,7 +70,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) { } }; diff --git a/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc b/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc index 956557463b..ad05cdada7 100644 --- a/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc @@ -326,5 +326,28 @@ 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, 4o6subnet) { + 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)); +} + } // end of anonymous namespace