}
-// 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,"
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.
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_);
}
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
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() + "," +
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());
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());
}
// 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
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
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_);
}
}
// 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.
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
}
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());
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.
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);
}
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
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);
}
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);
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);
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);
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);
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.
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);
// 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);
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);
}
/// @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.
///
///
/// @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.
///
/// @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.
///
/// @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.
///
/// 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
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,"
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.
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_);
}
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());
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_);
}
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_);
}
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);
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
// 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.
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 ****************************
// 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>
/// @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: