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());
}
}
+ processClientFqdn(solicit, response, ctx);
+ assignLeases(solicit, response, ctx);
+
copyClientOptions(solicit, response);
CfgOptionList co_list;
buildCfgOptionList(solicit, ctx, co_list);
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) {
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);
appendRequestedOptions(request, reply, co_list);
appendRequestedVendorOptions(request, reply, ctx, co_list);
- processClientFqdn(request, reply, ctx);
- assignLeases(request, reply, ctx);
generateFqdn(reply);
createNameChangeRequests(reply, ctx);
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);
appendRequestedOptions(renew, reply, co_list);
appendRequestedVendorOptions(renew, reply, ctx, co_list);
- processClientFqdn(renew, reply, ctx);
- extendLeases(renew, reply, ctx);
generateFqdn(reply);
createNameChangeRequests(reply, ctx);
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);
appendRequestedOptions(rebind, reply, co_list);
appendRequestedVendorOptions(rebind, reply, ctx, co_list);
- processClientFqdn(rebind, reply, ctx);
- extendLeases(rebind, reply, ctx);
generateFqdn(reply);
createNameChangeRequests(reply, ctx);
// 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
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,"
"\"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::\","
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());
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>();
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
#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>
#include <util/buffer.h>
#include <boost/foreach.hpp>
#include <boost/pointer_cast.hpp>
+#include <algorithm>
#include <cstdlib>
#include <time.h>
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 {
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 {
/// @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_;
/// - 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\": {"
" \"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.
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
/// - 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\": {"
" \"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.
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.
+ 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
/// one
/// - DNS updates enabled
///
+/// - Configuration 2:
+/// - 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* CONFIGS[] = {
// Configuration 0
"{ \"interfaces-config\": {"
" \"dhcp-ddns\" : {"
" \"enable-updates\" : True, "
" \"qualifying-suffix\" : \"example.com\" }"
+ "}",
+
+// Configuration 2
+ "{ \"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"
"}"
};
ASSERT_TRUE(lease_server);
}
+// 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(SARRTest, 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(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());
+
+ // 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")));
+
+ // Perform 4-way exchange again.
+ ASSERT_NO_THROW(client.doSARR());
+
+ // 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"));
+}
+
// 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) {
}
//****************************** PoolParser ********************************
-PoolParser::PoolParser(const std::string&, PoolStoragePtr pools)
- :pools_(pools) {
+PoolParser::PoolParser(const std::string&, PoolStoragePtr pools)
+ :pools_(pools), options_(new CfgOption()) {
if (!pools_) {
isc_throw(isc::dhcp::DhcpConfigError, "parser logic error: "
boost::erase_all(txt, " "); // space
boost::erase_all(txt, "\t"); // tabulation
+ PoolPtr pool;
+
// Is this prefix/len notation?
size_t pos = txt.find("/");
if (pos != string::npos) {
<< " (" << text_pool->getPosition() << ")");
}
- PoolPtr pool(poolMaker(addr, len));
+ pool = poolMaker(addr, len);
local_pools_.push_back(pool);
- return;
- }
- // Is this min-max notation?
- pos = txt.find("-");
- if (pos != string::npos) {
- // using min-max notation
- isc::asiolink::IOAddress min(txt.substr(0,pos));
- isc::asiolink::IOAddress max(txt.substr(pos + 1));
+ } else {
- PoolPtr pool(poolMaker(min, max));
- local_pools_.push_back(pool);
- return;
+ // Is this min-max notation?
+ pos = txt.find("-");
+ if (pos != string::npos) {
+ // using min-max notation
+ isc::asiolink::IOAddress min(txt.substr(0,pos));
+ isc::asiolink::IOAddress max(txt.substr(pos + 1));
+
+ pool = poolMaker(min, max);
+ local_pools_.push_back(pool);
+ }
+ }
+
+ if (!pool) {
+ isc_throw(DhcpConfigError, "invalid pool definition: "
+ << text_pool->stringValue() <<
+ ". There are two acceptable formats <min address-max address>"
+ " or <prefix/len> ("
+ << text_pool->getPosition() << ")");
}
- isc_throw(DhcpConfigError, "invalid pool definition: "
- << text_pool->stringValue() <<
- ". There are two acceptable formats <min address-max address>"
- " or <prefix/len> ("
- << text_pool->getPosition() << ")");
+ // Parser pool specific options.
+ ConstElementPtr option_data = pool_structure->get("option-data");
+ if (option_data) {
+ try {
+ OptionDataListParserPtr option_parser(new OptionDataListParser("option-data",
+ options_,
+ AF_INET6));
+ option_parser->build(option_data);
+ option_parser->commit();
+ options_->copyTo(*pool->getCfgOption());;
+
+ } catch (const std::exception& ex) {
+ isc_throw(isc::dhcp::DhcpConfigError, ex.what()
+ << " (" << option_data->getPosition() << ")");
+ }
+ }
}
void
/// A temporary storage for pools configuration. It is a
/// storage where pools are stored by build function.
PoolStorage local_pools_;
+
+ /// A storage for pool specific option values.
+ CfgOptionPtr options_;
};
/// @brief Parser for a list of pools