Commit d8470a14 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

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

parent c4557eef
......@@ -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
......
// 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
// 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 */
......@@ -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
......
// 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
......@@ -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);
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment