Commit 042c0b6f authored by Francis Dupont's avatar Francis Dupont
Browse files

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

parent 74b6b67b
......@@ -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.
......
......@@ -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" } ]
}
],
......
// 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
......
......@@ -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
......
......@@ -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
......
......@@ -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));
......
......@@ -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);
......
......@@ -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());
......
......@@ -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() << ")");
......
......@@ -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.
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment