cfg_subnets4_unittest.cc 16.3 KB
Newer Older
1
// Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
2
//
3
4
5
// 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/.
6
7
8
9
10
11

#include <config.h>
#include <dhcp/classify.h>
#include <dhcp/tests/iface_mgr_test_config.h>
#include <dhcpsrv/cfg_subnets4.h>
#include <dhcpsrv/subnet.h>
12
#include <dhcpsrv/subnet_id.h>
13
#include <dhcpsrv/subnet_selector.h>
14
#include <gtest/gtest.h>
15
#include <vector>
16
17
18
19
20
21
22
23
24
25

using namespace isc;
using namespace isc::asiolink;
using namespace isc::dhcp;
using namespace isc::dhcp::test;

namespace {

// This test verifies that it is possible to retrieve a subnet using an
// IP address.
26
TEST(CfgSubnets4Test, selectSubnetByCiaddr) {
27
28
29
30
31
32
33
34
    CfgSubnets4 cfg;

    // Create 3 subnets.
    Subnet4Ptr subnet1(new Subnet4(IOAddress("192.0.2.0"), 26, 1, 2, 3));
    Subnet4Ptr subnet2(new Subnet4(IOAddress("192.0.2.64"), 26, 1, 2, 3));
    Subnet4Ptr subnet3(new Subnet4(IOAddress("192.0.2.128"), 26, 1, 2, 3));

    // Make sure that initially the subnets don't exist.
35
    SubnetSelector selector;
36
37
38
    selector.ciaddr_ = IOAddress("192.0.2.0");
    // Set some unicast local address to simulate a Renew.
    selector.local_address_ = IOAddress("10.0.0.100");
39
    ASSERT_FALSE(cfg.selectSubnet(selector));
40
41
42
43

    // Add one subnet and make sure it is returned.
    cfg.add(subnet1);
    selector.ciaddr_ = IOAddress("192.0.2.63");
44
    EXPECT_EQ(subnet1, cfg.selectSubnet(selector));
45
46
47
48
49
50
51

    // Add all other subnets.
    cfg.add(subnet2);
    cfg.add(subnet3);

    // Make sure they are returned for the appropriate addresses.
    selector.ciaddr_ = IOAddress("192.0.2.15");
52
    EXPECT_EQ(subnet1, cfg.selectSubnet(selector));
53
    selector.ciaddr_ = IOAddress("192.0.2.85");
54
    EXPECT_EQ(subnet2, cfg.selectSubnet(selector));
55
    selector.ciaddr_ = IOAddress("192.0.2.191");
56
    EXPECT_EQ(subnet3, cfg.selectSubnet(selector));
57
58
59
60

    // Also, make sure that the NULL pointer is returned if the subnet
    // cannot be found.
    selector.ciaddr_ = IOAddress("192.0.2.192");
61
    EXPECT_FALSE(cfg.selectSubnet(selector));
62
63
}

64
65
66
67
68
// This test verifies that it is possible to select a subnet by
// matching an interface name.
TEST(CfgSubnets4Test, selectSubnetByIface) {
    // The IfaceMgrTestConfig object initializes fake interfaces:
    // eth0, eth1 and lo on the configuration manager. The CfgSubnets4
69
    // object uses interface names to select the appropriate subnet.
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
    IfaceMgrTestConfig config(true);

    CfgSubnets4 cfg;

    // Create 3 subnets.
    Subnet4Ptr subnet1(new Subnet4(IOAddress("192.0.2.0"), 26, 1, 2, 3));
    Subnet4Ptr subnet2(new Subnet4(IOAddress("192.0.2.64"), 26, 1, 2, 3));
    Subnet4Ptr subnet3(new Subnet4(IOAddress("192.0.2.128"), 26, 1, 2, 3));
    // No interface defined for subnet1
    subnet2->setIface("lo");
    subnet3->setIface("eth1");

    cfg.add(subnet1);
    cfg.add(subnet2);
    cfg.add(subnet3);

    // Make sure that initially the subnets don't exist.
    SubnetSelector selector;
    // Set an interface to a name that is not defined in the config.
    // Subnet selection should fail.
    selector.iface_name_ = "eth0";
    ASSERT_FALSE(cfg.selectSubnet(selector));

    // Now select an interface name that matches. Selection should succeed
    // and return subnet3.
    selector.iface_name_ = "eth1";
    Subnet4Ptr selected = cfg.selectSubnet(selector);
    ASSERT_TRUE(selected);
    EXPECT_EQ(subnet3, selected);
}
100
101
102

// This test verifies that when the classification information is specified for
// subnets, the proper subnets are returned by the subnet configuration.
103
TEST(CfgSubnets4Test, selectSubnetByClasses) {
104
105
106
107
108
109
110
111
112
113
114
115
    CfgSubnets4 cfg;

    // Create 3 subnets.
    Subnet4Ptr subnet1(new Subnet4(IOAddress("192.0.2.0"), 26, 1, 2, 3));
    Subnet4Ptr subnet2(new Subnet4(IOAddress("192.0.2.64"), 26, 1, 2, 3));
    Subnet4Ptr subnet3(new Subnet4(IOAddress("192.0.2.128"), 26, 1, 2, 3));

    // Add them to the configuration.
    cfg.add(subnet1);
    cfg.add(subnet2);
    cfg.add(subnet3);

116
    SubnetSelector selector;
117
118
119
120

    selector.local_address_ = IOAddress("10.0.0.10");

    selector.ciaddr_ = IOAddress("192.0.2.5");
121
    EXPECT_EQ(subnet1, cfg.selectSubnet(selector));
122
    selector.ciaddr_ = IOAddress("192.0.2.70");
123
    EXPECT_EQ(subnet2, cfg.selectSubnet(selector));
124
    selector.ciaddr_ = IOAddress("192.0.2.130");
125
    EXPECT_EQ(subnet3, cfg.selectSubnet(selector));
126
127
128
129
130
131
132
133

    ClientClasses client_classes;
    client_classes.insert("bar");
    selector.client_classes_ = client_classes;

    // There are no class restrictions defined, so everything should work
    // as before
    selector.ciaddr_ = IOAddress("192.0.2.5");
134
    EXPECT_EQ(subnet1, cfg.selectSubnet(selector));
135
    selector.ciaddr_ = IOAddress("192.0.2.70");
136
    EXPECT_EQ(subnet2, cfg.selectSubnet(selector));
137
    selector.ciaddr_ = IOAddress("192.0.2.130");
138
    EXPECT_EQ(subnet3, cfg.selectSubnet(selector));
139
140
141
142
143
144
145
146
147

    // Now let's add client class restrictions.
    subnet1->allowClientClass("foo"); // Serve here only clients from foo class
    subnet2->allowClientClass("bar"); // Serve here only clients from bar class
    subnet3->allowClientClass("baz"); // Serve here only clients from baz class

    // The same check as above should result in client being served only in
    // bar class, i.e. subnet2.
    selector.ciaddr_ = IOAddress("192.0.2.5");
148
    EXPECT_FALSE(cfg.selectSubnet(selector));
149
    selector.ciaddr_ = IOAddress("192.0.2.70");
150
    EXPECT_EQ(subnet2, cfg.selectSubnet(selector));
151
    selector.ciaddr_ = IOAddress("192.0.2.130");
152
    EXPECT_FALSE(cfg.selectSubnet(selector));
153
154
155
156
157
158

    // Now let's check that client with wrong class is not supported.
    client_classes.clear();
    client_classes.insert("some_other_class");
    selector.client_classes_ = client_classes;
    selector.ciaddr_ = IOAddress("192.0.2.5");
159
    EXPECT_FALSE(cfg.selectSubnet(selector));
160
    selector.ciaddr_ = IOAddress("192.0.2.70");
161
    EXPECT_FALSE(cfg.selectSubnet(selector));
162
    selector.ciaddr_ = IOAddress("192.0.2.130");
163
    EXPECT_FALSE(cfg.selectSubnet(selector));
164
165
166
167

    // Finally, let's check that client without any classes is not supported.
    client_classes.clear();
    selector.ciaddr_ = IOAddress("192.0.2.5");
168
    EXPECT_FALSE(cfg.selectSubnet(selector));
169
    selector.ciaddr_ = IOAddress("192.0.2.70");
170
    EXPECT_FALSE(cfg.selectSubnet(selector));
171
    selector.ciaddr_ = IOAddress("192.0.2.130");
172
    EXPECT_FALSE(cfg.selectSubnet(selector));
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
// This test verifies the option selection can be used and is only
// used when present.
TEST(CfgSubnets4Test, selectSubnetByOptionSelect) {
    CfgSubnets4 cfg;

    // Create 3 subnets.
    Subnet4Ptr subnet1(new Subnet4(IOAddress("192.0.2.0"), 26, 1, 2, 3));
    Subnet4Ptr subnet2(new Subnet4(IOAddress("192.0.2.64"), 26, 1, 2, 3));
    Subnet4Ptr subnet3(new Subnet4(IOAddress("192.0.2.128"), 26, 1, 2, 3));

    // Add them to the configuration.
    cfg.add(subnet1);
    cfg.add(subnet2);
    cfg.add(subnet3);

    SubnetSelector selector;

    // Check that without option selection something else is used
    selector.ciaddr_ = IOAddress("192.0.2.5");
    EXPECT_EQ(subnet1, cfg.selectSubnet(selector));

    // The option selection has precedence
    selector.option_select_ = IOAddress("192.0.2.130");
    EXPECT_EQ(subnet3, cfg.selectSubnet(selector));

    // Over relay-info too
    selector.giaddr_ = IOAddress("10.0.0.1");
    subnet2->setRelayInfo(IOAddress("10.0.0.1"));
    EXPECT_EQ(subnet3, cfg.selectSubnet(selector));
    selector.option_select_ = IOAddress("0.0.0.0");
    EXPECT_EQ(subnet2, cfg.selectSubnet(selector));

    // Check that a not matching option selection it shall fail
    selector.option_select_ = IOAddress("10.0.0.1");
    EXPECT_FALSE(cfg.selectSubnet(selector));
}

212
213
// This test verifies that the relay information can be used to retrieve the
// subnet.
214
TEST(CfgSubnets4Test, selectSubnetByRelayAddress) {
215
216
217
218
219
220
221
222
223
224
225
226
    CfgSubnets4 cfg;

    // Create 3 subnets.
    Subnet4Ptr subnet1(new Subnet4(IOAddress("192.0.2.0"), 26, 1, 2, 3));
    Subnet4Ptr subnet2(new Subnet4(IOAddress("192.0.2.64"), 26, 1, 2, 3));
    Subnet4Ptr subnet3(new Subnet4(IOAddress("192.0.2.128"), 26, 1, 2, 3));

    // Add them to the configuration.
    cfg.add(subnet1);
    cfg.add(subnet2);
    cfg.add(subnet3);

227
    SubnetSelector selector;
228
229
230

    // Check that without relay-info specified, subnets are not selected
    selector.giaddr_ = IOAddress("10.0.0.1");
231
    EXPECT_FALSE(cfg.selectSubnet(selector));
232
    selector.giaddr_ = IOAddress("10.0.0.2");
233
    EXPECT_FALSE(cfg.selectSubnet(selector));
234
    selector.giaddr_ = IOAddress("10.0.0.3");
235
    EXPECT_FALSE(cfg.selectSubnet(selector));
236
237
238
239
240
241
242
243

    // Now specify relay info
    subnet1->setRelayInfo(IOAddress("10.0.0.1"));
    subnet2->setRelayInfo(IOAddress("10.0.0.2"));
    subnet3->setRelayInfo(IOAddress("10.0.0.3"));

    // And try again. This time relay-info is there and should match.
    selector.giaddr_ = IOAddress("10.0.0.1");
244
    EXPECT_EQ(subnet1, cfg.selectSubnet(selector));
245
    selector.giaddr_ = IOAddress("10.0.0.2");
246
    EXPECT_EQ(subnet2, cfg.selectSubnet(selector));
247
    selector.giaddr_ = IOAddress("10.0.0.3");
248
    EXPECT_EQ(subnet3, cfg.selectSubnet(selector));
249
250
251
252
}

// This test verifies that the subnet can be selected for the client
// using a source address if the client hasn't set the ciaddr.
253
TEST(CfgSubnets4Test, selectSubnetNoCiaddr) {
254
255
256
257
258
259
260
261
    CfgSubnets4 cfg;

    // Create 3 subnets.
    Subnet4Ptr subnet1(new Subnet4(IOAddress("192.0.2.0"), 26, 1, 2, 3));
    Subnet4Ptr subnet2(new Subnet4(IOAddress("192.0.2.64"), 26, 1, 2, 3));
    Subnet4Ptr subnet3(new Subnet4(IOAddress("192.0.2.128"), 26, 1, 2, 3));

    // Make sure that initially the subnets don't exist.
262
    SubnetSelector selector;
263
264
265
    selector.remote_address_ = IOAddress("192.0.2.0");
    // Set some unicast local address to simulate a Renew.
    selector.local_address_ = IOAddress("10.0.0.100");
266
    ASSERT_FALSE(cfg.selectSubnet(selector));
267
268
269
270

    // Add one subnet and make sure it is returned.
    cfg.add(subnet1);
    selector.remote_address_ = IOAddress("192.0.2.63");
271
    EXPECT_EQ(subnet1, cfg.selectSubnet(selector));
272
273
274
275
276
277
278

    // Add all other subnets.
    cfg.add(subnet2);
    cfg.add(subnet3);

    // Make sure they are returned for the appropriate addresses.
    selector.remote_address_ = IOAddress("192.0.2.15");
279
    EXPECT_EQ(subnet1, cfg.selectSubnet(selector));
280
    selector.remote_address_ = IOAddress("192.0.2.85");
281
    EXPECT_EQ(subnet2, cfg.selectSubnet(selector));
282
    selector.remote_address_ = IOAddress("192.0.2.191");
283
    EXPECT_EQ(subnet3, cfg.selectSubnet(selector));
284
285
286
287

    // Also, make sure that the NULL pointer is returned if the subnet
    // cannot be found.
    selector.remote_address_ = IOAddress("192.0.2.192");
288
    EXPECT_FALSE(cfg.selectSubnet(selector));
289
290
291
292
}

// This test verifies that the subnet can be selected using an address
// set on the local interface.
293
TEST(CfgSubnets4Test, selectSubnetInterface) {
294
295
296
297
    // The IfaceMgrTestConfig object initializes fake interfaces:
    // eth0, eth1 and lo on the configuration manager. The CfgSubnets4
    // object uses addresses assigned to these fake interfaces to
    // select the appropriate subnet.
298
299
300
    IfaceMgrTestConfig config(true);

    CfgSubnets4 cfg;
301
    SubnetSelector selector;
302
303
304
305

    // Initially, there are no subnets configured, so none of the IPv4
    // addresses assigned to eth0 and eth1 can match with any subnet.
    selector.iface_name_ = "eth0";
306
    EXPECT_FALSE(cfg.selectSubnet(selector));
307
    selector.iface_name_ = "eth1";
308
    EXPECT_FALSE(cfg.selectSubnet(selector));
309
310
311
312
313
314
315

    // Configure first subnet which address on eth0 corresponds to.
    Subnet4Ptr subnet1(new Subnet4(IOAddress("10.0.0.1"), 24, 1, 2, 3));
    cfg.add(subnet1);

    // The address on eth0 should match the existing subnet.
    selector.iface_name_ = "eth0";
316
    Subnet4Ptr subnet1_ret = cfg.selectSubnet(selector);
317
318
319
320
    ASSERT_TRUE(subnet1_ret);
    EXPECT_EQ(subnet1->get().first, subnet1_ret->get().first);
    // There should still be no match for eth1.
    selector.iface_name_ = "eth1";
321
    EXPECT_FALSE(cfg.selectSubnet(selector));
322
323
324
325
326
327
328
329

    // Configure a second subnet.
    Subnet4Ptr subnet2(new Subnet4(IOAddress("192.0.2.1"), 24, 1, 2, 3));
    cfg.add(subnet2);

    // There should be match between eth0 and subnet1 and between eth1 and
    // subnet 2.
    selector.iface_name_ = "eth0";
330
    subnet1_ret = cfg.selectSubnet(selector);
331
332
333
    ASSERT_TRUE(subnet1_ret);
    EXPECT_EQ(subnet1->get().first, subnet1_ret->get().first);
    selector.iface_name_ = "eth1";
334
    Subnet4Ptr subnet2_ret = cfg.selectSubnet(selector);
335
336
337
338
339
    ASSERT_TRUE(subnet2_ret);
    EXPECT_EQ(subnet2->get().first, subnet2_ret->get().first);

    // This function throws an exception if the name of the interface is wrong.
    selector.iface_name_ = "bogus-interface";
340
    EXPECT_THROW(cfg.selectSubnet(selector), isc::BadValue);
341
342
343
344
}

// Checks that detection of duplicated subnet IDs works as expected. It should
// not be possible to add two IPv4 subnets holding the same ID.
345
TEST(CfgSubnets4Test, duplication) {
346
347
348
349
350
351
352
353
354
    CfgSubnets4 cfg;

    Subnet4Ptr subnet1(new Subnet4(IOAddress("192.0.2.0"), 26, 1, 2, 3, 123));
    Subnet4Ptr subnet2(new Subnet4(IOAddress("192.0.2.64"), 26, 1, 2, 3, 124));
    Subnet4Ptr subnet3(new Subnet4(IOAddress("192.0.2.128"), 26, 1, 2, 3, 123));

    ASSERT_NO_THROW(cfg.add(subnet1));
    EXPECT_NO_THROW(cfg.add(subnet2));
    // Subnet 3 has the same ID as subnet 1. It shouldn't be able to add it.
355
    EXPECT_THROW(cfg.add(subnet3), isc::dhcp::DuplicateSubnetID);
356
357
}

358
// This test checks if the IPv4 subnet can be selected based on the IPv6 address.
359
TEST(CfgSubnets4Test, 4o6subnetMatchByAddress) {
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
    CfgSubnets4 cfg;

    Subnet4Ptr subnet1(new Subnet4(IOAddress("192.0.2.0"), 26, 1, 2, 3, 123));
    Subnet4Ptr subnet2(new Subnet4(IOAddress("192.0.2.64"), 26, 1, 2, 3, 124));
    Subnet4Ptr subnet3(new Subnet4(IOAddress("192.0.2.128"), 26, 1, 2, 3, 125));

    subnet2->get4o6().setSubnet4o6(IOAddress("2001:db8:1::"), 48);
    subnet3->get4o6().setSubnet4o6(IOAddress("2001:db8:2::"), 48);


    cfg.add(subnet1);
    cfg.add(subnet2);
    cfg.add(subnet3);

    SubnetSelector selector;
    selector.dhcp4o6_ = true;
    selector.remote_address_ = IOAddress("2001:db8:1::dead:beef");

    EXPECT_EQ(subnet2, cfg.selectSubnet(selector));
}

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
// This test checks if the IPv4 subnet can be selected based on the value of
// interface-id option.
TEST(CfgSubnets4Test, 4o6subnetMatchByInterfaceId) {
    CfgSubnets4 cfg;

    Subnet4Ptr subnet1(new Subnet4(IOAddress("192.0.2.0"), 26, 1, 2, 3, 123));
    Subnet4Ptr subnet2(new Subnet4(IOAddress("192.0.2.64"), 26, 1, 2, 3, 124));
    Subnet4Ptr subnet3(new Subnet4(IOAddress("192.0.2.128"), 26, 1, 2, 3, 125));

    const uint8_t dummyPayload1[] = { 1, 2, 3, 4};
    const uint8_t dummyPayload2[] = { 1, 2, 3, 5};
    std::vector<uint8_t> data1(dummyPayload1, dummyPayload1 + sizeof(dummyPayload1));
    std::vector<uint8_t> data2(dummyPayload2, dummyPayload2 + sizeof(dummyPayload2));

    OptionPtr interfaceId1(new Option(Option::V6, D6O_INTERFACE_ID, data1));
    OptionPtr interfaceId2(new Option(Option::V6, D6O_INTERFACE_ID, data2));

    subnet2->get4o6().setInterfaceId(interfaceId1);

    cfg.add(subnet1);
    cfg.add(subnet2);
    cfg.add(subnet3);

    SubnetSelector selector;
    selector.dhcp4o6_ = true;
    selector.interface_id_ = interfaceId2;
    // We have mismatched interface-id options (data1 vs data2). Should not match.
    EXPECT_FALSE(cfg.selectSubnet(selector));

    // This time we have correct interface-id. Should match.
    selector.interface_id_ = interfaceId1;
    EXPECT_EQ(subnet2, cfg.selectSubnet(selector));
}

// This test checks if the IPv4 subnet can be selected based on the value of
// interface name option.
TEST(CfgSubnets4Test, 4o6subnetMatchByInterfaceName) {
    CfgSubnets4 cfg;

    Subnet4Ptr subnet1(new Subnet4(IOAddress("192.0.2.0"), 26, 1, 2, 3, 123));
    Subnet4Ptr subnet2(new Subnet4(IOAddress("192.0.2.64"), 26, 1, 2, 3, 124));
    Subnet4Ptr subnet3(new Subnet4(IOAddress("192.0.2.128"), 26, 1, 2, 3, 125));

    subnet2->get4o6().setIface4o6("eth7");

    cfg.add(subnet1);
    cfg.add(subnet2);
    cfg.add(subnet3);

    SubnetSelector selector;
    selector.dhcp4o6_ = true;
    selector.iface_name_ = "eth5";
    // We have mismatched interface names. Should not match.
    EXPECT_FALSE(cfg.selectSubnet(selector));

    // This time we have correct names. Should match.
    selector.iface_name_ = "eth7";
    EXPECT_EQ(subnet2, cfg.selectSubnet(selector));
}

441
442

} // end of anonymous namespace