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
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
// Copyright (C) 2012-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/duid.h>
#include <dhcp/dhcp4.h>
#include <dhcp/dhcp6.h>
#include <dhcp/pkt4.h>
#include <dhcp/pkt6.h>
#include <dhcpsrv/host_mgr.h>
#include <dhcpsrv/lease_mgr.h>
#include <dhcpsrv/memfile_lease_mgr.h>
#include <hooks/hooks_manager.h>
#include <hooks/callout_handle.h>
#include <stats/stats_mgr.h>

#include <dhcpsrv/testutils/test_utils.h>
#include <dhcpsrv/tests/alloc_engine_utils.h>

#include <boost/shared_ptr.hpp><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <boost/scoped_ptr.hpp><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.

#include <iostream><--- 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 <algorithm><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <set><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <time.h><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.

using namespace std;
using namespace isc::hooks;
using namespace isc::asiolink;
using namespace isc::stats;
using namespace isc::util;

namespace isc {
namespace dhcp {
namespace test {

bool testStatistics(const std::string& stat_name, const int64_t exp_value,
                    const SubnetID subnet_id, bool fail_missing) {
    try {
        std::string name = (subnet_id == SUBNET_ID_UNUSED ? stat_name :
                            StatsMgr::generateName("subnet", subnet_id, stat_name));
        ObservationPtr observation = StatsMgr::instance().getObservation(name);
        if (observation) {
            if (observation->getInteger().first != exp_value) {
                ADD_FAILURE()
                    << "value of the observed statistics '"
                    << name << "' ("
                    << observation->getInteger().first << ") "
                    << "doesn't match expected value (" << exp_value << ")";
            }
            return (observation->getInteger().first == exp_value);
        } else {
            if (fail_missing) {
                ADD_FAILURE() << "Expected statistic " << name
                              << " not found.";
            } else {
                if (exp_value) {
                    ADD_FAILURE() << "Checking non existent statistic and expected value is not 0. Broken test?";
                }
            }
        }
        if (subnet_id != SUBNET_ID_UNUSED) {
            name = StatsMgr::generateName("subnet", subnet_id, StatsMgr::generateName("pool", 0, stat_name));
            observation = StatsMgr::instance().getObservation(name);
            if (observation) {
                if (observation->getInteger().first != exp_value) {
                    ADD_FAILURE()
                        << "value of the observed statistics '"
                        << name << "' ("
                        << observation->getInteger().first << ") "
                        << "doesn't match expected value (" << exp_value << ")";
                }
                return (observation->getInteger().first == exp_value);
            } else {
                if (fail_missing) {
                    ADD_FAILURE() << "Expected statistic " << name
                                  << " not found.";
                } else {
                    if (exp_value) {
                        ADD_FAILURE() << "Checking non existent statistic and expected value is not 0. Broken test?";
                    }
                }
            }
        }
    } catch (const std::exception& e) {
        ADD_FAILURE() << "Uncaught exception " << e.what();
    } catch (...) {
        ADD_FAILURE() << "Unknown exception";
    }
    return (false);
}

int64_t getStatistics(const std::string& stat_name, const SubnetID subnet_id) {
    try {
        std::string name = (subnet_id == SUBNET_ID_UNUSED ? stat_name :
                            StatsMgr::generateName("subnet", subnet_id, stat_name));
        ObservationPtr observation = StatsMgr::instance().getObservation(name);
        if (observation) {
            return (observation->getInteger().first);
        }
    } catch (const std::exception& e) {
        ADD_FAILURE() << "Uncaught exception " << e.what();
    } catch (...) {
        ADD_FAILURE() << "Unknown exception";
    }
    return (0);
}

void
AllocEngine4Test::testReuseLease4(const AllocEnginePtr& engine,
                                  Lease4Ptr& existing_lease,<--- Parameter 'existing_lease' can be declared as reference to const
                                  const std::string& addr,
                                  const bool fake_allocation,
                                  ExpectedResult exp_result,
                                  Lease4Ptr& result) {
    ASSERT_TRUE(engine);

    if (existing_lease) {
        // If an existing lease was specified, we need to add it to the
        // database. Let's wipe any leases for that address (if any). We
        // ignore any errors (previous lease may not exist)
        (void) LeaseMgrFactory::instance().deleteLease(existing_lease);

        // Let's add it.
        ASSERT_TRUE(LeaseMgrFactory::instance().addLease(existing_lease));
    }

    // A client comes along, asking specifically for a given address
    AllocEngine::ClientContext4 ctx(subnet_, clientid_, hwaddr_,
                                    IOAddress(addr), false, false,
                                    "", fake_allocation);
    if (fake_allocation) {
        ctx.query_.reset(new Pkt4(DHCPDISCOVER, 1234));
    } else {
        ctx.query_.reset(new Pkt4(DHCPREQUEST, 1234));
    }
    result = engine->allocateLease4(ctx);

    switch (exp_result) {
    case SHOULD_PASS:
        ASSERT_TRUE(result);

        checkLease4(result);
        break;

    case SHOULD_FAIL:
        ASSERT_FALSE(result);
        break;
    }
}

Lease4Ptr
AllocEngine4Test::generateDeclinedLease(const std::string& addr,
                                        time_t probation_period,
                                        int32_t expired) {
    // There's an assumption that hardware address is always present for IPv4
    // packet (always non-null). Client-id is optional (may be null).
    HWAddrPtr hwaddr(new HWAddr());
    time_t now = time(NULL);
    Lease4Ptr declined(new Lease4(addr, hwaddr, ClientIdPtr(), 495,
                                  now, subnet_->getID()));
    declined->decline(probation_period);
    declined->cltt_ = now - probation_period + expired;
    return (declined);
}

AllocEngine6Test::AllocEngine6Test() {
    CfgMgr::instance().clear();

    // This lease mgr needs to exist to before configuration commits.
    factory_.create("type=memfile universe=6 persist=false");

    duid_ = DuidPtr(new DUID(std::vector<uint8_t>(8, 0x42)));
    iaid_ = 42;

    // Create fresh instance of the HostMgr, and drop any previous HostMgr state.
    HostMgr::instance().create();

    // Let's use odd hardware type to check if there is no Ethernet
    // hardcoded anywhere.
    const uint8_t mac[] = { 0, 1, 22, 33, 44, 55 };
    hwaddr_ = HWAddrPtr(new HWAddr(mac, sizeof(mac), HTYPE_FDDI));
    // Initialize a subnet and short address pool.
    initSubnet(IOAddress("2001:db8:1::"),
               IOAddress("2001:db8:1::10"),
               IOAddress("2001:db8:1::20"),
               IOAddress("2001:db8:1:2::"),
               64, 80);

    initFqdn("", false, false);

    StatsMgr::instance().resetAll();
}

void
AllocEngine6Test::initSubnet(const asiolink::IOAddress& subnet,
                             const asiolink::IOAddress& pool_start,
                             const asiolink::IOAddress& pool_end,
                             const asiolink::IOAddress& pd_pool_prefix,
                             const uint8_t pd_pool_length,
                             const uint8_t pd_delegated_length) {
    CfgMgr& cfg_mgr = CfgMgr::instance();

    static SubnetID id(1);
    subnet_ = Subnet6::create(subnet, 56, 100, 200, 300, 400, id++);
    pool_ = Pool6Ptr(new Pool6(Lease::TYPE_NA, pool_start, pool_end));

    subnet_->addPool(pool_);

    if (!pd_pool_prefix.isV6Zero()) {
        pd_pool_ = Pool6Ptr(new Pool6(Lease::TYPE_PD, pd_pool_prefix,
                                      pd_pool_length, pd_delegated_length));
    }
    subnet_->addPool(pd_pool_);

    cfg_mgr.getStagingCfg()->getCfgSubnets6()->add(subnet_);
    cfg_mgr.commit();
}

void
AllocEngine6Test::findReservation(AllocEngine& engine,
    AllocEngine::ClientContext6& ctx) {
    engine.findReservation(ctx);
    // Let's check whether there's a hostname specified in the reservation
    if (ctx.currentHost()) {
        std::string hostname = ctx.currentHost()->getHostname();
        // If there is, let's use it
        if (!hostname.empty()) {
            ctx.hostname_ = hostname;
        }
    }
}

HostPtr
AllocEngine6Test::createHost6HWAddr(bool add_to_host_mgr, IPv6Resrv::Type type,
                                    HWAddrPtr& hwaddr, const asiolink::IOAddress& addr,
                                    uint8_t prefix_len) {
    HostPtr host(new Host(&hwaddr->hwaddr_[0], hwaddr->hwaddr_.size(),
                          Host::IDENT_HWADDR, SUBNET_ID_UNUSED, subnet_->getID(),
                          asiolink::IOAddress("0.0.0.0")));
    IPv6Resrv resv(type, addr, prefix_len);
    host->addReservation(resv);

    if (add_to_host_mgr) {
        CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
        CfgMgr::instance().commit();
    }
    return (host);
}

Lease6Collection
AllocEngine6Test::allocateTest(AllocEngine& engine, const Pool6Ptr& pool,
                               const asiolink::IOAddress& hint, bool fake,
                               bool in_pool, uint8_t hint_prefix_length) {
    Lease::Type type = pool->getType();
    uint8_t expected_len = pool->getLength();

    Pkt6Ptr query(new Pkt6(fake ? DHCPV6_SOLICIT : DHCPV6_REQUEST, 1234));

    AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "",
                                    fake, query);
    ctx.currentIA().iaid_ = iaid_;
    ctx.currentIA().type_ = type;
    ctx.currentIA().addHint(hint, hint_prefix_length);

