Browse Source

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

Tomek Mrugalski 9 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
 // 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
 // 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;
                 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_)) {
             if ((*subnet)->clientSupported(selector.client_classes_)) {
                 return (*subnet);
                 return (*subnet);
             }
             }
@@ -138,7 +138,20 @@ CfgSubnets4::selectSubnet(const SubnetSelector& selector) const {
                       << " doesn't exist and therefore it is impossible"
                       << " doesn't exist and therefore it is impossible"
                       " to find a suitable subnet for its IPv4 address");
                       " 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.
     // Unable to find a suitable address to use for subnet selection.
@@ -152,6 +165,38 @@ CfgSubnets4::selectSubnet(const SubnetSelector& selector) const {
 }
 }
 
 
 Subnet4Ptr
 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,
 CfgSubnets4::selectSubnet(const IOAddress& address,
                  const ClientClasses& client_classes) const {
                  const ClientClasses& client_classes) const {
     for (Subnet4Collection::const_iterator subnet = subnets_.begin();
     for (Subnet4Collection::const_iterator subnet = subnets_.begin();
@@ -162,8 +207,11 @@ CfgSubnets4::selectSubnet(const IOAddress& address,
             continue;
             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)) {
         if ((*subnet)->clientSupported(client_classes)) {
+            LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, DHCPSRV_CFGMGR_SUBNET4_ADDR)
+                .arg((*subnet)->toText())
+                .arg(address.toText());
             return (*subnet);
             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
 // 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
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -47,7 +47,7 @@ public:
         return (&subnets_);
         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
     /// This method tries to retrieve the subnet for the client using various
     /// parameters extracted from the client's message using the following
     /// parameters extracted from the client's message using the following
@@ -96,7 +96,7 @@ public:
     /// or they are insufficient to select a subnet.
     /// or they are insufficient to select a subnet.
     Subnet4Ptr selectSubnet(const SubnetSelector& selector) const;
     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
     /// 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
     /// parameter is in range with this subnet. This is mainly used for unit
@@ -120,6 +120,30 @@ public:
                             const ClientClasses& client_classes
                             const ClientClasses& client_classes
                             = ClientClasses()) const;
                             = 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
     /// @brief Attempts to do subnet selection based on DHCP4o6 information
     ///
     ///
     /// The algorithm implemented is as follows:
     /// 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
 returned the specified IPv4 subnet when given the address hint specified
 as the address is within the subnet.
 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
 % 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
 This is a debug message reporting that the DHCP configuration manager has
 returned the specified IPv4 subnet, because detected relay agent address
 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));
     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
 // This test verifies that when the classification information is specified for
 // subnets, the proper subnets are returned by the subnet configuration.
 // subnets, the proper subnets are returned by the subnet configuration.