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
|
// Copyright (C) 2015-2017 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/.
#ifndef LIBDHCPSRV_ALLOC_ENGINE_UTILS_H
#define LIBDHCPSRV_ALLOC_ENGINE_UTILS_H
#include <dhcpsrv/lease_mgr.h>
#include <dhcpsrv/lease_mgr_factory.h>
#include <dhcpsrv/alloc_engine.h>
#include <dhcpsrv/cfgmgr.h>
#include <asiolink/io_address.h>
#include <gtest/gtest.h>
#include <vector>
namespace isc {
namespace dhcp {
namespace test {
/// @file alloc_engine_utils.h
///
/// @brief This is a header file for all Allocation Engine tests.
///
/// There used to be one, huge (over 3kloc) alloc_engine_unittest.cc. It is now
/// split into serveral smaller files:
/// alloc_engine_utils.h - contains test class definitions (this file)
/// alloc_engine_utils.cc - contains test class implementation
/// alloc_engine4_unittest.cc - all unit-tests dedicated to IPv4
/// alloc_engine6_unittest.cc - all unit-tests dedicated to IPv6
/// alloc_engine_hooks_unittest.cc - all unit-tests dedicated to hooks
/// @brief Test that statistic manager holds a given value.
///
/// This function may be used in many allocation tests and there's no
/// single base class for it. @todo consider moving it src/lib/util.
///
/// @param stat_name Statistic name.
/// @param exp_value Expected value.
/// @param subnet_id subnet_id of the desired subnet, if not zero
///
/// @return true if the statistic manager holds a particular value,
/// false otherwise.
bool testStatistics(const std::string& stat_name, const int64_t exp_value,
const SubnetID subnet_id = 0);
/// @brief Allocation engine with some internal methods exposed
class NakedAllocEngine : public AllocEngine {
public:
/// @brief the sole constructor
/// @param engine_type specifies engine type (e.g. iterative)
/// @param attempts number of lease selection attempts before giving up
/// @param ipv6 specifies if the engine is IPv6 or IPv4
NakedAllocEngine(AllocEngine::AllocType engine_type,
unsigned int attempts, bool ipv6 = true)
:AllocEngine(engine_type, attempts, ipv6) {
}
// Expose internal classes for testing purposes
using AllocEngine::Allocator;
using AllocEngine::IterativeAllocator;
using AllocEngine::getAllocator;
/// @brief IterativeAllocator with internal methods exposed
class NakedIterativeAllocator: public AllocEngine::IterativeAllocator {
public:
/// @brief constructor
/// @param type pool types that will be iterated through
NakedIterativeAllocator(Lease::Type type)
:IterativeAllocator(type) {
}
using AllocEngine::IterativeAllocator::increasePrefix;
};
};
/// @brief Used in Allocation Engine tests for IPv6
class AllocEngine6Test : public ::testing::Test {
public:
/// @brief Specified expected result of a given operation
enum ExpectedResult {
SHOULD_PASS,
SHOULD_FAIL
};
/// @brief Default constructor
///
/// Sets duid_, iaid_, subnet_, pool_ fields to example values used
/// in many tests, initializes cfg_mgr configuration and creates
/// lease database.
AllocEngine6Test();
/// @brief Configures a subnet and adds one pool to it.
///
/// This function removes existing v6 subnets before configuring
/// a new one.
///
/// @param subnet Address of a subnet to be configured.
/// @param pool_start First address in the address pool.
/// @param pool_end Last address in the address pool.
/// @param pd_pool_prefix Prefix for the prefix delegation pool. It
/// defaults to 0 which means that PD pool is not specified.
/// @param pd_pool_length Length of the PD pool prefix.
/// @param pd_delegated_length Delegated prefix length.
void initSubnet(const asiolink::IOAddress& subnet,
const asiolink::IOAddress& pool_start,
const asiolink::IOAddress& pool_end,
const asiolink::IOAddress& pd_pool_prefix =
asiolink::IOAddress::IPV6_ZERO_ADDRESS(),
const uint8_t pd_pool_length = 0,
const uint8_t pd_delegated_length = 0);
/// @brief Initializes FQDN data for a test.
///
/// The initialized values are used by the test fixture class members to
/// verify the correctness of a lease.
///
/// @param hostname Hostname to be assigned to a lease.
/// @param fqdn_fwd Indicates whether or not to perform forward DNS update
/// for a lease.
/// @param fqdn_fwd Indicates whether or not to perform reverse DNS update
/// for a lease.
void initFqdn(const std::string& hostname, const bool fqdn_fwd,
const bool fqdn_rev) {
hostname_ = hostname;
fqdn_fwd_ = fqdn_fwd;
fqdn_rev_ = fqdn_rev;
}
/// @brief Wrapper around call to AllocEngine6::findRervation
///
/// If a reservation is found by the engine, the function sets
/// ctx.hostname_ accordingly.
///
/// @param engine allocation engine to use
/// @param ctx client context to pass into engine's findReservation method
void findReservation(AllocEngine& engine, AllocEngine::ClientContext6& ctx);
/// @brief attempts to convert leases collection to a single lease
///
/// This operation makes sense if there is at most one lease in the
/// collection. Otherwise it will throw.
///
/// @param col collection of leases (zero or one leases allowed)
/// @throw MultipleRecords if there is more than one lease
/// @return Lease6 pointer (or NULL if collection was empty)
Lease6Ptr expectOneLease(const Lease6Collection& col) {
if (col.size() > 1) {
isc_throw(MultipleRecords, "More than one lease found in collection");
}
if (col.empty()) {
return (Lease6Ptr());
}
return (*col.begin());
}
/// @brief checks if Lease6 matches expected configuration
///
/// @param lease lease to be checked
/// @param exp_type expected lease type
/// @param exp_pd_len expected prefix length
/// @param expected_in_subnet whether the lease is expected to be in subnet
/// @param expected_in_pool whether the lease is expected to be in dynamic
void checkLease6(const Lease6Ptr& lease, Lease::Type exp_type,
uint8_t exp_pd_len = 128, bool expected_in_subnet = true,
bool expected_in_pool = true) {
// that is belongs to the right subnet
EXPECT_EQ(lease->subnet_id_, subnet_->getID());
if (expected_in_subnet) {
EXPECT_TRUE(subnet_->inRange(lease->addr_));
} else {
EXPECT_FALSE(subnet_->inRange(lease->addr_));
}
if (expected_in_pool) {
EXPECT_TRUE(subnet_->inPool(exp_type, lease->addr_));
} else {
EXPECT_FALSE(subnet_->inPool(exp_type, lease->addr_));
}
// that it have proper parameters
EXPECT_EQ(exp_type, lease->type_);
EXPECT_EQ(iaid_, lease->iaid_);
EXPECT_EQ(subnet_->getValid(), lease->valid_lft_);
EXPECT_EQ(subnet_->getPreferred(), lease->preferred_lft_);
EXPECT_EQ(subnet_->getT1(), lease->t1_);
EXPECT_EQ(subnet_->getT2(), lease->t2_);
EXPECT_EQ(exp_pd_len, lease->prefixlen_);
EXPECT_EQ(fqdn_fwd_, lease->fqdn_fwd_);
EXPECT_EQ(fqdn_rev_, lease->fqdn_rev_);
EXPECT_EQ(hostname_, lease->hostname_);
EXPECT_TRUE(*lease->duid_ == *duid_);
/// @todo: check cltt
}
/// @brief Checks if specified address or prefix has been recorded as
/// allocated to the client.
///
/// @param lease Allocated lease.
/// @param ctx Context structure in which this function should check if
/// leased address is stored as allocated resource.
void checkAllocatedResources(const Lease6Ptr& lease,
AllocEngine::ClientContext6& ctx) {
EXPECT_TRUE(ctx.isAllocated(lease->addr_, lease->prefixlen_));
}
/// @brief Checks if specified address is increased properly
///
/// Method uses gtest macros to mark check failure. This is a proxy
/// method, since increaseAddress was moved to IOAddress class.
///
/// @param alloc IterativeAllocator that is tested
/// @param input address to be increased
/// @param exp_output expected address after increase
void
checkAddrIncrease(NakedAllocEngine::NakedIterativeAllocator&,
std::string input, std::string exp_output) {
EXPECT_EQ(exp_output, asiolink::IOAddress::increase(
asiolink::IOAddress(input)).toText());
}
/// @brief Checks if increasePrefix() works as expected
///
/// Method uses gtest macros to mark check failure.
///
/// @param alloc allocator to be tested
/// @param input IPv6 prefix (as a string)
/// @param prefix_len prefix len
/// @param exp_output expected output (string)
void
checkPrefixIncrease(NakedAllocEngine::NakedIterativeAllocator& alloc,
std::string input, uint8_t prefix_len,
std::string exp_output) {
EXPECT_EQ(exp_output, alloc.increasePrefix(asiolink::IOAddress(input),
prefix_len).toText());
}
/// @brief Checks if the simple allocation can succeed
///
/// The type of lease is determined by pool type (pool->getType()
///
/// @param pool pool from which the lease will be allocated from
/// @param hint address to be used as a hint
/// @param fake true - this is fake allocation (SOLICIT)
/// @param in_pool specifies whether the lease is expected to be in pool
/// @return allocated lease (or NULL)
Lease6Ptr simpleAlloc6Test(const Pool6Ptr& pool,
const asiolink::IOAddress& hint,
bool fake, bool in_pool = true);
/// @brief Checks if the allocation can succeed.
///
/// The type of lease is determined by pool type (pool->getType()).
/// This test is particularly useful in connection with @ref renewTest.
///
/// @param engine a reference to Allocation Engine
/// @param pool pool from which the lease will be allocated from
/// @param hint address to be used as a hint
/// @param fake true - this is fake allocation (SOLICIT)
/// @param in_pool specifies whether the lease is expected to be in pool
/// @return allocated lease(s) (may be empty)
Lease6Collection allocateTest(AllocEngine& engine, const Pool6Ptr& pool,
const asiolink::IOAddress& hint, bool fake,
bool in_pool = true);
/// @brief Checks if the allocation can be renewed.
///
/// The type of lease is determined by pool type (pool->getType()).
/// This test is particularly useful as a follow up to @ref allocateTest.
///
/// @param engine a reference to Allocation Engine
/// @param pool pool from which the lease will be allocated from
/// @param hints address to be used as a hint
/// @param in_pool specifies whether the lease is expected to be in pool
/// @return allocated lease(s) (may be empty)
Lease6Collection renewTest(AllocEngine& engine, const Pool6Ptr& pool,
AllocEngine::HintContainer& hints,
bool in_pool = true);
/// @brief Checks if the address allocation with a hint that is in range,
/// in pool, but is currently used, can succeed
///
/// Method uses gtest macros to mark check failure.
///
/// @param type lease type
/// @param used_addr address should be preallocated (simulates prior
/// allocation by some other user)
/// @param requested address requested by the client
/// @param expected_pd_len expected PD len (128 for addresses)
void allocWithUsedHintTest(Lease::Type type, asiolink::IOAddress used_addr,
asiolink::IOAddress requested,
uint8_t expected_pd_len);
/// @brief Generic test used for IPv6 lease allocation and reuse
///
/// This test inserts existing_lease (if specified, may be null) into the
/// LeaseMgr, then conducts lease allocation (pretends that client
/// sent either Solicit or Request, depending on fake_allocation).
/// Allocated lease is then returned (using result) for further inspection.
///
/// @param alloc_engine allocation engine
/// @param existing_lease optional lease to be inserted in the database
/// @param addr address to be requested by client
/// @param fake_allocation true = SOLICIT, false = REQUEST
/// @param exp_result expected result
/// @param result [out] allocated lease
void testReuseLease6(const AllocEnginePtr& alloc_engine,
Lease6Ptr& existing_lease,
const std::string& addr,
const bool fake_allocation,
ExpectedResult exp_result,
Lease6Ptr& result);
/// @brief Creates a declined IPv6 lease with specified expiration time
///
/// expired parameter controls probation period. Positive value
/// means that the lease will expire in X seconds. Negative means
/// that the lease expired X seconds ago. 0 means it expires now.
/// Probation period is a parameter that specifies for how long
/// a lease will stay unavailable after decline.
///
/// @param addr address of the lease
/// @param probation_period expressed in seconds
/// @param expired number of seconds when the lease will expire
Lease6Ptr generateDeclinedLease(const std::string& addr,
time_t probation_period,
int32_t expired);
/// @brief checks if bogus hint can be ignored and the allocation succeeds
///
/// This test checks if the allocation with a hing that is out of the blue
/// can succeed. The invalid hint should be ignored completely.
///
/// @param type Lease type
/// @param hint hint (as send by a client)
/// @param expected_pd_len (used in validation)
void allocBogusHint6(Lease::Type type, asiolink::IOAddress hint,
uint8_t expected_pd_len);
/// @brief Utility function that creates a host reservation (duid)
///
/// @param add_to_host_mgr true if the reservation should be added
/// @param type specifies reservation type
/// @param addr specifies reserved address or prefix
/// @param prefix_len prefix length (should be 128 for addresses)
/// @return created Host object.
HostPtr
createHost6(bool add_to_host_mgr, IPv6Resrv::Type type,
const asiolink::IOAddress& addr, uint8_t prefix_len) {
HostPtr host(new Host(&duid_->getDuid()[0], duid_->getDuid().size(),
Host::IDENT_DUID, SubnetID(0), subnet_->getID(),
asiolink::IOAddress("0.0.0.0")));
IPv6Resrv resv(type, addr, prefix_len);
host->addReservation(resv);
if (add_to_host_mgr) {
addHost(host);
}
return (host);
}
/// @brief Add a host reservation to the curent configuration
///
/// Adds the given host reservation to the current configuration by
/// casting it to non-const. We do it this way rather than adding it to
/// staging and then committing as that wipes out the current configuration
/// such as subnets.
///
/// @param host host reservation to add
void
addHost(HostPtr& host) {
SrvConfigPtr cfg = boost::const_pointer_cast<SrvConfig>(CfgMgr::instance().getCurrentCfg());
cfg->getCfgHosts()->add(host);
}
/// @brief Utility function that creates a host reservation (hwaddr)
///
/// @param add_to_host_mgr true if the reservation should be added
/// @param type specifies reservation type
/// @param hwaddr hardware address to be reserved to
/// @param addr specifies reserved address or prefix
/// @param prefix_len prefix length (should be 128 for addresses)
/// @return created Host object.
HostPtr
createHost6HWAddr(bool add_to_host_mgr, IPv6Resrv::Type type,
HWAddrPtr& hwaddr, const asiolink::IOAddress& addr,
uint8_t prefix_len);
virtual ~AllocEngine6Test() {
factory_.destroy();
}
DuidPtr duid_; ///< client-identifier (value used in tests)
HWAddrPtr hwaddr_; ///< client's hardware address
uint32_t iaid_; ///< IA identifier (value used in tests)
Subnet6Ptr subnet_; ///< subnet6 (used in tests)
Pool6Ptr pool_; ///< NA pool belonging to subnet_
Pool6Ptr pd_pool_; ///< PD pool belonging to subnet_
std::string hostname_; ///< Hostname
bool fqdn_fwd_; ///< Perform forward update for a lease.
bool fqdn_rev_; ///< Perform reverse update for a lease.
LeaseMgrFactory factory_; ///< pointer to LeaseMgr factory
};
/// @brief Used in Allocation Engine tests for IPv4
class AllocEngine4Test : public ::testing::Test {
public:
/// @brief Specified expected result of a given operation
enum ExpectedResult {
SHOULD_PASS,
SHOULD_FAIL
};
/// @brief Default constructor
///
/// Sets clientid_, hwaddr_, subnet_, pool_ fields to example values
/// used in many tests, initializes cfg_mgr configuration and creates
/// lease database.
///
/// It also re-initializes the Host Manager.
AllocEngine4Test();
/// @brief checks if Lease4 matches expected configuration
///
/// @param lease lease to be checked
void checkLease4(const Lease4Ptr& lease) {
// Check that is belongs to the right subnet
EXPECT_EQ(lease->subnet_id_, subnet_->getID());
EXPECT_TRUE(subnet_->inRange(lease->addr_));
EXPECT_TRUE(subnet_->inPool(Lease::TYPE_V4, lease->addr_));
// Check that it has proper parameters
EXPECT_EQ(subnet_->getValid(), lease->valid_lft_);
EXPECT_EQ(subnet_->getT1(), lease->t1_);
EXPECT_EQ(subnet_->getT2(), lease->t2_);
if (lease->client_id_ && !clientid_) {
ADD_FAILURE() << "Lease4 has a client-id, while it should have none.";
} else
if (!lease->client_id_ && clientid_) {
ADD_FAILURE() << "Lease4 has no client-id, but it was expected to have one.";
} else
if (lease->client_id_ && clientid_) {
EXPECT_TRUE(*lease->client_id_ == *clientid_);
}
EXPECT_TRUE(*lease->hwaddr_ == *hwaddr_);
/// @todo: check cltt
}
/// @brief Generic test used for IPv4 lease allocation and reuse
///
/// This test inserts existing_lease (if specified, may be null) into the
/// LeaseMgr, then conducts lease allocation (pretends that client
/// sent either Discover or Request, depending on fake_allocation).
/// Allocated lease is then returned (using result) for further inspection.
///
/// @param alloc_engine allocation engine
/// @param existing_lease optional lease to be inserted in the database
/// @param addr address to be requested by client
/// @param fake_allocation true = DISCOVER, false = REQUEST
/// @param exp_result expected result
/// @param result [out] allocated lease
void testReuseLease4(const AllocEnginePtr& alloc_engine,
Lease4Ptr& existing_lease,
const std::string& addr,
const bool fake_allocation,
ExpectedResult exp_result,
Lease4Ptr& result);
/// @brief Creates a declined IPv4 lease with specified expiration time
///
/// expired parameter controls probation period. Positive value
/// means that the lease will expire in X seconds. Negative means
/// that the lease expired X seconds ago. 0 means it expires now.
/// Probation period is a parameter that specifies for how long
/// a lease will stay unavailable after decline.
///
/// @param addr address of the lease
/// @param probation_period expressed in seconds
/// @param expired number of seconds when the lease will expire
Lease4Ptr generateDeclinedLease(const std::string& addr,
time_t probation_period,
int32_t expired);
/// @brief Create a subnet with a specified pool of addresses.
///
/// @param pool_start First address in the pool.
/// @param pool_end Last address in the pool.
void initSubnet(const asiolink::IOAddress& pool_start,
const asiolink::IOAddress& pool_end);
virtual ~AllocEngine4Test() {
factory_.destroy();
}
ClientIdPtr clientid_; ///< Client-identifier (value used in tests)
ClientIdPtr clientid2_; ///< Alternative client-identifier.
HWAddrPtr hwaddr_; ///< Hardware address (value used in tests)
HWAddrPtr hwaddr2_; ///< Alternative hardware address.
Subnet4Ptr subnet_; ///< Subnet4 (used in tests)
Pool4Ptr pool_; ///< Pool belonging to subnet_
LeaseMgrFactory factory_; ///< Pointer to LeaseMgr factory
AllocEngine::ClientContext4 ctx_; ///< Context information passed to various
///< allocation engine functions.
};
}; // namespace test
}; // namespace dhcp
}; // namespace isc
#endif
|