Commit 0a602468 authored by Marcin Siodelski's avatar Marcin Siodelski

[5022] DHCPv6 server now supports specifying options on pool level.

parent 6cfae24d
......@@ -820,6 +820,24 @@ Dhcpv6Srv::buildCfgOptionList(const Pkt6Ptr& question,
co_list.push_back(ctx.host_->getCfgOption6());
}
// Secondly, pool specific options. Pools are defined within a subnet, so
// if there is no subnet, there is nothing to do.
if (ctx.subnet_) {
BOOST_FOREACH(const AllocEngine::ResourceType& resource,
ctx.allocated_resources_) {
/// @todo This is has significant performance implications. We
/// are performing full scan of pools within this subnet to
/// find the one we're interested in. We need to implement the
/// Patricia trie based storage for pools.
PoolPtr pool = ctx.subnet_->getPool(resource.second == 128 ?
Lease::TYPE_NA : Lease::TYPE_PD,
resource.first, false);
if (pool && !pool->getCfgOption()->empty()) {
co_list.push_back(pool->getCfgOption());
}
}
};
// Next, subnet configured options.
if (ctx.subnet_ && !ctx.subnet_->getCfgOption()->empty()) {
co_list.push_back(ctx.subnet_->getCfgOption());
......@@ -2316,6 +2334,9 @@ Dhcpv6Srv::processSolicit(const Pkt6Ptr& solicit) {
}
}
processClientFqdn(solicit, response, ctx);
assignLeases(solicit, response, ctx);
copyClientOptions(solicit, response);
CfgOptionList co_list;
buildCfgOptionList(solicit, ctx, co_list);
......@@ -2323,9 +2344,6 @@ Dhcpv6Srv::processSolicit(const Pkt6Ptr& solicit) {
appendRequestedOptions(solicit, response, co_list);
appendRequestedVendorOptions(solicit, response, ctx, co_list);
processClientFqdn(solicit, response, ctx);
assignLeases(solicit, response, ctx);
// Only generate name change requests if sending a Reply as a result
// of receiving Rapid Commit option.
if (response->getType() == DHCPV6_REPLY) {
......@@ -2347,6 +2365,9 @@ Dhcpv6Srv::processRequest(const Pkt6Ptr& request) {
Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, request->getTransid()));
processClientFqdn(request, reply, ctx);
assignLeases(request, reply, ctx);
copyClientOptions(request, reply);
CfgOptionList co_list;
buildCfgOptionList(request, ctx, co_list);
......@@ -2354,8 +2375,6 @@ Dhcpv6Srv::processRequest(const Pkt6Ptr& request) {
appendRequestedOptions(request, reply, co_list);
appendRequestedVendorOptions(request, reply, ctx, co_list);
processClientFqdn(request, reply, ctx);
assignLeases(request, reply, ctx);
generateFqdn(reply);
createNameChangeRequests(reply, ctx);
......@@ -2374,6 +2393,9 @@ Dhcpv6Srv::processRenew(const Pkt6Ptr& renew) {
Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, renew->getTransid()));
processClientFqdn(renew, reply, ctx);
extendLeases(renew, reply, ctx);
copyClientOptions(renew, reply);
CfgOptionList co_list;
buildCfgOptionList(renew, ctx, co_list);
......@@ -2381,8 +2403,6 @@ Dhcpv6Srv::processRenew(const Pkt6Ptr& renew) {
appendRequestedOptions(renew, reply, co_list);
appendRequestedVendorOptions(renew, reply, ctx, co_list);
processClientFqdn(renew, reply, ctx);
extendLeases(renew, reply, ctx);
generateFqdn(reply);
createNameChangeRequests(reply, ctx);
......@@ -2401,6 +2421,9 @@ Dhcpv6Srv::processRebind(const Pkt6Ptr& rebind) {
Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, rebind->getTransid()));
processClientFqdn(rebind, reply, ctx);
extendLeases(rebind, reply, ctx);
copyClientOptions(rebind, reply);
CfgOptionList co_list;
buildCfgOptionList(rebind, ctx, co_list);
......@@ -2408,8 +2431,6 @@ Dhcpv6Srv::processRebind(const Pkt6Ptr& rebind) {
appendRequestedOptions(rebind, reply, co_list);
appendRequestedVendorOptions(rebind, reply, ctx, co_list);
processClientFqdn(rebind, reply, ctx);
extendLeases(rebind, reply, ctx);
generateFqdn(reply);
createNameChangeRequests(reply, ctx);
......
......@@ -206,6 +206,8 @@ public:
// Attempt to construct the local pool.
pool_.reset(new Pool6(Lease::TYPE_PD, IOAddress(addr_str),
prefix_len, delegated_len));
// Merge options specified for a pool into pool configuration.
options_->copyTo(*pool_->getCfgOption());
} catch (const std::exception& ex) {
// Some parameters don't exist or are invalid. Since we are not
// aware whether they don't exist or are invalid, let's append
......
......@@ -2556,7 +2556,9 @@ TEST_F(Dhcp6ParserTest, optionDataInMultipleSubnets) {
sizeof(user_class_expected));
}
TEST_F(Dhcp6ParserTest, optionDataInMultiplePools) {
// This test verifies that it is possible to specify options on
// pool levels.
TEST_F(Dhcp6ParserTest, optionDataMultiplePools) {
ConstElementPtr x;
string config = "{ " + genIfaceConfig() + ","
"\"preferred-lifetime\": 3000,"
......@@ -2564,20 +2566,20 @@ TEST_F(Dhcp6ParserTest, optionDataInMultiplePools) {
"\"renew-timer\": 1000, "
"\"subnet6\": [ { "
" \"pools\": [ { "
" \"pool\": \"2001:db8:1::10 - 2001:db8:1::100\""
/* " \"option-data\": [ {"
" \"pool\": \"2001:db8:1::10 - 2001:db8:1::100\","
" \"option-data\": [ {"
" \"name\": \"subscriber-id\","
" \"data\": \"0102030405060708090A\","
" \"csv-format\": False"
" } ]" */
" } ]"
" },"
" {"
" \"pool\": \"2001:db8:1::300 - 2001:db8:1::400\""
/* " \"option-data\": [ {"
" \"pool\": \"2001:db8:1::300 - 2001:db8:1::400\","
" \"option-data\": [ {"
" \"name\": \"user-class\","
" \"data\": \"FFFEFDFCFB\","
" \"csv-format\": False"
" } ]" */
" } ]"
" } ],"
" \"pd-pools\": [ { "
" \"prefix\": \"3000::\","
......@@ -2615,7 +2617,7 @@ TEST_F(Dhcp6ParserTest, optionDataInMultiplePools) {
PoolPtr pool = subnet->getPool(Lease::TYPE_PD, IOAddress("3000::"), false);
ASSERT_TRUE(pool);
Pool6Ptr pool6 = boost::dynamic_pointer_cast<Pool6>(pool);
ASSERT_TRUE(pool6);
OptionContainerPtr options1 = pool6->getCfgOption()->getAll("dhcp6");
ASSERT_EQ(1, options1->size());
......@@ -2629,21 +2631,22 @@ TEST_F(Dhcp6ParserTest, optionDataInMultiplePools) {
std::pair<OptionContainerTypeIndex::const_iterator,
OptionContainerTypeIndex::const_iterator> range1 =
idx1.equal_range(D6O_SUBSCRIBER_ID);
// Expect single option with the code equal to 38.
// Expect a single Subscriber ID option instance.
ASSERT_EQ(1, std::distance(range1.first, range1.second));
const uint8_t subid_expected[] = {
0x01, 0x02, 0x03, 0x04, 0x05,
0x06, 0x07, 0x08, 0x09, 0x0A
const uint8_t subscriber_id_expected[] = {
0x11, 0x22, 0x33, 0x44, 0x55, 0x66
};
// Check if option is valid in terms of code and carried data.
testOption(*range1.first, D6O_SUBSCRIBER_ID, subid_expected,
sizeof(subid_expected));
testOption(*range1.first, D6O_SUBSCRIBER_ID, subscriber_id_expected,
sizeof(subscriber_id_expected));
/* // Test another subnet in the same way.
Subnet6Ptr subnet2 = CfgMgr::instance().getStagingCfg()->getCfgSubnets6()->
selectSubnet(IOAddress("2001:db8:2::4"), classify_);
ASSERT_TRUE(subnet2);
OptionContainerPtr options2 = subnet2->getCfgOption()->getAll("dhcp6");
// Test another pool in the same way.
pool = subnet->getPool(Lease::TYPE_PD, IOAddress("3001::"), false);
ASSERT_TRUE(pool);
pool6 = boost::dynamic_pointer_cast<Pool6>(pool);
ASSERT_TRUE(pool6);
OptionContainerPtr options2 = pool6->getCfgOption()->getAll("dhcp6");
ASSERT_EQ(1, options2->size());
const OptionContainerTypeIndex& idx2 = options2->get<1>();
......@@ -2653,11 +2656,52 @@ TEST_F(Dhcp6ParserTest, optionDataInMultiplePools) {
ASSERT_EQ(1, std::distance(range2.first, range2.second));
const uint8_t user_class_expected[] = {
0xFF, 0xFE, 0xFD, 0xFC, 0xFB
0xAA, 0xBB, 0xCC, 0xDD, 0xEE
};
testOption(*range2.first, D6O_USER_CLASS, user_class_expected,
sizeof(user_class_expected)); */
sizeof(user_class_expected));
// Test options in NA pools.
pool = subnet->getPool(Lease::TYPE_NA, IOAddress("2001:db8:1::10"));
ASSERT_TRUE(pool);
pool6 = boost::dynamic_pointer_cast<Pool6>(pool);
ASSERT_TRUE(pool6);
OptionContainerPtr options3 = pool6->getCfgOption()->getAll("dhcp6");
ASSERT_EQ(1, options3->size());
const OptionContainerTypeIndex& idx3 = options3->get<1>();
std::pair<OptionContainerTypeIndex::const_iterator,
OptionContainerTypeIndex::const_iterator> range3 =
idx3.equal_range(D6O_SUBSCRIBER_ID);
ASSERT_EQ(1, std::distance(range3.first, range3.second));
const uint8_t subscriber_id_expected2[] = {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A
};
testOption(*range3.first, D6O_SUBSCRIBER_ID, subscriber_id_expected2,
sizeof(subscriber_id_expected2));
pool = subnet->getPool(Lease::TYPE_NA, IOAddress("2001:db8:1::300"));
ASSERT_TRUE(pool);
pool6 = boost::dynamic_pointer_cast<Pool6>(pool);
ASSERT_TRUE(pool6);
OptionContainerPtr options4 = pool6->getCfgOption()->getAll("dhcp6");
ASSERT_EQ(1, options4->size());
const OptionContainerTypeIndex& idx4 = options4->get<1>();
std::pair<OptionContainerTypeIndex::const_iterator,
OptionContainerTypeIndex::const_iterator> range4 =
idx4.equal_range(D6O_USER_CLASS);
ASSERT_EQ(1, std::distance(range4.first, range4.second));
const uint8_t user_class_expected2[] = {
0xFF, 0xFE, 0xFD, 0xFC, 0xFB
};
testOption(*range4.first, D6O_USER_CLASS, user_class_expected2,
sizeof(user_class_expected2));
}
// The goal of this test is to check that the option carrying a boolean
......
......@@ -8,11 +8,12 @@
#include <dhcp/dhcp6.h>
#include <dhcp/docsis3_option_defs.h>
#include <dhcp/option_custom.h>
#include <dhcp/option_int_array.h>
#include <dhcp/option_vendor.h>
#include <dhcp/option6_addrlst.h>
#include <dhcp/option6_ia.h>
#include <dhcp/option6_iaaddr.h>
#include <dhcp/option6_status_code.h>
#include <dhcp/option_int_array.h>
#include <dhcp/option_vendor.h>
#include <dhcp/pkt6.h>
#include <dhcpsrv/lease.h>
#include <dhcpsrv/pool.h>
......@@ -20,6 +21,7 @@
#include <util/buffer.h>
#include <boost/foreach.hpp>
#include <boost/pointer_cast.hpp>
#include <algorithm>
#include <cstdlib>
#include <time.h>
......@@ -803,6 +805,20 @@ Dhcp6Client::hasLeaseWithZeroLifetimeForPrefix(const asiolink::IOAddress& prefix
return (false);
}
bool
Dhcp6Client::hasOptionWithAddress(const uint16_t option_type,
const std::string& expected_address) const {
Option6AddrLstPtr opt = boost::dynamic_pointer_cast<
Option6AddrLst>(config_.findOption(option_type));
if (opt) {
Option6AddrLst::AddressContainer addrs = opt->getAddresses();
if (!addrs.empty()) {
return (std::find(addrs.begin(), addrs.end(),
IOAddress(expected_address)) != addrs.end());
}
}
return (false);
}
uint16_t
Dhcp6Client::getStatusCode(const uint32_t iaid) const {
......
......@@ -468,6 +468,19 @@ public:
bool hasLeaseWithZeroLifetimeForPrefix(const asiolink::IOAddress& prefix,
const uint8_t prefix_len) const;
/// @brief Checks that specified option exists and contains a desired
/// address.
///
/// The option must cast to the @ref Option6AddrLst type. The function
/// expects that this option contains at least one address and checks
/// first address for equality with @ref expected_address.
///
/// @param option_type Option type.
/// @param expected_address Desired address.
/// @param config Configuration obtained from the server.
bool hasOptionWithAddress(const uint16_t option_type,
const std::string& expected_address) const;
/// @brief Returns the value of the global status code for the last
/// transaction.
uint16_t getStatusCode() const {
......
......@@ -624,6 +624,7 @@ public:
/// @param stat_name this statistic is expected to be set to 1
void testReceiveStats(uint8_t pkt_type, const std::string& stat_name);
/// A subnet used in most tests
isc::dhcp::Subnet6Ptr subnet_;
......
......@@ -69,6 +69,15 @@ namespace {
/// - 1 subnet for eth0 and 1 subnet for eth1
/// - DOCSIS vendor config file sub-option
///
/// - Configuration 8:
/// - single subnet 3000::/32,
/// - two options specified in the subnet scope,
/// - one option specified at the global scope,
/// - two address pools: 3000::10-3000::20, 3000::40-3000::50,
/// - two prefix pools: 2001:db8:3::/64 and 2001:db8:4::/64,
/// - an option with unique value specified for each pool, so as it is
/// possible to test that pool specific options can be assigned.
///
const char* REBIND_CONFIGS[] = {
// Configuration 0
"{ \"interfaces-config\": {"
......@@ -256,7 +265,69 @@ const char* REBIND_CONFIGS[] = {
" \"interface-id\": \"\","
" \"interface\": \"eth1\""
" } ],"
"\"valid-lifetime\": 4000 }"
"\"valid-lifetime\": 4000 }",
// Configuration 8
"{ \"interfaces-config\": {"
" \"interfaces\": [ \"*\" ]"
"},"
"\"preferred-lifetime\": 3000,"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"option-data\": [ {"
" \"name\": \"dns-servers\","
" \"data\": \"3000:1::234\""
"},"
"{"
" \"name\": \"sntp-servers\","
" \"data\": \"3000:2::1\""
"} ],"
"\"subnet6\": [ { "
" \"option-data\": [ {"
" \"name\": \"dns-servers\","
" \"data\": \"3000:1::567\""
" },"
" {"
" \"name\": \"sntp-servers\","
" \"data\": \"3000:2::1\""
" } ],"
" \"pools\": [ { "
" \"pool\": \"3000::10 - 3000::20\","
" \"option-data\": [ {"
" \"name\": \"sntp-servers\","
" \"data\": \"3000:2::2\""
" } ]"
" },"
" {"
" \"pool\": \"3000::40 - 3000::50\","
" \"option-data\": [ {"
" \"name\": \"nisp-servers\","
" \"data\": \"3000:2::3\""
" } ]"
" } ],"
" \"pd-pools\": [ { "
" \"prefix\": \"2001:db8:3::\","
" \"prefix-len\": 64,"
" \"delegated-len\": 64,"
" \"option-data\": [ {"
" \"name\": \"dns-servers\","
" \"data\": \"3000:1::678\""
" } ]"
" },"
" {"
" \"prefix\": \"2001:db8:4::\","
" \"prefix-len\": 64,"
" \"delegated-len\": 64,"
" \"option-data\": [ {"
" \"name\": \"nis-servers\","
" \"data\": \"3000:1::789\""
" } ]"
" } ],"
" \"subnet\": \"3000::/32\", "
" \"interface\": \"eth0\""
" } ],"
"\"valid-lifetime\": 4000"
"}"
};
/// @brief Test fixture class for testing Rebind.
......@@ -1004,4 +1075,74 @@ TEST_F(RebindTest, docsisORO) {
EXPECT_EQ("normal_erouter_v6.cm", config_file->getValue());
}
// This test verifies that the same options can be specified on the global
// level, subnet level and pool level. The options associated with pools
// are used when the lease is handed out from these pools.
TEST_F(RebindTest, optionsInheritance) {
Dhcp6Client client;
// Request a single address and single prefix.
ASSERT_NO_THROW(client.requestPrefix(0xabac, 64, IOAddress("2001:db8:4::")));
ASSERT_NO_THROW(client.requestAddress(0xabca, IOAddress("3000::45")));
// Request two options configured for the pools from which the client may get
// a lease.
client.requestOption(D6O_NAME_SERVERS);
client.requestOption(D6O_NIS_SERVERS);
client.requestOption(D6O_NISP_SERVERS);
client.requestOption(D6O_SNTP_SERVERS);
ASSERT_NO_FATAL_FAILURE(configure(REBIND_CONFIGS[8], *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());
// Simulate aging of leases.
client.fastFwdTime(1000);
// Send Rebind message to the server.
ASSERT_NO_THROW(client.doRebind());
// We have provided hints so we should get leases appropriate
// for the hints we provided.
ASSERT_TRUE(client.hasLeaseForPrefix(IOAddress("2001:db8:4::"), 64));
ASSERT_TRUE(client.hasLeaseForAddress(IOAddress("3000::45")));
// We shouldn't have leases for the prefix and address which we didn't
// request.
ASSERT_FALSE(client.hasLeaseForPrefix(IOAddress("2001:db8:3::"), 64));
ASSERT_FALSE(client.hasLeaseForAddress(IOAddress("3000::11")));
// We should have received options associated with a prefix pool and
// address pool from which we have requested the leases. We should not
// have received options associated with the remaining pools. Instead,
// we should have received options associated with a subnet.
ASSERT_TRUE(client.hasOptionWithAddress(D6O_NAME_SERVERS, "3000:1::567"));
ASSERT_TRUE(client.hasOptionWithAddress(D6O_NIS_SERVERS, "3000:1::789"));
ASSERT_TRUE(client.hasOptionWithAddress(D6O_NISP_SERVERS, "3000:2::3"));
ASSERT_TRUE(client.hasOptionWithAddress(D6O_SNTP_SERVERS, "3000:2::1"));
// Let's now also request a prefix and an address from the remaining pools.
ASSERT_NO_THROW(client.requestPrefix(0x6806, 64, IOAddress("2001:db8:3::")));
ASSERT_NO_THROW(client.requestAddress(0x6860, IOAddress("3000::11")));
client.fastFwdTime(1000);
// Send another Rebind.
ASSERT_NO_THROW(client.doRebind());
// We should now have two prefixes from two distinct pools.
ASSERT_TRUE(client.hasLeaseForPrefix(IOAddress("2001:db8:3::"), 64));
ASSERT_TRUE(client.hasLeaseForPrefix(IOAddress("2001:db8:4::"), 64));
// We should also have two addresses from two distinct pools.
ASSERT_TRUE(client.hasLeaseForAddress(IOAddress("3000::45")));
ASSERT_TRUE(client.hasLeaseForAddress(IOAddress("3000::11")));
// This time, options from all pools should have been assigned.
ASSERT_TRUE(client.hasOptionWithAddress(D6O_NAME_SERVERS, "3000:1::678"));
ASSERT_TRUE(client.hasOptionWithAddress(D6O_NIS_SERVERS, "3000:1::789"));
ASSERT_TRUE(client.hasOptionWithAddress(D6O_NISP_SERVERS, "3000:2::3"));
ASSERT_TRUE(client.hasOptionWithAddress(D6O_SNTP_SERVERS, "3000:2::2"));
}
} // end of anonymous namespace
......@@ -44,6 +44,15 @@ namespace {
/// - 1 subnet with 2001:db8:1::/64 pool
/// - DOCSIS vendor config file sub-option
///
/// - Configuration 4:
/// - single subnet 3000::/32,
/// - two options specified in the subnet scope,
/// - one option specified at the global scope,
/// - two address pools: 3000::10-3000::20, 3000::40-3000::50,
/// - two prefix pools: 2001:db8:3::/64 and 2001:db8:4::/64,
/// - an option with unique value specified for each pool, so as it is
/// possible to test that pool specific options can be assigned.
///
const char* RENEW_CONFIGS[] = {
// Configuration 0
"{ \"interfaces-config\": {"
......@@ -117,8 +126,69 @@ const char* RENEW_CONFIGS[] = {
" \"interface-id\": \"\","
" \"interface\": \"eth0\""
" } ],"
"\"valid-lifetime\": 4000 }"
"\"valid-lifetime\": 4000 }",
// Configuration 4
"{ \"interfaces-config\": {"
" \"interfaces\": [ \"*\" ]"
"},"
"\"preferred-lifetime\": 3000,"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"option-data\": [ {"
" \"name\": \"dns-servers\","
" \"data\": \"3000:1::234\""
"},"
"{"
" \"name\": \"sntp-servers\","
" \"data\": \"3000:2::1\""
"} ],"
"\"subnet6\": [ { "
" \"option-data\": [ {"
" \"name\": \"dns-servers\","
" \"data\": \"3000:1::567\""
" },"
" {"
" \"name\": \"sntp-servers\","
" \"data\": \"3000:2::1\""
" } ],"
" \"pools\": [ { "
" \"pool\": \"3000::10 - 3000::20\","
" \"option-data\": [ {"
" \"name\": \"sntp-servers\","
" \"data\": \"3000:2::2\""
" } ]"
" },"
" {"
" \"pool\": \"3000::40 - 3000::50\","
" \"option-data\": [ {"
" \"name\": \"nisp-servers\","
" \"data\": \"3000:2::3\""
" } ]"
" } ],"
" \"pd-pools\": [ { "
" \"prefix\": \"2001:db8:3::\","
" \"prefix-len\": 64,"
" \"delegated-len\": 64,"
" \"option-data\": [ {"
" \"name\": \"dns-servers\","
" \"data\": \"3000:1::678\""
" } ]"
" },"
" {"
" \"prefix\": \"2001:db8:4::\","
" \"prefix-len\": 64,"
" \"delegated-len\": 64,"
" \"option-data\": [ {"
" \"name\": \"nis-servers\","
" \"data\": \"3000:1::789\""
" } ]"
" } ],"
" \"subnet\": \"3000::/32\", "
" \"interface\": \"eth0\""
" } ],"
"\"valid-lifetime\": 4000"
"}"
};
/// @brief Test fixture class for testing Renew.
......@@ -472,4 +542,73 @@ TEST_F(RenewTest, docsisORO) {
EXPECT_EQ("normal_erouter_v6.cm", config_file->getValue());
}
// This test verifies that the same options can be specified on the global
// level, subnet level and pool level. The options associated with pools
// are used when the lease is handed out from these pools.
TEST_F(RenewTest, optionsInheritance) {
Dhcp6Client client;
// Request a single address and single prefix.
ASSERT_NO_THROW(client.requestPrefix(0xabac, 64, IOAddress("2001:db8:4::")));
ASSERT_NO_THROW(client.requestAddress(0xabca, IOAddress("3000::45")));
// Request two options configured for the pools from which the client may get
// a lease.
client.requestOption(D6O_NAME_SERVERS);
client.requestOption(D6O_NIS_SERVERS);
client.requestOption(D6O_NISP_SERVERS);
client.requestOption(D6O_SNTP_SERVERS);
ASSERT_NO_FATAL_FAILURE(configure(RENEW_CONFIGS[4], *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());
// Simulate aging of leases.
client.fastFwdTime(1000);
// Send Renew message to the server.
ASSERT_NO_THROW(client.doRenew());
// We have provided hints so we should get leases appropriate
// for the hints we provided.
ASSERT_TRUE(client.hasLeaseForPrefix(IOAddress("2001:db8:4::"), 64));
ASSERT_TRUE(client.hasLeaseForAddress(IOAddress("3000::45")));
// We shouldn't have leases for the prefix and address which we didn't
// request.
ASSERT_FALSE(client.hasLeaseForPrefix(IOAddress("2001:db8:3::"), 64));
ASSERT_FALSE(client.hasLeaseForAddress(IOAddress("3000::11")));
// We should have received options associated with a prefix pool and
// address pool from which we have requested the leases. We should not
// have received options associated with the remaining pools. Instead,
// we should have received options associated with a subnet.
ASSERT_TRUE(client.hasOptionWithAddress(D6O_NAME_SERVERS, "3000:1::567"));
ASSERT_TRUE(client.hasOptionWithAddress(D6O_NIS_SERVERS, "3000:1::789"));
ASSERT_TRUE(client.hasOptionWithAddress(D6O_NISP_SERVERS, "3000:2::3"));
ASSERT_TRUE(client.hasOptionWithAddress(D6O_SNTP_SERVERS, "3000:2::1"));
// Let's now also request a prefix and an address from the remaining pools.
ASSERT_NO_THROW(client.requestPrefix(0x6806, 64, IOAddress("2001:db8:3::")));
ASSERT_NO_THROW(client.requestAddress(0x6860, IOAddress("3000::11")));
client.fastFwdTime(1000);
// Send another Renew.
ASSERT_NO_THROW(client.doRenew());
// We should now have two prefixes from two distinct pools.
ASSERT_TRUE(client.hasLeaseForPrefix(IOAddress("2001:db8:3::"), 64));
ASSERT_TRUE(client.hasLeaseForPrefix(IOAddress("2001:db8:4::"), 64));
// We should also have two addresses from two distinct pools.