Parcourir la source

[4112] selectSubnet4o6 implemented + one unit-test

Tomek Mrugalski il y a 9 ans
Parent
commit
38a25c7c22

+ 1 - 0
src/lib/dhcpsrv/Makefile.am

@@ -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 += alloc_engine_log.cc alloc_engine_log.h
 libkea_dhcpsrv_la_SOURCES += base_host_data_source.h
 libkea_dhcpsrv_la_SOURCES += base_host_data_source.h
 libkea_dhcpsrv_la_SOURCES += callout_handle_store.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_db_access.cc cfg_db_access.h
 libkea_dhcpsrv_la_SOURCES += cfg_duid.cc cfg_duid.h
 libkea_dhcpsrv_la_SOURCES += cfg_duid.cc cfg_duid.h
 libkea_dhcpsrv_la_SOURCES += cfg_hosts.cc cfg_hosts.h
 libkea_dhcpsrv_la_SOURCES += cfg_hosts.cc cfg_hosts.h

+ 107 - 0
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 <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

+ 49 - 0
src/lib/dhcpsrv/cfg_subnets4.cc

@@ -9,6 +9,8 @@
 #include <dhcpsrv/cfg_subnets4.h>
 #include <dhcpsrv/cfg_subnets4.h>
 #include <dhcpsrv/dhcpsrv_log.h>
 #include <dhcpsrv/dhcpsrv_log.h>
 #include <dhcpsrv/subnet_id.h>
 #include <dhcpsrv/subnet_id.h>
+#include <dhcpsrv/addr_utilities.h>
+#include <asiolink/io_address.h>
 #include <stats/stats_mgr.h>
 #include <stats/stats_mgr.h>
 
 
 using namespace isc::asiolink;
 using namespace isc::asiolink;
@@ -30,7 +32,54 @@ CfgSubnets4::add(const Subnet4Ptr& subnet) {
 }
 }
 
 
 Subnet4Ptr
 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 != 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 {
 CfgSubnets4::selectSubnet(const SubnetSelector& selector) const {
+
+    if (selector.dhcp4o6_) {
+        return selectSubnet4o6(selector);
+    }
+
     // First use RAI link select sub-option or subnet select option
     // First use RAI link select sub-option or subnet select option
     if (!selector.option_select_.isV4Zero()) {
     if (!selector.option_select_.isV4Zero()) {
         return (selectSubnet(selector.option_select_,
         return (selectSubnet(selector.option_select_,

+ 21 - 0
src/lib/dhcpsrv/cfg_subnets4.h

@@ -147,6 +147,27 @@ private:
     /// @return true if the duplicate subnet exists.
     /// @return true if the duplicate subnet exists.
     bool isDuplicate(const Subnet4& subnet) const;
     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.
     /// @brief A container for IPv4 subnets.
     Subnet4Collection subnets_;
     Subnet4Collection subnets_;
 
 

+ 1 - 76
src/lib/dhcpsrv/subnet.h

@@ -12,6 +12,7 @@
 #include <dhcp/classify.h>
 #include <dhcp/classify.h>
 #include <dhcp/option_space_container.h>
 #include <dhcp/option_space_container.h>
 #include <dhcpsrv/cfg_option.h>
 #include <dhcpsrv/cfg_option.h>
+#include <dhcpsrv/cfg_4o6.h>
 #include <dhcpsrv/pool.h>
 #include <dhcpsrv/pool.h>
 #include <dhcpsrv/triplet.h>
 #include <dhcpsrv/triplet.h>
 #include <dhcpsrv/lease.h>
 #include <dhcpsrv/lease.h>
@@ -499,82 +500,6 @@ private:
 /// @brief A generic pointer to either Subnet4 or Subnet6 object
 /// @brief A generic pointer to either Subnet4 or Subnet6 object
 typedef boost::shared_ptr<Subnet> SubnetPtr;
 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.
 /// @brief A configuration holder for IPv4 subnet.
 ///
 ///

+ 5 - 1
src/lib/dhcpsrv/subnet_selector.h

@@ -48,6 +48,9 @@ struct SubnetSelector {
     /// @brief Name of the interface on which the message was received.
     /// @brief Name of the interface on which the message was received.
     std::string iface_name_;
     std::string iface_name_;
 
 
+    /// @brief Specifies if the packet is DHCP4o6
+    bool dhcp4o6_;
+
     /// @brief Default constructor.
     /// @brief Default constructor.
     ///
     ///
     /// Sets the default values for the @c Selector.
     /// Sets the default values for the @c Selector.
@@ -59,7 +62,8 @@ struct SubnetSelector {
           first_relay_linkaddr_(asiolink::IOAddress("::")),
           first_relay_linkaddr_(asiolink::IOAddress("::")),
           local_address_(asiolink::IOAddress("0.0.0.0")),
           local_address_(asiolink::IOAddress("0.0.0.0")),
           remote_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) {
     }
     }
 };
 };
 
 

+ 23 - 0
src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc

@@ -318,5 +318,28 @@ TEST(CfgSubnets4Test, duplication) {
     EXPECT_THROW(cfg.add(subnet3), isc::dhcp::DuplicateSubnetID);
     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
 } // end of anonymous namespace