2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-30 05:27:55 +00:00

[4097a] Finished the DHCPv6 part

This commit is contained in:
Francis Dupont 2015-11-22 09:06:29 +01:00
parent db7f698d7f
commit c50a482e47
8 changed files with 521 additions and 141 deletions

View File

@ -1826,10 +1826,8 @@ TEST_F(Dhcp4ParserTest, optionStandardDefOverride) {
}
// Goal of this test is to verify that global option
// data is configured for the subnet if the subnet
// configuration does not include options configuration.
TEST_F(Dhcp4ParserTest, optionDataDefaults) {
// Goal of this test is to verify that global option data is configured
TEST_F(Dhcp4ParserTest, optionDataDefaultsGlobal) {
ConstElementPtr x;
string config = "{ " + genIfaceConfig() + "," +
"\"rebind-timer\": 2000,"
@ -1855,10 +1853,78 @@ TEST_F(Dhcp4ParserTest, optionDataDefaults) {
EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
checkResult(x, 0);
// These options are global
Subnet4Ptr subnet = CfgMgr::instance().getStagingCfg()->
getCfgSubnets4()->selectSubnet(IOAddress("192.0.2.200"));
ASSERT_TRUE(subnet);
OptionContainerPtr options = subnet->getCfgOption()->getAll("dhcp4");
ASSERT_EQ(0, options->size());
options = CfgMgr::instance().getStagingCfg()->getCfgOption()->getAll("dhcp4");
ASSERT_EQ(2, options->size());
// Get the search index. Index #1 is to search using option code.
const OptionContainerTypeIndex& idx = options->get<1>();
// Get the options for specified index. Expecting one option to be
// returned but in theory we may have multiple options with the same
// code so we get the range.
std::pair<OptionContainerTypeIndex::const_iterator,
OptionContainerTypeIndex::const_iterator> range =
idx.equal_range(56);
// Expect single option with the code equal to 56.
ASSERT_EQ(1, std::distance(range.first, range.second));
const uint8_t foo_expected[] = {
0xAB, 0xCD, 0xEF, 0x01, 0x05
};
// Check if option is valid in terms of code and carried data.
testOption(*range.first, 56, foo_expected, sizeof(foo_expected));
range = idx.equal_range(23);
ASSERT_EQ(1, std::distance(range.first, range.second));
// Do another round of testing with second option.
const uint8_t foo2_expected[] = {
0x01
};
testOption(*range.first, 23, foo2_expected, sizeof(foo2_expected));
}
// Goal of this test is to verify that subnet option data is configured
TEST_F(Dhcp4ParserTest, optionDataDefaultsSubnet) {
ConstElementPtr x;
string config = "{ " + genIfaceConfig() + "," +
"\"rebind-timer\": 2000,"
"\"renew-timer\": 1000,"
"\"subnet4\": [ { "
" \"pools\": [ { \"pool\": \"192.0.2.1 - 192.0.2.100\" } ],"
" \"subnet\": \"192.0.2.0/24\","
" \"option-data\": [ {"
" \"name\": \"dhcp-message\","
" \"data\": \"ABCDEF0105\","
" \"csv-format\": False"
" },"
" {"
" \"name\": \"default-ip-ttl\","
" \"data\": \"01\","
" \"csv-format\": False"
" } ]"
" } ],"
"\"valid-lifetime\": 4000 }";
ElementPtr json = Element::fromJSON(config);
EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
checkResult(x, 0);
// These options are subnet options
OptionContainerPtr options =
CfgMgr::instance().getStagingCfg()->getCfgOption()->getAll("dhcp4");
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");
ASSERT_EQ(2, options->size());
// Get the search index. Index #1 is to search using option code.
@ -1933,21 +1999,21 @@ TEST_F(Dhcp4ParserTest, optionDataTwoSpaces) {
ASSERT_TRUE(status);
checkResult(status, 0);
// Options should be now available for the subnet.
Subnet4Ptr subnet = CfgMgr::instance().getStagingCfg()->
getCfgSubnets4()->selectSubnet(IOAddress("192.0.2.200"));
ASSERT_TRUE(subnet);
// Options should be now available
// Try to get the option from the space dhcp4.
OptionDescriptor desc1 = subnet->getCfgOption()->get("dhcp4", 56);
OptionDescriptor desc1 =
CfgMgr::instance().getStagingCfg()->getCfgOption()->get("dhcp4", 56);
ASSERT_TRUE(desc1.option_);
EXPECT_EQ(56, desc1.option_->getType());
// Try to get the option from the space isc.
OptionDescriptor desc2 = subnet->getCfgOption()->get("isc", 56);
OptionDescriptor desc2 =
CfgMgr::instance().getStagingCfg()->getCfgOption()->get("isc", 56);
ASSERT_TRUE(desc2.option_);
EXPECT_EQ(56, desc1.option_->getType());
// Try to get the non-existing option from the non-existing
// option space and expect that option is not returned.
OptionDescriptor desc3 = subnet->getCfgOption()->get("non-existing", 56);
OptionDescriptor desc3 =
CfgMgr::instance().getStagingCfg()->getCfgOption()->get("non-existing", 56);
ASSERT_FALSE(desc3.option_);
}
@ -1960,8 +2026,8 @@ TEST_F(Dhcp4ParserTest, optionDataTwoSpaces) {
TEST_F(Dhcp4ParserTest, optionDataEncapsulate) {
// @todo DHCP configurations has many dependencies between
// parameters. First of all, configuration for subnet is
// inherited from the global values. Thus subnet has to be
// parameters. First of all, configuration for subnet was
// inherited from the global values. Thus subnet had to be
// configured when all global values have been configured.
// Also, an option can encapsulate another option only
// if the latter has been configured. For this reason in this
@ -2011,7 +2077,7 @@ TEST_F(Dhcp4ParserTest, optionDataEncapsulate) {
CfgMgr::instance().clear();
// Stage 2. Configure base option and a subnet. Please note that
// the configuration from the stage 2 is repeated because BIND
// the configuration from the stage 2 is repeated because Kea
// configuration manager sends whole configuration for the lists
// where at least one element is being modified or added.
config = "{ " + genIfaceConfig() + "," +
@ -2063,18 +2129,15 @@ TEST_F(Dhcp4ParserTest, optionDataEncapsulate) {
ASSERT_TRUE(status);
checkResult(status, 0);
// Get the subnet.
Subnet4Ptr subnet = CfgMgr::instance().getStagingCfg()->
getCfgSubnets4()->selectSubnet(IOAddress("192.0.2.5"));
ASSERT_TRUE(subnet);
// We should have one option available.
OptionContainerPtr options = subnet->getCfgOption()->getAll("dhcp4");
OptionContainerPtr options =
CfgMgr::instance().getStagingCfg()->getCfgOption()->getAll("dhcp4");
ASSERT_TRUE(options);
ASSERT_EQ(1, options->size());
// Get the option.
OptionDescriptor desc = subnet->getCfgOption()->get("dhcp4", 222);
OptionDescriptor desc =
CfgMgr::instance().getStagingCfg()->getCfgOption()->get("dhcp4", 222);
EXPECT_TRUE(desc.option_);
EXPECT_EQ(222, desc.option_->getType());
@ -2605,19 +2668,15 @@ TEST_F(Dhcp4ParserTest, stdOptionDataEncapsulate) {
ASSERT_TRUE(status);
checkResult(status, 0);
// Get the subnet.
Subnet4Ptr subnet = CfgMgr::instance().getStagingCfg()->
getCfgSubnets4()->selectSubnet(IOAddress("192.0.2.5"));
ASSERT_TRUE(subnet);
// We should have one option available.
OptionContainerPtr options = subnet->getCfgOption()->getAll("dhcp4");
OptionContainerPtr options =
CfgMgr::instance().getStagingCfg()->getCfgOption()->getAll("dhcp4");
ASSERT_TRUE(options);
ASSERT_EQ(1, options->size());
// Get the option.
OptionDescriptor desc =
subnet->getCfgOption()->get("dhcp4", DHO_VENDOR_ENCAPSULATED_OPTIONS);
OptionDescriptor desc = CfgMgr::instance().getStagingCfg()->
getCfgOption()->get("dhcp4", DHO_VENDOR_ENCAPSULATED_OPTIONS);
EXPECT_TRUE(desc.option_);
EXPECT_EQ(DHO_VENDOR_ENCAPSULATED_OPTIONS, desc.option_->getType());
@ -2651,7 +2710,7 @@ TEST_F(Dhcp4ParserTest, stdOptionDataEncapsulate) {
}
// This test checks if vendor options can be specified in the config file
// (in hex format), and later retrieved from configured subnet
// (in hex format), and later retrieved
TEST_F(Dhcp4ParserTest, vendorOptionsHex) {
// This configuration string is to configure two options
@ -2689,28 +2748,26 @@ TEST_F(Dhcp4ParserTest, vendorOptionsHex) {
ASSERT_TRUE(status);
checkResult(status, 0);
// Options should be now available for the subnet.
Subnet4Ptr subnet = CfgMgr::instance().getStagingCfg()->
getCfgSubnets4()->selectSubnet(IOAddress("192.0.2.5"));
ASSERT_TRUE(subnet);
// Try to get the option from the vendor space 4491
OptionDescriptor desc1 = subnet->getCfgOption()->get(VENDOR_ID_CABLE_LABS, 100);
OptionDescriptor desc1 = CfgMgr::instance().getStagingCfg()->
getCfgOption()->get(VENDOR_ID_CABLE_LABS, 100);
ASSERT_TRUE(desc1.option_);
EXPECT_EQ(100, desc1.option_->getType());
// Try to get the option from the vendor space 1234
OptionDescriptor desc2 = subnet->getCfgOption()->get(1234, 100);
OptionDescriptor desc2 =
CfgMgr::instance().getStagingCfg()->getCfgOption()->get(1234, 100);
ASSERT_TRUE(desc2.option_);
EXPECT_EQ(100, desc1.option_->getType());
// Try to get the non-existing option from the non-existing
// option space and expect that option is not returned.
OptionDescriptor desc3 = subnet->getCfgOption()->get(5678, 100);
OptionDescriptor desc3 =
CfgMgr::instance().getStagingCfg()->getCfgOption()->get(5678, 100);
ASSERT_FALSE(desc3.option_);
}
// This test checks if vendor options can be specified in the config file,
// (in csv format), and later retrieved from configured subnet
// (in csv format), and later retrieved
TEST_F(Dhcp4ParserTest, vendorOptionsCsv) {
// This configuration string is to configure two options
@ -2746,19 +2803,16 @@ TEST_F(Dhcp4ParserTest, vendorOptionsCsv) {
ASSERT_TRUE(status);
checkResult(status, 0);
// Options should be now available for the subnet.
Subnet4Ptr subnet = CfgMgr::instance().getStagingCfg()->
getCfgSubnets4()->selectSubnet(IOAddress("192.0.2.5"));
ASSERT_TRUE(subnet);
// Try to get the option from the vendor space 4491
OptionDescriptor desc1 = subnet->getCfgOption()->get(VENDOR_ID_CABLE_LABS, 100);
OptionDescriptor desc1 = CfgMgr::instance().getStagingCfg()->
getCfgOption()->get(VENDOR_ID_CABLE_LABS, 100);
ASSERT_TRUE(desc1.option_);
EXPECT_EQ(100, desc1.option_->getType());
// Try to get the non-existing option from the non-existing
// option space and expect that option is not returned.
OptionDescriptor desc2 = subnet->getCfgOption()->get(5678, 100);
OptionDescriptor desc2 =
CfgMgr::instance().getStagingCfg()->getCfgOption()->get(5678, 100);
ASSERT_FALSE(desc2.option_);
}

View File

@ -1844,8 +1844,10 @@ TEST_F(Dhcpv4SrvTest, subnetClassPriority) {
}
// Checks class options have the priority over global options
// Note it is not currently the case, cf #4205
TEST_F(Dhcpv4SrvTest, classGlobalPriority) {
IfaceMgrTestConfig test_config(true);
IfaceMgr::instance().openSockets4();
NakedDhcpv4Srv srv(0);
// A global ip-forwarding option is set in the response.
@ -1914,13 +1916,13 @@ TEST_F(Dhcpv4SrvTest, classGlobalPriority) {
ASSERT_TRUE(opt);
ASSERT_GT(opt->len(), opt->getHeaderLen());
// Classification sets the value to true/1, global to false/0
// Here class should have the priority but hasn't, cf #4205
EXPECT_EQ(0, opt->getUint8());
// Here class has the priority
EXPECT_NE(0, opt->getUint8());
}
// Checks if the client-class field is indeed used for subnet selection.
// Note that packet classification is already checked in Dhcpv4SrvTest
// .*clientClassification above.
// .*Classification above.
TEST_F(Dhcpv4SrvTest, clientClassify) {
// This test configures 2 subnets. We actually only need the

View File

@ -884,16 +884,16 @@ Dhcpv6Srv::copyClientOptions(const Pkt6Ptr& question, Pkt6Ptr& answer) {
}
void
Dhcpv6Srv::appendDefaultOptions(const Pkt6Ptr&, Pkt6Ptr& answer) {
Dhcpv6Srv::appendDefaultOptions(const Pkt6Ptr&, Pkt6Ptr& answer,
const CfgOptionList&) {
// add server-id
answer->addOption(getServerID());
}
void
Dhcpv6Srv::buildCfgOptionList(const Pkt6Ptr& question,
AllocEngine::ClientContext6& ctx) {
CfgOptionList& co_list = getCfgOptionList();
AllocEngine::ClientContext6& ctx,
CfgOptionList& co_list) {
// First subnet configured options
if (ctx.subnet_) {
co_list.push_back(ctx.subnet_->getCfgOption());
@ -921,7 +921,8 @@ Dhcpv6Srv::buildCfgOptionList(const Pkt6Ptr& question,
void
Dhcpv6Srv::appendRequestedOptions(const Pkt6Ptr& question, Pkt6Ptr& answer,
AllocEngine::ClientContext6& ctx) {
AllocEngine::ClientContext6& ctx,
const CfgOptionList& co_list) {
// Client requests some options using ORO option. Try to
// get this option from client's message.
@ -938,7 +939,6 @@ Dhcpv6Srv::appendRequestedOptions(const Pkt6Ptr& question, Pkt6Ptr& answer,
const std::vector<uint16_t>& requested_opts = option_oro->getValues();
BOOST_FOREACH(uint16_t opt, requested_opts) {
// Iterate on the configured option list
const CfgOptionList& co_list = getCfgOptionList();
for (CfgOptionList::const_iterator copts = co_list.begin();
copts != co_list.end(); ++copts) {
OptionDescriptor desc = (*copts)->get("dhcp6", opt);
@ -952,8 +952,10 @@ Dhcpv6Srv::appendRequestedOptions(const Pkt6Ptr& question, Pkt6Ptr& answer,
}
void
Dhcpv6Srv::appendRequestedVendorOptions(const Pkt6Ptr& question, Pkt6Ptr& answer,
AllocEngine::ClientContext6& ctx) {
Dhcpv6Srv::appendRequestedVendorOptions(const Pkt6Ptr& question,
Pkt6Ptr& answer,
AllocEngine::ClientContext6& ctx,
const CfgOptionList& co_list) {
// Leave if there is no subnet matching the incoming packet.
// There is no need to log the error message here because
@ -990,7 +992,6 @@ Dhcpv6Srv::appendRequestedVendorOptions(const Pkt6Ptr& question, Pkt6Ptr& answer
bool added = false;
const std::vector<uint16_t>& requested_opts = oro->getValues();
BOOST_FOREACH(uint16_t opt, requested_opts) {
const CfgOptionList& co_list = getCfgOptionList();
for (CfgOptionList::const_iterator copts = co_list.begin();
copts != co_list.end(); ++copts) {
OptionDescriptor desc = (*copts)->get(vendor_id, opt);
@ -2320,10 +2321,11 @@ Dhcpv6Srv::processSolicit(const Pkt6Ptr& solicit) {
}
copyClientOptions(solicit, response);
buildCfgOptionList(solicit, ctx);
appendDefaultOptions(solicit, response);
appendRequestedOptions(solicit, response, ctx);
appendRequestedVendorOptions(solicit, response, ctx);
CfgOptionList co_list;
buildCfgOptionList(solicit, ctx, co_list);
appendDefaultOptions(solicit, response, co_list);
appendRequestedOptions(solicit, response, ctx, co_list);
appendRequestedVendorOptions(solicit, response, ctx, co_list);
processClientFqdn(solicit, response, ctx);
assignLeases(solicit, response, ctx);
@ -2348,10 +2350,11 @@ Dhcpv6Srv::processRequest(const Pkt6Ptr& request) {
Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, request->getTransid()));
copyClientOptions(request, reply);
buildCfgOptionList(request, ctx);
appendDefaultOptions(request, reply);
appendRequestedOptions(request, reply, ctx);
appendRequestedVendorOptions(request, reply, ctx);
CfgOptionList co_list;
buildCfgOptionList(request, ctx, co_list);
appendDefaultOptions(request, reply, co_list);
appendRequestedOptions(request, reply, ctx, co_list);
appendRequestedVendorOptions(request, reply, ctx, co_list);
processClientFqdn(request, reply, ctx);
assignLeases(request, reply, ctx);
@ -2372,9 +2375,10 @@ Dhcpv6Srv::processRenew(const Pkt6Ptr& renew) {
Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, renew->getTransid()));
copyClientOptions(renew, reply);
buildCfgOptionList(renew, ctx);
appendDefaultOptions(renew, reply);
appendRequestedOptions(renew, reply, ctx);
CfgOptionList co_list;
buildCfgOptionList(renew, ctx, co_list);
appendDefaultOptions(renew, reply, co_list);
appendRequestedOptions(renew, reply, ctx, co_list);
processClientFqdn(renew, reply, ctx);
extendLeases(renew, reply, ctx);
@ -2395,9 +2399,10 @@ Dhcpv6Srv::processRebind(const Pkt6Ptr& rebind) {
Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, rebind->getTransid()));
copyClientOptions(rebind, reply);
buildCfgOptionList(rebind, ctx);
appendDefaultOptions(rebind, reply);
appendRequestedOptions(rebind, reply, ctx);
CfgOptionList co_list;
buildCfgOptionList(rebind, ctx, co_list);
appendDefaultOptions(rebind, reply, co_list);
appendRequestedOptions(rebind, reply, ctx, co_list);
processClientFqdn(rebind, reply, ctx);
extendLeases(rebind, reply, ctx);
@ -2426,9 +2431,10 @@ Dhcpv6Srv::processConfirm(const Pkt6Ptr& confirm) {
Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, confirm->getTransid()));
// Make sure that the necessary options are included.
copyClientOptions(confirm, reply);
buildCfgOptionList(confirm, ctx);
appendDefaultOptions(confirm, reply);
appendRequestedOptions(confirm, reply, ctx);
CfgOptionList co_list;
buildCfgOptionList(confirm, ctx, co_list);
appendDefaultOptions(confirm, reply, co_list);
appendRequestedOptions(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
// or no IAAddr options and that client's message has to be discarded.
@ -2507,7 +2513,9 @@ Dhcpv6Srv::processRelease(const Pkt6Ptr& release) {
Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, release->getTransid()));
copyClientOptions(release, reply);
appendDefaultOptions(release, reply);
CfgOptionList co_list;
// buildCfgOptionList(release, ctx, co_list);
appendDefaultOptions(release, reply, co_list);
releaseLeases(release, reply, ctx);
@ -2532,8 +2540,12 @@ Dhcpv6Srv::processDecline(const Pkt6Ptr& decline) {
// Copy client options (client-id, also relay information if present)
copyClientOptions(decline, reply);
// Get the configured option list
CfgOptionList co_list;
buildCfgOptionList(decline, ctx, co_list);
// Include server-id
appendDefaultOptions(decline, reply);
appendDefaultOptions(decline, reply, co_list);
if (declineLeases(decline, reply, ctx)) {
return (reply);
@ -2808,15 +2820,16 @@ Dhcpv6Srv::processInfRequest(const Pkt6Ptr& inf_request) {
copyClientOptions(inf_request, reply);
// Build the configured option list for append methods
buildCfgOptionList(inf_request, ctx);
CfgOptionList co_list;
buildCfgOptionList(inf_request, ctx, co_list);
// Append default options, i.e. options that the server is supposed
// to put in all messages it sends (server-id for now, but possibly other
// options once we start supporting authentication)
appendDefaultOptions(inf_request, reply);
appendDefaultOptions(inf_request, reply, co_list);
// Try to assign options that were requested by the client.
appendRequestedOptions(inf_request, reply, ctx);
appendRequestedOptions(inf_request, reply, ctx, co_list);
return (reply);
}

View File

@ -429,24 +429,17 @@ protected:
/// @param answer server's message (options will be copied here)
void copyClientOptions(const Pkt6Ptr& question, Pkt6Ptr& answer);
/// @brief Returns the configured option list
CfgOptionList& getCfgOptionList() {
return (cfg_option_list_);
}
/// @brief Returns the configured option list
const CfgOptionList& getCfgOptionList() const {
return (cfg_option_list_);
}
/// @brief Build the configured option list
///
/// @note The configured option list is an *ordered* list of
/// @c CfgOption objects used to append options to the response.
///
/// @param ex The exchange where the configured option list is cached
/// @param question client's message
/// @param ctx client context (for the subnet)
/// @param co_list configured option list to build
void buildCfgOptionList(const Pkt6Ptr& question,
AllocEngine::ClientContext6& ctx);
AllocEngine::ClientContext6& ctx,
CfgOptionList& co_list);
/// @brief Appends default options to server's answer.
///
@ -456,7 +449,9 @@ protected:
///
/// @param question client's message
/// @param answer server's message (options will be added here)
void appendDefaultOptions(const Pkt6Ptr& question, Pkt6Ptr& answer);
/// @param co_list configured option list (currently unused)
void appendDefaultOptions(const Pkt6Ptr& question, Pkt6Ptr& answer,
const CfgOptionList& co_list);
/// @brief Appends requested options to server's answer.
///
@ -465,8 +460,10 @@ protected:
/// @param question client's message
/// @param answer server's message (options will be added here)
/// @param ctx client context (contains subnet, duid and other parameters)
/// @param co_list configured option list
void appendRequestedOptions(const Pkt6Ptr& question, Pkt6Ptr& answer,
AllocEngine::ClientContext6& ctx);
AllocEngine::ClientContext6& ctx,
const CfgOptionList& co_list);
/// @brief Appends requested vendor options to server's answer.
///
@ -476,8 +473,10 @@ protected:
/// @param question client's message
/// @param answer server's message (vendor options will be added here)
/// @param ctx client context (contains subnet, duid and other parameters)
/// @param co_list configured option list
void appendRequestedVendorOptions(const Pkt6Ptr& question, Pkt6Ptr& answer,
AllocEngine::ClientContext6& ctx);
AllocEngine::ClientContext6& ctx,
const CfgOptionList& co_list);
/// @brief Assigns leases.
///
@ -825,9 +824,6 @@ private:
/// UDP port number on which server listens.
uint16_t port_;
/// @brief Configured option list for appending otions.
CfgOptionList cfg_option_list_;
protected:
/// Indicates if shutdown is in progress. Setting it to true will

View File

@ -2057,10 +2057,8 @@ TEST_F(Dhcp6ParserTest, optionStandardDefOverride) {
EXPECT_FALSE(def->getArrayType());
}
// Goal of this test is to verify that global option
// data is configured for the subnet if the subnet
// configuration does not include options configuration.
TEST_F(Dhcp6ParserTest, optionDataDefaults) {
// Goal of this test is to verify that global option data is configured
TEST_F(Dhcp6ParserTest, optionDataDefaultsGlobal) {
ConstElementPtr x;
string config = "{ " + genIfaceConfig() + ","
"\"preferred-lifetime\": 3000,"
@ -2086,10 +2084,86 @@ TEST_F(Dhcp6ParserTest, optionDataDefaults) {
EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
checkResult(x, 0);
// These options are global
Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
selectSubnet(IOAddress("2001:db8:1::5"), classify_);
ASSERT_TRUE(subnet);
OptionContainerPtr options = subnet->getCfgOption()->getAll("dhcp6");
ASSERT_EQ(0, options->size());
options = CfgMgr::instance().getStagingCfg()->getCfgOption()->getAll("dhcp6");
ASSERT_EQ(2, options->size());
// Get the search index. Index #1 is to search using option code.
const OptionContainerTypeIndex& idx = options->get<1>();
// Get the options for specified index. Expecting one option to be
// returned but in theory we may have multiple options with the same
// code so we get the range.
std::pair<OptionContainerTypeIndex::const_iterator,
OptionContainerTypeIndex::const_iterator> range =
idx.equal_range(D6O_SUBSCRIBER_ID);
// Expect single option with the code equal to 38.
ASSERT_EQ(1, std::distance(range.first, range.second));
const uint8_t subid_expected[] = {
0xAB, 0xCD, 0xEF, 0x01, 0x05
};
// Check if option is valid in terms of code and carried data.
testOption(*range.first, D6O_SUBSCRIBER_ID, subid_expected,
sizeof(subid_expected));
range = idx.equal_range(D6O_PREFERENCE);
ASSERT_EQ(1, std::distance(range.first, range.second));
// Do another round of testing with second option.
const uint8_t pref_expected[] = {
0x01
};
testOption(*range.first, D6O_PREFERENCE, pref_expected,
sizeof(pref_expected));
// Check that options with other option codes are not returned.
for (uint16_t code = 47; code < 57; ++code) {
range = idx.equal_range(code);
EXPECT_EQ(0, std::distance(range.first, range.second));
}
}
// Goal of this test is to verify that subnet option data is configured
TEST_F(Dhcp6ParserTest, optionDataDefaultsSubnet) {
ConstElementPtr x;
string config = "{ " + genIfaceConfig() + ","
"\"preferred-lifetime\": 3000,"
"\"rebind-timer\": 2000,"
"\"renew-timer\": 1000,"
"\"subnet6\": [ { "
" \"pools\": [ { \"pool\": \"2001:db8:1::/80\" } ],"
" \"subnet\": \"2001:db8:1::/64\","
" \"option-data\": [ {"
" \"name\": \"subscriber-id\","
" \"data\": \"ABCDEF0105\","
" \"csv-format\": False"
" },"
" {"
" \"name\": \"preference\","
" \"data\": \"01\""
" } ]"
" } ],"
"\"valid-lifetime\": 4000 }";
ElementPtr json = Element::fromJSON(config);
EXPECT_NO_THROW(x = configureDhcp6Server(srv_, json));
checkResult(x, 0);
// These options are subnet options
OptionContainerPtr options =
CfgMgr::instance().getStagingCfg()->getCfgOption()->getAll("dhcp6");
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");
ASSERT_EQ(2, options->size());
// Get the search index. Index #1 is to search using option code.
@ -2173,21 +2247,21 @@ TEST_F(Dhcp6ParserTest, optionDataTwoSpaces) {
ASSERT_TRUE(status);
checkResult(status, 0);
// Options should be now available for the subnet.
Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
selectSubnet(IOAddress("2001:db8:1::5"), classify_);
ASSERT_TRUE(subnet);
// Options should be now available
// Try to get the option from the space dhcp6.
OptionDescriptor desc1 = subnet->getCfgOption()->get("dhcp6", 38);
OptionDescriptor desc1 =
CfgMgr::instance().getStagingCfg()->getCfgOption()->get("dhcp6", 38);
ASSERT_TRUE(desc1.option_);
EXPECT_EQ(38, desc1.option_->getType());
// Try to get the option from the space isc.
OptionDescriptor desc2 = subnet->getCfgOption()->get("isc", 38);
OptionDescriptor desc2 =
CfgMgr::instance().getStagingCfg()->getCfgOption()->get("isc", 38);
ASSERT_TRUE(desc2.option_);
EXPECT_EQ(38, desc1.option_->getType());
// Try to get the non-existing option from the non-existing
// option space and expect that option is not returned.
OptionDescriptor desc3 = subnet->getCfgOption()->get("non-existing", 38);
OptionDescriptor desc3 = CfgMgr::instance().getStagingCfg()->
getCfgOption()->get("non-existing", 38);
ASSERT_FALSE(desc3.option_);
}
@ -2306,18 +2380,15 @@ TEST_F(Dhcp6ParserTest, optionDataEncapsulate) {
ASSERT_TRUE(status);
checkResult(status, 0);
// Get the subnet.
Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
selectSubnet(IOAddress("2001:db8:1::5"), classify_);
ASSERT_TRUE(subnet);
// We should have one option available.
OptionContainerPtr options = subnet->getCfgOption()->getAll("dhcp6");
OptionContainerPtr options =
CfgMgr::instance().getStagingCfg()->getCfgOption()->getAll("dhcp6");
ASSERT_TRUE(options);
ASSERT_EQ(1, options->size());
// Get the option.
OptionDescriptor desc = subnet->getCfgOption()->get("dhcp6", 100);
OptionDescriptor desc =
CfgMgr::instance().getStagingCfg()->getCfgOption()->get("dhcp6", 100);
EXPECT_TRUE(desc.option_);
EXPECT_EQ(100, desc.option_->getType());
@ -2681,23 +2752,22 @@ TEST_F(Dhcp6ParserTest, vendorOptionsHex) {
ASSERT_TRUE(status);
checkResult(status, 0);
// Options should be now available for the subnet.
Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
selectSubnet(IOAddress("2001:db8:1::5"), classify_);
ASSERT_TRUE(subnet);
// Options should be now available
// Try to get the option from the vendor space 4491
OptionDescriptor desc1 = subnet->getCfgOption()->get(4491, 100);
OptionDescriptor desc1 =
CfgMgr::instance().getStagingCfg()->getCfgOption()->get(4491, 100);
ASSERT_TRUE(desc1.option_);
EXPECT_EQ(100, desc1.option_->getType());
// Try to get the option from the vendor space 1234
OptionDescriptor desc2 = subnet->getCfgOption()->get(1234, 100);
OptionDescriptor desc2 =
CfgMgr::instance().getStagingCfg()->getCfgOption()->get(1234, 100);
ASSERT_TRUE(desc2.option_);
EXPECT_EQ(100, desc1.option_->getType());
// Try to get the non-existing option from the non-existing
// option space and expect that option is not returned.
OptionDescriptor desc3 = subnet->getCfgOption()->get(5678, 38);
OptionDescriptor desc3 =
CfgMgr::instance().getStagingCfg()->getCfgOption()->get(5678, 38);
ASSERT_FALSE(desc3.option_);
}
@ -2739,19 +2809,17 @@ TEST_F(Dhcp6ParserTest, vendorOptionsCsv) {
ASSERT_TRUE(status);
checkResult(status, 0);
// Options should be now available for the subnet.
Subnet6Ptr subnet = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
selectSubnet(IOAddress("2001:db8:1::5"), classify_);
ASSERT_TRUE(subnet);
// Options should be now available.
// Try to get the option from the vendor space 4491
OptionDescriptor desc1 = subnet->getCfgOption()->get(4491, 100);
OptionDescriptor desc1 =
CfgMgr::instance().getStagingCfg()->getCfgOption()->get(4491, 100);
ASSERT_TRUE(desc1.option_);
EXPECT_EQ(100, desc1.option_->getType());
// Try to get the non-existing option from the non-existing
// option space and expect that option is not returned.
OptionDescriptor desc2 = subnet->getCfgOption()->get(5678, 100);
OptionDescriptor desc2 =
CfgMgr::instance().getStagingCfg()->getCfgOption()->get(5678, 100);
ASSERT_FALSE(desc2.option_);
}

View File

@ -1807,8 +1807,8 @@ TEST_F(Dhcpv6SrvTest, unpackOptions) {
EXPECT_EQ(0x0, option_bar->getValue());
}
// Checks if client packets are classified properly
TEST_F(Dhcpv6SrvTest, clientClassification) {
// Checks if DOCSIS client packets are classified properly
TEST_F(Dhcpv6SrvTest, docsisClientClassification) {
NakedDhcpv6Srv srv(0);
@ -1836,10 +1836,252 @@ TEST_F(Dhcpv6SrvTest, clientClassification) {
EXPECT_FALSE(sol2->inClass(srv.VENDOR_CLASS_PREFIX + "docsis3.0"));
}
// Checks if client packets are classified properly using match expressions.
TEST_F(Dhcpv6SrvTest, matchClassification) {
IfaceMgrTestConfig test_config(true);
NakedDhcpv6Srv srv(0);
// The router class matches incoming packets with foo in a host-name
// option (code 1234) and sets an ipv6-forwarding option in the response.
string config = "{ \"interfaces-config\": {"
" \"interfaces\": [ \"*\" ] }, "
"\"preferred-lifetime\": 3000,"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"valid-lifetime\": 4000, "
"\"option-def\": [ "
"{ \"name\": \"host-name\","
" \"code\": 1234,"
" \"type\": \"string\" },"
"{ \"name\": \"ipv6-forwarding\","
" \"code\": 2345,"
" \"type\": \"boolean\" }],"
"\"subnet6\": [ "
"{ \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ], "
" \"subnet\": \"2001:db8:1::/48\", "
" \"interface\": \"eth1\" } ],"
"\"client-classes\": [ "
"{ \"name\": \"router\", "
" \"option-data\": ["
" { \"name\": \"ipv6-forwarding\", "
" \"data\": \"true\" } ], "
" \"test\": \"option[1234] == 'foo'\" } ] }";
ASSERT_NO_THROW(configure(config));
// Create packets with enough to select the subnet
OptionPtr clientid = generateClientId();
Pkt6Ptr query1(new Pkt6(DHCPV6_SOLICIT, 1234));
query1->setRemoteAddr(IOAddress("fe80::abcd"));
query1->addOption(clientid);
query1->setIface("eth1");
query1->addOption(generateIA(D6O_IA_NA, 123, 1500, 3000));
Pkt6Ptr query2(new Pkt6(DHCPDISCOVER, 1234));
query2->setRemoteAddr(IOAddress("fe80::abcd"));
query2->addOption(clientid);
query2->setIface("eth1");
query2->addOption(generateIA(D6O_IA_NA, 234, 1500, 3000));
Pkt6Ptr query3(new Pkt6(DHCPDISCOVER, 1234));
query3->setRemoteAddr(IOAddress("fe80::abcd"));
query3->addOption(clientid);
query3->setIface("eth1");
query3->addOption(generateIA(D6O_IA_NA, 345, 1500, 3000));
// Create and add an ORO option to the first 2 queries
OptionUint16ArrayPtr oro(new OptionUint16Array(Option::V6, D6O_ORO));
ASSERT_TRUE(oro);
oro->addValue(2345);
query1->addOption(oro);
query2->addOption(oro);
// Create and add a host-name option to the first and last queries
OptionStringPtr hostname(new OptionString(Option::V6, 1234, "foo"));
ASSERT_TRUE(hostname);
query1->addOption(hostname);
query3->addOption(hostname);
// Classify packets
srv.classifyPacket(query1);
srv.classifyPacket(query2);
srv.classifyPacket(query3);
// Packets at the exception of the second should be in the router class
EXPECT_TRUE(query1->inClass("router"));
EXPECT_FALSE(query2->inClass("router"));
EXPECT_TRUE(query3->inClass("router"));
// Process queries
Pkt6Ptr response1 = srv.processSolicit(query1);
Pkt6Ptr response2 = srv.processSolicit(query2);
Pkt6Ptr response3 = srv.processSolicit(query3);
// Classification processing should add an ip-forwarding option
OptionPtr opt1 = response1->getOption(2345);
EXPECT_TRUE(opt1);
// But only for the first exchange: second was not classified
OptionPtr opt2 = response2->getOption(2345);
EXPECT_FALSE(opt2);
// But only for the first exchange: third has no ORO
OptionPtr opt3 = response3->getOption(2345);
EXPECT_FALSE(opt3);
}
// Checks subnet options have the priority over class options
TEST_F(Dhcpv6SrvTest, subnetClassPriority) {
IfaceMgrTestConfig test_config(true);
NakedDhcpv6Srv srv(0);
// Subnet sets an ipv6-forwarding option in the response.
// The router class matches incoming packets with foo in a host-name
// option (code 1234) and sets an ipv6-forwarding option in the response.
string config = "{ \"interfaces-config\": {"
" \"interfaces\": [ \"*\" ] }, "
"\"preferred-lifetime\": 3000,"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"valid-lifetime\": 4000, "
"\"option-def\": [ "
"{ \"name\": \"host-name\","
" \"code\": 1234,"
" \"type\": \"string\" },"
"{ \"name\": \"ipv6-forwarding\","
" \"code\": 2345,"
" \"type\": \"boolean\" }],"
"\"subnet6\": [ "
"{ \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ], "
" \"subnet\": \"2001:db8:1::/48\", "
" \"interface\": \"eth1\", "
" \"option-data\": ["
" { \"name\": \"ipv6-forwarding\", "
" \"data\": \"false\" } ] } ], "
"\"client-classes\": [ "
"{ \"name\": \"router\","
" \"option-data\": ["
" { \"name\": \"ipv6-forwarding\", "
" \"data\": \"true\" } ], "
" \"test\": \"option[1234] == 'foo'\" } ] }";
ASSERT_NO_THROW(configure(config));
// Create a packet with enough to select the subnet and go through
// the SOLICIT processing
Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 1234));
query->setRemoteAddr(IOAddress("fe80::abcd"));
OptionPtr clientid = generateClientId();
query->addOption(clientid);
query->setIface("eth1");
query->addOption(generateIA(D6O_IA_NA, 123, 1500, 3000));
// Create and add a ORO option to the query
OptionUint16ArrayPtr oro(new OptionUint16Array(Option::V6, D6O_ORO));
ASSERT_TRUE(oro);
oro->addValue(2345);
query->addOption(oro);
// Create and add a host-name option to the query
OptionStringPtr hostname(new OptionString(Option::V6, 1234, "foo"));
ASSERT_TRUE(hostname);
query->addOption(hostname);
// Classify the packet
srv.classifyPacket(query);
// The packet should be in the router class
EXPECT_TRUE(query->inClass("router"));
// Process the query
Pkt6Ptr response = srv.processSolicit(query);
// A processing should add an ip-forwarding option
OptionPtr opt = response->getOption(2345);
ASSERT_TRUE(opt);
ASSERT_GT(opt->len(), opt->getHeaderLen());
// Classification sets the value to true/1, subnet to false/0
// Here subnet has the priority
EXPECT_EQ(0, opt->getUint8());
}
// Checks class options have the priority over global options
TEST_F(Dhcpv6SrvTest, classGlobalPriority) {
IfaceMgrTestConfig test_config(true);
NakedDhcpv6Srv srv(0);
// A global ipv6-forwarding option is set in the response.
// The router class matches incoming packets with foo in a host-name
// option (code 1234) and sets an ipv6-forwarding option in the response.
string config = "{ \"interfaces-config\": {"
" \"interfaces\": [ \"*\" ] }, "
"\"preferred-lifetime\": 3000,"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"valid-lifetime\": 4000, "
"\"option-def\": [ "
"{ \"name\": \"host-name\","
" \"code\": 1234,"
" \"type\": \"string\" },"
"{ \"name\": \"ipv6-forwarding\","
" \"code\": 2345,"
" \"type\": \"boolean\" }],"
"\"subnet6\": [ "
"{ \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ], "
" \"subnet\": \"2001:db8:1::/48\", "
" \"interface\": \"eth1\" } ],"
"\"option-data\": ["
" { \"name\": \"ipv6-forwarding\", "
" \"data\": \"false\" } ], "
"\"client-classes\": [ "
"{ \"name\": \"router\","
" \"option-data\": ["
" { \"name\": \"ipv6-forwarding\", "
" \"data\": \"true\" } ], "
" \"test\": \"option[1234] == 'foo'\" } ] }";
ASSERT_NO_THROW(configure(config));
// Create a packet with enough to select the subnet and go through
// the SOLICIT processing
Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 1234));
query->setRemoteAddr(IOAddress("fe80::abcd"));
OptionPtr clientid = generateClientId();
query->addOption(clientid);
query->setIface("eth1");
query->addOption(generateIA(D6O_IA_NA, 123, 1500, 3000));
// Create and add a ORO option to the query
OptionUint16ArrayPtr oro(new OptionUint16Array(Option::V6, D6O_ORO));
ASSERT_TRUE(oro);
oro->addValue(2345);
query->addOption(oro);
// Create and add a host-name option to the query
OptionStringPtr hostname(new OptionString(Option::V6, 1234, "foo"));
ASSERT_TRUE(hostname);
query->addOption(hostname);
// Classify the packet
srv.classifyPacket(query);
// The packet should be in the router class
EXPECT_TRUE(query->inClass("router"));
// Process the query
Pkt6Ptr response = srv.processSolicit(query);
// A processing should add an ip-forwarding option
OptionPtr opt = response->getOption(2345);
ASSERT_TRUE(opt);
ASSERT_GT(opt->len(), opt->getHeaderLen());
// Classification sets the value to true/1, global to false/0
// Here class has the priority
EXPECT_NE(0, opt->getUint8());
}
// Checks if the client-class field is indeed used for subnet selection.
// Note that packet classification is already checked in Dhcpv6SrvTest
// .clientClassification above.
TEST_F(Dhcpv6SrvTest, clientClassify2) {
// .*Classification above.
TEST_F(Dhcpv6SrvTest, clientClassifySubnet) {
// This test configures 2 subnets. We actually only need the
// first one, but since there's still this ugly hack that picks
@ -1895,7 +2137,7 @@ TEST_F(Dhcpv6SrvTest, clientClassify2) {
// Tests whether a packet with custom vendor-class (not erouter or docsis)
// is classified properly.
TEST_F(Dhcpv6SrvTest, clientClassification3) {
TEST_F(Dhcpv6SrvTest, vendorClientClassification2) {
NakedDhcpv6Srv srv(0);
// Let's create a SOLICIT.

View File

@ -730,6 +730,12 @@ OptionDataListParser::commit() {
BOOST_FOREACH(ParserPtr parser, parsers_) {
parser->commit();
}
// Append suboptions to the top-level options
if (cfg_) {
cfg_->encapsulate();
} else {
CfgMgr::instance().getStagingCfg()->getCfgOption()->encapsulate();
}
}
// ******************************** OptionDefParser ****************************
@ -1260,10 +1266,8 @@ SubnetConfigParser::createSubnet() {
// options but it is no longer the case (they have a different
// and not consecutive priority).
// Copy all options to the subnet configuration.
// Copy options to the subnet configuration.
options_->copyTo(*subnet_->getCfgOption());
// Append suboptions to the top-level options.
subnet_->getCfgOption()->encapsulate();
}
isc::dhcp::Triplet<uint32_t>

View File

@ -718,7 +718,8 @@ public:
/// @brief Commit all option values.
///
/// This function invokes commit for all option values.
/// This function invokes commit for all option values
/// and append suboptions to the top-level options.
void commit();
private: