Browse Source

[master] Merge branch 'trac4308' (interface selection based on name)

Tomek Mrugalski 8 years ago
parent
commit
7c74ecdbb6

+ 52 - 4
src/lib/dhcpsrv/cfg_subnets4.cc

@@ -1,4 +1,4 @@
-// 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
@@ -100,7 +100,7 @@ CfgSubnets4::selectSubnet(const SubnetSelector& selector) const {
                 continue;
             }
 
-            // Eliminate those subnets that do not meet client class criteria.
+            // If a subnet meets the client class criteria return it.
             if ((*subnet)->clientSupported(selector.client_classes_)) {
                 return (*subnet);
             }
@@ -138,7 +138,20 @@ CfgSubnets4::selectSubnet(const SubnetSelector& selector) const {
                       << " doesn't exist and therefore it is impossible"
                       " to find a suitable subnet for its IPv4 address");
         }
-        iface->getAddress4(address);
+
+        // Attempt to select subnet based on the interface name.
+        Subnet4Ptr subnet = selectSubnet(selector.iface_name_,
+                                         selector.client_classes_);
+
+        // If it matches - great. If not, we'll try to use a different
+        // selection criteria below.
+        if (subnet) {
+            return (subnet);
+        } else {
+            // Let's try to get an address from the local interface and
+            // try to match it to defined subnet.
+            iface->getAddress4(address);
+        }
     }
 
     // Unable to find a suitable address to use for subnet selection.
@@ -152,6 +165,38 @@ CfgSubnets4::selectSubnet(const SubnetSelector& selector) const {
 }
 
 Subnet4Ptr
+CfgSubnets4::selectSubnet(const std::string& iface,
+                 const ClientClasses& client_classes) const {
+    for (Subnet4Collection::const_iterator subnet = subnets_.begin();
+         subnet != subnets_.end(); ++subnet) {
+
+        // If there's no interface specified for this subnet, proceed to
+        // the next subnet.
+        if ((*subnet)->getIface().empty()) {
+            continue;
+        }
+
+        // If it's specified, but does not match, proceed to the next
+        // subnet.
+        if ((*subnet)->getIface() != iface) {
+            continue;
+        }
+
+        // If a subnet meets the client class criteria return it.
+        if ((*subnet)->clientSupported(client_classes)) {
+            LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE,
+                      DHCPSRV_CFGMGR_SUBNET4_IFACE)
+                .arg((*subnet)->toText())
+                .arg(iface);
+            return (*subnet);
+        }
+    }
+
+    // Failed to find a subnet.
+    return (Subnet4Ptr());
+}
+
+Subnet4Ptr
 CfgSubnets4::selectSubnet(const IOAddress& address,
                  const ClientClasses& client_classes) const {
     for (Subnet4Collection::const_iterator subnet = subnets_.begin();
@@ -162,8 +207,11 @@ CfgSubnets4::selectSubnet(const IOAddress& address,
             continue;
         }
 
-        // Eliminate those subnets that do not meet client class criteria.
+        // If a subnet meets the client class criteria return it.
         if ((*subnet)->clientSupported(client_classes)) {
+            LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_SUBNET4_ADDR)
+                .arg((*subnet)->toText())
+                .arg(address.toText());
             return (*subnet);
         }
     }

+ 27 - 3
src/lib/dhcpsrv/cfg_subnets4.h

@@ -1,4 +1,4 @@
-// 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
@@ -47,7 +47,7 @@ public:
         return (&subnets_);
     }
 
-    /// @brief Returns pointer to the selected subnet.
+    /// @brief Returns a 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
@@ -96,7 +96,7 @@ public:
     /// or they are insufficient to select a subnet.
     Subnet4Ptr selectSubnet(const SubnetSelector& selector) const;
 
-    /// @brief Returns pointer to a subnet if provided address is in its range.
+    /// @brief Returns a pointer to a subnet if provided address is in its range.
     ///
     /// This method returns a pointer to the subnet if the address passed in
     /// parameter is in range with this subnet. This is mainly used for unit
