dhcp4_test_utils.h 16.8 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

15
16
17
/// @file   dhcp4_test_utils.h
///
/// @brief  This file contains utility classes used for DHCPv4 server testing
18

19
20
21
22
#ifndef DHCP4_TEST_UTILS_H
#define DHCP4_TEST_UTILS_H

#include <gtest/gtest.h>
23
#include <dhcp/iface_mgr.h>
24
#include <dhcp/option4_addrlst.h>
25
#include <dhcp/pkt4.h>
26
27
#include <dhcp/pkt_filter.h>
#include <dhcp/pkt_filter_inet.h>
28
29
#include <dhcpsrv/subnet.h>
#include <dhcpsrv/lease.h>
30
#include <dhcp4/dhcp4_srv.h>
31
32
#include <asiolink/io_address.h>
#include <config/ccsession.h>
33
#include <list>
34

35
36
#include <boost/shared_ptr.hpp>

37
namespace isc {
38
namespace dhcp {
39
40
41
42
43
44
45
46
47
48
49
50
namespace test {

/// @brief Dummy Packet Filtering class.
///
/// This class reports capability to respond directly to the client which
/// doesn't have address configured yet.
///
/// All packet and socket handling functions do nothing because they are not
/// used in unit tests.
class PktFilterTest : public PktFilter {
public:

51
52
53
54
55
56
57
    /// @brief Constructor.
    ///
    /// Sets the 'direct response' capability to true.
    PktFilterTest()
        : direct_resp_supported_(true) {
    }

58
59
60
61
    /// @brief Reports 'direct response' capability.
    ///
    /// @return always true.
    virtual bool isDirectResponseSupported() const {
62
        return (direct_resp_supported_);
63
64
65
    }

    /// Does nothing.
66
67
68
69
    virtual SocketInfo openSocket(const Iface&,
                                  const isc::asiolink::IOAddress& addr,
                                  const uint16_t port, const bool, const bool) {
        return (SocketInfo(addr, port, 0));
70
71
72
73
74
75
76
77
78
79
80
81
    }

    /// Does nothing.
    virtual Pkt4Ptr receive(const Iface&, const SocketInfo&) {
        return Pkt4Ptr();
    }

    /// Does nothing.
    virtual int send(const Iface&, uint16_t, const Pkt4Ptr&) {
        return (0);
    }

82
83
84
85
    /// @brief Holds a boolean value which indicates whether direct response
    /// capability is supported (true) or not (false).
    bool direct_resp_supported_;

86
87
};

88
89
typedef boost::shared_ptr<PktFilterTest> PktFilterTestPtr;

90
91
92
93
94
95
96
class Dhcpv4SrvTest : public ::testing::Test {
public:

    /// @brief Constructor
    ///
    /// Initializes common objects used in many tests.
    /// Also sets up initial configuration in CfgMgr.
97
    Dhcpv4SrvTest();
98

Tomek Mrugalski's avatar
Tomek Mrugalski committed
99
    /// @brief destructor
100
    virtual ~Dhcpv4SrvTest();
101
102
103

    /// @brief Add 'Parameter Request List' option to the packet.
    ///
Tomek Mrugalski's avatar
Tomek Mrugalski committed
104
    /// This function adds PRL option comprising the following option codes:
105
106
107
108
109
110
111
    /// - 5 - Name Server
    /// - 15 - Domain Name
    /// - 7 - Log Server
    /// - 8 - Quotes Server
    /// - 9 - LPR Server
    ///
    /// @param pkt packet to add PRL option to.
112
    void addPrlOption(Pkt4Ptr& pkt);
113
114
115
116
117
118
119
120
121

    /// @brief Configures options being requested in the PRL option.
    ///
    /// The lpr-servers option is NOT configured here although it is
    /// added to the 'Parameter Request List' option in the
    /// \ref addPrlOption. When requested option is not configured
    /// the server should not return it in its response. The goal
    /// of not configuring the requested option is to verify that
    /// the server will not return it.
122
    void configureRequestedOptions();
123
124
125
126

    /// @brief checks that the response matches request
    /// @param q query (client's message)
    /// @param a answer (server's message)
127
    void messageCheck(const Pkt4Ptr& q, const Pkt4Ptr& a);
128

129
130
    /// @brief Check that certain basic (always added when lease is acquired)
    /// are present in a message.
131
    ///
132
133
134
    /// @param pkt A message to be checked.
    /// @return Assertion result which indicates whether test passed or failed.
    ::testing::AssertionResult basicOptionsPresent(const Pkt4Ptr& pkt);
135

136
137
    /// @brief Check that certain basic (always added when lease is acquired)
    /// are not present.
138
139
    ///
    /// @param pkt A packet to be checked.
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
    /// @return Assertion result which indicates whether test passed or failed.
    ::testing::AssertionResult noBasicOptions(const Pkt4Ptr& pkt);

