From: Marcin Siodelski Date: Thu, 12 Nov 2015 19:53:23 +0000 (+0100) Subject: [3874] Added DUID factory class in libdhcp++. X-Git-Tag: trac4231_base~39^2~14 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=d8470a140729e0cc6281f76af86678517419da5a;p=thirdparty%2Fkea.git [3874] Added DUID factory class in libdhcp++. --- diff --git a/src/lib/dhcp/Makefile.am b/src/lib/dhcp/Makefile.am index b892192b9c..bd763c2e8f 100644 --- a/src/lib/dhcp/Makefile.am +++ b/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 diff --git a/src/lib/dhcp/duid_factory.cc b/src/lib/dhcp/duid_factory.cc new file mode 100644 index 0000000000..19cb9082c3 --- /dev/null +++ b/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 +#include +#include +#include +#include +#include +#include +#include +#include + +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& ll_identifier) { + uint32_t time_out = time_in; + if (time_out == 0) { + time_out = static_cast(time(NULL) - DUID_TIME_EPOCH); + } + + uint16_t htype_out = htype; + if (htype_out == 0) { + htype_out = HTYPE_ETHER; + } + + std::vector ll_identifier_out = ll_identifier; + if (ll_identifier_out.empty()) { + createLinkLayerId(ll_identifier_out); + } + + std::vector 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& identifier) { +}*/ + +/*void +DUIDFactory::createLL(const uint16_t htype, const std::vector& ll_identifier) { +} */ + +void +DUIDFactory::createLinkLayerId(std::vector& 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 diff --git a/src/lib/dhcp/duid_factory.h b/src/lib/dhcp/duid_factory.h new file mode 100644 index 0000000000..f0b705f9fc --- /dev/null +++ b/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 +#include +#include +#include + +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& ll_identifier); + + void createEN(const uint32_t enterprise_id, const std::vector& identifier); + + void createLL(const uint16_t htype, const std::vector& ll_identifier); + + DuidPtr get(); + +private: + + void createLinkLayerId(std::vector& identifier) const; + + std::string storage_location_; + + DuidPtr duid_; + +}; + +}; // end of isc::dhcp namespace +}; // end of isc namespace + +#endif /* DUID_FACTORY_H */ diff --git a/src/lib/dhcp/tests/Makefile.am b/src/lib/dhcp/tests/Makefile.am index 8faf432b14..9a42095c5b 100644 --- a/src/lib/dhcp/tests/Makefile.am +++ b/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 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 0000000000..919e02c286 --- /dev/null +++ b/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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 toVector(const std::string& hex) const; + + std::string toString(const std::vector& 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(remove(absolutePath(DEFAULT_DUID_FILE).c_str())); +} + +std::vector +DUIDFactoryTest::toVector(const std::string& hex) const { + std::vector 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& 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(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())); + 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())); + 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())); + 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 diff --git a/src/lib/dhcp/tests/iface_mgr_test_config.cc b/src/lib/dhcp/tests/iface_mgr_test_config.cc index 2cbaac1081..f173ae62a8 100644 --- a/src/lib/dhcp/tests/iface_mgr_test_config.cc +++ b/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 mac_vec(6, 1); + iface->setMac(&mac_vec[0], mac_vec.size()); + return (iface); }