    Lease6Collection leases;

    findReservation(engine, ctx);
    EXPECT_NO_THROW(leases = engine.allocateLeases6(ctx));

    for (auto const& it : leases) {

        // Do all checks on the lease
        checkLease6(duid_, it, type, expected_len, in_pool, in_pool);

        // Check that context has been updated with allocated addresses or
        // prefixes.
        checkAllocatedResources(it, ctx);

        // Check that the lease is indeed in LeaseMgr
        Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(type,
                                                                   it->addr_);
        if (!fake) {
            // This is a real (REQUEST) allocation, the lease must be in the DB
            EXPECT_TRUE(from_mgr) << "Lease " << from_mgr->addr_.toText()
                                  << " returned by allocateLeases6(), "
                                  << "but was not present in LeaseMgr";
            if (!from_mgr) {
                return (leases);
            }

            // Now check that the lease in LeaseMgr has the same parameters
            detailCompareLease(it, from_mgr);
        } else {
            // This is a fake (SOLICIT) allocation, the lease must not be in DB
            EXPECT_FALSE(from_mgr) << "Lease " << from_mgr->addr_.toText()
                                   << " returned by allocateLeases6(), "
                                   << "was present in LeaseMgr (expected to be"
                                   << " not present)";
            if (from_mgr) {
                return (leases);
            }
        }
    }

