1 // Copyright (C) 2013-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 /// @file dhcp4_test_utils.h
8 ///
9 /// @brief This file contains utility classes used for DHCPv4 server testing
10
11 #ifndef DHCP4_TEST_UTILS_H
12 #define DHCP4_TEST_UTILS_H
13
14 #include <gtest/gtest.h>
15 #include <dhcp/iface_mgr.h>
16 #include <dhcp/option4_addrlst.h>
17 #include <dhcp/pkt4.h>
18 #include <dhcp/pkt_filter.h>
19 #include <dhcp/pkt_filter_inet.h>
20 #include <dhcpsrv/alloc_engine.h>
21 #include <dhcpsrv/subnet.h>
22 #include <dhcpsrv/lease.h>
23 #include <dhcpsrv/lease_mgr_factory.h>
24 #include <dhcp4/dhcp4_srv.h>
25 #include <dhcp4/parser_context.h>
26 #include <asiolink/io_address.h>
27 #include <cc/command_interpreter.h>
28 #include <util/multi_threading_mgr.h>
29 #include <list>
30
31 #include <boost/shared_ptr.hpp>
32
33 namespace isc {
34 namespace dhcp {
35 namespace test {
36
37 /// @brief Dummy Packet Filtering class.
38 ///
39 /// This class reports capability to respond directly to the client which
40 /// doesn't have address configured yet.
41 ///
42 /// All packet and socket handling functions do nothing because they are not
43 /// used in unit tests.
44 class PktFilterTest : public PktFilter {
45 public:
46
47 /// @brief Constructor.
48 ///
49 /// Sets the 'direct response' capability to true.
PktFilterTest()50 PktFilterTest()
51 : direct_resp_supported_(true) {
52 }
53
54 /// @brief Reports 'direct response' capability.
55 ///
56 /// @return always true.
isDirectResponseSupported()57 virtual bool isDirectResponseSupported() const {
58 return (direct_resp_supported_);
59 }
60
61 /// Does nothing.
openSocket(Iface &,const isc::asiolink::IOAddress & addr,const uint16_t port,const bool,const bool)62 virtual SocketInfo openSocket(Iface&,
63 const isc::asiolink::IOAddress& addr,
64 const uint16_t port, const bool, const bool) {
65 return (SocketInfo(addr, port, 0));
66 }
67
68 /// Does nothing.
receive(Iface &,const SocketInfo &)69 virtual Pkt4Ptr receive(Iface&, const SocketInfo&) {
70 return Pkt4Ptr();
71 }
72
73 /// Does nothing.
send(const Iface &,uint16_t,const Pkt4Ptr &)74 virtual int send(const Iface&, uint16_t, const Pkt4Ptr&) {
75 return (0);
76 }
77
78 /// @brief Holds a boolean value which indicates whether direct response
79 /// capability is supported (true) or not (false).
80 bool direct_resp_supported_;
81
82 };
83
84 typedef boost::shared_ptr<PktFilterTest> PktFilterTestPtr;
85
86 /// Forward definition for Dhcp4Client defined in dhcp4_client.h
87 /// dhcp4_client.h includes dhcp_test_utils.h (this file), so to avoid
88 /// circular dependencies, we need a forward class declaration.
89 class Dhcp4Client;
90
91 /// @brief "Naked" DHCPv4 server, exposes internal fields
92 class NakedDhcpv4Srv: public Dhcpv4Srv {
93 public:
94
95 /// @brief Constructor.
96 ///
97 /// This constructor disables default modes of operation used by the
98 /// Dhcpv4Srv class:
99 /// - Send/receive broadcast messages through sockets on interfaces
100 /// which support broadcast traffic.
101 /// - Direct DHCPv4 traffic - communication with clients which do not
102 /// have IP address assigned yet.
103 ///
104 /// Enabling these modes requires root privileges so they must be
105 /// disabled for unit testing.
106 ///
107 /// Note, that disabling broadcast options on sockets does not impact
108 /// the operation of these tests because they use local loopback
109 /// interface which doesn't have broadcast capability anyway. It rather
110 /// prevents setting broadcast options on other (broadcast capable)
111 /// sockets which are opened on other interfaces in Dhcpv4Srv constructor.
112 ///
113 /// The Direct DHCPv4 Traffic capability can be disabled here because
114 /// it is tested with PktFilterLPFTest unittest. The tests which belong
115 /// to PktFilterLPFTest can be enabled on demand when root privileges can
116 /// be guaranteed.
117 ///
118 /// @param port port number to listen on; the default value 0 indicates
119 /// that sockets should not be opened.
120 NakedDhcpv4Srv(uint16_t port = 0)
Dhcpv4Srv(port,false,false)121 : Dhcpv4Srv(port, false, false) {
122 // Create a default lease database backend.
123 std::string dbconfig = "type=memfile universe=4 persist=false";
124 isc::dhcp::LeaseMgrFactory::create(dbconfig);
125 // Create fixed server id.
126 server_id_.reset(new Option4AddrLst(DHO_DHCP_SERVER_IDENTIFIER,
127 asiolink::IOAddress("192.0.3.1")));
128 LeaseMgr::setIOService(getIOService());
129 }
130
131 /// @brief Returns fixed server identifier assigned to the naked server
132 /// instance.
getServerID()133 OptionPtr getServerID() const {
134 return (server_id_);
135 }
136
137 /// @brief fakes packet reception
138 /// @param timeout ignored
139 ///
140 /// The method receives all packets queued in receive queue, one after
141 /// another. Once the queue is empty, it initiates the shutdown procedure.
142 ///
143 /// See fake_received_ field for description
receivePacket(int)144 virtual Pkt4Ptr receivePacket(int /*timeout*/) {
145
146 // If there is anything prepared as fake incoming traffic, use it
147 if (!fake_received_.empty()) {
148 Pkt4Ptr pkt = fake_received_.front();
149 fake_received_.pop_front();
150 return (pkt);
151 }
152
153 // Make sure the server processed all packets in MT.
154 isc::util::MultiThreadingMgr::instance().getThreadPool().wait(3);
155
156 // If not, just trigger shutdown and return immediately
157 shutdown();
158 return (Pkt4Ptr());
159 }
160
161 /// @brief fake packet sending
162 ///
163 /// Pretend to send a packet, but instead just store it in fake_send_ list
164 /// where test can later inspect server's response.
sendPacket(const Pkt4Ptr & pkt)165 virtual void sendPacket(const Pkt4Ptr& pkt) {
166 if (isc::util::MultiThreadingMgr::instance().getMode()) {
167 std::lock_guard<std::mutex> lk(mutex_);
168 fake_sent_.push_back(pkt);
169 } else {
170 fake_sent_.push_back(pkt);
171 }
172 }
173
174 /// @brief fake receive packet from server
175 ///
176 /// The client uses this packet as a reply from the server.
177 ///
178 /// @return The received packet.
receiveOneMsg()179 Pkt4Ptr receiveOneMsg() {
180 if (isc::util::MultiThreadingMgr::instance().getMode()) {
181 std::lock_guard<std::mutex> lk(mutex_);
182 return (receiveOneMsgInternal());
183 } else {
184 return (receiveOneMsgInternal());
185 }
186 }
187
188 /// @brief fake receive packet from server
189 ///
190 /// The client uses this packet as a reply from the server.
191 /// This function should be called in a thread safe context.
192 ///
193 /// @return The received packet.
receiveOneMsgInternal()194 Pkt4Ptr receiveOneMsgInternal() {
195 // Return empty pointer if server hasn't responded.
196 if (fake_sent_.empty()) {
197 return (Pkt4Ptr());
198 }
199 Pkt4Ptr msg = fake_sent_.front();
200 fake_sent_.pop_front();
201 return (msg);
202 }
203
204 /// @brief adds a packet to fake receive queue
205 ///
206 /// See fake_received_ field for description
fakeReceive(const Pkt4Ptr & pkt)207 void fakeReceive(const Pkt4Ptr& pkt) {
208 fake_received_.push_back(pkt);
209 }
210
~NakedDhcpv4Srv()211 virtual ~NakedDhcpv4Srv() {
212 }
213
214 /// @brief Runs processing DHCPREQUEST.
215 ///
216 /// @param request a message received from client
217 /// @return DHCPACK or DHCPNAK message
processRequest(Pkt4Ptr & request)218 Pkt4Ptr processRequest(Pkt4Ptr& request) {
219 AllocEngine::ClientContext4Ptr context(new AllocEngine::ClientContext4());
220 return (processRequest(request, context));
221 }
222
223 /// @brief Runs processing DHCPRELEASE.
224 ///
225 /// @param release message received from client
processRelease(Pkt4Ptr & release)226 void processRelease(Pkt4Ptr& release) {
227 AllocEngine::ClientContext4Ptr context(new AllocEngine::ClientContext4());
228 processRelease(release, context);
229 }
230
231 /// @brief Runs processing DHCPDECLINE.
232 ///
233 /// @param decline message received from client
processDecline(Pkt4Ptr & decline)234 void processDecline(Pkt4Ptr& decline) {
235 AllocEngine::ClientContext4Ptr context(new AllocEngine::ClientContext4());
236 processDecline(decline, context);
237 }
238
239 /// @brief Dummy server identifier option used by various tests.
240 OptionPtr server_id_;
241
242 /// @brief packets we pretend to receive.
243 ///
244 /// Instead of setting up sockets on interfaces that change between OSes, it
245 /// is much easier to fake packet reception. This is a list of packets that
246 /// we pretend to have received. You can schedule new packets to be received
247 /// using fakeReceive() and NakedDhcpv4Srv::receivePacket() methods.
248 std::list<Pkt4Ptr> fake_received_;
249
250 /// @brief packets we pretend to send.
251 std::list<Pkt4Ptr> fake_sent_;
252
253 using Dhcpv4Srv::adjustIfaceData;
254 using Dhcpv4Srv::appendServerID;
255 using Dhcpv4Srv::processDiscover;
256 using Dhcpv4Srv::processRequest;
257 using Dhcpv4Srv::processRelease;
258 using Dhcpv4Srv::processDecline;
259 using Dhcpv4Srv::processInform;
260 using Dhcpv4Srv::processClientName;
261 using Dhcpv4Srv::createNameChangeRequests;
262 using Dhcpv4Srv::acceptServerId;
263 using Dhcpv4Srv::sanityCheck;
264 using Dhcpv4Srv::srvidToString;
265 using Dhcpv4Srv::classifyPacket;
266 using Dhcpv4Srv::deferredUnpack;
267 using Dhcpv4Srv::accept;
268 using Dhcpv4Srv::acceptMessageType;
269 using Dhcpv4Srv::selectSubnet;
270 using Dhcpv4Srv::setSendResponsesToSource;
271 using Dhcpv4Srv::VENDOR_CLASS_PREFIX;
272 using Dhcpv4Srv::shutdown_;
273 using Dhcpv4Srv::alloc_engine_;
274 using Dhcpv4Srv::server_port_;
275 using Dhcpv4Srv::client_port_;
276
277 /// @brief Mutex to protect the packet buffers.
278 std::mutex mutex_;
279 };
280
281 // We need to pass one reference to the Dhcp4Client, which is defined in
282 // dhcp4_client.h. That header includes this file. To avoid circular
283 // dependencies, we use forward declaration here.
284 class Dhcp4Client;
285
286 /// @brief Base class for DHCPv4 server testing.
287 ///
288 /// Currently it configures the test data path directory in
289 /// the @c CfgMgr. When the object is destroyed, the original
290 /// path is reverted.
291 class BaseServerTest : public ::testing::Test {
292 public:
293
294 /// @brief Constructor.
295 BaseServerTest();
296
297 /// @brief Destructor.
298 virtual ~BaseServerTest();
299
300 private:
301
302 /// @brief Holds the original data directory.
303 std::string original_datadir_;
304
305 };
306
307 class Dhcpv4SrvTest : public BaseServerTest {
308 public:
309
310 enum ExpectedResult {
311 SHOULD_PASS, // pass = accept decline, move lease to declined state.
312 SHOULD_FAIL // fail = reject the decline
313 };
314
315 class Dhcpv4SrvMTTestGuard {
316 public:
Dhcpv4SrvMTTestGuard(Dhcpv4SrvTest & test,bool mt_enabled)317 Dhcpv4SrvMTTestGuard(Dhcpv4SrvTest& test, bool mt_enabled) : test_(test) {
318 test_.setMultiThreading(mt_enabled);
319 }
~Dhcpv4SrvMTTestGuard()320 ~Dhcpv4SrvMTTestGuard() {
321 test_.setMultiThreading(false);
322 }
323 Dhcpv4SrvTest& test_;
324 };
325
326 /// @brief Constructor
327 ///
328 /// Initializes common objects used in many tests.
329 /// Also sets up initial configuration in CfgMgr.
330 Dhcpv4SrvTest();
331
332 /// @brief Destructor
333 ///
334 /// Removes existing configuration.
335 virtual ~Dhcpv4SrvTest();
336
337 /// @brief Add 'Parameter Request List' option to the packet.
338 ///
339 /// This function adds PRL option comprising the following option codes:
340 /// - 5 - Name Server
341 /// - 15 - Domain Name
342 /// - 7 - Log Server
343 /// - 8 - Quotes Server
344 /// - 9 - LPR Server
345 ///
346 /// @param pkt packet to add PRL option to.
347 void addPrlOption(Pkt4Ptr& pkt);
348
349 /// @brief Configures options being requested in the PRL option.
350 ///
351 /// The lpr-servers option is NOT configured here although it is
352 /// added to the 'Parameter Request List' option in the
353 /// \ref addPrlOption. When requested option is not configured
354 /// the server should not return it in its response. The goal
355 /// of not configuring the requested option is to verify that
356 /// the server will not return it.
357 void configureRequestedOptions();
358
359 /// @brief Configures server identifier at different levels.
360 void configureServerIdentifier();
361
362 /// @brief checks that the response matches request
363 ///
364 /// @param q query (client's message)
365 /// @param a answer (server's message)
366 void messageCheck(const Pkt4Ptr& q, const Pkt4Ptr& a);
367
368 /// @brief Check that certain basic (always added when lease is acquired)
369 /// are present in a message.
370 ///
371 /// @param pkt A message to be checked.
372 /// @return Assertion result which indicates whether test passed or failed.
373 ::testing::AssertionResult basicOptionsPresent(const Pkt4Ptr& pkt);
374
375 /// @brief Check that certain basic (always added when lease is acquired)
376 /// are not present.
377 ///
378 /// @param pkt A packet to be checked.
379 /// @return Assertion result which indicates whether test passed or failed.
380 ::testing::AssertionResult noBasicOptions(const Pkt4Ptr& pkt);
381
382 /// @brief Check that certain requested options are present in the message.
383 ///
384 /// @param pkt A message to be checked.
385 /// @return Assertion result which indicates whether test passed or failed.
386 ::testing::AssertionResult requestedOptionsPresent(const Pkt4Ptr& pkt);
387
388 /// @brief Check that certain options (requested with PRL option)
389 /// are not present.
390 ///
391 /// @param pkt A packet to be checked.
392 /// @return Assertion result which indicates whether test passed or failed.
393 ::testing::AssertionResult noRequestedOptions(const Pkt4Ptr& pkt);
394
395 /// @brief generates client-id option
396 ///
397 /// Generate client-id option of specified length
398 /// Ids with different lengths are sufficient to generate
399 /// unique ids. If more fine grained control is required,
400 /// tests generate client-ids on their own.
401 /// Sets client_id_ field.
402 /// @param size size of the client-id to be generated
403 OptionPtr generateClientId(size_t size = 4);
404
405 /// @brief generate hardware address
406 ///
407 /// @param size size of the generated MAC address
408 /// @param pointer to Hardware Address object
409 HWAddrPtr generateHWAddr(size_t size = 6);
410
411 /// @brief Convenience method for making a server identifier option instance.
412 ///
413 /// @param address IP address to add to the option
414 ///
415 /// @return Pointer to the newly constructed option.
416 OptionCustomPtr makeServerIdOption(const isc::asiolink::IOAddress& address);
417
418 /// @brief Convenience method for making a fqdn list option instance.
419 ///
420 /// @return Pointer to the newly constructed option.
421 OptionPtr makeFqdnListOption();
422
423 /// Check that address was returned from proper range, that its lease
424 /// lifetime is correct, that T1 and T2 are returned properly
425 /// @param rsp response to be checked
426 /// @param subnet subnet that should be used to verify assigned address
427 /// and options
428 /// @param t1_present check that t1 must be present (true) or must not be
429 /// present (false)
430 /// @param t2_present check that t2 must be present (true) or must not be
431 /// present (false)
432 /// @param expected_valid check that lease lifetime has the not-zero
433 /// expected value (zero value means that do not check).
434 void checkAddressParams(const Pkt4Ptr& rsp, const Subnet4Ptr subnet,
435 bool t1_present = false,
436 bool t2_present = false,
437 uint32_t expected_valid = 0);
438
439 /// @brief Basic checks for generated response (message type and trans-id).
440 ///
441 /// @param rsp response packet to be validated
442 /// @param expected_message_type expected message type
443 /// @param expected_transid expected transaction-id
444 void checkResponse(const Pkt4Ptr& rsp, int expected_message_type,
445 uint32_t expected_transid);
446
447 /// @brief Checks if the lease sent to client is present in the database
448 ///
449 /// @param rsp response packet to be validated
450 /// @param client_id expected client-identifier (or NULL)
451 /// @param HWAddr expected hardware address (not used now)
452 /// @param expected_addr expected address
453 Lease4Ptr checkLease(const Pkt4Ptr& rsp, const OptionPtr& client_id,
454 const HWAddrPtr&,
455 const isc::asiolink::IOAddress& expected_addr);
456
457 /// @brief Checks if server response (OFFER, ACK, NAK) includes proper server-id
458 ///
459 /// @param rsp response packet to be validated
460 /// @param expected_srvid expected value of server-id
461 void checkServerId(const Pkt4Ptr& rsp, const OptionPtr& expected_srvid);
462
463 /// @brief Checks if server response (OFFER, ACK, NAK) includes proper client-id
464 ///
465 /// This method follows values reported by CfgMgr in echoClientId() method.
466 /// Depending on its configuration, the client-id is either mandatory or
467 /// forbidden to appear in the response.
468 ///
469 /// @param rsp response packet to be validated
470 /// @param expected_clientid expected value of client-id
471 void checkClientId(const Pkt4Ptr& rsp, const OptionPtr& expected_clientid);
472
473 /// @brief Checks the value of the dhcp-server-identifier option in a packet
474 ///
475 /// @param packet packet to test
476 /// @param expected_address IP address the packet's option should contain
477 void checkServerIdOption(const Pkt4Ptr& packet, const isc::asiolink::IOAddress& expected_address);
478
479 /// @brief Create packet from output buffer of another packet.
480 ///
481 /// This function creates a packet using an output buffer from another
482 /// packet. This imitates reception of a packet from the wire. The
483 /// unpack function is then called to parse packet contents and to
484 /// create a collection of the options carried by this packet.
485 ///
486 /// This function is useful for unit tests which verify that the received
487 /// packet is parsed correctly. In those cases it is usually inappropriate
488 /// to create an instance of the packet, add options, set packet
489 /// fields and use such packet as an input to processDiscover or
490 /// processRequest function. This is because, such a packet has certain
491 /// options already initialized and there is no way to verify that they
492 /// have been initialized when packet instance was created or wire buffer
493 /// processing. By creating a packet from the buffer we guarantee that the
494 /// new packet is entirely initialized during wire data parsing.
495 ///
496 /// @param src_pkt A source packet, to be copied.
497 /// @param [out] dst_pkt A destination packet.
498 ///
499 /// @return assertion result indicating if a function completed with
500 /// success or failure.
501 static ::testing::AssertionResult
502 createPacketFromBuffer(const isc::dhcp::Pkt4Ptr& src_pkt,
503 isc::dhcp::Pkt4Ptr& dst_pkt);
504
505 /// @brief Tests if Discover or Request message is processed correctly
506 ///
507 /// This test verifies that the Parameter Request List option is handled
508 /// correctly, i.e. it checks that certain options are present in the
509 /// server's response when they are requested and that they are not present
510 /// when they are not requested or NAK occurs.
511 ///
512 /// @todo We need an additional test for PRL option using real traffic
513 /// capture.
514 ///
515 /// @param msg_type DHCPDISCOVER or DHCPREQUEST
516 void testDiscoverRequest(const uint8_t msg_type);
517
518 /// @brief Create test which verifies server identifier.
519 ///
520 /// @param expected_server_id expected server identifier
521 /// @param query the query used to get associated client classes
522 /// @param requested the requested address
523 /// @param server_id server identifier
524 void buildCfgOptionTest(isc::asiolink::IOAddress expected_server_id,
525 Pkt4Ptr& query,
526 isc::asiolink::IOAddress requested,
527 isc::asiolink::IOAddress server_id);
528
529 /// @brief Runs DHCPv4 configuration from the JSON string.
530 ///
531 /// @param config String holding server configuration in JSON format.
532 /// @param commit A boolean flag indicating if the new configuration
533 /// should be committed (if true), or not (if false).
534 /// @param open_sockets A boolean flag indicating if sockets should
535 /// be opened (if true), or not (if false).
536 void configure(const std::string& config,
537 const bool commit = true,
538 const bool open_sockets = true);
539
540 /// @brief Configure specified DHCP server using JSON string.
541 ///
542 /// @param config String holding server configuration in JSON format.
543 /// @param srv Instance of the server to be configured.
544 /// @param commit A boolean flag indicating if the new configuration
545 /// should be committed (if true), or not (if false).
546 /// @param open_sockets A boolean flag indicating if sockets should
547 /// be opened (if true), or not (if false).
548 void configure(const std::string& config,
549 NakedDhcpv4Srv& srv,
550 const bool commit = true,
551 const bool open_sockets = true);
552
553 /// @brief Configure specified DHCP server using JSON string.
554 ///
555 /// @param config String holding server configuration in JSON format.
556 /// @param srv Instance of the server to be configured.
557 /// @param commit A boolean flag indicating if the new configuration
558 /// should be committed (if true), or not (if false).
559 /// @param exp_rcode expected status code (default = 0 (success))
560 /// @return (a pair of status code and a string with result)
561 std::pair<int, std::string>
562 configureWithStatus(const std::string& config, NakedDhcpv4Srv& srv,
563 const bool commit = true, const int exp_rcode = 0);
564
565 /// @brief Pretends a packet of specified type was received.
566 ///
567 /// Instantiates fake network interfaces, configures passed Dhcpv4Srv,
568 /// then creates a message of specified type and sends it to the
569 /// server and then checks whether expected statistics were set
570 /// appropriately.
571 ///
572 /// @param srv the DHCPv4 server to be used
573 /// @param config JSON configuration to be used
574 /// @param pkt_type type of the packet to be faked
575 /// @param stat_name name of the expected statistic
576 void pretendReceivingPkt(NakedDhcpv4Srv& srv, const std::string& config,
577 uint8_t pkt_type, const std::string& stat_name);
578
579 /// @brief Create @c Dhcpv4Exchange from client's query.
580 Dhcpv4Exchange createExchange(const Pkt4Ptr& query);
581
582 /// @brief Performs 4-way exchange to obtain new lease.
583 ///
584 /// This is used as a preparatory step for Decline operation.
585 ///
586 /// @param client Client to be used to obtain a lease.
587 void acquireLease(Dhcp4Client& client);
588
589 /// @brief Tests if the acquired lease is or is not declined.
590 ///
591 /// @param client Dhcp4Client instance
592 /// @param hw_address_1 HW Address to be used to acquire the lease.
593 /// @param client_id_1 Client id to be used to acquire the lease.
594 /// @param hw_address_2 HW Address to be used to decline the lease.
595 /// @param client_id_2 Client id to be used to decline the lease.
596 /// @param expected_result SHOULD_PASS if the lease is expected to
597 /// be successfully declined, or SHOULD_FAIL if the lease is expected
598 /// to not be declined.
599 void acquireAndDecline(Dhcp4Client& client,
600 const std::string& hw_address_1,
601 const std::string& client_id_1,
602 const std::string& hw_address_2,
603 const std::string& client_id_2,
604 ExpectedResult expected_result);
605
606 /// @brief Checks if received relay agent info option is echoed back to the
607 /// client.
608 void relayAgentInfoEcho();
609
610 /// @brief Checks if received bad relay agent info option is not echoed back
611 /// to the client.
612 void badRelayAgentInfoEcho();
613
614 /// @brief Checks if client port can be overridden in packets being sent.
615 void portsClientPort();
616
617 /// @brief Checks if server port can be overridden in packets being sent.
618 void portsServerPort();
619
620 /// @brief This function cleans up after the test.
621 virtual void TearDown();
622
623 /// @brief Set multi-threading mode.
setMultiThreading(bool enabled)624 void setMultiThreading(bool enabled) {
625 multi_threading_ = enabled;
626 }
627
628 /// @brief A subnet used in most tests.
629 Subnet4Ptr subnet_;
630
631 /// @brief A pool used in most tests.
632 Pool4Ptr pool_;
633
634 /// @brief A client-id used in most tests.
635 ClientIdPtr client_id_;
636
637 /// @brief Return code
638 int rcode_;
639
640 /// @brief Comment received from configuration.
641 isc::data::ConstElementPtr comment_;
642
643 /// @brief Server object under test.
644 NakedDhcpv4Srv srv_;
645
646 /// @brief The multi-threading flag.
647 bool multi_threading_;
648 };
649
650 /// @brief Patch the server config to add interface-config/re-detect=false
651 /// @param json the server config
652 inline void
disableIfacesReDetect(isc::data::ConstElementPtr json)653 disableIfacesReDetect(isc::data::ConstElementPtr json) {
654 isc::data::ConstElementPtr ifaces_cfg = json->get("interfaces-config");
655 if (ifaces_cfg) {
656 isc::data::ElementPtr mutable_cfg =
657 boost::const_pointer_cast<isc::data::Element>(ifaces_cfg);
658 mutable_cfg->set("re-detect", isc::data::Element::create(false));
659 }
660 }
661
662 /// @brief Patch the server config to add multi-threading/enable-multi-threading
663 /// @param json the server config
664 inline void
configureMultiThreading(bool enabled,isc::data::ConstElementPtr json)665 configureMultiThreading(bool enabled, isc::data::ConstElementPtr json) {
666 isc::data::ConstElementPtr multi_threading = json->get("multi-threading");
667 if (!multi_threading) {
668 isc::data::ElementPtr mutable_cfg =
669 boost::const_pointer_cast<isc::data::Element>(json);
670 multi_threading = isc::data::Element::createMap();
671 mutable_cfg->set("multi-threading", multi_threading);
672 }
673
674 isc::data::ElementPtr mutable_cfg =
675 boost::const_pointer_cast<isc::data::Element>(multi_threading);
676 if (enabled) {
677 mutable_cfg->set("enable-multi-threading", isc::data::Element::create(true));
678 mutable_cfg->set("thread-pool-size", isc::data::Element::create(4));
679 mutable_cfg->set("packet-queue-size", isc::data::Element::create(4));
680 } else {
681 mutable_cfg->set("enable-multi-threading", isc::data::Element::create(false));
682 }
683 }
684
685 /// @brief Runs parser in JSON mode, useful for parser testing
686 ///
687 /// @param in string to be parsed
688 /// @return ElementPtr structure representing parsed JSON
689 inline isc::data::ElementPtr
parseJSON(const std::string & in)690 parseJSON(const std::string& in) {
691 isc::dhcp::Parser4Context ctx;
692 return (ctx.parseString(in, isc::dhcp::Parser4Context::PARSER_JSON));
693 }
694
695 /// @brief Runs parser in Dhcp4 mode
696 ///
697 /// This is a simplified Dhcp4 mode, so no outer { } and "Dhcp4" is
698 /// needed. This format is used by most of the tests.
699 ///
700 /// @param in string to be parsed
701 /// @param verbose display the exception message when it fails
702 /// @return ElementPtr structure representing parsed JSON
703 inline isc::data::ElementPtr
704 parseDHCP4(const std::string& in, bool verbose = false) {
705 try {
706 isc::dhcp::Parser4Context ctx;
707 isc::data::ElementPtr json;
708 json = ctx.parseString(in, isc::dhcp::Parser4Context::SUBPARSER_DHCP4);
709 disableIfacesReDetect(json);
710 return (json);
711 }
catch(const std::exception & ex)712 catch (const std::exception& ex) {
713 if (verbose) {
714 std::cout << "EXCEPTION: " << ex.what() << std::endl;
715 }
716 throw;
717 }
718 }
719
720 /// @brief Runs parser in option definition mode
721 ///
722 /// This function parses specified text as JSON that defines option definitions.
723 ///
724 /// @param in string to be parsed
725 /// @param verbose display the exception message when it fails
726 /// @return ElementPtr structure representing parsed JSON
727 inline isc::data::ElementPtr
728 parseOPTION_DEFS(const std::string& in, bool verbose = false) {
729 try {
730 isc::dhcp::Parser4Context ctx;
731 return (ctx.parseString(in, isc::dhcp::Parser4Context::PARSER_OPTION_DEFS));
732 }
catch(const std::exception & ex)733 catch (const std::exception& ex) {
734 if (verbose) {
735 std::cout << "EXCEPTION: " << ex.what() << std::endl;
736 }
737 throw;
738 }
739 }
740
741 } // end of isc::dhcp::test namespace
742 } // end of isc::dhcp namespace
743 } // end of isc namespace
744
745 #endif // DHCP4_TEST_UTILS_H
746