1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
// Copyright (C) 2015-2024 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include <config.h>
#include <dhcp/dhcp4.h>
#include <dhcp/duid_factory.h>
#include <dhcp/testutils/iface_mgr_test_config.h>
#include <testutils/io_utils.h>
#include <util/encode/encode.h>
#include <util/range_utilities.h>
#include <boost/algorithm/string.hpp><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <gtest/gtest.h><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <ctime><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <fstream><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <iomanip><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <sstream><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <stdio.h><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <string><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <vector><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.

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_name) 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-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.
    /// @param factory_ref Reference to DUID factory.
    void testLLT(const std::string& expected_htype,
                 const std::string& expected_time,
                 const bool time_equal,
                 const std::string& expected_hwaddr,
                 DUIDFactory& factory_ref);

    /// @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. If empty string specified the test method only checks
    /// that generated identifier consists of some random values.
    void testEN(const std::string& expected_enterprise_id,
                const std::string& expected_identifier = "");

    /// @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. If empty string specified the test method only checks
    /// that generated identifier consists of some random values.
    /// @param factory_ref Reference to DUID factory.
    void testEN(const std::string& expected_enterprise_id,
                const std::string& expected_identifier,
                DUIDFactory& factory_ref);

    /// @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 Tests creation of a DUID-LL.
    ///
    /// @param expected_htype Expected link layer type as string.
    /// @param expected_hwaddr Expected link layer type as string.
    /// @param factory_ref Reference to DUID factory.
    void testLL(const std::string& expected_htype,
                const std::string& expected_hwaddr,
                DUIDFactory& factory_ref);

    /// @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_;

};

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_name) const {
    std::ostringstream s;
    s << TEST_DATA_BUILDDIR << "/" << duid_file_name;
    return (s.str());
}

void
DUIDFactoryTest::removeDefaultFile() const {
    static_cast<void>(remove(absolutePath(DEFAULT_DUID_FILE).c_str()));
}

std::string
DUIDFactoryTest::readDefaultFile() const {
    return (isc::test::readFile(absolutePath(DEFAULT_DUID_FILE)));
}

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) {
    testLLT(expected_htype, expected_time, time_equal, expected_hwaddr,
            factory());
}

void
DUIDFactoryTest::testLLT(const std::string& expected_htype,
                         const std::string& expected_time,
                         const bool time_equal,
                         const std::string& expected_hwaddr,
                         DUIDFactory& factory_ref) {
    DuidPtr duid = factory_ref.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));

    // 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) {
    testEN(expected_enterprise_id, expected_identifier, factory());
}

void
DUIDFactoryTest::testEN(const std::string& expected_enterprise_id,
                        const std::string& expected_identifier,
                        DUIDFactory& factory_ref) {
    DuidPtr duid = factory_ref.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 expected 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) {
    testLL(expected_htype, expected_hwaddr, factory());
}

void
DUIDFactoryTest::testLL(const std::string& expected_htype,
                        const std::string& expected_hwaddr,
                        DUIDFactory& factory_ref) {
    DuidPtr duid = factory_ref.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.
    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());
}


// 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) {<--- syntax error
    // 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, "080808080808");
}

// 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, "080808080808");
}

// 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("0001", timeAsHexString(), false, "080808080808");
}

// This test verifies that the factory class creates DUID-LLT from
// explicitly 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, createLLTAllExplicitParameters) {
    ASSERT_NO_THROW(factory().createLLT(HTYPE_FDDI, 0xFAFAFAFA,
                                        toVector("24242424242424242424")));
    testLLT("0008", "FAFAFAFA", true, "24242424242424242424");
}

