2
0
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:
Marcin Siodelski 2016-10-26 08:18:05 +02:00
parent dad932d544
commit 0c0ca2fde7
67 changed files with 3561 additions and 511 deletions

View File

@ -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

View File

@ -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:

View 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"
}
]
}
}

View File

@ -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>

View File

@ -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>

View File

@ -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;

View File

@ -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());

View File

@ -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));

View File

@ -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) {

View File

@ -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",

View File

@ -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);

View File

@ -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.

View File

@ -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 {

View File

@ -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());

View File

@ -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");
}

View File

@ -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) {

View File

@ -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) {

View File

@ -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

View File

@ -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 */

View File

@ -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;

View File

@ -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.

View File

@ -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

View File

@ -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_;

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View 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

View 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

View File

@ -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);

View File

@ -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.

View File

@ -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;

View File

@ -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.

View File

@ -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;

View File

@ -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;

View File

@ -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);
}

View File

@ -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.
};

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);
}

View 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

View File

@ -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

View File

@ -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) {

View File

@ -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

View File

@ -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

View File

@ -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_,

View File

@ -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.

View File

@ -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_);
}
}
}

View File

@ -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

View File

@ -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 << "'");

View File

@ -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.

View File

@ -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", "");

View File

@ -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

View File

@ -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

View File

@ -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"));

View File

@ -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.

View File

@ -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());

View File

@ -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));

View File

@ -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());
}

View File

@ -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>

View File

@ -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());
}

View File

@ -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());

View File

@ -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.

View File

@ -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_);

View File

@ -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());

View File

@ -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);
}