@@ -120,6 +120,30 @@ public:
                             const ClientClasses& client_classes
                             = ClientClasses()) const;
 
+    /// @brief Returns a pointer to a subnet if provided interface name matches.
+    ///
+    /// This method returns a pointer to the subnet if the interface name passed
+    /// in parameter iface matches that of a subnet. This is mainly used for matching
+    /// local incoming traffic, even when the addresses on local interfaces do
+    /// not match a subnet definition. This method is also called by the
+    /// @c selectSubnet(SubnetSelector).
+    ///
+    /// @todo This method requires performance improvement! It currently
+    /// iterates over all existing subnets to find the one which fulfils
+    /// the search criteria. The subnet storage is implemented as a simple
+    /// STL vector which precludes fast searches using specific keys.
+    /// Hence, full scan is required. To improve the search performance a
+    /// different container type is required, e.g. multi-index container,
+    /// or something of a similar functionality.
+    ///
+    /// @param iface name of the interface to be matched.
+    /// @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.
+    Subnet4Ptr selectSubnet(const std::string& iface,
+                            const ClientClasses& client_classes) const;
+
     /// @brief Attempts to do subnet selection based on DHCP4o6 information
     ///
     /// The algorithm implemented is as follows:

+ 13 - 0
src/lib/dhcpsrv/dhcpsrv_messages.mes

@@ -102,6 +102,19 @@ This is a debug message reporting that the DHCP configuration manager has
 returned the specified IPv4 subnet when given the address hint specified
 as the address is within the subnet.
 
+% DHCPSRV_CFGMGR_SUBNET4_ADDR selected subnet %1 for packet received by matching address %2
+This is a debug message reporting that the DHCP configuration manager
+has returned the specified IPv4 subnet for a received packet. This particular
+subnet was selected, because an IPv4 address was matched which belonged to that
+subnet.
+
+% DHCPSRV_CFGMGR_SUBNET4_IFACE selected subnet %1 for packet received over interface %2
+This is a debug message reporting that the DHCP configuration manager
+has returned the specified IPv4 subnet for a packet received over
+the given interface.  This particular subnet was selected, because it
+was specified as being directly reachable over the given interface. (see
+'interface' parameter in the subnet4 definition).
+
 % DHCPSRV_CFGMGR_SUBNET4_RELAY selected subnet %1, because of matching relay addr %2
 This is a debug message reporting that the DHCP configuration manager has
 returned the specified IPv4 subnet, because detected relay agent address

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

@@ -61,6 +61,42 @@ TEST(CfgSubnets4Test, selectSubnetByCiaddr) {
     EXPECT_FALSE(cfg.selectSubnet(selector));
 }
 
+// This test verifies that it is possible to select a subnet by
+// matching an interface name.
+TEST(CfgSubnets4Test, selectSubnetByIface) {
+    // The IfaceMgrTestConfig object initializes fake interfaces:
+    // eth0, eth1 and lo on the configuration manager. The CfgSubnets4
+    // object uses interface names to select the appropriate subnet.
+    IfaceMgrTestConfig config(true);
+
+    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));
+    // No interface defined for subnet1
+    subnet2->setIface("lo");
+    subnet3->setIface("eth1");
+
+    cfg.add(subnet1);
+    cfg.add(subnet2);
+    cfg.add(subnet3);
+
+    // Make sure that initially the subnets don't exist.
+    SubnetSelector selector;
+    // Set an interface to a name that is not defined in the config.
+    // Subnet selection should fail.
+    selector.iface_name_ = "eth0";
+    ASSERT_FALSE(cfg.selectSubnet(selector));
+
+    // Now select an interface name that matches. Selection should succeed
+    // and return subnet3.
+    selector.iface_name_ = "eth1";
+    Subnet4Ptr selected = cfg.selectSubnet(selector);
+    ASSERT_TRUE(selected);
+    EXPECT_EQ(subnet3, selected);
+}
 
 // This test verifies that when the classification information is specified for
 // subnets, the proper subnets are returned by the subnet configuration.