Browse Source

[3874] Added DUID factory class in libdhcp++.

Marcin Siodelski 9 years ago
parent
commit
d8470a1407

+ 1 - 0
src/lib/dhcp/Makefile.am

@@ -17,6 +17,7 @@ libkea_dhcp___la_SOURCES  =
 libkea_dhcp___la_SOURCES += classify.cc classify.h
 libkea_dhcp___la_SOURCES += dhcp6.h dhcp4.h
 libkea_dhcp___la_SOURCES += duid.cc duid.h
+libkea_dhcp___la_SOURCES += duid_factory.cc duid_factory.h
 libkea_dhcp___la_SOURCES += hwaddr.cc hwaddr.h
 libkea_dhcp___la_SOURCES += iface_mgr.cc iface_mgr.h
 libkea_dhcp___la_SOURCES += iface_mgr_bsd.cc

+ 134 - 0
src/lib/dhcp/duid_factory.cc

@@ -0,0 +1,134 @@
+// Copyright (C) 2015 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
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <dhcp/duid_factory.h>
+#include <dhcp/iface_mgr.h>
+#include <util/io_utilities.h>
+#include <util/range_utilities.h>
+#include <util/strutil.h>
+#include <boost/foreach.hpp>
+#include <ctime>
+#include <string>
+#include <vector>
+
+using namespace isc::util;
+using namespace isc::util::str;
+
+namespace {
+
+const size_t MIN_MAC_LEN = 6;
+
+}
+
+namespace isc {
+namespace dhcp {
+
+DUIDFactory::DUIDFactory(const std::string& storage_location)
+    : storage_location_(trim(storage_location)), duid_() {
+}
+
+bool
+DUIDFactory::isPersisted() const {
+    return (!storage_location_.empty());
+}
+
+void
+DUIDFactory::createLLT(const uint16_t htype, const uint32_t time_in,
+                       const std::vector<uint8_t>& ll_identifier) {
+    uint32_t time_out = time_in;
+    if (time_out == 0) {
+        time_out = static_cast<uint32_t>(time(NULL) - DUID_TIME_EPOCH);
+    }
+
+    uint16_t htype_out = htype;
+    if (htype_out == 0) {
+        htype_out = HTYPE_ETHER;
+    }
+
+    std::vector<uint8_t> ll_identifier_out = ll_identifier;
+    if (ll_identifier_out.empty()) {
+        createLinkLayerId(ll_identifier_out);
+    }
+
+    std::vector<uint8_t> duid_out(2 + sizeof(time_out) + sizeof(htype_out));
+    writeUint16(DUID::DUID_LLT, &duid_out[0], 2);
+    writeUint16(htype_out, &duid_out[2], 2);
+    writeUint32(time_out, &duid_out[4], 4);
+    duid_out.insert(duid_out.end(), ll_identifier_out.begin(),
+                    ll_identifier_out.end());
+
+    duid_.reset(new DUID(duid_out));
+}
+
+/*void
+DUIDFactory::createEN(const uint32_t enterprise_id,
+                          const std::vector<uint8_t>& identifier) {
+}*/
+
+/*void
+DUIDFactory::createLL(const uint16_t htype, const std::vector<uint8_t>& ll_identifier) {
+} */
+
+void
+DUIDFactory::createLinkLayerId(std::vector<uint8_t>& identifier) const {
+    const IfaceMgr::IfaceCollection& ifaces = IfaceMgr::instance().getIfaces();
+
+    // Let's find suitable interface.
+    BOOST_FOREACH(IfacePtr iface, ifaces) {
+        // All the following checks could be merged into one multi-condition
+        // statement, but let's keep them separated as perhaps one day
+        // we will grow knobs to selectively turn them on or off. Also,
+        // this code is used only *once* during first start on a new machine
+        // and then server-id is stored. (or at least it will be once
+        // DUID storage is implemented)
+
+        // I wish there was a this_is_a_real_physical_interface flag...
+
+        // MAC address should be at least 6 bytes. Although there is no such
+        // requirement in any RFC, all decent physical interfaces (Ethernet,
+        // WiFi, InfiniBand, etc.) have 6 bytes long MAC address. We want to
+        // base our DUID on real hardware address, rather than virtual
+        // interface that pretends that underlying IP address is its MAC.
+        if (iface->getMacLen() < MIN_MAC_LEN) {
+            continue;
+        }
+
+        // Let's don't use loopback.
+        if (iface->flag_loopback_) {
+            continue;
+        }
+
+        // Let's skip downed interfaces. It is better to use working ones.
+        if (!iface->flag_up_) {
+            continue;
+        }
+
+        // Some interfaces (like lo on Linux) report 6-bytes long
+        // MAC address 00:00:00:00:00:00. Let's not use such weird interfaces
+        // to generate DUID.
+        if (isRangeZero(iface->getMac(), iface->getMac() + iface->getMacLen())) {
+            continue;
+        }
+
+        identifier.assign(iface->getMac(), iface->getMac() + iface->getMacLen());
+    }
+}
+
+DuidPtr
+DUIDFactory::get() {
+    return (duid_);
+}
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace

+ 55 - 0
src/lib/dhcp/duid_factory.h

@@ -0,0 +1,55 @@
+// Copyright (C) 2015 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
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef DUID_FACTORY_H
+#define DUID_FACTORY_H
+
+#include <dhcp/duid.h>
+#include <stdint.h>
+#include <string>
+#include <vector>
+
+namespace isc {
+namespace dhcp {
+
+class DUIDFactory {
+public:
+
+    DUIDFactory(const std::string& storage_location = "");
+
+    bool isPersisted() const;
+
+    void createLLT(const uint16_t htype, const uint32_t time_in,
+                   const std::vector<uint8_t>& ll_identifier);
+
+    void createEN(const uint32_t enterprise_id, const std::vector<uint8_t>& identifier);
+
+    void createLL(const uint16_t htype, const std::vector<uint8_t>& ll_identifier);
+
+    DuidPtr get();
+
+private:
+
+    void createLinkLayerId(std::vector<uint8_t>& identifier) const;
+
+    std::string storage_location_;
+
+    DuidPtr duid_;
+
+};
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
+
+#endif /* DUID_FACTORY_H */

+ 1 - 0
src/lib/dhcp/tests/Makefile.am

@@ -47,6 +47,7 @@ TESTS += libdhcp++_unittests
 
 libdhcp___unittests_SOURCES  = run_unittests.cc
 libdhcp___unittests_SOURCES += classify_unittest.cc
+libdhcp___unittests_SOURCES += duid_factory_unittest.cc
 libdhcp___unittests_SOURCES += hwaddr_unittest.cc
 libdhcp___unittests_SOURCES += iface_mgr_unittest.cc
 libdhcp___unittests_SOURCES += iface_mgr_test_config.cc iface_mgr_test_config.h

+ 198 - 0
src/lib/dhcp/tests/duid_factory_unittest.cc

@@ -0,0 +1,198 @@
+// Copyright (C) 2015  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
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+#include <dhcp/dhcp4.h>
+#include <dhcp/duid_factory.h>
+#include <dhcp/tests/iface_mgr_test_config.h>
+#include <util/encode/hex.h>
+#include <boost/algorithm/string.hpp>
+#include <gtest/gtest.h>
+#include <ctime>
+#include <iomanip>
+#include <sstream>
+#include <stdio.h>
+#include <string>
+#include <vector>
+
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::dhcp::test;
+
+namespace {
+
+const std::string DEFAULT_DUID_FILE = "duid-factory-test.duid";
+
+class DUIDFactoryTest : public ::testing::Test {
+public:
+
+    DUIDFactoryTest();
+
+    virtual ~DUIDFactoryTest();
+
+    std::string absolutePath(const std::string& duid_file_path) const;
+
+    void removeDefaultFile() const;
+
+    std::vector<uint8_t> toVector(const std::string& hex) const;
+
+    std::string toString(const std::vector<uint8_t>& vec) const;
+
+    std::string timeAsHexString() const;
+
+    void testLLT(const std::string& expected_htype,
+                 const std::string& expected_time,
+                 const bool time_equal,
+                 const std::string& expected_hwaddr);
+
+    DUIDFactory& factory() {
+        return (factory_);
+    }
+
+private:
+
+    IfaceMgrTestConfig iface_mgr_test_config_;
+
+    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_path) const {
+    std::ostringstream s;
+    s << TEST_DATA_BUILDDIR << "/" << duid_file_path;
+    return (s.str());
+}
+
+void
+DUIDFactoryTest::removeDefaultFile() const {
+    static_cast<void>(remove(absolutePath(DEFAULT_DUID_FILE).c_str()));
+}
+
+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) {
+    DuidPtr duid = factory().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));
+}
+
+
+// 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, "010101010101");
+}
+
+// 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, "010101010101");
+}
+
+// This test verifies that the factory class creates DUID-LLT from
+// the explcitly specified link layer type, when the time and link
+// layer address are generated.
+TEST_F(DUIDFactoryTest, createLLTExplicitHtype) {
+    ASSERT_NO_THROW(factory().createLLT(HTYPE_FDDI, 0, std::vector<uint8_t>()));
+    testLLT("0008", timeAsHexString(), false, "010101010101");
+}
+
+// This test verifies that the factory class creates DUID-LLT from
+// explcitly 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, createLLTAllExplcitParameters) {
+    ASSERT_NO_THROW(factory().createLLT(HTYPE_FDDI, 0xFAFAFAFA,
+                                        toVector("24242424242424242424")));
+    testLLT("0008", "FAFAFAFA", true, "24242424242424242424");
+}
+
+} // End anonymous namespace

+ 5 - 0
src/lib/dhcp/tests/iface_mgr_test_config.cc

@@ -95,6 +95,11 @@ IfaceMgrTestConfig::createIface(const std::string &name, const int ifindex) {
     iface->flag_broadcast_ = false;
     iface->flag_up_ = true;
     iface->flag_running_ = true;
+
+    // Set MAC address to 01:01:01:01:01:01.
+    std::vector<uint8_t> mac_vec(6, 1);
+    iface->setMac(&mac_vec[0], mac_vec.size());
+
     return (iface);
 }