From: Francis Dupont Date: Wed, 20 Sep 2017 14:59:17 +0000 (+0200) Subject: [5073a] On the right track (still a lot of code and new tests to do) X-Git-Tag: trac5363_base~6^2~9 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=042c0b6fc8435731980dfc5bddacb32ea96c6403;p=thirdparty%2Fkea.git [5073a] On the right track (still a lot of code and new tests to do) --- diff --git a/doc/examples/kea4/advanced.json b/doc/examples/kea4/advanced.json index 603df611cc..77a097140f 100644 --- a/doc/examples/kea4/advanced.json +++ b/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. diff --git a/doc/examples/kea4/classify.json b/doc/examples/kea4/classify.json index 99ec342c4c..fbe4753ec0 100644 --- a/doc/examples/kea4/classify.json +++ b/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" } ] } ], diff --git a/src/lib/dhcp/libdhcp++.cc b/src/lib/dhcp/libdhcp++.cc index df25719d9e..a98aecad46 100644 --- a/src/lib/dhcp/libdhcp++.cc +++ b/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 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 diff --git a/src/lib/dhcp/libdhcp++.h b/src/lib/dhcp/libdhcp++.h index b5791dcaf4..85f087675a 100644 --- a/src/lib/dhcp/libdhcp++.h +++ b/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 diff --git a/src/lib/dhcp/std_option_defs.h b/src/lib/dhcp/std_option_defs.h index 02815b4c5f..22701cb5c8 100644 --- a/src/lib/dhcp/std_option_defs.h +++ b/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 diff --git a/src/lib/dhcp/tests/libdhcp++_unittest.cc b/src/lib/dhcp/tests/libdhcp++_unittest.cc index af11a53bcc..b8b7305ab1 100644 --- a/src/lib/dhcp/tests/libdhcp++_unittest.cc +++ b/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)); diff --git a/src/lib/dhcpsrv/client_class_def.cc b/src/lib/dhcpsrv/client_class_def.cc index 135982d708..520ad079f4 100644 --- a/src/lib/dhcpsrv/client_class_def.cc +++ b/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); diff --git a/src/lib/dhcpsrv/client_class_def.h b/src/lib/dhcpsrv/client_class_def.h index 8bc5ad72a2..b645214789 100644 --- a/src/lib/dhcpsrv/client_class_def.h +++ b/src/lib/dhcpsrv/client_class_def.h @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -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()); diff --git a/src/lib/dhcpsrv/parsers/client_class_def_parser.cc b/src/lib/dhcpsrv/parsers/client_class_def_parser.cc index 01d35beb25..5cc99c12cf 100644 --- a/src/lib/dhcpsrv/parsers/client_class_def_parser.cc +++ b/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 +#include #include #include #include @@ -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() << ")"); diff --git a/src/lib/dhcpsrv/parsers/client_class_def_parser.h b/src/lib/dhcpsrv/parsers/client_class_def_parser.h index 392162bc0b..576ca4d63c 100644 --- a/src/lib/dhcpsrv/parsers/client_class_def_parser.h +++ b/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.