Browse Source

[5073a] On the right track (still a lot of code and new tests to do)

Francis Dupont 7 years ago
parent
commit
042c0b6fc8

+ 9 - 0
doc/examples/kea4/advanced.json

@@ -36,6 +36,15 @@
         "re-detect": true
     },
 
+    // Option 43 last resort definition can make legal messages to be
+    // rejected because they use not compatible "raw" value.
+    // The option definition can be applied to avoid this problem.
+    "option-def": [ {
+        "name": "vendor-encapsulated-options",
+        "code": 43,
+        "type": "binary"
+    } ],
+
     // We need to specify the the database used to store leases. As of
     // September 2016, four database backends are supported: MySQL,
     // PostgreSQL, Cassandra, and the in-memory database, Memfile.

+ 7 - 1
doc/examples/kea4/classify.json

@@ -55,12 +55,18 @@
 // In this particular class, we want to set specific values
 // of certain DHCPv4 fields. If the incoming packet matches the
 // test, those fields will be set in outgoing responses.
+// The option 43 is defined to encapsulate suboption inf the aastra space.
   {
       "name": "VoIP",
       "test": "substring(option[60].hex,0,6) == 'Aastra'",
       "next-server": "192.0.2.254",
       "server-hostname": "hal9000",
-      "boot-file-name": "/dev/null"
+      "boot-file-name": "/dev/null",
+      "option-def": [ {
+          "name": "vendor-encapsulated-options",
+          "code": 43,
+          "type": "empty",
+          "encapsulate": "aastra" } ]
   }
 
   ],

+ 21 - 1
src/lib/dhcp/libdhcp++.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2016 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2017 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
@@ -60,6 +60,9 @@ StagedValue<OptionDefSpaceContainer> LibDHCP::runtime_option_defs_;
 // Null container.
 const OptionDefContainerPtr null_option_def_container_(new OptionDefContainer());
 
+// Option 43 definition.
+OptionDefinitionPtr LibDHCP::last_resort_option43_def;
+
 // Those two vendor classes are used for cable modems:
 
 /// DOCSIS3.0 compatible cable modem
@@ -257,6 +260,13 @@ LibDHCP::commitRuntimeOptionDefs() {
     runtime_option_defs_.commit();
 }
 
+bool
+LibDHCP::deferOption(const std::string& space, const uint16_t code) {
+    return ((space == DHCP4_OPTION_SPACE) &&
+            ((code == DHO_VENDOR_ENCAPSULATED_OPTIONS) ||
+             ((code >= 224) && (code <= 254))));
+}
+
 OptionPtr
 LibDHCP::optionFactory(Option::Universe u,
                        uint16_t type,
@@ -501,6 +511,11 @@ size_t LibDHCP::unpackOptions4(const OptionBuffer& buf,
             num_defs = distance(range.first, range.second);
         }
 
+        // Check if option unpacking must be deferred
+        if (deferOption(option_space, opt_type)) {
+            num_defs = 0;
+        }
+
         OptionPtr opt;
         if (num_defs > 1) {
             // Multiple options of the same code are not supported right now!
@@ -814,6 +829,11 @@ void
 LibDHCP::initStdOptionDefs4() {
     initOptionSpace(v4option_defs_, STANDARD_V4_OPTION_DEFINITIONS,
                     STANDARD_V4_OPTION_DEFINITIONS_SIZE);
+    last_resort_option43_def.reset(new OptionDefinition(
+        LAST_RESORT_OPTION43_DEFINITION.name,
+        LAST_RESORT_OPTION43_DEFINITION.code,
+        LAST_RESORT_OPTION43_DEFINITION.type,
+        LAST_RESORT_OPTION43_DEFINITION.array));
 }
 
 void

+ 13 - 0
src/lib/dhcp/libdhcp++.h

@@ -112,6 +112,19 @@ public:
     static OptionDefContainerPtr
     getRuntimeOptionDefs(const std::string& space);
 
+    /// @brief Checks if an option unpacking has to be deferred.
+    ///
+    /// DHCPv4 option 43 and 224-254 unpacking is done after classification.
+    ///
+    /// @space Option space name.
+    /// @param code Option code.
+    ///
+    /// @return True if option processing should be deferred.
+    static bool deferOption(const std::string& space, const uint16_t code);
+
+    /// @brief Last resort definition for DHCPv4 option 43
+    static OptionDefinitionPtr last_resort_option43_def;
+
     /// @brief Factory function to create instance of option.
     ///
     /// Factory method creates instance of specified option. The option

+ 7 - 2
src/lib/dhcp/std_option_defs.h

@@ -124,8 +124,7 @@ const OptionDefParams STANDARD_V4_OPTION_DEFINITIONS[] = {
     { "nis-domain", DHO_NIS_DOMAIN, OPT_STRING_TYPE, false, NO_RECORD_DEF, "" },
     { "nis-servers", DHO_NIS_SERVERS, OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF, "" },
     { "ntp-servers", DHO_NTP_SERVERS, OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF, "" },
-    { "vendor-encapsulated-options", DHO_VENDOR_ENCAPSULATED_OPTIONS,
-      OPT_EMPTY_TYPE, false, NO_RECORD_DEF, "vendor-encapsulated-options-space" },
+    /// vendor-encapsulated-options (43) is deferred
     { "netbios-name-servers", DHO_NETBIOS_NAME_SERVERS,
       OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF, "" },
     { "netbios-dd-server", DHO_NETBIOS_DD_SERVER,
@@ -217,6 +216,12 @@ const OptionDefParams STANDARD_V4_OPTION_DEFINITIONS[] = {
 const int STANDARD_V4_OPTION_DEFINITIONS_SIZE =
     sizeof(STANDARD_V4_OPTION_DEFINITIONS) / sizeof(STANDARD_V4_OPTION_DEFINITIONS[0]);
 
+/// Last resort definition for option 43
+const OptionDefParams LAST_RESORT_OPTION43_DEFINITION = {
+    "vendor-encapsulated-options", DHO_VENDOR_ENCAPSULATED_OPTIONS,
+    OPT_EMPTY_TYPE, false, NO_RECORD_DEF, "vendor-encapsulated-options-space"
+};
+
 /// Start Definition of DHCPv6 options
 
 // client-fqdn

+ 4 - 0
src/lib/dhcp/tests/libdhcp++_unittest.cc

@@ -862,6 +862,7 @@ TEST_F(LibDhcpTest, unpackOptions4) {
     ASSERT_EQ(1, addresses.size());
     EXPECT_EQ("10.0.0.10", addresses[0].toText());
 
+#if 0
     // Vendor Specific Information option
     x = options.find(43);
     ASSERT_FALSE(x == options.end());
@@ -876,6 +877,7 @@ TEST_F(LibDhcpTest, unpackOptions4) {
     ASSERT_TRUE(eso);
     EXPECT_EQ(0xdc, eso->getType());
     EXPECT_EQ(2, eso->len());
+#endif
 
     // Checking DHCP Relay Agent Information Option.
     x = options.find(DHO_DHCP_AGENT_OPTIONS);
@@ -1174,6 +1176,7 @@ TEST_F(LibDhcpTest, stdOptionDefs4) {
     LibDhcpTest::testStdOptionDefs4(DHO_NTP_SERVERS, begin, end,
                                     typeid(Option4AddrLst));
 
+#if 0
     // The following option requires well formed buffer to be created from.
     // Not just a dummy one. This buffer includes some suboptions.
     OptionBuffer vendor_opts_buf = createVendorOption();
@@ -1182,6 +1185,7 @@ TEST_F(LibDhcpTest, stdOptionDefs4) {
                                     vendor_opts_buf.end(),
                                     typeid(OptionCustom),
                                     "vendor-encapsulated-options-space");
+#endif
 
     LibDhcpTest::testStdOptionDefs4(DHO_NETBIOS_NAME_SERVERS, begin, end,
                                     typeid(Option4AddrLst));

+ 23 - 0
src/lib/dhcpsrv/client_class_def.cc

@@ -45,6 +45,10 @@ ClientClassDef::ClientClassDef(const ClientClassDef& rhs)
         *match_expr_ = *(rhs.match_expr_);
     }
 
+    if (rhs.cfg_option_def_) {
+        rhs.cfg_option_def_->copyTo(*cfg_option_def_);
+    }
+
     if (rhs.cfg_option_) {
         rhs.cfg_option_->copyTo(*cfg_option_);
     }
@@ -87,6 +91,16 @@ ClientClassDef::setTest(const std::string& test) {
     test_ = test;
 }
 
+const CfgOptionDefPtr&
+ClientClassDef::getCfgOptionDef() const {
+    return (cfg_option_def_);
+}
+
+void
+ClientClassDef::setCfgOptionDef(const CfgOptionDefPtr& cfg_option_def) {
+    cfg_option_def_ = cfg_option_def;
+}
+
 const CfgOptionPtr&
 ClientClassDef::getCfgOption() const {
     return (cfg_option_);
@@ -106,6 +120,9 @@ ClientClassDef::equals(const ClientClassDef& other) const {
         ((!cfg_option_ && !other.cfg_option_) ||
         (cfg_option_ && other.cfg_option_ &&
          (*cfg_option_ == *other.cfg_option_))) &&
+        ((!cfg_option_def_ && !other.cfg_option_def_) ||
+        (cfg_option_def_ && other.cfg_option_def_ &&
+         (*cfg_option_def_ == *other.cfg_option_def_))) &&
             (next_server_ == other.next_server_) &&
             (sname_ == other.sname_) &&
             (filename_ == other.filename_));
@@ -121,6 +138,10 @@ ClientClassDef:: toElement() const {
     if (!test_.empty()) {
         result->set("test", Element::create(test_));
     }
+    // Set option-def
+    if (cfg_option_def_) {
+        result->set("option-def", cfg_option_def_->toElement());
+    }
     // Set option-data
     result->set("option-data", cfg_option_->toElement());
     if (family != AF_INET) {
@@ -163,11 +184,13 @@ ClientClassDictionary::addClass(const std::string& name,
                                 const ExpressionPtr& match_expr,
                                 const std::string& test,
                                 const CfgOptionPtr& cfg_option,
+                                CfgOptionDefPtr cfg_option_def,
                                 asiolink::IOAddress next_server,
                                 const std::string& sname,
                                 const std::string& filename) {
     ClientClassDefPtr cclass(new ClientClassDef(name, match_expr, cfg_option));
     cclass->setTest(test);
+    cclass->setCfgOptionDef(cfg_option_def);
     cclass->setNextServer(next_server);
     cclass->setSname(sname);
     cclass->setFilename(filename);

+ 14 - 0
src/lib/dhcpsrv/client_class_def.h

@@ -9,6 +9,7 @@
 
 #include <cc/cfg_to_element.h>
 #include <dhcpsrv/cfg_option.h>
+#include <dhcpsrv/cfg_option_def.h>
 #include <eval/token.h>
 #include <exceptions/exceptions.h>
 
@@ -79,6 +80,14 @@ public:
     /// @param test the original expression to assign the class
     void setTest(const std::string& test);
 
+    /// @brief Fetches the class's option definitions
+    const CfgOptionDefPtr& getCfgOptionDef() const;
+
+    /// @brief Sets the class's option definition collection
+    ///
+    /// @param cfg_option_def the option definitions to assign the class
+    void setCfgOptionDef(const CfgOptionDefPtr& cfg_option_def);
+
     /// @brief Fetches the class's option collection
     const CfgOptionPtr& getCfgOption() const;
 
@@ -171,6 +180,9 @@ private:
     /// this class.
     std::string test_;
 
+    /// @brief The option definition configuration for this class
+    CfgOptionDefPtr cfg_option_def_;
+
     /// @brief The option data configuration for this class
     CfgOptionPtr cfg_option_;
 
@@ -223,6 +235,7 @@ public:
     /// @param match_expr Expression the class will use to determine membership
     /// @param test Original version of match_expr
     /// @param options Collection of options members should be given
+    /// @param defs Option definitions (optional)
     /// @param next_server next-server value for this class (optional)
     /// @param sname server-name value for this class (optional)
     /// @param filename boot-file-name value for this class (optional)
@@ -232,6 +245,7 @@ public:
     /// others.
     void addClass(const std::string& name, const ExpressionPtr& match_expr,
                   const std::string& test, const CfgOptionPtr& options,
+                  CfgOptionDefPtr defs = 0,
                   asiolink::IOAddress next_server = asiolink::IOAddress("0.0.0.0"),
                   const std::string& sname = std::string(),
                   const std::string& filename = std::string());

+ 29 - 1
src/lib/dhcpsrv/parsers/client_class_def_parser.cc

@@ -5,6 +5,7 @@
 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 #include <config.h>
+#include <dhcp/libdhcp++.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/client_class_def.h>
 #include <dhcpsrv/parsers/dhcp_parsers.h>
@@ -81,6 +82,33 @@ ClientClassDefParser::parse(ClientClassDictionaryPtr& class_dictionary,
         test = test_cfg->stringValue();
     }
 
+    // Parse option def
+    CfgOptionDefPtr defs(new CfgOptionDef());
+    ConstElementPtr option_defs = class_def_cfg->get("option-def");
+    if (option_defs) {
+        OptionDefParser parser;
+        BOOST_FOREACH(ConstElementPtr option_def, option_defs->listValue()) {
+            OptionDefinitionTuple def;
+                
+            def = parser.parse(option_def);
+            // Verify if the defition is for an option which are
+            // in a deferred processing list.
+            if (!LibDHCP::deferOption(def.second, def.first->getCode())) {
+                isc_throw(DhcpConfigError,
+                          "Not allowed option definition for code '"
+                          << def.first->getCode() << "' in space '"
+                          << def.second << "' at ("
+                          << option_def->getPosition() << ")");
+            }
+            try {
+                defs->add(def.first, def.second);
+            } catch (const std::exception& ex) {
+                isc_throw(DhcpConfigError, ex.what() << " ("
+                          << option_def->getPosition() << ")");
+            }
+        }
+    }
+
     // Parse option data
     CfgOptionPtr options(new CfgOption());
     ConstElementPtr option_data = class_def_cfg->get("option-data");
@@ -145,7 +173,7 @@ ClientClassDefParser::parse(ClientClassDictionaryPtr& class_dictionary,
     // Add the client class definition
     try {
         class_dictionary->addClass(name, match_expr, test, options,
-                                   next_server, sname, filename);
+                                   defs, next_server, sname, filename);
     } catch (const std::exception& ex) {
         isc_throw(DhcpConfigError, "Can't add class: " << ex.what()
                   << " (" << class_def_cfg->getPosition() << ")");

+ 7 - 2
src/lib/dhcpsrv/parsers/client_class_def_parser.h

@@ -17,8 +17,8 @@
 ///
 /// These parsers are used to parse lists of client class definitions
 /// into a ClientClassDictionary of ClientClassDef instances.  Each
-/// ClientClassDef consists of (at least) a name, an expression, and
-/// option-data.  The latter two are currently optional.
+/// ClientClassDef consists of (at least) a name, an expression, option-def
+/// and option-data.  Currently only a not empty name is required.
 ///
 /// There parsers defined are:
 ///
@@ -36,6 +36,11 @@
 /// -# "test" - a string containing the logical expression used to determine
 /// membership in the class. This is passed into the eval parser.
 ///
+/// -# "option-def" - a list which defines the options which processing
+/// is deferred. This element is optional and parsed using the @ref
+/// isc::dhcp::OptionDefParser. A check is done to verify definitions
+/// are only for deferred processing option (DHCPv4 43 and 224-254).
+///
 /// -# "option-data" - a list which defines the options that should be
 /// assigned to remembers of the class.  This element is optional and parsed
 /// using the @ref isc::dhcp::OptionDataListParser.