// This test verifies that the createLLT function will try to reuse existing
// DUID for the non-explicitly specified values.
TEST_F(DUIDFactoryTest, createLLTReuse) {
    // Create DUID-LLT and store it in a file.
    ASSERT_NO_THROW(factory().createLLT(HTYPE_FDDI, 0xFAFAFAFA,
                                        toVector("242424242424")));
    // Create another factory class, which uses the same file.
    DUIDFactory factory2(absolutePath(DEFAULT_DUID_FILE));
    // Create DUID-LLT without specifying hardware type, time and
    // link layer address. The factory function should use the
    // values in the existing DUID.
    ASSERT_NO_THROW(factory2.createLLT(0, 0, std::vector<uint8_t>()));
    testLLT("0008", "FAFAFAFA", true, "242424242424", factory2);

    // Try to reuse only a time value.
    DUIDFactory factory3(absolutePath(DEFAULT_DUID_FILE));
    ASSERT_NO_THROW(factory3.createLLT(HTYPE_ETHER, 0,
                                       toVector("121212121212")));
    testLLT("0001", "FAFAFAFA", true, "121212121212", factory3);

    // Reuse only a hardware type.
    DUIDFactory factory4(absolutePath(DEFAULT_DUID_FILE));
    ASSERT_NO_THROW(factory4.createLLT(0, 0x23432343,
                                       toVector("455445544554")));
    testLLT("0001", "23432343", true, "455445544554", factory4);

    // Reuse link layer address. Note that in this case the hardware
    // type is set to the type of the interface from which hardware
    // address is obtained and the explicit value is ignored.
    DUIDFactory factory5(absolutePath(DEFAULT_DUID_FILE));
    ASSERT_NO_THROW(factory5.createLLT(HTYPE_FDDI, 0x11111111,
                                       std::vector<uint8_t>()));
    testLLT("0001", "11111111", true, "455445544554", factory5);
}

// 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 createEN function will try to reuse existing
// DUID for the non-explicitly specified values.
TEST_F(DUIDFactoryTest, createENReuse) {
    // Create DUID-EN and store it in a file.
    ASSERT_NO_THROW(factory().createEN(0xFAFAFAFA, toVector("242424242424")));
    // Create another factory class, which uses the same file.
    DUIDFactory factory2(absolutePath(DEFAULT_DUID_FILE));
    ASSERT_NO_THROW(factory2.createEN(0, std::vector<uint8_t>()));
    testEN("FAFAFAFA", "242424242424", factory2);

    // Reuse only enterprise id.
    DUIDFactory factory3(absolutePath(DEFAULT_DUID_FILE));
    ASSERT_NO_THROW(factory3.createEN(0, toVector("121212121212")));
    testEN("FAFAFAFA", "121212121212", factory3);

    // Reuse only variable length identifier.
    DUIDFactory factory4(absolutePath(DEFAULT_DUID_FILE));
    ASSERT_NO_THROW(factory4.createEN(0x1234, std::vector<uint8_t>()));
    testEN("00001234", "121212121212", factory4);
}

// 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", "080808080808");
}

// 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", "080808080808");
}

// 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 the createLL function will try to reuse existing
// DUID for the non-explicitly specified values.
TEST_F(DUIDFactoryTest, createLLReuse) {
    // Create DUID-EN and store it in a file.
    ASSERT_NO_THROW(factory().createLL(HTYPE_FDDI, toVector("242424242424")));
    // Create another factory class, which uses the same file.
    DUIDFactory factory2(absolutePath(DEFAULT_DUID_FILE));
    // Create DUID-LL without specifying hardware type, time and
    // link layer address. The factory function should use the
    // values in the existing DUID.
    ASSERT_NO_THROW(factory2.createLL(0, std::vector<uint8_t>()));
    testLL("0008", "242424242424", factory2);

    // Reuse only hardware type
    DUIDFactory factory3(absolutePath(DEFAULT_DUID_FILE));
    ASSERT_NO_THROW(factory3.createLL(0, toVector("121212121212")));
    testLL("0008", "121212121212", factory3);

    // Reuse link layer address. Note that when the link layer address is
    // reused, the explicit value of hardware type is reused too and the
    // explicit value of hardware type is ignored.
    DUIDFactory factory4(absolutePath(DEFAULT_DUID_FILE));
    ASSERT_NO_THROW(factory4.createLL(HTYPE_ETHER, std::vector<uint8_t>()));
    testLL("0008", "121212121212", factory4);
}

// 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, "080808080808");

    ASSERT_NO_THROW(factory().createEN(0, toVector("12131415")));
    testEN("000009BF", "12131415");
}

} // End anonymous namespace