|
@@ -1,4 +1,4 @@
|
|
|
-// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
|
|
|
+// Copyright (C) 2012-2014 Internet Systems Consortium, Inc. ("ISC")
|
|
|
//
|
|
|
// Permission to use, copy, modify, and/or distribute this software for any
|
|
|
// purpose with or without fee is hereby granted, provided that the above
|
|
@@ -30,6 +30,7 @@
|
|
|
|
|
|
#include "marker_file.h"
|
|
|
#include "test_libraries.h"
|
|
|
+#include "test_data_files_config.h"
|
|
|
|
|
|
#include <boost/foreach.hpp>
|
|
|
#include <boost/scoped_ptr.hpp>
|
|
@@ -50,6 +51,23 @@ using namespace std;
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
+/// @brief Prepends the given name with the DHCP4 source directory
|
|
|
+///
|
|
|
+/// @param name file name of the desired file
|
|
|
+/// @return string containing the absolute path of the file in the DHCP source
|
|
|
+/// directory.
|
|
|
+std::string specfile(const std::string& name) {
|
|
|
+ return (std::string(DHCP4_SRC_DIR) + "/" + name);
|
|
|
+}
|
|
|
+
|
|
|
+/// @brief Tests that the spec file is valid.
|
|
|
+/// Verifies that the BIND10 DHCP-DDNS configuration specification file
|
|
|
+// is valid.
|
|
|
+TEST(Dhcp4SpecTest, basicSpec) {
|
|
|
+ (isc::config::moduleSpecFromFile(specfile("dhcp4.spec")));
|
|
|
+ ASSERT_NO_THROW(isc::config::moduleSpecFromFile(specfile("dhcp4.spec")));
|
|
|
+}
|
|
|
+
|
|
|
class Dhcp4ParserTest : public ::testing::Test {
|
|
|
public:
|
|
|
Dhcp4ParserTest()
|
|
@@ -119,19 +137,19 @@ public:
|
|
|
params["name"] = param_value;
|
|
|
params["space"] = "dhcp4";
|
|
|
params["code"] = "56";
|
|
|
- params["data"] = "AB CDEF0105";
|
|
|
+ params["data"] = "ABCDEF0105";
|
|
|
params["csv-format"] = "False";
|
|
|
} else if (parameter == "space") {
|
|
|
params["name"] = "dhcp-message";
|
|
|
params["space"] = param_value;
|
|
|
params["code"] = "56";
|
|
|
- params["data"] = "AB CDEF0105";
|
|
|
+ params["data"] = "ABCDEF0105";
|
|
|
params["csv-format"] = "False";
|
|
|
} else if (parameter == "code") {
|
|
|
params["name"] = "dhcp-message";
|
|
|
params["space"] = "dhcp4";
|
|
|
params["code"] = param_value;
|
|
|
- params["data"] = "AB CDEF0105";
|
|
|
+ params["data"] = "ABCDEF0105";
|
|
|
params["csv-format"] = "False";
|
|
|
} else if (parameter == "data") {
|
|
|
params["name"] = "dhcp-message";
|
|
@@ -143,7 +161,7 @@ public:
|
|
|
params["name"] = "dhcp-message";
|
|
|
params["space"] = "dhcp4";
|
|
|
params["code"] = "56";
|
|
|
- params["data"] = "AB CDEF0105";
|
|
|
+ params["data"] = "ABCDEF0105";
|
|
|
params["csv-format"] = param_value;
|
|
|
}
|
|
|
return (createConfigWithOption(params));
|
|
@@ -194,6 +212,62 @@ public:
|
|
|
return (stream.str());
|
|
|
}
|
|
|
|
|
|
+ /// @brief Returns an option from the subnet.
|
|
|
+ ///
|
|
|
+ /// This function returns an option from a subnet to which the
|
|
|
+ /// specified subnet address belongs. The option is identified
|
|
|
+ /// by its code.
|
|
|
+ ///
|
|
|
+ /// @param subnet_address Address which belongs to the subnet from
|
|
|
+ /// which the option is to be returned.
|
|
|
+ /// @param option_code Code of the option to be returned.
|
|
|
+ /// @param expected_options_count Expected number of options in
|
|
|
+ /// the particular subnet.
|
|
|
+ ///
|
|
|
+ /// @return Descriptor of the option. If the descriptor holds a
|
|
|
+ /// NULL option pointer, it means that there was no such option
|
|
|
+ /// in the subnet.
|
|
|
+ Subnet::OptionDescriptor
|
|
|
+ getOptionFromSubnet(const IOAddress& subnet_address,
|
|
|
+ const uint16_t option_code,
|
|
|
+ const uint16_t expected_options_count = 1) {
|
|
|
+ Subnet4Ptr subnet = CfgMgr::instance().getSubnet4(subnet_address);
|
|
|
+ if (!subnet) {
|
|
|
+ /// @todo replace toText() with the use of operator <<.
|
|
|
+ ADD_FAILURE() << "A subnet for the specified address "
|
|
|
+ << subnet_address.toText()
|
|
|
+ << "does not exist in Config Manager";
|
|
|
+ }
|
|
|
+ Subnet::OptionContainerPtr options =
|
|
|
+ subnet->getOptionDescriptors("dhcp4");
|
|
|
+ if (expected_options_count != options->size()) {
|
|
|
+ ADD_FAILURE() << "The number of options in the subnet '"
|
|
|
+ << subnet_address.toText() << "' is different "
|
|
|
+ " than expected number of options '"
|
|
|
+ << expected_options_count << "'";
|
|
|
+ }
|
|
|
+
|
|
|
+ // Get the search index. Index #1 is to search using option code.
|
|
|
+ const Subnet::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<Subnet::OptionContainerTypeIndex::const_iterator,
|
|
|
+ Subnet::OptionContainerTypeIndex::const_iterator> range =
|
|
|
+ idx.equal_range(option_code);
|
|
|
+ if (std::distance(range.first, range.second) > 1) {
|
|
|
+ ADD_FAILURE() << "There is more than one option having the"
|
|
|
+ " option code '" << option_code << "' in a subnet '"
|
|
|
+ << subnet_address.toText() << "'. Expected "
|
|
|
+ " at most one option";
|
|
|
+ } else if (std::distance(range.first, range.second) == 0) {
|
|
|
+ return (Subnet::OptionDescriptor(OptionPtr(), false));
|
|
|
+ }
|
|
|
+
|
|
|
+ return (*range.first);
|
|
|
+ }
|
|
|
+
|
|
|
/// @brief Test invalid option parameter value.
|
|
|
///
|
|
|
/// This test function constructs the simple configuration
|
|
@@ -215,6 +289,24 @@ public:
|
|
|
ASSERT_EQ(1, rcode_);
|
|
|
}
|
|
|
|
|
|
+ /// @brief Test invalid option paramater value.
|
|
|
+ ///
|
|
|
+ /// This test function constructs the simple configuration
|
|
|
+ /// string and injects invalid option configuration into it.
|
|
|
+ /// It expects that parser will fail with provided option code.
|
|
|
+ ///
|
|
|
+ /// @param params Map of parameters defining an option.
|
|
|
+ void
|
|
|
+ testInvalidOptionParam(const std::map<std::string, std::string>& params) {
|
|
|
+ ConstElementPtr x;
|
|
|
+ std::string config = createConfigWithOption(params);
|
|
|
+ ElementPtr json = Element::fromJSON(config);
|
|
|
+ EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
|
|
|
+ ASSERT_TRUE(x);
|
|
|
+ comment_ = parseAnswer(rcode_, x);
|
|
|
+ ASSERT_EQ(1, rcode_);
|
|
|
+ }
|
|
|
+
|
|
|
/// @brief Test option against given code and data.
|
|
|
///
|
|
|
/// @param option_desc option descriptor that carries the option to
|
|
@@ -256,6 +348,39 @@ public:
|
|
|
expected_data_len));
|
|
|
}
|
|
|
|
|
|
+ /// @brief Test option configuration.
|
|
|
+ ///
|
|
|
+ /// This function creates a configuration for a specified option using
|
|
|
+ /// a map of parameters specified as the argument. The map holds
|
|
|
+ /// name/value pairs which identifies option's configuration parameters:
|
|
|
+ /// - name
|
|
|
+ /// - space
|
|
|
+ /// - code
|
|
|
+ /// - data
|
|
|
+ /// - csv-format.
|
|
|
+ /// This function applies a new server configuration and checks that the
|
|
|
+ /// option being configured is inserted into CfgMgr. The raw contents of
|
|
|
+ /// this option are compared with the binary data specified as expected
|
|
|
+ /// data passed to this function.
|
|
|
+ ///
|
|
|
+ /// @param params Map of parameters defining an option.
|
|
|
+ /// @param option_code Option code.
|
|
|
+ /// @param expected_data Array containing binary data expected to be stored
|
|
|
+ /// in the configured option.
|
|
|
+ /// @param expected_data_len Length of the array holding reference data.
|
|
|
+ void testConfiguration(const std::map<std::string, std::string>& params,
|
|
|
+ const uint16_t option_code,
|
|
|
+ const uint8_t* expected_data,
|
|
|
+ const size_t expected_data_len) {
|
|
|
+ std::string config = createConfigWithOption(params);
|
|
|
+ ASSERT_TRUE(executeConfiguration(config, "parse option configuration"));
|
|
|
+ // The subnet should now hold one option with the specified option code.
|
|
|
+ Subnet::OptionDescriptor desc =
|
|
|
+ getOptionFromSubnet(IOAddress("192.0.2.24"), option_code);
|
|
|
+ ASSERT_TRUE(desc.option);
|
|
|
+ testOption(desc, option_code, expected_data, expected_data_len);
|
|
|
+ }
|
|
|
+
|
|
|
/// @brief Parse and Execute configuration
|
|
|
///
|
|
|
/// Parses a configuration and executes a configuration of the server.
|
|
@@ -321,6 +446,7 @@ public:
|
|
|
"\"renew-timer\": 1000, "
|
|
|
"\"valid-lifetime\": 4000, "
|
|
|
"\"subnet4\": [ ], "
|
|
|
+ "\"dhcp-ddns\": { \"enable-updates\" : false }, "
|
|
|
"\"option-def\": [ ], "
|
|
|
"\"option-data\": [ ] }";
|
|
|
static_cast<void>(executeConfiguration(config,
|
|
@@ -410,8 +536,195 @@ TEST_F(Dhcp4ParserTest, subnetGlobalDefaults) {
|
|
|
EXPECT_EQ(1000, subnet->getT1());
|
|
|
EXPECT_EQ(2000, subnet->getT2());
|
|
|
EXPECT_EQ(4000, subnet->getValid());
|
|
|
+
|
|
|
+ // Check that subnet-id is 1
|
|
|
+ EXPECT_EQ(1, subnet->getID());
|
|
|
+}
|
|
|
+
|
|
|
+// Goal of this test is to verify that multiple subnets get unique
|
|
|
+// subnet-ids. Also, test checks that it's possible to do reconfiguration
|
|
|
+// multiple times.
|
|
|
+TEST_F(Dhcp4ParserTest, multipleSubnets) {
|
|
|
+ ConstElementPtr x;
|
|
|
+ string config = "{ \"interfaces\": [ \"*\" ],"
|
|
|
+ "\"rebind-timer\": 2000, "
|
|
|
+ "\"renew-timer\": 1000, "
|
|
|
+ "\"subnet4\": [ { "
|
|
|
+ " \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
|
|
|
+ " \"subnet\": \"192.0.2.0/24\" "
|
|
|
+ " },"
|
|
|
+ " {"
|
|
|
+ " \"pool\": [ \"192.0.3.101 - 192.0.3.150\" ],"
|
|
|
+ " \"subnet\": \"192.0.3.0/24\" "
|
|
|
+ " },"
|
|
|
+ " {"
|
|
|
+ " \"pool\": [ \"192.0.4.101 - 192.0.4.150\" ],"
|
|
|
+ " \"subnet\": \"192.0.4.0/24\" "
|
|
|
+ " },"
|
|
|
+ " {"
|
|
|
+ " \"pool\": [ \"192.0.5.101 - 192.0.5.150\" ],"
|
|
|
+ " \"subnet\": \"192.0.5.0/24\" "
|
|
|
+ " } ],"
|
|
|
+ "\"valid-lifetime\": 4000 }";
|
|
|
+
|
|
|
+ ElementPtr json = Element::fromJSON(config);
|
|
|
+
|
|
|
+ EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
|
|
|
+ ASSERT_TRUE(x);
|
|
|
+ comment_ = parseAnswer(rcode_, x);
|
|
|
+ ASSERT_EQ(0, rcode_);
|
|
|
+
|
|
|
+ int cnt = 0; // Number of reconfigurations
|
|
|
+
|
|
|
+ do {
|
|
|
+ ElementPtr json = Element::fromJSON(config);
|
|
|
+
|
|
|
+ EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
|
|
|
+ ASSERT_TRUE(x);
|
|
|
+ comment_ = parseAnswer(rcode_, x);
|
|
|
+ ASSERT_EQ(0, rcode_);
|
|
|
+
|
|
|
+ const Subnet4Collection* subnets = CfgMgr::instance().getSubnets4();
|
|
|
+ ASSERT_TRUE(subnets);
|
|
|
+ ASSERT_EQ(4, subnets->size()); // We expect 4 subnets
|
|
|
+
|
|
|
+ // Check subnet-ids of each subnet (it should be monotonously increasing)
|
|
|
+ EXPECT_EQ(1, subnets->at(0)->getID());
|
|
|
+ EXPECT_EQ(2, subnets->at(1)->getID());
|
|
|
+ EXPECT_EQ(3, subnets->at(2)->getID());
|
|
|
+ EXPECT_EQ(4, subnets->at(3)->getID());
|
|
|
+
|
|
|
+ // Repeat reconfiguration process 10 times and check that the subnet-id
|
|
|
+ // is set to the same value. Technically, just two iterations would be
|
|
|
+ // sufficient, but it's nice to have a test that exercises reconfiguration
|
|
|
+ // a bit.
|
|
|
+ } while (++cnt < 10);
|
|
|
}
|
|
|
|
|
|
+// Goal of this test is to verify that a previously configured subnet can be
|
|
|
+// deleted in subsequent reconfiguration.
|
|
|
+TEST_F(Dhcp4ParserTest, reconfigureRemoveSubnet) {
|
|
|
+ ConstElementPtr x;
|
|
|
+
|
|
|
+ // All four subnets
|
|
|
+ string config4 = "{ \"interfaces\": [ \"*\" ],"
|
|
|
+ "\"rebind-timer\": 2000, "
|
|
|
+ "\"renew-timer\": 1000, "
|
|
|
+ "\"subnet4\": [ { "
|
|
|
+ " \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
|
|
|
+ " \"subnet\": \"192.0.2.0/24\" "
|
|
|
+ " },"
|
|
|
+ " {"
|
|
|
+ " \"pool\": [ \"192.0.3.101 - 192.0.3.150\" ],"
|
|
|
+ " \"subnet\": \"192.0.3.0/24\" "
|
|
|
+ " },"
|
|
|
+ " {"
|
|
|
+ " \"pool\": [ \"192.0.4.101 - 192.0.4.150\" ],"
|
|
|
+ " \"subnet\": \"192.0.4.0/24\" "
|
|
|
+ " },"
|
|
|
+ " {"
|
|
|
+ " \"pool\": [ \"192.0.5.101 - 192.0.5.150\" ],"
|
|
|
+ " \"subnet\": \"192.0.5.0/24\" "
|
|
|
+ " } ],"
|
|
|
+ "\"valid-lifetime\": 4000 }";
|
|
|
+
|
|
|
+ // Three subnets (the last one removed)
|
|
|
+ string config_first3 = "{ \"interfaces\": [ \"*\" ],"
|
|
|
+ "\"rebind-timer\": 2000, "
|
|
|
+ "\"renew-timer\": 1000, "
|
|
|
+ "\"subnet4\": [ { "
|
|
|
+ " \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
|
|
|
+ " \"subnet\": \"192.0.2.0/24\" "
|
|
|
+ " },"
|
|
|
+ " {"
|
|
|
+ " \"pool\": [ \"192.0.3.101 - 192.0.3.150\" ],"
|
|
|
+ " \"subnet\": \"192.0.3.0/24\" "
|
|
|
+ " },"
|
|
|
+ " {"
|
|
|
+ " \"pool\": [ \"192.0.4.101 - 192.0.4.150\" ],"
|
|
|
+ " \"subnet\": \"192.0.4.0/24\" "
|
|
|
+ " } ],"
|
|
|
+ "\"valid-lifetime\": 4000 }";
|
|
|
+
|
|
|
+ // Second subnet removed
|
|
|
+ string config_second_removed = "{ \"interfaces\": [ \"*\" ],"
|
|
|
+ "\"rebind-timer\": 2000, "
|
|
|
+ "\"renew-timer\": 1000, "
|
|
|
+ "\"subnet4\": [ { "
|
|
|
+ " \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
|
|
|
+ " \"subnet\": \"192.0.2.0/24\" "
|
|
|
+ " },"
|
|
|
+ " {"
|
|
|
+ " \"pool\": [ \"192.0.4.101 - 192.0.4.150\" ],"
|
|
|
+ " \"subnet\": \"192.0.4.0/24\" "
|
|
|
+ " },"
|
|
|
+ " {"
|
|
|
+ " \"pool\": [ \"192.0.5.101 - 192.0.5.150\" ],"
|
|
|
+ " \"subnet\": \"192.0.5.0/24\" "
|
|
|
+ " } ],"
|
|
|
+ "\"valid-lifetime\": 4000 }";
|
|
|
+
|
|
|
+ // CASE 1: Configure 4 subnets, then reconfigure and remove the
|
|
|
+ // last one.
|
|
|
+
|
|
|
+ ElementPtr json = Element::fromJSON(config4);
|
|
|
+ EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
|
|
|
+ ASSERT_TRUE(x);
|
|
|
+ comment_ = parseAnswer(rcode_, x);
|
|
|
+ ASSERT_EQ(0, rcode_);
|
|
|
+
|
|
|
+ const Subnet4Collection* subnets = CfgMgr::instance().getSubnets4();
|
|
|
+ ASSERT_TRUE(subnets);
|
|
|
+ ASSERT_EQ(4, subnets->size()); // We expect 4 subnets
|
|
|
+
|
|
|
+ // Do the reconfiguration (the last subnet is removed)
|
|
|
+ json = Element::fromJSON(config_first3);
|
|
|
+ EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
|
|
|
+ ASSERT_TRUE(x);
|
|
|
+ comment_ = parseAnswer(rcode_, x);
|
|
|
+ ASSERT_EQ(0, rcode_);
|
|
|
+
|
|
|
+ subnets = CfgMgr::instance().getSubnets4();
|
|
|
+ ASSERT_TRUE(subnets);
|
|
|
+ ASSERT_EQ(3, subnets->size()); // We expect 3 subnets now (4th is removed)
|
|
|
+
|
|
|
+ // Check subnet-ids of each subnet (it should be monotonously increasing)
|
|
|
+ EXPECT_EQ(1, subnets->at(0)->getID());
|
|
|
+ EXPECT_EQ(2, subnets->at(1)->getID());
|
|
|
+ EXPECT_EQ(3, subnets->at(2)->getID());
|
|
|
+
|
|
|
+ /// CASE 2: Configure 4 subnets, then reconfigure and remove one
|
|
|
+ /// from in between (not first, not last)
|
|
|
+
|
|
|
+#if 0
|
|
|
+ /// @todo: Uncomment subnet removal test as part of #3281.
|
|
|
+ json = Element::fromJSON(config4);
|
|
|
+ EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
|
|
|
+ ASSERT_TRUE(x);
|
|
|
+ comment_ = parseAnswer(rcode_, x);
|
|
|
+ ASSERT_EQ(0, rcode_);
|
|
|
+
|
|
|
+ // Do reconfiguration
|
|
|
+ json = Element::fromJSON(config_second_removed);
|
|
|
+ EXPECT_NO_THROW(x = configureDhcp4Server(*srv_, json));
|
|
|
+ ASSERT_TRUE(x);
|
|
|
+ comment_ = parseAnswer(rcode_, x);
|
|
|
+ ASSERT_EQ(0, rcode_);
|
|
|
+
|
|
|
+ subnets = CfgMgr::instance().getSubnets4();
|
|
|
+ ASSERT_TRUE(subnets);
|
|
|
+ ASSERT_EQ(3, subnets->size()); // We expect 4 subnets
|
|
|
+
|
|
|
+ EXPECT_EQ(1, subnets->at(0)->getID());
|
|
|
+ // The second subnet (with subnet-id = 2) is no longer there
|
|
|
+ EXPECT_EQ(3, subnets->at(1)->getID());
|
|
|
+ EXPECT_EQ(4, subnets->at(2)->getID());
|
|
|
+#endif
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+/// @todo: implement subnet removal test as part of #3281.
|
|
|
+
|
|
|
// Checks if the next-server defined as global parameter is taken into
|
|
|
// consideration.
|
|
|
TEST_F(Dhcp4ParserTest, nextServerGlobal) {
|
|
@@ -1188,7 +1501,7 @@ TEST_F(Dhcp4ParserTest, optionDataDefaults) {
|
|
|
" \"name\": \"dhcp-message\","
|
|
|
" \"space\": \"dhcp4\","
|
|
|
" \"code\": 56,"
|
|
|
- " \"data\": \"AB CDEF0105\","
|
|
|
+ " \"data\": \"ABCDEF0105\","
|
|
|
" \"csv-format\": False"
|
|
|
" },"
|
|
|
" {"
|
|
@@ -1261,7 +1574,7 @@ TEST_F(Dhcp4ParserTest, optionDataTwoSpaces) {
|
|
|
" \"name\": \"dhcp-message\","
|
|
|
" \"space\": \"dhcp4\","
|
|
|
" \"code\": 56,"
|
|
|
- " \"data\": \"AB CDEF0105\","
|
|
|
+ " \"data\": \"ABCDEF0105\","
|
|
|
" \"csv-format\": False"
|
|
|
" },"
|
|
|
" {"
|
|
@@ -1438,7 +1751,6 @@ TEST_F(Dhcp4ParserTest, optionDataEncapsulate) {
|
|
|
" } ]"
|
|
|
"}";
|
|
|
|
|
|
-
|
|
|
json = Element::fromJSON(config);
|
|
|
|
|
|
EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, json));
|
|
@@ -1494,7 +1806,7 @@ TEST_F(Dhcp4ParserTest, optionDataInSingleSubnet) {
|
|
|
" \"name\": \"dhcp-message\","
|
|
|
" \"space\": \"dhcp4\","
|
|
|
" \"code\": 56,"
|
|
|
- " \"data\": \"AB CDEF0105\","
|
|
|
+ " \"data\": \"ABCDEF0105\","
|
|
|
" \"csv-format\": False"
|
|
|
" },"
|
|
|
" {"
|
|
@@ -1545,6 +1857,89 @@ TEST_F(Dhcp4ParserTest, optionDataInSingleSubnet) {
|
|
|
testOption(*range.first, 23, foo2_expected, sizeof(foo2_expected));
|
|
|
}
|
|
|
|
|
|
+// The goal of this test is to check that the option carrying a boolean
|
|
|
+// value can be configured using one of the values: "true", "false", "0"
|
|
|
+// or "1".
|
|
|
+TEST_F(Dhcp4ParserTest, optionDataBoolean) {
|
|
|
+ // Create configuration. Use standard option 19 (ip-forwarding).
|
|
|
+ std::map<std::string, std::string> params;
|
|
|
+ params["name"] = "ip-forwarding";
|
|
|
+ params["space"] = "dhcp4";
|
|
|
+ params["code"] = "19";
|
|
|
+ params["data"] = "true";
|
|
|
+ params["csv-format"] = "true";
|
|
|
+
|
|
|
+ std::string config = createConfigWithOption(params);
|
|
|
+ ASSERT_TRUE(executeConfiguration(config, "parse configuration with a"
|
|
|
+ " boolean value"));
|
|
|
+
|
|
|
+ // The subnet should now hold one option with the code 19.
|
|
|
+ Subnet::OptionDescriptor desc = getOptionFromSubnet(IOAddress("192.0.2.24"),
|
|
|
+ 19);
|
|
|
+ ASSERT_TRUE(desc.option);
|
|
|
+
|
|
|
+ // This option should be set to "true", represented as 0x1 in the option
|
|
|
+ // buffer.
|
|
|
+ uint8_t expected_option_data[] = {
|
|
|
+ 0x1
|
|
|
+ };
|
|
|
+ testConfiguration(params, 19, expected_option_data,
|
|
|
+ sizeof(expected_option_data));
|
|
|
+
|
|
|
+ // Configure the option with the "1" value. This should have the same
|
|
|
+ // effect as if "true" was specified.
|
|
|
+ params["data"] = "1";
|
|
|
+ testConfiguration(params, 19, expected_option_data,
|
|
|
+ sizeof(expected_option_data));
|
|
|
+
|
|
|
+ // The value of "1" with a few leading zeros should work too.
|
|
|
+ params["data"] = "00001";
|
|
|
+ testConfiguration(params, 19, expected_option_data,
|
|
|
+ sizeof(expected_option_data));
|
|
|
+
|
|
|
+ // Configure the option with the "false" value.
|
|
|
+ params["data"] = "false";
|
|
|
+ // The option buffer should now hold the value of 0.
|
|
|
+ expected_option_data[0] = 0;
|
|
|
+ testConfiguration(params, 19, expected_option_data,
|
|
|
+ sizeof(expected_option_data));
|
|
|
+
|
|
|
+ // Specifying "0" should have the same effect as "false".
|
|
|
+ params["data"] = "0";
|
|
|
+ testConfiguration(params, 19, expected_option_data,
|
|
|
+ sizeof(expected_option_data));
|
|
|
+
|
|
|
+ // The same effect should be for multiple 0 chars.
|
|
|
+ params["data"] = "00000";
|
|
|
+ testConfiguration(params, 19, expected_option_data,
|
|
|
+ sizeof(expected_option_data));
|
|
|
+
|
|
|
+ // Bogus values should not be accepted.
|
|
|
+ params["data"] = "bugus";
|
|
|
+ testInvalidOptionParam(params);
|
|
|
+
|
|
|
+ params["data"] = "2";
|
|
|
+ testInvalidOptionParam(params);
|
|
|
+
|
|
|
+ // Now let's test that it is possible to use binary format.
|
|
|
+ params["data"] = "0";
|
|
|
+ params["csv-format"] = "false";
|
|
|
+ testConfiguration(params, 19, expected_option_data,
|
|
|
+ sizeof(expected_option_data));
|
|
|
+
|
|
|
+ // The binary 1 should work as well.
|
|
|
+ params["data"] = "1";
|
|
|
+ expected_option_data[0] = 1;
|
|
|
+ testConfiguration(params, 19, expected_option_data,
|
|
|
+ sizeof(expected_option_data));
|
|
|
+
|
|
|
+ // As well as an even number of digits.
|
|
|
+ params["data"] = "01";
|
|
|
+ testConfiguration(params, 19, expected_option_data,
|
|
|
+ sizeof(expected_option_data));
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
// Goal of this test is to verify options configuration
|
|
|
// for multiple subnets.
|
|
|
TEST_F(Dhcp4ParserTest, optionDataInMultipleSubnets) {
|
|
@@ -1622,6 +2017,8 @@ TEST_F(Dhcp4ParserTest, optionDataInMultipleSubnets) {
|
|
|
testOption(*range2.first, 23, foo2_expected, sizeof(foo2_expected));
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+
|
|
|
// Verify that empty option name is rejected in the configuration.
|
|
|
TEST_F(Dhcp4ParserTest, optionNameEmpty) {
|
|
|
// Empty option names not allowed.
|
|
@@ -1672,14 +2069,6 @@ TEST_F(Dhcp4ParserTest, optionDataUnexpectedPrefix) {
|
|
|
testInvalidOptionParam("0x0102", "data");
|
|
|
}
|
|
|
|
|
|
-// Verify that option data consisting od an odd number of
|
|
|
-// hexadecimal digits is rejected in the configuration.
|
|
|
-TEST_F(Dhcp4ParserTest, optionDataOddLength) {
|
|
|
- // Option code 0 is reserved and should not be accepted
|
|
|
- // by configuration parser.
|
|
|
- testInvalidOptionParam("123", "data");
|
|
|
-}
|
|
|
-
|
|
|
// Verify that either lower or upper case characters are allowed
|
|
|
// to specify the option data.
|
|
|
TEST_F(Dhcp4ParserTest, optionDataLowerCase) {
|
|
@@ -1995,7 +2384,7 @@ TEST_F(Dhcp4ParserTest, vendorOptionsHex) {
|
|
|
" \"name\": \"option-one\","
|
|
|
" \"space\": \"vendor-4491\"," // VENDOR_ID_CABLE_LABS = 4491
|
|
|
" \"code\": 100," // just a random code
|
|
|
- " \"data\": \"AB CDEF0105\","
|
|
|
+ " \"data\": \"ABCDEF0105\","
|
|
|
" \"csv-format\": False"
|
|
|
" },"
|
|
|
" {"
|
|
@@ -2127,7 +2516,7 @@ buildHooksLibrariesConfig(const std::vector<std::string>& libraries) {
|
|
|
" \"name\": \"dhcp-message\","
|
|
|
" \"space\": \"dhcp4\","
|
|
|
" \"code\": 56,"
|
|
|
- " \"data\": \"AB CDEF0105\","
|
|
|
+ " \"data\": \"ABCDEF0105\","
|
|
|
" \"csv-format\": False"
|
|
|
" },"
|
|
|
" {"
|
|
@@ -2299,6 +2688,113 @@ TEST_F(Dhcp4ParserTest, allInterfaces) {
|
|
|
EXPECT_TRUE(CfgMgr::instance().isActiveIface("eth2"));
|
|
|
}
|
|
|
|
|
|
+// This test checks the ability of the server to parse a configuration
|
|
|
+// containing a full, valid dhcp-ddns (D2ClientConfig) entry.
|
|
|
+TEST_F(Dhcp4ParserTest, d2ClientConfig) {
|
|
|
+ ConstElementPtr status;
|
|
|
+
|
|
|
+ // Verify that the D2 configuraiton can be fetched and is set to disabled.
|
|
|
+ D2ClientConfigPtr d2_client_config = CfgMgr::instance().getD2ClientConfig();
|
|
|
+ EXPECT_FALSE(d2_client_config->getEnableUpdates());
|
|
|
+
|
|
|
+ // Verify that the convenience method agrees.
|
|
|
+ ASSERT_FALSE(CfgMgr::instance().ddnsEnabled());
|
|
|
+
|
|
|
+ string config_str = "{ \"interfaces\": [ \"*\" ],"
|
|
|
+ "\"rebind-timer\": 2000, "
|
|
|
+ "\"renew-timer\": 1000, "
|
|
|
+ "\"subnet4\": [ { "
|
|
|
+ " \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
|
|
|
+ " \"subnet\": \"192.0.2.0/24\" } ],"
|
|
|
+ " \"dhcp-ddns\" : {"
|
|
|
+ " \"enable-updates\" : true, "
|
|
|
+ " \"server-ip\" : \"192.168.2.1\", "
|
|
|
+ " \"server-port\" : 777, "
|
|
|
+ " \"ncr-protocol\" : \"UDP\", "
|
|
|
+ " \"ncr-format\" : \"JSON\", "
|
|
|
+ " \"always-include-fqdn\" : true, "
|
|
|
+ " \"allow-client-update\" : true, "
|
|
|
+ " \"override-no-update\" : true, "
|
|
|
+ " \"override-client-update\" : true, "
|
|
|
+ " \"replace-client-name\" : true, "
|
|
|
+ " \"generated-prefix\" : \"test.prefix\", "
|
|
|
+ " \"qualifying-suffix\" : \"test.suffix.\" },"
|
|
|
+ "\"valid-lifetime\": 4000 }";
|
|
|
|
|
|
+ // Convert the JSON string to configuration elements.
|
|
|
+ ElementPtr config;
|
|
|
+ ASSERT_NO_THROW(config = Element::fromJSON(config_str));
|
|
|
+
|
|
|
+ // Pass the configuration in for parsing.
|
|
|
+ EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, config));
|
|
|
+
|
|
|
+ // check if returned status is OK
|
|
|
+ checkResult(status, 0);
|
|
|
+
|
|
|
+ // Verify that DHCP-DDNS updating is enabled.
|
|
|
+ EXPECT_TRUE(CfgMgr::instance().ddnsEnabled());
|
|
|
+
|
|
|
+ // Verify that the D2 configuration can be retrieved.
|
|
|
+ d2_client_config = CfgMgr::instance().getD2ClientConfig();
|
|
|
+ ASSERT_TRUE(d2_client_config);
|
|
|
+
|
|
|
+ // Verify that the configuration values are correct.
|
|
|
+ EXPECT_TRUE(d2_client_config->getEnableUpdates());
|
|
|
+ EXPECT_EQ("192.168.2.1", d2_client_config->getServerIp().toText());
|
|
|
+ EXPECT_EQ(777, d2_client_config->getServerPort());
|
|
|
+ EXPECT_EQ(dhcp_ddns::NCR_UDP, d2_client_config->getNcrProtocol());
|
|
|
+ EXPECT_EQ(dhcp_ddns::FMT_JSON, d2_client_config->getNcrFormat());
|
|
|
+ EXPECT_TRUE(d2_client_config->getAlwaysIncludeFqdn());
|
|
|
+ EXPECT_TRUE(d2_client_config->getOverrideNoUpdate());
|
|
|
+ EXPECT_TRUE(d2_client_config->getOverrideClientUpdate());
|
|
|
+ EXPECT_TRUE(d2_client_config->getReplaceClientName());
|
|
|
+ EXPECT_EQ("test.prefix", d2_client_config->getGeneratedPrefix());
|
|
|
+ EXPECT_EQ("test.suffix.", d2_client_config->getQualifyingSuffix());
|
|
|
+}
|
|
|
+
|
|
|
+// This test checks the ability of the server to handle a configuration
|
|
|
+// containing an invalid dhcp-ddns (D2ClientConfig) entry.
|
|
|
+TEST_F(Dhcp4ParserTest, invalidD2ClientConfig) {
|
|
|
+ ConstElementPtr status;
|
|
|
+
|
|
|
+ // Configuration string with an invalid D2 client config,
|
|
|
+ // "server-ip" is missing.
|
|
|
+ string config_str = "{ \"interfaces\": [ \"*\" ],"
|
|
|
+ "\"rebind-timer\": 2000, "
|
|
|
+ "\"renew-timer\": 1000, "
|
|
|
+ "\"subnet4\": [ { "
|
|
|
+ " \"pool\": [ \"192.0.2.1 - 192.0.2.100\" ],"
|
|
|
+ " \"subnet\": \"192.0.2.0/24\" } ],"
|
|
|
+ " \"dhcp-ddns\" : {"
|
|
|
+ " \"enable-updates\" : true, "
|
|
|
+ " \"server-port\" : 5301, "
|
|
|
+ " \"ncr-protocol\" : \"UDP\", "
|
|
|
+ " \"ncr-format\" : \"JSON\", "
|
|
|
+ " \"always-include-fqdn\" : true, "
|
|
|
+ " \"allow-client-update\" : true, "
|
|
|
+ " \"override-no-update\" : true, "
|
|
|
+ " \"override-client-update\" : true, "
|
|
|
+ " \"replace-client-name\" : true, "
|
|
|
+ " \"generated-prefix\" : \"test.prefix\", "
|
|
|
+ " \"qualifying-suffix\" : \"test.suffix.\" },"
|
|
|
+ "\"valid-lifetime\": 4000 }";
|
|
|
+
|
|
|
+ // Convert the JSON string to configuration elements.
|
|
|
+ ElementPtr config;
|
|
|
+ ASSERT_NO_THROW(config = Element::fromJSON(config_str));
|
|
|
+
|
|
|
+ // Configuration should not throw, but should fail.
|
|
|
+ EXPECT_NO_THROW(status = configureDhcp4Server(*srv_, config));
|
|
|
+
|
|
|
+ // check if returned status is failed.
|
|
|
+ checkResult(status, 1);
|
|
|
+
|
|
|
+ // Verify that the D2 configuraiton can be fetched and is set to disabled.
|
|
|
+ D2ClientConfigPtr d2_client_config = CfgMgr::instance().getD2ClientConfig();
|
|
|
+ EXPECT_FALSE(d2_client_config->getEnableUpdates());
|
|
|
+
|
|
|
+ // Verify that the convenience method agrees.
|
|
|
+ ASSERT_FALSE(CfgMgr::instance().ddnsEnabled());
|
|
|
+}
|
|
|
|
|
|
}
|