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