    return (leases);
}

Lease6Ptr
AllocEngine6Test::simpleAlloc6Test(const Pool6Ptr& pool, const IOAddress& hint,
                                   bool fake, bool in_pool) {
    return (simpleAlloc6Test(pool, duid_, hint, fake, in_pool));
}

Lease6Ptr
AllocEngine6Test::simpleAlloc6Test(const Pool6Ptr& pool, const IOAddress& hint,
                                   uint32_t preferred, uint32_t valid,
                                   uint32_t exp_preferred, uint32_t exp_valid,
                                   ClientClassDefPtr class_def) {
    Lease::Type type = pool->getType();
    uint8_t expected_len = pool->getLength();

    boost::scoped_ptr<AllocEngine> engine;
    EXPECT_NO_THROW(engine.reset(new AllocEngine(100)));
    // We can't use ASSERT macros in non-void methods
    EXPECT_TRUE(engine);
    if (!engine) {
        return (Lease6Ptr());
    }

    Pkt6Ptr query(new Pkt6(DHCPV6_REQUEST, 1234));

    AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false, query);
    ctx.hwaddr_ = hwaddr_;
    ctx.addHostIdentifier(Host::IDENT_HWADDR, hwaddr_->hwaddr_);
    ctx.currentIA().iaid_ = iaid_;
    ctx.currentIA().type_ = type;
    ctx.currentIA().addHint(hint, expected_len, preferred, valid);
    subnet_->setPreferred(Triplet<uint32_t>(200, 300, 400));
    subnet_->setValid(Triplet<uint32_t>(300, 400, 500));

