summaryrefslogtreecommitdiffstats
path: root/src/lib/dhcpsrv/tests/pool_unittest.cc
blob: 1ce452fd3bbc46cb108f2c3241b66994ea7c0f70 (plain)
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
// Copyright (C) 2012-2016 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 <asiolink/io_address.h>
#include <dhcp/option6_pdexclude.h>
#include <dhcpsrv/pool.h>

#include <boost/scoped_ptr.hpp>

#include <gtest/gtest.h>

#include <iostream>
#include <vector>
#include <sstream>

using boost::scoped_ptr;
using namespace isc;
using namespace isc::dhcp;
using namespace isc::asiolink;

namespace {

TEST(Pool4Test, constructor_first_last) {

    // let's construct 192.0.2.1-192.0.2.255 pool
    Pool4 pool1(IOAddress("192.0.2.1"), IOAddress("192.0.2.255"));

    EXPECT_EQ(IOAddress("192.0.2.1"), pool1.getFirstAddress());
    EXPECT_EQ(IOAddress("192.0.2.255"), pool1.getLastAddress());

    // This is Pool4, IPv6 addresses do not belong here
    EXPECT_THROW(Pool4(IOAddress("2001:db8::1"),
                       IOAddress("192.168.0.5")), BadValue);
    EXPECT_THROW(Pool4(IOAddress("192.168.0.2"),
                       IOAddress("2001:db8::1")), BadValue);

    // Should throw. Range should be 192.0.2.1-192.0.2.2, not
    // the other way around.
    EXPECT_THROW(Pool4(IOAddress("192.0.2.2"), IOAddress("192.0.2.1")),
                 BadValue);
}

TEST(Pool4Test, constructor_prefix_len) {

    // let's construct 2001:db8:1::/96 pool
    Pool4 pool1(IOAddress("192.0.2.0"), 25);

    EXPECT_EQ("192.0.2.0", pool1.getFirstAddress().toText());
    EXPECT_EQ("192.0.2.127", pool1.getLastAddress().toText());

    // No such thing as /33 prefix
    EXPECT_THROW(Pool4(IOAddress("192.0.2.1"), 33), BadValue);

    // /0 prefix does not make sense
    EXPECT_THROW(Pool4(IOAddress("192.0.2.0"), 0), BadValue);

    // This is Pool6, IPv4 addresses do not belong here
    EXPECT_THROW(Pool4(IOAddress("2001:db8::1"), 20), BadValue);
}

TEST(Pool4Test, in_range) {
   Pool4 pool1(IOAddress("192.0.2.10"), IOAddress("192.0.2.20"));

   EXPECT_FALSE(pool1.inRange(IOAddress("192.0.2.0")));
   EXPECT_TRUE(pool1.inRange(IOAddress("192.0.2.10")));
   EXPECT_TRUE(pool1.inRange(IOAddress("192.0.2.17")));
   EXPECT_TRUE(pool1.inRange(IOAddress("192.0.2.20")));
   EXPECT_FALSE(pool1.inRange(IOAddress("192.0.2.21")));
   EXPECT_FALSE(pool1.inRange(IOAddress("192.0.2.255")));
   EXPECT_FALSE(pool1.inRange(IOAddress("255.255.255.255")));
   EXPECT_FALSE(pool1.inRange(IOAddress("0.0.0.0")));
}

// Checks if the number of possible leases in range is reported correctly.
TEST(Pool4Test, leasesCount) {
    Pool4 pool1(IOAddress("192.0.2.10"), IOAddress("192.0.2.20"));
    EXPECT_EQ(11, pool1.getCapacity());

    Pool4 pool2(IOAddress("192.0.2.0"), IOAddress("192.0.2.255"));
    EXPECT_EQ(256, pool2.getCapacity());

    Pool4 pool3(IOAddress("192.168.0.0"), IOAddress("192.168.255.255"));
    EXPECT_EQ(65536, pool3.getCapacity());

    Pool4 pool4(IOAddress("10.0.0.0"), IOAddress("10.255.255.255"));
    EXPECT_EQ(16777216, pool4.getCapacity());
}

// This test creates 100 pools and verifies that their IDs are unique.
TEST(Pool4Test, unique_id) {

    const int num_pools = 100;
    std::vector<Pool4Ptr> pools;

    for (int i = 0; i < num_pools; ++i) {
        pools.push_back(Pool4Ptr(new Pool4(IOAddress("192.0.2.0"),
                                           IOAddress("192.0.2.255"))));
    }

    for (int i = 0; i < num_pools; ++i) {
        for (int j = i + 1; j < num_pools; ++j) {
            if (pools[i]->getId() == pools[j]->getId()) {
                FAIL() << "Pool-ids must be unique";
            }
        }
    }
}

// Simple check if toText returns reasonable values
TEST(Pool4Test,toText) {
    Pool4 pool1(IOAddress("192.0.2.7"), IOAddress("192.0.2.17"));
    EXPECT_EQ("type=V4, 192.0.2.7-192.0.2.17", pool1.toText());

    Pool4 pool2(IOAddress("192.0.2.128"), 28);
    EXPECT_EQ("type=V4, 192.0.2.128-192.0.2.143", pool2.toText());
}

TEST(Pool6Test, constructor_first_last) {

    // let's construct 2001:db8:1:: - 2001:db8:1::ffff:ffff:ffff:ffff pool
    Pool6 pool1(Lease::TYPE_NA, IOAddress("2001:db8:1::"),
                IOAddress("2001:db8:1::ffff:ffff:ffff:ffff"));

    EXPECT_EQ(Lease::TYPE_NA, pool1.getType());
    EXPECT_EQ(IOAddress("2001:db8:1::"), pool1.getFirstAddress());
    EXPECT_EQ(IOAddress("2001:db8:1::ffff:ffff:ffff:ffff"),
              pool1.getLastAddress());

    // This is Pool6, IPv4 addresses do not belong here
    EXPECT_THROW(Pool6(Lease::TYPE_NA, IOAddress("2001:db8::1"),
                       IOAddress("192.168.0.5")), BadValue);
    EXPECT_THROW(Pool6(Lease::TYPE_NA, IOAddress("192.168.0.2"),
                       IOAddress("2001:db8::1")), BadValue);

    // Should throw. Range should be 2001:db8::1 - 2001:db8::2, not
    // the other way around.
    EXPECT_THROW(Pool6(Lease::TYPE_NA, IOAddress("2001:db8::2"),
                       IOAddress("2001:db8::1")), BadValue);
}

TEST(Pool6Test, constructor_prefix_len) {

    // let's construct 2001:db8:1::/96 pool
    Pool6 pool1(Lease::TYPE_NA, IOAddress("2001:db8:1::"), 96);

    EXPECT_EQ(Lease::TYPE_NA, pool1.getType());
    EXPECT_EQ("2001:db8:1::", pool1.getFirstAddress().toText());
    EXPECT_EQ("2001:db8:1::ffff:ffff", pool1.getLastAddress().toText());

    // No such thing as /130 prefix
    EXPECT_THROW(Pool6(Lease::TYPE_NA, IOAddress("2001:db8::"), 130),
                 BadValue);

    // /0 prefix does not make sense
    EXPECT_THROW(Pool6(Lease::TYPE_NA, IOAddress("2001:db8::"), 0),
                 BadValue);

    // This is Pool6, IPv4 addresses do not belong here
    EXPECT_THROW(Pool6(Lease::TYPE_NA, IOAddress("192.168.0.2"), 96),
                 BadValue);

    // Delegated prefix length for addresses must be /128
    EXPECT_THROW(Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1::"), 96, 125),
                 BadValue);
}

TEST(Pool6Test, in_range) {
   Pool6 pool1(Lease::TYPE_NA, IOAddress("2001:db8:1::1"),
               IOAddress("2001:db8:1::f"));

   EXPECT_FALSE(pool1.inRange(IOAddress("2001:db8:1::")));
   EXPECT_TRUE(pool1.inRange(IOAddress("2001:db8:1::1")));
   EXPECT_TRUE(pool1.inRange(IOAddress("2001:db8:1::7")));
   EXPECT_TRUE(pool1.inRange(IOAddress("2001:db8:1::f")));
   EXPECT_FALSE(pool1.inRange(IOAddress("2001:db8:1::10")));
   EXPECT_FALSE(pool1.inRange(IOAddress("::")));
}

// Checks that Prefix Delegation pools are handled properly
TEST(Pool6Test, PD) {

    // Let's construct 2001:db8:1::/96 PD pool, split into /112 prefixes
    Pool6 pool1(Lease::TYPE_PD, IOAddress("2001:db8:1::"), 96, 112);

    EXPECT_EQ(Lease::TYPE_PD, pool1.getType());
    EXPECT_EQ(112, pool1.getLength());
    EXPECT_EQ("2001:db8:1::", pool1.getFirstAddress().toText());
    EXPECT_EQ("2001:db8:1::ffff:ffff", pool1.getLastAddress().toText());

    // Check that it's not possible to have min-max range for PD
    EXPECT_THROW(Pool6 pool2(Lease::TYPE_PD, IOAddress("2001:db8:1::1"),
                             IOAddress("2001:db8:1::f")), BadValue);

    // Check that it's not allowed to delegate bigger prefix than the pool
    // Let's try to split /64 prefix into /56 chunks (should be impossible)
    EXPECT_THROW(Pool6 pool3(Lease::TYPE_PD, IOAddress("2001:db8:1::"),
                             64, 56), BadValue);

    // It should be possible to have a pool split into just a single chunk
    // Let's try to split 2001:db8:1::/77 into a single /77 delegated prefix
    EXPECT_NO_THROW(Pool6 pool4(Lease::TYPE_PD, IOAddress("2001:db8:1::"),
                                77, 77));
}

// Checks that prefix pools with excluded prefixes are handled properly.
TEST(Pool6Test, PDExclude) {
    Pool6Ptr pool;

    // Create a pool with a good excluded prefix. The good excluded prefix
    // is the one for which is a sub-prefix of the main prefix.
    ASSERT_NO_THROW(pool.reset(new Pool6(IOAddress("2001:db8:1::"), 96, 112,
                                         IOAddress("2001:db8:1::2000"), 120)));

    // Verify pool properties.
    EXPECT_EQ(Lease::TYPE_PD, pool->getType());
    EXPECT_EQ(112, pool->getLength());
    EXPECT_EQ("2001:db8:1::", pool->getFirstAddress().toText());
    EXPECT_EQ("2001:db8:1::ffff:ffff", pool->getLastAddress().toText());

    // It should include Prefix Exclude option.
    Option6PDExcludePtr pd_exclude_option = pool->getPrefixExcludeOption();
    ASSERT_TRUE(pd_exclude_option);
    EXPECT_EQ("2001:db8:1::2:2000", pd_exclude_option->
              getExcludedPrefix(IOAddress("2001:db8:1:0:0:0:2::"), 112).toText());
    EXPECT_EQ(120, static_cast<unsigned>(pd_exclude_option->getExcludedPrefixLength()));

    // Create another pool instance, but with the excluded prefix being
    // "unspecified".
    ASSERT_NO_THROW(pool.reset(new Pool6(IOAddress("2001:db8:1::"), 96, 112,
                                         IOAddress::IPV6_ZERO_ADDRESS(), 0)));

    EXPECT_EQ(Lease::TYPE_PD, pool->getType());
    EXPECT_EQ(112, pool->getLength());
    EXPECT_EQ("2001:db8:1::", pool->getFirstAddress().toText());
    EXPECT_EQ("2001:db8:1::ffff:ffff", pool->getLastAddress().toText());

    ASSERT_FALSE(pool->getPrefixExcludeOption());

    // Excluded prefix length must be greater than the main prefix length.
    EXPECT_THROW(Pool6(IOAddress("2001:db8:1::"), 96, 112,
                       IOAddress("2001:db8:1::"), 112),
                 BadValue);

    // Again, the excluded prefix length must be greater than main prefix
    // length.
    EXPECT_THROW(Pool6(IOAddress("2001:db8:1::"), 96, 112,
                       IOAddress("2001:db8:1::"), 104),
                 BadValue);

    // The "unspecified" excluded prefix must have both values set to 0.
    EXPECT_THROW(Pool6(IOAddress("2001:db8:1::"), 48, 64,
                       IOAddress("2001:db8:1::"), 0),
                 BadValue);

    // Similar case as above, but the prefix value is 0 and the length
    // is non zero.
    EXPECT_THROW(Pool6(IOAddress("2001:db8:1::"), 48, 64,
                       IOAddress::IPV6_ZERO_ADDRESS(), 72),
                 BadValue);

    // Excluded prefix must be an IPv6 prefix.
    EXPECT_THROW(Pool6(IOAddress("10::"), 8, 16,
                       IOAddress("10.0.0.0"), 24),
                 BadValue);

    // Excluded prefix length must not be greater than 128.
    EXPECT_THROW(Pool6(IOAddress("2001:db8:1::"), 48, 64,
                       IOAddress("2001:db8:1::"), 129),
                 BadValue);
}

// Checks that temporary address pools are handled properly
TEST(Pool6Test, TA) {
    // Note: since we defined TA pool types during PD work, we can test it
    // now. Although the configuration to take advantage of it is not
    // planned for now, we will support it some day.

    // Let's construct 2001:db8:1::/96 temporary addresses
    Pool6Ptr pool1;
    EXPECT_NO_THROW(pool1.reset(new Pool6(Lease::TYPE_TA,
                                          IOAddress("2001:db8:1::"), 96)));

    // Check that TA range can be only defined for single addresses
    EXPECT_THROW(Pool6(Lease::TYPE_TA, IOAddress("2001:db8:1::"), 96, 127),
                 BadValue);

    ASSERT_TRUE(pool1);
    EXPECT_EQ(Lease::TYPE_TA, pool1->getType());
    EXPECT_EQ(128, pool1->getLength()); // singular addresses, not prefixes
    EXPECT_EQ("2001:db8:1::", pool1->getFirstAddress().toText());
    EXPECT_EQ("2001:db8:1::ffff:ffff", pool1->getLastAddress().toText());

    // Check that it's possible to have min-max range for TA
    Pool6Ptr pool2;
    EXPECT_NO_THROW(pool2.reset(new Pool6(Lease::TYPE_TA,
                                          IOAddress("2001:db8:1::1"),
                                          IOAddress("2001:db8:1::f"))));
    ASSERT_TRUE(pool2);
    EXPECT_EQ(Lease::TYPE_TA, pool2->getType());
    EXPECT_EQ(128, pool2->getLength()); // singular addresses, not prefixes
    EXPECT_EQ("2001:db8:1::1", pool2->getFirstAddress().toText());
    EXPECT_EQ("2001:db8:1::f", pool2->getLastAddress().toText());
}

// This test creates 100 pools and verifies that their IDs are unique.
TEST(Pool6Test, unique_id) {

    const int num_pools = 100;
    std::vector<Pool6Ptr> pools;

    for (int i = 0; i < num_pools; ++i) {
        pools.push_back(Pool6Ptr(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1::"),
                                           IOAddress("2001:db8:1::ffff:ffff:ffff:ffff"))));
    }

