#include <dhcp/duid_factory.h>
#include <dhcp/iface_mgr.h>
+#include <exceptions/exceptions.h>
#include <util/io_utilities.h>
#include <util/range_utilities.h>
#include <util/strutil.h>
#include <boost/foreach.hpp>
#include <ctime>
+#include <fstream>
+#include <stdlib.h>
#include <string>
#include <vector>
namespace {
+/// @brief Length of the DUID type field.
+const size_t DUID_TYPE_LEN = 2;
+
+/// @brief Minimal length of the MAC address.
const size_t MIN_MAC_LEN = 6;
+/// @brief Length of the enterprise if field.
+const size_t ENTERPRISE_ID_LEN = 4;
+
+/// @brief Default length of the variable length identifier in the DUID-EN.
+const size_t DUID_EN_IDENTIFIER_LEN = 6;
+
}
namespace isc {
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 unspecified, use current time.
if (time_out == 0) {
time_out = static_cast<uint32_t>(time(NULL) - DUID_TIME_EPOCH);
}
+ std::vector<uint8_t> ll_identifier_out = ll_identifier;
uint16_t htype_out = htype;
- if (htype_out == 0) {
- htype_out = HTYPE_ETHER;
- }
- std::vector<uint8_t> ll_identifier_out = ll_identifier;
+ // If link layer address unspecified, use address of one of the
+ // interfaces present in the system. Also, update the link
+ // layer type accordingly.
if (ll_identifier_out.empty()) {
- createLinkLayerId(ll_identifier_out);
+ createLinkLayerId(ll_identifier_out, htype_out);
+
+ } else if (htype_out == 0) {
+ // If link layer type unspecified and link layer adddress
+ // is specified, use HTYPE_ETHER.
+ htype_out = HTYPE_ETHER;
+
}
- std::vector<uint8_t> duid_out(2 + sizeof(time_out) + sizeof(htype_out));
+ // Render DUID.
+ std::vector<uint8_t> duid_out(DUID_TYPE_LEN + 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));
+ // Set new DUID and persist in a file.
+ set(duid_out);
}
-/*void
+void
DUIDFactory::createEN(const uint32_t enterprise_id,
- const std::vector<uint8_t>& identifier) {
-}*/
+ const std::vector<uint8_t>& identifier) {
+ // Enterprise id 0 means "unspecified". In this case use the ISC
+ // enterprise id.
+ uint32_t enterprise_id_out = enterprise_id != 0 ?
+ enterprise_id : ENTERPRISE_ID_ISC;
+
+ // Render DUID.
+ std::vector<uint8_t> duid_out(DUID_TYPE_LEN + ENTERPRISE_ID_LEN);
+ writeUint16(DUID::DUID_EN, &duid_out[0], 2);
+ writeUint32(enterprise_id_out, &duid_out[2], ENTERPRISE_ID_LEN);
+
+ if (identifier.empty()) {
+ // Identifier is empty, so we have to extend the DUID by 6 bytes
+ // to fit the random identifier.
+ duid_out.resize(DUID_TYPE_LEN + ENTERPRISE_ID_LEN +
+ DUID_EN_IDENTIFIER_LEN);
+ // Variable length identifier consists of random numbers. The generated
+ // identifier is always 6 bytes long.
+ ::srandom(time(NULL));
+ fillRandom(&duid_out[DUID_TYPE_LEN + ENTERPRISE_ID_LEN],
+ &duid_out[DUID_TYPE_LEN + ENTERPRISE_ID_LEN +
+ DUID_EN_IDENTIFIER_LEN]);
+
+ } else {
+ // Append the specified identifier to the end of DUID.
+ duid_out.insert(duid_out.end(), identifier.begin(), identifier.end());
+ }
+
+ // Set new DUID and persist in a file.
+ set(duid_out);
+}
+
+void
+DUIDFactory::createLL(const uint16_t htype,
+ const std::vector<uint8_t>& ll_identifier) {
+ std::vector<uint8_t> ll_identifier_out = ll_identifier;
+ uint16_t htype_out = htype;
+
+ // If link layer address unspecified, use address of one of the
+ // interfaces present in the system. Also, update the link
+ // layer type accordingly.
+ if (ll_identifier_out.empty()) {
+ createLinkLayerId(ll_identifier_out, htype_out);
+
+ } else if (htype_out == 0) {
+ // If link layer type unspecified and link layer adddress
+ // is specified, use HTYPE_ETHER.
+ htype_out = HTYPE_ETHER;
+
+ }
+
+ // Render DUID.
+ std::vector<uint8_t> duid_out(DUID_TYPE_LEN + sizeof(htype_out));
+ writeUint16(DUID::DUID_LL, &duid_out[0], 2);
+ writeUint16(htype_out, &duid_out[2], 2);
+ duid_out.insert(duid_out.end(), ll_identifier_out.begin(),
+ ll_identifier_out.end());
-/*void
-DUIDFactory::createLL(const uint16_t htype, const std::vector<uint8_t>& ll_identifier) {
-} */
+ // Set new DUID and persist in a file.
+ set(duid_out);
+}
void
-DUIDFactory::createLinkLayerId(std::vector<uint8_t>& identifier) const {
+DUIDFactory::createLinkLayerId(std::vector<uint8_t>& identifier,
+ uint16_t& htype) const {
const IfaceMgr::IfaceCollection& ifaces = IfaceMgr::instance().getIfaces();
// Let's find suitable interface.
continue;
}
+ // Assign link layer address and type.
identifier.assign(iface->getMac(), iface->getMac() + iface->getMacLen());
+ htype = iface->getHWType();
+ }
+
+ // We failed to find an interface which link layer address could be
+ // used for generating DUID-LLT.
+ if (identifier.empty()) {
+ isc_throw(Unexpected, "unable to find suitable interface for "
+ " generating a DUID-LLT");
}
}
+void
+DUIDFactory::set(const std::vector<uint8_t>& duid_vector) {
+ // Check the minimal length.
+ if (duid_vector.size() < DUID::MIN_DUID_LEN) {
+ isc_throw(BadValue, "generated DUID must have at least "
+ << DUID::MIN_DUID_LEN << " bytes");
+ }
+
+ // Persist DUID in a file if file location specified.
+ if (isPersisted()) {
+ std::ofstream ofs;
+ try {
+ ofs.open(storage_location_, std::ofstream::out |
+ std::ofstream::trunc);
+ if (!ofs.good()) {
+ isc_throw(InvalidOperation, "unable to open DUID file "
+ << storage_location_ << " for writing");
+ }
+
+ // Create temporary DUID object.
+ DUID duid(duid_vector);
+
+ // Write DUID to file.
+ ofs << duid.toText();
+ if (!ofs.good()) {
+ isc_throw(InvalidOperation, "unable to write to DUID file "
+ << storage_location_);
+ }
+ } catch (...) {
+ // Close stream before leaving the function.
+ ofs.close();
+ throw;
+ }
+ ofs.close();
+ }
+
+ duid_.reset(new DUID(duid_vector));
+}
+
DuidPtr
DUIDFactory::get() {
+ // If DUID is initialized, return it.
+ if (duid_) {
+ return (duid_);
+ }
+
+ // If DUID object hasn't been initialized then we need to retrieve a
+ // DUID from the file or create one.
+ std::ostringstream duid_str;
+ if (isPersisted()) {
+ std::ifstream ifs;
+ ifs.open(storage_location_, std::ifstream::in);
+ if (ifs.good()) {
+ std::string read_contents;
+ while (!ifs.eof() && ifs.good()) {
+ ifs >> read_contents;
+ duid_str << read_contents;
+ }
+ }
+ ifs.close();
+
+ // If we have read anything from the file, let's try to use it to
+ // create a DUID.
+ if (duid_str.tellp() != 0) {
+ try {
+ duid_.reset(new DUID(DUID::fromText(duid_str.str())));
+ return (duid_);
+
+ } catch (...) {
+ // The contents of this file don't represent a valid DUID.
+ // We'll need to generate it.
+ }
+ }
+
+ }
+
+ try {
+ // There is no file with a DUID or the DUID stored in the file is
+ // invalid. We need to generate a new DUID.
+ createLLT(0, 0, std::vector<uint8_t>());
+
+ } catch (...) {
+ // It is possible that the creation of the DUID-LLT failed if there
+ // are no suitable interfaces present in the system.
+ }
+
+ if (!duid_) {
+ // Fall back to creation of DUID enterprise. If that fails we allow
+ // for propagating exception to indicate a fatal error. This may
+ // be the case if we failed to write it to a file.
+ createEN(0, std::vector<uint8_t>());
+ }
+
return (duid_);
}
#define DUID_FACTORY_H
#include <dhcp/duid.h>
+#include <boost/noncopyable.hpp>
#include <stdint.h>
#include <string>
#include <vector>
namespace isc {
namespace dhcp {
-class DUIDFactory {
+/// @brief Factory for generating DUIDs (DHCP Unique Identifiers).
+///
+/// DHCPv6 clients and servers are identified by DUIDs (see RFC3315).
+/// DUIDs are unique identifiers carried in the appropriate DHCP
+/// options. RFC3315 defines 3 types of DUIDs:
+/// -# DUID-LLT
+/// -# DUID-EN
+/// -# DUID-LL
+///
+/// of which the DUID-LLT is recommended for all general purpose computing
+/// devices. RFC6355 defines new DUID-UUID and any future specifications may
+/// define new DUID types. The current implementation of the class only
+/// supports DUID types defined in RFC3315.
+///
+/// In most cases DUIDs can be generated automatically, i.e. no manual
+/// configuration is required. For example, DUID-LLT is composed of the
+/// current time and link layer address and type of one of the network
+/// interfaces. Once the DUID is generated it should be stored in the persistent
+/// storage and used by a server or client even when the network interface which
+/// address had been used to generate the DUID is removed.
+///
+/// In some cases administrators may elect to use other types of DUIDs, which
+/// are easier to generate (in case of lack of persistent storage or when
+/// specifics of the device favors some generation methods), e.g. DUID-EN
+/// doesn't rely on the link layer addresses of interfaces present in the
+/// system.
+///
+/// In some cases administrators may want to influence the value of the
+/// generated DUID. For example, DUID-EN includes enterprise identifier and
+/// the administrator may want to select this identifier.
+///
+/// This class allows for selecting a type of DUID to be generated. It also
+/// allows for setting desired values for the components of the DUIDs
+/// being generated, while leaving other components unspecified. For example
+/// an administrator may elect to set the enterprise id for the DUID-EN
+/// and leave the variable length identifier unspecified. The variable
+/// length identifier will be autogenerated.
+///
+/// This class is also responsible for storing the generated DUID in a
+/// file. The location of this file is specified in the class constructor.
+/// If this location is not specified the DUID is not persisted, i.e. is
+/// lost when the server or client shuts down. However, the DUID may be
+/// reconstructed according to the configuration of the client or server
+/// when they are back online.
+class DUIDFactory : public boost::noncopyable {
public:
+ /// @brief Constructor.
+ ///
+ /// @param storage_location Absolute path to the file where DUID is
+ /// stored.
DUIDFactory(const std::string& storage_location = "");
+ /// @brief Checks if generated DUID will be persisted in the file.
+ ///
+ /// @return true if generated DUIDs are persisted in a file, false
+ /// otherwise.
bool isPersisted() const;
+ /// @brief Generates DUID-LLT.
+ ///
+ /// This method generates DUID-LLT.
+ ///
+ /// @param htype Link layer type. If this is set to 0 and link layer
+ /// address is empty a default value of @c HTYPE_ETHER is used.
+ /// Otherwise a link layer type of selected interface is used.
+ /// @param time_in Explicit value of time for the DUID. If this is
+ /// set to 0 a current time is used, otherwise a value specified is
+ /// used.
+ /// @param ll_identifier Data to be used as link layer address. If
+ /// this is an empty vector this method will iterate over all
+ /// active interfaces and will pick link layer address of one of them.
+ ///
+ /// @throw isc::Unexpected if none of the interfaces includes has a
+ /// suitable link layer address.
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);
-
+ /// @brief Generates DUID-EN.
+ ///
+ /// @param enterprise_id Enterprise id. If this value is 0, the ISC's
+ /// enterprise id is used.
+ /// @param identifier Data to be used as variable length identifier.
+ /// If this is an empty vector, the 6-bytes long vector with random
+ /// values is generated.
+ void createEN(const uint32_t enterprise_id,
+ const std::vector<uint8_t>& identifier);
+
+ /// @brief Generates DUID-LL.
+ ///
+ /// @param htype Link layer type. If this is set to 0 and link layer
+ /// address is empty a default value of @c HTYPE_ETHER is used.
+ /// Otherwise a link layer type of selected interface is used.
+ /// @param ll_identifier Data to be used as link layer address. If
+ /// this is an empty vector this method will iterate over all
+ /// active interfaces and will pick link layer address of one of them.
+ ///
+ /// @throw isc::Unexpected if none of the interfaces includes has a
+ /// suitable link layer address.
+ void createLL(const uint16_t htype,
+ const std::vector<uint8_t>& ll_identifier);
+
+ /// @brief Returns current DUID.
+ ///
+ /// This method first checks if the DUID has been generated, i.e. as a
+ /// result of calling DUIDFactory::createLLT. If the DUID hasn't been
+ /// generated, this method will try to read the DUID from the persistent
+ /// storage. If the DUID is found in persistent storage it is returned.
+ /// Otherwise, the DUID-LLT is generated and returned. In some cases the
+ /// generation of the DUID-LLT may fail, e.g. when there are no interfaces
+ /// with a suitable link layer address. In this case, this method will
+ /// generate DUID-EN, with the ISC enterprise id. If this fails, e.g. as a
+ /// result of error while persisting the generated DUID-EN, exception
+ /// is thrown.
+ ///
+ /// @return Instance of the DUID read from file, or generated.
DuidPtr get();
private:
- void createLinkLayerId(std::vector<uint8_t>& identifier) const;
-
+ /// @brief Creates link layer identifier.
+ ///
+ /// This method iterates over existing network interfaces and finds the
+ /// one with a suitable link layer address to generate a DUID-LLT or
+ /// DUID-LL. It uses selected link layer address to generate identifier
+ /// held in those DUID types.
+ ///
+ /// @param [out] identifier Link layer address for the DUID.
+ /// @param [out] htype Link layer type to be included in the DUID.
+ void createLinkLayerId(std::vector<uint8_t>& identifier,
+ uint16_t& htype) const;
+
+ /// @brief Sets a new DUID as current.
+ ///
+ /// The generated DUID is persisted in the file, if such file is specified.
+ /// The new DUID will be returned when @c DUIDFactory::get is called.
+ ///
+ /// @param duid_vector New DUID represented as vector of bytes.
+ void set(const std::vector<uint8_t>& duid_vector);
+
+ /// @brief Location of the file holding generated DUID (if specified).
std::string storage_location_;
+ /// @brief Pointer to generated DUID.
DuidPtr duid_;
};
#include <dhcp/duid_factory.h>
#include <dhcp/tests/iface_mgr_test_config.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>
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_path) 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-EN.
+ ///
+ /// @param expected_enterprise_id Expected enterprise id as string.
+ /// @param expected_identifier Expected variable length identifier
+ /// as string.
+ void testEN(const std::string& expected_enterprise_id,
+ const std::string& expected_identifier = "");
+
+ /// @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 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_;
};
static_cast<void>(remove(absolutePath(DEFAULT_DUID_FILE).c_str()));
}
+std::string
+DUIDFactoryTest::readDefaultFile() const {
+ std::ifstream ifs;
+ ifs.open(absolutePath(DEFAULT_DUID_FILE), std::ifstream::in);
+ if (!ifs.good()) {
+ return (std::string());
+ }
+ std::string buf;
+ std::ostringstream output;
+ while (!ifs.eof() && ifs.good()) {
+ ifs >> buf;
+ output << buf;
+ }
+ ifs.close();
+
+ return (output.str());
+}
+
std::vector<uint8_t>
DUIDFactoryTest::toVector(const std::string& hex) const {
std::vector<uint8_t> vec;
// 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) {
+ DuidPtr duid = factory().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 expecyed 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) {
+ DuidPtr duid = factory().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 HTYPE_ETHER
+ 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());
}
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.
+// 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("0008", timeAsHexString(), false, "010101010101");
+ testLLT("0001", timeAsHexString(), false, "010101010101");
}
// This test verifies that the factory class creates DUID-LLT from
testLLT("0008", "FAFAFAFA", true, "24242424242424242424");
}
+// 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 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", "010101010101");
+}
+
+// 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", "010101010101");
+}
+
+// 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 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, "010101010101");
+
+ ASSERT_NO_THROW(factory().createEN(0, toVector("12131415")));
+ testEN("000009BF", "12131415");
+}
+
} // End anonymous namespace
// 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());
+ iface->setHWType(HTYPE_ETHER);
return (iface);
}