    if (class_def) {
        std::cout << "adding class defintion" << std::endl;
        CfgMgr::instance().getStagingCfg()->getClientClassDictionary()->addClass(class_def);
        ctx.query_->addClass(class_def->getName());
    }

    // Set some non-standard callout status to make sure it doesn't affect the
    // allocation.
    ctx.callout_handle_ = HooksManager::createCalloutHandle();
    ctx.callout_handle_->setStatus(CalloutHandle::NEXT_STEP_SKIP);

    findReservation(*engine, ctx);
    Lease6Ptr lease;
    EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));

    // Check that we got a lease
    EXPECT_TRUE(lease);
    if (!lease) {
        return (Lease6Ptr());
    }

    // Do all checks on the lease
    checkLease6(duid_, lease, type, expected_len, true, true);

    // Check expected preferred and valid lifetimes.
    EXPECT_EQ(exp_preferred, lease->preferred_lft_);
    EXPECT_EQ(exp_valid, lease->valid_lft_);

    return (lease);
}

Lease6Ptr
AllocEngine6Test::simpleAlloc6Test(const Pool6Ptr& pool, const DuidPtr& duid,
                                   const IOAddress& hint, bool fake, bool in_pool) {
    Lease::Type type = pool->getType();
    uint8_t expected_len = pool->getLength();

    boost::scoped_ptr<AllocEngine> engine;
    EXPECT_NO_THROW(engine.reset(new AllocEngine(100)));
    // We can't use ASSERT macros in non-void methods
    EXPECT_TRUE(engine);
    if (!engine) {
        return (Lease6Ptr());
    }

    Pkt6Ptr query(new Pkt6(fake ? DHCPV6_SOLICIT : DHCPV6_REQUEST, 1234));

    AllocEngine::ClientContext6 ctx(subnet_, duid, false, false, "", fake, query);
    ctx.hwaddr_ = hwaddr_;
    ctx.addHostIdentifier(Host::IDENT_HWADDR, hwaddr_->hwaddr_);
    ctx.currentIA().iaid_ = iaid_;
    ctx.currentIA().type_ = type;
    ctx.currentIA().addHint(hint);

    // Set some non-standard callout status to make sure it doesn't affect the
    // allocation.
    ctx.callout_handle_ = HooksManager::createCalloutHandle();
    ctx.callout_handle_->setStatus(CalloutHandle::NEXT_STEP_SKIP);

    findReservation(*engine, ctx);
    Lease6Ptr lease;
    EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));

    // Check that we got a lease
    EXPECT_TRUE(lease);
    if (!lease) {
        return (Lease6Ptr());
    }

    // Do all checks on the lease
    checkLease6(duid, lease, type, expected_len, in_pool, in_pool);

    // Check that the lease is indeed in LeaseMgr
    Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(type, lease->addr_);
    if (!fake) {
        // This is a real (REQUEST) allocation, the lease must be in the DB
        EXPECT_TRUE(from_mgr);
        if (!from_mgr) {
            return (Lease6Ptr());
        }

        // Now check that the lease in LeaseMgr has the same parameters
        detailCompareLease(lease, from_mgr);
    } else {
        // This is a fake (SOLICIT) allocation, the lease must not be in DB
        EXPECT_FALSE(from_mgr);
        if (from_mgr) {
            return (Lease6Ptr());
        }
    }

    return (lease);
}

