Commit 3e996717 authored by Francis Dupont's avatar Francis Dupont

[master] Finished merge of trac5241 (always-send)

parents 408e36f7 4c7bf159
1283. [func] fdupont
An 'always-send' parameter has been added to options
configuration. It allows an option to be always sent, even if
a client didn't request it.
(Trac #5241, git xxx)
1282. [bug] fdupont
Now all interface service sockets are closed before interface
re-detection. Note if the re-configuration fails they remain
......
......@@ -125,6 +125,14 @@
{
"name": "default-ip-ttl",
"data": "0xf0"
},
// At a few exceptions options are added to response only when
// the client requests them. The always-send flag should be used
// to enforce a particular option.
{
"name": "vendor-class-identifier",
"data": "isc",
"always-send": true
}
],
......
......@@ -111,6 +111,15 @@
{
"name": "bootfile-param",
"data": "root=/dev/sda2, quiet, splash"
},
// At a few exceptions options are added to response only when
// the client requests them. The always-send flag should be used
// to enforce a particular option.
{
"name": "pana-agent",
"data": "2001:db8:2::123",
"always-send": true
}
],
"pools": [
......
......@@ -990,8 +990,56 @@ temporarily override a list of interface names and listen on all interfaces.
},
...
]
} </screen>
}
</screen>
Defined options are added to response when the client requests them
at a few exceptions which are always added. To enforce the addition
of a particular option set the always-send flag to true as in:
<screen>
"Dhcp4": {
"option-data": [
{
<userinput>"name": "domain-name-servers",
"data": "192.0.2.1, 192.0.2.2",
"always-send": true</userinput>
},
...
]
}
</screen>
The effect is the same as if the client added the option code in the
Parameter Request List option (or its equivalent for vendor
options) so in:
<screen>
"Dhcp4": {
"option-data": [
{
<userinput>"name": "domain-name-servers",
"data": "192.0.2.1, 192.0.2.2",
"always-send": true</userinput>
},
...
],
"subnet4": [
{
"subnet": "192.0.3.0/24",
"option-data": [
{
<userinput>"name": "domain-name-servers",
"data": "192.0.3.1, 192.0.3.2"</userinput>
},
...
],
...
},
...
],
...
}
</screen>
The Domain Name Servers option is always added to responses
(the always-send is "sticky") but the value is the subnet one
when the client is localized in the subnet.
</para>
<para>
The <command>name</command> parameter specifies the option name. For a
......
......@@ -1010,7 +1010,7 @@ temporarily override a list of interface names and listen on all interfaces.
be skipped, unless you want to specify the option value as
hexstring. Therefore the above example can be simplified to:
<screen>
"Dhcp4": {
"Dhcp6": {
"option-data": [
{
<userinput>"name": "dns-servers",
......@@ -1018,11 +1018,57 @@ temporarily override a list of interface names and listen on all interfaces.
},
...
]
} </screen>
}
</screen>
Defined options are added to response when the client requests them
at a few exceptions which are always added. To enforce the addition
of a particular option set the always-send flag to true as in:
<screen>
"Dhcp6": {
"option-data": [
{
<userinput>"name": "dns-servers",
"data": "2001:db8::cafe, 2001:db8::babe",
"always-send": true</userinput>
},
...
]
}
</screen>
The effect is the same as if the client added the option code in the
Option Request Option (or its equivalent for vendor options) so in:
<screen>
"Dhcp6": {
"option-data": [
{
<userinput>"name": "dns-servers",
"data": "2001:db8::cafe, 2001:db8::babe",
"always-send": true</userinput>
},
...
],
"subnet6": [
{
"subnet": "2001:db8:1::/64",
"option-data": [
{
<userinput>"name": "dns-servers",
"data": "2001:db8:1::cafe, 2001:db8:1::babe"</userinput>
},
...
],
...
},
...
],
...
}
</screen>
The DNS Servers option is always added to responses
(the always-send is "sticky") but the value is the subnet one
when the client is localized in the subnet.
</para>
<para>
It is possible to override options on a per-subnet basis. If
clients connected to most of your subnets are expected to get the
......
This diff is collapsed.
......@@ -496,6 +496,15 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence}
}
}
\"always-send\" {
switch(driver.ctx_) {
case isc::dhcp::Parser4Context::OPTION_DATA:
return isc::dhcp::Dhcp4Parser::make_ALWAYS_SEND(driver.loc_);
default:
return isc::dhcp::Dhcp4Parser::make_STRING("always-send", driver.loc_);
}
}
\"pools\" {
switch(driver.ctx_) {
case isc::dhcp::Parser4Context::SUBNET4:
......
This diff is collapsed.
......@@ -219,7 +219,7 @@ namespace isc { namespace dhcp {
/// Both variants must be built beforehand, because swapping the actual
/// data requires reading it (with as()), and this is not possible on
/// unconstructed variants: it would require some dynamic testing, which
/// should not be the variant's responsibility.
/// should not be the variant's responsability.
/// Swapping between built and (possibly) non-built is done with
/// variant::move ().
template <typename T>
......@@ -398,81 +398,81 @@ namespace isc { namespace dhcp {
TOKEN_CODE = 306,
TOKEN_SPACE = 307,
TOKEN_CSV_FORMAT = 308,
TOKEN_RECORD_TYPES = 309,
TOKEN_ENCAPSULATE = 310,
TOKEN_ARRAY = 311,
TOKEN_POOLS = 312,
TOKEN_POOL = 313,
TOKEN_USER_CONTEXT = 314,
TOKEN_SUBNET = 315,
TOKEN_INTERFACE = 316,
TOKEN_INTERFACE_ID = 317,
TOKEN_ID = 318,
TOKEN_RAPID_COMMIT = 319,
TOKEN_RESERVATION_MODE = 320,
TOKEN_DISABLED = 321,
TOKEN_OUT_OF_POOL = 322,
TOKEN_ALL = 323,
TOKEN_HOST_RESERVATION_IDENTIFIERS = 324,
TOKEN_CLIENT_CLASSES = 325,
TOKEN_TEST = 326,
TOKEN_CLIENT_CLASS = 327,
TOKEN_RESERVATIONS = 328,
TOKEN_DUID = 329,
TOKEN_HW_ADDRESS = 330,
TOKEN_CIRCUIT_ID = 331,
TOKEN_CLIENT_ID = 332,
TOKEN_HOSTNAME = 333,
TOKEN_FLEX_ID = 334,
TOKEN_RELAY = 335,
TOKEN_IP_ADDRESS = 336,
TOKEN_HOOKS_LIBRARIES = 337,
TOKEN_LIBRARY = 338,
TOKEN_PARAMETERS = 339,
TOKEN_EXPIRED_LEASES_PROCESSING = 340,
TOKEN_RECLAIM_TIMER_WAIT_TIME = 341,
TOKEN_FLUSH_RECLAIMED_TIMER_WAIT_TIME = 342,
TOKEN_HOLD_RECLAIMED_TIME = 343,
TOKEN_MAX_RECLAIM_LEASES = 344,
TOKEN_MAX_RECLAIM_TIME = 345,
TOKEN_UNWARNED_RECLAIM_CYCLES = 346,
TOKEN_DHCP4O6_PORT = 347,
TOKEN_CONTROL_SOCKET = 348,
TOKEN_SOCKET_TYPE = 349,
TOKEN_SOCKET_NAME = 350,
TOKEN_DHCP_DDNS = 351,
TOKEN_ENABLE_UPDATES = 352,
TOKEN_QUALIFYING_SUFFIX = 353,
TOKEN_SERVER_IP = 354,
TOKEN_SERVER_PORT = 355,
TOKEN_SENDER_IP = 356,
TOKEN_SENDER_PORT = 357,
TOKEN_MAX_QUEUE_SIZE = 358,
TOKEN_NCR_PROTOCOL = 359,
TOKEN_NCR_FORMAT = 360,
TOKEN_ALWAYS_INCLUDE_FQDN = 361,
TOKEN_OVERRIDE_NO_UPDATE = 362,
TOKEN_OVERRIDE_CLIENT_UPDATE = 363,
TOKEN_REPLACE_CLIENT_NAME = 364,
TOKEN_GENERATED_PREFIX = 365,
TOKEN_TCP = 366,
TOKEN_JSON = 367,
TOKEN_WHEN_PRESENT = 368,
TOKEN_NEVER = 369,
TOKEN_ALWAYS = 370,
TOKEN_WHEN_NOT_PRESENT = 371,
TOKEN_LOGGING = 372,
TOKEN_LOGGERS = 373,
TOKEN_OUTPUT_OPTIONS = 374,
TOKEN_OUTPUT = 375,
TOKEN_DEBUGLEVEL = 376,
TOKEN_SEVERITY = 377,
TOKEN_FLUSH = 378,
TOKEN_MAXSIZE = 379,
TOKEN_MAXVER = 380,
TOKEN_DHCP6 = 381,
TOKEN_DHCPDDNS = 382,
TOKEN_CONTROL_AGENT = 383,
TOKEN_ALWAYS_SEND = 309,
TOKEN_RECORD_TYPES = 310,
TOKEN_ENCAPSULATE = 311,
TOKEN_ARRAY = 312,
TOKEN_POOLS = 313,
TOKEN_POOL = 314,
TOKEN_USER_CONTEXT = 315,
TOKEN_SUBNET = 316,
TOKEN_INTERFACE = 317,
TOKEN_INTERFACE_ID = 318,
TOKEN_ID = 319,
TOKEN_RAPID_COMMIT = 320,
TOKEN_RESERVATION_MODE = 321,
TOKEN_DISABLED = 322,
TOKEN_OUT_OF_POOL = 323,
TOKEN_ALL = 324,
TOKEN_HOST_RESERVATION_IDENTIFIERS = 325,
TOKEN_CLIENT_CLASSES = 326,
TOKEN_TEST = 327,
TOKEN_CLIENT_CLASS = 328,
TOKEN_RESERVATIONS = 329,
TOKEN_DUID = 330,
TOKEN_HW_ADDRESS = 331,
TOKEN_CIRCUIT_ID = 332,
TOKEN_CLIENT_ID = 333,
TOKEN_HOSTNAME = 334,
TOKEN_FLEX_ID = 335,
TOKEN_RELAY = 336,
TOKEN_IP_ADDRESS = 337,
TOKEN_HOOKS_LIBRARIES = 338,
TOKEN_LIBRARY = 339,
TOKEN_PARAMETERS = 340,
TOKEN_EXPIRED_LEASES_PROCESSING = 341,
TOKEN_RECLAIM_TIMER_WAIT_TIME = 342,
TOKEN_FLUSH_RECLAIMED_TIMER_WAIT_TIME = 343,
TOKEN_HOLD_RECLAIMED_TIME = 344,
TOKEN_MAX_RECLAIM_LEASES = 345,
TOKEN_MAX_RECLAIM_TIME = 346,
TOKEN_UNWARNED_RECLAIM_CYCLES = 347,
TOKEN_DHCP4O6_PORT = 348,
TOKEN_CONTROL_SOCKET = 349,
TOKEN_SOCKET_TYPE = 350,
TOKEN_SOCKET_NAME = 351,
TOKEN_DHCP_DDNS = 352,
TOKEN_ENABLE_UPDATES = 353,
TOKEN_QUALIFYING_SUFFIX = 354,
TOKEN_SERVER_IP = 355,
TOKEN_SERVER_PORT = 356,
TOKEN_SENDER_IP = 357,
TOKEN_SENDER_PORT = 358,
TOKEN_MAX_QUEUE_SIZE = 359,
TOKEN_NCR_PROTOCOL = 360,
TOKEN_NCR_FORMAT = 361,
TOKEN_ALWAYS_INCLUDE_FQDN = 362,
TOKEN_OVERRIDE_NO_UPDATE = 363,
TOKEN_OVERRIDE_CLIENT_UPDATE = 364,
TOKEN_REPLACE_CLIENT_NAME = 365,
TOKEN_GENERATED_PREFIX = 366,
TOKEN_TCP = 367,
TOKEN_JSON = 368,
TOKEN_WHEN_PRESENT = 369,
TOKEN_NEVER = 370,
TOKEN_ALWAYS = 371,
TOKEN_WHEN_NOT_PRESENT = 372,
TOKEN_LOGGING = 373,
TOKEN_LOGGERS = 374,
TOKEN_OUTPUT_OPTIONS = 375,
TOKEN_OUTPUT = 376,
TOKEN_DEBUGLEVEL = 377,
TOKEN_SEVERITY = 378,
TOKEN_FLUSH = 379,
TOKEN_MAXSIZE = 380,
TOKEN_MAXVER = 381,
TOKEN_DHCP6 = 382,
TOKEN_DHCPDDNS = 383,
TOKEN_TOPLEVEL_JSON = 384,
TOKEN_TOPLEVEL_DHCP4 = 385,
TOKEN_SUB_DHCP4 = 386,
......@@ -810,6 +810,10 @@ namespace isc { namespace dhcp {
symbol_type
make_CSV_FORMAT (const location_type& l);
static inline
symbol_type
make_ALWAYS_SEND (const location_type& l);
static inline
symbol_type
make_RECORD_TYPES (const location_type& l);
......@@ -1106,10 +1110,6 @@ namespace isc { namespace dhcp {
symbol_type
make_DHCPDDNS (const location_type& l);
static inline
symbol_type
make_CONTROL_AGENT (const location_type& l);
static inline
symbol_type
make_TOPLEVEL_JSON (const location_type& l);
......@@ -1375,8 +1375,8 @@ namespace isc { namespace dhcp {
enum
{
yyeof_ = 0,
yylast_ = 748, ///< Last index in yytable_.
yynnts_ = 317, ///< Number of nonterminal symbols.
yylast_ = 751, ///< Last index in yytable_.
yynnts_ = 316, ///< Number of nonterminal symbols.
yyfinal_ = 24, ///< Termination state number.
yyterror_ = 1,
yyerrcode_ = 256,
......@@ -1476,8 +1476,8 @@ namespace isc { namespace dhcp {
case 199: // socket_type
case 209: // db_type
case 286: // hr_mode
case 418: // ncr_protocol_value
case 426: // replace_client_name_value
case 419: // ncr_protocol_value
case 427: // replace_client_name_value
value.copy< ElementPtr > (other.value);
break;
......@@ -1519,8 +1519,8 @@ namespace isc { namespace dhcp {
case 199: // socket_type
case 209: // db_type
case 286: // hr_mode
case 418: // ncr_protocol_value
case 426: // replace_client_name_value
case 419: // ncr_protocol_value
case 427: // replace_client_name_value
value.copy< ElementPtr > (v);
break;
......@@ -1621,8 +1621,8 @@ namespace isc { namespace dhcp {
case 199: // socket_type
case 209: // db_type
case 286: // hr_mode
case 418: // ncr_protocol_value
case 426: // replace_client_name_value
case 419: // ncr_protocol_value
case 427: // replace_client_name_value
value.template destroy< ElementPtr > ();
break;
......@@ -1670,8 +1670,8 @@ namespace isc { namespace dhcp {
case 199: // socket_type
case 209: // db_type
case 286: // hr_mode
case 418: // ncr_protocol_value
case 426: // replace_client_name_value
case 419: // ncr_protocol_value
case 427: // replace_client_name_value
value.move< ElementPtr > (s.value);
break;
......@@ -2077,6 +2077,12 @@ namespace isc { namespace dhcp {
return symbol_type (token::TOKEN_CSV_FORMAT, l);
}
Dhcp4Parser::symbol_type
Dhcp4Parser::make_ALWAYS_SEND (const location_type& l)
{
return symbol_type (token::TOKEN_ALWAYS_SEND, l);
}
Dhcp4Parser::symbol_type
Dhcp4Parser::make_RECORD_TYPES (const location_type& l)
{
......@@ -2521,12 +2527,6 @@ namespace isc { namespace dhcp {
return symbol_type (token::TOKEN_DHCPDDNS, l);
}
Dhcp4Parser::symbol_type
Dhcp4Parser::make_CONTROL_AGENT (const location_type& l)
{
return symbol_type (token::TOKEN_CONTROL_AGENT, l);
}
Dhcp4Parser::symbol_type
Dhcp4Parser::make_TOPLEVEL_JSON (const location_type& l)
{
......
......@@ -96,6 +96,7 @@ using namespace std;
CODE "code"
SPACE "space"
CSV_FORMAT "csv-format"
ALWAYS_SEND "always-send"
RECORD_TYPES "record-types"
ENCAPSULATE "encapsulate"
ARRAY "array"
......@@ -1134,6 +1135,7 @@ option_data_param: option_data_name
| option_data_code
| option_data_space
| option_data_csv_format
| option_data_always_send
| unknown_map_entry
;
......@@ -1156,6 +1158,11 @@ option_data_csv_format: CSV_FORMAT COLON BOOLEAN {
ctx.stack_.back()->set("csv-format", space);
};
option_data_always_send: ALWAYS_SEND COLON BOOLEAN {
ElementPtr persist(new BoolElement($3, ctx.loc2pos(@3)));
ctx.stack_.back()->set("always-send", persist);
};
// ---- pools ------------------------------------
// This defines the "pools": [ ... ] entry that may appear in subnet4.
......
......@@ -1226,21 +1226,37 @@ Dhcpv4Srv::appendRequestedOptions(Dhcpv4Exchange& ex) {
}
Pkt4Ptr query = ex.getQuery();
Pkt4Ptr resp = ex.getResponse();
std::vector<uint8_t> requested_opts;
// try to get the 'Parameter Request List' option which holds the
// codes of requested options.
OptionUint8ArrayPtr option_prl = boost::dynamic_pointer_cast<
OptionUint8Array>(query->getOption(DHO_DHCP_PARAMETER_REQUEST_LIST));
// If there is no PRL option in the message from the client then
// there is nothing to do.
if (!option_prl) {
return;
// Get the codes of requested options.
if (option_prl) {
requested_opts = option_prl->getValues();
}
// Iterate on the configured option list to add persistent options
for (CfgOptionList::const_iterator copts = co_list.begin();
copts != co_list.end(); ++copts) {
const OptionContainerPtr& opts = (*copts)->getAll(DHCP4_OPTION_SPACE);
if (!opts) {
continue;
}
// Get persistent options
const OptionContainerPersistIndex& idx = opts->get<2>();
const OptionContainerPersistRange& range = idx.equal_range(true);
for (OptionContainerPersistIndex::const_iterator desc = range.first;
desc != range.second; ++desc) {
// Add the persistent option code to requested options
if (desc->option_) {
uint8_t code = static_cast<uint8_t>(desc->option_->getType());
requested_opts.push_back(code);
}
}
}
Pkt4Ptr resp = ex.getResponse();
// Get the codes of requested options.
const std::vector<uint8_t>& requested_opts = option_prl->getValues();
// For each requested option code get the instance of the option
// to be returned to the client.
for (std::vector<uint8_t>::const_iterator opt = requested_opts.begin();
......@@ -1288,15 +1304,39 @@ Dhcpv4Srv::appendRequestedVendorOptions(Dhcpv4Exchange& ex) {
}
uint32_t vendor_id = vendor_req->getVendorId();
std::vector<uint8_t> requested_opts;
// Let's try to get ORO within that vendor-option
/// @todo This is very specific to vendor-id=4491 (Cable Labs). Other
/// vendors may have different policies.
OptionUint8ArrayPtr oro =
boost::dynamic_pointer_cast<OptionUint8Array>(vendor_req->getOption(DOCSIS3_V4_ORO));
// Get the list of options that client requested.
if (oro) {
requested_opts = oro->getValues();
}
// Iterate on the configured option list to add persistent options
for (CfgOptionList::const_iterator copts = co_list.begin();
copts != co_list.end(); ++copts) {
const OptionContainerPtr& opts = (*copts)->getAll(vendor_id);
if (!opts) {
continue;
}
// Get persistent options
const OptionContainerPersistIndex& idx = opts->get<2>();
const OptionContainerPersistRange& range = idx.equal_range(true);
for (OptionContainerPersistIndex::const_iterator desc = range.first;
desc != range.second; ++desc) {
// Add the persistent option code to requested options
if (desc->option_) {
uint8_t code = static_cast<uint8_t>(desc->option_->getType());
requested_opts.push_back(code);
}
}
}
// Option ORO not found. Don't do anything then.
if (!oro) {
// If there is nothing to add don't do anything then.
if (requested_opts.empty()) {
return;
}
......@@ -1304,8 +1344,6 @@ Dhcpv4Srv::appendRequestedVendorOptions(Dhcpv4Exchange& ex) {
// Get the list of options that client requested.
bool added = false;
const std::vector<uint8_t>& requested_opts = oro->getValues();
for (std::vector<uint8_t>::const_iterator code = requested_opts.begin();
code != requested_opts.end(); ++code) {
if (!vendor_rsp->getOption(*code)) {
......
// Generated 201707141116
// Generated 201707281415
// A Bison parser, made by GNU Bison 3.0.4.
// Locations for Bison parsers in C++
......
// Generated 201707141116
// Generated 201707281415
// A Bison parser, made by GNU Bison 3.0.4.
// Positions for Bison parsers in C++
......
......@@ -45,7 +45,8 @@ const SimpleDefaults SimpleParser4::OPTION4_DEF_DEFAULTS = {
/// for those option-data declarations.
const SimpleDefaults SimpleParser4::OPTION4_DEFAULTS = {
{ "space", Element::string, "dhcp4"},
{ "csv-format", Element::boolean, "true"}
{ "csv-format", Element::boolean, "true"},
{ "always-send", Element::boolean, "false"}
};
/// @brief This table defines default global values for DHCPv4
......
// Generated 201707141116
// Generated 201707281415
// A Bison parser, made by GNU Bison 3.0.4.
// Stack handling for Bison parsers in C++
......
......@@ -2820,7 +2820,7 @@ TEST_F(Dhcp4ParserTest, optionDataSinglePool) {
// Expect a single option with the code equal to 100.
ASSERT_EQ(1, std::distance(range.first, range.second));
const uint8_t foo_expected[] = {
0xAB, 0xCD, 0xEF, 0x01, 0x05
0xAB, 0xCD, 0xEF, 0x01, 0x05
};
// Check if option is valid in terms of code and carried data.
testOption(*range.first, 56, foo_expected, sizeof(foo_expected));
......@@ -2893,7 +2893,7 @@ TEST_F(Dhcp4ParserTest, optionDataMultiplePools) {
// Expect a single option with the code equal to 100.
ASSERT_EQ(1, std::distance(range1.first, range1.second));
const uint8_t foo_expected[] = {
0xAB, 0xCD, 0xEF, 0x01, 0x05
0xAB, 0xCD, 0xEF, 0x01, 0x05
};
// Check if option is valid in terms of code and carried data.
testOption(*range1.first, 56, foo_expected, sizeof(foo_expected));
......@@ -2909,8 +2909,8 @@ TEST_F(Dhcp4ParserTest, optionDataMultiplePools) {
const OptionContainerTypeIndex& idx2 = options2->get<1>();
std::pair<OptionContainerTypeIndex::const_iterator,
OptionContainerTypeIndex::const_iterator> range2 =
idx2.equal_range(23);
OptionContainerTypeIndex::const_iterator> range2 =
idx2.equal_range(23);
ASSERT_EQ(1,