    /// @brief Check that certain requested options are present in the message.
    ///
    /// @param pkt A message to be checked.
    /// @return Assertion result which indicates whether test passed or failed.
    ::testing::AssertionResult requestedOptionsPresent(const Pkt4Ptr& pkt);

    /// @brief Check that certain options (requested with PRL option)
    /// are not present.
    ///
    /// @param pkt A packet to be checked.
    /// @return Assertion result which indicates whether test passed or failed.
    ::testing::AssertionResult noRequestedOptions(const Pkt4Ptr& pkt);
155
156
157
158
159
160
161
162
163

    /// @brief generates client-id option
    ///
    /// Generate client-id option of specified length
    /// Ids with different lengths are sufficent to generate
    /// unique ids. If more fine grained control is required,
    /// tests generate client-ids on their own.
    /// Sets client_id_ field.
    /// @param size size of the client-id to be generated
164
    OptionPtr generateClientId(size_t size = 4);
165
166
167
168
169

    /// @brief generate hardware address
    ///
    /// @param size size of the generated MAC address
    /// @param pointer to Hardware Address object
170
    HWAddrPtr generateHWAddr(size_t size = 6);
171
172
173
174
175
176
177
178
179
180

    /// Check that address was returned from proper range, that its lease
    /// lifetime is correct, that T1 and T2 are returned properly
    /// @param rsp response to be checked
    /// @param subnet subnet that should be used to verify assigned address
    ///        and options
    /// @param t1_mandatory is T1 mandatory?
    /// @param t2_mandatory is T2 mandatory?
    void checkAddressParams(const Pkt4Ptr& rsp, const SubnetPtr subnet,
                            bool t1_mandatory = false,
181
                            bool t2_mandatory = false);
182
183
184
185
186
187
188

    /// @brief Basic checks for generated response (message type and trans-id).
    ///
    /// @param rsp response packet to be validated
    /// @param expected_message_type expected message type
    /// @param expected_transid expected transaction-id
    void checkResponse(const Pkt4Ptr& rsp, uint8_t expected_message_type,
189
                       uint32_t expected_transid);
190
191
192
193
194
195
196
197
198

    /// @brief Checks if the lease sent to client is present in the database
    ///
    /// @param rsp response packet to be validated
    /// @param client_id expected client-identifier (or NULL)
    /// @param HWAddr expected hardware address (not used now)
    /// @param expected_addr expected address
    Lease4Ptr checkLease(const Pkt4Ptr& rsp, const OptionPtr& client_id,
                         const HWAddrPtr&,
199
                         const isc::asiolink::IOAddress& expected_addr);
200
201
202
203

    /// @brief Checks if server response (OFFER, ACK, NAK) includes proper server-id
    /// @param rsp response packet to be validated
    /// @param expected_srvid expected value of server-id
204
    void checkServerId(const Pkt4Ptr& rsp, const OptionPtr& expected_srvid);
205
206

