mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-22 01:49:48 +00:00
[5016] Merged pull request #24 from github24 and squashed it.
This change includes the code implemented by the pull request submitter as well as Marcin's fixes/changes on top of it, but without a review.
This commit is contained in:
parent
dad932d544
commit
0c0ca2fde7
6
AUTHORS
6
AUTHORS
@ -117,6 +117,12 @@ We have received the following contributions:
|
||||
strings to avoid issues with creation of the database
|
||||
when MySQL server operates in ANSI_QUOTES mode.
|
||||
|
||||
- Cristian Secareanu, Qualitance
|
||||
2016-10: Support for IPv6 prefix and PDEXCLUDE option
|
||||
|
||||
- Andrei Pavel, Qualitance
|
||||
2016-10: Support for DHCPv6 options defined in RFC6603 and RFC7598
|
||||
|
||||
Kea uses log4cplus (http://sourceforge.net/projects/log4cplus/) for logging,
|
||||
Boost (http://www.boost.org/) library for almost everything, and can use Botan
|
||||
(http://botan.randombit.net/) or OpenSSL (https://www.openssl.org/) for
|
||||
|
@ -29,6 +29,7 @@ nobase_dist_doc_DATA += examples/kea6/pgsql-reservations.json
|
||||
nobase_dist_doc_DATA += examples/kea6/reservations.json
|
||||
nobase_dist_doc_DATA += examples/kea6/several-subnets.json
|
||||
nobase_dist_doc_DATA += examples/kea6/simple.json
|
||||
nobase_dist_doc_DATA += examples/kea6/softwire46.json
|
||||
nobase_dist_doc_DATA += examples/kea6/stateless.json
|
||||
|
||||
devel:
|
||||
|
91
doc/examples/kea6/softwire46.json
Normal file
91
doc/examples/kea6/softwire46.json
Normal file
@ -0,0 +1,91 @@
|
||||
# This is an example configuration file for DHCPv6 server in Kea.
|
||||
# It demonstrates how user can specify values for Softwire options
|
||||
# defined in RFC 7598.
|
||||
|
||||
{ "Dhcp6":
|
||||
|
||||
{
|
||||
# Kea is told to listen on ethX interface only.
|
||||
"interfaces-config": {
|
||||
"interfaces": [ "ethX" ]
|
||||
},
|
||||
|
||||
# We need to specify lease type. As of May 2014, three backends are supported:
|
||||
# memfile, mysql and pgsql. We'll just use memfile, because it doesn't require
|
||||
# any prior set up.
|
||||
"lease-database": {
|
||||
"type": "memfile"
|
||||
},
|
||||
|
||||
# Addresses will be assigned with preferred and valid lifetimes
|
||||
# being 3000 and 4000, respectively. Client is told to start
|
||||
# renewing after 1000 seconds. If the server does not respond
|
||||
# after 2000 seconds since the lease was granted, client is supposed
|
||||
# to start REBIND procedure (emergency renewal that allows switching
|
||||
# to a different server).
|
||||
"preferred-lifetime": 3000,
|
||||
"valid-lifetime": 4000,
|
||||
"renew-timer": 1000,
|
||||
"rebind-timer": 2000,
|
||||
|
||||
# The following list defines subnets. Each subnet consists of at
|
||||
# least subnet and pool entries.
|
||||
"subnet6": [
|
||||
{
|
||||
"pools": [ { "pool": "2001:db8:1::/80" } ],
|
||||
"subnet": "2001:db8:1::/64",
|
||||
"interface": "ethX",
|
||||
# Include MAP-E Container option for hosts connected to this subnet.
|
||||
"option-data": [
|
||||
{
|
||||
"name": "s46-cont-mape"
|
||||
}
|
||||
],
|
||||
# Send host specific softwire options.
|
||||
"reservations": [
|
||||
{
|
||||
"duid": "01:02:03:04:05:06:07:08:09:0A",
|
||||
"option-data": [
|
||||
# These two options will be included in the MAP-E Container
|
||||
{
|
||||
"space": "s46-cont-mape-options",
|
||||
"name": "s46-rule",
|
||||
"data": "1, 0, 24, 192.0.2.0, 2001:db8:1::/64"
|
||||
},
|
||||
{
|
||||
"space": "s46-cont-mape-options",
|
||||
"name": "s46-br",
|
||||
"data": "2001:db8:cafe::1"
|
||||
},
|
||||
# This option will be included in the S46 Rule option.
|
||||
{
|
||||
"space": "s46-rule-options",
|
||||
"name": "s46-portparams",
|
||||
"data": "0, 3/4"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
# The following configures logging. Kea will log all debug messages
|
||||
# to /var/log/kea-debug.log file.
|
||||
"Logging": {
|
||||
"loggers": [
|
||||
{
|
||||
"name": "kea-dhcp6",
|
||||
"output_options": [
|
||||
{
|
||||
"output": "/var/log/kea-debug.log"
|
||||
}
|
||||
],
|
||||
"debuglevel": 99,
|
||||
"severity": "DEBUG"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -469,7 +469,7 @@ If a timeout is given though, it should be an integer greater than zero.
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section id="hosts-storage4">
|
||||
<section id="hosts4-storage">
|
||||
<title>Hosts Storage</title>
|
||||
<para>Kea is also able to store information about host reservations in the
|
||||
database. The hosts database configuration uses the same syntax as the lease
|
||||
@ -1216,6 +1216,8 @@ It is merely echoed by the server
|
||||
<row><entry>fqdn</entry><entry>Fully qualified domain name (e.g. www.example.com)</entry></row>
|
||||
<row><entry>ipv4-address</entry><entry>IPv4 address in the usual dotted-decimal notation (e.g. 192.0.2.1)</entry></row>
|
||||
<row><entry>ipv6-address</entry><entry>IPv6 address in the usual colon notation (e.g. 2001:db8::1)</entry></row>
|
||||
<row><entry>ipv6-prefix</entry><entry>IPv6 prefix and prefix length specified using CIDR notation, e.g. 2001:db8:1::/64. This data type is used to represent an 8-bit field conveying a prefix length and the variable length prefix value</entry></row>
|
||||
<row><entry>psid</entry><entry>PSID and PSID length separated by a slash, e.g. 3/4 specifies PSID=3 and PSID length=4. In the wire format it is represented by an 8-bit field carrying PSID length (in this case equal to 4) and the 16-bits long PSID value field (in this case equal to "0011000000000000b" using binary notation). Allowed values for a PSID length are 0 to 16. See <ulink url="http://tools.ietf.org/html/rfc7597">RFC 7597</ulink> for the details about the PSID wire representation</entry></row>
|
||||
<row><entry>record</entry><entry>Structured data that may comprise any types (except "record" and "empty")</entry></row>
|
||||
<row><entry>string</entry><entry>Any text</entry></row>
|
||||
<row><entry>uint8</entry><entry>8 bit unsigned integer with allowed values 0 to 255</entry></row>
|
||||
|
@ -799,7 +799,6 @@ temporarily override a list of interface names and listen on all interfaces.
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<!-- @todo: add real meat to the prefix delegation config this is just place holder stuff -->
|
||||
<title>Subnet and Prefix Delegation Pools</title>
|
||||
<para>
|
||||
Subnets may also be configured to delegate prefixes, as defined in
|
||||
@ -833,6 +832,42 @@ temporarily override a list of interface names and listen on all interfaces.
|
||||
...
|
||||
}</screen>
|
||||
</para>
|
||||
|
||||
</section>
|
||||
|
||||
<section id="pd-exclude-option">
|
||||
<title>Prefix Exclude Option</title>
|
||||
<para>
|
||||
For each delegated prefix the delegating router may choose to exclude
|
||||
a single prefix out of the delegated prefix as specified in the
|
||||
<ulink url="http://tools.ietf.org/html/rfc6603"> RFC 6603</ulink>.
|
||||
The requesting router must not assign the excluded prefix to any
|
||||
of its downstream interfaces and it is intended to be used on a
|
||||
link through which the delegating router exchanges DHCPv6 messages with
|
||||
the requesting router. The configuration example below demonstrates how
|
||||
to specify an excluded prefix within a prefix pool definition. The
|
||||
excluded prefix "2001:db8:1:babe:cafe:80::/72" will be sent to a
|
||||
requesting router which includes Prefix Exclude option in the ORO, and
|
||||
which is delegated a prefix from this pool.
|
||||
</para>
|
||||
<screen>
|
||||
"Dhcp6": {
|
||||
"subnet6": [
|
||||
{
|
||||
"subnet": "2001:db8:1::/48",
|
||||
"pd-pools": [
|
||||
{
|
||||
"prefix": "2001:db8:1:8000::",
|
||||
"prefix-len": 48,
|
||||
"delegated-len": 64,
|
||||
"excluded-prefix": "2001:db8:1:babe:cafe:80::",
|
||||
"excluded-prefix-len": 72
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
</screen>
|
||||
</section>
|
||||
|
||||
<section id="dhcp6-std-options">
|
||||
@ -1110,11 +1145,21 @@ temporarily override a list of interface names and listen on all interfaces.
|
||||
<row><entry>bootfile-param</entry><entry>60</entry><entry>binary</entry><entry>false</entry></row>
|
||||
<row><entry>client-arch-type</entry><entry>61</entry><entry>uint16</entry><entry>true</entry></row>
|
||||
<row><entry>nii</entry><entry>62</entry><entry>record (uint8, uint8, uint8)</entry><entry>false</entry></row>
|
||||
<row><entry>aftr-name</entry><entry>64</entry><entry>fqdn</entry><entry>false</entry></row>
|
||||
<row><entry>erp-local-domain-name</entry><entry>65</entry><entry>fqdn</entry><entry>false</entry></row>
|
||||
<row><entry>rsoo</entry><entry>66</entry><entry>empty</entry><entry>false</entry></row>
|
||||
<row><entry>pd-exclude</entry><entry>67</entry><entry>binary</entry><entry>false</entry></row>
|
||||
<row><entry>client-linklayer-addr</entry><entry>79</entry><entry>binary</entry><entry>false</entry></row>
|
||||
<!-- <row><entry>dhcpv4-message</entry><entry>87</entry><entry>binary</entry><entry>false</entry></row> -->
|
||||
<row><entry>dhcp4o6-server-addr</entry><entry>88</entry><entry>ipv6-address</entry><entry>true</entry></row>
|
||||
<row><entry>s46-rule</entry><entry>89</entry><entry>record (uint8, uint8, uint8, ipv4-address, ipv6-prefix)</entry><entry>false</entry></row>
|
||||
<row><entry>s46-br</entry><entry>90</entry><entry>ipv6-address</entry><entry>false</entry></row>
|
||||
<row><entry>s46-dmr</entry><entry>91</entry><entry>ipv6-prefix</entry><entry>false</entry></row>
|
||||
<row><entry>s46-v4v6bind</entry><entry>92</entry><entry>record (ipv4-address, ipv6-prefix)</entry><entry>false</entry></row>
|
||||
<row><entry>s46-portparams</entry><entry>93</entry><entry>record(uint8, psid)</entry><entry>false</entry></row>
|
||||
<row><entry>s46-cont-mape</entry><entry>94</entry><entry>empty</entry><entry>false</entry></row>
|
||||
<row><entry>s46-cont-mapt</entry><entry>95</entry><entry>empty</entry><entry>false</entry></row>
|
||||
<row><entry>s46-cont-lw</entry><entry>96</entry><entry>empty</entry><entry>false</entry></row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
@ -1142,6 +1187,149 @@ temporarily override a list of interface names and listen on all interfaces.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id="s46-options">
|
||||
<title>Common Softwire46 Options</title>
|
||||
<para>
|
||||
Softwire46 options are involved in IPv4 over IPv6 provisioning by
|
||||
means of tunneling or translation as specified in the
|
||||
<ulink url="http://tools.ietf.org/html/rfc7598">RFC 7598</ulink>.
|
||||
The following sections provide configuration examples of these
|
||||
options.
|
||||
</para>
|
||||
|
||||
<section id="s46-containers">
|
||||
<title>Softwire46 Container Options</title>
|
||||
<para>
|
||||
S46 container options group rules and optional port parameters
|
||||
for a specified domain. There are three container options specified
|
||||
in the "dhcp6" (top level) option space: MAP-E Container option,
|
||||
MAP-T Container option and S46 Lieghtweight 4over6 Container option.
|
||||
These options only contain encapsulated options specified below.
|
||||
They do not include any data fields.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
In order to configure the server to send specific container option
|
||||
along with all encapsulated options, the container option must be
|
||||
included in the server configuration as shown below:
|
||||
<screen>
|
||||
"Dhcp6": {
|
||||
...
|
||||
"option-data": [
|
||||
{
|
||||
"name": "s46-cont-mape"
|
||||
} ],
|
||||
...
|
||||
}
|
||||
</screen>
|
||||
|
||||
This configuration will cause the server to include MAP-E Container
|
||||
option to the client. Use "s46-cont-mapt" or "s46-cont-lw" for the
|
||||
MAP-T Container and S46 Lightweight 4over6 Container options
|
||||
respectively.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
All remaining softwire options described below are included in one
|
||||
of the container options. Thus, they have to be included in appropriate
|
||||
option spaces by selecting a "space" name, which specifies in which
|
||||
option they are supposed to be included.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>S46 Rule Option</title>
|
||||
<para>
|
||||
The S46 Rule option is used for conveying the Basic Mapping Rule (BMR)
|
||||
and Forwarding Mapping Rule (FMR).
|
||||
<screen>
|
||||
{
|
||||
"space": "s46-cont-mape-options",
|
||||
"name": "s46-rule",
|
||||
"data": "1, 0, 24, 192.0.2.0, 2001:db8:1::/64"
|
||||
}
|
||||
</screen>
|
||||
Other possible "space" value is "s46-cont-mapt-options".
|
||||
</para>
|
||||
|
||||
</section>
|
||||
<section>
|
||||
<title>S46 BR Option</title>
|
||||
<para>
|
||||
The S46 BR option is used to convey the IPv6 address of the
|
||||
Border Relay. This option is mandatory in the MAP-E
|
||||
Container option and not permitted in the MAP-T and
|
||||
S46 Lightweight 4over6 Container options.
|
||||
<screen>
|
||||
{
|
||||
"space": "s46-cont-mape-options",
|
||||
"name": "s46-br",
|
||||
"data": "2001:db8:cafe::1",
|
||||
}
|
||||
</screen>
|
||||
Other possible "space" value is "s46-cont-lw-options".
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>S46 DMR Option</title>
|
||||
<para>
|
||||
The S46 DMR option is used to convey values for the Default
|
||||
Mapping Rule (DMR). This option is mandatory in the MAP-T
|
||||
container option and not permitted in the MAP-E and S46
|
||||
Lightweight 4over6 Container options.
|
||||
<screen>
|
||||
{
|
||||
"space": "s46-cont-mapt-options",
|
||||
"name": "s46-dmr",
|
||||
"data": "2001:db8:cafe::/64",
|
||||
}
|
||||
</screen>
|
||||
This option must not be included in other containers.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>S46 IPv4/IPv6 Address Binding option.</title>
|
||||
<para>
|
||||
The S46 IPv4/IPv6 Address Binding option may be used to specify
|
||||
the full or shared IPv4 address of the Customer Edge (CE).
|
||||
The IPv6 prefix field is used by the CE to identify the
|
||||
correct prefix to use for the tunnel source.
|
||||
<screen>
|
||||
{
|
||||
"space": "s46-cont-lw",
|
||||
"name": "s46-v4v6bind",
|
||||
"data": "192.0.2.3, 2001:db8:1:cafe::/64"
|
||||
}
|
||||
</screen>
|
||||
This option must not be included in other containers.
|
||||
</para>
|
||||
</section>
|
||||
<section>
|
||||
<title>S46 Port Parameters</title>
|
||||
<para>
|
||||
The S46 Port Parameters option specifies optional port set
|
||||
information that MAY be provided to CEs
|
||||
<screen>
|
||||
{
|
||||
"space": "s46-rule-options",
|
||||
"name": "s46-portparams",
|
||||
"data": "2, 3/4",
|
||||
}
|
||||
</screen>
|
||||
Other possible "space" value is "s46-v4v6bind" to include
|
||||
this option in the S46 IPv4/IPv6 Address Binding option.
|
||||
</para>
|
||||
<para>
|
||||
Note that the second value in the example above specifies the
|
||||
PSID and PSID length fields in the format of PSID/PSID length.
|
||||
This is equivalent to the values of PSID-len=4 and
|
||||
PSID=12288 conveyed in the S46 Port Parameters option.
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section id="dhcp6-custom-options">
|
||||
<title>Custom DHCPv6 Options</title>
|
||||
<para>It is possible to define options in addition to the standard ones.
|
||||
@ -2371,8 +2559,8 @@ should include options from the isc option space:
|
||||
"pd-pools": [
|
||||
{
|
||||
"prefix": "2001:db8:1:8000::",
|
||||
"prefix-len": 56,
|
||||
"delegated-len": 64
|
||||
"prefix-len": 48,
|
||||
"delegated-len": 64,
|
||||
}
|
||||
],
|
||||
<userinput>"reservations": [
|
||||
@ -3895,6 +4083,12 @@ If not specified, the default value is:
|
||||
to echo back the options, checks whether an option is RSOO-enabled,
|
||||
ability to mark additional options as RSOO-enabled.</simpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<simpara><emphasis>Prefix Exclude Option for DHCPv6-based Prefix
|
||||
Delegation</emphasis>,
|
||||
<ulink url="http://tools.ietf.org/html/rfc6603">RFC
|
||||
6603</ulink>: Prefix Exclude option is supported.</simpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<simpara><emphasis>Client Link-Layer Address Option in
|
||||
DHCPv6</emphasis>,
|
||||
@ -3909,6 +4103,13 @@ If not specified, the default value is:
|
||||
7550</ulink>: All recommendations related to the DHCPv6 server
|
||||
operation are supported.</simpara>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<simpara><emphasis>DHCPv6 Options for Configuration of Softwire
|
||||
Address and Port-Mapped Clients</emphasis>,
|
||||
<ulink url="http://tools.ietf.org/html/rfc7598">RFC
|
||||
7598</ulink>: All options specified in this specification are
|
||||
supported by the DHCPv6 server.</simpara>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</section>
|
||||
|
||||
|
@ -1209,7 +1209,7 @@ Dhcpv4Srv::appendRequestedOptions(Dhcpv4Exchange& ex) {
|
||||
// Iterate on the configured option list
|
||||
for (CfgOptionList::const_iterator copts = co_list.begin();
|
||||
copts != co_list.end(); ++copts) {
|
||||
OptionDescriptor desc = (*copts)->get("dhcp4", *opt);
|
||||
OptionDescriptor desc = (*copts)->get(DHCP4_OPTION_SPACE, *opt);
|
||||
// Got it: add it and jump to the outer loop
|
||||
if (desc.option_) {
|
||||
resp->addOption(desc.option_);
|
||||
@ -1320,7 +1320,8 @@ Dhcpv4Srv::appendBasicOptions(Dhcpv4Exchange& ex) {
|
||||
// Check whether option has been configured.
|
||||
for (CfgOptionList::const_iterator copts = co_list.begin();
|
||||
copts != co_list.end(); ++copts) {
|
||||
OptionDescriptor desc = (*copts)->get("dhcp4", required_options[i]);
|
||||
OptionDescriptor desc = (*copts)->get(DHCP4_OPTION_SPACE,
|
||||
required_options[i]);
|
||||
if (desc.option_) {
|
||||
resp->addOption(desc.option_);
|
||||
break;
|
||||
|
@ -146,7 +146,7 @@ public:
|
||||
std::map<std::string, std::string> params;
|
||||
if (parameter == "name") {
|
||||
params["name"] = param_value;
|
||||
params["space"] = "dhcp4";
|
||||
params["space"] = DHCP4_OPTION_SPACE;
|
||||
params["code"] = "56";
|
||||
params["data"] = "ABCDEF0105";
|
||||
params["csv-format"] = "False";
|
||||
@ -158,19 +158,19 @@ public:
|
||||
params["csv-format"] = "False";
|
||||
} else if (parameter == "code") {
|
||||
params["name"] = "dhcp-message";
|
||||
params["space"] = "dhcp4";
|
||||
params["space"] = DHCP4_OPTION_SPACE;
|
||||
params["code"] = param_value;
|
||||
params["data"] = "ABCDEF0105";
|
||||
params["csv-format"] = "False";
|
||||
} else if (parameter == "data") {
|
||||
params["name"] = "dhcp-message";
|
||||
params["space"] = "dhcp4";
|
||||
params["space"] = DHCP4_OPTION_SPACE;
|
||||
params["code"] = "56";
|
||||
params["data"] = param_value;
|
||||
params["csv-format"] = "False";
|
||||
} else if (parameter == "csv-format") {
|
||||
params["name"] = "dhcp-message";
|
||||
params["space"] = "dhcp4";
|
||||
params["space"] = DHCP4_OPTION_SPACE;
|
||||
params["code"] = "56";
|
||||
params["data"] = "ABCDEF0105";
|
||||
params["csv-format"] = param_value;
|
||||
@ -251,7 +251,7 @@ public:
|
||||
<< "does not exist in Config Manager";
|
||||
}
|
||||
OptionContainerPtr options =
|
||||
subnet->getCfgOption()->getAll("dhcp4");
|
||||
subnet->getCfgOption()->getAll(DHCP4_OPTION_SPACE);
|
||||
if (expected_options_count != options->size()) {
|
||||
ADD_FAILURE() << "The number of options in the subnet '"
|
||||
<< subnet_address.toText() << "' is different "
|
||||
@ -475,7 +475,7 @@ public:
|
||||
template<typename ReturnType>
|
||||
ReturnType
|
||||
retrieveOption(const Host& host, const uint16_t option_code) const {
|
||||
return (retrieveOption<ReturnType>(host, "dhcp4", option_code));
|
||||
return (retrieveOption<ReturnType>(host, DHCP4_OPTION_SPACE, option_code));
|
||||
}
|
||||
|
||||
/// @brief Retrieve an option associated with a host.
|
||||
@ -1823,7 +1823,7 @@ TEST_F(Dhcp4ParserTest, optionStandardDefOverride) {
|
||||
ElementPtr json = Element::fromJSON(config);
|
||||
|
||||
OptionDefinitionPtr def = CfgMgr::instance().getStagingCfg()->
|
||||
getCfgOptionDef()->get("dhcp4", 109);
|
||||
getCfgOptionDef()->get(DHCP4_OPTION_SPACE, 109);
|
||||
ASSERT_FALSE(def);
|
||||
|
||||
// Use the configuration string to create new option definition.
|
||||
@ -1834,7 +1834,7 @@ TEST_F(Dhcp4ParserTest, optionStandardDefOverride) {
|
||||
|
||||
// The option definition should now be available in the CfgMgr.
|
||||
def = CfgMgr::instance().getStagingCfg()->
|
||||
getCfgOptionDef()->get("dhcp4", 109);
|
||||
getCfgOptionDef()->get(DHCP4_OPTION_SPACE, 109);
|
||||
ASSERT_TRUE(def);
|
||||
|
||||
// Check the option data.
|
||||
@ -1885,7 +1885,7 @@ TEST_F(Dhcp4ParserTest, optionStandardDefOverride) {
|
||||
checkResult(status, 0);
|
||||
|
||||
def = CfgMgr::instance().getStagingCfg()->
|
||||
getCfgOptionDef()->get("dhcp4", 213);
|
||||
getCfgOptionDef()->get(DHCP4_OPTION_SPACE, 213);
|
||||
ASSERT_TRUE(def);
|
||||
|
||||
// Check the option data.
|
||||
@ -1927,10 +1927,10 @@ TEST_F(Dhcp4ParserTest, optionDataDefaultsGlobal) {
|
||||
Subnet4Ptr subnet = CfgMgr::instance().getStagingCfg()->
|
||||
getCfgSubnets4()->selectSubnet(IOAddress("192.0.2.200"));
|
||||
ASSERT_TRUE(subnet);
|
||||
OptionContainerPtr options = subnet->getCfgOption()->getAll("dhcp4");
|
||||
OptionContainerPtr options = subnet->getCfgOption()->getAll(DHCP4_OPTION_SPACE);
|
||||
ASSERT_EQ(0, options->size());
|
||||
|
||||
options = CfgMgr::instance().getStagingCfg()->getCfgOption()->getAll("dhcp4");
|
||||
options = CfgMgr::instance().getStagingCfg()->getCfgOption()->getAll(DHCP4_OPTION_SPACE);
|
||||
ASSERT_EQ(2, options->size());
|
||||
|
||||
// Get the search index. Index #1 is to search using option code.
|
||||
@ -1994,13 +1994,13 @@ TEST_F(Dhcp4ParserTest, optionDataDefaultsSubnet) {
|
||||
|
||||
// These options are subnet options
|
||||
OptionContainerPtr options =
|
||||
CfgMgr::instance().getStagingCfg()->getCfgOption()->getAll("dhcp4");
|
||||
CfgMgr::instance().getStagingCfg()->getCfgOption()->getAll(DHCP4_OPTION_SPACE);
|
||||
ASSERT_EQ(0, options->size());
|
||||
|
||||
Subnet4Ptr subnet = CfgMgr::instance().getStagingCfg()->
|
||||
getCfgSubnets4()->selectSubnet(IOAddress("192.0.2.200"));
|
||||
ASSERT_TRUE(subnet);
|
||||
options = subnet->getCfgOption()->getAll("dhcp4");
|
||||
options = subnet->getCfgOption()->getAll(DHCP4_OPTION_SPACE);
|
||||
ASSERT_EQ(2, options->size());
|
||||
|
||||
// Get the search index. Index #1 is to search using option code.
|
||||
@ -2078,7 +2078,7 @@ TEST_F(Dhcp4ParserTest, optionDataTwoSpaces) {
|
||||
// Options should be now available
|
||||
// Try to get the option from the space dhcp4.
|
||||
OptionDescriptor desc1 =
|
||||
CfgMgr::instance().getStagingCfg()->getCfgOption()->get("dhcp4", 56);
|
||||
CfgMgr::instance().getStagingCfg()->getCfgOption()->get(DHCP4_OPTION_SPACE, 56);
|
||||
ASSERT_TRUE(desc1.option_);
|
||||
EXPECT_EQ(56, desc1.option_->getType());
|
||||
// Try to get the option from the space isc.
|
||||
@ -2207,13 +2207,13 @@ TEST_F(Dhcp4ParserTest, optionDataEncapsulate) {
|
||||
|
||||
// We should have one option available.
|
||||
OptionContainerPtr options =
|
||||
CfgMgr::instance().getStagingCfg()->getCfgOption()->getAll("dhcp4");
|
||||
CfgMgr::instance().getStagingCfg()->getCfgOption()->getAll(DHCP4_OPTION_SPACE);
|
||||
ASSERT_TRUE(options);
|
||||
ASSERT_EQ(1, options->size());
|
||||
|
||||
// Get the option.
|
||||
OptionDescriptor desc =
|
||||
CfgMgr::instance().getStagingCfg()->getCfgOption()->get("dhcp4", 222);
|
||||
CfgMgr::instance().getStagingCfg()->getCfgOption()->get(DHCP4_OPTION_SPACE, 222);
|
||||
EXPECT_TRUE(desc.option_);
|
||||
EXPECT_EQ(222, desc.option_->getType());
|
||||
|
||||
@ -2267,7 +2267,7 @@ TEST_F(Dhcp4ParserTest, optionDataInSingleSubnet) {
|
||||
Subnet4Ptr subnet = CfgMgr::instance().getStagingCfg()->
|
||||
getCfgSubnets4()->selectSubnet(IOAddress("192.0.2.24"));
|
||||
ASSERT_TRUE(subnet);
|
||||
OptionContainerPtr options = subnet->getCfgOption()->getAll("dhcp4");
|
||||
OptionContainerPtr options = subnet->getCfgOption()->getAll(DHCP4_OPTION_SPACE);
|
||||
ASSERT_EQ(2, options->size());
|
||||
|
||||
// Get the search index. Index #1 is to search using option code.
|
||||
@ -2303,7 +2303,7 @@ TEST_F(Dhcp4ParserTest, optionDataBoolean) {
|
||||
// Create configuration. Use standard option 19 (ip-forwarding).
|
||||
std::map<std::string, std::string> params;
|
||||
params["name"] = "ip-forwarding";
|
||||
params["space"] = "dhcp4";
|
||||
params["space"] = DHCP4_OPTION_SPACE;
|
||||
params["code"] = "19";
|
||||
params["data"] = "true";
|
||||
params["csv-format"] = "true";
|
||||
@ -2414,7 +2414,7 @@ TEST_F(Dhcp4ParserTest, optionDataInMultipleSubnets) {
|
||||
Subnet4Ptr subnet1 = CfgMgr::instance().getStagingCfg()->
|
||||
getCfgSubnets4()->selectSubnet(IOAddress("192.0.2.100"));
|
||||
ASSERT_TRUE(subnet1);
|
||||
OptionContainerPtr options1 = subnet1->getCfgOption()->getAll("dhcp4");
|
||||
OptionContainerPtr options1 = subnet1->getCfgOption()->getAll(DHCP4_OPTION_SPACE);
|
||||
ASSERT_EQ(1, options1->size());
|
||||
|
||||
// Get the search index. Index #1 is to search using option code.
|
||||
@ -2439,7 +2439,7 @@ TEST_F(Dhcp4ParserTest, optionDataInMultipleSubnets) {
|
||||
Subnet4Ptr subnet2 = CfgMgr::instance().getStagingCfg()->
|
||||
getCfgSubnets4()->selectSubnet(IOAddress("192.0.3.102"));
|
||||
ASSERT_TRUE(subnet2);
|
||||
OptionContainerPtr options2 = subnet2->getCfgOption()->getAll("dhcp4");
|
||||
OptionContainerPtr options2 = subnet2->getCfgOption()->getAll(DHCP4_OPTION_SPACE);
|
||||
ASSERT_EQ(1, options2->size());
|
||||
|
||||
const OptionContainerTypeIndex& idx2 = options2->get<1>();
|
||||
@ -2517,7 +2517,7 @@ TEST_F(Dhcp4ParserTest, optionDataLowerCase) {
|
||||
Subnet4Ptr subnet = CfgMgr::instance().getStagingCfg()->
|
||||
getCfgSubnets4()->selectSubnet(IOAddress("192.0.2.5"));
|
||||
ASSERT_TRUE(subnet);
|
||||
OptionContainerPtr options = subnet->getCfgOption()->getAll("dhcp4");
|
||||
OptionContainerPtr options = subnet->getCfgOption()->getAll(DHCP4_OPTION_SPACE);
|
||||
ASSERT_EQ(1, options->size());
|
||||
|
||||
// Get the search index. Index #1 is to search using option code.
|
||||
@ -2544,7 +2544,7 @@ TEST_F(Dhcp4ParserTest, stdOptionData) {
|
||||
ConstElementPtr x;
|
||||
std::map<std::string, std::string> params;
|
||||
params["name"] = "nis-servers";
|
||||
params["space"] = "dhcp4";
|
||||
params["space"] = DHCP4_OPTION_SPACE;
|
||||
// Option code 41 means nis-servers.
|
||||
params["code"] = "41";
|
||||
// Specify option values in a CSV (user friendly) format.
|
||||
@ -2561,7 +2561,7 @@ TEST_F(Dhcp4ParserTest, stdOptionData) {
|
||||
getCfgSubnets4()->selectSubnet(IOAddress("192.0.2.5"));
|
||||
ASSERT_TRUE(subnet);
|
||||
OptionContainerPtr options =
|
||||
subnet->getCfgOption()->getAll("dhcp4");
|
||||
subnet->getCfgOption()->getAll(DHCP4_OPTION_SPACE);
|
||||
ASSERT_TRUE(options);
|
||||
ASSERT_EQ(1, options->size());
|
||||
|
||||
@ -2746,13 +2746,13 @@ TEST_F(Dhcp4ParserTest, stdOptionDataEncapsulate) {
|
||||
|
||||
// We should have one option available.
|
||||
OptionContainerPtr options =
|
||||
CfgMgr::instance().getStagingCfg()->getCfgOption()->getAll("dhcp4");
|
||||
CfgMgr::instance().getStagingCfg()->getCfgOption()->getAll(DHCP4_OPTION_SPACE);
|
||||
ASSERT_TRUE(options);
|
||||
ASSERT_EQ(1, options->size());
|
||||
|
||||
// Get the option.
|
||||
OptionDescriptor desc = CfgMgr::instance().getStagingCfg()->
|
||||
getCfgOption()->get("dhcp4", DHO_VENDOR_ENCAPSULATED_OPTIONS);
|
||||
getCfgOption()->get(DHCP4_OPTION_SPACE, DHO_VENDOR_ENCAPSULATED_OPTIONS);
|
||||
EXPECT_TRUE(desc.option_);
|
||||
EXPECT_EQ(DHO_VENDOR_ENCAPSULATED_OPTIONS, desc.option_->getType());
|
||||
|
||||
|
@ -402,7 +402,7 @@ TEST_F(Dhcpv4SrvTest, initResponse) {
|
||||
// client-id echo is optional
|
||||
// rai echo is done in relayAgentInfoEcho
|
||||
// Do subnet selection option
|
||||
OptionDefinitionPtr sbnsel_def = LibDHCP::getOptionDef(Option::V4,
|
||||
OptionDefinitionPtr sbnsel_def = LibDHCP::getOptionDef(DHCP4_OPTION_SPACE,
|
||||
DHO_SUBNET_SELECTION);
|
||||
ASSERT_TRUE(sbnsel_def);
|
||||
OptionCustomPtr sbnsel(new OptionCustom(*sbnsel_def, Option::V4));
|
||||
@ -2170,7 +2170,7 @@ TEST_F(Dhcpv4SrvTest, relayLinkSelect) {
|
||||
dis->addOption(clientid);
|
||||
|
||||
// Let's create a Relay Agent Information option
|
||||
OptionDefinitionPtr rai_def = LibDHCP::getOptionDef(Option::V4,
|
||||
OptionDefinitionPtr rai_def = LibDHCP::getOptionDef(DHCP4_OPTION_SPACE,
|
||||
DHO_DHCP_AGENT_OPTIONS);
|
||||
ASSERT_TRUE(rai_def);
|
||||
OptionCustomPtr rai(new OptionCustom(*rai_def, Option::V4));
|
||||
@ -2197,7 +2197,7 @@ TEST_F(Dhcpv4SrvTest, relayLinkSelect) {
|
||||
EXPECT_TRUE(subnet2 == srv_.selectSubnet(dis));
|
||||
|
||||
// Subnet select option has a lower precedence
|
||||
OptionDefinitionPtr sbnsel_def = LibDHCP::getOptionDef(Option::V4,
|
||||
OptionDefinitionPtr sbnsel_def = LibDHCP::getOptionDef(DHCP4_OPTION_SPACE,
|
||||
DHO_SUBNET_SELECTION);
|
||||
ASSERT_TRUE(sbnsel_def);
|
||||
OptionCustomPtr sbnsel(new OptionCustom(*sbnsel_def, Option::V4));
|
||||
@ -2280,7 +2280,7 @@ TEST_F(Dhcpv4SrvTest, subnetSelect) {
|
||||
dis->addOption(clientid);
|
||||
|
||||
// Let's create a Subnet Selection option
|
||||
OptionDefinitionPtr sbnsel_def = LibDHCP::getOptionDef(Option::V4,
|
||||
OptionDefinitionPtr sbnsel_def = LibDHCP::getOptionDef(DHCP4_OPTION_SPACE,
|
||||
DHO_SUBNET_SELECTION);
|
||||
ASSERT_TRUE(sbnsel_def);
|
||||
OptionCustomPtr sbnsel(new OptionCustom(*sbnsel_def, Option::V4));
|
||||
|
@ -62,7 +62,7 @@ Dhcpv4SrvTest::Dhcpv4SrvTest()
|
||||
// Add Router option.
|
||||
Option4AddrLstPtr opt_routers(new Option4AddrLst(DHO_ROUTERS));
|
||||
opt_routers->setAddress(IOAddress("192.0.2.2"));
|
||||
subnet_->getCfgOption()->add(opt_routers, false, "dhcp4");
|
||||
subnet_->getCfgOption()->add(opt_routers, false, DHCP4_OPTION_SPACE);
|
||||
|
||||
CfgMgr::instance().clear();
|
||||
CfgMgr::instance().getStagingCfg()->getCfgSubnets4()->add(subnet_);
|
||||
@ -110,24 +110,24 @@ void Dhcpv4SrvTest::configureRequestedOptions() {
|
||||
option_dns_servers(new Option4AddrLst(DHO_DOMAIN_NAME_SERVERS));
|
||||
option_dns_servers->addAddress(IOAddress("192.0.2.1"));
|
||||
option_dns_servers->addAddress(IOAddress("192.0.2.100"));
|
||||
ASSERT_NO_THROW(subnet_->getCfgOption()->add(option_dns_servers, false, "dhcp4"));
|
||||
ASSERT_NO_THROW(subnet_->getCfgOption()->add(option_dns_servers, false, DHCP4_OPTION_SPACE));
|
||||
|
||||
// domain-name
|
||||
OptionDefinition def("domain-name", DHO_DOMAIN_NAME, OPT_FQDN_TYPE);
|
||||
OptionCustomPtr option_domain_name(new OptionCustom(def, Option::V4));
|
||||
option_domain_name->writeFqdn("example.com");
|
||||
subnet_->getCfgOption()->add(option_domain_name, false, "dhcp4");
|
||||
subnet_->getCfgOption()->add(option_domain_name, false, DHCP4_OPTION_SPACE);
|
||||
|
||||
// log-servers
|
||||
Option4AddrLstPtr option_log_servers(new Option4AddrLst(DHO_LOG_SERVERS));
|
||||
option_log_servers->addAddress(IOAddress("192.0.2.2"));
|
||||
option_log_servers->addAddress(IOAddress("192.0.2.10"));
|
||||
ASSERT_NO_THROW(subnet_->getCfgOption()->add(option_log_servers, false, "dhcp4"));
|
||||
ASSERT_NO_THROW(subnet_->getCfgOption()->add(option_log_servers, false, DHCP4_OPTION_SPACE));
|
||||
|
||||
// cookie-servers
|
||||
Option4AddrLstPtr option_cookie_servers(new Option4AddrLst(DHO_COOKIE_SERVERS));
|
||||
option_cookie_servers->addAddress(IOAddress("192.0.2.1"));
|
||||
ASSERT_NO_THROW(subnet_->getCfgOption()->add(option_cookie_servers, false, "dhcp4"));
|
||||
ASSERT_NO_THROW(subnet_->getCfgOption()->add(option_cookie_servers, false, DHCP4_OPTION_SPACE));
|
||||
}
|
||||
|
||||
void Dhcpv4SrvTest::messageCheck(const Pkt4Ptr& q, const Pkt4Ptr& a) {
|
||||
|
@ -560,6 +560,18 @@
|
||||
"item_optional": false,
|
||||
"item_default": 128
|
||||
},
|
||||
{
|
||||
"item_name": "excluded-prefix",
|
||||
"item_type": "string",
|
||||
"item_optional": true,
|
||||
"item_default": ""
|
||||
},
|
||||
{
|
||||
"item_name": "excluded-prefix-len",
|
||||
"item_type": "integer",
|
||||
"item_optional": true,
|
||||
"item_default": 128
|
||||
},
|
||||
{
|
||||
"item_name": "option-data",
|
||||
"item_type": "list",
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <dhcp/option6_iaaddr.h>
|
||||
#include <dhcp/option6_iaprefix.h>
|
||||
#include <dhcp/option6_status_code.h>
|
||||
#include <dhcp/option6_pdexclude.h>
|
||||
#include <dhcp/option_custom.h>
|
||||
#include <dhcp/option_vendor.h>
|
||||
#include <dhcp/option_vendor_class.h>
|
||||
@ -70,6 +71,7 @@
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <iomanip>
|
||||
@ -873,6 +875,7 @@ Dhcpv6Srv::buildCfgOptionList(const Pkt6Ptr& question,
|
||||
|
||||
void
|
||||
Dhcpv6Srv::appendRequestedOptions(const Pkt6Ptr& question, Pkt6Ptr& answer,
|
||||
AllocEngine::ClientContext6& ctx,
|
||||
const CfgOptionList& co_list) {
|
||||
|
||||
// Client requests some options using ORO option. Try to
|
||||
@ -881,18 +884,39 @@ Dhcpv6Srv::appendRequestedOptions(const Pkt6Ptr& question, Pkt6Ptr& answer,
|
||||
boost::dynamic_pointer_cast<OptionIntArray<uint16_t> >
|
||||
(question->getOption(D6O_ORO));
|
||||
|
||||
// Option ORO not found? We're done here then.
|
||||
if (!option_oro || co_list.empty()) {
|
||||
// If there is no ORO option, there is nothing more to do.
|
||||
if (!option_oro) {
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
// Get the list of options that client requested.
|
||||
const std::vector<uint16_t>& requested_opts = option_oro->getValues();
|
||||
|
||||
if (co_list.empty()) {
|
||||
// If there are no options configured, we at least have to check if
|
||||
// the client has requested PD exclude, which is configured as
|
||||
// part of the pool configuration.
|
||||
ctx.pd_exclude_requested_ = (std::find(requested_opts.begin(),
|
||||
requested_opts.end(),
|
||||
D6O_PD_EXCLUDE) !=
|
||||
requested_opts.end());
|
||||
return;
|
||||
}
|
||||
|
||||
BOOST_FOREACH(uint16_t opt, requested_opts) {
|
||||
// Prefix Exclude option requires special handling, as it can
|
||||
// be configured as part of the pool configuration.
|
||||
if (opt == D6O_PD_EXCLUDE) {
|
||||
ctx.pd_exclude_requested_ = true;
|
||||
// Prefix Exclude can only be included in the IA Prefix option
|
||||
// of IA_PD. Thus there is nothing more to do here.
|
||||
continue;
|
||||
}
|
||||
// Iterate on the configured option list
|
||||
for (CfgOptionList::const_iterator copts = co_list.begin();
|
||||
copts != co_list.end(); ++copts) {
|
||||
OptionDescriptor desc = (*copts)->get("dhcp6", opt);
|
||||
OptionDescriptor desc = (*copts)->get(DHCP6_OPTION_SPACE, opt);
|
||||
// Got it: add it and jump to the outer loop
|
||||
if (desc.option_) {
|
||||
answer->addOption(desc.option_);
|
||||
@ -1569,6 +1593,20 @@ Dhcpv6Srv::assignIA_PD(const Pkt6Ptr& query, const Pkt6Ptr& answer,
|
||||
(*l)->prefixlen_, (*l)->preferred_lft_,
|
||||
(*l)->valid_lft_));
|
||||
ia_rsp->addOption(addr);
|
||||
|
||||
if (ctx.pd_exclude_requested_) {
|
||||
// PD exclude option has been requested via ORO, thus we need to
|
||||
// include it if the pool configuration specifies this option.
|
||||
Pool6Ptr pool = boost::dynamic_pointer_cast<
|
||||
Pool6>(subnet->getPool(Lease::TYPE_PD, (*l)->addr_));
|
||||
if (pool && pool->getExcludedPrefixLength() > 0) {
|
||||
OptionPtr opt(new Option6PDExclude((*l)->addr_,
|
||||
(*l)->prefixlen_,
|
||||
pool->getExcludedPrefix(),
|
||||
pool->getExcludedPrefixLength()));
|
||||
addr->addOption(opt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// It would be possible to insert status code=0(success) as well,
|
||||
@ -1839,10 +1877,28 @@ Dhcpv6Srv::extendIA_PD(const Pkt6Ptr& query,
|
||||
|
||||
// For all the leases we have now, add the IAPPREFIX with non-zero lifetimes
|
||||
for (Lease6Collection::const_iterator l = leases.begin(); l != leases.end(); ++l) {
|
||||
|
||||
Option6IAPrefixPtr prf(new Option6IAPrefix(D6O_IAPREFIX,
|
||||
(*l)->addr_, (*l)->prefixlen_,
|
||||
(*l)->preferred_lft_, (*l)->valid_lft_));
|
||||
ia_rsp->addOption(prf);
|
||||
|
||||
if (ctx.pd_exclude_requested_) {
|
||||
// PD exclude option has been requested via ORO, thus we need to
|
||||
// include it if the pool configuration specifies this option.
|
||||
Pool6Ptr pool = boost::dynamic_pointer_cast<
|
||||
Pool6>(subnet->getPool(Lease::TYPE_PD, (*l)->addr_));
|
||||
|
||||
if (pool && pool->getExcludedPrefixLength() > 0) {
|
||||
OptionPtr opt(new Option6PDExclude((*l)->addr_,
|
||||
(*l)->prefixlen_,
|
||||
pool->getExcludedPrefix(),
|
||||
pool->getExcludedPrefixLength()));
|
||||
prf->addOption(opt);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LOG_INFO(lease6_logger, DHCP6_PD_LEASE_RENEW)
|
||||
.arg(query->getLabel())
|
||||
.arg((*l)->addr_.toText())
|
||||
@ -2337,7 +2393,7 @@ Dhcpv6Srv::processSolicit(const Pkt6Ptr& solicit) {
|
||||
CfgOptionList co_list;
|
||||
buildCfgOptionList(solicit, ctx, co_list);
|
||||
appendDefaultOptions(solicit, response, co_list);
|
||||
appendRequestedOptions(solicit, response, co_list);
|
||||
appendRequestedOptions(solicit, response, ctx, co_list);
|
||||
appendRequestedVendorOptions(solicit, response, ctx, co_list);
|
||||
|
||||
// Only generate name change requests if sending a Reply as a result
|
||||
@ -2368,7 +2424,7 @@ Dhcpv6Srv::processRequest(const Pkt6Ptr& request) {
|
||||
CfgOptionList co_list;
|
||||
buildCfgOptionList(request, ctx, co_list);
|
||||
appendDefaultOptions(request, reply, co_list);
|
||||
appendRequestedOptions(request, reply, co_list);
|
||||
appendRequestedOptions(request, reply, ctx, co_list);
|
||||
appendRequestedVendorOptions(request, reply, ctx, co_list);
|
||||
|
||||
generateFqdn(reply);
|
||||
@ -2396,7 +2452,7 @@ Dhcpv6Srv::processRenew(const Pkt6Ptr& renew) {
|
||||
CfgOptionList co_list;
|
||||
buildCfgOptionList(renew, ctx, co_list);
|
||||
appendDefaultOptions(renew, reply, co_list);
|
||||
appendRequestedOptions(renew, reply, co_list);
|
||||
appendRequestedOptions(renew, reply, ctx, co_list);
|
||||
appendRequestedVendorOptions(renew, reply, ctx, co_list);
|
||||
|
||||
generateFqdn(reply);
|
||||
@ -2424,7 +2480,7 @@ Dhcpv6Srv::processRebind(const Pkt6Ptr& rebind) {
|
||||
CfgOptionList co_list;
|
||||
buildCfgOptionList(rebind, ctx, co_list);
|
||||
appendDefaultOptions(rebind, reply, co_list);
|
||||
appendRequestedOptions(rebind, reply, co_list);
|
||||
appendRequestedOptions(rebind, reply, ctx, co_list);
|
||||
appendRequestedVendorOptions(rebind, reply, ctx, co_list);
|
||||
|
||||
generateFqdn(reply);
|
||||
@ -2457,7 +2513,7 @@ Dhcpv6Srv::processConfirm(const Pkt6Ptr& confirm) {
|
||||
CfgOptionList co_list;
|
||||
buildCfgOptionList(confirm, ctx, co_list);
|
||||
appendDefaultOptions(confirm, reply, co_list);
|
||||
appendRequestedOptions(confirm, reply, co_list);
|
||||
appendRequestedOptions(confirm, reply, ctx, co_list);
|
||||
appendRequestedVendorOptions(confirm, reply, ctx, co_list);
|
||||
// Indicates if at least one address has been verified. If no addresses
|
||||
// are verified it means that the client has sent no IA_NA options
|
||||
@ -2862,7 +2918,7 @@ Dhcpv6Srv::processInfRequest(const Pkt6Ptr& inf_request) {
|
||||
appendDefaultOptions(inf_request, reply, co_list);
|
||||
|
||||
// Try to assign options that were requested by the client.
|
||||
appendRequestedOptions(inf_request, reply, co_list);
|
||||
appendRequestedOptions(inf_request, reply, ctx, co_list);
|
||||
|
||||
// Try to assigne vendor options that were requested by the client.
|
||||
appendRequestedVendorOptions(inf_request, reply, ctx, co_list);
|
||||
|
@ -475,8 +475,13 @@ protected:
|
||||
///
|
||||
/// @param question client's message
|
||||
/// @param answer server's message (options will be added here)
|
||||
/// @param [out] ctx client context. This method sets the
|
||||
/// ctx.pd_exclude_requested_ field to 'true' if the Prefix Exclude
|
||||
/// option has been requested.
|
||||
///
|
||||
/// @param co_list configured option list
|
||||
void appendRequestedOptions(const Pkt6Ptr& question, Pkt6Ptr& answer,
|
||||
AllocEngine::ClientContext6& ctx,
|
||||
const CfgOptionList& co_list);
|
||||
|
||||
/// @brief Appends requested vendor options to server's answer.
|
||||
|
@ -174,11 +174,12 @@ public:
|
||||
BOOST_FOREACH(ConfigPair param, pd_pool_->mapValue()) {
|
||||
std::string entry(param.first);
|
||||
ParserPtr parser;
|
||||
if (entry == "prefix") {
|
||||
if (entry == "prefix" || entry =="excluded-prefix") {
|
||||
StringParserPtr str_parser(new StringParser(entry,
|
||||
string_values_));
|
||||
parser = str_parser;
|
||||
} else if (entry == "prefix-len" || entry == "delegated-len") {
|
||||
} else if (entry == "prefix-len" || entry == "delegated-len" ||
|
||||
entry == "excluded-prefix-len") {
|
||||
Uint32ParserPtr code_parser(new Uint32Parser(entry,
|
||||
uint32_values_));
|
||||
parser = code_parser;
|
||||
@ -200,13 +201,18 @@ public:
|
||||
// Try to obtain the pool parameters. It will throw an exception if any
|
||||
// of the required parameters are not present or invalid.
|
||||
try {
|
||||
std::string addr_str = string_values_->getParam("prefix");
|
||||
uint32_t prefix_len = uint32_values_->getParam("prefix-len");
|
||||
uint32_t delegated_len = uint32_values_->getParam("delegated-len");
|
||||
const std::string addr_str = string_values_->getParam("prefix");
|
||||
const uint32_t prefix_len = uint32_values_->getParam("prefix-len");
|
||||
const uint32_t delegated_len = uint32_values_->getParam("delegated-len");
|
||||
const std::string excluded_prefix_str =
|
||||
string_values_->getOptionalParam("excluded-prefix", "::");
|
||||
const uint32_t excluded_prefix_len =
|
||||
uint32_values_->getOptionalParam("excluded-prefix-len", 0);
|
||||
|
||||
// Attempt to construct the local pool.
|
||||
pool_.reset(new Pool6(Lease::TYPE_PD, IOAddress(addr_str),
|
||||
prefix_len, delegated_len));
|
||||
pool_.reset(new Pool6(IOAddress(addr_str), prefix_len,
|
||||
delegated_len, IOAddress(excluded_prefix_str),
|
||||
excluded_prefix_len));
|
||||
// Merge options specified for a pool into pool configuration.
|
||||
options_->copyTo(*pool_->getCfgOption());
|
||||
} catch (const std::exception& ex) {
|
||||
@ -634,7 +640,8 @@ public:
|
||||
}
|
||||
|
||||
if (!code) {
|
||||
OptionDefinitionPtr def = LibDHCP::getOptionDef(Option::V6, option_str);
|
||||
const OptionDefinitionPtr def = LibDHCP::getOptionDef(DHCP6_OPTION_SPACE,
|
||||
option_str);
|
||||
if (def) {
|
||||
code = def->getCode();
|
||||
} else {
|
||||
|
@ -146,7 +146,7 @@ public:
|
||||
std::map<std::string, std::string> params;
|
||||
if (parameter == "name") {
|
||||
params["name"] = param_value;
|
||||
params["space"] = "dhcp6";
|
||||
params["space"] = DHCP6_OPTION_SPACE;
|
||||
params["code"] = "38";
|
||||
params["data"] = "ABCDEF0105";
|
||||
params["csv-format"] = "False";
|
||||
@ -158,19 +158,19 @@ public:
|
||||
params["csv-format"] = "False";
|
||||
} else if (parameter == "code") {
|
||||
params["name"] = "subscriber-id";
|
||||
params["space"] = "dhcp6";
|
||||
params["space"] = DHCP6_OPTION_SPACE;
|
||||
params["code"] = param_value;
|
||||
params["data"] = "ABCDEF0105";
|
||||
params["csv-format"] = "False";
|
||||
} else if (parameter == "data") {
|
||||
params["name"] = "subscriber-id";
|
||||
params["space"] = "dhcp6";
|
||||
params["space"] = DHCP6_OPTION_SPACE;
|
||||
params["code"] = "38";
|
||||
params["data"] = param_value;
|
||||
params["csv-format"] = "False";
|
||||
} else if (parameter == "csv-format") {
|
||||
params["name"] = "subscriber-id";
|
||||
params["space"] = "dhcp6";
|
||||
params["space"] = DHCP6_OPTION_SPACE;
|
||||
params["code"] = "38";
|
||||
params["data"] = "ABCDEF0105";
|
||||
params["csv-format"] = param_value;
|
||||
@ -260,7 +260,7 @@ public:
|
||||
<< " does not exist in Config Manager";
|
||||
}
|
||||
OptionContainerPtr options =
|
||||
subnet->getCfgOption()->getAll("dhcp6");
|
||||
subnet->getCfgOption()->getAll(DHCP6_OPTION_SPACE);
|
||||
if (expected_options_count != options->size()) {
|
||||
ADD_FAILURE() << "The number of options in the subnet '"
|
||||
<< subnet_address.toText() << "' is different "
|
||||
@ -383,7 +383,7 @@ public:
|
||||
template<typename ReturnType>
|
||||
ReturnType
|
||||
retrieveOption(const Host& host, const uint16_t option_code) const {
|
||||
return (retrieveOption<ReturnType>(host, "dhcp6", option_code));
|
||||
return (retrieveOption<ReturnType>(host, DHCP6_OPTION_SPACE, option_code));
|
||||
}
|
||||
|
||||
/// @brief Retrieve an option associated with a host.
|
||||
@ -1378,6 +1378,60 @@ TEST_F(Dhcp6ParserTest, pdPoolBasics) {
|
||||
EXPECT_EQ(lastAddrInPrefix(prefixAddress, 64), p6->getLastAddress());
|
||||
}
|
||||
|
||||
// This test verifies that it is possible to specify a prefix pool with an
|
||||
// excluded prefix (see RFC6603).
|
||||
TEST_F(Dhcp6ParserTest, pdPoolPrefixExclude) {
|
||||
|
||||
ConstElementPtr x;
|
||||
|
||||
// Define a single valid pd pool.
|
||||
string config =
|
||||
"{ " + genIfaceConfig() + ","
|
||||
"\"preferred-lifetime\": 3000,"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
"\"subnet6\": [ { "
|
||||
" \"subnet\": \"2001:db8:1::/64\","
|
||||
" \"pd-pools\": ["
|
||||
" { \"prefix\": \"3000::\", "
|
||||
" \"prefix-len\": 48, "
|
||||
" \"delegated-len\": 64,"
|
||||
" \"excluded-prefix\": \"3000:1::\","
|
||||
" \"excluded-prefix-len\": 72"
|
||||
" } ],"
|
||||
"\"valid-lifetime\": 4000 }"
|
||||
"] }";
|
||||
|
||||
// Convert the JSON string into Elements.
|
||||
ElementPtr json;
|
||||
ASSERT_NO_THROW(json = Element::fromJSON(config));
|
||||
|
||||
// Verify that DHCP6 configuration processing succeeds.
|
||||
// Returned value must be non-empty ConstElementPtr to config result.
|
||||
// rcode should be 0 which indicates successful configuration processing.
|
||||
EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
|
||||
checkResult(x, 0);
|
||||
|
||||
// Test that we can retrieve the subnet.
|
||||
Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
|
||||
selectSubnet(IOAddress("2001:db8:1::5"), classify_);
|
||||
ASSERT_TRUE(subnet);
|
||||
|
||||
// Fetch the collection of PD pools. It should have 1 entry.
|
||||
PoolCollection pc;
|
||||
ASSERT_NO_THROW(pc = subnet->getPools(Lease::TYPE_PD));
|
||||
EXPECT_EQ(1, pc.size());
|
||||
|
||||
// Get a pointer to the pd pool instance, and verify its contents.
|
||||
Pool6Ptr p6;
|
||||
ASSERT_NO_THROW(p6 = boost::dynamic_pointer_cast<Pool6>(pc[0]));
|
||||
ASSERT_TRUE(p6);
|
||||
EXPECT_EQ("3000::", p6->getFirstAddress().toText());
|
||||
EXPECT_EQ(64, p6->getLength());
|
||||
EXPECT_EQ("3000:1::", p6->getExcludedPrefix().toText());
|
||||
EXPECT_EQ(72, static_cast<unsigned>(p6->getExcludedPrefixLength()));
|
||||
}
|
||||
|
||||
// Goal of this test is verify that a list of PD pools can be configured.
|
||||
// It also verifies that a subnet may be configured with both regular pools
|
||||
// and pd pools.
|
||||
@ -2059,7 +2113,7 @@ TEST_F(Dhcp6ParserTest, optionStandardDefOverride) {
|
||||
ElementPtr json = Element::fromJSON(config);
|
||||
|
||||
OptionDefinitionPtr def = CfgMgr::instance().getStagingCfg()->
|
||||
getCfgOptionDef()->get("dhcp6", 100);
|
||||
getCfgOptionDef()->get(DHCP6_OPTION_SPACE, 100);
|
||||
ASSERT_FALSE(def);
|
||||
|
||||
// Use the configuration string to create new option definition.
|
||||
@ -2070,7 +2124,7 @@ TEST_F(Dhcp6ParserTest, optionStandardDefOverride) {
|
||||
|
||||
// The option definition should now be available in the CfgMgr.
|
||||
def = CfgMgr::instance().getStagingCfg()->
|
||||
getCfgOptionDef()->get("dhcp6", 100);
|
||||
getCfgOptionDef()->get(DHCP6_OPTION_SPACE, 100);
|
||||
ASSERT_TRUE(def);
|
||||
|
||||
// Check the option data.
|
||||
@ -2121,7 +2175,7 @@ TEST_F(Dhcp6ParserTest, optionStandardDefOverride) {
|
||||
checkResult(status, 0);
|
||||
|
||||
def = CfgMgr::instance().getStagingCfg()->
|
||||
getCfgOptionDef()->get("dhcp6", 63);
|
||||
getCfgOptionDef()->get(DHCP6_OPTION_SPACE, 63);
|
||||
ASSERT_TRUE(def);
|
||||
|
||||
// Check the option data.
|
||||
@ -2162,10 +2216,10 @@ TEST_F(Dhcp6ParserTest, optionDataDefaultsGlobal) {
|
||||
Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
|
||||
selectSubnet(IOAddress("2001:db8:1::5"), classify_);
|
||||
ASSERT_TRUE(subnet);
|
||||
OptionContainerPtr options = subnet->getCfgOption()->getAll("dhcp6");
|
||||
OptionContainerPtr options = subnet->getCfgOption()->getAll(DHCP6_OPTION_SPACE);
|
||||
ASSERT_EQ(0, options->size());
|
||||
|
||||
options = CfgMgr::instance().getStagingCfg()->getCfgOption()->getAll("dhcp6");
|
||||
options = CfgMgr::instance().getStagingCfg()->getCfgOption()->getAll(DHCP6_OPTION_SPACE);
|
||||
ASSERT_EQ(2, options->size());
|
||||
|
||||
// Get the search index. Index #1 is to search using option code.
|
||||
@ -2231,13 +2285,13 @@ TEST_F(Dhcp6ParserTest, optionDataDefaultsSubnet) {
|
||||
|
||||
// These options are subnet options
|
||||
OptionContainerPtr options =
|
||||
CfgMgr::instance().getStagingCfg()->getCfgOption()->getAll("dhcp6");
|
||||
CfgMgr::instance().getStagingCfg()->getCfgOption()->getAll(DHCP6_OPTION_SPACE);
|
||||
ASSERT_EQ(0, options->size());
|
||||
|
||||
Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
|
||||
selectSubnet(IOAddress("2001:db8:1::5"), classify_);
|
||||
ASSERT_TRUE(subnet);
|
||||
options = subnet->getCfgOption()->getAll("dhcp6");
|
||||
options = subnet->getCfgOption()->getAll(DHCP6_OPTION_SPACE);
|
||||
ASSERT_EQ(2, options->size());
|
||||
|
||||
// Get the search index. Index #1 is to search using option code.
|
||||
@ -2324,7 +2378,7 @@ TEST_F(Dhcp6ParserTest, optionDataTwoSpaces) {
|
||||
// Options should be now available
|
||||
// Try to get the option from the space dhcp6.
|
||||
OptionDescriptor desc1 =
|
||||
CfgMgr::instance().getStagingCfg()->getCfgOption()->get("dhcp6", 38);
|
||||
CfgMgr::instance().getStagingCfg()->getCfgOption()->get(DHCP6_OPTION_SPACE, 38);
|
||||
ASSERT_TRUE(desc1.option_);
|
||||
EXPECT_EQ(38, desc1.option_->getType());
|
||||
// Try to get the option from the space isc.
|
||||
@ -2456,13 +2510,13 @@ TEST_F(Dhcp6ParserTest, optionDataEncapsulate) {
|
||||
|
||||
// We should have one option available.
|
||||
OptionContainerPtr options =
|
||||
CfgMgr::instance().getStagingCfg()->getCfgOption()->getAll("dhcp6");
|
||||
CfgMgr::instance().getStagingCfg()->getCfgOption()->getAll(DHCP6_OPTION_SPACE);
|
||||
ASSERT_TRUE(options);
|
||||
ASSERT_EQ(1, options->size());
|
||||
|
||||
// Get the option.
|
||||
OptionDescriptor desc =
|
||||
CfgMgr::instance().getStagingCfg()->getCfgOption()->get("dhcp6", 100);
|
||||
CfgMgr::instance().getStagingCfg()->getCfgOption()->get(DHCP6_OPTION_SPACE, 100);
|
||||
EXPECT_TRUE(desc.option_);
|
||||
EXPECT_EQ(100, desc.option_->getType());
|
||||
|
||||
@ -2514,7 +2568,7 @@ TEST_F(Dhcp6ParserTest, optionDataInMultipleSubnets) {
|
||||
Subnet6Ptr subnet1 = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
|
||||
selectSubnet(IOAddress("2001:db8:1::5"), classify_);
|
||||
ASSERT_TRUE(subnet1);
|
||||
OptionContainerPtr options1 = subnet1->getCfgOption()->getAll("dhcp6");
|
||||
OptionContainerPtr options1 = subnet1->getCfgOption()->getAll(DHCP6_OPTION_SPACE);
|
||||
ASSERT_EQ(1, options1->size());
|
||||
|
||||
// Get the search index. Index #1 is to search using option code.
|
||||
@ -2540,7 +2594,7 @@ TEST_F(Dhcp6ParserTest, optionDataInMultipleSubnets) {
|
||||
Subnet6Ptr subnet2 = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
|
||||
selectSubnet(IOAddress("2001:db8:2::4"), classify_);
|
||||
ASSERT_TRUE(subnet2);
|
||||
OptionContainerPtr options2 = subnet2->getCfgOption()->getAll("dhcp6");
|
||||
OptionContainerPtr options2 = subnet2->getCfgOption()->getAll(DHCP6_OPTION_SPACE);
|
||||
ASSERT_EQ(1, options2->size());
|
||||
|
||||
const OptionContainerTypeIndex& idx2 = options2->get<1>();
|
||||
@ -2711,7 +2765,7 @@ TEST_F(Dhcp6ParserTest, optionDataBoolean) {
|
||||
// Create configuration. Use standard option 1000.
|
||||
std::map<std::string, std::string> params;
|
||||
params["name"] = "bool-option";
|
||||
params["space"] = "dhcp6";
|
||||
params["space"] = DHCP6_OPTION_SPACE;
|
||||
params["code"] = "1000";
|
||||
params["data"] = "true";
|
||||
params["csv-format"] = "true";
|
||||
@ -2857,7 +2911,7 @@ TEST_F(Dhcp6ParserTest, optionDataLowerCase) {
|
||||
Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
|
||||
selectSubnet(IOAddress("2001:db8:1::5"), classify_);
|
||||
ASSERT_TRUE(subnet);
|
||||
OptionContainerPtr options = subnet->getCfgOption()->getAll("dhcp6");
|
||||
OptionContainerPtr options = subnet->getCfgOption()->getAll(DHCP6_OPTION_SPACE);
|
||||
ASSERT_EQ(1, options->size());
|
||||
|
||||
// Get the search index. Index #1 is to search using option code.
|
||||
@ -2885,7 +2939,7 @@ TEST_F(Dhcp6ParserTest, stdOptionData) {
|
||||
ConstElementPtr x;
|
||||
std::map<std::string, std::string> params;
|
||||
params["name"] = "ia-na";
|
||||
params["space"] = "dhcp6";
|
||||
params["space"] = DHCP6_OPTION_SPACE;
|
||||
// Option code 3 means OPTION_IA_NA.
|
||||
params["code"] = "3";
|
||||
params["data"] = "12345, 6789, 1516";
|
||||
@ -2900,7 +2954,7 @@ TEST_F(Dhcp6ParserTest, stdOptionData) {
|
||||
Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
|
||||
selectSubnet(IOAddress("2001:db8:1::5"), classify_);
|
||||
ASSERT_TRUE(subnet);
|
||||
OptionContainerPtr options = subnet->getCfgOption()->getAll("dhcp6");
|
||||
OptionContainerPtr options = subnet->getCfgOption()->getAll(DHCP6_OPTION_SPACE);
|
||||
ASSERT_EQ(1, options->size());
|
||||
|
||||
// Get the search index. Index #1 is to search using option code.
|
||||
@ -3155,12 +3209,12 @@ TEST_F(Dhcp6ParserTest, DISABLED_stdOptionDataEncapsulate) {
|
||||
ASSERT_TRUE(subnet);
|
||||
|
||||
// We should have one option available.
|
||||
OptionContainerPtr options = subnet->getCfgOption()->getAll("dhcp6");
|
||||
OptionContainerPtr options = subnet->getCfgOption()->getAll(DHCP6_OPTION_SPACE);
|
||||
ASSERT_TRUE(options);
|
||||
ASSERT_EQ(1, options->size());
|
||||
|
||||
// Get the option.
|
||||
OptionDescriptor desc = subnet->getCfgOption()->get("dhcp6", D6O_VENDOR_OPTS);
|
||||
OptionDescriptor desc = subnet->getCfgOption()->get(DHCP6_OPTION_SPACE, D6O_VENDOR_OPTS);
|
||||
EXPECT_TRUE(desc.option_);
|
||||
EXPECT_EQ(D6O_VENDOR_OPTS, desc.option_->getType());
|
||||
|
||||
|
@ -1838,7 +1838,7 @@ TEST_F(Dhcpv6SrvTest, relayOverride) {
|
||||
/// @param payload specified payload (0 = fill payload with repeating option code)
|
||||
/// @return RSOO with nested options
|
||||
OptionPtr createRSOO(const std::vector<uint16_t>& codes, uint8_t payload = 0) {
|
||||
OptionDefinitionPtr def = LibDHCP::getOptionDef(Option::V6, D6O_RSOO);
|
||||
OptionDefinitionPtr def = LibDHCP::getOptionDef(DHCP6_OPTION_SPACE, D6O_RSOO);
|
||||
if (!def) {
|
||||
isc_throw(BadValue, "Can't find RSOO definition");
|
||||
}
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <dhcp/docsis3_option_defs.h>
|
||||
#include <dhcp/option_string.h>
|
||||
#include <dhcp/option_vendor.h>
|
||||
#include <dhcp/option6_pdexclude.h>
|
||||
#include <dhcp/tests/iface_mgr_test_config.h>
|
||||
#include <dhcp6/json_config_parser.h>
|
||||
#include <dhcp6/tests/dhcp6_message_test.h>
|
||||
@ -53,6 +54,13 @@ namespace {
|
||||
/// - an option with unique value specified for each pool, so as it is
|
||||
/// possible to test that pool specific options can be assigned.
|
||||
///
|
||||
/// - Configuration 5:
|
||||
/// - addresses and prefixes
|
||||
/// - 1 subnet with one address pool and one prefix pool
|
||||
/// - address pool: 2001:db8:1::/64
|
||||
/// - prefix pool: 3000::/72
|
||||
/// - excluded prefix 3000::1000/120 in a prefix pool.
|
||||
///
|
||||
const char* RENEW_CONFIGS[] = {
|
||||
// Configuration 0
|
||||
"{ \"interfaces-config\": {"
|
||||
@ -188,7 +196,29 @@ const char* RENEW_CONFIGS[] = {
|
||||
" \"interface\": \"eth0\""
|
||||
" } ],"
|
||||
"\"valid-lifetime\": 4000"
|
||||
"}"
|
||||
"}",
|
||||
|
||||
// Configuration 5
|
||||
"{ \"interfaces-config\": {"
|
||||
" \"interfaces\": [ \"*\" ]"
|
||||
"},"
|
||||
"\"preferred-lifetime\": 3000,"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
"\"subnet6\": [ { "
|
||||
" \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
|
||||
" \"pd-pools\": ["
|
||||
" { \"prefix\": \"3000::\", "
|
||||
" \"prefix-len\": 72, "
|
||||
" \"delegated-len\": 80,"
|
||||
" \"excluded-prefix\": \"3000::1000\","
|
||||
" \"excluded-prefix-len\": 120"
|
||||
" } ],"
|
||||
" \"subnet\": \"2001:db8:1::/48\", "
|
||||
" \"interface-id\": \"\","
|
||||
" \"interface\": \"eth0\""
|
||||
" } ],"
|
||||
"\"valid-lifetime\": 4000 }
|
||||
};
|
||||
|
||||
/// @brief Test fixture class for testing Renew.
|
||||
@ -269,6 +299,91 @@ TEST_F(RenewTest, requestPrefixInRenew) {
|
||||
EXPECT_EQ(STATUS_Success, client.getStatusCode(pd_iaid_));
|
||||
}
|
||||
|
||||
// Test that it is possible to renew a prefix lease with a Prefix Exclude
|
||||
// option being included during renew.
|
||||
TEST_F(RenewTest, renewWithExcludedPrefix) {
|
||||
Dhcp6Client client;
|
||||
|
||||
// Configure client to request IA_NA and IA_PD.
|
||||
client.requestAddress(na_iaid_);
|
||||
client.requestPrefix(pd_iaid_);
|
||||
|
||||
// Request Prefix Exclude option.
|
||||
client.requestOption(D6O_PD_EXCLUDE);
|
||||
|
||||
// Configure the server with NA pools only.
|
||||
ASSERT_NO_THROW(configure(RENEW_CONFIGS[2], *client.getServer()));
|
||||
|
||||
// Perform 4-way exchange.
|
||||
ASSERT_NO_THROW(client.doSARR());
|
||||
|
||||
// Simulate aging of leases.
|
||||
client.fastFwdTime(1000);
|
||||
|
||||
// Make sure that the client has acquired NA lease.
|
||||
std::vector<Lease6> leases_client_na = client.getLeasesByType(Lease::TYPE_NA);
|
||||
ASSERT_EQ(1, leases_client_na.size());
|
||||
EXPECT_EQ(STATUS_Success, client.getStatusCode(na_iaid_));
|
||||
|
||||
// The client should also acquire a PD lease.
|
||||
std::vector<Lease6> leases_client_pd = client.getLeasesByType(Lease::TYPE_PD);
|
||||
ASSERT_EQ(1, leases_client_pd.size());
|
||||
ASSERT_EQ(STATUS_Success, client.getStatusCode(pd_iaid_));
|
||||
|
||||
// Send Renew message to the server, including IA_NA and IA_PD.
|
||||
ASSERT_NO_THROW(client.doRenew());
|
||||
|
||||
std::vector<Lease6> leases_client_na_renewed =
|
||||
client.getLeasesByType(Lease::TYPE_NA);
|
||||
ASSERT_EQ(1, leases_client_na_renewed.size());
|
||||
EXPECT_EQ(STATUS_Success, client.getStatusCode(na_iaid_));
|
||||
|
||||
std::vector<Lease6> leases_client_pd_renewed =
|
||||
client.getLeasesByType(Lease::TYPE_PD);
|
||||
ASSERT_EQ(1, leases_client_pd_renewed.size());
|
||||
EXPECT_EQ(STATUS_Success, client.getStatusCode(pd_iaid_));
|
||||
|
||||
// Make sure that Prefix Exclude option hasn't been included.
|
||||
OptionPtr option = client.getContext().response_->getOption(D6O_IA_PD);
|
||||
ASSERT_TRUE(option);
|
||||
option = option->getOption(D6O_IAPREFIX);
|
||||
ASSERT_TRUE(option);
|
||||
option = option->getOption(D6O_PD_EXCLUDE);
|
||||
ASSERT_FALSE(option);
|
||||
|
||||
// Reconfigure the server to use the prefix pool with excluded prefix.
|
||||
configure(RENEW_CONFIGS[4], *client.getServer());
|
||||
|
||||
// Send Renew message to the server, including IA_NA and IA_PD.
|
||||
ASSERT_NO_THROW(client.doRenew());
|
||||
|
||||
// Make sure that the client has acquired NA lease.
|
||||
leases_client_na_renewed = client.getLeasesByType(Lease::TYPE_NA);
|
||||
ASSERT_EQ(1, leases_client_na_renewed.size());
|
||||
EXPECT_EQ(STATUS_Success, client.getStatusCode(na_iaid_));
|
||||
|
||||
// Make sure that the client has acquired PD lease.
|
||||
leases_client_pd_renewed = client.getLeasesByType(Lease::TYPE_PD);
|
||||
ASSERT_EQ(1, leases_client_pd_renewed.size());
|
||||
EXPECT_EQ(STATUS_Success, client.getStatusCode(pd_iaid_));
|
||||
|
||||
// The leases should have been renewed.
|
||||
EXPECT_EQ(1000, leases_client_na_renewed[0].cltt_ - leases_client_na[0].cltt_);
|
||||
EXPECT_EQ(1000, leases_client_pd_renewed[0].cltt_ - leases_client_pd[0].cltt_);
|
||||
|
||||
// This time, the Prefix Exclude option should be included.
|
||||
option = client.getContext().response_->getOption(D6O_IA_PD);
|
||||
ASSERT_TRUE(option);
|
||||
option = option->getOption(D6O_IAPREFIX);
|
||||
ASSERT_TRUE(option);
|
||||
option = option->getOption(D6O_PD_EXCLUDE);
|
||||
ASSERT_TRUE(option);
|
||||
Option6PDExcludePtr pd_exclude = boost::dynamic_pointer_cast<Option6PDExclude>(option);
|
||||
ASSERT_TRUE(pd_exclude);
|
||||
EXPECT_EQ("3000::1000", pd_exclude->getExcludedPrefix().toText());
|
||||
EXPECT_EQ(120, static_cast<unsigned>(pd_exclude->getExcludedPrefixLength()));
|
||||
}
|
||||
|
||||
// This test verifies that the client can request a prefix delegation
|
||||
// with a hint, while it is renewing an address lease.
|
||||
TEST_F(RenewTest, requestPrefixInRenewUseHint) {
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <config.h>
|
||||
#include <dhcp/tests/iface_mgr_test_config.h>
|
||||
#include <dhcp/option6_client_fqdn.h>
|
||||
#include <dhcp/option6_pdexclude.h>
|
||||
#include <dhcp6/tests/dhcp6_test_utils.h>
|
||||
#include <dhcp6/tests/dhcp6_client.h>
|
||||
#include <dhcpsrv/cfgmgr.h>
|
||||
@ -48,6 +49,11 @@ namespace {
|
||||
/// - an option with unique value specified for each pool, so as it is
|
||||
/// possible to test that pool specific options can be assigned.
|
||||
///
|
||||
/// Configuration 3:
|
||||
/// - one subnet 3000::/32 used on eth0 interface
|
||||
/// - prefixes of length 64, delegated from the pool: 2001:db8:3::/48
|
||||
/// - Excluded Prefix specified (RFC 6603).
|
||||
///
|
||||
const char* CONFIGS[] = {
|
||||
// Configuration 0
|
||||
"{ \"interfaces-config\": {"
|
||||
@ -153,7 +159,28 @@ const char* CONFIGS[] = {
|
||||
" \"interface\": \"eth0\""
|
||||
" } ],"
|
||||
"\"valid-lifetime\": 4000"
|
||||
"}"
|
||||
"}",
|
||||
|
||||
// Configuration 3
|
||||
"{ \"interfaces-config\": {"
|
||||
" \"interfaces\": [ \"*\" ]"
|
||||
"},"
|
||||
"\"preferred-lifetime\": 3000,"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
"\"subnet6\": [ { "
|
||||
" \"pd-pools\": ["
|
||||
" { \"prefix\": \"2001:db8:3::\", "
|
||||
" \"prefix-len\": 48, "
|
||||
" \"delegated-len\": 64,"
|
||||
" \"excluded-prefix\": \"2001:db8:3::1000\","
|
||||
" \"excluded-prefix-len\": 120"
|
||||
" } ],"
|
||||
" \"subnet\": \"3000::/32\", "
|
||||
" \"interface-id\": \"\","
|
||||
" \"interface\": \"eth0\""
|
||||
" } ],"
|
||||
"\"valid-lifetime\": 4000 }"
|
||||
};
|
||||
|
||||
/// @brief Test fixture class for testing 4-way exchange: Solicit-Advertise,
|
||||
@ -305,6 +332,46 @@ TEST_F(SARRTest, optionsInheritance) {
|
||||
ASSERT_TRUE(client.hasOptionWithAddress(D6O_SNTP_SERVERS, "3000:2::2"));
|
||||
}
|
||||
|
||||
/// This test verifies that it is possible to specify an excluded prefix
|
||||
/// (RFC 6603) and send it back to the client requesting prefix delegation.
|
||||
TEST_F(SARRTest, directClientExcludedPrefix) {
|
||||
Dhcp6Client client;
|
||||
// Configure client to request IA_PD.
|
||||
client.requestPrefix();
|
||||
client.requestOption(D6O_PD_EXCLUDE);
|
||||
configure(CONFIGS[2], *client.getServer());
|
||||
// Make sure we ended-up having expected number of subnets configured.
|
||||
const Subnet6Collection* subnets = CfgMgr::instance().getCurrentCfg()->
|
||||
getCfgSubnets6()->getAll();
|
||||
ASSERT_EQ(1, subnets->size());
|
||||
// Perform 4-way exchange.
|
||||
ASSERT_NO_THROW(client.doSARR());
|
||||
// Server should have assigned a prefix.
|
||||
ASSERT_EQ(1, client.getLeaseNum());
|
||||
Lease6 lease_client = client.getLease(0);
|
||||
EXPECT_EQ(64, lease_client.prefixlen_);
|
||||
EXPECT_EQ(3000, lease_client.preferred_lft_);
|
||||
EXPECT_EQ(4000, lease_client.valid_lft_);
|
||||
Lease6Ptr lease_server = checkLease(lease_client);
|
||||
// Check that the server recorded the lease.
|
||||
ASSERT_TRUE(lease_server);
|
||||
|
||||
OptionPtr option = client.getContext().response_->getOption(D6O_IA_PD);
|
||||
ASSERT_TRUE(option);
|
||||
Option6IAPtr ia = boost::dynamic_pointer_cast<Option6IA>(option);
|
||||
ASSERT_TRUE(ia);
|
||||
option = ia->getOption(D6O_IAPREFIX);
|
||||
ASSERT_TRUE(option);
|
||||
Option6IAPrefixPtr pd_option = boost::dynamic_pointer_cast<Option6IAPrefix>(option);
|
||||
ASSERT_TRUE(pd_option);
|
||||
option = pd_option->getOption(D6O_PD_EXCLUDE);
|
||||
ASSERT_TRUE(option);
|
||||
Option6PDExcludePtr pd_exclude = boost::dynamic_pointer_cast<Option6PDExclude>(option);
|
||||
ASSERT_TRUE(pd_exclude);
|
||||
EXPECT_EQ("2001:db8:3::1000", pd_exclude->getExcludedPrefix().toText());
|
||||
EXPECT_EQ(120, static_cast<unsigned>(pd_exclude->getExcludedPrefixLength()));
|
||||
}
|
||||
|
||||
// Check that when the client includes the Rapid Commit option in its
|
||||
// Solicit, the server responds with Reply and commits the lease.
|
||||
TEST_F(SARRTest, rapidCommitEnable) {
|
||||
|
@ -25,6 +25,7 @@ libkea_dhcp___la_SOURCES += option4_client_fqdn.cc option4_client_fqdn.h
|
||||
libkea_dhcp___la_SOURCES += option6_ia.cc option6_ia.h
|
||||
libkea_dhcp___la_SOURCES += option6_iaaddr.cc option6_iaaddr.h
|
||||
libkea_dhcp___la_SOURCES += option6_iaprefix.cc option6_iaprefix.h
|
||||
libkea_dhcp___la_SOURCES += option6_pdexclude.cc option6_pdexclude.h
|
||||
libkea_dhcp___la_SOURCES += option6_addrlst.cc option6_addrlst.h
|
||||
libkea_dhcp___la_SOURCES += option6_client_fqdn.cc option6_client_fqdn.h
|
||||
libkea_dhcp___la_SOURCES += option6_status_code.cc option6_status_code.h
|
||||
|
@ -79,10 +79,10 @@
|
||||
#define D6O_CLIENT_ARCH_TYPE 61 /* RFC5970 */
|
||||
#define D6O_NII 62 /* RFC5970 */
|
||||
//#define D6O_GEOLOCATION 63 /* RFC6225 */
|
||||
//#define D6O_AFTR_NAME 64 /* RFC6334 */
|
||||
#define D6O_AFTR_NAME 64 /* RFC6334 */
|
||||
#define D6O_ERP_LOCAL_DOMAIN_NAME 65 /* RFC6440 */
|
||||
#define D6O_RSOO 66 /* RFC6422 */
|
||||
//#define D6O_PD_EXCLUDE 67 /* RFC6603 */
|
||||
#define D6O_PD_EXCLUDE 67 /* RFC6603 */
|
||||
//#define D6O_VSS 68 /* RFC6607 */
|
||||
//#define D6O_MIP6_IDINF 69 /* RFC6610 */
|
||||
//#define D6O_MIP6_UDINF 70 /* RFC6610 */
|
||||
@ -104,14 +104,14 @@
|
||||
//#define D6O_V6_PCP_SERVER 86 /* RFC7291 */
|
||||
#define D6O_DHCPV4_MSG 87 /* RFC7341 */
|
||||
#define D6O_DHCPV4_O_DHCPV6_SERVER 88 /* RFC7341 */
|
||||
//#define D6O_S46_RULE 89 /* RFC7598 */
|
||||
//#define D6O_S46_BR 90 /* RFC7598 */
|
||||
//#define D6O_S46_DMR 91 /* RFC7598 */
|
||||
//#define D6O_S46_V4V6BIND 92 /* RFC7598 */
|
||||
//#define D6O_S46_PORTPARAMS 93 /* RFC7598 */
|
||||
//#define D6O_S46_CONT_MAPE 94 /* RFC7598 */
|
||||
//#define D6O_S46_CONT_MAPT 95 /* RFC7598 */
|
||||
//#define D6O_S46_CONT_LW 96 /* RFC7598 */
|
||||
#define D6O_S46_RULE 89 /* RFC7598 */
|
||||
#define D6O_S46_BR 90 /* RFC7598 */
|
||||
#define D6O_S46_DMR 91 /* RFC7598 */
|
||||
#define D6O_S46_V4V6BIND 92 /* RFC7598 */
|
||||
#define D6O_S46_PORTPARAMS 93 /* RFC7598 */
|
||||
#define D6O_S46_CONT_MAPE 94 /* RFC7598 */
|
||||
#define D6O_S46_CONT_MAPT 95 /* RFC7598 */
|
||||
#define D6O_S46_CONT_LW 96 /* RFC7598 */
|
||||
//#define D6O_4RD 97 /* RFC7600 */
|
||||
//#define D6O_4RD_MAP_RULE 98 /* RFC7600 */
|
||||
//#define D6O_4RD_NON_MAP_RULE 99 /* RFC7600 */
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2013-2016 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
|
||||
@ -58,7 +58,8 @@ const OptionDefParams DOCSIS3_V6_DEFS[] = {
|
||||
};
|
||||
|
||||
/// Number of option definitions defined.
|
||||
const int DOCSIS3_V6_DEFS_SIZE = sizeof(DOCSIS3_V6_DEFS) / sizeof(OptionDefParams);
|
||||
const int DOCSIS3_V6_DEFS_SIZE =
|
||||
sizeof(DOCSIS3_V6_DEFS) / sizeof(DOCSIS3_V6_DEFS[0]);
|
||||
|
||||
/// The class as specified in vendor-class option by the devices
|
||||
extern const char* DOCSIS3_CLASS_EROUTER;
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2015-2016 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
|
||||
@ -28,7 +28,7 @@ const size_t DUID_TYPE_LEN = 2;
|
||||
/// @brief Minimal length of the MAC address.
|
||||
const size_t MIN_MAC_LEN = 6;
|
||||
|
||||
/// @brief Length of the enterprise if field.
|
||||
/// @brief Length of the enterprise ID field.
|
||||
const size_t ENTERPRISE_ID_LEN = 4;
|
||||
|
||||
/// @brief Default length of the variable length identifier in the DUID-EN.
|
||||
|
@ -45,8 +45,13 @@ OptionDefContainerPtr LibDHCP::v4option_defs_(new OptionDefContainer());
|
||||
// Static container with DHCPv6 option definitions.
|
||||
OptionDefContainerPtr LibDHCP::v6option_defs_(new OptionDefContainer());
|
||||
|
||||
// Static container with option definitions grouped by option space.
|
||||
OptionDefContainers LibDHCP::option_defs_;
|
||||
|
||||
// Static container with vendor option definitions for DHCPv4.
|
||||
VendorOptionDefContainers LibDHCP::vendor4_defs_;
|
||||
|
||||
// Static container with vendor option definitions for DHCPv6.
|
||||
VendorOptionDefContainers LibDHCP::vendor6_defs_;
|
||||
|
||||
// Static container with option definitions created in runtime.
|
||||
@ -70,23 +75,28 @@ void initOptionSpace(OptionDefContainerPtr& defs,
|
||||
size_t params_size);
|
||||
|
||||
const OptionDefContainerPtr&
|
||||
LibDHCP::getOptionDefs(const Option::Universe u) {
|
||||
switch (u) {
|
||||
case Option::V4:
|
||||
if (v4option_defs_->empty()) {
|
||||
initStdOptionDefs4();
|
||||
initVendorOptsDocsis4();
|
||||
}
|
||||
return (v4option_defs_);
|
||||
case Option::V6:
|
||||
if (v6option_defs_->empty()) {
|
||||
initStdOptionDefs6();
|
||||
initVendorOptsDocsis6();
|
||||
}
|
||||
return (v6option_defs_);
|
||||
default:
|
||||
isc_throw(isc::BadValue, "invalid universe " << u << " specified");
|
||||
LibDHCP::getOptionDefs(const std::string& space) {
|
||||
// If any of the containers is not initialized, it means that we haven't
|
||||
// initialized option definitions at all.
|
||||
if (v4option_defs_->empty()) {
|
||||
initStdOptionDefs4();
|
||||
initVendorOptsDocsis4();
|
||||
initStdOptionDefs6();
|
||||
initVendorOptsDocsis6();
|
||||
}
|
||||
|
||||
if (space == DHCP4_OPTION_SPACE) {
|
||||
return (v4option_defs_);
|
||||
|
||||
} else if (space == DHCP6_OPTION_SPACE) {
|
||||
return (v6option_defs_);
|
||||
}
|
||||
|
||||
OptionDefContainers::const_iterator container = option_defs_.find(space);
|
||||
if (container != option_defs_.end()) {
|
||||
return (container->second);
|
||||
}
|
||||
return (null_option_def_container_);
|
||||
}
|
||||
|
||||
const OptionDefContainerPtr&
|
||||
@ -127,8 +137,8 @@ LibDHCP::getVendorOption6Defs(const uint32_t vendor_id) {
|
||||
}
|
||||
|
||||
OptionDefinitionPtr
|
||||
LibDHCP::getOptionDef(const Option::Universe u, const uint16_t code) {
|
||||
const OptionDefContainerPtr& defs = getOptionDefs(u);
|
||||
LibDHCP::getOptionDef(const std::string& space, const uint16_t code) {
|
||||
const OptionDefContainerPtr& defs = getOptionDefs(space);
|
||||
const OptionDefContainerTypeIndex& idx = defs->get<1>();
|
||||
const OptionDefContainerTypeRange& range = idx.equal_range(code);
|
||||
if (range.first != range.second) {
|
||||
@ -138,18 +148,16 @@ LibDHCP::getOptionDef(const Option::Universe u, const uint16_t code) {
|
||||
}
|
||||
|
||||
OptionDefinitionPtr
|
||||
LibDHCP::getOptionDef(const Option::Universe u, const std::string& name) {
|
||||
const OptionDefContainerPtr defs = getOptionDefs(u);
|
||||
LibDHCP::getOptionDef(const std::string& space, const std::string& name) {
|
||||
const OptionDefContainerPtr defs = getOptionDefs(space);
|
||||
const OptionDefContainerNameIndex& idx = defs->get<2>();
|
||||
const OptionDefContainerNameRange& range = idx.equal_range(name);
|
||||
if (range.first != range.second) {
|
||||
return (*range.first);
|
||||
}
|
||||
return (OptionDefinitionPtr());
|
||||
|
||||
}
|
||||
|
||||
|
||||
OptionDefinitionPtr
|
||||
LibDHCP::getVendorOptionDef(const Option::Universe u, const uint32_t vendor_id,
|
||||
const std::string& name) {
|
||||
@ -249,34 +257,6 @@ LibDHCP::commitRuntimeOptionDefs() {
|
||||
runtime_option_defs_.commit();
|
||||
}
|
||||
|
||||
bool
|
||||
LibDHCP::isStandardOption(const Option::Universe u, const uint16_t code) {
|
||||
if (u == Option::V6) {
|
||||
if (code < 79 &&
|
||||
code != 10 &&
|
||||
code != 35) {
|
||||
return (true);
|
||||
}
|
||||
|
||||
} else if (u == Option::V4) {
|
||||
if (!(code == 84 ||
|
||||
code == 96 ||
|
||||
(code > 101 && code < 112) ||
|
||||
code == 115 ||
|
||||
code == 126 ||
|
||||
code == 127 ||
|
||||
(code > 146 && code < 150) ||
|
||||
(code > 177 && code < 208) ||
|
||||
(code > 213 && code < 220) ||
|
||||
(code > 221 && code < 255))) {
|
||||
return (true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return (false);
|
||||
}
|
||||
|
||||
OptionPtr
|
||||
LibDHCP::optionFactory(Option::Universe u,
|
||||
uint16_t type,
|
||||
@ -312,7 +292,7 @@ size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
|
||||
size_t last_offset = 0;
|
||||
|
||||
// Get the list of standard option definitions.
|
||||
const OptionDefContainerPtr& option_defs = LibDHCP::getOptionDefs(Option::V6);
|
||||
const OptionDefContainerPtr& option_defs = LibDHCP::getOptionDefs(option_space);
|
||||
// Runtime option definitions for non standard option space and if
|
||||
// the definition doesn't exist within the standard option definitions.
|
||||
const OptionDefContainerPtr& runtime_option_defs = LibDHCP::getRuntimeOptionDefs(option_space);
|
||||
@ -448,7 +428,7 @@ size_t LibDHCP::unpackOptions4(const OptionBuffer& buf,
|
||||
size_t last_offset = 0;
|
||||
|
||||
// Get the list of standard option definitions.
|
||||
const OptionDefContainerPtr& option_defs = LibDHCP::getOptionDefs(Option::V4);
|
||||
const OptionDefContainerPtr& option_defs = LibDHCP::getOptionDefs(option_space);
|
||||
// Runtime option definitions for non standard option space and if
|
||||
// the definition doesn't exist within the standard option definitions.
|
||||
const OptionDefContainerPtr& runtime_option_defs = LibDHCP::getRuntimeOptionDefs(option_space);
|
||||
@ -832,27 +812,42 @@ void LibDHCP::OptionFactoryRegister(Option::Universe u,
|
||||
|
||||
void
|
||||
LibDHCP::initStdOptionDefs4() {
|
||||
initOptionSpace(v4option_defs_, OPTION_DEF_PARAMS4, OPTION_DEF_PARAMS_SIZE4);
|
||||
initOptionSpace(v4option_defs_, STANDARD_V4_OPTION_DEFINITIONS,
|
||||
STANDARD_V4_OPTION_DEFINITIONS_SIZE);
|
||||
}
|
||||
|
||||
void
|
||||
LibDHCP::initStdOptionDefs6() {
|
||||
initOptionSpace(v6option_defs_, OPTION_DEF_PARAMS6, OPTION_DEF_PARAMS_SIZE6);
|
||||
initOptionSpace(v6option_defs_, STANDARD_V6_OPTION_DEFINITIONS,
|
||||
STANDARD_V6_OPTION_DEFINITIONS_SIZE);
|
||||
initOptionSpace(option_defs_[MAPE_V6_OPTION_SPACE], MAPE_V6_OPTION_DEFINITIONS,
|
||||
MAPE_V6_OPTION_DEFINITIONS_SIZE);
|
||||
initOptionSpace(option_defs_[MAPT_V6_OPTION_SPACE], MAPT_V6_OPTION_DEFINITIONS,
|
||||
MAPT_V6_OPTION_DEFINITIONS_SIZE);
|
||||
initOptionSpace(option_defs_[LW_V6_OPTION_SPACE], LW_V6_OPTION_DEFINITIONS,
|
||||
LW_V6_OPTION_DEFINITIONS_SIZE);
|
||||
initOptionSpace(option_defs_[V4V6_RULE_OPTION_SPACE], V4V6_RULE_OPTION_DEFINITIONS,
|
||||
V4V6_RULE_OPTION_DEFINITIONS_SIZE);
|
||||
initOptionSpace(option_defs_[V4V6_BIND_OPTION_SPACE], V4V6_BIND_OPTION_DEFINITIONS,
|
||||
V4V6_BIND_OPTION_DEFINITIONS_SIZE);
|
||||
}
|
||||
|
||||
void
|
||||
LibDHCP::initVendorOptsDocsis4() {
|
||||
initOptionSpace(vendor4_defs_[VENDOR_ID_CABLE_LABS], DOCSIS3_V4_DEFS, DOCSIS3_V4_DEFS_SIZE);
|
||||
initOptionSpace(vendor4_defs_[VENDOR_ID_CABLE_LABS], DOCSIS3_V4_DEFS,
|
||||
DOCSIS3_V4_DEFS_SIZE);
|
||||
}
|
||||
|
||||
void
|
||||
LibDHCP::initVendorOptsDocsis6() {
|
||||
initOptionSpace(vendor6_defs_[VENDOR_ID_CABLE_LABS], DOCSIS3_V6_DEFS, DOCSIS3_V6_DEFS_SIZE);
|
||||
initOptionSpace(vendor6_defs_[VENDOR_ID_CABLE_LABS], DOCSIS3_V6_DEFS,
|
||||
DOCSIS3_V6_DEFS_SIZE);
|
||||
}
|
||||
|
||||
void
|
||||
LibDHCP::initVendorOptsIsc6() {
|
||||
initOptionSpace(vendor6_defs_[ENTERPRISE_ID_ISC], ISC_V6_DEFS, ISC_V6_DEFS_SIZE);
|
||||
initOptionSpace(vendor6_defs_[ENTERPRISE_ID_ISC], ISC_V6_OPTION_DEFINITIONS,
|
||||
ISC_V6_OPTION_DEFINITIONS_SIZE);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
|
@ -27,38 +27,35 @@ public:
|
||||
/// Map of factory functions.
|
||||
typedef std::map<unsigned short, Option::Factory*> FactoryMap;
|
||||
|
||||
/// @brief Return collection of option definitions.
|
||||
/// @brief Returns collection of option definitions.
|
||||
///
|
||||
/// Method returns the collection of DHCP standard DHCP
|
||||
/// option definitions.
|
||||
/// @todo DHCPv4 option definitions are not implemented. For now
|
||||
/// this function will throw isc::NotImplemented in case of attempt
|
||||
/// to get option definitions for V4 universe.
|
||||
/// This method returns a collection of option definitions for a specified
|
||||
/// option space.
|
||||
///
|
||||
/// @param u universe of the options (V4 or V6).
|
||||
/// @param space Option space.
|
||||
///
|
||||
/// @return Pointer to a collection of option definitions.
|
||||
static const OptionDefContainerPtr& getOptionDefs(const Option::Universe u);
|
||||
static const OptionDefContainerPtr& getOptionDefs(const std::string& space);
|
||||
|
||||
/// @brief Return the first option definition matching a
|
||||
/// particular option code.
|
||||
///
|
||||
/// @param u universe (V4 or V6)
|
||||
/// @param space option space.
|
||||
/// @param code option code.
|
||||
///
|
||||
/// @return reference to an option definition being requested
|
||||
/// or NULL pointer if option definition has not been found.
|
||||
static OptionDefinitionPtr getOptionDef(const Option::Universe u,
|
||||
static OptionDefinitionPtr getOptionDef(const std::string& space,
|
||||
const uint16_t code);
|
||||
|
||||
/// @brief Return the definition of option having a specified name.
|
||||
///
|
||||
/// @param u universe (v4 or V6)
|
||||
/// @param space option space.
|
||||
/// @param name Option name.
|
||||
///
|
||||
/// @return Pointer to the option definition or NULL pointer if option
|
||||
/// definition has not been found.
|
||||
static OptionDefinitionPtr getOptionDef(const Option::Universe u,
|
||||
static OptionDefinitionPtr getOptionDef(const std::string& option_space,
|
||||
const std::string& name);
|
||||
|
||||
/// @brief Returns vendor option definition for a given vendor-id and code
|
||||
@ -115,21 +112,6 @@ public:
|
||||
static OptionDefContainerPtr
|
||||
getRuntimeOptionDefs(const std::string& space);
|
||||
|
||||
/// @brief Check if the specified option is a standard option.
|
||||
///
|
||||
/// @param u universe (V4 or V6)
|
||||
/// @param code option code.
|
||||
///
|
||||
/// @return true if the specified option is a standard option.
|
||||
/// @todo We already create option definitions for the subset if
|
||||
/// standard options. We are aiming that this function checks
|
||||
/// the presence of the standard option definition and if it finds
|
||||
/// it, then the true value is returned. However, at this point
|
||||
/// this is not doable because some of the definitions (for less
|
||||
/// important options) are not created yet.
|
||||
static bool isStandardOption(const Option::Universe u,
|
||||
const uint16_t code);
|
||||
|
||||
/// @brief Factory function to create instance of option.
|
||||
///
|
||||
/// Factory method creates instance of specified option. The option
|
||||
@ -377,6 +359,9 @@ private:
|
||||
/// Container with DHCPv6 option definitions.
|
||||
static OptionDefContainerPtr v6option_defs_;
|
||||
|
||||
/// Container that holds option definitions for various option spaces.
|
||||
static OptionDefContainers option_defs_;
|
||||
|
||||
/// Container for v4 vendor option definitions
|
||||
static VendorOptionDefContainers vendor4_defs_;
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <dhcp/dhcp6.h>
|
||||
#include <dhcp/libdhcp++.h>
|
||||
#include <dhcp/option6_ia.h>
|
||||
#include <dhcp/option_space.h>
|
||||
#include <exceptions/exceptions.h>
|
||||
#include <util/io_utilities.h>
|
||||
|
||||
@ -24,13 +25,13 @@ namespace dhcp {
|
||||
Option6IA::Option6IA(uint16_t type, uint32_t iaid)
|
||||
:Option(Option::V6, type), iaid_(iaid), t1_(0), t2_(0) {
|
||||
|
||||
// IA_TA has different layout than IA_NA and IA_PD. We can't sue this class
|
||||
// IA_TA has different layout than IA_NA and IA_PD. We can't use this class
|
||||
if (type == D6O_IA_TA) {
|
||||
isc_throw(BadValue, "Can't use Option6IA for IA_TA as it has "
|
||||
"a different layout");
|
||||
}
|
||||
|
||||
setEncapsulatedSpace("dhcp6");
|
||||
setEncapsulatedSpace(DHCP6_OPTION_SPACE);
|
||||
}
|
||||
|
||||
Option6IA::Option6IA(uint16_t type, OptionBufferConstIter begin,
|
||||
@ -43,7 +44,7 @@ Option6IA::Option6IA(uint16_t type, OptionBufferConstIter begin,
|
||||
"a different layout");
|
||||
}
|
||||
|
||||
setEncapsulatedSpace("dhcp6");
|
||||
setEncapsulatedSpace(DHCP6_OPTION_SPACE);
|
||||
|
||||
unpack(begin, end);
|
||||
}
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <dhcp/dhcp6.h>
|
||||
#include <dhcp/libdhcp++.h>
|
||||
#include <dhcp/option6_iaaddr.h>
|
||||
#include <dhcp/option_space.h>
|
||||
#include <exceptions/exceptions.h>
|
||||
#include <util/io_utilities.h>
|
||||
|
||||
@ -29,7 +30,7 @@ Option6IAAddr::Option6IAAddr(uint16_t type, const isc::asiolink::IOAddress& addr
|
||||
uint32_t pref, uint32_t valid)
|
||||
:Option(V6, type), addr_(addr), preferred_(pref),
|
||||
valid_(valid) {
|
||||
setEncapsulatedSpace("dhcp6");
|
||||
setEncapsulatedSpace(DHCP6_OPTION_SPACE);
|
||||
if (!addr.isV6()) {
|
||||
isc_throw(isc::BadValue, addr_ << " is not an IPv6 address");
|
||||
}
|
||||
@ -38,7 +39,7 @@ Option6IAAddr::Option6IAAddr(uint16_t type, const isc::asiolink::IOAddress& addr
|
||||
Option6IAAddr::Option6IAAddr(uint32_t type, OptionBuffer::const_iterator begin,
|
||||
OptionBuffer::const_iterator end)
|
||||
:Option(V6, type), addr_("::") {
|
||||
setEncapsulatedSpace("dhcp6");
|
||||
setEncapsulatedSpace(DHCP6_OPTION_SPACE);
|
||||
unpack(begin, end);
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <dhcp/dhcp6.h>
|
||||
#include <dhcp/libdhcp++.h>
|
||||
#include <dhcp/option6_iaprefix.h>
|
||||
#include <dhcp/option_space.h>
|
||||
#include <exceptions/exceptions.h>
|
||||
#include <util/io_utilities.h>
|
||||
|
||||
@ -28,7 +29,7 @@ namespace dhcp {
|
||||
Option6IAPrefix::Option6IAPrefix(uint16_t type, const isc::asiolink::IOAddress& prefix,
|
||||
uint8_t prefix_len, uint32_t pref, uint32_t valid)
|
||||
:Option6IAAddr(type, prefix, pref, valid), prefix_len_(prefix_len) {
|
||||
setEncapsulatedSpace("dhcp6");
|
||||
setEncapsulatedSpace(DHCP6_OPTION_SPACE);
|
||||
// Option6IAAddr will check if prefix is IPv6 and will throw if it is not
|
||||
if (prefix_len > 128) {
|
||||
isc_throw(BadValue, static_cast<unsigned>(prefix_len)
|
||||
@ -40,7 +41,7 @@ Option6IAPrefix::Option6IAPrefix(uint16_t type, const isc::asiolink::IOAddress&
|
||||
Option6IAPrefix::Option6IAPrefix(uint32_t type, OptionBuffer::const_iterator begin,
|
||||
OptionBuffer::const_iterator end)
|
||||
:Option6IAAddr(type, begin, end) {
|
||||
setEncapsulatedSpace("dhcp6");
|
||||
setEncapsulatedSpace(DHCP6_OPTION_SPACE);
|
||||
unpack(begin, end);
|
||||
}
|
||||
|
||||
|
227
src/lib/dhcp/option6_pdexclude.cc
Normal file
227
src/lib/dhcp/option6_pdexclude.cc
Normal file
@ -0,0 +1,227 @@
|
||||
// Copyright (C) 2016 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
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <asiolink/io_address.h>
|
||||
#include <exceptions/exceptions.h>
|
||||
#include <dhcp/dhcp6.h>
|
||||
#include <dhcp/option6_pdexclude.h>
|
||||
#include <exceptions/exceptions.h>
|
||||
#include <util/io_utilities.h>
|
||||
|
||||
#include <boost/dynamic_bitset.hpp>
|
||||
#include <iostream>
|
||||
#include <stdint.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace isc;
|
||||
using namespace isc::dhcp;
|
||||
using namespace isc::asiolink;
|
||||
using namespace isc::util;
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
|
||||
Option6PDExclude::Option6PDExclude(const isc::asiolink::IOAddress& delegated_prefix,
|
||||
const uint8_t delegated_prefix_length,
|
||||
const isc::asiolink::IOAddress& excluded_prefix,
|
||||
const uint8_t excluded_prefix_length)
|
||||
: Option(V6, D6O_PD_EXCLUDE),
|
||||
delegated_prefix_(delegated_prefix),
|
||||
delegated_prefix_length_(delegated_prefix_length),
|
||||
excluded_prefix_(excluded_prefix),
|
||||
excluded_prefix_length_(excluded_prefix_length) {
|
||||
|
||||
// Expecting v6 prefixes of sane length.
|
||||
if (!delegated_prefix_.isV6() || !excluded_prefix_.isV6() ||
|
||||
(delegated_prefix_length_ > 128) || (excluded_prefix_length_ > 128)) {
|
||||
isc_throw(BadValue, "invalid delegated or excluded prefix values specified: "
|
||||
<< delegated_prefix_ << "/"
|
||||
<< static_cast<int>(delegated_prefix_length_) << ", "
|
||||
<< excluded_prefix_ << "/"
|
||||
<< static_cast<int>(excluded_prefix_length_));
|
||||
}
|
||||
|
||||
// Excluded prefix must be longer than the delegated prefix.
|
||||
if (excluded_prefix_length_ <= delegated_prefix_length_) {
|
||||
isc_throw(BadValue, "length of the excluded prefix "
|
||||
<< excluded_prefix_ << "/"
|
||||
<< static_cast<int>(excluded_prefix_length_)
|
||||
<< " must be greater than the length of the"
|
||||
" delegated prefix " << delegated_prefix_ << "/"
|
||||
<< static_cast<int>(delegated_prefix_length_));
|
||||
}
|
||||
|
||||
// Both prefixes must share common part with a length equal to the
|
||||
// delegated prefix length.
|
||||
std::vector<uint8_t> delegated_prefix_bytes = delegated_prefix_.toBytes();
|
||||
boost::dynamic_bitset<uint8_t> delegated_prefix_bits(delegated_prefix_bytes.rbegin(),
|
||||
delegated_prefix_bytes.rend());
|
||||
|
||||
std::vector<uint8_t> excluded_prefix_bytes = excluded_prefix_.toBytes();
|
||||
boost::dynamic_bitset<uint8_t> excluded_prefix_bits(excluded_prefix_bytes.rbegin(),
|
||||
excluded_prefix_bytes.rend());
|
||||
|
||||
|
||||
// See RFC6603, section 4.2: assert(p1>>s == p2>>s)
|
||||
const uint8_t delta = 128 - delegated_prefix_length;
|
||||
|
||||
if ((delegated_prefix_bits >> delta) != (excluded_prefix_bits >> delta)) {
|
||||
isc_throw(BadValue, "excluded prefix "
|
||||
<< excluded_prefix_ << "/"
|
||||
<< static_cast<int>(excluded_prefix_length_)
|
||||
<< " must have the same common prefix part of "
|
||||
<< static_cast<int>(delegated_prefix_length)
|
||||
<< " as the delegated prefix "
|
||||
<< delegated_prefix_ << "/"
|
||||
<< static_cast<int>(delegated_prefix_length_));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Option6PDExclude::Option6PDExclude(const isc::asiolink::IOAddress& delegated_prefix,
|
||||
const uint8_t delegated_prefix_length,
|
||||
OptionBufferConstIter begin,
|
||||
OptionBufferConstIter end)
|
||||
: Option(V6, D6O_PD_EXCLUDE),
|
||||
delegated_prefix_(delegated_prefix),
|
||||
delegated_prefix_length_(delegated_prefix_length),
|
||||
excluded_prefix_(IOAddress::IPV6_ZERO_ADDRESS()),
|
||||
excluded_prefix_length_(0) {
|
||||
unpack(begin, end);
|
||||
}
|
||||
|
||||
OptionPtr
|
||||
Option6PDExclude::clone() const {
|
||||
return (cloneInternal<Option6PDExclude>());
|
||||
}
|
||||
|
||||
void
|
||||
Option6PDExclude::pack(isc::util::OutputBuffer& buf) const {
|
||||
// Header = option code and length.
|
||||
packHeader(buf);
|
||||
|
||||
// Excluded prefix length is always 1 byte long field.
|
||||
buf.writeUint8(excluded_prefix_length_);
|
||||
|
||||
// Retrieve entire prefix and convert it to bit representation.
|
||||
std::vector<uint8_t> excluded_prefix_bytes = excluded_prefix_.toBytes();
|
||||
boost::dynamic_bitset<uint8_t> bits(excluded_prefix_bytes.rbegin(),
|
||||
excluded_prefix_bytes.rend());
|
||||
|
||||
// Shifting prefix by delegated prefix length leaves us with only a
|
||||
// subnet id part of the excluded prefix.
|
||||
bits = bits << delegated_prefix_length_;
|
||||
|
||||
// Calculate subnet id length.
|
||||
const uint8_t subnet_id_length = getSubnetIDLength();
|
||||
for (uint8_t i = 0; i < subnet_id_length; ++i) {
|
||||
// Retrieve bit representation of the current byte.
|
||||
const boost::dynamic_bitset<uint8_t> first_byte = bits >> 120;
|
||||
// Convert it to a numeric value.
|
||||
uint8_t val = static_cast<uint8_t>(first_byte.to_ulong());
|
||||
|
||||
// Zero padded bits follow when excluded_prefix_length_ is not divisible by 8.
|
||||
if (i == subnet_id_length - 1) {
|
||||
uint8_t length_delta = excluded_prefix_length_ - delegated_prefix_length_;
|
||||
uint8_t mask = 0xFF;
|
||||
mask <<= (8 - (length_delta % 8));
|
||||
val &= mask;
|
||||
}
|
||||
// Store calculated value in a buffer.
|
||||
buf.writeUint8(val);
|
||||
|
||||
// Go to the next byte.
|
||||
bits <<= 8;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Option6PDExclude::unpack(OptionBufferConstIter begin,
|
||||
OptionBufferConstIter end) {
|
||||
|
||||
// At this point we don't know the excluded prefix length, but the
|
||||
// minimum requirement is that reminder of this option includes the
|
||||
// excluded prefix length and at least 1 byte of the IPv6 subnet id.
|
||||
if (std::distance(begin, end) < 2) {
|
||||
isc_throw(BadValue, "truncated Prefix Exclude option");
|
||||
}
|
||||
|
||||
// We can safely read the excluded prefix length and move forward.
|
||||
excluded_prefix_length_ = *begin++;
|
||||
|
||||
// We parsed the excluded prefix length so we can now determine the
|
||||
// size of the IPv6 subnet id. The reminder of the option should
|
||||
// include data of that size. If the option size is lower than the
|
||||
// subnet id length we report an error.
|
||||
const unsigned int subnet_id_length = getSubnetIDLength();
|
||||
if (subnet_id_length > std::distance(begin, end)) {
|
||||
isc_throw(BadValue, "truncated Prefix Exclude option, expected "
|
||||
"IPv6 subnet id length is " << subnet_id_length);
|
||||
}
|
||||
|
||||
// Get binary representation of the delegated prefix.
|
||||
std::vector<uint8_t> delegated_prefix_bytes = delegated_prefix_.toBytes();
|
||||
// We need to calculate how many bytes include the useful data and assign
|
||||
// zeros to remaining bytes (beyond the prefix length).
|
||||
const uint8_t bytes_length = (delegated_prefix_length_ / 8) +
|
||||
static_cast<uint8_t>(delegated_prefix_length_ % 8 != 0);
|
||||
std::fill(delegated_prefix_bytes.begin() + bytes_length,
|
||||
delegated_prefix_bytes.end(), 0);
|
||||
|
||||
// Convert the delegated prefix to bit format.
|
||||
boost::dynamic_bitset<uint8_t> bits(delegated_prefix_bytes.rbegin(),
|
||||
delegated_prefix_bytes.rend());
|
||||
|
||||
// Convert subnet id to bit format.
|
||||
std::vector<uint8_t> subnet_id_bytes(begin, end);
|
||||
boost::dynamic_bitset<uint8_t> subnet_id_bits(subnet_id_bytes.rbegin(),
|
||||
subnet_id_bytes.rend());
|
||||
|
||||
// Subnet id parsed, proceed to the end of the option.
|
||||
begin = end;
|
||||
|
||||
// Concatenate the delegated prefix with subnet id. The resulting prefix
|
||||
// is an excluded prefix in bit format.
|
||||
for (int i = subnet_id_bits.size() - 1; i >= 0; --i) {
|
||||
bits.set(128 - delegated_prefix_length_ - subnet_id_bits.size() + i,
|
||||
subnet_id_bits.test(i));
|
||||
}
|
||||
|
||||
// Convert the prefix to binary format.
|
||||
std::vector<uint8_t> bytes(V6ADDRESS_LEN);
|
||||
boost::to_block_range(bits, bytes.rbegin());
|
||||
|
||||
// And create a prefix object from bytes.
|
||||
excluded_prefix_ = IOAddress::fromBytes(AF_INET6, &bytes[0]);
|
||||
}
|
||||
|
||||
uint16_t
|
||||
Option6PDExclude::len() const {
|
||||
return (getHeaderLen() + sizeof(excluded_prefix_length_)
|
||||
+ getSubnetIDLength());
|
||||
}
|
||||
|
||||
std::string
|
||||
Option6PDExclude::toText(int indent) const {
|
||||
std::ostringstream s;
|
||||
s << headerToText(indent) << ": ";
|
||||
s << excluded_prefix_ << "/"
|
||||
<< static_cast<int>(excluded_prefix_length_);
|
||||
return (s.str());
|
||||
}
|
||||
|
||||
uint8_t
|
||||
Option6PDExclude::getSubnetIDLength() const {
|
||||
uint8_t subnet_id_length_bits = excluded_prefix_length_ -
|
||||
delegated_prefix_length_ - 1;
|
||||
uint8_t subnet_id_length = (subnet_id_length_bits / 8) + 1;
|
||||
return (subnet_id_length);
|
||||
}
|
||||
|
||||
} // end of namespace isc::dhcp
|
||||
} // end of namespace isc
|
134
src/lib/dhcp/option6_pdexclude.h
Normal file
134
src/lib/dhcp/option6_pdexclude.h
Normal file
@ -0,0 +1,134 @@
|
||||
// Copyright (C) 2016 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
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef OPTION6_PDEXCLUDE_H
|
||||
#define OPTION6_PDEXCLUDE_H
|
||||
|
||||
#include <asiolink/io_address.h>
|
||||
#include <dhcp/option.h>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
|
||||
/// @brief DHCPv6 option class representing Prefix Exclude Option (RFC 6603).
|
||||
///
|
||||
/// This class represents DHCPv6 Prefix Exclude option (67). This option is
|
||||
/// carried in the IA Prefix option and it conveys a single prefix which is
|
||||
/// used by the delegating router to communicate with a requesting router on
|
||||
/// the requesting router's uplink. This prefix is not used on the
|
||||
/// requesting router's downlinks (is excluded from other delegated prefixes).
|
||||
class Option6PDExclude: public Option {
|
||||
public:
|
||||
|
||||
/// @brief Constructor.
|
||||
///
|
||||
/// @param delegated_prefix Delagated prefix.
|
||||
/// @param delegated_prefix_length Delegated prefix length.
|
||||
/// @param excluded_prefix Excluded prefix.
|
||||
/// @param excluded_prefix_length Excluded prefix length.
|
||||
///
|
||||
/// @throw BadValue if prefixes are invalid, if excluded prefix length
|
||||
/// is not greater than delegated prefix length or if common parts of
|
||||
/// prefixes does not match.
|
||||
Option6PDExclude(const isc::asiolink::IOAddress& delegated_prefix,
|
||||
const uint8_t delegated_prefix_length,
|
||||
const isc::asiolink::IOAddress& excluded_prefix,
|
||||
const uint8_t excluded_prefix_length);
|
||||
|
||||
/// @brief Constructor, creates option instance from part of the buffer.
|
||||
///
|
||||
/// This constructor is mostly used to parse Prefix Exclude options in the
|
||||
/// received messages.
|
||||
///
|
||||
/// @param begin Lower bound of the buffer to create option from.
|
||||
/// @param end Upper bound of the buffer to create option from.
|
||||
Option6PDExclude(const isc::asiolink::IOAddress& delegated_prefix,
|
||||
const uint8_t delegated_prefix_length,
|
||||
OptionBufferConstIter begin, OptionBufferConstIter end);
|
||||
|
||||
/// @brief Copies this option and returns a pointer to the copy.
|
||||
virtual OptionPtr clone() const;
|
||||
|
||||
/// @brief Writes option in wire-format to a buffer.
|
||||
///
|
||||
/// Writes option in wire-format to buffer, returns pointer to first unused
|
||||
/// byte after stored option (that is useful for writing options one after
|
||||
/// another).
|
||||
///
|
||||
/// The format of the option includes excluded prefix length specified as
|
||||
/// a number of bits. It also includes IPv6 subnet ID field which is
|
||||
/// computed from the delegated and excluded prefixes, according to the
|
||||
/// section 4.2 of RFC 6603.
|
||||
///
|
||||
/// @param [out] buf Pointer to a buffer.
|
||||
virtual void pack(isc::util::OutputBuffer& buf) const;
|
||||
|
||||
/// @brief Parses received buffer.
|
||||
///
|
||||
/// @param begin iterator to first byte of option data
|
||||
/// @param end iterator to end of option data (first byte after option end)
|
||||
virtual void unpack(OptionBufferConstIter begin, OptionBufferConstIter end);
|
||||
|
||||
/// @brief Returns length of the complete option (data length + DHCPv6
|
||||
/// option header)
|
||||
///
|
||||
/// @return length of the option
|
||||
virtual uint16_t len() const;
|
||||
|
||||
/// @brief Returns Prefix Exclude option in textual format.
|
||||
///
|
||||
/// @param ident Number of spaces to be inserted before the text.
|
||||
virtual std::string toText(int indent = 0) const;
|
||||
|
||||
/// @brief Returns delegated prefix.
|
||||
isc::asiolink::IOAddress getDelegatedPrefix() const {
|
||||
return (delegated_prefix_);
|
||||
}
|
||||
|
||||
/// @brief Returns delegated prefix length.
|
||||
uint8_t getDelegatedPrefixLength() const {
|
||||
return (delegated_prefix_length_);
|
||||
}
|
||||
|
||||
/// @brief Returns excluded prefix.
|
||||
isc::asiolink::IOAddress getExcludedPrefix() const {
|
||||
return (excluded_prefix_);
|
||||
}
|
||||
|
||||
/// @brief Returns excluded prefix length.
|
||||
uint8_t getExcludedPrefixLength() const {
|
||||
return (excluded_prefix_length_);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// @brief Returns IPv6 subnet ID length in octets.
|
||||
///
|
||||
/// The IPv6 subnet ID length is between 1 and 16 octets.
|
||||
uint8_t getSubnetIDLength() const;
|
||||
|
||||
/// @brief Holds delegated prefix.
|
||||
isc::asiolink::IOAddress delegated_prefix_;
|
||||
|
||||
/// @brief Holds delegated prefix length,
|
||||
uint8_t delegated_prefix_length_;
|
||||
|
||||
/// @brief Holds excluded prefix.
|
||||
isc::asiolink::IOAddress excluded_prefix_;
|
||||
|
||||
/// @brief Holds excluded prefix length.
|
||||
uint8_t excluded_prefix_length_;
|
||||
};
|
||||
|
||||
/// @brief Pointer to the @ref Option6PDExclude object.
|
||||
typedef boost::shared_ptr<Option6PDExclude> Option6PDExcludePtr;
|
||||
|
||||
} // isc::dhcp namespace
|
||||
} // isc namespace
|
||||
|
||||
#endif // OPTION6_PDEXCLUDE_H
|
@ -10,6 +10,8 @@
|
||||
#include <dhcp/option_custom.h>
|
||||
#include <util/encode/hex.h>
|
||||
|
||||
using namespace isc::asiolink;
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
|
||||
@ -46,7 +48,7 @@ OptionCustom::clone() const {
|
||||
}
|
||||
|
||||
void
|
||||
OptionCustom::addArrayDataField(const asiolink::IOAddress& address) {
|
||||
OptionCustom::addArrayDataField(const IOAddress& address) {
|
||||
checkArrayType();
|
||||
|
||||
if ((address.isV4() && definition_.getType() != OPT_IPV4_ADDRESS_TYPE) ||
|
||||
@ -71,6 +73,36 @@ OptionCustom::addArrayDataField(const bool value) {
|
||||
buffers_.push_back(buf);
|
||||
}
|
||||
|
||||
void
|
||||
OptionCustom::addArrayDataField(const PrefixLen& prefix_len,
|
||||
const asiolink::IOAddress& prefix) {
|
||||
checkArrayType();
|
||||
|
||||
if (definition_.getType() != OPT_IPV6_PREFIX_TYPE) {
|
||||
isc_throw(BadDataTypeCast, "IPv6 prefix can be specified only for"
|
||||
" an option comprising an array of IPv6 prefix values");
|
||||
}
|
||||
|
||||
OptionBuffer buf;
|
||||
OptionDataTypeUtil::writePrefix(prefix_len, prefix, buf);
|
||||
buffers_.push_back(buf);
|
||||
}
|
||||
|
||||
void
|
||||
OptionCustom::addArrayDataField(const PSIDLen& psid_len, const PSID& psid) {
|
||||
checkArrayType();
|
||||
|
||||
if (definition_.getType() != OPT_PSID_TYPE) {
|
||||
isc_throw(BadDataTypeCast, "PSID value can be specified onlu for"
|
||||
" an option comprising an array of PSID length / value"
|
||||
" tuples");
|
||||
}
|
||||
|
||||
OptionBuffer buf;
|
||||
OptionDataTypeUtil::writePsid(psid_len, psid, buf);
|
||||
buffers_.push_back(buf);
|
||||
}
|
||||
|
||||
void
|
||||
OptionCustom::checkIndex(const uint32_t index) const {
|
||||
if (index >= buffers_.size()) {
|
||||
@ -110,10 +142,17 @@ OptionCustom::createBuffers() {
|
||||
// For variable data sizes the utility function returns zero.
|
||||
// It is ok for string values because the default string
|
||||
// is 'empty'. However for FQDN the empty value is not valid
|
||||
// so we initialize it to '.'.
|
||||
if (data_size == 0 &&
|
||||
*field == OPT_FQDN_TYPE) {
|
||||
OptionDataTypeUtil::writeFqdn(".", buf);
|
||||
// so we initialize it to '.'. For prefix there is a prefix
|
||||
// length fixed field.
|
||||
if (data_size == 0) {
|
||||
if (*field == OPT_FQDN_TYPE) {
|
||||
OptionDataTypeUtil::writeFqdn(".", buf);
|
||||
|
||||
} else if (*field == OPT_IPV6_PREFIX_TYPE) {
|
||||
OptionDataTypeUtil::writePrefix(PrefixLen(0),
|
||||
IOAddress::IPV6_ZERO_ADDRESS(),
|
||||
buf);
|
||||
}
|
||||
} else {
|
||||
// At this point we can resize the buffer. Note that
|
||||
// for string values we are setting the empty buffer
|
||||
@ -135,9 +174,15 @@ OptionCustom::createBuffers() {
|
||||
// so we have to allocate exactly one buffer.
|
||||
OptionBuffer buf;
|
||||
size_t data_size = OptionDataTypeUtil::getDataTypeLen(data_type);
|
||||
if (data_size == 0 &&
|
||||
data_type == OPT_FQDN_TYPE) {
|
||||
OptionDataTypeUtil::writeFqdn(".", buf);
|
||||
if (data_size == 0) {
|
||||
if (data_type == OPT_FQDN_TYPE) {
|
||||
OptionDataTypeUtil::writeFqdn(".", buf);
|
||||
|
||||
} else if (data_type == OPT_IPV6_PREFIX_TYPE) {
|
||||
OptionDataTypeUtil::writePrefix(PrefixLen(0),
|
||||
IOAddress::IPV6_ZERO_ADDRESS(),
|
||||
buf);
|
||||
}
|
||||
} else {
|
||||
// Note that if our option holds a string value then
|
||||
// we are making empty buffer here.
|
||||
@ -191,7 +236,7 @@ OptionCustom::createBuffers(const OptionBuffer& data_buf) {
|
||||
// 1 byte larger than the size of the string
|
||||
// representation of this FQDN.
|
||||
data_size = fqdn.size() + 1;
|
||||
} else if ( (*field == OPT_BINARY_TYPE) || (*field == OPT_STRING_TYPE) ) {
|
||||
} else if ((*field == OPT_BINARY_TYPE) || (*field == OPT_STRING_TYPE)) {
|
||||
// In other case we are dealing with string or binary value
|
||||
// which size can't be determined. Thus we consume the
|
||||
// remaining part of the buffer for it. Note that variable
|
||||
@ -199,19 +244,28 @@ OptionCustom::createBuffers(const OptionBuffer& data_buf) {
|
||||
// that the validate() function in OptionDefinition object
|
||||
// should have checked wheter it is a case for this option.
|
||||
data_size = std::distance(data, data_buf.end());
|
||||
} else if (*field == OPT_IPV6_PREFIX_TYPE ) {
|
||||
// The size of the IPV6 prefix type is determined as
|
||||
// one byte (which is the size of the prefix in bits)
|
||||
// followed by the prefix bits (right-padded with
|
||||
// zeros to the nearest octet boundary).
|
||||
if (std::distance(data, data_buf.end()) > 0) {
|
||||
data_size = static_cast<size_t>(sizeof(uint8_t) + (*data + 7) / 8);
|
||||
}
|
||||
} else {
|
||||
// If we reached the end of buffer we assume that this option is
|
||||
// truncated because there is no remaining data to initialize
|
||||
// an option field.
|
||||
isc_throw(OutOfRange, "option buffer truncated");
|
||||
}
|
||||
} else {
|
||||
// Our data field requires that there is a certain chunk of
|
||||
// data left in the buffer. If not, option is truncated.
|
||||
if (std::distance(data, data_buf.end()) < data_size) {
|
||||
isc_throw(OutOfRange, "option buffer truncated");
|
||||
}
|
||||
}
|
||||
|
||||
// Our data field requires that there is a certain chunk of
|
||||
// data left in the buffer. If not, option is truncated.
|
||||
if (std::distance(data, data_buf.end()) < data_size) {
|
||||
isc_throw(OutOfRange, "option buffer truncated");
|
||||
}
|
||||
|
||||
// Store the created buffer.
|
||||
buffers.push_back(OptionBuffer(data, data + data_size));
|
||||
// Proceed to the next data field.
|
||||
@ -253,6 +307,13 @@ OptionCustom::createBuffers(const OptionBuffer& data_buf) {
|
||||
// 1 byte larger than the size of the string
|
||||
// representation of this FQDN.
|
||||
data_size = fqdn.size() + 1;
|
||||
|
||||
} else if (data_type == OPT_IPV6_PREFIX_TYPE) {
|
||||
PrefixTuple prefix =
|
||||
OptionDataTypeUtil::readPrefix(OptionBuffer(data, data_buf.end()));
|
||||
// Data size comprises 1 byte holding a prefix length and the
|
||||
// prefix length (in bytes) rounded to the nearest byte boundary.
|
||||
data_size = sizeof(uint8_t) + (prefix.first.asUint8() + 7) / 8;
|
||||
}
|
||||
// We don't perform other checks for data types that can't be
|
||||
// used together with array indicator such as strings, empty field
|
||||
@ -284,11 +345,17 @@ OptionCustom::createBuffers(const OptionBuffer& data_buf) {
|
||||
// 1 bytes larger than the size of the string
|
||||
// representation of this FQDN.
|
||||
data_size = fqdn.size() + 1;
|
||||
|
||||
} else if (data_type == OPT_IPV6_PREFIX_TYPE) {
|
||||
if (!data_buf.empty()) {
|
||||
data_size = static_cast<size_t>
|
||||
(sizeof(uint8_t) + (data_buf[0] + 7) / 8);
|
||||
}
|
||||
} else {
|
||||
data_size = std::distance(data, data_buf.end());
|
||||
}
|
||||
}
|
||||
if (data_size > 0) {
|
||||
if ((data_size > 0) && (std::distance(data, data_buf.end()) >= data_size)) {
|
||||
buffers.push_back(OptionBuffer(data, data + data_size));
|
||||
data += data_size;
|
||||
} else {
|
||||
@ -383,7 +450,7 @@ OptionCustom::pack(isc::util::OutputBuffer& buf) const {
|
||||
}
|
||||
|
||||
|
||||
asiolink::IOAddress
|
||||
IOAddress
|
||||
OptionCustom::readAddress(const uint32_t index) const {
|
||||
checkIndex(index);
|
||||
|
||||
@ -402,10 +469,8 @@ OptionCustom::readAddress(const uint32_t index) const {
|
||||
}
|
||||
|
||||
void
|
||||
OptionCustom::writeAddress(const asiolink::IOAddress& address,
|
||||
OptionCustom::writeAddress(const IOAddress& address,
|
||||
const uint32_t index) {
|
||||
using namespace isc::asiolink;
|
||||
|
||||
checkIndex(index);
|
||||
|
||||
if ((address.isV4() && buffers_[index].size() != V4ADDRESS_LEN) ||
|
||||
@ -471,6 +536,45 @@ OptionCustom::writeFqdn(const std::string& fqdn, const uint32_t index) {
|
||||
std::swap(buffers_[index], buf);
|
||||
}
|
||||
|
||||
PrefixTuple
|
||||
OptionCustom::readPrefix(const uint32_t index) const {
|
||||
checkIndex(index);
|
||||
return (OptionDataTypeUtil::readPrefix(buffers_[index]));
|
||||
}
|
||||
|
||||
void
|
||||
OptionCustom::writePrefix(const PrefixLen& prefix_len,
|
||||
const IOAddress& prefix,
|
||||
const uint32_t index) {
|
||||
checkIndex(index);
|
||||
|
||||
OptionBuffer buf;
|
||||
OptionDataTypeUtil::writePrefix(prefix_len, prefix, buf);
|
||||
// If there are no errors while writing PSID to a buffer, we can
|
||||
// replace the current buffer with a new buffer.
|
||||
std::swap(buffers_[index], buf);
|
||||
}
|
||||
|
||||
|
||||
PSIDTuple
|
||||
OptionCustom::readPsid(const uint32_t index) const {
|
||||
checkIndex(index);
|
||||
return (OptionDataTypeUtil::readPsid(buffers_[index]));
|
||||
}
|
||||
|
||||
void
|
||||
OptionCustom::writePsid(const PSIDLen& psid_len, const PSID& psid,
|
||||
const uint32_t index) {
|
||||
checkIndex(index);
|
||||
|
||||
OptionBuffer buf;
|
||||
OptionDataTypeUtil::writePsid(psid_len, psid, buf);
|
||||
// If there are no errors while writing PSID to a buffer, we can
|
||||
// replace the current buffer with a new buffer.
|
||||
std::swap(buffers_[index], buf);
|
||||
}
|
||||
|
||||
|
||||
std::string
|
||||
OptionCustom::readString(const uint32_t index) const {
|
||||
checkIndex(index);
|
||||
|
@ -7,6 +7,7 @@
|
||||
#ifndef OPTION_CUSTOM_H
|
||||
#define OPTION_CUSTOM_H
|
||||
|
||||
#include <asiolink/io_address.h>
|
||||
#include <dhcp/option.h>
|
||||
#include <dhcp/option_definition.h>
|
||||
#include <util/io_utilities.h>
|
||||
@ -112,6 +113,19 @@ public:
|
||||
buffers_.push_back(buf);
|
||||
}
|
||||
|
||||
/// @brief Create new buffer and store variable length prefix in it.
|
||||
///
|
||||
/// @param prefix_len Prefix length.
|
||||
/// @param prefix Prefix.
|
||||
void addArrayDataField(const PrefixLen& prefix_len,
|
||||
const asiolink::IOAddress& prefix);
|
||||
|
||||
/// @brief Create new buffer and store PSID length / value in it.
|
||||
///
|
||||
/// @param psid_len PSID length.
|
||||
/// @param psid PSID.
|
||||
void addArrayDataField(const PSIDLen& psid_len, const PSID& psid);
|
||||
|
||||
/// @brief Return a number of the data fields.
|
||||
///
|
||||
/// @return number of data fields held by the option.
|
||||
@ -228,6 +242,45 @@ public:
|
||||
std::swap(buffers_[index], buf);
|
||||
}
|
||||
|
||||
/// @brief Read a buffer as variable length prefix.
|
||||
///
|
||||
/// @param index buffer index.
|
||||
///
|
||||
/// @return Prefix length / value tuple.
|
||||
/// @throw isc::OutOfRange of index is out of range.
|
||||
PrefixTuple readPrefix(const uint32_t index = 0) const;
|
||||
|
||||
/// @brief Write prefix length and value into a buffer.
|
||||
///
|
||||
/// @param prefix_len Prefix length.
|
||||
/// @param prefix Prefix value.
|
||||
/// @param index Buffer index.
|
||||
///
|
||||
/// @throw isc::OutOfRange if index is out of range.
|
||||
void writePrefix(const PrefixLen& prefix_len,
|
||||
const asiolink::IOAddress& prefix,
|
||||
const uint32_t index = 0);
|
||||
|
||||
/// @brief Read a buffer as a PSID length / value tuple.
|
||||
///
|
||||
/// @param index buffer index.
|
||||
///
|
||||
/// @return PSID length / value tuple.
|
||||
/// @throw isc::OutOfRange of index is out of range.
|
||||
PSIDTuple readPsid(const uint32_t index = 0) const;
|
||||
|
||||
/// @brief Write PSID length / value into a buffer.
|
||||
///
|
||||
/// @param psid_len PSID length value.
|
||||
/// @param psid PSID value in the range of 0 .. 2^(PSID length).
|
||||
/// @param index buffer index.
|
||||
///
|
||||
/// @throw isc::dhcp::BadDataTypeCast if PSID length or value is
|
||||
/// invalid.
|
||||
/// @throw isc::OutOfRange if index is out of range.
|
||||
void writePsid(const PSIDLen& psid_len, const PSID& psid,
|
||||
const uint32_t index = 0);
|
||||
|
||||
/// @brief Read a buffer as string value.
|
||||
///
|
||||
/// @param index buffer index.
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2012-2016 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
|
||||
@ -8,6 +8,9 @@
|
||||
#include <dns/labelsequence.h>
|
||||
#include <dns/name.h>
|
||||
#include <util/encode/hex.h>
|
||||
#include <algorithm>
|
||||
|
||||
using namespace isc::asiolink;
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
@ -24,6 +27,8 @@ OptionDataTypeUtil::OptionDataTypeUtil() {
|
||||
data_types_["uint32"] = OPT_UINT32_TYPE;
|
||||
data_types_["ipv4-address"] = OPT_IPV4_ADDRESS_TYPE;
|
||||
data_types_["ipv6-address"] = OPT_IPV6_ADDRESS_TYPE;
|
||||
data_types_["ipv6-prefix"] = OPT_IPV6_PREFIX_TYPE;
|
||||
data_types_["psid"] = OPT_PSID_TYPE;
|
||||
data_types_["string"] = OPT_STRING_TYPE;
|
||||
data_types_["fqdn"] = OPT_FQDN_TYPE;
|
||||
data_types_["record"] = OPT_RECORD_TYPE;
|
||||
@ -39,6 +44,8 @@ OptionDataTypeUtil::OptionDataTypeUtil() {
|
||||
data_type_names_[OPT_UINT32_TYPE] = "uint32";
|
||||
data_type_names_[OPT_IPV4_ADDRESS_TYPE] = "ipv4-address";
|
||||
data_type_names_[OPT_IPV6_ADDRESS_TYPE] = "ipv6-address";
|
||||
data_type_names_[OPT_IPV6_PREFIX_TYPE] = "ipv6-prefix";
|
||||
data_type_names_[OPT_PSID_TYPE] = "psid";
|
||||
data_type_names_[OPT_STRING_TYPE] = "string";
|
||||
data_type_names_[OPT_FQDN_TYPE] = "fqdn";
|
||||
data_type_names_[OPT_RECORD_TYPE] = "record";
|
||||
@ -86,6 +93,9 @@ OptionDataTypeUtil::getDataTypeLen(const OptionDataType data_type) {
|
||||
case OPT_IPV6_ADDRESS_TYPE:
|
||||
return (asiolink::V6ADDRESS_LEN);
|
||||
|
||||
case OPT_PSID_TYPE:
|
||||
return (3);
|
||||
|
||||
default:
|
||||
;
|
||||
}
|
||||
@ -237,6 +247,207 @@ OptionDataTypeUtil::getLabelCount(const std::string& text_name) {
|
||||
}
|
||||
}
|
||||
|
||||
PrefixTuple
|
||||
OptionDataTypeUtil::readPrefix(const std::vector<uint8_t>& buf) {
|
||||
// Prefix typically consists of the prefix length and the
|
||||
// actual value. If prefix length is 0, the buffer length should
|
||||
// be at least 1 byte to hold this length value.
|
||||
if (buf.empty()) {
|
||||
isc_throw(BadDataTypeCast, "unable to read prefix length from "
|
||||
"a truncated buffer");
|
||||
}
|
||||
|
||||
// Surround everything with try-catch to unify exceptions being
|
||||
// thrown by various functions and constructors.
|
||||
try {
|
||||
// Try to create PrefixLen object from the prefix length held
|
||||
// in the buffer. This may cause an exception if the length is
|
||||
// invalid (greater than 128).
|
||||
PrefixLen prefix_len(buf.at(0));
|
||||
|
||||
// Convert prefix length to bytes, because we operate on bytes,
|
||||
// rather than bits.
|
||||
uint8_t prefix_len_bytes = (prefix_len.asUint8() / 8);
|
||||
// Check if we need to zero pad any bits. This is the case when
|
||||
// the prefix length is not divisible by 8 (bits per byte). The
|
||||
// calculations below may require some explanations. We first
|
||||
// perform prefix_len % 8 to get the number of useful bits beyond
|
||||
// the current prefix_len_bytes value. By substracting it from 8
|
||||
// we get the number of zero padded bits, but with the special
|
||||
// case of 8 when the result of substraction is 0. The value of
|
||||
// 8 really means no padding so we make a modulo division once
|
||||
// again to turn 8s to 0s.
|
||||
const uint8_t zero_padded_bits =
|
||||
static_cast<uint8_t>((8 - (prefix_len.asUint8() % 8)) % 8);
|
||||
// If there are zero padded bits, it means that we need an extra
|
||||
// byte to be retrieved from the buffer.
|
||||
if (zero_padded_bits > 0) {
|
||||
++prefix_len_bytes;
|
||||
}
|
||||
|
||||
// Make sure that the buffer is long enough. We substract 1 to
|
||||
// also account for the fact that the buffer includes a prefix
|
||||
// length besides a prefix.
|
||||
if ((buf.size() - 1) < prefix_len_bytes) {
|
||||
isc_throw(BadDataTypeCast, "unable to read a prefix having length of "
|
||||
<< prefix_len.asUnsigned() << " from a truncated buffer");
|
||||
}
|
||||
|
||||
// It is possible for a prefix to be zero if the prefix length
|
||||
// is zero.
|
||||
IOAddress prefix(IOAddress::IPV6_ZERO_ADDRESS());
|
||||
|
||||
// If there is anything more than prefix length is this buffer
|
||||
// we need to read it.
|
||||
if (buf.size() > 1) {
|
||||
// Buffer has to be copied, because we will modify its
|
||||
// contents by setting certain bits to 0, if necessary.
|
||||
std::vector<uint8_t> prefix_buf(buf.begin() + 1, buf.end());
|
||||
// All further conversions require that the buffer length is
|
||||
// 16 bytes.
|
||||
if (prefix_buf.size() < V6ADDRESS_LEN) {
|
||||
prefix_buf.resize(V6ADDRESS_LEN);
|
||||
if (prefix_len_bytes < prefix_buf.size()) {
|
||||
// Zero all bits in the buffer beyond prefix length
|
||||
// position.
|
||||
std::fill(prefix_buf.begin() + prefix_len_bytes,
|
||||
prefix_buf.end(), 0);
|
||||
|
||||
if (zero_padded_bits) {
|
||||
// There is a byte that require zero padding. We
|
||||
// achieve that by shifting the value of that byte
|
||||
// back and forth by the number of zeroed bits.
|
||||
prefix_buf.at(prefix_len_bytes - 1) =
|
||||
(prefix_buf.at(prefix_len_bytes - 1)
|
||||
>> zero_padded_bits)
|
||||
<< zero_padded_bits;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Convert the buffer to the IOAddress object.
|
||||
prefix = IOAddress::fromBytes(AF_INET6, &prefix_buf[0]);
|
||||
}
|
||||
|
||||
return (std::make_pair(prefix_len, prefix));
|
||||
|
||||
} catch (const BadDataTypeCast& ex) {
|
||||
// Pass through the BadDataTypeCast exceptions.
|
||||
throw;
|
||||
|
||||
} catch (const std::exception& ex) {
|
||||
// If an exception of a different type has been thrown, insert
|
||||
// a text that indicates that the failure occurred during reading
|
||||
// the prefix and modify exception type to BadDataTypeCast.
|
||||
isc_throw(BadDataTypeCast, "unable to read a prefix from a buffer: "
|
||||
<< ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
OptionDataTypeUtil::writePrefix(const PrefixLen& prefix_len,
|
||||
const IOAddress& prefix,
|
||||
std::vector<uint8_t>& buf) {
|
||||
// Prefix must be an IPv6 prefix.
|
||||
if (!prefix.isV6()) {
|
||||
isc_throw(BadDataTypeCast, "illegal prefix value "
|
||||
<< prefix);
|
||||
}
|
||||
|
||||
// We don't need to validate the prefix_len value, because it is
|
||||
// already validated by the PrefixLen class.
|
||||
buf.push_back(prefix_len.asUint8());
|
||||
|
||||
// Convert the prefix length to a number of bytes.
|
||||
uint8_t prefix_len_bytes = prefix_len.asUint8() / 8;
|
||||
// Check if there are any bits that require zero padding. See the
|
||||
// commentary in readPrefix to see how this is calculated.
|
||||
const uint8_t zero_padded_bits =
|
||||
static_cast<uint8_t>((8 - (prefix_len.asUint8() % 8)) % 8);
|
||||
// If zero padding is needed it means that we need to extend the
|
||||
// buffer to hold the "partially occupied" byte.
|
||||
if (zero_padded_bits > 0) {
|
||||
++prefix_len_bytes;
|
||||
}
|
||||
|
||||
// Convert the prefix to byte representation and append it to
|
||||
// our output buffer.
|
||||
std::vector<uint8_t> prefix_bytes = prefix.toBytes();
|
||||
buf.insert(buf.end(), prefix_bytes.begin(),
|
||||
prefix_bytes.begin() + prefix_len_bytes);
|
||||
// If the last byte requires zero padding we achieve that by shifting
|
||||
// bits back and forth by the number of insignificant bits.
|
||||
if (zero_padded_bits) {
|
||||
*buf.rbegin() = (*buf.rbegin() >> zero_padded_bits) << zero_padded_bits;
|
||||
}
|
||||
}
|
||||
|
||||
PSIDTuple
|
||||
OptionDataTypeUtil::readPsid(const std::vector<uint8_t>& buf) {
|
||||
if (buf.size() < 3) {
|
||||
isc_throw(BadDataTypeCast, "unable to read PSID from the buffer."
|
||||
<< " Invalid buffer size " << buf.size()
|
||||
<< ". Expected 3 bytes (PSID length and PSID value)");
|
||||
}
|
||||
|
||||
// Read PSID length.
|
||||
uint8_t psid_len = buf[0];
|
||||
|
||||
// PSID length must not be greater than 16 bits.
|
||||
if (psid_len > sizeof(uint16_t) * 8) {
|
||||
isc_throw(BadDataTypeCast, "invalid PSID length value "
|
||||
<< static_cast<unsigned>(psid_len)
|
||||
<< ", this value is expected to be in range of 0 to 16");
|
||||
}
|
||||
|
||||
// Read two bytes of PSID value.
|
||||
uint16_t psid = isc::util::readUint16(&buf[1], 2);
|
||||
|
||||
// We need to check that the PSID value does not exceed the maximum value
|
||||
// for a specified PSID length. That means that all bits placed further than
|
||||
// psid_len from the left must be set to 0. So, we create a bit mask
|
||||
// by shifting a value of 0xFFFF to the left and right by psid_len. This
|
||||
// leaves us with psid_len leftmost bits unset and the rest set. Next, we
|
||||
// apply the mask on the PSID value from the buffer and make sure the result
|
||||
// is 0. Otherwise, it means that there are some bits set in the PSID which
|
||||
// aren't supposed to be set.
|
||||
if ((psid_len > 0) &&
|
||||
((psid & static_cast<uint16_t>(static_cast<uint16_t>(0xFFFF << psid_len)
|
||||
>> psid_len)) != 0)) {
|
||||
isc_throw(BadDataTypeCast, "invalid PSID value " << psid
|
||||
<< " for a specified PSID length "
|
||||
<< static_cast<unsigned>(psid_len));
|
||||
}
|
||||
|
||||
// All is good, so we can convert the PSID value read from the buffer to
|
||||
// the port set number.
|
||||
psid = psid >> (sizeof(psid) * 8 - psid_len);
|
||||
return (std::make_pair(PSIDLen(psid_len), PSID(psid)));
|
||||
}
|
||||
|
||||
void
|
||||
OptionDataTypeUtil::writePsid(const PSIDLen& psid_len, const PSID& psid,
|
||||
std::vector<uint8_t>& buf) {
|
||||
if (psid_len.asUint8() > sizeof(psid) * 8) {
|
||||
isc_throw(BadDataTypeCast, "invalid PSID length value "
|
||||
<< psid_len.asUnsigned()
|
||||
<< ", this value is expected to be in range of 0 to 16");
|
||||
}
|
||||
|
||||
if (psid_len.asUint8() > 0 &&
|
||||
(psid.asUint16() > (0xFFFF >> (sizeof(uint16_t) * 8 - psid_len.asUint8())))) {
|
||||
isc_throw(BadDataTypeCast, "invalid PSID value " << psid.asUint16()
|
||||
<< " for a specified PSID length "
|
||||
<< psid_len.asUnsigned());
|
||||
}
|
||||
|
||||
buf.resize(buf.size() + 3);
|
||||
buf.at(buf.size() - 3) = psid_len.asUint8();
|
||||
isc::util::writeUint16(static_cast<uint16_t>
|
||||
(psid.asUint16() << (sizeof(uint16_t) * 8 - psid_len.asUint8())),
|
||||
&buf[buf.size() - 2], 2);
|
||||
}
|
||||
|
||||
|
||||
std::string
|
||||
OptionDataTypeUtil::readString(const std::vector<uint8_t>& buf) {
|
||||
std::string value;
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2012-2016 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
|
||||
@ -13,6 +13,7 @@
|
||||
#include <util/io_utilities.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <utility>
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
@ -53,12 +54,33 @@ enum OptionDataType {
|
||||
OPT_ANY_ADDRESS_TYPE,
|
||||
OPT_IPV4_ADDRESS_TYPE,
|
||||
OPT_IPV6_ADDRESS_TYPE,
|
||||
OPT_IPV6_PREFIX_TYPE,
|
||||
OPT_PSID_TYPE,
|
||||
OPT_STRING_TYPE,
|
||||
OPT_FQDN_TYPE,
|
||||
OPT_RECORD_TYPE,
|
||||
OPT_UNKNOWN_TYPE
|
||||
};
|
||||
|
||||
/// @brief Parameters being used to make up an option definition.
|
||||
struct OptionDefParams {
|
||||
const char* name; // option name
|
||||
uint16_t code; // option code
|
||||
OptionDataType type; // data type
|
||||
bool array; // is array
|
||||
const OptionDataType* records; // record fields
|
||||
size_t records_size; // number of fields in a record
|
||||
const char* encapsulates; // option space encapsulated by the
|
||||
// particular option.
|
||||
};
|
||||
|
||||
/// @brief Encapsulation of option definition parameters and the structure size.
|
||||
struct OptionDefParamsEncapsulation {
|
||||
const struct OptionDefParams* optionDefParams; // parameters structure
|
||||
const int size; // structure size
|
||||
const char* space; // option space
|
||||
};
|
||||
|
||||
/// @brief Trait class for data types supported in DHCP option definitions.
|
||||
///
|
||||
/// This is useful to check whether the type specified as template parameter
|
||||
@ -173,6 +195,123 @@ struct OptionDataTypeTraits<std::string> {
|
||||
static const OptionDataType type = OPT_STRING_TYPE;
|
||||
};
|
||||
|
||||
/// @brief Encapsulates PSID length.
|
||||
class PSIDLen {
|
||||
public:
|
||||
|
||||
/// @brief Default constructor.
|
||||
PSIDLen() : psid_len_(0) { }
|
||||
|
||||
/// @brief Constructor.
|
||||
///
|
||||
/// It checks that the specified value is not greater than
|
||||
/// 16, which is a maximum value for the PSID length.
|
||||
///
|
||||
/// @param psid_len PSID length.
|
||||
/// @throw isc::OutOfRange If specified PSID length is greater than 16.
|
||||
explicit PSIDLen(const uint8_t psid_len)
|
||||
: psid_len_(psid_len) {
|
||||
if (psid_len_ > sizeof(uint16_t) * 8) {
|
||||
isc_throw(isc::OutOfRange, "invalid value "
|
||||
<< asUnsigned() << " of PSID length");
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Returns PSID length as uint8_t value.
|
||||
uint8_t asUint8() const {
|
||||
return (psid_len_);
|
||||
}
|
||||
|
||||
/// @brief Returns PSID length as unsigned int.
|
||||
///
|
||||
/// This is useful to convert the value to a numeric type which
|
||||
/// can be logged directly. Note that the uint8_t value has to
|
||||
/// be cast to an integer value to be logged as a number. This
|
||||
/// is because the uint8_t is often implemented as char, in which
|
||||
/// case directly loggingan uint8_t value prints a character rather
|
||||
/// than a number.
|
||||
unsigned int asUnsigned() const {
|
||||
return (static_cast<unsigned>(psid_len_));
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// @brief PSID length.
|
||||
uint8_t psid_len_;
|
||||
};
|
||||
|
||||
/// @brief Encapsulates PSID value.
|
||||
class PSID {
|
||||
public:
|
||||
|
||||
/// @brief Default constructor.
|
||||
PSID() : psid_(0) { }
|
||||
|
||||
/// @brief Constructor.
|
||||
///
|
||||
/// This constructor doesn't perform any checks on the input data.
|
||||
///
|
||||
/// @param psid PSID value.
|
||||
explicit PSID(const uint16_t psid)
|
||||
: psid_(psid) {
|
||||
}
|
||||
|
||||
/// @brief Returns PSID value as a number.
|
||||
uint16_t asUint16() const {
|
||||
return (psid_);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// @brief PSID value.
|
||||
uint16_t psid_;
|
||||
|
||||
};
|
||||
|
||||
/// @brief Defines a pair of PSID length / value.
|
||||
typedef std::pair<PSIDLen, PSID> PSIDTuple;
|
||||
|
||||
/// @brief Encapsulates prefix length.
|
||||
class PrefixLen {
|
||||
public:
|
||||
|
||||
/// @brief Default constructor.
|
||||
PrefixLen() : prefix_len_(0) { }
|
||||
|
||||
/// @brief Constructor.
|
||||
///
|
||||
/// This constructor checks if the specified prefix length is
|
||||
/// in the range of 0 to 128.
|
||||
///
|
||||
/// @param prefix_len Prefix length value.
|
||||
/// @throw isc::OutOfRange If specified prefix length is greater than 128.
|
||||
explicit PrefixLen(const uint8_t prefix_len)
|
||||
: prefix_len_(prefix_len) {
|
||||
}
|
||||
|
||||
/// @brief Returns prefix length as uint8_t value.
|
||||
uint8_t asUint8() const {
|
||||
return (prefix_len_);
|
||||
}
|
||||
|
||||
/// @brief Returns prefix length as unsigned int.
|
||||
///
|
||||
/// This is useful to convert the value to a numeric type which
|
||||
/// can be logged directly. See @ref PSIDLen::asUnsigned for the
|
||||
/// use cases of this accessor.
|
||||
unsigned int asUnsigned() const {
|
||||
return (static_cast<unsigned>(prefix_len_));
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// @brief Prefix length.
|
||||
uint8_t prefix_len_;
|
||||
};
|
||||
|
||||
/// @brief Defines a pair of prefix length / value.
|
||||
typedef std::pair<PrefixLen, asiolink::IOAddress> PrefixTuple;
|
||||
|
||||
/// @brief Utility class for option data types.
|
||||
///
|
||||
/// This class provides a set of utility functions to operate on
|
||||
@ -221,7 +360,7 @@ public:
|
||||
///
|
||||
/// @param buf input buffer.
|
||||
/// @param family address family: AF_INET or AF_INET6.
|
||||
///
|
||||
///
|
||||
/// @throw isc::dhcp::BadDataTypeCast when the data being read
|
||||
/// is truncated.
|
||||
/// @return address being read.
|
||||
@ -378,6 +517,59 @@ public:
|
||||
/// @throw isc::dhcp::BadDataTypeCast if provided name is malformed.
|
||||
static unsigned int getLabelCount(const std::string& text_name);
|
||||
|
||||
/// @brief Read prefix from a buffer.
|
||||
///
|
||||
/// This method reads prefix length and a prefix value from a buffer.
|
||||
/// The prefix value has variable length and this length is determined
|
||||
/// from the first byte of the buffer. If the length is not divisible
|
||||
/// by 8, the prefix is padded with zeros to the next byte boundary.
|
||||
///
|
||||
/// @param buf input buffer holding a prefix length / prefix tuple.
|
||||
///
|
||||
/// @return Prefix length and value.
|
||||
static PrefixTuple readPrefix(const std::vector<uint8_t>& buf);
|
||||
|
||||
/// @brief Append prefix into a buffer.
|
||||
///
|
||||
/// This method writes prefix length (1 byte) followed by a variable
|
||||
/// length prefix.
|
||||
///
|
||||
/// @param prefix_len Prefix length in bits (0 to 128).
|
||||
/// @param prefix Prefix value.
|
||||
/// @param [out] Output buffer.
|
||||
static void writePrefix(const PrefixLen& prefix_len,
|
||||
const asiolink::IOAddress& prefix,
|
||||
std::vector<uint8_t>& buf);
|
||||
|
||||
/// @brief Read PSID length / value tuple from a buffer.
|
||||
///
|
||||
/// This method reads three bytes from a buffer. The first byte
|
||||
/// holds a PSID length value. The remaining two bytes contain a
|
||||
/// zero padded PSID value.
|
||||
///
|
||||
/// @return PSID length / value tuple.
|
||||
/// @throw isc::dhcp::BadDataTypeCast if PSID length or value held
|
||||
/// in the buffer is incorrect or the buffer is truncated.
|
||||
static PSIDTuple readPsid(const std::vector<uint8_t>& buf);
|
||||
|
||||
/// @brief Append PSID length/value into a buffer.
|
||||
///
|
||||
/// This method appends 1 byte of PSID length and 2 bytes of PSID
|
||||
/// value into a buffer. The PSID value contains a PSID length
|
||||
/// number of significant bits, followed by 16 - PSID length
|
||||
/// zero bits.
|
||||
///
|
||||
/// @param psid_len PSID length in the range of 0 to 16 holding the
|
||||
/// number of significant bits within the PSID value.
|
||||
/// @param psid PSID value, where the lowest value is 0, and the
|
||||
/// highest value is 2^(PSID length).
|
||||
/// @param [out] buf output buffer.
|
||||
///
|
||||
/// @throw isc::dhcp::BadDataTypeCast if specified psid_len or
|
||||
/// psid value is incorrect.
|
||||
static void writePsid(const PSIDLen& psid_len, const PSID& psid,
|
||||
std::vector<uint8_t>& buf);
|
||||
|
||||
/// @brief Read string value from a buffer.
|
||||
///
|
||||
/// @param buf input buffer.
|
||||
|
@ -20,7 +20,6 @@
|
||||
#include <dhcp/option_int.h>
|
||||
#include <dhcp/option_int_array.h>
|
||||
#include <dhcp/option_opaque_data_tuples.h>
|
||||
#include <dhcp/option_space.h>
|
||||
#include <dhcp/option_string.h>
|
||||
#include <dhcp/option_vendor.h>
|
||||
#include <dhcp/option_vendor_class.h>
|
||||
@ -28,6 +27,7 @@
|
||||
#include <util/strutil.h>
|
||||
#include <boost/algorithm/string/classification.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/dynamic_bitset.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace isc::util;
|
||||
@ -565,6 +565,87 @@ OptionDefinition::writeToBuffer(const std::string& value,
|
||||
OptionDataTypeUtil::writeAddress(address, buf);
|
||||
return;
|
||||
}
|
||||
case OPT_IPV6_PREFIX_TYPE:
|
||||
{
|
||||
std::string txt = value;
|
||||
|
||||
// first let's remove any whitespaces
|
||||
boost::erase_all(txt, " "); // space
|
||||
boost::erase_all(txt, "\t"); // tabulation
|
||||
|
||||
// Is this prefix/len notation?
|
||||
size_t pos = txt.find("/");
|
||||
|
||||
if (pos == string::npos) {
|
||||
isc_throw(BadDataTypeCast, "provided address/prefix "
|
||||
<< value
|
||||
<< " is not valid.");
|
||||
}
|
||||
|
||||
std::string txt_address = txt.substr(0, pos);
|
||||
isc::asiolink::IOAddress address = isc::asiolink::IOAddress(txt_address);
|
||||
if (!address.isV6()) {
|
||||
isc_throw(BadDataTypeCast, "provided address "
|
||||
<< txt_address
|
||||
<< " is not a valid IPv4 or IPv6 address.");
|
||||
}
|
||||
|
||||
std::string txt_prefix = txt.substr(pos + 1);
|
||||
uint8_t len = 0;
|
||||
try {
|
||||
// start with the first character after /
|
||||
len = lexicalCastWithRangeCheck<uint8_t>(txt_prefix);
|
||||
} catch (...) {
|
||||
isc_throw(BadDataTypeCast, "provided prefix "
|
||||
<< txt_prefix
|
||||
<< " is not valid.");
|
||||
}
|
||||
|
||||
|
||||
// Write a prefix.
|
||||
OptionDataTypeUtil::writePrefix(PrefixLen(len), address, buf);
|
||||
|
||||
return;
|
||||
}
|
||||
case OPT_PSID_TYPE:
|
||||
{
|
||||
std::string txt = value;
|
||||
|
||||
// first let's remove any whitespaces
|
||||
boost::erase_all(txt, " "); // space
|
||||
boost::erase_all(txt, "\t"); // tabulation
|
||||
|
||||
// Is this prefix/len notation?
|
||||
size_t pos = txt.find("/");
|
||||
|
||||
if (pos == string::npos) {
|
||||
isc_throw(BadDataTypeCast, "provided PSID value "
|
||||
<< value << " is not valid");
|
||||
}
|
||||
|
||||
const std::string txt_psid = txt.substr(0, pos);
|
||||
const std::string txt_psid_len = txt.substr(pos + 1);
|
||||
|
||||
uint16_t psid = 0;
|
||||
uint8_t psid_len = 0;
|
||||
|
||||
try {
|
||||
psid = lexicalCastWithRangeCheck<uint16_t>(txt_psid);
|
||||
} catch (...) {
|
||||
isc_throw(BadDataTypeCast, "provided PSID "
|
||||
<< txt_psid << " is not valid");
|
||||
}
|
||||
|
||||
try {
|
||||
psid_len = lexicalCastWithRangeCheck<uint8_t>(txt_psid_len);
|
||||
} catch (...) {
|
||||
isc_throw(BadDataTypeCast, "provided PSID length "
|
||||
<< txt_psid_len << " is not valid");
|
||||
}
|
||||
|
||||
OptionDataTypeUtil::writePsid(PSIDLen(psid_len), PSID(psid), buf);
|
||||
return;
|
||||
}
|
||||
case OPT_STRING_TYPE:
|
||||
OptionDataTypeUtil::writeString(value, buf);
|
||||
return;
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <boost/multi_index_container.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
@ -117,7 +118,9 @@ class OptionIntArray;
|
||||
/// - "uint16"
|
||||
/// - "uint32"
|
||||
/// - "ipv4-address" (IPv4 Address)
|
||||
/// - "ipv6-address" (IPV6 Address)
|
||||
/// - "ipv6-address" (IPv6 Address)
|
||||
/// - "ipv6-prefix" (IPv6 variable length prefix)
|
||||
/// - "psid" (PSID length / value)
|
||||
/// - "string"
|
||||
/// - "fqdn" (fully qualified name)
|
||||
/// - "record" (set of data fields of different types)
|
||||
@ -717,6 +720,9 @@ typedef boost::multi_index_container<
|
||||
/// Pointer to an option definition container.
|
||||
typedef boost::shared_ptr<OptionDefContainer> OptionDefContainerPtr;
|
||||
|
||||
/// Container that holds option definitions for various option spaces.
|
||||
typedef std::map<std::string, OptionDefContainerPtr> OptionDefContainers;
|
||||
|
||||
/// Container that holds various vendor option containers
|
||||
typedef std::map<uint32_t, OptionDefContainerPtr> VendorOptionDefContainers;
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <dhcp/libdhcp++.h>
|
||||
#include <dhcp/option.h>
|
||||
#include <dhcp/option_data_types.h>
|
||||
#include <dhcp/option_space.h>
|
||||
#include <util/io_utilities.h>
|
||||
|
||||
#include <stdint.h>
|
||||
@ -66,7 +67,7 @@ public:
|
||||
if (!OptionDataTypeTraits<T>::integer_type) {
|
||||
isc_throw(dhcp::InvalidDataType, "non-integer type");
|
||||
}
|
||||
setEncapsulatedSpace(u == Option::V4 ? "dhcp4" : "dhcp6");
|
||||
setEncapsulatedSpace(u == Option::V4 ? DHCP4_OPTION_SPACE : DHCP6_OPTION_SPACE);
|
||||
}
|
||||
|
||||
/// @brief Constructor.
|
||||
@ -90,7 +91,7 @@ public:
|
||||
if (!OptionDataTypeTraits<T>::integer_type) {
|
||||
isc_throw(dhcp::InvalidDataType, "non-integer type");
|
||||
}
|
||||
setEncapsulatedSpace(u == Option::V4 ? "dhcp4" : "dhcp6");
|
||||
setEncapsulatedSpace(u == Option::V4 ? DHCP4_OPTION_SPACE : DHCP6_OPTION_SPACE);
|
||||
unpack(begin, end);
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2012-2016 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
|
||||
@ -13,8 +13,13 @@
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
|
||||
#define DHCP4_OPTION_SPACE "dhcp4"
|
||||
#define DHCP6_OPTION_SPACE "dhcp6"
|
||||
#define DHCP4_OPTION_SPACE "dhcp4"
|
||||
#define DHCP6_OPTION_SPACE "dhcp6"
|
||||
#define MAPE_V6_OPTION_SPACE "s46-cont-mape-options"
|
||||
#define MAPT_V6_OPTION_SPACE "s46-cont-mapt-options"
|
||||
#define LW_V6_OPTION_SPACE "s46-cont-lw-options"
|
||||
#define V4V6_RULE_OPTION_SPACE "s46-rule-options"
|
||||
#define V4V6_BIND_OPTION_SPACE "s46-v4v6bind-options"
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
@ -174,7 +179,7 @@ public:
|
||||
void setVendorSpace(const uint32_t enterprise_number);
|
||||
|
||||
private:
|
||||
|
||||
|
||||
uint32_t enterprise_number_; ///< IANA assigned enterprise number.
|
||||
};
|
||||
|
||||
|
@ -203,7 +203,7 @@ Pkt4::unpack() {
|
||||
// a vector as an input.
|
||||
buffer_in.readVector(opts_buffer, opts_len);
|
||||
|
||||
size_t offset = LibDHCP::unpackOptions4(opts_buffer, "dhcp4", options_);
|
||||
size_t offset = LibDHCP::unpackOptions4(opts_buffer, DHCP4_OPTION_SPACE, options_);
|
||||
|
||||
// If offset is not equal to the size and there is no DHO_END,
|
||||
// then something is wrong here. We either parsed past input
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <dhcp/dhcp6.h>
|
||||
#include <dhcp/libdhcp++.h>
|
||||
#include <dhcp/option.h>
|
||||
#include <dhcp/option_space.h>
|
||||
#include <dhcp/option_vendor_class.h>
|
||||
#include <dhcp/option_vendor.h>
|
||||
#include <dhcp/pkt6.h>
|
||||
@ -403,7 +404,7 @@ Pkt6::unpackMsg(OptionBuffer::const_iterator begin,
|
||||
|
||||
// If custom option parsing function has been set, use this function
|
||||
// to parse options. Otherwise, use standard function from libdhcp.
|
||||
size_t offset = LibDHCP::unpackOptions6(opt_buffer, "dhcp6", options_);
|
||||
size_t offset = LibDHCP::unpackOptions6(opt_buffer, DHCP6_OPTION_SPACE, options_);
|
||||
|
||||
// If offset is not equal to the size, then something is wrong here. We
|
||||
// either parsed past input buffer (bug in our code) or we haven't parsed
|
||||
@ -453,7 +454,7 @@ Pkt6::unpackRelayMsg() {
|
||||
|
||||
// If custom option parsing function has been set, use this function
|
||||
// to parse options. Otherwise, use standard function from libdhcp.
|
||||
LibDHCP::unpackOptions6(opt_buffer, "dhcp6", relay.options_,
|
||||
LibDHCP::unpackOptions6(opt_buffer, DHCP6_OPTION_SPACE, relay.options_,
|
||||
&relay_msg_offset, &relay_msg_len);
|
||||
|
||||
/// @todo: check that each option appears at most once
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <dhcp/option_data_types.h>
|
||||
#include <dhcp/dhcp4.h>
|
||||
#include <dhcp/dhcp6.h>
|
||||
#include <dhcp/option_space.h>
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
@ -36,18 +37,6 @@ namespace {
|
||||
#define NO_RECORD_DEF 0, 0
|
||||
#endif
|
||||
|
||||
/// @brief Parameters being used to make up an option definition.
|
||||
struct OptionDefParams {
|
||||
const char* name; // option name
|
||||
uint16_t code; // option code
|
||||
OptionDataType type; // data type
|
||||
bool array; // is array
|
||||
const OptionDataType* records; // record fields
|
||||
size_t records_size; // number of fields in a record
|
||||
const char* encapsulates; // option space encapsulated by
|
||||
// the particular option.
|
||||
};
|
||||
|
||||
// fqdn option record fields.
|
||||
//
|
||||
// Note that the flags field indicates the type of domain
|
||||
@ -72,7 +61,7 @@ RECORD_DECL(CLIENT_NDI_RECORDS, OPT_UINT8_TYPE, OPT_UINT8_TYPE, OPT_UINT8_TYPE);
|
||||
RECORD_DECL(UUID_GUID_RECORDS, OPT_UINT8_TYPE, OPT_BINARY_TYPE);
|
||||
|
||||
/// @brief Definitions of standard DHCPv4 options.
|
||||
const OptionDefParams OPTION_DEF_PARAMS4[] = {
|
||||
const OptionDefParams STANDARD_V4_OPTION_DEFINITIONS[] = {
|
||||
{ "subnet-mask", DHO_SUBNET_MASK, OPT_IPV4_ADDRESS_TYPE, false, NO_RECORD_DEF, "" },
|
||||
{ "time-offset", DHO_TIME_OFFSET, OPT_INT32_TYPE, false, NO_RECORD_DEF, "" },
|
||||
{ "routers", DHO_ROUTERS, OPT_IPV4_ADDRESS_TYPE, true, NO_RECORD_DEF, "" },
|
||||
@ -230,9 +219,8 @@ const OptionDefParams OPTION_DEF_PARAMS4[] = {
|
||||
};
|
||||
|
||||
/// Number of option definitions defined.
|
||||
const int OPTION_DEF_PARAMS_SIZE4 =
|
||||
sizeof(OPTION_DEF_PARAMS4) / sizeof(OPTION_DEF_PARAMS4[0]);
|
||||
|
||||
const int STANDARD_V4_OPTION_DEFINITIONS_SIZE =
|
||||
sizeof(STANDARD_V4_OPTION_DEFINITIONS) / sizeof(STANDARD_V4_OPTION_DEFINITIONS[0]);
|
||||
|
||||
/// Start Definition of DHCPv6 options
|
||||
|
||||
@ -257,6 +245,13 @@ RECORD_DECL(LQ_QUERY_RECORDS, OPT_UINT8_TYPE, OPT_IPV6_ADDRESS_TYPE);
|
||||
RECORD_DECL(LQ_RELAY_DATA_RECORDS, OPT_IPV6_ADDRESS_TYPE, OPT_BINARY_TYPE);
|
||||
// remote-id
|
||||
RECORD_DECL(REMOTE_ID_RECORDS, OPT_UINT32_TYPE, OPT_BINARY_TYPE);
|
||||
// s46-rule
|
||||
RECORD_DECL(S46_RULE, OPT_UINT8_TYPE, OPT_UINT8_TYPE, OPT_UINT8_TYPE,
|
||||
OPT_IPV4_ADDRESS_TYPE, OPT_IPV6_PREFIX_TYPE);
|
||||
// s46-v4v6bind
|
||||
RECORD_DECL(S46_V4V6BIND, OPT_IPV4_ADDRESS_TYPE, OPT_IPV6_PREFIX_TYPE);
|
||||
// s46-portparams
|
||||
RECORD_DECL(S46_PORTPARAMS, OPT_UINT8_TYPE, OPT_PSID_TYPE);
|
||||
// status-code
|
||||
RECORD_DECL(STATUS_CODE_RECORDS, OPT_UINT16_TYPE, OPT_STRING_TYPE);
|
||||
// vendor-class
|
||||
@ -280,7 +275,7 @@ RECORD_DECL(CLIENT_NII_RECORDS, OPT_UINT8_TYPE, OPT_UINT8_TYPE, OPT_UINT8_TYPE);
|
||||
/// This however does not work on Solaris (GCC) which issues a
|
||||
/// warning about lack of initializers for some struct members
|
||||
/// causing build to fail.
|
||||
const OptionDefParams OPTION_DEF_PARAMS6[] = {
|
||||
const OptionDefParams STANDARD_V6_OPTION_DEFINITIONS[] = {
|
||||
{ "clientid", D6O_CLIENTID, OPT_BINARY_TYPE, false, NO_RECORD_DEF, "" },
|
||||
{ "serverid", D6O_SERVERID, OPT_BINARY_TYPE, false, NO_RECORD_DEF, "" },
|
||||
{ "ia-na", D6O_IA_NA, OPT_RECORD_TYPE, false, RECORD_DEF(IA_NA_RECORDS), "" },
|
||||
@ -366,6 +361,7 @@ const OptionDefParams OPTION_DEF_PARAMS6[] = {
|
||||
{ "erp-local-domain-name", D6O_ERP_LOCAL_DOMAIN_NAME, OPT_FQDN_TYPE, false,
|
||||
NO_RECORD_DEF, "" },
|
||||
{ "rsoo", D6O_RSOO, OPT_EMPTY_TYPE, false, NO_RECORD_DEF, "rsoo-opts" },
|
||||
{ "pd-exclude", D6O_PD_EXCLUDE, OPT_BINARY_TYPE, false, NO_RECORD_DEF, "" },
|
||||
{ "client-linklayer-addr", D6O_CLIENT_LINKLAYER_ADDR, OPT_BINARY_TYPE, false,
|
||||
NO_RECORD_DEF, "" },
|
||||
{ "dhcpv4-message", D6O_DHCPV4_MSG, OPT_BINARY_TYPE, false, NO_RECORD_DEF, "" },
|
||||
@ -378,7 +374,14 @@ const OptionDefParams OPTION_DEF_PARAMS6[] = {
|
||||
{ "signature", D6O_SIGNATURE, OPT_RECORD_TYPE, false,
|
||||
RECORD_DEF(SIGNATURE_RECORDS), "" },
|
||||
{ "timestamp", D6O_TIMESTAMP, OPT_BINARY_TYPE, false,
|
||||
NO_RECORD_DEF, "" }
|
||||
NO_RECORD_DEF, "" },
|
||||
{ "aftr-name", D6O_AFTR_NAME, OPT_FQDN_TYPE, false, NO_RECORD_DEF, "" },
|
||||
{ "s46-cont-mape", D6O_S46_CONT_MAPE, OPT_EMPTY_TYPE, false, NO_RECORD_DEF,
|
||||
MAPE_V6_OPTION_SPACE },
|
||||
{ "s46-cont-mapt", D6O_S46_CONT_MAPT, OPT_EMPTY_TYPE, false, NO_RECORD_DEF,
|
||||
MAPT_V6_OPTION_SPACE },
|
||||
{ "s46-cont-lw", D6O_S46_CONT_LW, OPT_EMPTY_TYPE, false, NO_RECORD_DEF,
|
||||
LW_V6_OPTION_SPACE }
|
||||
|
||||
// @todo There is still a bunch of options for which we have to provide
|
||||
// definitions but we don't do it because they are not really
|
||||
@ -386,8 +389,17 @@ const OptionDefParams OPTION_DEF_PARAMS6[] = {
|
||||
};
|
||||
|
||||
/// Number of option definitions defined.
|
||||
const int OPTION_DEF_PARAMS_SIZE6 =
|
||||
sizeof(OPTION_DEF_PARAMS6) / sizeof(OPTION_DEF_PARAMS6[0]);
|
||||
const int STANDARD_V6_OPTION_DEFINITIONS_SIZE =
|
||||
sizeof(STANDARD_V6_OPTION_DEFINITIONS) /
|
||||
sizeof(STANDARD_V6_OPTION_DEFINITIONS[0]);
|
||||
|
||||
// Option definitions that belong to two or more option spaces are defined here.
|
||||
const OptionDefParams OPTION_DEF_PARAMS_S46_BR = { "s46-br", D6O_S46_BR,
|
||||
OPT_IPV6_ADDRESS_TYPE, false, NO_RECORD_DEF, "" };
|
||||
const OptionDefParams OPTION_DEF_PARAMS_S46_RULE = { "s46-rule", D6O_S46_RULE,
|
||||
OPT_RECORD_TYPE, false, RECORD_DEF(S46_RULE), V4V6_RULE_OPTION_SPACE };
|
||||
const OptionDefParams OPTION_DEF_PARAMS_S46_PORTPARAMS = { "s46-portparams",
|
||||
D6O_S46_PORTPARAMS, OPT_RECORD_TYPE, false, RECORD_DEF(S46_PORTPARAMS), "" };
|
||||
|
||||
/// @brief Definitions of vendor-specific DHCPv6 options, defined by ISC.
|
||||
/// 4o6-* options are used for inter-process communication. For details, see
|
||||
@ -395,12 +407,65 @@ const int OPTION_DEF_PARAMS_SIZE6 =
|
||||
///
|
||||
/// @todo: As those options are defined by ISC, they do not belong in std_option_defs.h.
|
||||
/// We need to move them to a separate file, e.g. isc_option_defs.h
|
||||
const OptionDefParams ISC_V6_DEFS[] = {
|
||||
{ "4o6-interface", ISC_V6_4O6_INTERFACE, OPT_STRING_TYPE, false, NO_RECORD_DEF, "" },
|
||||
{ "4o6-source-address", ISC_V6_4O6_SRC_ADDRESS, OPT_IPV6_ADDRESS_TYPE, false, NO_RECORD_DEF, "" }
|
||||
const OptionDefParams ISC_V6_OPTION_DEFINITIONS[] = {
|
||||
{ "4o6-interface", ISC_V6_4O6_INTERFACE, OPT_STRING_TYPE, false,
|
||||
NO_RECORD_DEF, "" },
|
||||
{ "4o6-source-address", ISC_V6_4O6_SRC_ADDRESS, OPT_IPV6_ADDRESS_TYPE,
|
||||
false, NO_RECORD_DEF, "" }
|
||||
};
|
||||
|
||||
const int ISC_V6_DEFS_SIZE = sizeof(ISC_V6_DEFS) / sizeof(OptionDefParams);
|
||||
const int ISC_V6_OPTION_DEFINITIONS_SIZE =
|
||||
sizeof(ISC_V6_OPTION_DEFINITIONS) /
|
||||
sizeof(ISC_V6_OPTION_DEFINITIONS[0]);
|
||||
|
||||
/// @brief MAPE option definitions
|
||||
const OptionDefParams MAPE_V6_OPTION_DEFINITIONS[] = {
|
||||
OPTION_DEF_PARAMS_S46_BR,
|
||||
OPTION_DEF_PARAMS_S46_RULE
|
||||
};
|
||||
|
||||
const int MAPE_V6_OPTION_DEFINITIONS_SIZE =
|
||||
sizeof(MAPE_V6_OPTION_DEFINITIONS) /
|
||||
sizeof(MAPE_V6_OPTION_DEFINITIONS[0]);
|
||||
|
||||
/// @brief MAPT option definitions
|
||||
const OptionDefParams MAPT_V6_OPTION_DEFINITIONS[] = {
|
||||
OPTION_DEF_PARAMS_S46_RULE,
|
||||
{ "s46-dmr", D6O_S46_DMR, OPT_IPV6_PREFIX_TYPE, false, NO_RECORD_DEF, "" }
|
||||
};
|
||||
|
||||
const int MAPT_V6_OPTION_DEFINITIONS_SIZE =
|
||||
sizeof(MAPT_V6_OPTION_DEFINITIONS) /
|
||||
sizeof(MAPT_V6_OPTION_DEFINITIONS[0]);
|
||||
|
||||
/// @brief LW option definitions
|
||||
const OptionDefParams LW_V6_OPTION_DEFINITIONS[] = {
|
||||
OPTION_DEF_PARAMS_S46_BR,
|
||||
{ "s46-v4v6bind", D6O_S46_V4V6BIND, OPT_RECORD_TYPE, false,
|
||||
RECORD_DEF(S46_V4V6BIND), V4V6_BIND_OPTION_SPACE }
|
||||
};
|
||||
|
||||
const int LW_V6_OPTION_DEFINITIONS_SIZE =
|
||||
sizeof(LW_V6_OPTION_DEFINITIONS) /
|
||||
sizeof(LW_V6_OPTION_DEFINITIONS[0]);
|
||||
|
||||
/// @brief Rule option definitions
|
||||
const OptionDefParams V4V6_RULE_OPTION_DEFINITIONS[] = {
|
||||
OPTION_DEF_PARAMS_S46_PORTPARAMS
|
||||
};
|
||||
|
||||
const int V4V6_RULE_OPTION_DEFINITIONS_SIZE =
|
||||
sizeof(V4V6_RULE_OPTION_DEFINITIONS) /
|
||||
sizeof(V4V6_RULE_OPTION_DEFINITIONS[0]);
|
||||
|
||||
/// @brief Bind option definitions
|
||||
const OptionDefParams V4V6_BIND_OPTION_DEFINITIONS[] = {
|
||||
OPTION_DEF_PARAMS_S46_PORTPARAMS
|
||||
};
|
||||
|
||||
const int V4V6_BIND_OPTION_DEFINITIONS_SIZE =
|
||||
sizeof(V4V6_BIND_OPTION_DEFINITIONS) /
|
||||
sizeof(V4V6_BIND_OPTION_DEFINITIONS[0]);
|
||||
|
||||
} // unnamed namespace
|
||||
|
||||
|
@ -54,6 +54,7 @@ libdhcp___unittests_SOURCES += option6_client_fqdn_unittest.cc
|
||||
libdhcp___unittests_SOURCES += option6_ia_unittest.cc
|
||||
libdhcp___unittests_SOURCES += option6_iaaddr_unittest.cc
|
||||
libdhcp___unittests_SOURCES += option6_iaprefix_unittest.cc
|
||||
libdhcp___unittests_SOURCES += option6_pdexclude_unittest.cc
|
||||
libdhcp___unittests_SOURCES += option6_status_code_unittest.cc
|
||||
libdhcp___unittests_SOURCES += option_int_unittest.cc
|
||||
libdhcp___unittests_SOURCES += option_int_array_unittest.cc
|
||||
|
@ -101,8 +101,8 @@ public:
|
||||
const std::type_info& expected_type,
|
||||
const std::string& encapsulates = "") {
|
||||
// Use V4 universe.
|
||||
testStdOptionDefs(Option::V4, code, begin, end, expected_type,
|
||||
encapsulates);
|
||||
testStdOptionDefs(Option::V4, DHCP4_OPTION_SPACE, code, begin, end,
|
||||
expected_type, encapsulates);
|
||||
}
|
||||
|
||||
/// @brief Test DHCPv6 option definition.
|
||||
@ -125,8 +125,33 @@ public:
|
||||
const std::type_info& expected_type,
|
||||
const std::string& encapsulates = "") {
|
||||
// Use V6 universe.
|
||||
testStdOptionDefs(Option::V6, code, begin, end, expected_type,
|
||||
encapsulates);
|
||||
testStdOptionDefs(Option::V6, DHCP6_OPTION_SPACE, code, begin,
|
||||
end, expected_type, encapsulates);
|
||||
}
|
||||
|
||||
/// @brief Test DHCPv6 option definition in a given option space.
|
||||
///
|
||||
/// This function tests if option definition for an option from a
|
||||
/// given option space has been initialized correctly.
|
||||
///
|
||||
/// @param option_space option space.
|
||||
/// @param code option code.
|
||||
/// @param begin iterator pointing at beginning of a buffer to
|
||||
/// be used to create option instance.
|
||||
/// @param end iterator pointing at end of a buffer to be
|
||||
/// used to create option instance.
|
||||
/// @param expected_type type of the option created by the
|
||||
/// factory function returned by the option definition.
|
||||
/// @param encapsulates name of the option space being encapsulated
|
||||
/// by the option.
|
||||
static void testOptionDefs6(const std::string& option_space,
|
||||
const uint16_t code,
|
||||
const OptionBufferConstIter begin,
|
||||
const OptionBufferConstIter end,
|
||||
const std::type_info& expected_type,
|
||||
const std::string& encapsulates = "") {
|
||||
testStdOptionDefs(Option::V6, option_space, code, begin,
|
||||
end, expected_type, encapsulates);
|
||||
}
|
||||
|
||||
/// @brief Create a sample DHCPv4 option 43 with suboptions.
|
||||
@ -216,6 +241,7 @@ private:
|
||||
/// This function tests if option definition for standard
|
||||
/// option has been initialized correctly.
|
||||
///
|
||||
/// @param option_space option space.
|
||||
/// @param code option code.
|
||||
/// @param begin iterator pointing at beginning of a buffer to
|
||||
/// be used to create option instance.
|
||||
@ -225,7 +251,8 @@ private:
|
||||
/// factory function returned by the option definition.
|
||||
/// @param encapsulates name of the option space being encapsulated
|
||||
/// by the option.
|
||||
static void testStdOptionDefs(const Option::Universe u,
|
||||
static void testStdOptionDefs(const Option::Universe& u,
|
||||
const std::string& option_space,
|
||||
const uint16_t code,
|
||||
const OptionBufferConstIter begin,
|
||||
const OptionBufferConstIter end,
|
||||
@ -235,7 +262,7 @@ private:
|
||||
// the definition for a particular option code.
|
||||
// We don't have to initialize option definitions here because they
|
||||
// are initialized in the class's constructor.
|
||||
OptionDefContainerPtr options = LibDHCP::getOptionDefs(u);
|
||||
OptionDefContainerPtr options = LibDHCP::getOptionDefs(option_space);
|
||||
// Get the container index #1. This one allows for searching
|
||||
// option definitions using option code.
|
||||
const OptionDefContainerTypeIndex& idx = options->get<1>();
|
||||
@ -680,7 +707,7 @@ TEST_F(LibDhcpTest, packOptions4) {
|
||||
// Get the option definition for RAI option. This option is represented
|
||||
// by OptionCustom which requires a definition to be passed to
|
||||
// the constructor.
|
||||
OptionDefinitionPtr rai_def = LibDHCP::getOptionDef(Option::V4,
|
||||
OptionDefinitionPtr rai_def = LibDHCP::getOptionDef(DHCP4_OPTION_SPACE,
|
||||
DHO_DHCP_AGENT_OPTIONS);
|
||||
ASSERT_TRUE(rai_def);
|
||||
// Create RAI option.
|
||||
@ -1012,69 +1039,6 @@ TEST_F(LibDhcpTest, unpackSubOptions4) {
|
||||
EXPECT_EQ(0x0, option_bar->getValue());
|
||||
}
|
||||
|
||||
TEST_F(LibDhcpTest, isStandardOption4) {
|
||||
// Get all option codes that are not occupied by standard options.
|
||||
const uint16_t unassigned_codes[] = { 84, 96, 102, 103, 104, 105, 106, 107, 108,
|
||||
109, 110, 111, 115, 126, 127, 147, 148, 149,
|
||||
178, 179, 180, 181, 182, 183, 184, 185, 186,
|
||||
187, 188, 189, 190, 191, 192, 193, 194, 195,
|
||||
196, 197, 198, 199, 200, 201, 202, 203, 204,
|
||||
205, 206, 207, 214, 215, 216, 217, 218, 219,
|
||||
222, 223, 224, 225, 226, 227, 228, 229, 230,
|
||||
231, 232, 233, 234, 235, 236, 237, 238, 239,
|
||||
240, 241, 242, 243, 244, 245, 246, 247, 248,
|
||||
249, 250, 251, 252, 253, 254 };
|
||||
const size_t unassigned_num = sizeof(unassigned_codes) / sizeof(unassigned_codes[0]);
|
||||
|
||||
// Try all possible option codes.
|
||||
for (size_t i = 0; i < 256; ++i) {
|
||||
// Some ranges of option codes are unassigned and thus the isStandardOption
|
||||
// should return false for them.
|
||||
bool check_unassigned = false;
|
||||
// Check the array of unassigned options to find out whether option code
|
||||
// is assigned to standard option or unassigned.
|
||||
for (size_t j = 0; j < unassigned_num; ++j) {
|
||||
// If option code is found within the array of unassigned options
|
||||
// we the isStandardOption function should return false.
|
||||
if (unassigned_codes[j] == i) {
|
||||
check_unassigned = true;
|
||||
EXPECT_FALSE(LibDHCP::isStandardOption(Option::V4,
|
||||
unassigned_codes[j]))
|
||||
<< "Test failed for option code " << unassigned_codes[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If the option code belongs to the standard option then the
|
||||
// isStandardOption should return true.
|
||||
if (!check_unassigned) {
|
||||
EXPECT_TRUE(LibDHCP::isStandardOption(Option::V4, i))
|
||||
<< "Test failed for the option code " << i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(LibDhcpTest, isStandardOption6) {
|
||||
// All option codes in the range from 0 to 78 (except 10 and 35)
|
||||
// identify the standard options.
|
||||
for (uint16_t code = 0; code < 79; ++code) {
|
||||
if (code != 10 && code != 35) {
|
||||
EXPECT_TRUE(LibDHCP::isStandardOption(Option::V6, code))
|
||||
<< "Test failed for option code " << code;
|
||||
}
|
||||
}
|
||||
|
||||
// Check the option codes 10 and 35. They are unassigned.
|
||||
EXPECT_FALSE(LibDHCP::isStandardOption(Option::V6, 10));
|
||||
EXPECT_FALSE(LibDHCP::isStandardOption(Option::V6, 35));
|
||||
|
||||
// Check a range of option codes above 78. Those are option codes
|
||||
// identifying non-standard options.
|
||||
for (uint16_t code = 79; code < 512; ++code) {
|
||||
EXPECT_FALSE(LibDHCP::isStandardOption(Option::V6, code))
|
||||
<< "Test failed for option code " << code;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(LibDhcpTest, stdOptionDefs4) {
|
||||
|
||||
// Create a buffer that holds dummy option data.
|
||||
@ -1619,18 +1583,45 @@ TEST_F(LibDhcpTest, stdOptionDefs6) {
|
||||
|
||||
LibDhcpTest::testStdOptionDefs6(D6O_TIMESTAMP, begin, begin + 8,
|
||||
typeid(Option));
|
||||
|
||||
// RFC7598 options
|
||||
LibDhcpTest::testOptionDefs6(MAPE_V6_OPTION_SPACE, D6O_S46_RULE, begin, end,
|
||||
typeid(OptionCustom), "s46-rule-options");
|
||||
LibDhcpTest::testOptionDefs6(MAPT_V6_OPTION_SPACE, D6O_S46_RULE, begin, end,
|
||||
typeid(OptionCustom), "s46-rule-options");
|
||||
LibDhcpTest::testOptionDefs6(MAPE_V6_OPTION_SPACE, D6O_S46_BR, begin, end,
|
||||
typeid(OptionCustom));
|
||||
LibDhcpTest::testOptionDefs6(LW_V6_OPTION_SPACE, D6O_S46_BR, begin, end,
|
||||
typeid(OptionCustom));
|
||||
LibDhcpTest::testOptionDefs6(MAPT_V6_OPTION_SPACE, D6O_S46_DMR, begin, end,
|
||||
typeid(OptionCustom));
|
||||
LibDhcpTest::testOptionDefs6(LW_V6_OPTION_SPACE, D6O_S46_V4V6BIND, begin,
|
||||
end, typeid(OptionCustom),
|
||||
"s46-v4v6bind-options");
|
||||
LibDhcpTest::testOptionDefs6(V4V6_RULE_OPTION_SPACE, D6O_S46_PORTPARAMS,
|
||||
begin, end, typeid(OptionCustom), "");
|
||||
LibDhcpTest::testStdOptionDefs6(D6O_S46_CONT_MAPE, begin, end,
|
||||
typeid(OptionCustom),
|
||||
"s46-cont-mape-options");
|
||||
LibDhcpTest::testStdOptionDefs6(D6O_S46_CONT_MAPT, begin, end,
|
||||
typeid(OptionCustom),
|
||||
"s46-cont-mapt-options");
|
||||
LibDhcpTest::testStdOptionDefs6(D6O_S46_CONT_LW, begin, end,
|
||||
typeid(OptionCustom),
|
||||
"s46-cont-lw-options");
|
||||
|
||||
}
|
||||
|
||||
// This test checks if the DHCPv6 option definition can be searched by
|
||||
// an option name.
|
||||
TEST_F(LibDhcpTest, getOptionDefByName6) {
|
||||
// Get all definitions.
|
||||
const OptionDefContainerPtr defs = LibDHCP::getOptionDefs(Option::V6);
|
||||
const OptionDefContainerPtr defs = LibDHCP::getOptionDefs(DHCP6_OPTION_SPACE);
|
||||
// For each definition try to find it using option name.
|
||||
for (OptionDefContainer::const_iterator def = defs->begin();
|
||||
def != defs->end(); ++def) {
|
||||
OptionDefinitionPtr def_by_name =
|
||||
LibDHCP::getOptionDef(Option::V6, (*def)->getName());
|
||||
LibDHCP::getOptionDef(DHCP6_OPTION_SPACE, (*def)->getName());
|
||||
ASSERT_TRUE(def_by_name);
|
||||
ASSERT_TRUE(**def == *def_by_name);
|
||||
}
|
||||
@ -1641,12 +1632,12 @@ TEST_F(LibDhcpTest, getOptionDefByName6) {
|
||||
// an option name.
|
||||
TEST_F(LibDhcpTest, getOptionDefByName4) {
|
||||
// Get all definitions.
|
||||
const OptionDefContainerPtr defs = LibDHCP::getOptionDefs(Option::V4);
|
||||
const OptionDefContainerPtr defs = LibDHCP::getOptionDefs(DHCP4_OPTION_SPACE);
|
||||
// For each definition try to find it using option name.
|
||||
for (OptionDefContainer::const_iterator def = defs->begin();
|
||||
def != defs->end(); ++def) {
|
||||
OptionDefinitionPtr def_by_name =
|
||||
LibDHCP::getOptionDef(Option::V4, (*def)->getName());
|
||||
LibDHCP::getOptionDef(DHCP4_OPTION_SPACE, (*def)->getName());
|
||||
ASSERT_TRUE(def_by_name);
|
||||
ASSERT_TRUE(**def == *def_by_name);
|
||||
}
|
||||
|
175
src/lib/dhcp/tests/option6_pdexclude_unittest.cc
Normal file
175
src/lib/dhcp/tests/option6_pdexclude_unittest.cc
Normal file
@ -0,0 +1,175 @@
|
||||
// Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// Author: Andrei Pavel <andrei.pavel@qualitance.com>
|
||||
//
|
||||
// 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
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <asiolink/io_address.h>
|
||||
#include <exceptions/exceptions.h>
|
||||
#include <dhcp/option6_pdexclude.h>
|
||||
#include <dhcpsrv/pool.h>
|
||||
#include <util/buffer.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace isc;
|
||||
using namespace isc::dhcp;
|
||||
using namespace asiolink;
|
||||
|
||||
namespace {
|
||||
|
||||
// Prefix constants used in unit tests.
|
||||
const IOAddress v4("192.0.2.0");
|
||||
const IOAddress bee0("2001:db8:dead:bee0::");
|
||||
const IOAddress beef("2001:db8:dead:beef::");
|
||||
const IOAddress cafe("2001:db8:dead:cafe::");
|
||||
const IOAddress beef01("2001:db8:dead:beef::01");
|
||||
|
||||
// This test verifies that the constructor sets parameters appropriately.
|
||||
TEST(Option6PDExcludeTest, constructor) {
|
||||
Option6PDExclude option = Option6PDExclude(beef, 56, beef01, 60);
|
||||
|
||||
EXPECT_EQ(beef, option.getDelegatedPrefix());
|
||||
EXPECT_EQ(56, option.getDelegatedPrefixLength());
|
||||
EXPECT_EQ(beef01, option.getExcludedPrefix());
|
||||
EXPECT_EQ(60, option.getExcludedPrefixLength());
|
||||
// Total length is a sum of option header length, excluded prefix
|
||||
// length (always 1 byte) and delegated prefix length - excluded prefix
|
||||
// length rounded to bytes.
|
||||
EXPECT_EQ(Option::OPTION6_HDR_LEN + 1 + 1, option.len());
|
||||
|
||||
// v4 prefix is not accepted.
|
||||
EXPECT_THROW(Option6PDExclude(v4, 56, beef01, 64), BadValue);
|
||||
EXPECT_THROW(Option6PDExclude(beef, 56, v4, 64), BadValue);
|
||||
// Length greater than 128 is not accepted.
|
||||
EXPECT_THROW(Option6PDExclude(beef, 128, beef01, 129), BadValue);
|
||||
// Excluded prefix length must be greater than delegated prefix length.
|
||||
EXPECT_THROW(Option6PDExclude(beef, 56, beef01, 56), BadValue);
|
||||
// Both prefixes shifted by 56 must be equal (see RFC6603, section 4.2).
|
||||
EXPECT_THROW(Option6PDExclude(cafe, 56, beef01, 64), BadValue);
|
||||
}
|
||||
|
||||
// This test verifies that on-wire format of the Prefix Exclude option is
|
||||
// created properly.
|
||||
TEST(Option6PDExcludeTest, pack) {
|
||||
// Expected wire format of the option.
|
||||
const uint8_t expected_data[] = {
|
||||
0x00, 0x43, // option code 67
|
||||
0x00, 0x02, // option length 2
|
||||
0x3F, 0x70 // excluded prefix length 59 + subnet id
|
||||
};
|
||||
std::vector<uint8_t> expected_vec(expected_data,
|
||||
expected_data + sizeof(expected_data));
|
||||
// Generate wire format of the option.
|
||||
util::OutputBuffer buf(128);
|
||||
Option6PDExcludePtr option;
|
||||
ASSERT_NO_THROW(option.reset(new Option6PDExclude(IOAddress("2001:db8:dead:bee0::"),
|
||||
59,
|
||||
IOAddress("2001:db8:dead:beef::"),
|
||||
63)));
|
||||
ASSERT_NO_THROW(option->pack(buf));
|
||||
|
||||
// Check that size matches.
|
||||
ASSERT_EQ(expected_vec.size(), buf.getLength());
|
||||
|
||||
// Check that the generated wire format is correct.
|
||||
const uint8_t* data = static_cast<const uint8_t*>(buf.getData());
|
||||
std::vector<uint8_t> vec(data, data + buf.getLength());
|
||||
ASSERT_TRUE(std::equal(vec.begin(), vec.end(), expected_vec.begin()));
|
||||
}
|
||||
|
||||
// This test verifies parsing option wire format with subnet id of
|
||||
// 1 byte.
|
||||
TEST(Option6PDExcludeTest, unpack1ByteSubnetId) {
|
||||
const uint8_t data[] = {
|
||||
0x00, 0x43, // option code 67
|
||||
0x00, 0x02, // option length 2
|
||||
0x40, 0x78 // excluded prefix length 60 + subnet id
|
||||
};
|
||||
std::vector<uint8_t> vec(data, data + sizeof(data));
|
||||
|
||||
// Parse option.
|
||||
Option6PDExcludePtr option;
|
||||
ASSERT_NO_THROW(
|
||||
option.reset(new Option6PDExclude(IOAddress("2001:db8:dead:bee0::1"),
|
||||
59, vec.begin() + 4, vec.end()))
|
||||
);
|
||||
|
||||
// Make sure that the option has been parsed correctly.
|
||||
EXPECT_EQ("2001:db8:dead:bee0::1", option->getDelegatedPrefix().toText());
|
||||
EXPECT_EQ(59, static_cast<int>(option->getDelegatedPrefixLength()));
|
||||
EXPECT_EQ("2001:db8:dead:beef::", option->getExcludedPrefix().toText());
|
||||
EXPECT_EQ(64, static_cast<int>(option->getExcludedPrefixLength()));
|
||||
}
|
||||
|
||||
// This test verifies parsing option wire format with subnet id of
|
||||
// 1 bytes.
|
||||
TEST(Option6PDExcludeTest, unpack2ByteSubnetId) {
|
||||
const uint8_t data[] = {
|
||||
0x00, 0x43, // option code 67
|
||||
0x00, 0x02, // option length
|
||||
0x40, 0xbe, 0xef // excluded prefix length 60 + subnet id
|
||||
};
|
||||
std::vector<uint8_t> vec(data, data + sizeof(data));
|
||||
|
||||
// Parse option.
|
||||
Option6PDExcludePtr option;
|
||||
ASSERT_NO_THROW(
|
||||
option.reset(new Option6PDExclude(IOAddress("2001:db8:dead::"),
|
||||
48, vec.begin() + 4, vec.end()))
|
||||
);
|
||||
|
||||
// Make sure that the option has been parsed correctly.
|
||||
EXPECT_EQ("2001:db8:dead::", option->getDelegatedPrefix().toText());
|
||||
EXPECT_EQ(48, static_cast<int>(option->getDelegatedPrefixLength()));
|
||||
EXPECT_EQ("2001:db8:dead:beef::", option->getExcludedPrefix().toText());
|
||||
EXPECT_EQ(64, static_cast<int>(option->getExcludedPrefixLength()));
|
||||
}
|
||||
|
||||
// This test verifies that errors are reported when option buffer contains
|
||||
// invalid option data.
|
||||
TEST(Option6PDExcludeTest, unpackErrors) {
|
||||
const uint8_t data[] = {
|
||||
0x00, 0x43,
|
||||
0x00, 0x02,
|
||||
0x40, 0x78
|
||||
};
|
||||
std::vector<uint8_t> vec(data, data + sizeof(data));
|
||||
|
||||
// Option has no IPv6 subnet id.
|
||||
EXPECT_THROW(Option6PDExclude(IOAddress("2001:db8:dead:bee0::"),
|
||||
59, vec.begin() + 4, vec.end() - 1),
|
||||
BadValue);
|
||||
|
||||
// Option has IPv6 subnet id of 1 byte, but it should have 2 bytes.
|
||||
EXPECT_THROW(Option6PDExclude(IOAddress("2001:db8:dead::"), 48,
|
||||
vec.begin() + 4, vec.end()),
|
||||
BadValue);
|
||||
}
|
||||
|
||||
// This test verifies conversion of the Prefix Exclude option to the
|
||||
// textual format.
|
||||
TEST(Option6PDExcludeTest, toText) {
|
||||
Option6PDExclude option(bee0, 59, beef, 64);
|
||||
EXPECT_EQ("type=00067, len=00002: 2001:db8:dead:beef::/64",
|
||||
option.toText());
|
||||
}
|
||||
|
||||
// This test verifies calculation of the Prefix Exclude option length.
|
||||
TEST(Option6PDExcludeTest, len) {
|
||||
Option6PDExcludePtr option;
|
||||
// The IPv6 subnet id is 2 bytes long. Hence the total length is
|
||||
// 2 bytes (option code) + 2 bytes (option length) + 1 byte
|
||||
// (excluded prefix length) + 2 bytes (IPv6 subnet id) = 7 bytes.
|
||||
ASSERT_NO_THROW(option.reset(new Option6PDExclude(bee0, 48, beef, 64)));
|
||||
EXPECT_EQ(7, option->len());
|
||||
|
||||
// IPv6 subnet id is 1 byte long. The total length is 6.
|
||||
ASSERT_NO_THROW(option.reset(new Option6PDExclude(bee0, 59, beef, 64)));
|
||||
EXPECT_EQ(6, option->len());
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
@ -18,6 +18,11 @@ using namespace isc::dhcp;
|
||||
|
||||
namespace {
|
||||
|
||||
/// @brief Default (zero) prefix tuple.
|
||||
const PrefixTuple
|
||||
ZERO_PREFIX_TUPLE(std::make_pair(PrefixLen(0),
|
||||
IOAddress(IOAddress::IPV6_ZERO_ADDRESS())));
|
||||
|
||||
/// @brief OptionCustomTest test class.
|
||||
class OptionCustomTest : public ::testing::Test {
|
||||
public:
|
||||
@ -476,6 +481,82 @@ TEST_F(OptionCustomTest, ipv6AddressData) {
|
||||
);
|
||||
}
|
||||
|
||||
// The purpose of this test is to verify that the option definition comprising
|
||||
// single variable length prefix can be used to create an instance of custom
|
||||
// option.
|
||||
TEST_F(OptionCustomTest, prefixData) {
|
||||
OptionDefinition opt_def("option-foo", 1000, "ipv6-prefix",
|
||||
"option-foo-space");
|
||||
|
||||
// Initialize input buffer.
|
||||
OptionBuffer buf;
|
||||
writeInt<uint8_t>(32, buf);
|
||||
writeInt<uint32_t>(0x30000001, buf);
|
||||
|
||||
// Append suboption.
|
||||
appendV6Suboption(buf);
|
||||
|
||||
// Create custom option using input buffer.
|
||||
boost::scoped_ptr<OptionCustom> option;
|
||||
ASSERT_NO_THROW(
|
||||
option.reset(new OptionCustom(opt_def, Option::V6, buf));
|
||||
);
|
||||
ASSERT_TRUE(option);
|
||||
|
||||
// We should have just one data field.
|
||||
ASSERT_EQ(1, option->getDataFieldsNum());
|
||||
|
||||
// Custom option should comprise exactly one buffer that represents
|
||||
// a prefix.
|
||||
PrefixTuple prefix(ZERO_PREFIX_TUPLE);
|
||||
// Read prefix from buffer #0.
|
||||
ASSERT_NO_THROW(prefix = option->readPrefix(0));
|
||||
|
||||
// The prefix comprises a prefix length and prefix value.
|
||||
EXPECT_EQ(32, prefix.first.asUnsigned());
|
||||
EXPECT_EQ("3000:1::", prefix.second.toText());
|
||||
|
||||
// Parsed option should have one suboption.
|
||||
EXPECT_TRUE(hasV6Suboption(option.get()));
|
||||
}
|
||||
|
||||
// The purpose of this test is to verify that the option definition comprising
|
||||
// single PSID can be used to create an instance of custom option.
|
||||
TEST_F(OptionCustomTest, psidData) {
|
||||
OptionDefinition opt_def("option-foo", 1000, "psid",
|
||||
"option-foo-space");
|
||||
|
||||
// Initialize input buffer.
|
||||
OptionBuffer buf;
|
||||
writeInt<uint8_t>(4, buf);
|
||||
writeInt<uint16_t>(0x8000, buf);
|
||||
|
||||
// Append suboption.
|
||||
appendV6Suboption(buf);
|
||||
|
||||
// Create custom option using input buffer.
|
||||
boost::scoped_ptr<OptionCustom> option;
|
||||
ASSERT_NO_THROW(
|
||||
option.reset(new OptionCustom(opt_def, Option::V6, buf));
|
||||
);
|
||||
ASSERT_TRUE(option);
|
||||
|
||||
// We should have just one data field.
|
||||
ASSERT_EQ(1, option->getDataFieldsNum());
|
||||
|
||||
// Custom option should comprise exactly one buffer that represents
|
||||
// a PSID length / PSID value tuple.
|
||||
PSIDTuple psid;
|
||||
// Read PSID length / PSID value from buffer #0.
|
||||
ASSERT_NO_THROW(psid = option->readPsid(0));
|
||||
|
||||
// The PSID comprises a PSID length and PSID value.
|
||||
EXPECT_EQ(4, psid.first.asUnsigned());
|
||||
EXPECT_EQ(0x08, psid.second.asUint16());
|
||||
|
||||
// Parsed option should have one suboption.
|
||||
EXPECT_TRUE(hasV6Suboption(option.get()));
|
||||
}
|
||||
|
||||
// The purpose of this test is to verify that the option definition comprising
|
||||
// string value can be used to create an instance of custom option.
|
||||
@ -768,6 +849,97 @@ TEST_F(OptionCustomTest, fqdnDataArray) {
|
||||
EXPECT_EQ("example.com.", domain1);
|
||||
}
|
||||
|
||||
// The purpose of this test is to verify that the option definition comprising
|
||||
// an array of IPv6 prefixes can be used to create an instance of OptionCustom.
|
||||
TEST_F(OptionCustomTest, prefixDataArray) {
|
||||
OptionDefinition opt_def("option-foo", 1000, "ipv6-prefix", true);
|
||||
|
||||
// The following buffer comprises three prefixes with different
|
||||
// prefix lengths.
|
||||
const uint8_t data[] = {
|
||||
32, 0x30, 0x01, 0x00, 0x01, // 3001:1::/32
|
||||
16, 0x30, 0x00, // 3000::/16
|
||||
48, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x01 // 2001:db8:1::/48
|
||||
};
|
||||
|
||||
// Initialize input buffer
|
||||
OptionBuffer buf(data,
|
||||
data + static_cast<size_t>(sizeof(data) / sizeof(char)));
|
||||
|
||||
// Create custom option using input buffer.
|
||||
boost::scoped_ptr<OptionCustom> option;
|
||||
ASSERT_NO_THROW(
|
||||
option.reset(new OptionCustom(opt_def, Option::V6, buf));
|
||||
);
|
||||
ASSERT_TRUE(option);
|
||||
|
||||
// We should have 3 data fields with 3 prefixes.
|
||||
ASSERT_EQ(3, option->getDataFieldsNum());
|
||||
|
||||
PrefixTuple prefix0(ZERO_PREFIX_TUPLE);
|
||||
PrefixTuple prefix1(ZERO_PREFIX_TUPLE);
|
||||
PrefixTuple prefix2(ZERO_PREFIX_TUPLE);
|
||||
|
||||
ASSERT_NO_THROW(prefix0 = option->readPrefix(0));
|
||||
ASSERT_NO_THROW(prefix1 = option->readPrefix(1));
|
||||
ASSERT_NO_THROW(prefix2 = option->readPrefix(2));
|
||||
|
||||
EXPECT_EQ(32, prefix0.first.asUnsigned());
|
||||
EXPECT_EQ("3001:1::", prefix0.second.toText());
|
||||
|
||||
EXPECT_EQ(16, prefix1.first.asUnsigned());
|
||||
EXPECT_EQ("3000::", prefix1.second.toText());
|
||||
|
||||
EXPECT_EQ(48, prefix2.first.asUnsigned());
|
||||
EXPECT_EQ("2001:db8:1::", prefix2.second.toText());
|
||||
}
|
||||
|
||||
// The purpose of this test is to verify that the option definition comprising
|
||||
// an array of PSIDs can be used to create an instance of OptionCustom.
|
||||
TEST_F(OptionCustomTest, psidDataArray) {
|
||||
OptionDefinition opt_def("option-foo", 1000, "psid", true);
|
||||
|
||||
// The following buffer comprises three PSIDs.
|
||||
const uint8_t data[] = {
|
||||
4, 0x80, 0x00, // PSID len = 4, PSID = '1000 000000000000b'
|
||||
6, 0xD4, 0x00, // PSID len = 6, PSID = '110101 0000000000b'
|
||||
1, 0x80, 0x00 // PSID len = 1, PSID = '1 000000000000000b'
|
||||
};
|
||||
// Initialize input buffer.
|
||||
OptionBuffer buf(data,
|
||||
data + static_cast<size_t>(sizeof(data) / sizeof(char)));
|
||||
|
||||
// Create custom option using input buffer.
|
||||
boost::scoped_ptr<OptionCustom> option;
|
||||
ASSERT_NO_THROW(
|
||||
option.reset(new OptionCustom(opt_def, Option::V6, buf));
|
||||
);
|
||||
ASSERT_TRUE(option);
|
||||
|
||||
// We should have 3 data fields with 3 PSIDs.
|
||||
ASSERT_EQ(3, option->getDataFieldsNum());
|
||||
|
||||
PSIDTuple psid0;
|
||||
PSIDTuple psid1;
|
||||
PSIDTuple psid2;
|
||||
|
||||
ASSERT_NO_THROW(psid0 = option->readPsid(0));
|
||||
ASSERT_NO_THROW(psid1 = option->readPsid(1));
|
||||
ASSERT_NO_THROW(psid2 = option->readPsid(2));
|
||||
|
||||
// PSID value is equal to '1000b' (8).
|
||||
EXPECT_EQ(4, psid0.first.asUnsigned());
|
||||
EXPECT_EQ(0x08, psid0.second.asUint16());
|
||||
|
||||
// PSID value is equal to '110101b' (0x35)
|
||||
EXPECT_EQ(6, psid1.first.asUnsigned());
|
||||
EXPECT_EQ(0x35, psid1.second.asUint16());
|
||||
|
||||
// PSID value is equal to '1b' (1).
|
||||
EXPECT_EQ(1, psid2.first.asUnsigned());
|
||||
EXPECT_EQ(0x01, psid2.second.asUint16());
|
||||
}
|
||||
|
||||
// The purpose of this test is to verify that the opton definition comprising
|
||||
// a record of fixed-size fields can be used to create an option with a
|
||||
// suboption.
|
||||
@ -821,6 +993,7 @@ TEST_F(OptionCustomTest, recordData) {
|
||||
ASSERT_NO_THROW(opt_def.addRecordField("fqdn"));
|
||||
ASSERT_NO_THROW(opt_def.addRecordField("ipv4-address"));
|
||||
ASSERT_NO_THROW(opt_def.addRecordField("ipv6-address"));
|
||||
ASSERT_NO_THROW(opt_def.addRecordField("psid"));
|
||||
ASSERT_NO_THROW(opt_def.addRecordField("string"));
|
||||
|
||||
const char fqdn_data[] = {
|
||||
@ -841,6 +1014,9 @@ TEST_F(OptionCustomTest, recordData) {
|
||||
writeAddress(IOAddress("192.168.0.1"), buf);
|
||||
// Initialize field 4 to IPv6 address.
|
||||
writeAddress(IOAddress("2001:db8:1::1"), buf);
|
||||
// Initialize PSID len and PSID value.
|
||||
writeInt<uint8_t>(6, buf);
|
||||
writeInt<uint16_t>(0xD400, buf);
|
||||
// Initialize field 5 to string value.
|
||||
writeString("ABCD", buf);
|
||||
|
||||
@ -851,7 +1027,7 @@ TEST_F(OptionCustomTest, recordData) {
|
||||
ASSERT_TRUE(option);
|
||||
|
||||
// We should have 6 data fields.
|
||||
ASSERT_EQ(6, option->getDataFieldsNum());
|
||||
ASSERT_EQ(7, option->getDataFieldsNum());
|
||||
|
||||
// Verify value in the field 0.
|
||||
uint16_t value0 = 0;
|
||||
@ -879,9 +1055,15 @@ TEST_F(OptionCustomTest, recordData) {
|
||||
EXPECT_EQ("2001:db8:1::1", value4.toText());
|
||||
|
||||
// Verify value in the field 5.
|
||||
std::string value5;
|
||||
ASSERT_NO_THROW(value5 = option->readString(5));
|
||||
EXPECT_EQ("ABCD", value5);
|
||||
PSIDTuple value5;
|
||||
ASSERT_NO_THROW(value5 = option->readPsid(5));
|
||||
EXPECT_EQ(6, value5.first.asUnsigned());
|
||||
EXPECT_EQ(0x35, value5.second.asUint16());
|
||||
|
||||
// Verify value in the field 6.
|
||||
std::string value6;
|
||||
ASSERT_NO_THROW(value6 = option->readString(6));
|
||||
EXPECT_EQ("ABCD", value6);
|
||||
}
|
||||
|
||||
// The purpose of this test is to verify that truncated buffer
|
||||
@ -1073,6 +1255,64 @@ TEST_F(OptionCustomTest, setIpv6AddressData) {
|
||||
EXPECT_EQ("2001:db8:1::1", address.toText());
|
||||
}
|
||||
|
||||
// The purpose of this test is to verify that an option comprising
|
||||
// a prefix can be created and that the prefix can be overriden by
|
||||
// a new value.
|
||||
TEST_F(OptionCustomTest, setPrefixData) {
|
||||
OptionDefinition opt_def("option-foo", 1000, "ipv6-prefix");
|
||||
|
||||
// Create an option and let the data field be initialized
|
||||
// to default value (do not provide any data buffer).
|
||||
boost::scoped_ptr<OptionCustom> option;
|
||||
ASSERT_NO_THROW(
|
||||
option.reset(new OptionCustom(opt_def, Option::V6));
|
||||
);
|
||||
ASSERT_TRUE(option);
|
||||
|
||||
// Make sure the default prefix is set.
|
||||
PrefixTuple prefix(ZERO_PREFIX_TUPLE);
|
||||
ASSERT_NO_THROW(prefix = option->readPrefix());
|
||||
EXPECT_EQ(0, prefix.first.asUnsigned());
|
||||
EXPECT_EQ("::", prefix.second.toText());
|
||||
|
||||
// Write prefix.
|
||||
ASSERT_NO_THROW(option->writePrefix(PrefixLen(48), IOAddress("2001:db8:1::")));
|
||||
|
||||
// Read prefix back and make sure it is the one we just set.
|
||||
ASSERT_NO_THROW(prefix = option->readPrefix());
|
||||
EXPECT_EQ(48, prefix.first.asUnsigned());
|
||||
EXPECT_EQ("2001:db8:1::", prefix.second.toText());
|
||||
}
|
||||
|
||||
// The purpose of this test is to verify that an option comprising
|
||||
// a single PSID can be created and that the PSID can be overriden
|
||||
// by a new value.
|
||||
TEST_F(OptionCustomTest, setPsidData) {
|
||||
OptionDefinition opt_def("option-foo", 1000, "psid");
|
||||
|
||||
// Create an option and let the data field be initialized
|
||||
// to default value (do not provide any data buffer).
|
||||
boost::scoped_ptr<OptionCustom> option;
|
||||
ASSERT_NO_THROW(
|
||||
option.reset(new OptionCustom(opt_def, Option::V6));
|
||||
);
|
||||
ASSERT_TRUE(option);
|
||||
|
||||
// Make sure the default PSID is set.
|
||||
PSIDTuple psid;
|
||||
ASSERT_NO_THROW(psid = option->readPsid());
|
||||
EXPECT_EQ(0, psid.first.asUnsigned());
|
||||
EXPECT_EQ(0, psid.second.asUint16());
|
||||
|
||||
// Write PSID.
|
||||
ASSERT_NO_THROW(option->writePsid(PSIDLen(4), PSID(8)));
|
||||
|
||||
// Read PSID back and make sure it is the one we just set.
|
||||
ASSERT_NO_THROW(psid = option->readPsid());
|
||||
EXPECT_EQ(4, psid.first.asUnsigned());
|
||||
EXPECT_EQ(8, psid.second.asUint16());
|
||||
}
|
||||
|
||||
// The purpose of this test is to verify that an option comprising
|
||||
// single string value can be created and that this value
|
||||
// is initialized to the default value. Also, this test checks that
|
||||
@ -1280,6 +1520,96 @@ TEST_F(OptionCustomTest, setIpv6AddressDataArray) {
|
||||
);
|
||||
}
|
||||
|
||||
/// The purpose of this test is to verify that an option comprising an
|
||||
/// array of PSIDs can be created with no PSIDs and that PSIDs can be
|
||||
/// later added after the option has been created.
|
||||
TEST_F(OptionCustomTest, setPSIDPrefixArray) {
|
||||
OptionDefinition opt_def("option-foo", 1000, "psid", true);
|
||||
|
||||
// Create an option and let the data field be initialized
|
||||
// to default value (do not provide any data buffer).
|
||||
boost::scoped_ptr<OptionCustom> option;
|
||||
ASSERT_NO_THROW(
|
||||
option.reset(new OptionCustom(opt_def, Option::V6));
|
||||
);
|
||||
ASSERT_TRUE(option);
|
||||
|
||||
// Initially, the array does not contain any data fields.
|
||||
ASSERT_EQ(0, option->getDataFieldsNum());
|
||||
|
||||
// Add 3 new PSIDs
|
||||
ASSERT_NO_THROW(option->addArrayDataField(PSIDLen(4), PSID(1)));
|
||||
ASSERT_NO_THROW(option->addArrayDataField(PSIDLen(0), PSID(123)));
|
||||
ASSERT_NO_THROW(option->addArrayDataField(PSIDLen(1), PSID(1)));
|
||||
|
||||
// Verify the stored values.
|
||||
ASSERT_NO_THROW({
|
||||
PSIDTuple psid0 = option->readPsid(0);
|
||||
EXPECT_EQ(4, psid0.first.asUnsigned());
|
||||
EXPECT_EQ(1, psid0.second.asUint16());
|
||||
});
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
PSIDTuple psid1 = option->readPsid(1);
|
||||
EXPECT_EQ(0, psid1.first.asUnsigned());
|
||||
EXPECT_EQ(0, psid1.second.asUint16());
|
||||
});
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
PSIDTuple psid2 = option->readPsid(2);
|
||||
EXPECT_EQ(1, psid2.first.asUnsigned());
|
||||
EXPECT_EQ(1, psid2.second.asUint16());
|
||||
});
|
||||
}
|
||||
|
||||
/// The purpose of this test is to verify that an option comprising an
|
||||
/// array of IPv6 prefixes can be created with no prefixes and that
|
||||
/// prefixes can be later added after the option has been created.
|
||||
TEST_F(OptionCustomTest, setIPv6PrefixDataArray) {
|
||||
OptionDefinition opt_def("option-foo", 1000, "ipv6-prefix", true);
|
||||
|
||||
// Create an option and let the data field be initialized
|
||||
// to default value (do not provide any data buffer).
|
||||
boost::scoped_ptr<OptionCustom> option;
|
||||
ASSERT_NO_THROW(
|
||||
option.reset(new OptionCustom(opt_def, Option::V6));
|
||||
);
|
||||
ASSERT_TRUE(option);
|
||||
|
||||
// Initially, the array does not contain any data fields.
|
||||
ASSERT_EQ(0, option->getDataFieldsNum());
|
||||
|
||||
// Add 3 new IPv6 prefixes into the array.
|
||||
ASSERT_NO_THROW(option->addArrayDataField(PrefixLen(64),
|
||||
IOAddress("2001:db8:1::")));
|
||||
ASSERT_NO_THROW(option->addArrayDataField(PrefixLen(32),
|
||||
IOAddress("3001:1::")));
|
||||
ASSERT_NO_THROW(option->addArrayDataField(PrefixLen(16),
|
||||
IOAddress("3000::")));
|
||||
|
||||
// We should have now 3 addresses added.
|
||||
ASSERT_EQ(3, option->getDataFieldsNum());
|
||||
|
||||
// Verify the stored values.
|
||||
ASSERT_NO_THROW({
|
||||
PrefixTuple prefix0 = option->readPrefix(0);
|
||||
EXPECT_EQ(64, prefix0.first.asUnsigned());
|
||||
EXPECT_EQ("2001:db8:1::", prefix0.second.toText());
|
||||
});
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
PrefixTuple prefix1 = option->readPrefix(1);
|
||||
EXPECT_EQ(32, prefix1.first.asUnsigned());
|
||||
EXPECT_EQ("3001:1::", prefix1.second.toText());
|
||||
});
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
PrefixTuple prefix2 = option->readPrefix(2);
|
||||
EXPECT_EQ(16, prefix2.first.asUnsigned());
|
||||
EXPECT_EQ("3000::", prefix2.second.toText());
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(OptionCustomTest, setRecordData) {
|
||||
OptionDefinition opt_def("OPTION_FOO", 1000, "record");
|
||||
|
||||
@ -1288,6 +1618,8 @@ TEST_F(OptionCustomTest, setRecordData) {
|
||||
ASSERT_NO_THROW(opt_def.addRecordField("fqdn"));
|
||||
ASSERT_NO_THROW(opt_def.addRecordField("ipv4-address"));
|
||||
ASSERT_NO_THROW(opt_def.addRecordField("ipv6-address"));
|
||||
ASSERT_NO_THROW(opt_def.addRecordField("psid"));
|
||||
ASSERT_NO_THROW(opt_def.addRecordField("ipv6-prefix"));
|
||||
ASSERT_NO_THROW(opt_def.addRecordField("string"));
|
||||
|
||||
// Create an option and let the data field be initialized
|
||||
@ -1300,7 +1632,7 @@ TEST_F(OptionCustomTest, setRecordData) {
|
||||
|
||||
// The number of elements should be equal to number of elements
|
||||
// in the record.
|
||||
ASSERT_EQ(6, option->getDataFieldsNum());
|
||||
ASSERT_EQ(8, option->getDataFieldsNum());
|
||||
|
||||
// Check that the default values have been correctly set.
|
||||
uint16_t value0;
|
||||
@ -1318,9 +1650,17 @@ TEST_F(OptionCustomTest, setRecordData) {
|
||||
IOAddress value4("2001:db8:1::1");
|
||||
ASSERT_NO_THROW(value4 = option->readAddress(4));
|
||||
EXPECT_EQ("::", value4.toText());
|
||||
std::string value5 = "xyz";
|
||||
ASSERT_NO_THROW(value5 = option->readString(5));
|
||||
EXPECT_TRUE(value5.empty());
|
||||
PSIDTuple value5;
|
||||
ASSERT_NO_THROW(value5 = option->readPsid(5));
|
||||
EXPECT_EQ(0, value5.first.asUnsigned());
|
||||
EXPECT_EQ(0, value5.second.asUint16());
|
||||
PrefixTuple value6(ZERO_PREFIX_TUPLE);
|
||||
ASSERT_NO_THROW(value6 = option->readPrefix(6));
|
||||
EXPECT_EQ(0, value6.first.asUnsigned());
|
||||
EXPECT_EQ("::", value6.second.toText());
|
||||
std::string value7 = "xyz";
|
||||
ASSERT_NO_THROW(value7 = option->readString(7));
|
||||
EXPECT_TRUE(value7.empty());
|
||||
|
||||
// Override each value with a new value.
|
||||
ASSERT_NO_THROW(option->writeInteger<uint16_t>(1234, 0));
|
||||
@ -1328,7 +1668,10 @@ TEST_F(OptionCustomTest, setRecordData) {
|
||||
ASSERT_NO_THROW(option->writeFqdn("example.com", 2));
|
||||
ASSERT_NO_THROW(option->writeAddress(IOAddress("192.168.0.1"), 3));
|
||||
ASSERT_NO_THROW(option->writeAddress(IOAddress("2001:db8:1::100"), 4));
|
||||
ASSERT_NO_THROW(option->writeString("hello world", 5));
|
||||
ASSERT_NO_THROW(option->writePsid(PSIDLen(4), PSID(8), 5));
|
||||
ASSERT_NO_THROW(option->writePrefix(PrefixLen(48),
|
||||
IOAddress("2001:db8:1::"), 6));
|
||||
ASSERT_NO_THROW(option->writeString("hello world", 7));
|
||||
|
||||
// Check that the new values have been correctly set.
|
||||
ASSERT_NO_THROW(value0 = option->readInteger<uint16_t>(0));
|
||||
@ -1341,8 +1684,14 @@ TEST_F(OptionCustomTest, setRecordData) {
|
||||
EXPECT_EQ("192.168.0.1", value3.toText());
|
||||
ASSERT_NO_THROW(value4 = option->readAddress(4));
|
||||
EXPECT_EQ("2001:db8:1::100", value4.toText());
|
||||
ASSERT_NO_THROW(value5 = option->readString(5));
|
||||
EXPECT_EQ(value5, "hello world");
|
||||
ASSERT_NO_THROW(value5 = option->readPsid(5));
|
||||
EXPECT_EQ(4, value5.first.asUnsigned());
|
||||
EXPECT_EQ(8, value5.second.asUint16());
|
||||
ASSERT_NO_THROW(value6 = option->readPrefix(6));
|
||||
EXPECT_EQ(48, value6.first.asUnsigned());
|
||||
EXPECT_EQ("2001:db8:1::", value6.second.toText());
|
||||
ASSERT_NO_THROW(value7 = option->readString(7));
|
||||
EXPECT_EQ(value7, "hello world");
|
||||
}
|
||||
|
||||
// The purpose of this test is to verify that pack function for
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2012-2016 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
|
||||
@ -7,12 +7,19 @@
|
||||
#include <config.h>
|
||||
#include <dhcp/option_data_types.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <utility>
|
||||
|
||||
using namespace isc;
|
||||
using namespace isc::asiolink;
|
||||
using namespace isc::dhcp;
|
||||
|
||||
namespace {
|
||||
|
||||
/// @brief Default (zero) prefix tuple.
|
||||
const PrefixTuple
|
||||
ZERO_PREFIX_TUPLE(std::make_pair(PrefixLen(0),
|
||||
IOAddress(IOAddress::IPV6_ZERO_ADDRESS())));
|
||||
|
||||
/// @brief Test class for option data type utilities.
|
||||
class OptionDataTypesTest : public ::testing::Test {
|
||||
public:
|
||||
@ -456,6 +463,258 @@ TEST_F(OptionDataTypesTest, writeFqdn) {
|
||||
);
|
||||
}
|
||||
|
||||
// The purpose of this test is to verify that the variable length prefix
|
||||
// can be read from a buffer correctly.
|
||||
TEST_F(OptionDataTypesTest, readPrefix) {
|
||||
std::vector<uint8_t> buf;
|
||||
|
||||
// Prefix 2001:db8::/64
|
||||
writeInt<uint8_t>(64, buf);
|
||||
writeInt<uint32_t>(0x20010db8, buf);
|
||||
writeInt<uint32_t>(0, buf);
|
||||
|
||||
PrefixTuple prefix(ZERO_PREFIX_TUPLE);
|
||||
ASSERT_NO_THROW(prefix = OptionDataTypeUtil::readPrefix(buf));
|
||||
EXPECT_EQ(64, prefix.first.asUnsigned());
|
||||
EXPECT_EQ("2001:db8::", prefix.second.toText());
|
||||
|
||||
buf.clear();
|
||||
|
||||
// Prefix 2001:db8::/63
|
||||
writeInt<uint8_t>(63, buf);
|
||||
writeInt<uint32_t>(0x20010db8, buf);
|
||||
writeInt<uint32_t>(0, buf);
|
||||
|
||||
ASSERT_NO_THROW(prefix = OptionDataTypeUtil::readPrefix(buf));
|
||||
EXPECT_EQ(63, prefix.first.asUnsigned());
|
||||
EXPECT_EQ("2001:db8::", prefix.second.toText());
|
||||
|
||||
buf.clear();
|
||||
|
||||
// Prefix 2001:db8:c0000. Note that the last four bytes are filled with
|
||||
// 0xFF (all bits set). When the prefix is read those non-significant
|
||||
// bits (beyond prefix length) should be ignored (read as 0). Only first
|
||||
// two bits of 0xFFFFFFFF should be read, thus 0xC000, rather than 0xFFFF.
|
||||
writeInt<uint8_t>(34, buf);
|
||||
writeInt<uint32_t>(0x20010db8, buf);
|
||||
writeInt<uint32_t>(0xFFFFFFFF, buf);
|
||||
|
||||
ASSERT_NO_THROW(prefix = OptionDataTypeUtil::readPrefix(buf));
|
||||
EXPECT_EQ(34, prefix.first.asUnsigned());
|
||||
EXPECT_EQ("2001:db8:c000::", prefix.second.toText());
|
||||
|
||||
buf.clear();
|
||||
|
||||
// Prefix having a length of 0.
|
||||
writeInt<uint8_t>(0, buf);
|
||||
writeInt<uint16_t>(0x2001, buf);
|
||||
|
||||
ASSERT_NO_THROW(prefix = OptionDataTypeUtil::readPrefix(buf));
|
||||
EXPECT_EQ(0, prefix.first.asUnsigned());
|
||||
EXPECT_EQ("::", prefix.second.toText());
|
||||
|
||||
buf.clear();
|
||||
|
||||
// Prefix having a maximum length of 128.
|
||||
writeInt<uint8_t>(128, buf);
|
||||
buf.insert(buf.end(), 16, 0x11);
|
||||
|
||||
ASSERT_NO_THROW(prefix = OptionDataTypeUtil::readPrefix(buf));
|
||||
EXPECT_EQ(128, prefix.first.asUnsigned());
|
||||
EXPECT_EQ("1111:1111:1111:1111:1111:1111:1111:1111",
|
||||
prefix.second.toText());
|
||||
|
||||
buf.clear();
|
||||
|
||||
// Prefix length is greater than 128. This should result in an
|
||||
// error.
|
||||
writeInt<uint8_t>(129, buf);
|
||||
writeInt<uint16_t>(0x3000, buf);
|
||||
buf.resize(17);
|
||||
|
||||
EXPECT_THROW(static_cast<void>(OptionDataTypeUtil::readPrefix(buf)),
|
||||
BadDataTypeCast);
|
||||
|
||||
buf.clear();
|
||||
|
||||
// Buffer truncated. Prefix length of 10 requires at least 2 bytes,
|
||||
// but there is only one byte.
|
||||
writeInt<uint8_t>(10, buf);
|
||||
writeInt<uint8_t>(1, buf);
|
||||
|
||||
EXPECT_THROW(static_cast<void>(OptionDataTypeUtil::readPrefix(buf)),
|
||||
BadDataTypeCast);
|
||||
}
|
||||
|
||||
// The purpose of this test is to verify that the variable length prefix
|
||||
// is written to a buffer correctly.
|
||||
TEST_F(OptionDataTypesTest, writePrefix) {
|
||||
// Initialize a buffer and store some value in it. We'll want to make
|
||||
// sure that the prefix being written will not override this value, but
|
||||
// will rather be appended.
|
||||
std::vector<uint8_t> buf(1, 1);
|
||||
|
||||
// Prefix 2001:db8:FFFF::/34 is equal to 2001:db8:C000::/34 because
|
||||
// there are only 34 significant bits. All other bits must be zeroed.
|
||||
ASSERT_NO_THROW(OptionDataTypeUtil::writePrefix(PrefixLen(34),
|
||||
IOAddress("2001:db8:FFFF::"),
|
||||
buf));
|
||||
ASSERT_EQ(7, buf.size());
|
||||
|
||||
EXPECT_EQ(1, static_cast<unsigned>(buf[0]));
|
||||
EXPECT_EQ(34, static_cast<unsigned>(buf[1]));
|
||||
EXPECT_EQ(0x20, static_cast<unsigned>(buf[2]));
|
||||
EXPECT_EQ(0x01, static_cast<unsigned>(buf[3]));
|
||||
EXPECT_EQ(0x0D, static_cast<unsigned>(buf[4]));
|
||||
EXPECT_EQ(0xB8, static_cast<unsigned>(buf[5]));
|
||||
EXPECT_EQ(0xC0, static_cast<unsigned>(buf[6]));
|
||||
|
||||
buf.clear();
|
||||
|
||||
// Prefix length is 0. The entire prefix should be ignored.
|
||||
ASSERT_NO_THROW(OptionDataTypeUtil::writePrefix(PrefixLen(0),
|
||||
IOAddress("2001:db8:FFFF::"),
|
||||
buf));
|
||||
ASSERT_EQ(1, buf.size());
|
||||
EXPECT_EQ(0, static_cast<unsigned>(buf[0]));
|
||||
|
||||
buf.clear();
|
||||
|
||||
// Prefix having a maximum length of 128.
|
||||
ASSERT_NO_THROW(OptionDataTypeUtil::writePrefix(PrefixLen(128),
|
||||
IOAddress("2001:db8::FF"),
|
||||
buf));
|
||||
|
||||
// We should now have a 17 bytes long buffer. 1 byte goes for a prefix
|
||||
// length field, the remaining ones hold the prefix.
|
||||
ASSERT_EQ(17, buf.size());
|
||||
// Because the prefix is 16 bytes long, we can simply use the
|
||||
// IOAddress convenience function to read it back and compare
|
||||
// it with the textual representation. This is simpler than
|
||||
// comparing each byte separately.
|
||||
IOAddress prefix_read = IOAddress::fromBytes(AF_INET6, &buf[1]);
|
||||
EXPECT_EQ("2001:db8::ff", prefix_read.toText());
|
||||
|
||||
buf.clear();
|
||||
|
||||
// It is illegal to use IPv4 address as prefix.
|
||||
EXPECT_THROW(OptionDataTypeUtil::writePrefix(PrefixLen(4),
|
||||
IOAddress("10.0.0.1"), buf),
|
||||
BadDataTypeCast);
|
||||
}
|
||||
|
||||
// The purpose of this test is to verify that the
|
||||
// PSID-len/PSID tuple can be read from a buffer.
|
||||
TEST_F(OptionDataTypesTest, readPsid) {
|
||||
std::vector<uint8_t> buf;
|
||||
|
||||
// PSID length is 6 (bits)
|
||||
writeInt<uint8_t>(6, buf);
|
||||
// 0xA400 is represented as 1010010000000000b, which is equivalent
|
||||
// of portset 0x29 (101001b).
|
||||
writeInt<uint16_t>(0xA400, buf);
|
||||
|
||||
PSIDTuple psid;
|
||||
ASSERT_NO_THROW(psid = OptionDataTypeUtil::readPsid(buf));
|
||||
EXPECT_EQ(6, psid.first.asUnsigned());
|
||||
EXPECT_EQ(0x29, psid.second.asUint16());
|
||||
|
||||
buf.clear();
|
||||
|
||||
// PSID length is 0, in which case PSID should be ignored.
|
||||
writeInt<uint8_t>(0, buf);
|
||||
// Let's put some junk into the PSID field to make sure it will
|
||||
// be ignored.
|
||||
writeInt<uint16_t>(0x1234, buf);
|
||||
ASSERT_NO_THROW(psid = OptionDataTypeUtil::readPsid(buf));
|
||||
EXPECT_EQ(0, psid.first.asUnsigned());
|
||||
EXPECT_EQ(0, psid.second.asUint16());
|
||||
|
||||
buf.clear();
|
||||
|
||||
// PSID length greater than 16 is not allowed.
|
||||
writeInt<uint8_t>(17, buf);
|
||||
writeInt<uint16_t>(0, buf);
|
||||
EXPECT_THROW(static_cast<void>(OptionDataTypeUtil::readPsid(buf)),
|
||||
BadDataTypeCast);
|
||||
|
||||
buf.clear();
|
||||
|
||||
// PSID length is 3 bits, but the PSID value is 11 (1011b), so it
|
||||
// is encoded on 4 bits, rather than 3.
|
||||
writeInt<uint8_t>(3, buf);
|
||||
writeInt<uint16_t>(0xB000, buf);
|
||||
EXPECT_THROW(static_cast<void>(OptionDataTypeUtil::readPsid(buf)),
|
||||
BadDataTypeCast);
|
||||
|
||||
buf.clear();
|
||||
|
||||
// Buffer is truncated - 2 bytes instead of 3.
|
||||
writeInt<uint8_t>(4, buf);
|
||||
writeInt<uint8_t>(0xF0, buf);
|
||||
EXPECT_THROW(static_cast<void>(OptionDataTypeUtil::readPsid(buf)),
|
||||
BadDataTypeCast);
|
||||
}
|
||||
|
||||
// The purpose of this test is to verify that the PSID-len/PSID
|
||||
// tuple is written to a buffer correctly.
|
||||
TEST_F(OptionDataTypesTest, writePsid) {
|
||||
// Let's create a buffer with some data in it. We want to make
|
||||
// sure that the existing data remain untouched when we write
|
||||
// PSID to the buffer.
|
||||
std::vector<uint8_t> buf(1, 1);
|
||||
// PSID length is 4 (bits), PSID value is 8.
|
||||
ASSERT_NO_THROW(OptionDataTypeUtil::writePsid(PSIDLen(4), PSID(8), buf));
|
||||
ASSERT_EQ(4, buf.size());
|
||||
// The byte which existed in the buffer should still hold the
|
||||
// same value.
|
||||
EXPECT_EQ(1, static_cast<unsigned>(buf[0]));
|
||||
// PSID length should be written as specified in the function call.
|
||||
EXPECT_EQ(4, static_cast<unsigned>(buf[1]));
|
||||
// The PSID structure is as follows:
|
||||
// UUUUPPPPPPPPPPPP, where "U" are useful bits on which we code
|
||||
// the PSID. "P" are zero padded bits. The PSID value 8 is coded
|
||||
// on four useful bits as '1000b'. That means that the PSID value
|
||||
// encoded in the PSID field is: '1000000000000000b', which is
|
||||
// 0x8000. The next two EXPECT_EQ statements verify that.
|
||||
EXPECT_EQ(0x80, static_cast<unsigned>(buf[2]));
|
||||
EXPECT_EQ(0x00, static_cast<unsigned>(buf[3]));
|
||||
|
||||
// Clear the buffer to make sure we don't append to the
|
||||
// existing data.
|
||||
buf.clear();
|
||||
|
||||
// The PSID length of 0 causes the PSID value (of 6) to be ignored.
|
||||
// As a result, the buffer should hold only zeros.
|
||||
ASSERT_NO_THROW(OptionDataTypeUtil::writePsid(PSIDLen(0), PSID(6), buf));
|
||||
ASSERT_EQ(3, buf.size());
|
||||
EXPECT_EQ(0, static_cast<unsigned>(buf[0]));
|
||||
EXPECT_EQ(0, static_cast<unsigned>(buf[1]));
|
||||
EXPECT_EQ(0, static_cast<unsigned>(buf[2]));
|
||||
|
||||
buf.clear();
|
||||
|
||||
// Another test case, to verify that we can use the maximum length
|
||||
// of PSID (16 bits).
|
||||
ASSERT_NO_THROW(OptionDataTypeUtil::writePsid(PSIDLen(16), PSID(5), buf));
|
||||
ASSERT_EQ(3, buf.size());
|
||||
// PSID length should be written with no change.
|
||||
EXPECT_EQ(16, static_cast<unsigned>(buf[0]));
|
||||
// Check PSID value.
|
||||
EXPECT_EQ(0x00, static_cast<unsigned>(buf[1]));
|
||||
EXPECT_EQ(0x05, static_cast<unsigned>(buf[2]));
|
||||
|
||||
// PSID length of 17 exceeds the maximum allowed value of 16.
|
||||
EXPECT_THROW(OptionDataTypeUtil::writePsid(PSIDLen(17), PSID(1), buf),
|
||||
OutOfRange);
|
||||
|
||||
// PSID length is 1, which allows for coding up to two (2^1)
|
||||
// port sets. These are namely port set 0 and port set 1. The
|
||||
// value of 2 is out of that range.
|
||||
EXPECT_THROW(OptionDataTypeUtil::writePsid(PSIDLen(1), PSID(2), buf),
|
||||
BadDataTypeCast);
|
||||
}
|
||||
|
||||
// The purpose of this test is to verify that the string
|
||||
// can be read from a buffer correctly.
|
||||
TEST_F(OptionDataTypesTest, readString) {
|
||||
|
@ -1241,7 +1241,7 @@ TEST_F(OptionDefinitionTest, integerInvalidType) {
|
||||
// see if it rejects it.
|
||||
OptionBuffer buf(1);
|
||||
EXPECT_THROW(
|
||||
OptionDefinition::factoryInteger<bool>(Option::V6, D6O_PREFERENCE, "dhcp6",
|
||||
OptionDefinition::factoryInteger<bool>(Option::V6, D6O_PREFERENCE, DHCP6_OPTION_SPACE,
|
||||
buf.begin(), buf.end()),
|
||||
isc::dhcp::InvalidDataType
|
||||
);
|
||||
@ -1289,4 +1289,225 @@ TEST_F(OptionDefinitionTest, haveClientFqdnFormat) {
|
||||
EXPECT_FALSE(opt_def_invalid.haveClientFqdnFormat());
|
||||
}
|
||||
|
||||
// This test verifies that a definition of an option with a single IPv6
|
||||
// prefix can be created and used to create an instance of the option.
|
||||
TEST_F(OptionDefinitionTest, prefix) {
|
||||
OptionDefinition opt_def("option-prefix", 1000, "ipv6-prefix");
|
||||
|
||||
// Create a buffer holding a prefix.
|
||||
OptionBuffer buf;
|
||||
buf.push_back(32);
|
||||
buf.push_back(0x30);
|
||||
buf.push_back(0x00);
|
||||
buf.resize(5);
|
||||
|
||||
OptionPtr option_v6;
|
||||
|
||||
// Create an instance of this option from the definition.
|
||||
ASSERT_NO_THROW(
|
||||
option_v6 = opt_def.optionFactory(Option::V6, 1000, buf);
|
||||
);
|
||||
|
||||
// Make sure that the returned option class is correct.
|
||||
const Option* optptr = option_v6.get();
|
||||
ASSERT_TRUE(optptr);
|
||||
ASSERT_TRUE(typeid(*optptr) == typeid(OptionCustom));
|
||||
|
||||
// Validate the value.
|
||||
OptionCustomPtr option_cast_v6 =
|
||||
boost::dynamic_pointer_cast<OptionCustom>(option_v6);
|
||||
ASSERT_EQ(1, option_cast_v6->getDataFieldsNum());
|
||||
PrefixTuple prefix = option_cast_v6->readPrefix();
|
||||
EXPECT_EQ(32, prefix.first.asUnsigned());
|
||||
EXPECT_EQ("3000::", prefix.second.toText());
|
||||
}
|
||||
|
||||
// This test verifies that a definition of an option with a single IPv6
|
||||
// prefix can be created and that the instance of this option can be
|
||||
// created by specifying the prefix in the textual format.
|
||||
TEST_F(OptionDefinitionTest, prefixTokenized) {
|
||||
OptionDefinition opt_def("option-prefix", 1000, "ipv6-prefix");
|
||||
|
||||
OptionPtr option_v6;
|
||||
// Specify a single prefix.
|
||||
std::vector<std::string> values(1, "2001:db8:1::/64");
|
||||
|
||||
// Create an instance of the option using the definition.
|
||||
ASSERT_NO_THROW(
|
||||
option_v6 = opt_def.optionFactory(Option::V6, 1000, values);
|
||||
);
|
||||
|
||||
// Make sure that the returned option class is correct.
|
||||
const Option* optptr = option_v6.get();
|
||||
ASSERT_TRUE(optptr);
|
||||
ASSERT_TRUE(typeid(*optptr) == typeid(OptionCustom));
|
||||
|
||||
// Validate the value.
|
||||
OptionCustomPtr option_cast_v6 =
|
||||
boost::dynamic_pointer_cast<OptionCustom>(option_v6);
|
||||
ASSERT_EQ(1, option_cast_v6->getDataFieldsNum());
|
||||
PrefixTuple prefix = option_cast_v6->readPrefix();
|
||||
EXPECT_EQ(64, prefix.first.asUnsigned());
|
||||
EXPECT_EQ("2001:db8:1::", prefix.second.toText());
|
||||
}
|
||||
|
||||
// This test verifies that a definition of an option with an array
|
||||
// of IPv6 prefixes can be created and that the instance of this
|
||||
// option can be created by specifying multiple prefixes in the
|
||||
// textual format.
|
||||
TEST_F(OptionDefinitionTest, prefixArrayTokenized) {
|
||||
OptionDefinition opt_def("option-prefix", 1000, "ipv6-prefix", true);
|
||||
|
||||
OptionPtr option_v6;
|
||||
|
||||
// Specify 3 prefixes
|
||||
std::vector<std::string> values;
|
||||
values.push_back("2001:db8:1:: /64");
|
||||
values.push_back("3000::/ 32");
|
||||
values.push_back("3001:1:: / 48");
|
||||
|
||||
// Create an instance of an option using the definition.
|
||||
ASSERT_NO_THROW(
|
||||
option_v6 = opt_def.optionFactory(Option::V6, 1000, values);
|
||||
);
|
||||
|
||||
// Make sure that the option class returned is correct.
|
||||
const Option* optptr = option_v6.get();
|
||||
ASSERT_TRUE(optptr);
|
||||
ASSERT_TRUE(typeid(*optptr) == typeid(OptionCustom));
|
||||
|
||||
OptionCustomPtr option_cast_v6 =
|
||||
boost::dynamic_pointer_cast<OptionCustom>(option_v6);
|
||||
|
||||
// There should be 3 prefixes in this option.
|
||||
ASSERT_EQ(3, option_cast_v6->getDataFieldsNum());
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
PrefixTuple prefix0 = option_cast_v6->readPrefix(0);
|
||||
EXPECT_EQ(64, prefix0.first.asUnsigned());
|
||||
EXPECT_EQ("2001:db8:1::", prefix0.second.toText());
|
||||
});
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
PrefixTuple prefix1 = option_cast_v6->readPrefix(1);
|
||||
EXPECT_EQ(32, prefix1.first.asUnsigned());
|
||||
EXPECT_EQ("3000::", prefix1.second.toText());
|
||||
});
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
PrefixTuple prefix2 = option_cast_v6->readPrefix(2);
|
||||
EXPECT_EQ(48, prefix2.first.asUnsigned());
|
||||
EXPECT_EQ("3001:1::", prefix2.second.toText());
|
||||
});
|
||||
}
|
||||
|
||||
// This test verifies that a definition of an option with a single PSID
|
||||
// value can be created and used to create an instance of the option.
|
||||
TEST_F(OptionDefinitionTest, psid) {
|
||||
OptionDefinition opt_def("option-psid", 1000, "psid");
|
||||
|
||||
OptionPtr option_v6;
|
||||
|
||||
// Create a buffer holding PSID.
|
||||
OptionBuffer buf;
|
||||
buf.push_back(6);
|
||||
buf.push_back(0x4);
|
||||
buf.push_back(0x0);
|
||||
|
||||
// Create an instance of this option from the definition.
|
||||
ASSERT_NO_THROW(
|
||||
option_v6 = opt_def.optionFactory(Option::V6, 1000, buf);
|
||||
);
|
||||
|
||||
// Make sure that the returned option class is correct.
|
||||
const Option* optptr = option_v6.get();
|
||||
ASSERT_TRUE(optptr);
|
||||
ASSERT_TRUE(typeid(*optptr) == typeid(OptionCustom));
|
||||
|
||||
// Validate the value.
|
||||
OptionCustomPtr option_cast_v6 =
|
||||
boost::dynamic_pointer_cast<OptionCustom>(option_v6);
|
||||
ASSERT_EQ(1, option_cast_v6->getDataFieldsNum());
|
||||
PSIDTuple psid = option_cast_v6->readPsid();
|
||||
EXPECT_EQ(6, psid.first.asUnsigned());
|
||||
EXPECT_EQ(1, psid.second.asUint16());
|
||||
}
|
||||
|
||||
// This test verifies that a definition of an option with a single PSID
|
||||
// value can be created and that the instance of this option can be
|
||||
// created by specifying PSID length and value in the textual format.
|
||||
TEST_F(OptionDefinitionTest, psidTokenized) {
|
||||
OptionDefinition opt_def("option-psid", 1000, "psid");
|
||||
|
||||
OptionPtr option_v6;
|
||||
// Specify a single PSID with a length of 6 and value of 3.
|
||||
std::vector<std::string> values(1, "3 / 6");
|
||||
|
||||
// Create an instance of the option using the definition.
|
||||
ASSERT_NO_THROW(
|
||||
option_v6 = opt_def.optionFactory(Option::V6, 1000, values);
|
||||
);
|
||||
|
||||
// Make sure that the returned option class is correct.
|
||||
const Option* optptr = option_v6.get();
|
||||
ASSERT_TRUE(optptr);
|
||||
ASSERT_TRUE(typeid(*optptr) == typeid(OptionCustom));
|
||||
|
||||
// Validate the value.
|
||||
OptionCustomPtr option_cast_v6 =
|
||||
boost::dynamic_pointer_cast<OptionCustom>(option_v6);
|
||||
ASSERT_EQ(1, option_cast_v6->getDataFieldsNum());
|
||||
PSIDTuple psid = option_cast_v6->readPsid();
|
||||
EXPECT_EQ(6, psid.first.asUnsigned());
|
||||
EXPECT_EQ(3, psid.second.asUint16());
|
||||
}
|
||||
|
||||
// This test verifies that a definition of an option with an array
|
||||
// of PSIDs can be created and that the instance of this option can be
|
||||
// created by specifying multiple PSIDs in the textual format.
|
||||
TEST_F(OptionDefinitionTest, psidArrayTokenized) {
|
||||
OptionDefinition opt_def("option-psid", 1000, "psid", true);
|
||||
|
||||
OptionPtr option_v6;
|
||||
|
||||
// Specify 3 PSIDs.
|
||||
std::vector<std::string> values;
|
||||
values.push_back("3 / 6");
|
||||
values.push_back("0/1");
|
||||
values.push_back("7 / 3");
|
||||
|
||||
// Create an instance of an option using the definition.
|
||||
ASSERT_NO_THROW(
|
||||
option_v6 = opt_def.optionFactory(Option::V6, 1000, values);
|
||||
);
|
||||
|
||||
// Make sure that the option class returned is correct.
|
||||
const Option* optptr = option_v6.get();
|
||||
ASSERT_TRUE(optptr);
|
||||
ASSERT_TRUE(typeid(*optptr) == typeid(OptionCustom));
|
||||
|
||||
OptionCustomPtr option_cast_v6 =
|
||||
boost::dynamic_pointer_cast<OptionCustom>(option_v6);
|
||||
|
||||
// There should be 3 PSIDs in this option.
|
||||
ASSERT_EQ(3, option_cast_v6->getDataFieldsNum());
|
||||
|
||||
// Check their values.
|
||||
PSIDTuple psid0;
|
||||
PSIDTuple psid1;
|
||||
PSIDTuple psid2;
|
||||
|
||||
psid0 = option_cast_v6->readPsid(0);
|
||||
EXPECT_EQ(6, psid0.first.asUnsigned());
|
||||
EXPECT_EQ(3, psid0.second.asUint16());
|
||||
|
||||
psid1 = option_cast_v6->readPsid(1);
|
||||
EXPECT_EQ(1, psid1.first.asUnsigned());
|
||||
EXPECT_EQ(0, psid1.second.asUint16());
|
||||
|
||||
psid2 = option_cast_v6->readPsid(2);
|
||||
EXPECT_EQ(3, psid2.first.asUnsigned());
|
||||
EXPECT_EQ(7, psid2.second.asUint16());
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <dhcp/libdhcp++.h>
|
||||
#include <dhcp/option.h>
|
||||
#include <dhcp/option_int.h>
|
||||
#include <dhcp/option_space.h>
|
||||
#include <exceptions/exceptions.h>
|
||||
#include <util/buffer.h>
|
||||
|
||||
@ -593,15 +594,14 @@ TEST_F(OptionTest, setEncapsulatedSpace) {
|
||||
Option optv6(Option::V6, 258);
|
||||
EXPECT_TRUE(optv6.getEncapsulatedSpace().empty());
|
||||
|
||||
optv6.setEncapsulatedSpace("dhcp6");
|
||||
EXPECT_EQ("dhcp6", optv6.getEncapsulatedSpace());
|
||||
optv6.setEncapsulatedSpace(DHCP6_OPTION_SPACE);
|
||||
EXPECT_EQ(DHCP6_OPTION_SPACE, optv6.getEncapsulatedSpace());
|
||||
|
||||
Option optv4(Option::V4, 125);
|
||||
EXPECT_TRUE(optv4.getEncapsulatedSpace().empty());
|
||||
|
||||
optv4.setEncapsulatedSpace("dhcp4");
|
||||
EXPECT_EQ("dhcp4", optv4.getEncapsulatedSpace());
|
||||
|
||||
optv4.setEncapsulatedSpace(DHCP4_OPTION_SPACE);
|
||||
EXPECT_EQ(DHCP4_OPTION_SPACE, optv4.getEncapsulatedSpace());
|
||||
}
|
||||
|
||||
// This test verifies that cloneInternal returns NULL pointer if
|
||||
|
@ -350,7 +350,7 @@ AllocEngine::ClientContext6::ClientContext6(const Subnet6Ptr& subnet,
|
||||
duid_(duid), hwaddr_(), host_identifiers_(), host_(),
|
||||
fwd_dns_update_(fwd_dns), rev_dns_update_(rev_dns),
|
||||
hostname_(hostname), callout_handle_(callout_handle),
|
||||
allocated_resources_(), ias_() {
|
||||
allocated_resources_(), ias_(), pd_exclude_requested_(false) {
|
||||
|
||||
// Initialize host identifiers.
|
||||
if (duid) {
|
||||
@ -688,10 +688,11 @@ AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) {
|
||||
// non-PD leases.
|
||||
uint8_t prefix_len = 128;
|
||||
if (ctx.currentIA().type_ == Lease::TYPE_PD) {
|
||||
Pool6Ptr pool = boost::dynamic_pointer_cast<Pool6>(
|
||||
pool = boost::dynamic_pointer_cast<Pool6>(
|
||||
ctx.subnet_->getPool(ctx.currentIA().type_, candidate, false));
|
||||
/// @todo: verify that the pool is non-null
|
||||
prefix_len = pool->getLength();
|
||||
if (pool) {
|
||||
prefix_len = pool->getLength();
|
||||
}
|
||||
}
|
||||
|
||||
Lease6Ptr existing = LeaseMgrFactory::instance().getLease6(ctx.currentIA().type_,
|
||||
|
@ -397,6 +397,10 @@ public:
|
||||
/// @brief Container holding IA specific contexts.
|
||||
std::vector<IAContext> ias_;
|
||||
|
||||
/// @brief Indicates if PD exclude option has been requested by a
|
||||
/// client.
|
||||
bool pd_exclude_requested_;
|
||||
|
||||
/// @brief Convenience method adding allocated prefix or address.
|
||||
///
|
||||
/// @param prefix Prefix or address.
|
||||
|
@ -105,19 +105,31 @@ CfgOption::encapsulateInternal(const std::string& option_space) {
|
||||
// from the option spaces they encapsulate.
|
||||
for (OptionContainer::const_iterator opt = options->begin();
|
||||
opt != options->end(); ++opt) {
|
||||
// Get encapsulated option space for the option.
|
||||
const std::string& encap_space = opt->option_->getEncapsulatedSpace();
|
||||
// Empty value means that no option space is encapsulated.
|
||||
if (!encap_space.empty()) {
|
||||
// Retrieve all options from the encapsulated option space.
|
||||
OptionContainerPtr encap_options = getAll(encap_space);
|
||||
for (OptionContainer::const_iterator encap_opt =
|
||||
encap_options->begin(); encap_opt != encap_options->end();
|
||||
++encap_opt) {
|
||||
// Add sub-option if there isn't one added already.
|
||||
if (!opt->option_->getOption(encap_opt->option_->getType())) {
|
||||
opt->option_->addOption(encap_opt->option_);
|
||||
}
|
||||
encapsulateInternal(opt->option_);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CfgOption::encapsulateInternal(const OptionPtr& option) {
|
||||
// Get encapsulated option space for the option.
|
||||
const std::string& encap_space = option->getEncapsulatedSpace();
|
||||
// Empty value means that no option space is encapsulated.
|
||||
if (!encap_space.empty()) {
|
||||
// Retrieve all options from the encapsulated option space.
|
||||
OptionContainerPtr encap_options = getAll(encap_space);
|
||||
for (OptionContainer::const_iterator encap_opt =
|
||||
encap_options->begin(); encap_opt != encap_options->end();
|
||||
++encap_opt) {
|
||||
// Add sub-option if there isn't one added already.
|
||||
if (!option->getOption(encap_opt->option_->getType())) {
|
||||
option->addOption(encap_opt->option_);
|
||||
}
|
||||
// This is a workaround for preventing infinite recursion when
|
||||
// trying to encapsulate options created with default global option
|
||||
// spaces.
|
||||
if (encap_space != DHCP4_OPTION_SPACE &&
|
||||
encap_space != DHCP6_OPTION_SPACE) {
|
||||
encapsulateInternal(encap_opt->option_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -409,6 +409,16 @@ private:
|
||||
/// which encapsulated options are appended.
|
||||
void encapsulateInternal(const std::string& option_space);
|
||||
|
||||
/// @brief Appends encapsulated options from the option space encapsulated
|
||||
/// by the specified option.
|
||||
///
|
||||
/// This method will go over all options belonging to the encapsulated space
|
||||
/// and will check which option spaces they encapsulate recursively,
|
||||
/// adding these options to the current option
|
||||
///
|
||||
/// @param option which encapsulated options.
|
||||
void encapsulateInternal(const OptionPtr& option);
|
||||
|
||||
/// @brief Merges data from two option containers.
|
||||
///
|
||||
/// This method merges options from one option container to another
|
||||
|
@ -89,12 +89,7 @@ CfgOptionDef::add(const OptionDefinitionPtr& def,
|
||||
" space '" << option_space << "'");
|
||||
|
||||
// Must not override standard option definition.
|
||||
} else if (((option_space == DHCP4_OPTION_SPACE) &&
|
||||
LibDHCP::isStandardOption(Option::V4, def->getCode()) &&
|
||||
LibDHCP::getOptionDef(Option::V4, def->getCode())) ||
|
||||
((option_space == DHCP6_OPTION_SPACE) &&
|
||||
LibDHCP::isStandardOption(Option::V6, def->getCode()) &&
|
||||
LibDHCP::getOptionDef(Option::V6, def->getCode()))) {
|
||||
} else if (LibDHCP::getOptionDef(option_space, def->getCode())) {
|
||||
isc_throw(BadValue, "unable to override definition of option '"
|
||||
<< def->getCode() << "' in standard option space '"
|
||||
<< option_space << "'");
|
||||
|
@ -840,13 +840,8 @@ private:
|
||||
// class, using option definition. Thus, we need to find the
|
||||
// option definition for this option code and option space.
|
||||
|
||||
// If the option space is a standard DHCPv4 or DHCPv6 option space,
|
||||
// this is most likely a standard option, for which we have a
|
||||
// definition created within libdhcp++.
|
||||
OptionDefinitionPtr def;
|
||||
if ((space == DHCP4_OPTION_SPACE) || (space == DHCP6_OPTION_SPACE)) {
|
||||
def = LibDHCP::getOptionDef(universe_, code_);
|
||||
}
|
||||
// Check if this is a standard option.
|
||||
OptionDefinitionPtr def = LibDHCP::getOptionDef(space, code_);
|
||||
|
||||
// Otherwise, we may check if this an option encapsulated within the
|
||||
// vendor space.
|
||||
|
@ -525,7 +525,8 @@ OptionDataParser::extractCSVFormat() const {
|
||||
|
||||
std::string
|
||||
OptionDataParser::extractSpace() const {
|
||||
std::string space = address_family_ == AF_INET ? "dhcp4" : "dhcp6";
|
||||
std::string space = address_family_ == AF_INET ?
|
||||
DHCP4_OPTION_SPACE : DHCP6_OPTION_SPACE;
|
||||
try {
|
||||
space = string_values_->getParam("space");
|
||||
|
||||
@ -565,21 +566,15 @@ template<typename SearchKey>
|
||||
OptionDefinitionPtr
|
||||
OptionDataParser::findOptionDefinition(const std::string& option_space,
|
||||
const SearchKey& search_key) const {
|
||||
const Option::Universe u = address_family_ == AF_INET ?
|
||||
Option::V4 : Option::V6;
|
||||
OptionDefinitionPtr def;
|
||||
|
||||
if ((option_space == DHCP4_OPTION_SPACE) ||
|
||||
(option_space == DHCP6_OPTION_SPACE)) {
|
||||
def = LibDHCP::getOptionDef(u, search_key);
|
||||
|
||||
}
|
||||
OptionDefinitionPtr def = LibDHCP::getOptionDef(option_space, search_key);
|
||||
|
||||
if (!def) {
|
||||
// Check if this is a vendor-option. If it is, get vendor-specific
|
||||
// definition.
|
||||
uint32_t vendor_id = LibDHCP::optionSpaceToVendorId(option_space);
|
||||
if (vendor_id) {
|
||||
const Option::Universe u = address_family_ == AF_INET ?
|
||||
Option::V4 : Option::V6;
|
||||
def = LibDHCP::getVendorOptionDef(u, vendor_id, search_key);
|
||||
}
|
||||
}
|
||||
@ -838,7 +833,8 @@ OptionDefParser::createOptionDef(ConstElementPtr option_def_element) {
|
||||
std::string record_types =
|
||||
string_values_->getOptionalParam("record-types", "");
|
||||
std::string space = string_values_->getOptionalParam("space",
|
||||
global_context_->universe_ == Option::V4 ? "dhcp4" : "dhcp6");
|
||||
global_context_->universe_ == Option::V4 ? DHCP4_OPTION_SPACE :
|
||||
DHCP6_OPTION_SPACE);
|
||||
std::string encapsulates =
|
||||
string_values_->getOptionalParam("encapsulate", "");
|
||||
|
||||
|
@ -76,7 +76,9 @@ Pool4::Pool4( const isc::asiolink::IOAddress& prefix, uint8_t prefix_len)
|
||||
|
||||
Pool6::Pool6(Lease::Type type, const isc::asiolink::IOAddress& first,
|
||||
const isc::asiolink::IOAddress& last)
|
||||
:Pool(type, first, last), prefix_len_(128) {
|
||||
: Pool(type, first, last), prefix_len_(128),
|
||||
excluded_prefix_(IOAddress::IPV6_ZERO_ADDRESS()),
|
||||
excluded_prefix_len_(0) {
|
||||
|
||||
// check if specified address boundaries are sane
|
||||
if (!first.isV6() || !last.isV6()) {
|
||||
@ -117,21 +119,91 @@ Pool6::Pool6(Lease::Type type, const isc::asiolink::IOAddress& first,
|
||||
}
|
||||
|
||||
Pool6::Pool6(Lease::Type type, const isc::asiolink::IOAddress& prefix,
|
||||
uint8_t prefix_len, uint8_t delegated_len /* = 128 */)
|
||||
:Pool(type, prefix, IOAddress("::")), prefix_len_(delegated_len) {
|
||||
const uint8_t prefix_len, const uint8_t delegated_len /* = 128 */)
|
||||
: Pool(type, prefix, IOAddress::IPV6_ZERO_ADDRESS()),
|
||||
prefix_len_(delegated_len),
|
||||
excluded_prefix_(IOAddress::IPV6_ZERO_ADDRESS()),
|
||||
excluded_prefix_len_(0) {
|
||||
|
||||
// check if the prefix is sane
|
||||
init(type, prefix, prefix_len, delegated_len,
|
||||
IOAddress::IPV6_ZERO_ADDRESS(), 0);
|
||||
}
|
||||
|
||||
Pool6::Pool6(const asiolink::IOAddress& prefix, const uint8_t prefix_len,
|
||||
const uint8_t delegated_len,
|
||||
const asiolink::IOAddress& excluded_prefix,
|
||||
const uint8_t excluded_prefix_len)
|
||||
: Pool(Lease::TYPE_PD, prefix, IOAddress::IPV6_ZERO_ADDRESS()),
|
||||
prefix_len_(delegated_len),
|
||||
excluded_prefix_(excluded_prefix),
|
||||
excluded_prefix_len_(excluded_prefix_len) {
|
||||
|
||||
init(Lease::TYPE_PD, prefix, prefix_len, delegated_len, excluded_prefix,
|
||||
excluded_prefix_len);
|
||||
|
||||
// The excluded prefix can only be specified using this constructor.
|
||||
// Therefore, the initialization of the excluded prefix is takes place
|
||||
// here, rather than in the init(...) function.
|
||||
if (!excluded_prefix_.isV6()) {
|
||||
isc_throw(BadValue, "excluded prefix must be an IPv6 prefix");
|
||||
}
|
||||
|
||||
// An "unspecified" prefix should have both value and length equal to 0.
|
||||
if ((excluded_prefix_.isV6Zero() && (excluded_prefix_len_ != 0)) ||
|
||||
(!excluded_prefix_.isV6Zero() && (excluded_prefix_len_ == 0))) {
|
||||
isc_throw(BadValue, "invalid excluded prefix "
|
||||
<< excluded_prefix_ << "/"
|
||||
<< static_cast<unsigned>(excluded_prefix_len_));
|
||||
}
|
||||
|
||||
// If excluded prefix has been specified.
|
||||
if (!excluded_prefix_.isV6Zero() && (excluded_prefix_len_ != 0)) {
|
||||
|
||||
// Excluded prefix length must not be greater than 128.
|
||||
if (excluded_prefix_len_ > 128) {
|
||||
isc_throw(BadValue, "excluded prefix length "
|
||||
<< static_cast<unsigned>(excluded_prefix_len_)
|
||||
<< " must not be greater than 128");
|
||||
}
|
||||
|
||||
// Excluded prefix must be a sub-prefix of a delegated prefix. First
|
||||
// check the prefix length as it is less involved.
|
||||
if (excluded_prefix_len_ <= prefix_len_) {
|
||||
isc_throw(BadValue, "excluded prefix length "
|
||||
<< static_cast<unsigned>(excluded_prefix_len_)
|
||||
<< " must be lower than the delegated prefix length "
|
||||
<< static_cast<unsigned>(prefix_len_));
|
||||
}
|
||||
|
||||
/// @todo Check that the prefixes actually match. Theoretically, a
|
||||
/// user could specify a prefix which sets insgnificant bits. We should
|
||||
/// clear insignificant bits based on the prefix length but this
|
||||
/// should be considered a part of the IOAddress class, perhaps and
|
||||
/// requires a bit of work (mainly in terms of testing).
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Pool6::init(const Lease::Type& type,
|
||||
const asiolink::IOAddress& prefix,
|
||||
const uint8_t prefix_len,
|
||||
const uint8_t delegated_len,
|
||||
const asiolink::IOAddress& /*excluded_prefix*/,
|
||||
const uint8_t excluded_prefix_len) {
|
||||
// Check if the prefix is sane
|
||||
if (!prefix.isV6()) {
|
||||
isc_throw(BadValue, "Invalid Pool6 address boundaries: not IPv6");
|
||||
}
|
||||
|
||||
// check if the prefix length is sane
|
||||
// Check if the prefix length is sane
|
||||
if (prefix_len == 0 || prefix_len > 128) {
|
||||
isc_throw(BadValue, "Invalid prefix length: " << static_cast<unsigned>(prefix_len));
|
||||
isc_throw(BadValue, "Invalid prefix length: "
|
||||
<< static_cast<unsigned>(prefix_len));
|
||||
}
|
||||
|
||||
if (prefix_len > delegated_len) {
|
||||
isc_throw(BadValue, "Delegated length (" << static_cast<int>(delegated_len)
|
||||
isc_throw(BadValue, "Delegated length ("
|
||||
<< static_cast<int>(delegated_len)
|
||||
<< ") must be longer than or equal to prefix length ("
|
||||
<< static_cast<int>(prefix_len) << ")");
|
||||
}
|
||||
@ -142,6 +214,13 @@ Pool6::Pool6(Lease::Type type, const isc::asiolink::IOAddress& prefix,
|
||||
<< " be 128.");
|
||||
}
|
||||
|
||||
// excluded_prefix_len == 0 means there's no excluded prefix at all.
|
||||
if (excluded_prefix_len && (excluded_prefix_len < delegated_len)) {
|
||||
isc_throw(BadValue, "Excluded prefix (" << static_cast<int>(excluded_prefix_len)
|
||||
<< ") must be longer than the delegated prefix length ("
|
||||
<< static_cast<int>(delegated_len));
|
||||
}
|
||||
|
||||
/// @todo: We should probably implement checks against weird addresses
|
||||
/// here, like ::, starting with fe80, starting with ff etc. .
|
||||
|
||||
@ -156,11 +235,17 @@ Pool6::Pool6(Lease::Type type, const isc::asiolink::IOAddress& prefix,
|
||||
|
||||
std::string
|
||||
Pool6::toText() const {
|
||||
std::stringstream tmp;
|
||||
tmp << "type=" << Lease::typeToText(type_) << ", " << first_
|
||||
<< "-" << last_ << ", delegated_len="
|
||||
<< static_cast<int>(prefix_len_);
|
||||
return (tmp.str());
|
||||
std::ostringstream s;
|
||||
s << "type=" << Lease::typeToText(type_) << ", " << first_
|
||||
<< "-" << last_ << ", delegated_len="
|
||||
<< static_cast<int>(prefix_len_);
|
||||
|
||||
if (excluded_prefix_len_ > 0) {
|
||||
s << ", excluded_prefix=" << excluded_prefix_
|
||||
<< ", excluded_prefix_len="
|
||||
<< static_cast<unsigned>(excluded_prefix_len_);
|
||||
}
|
||||
return (s.str());
|
||||
}
|
||||
|
||||
}; // end of isc::dhcp namespace
|
||||
|
@ -213,10 +213,27 @@ public:
|
||||
/// @param type type of the pool (IA, TA or PD)
|
||||
/// @param prefix specifies prefix of the pool
|
||||
/// @param prefix_len specifies prefix length of the pool
|
||||
/// @param delegated_len specifies lenght of the delegated prefixes
|
||||
/// @param delegated_len specifies length of the delegated prefixes
|
||||
Pool6(Lease::Type type, const isc::asiolink::IOAddress& prefix,
|
||||
uint8_t prefix_len, uint8_t delegated_len = 128);
|
||||
|
||||
/// @brief Constructor for DHCPv6 prefix pool with an excluded prefix.
|
||||
///
|
||||
/// If @c excluded_prefix is equal to '::' and the @c excluded_prefix_len
|
||||
/// is equal to 0, the excluded prefix is assumed to be unspecified for
|
||||
/// the pool. In this case, the server will not send the Prefix Exclude
|
||||
/// option to a client.
|
||||
///
|
||||
/// @param prefix specified a prefix of the pool.
|
||||
/// @param prefix_Leb specifies prefix length of the pool.
|
||||
/// @param delegated_len specifies length of the delegated prefixes.
|
||||
/// @param excluded_prefix specifies an excluded prefix as per RFC6603.
|
||||
/// @param excluded_prefix_len specifies length of an excluded prefix.
|
||||
Pool6(const asiolink::IOAddress& prefix, const uint8_t prefix_len,
|
||||
const uint8_t delegated_len,
|
||||
const asiolink::IOAddress& excluded_prefix,
|
||||
const uint8_t excluded_prefix_len);
|
||||
|
||||
/// @brief returns pool type
|
||||
///
|
||||
/// @return pool type
|
||||
@ -233,14 +250,75 @@ public:
|
||||
return (prefix_len_);
|
||||
}
|
||||
|
||||
/// @brief Returns an excluded prefix.
|
||||
///
|
||||
/// An excluded prefix can be specified for a prefix pool as specified
|
||||
/// in RFC6603.
|
||||
///
|
||||
/// @return Reference to an IOAddress object representing an excluded
|
||||
/// prefix pool. A value of '::' if the excluded prefix is not specified.
|
||||
const isc::asiolink::IOAddress& getExcludedPrefix() const{
|
||||
return (excluded_prefix_);
|
||||
}
|
||||
|
||||
/// @brief Returns an excluded prefix length.
|
||||
///
|
||||
/// An excluded prefix can be specified for a prefix pool as specified
|
||||
/// in RFC6603.
|
||||
///
|
||||
/// @return Excluded prefix length in the range of 2 to 128, if the
|
||||
/// excluded prefix is specified. A value of 0 if the excluded prefix
|
||||
/// is not specified.
|
||||
uint8_t getExcludedPrefixLength() const{
|
||||
return (excluded_prefix_len_);
|
||||
}
|
||||
|
||||
/// @brief returns textual representation of the pool
|
||||
///
|
||||
/// @return textual representation
|
||||
virtual std::string toText() const;
|
||||
|
||||
private:
|
||||
|
||||
/// @brief Generic method initializing a DHCPv6 pool.
|
||||
///
|
||||
/// This method should be called by the constructors to initialize
|
||||
/// DHCPv6 pools.
|
||||
///
|
||||
/// @param Lease/pool type.
|
||||
/// @param prefix An address or delegated prefix (depending on the
|
||||
/// pool type specified as @c type).
|
||||
/// @param prefix_len Prefix length. If a pool is an address pool,
|
||||
/// this value should be set to 128.
|
||||
/// @param delegated_len Length of the delegated prefixes. If a pool
|
||||
/// is an address pool, this value should be set to 128.
|
||||
/// @param excluded_prefix An excluded prefix as per RFC6603. This
|
||||
/// value should only be specified for prefix pools. The value of
|
||||
/// '::' means "unspecified".
|
||||
/// @param excluded_prefix_len Length of the excluded prefix. This
|
||||
/// is only specified for prefix pools. The value of 0 should be
|
||||
/// used when @c excluded_prefix is not specified.
|
||||
void init(const Lease::Type& type,
|
||||
const asiolink::IOAddress& prefix,
|
||||
const uint8_t prefix_len,
|
||||
const uint8_t delegated_len,
|
||||
const asiolink::IOAddress& excluded_prefix,
|
||||
const uint8_t excluded_prefix_len);
|
||||
|
||||
/// @brief Defines prefix length (for TYPE_PD only)
|
||||
uint8_t prefix_len_;
|
||||
|
||||
/// @brief The excluded prefix (RFC6603).
|
||||
///
|
||||
/// This prefix can only be specified for DHCPv6 prefix pools.
|
||||
/// An "unspecified" prefix has a value of '::'.
|
||||
isc::asiolink::IOAddress excluded_prefix_;
|
||||
|
||||
/// @brief The excluded prefix length (RFC6603).
|
||||
///
|
||||
/// This value can only be specified for DHCPv6 prefix pool.
|
||||
/// An "unspecified" prefix has a length of 0.
|
||||
uint8_t excluded_prefix_len_;
|
||||
};
|
||||
|
||||
/// @brief a pointer an IPv6 Pool
|
||||
|
@ -182,7 +182,7 @@ TEST(CfgOptionDefTest, get) {
|
||||
// Check that an option definition can be added to the standard
|
||||
// (dhcp4 and dhcp6) option spaces when the option code is not
|
||||
// reserved by the standard option.
|
||||
OptionDefinitionPtr def6(new OptionDefinition("option-foo", 79, "uint16"));
|
||||
OptionDefinitionPtr def6(new OptionDefinition("option-foo", 1000, "uint16"));
|
||||
EXPECT_NO_THROW(cfg.add(def6, DHCP6_OPTION_SPACE));
|
||||
|
||||
OptionDefinitionPtr def4(new OptionDefinition("option-foo", 222, "uint16"));
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <dhcp/option_int.h>
|
||||
#include <dhcp/option_space.h>
|
||||
#include <dhcpsrv/cfg_option.h>
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/pointer_cast.hpp>
|
||||
#include <gtest/gtest.h>
|
||||
#include <iterator>
|
||||
@ -22,8 +23,73 @@ using namespace isc::dhcp;
|
||||
|
||||
namespace {
|
||||
|
||||
/// This class fixture for testing @c CfgOption class, holding option
|
||||
/// configuration.
|
||||
class CfgOptionTest : public ::testing::Test {
|
||||
public:
|
||||
|
||||
/// @brief Generates encapsulated options and adds them to CfgOption
|
||||
///
|
||||
/// This method generates the following options:
|
||||
/// - 1000-1019 options: uint16 with value 1234, encapsulate "foo"
|
||||
/// - 1-19 options: uint8 with value 1, encapsulate "foo-subs"
|
||||
/// - 1-9 options: uint8 with value 3
|
||||
/// - 1020-1039 options: uint16 with value 2345, encapsulate "bar"
|
||||
/// - 100-119 options: uint8 with value 2, encapsulate "bar-subs"
|
||||
/// - 501-509 options: uint8 with value 4
|
||||
void generateEncapsulatedOptions(CfgOption& cfg) {
|
||||
// Create top-level options encapsulating "foo" option space.
|
||||
for (uint16_t code = 1000; code < 1020; ++code) {
|
||||
OptionUint16Ptr option = OptionUint16Ptr(new OptionUint16(Option::V6,
|
||||
code, 1234));
|
||||
option->setEncapsulatedSpace("foo");
|
||||
ASSERT_NO_THROW(cfg.add(option, false, DHCP6_OPTION_SPACE));
|
||||
}
|
||||
|
||||
// Create top level options encapsulating "bar" option space.
|
||||
for (uint16_t code = 1020; code < 1040; ++code) {
|
||||
OptionUint16Ptr option = OptionUint16Ptr(new OptionUint16(Option::V6,
|
||||
code, 2345));
|
||||
option->setEncapsulatedSpace("bar");
|
||||
ASSERT_NO_THROW(cfg.add(option, false, DHCP6_OPTION_SPACE));
|
||||
}
|
||||
|
||||
// Create sub-options belonging to "foo" option space and encapsulating
|
||||
// foo-subs option space.
|
||||
for (uint16_t code = 1; code < 20; ++code) {
|
||||
OptionUint8Ptr option = OptionUint8Ptr(new OptionUint8(Option::V6, code,
|
||||
0x01));
|
||||
option->setEncapsulatedSpace("foo-subs");
|
||||
ASSERT_NO_THROW(cfg.add(option, false, "foo"));
|
||||
}
|
||||
|
||||
// Create sub-options belonging to "bar" option space and encapsulating
|
||||
// bar-subs option space.
|
||||
for (uint16_t code = 100; code < 119; ++code) {
|
||||
OptionUint8Ptr option = OptionUint8Ptr(new OptionUint8(Option::V6,
|
||||
code, 0x02));
|
||||
option->setEncapsulatedSpace("bar-subs");
|
||||
ASSERT_NO_THROW(cfg.add(option, false, "bar"));
|
||||
}
|
||||
|
||||
// Create sub-options belonging to "foo-subs" option space.
|
||||
for (uint16_t code = 1; code < 10; ++code) {
|
||||
OptionUint8Ptr option = OptionUint8Ptr(new OptionUint8(Option::V6, code,
|
||||
0x03));
|
||||
ASSERT_NO_THROW(cfg.add(option, false, "foo-subs"));
|
||||
}
|
||||
|
||||
// Create sub-options belonging to "bar-subs" option space.
|
||||
for (uint16_t code = 501; code < 510; ++code) {
|
||||
OptionUint8Ptr option = OptionUint8Ptr(new OptionUint8(Option::V6,
|
||||
code, 0x04));
|
||||
ASSERT_NO_THROW(cfg.add(option, false, "bar-subs"));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// This test verifies the empty predicate.
|
||||
TEST(CfgOptionTest, empty) {
|
||||
TEST_F(CfgOptionTest, empty) {
|
||||
CfgOption cfg1;
|
||||
CfgOption cfg2;
|
||||
|
||||
@ -33,7 +99,7 @@ TEST(CfgOptionTest, empty) {
|
||||
|
||||
// Add an option to each configuration
|
||||
OptionPtr option(new Option(Option::V6, 1));
|
||||
ASSERT_NO_THROW(cfg1.add(option, false, "dhcp6"));
|
||||
ASSERT_NO_THROW(cfg1.add(option, false, DHCP6_OPTION_SPACE));
|
||||
ASSERT_NO_THROW(cfg2.add(option, true, "isc"));
|
||||
|
||||
// The first option configuration has an option
|
||||
@ -44,7 +110,7 @@ TEST(CfgOptionTest, empty) {
|
||||
}
|
||||
|
||||
// This test verifies that the option configurations can be compared.
|
||||
TEST(CfgOptionTest, equals) {
|
||||
TEST_F(CfgOptionTest, equals) {
|
||||
CfgOption cfg1;
|
||||
CfgOption cfg2;
|
||||
|
||||
@ -97,13 +163,13 @@ TEST(CfgOptionTest, equals) {
|
||||
|
||||
// This test verifies that multiple options can be added to the configuration
|
||||
// and that they can be retrieved using the option space name.
|
||||
TEST(CfgOptionTest, add) {
|
||||
TEST_F(CfgOptionTest, add) {
|
||||
CfgOption cfg;
|
||||
|
||||
// Differentiate options by their codes (100-109)
|
||||
for (uint16_t code = 100; code < 110; ++code) {
|
||||
OptionPtr option(new Option(Option::V6, code, OptionBuffer(10, 0xFF)));
|
||||
ASSERT_NO_THROW(cfg.add(option, false, "dhcp6"));
|
||||
ASSERT_NO_THROW(cfg.add(option, false, DHCP6_OPTION_SPACE));
|
||||
}
|
||||
|
||||
// Add 7 options to another option space. The option codes partially overlap
|
||||
@ -114,7 +180,7 @@ TEST(CfgOptionTest, add) {
|
||||
}
|
||||
|
||||
// Get options from the Subnet and check if all 10 are there.
|
||||
OptionContainerPtr options = cfg.getAll("dhcp6");
|
||||
OptionContainerPtr options = cfg.getAll(DHCP6_OPTION_SPACE);
|
||||
ASSERT_TRUE(options);
|
||||
ASSERT_EQ(10, options->size());
|
||||
|
||||
@ -147,7 +213,7 @@ TEST(CfgOptionTest, add) {
|
||||
}
|
||||
|
||||
// This test verifies that two option configurations can be merged.
|
||||
TEST(CfgOptionTest, merge) {
|
||||
TEST_F(CfgOptionTest, merge) {
|
||||
CfgOption cfg_src;
|
||||
CfgOption cfg_dst;
|
||||
|
||||
@ -155,7 +221,7 @@ TEST(CfgOptionTest, merge) {
|
||||
// from the range of 100 to 109 and holding one byte of data equal to 0xFF.
|
||||
for (uint16_t code = 100; code < 110; ++code) {
|
||||
OptionPtr option(new Option(Option::V6, code, OptionBuffer(1, 0xFF)));
|
||||
ASSERT_NO_THROW(cfg_src.add(option, false, "dhcp6"));
|
||||
ASSERT_NO_THROW(cfg_src.add(option, false, DHCP6_OPTION_SPACE));
|
||||
}
|
||||
|
||||
// Create collection of options in vendor space 123, with option codes
|
||||
@ -172,7 +238,7 @@ TEST(CfgOptionTest, merge) {
|
||||
// 100 to 108.
|
||||
for (uint16_t code = 100; code < 110; code += 2) {
|
||||
OptionPtr option(new Option(Option::V6, code, OptionBuffer(1, 0x01)));
|
||||
ASSERT_NO_THROW(cfg_dst.add(option, false, "dhcp6"));
|
||||
ASSERT_NO_THROW(cfg_dst.add(option, false, DHCP6_OPTION_SPACE));
|
||||
}
|
||||
|
||||
// Create collection of options having odd option codes in the range of
|
||||
@ -189,7 +255,7 @@ TEST(CfgOptionTest, merge) {
|
||||
|
||||
// Validate the options in the dhcp6 option space in the destination.
|
||||
for (uint16_t code = 100; code < 110; ++code) {
|
||||
OptionDescriptor desc = cfg_dst.get("dhcp6", code);
|
||||
OptionDescriptor desc = cfg_dst.get(DHCP6_OPTION_SPACE, code);
|
||||
ASSERT_TRUE(desc.option_);
|
||||
ASSERT_EQ(1, desc.option_->getData().size());
|
||||
// The options with even option codes should hold one byte of data
|
||||
@ -222,7 +288,7 @@ TEST(CfgOptionTest, merge) {
|
||||
|
||||
// This test verifies that the options configuration can be copied between
|
||||
// objects.
|
||||
TEST(CfgOptionTest, copy) {
|
||||
TEST_F(CfgOptionTest, copy) {
|
||||
CfgOption cfg_src;
|
||||
// Add 10 options to the custom option space in the source configuration.
|
||||
for (uint16_t code = 100; code < 110; ++code) {
|
||||
@ -262,60 +328,71 @@ TEST(CfgOptionTest, copy) {
|
||||
|
||||
// This test verifies that encapsulated options are added as sub-options
|
||||
// to the top level options on request.
|
||||
TEST(CfgOptionTest, encapsulate) {
|
||||
TEST_F(CfgOptionTest, encapsulate) {
|
||||
CfgOption cfg;
|
||||
// Create top-level options encapsulating "foo" option space.
|
||||
for (uint16_t code = 1000; code < 1020; ++code) {
|
||||
OptionUint16Ptr option = OptionUint16Ptr(new OptionUint16(Option::V6,
|
||||
code, 1234));
|
||||
option->setEncapsulatedSpace("foo");
|
||||
ASSERT_NO_THROW(cfg.add(option, false, DHCP6_OPTION_SPACE));
|
||||
}
|
||||
|
||||
// Create top level options encapsulating "bar" option space.
|
||||
for (uint16_t code = 1020; code < 1040; ++code) {
|
||||
OptionUint16Ptr option = OptionUint16Ptr(new OptionUint16(Option::V6,
|
||||
code, 2345));
|
||||
option->setEncapsulatedSpace("bar");
|
||||
ASSERT_NO_THROW(cfg.add(option, false, DHCP6_OPTION_SPACE));
|
||||
}
|
||||
generateEncapsulatedOptions(cfg);
|
||||
|
||||
// Create sub-options belonging to "foo" option space.
|
||||
for (uint16_t code = 1; code < 20; ++code) {
|
||||
OptionUint8Ptr option = OptionUint8Ptr(new OptionUint8(Option::V6, code,
|
||||
0x01));
|
||||
ASSERT_NO_THROW(cfg.add(option, false, "foo"));
|
||||
}
|
||||
|
||||
// Create sub-options belonging to "bar" option space.
|
||||
for (uint16_t code = 100; code < 130; ++code) {
|
||||
OptionUint8Ptr option = OptionUint8Ptr(new OptionUint8(Option::V6,
|
||||
code, 0x02));
|
||||
ASSERT_NO_THROW(cfg.add(option, false, "bar"));
|
||||
}
|
||||
|
||||
// Append options from "foo" and "bar" space as sub-options.
|
||||
// Append options from "foo" and "bar" space as sub-options and options
|
||||
// from "foo-subs" and "bar-subs" as sub-options of "foo" and "bar"
|
||||
// options.
|
||||
ASSERT_NO_THROW(cfg.encapsulate());
|
||||
|
||||
// Verify that we have 40 top-level options.
|
||||
OptionContainerPtr options = cfg.getAll(DHCP6_OPTION_SPACE);
|
||||
ASSERT_EQ(40, options->size());
|
||||
|
||||
// Iterate over top level options.
|
||||
for (uint16_t code = 1000; code < 1040; ++code) {
|
||||
|
||||
OptionUint16Ptr option = boost::dynamic_pointer_cast<
|
||||
OptionUint16>(cfg.get(DHCP6_OPTION_SPACE, code).option_);
|
||||
ASSERT_TRUE(option) << "option with code " << code << " not found";
|
||||
const OptionCollection& suboptions = option->getOptions();
|
||||
for (OptionCollection::const_iterator suboption =
|
||||
suboptions.begin(); suboption != suboptions.end();
|
||||
++suboption) {
|
||||
OptionUint8Ptr opt = boost::dynamic_pointer_cast<
|
||||
OptionUint8>(suboption->second);
|
||||
ASSERT_TRUE(opt);
|
||||
if (code < 1020) {
|
||||
EXPECT_EQ(0x01, opt->getValue());
|
||||
|
||||
// First level sub options. There are 19 sub-options for each top
|
||||
// level option.
|
||||
const OptionCollection& first_level = option->getOptions();
|
||||
ASSERT_EQ(19, first_level.size());
|
||||
|
||||
// Iterate over all first level sub-options.
|
||||
std::pair<unsigned int, OptionPtr> first_level_opt;
|
||||
BOOST_FOREACH(first_level_opt, first_level) {
|
||||
// Each option in this test comprises a single one byte field and
|
||||
// should cast to OptionUint8 type.
|
||||
OptionUint8Ptr first_level_uint8 = boost::dynamic_pointer_cast<
|
||||
OptionUint8>(first_level_opt.second);
|
||||
ASSERT_TRUE(first_level_uint8);
|
||||
|
||||
const unsigned int value = static_cast<unsigned int>(first_level_uint8->getValue());
|
||||
// There are two sets of first level sub-options. Those that include
|
||||
// a value of 1 and those that include a value of 2.
|
||||
if (first_level_uint8->getType() < 20) {
|
||||
EXPECT_EQ(1, value);
|
||||
} else {
|
||||
EXPECT_EQ(0x02, opt->getValue());
|
||||
EXPECT_EQ(2, value);
|
||||
}
|
||||
|
||||
// Each first level sub-option should include 9 second level
|
||||
// sub options.
|
||||
const OptionCollection& second_level = first_level_uint8->getOptions();
|
||||
ASSERT_EQ(9, second_level.size());
|
||||
|
||||
// Iterate over sub-options and make sure they include the expected
|
||||
// values.
|
||||
std::pair<unsigned int, OptionPtr> second_level_opt;
|
||||
BOOST_FOREACH(second_level_opt, second_level) {
|
||||
OptionUint8Ptr second_level_uint8 = boost::dynamic_pointer_cast<
|
||||
OptionUint8>(second_level_opt.second);
|
||||
ASSERT_TRUE(second_level_uint8);
|
||||
const unsigned value = static_cast<
|
||||
unsigned>(second_level_uint8->getValue());
|
||||
// Certain sub-options should have a value of 3, other the values
|
||||
// of 4.
|
||||
if (second_level_uint8->getType() < 20) {
|
||||
EXPECT_EQ(3, value);
|
||||
} else {
|
||||
EXPECT_EQ(4, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -323,13 +400,13 @@ TEST(CfgOptionTest, encapsulate) {
|
||||
|
||||
// This test verifies that single option can be retrieved from the configuration
|
||||
// using option code and option space.
|
||||
TEST(CfgOptionTest, get) {
|
||||
TEST_F(CfgOptionTest, get) {
|
||||
CfgOption cfg;
|
||||
|
||||
// Add 10 options to a "dhcp6" option space in the subnet.
|
||||
for (uint16_t code = 100; code < 110; ++code) {
|
||||
OptionPtr option(new Option(Option::V6, code, OptionBuffer(10, 0xFF)));
|
||||
ASSERT_NO_THROW(cfg.add(option, false, "dhcp6"));
|
||||
ASSERT_NO_THROW(cfg.add(option, false, DHCP6_OPTION_SPACE));
|
||||
}
|
||||
|
||||
// Check that we can get each added option descriptor using
|
||||
@ -341,7 +418,7 @@ TEST(CfgOptionTest, get) {
|
||||
// Returned descriptor should contain NULL option ptr.
|
||||
EXPECT_FALSE(desc.option_);
|
||||
// Now, try the valid option space.
|
||||
desc = cfg.get("dhcp6", code);
|
||||
desc = cfg.get(DHCP6_OPTION_SPACE, code);
|
||||
// Test that the option code matches the expected code.
|
||||
ASSERT_TRUE(desc.option_);
|
||||
EXPECT_EQ(code, desc.option_->getType());
|
||||
@ -350,7 +427,7 @@ TEST(CfgOptionTest, get) {
|
||||
|
||||
// This test verifies that the same options can be added to the configuration
|
||||
// under different option space.
|
||||
TEST(CfgOptionTest, addNonUniqueOptions) {
|
||||
TEST_F(CfgOptionTest, addNonUniqueOptions) {
|
||||
CfgOption cfg;
|
||||
|
||||
// Create a set of options with non-unique codes.
|
||||
@ -358,12 +435,12 @@ TEST(CfgOptionTest, addNonUniqueOptions) {
|
||||
// In the inner loop we create options with unique codes (100-109).
|
||||
for (uint16_t code = 100; code < 110; ++code) {
|
||||
OptionPtr option(new Option(Option::V6, code, OptionBuffer(10, 0xFF)));
|
||||
ASSERT_NO_THROW(cfg.add(option, false, "dhcp6"));
|
||||
ASSERT_NO_THROW(cfg.add(option, false, DHCP6_OPTION_SPACE));
|
||||
}
|
||||
}
|
||||
|
||||
// Sanity check that all options are there.
|
||||
OptionContainerPtr options = cfg.getAll("dhcp6");
|
||||
OptionContainerPtr options = cfg.getAll(DHCP6_OPTION_SPACE);
|
||||
ASSERT_EQ(20, options->size());
|
||||
|
||||
// Use container index #1 to get the options by their codes.
|
||||
@ -412,11 +489,11 @@ TEST(Subnet6Test, addPersistentOption) {
|
||||
// and options with these codes will be flagged non-persistent.
|
||||
// Options with other codes will be flagged persistent.
|
||||
bool persistent = (code % 3) ? true : false;
|
||||
ASSERT_NO_THROW(cfg.add(option, persistent, "dhcp6"));
|
||||
ASSERT_NO_THROW(cfg.add(option, persistent, DHCP6_OPTION_SPACE));
|
||||
}
|
||||
|
||||
// Get added options from the subnet.
|
||||
OptionContainerPtr options = cfg.getAll("dhcp6");
|
||||
OptionContainerPtr options = cfg.getAll(DHCP6_OPTION_SPACE);
|
||||
|
||||
// options->get<2> returns reference to container index #2. This
|
||||
// index is used to access options by the 'persistent' flag.
|
||||
@ -438,7 +515,7 @@ TEST(Subnet6Test, addPersistentOption) {
|
||||
}
|
||||
|
||||
// This test verifies that the vendor option can be added to the configuration.
|
||||
TEST(CfgOptionTest, addVendorOptions) {
|
||||
TEST_F(CfgOptionTest, addVendorOptions) {
|
||||
CfgOption cfg;
|
||||
|
||||
// Differentiate options by their codes (100-109)
|
||||
@ -494,7 +571,7 @@ TEST(CfgOptionTest, addVendorOptions) {
|
||||
|
||||
// This test verifies that option space names for the vendor options are
|
||||
// correct.
|
||||
TEST(CfgOptionTest, getVendorIdsSpaceNames) {
|
||||
TEST_F(CfgOptionTest, getVendorIdsSpaceNames) {
|
||||
CfgOption cfg;
|
||||
|
||||
// Create 10 options, each goes under a different vendor id.
|
||||
|
@ -283,7 +283,7 @@ TEST_F(ClientClassDefParserTest, nameOnlyValid) {
|
||||
cfg_option = cclass->getCfgOption();
|
||||
ASSERT_TRUE(cfg_option);
|
||||
OptionContainerPtr oc;
|
||||
ASSERT_TRUE(oc = cclass->getCfgOption()->getAll("dhcp4"));
|
||||
ASSERT_TRUE(oc = cclass->getCfgOption()->getAll(DHCP4_OPTION_SPACE));
|
||||
EXPECT_EQ(0, oc->size());
|
||||
|
||||
// Verify we have no expression.
|
||||
@ -314,7 +314,7 @@ TEST_F(ClientClassDefParserTest, nameAndExpressionClass) {
|
||||
cfg_option = cclass->getCfgOption();
|
||||
ASSERT_TRUE(cfg_option);
|
||||
OptionContainerPtr oc;
|
||||
ASSERT_TRUE(oc = cclass->getCfgOption()->getAll("dhcp4"));
|
||||
ASSERT_TRUE(oc = cclass->getCfgOption()->getAll(DHCP4_OPTION_SPACE));
|
||||
EXPECT_EQ(0, oc->size());
|
||||
|
||||
// Verify we can retrieve the expression
|
||||
@ -357,7 +357,7 @@ TEST_F(ClientClassDefParserTest, nameAndOptionsClass) {
|
||||
EXPECT_EQ("MICROSOFT", cclass->getName());
|
||||
|
||||
// Our one option should exist.
|
||||
OptionDescriptor od = cclass->getCfgOption()->get("dhcp4", 6);
|
||||
OptionDescriptor od = cclass->getCfgOption()->get(DHCP4_OPTION_SPACE, 6);
|
||||
ASSERT_TRUE(od.option_);
|
||||
EXPECT_EQ(6, od.option_->getType());
|
||||
|
||||
@ -393,7 +393,7 @@ TEST_F(ClientClassDefParserTest, basicValidClass) {
|
||||
EXPECT_EQ("MICROSOFT", cclass->getName());
|
||||
|
||||
// Our one option should exist.
|
||||
OptionDescriptor od = cclass->getCfgOption()->get("dhcp4", 6);
|
||||
OptionDescriptor od = cclass->getCfgOption()->get(DHCP4_OPTION_SPACE, 6);
|
||||
ASSERT_TRUE(od.option_);
|
||||
EXPECT_EQ(6, od.option_->getType());
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include <config.h>
|
||||
#include <dhcpsrv/client_class_def.h>
|
||||
#include <dhcp/option_space.h>
|
||||
#include <exceptions/exceptions.h>
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <asiolink/io_address.h>
|
||||
@ -69,13 +70,13 @@ TEST(ClientClassDef, cfgOptionBasics) {
|
||||
OptionPtr option;
|
||||
test_options.reset(new CfgOption());
|
||||
option.reset(new Option(Option::V4, 17, OptionBuffer(10, 0xFF)));
|
||||
ASSERT_NO_THROW(test_options->add(option, false, "dhcp4"));
|
||||
ASSERT_NO_THROW(test_options->add(option, false, DHCP4_OPTION_SPACE));
|
||||
|
||||
option.reset(new Option(Option::V6, 101, OptionBuffer(10, 0xFF)));
|
||||
ASSERT_NO_THROW(test_options->add(option, false, "isc"));
|
||||
|
||||
option.reset(new Option(Option::V6, 100, OptionBuffer(10, 0xFF)));
|
||||
ASSERT_NO_THROW(test_options->add(option, false, "dhcp6"));
|
||||
ASSERT_NO_THROW(test_options->add(option, false, DHCP6_OPTION_SPACE));
|
||||
|
||||
// Now remake the client class with cfg_option
|
||||
ASSERT_NO_THROW(cclass.reset(new ClientClassDef(name, expr, test_options)));
|
||||
@ -83,7 +84,7 @@ TEST(ClientClassDef, cfgOptionBasics) {
|
||||
ASSERT_TRUE(class_options);
|
||||
|
||||
// Now make sure we can find all the options
|
||||
OptionDescriptor opt_desc = class_options->get("dhcp4",17);
|
||||
OptionDescriptor opt_desc = class_options->get(DHCP4_OPTION_SPACE,17);
|
||||
ASSERT_TRUE(opt_desc.option_);
|
||||
EXPECT_EQ(17, opt_desc.option_->getType());
|
||||
|
||||
@ -91,7 +92,7 @@ TEST(ClientClassDef, cfgOptionBasics) {
|
||||
ASSERT_TRUE(opt_desc.option_);
|
||||
EXPECT_EQ(101, opt_desc.option_->getType());
|
||||
|
||||
opt_desc = class_options->get("dhcp6",100);
|
||||
opt_desc = class_options->get(DHCP6_OPTION_SPACE,100);
|
||||
ASSERT_TRUE(opt_desc.option_);
|
||||
EXPECT_EQ(100, opt_desc.option_->getType());
|
||||
}
|
||||
@ -113,7 +114,7 @@ TEST(ClientClassDef, copyAndEquality) {
|
||||
OptionPtr option;
|
||||
test_options.reset(new CfgOption());
|
||||
option.reset(new Option(Option::V4, 17, OptionBuffer(10, 0xFF)));
|
||||
ASSERT_NO_THROW(test_options->add(option, false, "dhcp4"));
|
||||
ASSERT_NO_THROW(test_options->add(option, false, DHCP4_OPTION_SPACE));
|
||||
|
||||
// Now remake the client class with cfg_option
|
||||
ASSERT_NO_THROW(cclass.reset(new ClientClassDef("class_one", expr,
|
||||
@ -176,7 +177,7 @@ TEST(ClientClassDef, copyAndEquality) {
|
||||
// Make a class that with same name and expression, but different options
|
||||
// verify that the equality tools reflect that the classes are not equal.
|
||||
option.reset(new Option(Option::V4, 20, OptionBuffer(10, 0xFF)));
|
||||
ASSERT_NO_THROW(test_options->add(option, false, "dhcp4"));
|
||||
ASSERT_NO_THROW(test_options->add(option, false, DHCP4_OPTION_SPACE));
|
||||
ASSERT_NO_THROW(cclass2.reset(new ClientClassDef("class_one", expr,
|
||||
test_options)));
|
||||
EXPECT_FALSE(cclass->equals(*cclass2));
|
||||
|
@ -31,6 +31,7 @@
|
||||
|
||||
using namespace std;
|
||||
using namespace isc;
|
||||
using namespace isc::asiolink;
|
||||
using namespace isc::config;
|
||||
using namespace isc::data;
|
||||
using namespace isc::dhcp;
|
||||
@ -571,7 +572,7 @@ TEST_F(ParseConfigTest, defaultSpaceOptionDefTest) {
|
||||
|
||||
// Verify that the option definition can be retrieved.
|
||||
OptionDefinitionPtr def =
|
||||
CfgMgr::instance().getStagingCfg()->getCfgOptionDef()->get("dhcp6", 10000);
|
||||
CfgMgr::instance().getStagingCfg()->getCfgOptionDef()->get(DHCP6_OPTION_SPACE, 10000);
|
||||
ASSERT_TRUE(def);
|
||||
|
||||
// Verify that the option definition is correct.
|
||||
@ -683,7 +684,7 @@ TEST_F(ParseConfigTest, escapedOptionDataTest) {
|
||||
ASSERT_EQ(0, rcode);
|
||||
|
||||
// Verify that the option can be retrieved.
|
||||
OptionPtr opt = getOptionPtr("dhcp4", DHO_BOOT_FILE_NAME);
|
||||
OptionPtr opt = getOptionPtr(DHCP4_OPTION_SPACE, DHO_BOOT_FILE_NAME);
|
||||
ASSERT_TRUE(opt);
|
||||
|
||||
util::OutputBuffer buf(100);
|
||||
@ -721,7 +722,7 @@ TEST_F(ParseConfigTest, optionDataCSVFormatWithOptionDef) {
|
||||
|
||||
// Verify that the option data is correct.
|
||||
OptionCustomPtr addr_opt = boost::dynamic_pointer_cast<
|
||||
OptionCustom>(getOptionPtr("dhcp4", 16));
|
||||
OptionCustom>(getOptionPtr(DHCP4_OPTION_SPACE, 16));
|
||||
ASSERT_TRUE(addr_opt);
|
||||
EXPECT_EQ("192.0.2.0", addr_opt->readAddress().toText());
|
||||
|
||||
@ -741,7 +742,7 @@ TEST_F(ParseConfigTest, optionDataCSVFormatWithOptionDef) {
|
||||
|
||||
// Verify that the option data is correct.
|
||||
addr_opt = boost::dynamic_pointer_cast<
|
||||
OptionCustom>(getOptionPtr("dhcp4", 16));
|
||||
OptionCustom>(getOptionPtr(DHCP4_OPTION_SPACE, 16));
|
||||
ASSERT_TRUE(addr_opt);
|
||||
EXPECT_EQ("192.0.2.0", addr_opt->readAddress().toText());
|
||||
|
||||
@ -761,11 +762,55 @@ TEST_F(ParseConfigTest, optionDataCSVFormatWithOptionDef) {
|
||||
|
||||
// Verify that the option data is correct.
|
||||
addr_opt = boost::dynamic_pointer_cast<
|
||||
OptionCustom>(getOptionPtr("dhcp4", 16));
|
||||
OptionCustom>(getOptionPtr(DHCP4_OPTION_SPACE, 16));
|
||||
ASSERT_TRUE(addr_opt);
|
||||
EXPECT_EQ("192.0.2.0", addr_opt->readAddress().toText());
|
||||
}
|
||||
|
||||
// This test verifies that definitions of standard encapsulated
|
||||
// options can be used.
|
||||
TEST_F(ParseConfigTest, encapsulatedOptionData) {
|
||||
std::string config =
|
||||
"{ \"option-data\": [ {"
|
||||
" \"space\": \"s46-cont-mape-options\","
|
||||
" \"name\": \"s46-rule\","
|
||||
" \"data\": \"1, 0, 24, 192.0.2.0, 2001:db8:1::/64\""
|
||||
" } ]"
|
||||
"}";
|
||||
|
||||
// Make sure that we're using correct universe.
|
||||
parser_context_->universe_ = Option::V6;
|
||||
int rcode = 0;
|
||||
ASSERT_NO_THROW(rcode = parseConfiguration(config));
|
||||
ASSERT_EQ(0, rcode);
|
||||
|
||||
// Verify that the option data is correct.
|
||||
OptionCustomPtr s46_rule = boost::dynamic_pointer_cast<OptionCustom>
|
||||
(getOptionPtr(MAPE_V6_OPTION_SPACE, D6O_S46_RULE));
|
||||
ASSERT_TRUE(s46_rule);
|
||||
|
||||
uint8_t flags;
|
||||
uint8_t ea_len;
|
||||
uint8_t prefix4_len;
|
||||
IOAddress ipv4_prefix(IOAddress::IPV4_ZERO_ADDRESS());
|
||||
PrefixTuple ipv6_prefix(PrefixLen(0), IOAddress::IPV6_ZERO_ADDRESS());;
|
||||
|
||||
ASSERT_NO_THROW({
|
||||
flags = s46_rule->readInteger<uint8_t>(0);
|
||||
ea_len = s46_rule->readInteger<uint8_t>(1);
|
||||
prefix4_len = s46_rule->readInteger<uint8_t>(2);
|
||||
ipv4_prefix = s46_rule->readAddress(3);
|
||||
ipv6_prefix = s46_rule->readPrefix(4);
|
||||
});
|
||||
|
||||
EXPECT_EQ(1, flags);
|
||||
EXPECT_EQ(0, ea_len);
|
||||
EXPECT_EQ(24, prefix4_len);
|
||||
EXPECT_EQ("192.0.2.0", ipv4_prefix.toText());
|
||||
EXPECT_EQ(64, ipv6_prefix.first.asUnsigned());
|
||||
EXPECT_EQ("2001:db8:1::", ipv6_prefix.second.toText());
|
||||
}
|
||||
|
||||
// This test checks behavior of the configuration parser for option data
|
||||
// for different values of csv-format parameter and when there is no
|
||||
// option definition.
|
||||
@ -816,7 +861,7 @@ TEST_F(ParseConfigTest, optionDataCSVFormatNoOptionDef) {
|
||||
"}";
|
||||
ASSERT_NO_THROW(rcode = parseConfiguration(config));
|
||||
ASSERT_EQ(0, rcode);
|
||||
OptionPtr opt = getOptionPtr("dhcp6", 25000);
|
||||
OptionPtr opt = getOptionPtr(DHCP6_OPTION_SPACE, 25000);
|
||||
ASSERT_TRUE(opt);
|
||||
ASSERT_EQ(1, opt->getData().size());
|
||||
EXPECT_EQ(0, opt->getData()[0]);
|
||||
@ -835,7 +880,7 @@ TEST_F(ParseConfigTest, optionDataCSVFormatNoOptionDef) {
|
||||
"}";
|
||||
ASSERT_NO_THROW(rcode = parseConfiguration(config));
|
||||
EXPECT_EQ(0, rcode);
|
||||
opt = getOptionPtr("dhcp6", 25000);
|
||||
opt = getOptionPtr(DHCP6_OPTION_SPACE, 25000);
|
||||
ASSERT_TRUE(opt);
|
||||
ASSERT_EQ(3, opt->getData().size());
|
||||
EXPECT_EQ(0x12, opt->getData()[0]);
|
||||
@ -857,7 +902,7 @@ TEST_F(ParseConfigTest, optionDataNoName) {
|
||||
ASSERT_NO_THROW(rcode = parseConfiguration(config));
|
||||
EXPECT_EQ(0, rcode);
|
||||
Option6AddrLstPtr opt = boost::dynamic_pointer_cast<
|
||||
Option6AddrLst>(getOptionPtr("dhcp6", 23));
|
||||
Option6AddrLst>(getOptionPtr(DHCP6_OPTION_SPACE, 23));
|
||||
ASSERT_TRUE(opt);
|
||||
ASSERT_EQ(1, opt->getAddresses().size());
|
||||
EXPECT_EQ( "2001:db8:1::1", opt->getAddresses()[0].toText());
|
||||
@ -877,7 +922,7 @@ TEST_F(ParseConfigTest, optionDataNoCode) {
|
||||
ASSERT_NO_THROW(rcode = parseConfiguration(config));
|
||||
EXPECT_EQ(0, rcode);
|
||||
Option6AddrLstPtr opt = boost::dynamic_pointer_cast<
|
||||
Option6AddrLst>(getOptionPtr("dhcp6", 23));
|
||||
Option6AddrLst>(getOptionPtr(DHCP6_OPTION_SPACE, 23));
|
||||
ASSERT_TRUE(opt);
|
||||
ASSERT_EQ(1, opt->getAddresses().size());
|
||||
EXPECT_EQ( "2001:db8:1::1", opt->getAddresses()[0].toText());
|
||||
@ -896,7 +941,7 @@ TEST_F(ParseConfigTest, optionDataMinimal) {
|
||||
ASSERT_NO_THROW(rcode = parseConfiguration(config));
|
||||
EXPECT_EQ(0, rcode);
|
||||
Option6AddrLstPtr opt = boost::dynamic_pointer_cast<
|
||||
Option6AddrLst>(getOptionPtr("dhcp6", 23));
|
||||
Option6AddrLst>(getOptionPtr(DHCP6_OPTION_SPACE, 23));
|
||||
ASSERT_TRUE(opt);
|
||||
ASSERT_EQ(1, opt->getAddresses().size());
|
||||
EXPECT_EQ( "2001:db8:1::10", opt->getAddresses()[0].toText());
|
||||
@ -912,7 +957,7 @@ TEST_F(ParseConfigTest, optionDataMinimal) {
|
||||
rcode = 0;
|
||||
ASSERT_NO_THROW(rcode = parseConfiguration(config));
|
||||
EXPECT_EQ(0, rcode);
|
||||
opt = boost::dynamic_pointer_cast<Option6AddrLst>(getOptionPtr("dhcp6",
|
||||
opt = boost::dynamic_pointer_cast<Option6AddrLst>(getOptionPtr(DHCP6_OPTION_SPACE,
|
||||
23));
|
||||
ASSERT_TRUE(opt);
|
||||
ASSERT_EQ(1, opt->getAddresses().size());
|
||||
@ -942,7 +987,7 @@ TEST_F(ParseConfigTest, optionDataMinimalWithOptionDef) {
|
||||
ASSERT_NO_THROW(rcode = parseConfiguration(config));
|
||||
EXPECT_EQ(0, rcode);
|
||||
Option6AddrLstPtr opt = boost::dynamic_pointer_cast<
|
||||
Option6AddrLst>(getOptionPtr("dhcp6", 2345));
|
||||
Option6AddrLst>(getOptionPtr(DHCP6_OPTION_SPACE, 2345));
|
||||
ASSERT_TRUE(opt);
|
||||
ASSERT_EQ(2, opt->getAddresses().size());
|
||||
EXPECT_EQ("2001:db8:1::10", opt->getAddresses()[0].toText());
|
||||
@ -967,7 +1012,7 @@ TEST_F(ParseConfigTest, optionDataMinimalWithOptionDef) {
|
||||
rcode = 0;
|
||||
ASSERT_NO_THROW(rcode = parseConfiguration(config));
|
||||
EXPECT_EQ(0, rcode);
|
||||
opt = boost::dynamic_pointer_cast<Option6AddrLst>(getOptionPtr("dhcp6",
|
||||
opt = boost::dynamic_pointer_cast<Option6AddrLst>(getOptionPtr(DHCP6_OPTION_SPACE,
|
||||
2345));
|
||||
ASSERT_TRUE(opt);
|
||||
ASSERT_EQ(2, opt->getAddresses().size());
|
||||
@ -989,7 +1034,7 @@ TEST_F(ParseConfigTest, emptyOptionData) {
|
||||
ASSERT_NO_THROW(rcode = parseConfiguration(config));
|
||||
EXPECT_EQ(0, rcode);
|
||||
const Option6AddrLstPtr opt = boost::dynamic_pointer_cast<
|
||||
Option6AddrLst>(getOptionPtr("dhcp6", D6O_DHCPV4_O_DHCPV6_SERVER));
|
||||
Option6AddrLst>(getOptionPtr(DHCP6_OPTION_SPACE, D6O_DHCPV4_O_DHCPV6_SERVER));
|
||||
ASSERT_TRUE(opt);
|
||||
ASSERT_EQ(0, opt->getAddresses().size());
|
||||
}
|
||||
@ -1009,7 +1054,7 @@ TEST_F(ParseConfigTest, optionDataNoSubOpion) {
|
||||
int rcode = 0;
|
||||
ASSERT_NO_THROW(rcode = parseConfiguration(config));
|
||||
EXPECT_EQ(0, rcode);
|
||||
const OptionPtr opt = getOptionPtr("dhcp4", DHO_VENDOR_ENCAPSULATED_OPTIONS);
|
||||
const OptionPtr opt = getOptionPtr(DHCP4_OPTION_SPACE, DHO_VENDOR_ENCAPSULATED_OPTIONS);
|
||||
ASSERT_TRUE(opt);
|
||||
ASSERT_EQ(0, opt->getOptions().size());
|
||||
}
|
||||
|
@ -8,7 +8,6 @@
|
||||
#include <dhcp/libdhcp++.h>
|
||||
#include <dhcp/option4_addrlst.h>
|
||||
#include <dhcp/option6_addrlst.h>
|
||||
#include <dhcp/option_space.h>
|
||||
#include <dhcp/option_string.h>
|
||||
#include <dhcp/option_int.h>
|
||||
#include <dhcp/option_vendor.h>
|
||||
|
@ -77,13 +77,13 @@ protected:
|
||||
OptionPtr
|
||||
retrieveOption(const Host& host, const std::string& option_space,
|
||||
const uint16_t option_code) const {
|
||||
if ((option_space != "dhcp6") && (option_space != "dhcp4")) {
|
||||
if ((option_space != DHCP6_OPTION_SPACE) && (option_space != DHCP4_OPTION_SPACE)) {
|
||||
return (OptionPtr());
|
||||
}
|
||||
|
||||
// Retrieve a pointer to the appropriate container depending if we're
|
||||
// interested in DHCPv4 or DHCPv6 options.
|
||||
ConstCfgOptionPtr cfg_option = (option_space == "dhcp4" ?
|
||||
ConstCfgOptionPtr cfg_option = (option_space == DHCP4_OPTION_SPACE ?
|
||||
host.getCfgOption4() : host.getCfgOption6());
|
||||
|
||||
// Retrieve options.
|
||||
@ -779,7 +779,7 @@ TEST_F(HostReservationParserTest, options4) {
|
||||
|
||||
// Retrieve and sanity check name servers.
|
||||
Option4AddrLstPtr opt_dns = boost::dynamic_pointer_cast<
|
||||
Option4AddrLst>(retrieveOption(*hosts[0], "dhcp4", DHO_NAME_SERVERS));
|
||||
Option4AddrLst>(retrieveOption(*hosts[0], DHCP4_OPTION_SPACE, DHO_NAME_SERVERS));
|
||||
ASSERT_TRUE(opt_dns);
|
||||
Option4AddrLst::AddressContainer dns_addrs = opt_dns->getAddresses();
|
||||
ASSERT_EQ(2, dns_addrs.size());
|
||||
@ -788,7 +788,7 @@ TEST_F(HostReservationParserTest, options4) {
|
||||
|
||||
// Retrieve and sanity check log servers.
|
||||
Option4AddrLstPtr opt_log = boost::dynamic_pointer_cast<
|
||||
Option4AddrLst>(retrieveOption(*hosts[0], "dhcp4", DHO_LOG_SERVERS));
|
||||
Option4AddrLst>(retrieveOption(*hosts[0], DHCP4_OPTION_SPACE, DHO_LOG_SERVERS));
|
||||
ASSERT_TRUE(opt_log);
|
||||
Option4AddrLst::AddressContainer log_addrs = opt_log->getAddresses();
|
||||
ASSERT_EQ(1, log_addrs.size());
|
||||
@ -796,7 +796,7 @@ TEST_F(HostReservationParserTest, options4) {
|
||||
|
||||
// Retrieve and sanity check default IP TTL.
|
||||
OptionUint8Ptr opt_ttl = boost::dynamic_pointer_cast<
|
||||
OptionUint8>(retrieveOption(*hosts[0], "dhcp4", DHO_DEFAULT_IP_TTL));
|
||||
OptionUint8>(retrieveOption(*hosts[0], DHCP4_OPTION_SPACE, DHO_DEFAULT_IP_TTL));
|
||||
ASSERT_TRUE(opt_ttl);
|
||||
EXPECT_EQ(64, opt_ttl->getValue());
|
||||
}
|
||||
@ -837,7 +837,7 @@ TEST_F(HostReservationParserTest, options6) {
|
||||
|
||||
// Retrieve and sanity check DNS servers option.
|
||||
Option6AddrLstPtr opt_dns = boost::dynamic_pointer_cast<
|
||||
Option6AddrLst>(retrieveOption(*hosts[0], "dhcp6", D6O_NAME_SERVERS));
|
||||
Option6AddrLst>(retrieveOption(*hosts[0], DHCP6_OPTION_SPACE, D6O_NAME_SERVERS));
|
||||
ASSERT_TRUE(opt_dns);
|
||||
Option6AddrLst::AddressContainer dns_addrs = opt_dns->getAddresses();
|
||||
ASSERT_EQ(2, dns_addrs.size());
|
||||
@ -846,7 +846,7 @@ TEST_F(HostReservationParserTest, options6) {
|
||||
|
||||
// Retrieve and sanity check NIS servers option.
|
||||
Option6AddrLstPtr opt_nis = boost::dynamic_pointer_cast<
|
||||
Option6AddrLst>(retrieveOption(*hosts[0], "dhcp6", D6O_NIS_SERVERS));
|
||||
Option6AddrLst>(retrieveOption(*hosts[0], DHCP6_OPTION_SPACE, D6O_NIS_SERVERS));
|
||||
ASSERT_TRUE(opt_nis);
|
||||
Option6AddrLst::AddressContainer nis_addrs = opt_nis->getAddresses();
|
||||
ASSERT_EQ(1, nis_addrs.size());
|
||||
@ -854,7 +854,7 @@ TEST_F(HostReservationParserTest, options6) {
|
||||
|
||||
// Retrieve and sanity check preference option.
|
||||
OptionUint8Ptr opt_prf = boost::dynamic_pointer_cast<
|
||||
OptionUint8>(retrieveOption(*hosts[0], "dhcp6", D6O_PREFERENCE));
|
||||
OptionUint8>(retrieveOption(*hosts[0], DHCP6_OPTION_SPACE, D6O_PREFERENCE));
|
||||
ASSERT_TRUE(opt_prf);
|
||||
EXPECT_EQ(11, opt_prf->getValue());
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <config.h>
|
||||
|
||||
#include <dhcpsrv/host.h>
|
||||
#include <dhcp/option_space.h>
|
||||
#include <util/encode/hex.h>
|
||||
#include <util/range_utilities.h>
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
@ -769,7 +770,7 @@ TEST_F(HostTest, addOptions4) {
|
||||
// Differentiate options by their codes (100-109)
|
||||
for (uint16_t code = 100; code < 110; ++code) {
|
||||
OptionPtr option(new Option(Option::V4, code, OptionBuffer(10, 0xFF)));
|
||||
ASSERT_NO_THROW(host.getCfgOption4()->add(option, false, "dhcp4"));
|
||||
ASSERT_NO_THROW(host.getCfgOption4()->add(option, false, DHCP4_OPTION_SPACE));
|
||||
}
|
||||
|
||||
// Add 7 options to another option space. The option codes partially overlap
|
||||
@ -780,20 +781,20 @@ TEST_F(HostTest, addOptions4) {
|
||||
}
|
||||
|
||||
// Get options from the Subnet and check if all 10 are there.
|
||||
OptionContainerPtr options = host.getCfgOption4()->getAll("dhcp4");
|
||||
OptionContainerPtr options = host.getCfgOption4()->getAll(DHCP4_OPTION_SPACE);
|
||||
ASSERT_TRUE(options);
|
||||
ASSERT_EQ(10, options->size());
|
||||
|
||||
// It should be possible to retrieve DHCPv6 options but the container
|
||||
// should be empty.
|
||||
OptionContainerPtr options6 = host.getCfgOption6()->getAll("dhcp6");
|
||||
OptionContainerPtr options6 = host.getCfgOption6()->getAll(DHCP6_OPTION_SPACE);
|
||||
ASSERT_TRUE(options6);
|
||||
EXPECT_TRUE(options6->empty());
|
||||
|
||||
// Also make sure that for dhcp4 option space no DHCPv6 options are
|
||||
// returned. This is to check that containers for DHCPv4 and DHCPv6
|
||||
// options do not share information.
|
||||
options6 = host.getCfgOption6()->getAll("dhcp4");
|
||||
options6 = host.getCfgOption6()->getAll(DHCP4_OPTION_SPACE);
|
||||
ASSERT_TRUE(options6);
|
||||
EXPECT_TRUE(options6->empty());
|
||||
|
||||
@ -833,7 +834,7 @@ TEST_F(HostTest, addOptions6) {
|
||||
// Differentiate options by their codes (100-109)
|
||||
for (uint16_t code = 100; code < 110; ++code) {
|
||||
OptionPtr option(new Option(Option::V6, code, OptionBuffer(10, 0xFF)));
|
||||
ASSERT_NO_THROW(host.getCfgOption6()->add(option, false, "dhcp6"));
|
||||
ASSERT_NO_THROW(host.getCfgOption6()->add(option, false, DHCP6_OPTION_SPACE));
|
||||
}
|
||||
|
||||
// Add 7 options to another option space. The option codes partially overlap
|
||||
@ -844,20 +845,20 @@ TEST_F(HostTest, addOptions6) {
|
||||
}
|
||||
|
||||
// Get options from the Subnet and check if all 10 are there.
|
||||
OptionContainerPtr options = host.getCfgOption6()->getAll("dhcp6");
|
||||
OptionContainerPtr options = host.getCfgOption6()->getAll(DHCP6_OPTION_SPACE);
|
||||
ASSERT_TRUE(options);
|
||||
ASSERT_EQ(10, options->size());
|
||||
|
||||
// It should be possible to retrieve DHCPv4 options but the container
|
||||
// should be empty.
|
||||
OptionContainerPtr options4 = host.getCfgOption4()->getAll("dhcp4");
|
||||
OptionContainerPtr options4 = host.getCfgOption4()->getAll(DHCP4_OPTION_SPACE);
|
||||
ASSERT_TRUE(options4);
|
||||
EXPECT_TRUE(options4->empty());
|
||||
|
||||
// Also make sure that for dhcp6 option space no DHCPv4 options are
|
||||
// returned. This is to check that containers for DHCPv4 and DHCPv6
|
||||
// options do not share information.
|
||||
options4 = host.getCfgOption4()->getAll("dhcp6");
|
||||
options4 = host.getCfgOption4()->getAll(DHCP6_OPTION_SPACE);
|
||||
ASSERT_TRUE(options4);
|
||||
EXPECT_TRUE(options4->empty());
|
||||
|
||||
|
@ -206,6 +206,68 @@ TEST(Pool6Test, PD) {
|
||||
77, 77));
|
||||
}
|
||||
|
||||
// Checks that prefix pools with excluded prefixes are handled properly.
|
||||
TEST(Pool6Test, PDExclude) {
|
||||
Pool6Ptr pool;
|
||||
|
||||
// Create a pool with a good excluded prefix. The good excluded prefix
|
||||
// is the one for which is a sub-prefix of the main prefix.
|
||||
ASSERT_NO_THROW(pool.reset(new Pool6(IOAddress("2001:db8:1::"), 96, 112,
|
||||
IOAddress("2001:db8:1:2::"), 120)));
|
||||
|
||||
// Verify pool properties.
|
||||
EXPECT_EQ(Lease::TYPE_PD, pool->getType());
|
||||
EXPECT_EQ(112, pool->getLength());
|
||||
EXPECT_EQ("2001:db8:1::", pool->getFirstAddress().toText());
|
||||
EXPECT_EQ("2001:db8:1::ffff:ffff", pool->getLastAddress().toText());
|
||||
EXPECT_EQ("2001:db8:1:2::", pool->getExcludedPrefix().toText());
|
||||
EXPECT_EQ(120, static_cast<unsigned>(pool->getExcludedPrefixLength()));
|
||||
|
||||
// Create another pool instance, but with the excluded prefix being
|
||||
// "unspecified".
|
||||
ASSERT_NO_THROW(pool.reset(new Pool6(IOAddress("2001:db8:1::"), 96, 112,
|
||||
IOAddress::IPV6_ZERO_ADDRESS(), 0)));
|
||||
|
||||
EXPECT_EQ(Lease::TYPE_PD, pool->getType());
|
||||
EXPECT_EQ(112, pool->getLength());
|
||||
EXPECT_EQ("2001:db8:1::", pool->getFirstAddress().toText());
|
||||
EXPECT_EQ("2001:db8:1::ffff:ffff", pool->getLastAddress().toText());
|
||||
EXPECT_TRUE(pool->getExcludedPrefix().isV6Zero());
|
||||
EXPECT_EQ(0, static_cast<unsigned>(pool->getExcludedPrefixLength()));
|
||||
|
||||
// Excluded prefix length must be greater than the main prefix length.
|
||||
EXPECT_THROW(Pool6(IOAddress("2001:db8:1::"), 96, 112,
|
||||
IOAddress("2001:db8:1::"), 112),
|
||||
BadValue);
|
||||
|
||||
// Again, the excluded prefix length must be greater than main prefix
|
||||
// length.
|
||||
EXPECT_THROW(Pool6(IOAddress("2001:db8:1::"), 96, 112,
|
||||
IOAddress("2001:db8:1::"), 104),
|
||||
BadValue);
|
||||
|
||||
// The "unspecified" excluded prefix must have both values set to 0.
|
||||
EXPECT_THROW(Pool6(IOAddress("2001:db8:1::"), 48, 64,
|
||||
IOAddress("2001:db8:1::"), 0),
|
||||
BadValue);
|
||||
|
||||
// Similar case as above, but the prefix value is 0 and the length
|
||||
// is non zero.
|
||||
EXPECT_THROW(Pool6(IOAddress("2001:db8:1::"), 48, 64,
|
||||
IOAddress::IPV6_ZERO_ADDRESS(), 72),
|
||||
BadValue);
|
||||
|
||||
// Excluded prefix must be an IPv6 prefix.
|
||||
EXPECT_THROW(Pool6(IOAddress("10::"), 8, 16,
|
||||
IOAddress("10.0.0.0"), 24),
|
||||
BadValue);
|
||||
|
||||
// Excluded prefix length must not be greater than 128.
|
||||
EXPECT_THROW(Pool6(IOAddress("2001:db8:1::"), 48, 64,
|
||||
IOAddress("2001:db8:1::"), 129),
|
||||
BadValue);
|
||||
}
|
||||
|
||||
// Checks that temporary address pools are handled properly
|
||||
TEST(Pool6Test, TA) {
|
||||
// Note: since we defined TA pool types during PD work, we can test it
|
||||
@ -261,7 +323,7 @@ TEST(Pool6Test, unique_id) {
|
||||
}
|
||||
|
||||
// Simple check if toText returns reasonable values
|
||||
TEST(Poo6Test,toText) {
|
||||
TEST(Pool6Test,toText) {
|
||||
Pool6 pool1(Lease::TYPE_NA, IOAddress("2001:db8::1"),
|
||||
IOAddress("2001:db8::2"));
|
||||
EXPECT_EQ("type=IA_NA, 2001:db8::1-2001:db8::2, delegated_len=128",
|
||||
@ -270,6 +332,13 @@ TEST(Poo6Test,toText) {
|
||||
Pool6 pool2(Lease::TYPE_PD, IOAddress("2001:db8:1::"), 96, 112);
|
||||
EXPECT_EQ("type=IA_PD, 2001:db8:1::-2001:db8:1::ffff:ffff, delegated_len=112",
|
||||
pool2.toText());
|
||||
|
||||
Pool6 pool3(IOAddress("2001:db8:1::"), 96, 112,
|
||||
IOAddress("2001:db8:1::1000"), 120);
|
||||
EXPECT_EQ("type=IA_PD, 2001:db8:1::-2001:db8:1::ffff:ffff, delegated_len=112,"
|
||||
" excluded_prefix=2001:db8:1::1000, excluded_prefix_len=120",
|
||||
pool3.toText());
|
||||
|
||||
}
|
||||
|
||||
// Checks if the number of possible leases in range is reported correctly.
|
||||
|
@ -284,7 +284,7 @@ TEST_F(SrvConfigTest, copy) {
|
||||
|
||||
// Add an option.
|
||||
OptionPtr option(new Option(Option::V6, 1000, OptionBuffer(10, 0xFF)));
|
||||
conf1.getCfgOption()->add(option, true, "dhcp6");
|
||||
conf1.getCfgOption()->add(option, true, DHCP6_OPTION_SPACE);
|
||||
|
||||
// Add a class dictionary
|
||||
conf1.setClientClassDictionary(ref_dictionary_);
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <asiolink/io_address.h>
|
||||
#include <dhcp/option.h>
|
||||
#include <dhcp/dhcp6.h>
|
||||
#include <dhcp/option_space.h>
|
||||
#include <dhcpsrv/subnet.h>
|
||||
#include <exceptions/exceptions.h>
|
||||
|
||||
@ -350,7 +351,7 @@ TEST(Subnet4Test, addInvalidOption) {
|
||||
// should result in exception.
|
||||
OptionPtr option2;
|
||||
ASSERT_FALSE(option2);
|
||||
EXPECT_THROW(subnet->getCfgOption()->add(option2, false, "dhcp4"),
|
||||
EXPECT_THROW(subnet->getCfgOption()->add(option2, false, DHCP4_OPTION_SPACE),
|
||||
isc::BadValue);
|
||||
}
|
||||
|
||||
@ -870,7 +871,7 @@ TEST(Subnet6Test, addOptions) {
|
||||
// Differentiate options by their codes (100-109)
|
||||
for (uint16_t code = 100; code < 110; ++code) {
|
||||
OptionPtr option(new Option(Option::V6, code, OptionBuffer(10, 0xFF)));
|
||||
ASSERT_NO_THROW(subnet->getCfgOption()->add(option, false, "dhcp6"));
|
||||
ASSERT_NO_THROW(subnet->getCfgOption()->add(option, false, DHCP6_OPTION_SPACE));
|
||||
}
|
||||
|
||||
// Add 7 options to another option space. The option codes partially overlap
|
||||
@ -881,7 +882,7 @@ TEST(Subnet6Test, addOptions) {
|
||||
}
|
||||
|
||||
// Get options from the Subnet and check if all 10 are there.
|
||||
OptionContainerPtr options = subnet->getCfgOption()->getAll("dhcp6");
|
||||
OptionContainerPtr options = subnet->getCfgOption()->getAll(DHCP6_OPTION_SPACE);
|
||||
ASSERT_TRUE(options);
|
||||
ASSERT_EQ(10, options->size());
|
||||
|
||||
@ -922,12 +923,12 @@ TEST(Subnet6Test, addNonUniqueOptions) {
|
||||
// In the inner loop we create options with unique codes (100-109).
|
||||
for (uint16_t code = 100; code < 110; ++code) {
|
||||
OptionPtr option(new Option(Option::V6, code, OptionBuffer(10, 0xFF)));
|
||||
ASSERT_NO_THROW(subnet->getCfgOption()->add(option, false, "dhcp6"));
|
||||
ASSERT_NO_THROW(subnet->getCfgOption()->add(option, false, DHCP6_OPTION_SPACE));
|
||||
}
|
||||
}
|
||||
|
||||
// Sanity check that all options are there.
|
||||
OptionContainerPtr options = subnet->getCfgOption()->getAll("dhcp6");
|
||||
OptionContainerPtr options = subnet->getCfgOption()->getAll(DHCP6_OPTION_SPACE);
|
||||
ASSERT_EQ(20, options->size());
|
||||
|
||||
// Use container index #1 to get the options by their codes.
|
||||
@ -974,11 +975,11 @@ TEST(Subnet6Test, addPersistentOption) {
|
||||
// and options with these codes will be flagged non-persistent.
|
||||
// Options with other codes will be flagged persistent.
|
||||
bool persistent = (code % 3) ? true : false;
|
||||
ASSERT_NO_THROW(subnet->getCfgOption()->add(option, persistent, "dhcp6"));
|
||||
ASSERT_NO_THROW(subnet->getCfgOption()->add(option, persistent, DHCP6_OPTION_SPACE));
|
||||
}
|
||||
|
||||
// Get added options from the subnet.
|
||||
OptionContainerPtr options = subnet->getCfgOption()->getAll("dhcp6");
|
||||
OptionContainerPtr options = subnet->getCfgOption()->getAll(DHCP6_OPTION_SPACE);
|
||||
|
||||
// options->get<2> returns reference to container index #2. This
|
||||
// index is used to access options by the 'persistent' flag.
|
||||
@ -1005,7 +1006,7 @@ TEST(Subnet6Test, getOptions) {
|
||||
// Add 10 options to a "dhcp6" option space in the subnet.
|
||||
for (uint16_t code = 100; code < 110; ++code) {
|
||||
OptionPtr option(new Option(Option::V6, code, OptionBuffer(10, 0xFF)));
|
||||
ASSERT_NO_THROW(subnet->getCfgOption()->add(option, false, "dhcp6"));
|
||||
ASSERT_NO_THROW(subnet->getCfgOption()->add(option, false, DHCP6_OPTION_SPACE));
|
||||
}
|
||||
|
||||
// Check that we can get each added option descriptor using
|
||||
@ -1017,7 +1018,7 @@ TEST(Subnet6Test, getOptions) {
|
||||
// Returned descriptor should contain NULL option ptr.
|
||||
EXPECT_FALSE(desc.option_);
|
||||
// Now, try the valid option space.
|
||||
desc = subnet->getCfgOption()->get("dhcp6", code);
|
||||
desc = subnet->getCfgOption()->get(DHCP6_OPTION_SPACE, code);
|
||||
// Test that the option code matches the expected code.
|
||||
ASSERT_TRUE(desc.option_);
|
||||
EXPECT_EQ(code, desc.option_->getType());
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <dhcp/option.h>
|
||||
#include <dhcp/option_definition.h>
|
||||
#include <dhcp/libdhcp++.h>
|
||||
#include <dhcp/option_space.h>
|
||||
#include <eval/eval_context.h>
|
||||
#include <eval/parser.h>
|
||||
#include <exceptions/exceptions.h>
|
||||
@ -79,11 +80,12 @@ uint16_t
|
||||
EvalContext::convertOptionName(const std::string& option_name,
|
||||
const isc::eval::location& loc)
|
||||
{
|
||||
OptionDefinitionPtr option_def = LibDHCP::getOptionDef(option_universe_,
|
||||
const std::string global_space = (option_universe_ == Option::V4) ?
|
||||
DHCP4_OPTION_SPACE : DHCP6_OPTION_SPACE;
|
||||
|
||||
OptionDefinitionPtr option_def = LibDHCP::getOptionDef(global_space,
|
||||
option_name);
|
||||
if (!option_def) {
|
||||
const std::string global_space =
|
||||
(option_universe_ == Option::V4) ? "dhcp4" : "dhcp6";
|
||||
option_def = LibDHCP::getRuntimeOptionDef(global_space, option_name);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user