Lease6Collection
AllocEngine6Test::renewTest(AllocEngine& engine, const Pool6Ptr& pool,
                            AllocEngine::HintContainer& hints,<--- Parameter 'hints' can be declared as reference to const
                            bool in_subnet, bool in_pool) {
    Lease::Type type = pool->getType();
    uint8_t expected_len = pool->getLength();

    Pkt6Ptr query(new Pkt6(DHCPV6_RENEW, 1234));
    AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "",
                                    false, query);
    ctx.currentIA().hints_ = hints;
    ctx.currentIA().iaid_ = iaid_;
    ctx.currentIA().type_ = type;

    findReservation(engine, ctx);
    Lease6Collection leases = engine.renewLeases6(ctx);

    for (auto const& it : leases) {

        // Do all checks on the lease
        checkLease6(duid_, it, type, expected_len, in_subnet, in_pool);

        // Check that context has been updated with allocated addresses or
        // prefixes.
        checkAllocatedResources(it, ctx);

        // Check that the lease is indeed in LeaseMgr
        Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(type,
                                                                   it->addr_);

        // This is a real (REQUEST) allocation, the lease must be in the DB
        EXPECT_TRUE(from_mgr) << "Lease " << from_mgr->addr_.toText()
                              << " returned by allocateLeases6(), "
                              << "but was not present in LeaseMgr";
        if (!from_mgr) {
            return (leases);
        }

        // Now check that the lease in LeaseMgr has the same parameters
        detailCompareLease(it, from_mgr);
    }

    return (leases);
}

void
AllocEngine6Test::allocWithUsedHintTest(Lease::Type type, IOAddress used_addr,
                                        IOAddress requested,
                                        uint8_t expected_pd_len) {
    boost::scoped_ptr<AllocEngine> engine;
    ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
    ASSERT_TRUE(engine);

    // Let's create a lease and put it in the LeaseMgr
    DuidPtr duid2 = boost::shared_ptr<DUID>(new DUID(vector<uint8_t>(8, 0xff)));
    time_t now = time(NULL);
    Lease6Ptr used(new Lease6(type, used_addr,
                              duid2, 1, 2, now, subnet_->getID()));
    ASSERT_TRUE(LeaseMgrFactory::instance().addLease(used));

    // Another client comes in and request an address that is in pool, but
    // unfortunately it is used already. The same address must not be allocated
    // twice.

    Pkt6Ptr query(new Pkt6(DHCPV6_REQUEST, 1234));
    AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false,
                                    query);
    ctx.currentIA().iaid_ = iaid_;
    ctx.currentIA().type_ = type;
    ctx.currentIA().addHint(requested);

    Lease6Ptr lease;
    EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));

    // Check that we got a lease
    ASSERT_TRUE(lease);

    // Allocated address must be different
    EXPECT_NE(used_addr, lease->addr_);

    // We should NOT get what we asked for, because it is used already
    EXPECT_NE(requested, lease->addr_);

    // Do all checks on the lease
    checkLease6(duid_, lease, type, expected_pd_len);

    // Check that the lease is indeed in LeaseMgr
    Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->type_,
                                                               lease->addr_);
    ASSERT_TRUE(from_mgr);

    // Now check that the lease in LeaseMgr has the same parameters
    detailCompareLease(lease, from_mgr);
}

void
AllocEngine6Test::allocBogusHint6(Lease::Type type, asiolink::IOAddress hint,
                                  uint8_t expected_pd_len) {
    boost::scoped_ptr<AllocEngine> engine;
    ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
    ASSERT_TRUE(engine);

    // Client would like to get a 3000::abc lease, which does not belong to any
    // supported lease. Allocation engine should ignore it and carry on
    // with the normal allocation

    Pkt6Ptr query(new Pkt6(DHCPV6_REQUEST, 1234));
    AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false,
                                    query);
    ctx.currentIA().iaid_ = iaid_;
    ctx.currentIA().type_ = type;
    ctx.currentIA().addHint(hint);

    Lease6Ptr lease;
    EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));

    // Check that we got a lease
    ASSERT_TRUE(lease);

    // We should NOT get what we asked for, because it is used already
    EXPECT_NE(hint, lease->addr_);

    // Do all checks on the lease
    checkLease6(duid_, lease, type, expected_pd_len);

    // Check that the lease is indeed in LeaseMgr
    Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->type_,
                                                               lease->addr_);
    ASSERT_TRUE(from_mgr);

    // Now check that the lease in LeaseMgr has the same parameters
    detailCompareLease(lease, from_mgr);
}

