summaryrefslogtreecommitdiffstats
path: root/src/lib/dhcp/tests/duid_factory_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/dhcp/tests/duid_factory_unittest.cc')
-rw-r--r--src/lib/dhcp/tests/duid_factory_unittest.cc529
1 files changed, 529 insertions, 0 deletions
diff --git a/src/lib/dhcp/tests/duid_factory_unittest.cc b/src/lib/dhcp/tests/duid_factory_unittest.cc
new file mode 100644
index 0000000..1669983
--- /dev/null
+++ b/src/lib/dhcp/tests/duid_factory_unittest.cc
@@ -0,0 +1,529 @@
+// Copyright (C) 2015-2017 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// 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 <config.h>
+#include <dhcp/dhcp4.h>
+#include <dhcp/duid_factory.h>
+#include <dhcp/tests/iface_mgr_test_config.h>
+#include <testutils/io_utils.h>
+#include <util/encode/hex.h>
+#include <util/range_utilities.h>
+#include <boost/algorithm/string.hpp>
+#include <gtest/gtest.h>
+#include <ctime>
+#include <fstream>
+#include <iomanip>
+#include <sstream>
+#include <stdio.h>
+#include <string>
+#include <vector>
+
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::dhcp::test;
+using namespace isc::util;
+
+namespace {
+
+/// @brief Name of the file holding DUID generated during a test.
+const std::string DEFAULT_DUID_FILE = "duid-factory-test.duid";
+
+/// @brief Test fixture class for @c DUIDFactory.
+class DUIDFactoryTest : public ::testing::Test {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// Creates fake interface configuration. It also creates an instance
+ /// of the @c DUIDFactory object used throughout the tests.
+ DUIDFactoryTest();
+
+ /// @brief Destructor.
+ virtual ~DUIDFactoryTest();
+
+ /// @brief Returns absolute path to a test DUID storage.
+ ///
+ /// @param duid_file_name Name of the file holding test DUID.
+ std::string absolutePath(const std::string& duid_file_name) const;
+
+ /// @brief Removes default DUID file used in the tests.
+ ///
+ /// This method is called from both constructor and destructor.
+ void removeDefaultFile() const;
+
+ /// @brief Returns contents of the DUID file.
+ std::string readDefaultFile() const;
+
+ /// @brief Converts string of hexadecimal digits to vector.
+ ///
+ /// @param hex String representation.
+ /// @return Vector created from the converted string.
+ std::vector<uint8_t> toVector(const std::string& hex) const;
+
+ /// @brief Converts vector to string of hexadecimal digits.
+ ///
+ /// @param vec Input vector.
+ /// @return String of hexadecimal digits converted from vector.
+ std::string toString(const std::vector<uint8_t>& vec) const;
+
+ /// @brief Converts current time to a string of hexadecimal digits.
+ ///
+ /// @return Time represented as text.
+ std::string timeAsHexString() const;
+
+ /// @brief Tests creation of a DUID-LLT.
+ ///
+ /// @param expected_htype Expected link layer type as string.
+ /// @param expected_time Expected time as string.
+ /// @param time_equal Indicates if @c expected time should be
+ /// compared for equality with the time being part of a DUID
+ /// (if true), or the time being part of the DUID should be
+ /// less or equal current time (if false).
+ /// @param expected_hwaddr Expected link layer type as string.
+ void testLLT(const std::string& expected_htype,
+ const std::string& expected_time,
+ const bool time_equal,
+ const std::string& expected_hwaddr);
+
+ /// @brief Tests creation of a DUID-LLT.
+ ///
+ /// @param expected_htype Expected link layer type as string.
+ /// @param expected_time Expected time as string.
+ /// @param time_equal Indicates if @c expected time should be
+ /// compared for equality with the time being part of a DUID
+ /// (if true), or the time being part of the DUID should be
+ /// less or equal current time (if false).
+ /// @param expected_hwaddr Expected link layer type as string.
+ /// @param factory_ref Reference to DUID factory.
+ void testLLT(const std::string& expected_htype,
+ const std::string& expected_time,
+ const bool time_equal,
+ const std::string& expected_hwaddr,
+ DUIDFactory& factory_ref);
+
+ /// @brief Tests creation of a DUID-EN.
+ ///
+ /// @param expected_enterprise_id Expected enterprise id as string.
+ /// @param expected_identifier Expected variable length identifier
+ /// as string. If empty string specified the test method only checks
+ /// that generated identifier consists of some random values.
+ void testEN(const std::string& expected_enterprise_id,
+ const std::string& expected_identifier = "");
+
+ /// @brief Tests creation of a DUID-EN.
+ ///
+ /// @param expected_enterprise_id Expected enterprise id as string.
+ /// @param expected_identifier Expected variable length identifier
+ /// as string. If empty string specified the test method only checks
+ /// that generated identifier consists of some random values.
+ /// @param factory_ref Reference to DUID factory.
+ void testEN(const std::string& expected_enterprise_id,
+ const std::string& expected_identifier,
+ DUIDFactory& factory_ref);
+
+ /// @brief Tests creation of a DUID-LL.
+ ///
+ /// @param expected_htype Expected link layer type as string.
+ /// @param expected_hwaddr Expected link layer type as string.
+ void testLL(const std::string& expected_htype,
+ const std::string& expected_hwaddr);
+
+ /// @brief Tests creation of a DUID-LL.
+ ///
+ /// @param expected_htype Expected link layer type as string.
+ /// @param expected_hwaddr Expected link layer type as string.
+ /// @param factory_ref Reference to DUID factory.
+ void testLL(const std::string& expected_htype,
+ const std::string& expected_hwaddr,
+ DUIDFactory& factory_ref);
+
+ /// @brief Returns reference to a default factory.
+ DUIDFactory& factory() {
+ return (factory_);
+ }
+
+private:
+
+ /// @brief Creates fake interface configuration.
+ IfaceMgrTestConfig iface_mgr_test_config_;
+
+ /// @brief Holds default instance of the @c DUIDFactory class, being
+ /// used throughout the tests.
+ DUIDFactory factory_;
+
+};
+
+DUIDFactoryTest::DUIDFactoryTest()
+ : iface_mgr_test_config_(true),
+ factory_(absolutePath(DEFAULT_DUID_FILE)) {
+ removeDefaultFile();
+}
+
+DUIDFactoryTest::~DUIDFactoryTest() {
+ removeDefaultFile();
+}
+
+std::string
+DUIDFactoryTest::absolutePath(const std::string& duid_file_name) const {
+ std::ostringstream s;
+ s << TEST_DATA_BUILDDIR << "/" << duid_file_name;
+ return (s.str());
+}
+
+void
+DUIDFactoryTest::removeDefaultFile() const {
+ static_cast<void>(remove(absolutePath(DEFAULT_DUID_FILE).c_str()));
+}
+
+std::string
+DUIDFactoryTest::readDefaultFile() const {
+ return (isc::test::readFile(absolutePath(DEFAULT_DUID_FILE)));
+}
+
+std::vector<uint8_t>
+DUIDFactoryTest::toVector(const std::string& hex) const {
+ std::vector<uint8_t> vec;
+ try {
+ util::encode::decodeHex(hex, vec);
+ } catch (...) {
+ ADD_FAILURE() << "toVector: the following string " << hex
+ << " is not a valid hex string";
+ }
+
+ return (vec);
+}
+
+std::string
+DUIDFactoryTest::toString(const std::vector<uint8_t>& vec) const {
+ try {
+ return (util::encode::encodeHex(vec));
+ } catch (...) {
+ ADD_FAILURE() << "toString: unable to encode vector to"
+ " hexadecimal string";
+ }
+ return ("");
+}
+
+std::string
+DUIDFactoryTest::timeAsHexString() const {
+ time_t current_time = time(NULL) - DUID_TIME_EPOCH;
+ std::ostringstream s;
+ s << std::hex << std::setw(8) << std::setfill('0') << current_time;
+ return (boost::to_upper_copy<std::string>(s.str()));
+}
+
+void
+DUIDFactoryTest::testLLT(const std::string& expected_htype,
+ const std::string& expected_time,
+ const bool time_equal,
+ const std::string& expected_hwaddr) {
+ testLLT(expected_htype, expected_time, time_equal, expected_hwaddr,
+ factory());
+}
+
+void
+DUIDFactoryTest::testLLT(const std::string& expected_htype,
+ const std::string& expected_time,
+ const bool time_equal,
+ const std::string& expected_hwaddr,
+ DUIDFactory& factory_ref) {
+ DuidPtr duid = factory_ref.get();
+ ASSERT_TRUE(duid);
+ ASSERT_GE(duid->getDuid().size(), 14);
+ std::string duid_text = toString(duid->getDuid());
+
+ // DUID type LLT
+ EXPECT_EQ("0001", duid_text.substr(0, 4));
+ // Link layer type HTYPE_ETHER
+ EXPECT_EQ(expected_htype, duid_text.substr(4, 4));
+
+ // Verify if time is correct.
+ if (time_equal) {
+ // Strict time check.
+ EXPECT_EQ(expected_time, duid_text.substr(8, 8));
+ } else {
+ // Timestamp equal or less current time.
+ EXPECT_LE(duid_text.substr(8, 8), expected_time);
+ }
+
+ // MAC address of the interface.
+ EXPECT_EQ(expected_hwaddr, duid_text.substr(16));
+
+ // Compare DUID with the one stored in the file.
+ EXPECT_EQ(duid->toText(), readDefaultFile());
+}
+
+void
+DUIDFactoryTest::testEN(const std::string& expected_enterprise_id,
+ const std::string& expected_identifier) {
+ testEN(expected_enterprise_id, expected_identifier, factory());
+}
+
+void
+DUIDFactoryTest::testEN(const std::string& expected_enterprise_id,
+ const std::string& expected_identifier,
+ DUIDFactory& factory_ref) {
+ DuidPtr duid = factory_ref.get();
+ ASSERT_TRUE(duid);
+ ASSERT_GE(duid->getDuid().size(), 8);
+ std::string duid_text = toString(duid->getDuid());
+
+ // DUID type EN.
+ EXPECT_EQ("0002", duid_text.substr(0, 4));
+ // Verify enterprise ID.
+ EXPECT_EQ(expected_enterprise_id, duid_text.substr(4, 8));
+
+ // If no expected identifier, we should at least check that the
+ // generated identifier contains some random non-zero digits.
+ if (expected_identifier.empty()) {
+ EXPECT_FALSE(isRangeZero(duid->getDuid().begin(),
+ duid->getDuid().end()));
+ } else {
+ // Check if identifier matches.
+ EXPECT_EQ(expected_identifier, duid_text.substr(12));
+ }
+
+ // Compare DUID with the one stored in the file.
+ EXPECT_EQ(duid->toText(), readDefaultFile());
+}
+
+void
+DUIDFactoryTest::testLL(const std::string& expected_htype,
+ const std::string& expected_hwaddr) {
+ testLL(expected_htype, expected_hwaddr, factory());
+}
+
+void
+DUIDFactoryTest::testLL(const std::string& expected_htype,
+ const std::string& expected_hwaddr,
+ DUIDFactory& factory_ref) {
+ DuidPtr duid = factory_ref.get();
+ ASSERT_TRUE(duid);
+ ASSERT_GE(duid->getDuid().size(), 8);
+ std::string duid_text = toString(duid->getDuid());
+
+ // DUID type LL
+ EXPECT_EQ("0003", duid_text.substr(0, 4));
+ // Link layer type.
+ EXPECT_EQ(expected_htype, duid_text.substr(4, 4));
+
+ // MAC address of the interface.
+ EXPECT_EQ(expected_hwaddr, duid_text.substr(8));
+
+ // Compare DUID with the one stored in the file.
+ EXPECT_EQ(duid->toText(), readDefaultFile());
+}
+
+
+// This test verifies that the factory class will generate the entire
+// DUID-LLT if there are no explicit values specified for the
+// time, link layer type and link layer address.
+TEST_F(DUIDFactoryTest, createLLT) {
+ // Use 0 values for time and link layer type and empty vector for
+ // the link layer address. The createLLT function will need to
+ // use current time, HTYPE_ETHER and MAC address of one of the
+ // interfaces.
+ ASSERT_NO_THROW(factory().createLLT(0, 0, std::vector<uint8_t>()));
+ testLLT("0001", timeAsHexString(), false, "080808080808");
+}
+
+// This test verifies that the factory class creates a DUID-LLT from
+// the explicitly specified time, when link layer type and address are
+// generated.
+TEST_F(DUIDFactoryTest, createLLTExplicitTime) {
+ ASSERT_NO_THROW(factory().createLLT(0, 0xABCDEF, std::vector<uint8_t>()));
+ testLLT("0001", "00ABCDEF", true, "080808080808");
+}
+
+// This test verifies that the factory class creates DUID-LLT with
+// the link layer type of the interface which link layer address
+// is used to generate the DUID.
+TEST_F(DUIDFactoryTest, createLLTExplicitHtype) {
+ ASSERT_NO_THROW(factory().createLLT(HTYPE_FDDI, 0, std::vector<uint8_t>()));
+ testLLT("0001", timeAsHexString(), false, "080808080808");
+}
+
+// This test verifies that the factory class creates DUID-LLT from
+// explicitly specified link layer address, when other parameters
+// are generated.
+TEST_F(DUIDFactoryTest, createLLTExplicitLinkLayerAddress) {
+ ASSERT_NO_THROW(factory().createLLT(0, 0, toVector("121212121212")));
+ testLLT("0001", timeAsHexString(), false, "121212121212");
+}
+
+// This test verifies that the factory function creates DUID-LLT from
+// all values explicitly specified.
+TEST_F(DUIDFactoryTest, createLLTAllExplicitParameters) {
+ ASSERT_NO_THROW(factory().createLLT(HTYPE_FDDI, 0xFAFAFAFA,
+ toVector("24242424242424242424")));
+ testLLT("0008", "FAFAFAFA", true, "24242424242424242424");
+}
+
+// This test verifies that the createLLT function will try to reuse existing
+// DUID for the non-explicitly specified values.
+TEST_F(DUIDFactoryTest, createLLTReuse) {
+ // Create DUID-LLT and store it in a file.
+ ASSERT_NO_THROW(factory().createLLT(HTYPE_FDDI, 0xFAFAFAFA,
+ toVector("242424242424")));
+ // Create another factory class, which uses the same file.
+ DUIDFactory factory2(absolutePath(DEFAULT_DUID_FILE));
+ // Create DUID-LLT without specifying hardware type, time and
+ // link layer address. The factory function should use the
+ // values in the existing DUID.
+ ASSERT_NO_THROW(factory2.createLLT(0, 0, std::vector<uint8_t>()));
+ testLLT("0008", "FAFAFAFA", true, "242424242424", factory2);
+
+ // Try to reuse only a time value.
+ DUIDFactory factory3(absolutePath(DEFAULT_DUID_FILE));
+ ASSERT_NO_THROW(factory3.createLLT(HTYPE_ETHER, 0,
+ toVector("121212121212")));
+ testLLT("0001", "FAFAFAFA", true, "121212121212", factory3);
+
+ // Reuse only a hardware type.
+ DUIDFactory factory4(absolutePath(DEFAULT_DUID_FILE));
+ ASSERT_NO_THROW(factory4.createLLT(0, 0x23432343,
+ toVector("455445544554")));
+ testLLT("0001", "23432343", true, "455445544554", factory4);
+
+ // Reuse link layer address. Note that in this case the hardware
+ // type is set to the type of the interface from which hardware
+ // address is obtained and the explicit value is ignored.
+ DUIDFactory factory5(absolutePath(DEFAULT_DUID_FILE));
+ ASSERT_NO_THROW(factory5.createLLT(HTYPE_FDDI, 0x11111111,
+ std::vector<uint8_t>()));
+ testLLT("0001", "11111111", true, "455445544554", factory5);
+}
+
+// This test verifies that the DUID-EN can be generated entirely. Such
+// generated DUID contains ISC enterprise id and the random identifier.
+TEST_F(DUIDFactoryTest, createEN) {
+ ASSERT_NO_THROW(factory().createEN(0, std::vector<uint8_t>()));
+ testEN("000009BF");
+}
+
+// This test verifies that the DUID-EN may contain custom enterprise id.
+TEST_F(DUIDFactoryTest, createENExplicitEnterpriseId) {
+ ASSERT_NO_THROW(factory().createEN(0xABCDEFAB, std::vector<uint8_t>()));
+ testEN("ABCDEFAB");
+}
+
+// This test verifies that DUID-EN may contain custom variable length
+// identifier and default enterprise id.
+TEST_F(DUIDFactoryTest, createENExplicitIdentifier) {
+ ASSERT_NO_THROW(factory().createEN(0, toVector("1212121212121212")));
+ testEN("000009BF", "1212121212121212");
+}
+
+// This test verifies that DUID-EN can be created from explicit enterprise id
+// and identifier.
+TEST_F(DUIDFactoryTest, createENAllExplicitParameters) {
+ ASSERT_NO_THROW(factory().createEN(0x01020304, toVector("ABCD")));
+ testEN("01020304", "ABCD");
+}
+
+// This test verifies that the createEN function will try to reuse existing
+// DUID for the non-explicitly specified values.
+TEST_F(DUIDFactoryTest, createENReuse) {
+ // Create DUID-EN and store it in a file.
+ ASSERT_NO_THROW(factory().createEN(0xFAFAFAFA, toVector("242424242424")));
+ // Create another factory class, which uses the same file.
+ DUIDFactory factory2(absolutePath(DEFAULT_DUID_FILE));
+ ASSERT_NO_THROW(factory2.createEN(0, std::vector<uint8_t>()));
+ testEN("FAFAFAFA", "242424242424", factory2);
+
+ // Reuse only enterprise id.
+ DUIDFactory factory3(absolutePath(DEFAULT_DUID_FILE));
+ ASSERT_NO_THROW(factory3.createEN(0, toVector("121212121212")));
+ testEN("FAFAFAFA", "121212121212", factory3);
+
+ // Reuse only variable length identifier.
+ DUIDFactory factory4(absolutePath(DEFAULT_DUID_FILE));
+ ASSERT_NO_THROW(factory4.createEN(0x1234, std::vector<uint8_t>()));
+ testEN("00001234", "121212121212", factory4);
+}
+
+// This test verifies that the DUID-LL is generated when neither link layer
+// type nor address is specified.
+TEST_F(DUIDFactoryTest, createLL) {
+ ASSERT_NO_THROW(factory().createLL(0, std::vector<uint8_t>()));
+ testLL("0001", "080808080808");
+}
+
+// This test verifies that the DUID-LL is generated and the link layer type
+// used is taken from the interface used to generate link layer address.
+TEST_F(DUIDFactoryTest, createLLExplicitHtype) {
+ ASSERT_NO_THROW(factory().createLL(HTYPE_FDDI, std::vector<uint8_t>()));
+ testLL("0001", "080808080808");
+}
+
+// This test verifies that DUID-LL is created from explicitly provided
+// link layer type and address.
+TEST_F(DUIDFactoryTest, createLLAllExplicitParameters) {
+ ASSERT_NO_THROW(factory().createLL(HTYPE_FDDI, toVector("242424242424")));
+ testLL("0008", "242424242424");
+}
+
+// This test verifies that DUID-LLT is created when caller wants to obtain
+// it and it doesn't exist.
+TEST_F(DUIDFactoryTest, createLLTIfNotExists) {
+ DuidPtr duid;
+ ASSERT_NO_THROW(duid = factory().get());
+ ASSERT_TRUE(duid);
+ EXPECT_EQ(DUID::DUID_LLT, duid->getType());
+}
+
+// This test verifies that DUID-EN when there is no suitable interface to
+// use to create DUID-LLT.
+TEST_F(DUIDFactoryTest, createENIfNotExists) {
+ // Remove interfaces. The DUID-LLT is a default type but it requires
+ // that an interface with a suitable link-layer address is present
+ // in the system. By removing the interfaces we cause the factory
+ // to fail to generate DUID-LLT. It should fall back to DUID-EN.
+ IfaceMgr::instance().clearIfaces();
+
+ DuidPtr duid;
+ ASSERT_NO_THROW(duid = factory().get());
+ ASSERT_TRUE(duid);
+ EXPECT_EQ(DUID::DUID_EN, duid->getType());
+}
+
+// This test verifies that the createLL function will try to reuse existing
+// DUID for the non-explicitly specified values.
+TEST_F(DUIDFactoryTest, createLLReuse) {
+ // Create DUID-EN and store it in a file.
+ ASSERT_NO_THROW(factory().createLL(HTYPE_FDDI, toVector("242424242424")));
+ // Create another factory class, which uses the same file.
+ DUIDFactory factory2(absolutePath(DEFAULT_DUID_FILE));
+ // Create DUID-LL without specifying hardware type, time and
+ // link layer address. The factory function should use the
+ // values in the existing DUID.
+ ASSERT_NO_THROW(factory2.createLL(0, std::vector<uint8_t>()));
+ testLL("0008", "242424242424", factory2);
+
+ // Reuse only hardware type
+ DUIDFactory factory3(absolutePath(DEFAULT_DUID_FILE));
+ ASSERT_NO_THROW(factory3.createLL(0, toVector("121212121212")));
+ testLL("0008", "121212121212", factory3);
+
+ // Reuse link layer address. Note that when the link layer address is
+ // reused, the explicit value of hardware type is reused too and the
+ // explicit value of hardware type is ignored.
+ DUIDFactory factory4(absolutePath(DEFAULT_DUID_FILE));
+ ASSERT_NO_THROW(factory4.createLL(HTYPE_ETHER, std::vector<uint8_t>()));
+ testLL("0008", "121212121212", factory4);
+}
+
+// This test verifies that it is possible to override a DUID.
+TEST_F(DUIDFactoryTest, override) {
+ // Create default DUID-LLT.
+ ASSERT_NO_THROW(static_cast<void>(factory().get()));
+ testLLT("0001", timeAsHexString(), false, "080808080808");
+
+ ASSERT_NO_THROW(factory().createEN(0, toVector("12131415")));
+ testEN("000009BF", "12131415");
+}
+
+} // End anonymous namespace