Browse Source

[master] Merge branch 'trac4105' (4o6 configuration parameters)

Tomek Mrugalski 9 years ago
parent
commit
042c5348e4

+ 43 - 0
src/bin/dhcp4/json_config_parser.cc

@@ -200,6 +200,12 @@ protected:
             parser = new OptionDataListParser(config_id, options_, AF_INET);
         } else if (config_id.compare("match-client-id") == 0) {
             parser = new BooleanParser(config_id, boolean_values_);
+        } else if (config_id.compare("4o6-subnet") == 0) {
+            parser = new StringParser(config_id, string_values_);
+        } else if (config_id.compare("4o6-interface") == 0) {
+            parser = new StringParser(config_id, string_values_);
+        } else if (config_id.compare("4o6-interface-id") == 0) {
+            parser = new StringParser(config_id, string_values_);
         } else {
             isc_throw(NotImplemented, "unsupported parameter: " << config_id);
         }
@@ -305,6 +311,43 @@ protected:
                       << ")");
         }
 
+        // Try 4o6 specific parameter: 4o6-interface
+        string iface4o6 = string_values_->getOptionalParam("4o6-interface", "");
+        if (!iface4o6.empty()) {
+            subnet4->get4o6().setIface4o6(iface4o6);
+            subnet4->get4o6().enabled(true);
+        }
+
+        // Try 4o6 specific parameter: 4o6-subnet
+        string subnet4o6 = string_values_->getOptionalParam("4o6-subnet", "");
+        if (!subnet4o6.empty()) {
+            size_t slash = subnet4o6.find("/");
+            if (slash == std::string::npos) {
+                isc_throw(DhcpConfigError, "Missing / in the 4o6-subnet parameter:"
+                          + subnet4o6 +", expected format: prefix6/length");
+            }
+            string prefix = subnet4o6.substr(0, slash);
+            string lenstr = subnet4o6.substr(slash + 1);
+
+            uint8_t len = 128;
+            try {
+                len = boost::lexical_cast<unsigned int>(lenstr.c_str());
+            } catch (const boost::bad_lexical_cast &) {
+                isc_throw(DhcpConfigError, "Invalid prefix length specified in "
+                          "4o6-subnet parameter: " + subnet4o6 + ", expected 0..128 value");
+            }
+            subnet4->get4o6().setSubnet4o6(IOAddress(prefix), len);
+            subnet4->get4o6().enabled(true);
+        }
+
+        // Try 4o6 specific paramter: 4o6-interface-id
+        std::string ifaceid = string_values_->getOptionalParam("4o6-interface-id", "");
+        if (!ifaceid.empty()) {
+            OptionBuffer tmp(ifaceid.begin(), ifaceid.end());
+            OptionPtr opt(new Option(Option::V6, D6O_INTERFACE_ID, tmp));
+            subnet4->get4o6().setInterfaceId(opt);
+            subnet4->get4o6().enabled(true);
+        }
 
         // Try setting up client class (if specified)
         try {

+ 241 - 0
src/bin/dhcp4/tests/config_parser_unittest.cc

@@ -3782,4 +3782,245 @@ TEST_F(Dhcp4ParserTest, expiredLeasesProcessingError) {
     EXPECT_TRUE(errorContainsPosition(status, "<string>"));
 }
 
+
+// Checks if the DHCPv4 is able to parse the configuration without 4o6 parameters
+// and does not set 4o6 fields at all.
+TEST_F(Dhcp4ParserTest, 4o6default) {
+
+    ConstElementPtr status;
+
+    // Just a plain v4 config (no 4o6 parameters)
+    string config = "{ " + genIfaceConfig() + "," +
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet4\": [ { "
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
+        "    \"subnet\": \"192.0.2.0/24\" } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+
+    // check if returned status is OK
+    checkResult(status, 0);
+
+    // Now check if the configuration was indeed handled and we have
+    // expected pool configured.
+    Subnet4Ptr subnet = CfgMgr::instance().getStagingCfg()->
+        getCfgSubnets4()->selectSubnet(IOAddress("192.0.2.200"));
+    ASSERT_TRUE(subnet);
+
+    Cfg4o6& dhcp4o6 = subnet->get4o6();
+    EXPECT_FALSE(dhcp4o6.enabled());
+}
+
+// Checks if the DHCPv4 is able to parse the configuration with 4o6 subnet
+// defined.
+TEST_F(Dhcp4ParserTest, 4o6subnet) {
+
+    ConstElementPtr status;
+
+    // Just a plain v4 config (no 4o6 parameters)
+    string config = "{ " + genIfaceConfig() + "," +
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet4\": [ { "
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
+        "    \"subnet\": \"192.0.2.0/24\","
+        "    \"4o6-subnet\": \"2001:db8::123/45\" } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+
+    // check if returned status is OK
+    checkResult(status, 0);
+
+    // Now check if the configuration was indeed handled and we have
+    // expected pool configured.
+    Subnet4Ptr subnet = CfgMgr::instance().getStagingCfg()->
+        getCfgSubnets4()->selectSubnet(IOAddress("192.0.2.200"));
+    ASSERT_TRUE(subnet);
+
+    Cfg4o6& dhcp4o6 = subnet->get4o6();
+    EXPECT_TRUE(dhcp4o6.enabled());
+    EXPECT_EQ(IOAddress("2001:db8::123"), dhcp4o6.getSubnet4o6().first);
+    EXPECT_EQ(45, dhcp4o6.getSubnet4o6().second);
+}
+
+// Checks if the DHCPv4 is able to parse the configuration with 4o6 subnet
+// defined.
+TEST_F(Dhcp4ParserTest, 4o6subnetBogus) {
+
+    ConstElementPtr status;
+
+    // Just a plain v4 config (no 4o6 parameters)
+    string config[] = {
+        // Bogus configuration 1: missing / in subnet
+        "{ " + genIfaceConfig() + "," +
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet4\": [ { "
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
+        "    \"subnet\": \"192.0.2.0/24\","
+        "    \"4o6-subnet\": \"2001:db8::123\" } ],"
+        "\"valid-lifetime\": 4000 }",
+
+        // Bogus configuration 2: incorrect address
+                "{ " + genIfaceConfig() + "," +
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet4\": [ { "
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
+        "    \"subnet\": \"192.0.2.0/24\","
+        "    \"4o6-subnet\": \"2001:db8:bogus/45\" } ],"
+        "\"valid-lifetime\": 4000 }",
+
+        // Bogus configuration 3: incorrect prefix lenght
+        "{ " + genIfaceConfig() + "," +
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet4\": [ { "
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
+        "    \"subnet\": \"192.0.2.0/24\","
+        "    \"4o6-subnet\": \"2001:db8::123/200\" } ],"
+        "\"valid-lifetime\": 4000 }"
+    };
+
+    ElementPtr json1 = Element::fromJSON(config[0]);
+    ElementPtr json2 = Element::fromJSON(config[0]);
+    ElementPtr json3 = Element::fromJSON(config[0]);
+
+    // Check that the first config is rejected.
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json1));
+    checkResult(status, 1);
+
+    // Check that the second config is rejected.
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json2));
+    checkResult(status, 1);
+
+    // Check that the third config is rejected.
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json3));
+    checkResult(status, 1);
+}
+
+
+// Checks if the DHCPv4 is able to parse the configuration with 4o6 network
+// interface defined.
+TEST_F(Dhcp4ParserTest, 4o6iface) {
+
+    ConstElementPtr status;
+
+    // Just a plain v4 config (no 4o6 parameters)
+    string config = "{ " + genIfaceConfig() + "," +
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet4\": [ { "
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
+        "    \"subnet\": \"192.0.2.0/24\","
+        "    \"4o6-interface\": \"ethX\" } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+
+    // check if returned status is OK
+    checkResult(status, 0);
+
+    // Now check if the configuration was indeed handled and we have
+    // expected pool configured.
+    Subnet4Ptr subnet = CfgMgr::instance().getStagingCfg()->
+        getCfgSubnets4()->selectSubnet(IOAddress("192.0.2.200"));
+    ASSERT_TRUE(subnet);
+
+    Cfg4o6& dhcp4o6 = subnet->get4o6();
+    EXPECT_TRUE(dhcp4o6.enabled());
+    EXPECT_EQ("ethX", dhcp4o6.getIface4o6());
+}
+
+// Checks if the DHCPv4 is able to parse the configuration with both 4o6 network
+// interface and v6 subnet defined.
+TEST_F(Dhcp4ParserTest, 4o6subnetIface) {
+
+    ConstElementPtr status;
+
+    // Just a plain v4 config (no 4o6 parameters)
+    string config = "{ " + genIfaceConfig() + "," +
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet4\": [ { "
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
+        "    \"subnet\": \"192.0.2.0/24\","
+        "    \"4o6-subnet\": \"2001:db8::543/21\","
+        "    \"4o6-interface\": \"ethX\" } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+
+    // check if returned status is OK
+    checkResult(status, 0);
+
+    // Now check if the configuration was indeed handled and we have
+    // expected subnet configured...
+    Subnet4Ptr subnet = CfgMgr::instance().getStagingCfg()->
+        getCfgSubnets4()->selectSubnet(IOAddress("192.0.2.200"));
+    ASSERT_TRUE(subnet);
+
+    // ... and that subnet has 4o6 network interface specified.
+    Cfg4o6& dhcp4o6 = subnet->get4o6();
+    EXPECT_TRUE(dhcp4o6.enabled());
+    EXPECT_EQ(IOAddress("2001:db8::543"), dhcp4o6.getSubnet4o6().first);
+    EXPECT_EQ(21, dhcp4o6.getSubnet4o6().second);
+    EXPECT_EQ("ethX", dhcp4o6.getIface4o6());
+}
+
+// Checks if the DHCPv4 is able to parse the configuration with 4o6 network
+// interface-id.
+TEST_F(Dhcp4ParserTest, 4o6subnetInterfaceId) {
+
+    ConstElementPtr status;
+
+    // Just a plain v4 config (no 4o6 parameters)
+    string config = "{ " + genIfaceConfig() + "," +
+        "\"rebind-timer\": 2000, "
+        "\"renew-timer\": 1000, "
+        "\"subnet4\": [ { "
+        "    \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
+        "    \"subnet\": \"192.0.2.0/24\","
+        "    \"4o6-interface-id\": \"vlan123\" } ],"
+        "\"valid-lifetime\": 4000 }";
+
+    ElementPtr json = Element::fromJSON(config);
+
+    EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
+
+    // check if returned status is OK
+    checkResult(status, 0);
+
+    // Now check if the configuration was indeed handled and we have
+    // expected 4o6-interface-id configured.
+    Subnet4Ptr subnet = CfgMgr::instance().getStagingCfg()->
+        getCfgSubnets4()->selectSubnet(IOAddress("192.0.2.200"));
+    ASSERT_TRUE(subnet);
+
+    Cfg4o6& dhcp4o6 = subnet->get4o6();
+    EXPECT_TRUE(dhcp4o6.enabled());
+    OptionPtr ifaceid = dhcp4o6.getInterfaceId();
+    ASSERT_TRUE(ifaceid);
+
+    vector<uint8_t> data = ifaceid->getData();
+    const char *exp_data = "vlan123";
+    // Let's convert vlan123 to vector<uint8_t> format.
+    // We need to skip the last \0 byte, thuse sizeof() - 1.
+    vector<uint8_t> exp(exp_data, exp_data + sizeof(exp_data) - 1);
+
+    EXPECT_TRUE(exp == data);
+}
+
+
 }

+ 88 - 0
src/lib/dhcpsrv/subnet.h

@@ -507,6 +507,83 @@ 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.
 ///
 /// This class represents an IPv4 subnet.
@@ -559,6 +636,14 @@ public:
         return (match_client_id_);
     }
 
+    /// @brief Returns DHCP4o6 configuration parameters.
+    ///
+    /// This structure is always available. If the 4o6 is not enabled, its
+    /// enabled_ field will be set to false.
+    Cfg4o6& get4o6() {
+        return (dhcp4o6_);
+    }
+
 private:
 
     /// @brief Returns default address for pool selection
@@ -581,6 +666,9 @@ private:
     /// @brief Should server use client identifiers for client lease
     /// lookup.
     bool match_client_id_;
+
+    /// @brief All the information related to DHCP4o6
+    Cfg4o6 dhcp4o6_;
 };
 
 /// @brief A pointer to a @c Subnet4 object