1 // Copyright (C) 2017-2021 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7 #include <config.h>
8 #include <asiolink/io_address.h>
9 #include <cc/data.h>
10 #include <dhcp/dhcp4.h>
11 #include <dhcp/tests/iface_mgr_test_config.h>
12 #include <dhcp/option.h>
13 #include <dhcp/option_int.h>
14 #include <dhcp/option_string.h>
15 #include <dhcpsrv/cfgmgr.h>
16 #include <dhcpsrv/cfg_subnets4.h>
17 #include <dhcpsrv/lease_mgr_factory.h>
18 #include <dhcp4/json_config_parser.h>
19 #include <dhcp4/tests/dhcp4_client.h>
20 #include <dhcp4/tests/dhcp4_test_utils.h>
21 #include <stats/stats_mgr.h>
22 #include <boost/pointer_cast.hpp>
23 #include <boost/shared_ptr.hpp>
24 #include <functional>
25
26 using namespace isc;
27 using namespace isc::asiolink;
28 using namespace isc::data;
29 using namespace isc::dhcp;
30 using namespace isc::dhcp::test;
31 using namespace isc::stats;
32
33 namespace {
34
35 /// @brief Array of server configurations used throughout the tests.
36 const char* NETWORKS_CONFIG[] = {
37 // Configuration #0
38 // - 1 shared network with 2 subnets (interface specified)
39 // - 1 "plain" subnet (different interface specified)
40 "{"
41 " \"interfaces-config\": {"
42 " \"interfaces\": [ \"*\" ]"
43 " },"
44 " \"valid-lifetime\": 600,"
45 " \"shared-networks\": ["
46 " {"
47 " \"name\": \"frog\","
48 " \"interface\": \"eth1\","
49 " \"comment\": \"example\","
50 " \"subnet4\": ["
51 " {"
52 " \"subnet\": \"192.0.2.0/26\","
53 " \"id\": 10,"
54 " \"pools\": ["
55 " {"
56 " \"pool\": \"192.0.2.63 - 192.0.2.63\""
57 " }"
58 " ]"
59 " },"
60 " {"
61 " \"subnet\": \"10.0.0.0/24\","
62 " \"id\": 100,"
63 " \"pools\": ["
64 " {"
65 " \"pool\": \"10.0.0.16 - 10.0.0.16\""
66 " }"
67 " ]"
68 " }"
69 " ]"
70 " }"
71 " ],"
72 " \"subnet4\": ["
73 " {"
74 " \"subnet\": \"192.0.2.64/26\","
75 " \"id\": 1000,"
76 " \"interface\": \"eth0\","
77 " \"pools\": ["
78 " {"
79 " \"pool\": \"192.0.2.65 - 192.0.2.65\""
80 " }"
81 " ]"
82 " }"
83 " ]"
84 "}",
85
86 // Configuration #1
87 // - 1 shared networks with 1 subnet, relay ip specified
88 // - 1 "plain" subnet, relay ip specified
89 "{"
90 " \"interfaces-config\": {"
91 " \"interfaces\": [ \"*\" ]"
92 " },"
93 " \"valid-lifetime\": 600,"
94 " \"shared-networks\": ["
95 " {"
96 " \"name\": \"frog\","
97 " \"relay\": {"
98 " \"ip-address\": \"192.3.5.6\""
99 " },"
100 " \"subnet4\": ["
101 " {"
102 " \"subnet\": \"192.0.2.0/26\","
103 " \"id\": 10,"
104 " \"pools\": ["
105 " {"
106 " \"pool\": \"192.0.2.63 - 192.0.2.63\""
107 " }"
108 " ]"
109 " }"
110 " ]"
111 " }"
112 " ],"
113 " \"subnet4\": ["
114 " {"
115 " \"subnet\": \"192.0.2.64/26\","
116 " \"id\": 1000,"
117 " \"relay\": {"
118 " \"ip-address\": \"192.1.2.3\""
119 " },"
120 " \"pools\": ["
121 " {"
122 " \"pool\": \"192.0.2.65 - 192.0.2.65\""
123 " }"
124 " ]"
125 " }"
126 " ]"
127 "}",
128
129 // Configuration #2
130 // - 2 classes defined
131 // - 1 shared network with 2 subnets (first has class restriction)
132 "{"
133 " \"interfaces-config\": {"
134 " \"interfaces\": [ \"*\" ]"
135 " },"
136 " \"valid-lifetime\": 600,"
137 " \"client-classes\": ["
138 " {"
139 " \"name\": \"a-devices\","
140 " \"test\": \"option[93].hex == 0x0001\""
141 " },"
142 " {"
143 " \"name\": \"b-devices\","
144 " \"test\": \"option[93].hex == 0x0002\""
145 " }"
146 " ],"
147 " \"shared-networks\": ["
148 " {"
149 " \"name\": \"frog\","
150 " \"relay\": {"
151 " \"ip-address\": \"192.3.5.6\""
152 " },"
153 " \"subnet4\": ["
154 " {"
155 " \"subnet\": \"192.0.2.0/26\","
156 " \"id\": 10,"
157 " \"pools\": ["
158 " {"
159 " \"pool\": \"192.0.2.63 - 192.0.2.63\""
160 " }"
161 " ],"
162 " \"client-class\": \"a-devices\""
163 " },"
164 " {"
165 " \"subnet\": \"10.0.0.0/24\","
166 " \"id\": 100,"
167 " \"pools\": ["
168 " {"
169 " \"pool\": \"10.0.0.16 - 10.0.0.16\""
170 " }"
171 " ]"
172 " }"
173 " ]"
174 " }"
175 " ]"
176 "}",
177
178 // Configuration #3
179 // - 2 classes specified
180 // - 1 shared network with 2 subnets (each with class restriction)
181 "{"
182 " \"interfaces-config\": {"
183 " \"interfaces\": [ \"*\" ]"
184 " },"
185 " \"valid-lifetime\": 600,"
186 " \"client-classes\": ["
187 " {"
188 " \"name\": \"a-devices\","
189 " \"test\": \"option[93].hex == 0x0001\""
190 " },"
191 " {"
192 " \"name\": \"b-devices\","
193 " \"test\": \"option[93].hex == 0x0002\""
194 " }"
195 " ],"
196 " \"shared-networks\": ["
197 " {"
198 " \"name\": \"frog\","
199 " \"relay\": {"
200 " \"ip-address\": \"192.3.5.6\""
201 " },"
202 " \"subnet4\": ["
203 " {"
204 " \"subnet\": \"192.0.2.0/26\","
205 " \"id\": 10,"
206 " \"pools\": ["
207 " {"
208 " \"pool\": \"192.0.2.63 - 192.0.2.63\""
209 " }"
210 " ],"
211 " \"client-class\": \"a-devices\""
212 " },"
213 " {"
214 " \"subnet\": \"10.0.0.0/24\","
215 " \"id\": 100,"
216 " \"pools\": ["
217 " {"
218 " \"pool\": \"10.0.0.16 - 10.0.0.16\""
219 " }"
220 " ],"
221 " \"client-class\": \"b-devices\""
222 " }"
223 " ]"
224 " }"
225 " ]"
226 "}",
227
228 // Configuration #4
229 // - 1 shared network with 2 subnets, each has one host reservation
230 "{"
231 " \"interfaces-config\": {"
232 " \"interfaces\": [ \"*\" ]"
233 " },"
234 " \"valid-lifetime\": 600,"
235 " \"shared-networks\": ["
236 " {"
237 " \"name\": \"frog\","
238 " \"relay\": {"
239 " \"ip-address\": \"192.3.5.6\""
240 " },"
241 " \"subnet4\": ["
242 " {"
243 " \"subnet\": \"192.0.2.0/26\","
244 " \"id\": 10,"
245 " \"pools\": ["
246 " {"
247 " \"pool\": \"192.0.2.1 - 192.0.2.63\""
248 " }"
249 " ],"
250 " \"reservations\": ["
251 " {"
252 " \"hw-address\": \"aa:bb:cc:dd:ee:ff\","
253 " \"ip-address\": \"192.0.2.28\""
254 " }"
255 " ]"
256 " },"
257 " {"
258 " \"subnet\": \"10.0.0.0/24\","
259 " \"id\": 100,"
260 " \"pools\": ["
261 " {"
262 " \"pool\": \"10.0.0.1 - 10.0.0.254\""
263 " }"
264 " ],"
265 " \"reservations\": ["
266 " {"
267 " \"hw-address\": \"11:22:33:44:55:66\","
268 " \"ip-address\": \"10.0.0.29\""
269 " }"
270 " ]"
271 " }"
272 " ]"
273 " }"
274 " ]"
275 "}",
276
277 // Configuration #5
278 // - 1 shared network, with 2 subnets. Each has host reservation
279 // - similar to config #4, but with different hw-address reserved
280 "{"
281 " \"interfaces-config\": {"
282 " \"interfaces\": [ \"*\" ]"
283 " },"
284 " \"valid-lifetime\": 600,"
285 " \"shared-networks\": ["
286 " {"
287 " \"name\": \"frog\","
288 " \"relay\": {"
289 " \"ip-address\": \"192.3.5.6\""
290 " },"
291 " \"subnet4\": ["
292 " {"
293 " \"subnet\": \"192.0.2.0/26\","
294 " \"id\": 10,"
295 " \"pools\": ["
296 " {"
297 " \"pool\": \"192.0.2.1 - 192.0.2.63\""
298 " }"
299 " ],"
300 " \"reservations\": ["
301 " {"
302 " \"hw-address\": \"11:22:33:44:55:66\","
303 " \"ip-address\": \"192.0.2.28\""
304 " }"
305 " ]"
306 " },"
307 " {"
308 " \"subnet\": \"10.0.0.0/24\","
309 " \"id\": 100,"
310 " \"pools\": ["
311 " {"
312 " \"pool\": \"10.0.0.1 - 10.0.0.254\""
313 " }"
314 " ],"
315 " \"reservations\": ["
316 " {"
317 " \"hw-address\": \"aa:bb:cc:dd:ee:ff\","
318 " \"ip-address\": \"10.0.0.29\""
319 " }"
320 " ]"
321 " }"
322 " ]"
323 " }"
324 " ]"
325 "}",
326
327 // Configuration #6
328 // - 1 class
329 // - 1 shared network, with 2 subnets. First has class restriction and
330 // host reservation
331 "{"
332 " \"interfaces-config\": {"
333 " \"interfaces\": [ \"*\" ]"
334 " },"
335 " \"valid-lifetime\": 600,"
336 " \"client-classes\": ["
337 " {"
338 " \"name\": \"a-devices\","
339 " \"test\": \"option[93].hex == 0x0001\""
340 " }"
341 " ],"
342 " \"shared-networks\": ["
343 " {"
344 " \"name\": \"frog\","
345 " \"relay\": {"
346 " \"ip-address\": \"192.3.5.6\""
347 " },"
348 " \"subnet4\": ["
349 " {"
350 " \"subnet\": \"192.0.2.0/26\","
351 " \"id\": 10,"
352 " \"pools\": ["
353 " {"
354 " \"pool\": \"192.0.2.1 - 192.0.2.63\""
355 " }"
356 " ],"
357 " \"client-class\": \"a-devices\","
358 " \"reservations\": ["
359 " {"
360 " \"hw-address\": \"aa:bb:cc:dd:ee:ff\","
361 " \"ip-address\": \"192.0.2.28\""
362 " }"
363 " ]"
364 " },"
365 " {"
366 " \"subnet\": \"10.0.0.0/24\","
367 " \"id\": 100,"
368 " \"pools\": ["
369 " {"
370 " \"pool\": \"10.0.0.16 - 10.0.0.16\""
371 " }"
372 " ]"
373 " }"
374 " ]"
375 " }"
376 " ]"
377 "}",
378
379 // Configuration #7
380 // - 1 global option
381 // - 1 shared network with some options and 2 subnets (the first one has extra
382 // options)
383 // - 1 plain subnet (that has an option)
384 "{"
385 " \"interfaces-config\": {"
386 " \"interfaces\": [ \"*\" ]"
387 " },"
388 " \"valid-lifetime\": 600,"
389 " \"option-data\": ["
390 " {"
391 " \"name\": \"log-servers\","
392 " \"data\": \"1.2.3.4\""
393 " }"
394 " ],"
395 " \"shared-networks\": ["
396 " {"
397 " \"name\": \"frog\","
398 " \"interface\": \"eth1\","
399 " \"option-data\": ["
400 " {"
401 " \"name\": \"domain-name-servers\","
402 " \"data\": \"10.1.2.3\""
403 " },"
404 " {"
405 " \"name\": \"cookie-servers\","
406 " \"data\": \"10.6.5.4\""
407 " }"
408 " ],"
409 " \"subnet4\": ["
410 " {"
411 " \"subnet\": \"192.0.2.0/26\","
412 " \"id\": 10,"
413 " \"option-data\": ["
414 " {"
415 " \"name\": \"routers\","
416 " \"data\": \"192.0.2.5\""
417 " },"
418 " {"
419 " \"name\": \"cookie-servers\","
420 " \"data\": \"10.5.4.3\""
421 " }"
422 " ],"
423 " \"pools\": ["
424 " {"
425 " \"pool\": \"192.0.2.63 - 192.0.2.63\""
426 " }"
427 " ]"
428 " },"
429 " {"
430 " \"subnet\": \"10.0.0.0/24\","
431 " \"id\": 100,"
432 " \"pools\": ["
433 " {"
434 " \"pool\": \"10.0.0.16 - 10.0.0.16\""
435 " }"
436 " ]"
437 " }"
438 " ]"
439 " }"
440 " ],"
441 " \"subnet4\": ["
442 " {"
443 " \"subnet\": \"192.0.2.64/26\","
444 " \"id\": 1000,"
445 " \"interface\": \"eth0\","
446 " \"option-data\": ["
447 " {"
448 " \"name\": \"cookie-servers\","
449 " \"data\": \"10.1.1.1\""
450 " }"
451 " ],"
452 " \"pools\": ["
453 " {"
454 " \"pool\": \"192.0.2.65 - 192.0.2.65\""
455 " }"
456 " ]"
457 " }"
458 " ]"
459 "}",
460
461 // Configuration #8
462 // - two shared networks, each with two subnets (each with interface specified)
463 "{"
464 " \"interfaces-config\": {"
465 " \"interfaces\": [ \"*\" ]"
466 " },"
467 " \"valid-lifetime\": 600,"
468 " \"shared-networks\": ["
469 " {"
470 " \"name\": \"frog\","
471 " \"interface\": \"eth1\","
472 " \"subnet4\": ["
473 " {"
474 " \"subnet\": \"192.0.2.0/26\","
475 " \"id\": 10,"
476 " \"pools\": ["
477 " {"
478 " \"pool\": \"192.0.2.1 - 192.0.2.63\""
479 " }"
480 " ]"
481 " },"
482 " {"
483 " \"subnet\": \"192.0.2.64/26\","
484 " \"id\": 100,"
485 " \"pools\": ["
486 " {"
487 " \"pool\": \"192.0.2.65 - 192.0.2.127\""
488 " }"
489 " ]"
490 " }"
491 " ]"
492 " },"
493 " {"
494 " \"name\": \"dog\","
495 " \"interface\": \"eth0\","
496 " \"subnet4\": ["
497 " {"
498 " \"subnet\": \"10.0.0.0/26\","
499 " \"id\": 1000,"
500 " \"pools\": ["
501 " {"
502 " \"pool\": \"10.0.0.1 - 10.0.0.63\""
503 " }"
504 " ]"
505 " },"
506 " {"
507 " \"subnet\": \"10.0.0.64/26\","
508 " \"id\": 10000,"
509 " \"pools\": ["
510 " {"
511 " \"pool\": \"10.0.0.65 - 10.0.0.127\""
512 " }"
513 " ]"
514 " }"
515 " ]"
516 " }"
517 " ]"
518 "}",
519
520 // Configuration #9
521 // - 2 shared networks, each with relay ip address and 2 subnets
522 "{"
523 " \"interfaces-config\": {"
524 " \"interfaces\": [ \"*\" ]"
525 " },"
526 " \"valid-lifetime\": 600,"
527 " \"shared-networks\": ["
528 " {"
529 " \"name\": \"frog\","
530 " \"relay\": { \"ip-address\": \"10.1.2.3\" },"
531 " \"subnet4\": ["
532 " {"
533 " \"subnet\": \"192.0.2.0/26\","
534 " \"id\": 10,"
535 " \"pools\": ["
536 " {"
537 " \"pool\": \"192.0.2.1 - 192.0.2.63\""
538 " }"
539 " ]"
540 " },"
541 " {"
542 " \"subnet\": \"192.0.2.64/26\","
543 " \"id\": 100,"
544 " \"pools\": ["
545 " {"
546 " \"pool\": \"192.0.2.65 - 192.0.2.127\""
547 " }"
548 " ]"
549 " }"
550 " ]"
551 " },"
552 " {"
553 " \"name\": \"dog\","
554 " \"relay\": { \"ip-address\": \"192.1.2.3\" },"
555 " \"subnet4\": ["
556 " {"
557 " \"subnet\": \"10.0.0.0/26\","
558 " \"id\": 1000,"
559 " \"pools\": ["
560 " {"
561 " \"pool\": \"10.0.0.1 - 10.0.0.63\""
562 " }"
563 " ]"
564 " },"
565 " {"
566 " \"subnet\": \"10.0.0.64/26\","
567 " \"id\": 10000,"
568 " \"pools\": ["
569 " {"
570 " \"pool\": \"10.0.0.65 - 10.0.0.127\""
571 " }"
572 " ]"
573 " }"
574 " ]"
575 " }"
576 " ]"
577 "}",
578 // Configuration #10.
579 // - 1 client class
580 // - 1 shared network with two subnets (second has a host reservation)
581 "{"
582 " \"interfaces-config\": {"
583 " \"interfaces\": [ \"*\" ]"
584 " },"
585 " \"valid-lifetime\": 600,"
586 " \"client-classes\": ["
587 " {"
588 " \"name\": \"class-with-bootfile\","
589 " \"boot-file-name\": \"/dev/null\""
590 " }"
591 " ],"
592 " \"shared-networks\": ["
593 " {"
594 " \"name\": \"frog\","
595 " \"relay\": {"
596 " \"ip-address\": \"192.3.5.6\""
597 " },"
598 " \"subnet4\": ["
599 " {"
600 " \"subnet\": \"192.0.2.0/26\","
601 " \"id\": 10,"
602 " \"pools\": ["
603 " {"
604 " \"pool\": \"192.0.2.1 - 192.0.2.63\""
605 " }"
606 " ]"
607 " },"
608 " {"
609 " \"subnet\": \"10.0.0.0/24\","
610 " \"id\": 100,"
611 " \"pools\": ["
612 " {"
613 " \"pool\": \"10.0.0.1 - 10.0.0.254\""
614 " }"
615 " ],"
616 " \"reservations\": ["
617 " {"
618 " \"hw-address\": \"11:22:33:44:55:66\","
619 " \"ip-address\": \"10.0.0.29\","
620 " \"hostname\": \"test.example.org\","
621 " \"next-server\": \"10.10.10.10\","
622 " \"client-classes\": [ \"class-with-bootfile\" ]"
623 " }"
624 " ]"
625 " }"
626 " ]"
627 " }"
628 " ]"
629 "}",
630
631 // Configuration #11.
632 // - global value of match-client-id set to false
633 // - 1 shared network (match-client-id set to true) with 2 subnets
634 // - the first subnet has match-client-id set to false
635 "{"
636 " \"interfaces-config\": {"
637 " \"interfaces\": [ \"*\" ]"
638 " },"
639 " \"valid-lifetime\": 600,"
640 " \"match-client-id\": false,"
641 " \"shared-networks\": ["
642 " {"
643 " \"name\": \"frog\","
644 " \"interface\": \"eth1\","
645 " \"match-client-id\": true,"
646 " \"subnet4\": ["
647 " {"
648 " \"subnet\": \"192.0.2.0/26\","
649 " \"id\": 10,"
650 " \"match-client-id\": false"
651 " },"
652 " {"
653 " \"subnet\": \"192.0.2.64/26\","
654 " \"id\": 100,"
655 " \"pools\": ["
656 " {"
657 " \"pool\": \"192.0.2.65 - 192.0.2.127\""
658 " }"
659 " ]"
660 " }"
661 " ]"
662 " }"
663 " ]"
664 "}",
665
666 // Configuration #12.
667 // - global value of match-client-id set to false
668 // - 1 shared network (match-client-id set to false) with 2 subnets
669 // - the first subnet has match-client-id set to false
670 "{"
671 " \"interfaces-config\": {"
672 " \"interfaces\": [ \"*\" ]"
673 " },"
674 " \"valid-lifetime\": 600,"
675 " \"match-client-id\": false,"
676 " \"shared-networks\": ["
677 " {"
678 " \"name\": \"frog\","
679 " \"interface\": \"eth1\","
680 " \"match-client-id\": false,"
681 " \"subnet4\": ["
682 " {"
683 " \"subnet\": \"192.0.2.0/26\","
684 " \"id\": 10,"
685 " \"match-client-id\": false"
686 " },"
687 " {"
688 " \"subnet\": \"192.0.2.64/26\","
689 " \"id\": 100,"
690 " \"pools\": ["
691 " {"
692 " \"pool\": \"192.0.2.65 - 192.0.2.127\""
693 " }"
694 " ]"
695 " }"
696 " ]"
697 " }"
698 " ]"
699 "}",
700
701 // Configuration #13.
702 // - 2 classes
703 // - 2 shared networks, each with 1 subnet and client class restriction
704 "{"
705 " \"interfaces-config\": {"
706 " \"interfaces\": [ \"*\" ]"
707 " },"
708 " \"client-classes\": ["
709 " {"
710 " \"name\": \"a-devices\","
711 " \"test\": \"option[93].hex == 0x0001\""
712 " },"
713 " {"
714 " \"name\": \"b-devices\","
715 " \"test\": \"option[93].hex == 0x0002\""
716 " }"
717 " ],"
718 " \"valid-lifetime\": 600,"
719 " \"shared-networks\": ["
720 " {"
721 " \"name\": \"frog\","
722 " \"interface\": \"eth1\","
723 " \"client-class\": \"a-devices\","
724 " \"subnet4\": ["
725 " {"
726 " \"subnet\": \"192.0.2.0/26\","
727 " \"id\": 10,"
728 " \"pools\": ["
729 " {"
730 " \"pool\": \"192.0.2.63 - 192.0.2.63\""
731 " }"
732 " ]"
733 " }"
734 " ]"
735 " },"
736 " {"
737 " \"name\": \"dog\","
738 " \"interface\": \"eth1\","
739 " \"client-class\": \"b-devices\","
740 " \"subnet4\": ["
741 " {"
742 " \"subnet\": \"10.0.0.0/26\","
743 " \"id\": 1000,"
744 " \"pools\": ["
745 " {"
746 " \"pool\": \"10.0.0.63 - 10.0.0.63\""
747 " }"
748 " ]"
749 " }"
750 " ]"
751 " }"
752 " ]"
753 "}",
754 // Configuration #14
755 // - 1 shared networks with 2 subnets, relay ip specified,
756 // each relay has its own relay ip specified
757 // - 1 "plain" subnet, relay ip specified
758 "{"
759 " \"interfaces-config\": {"
760 " \"interfaces\": [ \"*\" ]"
761 " },"
762 " \"valid-lifetime\": 600,"
763 " \"shared-networks\": ["
764 " {"
765 " \"name\": \"frog\","
766 " \"relay\": {"
767 " \"ip-address\": \"192.3.5.6\""
768 " },"
769 " \"subnet4\": ["
770 " {"
771 " \"subnet\": \"192.0.2.0/26\","
772 " \"id\": 10,"
773 " \"relay\": {"
774 " \"ip-address\": \"192.1.1.1\""
775 " },"
776 " \"pools\": ["
777 " {"
778 " \"pool\": \"192.0.2.63 - 192.0.2.63\""
779 " }"
780 " ]"
781 " },"
782 " {"
783 " \"subnet\": \"10.0.0.0/24\","
784 " \"id\": 100,"
785 " \"relay\": {"
786 " \"ip-address\": \"192.2.2.2\""
787 " },"
788 " \"pools\": ["
789 " {"
790 " \"pool\": \"10.0.0.16 - 10.0.0.16\""
791 " }"
792 " ]"
793 " }"
794 " ]"
795 " }"
796 " ],"
797 " \"subnet4\": ["
798 " {"
799 " \"subnet\": \"192.0.2.64/26\","
800 " \"id\": 1000,"
801 " \"relay\": {"
802 " \"ip-address\": \"192.3.3.3\""
803 " },"
804 " \"pools\": ["
805 " {"
806 " \"pool\": \"192.0.2.65 - 192.0.2.65\""
807 " }"
808 " ]"
809 " }"
810 " ]"
811 "}",
812
813 // Configuration #15
814 // - two shared networks, each comes with its own server identifier.
815 "{"
816 " \"interfaces-config\": {"
817 " \"interfaces\": [ \"*\" ]"
818 " },"
819 " \"valid-lifetime\": 600,"
820 " \"shared-networks\": ["
821 " {"
822 " \"name\": \"frog\","
823 " \"interface\": \"eth1\","
824 " \"option-data\": ["
825 " {"
826 " \"name\": \"dhcp-server-identifier\","
827 " \"data\": \"1.2.3.4\""
828 " }"
829 " ],"
830 " \"subnet4\": ["
831 " {"
832 " \"subnet\": \"192.0.2.0/26\","
833 " \"id\": 10,"
834 " \"pools\": ["
835 " {"
836 " \"pool\": \"192.0.2.1 - 192.0.2.63\""
837 " }"
838 " ]"
839 " }"
840 " ]"
841 " },"
842 " {"
843 " \"name\": \"dog\","
844 " \"interface\": \"eth0\","
845 " \"option-data\": ["
846 " {"
847 " \"name\": \"dhcp-server-identifier\","
848 " \"data\": \"2.3.4.5\""
849 " }"
850 " ],"
851 " \"subnet4\": ["
852 " {"
853 " \"subnet\": \"10.0.0.0/26\","
854 " \"id\": 1000,"
855 " \"pools\": ["
856 " {"
857 " \"pool\": \"10.0.0.1 - 10.0.0.63\""
858 " }"
859 " ]"
860 " }"
861 " ]"
862 " }"
863 " ]"
864 "}",
865
866 // Configuration #16
867 // - 1 shared network with 1 subnet and 2 pools (first pool has class restriction)
868 "{"
869 " \"interfaces-config\": {"
870 " \"interfaces\": [ \"*\" ]"
871 " },"
872 " \"client-classes\": ["
873 " {"
874 " \"name\": \"a-devices\","
875 " \"test\": \"option[93].hex == 0x0001\""
876 " },"
877 " {"
878 " \"name\": \"b-devices\","
879 " \"test\": \"option[93].hex == 0x0002\""
880 " }"
881 " ],"
882 " \"valid-lifetime\": 600,"
883 " \"shared-networks\": ["
884 " {"
885 " \"name\": \"frog\","
886 " \"interface\": \"eth1\","
887 " \"subnet4\": ["
888 " {"
889 " \"subnet\": \"192.0.2.0/24\","
890 " \"id\": 10,"
891 " \"pools\": ["
892 " {"
893 " \"pool\": \"192.0.2.1 - 192.0.2.63\","
894 " \"client-class\": \"a-devices\""
895 " },"
896 " {"
897 " \"pool\": \"192.0.2.100 - 192.0.2.100\""
898 " }"
899 " ]"
900 " }"
901 " ]"
902 " }"
903 " ]"
904 "}",
905
906 // Configuration #17
907 // - 1 shared network with 1 subnet and 2 pools (each with class restriction)
908 "{"
909 " \"interfaces-config\": {"
910 " \"interfaces\": [ \"*\" ]"
911 " },"
912 " \"client-classes\": ["
913 " {"
914 " \"name\": \"a-devices\","
915 " \"test\": \"option[93].hex == 0x0001\""
916 " },"
917 " {"
918 " \"name\": \"b-devices\","
919 " \"test\": \"option[93].hex == 0x0002\""
920 " }"
921 " ],"
922 " \"valid-lifetime\": 600,"
923 " \"shared-networks\": ["
924 " {"
925 " \"name\": \"frog\","
926 " \"interface\": \"eth1\","
927 " \"subnet4\": ["
928 " {"
929 " \"subnet\": \"192.0.2.0/24\","
930 " \"id\": 10,"
931 " \"pools\": ["
932 " {"
933 " \"pool\": \"192.0.2.1 - 192.0.2.63\","
934 " \"client-class\": \"a-devices\""
935 " },"
936 " {"
937 " \"pool\": \"192.0.2.100 - 192.0.2.100\","
938 " \"client-class\": \"b-devices\""
939 " }"
940 " ]"
941 " }"
942 " ]"
943 " }"
944 " ]"
945 "}",
946
947 // Configuration #18
948 // - plain subnet and 2 pools (first pool has class restriction)
949 "{"
950 " \"interfaces-config\": {"
951 " \"interfaces\": [ \"*\" ]"
952 " },"
953 " \"client-classes\": ["
954 " {"
955 " \"name\": \"a-devices\","
956 " \"test\": \"option[93].hex == 0x0001\""
957 " },"
958 " {"
959 " \"name\": \"b-devices\","
960 " \"test\": \"option[93].hex == 0x0002\""
961 " }"
962 " ],"
963 " \"valid-lifetime\": 600,"
964 " \"subnet4\": ["
965 " {"
966 " \"subnet\": \"192.0.2.0/24\","
967 " \"id\": 10,"
968 " \"interface\": \"eth1\","
969 " \"pools\": ["
970 " {"
971 " \"pool\": \"192.0.2.1 - 192.0.2.63\","
972 " \"client-class\": \"a-devices\""
973 " },"
974 " {"
975 " \"pool\": \"192.0.2.100 - 192.0.2.100\""
976 " }"
977 " ]"
978 " }"
979 " ]"
980 "}",
981
982 // Configuration #19
983 // - plain subnet and 2 pools (each with class restriction)
984 "{"
985 " \"interfaces-config\": {"
986 " \"interfaces\": [ \"*\" ]"
987 " },"
988 " \"client-classes\": ["
989 " {"
990 " \"name\": \"a-devices\","
991 " \"test\": \"option[93].hex == 0x0001\""
992 " },"
993 " {"
994 " \"name\": \"b-devices\","
995 " \"test\": \"option[93].hex == 0x0002\""
996 " }"
997 " ],"
998 " \"valid-lifetime\": 600,"
999 " \"subnet4\": ["
1000 " {"
1001 " \"subnet\": \"192.0.2.0/24\","
1002 " \"id\": 10,"
1003 " \"interface\": \"eth1\","
1004 " \"pools\": ["
1005 " {"
1006 " \"pool\": \"192.0.2.1 - 192.0.2.63\","
1007 " \"client-class\": \"a-devices\""
1008 " },"
1009 " {"
1010 " \"pool\": \"192.0.2.100 - 192.0.2.100\","
1011 " \"client-class\": \"b-devices\""
1012 " }"
1013 " ]"
1014 " }"
1015 " ]"
1016 "}"
1017
1018 };
1019
1020 /// @Brief Test fixture class for DHCPv4 server using shared networks.
1021 class Dhcpv4SharedNetworkTest : public Dhcpv4SrvTest {
1022 public:
1023
1024 /// @brief Constructor.
Dhcpv4SharedNetworkTest()1025 Dhcpv4SharedNetworkTest()
1026 : Dhcpv4SrvTest(),
1027 iface_mgr_test_config_(true) {
1028 StatsMgr::instance().removeAll();
1029 }
1030
1031 /// @brief Specifies authoritative flag value
1032 ///
1033 /// Used to generate authoritative configs
1034 typedef enum AuthoritativeFlag {
1035 AUTH_DEFAULT, // explicit value not specified (use default)
1036 AUTH_YES, // defined explicitly as yes
1037 AUTH_NO // defined explicitly as no
1038 } AuthoritativeFlag;
1039
1040 /// @brief Returns subnet having specified address in range.
1041 ///
1042 /// @param address Address for which subnet is being searched.
1043 /// @return Pointer to the subnet having an address in range or null pointer
1044 /// if no subnet found.
getConfiguredSubnet(const IOAddress & address)1045 Subnet4Ptr getConfiguredSubnet(const IOAddress& address) {
1046 CfgSubnets4Ptr cfg = CfgMgr::instance().getCurrentCfg()->getCfgSubnets4();
1047 const Subnet4Collection* subnets = cfg->getAll();
1048 for (auto subnet_it = subnets->cbegin(); subnet_it != subnets->cend();
1049 ++subnet_it) {
1050 if ((*subnet_it)->inRange(address)) {
1051 return (*subnet_it);
1052 }
1053 }
1054 return (Subnet4Ptr());
1055 }
1056
1057 /// @brief Perform DORA exchange and checks the result
1058 ///
1059 /// This convenience method conducts DORA exchange with client
1060 /// packets using hint values specified by third parameter.
1061 /// The response is expected to be either ACK (ack = true) or
1062 /// NAK (ack = false). The received address is checked against
1063 /// exp_addr
1064 ///
1065 /// @param client client to perform the DORA exchange
1066 /// @param exp_addr expected address (in yiaddr field)
1067 /// @param hint the address the client is supposed to sent
1068 /// (empty string means send 0.0.0.0)
1069 /// @param ack expected response (true = ACK, false = NAK)
1070 void
doDORA(Dhcp4Client & client,std::string exp_addr,std::string hint="",bool ack=true)1071 doDORA(Dhcp4Client& client, std::string exp_addr, std::string hint = "",
1072 bool ack = true) {
1073
1074 if (hint.empty()) {
1075 ASSERT_NO_THROW(client.doDORA());
1076 } else {
1077 boost::shared_ptr<IOAddress> addr(new IOAddress(hint));
1078 ASSERT_NO_THROW(client.doDORA(addr));
1079 }
1080 Pkt4Ptr resp = client.getContext().response_;
1081 ASSERT_TRUE(resp);
1082 EXPECT_EQ((ack ? DHCPACK : DHCPNAK), resp->getType());
1083 if (ack) {
1084 EXPECT_EQ(exp_addr, resp->getYiaddr().toText());
1085 Lease4Ptr lease = LeaseMgrFactory::instance().getLease4(IOAddress(resp->getYiaddr()));
1086 ASSERT_TRUE(lease);
1087 // Make sure that the subnet id in the lease database is not messed up.
1088 Subnet4Ptr subnet = getConfiguredSubnet(resp->getYiaddr());
1089 ASSERT_TRUE(subnet);
1090 ASSERT_EQ(subnet->getID(), lease->subnet_id_);
1091
1092 } else {
1093 EXPECT_EQ("0.0.0.0", resp->getYiaddr().toText());
1094 }
1095 }
1096
1097 /// @brief Perform Discover/Offer exchange and checks the result
1098 ///
1099 /// This convenience method conducts Discover/Offer exchange with client
1100 /// packets using hint values specified by third parameter.
1101 /// The response is expected to be either ACK (ack = true) or
1102 /// NAK (ack = false). The received address is checked against
1103 /// exp_addr
1104 ///
1105 /// @param client client to perform the DORA exchange
1106 /// @param exp_addr expected address (in yiaddr field)
1107 /// @param hint the address the client is supposed to sent
1108 /// (empty string means send 0.0.0.0)
1109 /// @param ack expected response (true = ACK, false = NAK)
1110 void
doDiscover(Dhcp4Client & client,std::string exp_addr,std::string hint,bool ack=true)1111 doDiscover(Dhcp4Client& client, std::string exp_addr, std::string hint,
1112 bool ack = true) {
1113
1114 if (hint.empty()) {
1115 ASSERT_NO_THROW(client.doDiscover());
1116 } else {
1117 boost::shared_ptr<IOAddress> addr(new IOAddress(hint));
1118 ASSERT_NO_THROW(client.doDiscover(addr));
1119 }
1120 Pkt4Ptr resp = client.getContext().response_;
1121 ASSERT_TRUE(resp);
1122 EXPECT_EQ((ack ? DHCPOFFER : DHCPNAK), resp->getType());
1123 if (ack) {
1124 EXPECT_EQ(exp_addr, resp->getYiaddr().toText());
1125 } else {
1126 EXPECT_EQ("0.0.0.0", resp->getYiaddr().toText());
1127 }
1128 }
1129
1130 /// @brief Perform Request/Reply exchange and checks the result
1131 ///
1132 /// This convenience method conducts Request/Reply exchange with client
1133 /// packets using hint values specified by a parameter. The response is
1134 /// expected to be either ACK if exp_addr has a non-empty length,
1135 /// or NAK when exp_addr context is empty.
1136 ///
1137 /// @param client client to perform the DORA exchange
1138 /// @param exp_addr expected address (in yiaddr field)
1139 void
doRequest(Dhcp4Client & client,std::string exp_addr)1140 doRequest(Dhcp4Client& client, std::string exp_addr) {
1141 ASSERT_NO_THROW(client.doRequest());
1142 Pkt4Ptr resp = client.getContext().response_;
1143 ASSERT_TRUE(resp);
1144 if (exp_addr.empty()) {
1145 EXPECT_EQ(DHCPNAK, resp->getType());
1146 EXPECT_EQ("0.0.0.0", resp->getYiaddr().toText());
1147 } else {
1148 EXPECT_EQ(DHCPACK, resp->getType());
1149 EXPECT_EQ(exp_addr, resp->getYiaddr().toText());
1150 Lease4Ptr lease = LeaseMgrFactory::instance().getLease4(IOAddress(resp->getYiaddr()));
1151 ASSERT_TRUE(lease);
1152 // Make sure that the subnet id in the lease database is not messed up.
1153 Subnet4Ptr subnet = getConfiguredSubnet(resp->getYiaddr());
1154 ASSERT_TRUE(subnet);
1155 ASSERT_EQ(subnet->getID(), lease->subnet_id_);
1156 }
1157 }
1158
1159 /// @brief Verifies lease statistics against values held by StatsMgr.
1160 ///
1161 /// This method retrieves lease statistics from the database and then compares it
1162 /// against values held by the StatsMgr. The compared statistics are number of
1163 /// assigned addresses and prefixes for a subnet.
verifyAssignedStats()1164 void verifyAssignedStats() {
1165 LeaseStatsQueryPtr query = LeaseMgrFactory::instance().startLeaseStatsQuery4();
1166 LeaseStatsRow row;
1167 while (query->getNextRow(row)) {
1168 // Only check valid leases.
1169 if (row.lease_state_ == Lease::STATE_DEFAULT) {
1170 ASSERT_EQ(row.state_count_, getStatsAssignedAddresses(row.subnet_id_))
1171 << "test failed for subnet id " << row.subnet_id_;
1172 }
1173 }
1174 }
1175
1176 /// @brief Retrieves subnet[id].assigned-addresses statistics for a subnet.
1177 ///
1178 /// @param subnet_id Identifier of a subnet for which statistics should be
1179 /// retrieved.
1180 /// @return Number of assigned addresses for a subnet.
getStatsAssignedAddresses(const SubnetID & subnet_id) const1181 int64_t getStatsAssignedAddresses(const SubnetID& subnet_id) const {
1182 // Retrieve statistics name, e.g. subnet[1234].assigned-addresses.
1183 const std::string stats_name = StatsMgr::generateName("subnet", subnet_id, "assigned-addresses");
1184 // Top element is a map with a subnet[id].assigned-addresses parameter.
1185 ConstElementPtr top_element = StatsMgr::instance().get(stats_name);
1186 if (top_element && (top_element->getType() == Element::map)) {
1187 // It contains two lists (nested).
1188 ConstElementPtr first_list = top_element->get(stats_name);
1189 if (first_list && (first_list->getType() == Element::list) &&
1190 (first_list->size() > 0)) {
1191 // Get the nested list which should have two elements, of which first
1192 // is the statistics value we're looking for.
1193 ConstElementPtr second_list = first_list->get(0);
1194 if (second_list && (second_list->getType() == Element::list)) {
1195 ConstElementPtr addresses_element = second_list->get(0);
1196 if (addresses_element && (addresses_element->getType() == Element::integer)) {
1197 return (addresses_element->intValue());
1198 }
1199 }
1200 }
1201 }
1202
1203 // Statistics invalid or not found.
1204 return (0);
1205 }
1206
1207 /// @brief Launches specific operation and verifies lease statistics before and
1208 /// after this operation.
1209 ///
1210 /// @param operation Operation to be launched.
testAssigned(const std::function<void ()> & operation)1211 void testAssigned(const std::function<void()>& operation) {
1212 ASSERT_NO_FATAL_FAILURE(verifyAssignedStats());
1213 operation();
1214 ASSERT_NO_FATAL_FAILURE(verifyAssignedStats());
1215 }
1216
1217 /// @brief Check precedence.
1218 ///
1219 /// @param config the configuration.
1220 /// @param ns_address expected name server address.
testPrecedence(const std::string & config,const std::string & ns_address)1221 void testPrecedence(const std::string& config, const std::string& ns_address) {
1222 // Create client and set MAC address to the one that has a reservation.
1223 Dhcp4Client client(Dhcp4Client::SELECTING);
1224 client.setIfaceName("eth1");
1225 client.setIfaceIndex(ETH1_INDEX);
1226 client.setHWAddress("aa:bb:cc:dd:ee:ff");
1227 // Request domain-name-servers.
1228 client.requestOptions(DHO_DOMAIN_NAME_SERVERS);
1229
1230 // Create server configuration.
1231 configure(config, *client.getServer());
1232
1233 // Perform a DORA.
1234 doDORA(client, "192.0.2.28", "192.0.2.28");
1235
1236 // Check response.
1237 Pkt4Ptr resp = client.getContext().response_;
1238 ASSERT_TRUE(resp);
1239 EXPECT_EQ(DHCPACK, resp->getType());
1240 EXPECT_EQ("192.0.2.28", resp->getYiaddr().toText());
1241
1242 // Check domain-name-servers option.
1243 OptionPtr opt = resp->getOption(DHO_DOMAIN_NAME_SERVERS);
1244 ASSERT_TRUE(opt);
1245 Option4AddrLstPtr servers =
1246 boost::dynamic_pointer_cast<Option4AddrLst>(opt);
1247 ASSERT_TRUE(servers);
1248 auto addrs = servers->getAddresses();
1249 ASSERT_EQ(1, addrs.size());
1250 EXPECT_EQ(ns_address, addrs[0].toText());
1251 }
1252
1253 /// @brief returns authoritative flag as JSON string
1254 /// @param f value to be specified (default, true or false)
auth(AuthoritativeFlag f)1255 string auth(AuthoritativeFlag f) {
1256 switch (f) {
1257 case AUTH_DEFAULT:
1258 return ("");
1259 case AUTH_YES:
1260 return (" \"authoritative\": true,");
1261 break;
1262 case AUTH_NO:
1263 return (" \"authoritative\": false,");
1264 }
1265 return ("");
1266 }
1267
1268 /// @brief generates Config file with specified authoritative flag values
1269 ///
1270 /// The config file has the same structure:
1271 /// - 1 shared network with 2 subnets (global authoritative flag)
1272 /// - first subnet: authoritative (subnet1 flag here)
1273 /// - second subnet: authoritative (subnet2 flag here)
1274 ///
1275 /// @param global governs presence/value of global authoritative flag
1276 /// @param subnet1 governs presence/value of authoritative flag in subnet1
1277 /// @param subnet2 governs presence/value of authoritative flag in subnet2
generateAuthConfig(AuthoritativeFlag global,AuthoritativeFlag subnet1,AuthoritativeFlag subnet2)1278 string generateAuthConfig(AuthoritativeFlag global, AuthoritativeFlag subnet1,
1279 AuthoritativeFlag subnet2) {
1280 string cfg = "{"
1281 " \"interfaces-config\": {"
1282 " \"interfaces\": [ \"*\" ]"
1283 " },"
1284 " \"valid-lifetime\": 600,"
1285 " \"shared-networks\": ["
1286 " {"
1287 " \"name\": \"frog\","
1288 " \"comment\": \"example\",";
1289 cfg += auth(global);
1290 cfg +=
1291 " \"subnet4\": ["
1292 " {"
1293 " \"subnet\": \"192.0.2.0/26\","
1294 " \"id\": 10,";
1295 cfg += auth(subnet1);
1296
1297 cfg +=
1298 " \"pools\": ["
1299 " {"
1300 " \"pool\": \"192.0.2.63 - 192.0.2.63\""
1301 " }"
1302 " ]"
1303 " },"
1304 " {"
1305 " \"subnet\": \"10.0.0.0/24\","
1306 " \"id\": 100,";
1307 cfg += auth(subnet2);
1308 cfg +=
1309 " \"pools\": ["
1310 " {"
1311 " \"pool\": \"10.0.0.16 - 10.0.0.16\""
1312 " }"
1313 " ]"
1314 " }"
1315 " ]"
1316 " }"
1317 " ]"
1318 "}";
1319
1320 return (cfg);
1321 }
1322
1323 /// @brief Destructor.
~Dhcpv4SharedNetworkTest()1324 virtual ~Dhcpv4SharedNetworkTest() {
1325 StatsMgr::instance().removeAll();
1326 }
1327
1328 /// @brief Interface Manager's fake configuration control.
1329 IfaceMgrTestConfig iface_mgr_test_config_;
1330 };
1331
1332 // Check user-context parsing
TEST_F(Dhcpv4SharedNetworkTest,parse)1333 TEST_F(Dhcpv4SharedNetworkTest, parse) {
1334 // Create client
1335 Dhcp4Client client1(Dhcp4Client::SELECTING);
1336
1337 // Don't use configure from utils
1338 Parser4Context ctx;
1339 ConstElementPtr json;
1340 ASSERT_NO_THROW(json = parseDHCP4(NETWORKS_CONFIG[0], true));
1341 ConstElementPtr status;
1342 disableIfacesReDetect(json);
1343 EXPECT_NO_THROW(status = configureDhcp4Server(*client1.getServer(), json));
1344 ASSERT_TRUE(status);
1345 int rcode;
1346 ConstElementPtr comment = config::parseAnswer(rcode, status);
1347 ASSERT_EQ(0, rcode) << " comment: " << comment->stringValue();
1348 ASSERT_NO_THROW( {
1349 CfgDbAccessPtr cfg_db = CfgMgr::instance().getStagingCfg()->getCfgDbAccess();
1350 cfg_db->setAppendedParameters("universe=4");
1351 cfg_db->createManagers();
1352 } );
1353 CfgMgr::instance().commit();
1354
1355 CfgSharedNetworks4Ptr cfg = CfgMgr::instance().getCurrentCfg()->getCfgSharedNetworks4();
1356 SharedNetwork4Ptr network = cfg->getByName("frog");
1357 ConstElementPtr context = network->getContext();
1358 ASSERT_TRUE(context);
1359 ASSERT_EQ(1, context->size());
1360 ASSERT_TRUE(context->get("comment"));
1361 EXPECT_EQ("\"example\"", context->get("comment")->str());
1362 }
1363
1364 // Running out of addresses within a subnet in a shared network.
TEST_F(Dhcpv4SharedNetworkTest,poolInSharedNetworkShortage)1365 TEST_F(Dhcpv4SharedNetworkTest, poolInSharedNetworkShortage) {
1366 // Create client #1
1367 Dhcp4Client client1(Dhcp4Client::SELECTING);
1368 client1.setIfaceName("eth1");
1369 client1.setIfaceIndex(ETH1_INDEX);
1370
1371 // Configure the server with one shared network including two subnets and
1372 // one subnet outside of the shared network.
1373 configure(NETWORKS_CONFIG[0], *client1.getServer());
1374
1375 // Client #1 requests an address in first subnet within a shared network.
1376 // We'll send a hint of 192.0.2.63 and expect to get it.
1377 testAssigned([this, &client1]() {
1378 doDORA(client1, "192.0.2.63", "192.0.2.63");
1379 });
1380
1381 // Client #2 The second client will request a lease and should be assigned
1382 // an address from the second subnet.
1383 Dhcp4Client client2(client1.getServer(), Dhcp4Client::SELECTING);
1384 client2.setIfaceName("eth1");
1385 client2.setIfaceIndex(ETH1_INDEX);
1386 testAssigned([this, &client2]() {
1387 doDORA(client2, "10.0.0.16");
1388 });
1389
1390 // Client #3. It sends DHCPDISCOVER which should be dropped by the server because
1391 // the server has no more addresses to assign.
1392 Dhcp4Client client3(client1.getServer(), Dhcp4Client::SELECTING);
1393 client3.setIfaceName("eth1");
1394 client3.setIfaceIndex(ETH1_INDEX);
1395 testAssigned([&client3]() {
1396 ASSERT_NO_THROW(client3.doDiscover());
1397 Pkt4Ptr resp3 = client3.getContext().response_;
1398 ASSERT_FALSE(resp3);
1399 });
1400
1401 // Client #3 should be assigned an address if subnet 3 is selected for this client.
1402 client3.setIfaceName("eth0");
1403 client3.setIfaceIndex(ETH0_INDEX);
1404 testAssigned([this, &client3]() {
1405 doDORA(client3, "192.0.2.65");
1406 });
1407
1408 // Client #1 should be able to renew its address.
1409 client1.setState(Dhcp4Client::RENEWING);
1410 testAssigned([this, &client1]() {
1411 doRequest(client1, "192.0.2.63");
1412 });
1413
1414 // Client #2 should be able to renew its address.
1415 client2.setState(Dhcp4Client::RENEWING);
1416 testAssigned([this, &client2]() {
1417 doRequest(client2, "10.0.0.16");
1418 });
1419 }
1420
1421 // Returning client sends 4-way exchange.
TEST_F(Dhcpv4SharedNetworkTest,returningClientStartsOver)1422 TEST_F(Dhcpv4SharedNetworkTest, returningClientStartsOver) {
1423 // Create client.
1424 Dhcp4Client client(Dhcp4Client::SELECTING);
1425 client.setIfaceName("eth1");
1426 client.setIfaceIndex(ETH1_INDEX);
1427 client.includeClientId("01:02:03:04");
1428
1429 // Configure the server with one shared network including two subnets and
1430 // one subnet outside of the shared network.
1431 configure(NETWORKS_CONFIG[0], *client.getServer());
1432
1433 // Client requests an address in first subnet within a shared network.
1434 // We'll send a hint of 192.0.2.63 and expect to get it.
1435 testAssigned([this, &client]() {
1436 doDORA(client, "192.0.2.63", "192.0.2.63");
1437 });
1438
1439
1440 // The client reboots and performs 4-way exchange again without a hint.
1441 // It should be assigned the same (existing) lease.
1442 testAssigned([this, &client]() {
1443 doDORA(client, "192.0.2.63");
1444 });
1445 }
1446
1447 // Shared network is selected based on giaddr value (relay specified
1448 // on shared network level)
TEST_F(Dhcpv4SharedNetworkTest,sharedNetworkSelectedByRelay1)1449 TEST_F(Dhcpv4SharedNetworkTest, sharedNetworkSelectedByRelay1) {
1450 // Create client #1. This is a relayed client which is using relay
1451 // address matching configured shared network.
1452 Dhcp4Client client1(Dhcp4Client::SELECTING);
1453 client1.useRelay(true, IOAddress("192.3.5.6"), IOAddress("10.0.0.2"));
1454
1455 // Configure the server with one shared network and one subnet outside of the
1456 // shared network.
1457 configure(NETWORKS_CONFIG[1], *client1.getServer());
1458
1459 // Client #1 should be assigned an address from shared network.
1460 testAssigned([this, &client1] {
1461 doDORA(client1, "192.0.2.63", "192.0.2.63");
1462 });
1463
1464 // Create client #2. This is a relayed client which is using relay
1465 // address matching subnet outside of the shared network.
1466 Dhcp4Client client2(client1.getServer(), Dhcp4Client::SELECTING);
1467 client2.useRelay(true, IOAddress("192.1.2.3"), IOAddress("10.0.0.3"));
1468 testAssigned([this, &client2] {
1469 doDORA(client2, "192.0.2.65", "192.0.2.63");
1470 });
1471 }
1472
1473 // Shared network is selected based on giaddr value (relay specified
1474 // on subnet in shared network level). Note the relay ip is specified
1475 // on the shared network level, but its value is overridden on subnet
1476 // level.
TEST_F(Dhcpv4SharedNetworkTest,sharedNetworkSelectedByRelay2)1477 TEST_F(Dhcpv4SharedNetworkTest, sharedNetworkSelectedByRelay2) {
1478 // Create client #1. This is a relayed client which is using relay
1479 // address matching configured subnet 1 in shared network.
1480 Dhcp4Client client1(Dhcp4Client::SELECTING);
1481 client1.useRelay(true, IOAddress("192.1.1.1"), IOAddress("10.0.0.2"));
1482
1483 // Configure the server with one shared network and one subnet outside of the
1484 // shared network.
1485 configure(NETWORKS_CONFIG[14], *client1.getServer());
1486
1487 // Client #1 should be assigned an address from shared network.
1488 testAssigned([this, &client1] {
1489 doDORA(client1, "192.0.2.63");
1490 });
1491
1492 // Create client #2. This is a relayed client which is using relay
1493 // address that is used for subnet 2 in the shared network.
1494 Dhcp4Client client2(client1.getServer(), Dhcp4Client::SELECTING);
1495 client2.useRelay(true, IOAddress("192.2.2.2"), IOAddress("10.0.0.3"));
1496 testAssigned([this, &client2] {
1497 doDORA(client2, "10.0.0.16");
1498 });
1499
1500 // Create client #3. This is a relayed client which is using relay
1501 // address matching subnet outside of the shared network.
1502 Dhcp4Client client3(client1.getServer(), Dhcp4Client::SELECTING);
1503 client3.useRelay(true, IOAddress("192.3.3.3"), IOAddress("10.0.0.4"));
1504 testAssigned([this, &client3] {
1505 doDORA(client3, "192.0.2.65");
1506 });
1507 }
1508
1509 // Providing a hint for any address belonging to a shared network.
TEST_F(Dhcpv4SharedNetworkTest,hintWithinSharedNetwork)1510 TEST_F(Dhcpv4SharedNetworkTest, hintWithinSharedNetwork) {
1511 // Create client.
1512 Dhcp4Client client(Dhcp4Client::SELECTING);
1513 client.setIfaceName("eth1");
1514 client.setIfaceIndex(ETH1_INDEX);
1515
1516 // Configure the server with one shared network including two subnets and
1517 // one subnet outside of the shared network.
1518 configure(NETWORKS_CONFIG[0], *client.getServer());
1519
1520 // Provide a hint to an existing address within first subnet. This address
1521 // should be offered out of this subnet.
1522 testAssigned([this, &client] {
1523 doDiscover(client, "192.0.2.63", "192.0.2.63");
1524 });
1525
1526 // Similarly, we should be offered an address from another subnet within
1527 // the same shared network when we ask for it.
1528 testAssigned([this, &client] {
1529 doDiscover(client, "10.0.0.16", "10.0.0.16");
1530 });
1531
1532 // Asking for an address that is not in address pool should result in getting
1533 // an address from one of the subnets, but generally hard to tell from which one.
1534 testAssigned([&client] {
1535 ASSERT_NO_THROW(client.doDiscover(boost::shared_ptr<IOAddress>(new IOAddress("10.0.0.23"))));
1536 });
1537
1538 Pkt4Ptr resp = client.getContext().response_;
1539 ASSERT_TRUE(resp);
1540
1541 // We expect one of the two addresses available in this shared network.
1542 EXPECT_EQ(DHCPOFFER, resp->getType());
1543 if ((resp->getYiaddr() != IOAddress("10.0.0.16")) &&
1544 (resp->getYiaddr() != IOAddress("192.0.2.63"))) {
1545 ADD_FAILURE() << "Unexpected address offered by the server " << resp->getYiaddr();
1546 }
1547 }
1548
1549 // Access to a subnet within shared network is restricted by client
1550 // classification.
TEST_F(Dhcpv4SharedNetworkTest,subnetInSharedNetworkSelectedByClass)1551 TEST_F(Dhcpv4SharedNetworkTest, subnetInSharedNetworkSelectedByClass) {
1552 // Create client #1
1553 Dhcp4Client client1(Dhcp4Client::SELECTING);
1554 client1.useRelay(true, IOAddress("192.3.5.6"));
1555
1556 // Configure the server with one shared network including two subnets in
1557 // it. The access to one of the subnets is restricted by client classification.
1558 configure(NETWORKS_CONFIG[2], *client1.getServer());
1559
1560 // Client #1 requests an address in the restricted subnet but can't be assigned
1561 // this address because the client doesn't belong to a certain class.
1562 testAssigned([this, &client1] {
1563 doDORA(client1, "10.0.0.16", "192.0.2.63");
1564 });
1565
1566 // Release the lease that the client has got, because we'll need this address
1567 // further in the test.
1568 testAssigned([&client1] {
1569 ASSERT_NO_THROW(client1.doRelease());
1570 });
1571
1572 // Add option93 which would cause the client to be classified as "a-devices".
1573 OptionPtr option93(new OptionUint16(Option::V4, 93, 0x0001));
1574 client1.addExtraOption(option93);
1575
1576 // This time, the allocation of the address provided as hint should be successful.
1577 testAssigned([this, &client1] {
1578 doDORA(client1, "192.0.2.63", "192.0.2.63");
1579 });
1580
1581 // Client 2 should be assigned an address from the unrestricted subnet.
1582 Dhcp4Client client2(client1.getServer(), Dhcp4Client::SELECTING);
1583 client2.useRelay(true, IOAddress("192.3.5.6"));
1584 client2.setIfaceName("eth1");
1585 client2.setIfaceIndex(ETH1_INDEX);
1586 testAssigned([this, &client2] {
1587 doDORA(client2, "10.0.0.16");
1588 });
1589
1590 // Now, let's reconfigure the server to also apply restrictions on the
1591 // subnet to which client2 now belongs.
1592 configure(NETWORKS_CONFIG[3], *client1.getServer());
1593
1594 // The client should be refused to renew the lease because it doesn't belong
1595 // to "b-devices" class.
1596 client2.setState(Dhcp4Client::RENEWING);
1597 testAssigned([this, &client2] {
1598 doRequest(client2, "");
1599 });
1600
1601 // If we add option93 with a value matching this class, the lease should
1602 // get renewed.
1603 OptionPtr option93_bis(new OptionUint16(Option::V4, 93, 0x0002));
1604 client2.addExtraOption(option93_bis);
1605
1606 testAssigned([this, &client2] {
1607 doRequest(client2, "10.0.0.16");
1608 });
1609 }
1610
1611 // IPv4 address reservation exists in one of the subnets within
1612 // shared network. This test also verifies that conflict resolution for
1613 // reserved addresses is working properly in case of shared networks.
TEST_F(Dhcpv4SharedNetworkTest,reservationInSharedNetwork)1614 TEST_F(Dhcpv4SharedNetworkTest, reservationInSharedNetwork) {
1615 // Create client #1. Explicitly set client's MAC address to the one that
1616 // has a reservation in the first subnet within shared network.
1617 Dhcp4Client client1(Dhcp4Client::SELECTING);
1618 client1.useRelay(true, IOAddress("192.3.5.6"));
1619 client1.setHWAddress("11:22:33:44:55:66");
1620
1621 // Create server configuration with a shared network including two subnets. There
1622 // is an IP address reservation in each subnet for two respective clients.
1623 configure(NETWORKS_CONFIG[4], *client1.getServer());
1624
1625 // Client #1 should get his reserved address from the second subnet.
1626 testAssigned([this, &client1] {
1627 doDORA(client1, "10.0.0.29", "192.0.2.28");
1628 });
1629
1630 // Create client #2
1631 Dhcp4Client client2(client1.getServer(), Dhcp4Client::SELECTING);
1632 client2.useRelay(true, IOAddress("192.3.5.6"));
1633 client2.setHWAddress("aa:bb:cc:dd:ee:ff");
1634
1635 // Client #2 should get its reserved address from the first subnet.
1636 testAssigned([this, &client2] {
1637 doDORA(client2, "192.0.2.28");
1638 });
1639
1640 // Reconfigure the server. Now, the first client gets second client's
1641 // reservation and vice versa.
1642 configure(NETWORKS_CONFIG[5], *client1.getServer());
1643
1644 // The first client is trying to renew the lease and should get a DHCPNAK.
1645 client1.setState(Dhcp4Client::RENEWING);
1646 testAssigned([this, &client1] {
1647 doRequest(client1, "");
1648 });
1649
1650 // Similarly, the second client is trying to renew the lease and should
1651 // get a DHCPNAK.
1652 client2.setState(Dhcp4Client::RENEWING);
1653 testAssigned([this, &client2] {
1654 doRequest(client2, "");
1655 });
1656
1657 // But the client should get a lease, if it does 4-way exchange. However, it
1658 // must not get any of the reserved addresses because one of them is reserved
1659 // for another client and for another one there is a valid lease.
1660 client1.setState(Dhcp4Client::SELECTING);
1661 testAssigned([this, &client1] {
1662 ASSERT_NO_THROW(doDORA(client1, "192.0.2.29", "192.0.2.29"));
1663 });
1664 Pkt4Ptr resp1 = client1.getContext().response_;
1665 ASSERT_TRUE(resp1);
1666 EXPECT_EQ(DHCPACK, resp1->getType());
1667 EXPECT_NE("10.0.0.29", resp1->getYiaddr().toText());
1668 EXPECT_NE("192.0.2.28", resp1->getYiaddr().toText());
1669
1670 // Client #2 is now doing 4-way exchange and should get its newly reserved
1671 // address, released by the 4-way transaction of client 1.
1672 client2.setState(Dhcp4Client::SELECTING);
1673 testAssigned([this, &client2] {
1674 doDORA(client2, "10.0.0.29");
1675 });
1676
1677 // Same for client #1.
1678 client1.setState(Dhcp4Client::SELECTING);
1679 testAssigned([this, &client1] {
1680 doDORA(client1, "192.0.2.28");
1681 });
1682 }
1683
1684 // Reserved address can't be assigned as long as access to a subnet is
1685 // restricted by classification.
TEST_F(Dhcpv4SharedNetworkTest,reservationAccessRestrictedByClass)1686 TEST_F(Dhcpv4SharedNetworkTest, reservationAccessRestrictedByClass) {
1687 // Create a client and set explicit MAC address for which there is a reservation
1688 // in first subnet belonging to a shared network.
1689 Dhcp4Client client(Dhcp4Client::SELECTING);
1690 client.useRelay(true, IOAddress("192.3.5.6"));
1691 client.setHWAddress("aa:bb:cc:dd:ee:ff");
1692
1693 // Create configuration with a shared network including two subnets. Access to
1694 // one of the subnets is restricted by client classification.
1695 configure(NETWORKS_CONFIG[6], *client.getServer());
1696
1697 // Assigned address should be allocated from the second subnet, because the
1698 // client doesn't belong to the "a-devices" class.
1699 testAssigned([this, &client] {
1700 doDORA(client, "10.0.0.16");
1701 });
1702
1703 // Add option 93 which would cause the client to be classified as "a-devices".
1704 OptionPtr option93(new OptionUint16(Option::V4, 93, 0x0001));
1705 client.addExtraOption(option93);
1706
1707 // Client renews its lease and should get DHCPNAK because this client now belongs
1708 // to the "a-devices" class and can be assigned a reserved address instead.
1709 client.setState(Dhcp4Client::RENEWING);
1710 testAssigned([this, &client] {
1711 doRequest(client, "");
1712 });
1713
1714 // Perform 4-way exchange again. It should be assigned a reserved address this time.
1715 client.setState(Dhcp4Client::SELECTING);
1716 testAssigned([this, &client] {
1717 doDORA(client, "192.0.2.28");
1718 });
1719 }
1720
1721 // Some options are specified on the shared subnet level, some on the
1722 // subnets level.
TEST_F(Dhcpv4SharedNetworkTest,optionsDerivation)1723 TEST_F(Dhcpv4SharedNetworkTest, optionsDerivation) {
1724 // Client #1.
1725 Dhcp4Client client1(Dhcp4Client::SELECTING);
1726 client1.setIfaceName("eth1");
1727 client1.setIfaceIndex(ETH1_INDEX);
1728 client1.requestOptions(DHO_LOG_SERVERS, DHO_COOKIE_SERVERS, DHO_DOMAIN_NAME_SERVERS);
1729
1730 configure(NETWORKS_CONFIG[7], *client1.getServer());
1731
1732 // Client #1 belongs to shared network. By providing a hint "192.0.2.63" we force
1733 // the server to select first subnet within the shared network for this client.
1734 doDORA(client1, "192.0.2.63", "192.0.2.63");
1735
1736 // This option is specified at the global level.
1737 ASSERT_EQ(1, client1.config_.log_servers_.size());
1738 EXPECT_EQ("1.2.3.4", client1.config_.log_servers_[0].toText());
1739
1740 // This option is specified on the subnet level.
1741 ASSERT_EQ(1, client1.config_.routers_.size());
1742 EXPECT_EQ("192.0.2.5", client1.config_.routers_[0].toText());
1743
1744 // This option is specified on the shared network level and the subnet level.
1745 // The instance on the subnet level should take precedence.
1746 ASSERT_EQ(1, client1.config_.quotes_servers_.size());
1747 EXPECT_EQ("10.5.4.3", client1.config_.quotes_servers_[0].toText());
1748
1749 // This option is only specified on the shared network level and should be
1750 // inherited by all subnets within this network.
1751 ASSERT_EQ(1, client1.config_.dns_servers_.size());
1752 EXPECT_EQ("10.1.2.3", client1.config_.dns_servers_[0].toText());
1753
1754 // Client #2.
1755 Dhcp4Client client2(Dhcp4Client::SELECTING);
1756 client2.setIfaceName("eth1");
1757 client2.setIfaceIndex(ETH1_INDEX);
1758 client2.requestOptions(DHO_LOG_SERVERS, DHO_COOKIE_SERVERS, DHO_DOMAIN_NAME_SERVERS);
1759
1760 // Request an address from the second subnet within the shared network.
1761 doDORA(client2, "10.0.0.16", "10.0.0.16");
1762
1763 // This option is specified at the global level.
1764 ASSERT_EQ(1, client2.config_.log_servers_.size());
1765 EXPECT_EQ("1.2.3.4", client2.config_.log_servers_[0].toText());
1766
1767 // This option is only specified on the shared network level and should be
1768 // inherited by all subnets within this network.
1769 ASSERT_EQ(1, client2.config_.quotes_servers_.size());
1770 EXPECT_EQ("10.6.5.4", client2.config_.quotes_servers_[0].toText());
1771
1772 // This option is only specified on the shared network level and should be
1773 // inherited by all subnets within this network.
1774 ASSERT_EQ(1, client2.config_.dns_servers_.size());
1775 EXPECT_EQ("10.1.2.3", client2.config_.dns_servers_[0].toText());
1776
1777 // Client #3.
1778 Dhcp4Client client3(Dhcp4Client::SELECTING);
1779 client3.setIfaceName("eth0");
1780 client3.setIfaceIndex(ETH0_INDEX);
1781 client3.requestOptions(DHO_LOG_SERVERS, DHO_COOKIE_SERVERS, DHO_DOMAIN_NAME_SERVERS);
1782
1783 // Client 3 should get an address from the subnet defined outside of the shared network.
1784 doDORA(client3, "192.0.2.65");
1785
1786 // This option is specified at the global level.
1787 ASSERT_EQ(1, client3.config_.log_servers_.size());
1788 EXPECT_EQ("1.2.3.4", client3.config_.log_servers_[0].toText());
1789
1790 // This option is specified on the subnet level.
1791 ASSERT_EQ(1, client3.config_.quotes_servers_.size());
1792 EXPECT_EQ("10.1.1.1", client3.config_.quotes_servers_[0].toText());
1793
1794 // This option is only specified on the shared network level and thus it should
1795 // not be returned to this client, because the client doesn't belong to the
1796 // shared network.
1797 ASSERT_EQ(0, client3.config_.dns_servers_.size());
1798 }
1799
1800 // Client has a lease in a subnet within shared network.
TEST_F(Dhcpv4SharedNetworkTest,initReboot)1801 TEST_F(Dhcpv4SharedNetworkTest, initReboot) {
1802 // Create client #1.
1803 Dhcp4Client client1(Dhcp4Client::SELECTING);
1804 client1.setIfaceName("eth1");
1805 client1.setIfaceIndex(ETH1_INDEX);
1806
1807 configure(NETWORKS_CONFIG[0], *client1.getServer());
1808
1809 // Perform 4-way exchange to obtain a lease. The client should get the lease from
1810 // the second subnet.
1811 testAssigned([this, &client1] {
1812 doDORA(client1, "10.0.0.16", "10.0.0.16");
1813 });
1814
1815 // The client1 transitions to INIT-REBOOT state in which the client1 remembers the
1816 // lease and sends DHCPREQUEST to all servers (server id) is not specified. If
1817 // the server doesn't know the client1 (doesn't have its lease), it should
1818 // drop the request. We want to make sure that the server responds (resp1) regardless
1819 // of the subnet from which the lease has been allocated.
1820 client1.setState(Dhcp4Client::INIT_REBOOT);
1821 testAssigned([this, &client1] {
1822 doRequest(client1, "10.0.0.16");
1823 });
1824
1825 // Create client #2.
1826 Dhcp4Client client2(client1.getServer(), Dhcp4Client::SELECTING);
1827 client2.setIfaceName("eth1");
1828 client2.setIfaceIndex(ETH1_INDEX);
1829
1830 // Let's make sure that the behavior is the same for the other subnet within the
1831 // same shared network.
1832 testAssigned([this, &client2] {
1833 doDORA(client2, "192.0.2.63", "192.0.2.63");
1834 });
1835
1836 // The client2 transitions to INIT-REBOOT state in which the client2 remembers the
1837 // lease and sends DHCPREQUEST to all servers (server id) is not specified. If
1838 // the server doesn't know the client2 (doesn't have its lease), it should
1839 // drop the request. We want to make sure that the server responds (resp2) regardless
1840 // of the subnet from which the lease has been allocated.
1841 client2.setState(Dhcp4Client::INIT_REBOOT);
1842 testAssigned([this, &client2] {
1843 doRequest(client2, "192.0.2.63");
1844 });
1845 }
1846
1847 // Host reservations include hostname, next server and client class.
TEST_F(Dhcpv4SharedNetworkTest,variousFieldsInReservation)1848 TEST_F(Dhcpv4SharedNetworkTest, variousFieldsInReservation) {
1849 // Create client.
1850 Dhcp4Client client(Dhcp4Client::SELECTING);
1851 client.setIfaceName("eth1");
1852 client.setIfaceIndex(ETH1_INDEX);
1853 client.setHWAddress("11:22:33:44:55:66");
1854
1855 // Include hostname to force the server to return hostname to
1856 // the client.
1857 client.includeHostname("my.example.org");
1858
1859 // Configure the server with a shared network including two subnets.
1860 // The client has address/hostname reservation in the second subnet.
1861 configure(NETWORKS_CONFIG[10], *client.getServer());
1862
1863 // Perform 4-way exchange.
1864 testAssigned([&client] {
1865 ASSERT_NO_THROW(client.doDORA());
1866 });
1867 Pkt4Ptr resp = client.getContext().response_;
1868 ASSERT_TRUE(resp);
1869 EXPECT_EQ(DHCPACK, resp->getType());
1870 EXPECT_EQ("10.0.0.29", resp->getYiaddr().toText());
1871
1872 // The client should get a hostname from the reservation, rather than
1873 // the hostname it has sent to the server. If there is a logic error,
1874 // the server would use the first subnet from the shared network to
1875 // assign the hostname. This subnet has no reservation so it would
1876 // return the same hostname that the client has sent. We expect
1877 // that the hostname being sent is the one that is incldued in the
1878 // reservations.
1879 OptionStringPtr hostname;
1880 hostname = boost::dynamic_pointer_cast<OptionString>(resp->getOption(DHO_HOST_NAME));
1881 ASSERT_TRUE(hostname);
1882 EXPECT_EQ("test.example.org", hostname->getValue());
1883
1884 // The next server value should also be set according to the settings
1885 // in host reservations.
1886 EXPECT_EQ("10.10.10.10", resp->getSiaddr().toText());
1887
1888 // The boot-file-name value should be derived from the client class
1889 // based on the static class reservations.
1890 const std::string expected_fname = "/dev/null";
1891 const OptionBuffer fname = resp->getFile();
1892 const std::string converted_fname(fname.cbegin(),
1893 fname.cbegin() + expected_fname.size());
1894 EXPECT_EQ(expected_fname, converted_fname);
1895 }
1896
1897 // Different shared network is selected for different local interface.
TEST_F(Dhcpv4SharedNetworkTest,sharedNetworkSelectionByInterface)1898 TEST_F(Dhcpv4SharedNetworkTest, sharedNetworkSelectionByInterface) {
1899 // Create client #1. The server receives requests from this client
1900 // via interface eth1 and should assign shared network "frog" for
1901 // this client.
1902 Dhcp4Client client1(Dhcp4Client::SELECTING);
1903 client1.setIfaceName("eth1");
1904 client1.setIfaceIndex(ETH1_INDEX);
1905
1906 // Create server configuration with two shared networks selected
1907 // by the local interface: eth1 and eth0.
1908 configure(NETWORKS_CONFIG[8], *client1.getServer());
1909
1910 // Perform 4-way exchange.
1911 testAssigned([&client1] {
1912 ASSERT_NO_THROW(client1.doDORA());
1913 });
1914 Pkt4Ptr resp1 = client1.getContext().response_;
1915 ASSERT_TRUE(resp1);
1916 EXPECT_EQ(DHCPACK, resp1->getType());
1917 // The client should be assigned an address from the 192.0.2.X
1918 // address range.
1919 EXPECT_EQ("192.0.2", resp1->getYiaddr().toText().substr(0, 7));
1920
1921 // Create client #2 which requests are received on eth0.
1922 Dhcp4Client client2(client1.getServer(), Dhcp4Client::SELECTING);
1923 client2.setIfaceName("eth0");
1924 client2.setIfaceIndex(ETH0_INDEX);
1925
1926 // Perform 4-way exchange.
1927 testAssigned([&client2] {
1928 ASSERT_NO_THROW(client2.doDORA());
1929 });
1930 Pkt4Ptr resp2 = client2.getContext().response_;
1931 ASSERT_TRUE(resp2);
1932 EXPECT_EQ(DHCPACK, resp2->getType());
1933 // The client should be assigned an address from the 10.0.0.X
1934 // address range.
1935 EXPECT_EQ("10.0.0", resp2->getYiaddr().toText().substr(0, 6));
1936 }
1937
1938 // Different shared network is selected for different relay address.
TEST_F(Dhcpv4SharedNetworkTest,sharedNetworkSelectionByRelay)1939 TEST_F(Dhcpv4SharedNetworkTest, sharedNetworkSelectionByRelay) {
1940 // Create relayed client #1.
1941 Dhcp4Client client1(Dhcp4Client::SELECTING);
1942 client1.useRelay(true, IOAddress("10.1.2.3"));
1943
1944 // Create server configuration with two shared networks selected
1945 // by the relay address.
1946 configure(NETWORKS_CONFIG[9], *client1.getServer());
1947
1948 // Perform 4-way exchange.
1949 testAssigned([&client1] {
1950 ASSERT_NO_THROW(client1.doDORA());
1951 });
1952 Pkt4Ptr resp1 = client1.getContext().response_;
1953 ASSERT_TRUE(resp1);
1954 EXPECT_EQ(DHCPACK, resp1->getType());
1955 // The client should be assigned an address from the 192.0.2.X
1956 // address range.
1957 EXPECT_EQ("192.0.2", resp1->getYiaddr().toText().substr(0, 7));
1958
1959 // Create relayed client #2.
1960 Dhcp4Client client2(client1.getServer(), Dhcp4Client::SELECTING);
1961 client2.useRelay(true, IOAddress("192.1.2.3"));
1962
1963 // Perform 4-way exchange.
1964 testAssigned([&client2] {
1965 ASSERT_NO_THROW(client2.doDORA());
1966 });
1967 Pkt4Ptr resp2 = client2.getContext().response_;
1968 ASSERT_TRUE(resp2);
1969 EXPECT_EQ(DHCPACK, resp2->getType());
1970 // The client should be assigned an address from the 10.0.0.X
1971 // address range.
1972 EXPECT_EQ("10.0.0", resp2->getYiaddr().toText().substr(0, 6));
1973 }
1974
1975 // Client id matching gets disabled on the shared network level.
TEST_F(Dhcpv4SharedNetworkTest,matchClientId)1976 TEST_F(Dhcpv4SharedNetworkTest, matchClientId) {
1977 // Create client using client identifier besides MAC address.
1978 Dhcp4Client client(Dhcp4Client::SELECTING);
1979 client.includeClientId("01:02:03:04");
1980 client.setIfaceName("eth1");
1981 client.setIfaceIndex(ETH1_INDEX);
1982
1983 // Create server configuration with match-client-id value initially
1984 // set to true. The client should be allocated a lease and the
1985 // client identifier should be included in this lease.
1986 configure(NETWORKS_CONFIG[11], *client.getServer());
1987
1988 // Perform 4-way exchange.
1989 testAssigned([&client] {
1990 ASSERT_NO_THROW(client.doDORA());
1991 });
1992 Pkt4Ptr resp1 = client.getContext().response_;
1993 ASSERT_TRUE(resp1);
1994 ASSERT_EQ(DHCPACK, resp1->getType());
1995
1996 // Reconfigure the server and turn off client identifier matching
1997 // on the shared network level. The subnet from which the client
1998 // is allocated an address should derive the match-client-id value
1999 // and ignore the fact that the client identifier is not matching.
2000 configure(NETWORKS_CONFIG[12], *client.getServer());
2001
2002 client.includeClientId("01:01:01:01");
2003 client.setState(Dhcp4Client::RENEWING);
2004
2005 // Try to renew the lease with modified MAC address.
2006 testAssigned([&client] {
2007 ASSERT_NO_THROW(client.doRequest());
2008 });
2009 Pkt4Ptr resp2 = client.getContext().response_;
2010 ASSERT_TRUE(resp2);
2011 ASSERT_EQ(DHCPACK, resp2->getType());
2012
2013 // The lease should get renewed.
2014 EXPECT_EQ(resp2->getYiaddr().toText(), resp1->getYiaddr().toText());
2015 }
2016
2017 // Shared network is selected based on the client class specified.
TEST_F(Dhcpv4SharedNetworkTest,sharedNetworkSelectedByClass)2018 TEST_F(Dhcpv4SharedNetworkTest, sharedNetworkSelectedByClass) {
2019 // Create client #1.
2020 Dhcp4Client client1(Dhcp4Client::SELECTING);
2021 client1.setIfaceName("eth1");
2022 client1.setIfaceIndex(ETH1_INDEX);
2023
2024 // Add option93 which would cause the client1 to be classified as "b-devices".
2025 OptionPtr option93(new OptionUint16(Option::V4, 93, 0x0002));
2026 client1.addExtraOption(option93);
2027
2028 // Configure the server with two shared networks which can be accessed
2029 // by clients belonging to "a-devices" and "b-devices" classes
2030 // respectively.
2031 configure(NETWORKS_CONFIG[13], *client1.getServer());
2032
2033 // Simply send DHCPDISCOVER to avoid allocating a lease.
2034 testAssigned([&client1] {
2035 ASSERT_NO_THROW(client1.doDiscover());
2036 });
2037 Pkt4Ptr resp1 = client1.getContext().response_;
2038 ASSERT_TRUE(resp1);
2039 ASSERT_EQ(DHCPOFFER, resp1->getType());
2040 // The client should be offered a lease from the second shared network.
2041 EXPECT_EQ("10.0.0.63", resp1->getYiaddr().toText());
2042
2043 // Create another client which will belong to a different class.
2044 Dhcp4Client client2(client1.getServer(), Dhcp4Client::SELECTING);
2045 client2.setIfaceName("eth1");
2046 client2.setIfaceIndex(ETH1_INDEX);
2047
2048 // Add option93 which would cause the client1 to be classified as "a-devices".
2049 option93.reset(new OptionUint16(Option::V4, 93, 0x0001));
2050 client2.addExtraOption(option93);
2051
2052 // Send DHCPDISCOVER. There is no lease in the lease database so the
2053 // client should be offered a lease based on the client class selection.
2054 testAssigned([this, &client2] {
2055 doDiscover(client2, "192.0.2.63", "");
2056 });
2057 }
2058
2059 // This test verifies that custom server identifier can be specified for a
2060 // shared network.
TEST_F(Dhcpv4SharedNetworkTest,customServerIdentifier)2061 TEST_F(Dhcpv4SharedNetworkTest, customServerIdentifier) {
2062 Dhcp4Client client1(Dhcp4Client::SELECTING);
2063 client1.setIfaceName("eth1");
2064 client1.setIfaceIndex(ETH1_INDEX);
2065
2066 // Configure DHCP server.
2067 ASSERT_NO_THROW(configure(NETWORKS_CONFIG[15], *client1.getServer()));
2068
2069 testAssigned([&client1] {
2070 ASSERT_NO_THROW(client1.doDORA());
2071 });
2072
2073 // Make sure that the server responded.
2074 ASSERT_TRUE(client1.getContext().response_);
2075 Pkt4Ptr resp = client1.getContext().response_;
2076 // Make sure that the server has responded with DHCPACK.
2077 ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
2078 // The explicitly configured server identifier should take precedence
2079 // over generated server identifier.
2080 EXPECT_EQ("1.2.3.4", client1.config_.serverid_.toText());
2081
2082 // Create another client using different interface.
2083 Dhcp4Client client2(client1.getServer(), Dhcp4Client::SELECTING);
2084 client2.setIfaceName("eth0");
2085 client2.setIfaceIndex(ETH0_INDEX);
2086
2087 testAssigned([&client2] {
2088 ASSERT_NO_THROW(client2.doDORA());
2089 });
2090
2091 // Make sure that the server responded.
2092 ASSERT_TRUE(client2.getContext().response_);
2093 resp = client2.getContext().response_;
2094 // Make sure that the server has responded with DHCPACK.
2095 ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
2096 // The explicitly configured server identifier should take precedence
2097 // over generated server identifier.
2098 EXPECT_EQ("2.3.4.5", client2.config_.serverid_.toText());
2099 }
2100
2101 // Access to a pool within shared network is restricted by client
2102 // classification.
TEST_F(Dhcpv4SharedNetworkTest,poolInSharedNetworkSelectedByClass)2103 TEST_F(Dhcpv4SharedNetworkTest, poolInSharedNetworkSelectedByClass) {
2104 // Create client #1
2105 Dhcp4Client client1(Dhcp4Client::SELECTING);
2106 client1.setIfaceName("eth1");
2107 client1.setIfaceIndex(ETH1_INDEX);
2108
2109 // Configure the server with one shared network including one subnet and
2110 // in 2 pools in it. The access to one of the pools is restricted
2111 // by client classification.
2112 configure(NETWORKS_CONFIG[16], *client1.getServer());
2113
2114 // Client #1 requests an address in the restricted pool but can't be assigned
2115 // this address because the client doesn't belong to a certain class.
2116 testAssigned([this, &client1] {
2117 doDORA(client1, "192.0.2.100", "192.0.2.63");
2118 });
2119
2120 // Release the lease that the client has got, because we'll need this address
2121 // further in the test.
2122 testAssigned([&client1] {
2123 ASSERT_NO_THROW(client1.doRelease());
2124 });
2125
2126 // Add option93 which would cause the client to be classified as "a-devices".
2127 OptionPtr option93(new OptionUint16(Option::V4, 93, 0x0001));
2128 client1.addExtraOption(option93);
2129
2130 // This time, the allocation of the address provided as hint should be successful.
2131 testAssigned([this, &client1] {
2132 doDORA(client1, "192.0.2.63", "192.0.2.63");
2133 });
2134
2135 // Client 2 should be assigned an address from the unrestricted pool.
2136 Dhcp4Client client2(client1.getServer(), Dhcp4Client::SELECTING);
2137 client2.setIfaceName("eth1");
2138 client2.setIfaceIndex(ETH1_INDEX);
2139 testAssigned([this, &client2] {
2140 doDORA(client2, "192.0.2.100");
2141 });
2142
2143 // Now, let's reconfigure the server to also apply restrictions on the
2144 // pool to which client2 now belongs.
2145 configure(NETWORKS_CONFIG[17], *client1.getServer());
2146
2147 // The client should be refused to renew the lease because it doesn't belong
2148 // to "b-devices" class.
2149 client2.setState(Dhcp4Client::RENEWING);
2150 testAssigned([this, &client2] {
2151 doRequest(client2, "");
2152 });
2153
2154 // If we add option93 with a value matching this class, the lease should
2155 // get renewed.
2156 OptionPtr option93_bis(new OptionUint16(Option::V4, 93, 0x0002));
2157 client2.addExtraOption(option93_bis);
2158
2159 testAssigned([this, &client2] {
2160 doRequest(client2, "192.0.2.100");
2161 });
2162 }
2163
2164 // Access to a pool within plain subnet is restricted by client classification.
TEST_F(Dhcpv4SharedNetworkTest,poolInSubnetSelectedByClass)2165 TEST_F(Dhcpv4SharedNetworkTest, poolInSubnetSelectedByClass) {
2166 // Create client #1
2167 Dhcp4Client client1(Dhcp4Client::SELECTING);
2168 client1.setIfaceName("eth1");
2169 client1.setIfaceIndex(ETH1_INDEX);
2170
2171 // Configure the server with one plain subnet including two pools.
2172 // The access to one of the pools is restricted by client classification.
2173 configure(NETWORKS_CONFIG[18], *client1.getServer());
2174
2175 // Client #1 requests an address in the restricted pool but can't be assigned
2176 // this address because the client doesn't belong to a certain class.
2177 testAssigned([this, &client1] {
2178 doDORA(client1, "192.0.2.100", "192.0.2.63");
2179 });
2180
2181 // Release the lease that the client has got, because we'll need this address
2182 // further in the test.
2183 testAssigned([&client1] {
2184 ASSERT_NO_THROW(client1.doRelease());
2185 });
2186
2187 // Add option93 which would cause the client to be classified as "a-devices".
2188 OptionPtr option93(new OptionUint16(Option::V4, 93, 0x0001));
2189 client1.addExtraOption(option93);
2190
2191 // This time, the allocation of the address provided as hint should be successful.
2192 testAssigned([this, &client1] {
2193 doDORA(client1, "192.0.2.63", "192.0.2.63");
2194 });
2195
2196 // Client 2 should be assigned an address from the unrestricted pool.
2197 Dhcp4Client client2(client1.getServer(), Dhcp4Client::SELECTING);
2198 client2.setIfaceName("eth1");
2199 client2.setIfaceIndex(ETH1_INDEX);
2200 testAssigned([this, &client2] {
2201 doDORA(client2, "192.0.2.100");
2202 });
2203
2204 // Now, let's reconfigure the server to also apply restrictions on the
2205 // pool to which client2 now belongs.
2206 configure(NETWORKS_CONFIG[19], *client1.getServer());
2207
2208 // The client should be refused to renew the lease because it doesn't belong
2209 // to "b-devices" class.
2210 client2.setState(Dhcp4Client::RENEWING);
2211 testAssigned([this, &client2] {
2212 doRequest(client2, "");
2213 });
2214
2215 // If we add option93 with a value matching this class, the lease should
2216 // get renewed.
2217 OptionPtr option93_bis(new OptionUint16(Option::V4, 93, 0x0002));
2218 client2.addExtraOption(option93_bis);
2219
2220 testAssigned([this, &client2] {
2221 doRequest(client2, "192.0.2.100");
2222 });
2223 }
2224
2225 // Shared network is selected based on giaddr value (relay specified
2226 // on shared network level, but response is send to source address.
TEST_F(Dhcpv4SharedNetworkTest,sharedNetworkSendToSourceTestingModeEnabled)2227 TEST_F(Dhcpv4SharedNetworkTest, sharedNetworkSendToSourceTestingModeEnabled) {
2228 // Create client #1. This is a relayed client which is using relay
2229 // address matching configured shared network.
2230 // Source address is set to unrelated to configuration.
2231
2232 Dhcp4Client client1(Dhcp4Client::SELECTING);
2233 // Put Kea into testing mode.
2234 client1.getServer()->setSendResponsesToSource(true);
2235 client1.useRelay(true, IOAddress("192.3.5.6"), IOAddress("1.1.1.2"));
2236 // Configure the server with one shared network and one subnet outside of the
2237 // shared network.
2238 configure(NETWORKS_CONFIG[1], *client1.getServer());
2239 // Client #1 should be assigned an address from shared network.
2240 testAssigned([this, &client1] {
2241 doDORA(client1, "192.0.2.63", "192.0.2.63");
2242 });
2243
2244 // normally Kea would send packet to 192.3.5.6 but we want it get from
2245 // 1.1.1.2 in send to source testing mode but still with correctly
2246 // assigned address.
2247 Pkt4Ptr resp1 = client1.getContext().response_;
2248 EXPECT_EQ("1.1.1.2", resp1->getLocalAddr().toText());
2249
2250 // Create client #2. This is a relayed client which is using relay
2251 // address matching subnet outside of the shared network.
2252 Dhcp4Client client2(client1.getServer(), Dhcp4Client::SELECTING);
2253 client2.useRelay(true, IOAddress("192.1.2.3"), IOAddress("2.2.2.3"));
2254 testAssigned([this, &client2] {
2255 doDORA(client2, "192.0.2.65", "192.0.2.63");
2256 });
2257
2258 Pkt4Ptr resp2 = client2.getContext().response_;
2259 EXPECT_EQ("2.2.2.3", resp2->getLocalAddr().toText());
2260 // reset testing mode.
2261 client1.getServer()->setSendResponsesToSource(false);
2262 }
2263
2264 // Verify option processing precedence
2265 // Order is global < class < shared-network < subnet < pool < host reservation
TEST_F(Dhcpv4SharedNetworkTest,precedenceGlobal)2266 TEST_F(Dhcpv4SharedNetworkTest, precedenceGlobal) {
2267 const std::string config =
2268 "{"
2269 " \"interfaces-config\": {"
2270 " \"interfaces\": [ \"*\" ]"
2271 " },"
2272 " \"valid-lifetime\": 600,"
2273 " \"option-data\": ["
2274 " {"
2275 " \"name\": \"domain-name-servers\","
2276 " \"data\": \"192.0.2.1\""
2277 " }"
2278 " ],"
2279 " \"shared-networks\": ["
2280 " {"
2281 " \"name\": \"frog\","
2282 " \"interface\": \"eth1\","
2283 " \"subnet4\": ["
2284 " {"
2285 " \"subnet\": \"192.0.2.0/26\","
2286 " \"id\": 10,"
2287 " \"pools\": ["
2288 " {"
2289 " \"pool\": \"192.0.2.1 - 192.0.2.63\""
2290 " }"
2291 " ],"
2292 " \"reservations\": ["
2293 " {"
2294 " \"hw-address\": \"aa:bb:cc:dd:ee:ff\","
2295 " \"ip-address\": \"192.0.2.28\""
2296 " }"
2297 " ]"
2298 " }"
2299 " ]"
2300 " }"
2301 " ]"
2302 "}";
2303
2304 testPrecedence(config, "192.0.2.1");
2305 }
2306
2307 // Verify option processing precedence
2308 // Order is global < class < shared-network < subnet < pool < host reservation
TEST_F(Dhcpv4SharedNetworkTest,precedenceClass)2309 TEST_F(Dhcpv4SharedNetworkTest, precedenceClass) {
2310 const std::string config =
2311 "{"
2312 " \"interfaces-config\": {"
2313 " \"interfaces\": [ \"*\" ]"
2314 " },"
2315 " \"valid-lifetime\": 600,"
2316 " \"option-data\": ["
2317 " {"
2318 " \"name\": \"domain-name-servers\","
2319 " \"data\": \"192.0.2.1\""
2320 " }"
2321 " ],"
2322 " \"client-classes\": ["
2323 " {"
2324 " \"name\": \"alpha\","
2325 " \"test\": \"'' == ''\","
2326 " \"option-data\": ["
2327 " {"
2328 " \"name\": \"domain-name-servers\","
2329 " \"data\": \"192.0.2.2\""
2330 " }"
2331 " ]"
2332 " }"
2333 " ],"
2334 " \"shared-networks\": ["
2335 " {"
2336 " \"name\": \"frog\","
2337 " \"interface\": \"eth1\","
2338 " \"subnet4\": ["
2339 " {"
2340 " \"subnet\": \"192.0.2.0/26\","
2341 " \"id\": 10,"
2342 " \"pools\": ["
2343 " {"
2344 " \"pool\": \"192.0.2.1 - 192.0.2.63\""
2345 " }"
2346 " ],"
2347 " \"reservations\": ["
2348 " {"
2349 " \"hw-address\": \"aa:bb:cc:dd:ee:ff\","
2350 " \"ip-address\": \"192.0.2.28\""
2351 " }"
2352 " ]"
2353 " }"
2354 " ]"
2355 " }"
2356 " ]"
2357 "}";
2358
2359 testPrecedence(config, "192.0.2.2");
2360 }
2361
2362 // Verify option processing precedence
2363 // Order is global < class < shared-network < subnet < pool < host reservation
TEST_F(Dhcpv4SharedNetworkTest,precedenceClasses)2364 TEST_F(Dhcpv4SharedNetworkTest, precedenceClasses) {
2365 const std::string config =
2366 "{"
2367 " \"interfaces-config\": {"
2368 " \"interfaces\": [ \"*\" ]"
2369 " },"
2370 " \"valid-lifetime\": 600,"
2371 " \"option-data\": ["
2372 " {"
2373 " \"name\": \"domain-name-servers\","
2374 " \"data\": \"192.0.2.1\""
2375 " }"
2376 " ],"
2377 " \"client-classes\": ["
2378 " {"
2379 " \"name\": \"beta\","
2380 " \"test\": \"'' == ''\","
2381 " \"option-data\": ["
2382 " {"
2383 " \"name\": \"domain-name-servers\","
2384 " \"data\": \"192.0.2.2\""
2385 " }"
2386 " ]"
2387 " },"
2388 " {"
2389 " \"name\": \"alpha\","
2390 " \"test\": \"'' == ''\","
2391 " \"option-data\": ["
2392 " {"
2393 " \"name\": \"domain-name-servers\","
2394 " \"data\": \"192.0.2.3\""
2395 " }"
2396 " ]"
2397 " }"
2398 " ],"
2399 " \"shared-networks\": ["
2400 " {"
2401 " \"name\": \"frog\","
2402 " \"interface\": \"eth1\","
2403 " \"subnet4\": ["
2404 " {"
2405 " \"subnet\": \"192.0.2.0/26\","
2406 " \"id\": 10,"
2407 " \"pools\": ["
2408 " {"
2409 " \"pool\": \"192.0.2.1 - 192.0.2.63\""
2410 " }"
2411 " ],"
2412 " \"reservations\": ["
2413 " {"
2414 " \"hw-address\": \"aa:bb:cc:dd:ee:ff\","
2415 " \"ip-address\": \"192.0.2.28\""
2416 " }"
2417 " ]"
2418 " }"
2419 " ]"
2420 " }"
2421 " ]"
2422 "}";
2423
2424 // Class order is the insert order
2425 testPrecedence(config, "192.0.2.2");
2426 }
2427
2428 // Verify option processing precedence
2429 // Order is global < class < shared-network < subnet < pool < host reservation
TEST_F(Dhcpv4SharedNetworkTest,precedenceNetwork)2430 TEST_F(Dhcpv4SharedNetworkTest, precedenceNetwork) {
2431 const std::string config =
2432 "{"
2433 " \"interfaces-config\": {"
2434 " \"interfaces\": [ \"*\" ]"
2435 " },"
2436 " \"valid-lifetime\": 600,"
2437 " \"option-data\": ["
2438 " {"
2439 " \"name\": \"domain-name-servers\","
2440 " \"data\": \"192.0.2.1\""
2441 " }"
2442 " ],"
2443 " \"client-classes\": ["
2444 " {"
2445 " \"name\": \"alpha\","
2446 " \"test\": \"'' == ''\","
2447 " \"option-data\": ["
2448 " {"
2449 " \"name\": \"domain-name-servers\","
2450 " \"data\": \"192.0.2.2\""
2451 " }"
2452 " ]"
2453 " }"
2454 " ],"
2455 " \"shared-networks\": ["
2456 " {"
2457 " \"name\": \"frog\","
2458 " \"interface\": \"eth1\","
2459 " \"option-data\": ["
2460 " {"
2461 " \"name\": \"domain-name-servers\","
2462 " \"data\": \"192.0.2.3\""
2463 " }"
2464 " ],"
2465 " \"subnet4\": ["
2466 " {"
2467 " \"subnet\": \"192.0.2.0/26\","
2468 " \"id\": 10,"
2469 " \"pools\": ["
2470 " {"
2471 " \"pool\": \"192.0.2.1 - 192.0.2.63\""
2472 " }"
2473 " ],"
2474 " \"reservations\": ["
2475 " {"
2476 " \"hw-address\": \"aa:bb:cc:dd:ee:ff\","
2477 " \"ip-address\": \"192.0.2.28\""
2478 " }"
2479 " ]"
2480 " }"
2481 " ]"
2482 " }"
2483 " ]"
2484 "}";
2485
2486 testPrecedence(config, "192.0.2.3");
2487 }
2488
2489 // Verify option processing precedence
2490 // Order is global < class < shared-network < subnet < pool < host reservation
TEST_F(Dhcpv4SharedNetworkTest,precedenceSubnet)2491 TEST_F(Dhcpv4SharedNetworkTest, precedenceSubnet) {
2492 const std::string config =
2493 "{"
2494 " \"interfaces-config\": {"
2495 " \"interfaces\": [ \"*\" ]"
2496 " },"
2497 " \"valid-lifetime\": 600,"
2498 " \"option-data\": ["
2499 " {"
2500 " \"name\": \"domain-name-servers\","
2501 " \"data\": \"192.0.2.1\""
2502 " }"
2503 " ],"
2504 " \"client-classes\": ["
2505 " {"
2506 " \"name\": \"alpha\","
2507 " \"test\": \"'' == ''\","
2508 " \"option-data\": ["
2509 " {"
2510 " \"name\": \"domain-name-servers\","
2511 " \"data\": \"192.0.2.2\""
2512 " }"
2513 " ]"
2514 " }"
2515 " ],"
2516 " \"shared-networks\": ["
2517 " {"
2518 " \"name\": \"frog\","
2519 " \"interface\": \"eth1\","
2520 " \"option-data\": ["
2521 " {"
2522 " \"name\": \"domain-name-servers\","
2523 " \"data\": \"192.0.2.3\""
2524 " }"
2525 " ],"
2526 " \"subnet4\": ["
2527 " {"
2528 " \"subnet\": \"192.0.2.0/26\","
2529 " \"id\": 10,"
2530 " \"option-data\": ["
2531 " {"
2532 " \"name\": \"domain-name-servers\","
2533 " \"data\": \"192.0.2.4\""
2534 " }"
2535 " ],"
2536 " \"pools\": ["
2537 " {"
2538 " \"pool\": \"192.0.2.1 - 192.0.2.63\""
2539 " }"
2540 " ],"
2541 " \"reservations\": ["
2542 " {"
2543 " \"hw-address\": \"aa:bb:cc:dd:ee:ff\","
2544 " \"ip-address\": \"192.0.2.28\""
2545 " }"
2546 " ]"
2547 " }"
2548 " ]"
2549 " }"
2550 " ]"
2551 "}";
2552
2553 testPrecedence(config, "192.0.2.4");
2554 }
2555
2556 // Verify option processing precedence
2557 // Order is global < class < shared-network < subnet < pool < host reservation
TEST_F(Dhcpv4SharedNetworkTest,precedencePool)2558 TEST_F(Dhcpv4SharedNetworkTest, precedencePool) {
2559 const std::string config =
2560 "{"
2561 " \"interfaces-config\": {"
2562 " \"interfaces\": [ \"*\" ]"
2563 " },"
2564 " \"valid-lifetime\": 600,"
2565 " \"option-data\": ["
2566 " {"
2567 " \"name\": \"domain-name-servers\","
2568 " \"data\": \"192.0.2.1\""
2569 " }"
2570 " ],"
2571 " \"client-classes\": ["
2572 " {"
2573 " \"name\": \"alpha\","
2574 " \"test\": \"'' == ''\","
2575 " \"option-data\": ["
2576 " {"
2577 " \"name\": \"domain-name-servers\","
2578 " \"data\": \"192.0.2.2\""
2579 " }"
2580 " ]"
2581 " }"
2582 " ],"
2583 " \"shared-networks\": ["
2584 " {"
2585 " \"name\": \"frog\","
2586 " \"interface\": \"eth1\","
2587 " \"option-data\": ["
2588 " {"
2589 " \"name\": \"domain-name-servers\","
2590 " \"data\": \"192.0.2.3\""
2591 " }"
2592 " ],"
2593 " \"subnet4\": ["
2594 " {"
2595 " \"subnet\": \"192.0.2.0/26\","
2596 " \"id\": 10,"
2597 " \"option-data\": ["
2598 " {"
2599 " \"name\": \"domain-name-servers\","
2600 " \"data\": \"192.0.2.4\""
2601 " }"
2602 " ],"
2603 " \"pools\": ["
2604 " {"
2605 " \"pool\": \"192.0.2.1 - 192.0.2.63\","
2606 " \"option-data\": ["
2607 " {"
2608 " \"name\": \"domain-name-servers\","
2609 " \"data\": \"192.0.2.5\""
2610 " }"
2611 " ]"
2612 " }"
2613 " ],"
2614 " \"reservations\": ["
2615 " {"
2616 " \"hw-address\": \"aa:bb:cc:dd:ee:ff\","
2617 " \"ip-address\": \"192.0.2.28\""
2618 " }"
2619 " ]"
2620 " }"
2621 " ]"
2622 " }"
2623 " ]"
2624 "}";
2625
2626 testPrecedence(config, "192.0.2.5");
2627 }
2628
2629 // Verify option processing precedence
2630 // Order is global < class < shared-network < subnet < pool < host reservation
TEST_F(Dhcpv4SharedNetworkTest,precedenceReservation)2631 TEST_F(Dhcpv4SharedNetworkTest, precedenceReservation) {
2632 const std::string config =
2633 "{"
2634 " \"interfaces-config\": {"
2635 " \"interfaces\": [ \"*\" ]"
2636 " },"
2637 " \"valid-lifetime\": 600,"
2638 " \"option-data\": ["
2639 " {"
2640 " \"name\": \"domain-name-servers\","
2641 " \"data\": \"192.0.2.1\""
2642 " }"
2643 " ],"
2644 " \"client-classes\": ["
2645 " {"
2646 " \"name\": \"alpha\","
2647 " \"test\": \"'' == ''\","
2648 " \"option-data\": ["
2649 " {"
2650 " \"name\": \"domain-name-servers\","
2651 " \"data\": \"192.0.2.2\""
2652 " }"
2653 " ]"
2654 " }"
2655 " ],"
2656 " \"shared-networks\": ["
2657 " {"
2658 " \"name\": \"frog\","
2659 " \"interface\": \"eth1\","
2660 " \"option-data\": ["
2661 " {"
2662 " \"name\": \"domain-name-servers\","
2663 " \"data\": \"192.0.2.3\""
2664 " }"
2665 " ],"
2666 " \"subnet4\": ["
2667 " {"
2668 " \"subnet\": \"192.0.2.0/26\","
2669 " \"id\": 10,"
2670 " \"option-data\": ["
2671 " {"
2672 " \"name\": \"domain-name-servers\","
2673 " \"data\": \"192.0.2.4\""
2674 " }"
2675 " ],"
2676 " \"pools\": ["
2677 " {"
2678 " \"pool\": \"192.0.2.1 - 192.0.2.63\","
2679 " \"option-data\": ["
2680 " {"
2681 " \"name\": \"domain-name-servers\","
2682 " \"data\": \"192.0.2.5\""
2683 " }"
2684 " ]"
2685 " }"
2686 " ],"
2687 " \"reservations\": ["
2688 " {"
2689 " \"hw-address\": \"aa:bb:cc:dd:ee:ff\","
2690 " \"ip-address\": \"192.0.2.28\","
2691 " \"option-data\": ["
2692 " {"
2693 " \"name\": \"domain-name-servers\","
2694 " \"data\": \"192.0.2.6\""
2695 " }"
2696 " ]"
2697 " }"
2698 " ]"
2699 " }"
2700 " ]"
2701 " }"
2702 " ]"
2703 "}";
2704
2705 testPrecedence(config, "192.0.2.6");
2706 }
2707
2708 // Verify authoritative sanitization.
2709 // This test generates many similar configs. They all have similar
2710 // structure:
2711 // - 1 shared-network (with possibly authoritative flag in it)
2712 // 2 subnets (with each possibly having its own authoritative flag)
2713 //
2714 // If the flag is specified on subnet-level, the value must match those
2715 // specified on subnet level.
TEST_F(Dhcpv4SharedNetworkTest,authoritative)2716 TEST_F(Dhcpv4SharedNetworkTest, authoritative) {
2717
2718 // Each scenario will be defined using those parameters.
2719 typedef struct scenario {
2720 bool exp_success;
2721 AuthoritativeFlag global;
2722 AuthoritativeFlag subnet1;
2723 AuthoritativeFlag subnet2;
2724 } scenario;
2725
2726 // We have the following scenarios. The default is no.
2727 // The only allowed combinations are those that end up with
2728 // having all three values (global, subnet1, subnet2) agree.
2729 scenario scenarios[] = {
2730 //result, global, subnet1, subnet2
2731 { true, AUTH_DEFAULT, AUTH_DEFAULT, AUTH_DEFAULT },
2732 { true, AUTH_YES, AUTH_DEFAULT, AUTH_DEFAULT },
2733 { true, AUTH_YES, AUTH_YES, AUTH_YES },
2734 { true, AUTH_NO, AUTH_DEFAULT, AUTH_DEFAULT },
2735 { true, AUTH_NO, AUTH_NO, AUTH_NO },
2736 { false, AUTH_DEFAULT, AUTH_YES, AUTH_NO },
2737 { false, AUTH_DEFAULT, AUTH_NO, AUTH_YES },
2738 { false, AUTH_DEFAULT, AUTH_YES, AUTH_YES },
2739 { false, AUTH_YES, AUTH_NO, AUTH_NO },
2740 { false, AUTH_YES, AUTH_DEFAULT, AUTH_NO },
2741 { false, AUTH_YES, AUTH_NO, AUTH_DEFAULT },
2742 { false, AUTH_YES, AUTH_NO, AUTH_NO },
2743 { false, AUTH_YES, AUTH_NO, AUTH_YES },
2744 { false, AUTH_YES, AUTH_YES, AUTH_NO }
2745 };
2746
2747 // Let's test them one by one
2748 int cnt = 0;
2749 for ( auto s : scenarios) {
2750 cnt++;
2751
2752 string cfg = generateAuthConfig(s.global, s.subnet1, s.subnet2);
2753
2754 // Create client and set MAC address to the one that has a reservation.
2755 Dhcp4Client client(Dhcp4Client::SELECTING);
2756
2757 stringstream tmp;
2758 tmp << "Testing scenario " << cnt;
2759 SCOPED_TRACE(tmp.str());
2760 // Create server configuration.
2761 auto result = configureWithStatus(cfg, *client.getServer(), true, s.exp_success? 0 : 1);
2762 if (s.exp_success) {
2763 EXPECT_EQ(result.first, 0) << result.second;
2764 } else {
2765 EXPECT_EQ(result.first, 1) << "Configuration expected to fail, but succeeded";
2766 }
2767 }
2768 }
2769
2770 } // end of anonymous namespace
2771