    for (int i = 0; i < num_pools; ++i) {
        for (int j = i + 1; j < num_pools; ++j) {
            if (pools[i]->getId() == pools[j]->getId()) {
                FAIL() << "Pool-ids must be unique";
            }
        }
    }

}

// Simple check if toText returns reasonable values
TEST(Pool6Test,toText) {
    Pool6 pool1(Lease::TYPE_NA, IOAddress("2001:db8::1"),
                IOAddress("2001:db8::2"));
    EXPECT_EQ("type=IA_NA, 2001:db8::1-2001:db8::2, delegated_len=128",
              pool1.toText());

    Pool6 pool2(Lease::TYPE_PD, IOAddress("2001:db8:1::"), 96, 112);
    EXPECT_EQ("type=IA_PD, 2001:db8:1::-2001:db8:1::ffff:ffff, delegated_len=112",
              pool2.toText());

    Pool6 pool3(IOAddress("2001:db8:1::"), 96, 112,
                IOAddress("2001:db8:1::1000"), 120);
    EXPECT_EQ("type=IA_PD, 2001:db8:1::-2001:db8:1::ffff:ffff, delegated_len=112,"
              " excluded_prefix_len=120",
              pool3.toText());

}

// Checks if the number of possible leases in range is reported correctly.
TEST(Pool6Test, leasesCount) {
    Pool6 pool1(Lease::TYPE_NA, IOAddress("2001:db8::1"),
                IOAddress("2001:db8::2"));
    EXPECT_EQ(2, pool1.getCapacity());

    Pool6 pool2(Lease::TYPE_PD, IOAddress("2001:db8:1::"), 96, 112);
    EXPECT_EQ(65536, pool2.getCapacity());
}

