|
@@ -4,15 +4,30 @@
|
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
|
|
+#include <dhcp/dhcp6.h>
|
|
|
+#include <dhcp/libdhcp++.h>
|
|
|
+#include <dhcp/option4_addrlst.h>
|
|
|
+#include <dhcp/option6_addrlst.h>
|
|
|
+#include <dhcp/option_space.h>
|
|
|
+#include <dhcp/option_string.h>
|
|
|
+#include <dhcp/option_int.h>
|
|
|
+#include <dhcp/option_vendor.h>
|
|
|
#include <dhcpsrv/tests/generic_host_data_source_unittest.h>
|
|
|
#include <dhcpsrv/tests/test_utils.h>
|
|
|
#include <dhcpsrv/database_connection.h>
|
|
|
#include <asiolink/io_address.h>
|
|
|
+#include <util/buffer.h>
|
|
|
+#include <boost/foreach.hpp>
|
|
|
#include <gtest/gtest.h>
|
|
|
+#include <cstring>
|
|
|
+#include <list>
|
|
|
+#include <string>
|
|
|
#include <sstream>
|
|
|
+#include <typeinfo>
|
|
|
|
|
|
using namespace std;
|
|
|
using namespace isc::asiolink;
|
|
|
+using namespace isc::util;
|
|
|
|
|
|
namespace isc {
|
|
|
namespace dhcp {
|
|
@@ -20,10 +35,11 @@ namespace test {
|
|
|
|
|
|
GenericHostDataSourceTest::GenericHostDataSourceTest()
|
|
|
:hdsptr_() {
|
|
|
-
|
|
|
+ LibDHCP::clearRuntimeOptionDefs();
|
|
|
}
|
|
|
|
|
|
GenericHostDataSourceTest::~GenericHostDataSourceTest() {
|
|
|
+ LibDHCP::clearRuntimeOptionDefs();
|
|
|
}
|
|
|
|
|
|
std::vector<uint8_t>
|
|
@@ -82,8 +98,8 @@ GenericHostDataSourceTest::initializeHost4(const std::string& address,
|
|
|
// subnet4 with subnet6.
|
|
|
static SubnetID subnet4 = 0;
|
|
|
static SubnetID subnet6 = 100;
|
|
|
- subnet4++;
|
|
|
- subnet6++;
|
|
|
+ ++subnet4;
|
|
|
+ ++subnet6;
|
|
|
|
|
|
IOAddress addr(address);
|
|
|
HostPtr host(new Host(&ident[0], ident.size(), id, subnet4, subnet6, addr));
|
|
@@ -229,12 +245,16 @@ void GenericHostDataSourceTest::compareHosts(const ConstHostPtr& host1,
|
|
|
compareReservations6(host1->getIPv6Reservations(),
|
|
|
host2->getIPv6Reservations());
|
|
|
|
|
|
- // And compare client classification details
|
|
|
+ // Compare client classification details
|
|
|
compareClientClasses(host1->getClientClasses4(),
|
|
|
host2->getClientClasses4());
|
|
|
|
|
|
compareClientClasses(host1->getClientClasses6(),
|
|
|
host2->getClientClasses6());
|
|
|
+
|
|
|
+ // Compare DHCPv4 and DHCPv6 options.
|
|
|
+ compareOptions(host1->getCfgOption4(), host2->getCfgOption4());
|
|
|
+ compareOptions(host1->getCfgOption6(), host2->getCfgOption6());
|
|
|
}
|
|
|
|
|
|
DuidPtr
|
|
@@ -313,6 +333,178 @@ GenericHostDataSourceTest::compareClientClasses(const ClientClasses& /*classes1*
|
|
|
/// This is part of the work for #4213.
|
|
|
}
|
|
|
|
|
|
+void
|
|
|
+GenericHostDataSourceTest::compareOptions(const ConstCfgOptionPtr& cfg1,
|
|
|
+ const ConstCfgOptionPtr& cfg2) const {
|
|
|
+ ASSERT_TRUE(cfg1);
|
|
|
+ ASSERT_TRUE(cfg2);
|
|
|
+
|
|
|
+ // Combine option space names with vendor space names in a single list.
|
|
|
+ std::list<std::string> option_spaces = cfg2->getOptionSpaceNames();
|
|
|
+ std::list<std::string> vendor_spaces = cfg2->getVendorIdsSpaceNames();
|
|
|
+ option_spaces.insert(option_spaces.end(), vendor_spaces.begin(),
|
|
|
+ vendor_spaces.end());
|
|
|
+
|
|
|
+ // Make sure that the number of option spaces is equal in both
|
|
|
+ // configurations.
|
|
|
+ EXPECT_EQ(option_spaces.size(), cfg1->getOptionSpaceNames().size());
|
|
|
+ EXPECT_EQ(vendor_spaces.size(), cfg1->getVendorIdsSpaceNames().size());
|
|
|
+
|
|
|
+ // Iterate over all option spaces existing in cfg2.
|
|
|
+ BOOST_FOREACH(std::string space, option_spaces) {
|
|
|
+ // Retrieve options belonging to the current option space.
|
|
|
+ OptionContainerPtr options1 = cfg1->getAll(space);
|
|
|
+ OptionContainerPtr options2 = cfg2->getAll(space);
|
|
|
+ ASSERT_TRUE(options1) << "failed for option space " << space;
|
|
|
+ ASSERT_TRUE(options2) << "failed for option space " << space;
|
|
|
+
|
|
|
+ // If number of options doesn't match, the test fails.
|
|
|
+ ASSERT_EQ(options1->size(), options2->size())
|
|
|
+ << "failed for option space " << space;
|
|
|
+
|
|
|
+ // Iterate over all options within this option space.
|
|
|
+ BOOST_FOREACH(OptionDescriptor desc1, *options1) {
|
|
|
+ OptionDescriptor desc2 = cfg2->get(space, desc1.option_->getType());
|
|
|
+ // Compare persistent flag.
|
|
|
+ EXPECT_EQ(desc1.persistent_, desc2.persistent_)
|
|
|
+ << "failed for option " << space << "." << desc1.option_->getType();
|
|
|
+ // Compare formatted value.
|
|
|
+ EXPECT_EQ(desc1.formatted_value_, desc2.formatted_value_)
|
|
|
+ << "failed for option " << space << "." << desc1.option_->getType();
|
|
|
+
|
|
|
+ // Retrieve options.
|
|
|
+ Option* option1 = desc1.option_.get();
|
|
|
+ Option* option2 = desc2.option_.get();
|
|
|
+
|
|
|
+ // Options must be represented by the same C++ class derived from
|
|
|
+ // the Option class.
|
|
|
+ EXPECT_TRUE(typeid(*option1) == typeid(*option2))
|
|
|
+ << "Comapared DHCP options, having option code "
|
|
|
+ << desc1.option_->getType() << " and belonging to the "
|
|
|
+ << space << " option space, are represented "
|
|
|
+ "by different C++ classes: "
|
|
|
+ << typeid(*option1).name() << " vs "
|
|
|
+ << typeid(*option2).name();
|
|
|
+
|
|
|
+ // Because we use different C++ classes to represent different
|
|
|
+ // options, the simplest way to make sure that the options are
|
|
|
+ // equal is to simply compare them in wire format.
|
|
|
+ OutputBuffer buf1(option1->len());
|
|
|
+ ASSERT_NO_THROW(option1->pack(buf1));
|
|
|
+ OutputBuffer buf2(option2->len());
|
|
|
+ ASSERT_NO_THROW(option2->pack(buf2));
|
|
|
+
|
|
|
+ ASSERT_EQ(buf1.getLength(), buf2.getLength())
|
|
|
+ << "failed for option " << space << "." << desc1.option_->getType();
|
|
|
+ EXPECT_EQ(0, memcmp(buf1.getData(), buf2.getData(), buf1.getLength()))
|
|
|
+ << "failed for option " << space << "." << desc1.option_->getType();
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+OptionDescriptor
|
|
|
+GenericHostDataSourceTest::createEmptyOption(const Option::Universe& universe,
|
|
|
+ const uint16_t option_type,
|
|
|
+ const bool persist) const {
|
|
|
+ OptionPtr option(new Option(universe, option_type));
|
|
|
+ OptionDescriptor desc(option, persist);
|
|
|
+ return (desc);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+OptionDescriptor
|
|
|
+GenericHostDataSourceTest::createVendorOption(const Option::Universe& universe,
|
|
|
+ const bool persist,
|
|
|
+ const bool formatted,
|
|
|
+ const uint32_t vendor_id) const {
|
|
|
+ OptionVendorPtr option(new OptionVendor(universe, vendor_id));
|
|
|
+
|
|
|
+ std::ostringstream s;
|
|
|
+ if (formatted) {
|
|
|
+ // Vendor id comprises vendor-id field, for which we need to
|
|
|
+ // assign a value in the textual (formatted) format.
|
|
|
+ s << vendor_id;
|
|
|
+ }
|
|
|
+
|
|
|
+ OptionDescriptor desc(option, persist, s.str());
|
|
|
+ return (desc);
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+GenericHostDataSourceTest::addTestOptions(const HostPtr& host,
|
|
|
+ const bool formatted,
|
|
|
+ const AddedOptions& added_options) const {
|
|
|
+
|
|
|
+ OptionDefSpaceContainer defs;
|
|
|
+
|
|
|
+ if ((added_options == DHCP4_ONLY) || (added_options == DHCP4_AND_DHCP6)) {
|
|
|
+ // Add DHCPv4 options.
|
|
|
+ CfgOptionPtr opts = host->getCfgOption4();
|
|
|
+ opts->add(createOption<OptionString>(Option::V4, DHO_BOOT_FILE_NAME,
|
|
|
+ true, formatted, "my-boot-file"),
|
|
|
+ DHCP4_OPTION_SPACE);
|
|
|
+ opts->add(createOption<OptionUint8>(Option::V4, DHO_DEFAULT_IP_TTL,
|
|
|
+ false, formatted, 64),
|
|
|
+ DHCP4_OPTION_SPACE);
|
|
|
+ opts->add(createOption<OptionUint32>(Option::V4, 1, false, formatted, 312131),
|
|
|
+ "vendor-encapsulated-options");
|
|
|
+ opts->add(createAddressOption<Option4AddrLst>(254, false, formatted, "192.0.2.3"),
|
|
|
+ DHCP4_OPTION_SPACE);
|
|
|
+ opts->add(createEmptyOption(Option::V4, 1, true), "isc");
|
|
|
+ opts->add(createAddressOption<Option4AddrLst>(2, false, formatted, "10.0.0.5",
|
|
|
+ "10.0.0.3", "10.0.3.4"),
|
|
|
+ "isc");
|
|
|
+
|
|
|
+ // Add definitions for DHCPv4 non-standard options.
|
|
|
+ defs.addItem(OptionDefinitionPtr(new OptionDefinition("vendor-encapsulated-1",
|
|
|
+ 1, "uint32")),
|
|
|
+ "vendor-encapsulated-options");
|
|
|
+ defs.addItem(OptionDefinitionPtr(new OptionDefinition("option-254", 254,
|
|
|
+ "ipv4-address", true)),
|
|
|
+ DHCP4_OPTION_SPACE);
|
|
|
+ defs.addItem(OptionDefinitionPtr(new OptionDefinition("isc-1", 1, "empty")),
|
|
|
+ "isc");
|
|
|
+ defs.addItem(OptionDefinitionPtr(new OptionDefinition("isc-2", 2,
|
|
|
+ "ipv4-address", true)),
|
|
|
+ "isc");
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((added_options == DHCP6_ONLY) || (added_options == DHCP4_AND_DHCP6)) {
|
|
|
+ // Add DHCPv6 options.
|
|
|
+ CfgOptionPtr opts = host->getCfgOption6();
|
|
|
+ opts->add(createOption<OptionString>(Option::V6, D6O_BOOTFILE_URL,
|
|
|
+ true, formatted, "my-boot-file"),
|
|
|
+ DHCP6_OPTION_SPACE);
|
|
|
+ opts->add(createOption<OptionUint32>(Option::V6, D6O_INFORMATION_REFRESH_TIME,
|
|
|
+ false, formatted, 3600),
|
|
|
+ DHCP6_OPTION_SPACE);
|
|
|
+ opts->add(createVendorOption(Option::V6, false, formatted, 2495),
|
|
|
+ DHCP6_OPTION_SPACE);
|
|
|
+ opts->add(createAddressOption<Option6AddrLst>(1024, false, formatted,
|
|
|
+ "2001:db8:1::1"),
|
|
|
+ DHCP6_OPTION_SPACE);
|
|
|
+ opts->add(createEmptyOption(Option::V6, 1, true), "isc2");
|
|
|
+ opts->add(createAddressOption<Option6AddrLst>(2, false, formatted, "3000::1",
|
|
|
+ "3000::2", "3000::3"),
|
|
|
+ "isc2");
|
|
|
+
|
|
|
+ // Add definitions for DHCPv6 non-standard options.
|
|
|
+ defs.addItem(OptionDefinitionPtr(new OptionDefinition("option-1024", 1024,
|
|
|
+ "ipv6-address", true)),
|
|
|
+ DHCP6_OPTION_SPACE);
|
|
|
+ defs.addItem(OptionDefinitionPtr(new OptionDefinition("option-1", 1, "empty")),
|
|
|
+ "isc2");
|
|
|
+ defs.addItem(OptionDefinitionPtr(new OptionDefinition("option-2", 2,
|
|
|
+ "ipv6-address", true)),
|
|
|
+ "isc2");
|
|
|
+ }
|
|
|
+
|
|
|
+ // Register created "runtime" option definitions. They will be used by a
|
|
|
+ // host data source to convert option data into the appropriate option
|
|
|
+ // classes when the options are retrieved.
|
|
|
+ LibDHCP::setRuntimeOptionDefs(defs);
|
|
|
+}
|
|
|
+
|
|
|
void GenericHostDataSourceTest::testBasic4(const Host::IdentifierType& id) {
|
|
|
// Make sure we have the pointer to the host data source.
|
|
|
ASSERT_TRUE(hdsptr_);
|
|
@@ -898,6 +1090,69 @@ void GenericHostDataSourceTest::testMultipleReservationsDifferentOrder(){
|
|
|
|
|
|
}
|
|
|
|
|
|
+void GenericHostDataSourceTest::testOptionsReservations4(const bool formatted) {
|
|
|
+ HostPtr host = initializeHost4("192.0.2.5", Host::IDENT_HWADDR);
|
|
|
+ // Add a bunch of DHCPv4 and DHCPv6 options for the host.
|
|
|
+ ASSERT_NO_THROW(addTestOptions(host, formatted, DHCP4_ONLY));
|
|
|
+ // Insert host and the options into respective tables.
|
|
|
+ ASSERT_NO_THROW(hdsptr_->add(host));
|
|
|
+ // Subnet id will be used in quries to the database.
|
|
|
+ SubnetID subnet_id = host->getIPv4SubnetID();
|
|
|
+
|
|
|
+ // getAll4(address)
|
|
|
+ ConstHostCollection hosts_by_addr = hdsptr_->getAll4(host->getIPv4Reservation());
|
|
|
+ ASSERT_EQ(1, hosts_by_addr.size());
|
|
|
+ ASSERT_NO_FATAL_FAILURE(compareHosts(host, *hosts_by_addr.begin()));
|
|
|
+
|
|
|
+ // get4(subnet_id, identifier_type, identifier, identifier_size)
|
|
|
+ ConstHostPtr host_by_id = hdsptr_->get4(subnet_id,
|
|
|
+ host->getIdentifierType(),
|
|
|
+ &host->getIdentifier()[0],
|
|
|
+ host->getIdentifier().size());
|
|
|
+ ASSERT_NO_FATAL_FAILURE(compareHosts(host, host_by_id));
|
|
|
+
|
|
|
+ // get4(subnet_id, address)
|
|
|
+ ConstHostPtr host_by_addr = hdsptr_->get4(subnet_id, IOAddress("192.0.2.5"));
|
|
|
+ ASSERT_NO_FATAL_FAILURE(compareHosts(host, host_by_addr));
|
|
|
+}
|
|
|
+
|
|
|
+void GenericHostDataSourceTest::testOptionsReservations6(const bool formatted) {
|
|
|
+ HostPtr host = initializeHost6("2001:db8::1", Host::IDENT_DUID, false);
|
|
|
+ // Add a bunch of DHCPv4 and DHCPv6 options for the host.
|
|
|
+ ASSERT_NO_THROW(addTestOptions(host, formatted, DHCP6_ONLY));
|
|
|
+ // Insert host, options and IPv6 reservations into respective tables.
|
|
|
+ ASSERT_NO_THROW(hdsptr_->add(host));
|
|
|
+ // Subnet id will be used in queries to the database.
|
|
|
+ SubnetID subnet_id = host->getIPv6SubnetID();
|
|
|
+
|
|
|
+ // get6(subnet_id, identifier_type, identifier, identifier_size)
|
|
|
+ ConstHostPtr host_by_id = hdsptr_->get6(subnet_id, host->getIdentifierType(),
|
|
|
+ &host->getIdentifier()[0],
|
|
|
+ host->getIdentifier().size());
|
|
|
+ ASSERT_NO_FATAL_FAILURE(compareHosts(host, host_by_id));
|
|
|
+
|
|
|
+ // get6(address, prefix_len)
|
|
|
+ ConstHostPtr host_by_addr = hdsptr_->get6(IOAddress("2001:db8::1"), 128);
|
|
|
+ ASSERT_NO_FATAL_FAILURE(compareHosts(host, host_by_addr));
|
|
|
+}
|
|
|
+
|
|
|
+void GenericHostDataSourceTest::testOptionsReservations46(const bool formatted) {
|
|
|
+ HostPtr host = initializeHost6("2001:db8::1", Host::IDENT_HWADDR, false);
|
|
|
+
|
|
|
+ // Add a bunch of DHCPv4 and DHCPv6 options for the host.
|
|
|
+ ASSERT_NO_THROW(addTestOptions(host, formatted, DHCP4_AND_DHCP6));
|
|
|
+ // Insert host, options and IPv6 reservations into respective tables.
|
|
|
+ ASSERT_NO_THROW(hdsptr_->add(host));
|
|
|
+
|
|
|
+ // getAll(identifier_type, identifier, identifier_size)
|
|
|
+ ConstHostCollection hosts_by_id = hdsptr_->getAll(host->getIdentifierType(),
|
|
|
+ &host->getIdentifier()[0],
|
|
|
+ host->getIdentifier().size());
|
|
|
+ ASSERT_EQ(1, hosts_by_id.size());
|
|
|
+ ASSERT_NO_FATAL_FAILURE(compareHosts(host, *hosts_by_id.begin()));
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
}; // namespace test
|
|
|
}; // namespace dhcp
|
|
|
}; // namespace isc
|