    /// @brief Checks if server response (OFFER, ACK, NAK) includes proper client-id
Tomek Mrugalski's avatar
Tomek Mrugalski committed
207
208
209
210
211
    ///
    /// This method follows values reported by CfgMgr in echoClientId() method.
    /// Depending on its configuration, the client-id is either mandatory or
    /// forbidden to appear in the response.
    ///
212
213
    /// @param rsp response packet to be validated
    /// @param expected_clientid expected value of client-id
214
    void checkClientId(const Pkt4Ptr& rsp, const OptionPtr& expected_clientid);
215

Tomek Mrugalski's avatar
Tomek Mrugalski committed
216
    /// @brief sets default fields in a captured packet
217
    ///
Tomek Mrugalski's avatar
Tomek Mrugalski committed
218
219
220
    /// Sets UDP ports, addresses and interface.
    ///
    /// @param pkt packet to have default fields set
221
    void captureSetDefaultFields(const Pkt4Ptr& pkt);
222

Tomek Mrugalski's avatar
Tomek Mrugalski committed
223
224
225
226
227
    /// @brief returns captured DISCOVER that went through a relay
    ///
    /// See method code for a detailed explanation.
    ///
    /// @return relayed DISCOVER
228
    Pkt4Ptr captureRelayedDiscover();
229

230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
    /// @brief Create packet from output buffer of another packet.
    ///
    /// This function creates a packet using an output buffer from another
    /// packet. This imitates reception of a packet from the wire. The
    /// unpack function is then called to parse packet contents and to
    /// create a collection of the options carried by this packet.
    ///
    /// This function is useful for unit tests which verify that the received
    /// packet is parsed correctly. In those cases it is usually inappropriate
    /// to create an instance of the packet, add options, set packet
    /// fields and use such packet as an input to processDiscover or
    /// processRequest function. This is because, such a packet has certain
    /// options already initialized and there is no way to verify that they
    /// have been initialized when packet instance was created or wire buffer
    /// processing. By creating a packet from the buffer we guarantee that the
    /// new packet is entirely initialized during wire data parsing.
    ///
    /// @param src_pkt A source packet, to be copied.
    /// @param [out] dst_pkt A destination packet.
    ///
    /// @return assertion result indicating if a function completed with
    /// success or failure.
    static ::testing::AssertionResult
    createPacketFromBuffer(const Pkt4Ptr& src_pkt,
                           Pkt4Ptr& dst_pkt);
255

256

Tomek Mrugalski's avatar
Tomek Mrugalski committed
257
258
259
260
    /// @brief generates a DHCPv4 packet based on provided hex string
    ///
    /// @return created packet
    Pkt4Ptr packetFromCapture(const std::string& hex_string);
261
262

    /// @brief This function cleans up after the test.
263
    virtual void TearDown();
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279

    /// @brief A subnet used in most tests
    Subnet4Ptr subnet_;

    /// @brief A pool used in most tests
    Pool4Ptr pool_;

    /// @brief A client-id used in most tests
    ClientIdPtr client_id_;

    int rcode_;

    isc::data::ConstElementPtr comment_;

};

280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
/// @brief Test fixture class to be used for tests which require fake
/// interfaces.
///
/// The DHCPv4 server must always append the server identifier to its response.
/// The server identifier is typically an IP address assigned to the interface
/// on which the query has been received. The DHCPv4 server uses IfaceMgr to
/// check this address. In order to test this functionality, a set of interfaces
/// must be known to the test. This test fixture class creates a set of well
/// known (fake) interfaces which can be assigned to the test DHCPv4 messages
/// so as the response (including server identifier) can be validated.
/// The real interfaces are removed from the IfaceMgr in the constructor and
/// they are re-assigned in the destructor.
class Dhcpv4SrvFakeIfaceTest : public Dhcpv4SrvTest {
public:
    /// @brief Constructor.
    ///
    /// Creates a set of fake interfaces:
    /// - lo, index: 0, address: 127.0.0.1
    /// - eth0, index: 1, address: 192.0.3.1
    /// - eth1, index: 2, address: 10.0.0.1
    ///
    /// These interfaces replace the real interfaces detected by the IfaceMgr.
    Dhcpv4SrvFakeIfaceTest();

304
305
    /// @brief Restores the original interface configuration.
    virtual void TearDown();
306
307
308
309
310
311
312
313
314
315

    /// @brief Creates an instance of the interface.
    ///
    /// @param name Name of the interface.
    /// @param ifindex Index of the interface.
    /// @param addr IP address assigned to the interface, represented as string.
    ///
    /// @return Iface Instance of the interface.
    static Iface createIface(const std::string& name, const int ifindex,
                             const std::string& addr);
316
317
318
319
320
321
322
323
324
325
326
327
328
329

    /// @brief Tests if Discover or Request message is processed correctly
    ///
    /// This test verifies that the Parameter Request List option is handled
    /// correctly, i.e. it checks that certain options are present in the
    /// server's response when they are requested and that they are not present
    /// when they are not requested or NAK occurs.
    ///
    /// @todo We need an additional test for PRL option using real traffic
    /// capture.
    ///
    /// @param msg_type DHCPDISCOVER or DHCPREQUEST
    void testDiscoverRequest(const uint8_t msg_type);

330
331
332
333
    /// @brief Holds a pointer to the packet filter object currently used
    /// by the IfaceMgr.
    PktFilterTestPtr current_pkt_filter_;

334
335
};

336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
/// @brief "Naked" DHCPv4 server, exposes internal fields
class NakedDhcpv4Srv: public Dhcpv4Srv {
public:

    /// @brief Constructor.
    ///
    /// This constructor disables default modes of operation used by the
    /// Dhcpv4Srv class:
    /// - Send/receive broadcast messages through sockets on interfaces
    /// which support broadcast traffic.
    /// - Direct DHCPv4 traffic - communication with clients which do not
    /// have IP address assigned yet.
    ///
    /// Enabling these modes requires root privilges so they must be
    /// disabled for unit testing.
    ///
    /// Note, that disabling broadcast options on sockets does not impact
    /// the operation of these tests because they use local loopback
    /// interface which doesn't have broadcast capability anyway. It rather
    /// prevents setting broadcast options on other (broadcast capable)
    /// sockets which are opened on other interfaces in Dhcpv4Srv constructor.
    ///
    /// The Direct DHCPv4 Traffic capability can be disabled here because
    /// it is tested with PktFilterLPFTest unittest. The tests which belong
    /// to PktFilterLPFTest can be enabled on demand when root privileges can
    /// be guaranteed.
    ///
    /// @param port port number to listen on; the default value 0 indicates
    /// that sockets should not be opened.
    NakedDhcpv4Srv(uint16_t port = 0)
        : Dhcpv4Srv(port, "type=memfile", false, false) {
367
368
369
370
371
372
373
374
375
        // Create fixed server id.
        server_id_.reset(new Option4AddrLst(DHO_DHCP_SERVER_IDENTIFIER,
                                            asiolink::IOAddress("192.0.3.1")));
    }

    /// @brief Returns fixed server identifier assigned to the naked server
    /// instance.
    OptionPtr getServerID() const {
        return (server_id_);
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
    }

    /// @brief fakes packet reception
    /// @param timeout ignored
    ///
    /// The method receives all packets queued in receive queue, one after
    /// another. Once the queue is empty, it initiates the shutdown procedure.
    ///
    /// See fake_received_ field for description
    virtual Pkt4Ptr receivePacket(int /*timeout*/) {

        // If there is anything prepared as fake incoming traffic, use it
        if (!fake_received_.empty()) {
            Pkt4Ptr pkt = fake_received_.front();
            fake_received_.pop_front();
            return (pkt);
        }

        // If not, just trigger shutdown and return immediately
        shutdown();
        return (Pkt4Ptr());
    }

    /// @brief fake packet sending
    ///
    /// Pretend to send a packet, but instead just store it in fake_send_ list
    /// where test can later inspect server's response.
    virtual void sendPacket(const Pkt4Ptr& pkt) {
        fake_sent_.push_back(pkt);
    }

    /// @brief adds a packet to fake receive queue
    ///
    /// See fake_received_ field for description
    void fakeReceive(const Pkt4Ptr& pkt) {
411
        pkt->setIface("eth0");
412
413
414
415
416
417
        fake_received_.push_back(pkt);
    }

    virtual ~NakedDhcpv4Srv() {
    }

418
419
420
    /// @brief Dummy server identifier option used by various tests.
    OptionPtr server_id_;

421
422
423
424
425
426
427
428
429
430
    /// @brief packets we pretend to receive
    ///
    /// Instead of setting up sockets on interfaces that change between OSes, it
    /// is much easier to fake packet reception. This is a list of packets that
    /// we pretend to have received. You can schedule new packets to be received
    /// using fakeReceive() and NakedDhcpv4Srv::receivePacket() methods.
    std::list<Pkt4Ptr> fake_received_;

    std::list<Pkt4Ptr> fake_sent_;

431
432
    using Dhcpv4Srv::adjustIfaceData;
    using Dhcpv4Srv::appendServerID;
433
434
435
436
437
    using Dhcpv4Srv::processDiscover;
    using Dhcpv4Srv::processRequest;
    using Dhcpv4Srv::processRelease;
    using Dhcpv4Srv::processDecline;
    using Dhcpv4Srv::processInform;
438
439
440
    using Dhcpv4Srv::processClientName;
    using Dhcpv4Srv::computeDhcid;
    using Dhcpv4Srv::createNameChangeRequests;
441
    using Dhcpv4Srv::acceptServerId;
442
443
    using Dhcpv4Srv::sanityCheck;
    using Dhcpv4Srv::srvidToString;
444
    using Dhcpv4Srv::unpackOptions;
445
    using Dhcpv4Srv::name_change_reqs_;
446
447
};

448
449
450
}; // end of isc::dhcp::test namespace
}; // end of isc::dhcp namespace
}; // end of isc namespace
451

Tomek Mrugalski's avatar
Tomek Mrugalski committed
452
#endif // DHCP4_TEST_UTILS_H