// This test checks that it is possible to specify pool specific options.
TEST(Pool6Test, addOptions) {
    // Create a pool to add options to it.
    Pool6Ptr pool(new Pool6(Lease::TYPE_PD, IOAddress("3000::"), 64, 128));

    // Differentiate options by their codes (100-109)
    for (uint16_t code = 100; code < 110; ++code) {
        OptionPtr option(new Option(Option::V6, code, OptionBuffer(10, 0xFF)));
        ASSERT_NO_THROW(pool->getCfgOption()->add(option, false, "dhcp6"));
    }

    // Add 7 options to another option space. The option codes partially overlap
    // with option codes that we have added to dhcp6 option space.
    for (uint16_t code = 105; code < 112; ++code) {
        OptionPtr option(new Option(Option::V6, code, OptionBuffer(10, 0xFF)));
        ASSERT_NO_THROW(pool->getCfgOption()->add(option, false, "isc"));
    }

    // Get options from the pool and check if all 10 are there.
    OptionContainerPtr options = pool->getCfgOption()->getAll("dhcp6");
    ASSERT_TRUE(options);
    ASSERT_EQ(10, options->size());

    // Validate codes of options added to dhcp6 option space.
    uint16_t expected_code = 100;
    for (OptionContainer::const_iterator option_desc = options->begin();
         option_desc != options->end(); ++option_desc) {
        ASSERT_TRUE(option_desc->option_);
        EXPECT_EQ(expected_code, option_desc->option_->getType());
        ++expected_code;
    }

    options = pool->getCfgOption()->getAll("isc");
    ASSERT_TRUE(options);
    ASSERT_EQ(7, options->size());

    // Validate codes of options added to isc option space.
    expected_code = 105;
    for (OptionContainer::const_iterator option_desc = options->begin();
         option_desc != options->end(); ++option_desc) {
        ASSERT_TRUE(option_desc->option_);
        EXPECT_EQ(expected_code, option_desc->option_->getType());
        ++expected_code;
    }

    // Try to get options from a non-existing option space.
    options = pool->getCfgOption()->getAll("abcd");
    ASSERT_TRUE(options);
    EXPECT_TRUE(options->empty());
}

}; // end of anonymous namespace