void
AllocEngine6Test::testReuseLease6(const AllocEnginePtr& engine,
                                  Lease6Ptr& existing_lease,<--- Parameter 'existing_lease' can be declared as reference to const
                                  const std::string& addr,
                                  const bool fake_allocation,
                                  ExpectedResult exp_result,
                                  Lease6Ptr& result) {
    ASSERT_TRUE(engine);

    if (existing_lease) {
        // If an existing lease was specified, we need to add it to the
        // database. Let's wipe any leases for that address (if any). We
        // ignore any errors (previous lease may not exist)
        (void) LeaseMgrFactory::instance().deleteLease(existing_lease);

        // Let's add it.
        ASSERT_TRUE(LeaseMgrFactory::instance().addLease(existing_lease));
    }

    // A client comes along, asking specifically for a given address

    Pkt6Ptr query(new Pkt6(fake_allocation ? DHCPV6_SOLICIT : DHCPV6_REQUEST, 1234));
    AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "",
                                    fake_allocation, query);
    ctx.currentIA().iaid_ = iaid_;
    ctx.currentIA().addHint(IOAddress(addr));

    Lease6Collection leases;

    leases = engine->allocateLeases6(ctx);

    switch (exp_result) {
    case SHOULD_PASS:
        ASSERT_FALSE(leases.empty());
        ASSERT_EQ(1, leases.size());
        result = leases[0];

        checkLease6(duid_, result, Lease::TYPE_NA, 128);
        break;

    case SHOULD_FAIL:
        ASSERT_TRUE(leases.empty());
        break;
    }
}

Lease6Ptr
AllocEngine6Test::generateDeclinedLease(const std::string& addr,
                                        time_t probation_period,
                                        int32_t expired) {
    Lease6Ptr declined(new Lease6(Lease::TYPE_NA, IOAddress(addr),
                       duid_, iaid_, 100, 100, subnet_->getID()));

    time_t now = time(NULL);
    declined->decline(probation_period);
    declined->cltt_ = now - probation_period + expired;
    return (declined);
}

void
AllocEngine4Test::initSubnet(const asiolink::IOAddress& pool_start,
                             const asiolink::IOAddress& pool_end) {
    CfgMgr& cfg_mgr = CfgMgr::instance();

    static SubnetID id(1);
    subnet_ = Subnet4::create(IOAddress("192.0.2.0"), 24, 1, 2, 3, id++);
    pool_ = Pool4Ptr(new Pool4(pool_start, pool_end));
    subnet_->addPool(pool_);

    cfg_mgr.getStagingCfg()->getCfgSubnets4()->add(subnet_);
}

AllocEngine4Test::AllocEngine4Test() {
    CfgMgr::instance().clear();

    // This lease mgr needs to exist to before configuration commits.
    factory_.create("type=memfile universe=4 persist=false");

    // Create fresh instance of the HostMgr, and drop any previous HostMgr state.
    HostMgr::instance().create();

    clientid_ = ClientIdPtr(new ClientId(vector<uint8_t>(8, 0x44)));
    clientid2_ = ClientIdPtr(new ClientId(vector<uint8_t>(8, 0x56)));

    uint8_t mac[] = { 0, 1, 22, 33, 44, 55};

    // Let's use odd hardware type to check if there is no Ethernet
    // hardcoded anywhere.
    hwaddr_ = HWAddrPtr(new HWAddr(mac, sizeof(mac), HTYPE_FDDI));

    // Allocate different MAC address for the tests that require two
    // different MAC addresses.
    ++mac[sizeof(mac) - 1];
    hwaddr2_ = HWAddrPtr(new HWAddr(mac, sizeof (mac), HTYPE_FDDI));

    // instantiate cfg_mgr
    CfgMgr& cfg_mgr = CfgMgr::instance();

    initSubnet(IOAddress("192.0.2.100"), IOAddress("192.0.2.109"));
    cfg_mgr.commit();


    // Create a default context. Note that remaining parameters must be
    // assigned when needed.
    ctx_.subnet_ = subnet_;
    ctx_.clientid_ = clientid_;
    ctx_.hwaddr_ = hwaddr_;
    ctx_.callout_handle_ = HooksManager::createCalloutHandle();
    ctx_.query_.reset(new Pkt4(DHCPREQUEST, 1234));

    StatsMgr::instance().resetAll();
}

}  // namespace test
}  // namespace dhcp
}  // namespace isc