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