duid_factory_unittest.cc 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. // Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this
  5. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6. #include <config.h>
  7. #include <dhcp/dhcp4.h>
  8. #include <dhcp/duid_factory.h>
  9. #include <dhcp/tests/iface_mgr_test_config.h>
  10. #include <testutils/io_utils.h>
  11. #include <util/encode/hex.h>
  12. #include <util/range_utilities.h>
  13. #include <boost/algorithm/string.hpp>
  14. #include <gtest/gtest.h>
  15. #include <ctime>
  16. #include <fstream>
  17. #include <iomanip>
  18. #include <sstream>
  19. #include <stdio.h>
  20. #include <string>
  21. #include <vector>
  22. using namespace isc;
  23. using namespace isc::dhcp;
  24. using namespace isc::dhcp::test;
  25. using namespace isc::util;
  26. namespace {
  27. /// @brief Name of the file holding DUID generated during a test.
  28. const std::string DEFAULT_DUID_FILE = "duid-factory-test.duid";
  29. /// @brief Test fixture class for @c DUIDFactory.
  30. class DUIDFactoryTest : public ::testing::Test {
  31. public:
  32. /// @brief Constructor.
  33. ///
  34. /// Creates fake interface configuration. It also creates an instance
  35. /// of the @c DUIDFactory object used throughout the tests.
  36. DUIDFactoryTest();
  37. /// @brief Destructor.
  38. virtual ~DUIDFactoryTest();
  39. /// @brief Returns absolute path to a test DUID storage.
  40. ///
  41. /// @param duid_file_name Name of the file holding test DUID.
  42. std::string absolutePath(const std::string& duid_file_name) const;
  43. /// @brief Removes default DUID file used in the tests.
  44. ///
  45. /// This method is called from both constructor and destructor.
  46. void removeDefaultFile() const;
  47. /// @brief Returns contents of the DUID file.
  48. std::string readDefaultFile() const;
  49. /// @brief Converts string of hexadecimal digits to vector.
  50. ///
  51. /// @param hex String representation.
  52. /// @return Vector created from the converted string.
  53. std::vector<uint8_t> toVector(const std::string& hex) const;
  54. /// @brief Converts vector to string of hexadecimal digits.
  55. ///
  56. /// @param vec Input vector.
  57. /// @return String of hexadecimal digits converted from vector.
  58. std::string toString(const std::vector<uint8_t>& vec) const;
  59. /// @brief Converts current time to a string of hexadecimal digits.
  60. ///
  61. /// @return Time represented as text.
  62. std::string timeAsHexString() const;
  63. /// @brief Tests creation of a DUID-LLT.
  64. ///
  65. /// @param expected_htype Expected link layer type as string.
  66. /// @param expected_time Expected time as string.
  67. /// @param time_equal Indicates if @c expected time should be
  68. /// compared for equality with the time being part of a DUID
  69. /// (if true), or the time being part of the DUID should be
  70. /// less or equal current time (if false).
  71. /// @param expected_hwaddr Expected link layer type as string.
  72. void testLLT(const std::string& expected_htype,
  73. const std::string& expected_time,
  74. const bool time_equal,
  75. const std::string& expected_hwaddr);
  76. /// @brief Tests creation of a DUID-LLT.
  77. ///
  78. /// @param expected_htype Expected link layer type as string.
  79. /// @param expected_time Expected time as string.
  80. /// @param time_equal Indicates if @c expected time should be
  81. /// compared for equality with the time being part of a DUID
  82. /// (if true), or the time being part of the DUID should be
  83. /// less or equal current time (if false).
  84. /// @param expected_hwaddr Expected link layer type as string.
  85. /// @param factory_ref Reference to DUID factory.
  86. void testLLT(const std::string& expected_htype,
  87. const std::string& expected_time,
  88. const bool time_equal,
  89. const std::string& expected_hwaddr,
  90. DUIDFactory& factory_ref);
  91. /// @brief Tests creation of a DUID-EN.
  92. ///
  93. /// @param expected_enterprise_id Expected enterprise id as string.
  94. /// @param expected_identifier Expected variable length identifier
  95. /// as string. If empty string specified the test method only checks
  96. /// that generated identifier consists of some random values.
  97. void testEN(const std::string& expected_enterprise_id,
  98. const std::string& expected_identifier = "");
  99. /// @brief Tests creation of a DUID-EN.
  100. ///
  101. /// @param expected_enterprise_id Expected enterprise id as string.
  102. /// @param expected_identifier Expected variable length identifier
  103. /// as string. If empty string specified the test method only checks
  104. /// that generated identifier consists of some random values.
  105. /// @param factory_ref Reference to DUID factory.
  106. void testEN(const std::string& expected_enterprise_id,
  107. const std::string& expected_identifier,
  108. DUIDFactory& factory_ref);
  109. /// @brief Tests creation of a DUID-LL.
  110. ///
  111. /// @param expected_htype Expected link layer type as string.
  112. /// @param expected_hwaddr Expected link layer type as string.
  113. void testLL(const std::string& expected_htype,
  114. const std::string& expected_hwaddr);
  115. /// @brief Tests creation of a DUID-LL.
  116. ///
  117. /// @param expected_htype Expected link layer type as string.
  118. /// @param expected_hwaddr Expected link layer type as string.
  119. /// @param factory_ref Reference to DUID factory.
  120. void testLL(const std::string& expected_htype,
  121. const std::string& expected_hwaddr,
  122. DUIDFactory& factory_ref);
  123. /// @brief Returns reference to a default factory.
  124. DUIDFactory& factory() {
  125. return (factory_);
  126. }
  127. private:
  128. /// @brief Creates fake interface configuration.
  129. IfaceMgrTestConfig iface_mgr_test_config_;
  130. /// @brief Holds default instance of the @c DUIDFactory class, being
  131. /// used throughout the tests.
  132. DUIDFactory factory_;
  133. };
  134. DUIDFactoryTest::DUIDFactoryTest()
  135. : iface_mgr_test_config_(true),
  136. factory_(absolutePath(DEFAULT_DUID_FILE)) {
  137. removeDefaultFile();
  138. }
  139. DUIDFactoryTest::~DUIDFactoryTest() {
  140. removeDefaultFile();
  141. }
  142. std::string
  143. DUIDFactoryTest::absolutePath(const std::string& duid_file_name) const {
  144. std::ostringstream s;
  145. s << TEST_DATA_BUILDDIR << "/" << duid_file_name;
  146. return (s.str());
  147. }
  148. void
  149. DUIDFactoryTest::removeDefaultFile() const {
  150. static_cast<void>(remove(absolutePath(DEFAULT_DUID_FILE).c_str()));
  151. }
  152. std::string
  153. DUIDFactoryTest::readDefaultFile() const {
  154. return (isc::test::readFile(absolutePath(DEFAULT_DUID_FILE)));
  155. }
  156. std::vector<uint8_t>
  157. DUIDFactoryTest::toVector(const std::string& hex) const {
  158. std::vector<uint8_t> vec;
  159. try {
  160. util::encode::decodeHex(hex, vec);
  161. } catch (...) {
  162. ADD_FAILURE() << "toVector: the following string " << hex
  163. << " is not a valid hex string";
  164. }
  165. return (vec);
  166. }
  167. std::string
  168. DUIDFactoryTest::toString(const std::vector<uint8_t>& vec) const {
  169. try {
  170. return (util::encode::encodeHex(vec));
  171. } catch (...) {
  172. ADD_FAILURE() << "toString: unable to encode vector to"
  173. " hexadecimal string";
  174. }
  175. return ("");
  176. }
  177. std::string
  178. DUIDFactoryTest::timeAsHexString() const {
  179. time_t current_time = time(NULL) - DUID_TIME_EPOCH;
  180. std::ostringstream s;
  181. s << std::hex << std::setw(8) << std::setfill('0') << current_time;
  182. return (boost::to_upper_copy<std::string>(s.str()));
  183. }
  184. void
  185. DUIDFactoryTest::testLLT(const std::string& expected_htype,
  186. const std::string& expected_time,
  187. const bool time_equal,
  188. const std::string& expected_hwaddr) {
  189. testLLT(expected_htype, expected_time, time_equal, expected_hwaddr,
  190. factory());
  191. }
  192. void
  193. DUIDFactoryTest::testLLT(const std::string& expected_htype,
  194. const std::string& expected_time,
  195. const bool time_equal,
  196. const std::string& expected_hwaddr,
  197. DUIDFactory& factory_ref) {
  198. DuidPtr duid = factory_ref.get();
  199. ASSERT_TRUE(duid);
  200. ASSERT_GE(duid->getDuid().size(), 14);
  201. std::string duid_text = toString(duid->getDuid());
  202. // DUID type LLT
  203. EXPECT_EQ("0001", duid_text.substr(0, 4));
  204. // Link layer type HTYPE_ETHER
  205. EXPECT_EQ(expected_htype, duid_text.substr(4, 4));
  206. // Verify if time is correct.
  207. if (time_equal) {
  208. // Strict time check.
  209. EXPECT_EQ(expected_time, duid_text.substr(8, 8));
  210. } else {
  211. // Timestamp equal or less current time.
  212. EXPECT_LE(duid_text.substr(8, 8), expected_time);
  213. }
  214. // MAC address of the interface.
  215. EXPECT_EQ(expected_hwaddr, duid_text.substr(16));
  216. // Compare DUID with the one stored in the file.
  217. EXPECT_EQ(duid->toText(), readDefaultFile());
  218. }
  219. void
  220. DUIDFactoryTest::testEN(const std::string& expected_enterprise_id,
  221. const std::string& expected_identifier) {
  222. testEN(expected_enterprise_id, expected_identifier, factory());
  223. }
  224. void
  225. DUIDFactoryTest::testEN(const std::string& expected_enterprise_id,
  226. const std::string& expected_identifier,
  227. DUIDFactory& factory_ref) {
  228. DuidPtr duid = factory_ref.get();
  229. ASSERT_TRUE(duid);
  230. ASSERT_GE(duid->getDuid().size(), 8);
  231. std::string duid_text = toString(duid->getDuid());
  232. // DUID type EN.
  233. EXPECT_EQ("0002", duid_text.substr(0, 4));
  234. // Verify enterprise ID.
  235. EXPECT_EQ(expected_enterprise_id, duid_text.substr(4, 8));
  236. // If no expected identifier, we should at least check that the
  237. // generated identifier contains some random non-zero digits.
  238. if (expected_identifier.empty()) {
  239. EXPECT_FALSE(isRangeZero(duid->getDuid().begin(),
  240. duid->getDuid().end()));
  241. } else {
  242. // Check if identifier matches.
  243. EXPECT_EQ(expected_identifier, duid_text.substr(12));
  244. }
  245. // Compare DUID with the one stored in the file.
  246. EXPECT_EQ(duid->toText(), readDefaultFile());
  247. }
  248. void
  249. DUIDFactoryTest::testLL(const std::string& expected_htype,
  250. const std::string& expected_hwaddr) {
  251. testLL(expected_htype, expected_hwaddr, factory());
  252. }
  253. void
  254. DUIDFactoryTest::testLL(const std::string& expected_htype,
  255. const std::string& expected_hwaddr,
  256. DUIDFactory& factory_ref) {
  257. DuidPtr duid = factory_ref.get();
  258. ASSERT_TRUE(duid);
  259. ASSERT_GE(duid->getDuid().size(), 8);
  260. std::string duid_text = toString(duid->getDuid());
  261. // DUID type LL
  262. EXPECT_EQ("0003", duid_text.substr(0, 4));
  263. // Link layer type.
  264. EXPECT_EQ(expected_htype, duid_text.substr(4, 4));
  265. // MAC address of the interface.
  266. EXPECT_EQ(expected_hwaddr, duid_text.substr(8));
  267. // Compare DUID with the one stored in the file.
  268. EXPECT_EQ(duid->toText(), readDefaultFile());
  269. }
  270. // This test verifies that the factory class will generate the entire
  271. // DUID-LLT if there are no explicit values specified for the
  272. // time, link layer type and link layer address.
  273. TEST_F(DUIDFactoryTest, createLLT) {
  274. // Use 0 values for time and link layer type and empty vector for
  275. // the link layer address. The createLLT function will need to
  276. // use current time, HTYPE_ETHER and MAC address of one of the
  277. // interfaces.
  278. ASSERT_NO_THROW(factory().createLLT(0, 0, std::vector<uint8_t>()));
  279. testLLT("0001", timeAsHexString(), false, "080808080808");
  280. }
  281. // This test verifies that the factory class creates a DUID-LLT from
  282. // the explicitly specified time, when link layer type and address are
  283. // generated.
  284. TEST_F(DUIDFactoryTest, createLLTExplicitTime) {
  285. ASSERT_NO_THROW(factory().createLLT(0, 0xABCDEF, std::vector<uint8_t>()));
  286. testLLT("0001", "00ABCDEF", true, "080808080808");
  287. }
  288. // This test verifies that the factory class creates DUID-LLT with
  289. // the link layer type of the interface which link layer address
  290. // is used to generate the DUID.
  291. TEST_F(DUIDFactoryTest, createLLTExplicitHtype) {
  292. ASSERT_NO_THROW(factory().createLLT(HTYPE_FDDI, 0, std::vector<uint8_t>()));
  293. testLLT("0001", timeAsHexString(), false, "080808080808");
  294. }
  295. // This test verifies that the factory class creates DUID-LLT from
  296. // explicitly specified link layer address, when other parameters
  297. // are generated.
  298. TEST_F(DUIDFactoryTest, createLLTExplicitLinkLayerAddress) {
  299. ASSERT_NO_THROW(factory().createLLT(0, 0, toVector("121212121212")));
  300. testLLT("0001", timeAsHexString(), false, "121212121212");
  301. }
  302. // This test verifies that the factory function creates DUID-LLT from
  303. // all values explicitly specified.
  304. TEST_F(DUIDFactoryTest, createLLTAllExplcitParameters) {
  305. ASSERT_NO_THROW(factory().createLLT(HTYPE_FDDI, 0xFAFAFAFA,
  306. toVector("24242424242424242424")));
  307. testLLT("0008", "FAFAFAFA", true, "24242424242424242424");
  308. }
  309. // This test verifies that the createLLT function will try to reuse existing
  310. // DUID for the non-explicitly specified values.
  311. TEST_F(DUIDFactoryTest, createLLTReuse) {
  312. // Create DUID-LLT and store it in a file.
  313. ASSERT_NO_THROW(factory().createLLT(HTYPE_FDDI, 0xFAFAFAFA,
  314. toVector("242424242424")));
  315. // Create another factory class, which uses the same file.
  316. DUIDFactory factory2(absolutePath(DEFAULT_DUID_FILE));
  317. // Create DUID-LLT without specifying hardware type, time and
  318. // link layer address. The factory function should use the
  319. // values in the existing DUID.
  320. ASSERT_NO_THROW(factory2.createLLT(0, 0, std::vector<uint8_t>()));
  321. testLLT("0008", "FAFAFAFA", true, "242424242424", factory2);
  322. // Try to reuse only a time value.
  323. DUIDFactory factory3(absolutePath(DEFAULT_DUID_FILE));
  324. ASSERT_NO_THROW(factory3.createLLT(HTYPE_ETHER, 0,
  325. toVector("121212121212")));
  326. testLLT("0001", "FAFAFAFA", true, "121212121212", factory3);
  327. // Reuse only a hardware type.
  328. DUIDFactory factory4(absolutePath(DEFAULT_DUID_FILE));
  329. ASSERT_NO_THROW(factory4.createLLT(0, 0x23432343,
  330. toVector("455445544554")));
  331. testLLT("0001", "23432343", true, "455445544554", factory4);
  332. // Reuse link layer address. Note that in this case the hardware
  333. // type is set to the type of the interface from which hardware
  334. // address is obtained and the explicit value is ignored.
  335. DUIDFactory factory5(absolutePath(DEFAULT_DUID_FILE));
  336. ASSERT_NO_THROW(factory5.createLLT(HTYPE_FDDI, 0x11111111,
  337. std::vector<uint8_t>()));
  338. testLLT("0001", "11111111", true, "455445544554", factory5);
  339. }
  340. // This test verifies that the DUID-EN can be generated entirely. Such
  341. // generated DUID contains ISC enterprise id and the random identifier.
  342. TEST_F(DUIDFactoryTest, createEN) {
  343. ASSERT_NO_THROW(factory().createEN(0, std::vector<uint8_t>()));
  344. testEN("000009BF");
  345. }
  346. // This test verifies that the DUID-EN may contain custom enterprise id.
  347. TEST_F(DUIDFactoryTest, createENExplicitEnterpriseId) {
  348. ASSERT_NO_THROW(factory().createEN(0xABCDEFAB, std::vector<uint8_t>()));
  349. testEN("ABCDEFAB");
  350. }
  351. // This test verifies that DUID-EN may contain custom variable length
  352. // identifier and default enterprise id.
  353. TEST_F(DUIDFactoryTest, createENExplicitIdentifier) {
  354. ASSERT_NO_THROW(factory().createEN(0, toVector("1212121212121212")));
  355. testEN("000009BF", "1212121212121212");
  356. }
  357. // This test verifies that DUID-EN can be created from explicit enterprise id
  358. // and identifier.
  359. TEST_F(DUIDFactoryTest, createENAllExplicitParameters) {
  360. ASSERT_NO_THROW(factory().createEN(0x01020304, toVector("ABCD")));
  361. testEN("01020304", "ABCD");
  362. }
  363. // This test verifies that the createEN function will try to reuse existing
  364. // DUID for the non-explicitly specified values.
  365. TEST_F(DUIDFactoryTest, createENReuse) {
  366. // Create DUID-EN and store it in a file.
  367. ASSERT_NO_THROW(factory().createEN(0xFAFAFAFA, toVector("242424242424")));
  368. // Create another factory class, which uses the same file.
  369. DUIDFactory factory2(absolutePath(DEFAULT_DUID_FILE));
  370. ASSERT_NO_THROW(factory2.createEN(0, std::vector<uint8_t>()));
  371. testEN("FAFAFAFA", "242424242424", factory2);
  372. // Reuse only enterprise id.
  373. DUIDFactory factory3(absolutePath(DEFAULT_DUID_FILE));
  374. ASSERT_NO_THROW(factory3.createEN(0, toVector("121212121212")));
  375. testEN("FAFAFAFA", "121212121212", factory3);
  376. // Reuse only variable length identifier.
  377. DUIDFactory factory4(absolutePath(DEFAULT_DUID_FILE));
  378. ASSERT_NO_THROW(factory4.createEN(0x1234, std::vector<uint8_t>()));
  379. testEN("00001234", "121212121212", factory4);
  380. }
  381. // This test verifies that the DUID-LL is generated when neither link layer
  382. // type nor address is specified.
  383. TEST_F(DUIDFactoryTest, createLL) {
  384. ASSERT_NO_THROW(factory().createLL(0, std::vector<uint8_t>()));
  385. testLL("0001", "080808080808");
  386. }
  387. // This test verifies that the DUID-LL is generated and the link layer type
  388. // used is taken from the interface used to generate link layer address.
  389. TEST_F(DUIDFactoryTest, createLLExplicitHtype) {
  390. ASSERT_NO_THROW(factory().createLL(HTYPE_FDDI, std::vector<uint8_t>()));
  391. testLL("0001", "080808080808");
  392. }
  393. // This test verifies that DUID-LL is created from explicitly provided
  394. // link layer type and address.
  395. TEST_F(DUIDFactoryTest, createLLAllExplicitParameters) {
  396. ASSERT_NO_THROW(factory().createLL(HTYPE_FDDI, toVector("242424242424")));
  397. testLL("0008", "242424242424");
  398. }
  399. // This test verifies that DUID-LLT is created when caller wants to obtain
  400. // it and it doesn't exist.
  401. TEST_F(DUIDFactoryTest, createLLTIfNotExists) {
  402. DuidPtr duid;
  403. ASSERT_NO_THROW(duid = factory().get());
  404. ASSERT_TRUE(duid);
  405. EXPECT_EQ(DUID::DUID_LLT, duid->getType());
  406. }
  407. // This test verifies that DUID-EN when there is no suitable interface to
  408. // use to create DUID-LLT.
  409. TEST_F(DUIDFactoryTest, createENIfNotExists) {
  410. // Remove interfaces. The DUID-LLT is a default type but it requires
  411. // that an interface with a suitable link-layer address is present
  412. // in the system. By removing the interfaces we cause the factory
  413. // to fail to generate DUID-LLT. It should fall back to DUID-EN.
  414. IfaceMgr::instance().clearIfaces();
  415. DuidPtr duid;
  416. ASSERT_NO_THROW(duid = factory().get());
  417. ASSERT_TRUE(duid);
  418. EXPECT_EQ(DUID::DUID_EN, duid->getType());
  419. }
  420. // This test verifies that the createLL function will try to reuse existing
  421. // DUID for the non-explicitly specified values.
  422. TEST_F(DUIDFactoryTest, createLLReuse) {
  423. // Create DUID-EN and store it in a file.
  424. ASSERT_NO_THROW(factory().createLL(HTYPE_FDDI, toVector("242424242424")));
  425. // Create another factory class, which uses the same file.
  426. DUIDFactory factory2(absolutePath(DEFAULT_DUID_FILE));
  427. // Create DUID-LL without specifying hardware type, time and
  428. // link layer address. The factory function should use the
  429. // values in the existing DUID.
  430. ASSERT_NO_THROW(factory2.createLL(0, std::vector<uint8_t>()));
  431. testLL("0008", "242424242424", factory2);
  432. // Reuse only hardware type
  433. DUIDFactory factory3(absolutePath(DEFAULT_DUID_FILE));
  434. ASSERT_NO_THROW(factory3.createLL(0, toVector("121212121212")));
  435. testLL("0008", "121212121212", factory3);
  436. // Reuse link layer address. Note that when the link layer address is
  437. // reused, the explicit value of hardware type is reused too and the
  438. // explicit value of hardware type is ignored.
  439. DUIDFactory factory4(absolutePath(DEFAULT_DUID_FILE));
  440. ASSERT_NO_THROW(factory4.createLL(HTYPE_ETHER, std::vector<uint8_t>()));
  441. testLL("0008", "121212121212", factory4);
  442. }
  443. // This test verifies that it is possible to override a DUID.
  444. TEST_F(DUIDFactoryTest, override) {
  445. // Create default DUID-LLT.
  446. ASSERT_NO_THROW(static_cast<void>(factory().get()));
  447. testLLT("0001", timeAsHexString(), false, "080808080808");
  448. ASSERT_NO_THROW(factory().createEN(0, toVector("12131415")));
  449. testEN("000009BF", "12131415");
  450. }
  451. } // End anonymous namespace