Commit a410f632 authored by Francis Dupont's avatar Francis Dupont
Browse files

[master] Merged trac5073a (option 43)

parents f2e33d67 80360862
......@@ -36,6 +36,19 @@
"re-detect": true
},
// Option 43 last resort definition can make well-formed messages
// to be rejected because they use not compatible "raw" value,
// and different vendors may define different sub-options.
// The option definition should be applied to avoid these problems,
// for instance by defining at the global scope the option as binary.
// In client-classes the option may be redefined as carrying vendor
// dependent sub-options.
"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 in 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" } ]
}
],
......
......@@ -33,23 +33,30 @@
behavior of almost any part of the DHCP message processing, including the assignment of
leases from different pools, the assignment of different options (or different values of
the same options) etc. In the current release of the software however, there are
only three mechanisms that take
advantage of client classification: subnet selection, assignment of different
only four mechanisms that take
advantage of client classification: subnet selection, definition of DHCPv4 private (codes 224-254) and code 43 options, assignment of different
options and, for DHCPv4 cable modems, the setting of specific options for use with
the TFTP server address and the boot file field.
</para>
<para>
The process of doing classification is conducted in three steps:
The process of doing classification is conducted in four steps:
<orderedlist>
<listitem><para>
Assess an incoming packet and assign it to zero or more classes.
</para></listitem>
<listitem><para>
If a private or code 43 DHCPv4 option is received, decoding it
following its client class or global (or for option 43 last
resort) definition.
</para></listitem>
<listitem><para>
Choose a subnet, possibly based on the class information.
</para></listitem>
<listitem><para>
Assign options, again possibly based on the class information.
For DHCPv4 private and code 43 options this includes class local
option definitions.
</para></listitem>
</orderedlist>
</para>
......
......@@ -1543,6 +1543,147 @@ It is merely echoed by the server
</note>
</section>
<section id="dhcp4-private-opts">
<title>DHCPv4 Private Options</title>
<para>
Options with code between 224 and 254 are reserved for private use.
They can be defined at the global scope or at client class local
scope: this allows to use option definitions depending on context
and to set option data accordingly. For instance to configure
an old PXEClient vendor:
<screen>
"Dhcp4": {
"client-class": [
{
<userinput>"name": "pxeclient",
"test": "option[vendor-class-identifier].text == 'PXEClient'",
"option-def": [
{
"name": "configfile",
"code": 209,
"type": "string"
}
],</userinput>
...
}, ...
],
...
}
</screen>
</para>
<para>
As the Vendor Specific Information option (code 43) has vendor
specific format, i.e. can carry either raw binary value or
sub-options, this mechanism is available for this option too.
</para>
<para>
In the following example taken from a real configuration two vendor
classes use the option 43 for different and incompatible purposes:
<screen>
"Dhcp4": {
"option-def": [
{
<userinput>"name": "cookie",
"code": 1,
"type": "string",
"space": "APC"
},
{
"name": "mtftp-ip",
"code": 1,
"type": "ipv4-address",
"space": "PXE"
},</userinput>
...
],
"client-class": [
{
<userinput>"name": "APC",
"test": "(option[vendor-class-identifier].text == 'APC'",
"option-def": [
{
"name": "vendor-encapsulated-options",
"type": "empty",
"encapsulate": "APC"
}
],
"option-data": [
{
"name": "cookie",
"space": "APC",
"data": "1APC"
},
{
"name": "vendor-encapsulated-options"
},</userinput>
...
],
...
},
{
<userinput>"name": "PXE",
"test": "(option[vendor-class-identifier].text == 'PXE'",
"option-def": [
{
"name": "vendor-encapsulated-options",
"type": "empty",
"encapsulate": "PXE"
}
],
"option-data": [
{
"name": "mtftp-ip",
"space": "PXE",
"data": "0.0.0.0"
},
{
"name": "vendor-encapsulated-options"
},</userinput>
...
],
...
},
...
],
...
}
</screen>
</para>
<para>
The definition used to decode a VSI option is:
<orderedlist>
<listitem><para>
The local definition of a client class the incoming packet belongs to
</para></listitem>
<listitem><para>
If none, the global definition
</para></listitem>
<listitem><para>
If none, the last resort definition described in the next section
<xref linkend="dhcp4-vendor-opts"/> (backward compatible with
previous Kea versions).
</para></listitem>
</orderedlist>
</para>
<note>
<para>
This last resort definition for the Vendor Specific Information
option (code 43) is not compatible with a raw binary value.
So when there are some known cases where a raw binary value
will be used, a client class must be defined with a classification
expression matching these cases and an option definition for
the VSI option with a binary type and no encapsulation.
</para>
</note>
<note>
<para>
Option definitions in client classes is allowed only for these
limited option set (codes 43 and from 224 to 254), and only
for DHCPv4.
</para>
</note>
</section>
<section id="dhcp4-vendor-opts">
<title>DHCPv4 Vendor Specific Options</title>
<para>
......@@ -1613,6 +1754,11 @@ It is merely echoed by the server
...
}</screen>
</para>
<para>
Another possibility, added in Kea 1.3, is to redefine the option,
see <xref linkend="dhcp4-private-opts"/>.
</para>
</section>
<section id="dhcp4-option-spaces">
......
......@@ -167,6 +167,12 @@ implemented within the context of the server and it has access to all objects
which define its configuration (including dynamically created option
definitions).
Private (codes 224-254) and VSI (code 43) options are not decoded
by @c LibDHCP::unpackOptions4 but by @ref isc::dhcp::Dhcpv4Srv::deferredUnpack
function after classification. To make this function to perform or not
deferred processing the simplest is to add or not the option code
to the @ref isc::dhcp::Pkt4::getDeferredOptions list.
@section dhcpv4DDNSIntegration DHCPv4 Server Support for the Dynamic DNS Updates
The DHCPv4 server supports processing of the DHCPv4 Client FQDN option (RFC4702)
and the DHCPv4 Host Name option (RFC2132). A client may send one of these options
......
This diff is collapsed.
......@@ -471,6 +471,7 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence}
\"option-def\" {
switch(driver.ctx_) {
case isc::dhcp::Parser4Context::DHCP4:
case isc::dhcp::Parser4Context::CLIENT_CLASSES:
return isc::dhcp::Dhcp4Parser::make_OPTION_DEF(driver.loc_);
default:
return isc::dhcp::Dhcp4Parser::make_STRING("option-def", driver.loc_);
......
This diff is collapsed.
......@@ -1390,7 +1390,7 @@ namespace isc { namespace dhcp {
enum
{
yyeof_ = 0,
yylast_ = 787, ///< Last index in yytable_.
yylast_ = 788, ///< Last index in yytable_.
yynnts_ = 329, ///< Number of nonterminal symbols.
yyfinal_ = 26, ///< Termination state number.
yyterror_ = 1,
......
......@@ -1535,6 +1535,7 @@ not_empty_client_class_params: client_class_param
client_class_param: client_class_name
| client_class_test
| option_def_list
| option_data_list
| next_server
| server_hostname
......
......@@ -9,6 +9,7 @@
#include <dhcp/duid.h>
#include <dhcp/hwaddr.h>
#include <dhcp/iface_mgr.h>
#include <dhcp/libdhcp++.h>
#include <dhcp/option4_addrlst.h>
#include <dhcp/option_int.h>
#include <dhcp/option_int_array.h>
......@@ -951,6 +952,9 @@ Dhcpv4Srv::processPacket(Pkt4Ptr& query, Pkt4Ptr& rsp) {
// class information.
classifyPacket(query);
// Now it is classified the deferred unpacking can be done.
deferredUnpack(query);
// Check whether the message should be further processed or discarded.
// There is no need to log anything here. This function logs by itself.
if (!accept(query)) {
......@@ -2874,6 +2878,65 @@ void Dhcpv4Srv::classifyPacket(const Pkt4Ptr& pkt) {
}
}
void
Dhcpv4Srv::deferredUnpack(Pkt4Ptr& query)
{
// Iterate on the list of deferred option codes
BOOST_FOREACH(const uint16_t& code, query->getDeferredOptions()) {
OptionDefinitionPtr def;
// Iterate on client classes
const ClientClasses& classes = query->getClasses();
for (ClientClasses::const_iterator cclass = classes.begin();
cclass != classes.end(); ++cclass) {
// Get the client class definition for this class
const ClientClassDefPtr& ccdef =
CfgMgr::instance().getCurrentCfg()->
getClientClassDictionary()->findClass(*cclass);
// If not found skip it
if (!ccdef) {
continue;
}
// If there is no option definition skip it
if (!ccdef->getCfgOptionDef()) {
continue;
}
def = ccdef->getCfgOptionDef()->get(DHCP4_OPTION_SPACE, code);
// Stop at the first client class with a defition
if (def) {
break;
}
}
// If not found try the global definition
if (!def) {
def = LibDHCP::getOptionDef(DHCP4_OPTION_SPACE, code);
}
if (!def) {
def = LibDHCP::getRuntimeOptionDef(DHCP4_OPTION_SPACE, code);
}
// Finish by last resort definition
if (!def) {
def = LibDHCP::getLastResortOptionDef(DHCP4_OPTION_SPACE, code);
}
// If not defined go to the next option
if (!def) {
continue;
}
// Get the existing option for its content and remove all
OptionPtr opt = query->getOption(code);
if (!opt) {
// should not happen but do not crash anyway
continue;
}
const OptionBuffer buf = opt->getData();
while (query->delOption(code)) {
// continue
}
// Unpack the option and add it
opt = def->optionFactory(Option::V4, code, buf.cbegin(), buf.cend());
query->addOption(opt);
}
}
void
Dhcpv4Srv::startD2() {
D2ClientMgr& d2_mgr = CfgMgr::instance().getD2ClientMgr();
......
......@@ -803,6 +803,17 @@ protected:
/// @param pkt packet to be classified
void classifyPacket(const Pkt4Ptr& pkt);
/// @brief Perform deferred option unpacking.
///
/// @note Options 43 and 224-254 are processed after classification.
/// If a class configures a definition it is applied, if none
/// the global (user) definition is applied. For option 43
/// a last resort definition (same definition as used in previous Kea
/// versions) is applied when none is found.
///
/// @param query Pointer to the client message.
void deferredUnpack(Pkt4Ptr& query);
/// @brief Allocation Engine.
/// Pointer to the allocation engine that we are currently using
/// It must be a pointer, because we will support changing engines
......
// Generated 201709141022
// Generated 201709191838
// A Bison parser, made by GNU Bison 3.0.4.
// Locations for Bison parsers in C++
......
// Generated 201709141022
// Generated 201709191838
// A Bison parser, made by GNU Bison 3.0.4.
// Positions for Bison parsers in C++
......
// Generated 201709141022
// Generated 201709191838
// A Bison parser, made by GNU Bison 3.0.4.
// Stack handling for Bison parsers in C++
......
......@@ -3427,7 +3427,6 @@ TEST_F(Dhcp4ParserTest, vendorOptionsCsv) {
}
// Tests of the hooks libraries configuration. All tests have the pre-
// condition (checked in the test fixture's SetUp() method) that no hooks
// libraries are loaded at the start of the tests.
......
This diff is collapsed.
......@@ -196,6 +196,7 @@ public:
using Dhcpv4Srv::sanityCheck;
using Dhcpv4Srv::srvidToString;
using Dhcpv4Srv::classifyPacket;
using Dhcpv4Srv::deferredUnpack;
using Dhcpv4Srv::accept;
using Dhcpv4Srv::acceptMessageType;
using Dhcpv4Srv::selectSubnet;
......
......@@ -1547,8 +1547,7 @@ const char* EXTRACTED_CONFIGS[] = {
" }\n"
" ],\n"
" \"valid-lifetime\": 4000\n"
" }\n"
};
" }\n"};
/// @brief unparsed configurations
const char* UNPARSED_CONFIGS[] = {
......@@ -5623,6 +5622,7 @@ const char* UNPARSED_CONFIGS[] = {
" \"name\": \"one\",\n"
" \"next-server\": \"0.0.0.0\",\n"
" \"option-data\": [ ],\n"
" \"option-def\": [ ],\n"
" \"server-hostname\": \"\"\n"
" },\n"
" {\n"
......@@ -5630,6 +5630,7 @@ const char* UNPARSED_CONFIGS[] = {
" \"name\": \"three\",\n"
" \"next-server\": \"0.0.0.0\",\n"
" \"option-data\": [ ],\n"
" \"option-def\": [ ],\n"
" \"server-hostname\": \"\"\n"
" },\n"
" {\n"
......@@ -5637,6 +5638,7 @@ const char* UNPARSED_CONFIGS[] = {
" \"name\": \"two\",\n"
" \"next-server\": \"0.0.0.0\",\n"
" \"option-data\": [ ],\n"
" \"option-def\": [ ],\n"
" \"server-hostname\": \"\"\n"
" }\n"
" ],\n"
......@@ -5988,8 +5990,7 @@ const char* UNPARSED_CONFIGS[] = {
" \"valid-lifetime\": 4000\n"
" }\n"
" ]\n"
" }\n"
};
" }\n"};
/// @brief the number of configurations
const size_t max_config_counter = sizeof(EXTRACTED_CONFIGS) / sizeof(char*);
......
// 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
......@@ -54,6 +54,9 @@ VendorOptionDefContainers LibDHCP::vendor4_defs_;
// Static container with vendor option definitions for DHCPv6.
VendorOptionDefContainers LibDHCP::vendor6_defs_;
// Static container with last resort option definitions for DHCPv4.
OptionDefContainerPtr LibDHCP::lastresort_defs_(new OptionDefContainer());
// Static container with option definitions created in runtime.
StagedValue<OptionDefSpaceContainer> LibDHCP::runtime_option_defs_;
......@@ -83,6 +86,7 @@ LibDHCP::getOptionDefs(const std::string& space) {
initVendorOptsDocsis4();
initStdOptionDefs6();
initVendorOptsDocsis6();
initLastResortOptionDefs();
}
if (space == DHCP4_OPTION_SPACE) {
......@@ -257,6 +261,45 @@ LibDHCP::commitRuntimeOptionDefs() {
runtime_option_defs_.commit();
}
OptionDefinitionPtr
LibDHCP::getLastResortOptionDef(const std::string& space, const uint16_t code) {
OptionDefContainerPtr container = getLastResortOptionDefs(space);
const OptionDefContainerTypeIndex& index = container->get<1>();
const OptionDefContainerTypeRange& range = index.equal_range(code);
if (range.first != range.second) {
return (*range.first);
}
return (OptionDefinitionPtr());
}
OptionDefinitionPtr
LibDHCP::getLastResortOptionDef(const std::string& space, const std::string& name) {
OptionDefContainerPtr container = getLastResortOptionDefs(space);
const OptionDefContainerNameIndex& index = container->get<2>();
const OptionDefContainerNameRange& range = index.equal_range(name);
if (range.first != range.second) {
return (*range.first);
}
return (OptionDefinitionPtr());
}
OptionDefContainerPtr
LibDHCP::getLastResortOptionDefs(const std::string& space) {
if (space == DHCP4_OPTION_SPACE) {
return (lastresort_defs_);
}
return (null_option_def_container_);
}
bool
LibDHCP::shouldDeferOptionUnpack(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,
......@@ -423,7 +466,8 @@ size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
size_t LibDHCP::unpackOptions4(const OptionBuffer& buf,
const std::string& option_space,
isc::dhcp::OptionCollection& options) {
isc::dhcp::OptionCollection& options,
std::list<uint16_t>& deferred) {
size_t offset = 0;
size_t last_offset = 0;
......@@ -501,6 +545,12 @@ size_t LibDHCP::unpackOptions4(const OptionBuffer& buf,
num_defs = distance(range.first, range.second);
}
// Check if option unpacking must be deferred
if (shouldDeferOptionUnpack(option_space, opt_type)) {
num_defs = 0;
deferred.push_back(opt_type);
}
OptionPtr opt;
if (num_defs > 1) {
// Multiple options of the same code are not supported right now!
......@@ -832,6 +882,12 @@ LibDHCP::initStdOptionDefs6() {
V4V6_BIND_OPTION_DEFINITIONS_SIZE);
}
void
LibDHCP::initLastResortOptionDefs() {
initOptionSpace(lastresort_defs_, LAST_RESORT_V4_OPTION_DEFINITIONS,
LAST_RESORT_V4_OPTION_DEFINITIONS_SIZE);
}
void
LibDHCP::initVendorOptsDocsis4() {
initOptionSpace(vendor4_defs_[VENDOR_ID_CABLE_LABS], DOCSIS3_V4_DEFS,
......
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