1 // Copyright (C) 2020-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 <dhcp/option.h>
9 #include <dhcp4/client_handler.h>
10 #include <dhcp4/tests/dhcp4_test_utils.h>
11 #include <stats/stats_mgr.h>
12 #include <util/multi_threading_mgr.h>
13 #include <unistd.h>
14
15 using namespace isc;
16 using namespace isc::dhcp;
17 using namespace isc::dhcp::test;
18 using namespace isc::stats;
19 using namespace isc::util;
20
21 namespace {
22
23 /// @brief Test fixture class for testing client handler.
24 class ClientHandleTest : public ::testing::Test {
25 public:
26
27 /// @brief Constructor.
28 ///
29 /// Creates the pkt4-receive-drop statistic.
ClientHandleTest()30 ClientHandleTest() : called1_(false), called2_(false), called3_(false) {
31 MultiThreadingMgr::instance().apply(false, 0, 0);
32 StatsMgr::instance().setValue("pkt4-receive-drop", static_cast<int64_t>(0));
33 }
34
35 /// @brief Destructor.
36 ///
37 /// Removes statistics.
~ClientHandleTest()38 ~ClientHandleTest() {
39 MultiThreadingMgr::instance().apply(false, 0, 0);
40 StatsMgr::instance().removeAll();
41 }
42
43 /// @brief Generates a client-id option.
44 ///
45 /// (from dhcp4_test_utils.h)
46 ///
47 /// @return A random client-id option.
generateClientId(uint8_t base=100)48 OptionPtr generateClientId(uint8_t base = 100) {
49 const size_t len = 32;
50 OptionBuffer duid(len);
51 for (size_t i = 0; i < len; ++i) {
52 duid[i] = base + i;
53 }
54 return (OptionPtr(new Option(Option::V4, DHO_DHCP_CLIENT_IDENTIFIER, duid)));
55 }
56
57 /// @brief Generates a hardware address.
58 ///
59 /// (from dhcp4_test_utils.h)
60 ///
61 /// @return A random hardware address.
generateHWAddr(uint8_t base=50)62 HWAddrPtr generateHWAddr(uint8_t base = 50) {
63 const uint16_t hw_type = 6;
64 const size_t len = 6;
65 OptionBuffer mac(len);
66 for (size_t i = 0; i < len; ++i) {
67 mac[i] = base + i;
68 }
69 return (HWAddrPtr(new HWAddr(mac, hw_type)));
70 }
71
72 /// @brief Check statistics.
73 ///
74 /// @param bumped True if pkt4-receive-drop should have been bumped by one,
75 /// false otherwise.
checkStat(bool bumped)76 void checkStat(bool bumped) {
77 ObservationPtr obs = StatsMgr::instance().getObservation("pkt4-receive-drop");
78 ASSERT_TRUE(obs);
79 if (bumped) {
80 EXPECT_EQ(1, obs->getInteger().first);
81 } else {
82 EXPECT_EQ(0, obs->getInteger().first);
83 }
84 }
85
86 /// @brief Waits for pending continuations.
waitForThreads()87 void waitForThreads() {
88 MultiThreadingMgr::instance().getThreadPool().wait(3);
89 }
90
91 /// @brief Set called1_ to true.
setCalled1()92 void setCalled1() {
93 called1_ = true;
94 }
95
96 /// @brief Set called2_ to true.
setCalled2()97 void setCalled2() {
98 called2_ = true;
99 }
100
101 /// @brief Set called3_ to true.
setCalled3()102 void setCalled3() {
103 called3_ = true;
104 }
105
106 /// @brief The called flag number 1.
107 bool called1_;
108
109 /// @brief The called flag number 2.
110 bool called2_;
111
112 /// @brief The called flag number 3.
113 bool called3_;
114 };
115
116 // Verifies behavior with empty block.
TEST_F(ClientHandleTest,empty)117 TEST_F(ClientHandleTest, empty) {
118 try {
119 // Get a client handler.
120 ClientHandler client_handler;
121 } catch (const std::exception& ex) {
122 ADD_FAILURE() << "unexpected exception: " << ex.what();
123 }
124 checkStat(false);
125 }
126
127 // Verifies behavior with one query.
TEST_F(ClientHandleTest,oneQuery)128 TEST_F(ClientHandleTest, oneQuery) {
129 // Get a query.
130 Pkt4Ptr dis(new Pkt4(DHCPDISCOVER, 1234));
131 dis->addOption(generateClientId());
132 dis->setHWAddr(generateHWAddr());
133
134 try {
135 // Get a client handler.
136 ClientHandler client_handler;
137
138 // Try to lock it.
139 bool duplicate = false;
140 EXPECT_NO_THROW(duplicate = !client_handler.tryLock(dis));
141
142 // Should return false (no duplicate).
143 EXPECT_FALSE(duplicate);
144 } catch (const std::exception& ex) {
145 ADD_FAILURE() << "unexpected exception: " << ex.what();
146 }
147 checkStat(false);
148 }
149
150 // Verifies behavior with two queries for the same client (id).
TEST_F(ClientHandleTest,sharedQueriesById)151 TEST_F(ClientHandleTest, sharedQueriesById) {
152 // Get two queries.
153 Pkt4Ptr dis(new Pkt4(DHCPDISCOVER, 1234));
154 Pkt4Ptr req(new Pkt4(DHCPREQUEST, 2345));
155 OptionPtr client_id = generateClientId();
156 // Same client ID: same client.
157 dis->addOption(client_id);
158 req->addOption(client_id);
159 dis->setHWAddr(generateHWAddr());
160 req->setHWAddr(generateHWAddr(55));
161
162 try {
163 // Get a client handler.
164 ClientHandler client_handler;
165
166 // Try to lock it with the discover.
167 bool duplicate = false;
168 EXPECT_NO_THROW(duplicate = !client_handler.tryLock(dis));
169
170 // Should return false (no duplicate).
171 EXPECT_FALSE(duplicate);
172
173 // Get a second client handler.
174 ClientHandler client_handler2;
175
176 // Try to lock it with a request.
177 EXPECT_NO_THROW(duplicate = !client_handler2.tryLock(req));
178
179 // Should return true (race with the duplicate).
180 EXPECT_TRUE(duplicate);
181 } catch (const std::exception& ex) {
182 ADD_FAILURE() << "unexpected exception: " << ex.what();
183 }
184 checkStat(true);
185 }
186
187 // Verifies behavior with two queries for the same client (hw).
TEST_F(ClientHandleTest,sharedQueriesByHWAddr)188 TEST_F(ClientHandleTest, sharedQueriesByHWAddr) {
189 // Get two queries.
190 Pkt4Ptr dis(new Pkt4(DHCPDISCOVER, 1234));
191 Pkt4Ptr req(new Pkt4(DHCPREQUEST, 2345));
192 dis->addOption(generateClientId());
193 req->addOption(generateClientId(111));
194 HWAddrPtr hwaddr = generateHWAddr();
195 // Same hardware address: same client.
196 dis->setHWAddr(hwaddr);
197 req->setHWAddr(hwaddr);
198
199 try {
200 // Get a client handler.
201 ClientHandler client_handler;
202
203 // Try to lock it with the discover.
204 bool duplicate = false;
205 EXPECT_NO_THROW(duplicate = !client_handler.tryLock(dis));
206
207 // Should return false (no duplicate).
208 EXPECT_FALSE(duplicate);
209
210 // Get a second client handler.
211 ClientHandler client_handler2;
212
213 // Try to lock it with a request.
214 EXPECT_NO_THROW(duplicate = !client_handler2.tryLock(req));
215
216 // Should return true (race with the duplicate).
217 EXPECT_TRUE(duplicate);
218 } catch (const std::exception& ex) {
219 ADD_FAILURE() << "unexpected exception: " << ex.what();
220 }
221 checkStat(true);
222 }
223
224 // Verifies behavior with two queries for the same client (hw only).
TEST_F(ClientHandleTest,sharedQueriesByHWAddrOnly)225 TEST_F(ClientHandleTest, sharedQueriesByHWAddrOnly) {
226 // Get two queries.
227 Pkt4Ptr dis(new Pkt4(DHCPDISCOVER, 1234));
228 Pkt4Ptr req(new Pkt4(DHCPREQUEST, 2345));
229 // No client ID.
230 HWAddrPtr hwaddr = generateHWAddr();
231 // Same hardware address: same client.
232 dis->setHWAddr(hwaddr);
233 req->setHWAddr(hwaddr);
234
235 try {
236 // Get a client handler.
237 ClientHandler client_handler;
238
239 // Try to lock it with the discover.
240 bool duplicate = false;
241 EXPECT_NO_THROW(duplicate = !client_handler.tryLock(dis));
242
243 // Should return false (no duplicate).
244 EXPECT_FALSE(duplicate);
245
246 // Get a second client handler.
247 ClientHandler client_handler2;
248
249 // Try to lock it with a request.
250 EXPECT_NO_THROW(duplicate = !client_handler2.tryLock(req));
251
252 // Should return true (race with the duplicate).
253 EXPECT_TRUE(duplicate);
254 } catch (const std::exception& ex) {
255 ADD_FAILURE() << "unexpected exception: " << ex.what();
256 }
257 checkStat(true);
258 }
259
260 // Verifies behavior with a sequence of two queries.
TEST_F(ClientHandleTest,sequence)261 TEST_F(ClientHandleTest, sequence) {
262 // Get two queries.
263 Pkt4Ptr dis(new Pkt4(DHCPDISCOVER, 1234));
264 Pkt4Ptr req(new Pkt4(DHCPREQUEST, 2345));
265 OptionPtr client_id = generateClientId();
266 // Same client ID: same client.
267 dis->addOption(client_id);
268 req->addOption(client_id);
269 HWAddrPtr hwaddr = generateHWAddr();
270 // Same hardware address: same client.
271 dis->setHWAddr(hwaddr);
272 req->setHWAddr(hwaddr);
273
274 try {
275 // Get a client handler.
276 ClientHandler client_handler;
277
278 // Try to lock it with the discover.
279 bool duplicate = false;
280 EXPECT_NO_THROW(duplicate = !client_handler.tryLock(dis));
281
282 // Should return false (no duplicate).
283 EXPECT_FALSE(duplicate);
284 } catch (const std::exception& ex) {
285 ADD_FAILURE() << "unexpected exception: " << ex.what();
286 }
287
288 // As it is a different block the lock was released.
289
290 try {
291 // Get a second client handler.
292 ClientHandler client_handler2;
293
294 // Try to lock it with a request.
295 bool duplicate = false;
296 EXPECT_NO_THROW(duplicate = !client_handler2.tryLock(req));
297
298 // Should return false (no duplicate).
299 EXPECT_FALSE(duplicate);
300 } catch (const std::exception& ex) {
301 ADD_FAILURE() << "unexpected exception: " << ex.what();
302 }
303 checkStat(false);
304 }
305
306 // Verifies behavior with different clients.
TEST_F(ClientHandleTest,notSharedQueries)307 TEST_F(ClientHandleTest, notSharedQueries) {
308 // Get two queries.
309 Pkt4Ptr dis(new Pkt4(DHCPDISCOVER, 1234));
310 Pkt4Ptr req(new Pkt4(DHCPREQUEST, 2345));
311 OptionPtr client_id = generateClientId();
312 OptionPtr client_id2 = generateClientId(111);
313 HWAddrPtr hwaddr = generateHWAddr();
314 HWAddrPtr hwaddr1 = generateHWAddr(55);
315 // Different client ID and hardware address: different client.
316 dis->addOption(client_id);
317 req->addOption(client_id2);
318 dis->setHWAddr(hwaddr);
319 req->setHWAddr(hwaddr1);
320
321 try {
322 // Get a client handler.
323 ClientHandler client_handler;
324
325 // Try to lock it with the discover.
326 bool duplicate = false;
327 EXPECT_NO_THROW(duplicate = !client_handler.tryLock(dis));
328
329 // Should return false (no duplicate).
330 EXPECT_FALSE(duplicate);
331
332 // Get a second client handler.
333 ClientHandler client_handler2;
334
335 // Try to lock it with a request.
336 EXPECT_NO_THROW(duplicate = !client_handler2.tryLock(req));
337
338 // Should return false (no duplicate).
339 EXPECT_FALSE(duplicate);
340 } catch (const std::exception& ex) {
341 ADD_FAILURE() << "unexpected exception: " << ex.what();
342 }
343 checkStat(false);
344 }
345
346 // Verifies behavior without client ID nor hardware address.
TEST_F(ClientHandleTest,noClientIdHWAddr)347 TEST_F(ClientHandleTest, noClientIdHWAddr) {
348 // Get two queries.
349 Pkt4Ptr dis(new Pkt4(DHCPDISCOVER, 1234));
350 Pkt4Ptr req(new Pkt4(DHCPREQUEST, 2345));
351 // No client id nor hardware address: nothing to recognize the client.
352
353 try {
354 // Get a client handler.
355 ClientHandler client_handler;
356
357 // Try to lock it with the discover.
358 bool duplicate = false;
359 EXPECT_NO_THROW(duplicate = !client_handler.tryLock(dis));
360
361 // Should return false (no duplicate).
362 EXPECT_FALSE(duplicate);
363
364 // Get a second client handler.
365 ClientHandler client_handler2;
366
367 // Try to lock it with a request.
368 EXPECT_NO_THROW(duplicate = !client_handler2.tryLock(req));
369
370 // Should return false (no duplicate).
371 EXPECT_FALSE(duplicate);
372 } catch (const std::exception& ex) {
373 ADD_FAILURE() << "unexpected exception: " << ex.what();
374 }
375 checkStat(false);
376 }
377
378 // Verifies the query is required.
TEST_F(ClientHandleTest,noQuery)379 TEST_F(ClientHandleTest, noQuery) {
380 Pkt4Ptr no_pkt;
381
382 try {
383 // Get a client handler.
384 ClientHandler client_handler;
385
386 EXPECT_THROW(client_handler.tryLock(no_pkt), InvalidParameter);
387 } catch (const std::exception& ex) {
388 ADD_FAILURE() << "unexpected exception: " << ex.what();
389 }
390 }
391
392 // Verifies that double tryLock call fails (id only).
TEST_F(ClientHandleTest,doubleTryLockById)393 TEST_F(ClientHandleTest, doubleTryLockById) {
394 // Get a query.
395 Pkt4Ptr dis(new Pkt4(DHCPDISCOVER, 1234));
396 dis->addOption(generateClientId());
397
398 try {
399 // Get a client handler.
400 ClientHandler client_handler;
401
402 // Try to lock it.
403 bool duplicate = false;
404 EXPECT_NO_THROW(duplicate = !client_handler.tryLock(dis));
405
406 // Should return false (no duplicate).
407 EXPECT_FALSE(duplicate);
408
409 // Try to lock a second time.
410 EXPECT_THROW(client_handler.tryLock(dis), Unexpected);
411 } catch (const std::exception& ex) {
412 ADD_FAILURE() << "unexpected exception: " << ex.what();
413 }
414 }
415
416 // Verifies that double tryLock call fails (hw only).
TEST_F(ClientHandleTest,doubleTryLockByHWAddr)417 TEST_F(ClientHandleTest, doubleTryLockByHWAddr) {
418 // Get a query without a client ID.
419 Pkt4Ptr dis(new Pkt4(DHCPDISCOVER, 1234));
420 dis->setHWAddr(generateHWAddr());
421
422 try {
423 // Get a client handler.
424 ClientHandler client_handler;
425
426 // Try to lock it.
427 bool duplicate = false;
428 EXPECT_NO_THROW(duplicate = !client_handler.tryLock(dis));
429
430 // Should return false (no duplicate).
431 EXPECT_FALSE(duplicate);
432
433 // Try to lock a second time.
434 EXPECT_THROW(client_handler.tryLock(dis), Unexpected);
435 } catch (const std::exception& ex) {
436 ADD_FAILURE() << "unexpected exception: " << ex.what();
437 }
438 }
439
440 // Cannot verifies that empty client ID fails because getClientId() handles
441 // this condition and replaces it by no client ID.
442
443 // Verifies behavior with two queries for the same client and
444 // multi-threading, by client id option version.
TEST_F(ClientHandleTest,serializeTwoQueriesById)445 TEST_F(ClientHandleTest, serializeTwoQueriesById) {
446 // Get two queries.
447 Pkt4Ptr dis(new Pkt4(DHCPDISCOVER, 1234));
448 Pkt4Ptr req(new Pkt4(DHCPREQUEST, 2345));
449 OptionPtr client_id = generateClientId();
450 // Same client ID: same client.
451 dis->addOption(client_id);
452 req->addOption(client_id);
453 dis->setHWAddr(generateHWAddr());
454 req->setHWAddr(generateHWAddr(55));
455
456 // Start multi-threading.
457 EXPECT_NO_THROW(MultiThreadingMgr::instance().apply(true, 1, 0));
458
459 try {
460 // Get a client handler.
461 ClientHandler client_handler;
462
463 // Create a continuation.
464 ContinuationPtr cont1 =
465 makeContinuation(std::bind(&ClientHandleTest::setCalled1, this));
466
467 // Try to lock it with the discover.
468 bool duplicate = false;
469 EXPECT_NO_THROW(duplicate = !client_handler.tryLock(dis, cont1));
470
471 // Should return false (no duplicate).
472 EXPECT_FALSE(duplicate);
473
474 // Get a second client handler.
475 ClientHandler client_handler2;
476
477 // Create a continuation.
478 ContinuationPtr cont2 =
479 makeContinuation(std::bind(&ClientHandleTest::setCalled2, this));
480
481 // Try to lock it with a request.
482 EXPECT_NO_THROW(duplicate = !client_handler2.tryLock(req, cont2));
483
484 // Should return true (race with the duplicate).
485 EXPECT_TRUE(duplicate);
486 } catch (const std::exception& ex) {
487 ADD_FAILURE() << "unexpected exception: " << ex.what();
488 }
489
490 // Give the second continuation a chance.
491 waitForThreads();
492
493 // Force multi-threading to stop;
494 MultiThreadingCriticalSection cs;
495
496 checkStat(false);
497 EXPECT_FALSE(called1_);
498 EXPECT_TRUE(called2_);
499 }
500
501 // Verifies behavior with two queries for the same client and
502 // multi-threading, by hardware address version.
TEST_F(ClientHandleTest,serializeTwoQueriesByHWAddr)503 TEST_F(ClientHandleTest, serializeTwoQueriesByHWAddr) {
504 // Get two queries.
505 Pkt4Ptr dis(new Pkt4(DHCPDISCOVER, 1234));
506 Pkt4Ptr req(new Pkt4(DHCPREQUEST, 2345));
507 dis->addOption(generateClientId());
508 req->addOption(generateClientId(111));
509 HWAddrPtr hwaddr = generateHWAddr();
510 // Same hardware address: same client.
511 dis->setHWAddr(hwaddr);
512 req->setHWAddr(hwaddr);
513
514 // Start multi-threading.
515 EXPECT_NO_THROW(MultiThreadingMgr::instance().apply(true, 1, 0));
516
517 try {
518 // Get a client handler.
519 ClientHandler client_handler;
520
521 // Create a continuation.
522 ContinuationPtr cont1 =
523 makeContinuation(std::bind(&ClientHandleTest::setCalled1, this));
524
525 // Try to lock it with the discover.
526 bool duplicate = false;
527 EXPECT_NO_THROW(duplicate = !client_handler.tryLock(dis, cont1));
528
529 // Should return false (no duplicate).
530 EXPECT_FALSE(duplicate);
531
532 // Get a second client handler.
533 ClientHandler client_handler2;
534
535 // Create a continuation.
536 ContinuationPtr cont2 =
537 makeContinuation(std::bind(&ClientHandleTest::setCalled2, this));
538
539 // Try to lock it with a request.
540 EXPECT_NO_THROW(duplicate = !client_handler2.tryLock(req, cont2));
541
542 // Should return true (race with the duplicate).
543 EXPECT_TRUE(duplicate);
544 } catch (const std::exception& ex) {
545 ADD_FAILURE() << "unexpected exception: " << ex.what();
546 }
547
548 // Give the second continuation a chance.
549 waitForThreads();
550
551 // Force multi-threading to stop;
552 MultiThreadingCriticalSection cs;
553
554 checkStat(false);
555 EXPECT_FALSE(called1_);
556 EXPECT_TRUE(called2_);
557 }
558
559 // Verifies behavior with two queries for the same client and multi-threading.
560 // Continuations are required for serialization. By client id option version.
TEST_F(ClientHandleTest,serializeNoContById)561 TEST_F(ClientHandleTest, serializeNoContById) {
562 // Get two queries.
563 Pkt4Ptr dis(new Pkt4(DHCPDISCOVER, 1234));
564 Pkt4Ptr req(new Pkt4(DHCPREQUEST, 2345));
565 OptionPtr client_id = generateClientId();
566 // Same client ID: same client.
567 dis->addOption(client_id);
568 req->addOption(client_id);
569 dis->setHWAddr(generateHWAddr());
570 req->setHWAddr(generateHWAddr(55));
571
572 // Start multi-threading.
573 EXPECT_NO_THROW(MultiThreadingMgr::instance().apply(true, 1, 0));
574
575 try {
576 // Get a client handler.
577 ClientHandler client_handler;
578
579 // Try to lock it with the discover.
580 bool duplicate = false;
581 EXPECT_NO_THROW(duplicate = !client_handler.tryLock(dis));
582
583 // Should return false (no duplicate).
584 EXPECT_FALSE(duplicate);
585
586 // Get a second client handler.
587 ClientHandler client_handler2;
588
589 // Try to lock it with a request.
590 EXPECT_NO_THROW(duplicate = !client_handler2.tryLock(req));
591
592 // Should return true (race with the duplicate).
593 EXPECT_TRUE(duplicate);
594 } catch (const std::exception& ex) {
595 ADD_FAILURE() << "unexpected exception: " << ex.what();
596 }
597
598 // Give the second continuation a chance even there is none...
599 waitForThreads();
600
601 // Force multi-threading to stop;
602 MultiThreadingCriticalSection cs;
603
604 checkStat(true);
605 }
606
607 // Verifies behavior with two queries for the same client and multi-threading.
608 // Continuations are required for serialization. By hardware address version.
TEST_F(ClientHandleTest,serializeNoContByHWAddr)609 TEST_F(ClientHandleTest, serializeNoContByHWAddr) {
610 // Get two queries.
611 Pkt4Ptr dis(new Pkt4(DHCPDISCOVER, 1234));
612 Pkt4Ptr req(new Pkt4(DHCPREQUEST, 2345));
613 dis->addOption(generateClientId());
614 req->addOption(generateClientId(111));
615 HWAddrPtr hwaddr = generateHWAddr();
616 // Same hardware address: same client.
617 dis->setHWAddr(hwaddr);
618 req->setHWAddr(hwaddr);
619
620 // Start multi-threading.
621 EXPECT_NO_THROW(MultiThreadingMgr::instance().apply(true, 1, 0));
622
623 try {
624 // Get a client handler.
625 ClientHandler client_handler;
626
627 // Try to lock it with the discover.
628 bool duplicate = false;
629 EXPECT_NO_THROW(duplicate = !client_handler.tryLock(dis));
630
631 // Should return false (no duplicate).
632 EXPECT_FALSE(duplicate);
633
634 // Get a second client handler.
635 ClientHandler client_handler2;
636
637 // Try to lock it with a request.
638 EXPECT_NO_THROW(duplicate = !client_handler2.tryLock(req));
639
640 // Should return true (race with the duplicate).
641 EXPECT_TRUE(duplicate);
642 } catch (const std::exception& ex) {
643 ADD_FAILURE() << "unexpected exception: " << ex.what();
644 }
645
646 // Give the second continuation a chance even there is none...
647 waitForThreads();
648
649 // Force multi-threading to stop;
650 MultiThreadingCriticalSection cs;
651
652 checkStat(true);
653 }
654
655 // Verifies behavior with three queries for the same client and
656 // multi-threading: currently we accept only two queries,
657 // a third one replaces second so we get the first (oldest) query and
658 // the last (newest) query when the client is busy.
659 // By client id option version.
TEST_F(ClientHandleTest,serializeThreeQueriesById)660 TEST_F(ClientHandleTest, serializeThreeQueriesById) {
661 // Get two queries.
662 Pkt4Ptr dis(new Pkt4(DHCPDISCOVER, 1234));
663 Pkt4Ptr req(new Pkt4(DHCPREQUEST, 2345));
664 Pkt4Ptr rel(new Pkt4(DHCPRELEASE, 3456));
665 OptionPtr client_id = generateClientId();
666 // Same client ID: same client.
667 dis->addOption(client_id);
668 req->addOption(client_id);
669 rel->addOption(client_id);
670 dis->setHWAddr(generateHWAddr());
671 req->setHWAddr(generateHWAddr(55));
672 rel->setHWAddr(generateHWAddr(66));
673
674 // Start multi-threading.
675 EXPECT_NO_THROW(MultiThreadingMgr::instance().apply(true, 1, 0));
676
677 try {
678 // Get a client handler.
679 ClientHandler client_handler;
680
681 // Create a continuation.
682 ContinuationPtr cont1 =
683 makeContinuation(std::bind(&ClientHandleTest::setCalled1, this));
684
685 // Try to lock it with the discover.
686 bool duplicate = false;
687 EXPECT_NO_THROW(duplicate = !client_handler.tryLock(dis, cont1));
688
689 // Should return false (no duplicate).
690 EXPECT_FALSE(duplicate);
691
692 // Get a second client handler.
693 ClientHandler client_handler2;
694
695 // Create a continuation.
696 ContinuationPtr cont2 =
697 makeContinuation(std::bind(&ClientHandleTest::setCalled2, this));
698
699 // Try to lock it with a request.
700 EXPECT_NO_THROW(duplicate = !client_handler2.tryLock(req, cont2));
701
702 // Should return true (race with the duplicate).
703 EXPECT_TRUE(duplicate);
704
705 // Get a third client handler.
706 ClientHandler client_handler3;
707
708 // Create a continuation.
709 ContinuationPtr cont3 =
710 makeContinuation(std::bind(&ClientHandleTest::setCalled3, this));
711
712 // Try to lock it with a release.
713 EXPECT_NO_THROW(duplicate = !client_handler3.tryLock(rel, cont3));
714
715 // Should return true (race with the duplicate).
716 EXPECT_TRUE(duplicate);
717 } catch (const std::exception& ex) {
718 ADD_FAILURE() << "unexpected exception: " << ex.what();
719 }
720
721 // Give the second continuation a chance.
722 waitForThreads();
723
724 // Force multi-threading to stop;
725 MultiThreadingCriticalSection cs;
726
727 checkStat(true);
728 EXPECT_FALSE(called1_);
729 EXPECT_FALSE(called2_);
730 EXPECT_TRUE(called3_);
731 }
732
733 // Verifies behavior with three queries for the same client and
734 // multi-threading: currently we accept only two queries,
735 // a third one replaces second so we get the first (oldest) query and
736 // the last (newest) query when the client is busy.
737 // By hardware address version.
TEST_F(ClientHandleTest,serializeThreeQueriesHWAddr)738 TEST_F(ClientHandleTest, serializeThreeQueriesHWAddr) {
739 // Get two queries.
740 Pkt4Ptr dis(new Pkt4(DHCPDISCOVER, 1234));
741 Pkt4Ptr req(new Pkt4(DHCPREQUEST, 2345));
742 Pkt4Ptr rel(new Pkt4(DHCPRELEASE, 3456));
743 dis->addOption(generateClientId());
744 req->addOption(generateClientId(111));
745 rel->addOption(generateClientId(99));
746 HWAddrPtr hwaddr = generateHWAddr();
747 // Same hardware address: same client.
748 dis->setHWAddr(hwaddr);
749 req->setHWAddr(hwaddr);
750 rel->setHWAddr(hwaddr);
751
752 // Start multi-threading.
753 EXPECT_NO_THROW(MultiThreadingMgr::instance().apply(true, 1, 0));
754
755 try {
756 // Get a client handler.
757 ClientHandler client_handler;
758
759 // Create a continuation.
760 ContinuationPtr cont1 =
761 makeContinuation(std::bind(&ClientHandleTest::setCalled1, this));
762
763 // Try to lock it with the discover.
764 bool duplicate = false;
765 EXPECT_NO_THROW(duplicate = !client_handler.tryLock(dis, cont1));
766
767 // Should return false (no duplicate).
768 EXPECT_FALSE(duplicate);
769
770 // Get a second client handler.
771 ClientHandler client_handler2;
772
773 // Create a continuation.
774 ContinuationPtr cont2 =
775 makeContinuation(std::bind(&ClientHandleTest::setCalled2, this));
776
777 // Try to lock it with a request.
778 EXPECT_NO_THROW(duplicate = !client_handler2.tryLock(req, cont2));
779
780 // Should return true (race with the duplicate).
781 EXPECT_TRUE(duplicate);
782
783 // Get a third client handler.
784 ClientHandler client_handler3;
785
786 // Create a continuation.
787 ContinuationPtr cont3 =
788 makeContinuation(std::bind(&ClientHandleTest::setCalled3, this));
789
790 // Try to lock it with a release.
791 EXPECT_NO_THROW(duplicate = !client_handler3.tryLock(rel, cont3));
792
793 // Should return true (race with the duplicate).
794 EXPECT_TRUE(duplicate);
795 } catch (const std::exception& ex) {
796 ADD_FAILURE() << "unexpected exception: " << ex.what();
797 }
798
799 // Give the second continuation a chance.
800 waitForThreads();
801
802 // Force multi-threading to stop;
803 MultiThreadingCriticalSection cs;
804
805 checkStat(true);
806 EXPECT_FALSE(called1_);
807 EXPECT_FALSE(called2_);
808 EXPECT_TRUE(called3_);
809 }
810
811 // Verifies behavior with three queries for the same client and
812 // multi-threading: currently we accept only two queries,
813 // a third one replaces second so we get the first (oldest) query and
814 // the last (newest) query when the client is busy.
815 // Mixed version (hardware address then client id option duplicates).
816 // Note the system is transitive because further races are detected
817 // when serialized packet processing is performed.
TEST_F(ClientHandleTest,serializeThreeQueriesMixed)818 TEST_F(ClientHandleTest, serializeThreeQueriesMixed) {
819 // Get two queries.
820 Pkt4Ptr dis(new Pkt4(DHCPDISCOVER, 1234));
821 Pkt4Ptr req(new Pkt4(DHCPREQUEST, 2345));
822 Pkt4Ptr rel(new Pkt4(DHCPRELEASE, 3456));
823 HWAddrPtr hwaddr = generateHWAddr();
824 // Same hardware address: same client for discover and request.
825 dis->setHWAddr(hwaddr);
826 req->setHWAddr(hwaddr);
827 rel->setHWAddr(generateHWAddr(55));
828 OptionPtr client_id = generateClientId();
829 // Same client ID: same client for discover and release.
830 dis->addOption(client_id);
831 req->addOption(generateClientId(111));
832 rel->addOption(client_id);
833
834 // Start multi-threading.
835 EXPECT_NO_THROW(MultiThreadingMgr::instance().apply(true, 1, 0));
836
837 try {
838 // Get a client handler.
839 ClientHandler client_handler;
840
841 // Create a continuation.
842 ContinuationPtr cont1 =
843 makeContinuation(std::bind(&ClientHandleTest::setCalled1, this));
844
845 // Try to lock it with the discover.
846 bool duplicate = false;
847 EXPECT_NO_THROW(duplicate = !client_handler.tryLock(dis, cont1));
848
849 // Should return false (no duplicate).
850 EXPECT_FALSE(duplicate);
851
852 // Get a second client handler.
853 ClientHandler client_handler2;
854
855 // Create a continuation.
856 ContinuationPtr cont2 =
857 makeContinuation(std::bind(&ClientHandleTest::setCalled2, this));
858
859 // Try to lock it with a request.
860 EXPECT_NO_THROW(duplicate = !client_handler2.tryLock(req, cont2));
861
862 // Should return true (race with the duplicate).
863 EXPECT_TRUE(duplicate);
864
865 // Get a third client handler.
866 ClientHandler client_handler3;
867
868 // Create a continuation.
869 ContinuationPtr cont3 =
870 makeContinuation(std::bind(&ClientHandleTest::setCalled3, this));
871
872 // Try to lock it with a release.
873 EXPECT_NO_THROW(duplicate = !client_handler3.tryLock(rel, cont3));
874
875 // Should return true (race with the duplicate).
876 EXPECT_TRUE(duplicate);
877 } catch (const std::exception& ex) {
878 ADD_FAILURE() << "unexpected exception: " << ex.what();
879 }
880
881 // Give the second continuation a chance.
882 waitForThreads();
883
884 // Force multi-threading to stop;
885 MultiThreadingCriticalSection cs;
886
887 checkStat(true);
888 EXPECT_FALSE(called1_);
889 EXPECT_FALSE(called2_);
890 EXPECT_TRUE(called3_);
891 }
892
893 } // end of anonymous namespace
894