test_control.h 46.9 KB
Newer Older
1
// Copyright (C) 2012-2017 Internet Systems Consortium, Inc. ("ISC")
2
//
3 4 5
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6

7 8
#ifndef TEST_CONTROL_H
#define TEST_CONTROL_H
9

10 11 12
#include "packet_storage.h"
#include "rate_control.h"
#include "stats_mgr.h"
13

14
#include <dhcp/iface_mgr.h>
15
#include <dhcp/dhcp6.h>
16 17
#include <dhcp/pkt4.h>
#include <dhcp/pkt6.h>
18
#include <util/random/random_number_generator.h>
19

Marcin Siodelski's avatar
Marcin Siodelski committed
20 21 22 23 24 25 26
#include <boost/noncopyable.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/function.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

#include <string>
#include <vector>
27

28 29 30
namespace isc {
namespace perfdhcp {

31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
/// Default transaction id offset in the packet template.
static const size_t DHCPV4_TRANSID_OFFSET = 4;
/// Default offset of MAC's last octet in the packet template..
static const size_t DHCPV4_RANDOMIZATION_OFFSET = 35;
/// Default elapsed time offset in the packet template.
static const size_t DHCPV4_ELAPSED_TIME_OFFSET = 8;
/// Default server id offset in the packet template.
static const size_t DHCPV4_SERVERID_OFFSET = 54;
/// Default requested ip offset in the packet template.
static const size_t DHCPV4_REQUESTED_IP_OFFSET = 240;
/// Default DHCPV6 transaction id offset in t the packet template.
static const size_t DHCPV6_TRANSID_OFFSET = 1;
/// Default DHCPV6 randomization offset (last octet of DUID)
/// in the packet template.
static const size_t DHCPV6_RANDOMIZATION_OFFSET = 21;
/// Default DHCPV6 elapsed time offset in the packet template.
static const size_t DHCPV6_ELAPSED_TIME_OFFSET = 84;
/// Default DHCPV6 server id offset in the packet template.
static const size_t DHCPV6_SERVERID_OFFSET = 22;
/// Default DHCPV6 IA_NA offset in the packet template.
static const size_t DHCPV6_IA_NA_OFFSET = 40;

53 54 55 56 57 58 59
/// @brief Exception thrown when the required option is not found in a packet.
class OptionNotFound : public Exception {
public:
    OptionNotFound(const char* file, size_t line, const char* what) :
        isc::Exception(file, line, what) { };
};

60 61
/// \brief Test Control class.
///
62
/// This singleton class is used to run the performance test with
63 64
/// with \ref TestControl::run function. This function can be executed
/// multiple times if desired because it resets TestControl's internal
65
/// state every time it is executed. Prior to running \ref TestControl::run,
66
/// one must make sure to parse command line options by calling
67 68
/// \ref CommandOptions::parse. Failing to do this will result in an exception.
///
69 70
/// The following major stages of the test are performed by this class:
/// - set default transaction id and MAC address generators - the generator
71
/// is an object of \ref TestControl::NumberGenerator type and it provides
72 73 74 75 76 77 78 79 80
/// the custom randomization algorithms,
/// - print command line arguments,
/// - register option factory functions which are used to generate DHCP options
/// being sent to a server,
/// - create the socket for communication with a server,
/// - read packet templates if user specified template files with '-T' command
/// line option,
/// - set the interrupt handler (invoked when ^C is pressed) which makes
/// perfdhcp stop gracefully and print the test results before exiting,
81 82 83 84 85 86
/// - executes an external command (if specified '-w' option), e.g. if user
/// specified -w ./foo in the command line then program will execute
/// "./foo start" at the beginning of the test and "./foo stop" when the test
/// ends,
/// - initialize the Statistics Manager,
/// - executes the main loop:
87 88
///   - calculate how many packets must be send to satisfy desired rate,
///   - receive incoming packets from the server,
89
///   - check the exit conditions - terminate the program if the exit criteria
90
///   are fulfilled, e.g. reached maximum number of packet drops,
91
///   - send the number of packets appropriate to satisfy the desired rate,
92
///   - optionally print intermediate reports,
93
/// - print statistics, e.g. achieved rate,
94 95 96
/// - optionally print some diagnostics.
///
/// With the '-w' command line option user may specify the external application
Marcin Siodelski's avatar
Marcin Siodelski committed
97
/// or script to be executed. This is executed twice, first when the test starts
98 99 100 101
/// and second time when the test ends. This external script or application must
/// accept 'start' and 'stop' arguments. The first time it is called, it is
/// called with the argument 'start' and the second time with the argument
/// 'stop'.
102
///
Marcin Siodelski's avatar
Marcin Siodelski committed
103
/// The application is executed by calling fork() to fork the current perfdhcp
104 105
/// process and then call execlp() to replace the current process image with
/// the new one.
106
///
107
/// Option factory functions are registered using
108
/// \ref dhcp::LibDHCP::OptionFactoryRegister. Registered factory functions
109
/// provide a way to create options of the same type in the same way.
110
///  When a new option instance is needed, the corresponding factory
111
/// function is called to create it. This is done by calling
112
/// \ref dhcp::Option::factory with DHCP message type specified as one of
113 114
///  parameters. Some of the parameters passed to factory function
/// may be ignored (e.g. option buffer).
115 116 117 118 119 120
/// Please note that naming convention for factory functions within this
/// class is as follows:
/// - factoryABC4 - factory function for DHCPv4 option,
/// - factoryDEF6 - factory function for DHCPv6 option,
/// - factoryGHI - factory function that can be used to create either
/// DHCPv4 or DHCPv6 option.
121 122
class TestControl : public boost::noncopyable {
public:
123

124
    /// Statistics Manager for DHCPv4.
125
    typedef StatsMgr<dhcp::Pkt4> StatsMgr4;
126
    /// Pointer to Statistics Manager for DHCPv4;
127
    typedef boost::shared_ptr<StatsMgr4> StatsMgr4Ptr;
128
    /// Statistics Manager for DHCPv6.
129
    typedef StatsMgr<dhcp::Pkt6> StatsMgr6;
130
    /// Pointer to Statistics Manager for DHCPv6.
131
    typedef boost::shared_ptr<StatsMgr6> StatsMgr6Ptr;
132
    /// Packet exchange type.
133
    typedef StatsMgr<>::ExchangeType ExchangeType;
134
    /// Packet template buffer.
135
    typedef std::vector<uint8_t> TemplateBuffer;
136 137
    /// Packet template buffers list.
    typedef std::vector<TemplateBuffer> TemplateBufferCollection;
138

139 140 141 142 143 144 145 146 147
    /// \brief Socket wrapper structure.
    ///
    /// This is the wrapper that holds descriptor of the socket
    /// used to run DHCP test. The wrapped socket is closed in
    /// the destructor. This prevents resource leaks when when
    /// function that created the socket ends (normally or
    /// when exception occurs). This structure extends parent
    /// structure with new field ifindex_ that holds interface
    /// index where socket is bound to.
148
    struct TestControlSocket : public dhcp::SocketInfo {
149 150 151 152 153
        /// Interface index.
        uint16_t ifindex_;
        /// Is socket valid. It will not be valid if the provided socket
        /// descriptor does not point to valid socket.
        bool valid_;
154

155 156 157 158
        /// \brief Constructor of socket wrapper class.
        ///
        /// This constructor uses provided socket descriptor to
        /// find the name of the interface where socket has been
159 160
        /// bound to. If provided socket descriptor is invalid then
        /// valid_ field is set to false;
161 162
        ///
        /// \param socket socket descriptor.
163
        TestControlSocket(const int socket);
164

165
        /// \brief Destructor of the socket wrapper class.
166
        ///
167
        /// Destructor closes wrapped socket.
168 169 170
        ~TestControlSocket();

    private:
171
        /// \brief Initialize socket data.
172
        ///
173 174
        /// This method initializes members of the class that Interface
        /// Manager holds: interface name, local address.
175 176 177
        ///
        /// \throw isc::BadValue if interface for specified socket
        /// descriptor does not exist.
178
        void initSocketData();
179 180
    };

181 182 183 184 185
    /// \brief Number generator class.
    ///
    /// This is default numbers generator class. The member function is
    /// used to generate uint32_t values. Other generator classes should
    /// derive from this one to implement generation algorithms
186
    /// (e.g. sequential or based on random function).
187 188
    class NumberGenerator {
    public:
189 190 191 192

        /// \brief Destructor.
        virtual ~NumberGenerator() { }

193 194 195 196 197 198 199 200 201
        /// \brief Generate number.
        ///
        /// \return Generate number.
        virtual uint32_t generate() = 0;
    };

    /// The default generator pointer.
    typedef boost::shared_ptr<NumberGenerator> NumberGeneratorPtr;

202
    /// \brief Sequential numbers generator class.
203
    class SequentialGenerator : public NumberGenerator {
204
    public:
205
        /// \brief Constructor.
206
        ///
207
        /// \param range maximum number generated. If 0 is given then
208 209
        /// range defaults to maximum uint32_t value.
        SequentialGenerator(uint32_t range = 0xFFFFFFFF) :
210 211 212 213 214 215 216 217
            NumberGenerator(),
            num_(0),
            range_(range) {
            if (range_ == 0) {
                range_ = 0xFFFFFFFF;
            }
        }

218
        /// \brief Generate number sequentially.
219 220
        ///
        /// \return generated number.
221
        virtual uint32_t generate() {
222 223 224
            uint32_t num = num_;
            num_ = (num_ + 1) % range_;
            return (num);
225
        }
226 227
    private:
        uint32_t num_;   ///< Current number.
228
        uint32_t range_; ///< Number of unique numbers generated.
229 230
    };

231
    /// \brief Length of the Ethernet HW address (MAC) in bytes.
232 233 234
    ///
    /// \todo Make this variable length as there are cases when HW
    /// address is longer than this (e.g. 20 bytes).
235 236
    static const uint8_t HW_ETHER_LEN = 6;

237 238 239 240 241 242
    /// TestControl is a singleton class. This method returns reference
    /// to its sole instance.
    ///
    /// \return the only existing instance of test control
    static TestControl& instance();

243
    /// brief\ Run performance test.
244 245
    ///
    /// Method runs whole performance test. Command line options must
246
    /// be parsed prior to running this function. Otherwise function will
247 248 249
    /// throw exception.
    ///
    /// \throw isc::InvalidOperation if command line options are not parsed.
250
    /// \throw isc::Unexpected if internal Test Controller error occurred.
251 252 253
    /// \return error_code, 3 if number of received packets is not equal
    /// to number of sent packets, 0 if everything is ok.
    int run();
254

255 256 257
    /// \brief Set new transaction id generator.
    ///
    /// \param generator generator object to be used.
258
    void setTransidGenerator(const NumberGeneratorPtr& generator) {
259 260 261 262
        transid_gen_.reset();
        transid_gen_ = generator;
    }

263 264 265 266 267 268 269 270 271 272 273
    /// \brief Set new MAC address generator.
    ///
    /// Set numbers generator that will be used to generate various
    /// MAC addresses to simulate number of clients.
    ///
    /// \param generator object to be used.
    void setMacAddrGenerator(const NumberGeneratorPtr& generator) {
        macaddr_gen_.reset();
        macaddr_gen_ = generator;
    }

274
    // We would really like following methods and members to be private but
275 276 277
    // they have to be accessible for unit-testing. Another, possibly better,
    // solution is to make this class friend of test class but this is not
    // what's followed in other classes.
278
protected:
279
    /// \brief Default constructor.
280
    ///
281
    /// Default constructor is protected as the object can be created
282 283 284
    /// only via \ref instance method.
    TestControl();

285 286 287
    /// Generate uniformly distributed integers in range of [min, max]
    isc::util::random::UniformRandomIntegerGenerator number_generator_;

288
    /// \brief Check if test exit conditions fulfilled.
289
    ///
290
    /// Method checks if the test exit conditions are fulfilled.
291 292 293 294 295 296
    /// Exit conditions are checked periodically from the
    /// main loop. Program should break the main loop when
    /// this method returns true. It is calling function
    /// responsibility to break main loop gracefully and
    /// cleanup after test execution.
    ///
297
    /// \return true if any of the exit conditions is fulfilled.
298 299
    bool checkExitConditions() const;

300 301 302 303 304 305 306 307
    /// \brief Removes cached DHCPv6 Reply packets every second.
    ///
    /// This function wipes cached Reply packets from the storage.
    /// The number of packets left in the storage after the call
    /// to this function should guarantee that the Renew packets
    /// can be sent at the given rate. Note that the Renew packets
    /// are generated for the existing leases, represented here as
    /// replies from the server.
308 309 310
    /// @todo Instead of cleaning packets periodically we could
    /// just stop adding new packets when the certain threshold
    /// has been reached.
311 312
    void cleanCachedPackets();

313 314 315 316 317 318 319 320
    /// \brief Creates DHCPREQUEST from a DHCPACK message.
    ///
    /// \param ack An instance of the DHCPACK message to be used to
    /// create a new message.
    ///
    /// \return Pointer to the created message.
    dhcp::Pkt4Ptr createRequestFromAck(const dhcp::Pkt4Ptr& ack);

321
    /// \brief Creates DHCPv6 message from the Reply packet.
Marcin Siodelski's avatar
Marcin Siodelski committed
322
    ///
323 324 325 326
    /// This function creates DHCPv6 Renew or Release message using the
    /// data from the Reply message by copying options from the Reply
    /// message.
    ///
327
    /// \param msg_type A type of the message to be created.
Marcin Siodelski's avatar
Marcin Siodelski committed
328
    /// \param reply An instance of the Reply packet which contents should
329
    /// be used to create an instance of the new message.
Marcin Siodelski's avatar
Marcin Siodelski committed
330
    ///
331 332 333 334 335 336 337
    /// \return created Release or Renew message
    /// \throw isc::BadValue if the msg_type is neither DHCPV6_RENEW nor
    /// DHCPV6_RELEASE or if the reply is NULL.
    /// \throw isc::Unexpected if mandatory options are missing in the
    /// Reply message.
    dhcp::Pkt6Ptr createMessageFromReply(const uint16_t msg_type,
                                         const dhcp::Pkt6Ptr& reply);
Marcin Siodelski's avatar
Marcin Siodelski committed
338

339 340 341 342 343 344 345
    /// \brief Factory function to create DHCPv6 ELAPSED_TIME option.
    ///
    /// This factory function creates DHCPv6 ELAPSED_TIME option instance.
    /// If empty buffer is passed the option buffer will be initialized
    /// to length 2 and values will be initialized to zeros. Otherwise
    /// function will initialize option buffer with values in passed buffer.
    ///
346 347 348 349
    /// \param u universe (ignored)
    /// \param type option-type (ignored).
    /// \param buf option-buffer containing option content (2 bytes) or
    /// empty buffer if option content has to be set to default (0) value.
350 351
    /// \throw if elapsed time buffer size is neither 2 nor 0.
    /// \return instance o the option.
352
    static dhcp::OptionPtr
353 354 355
    factoryElapsedTime6(dhcp::Option::Universe u,
                        uint16_t type,
                        const dhcp::OptionBuffer& buf);
356

357 358
    /// \brief Factory function to create generic option.
    ///
359 360 361
    /// This factory function creates option with specified universe,
    /// type and buf. It does not have any additional logic validating
    /// the buffer contents, size  etc.
362 363
    ///
    /// \param u universe (V6 or V4).
364
    /// \param type option-type (ignored).
365
    /// \param buf option-buffer.
366
    /// \return instance o the option.
367 368 369 370
    static dhcp::OptionPtr factoryGeneric(dhcp::Option::Universe u,
                                          uint16_t type,
                                          const dhcp::OptionBuffer& buf);

371 372 373
    /// \brief Factory function to create IA_NA option.
    ///
    /// This factory function creates DHCPv6 IA_NA option instance.
374 375 376
    ///
    /// \todo add support for IA Address options.
    ///
377 378 379
    /// \param u universe (ignored).
    /// \param type option-type (ignored).
    /// \param buf option-buffer carrying IANA suboptions.
380
    /// \return instance of IA_NA option.
381 382 383 384
    static dhcp::OptionPtr factoryIana6(dhcp::Option::Universe u,
                                        uint16_t type,
                                        const dhcp::OptionBuffer& buf);

385 386 387 388 389 390 391 392 393 394 395
    /// \brief Factory function to create IA_PD option.
    ///
    /// this factory function creates DHCPv6 IA_PD option instance.
    ///
    /// \param u universe (ignored).
    /// \param type option-type (ignored).
    /// \param buf option-buffer carrying sub-options.
    static dhcp::OptionPtr factoryIapd6(dhcp::Option::Universe u,
                                        uint16_t type,
                                        const dhcp::OptionBuffer& buf);

396 397 398 399 400 401 402
    /// \brief Factory function to create DHCPv6 ORO option.
    ///
    /// This factory function creates DHCPv6 Option Request Option instance.
    /// The created option will contain the following set of requested options:
    /// - D6O_NAME_SERVERS
    /// - D6O_DOMAIN_SEARCH
    ///
403 404 405
    /// \param u universe (ignored).
    /// \param type option-type (ignored).
    /// \param buf option-buffer (ignored).
406
    /// \return instance of ORO option.
407 408 409 410 411
    static dhcp::OptionPtr
    factoryOptionRequestOption6(dhcp::Option::Universe u,
                                uint16_t type,
                                const dhcp::OptionBuffer& buf);

412 413 414 415 416 417
    /// \brief Factory function to create DHCPv6 RAPID_COMMIT option instance.
    ///
    /// This factory function creates DHCPv6 RAPID_COMMIT option instance.
    /// The buffer passed to this option must be empty because option does
    /// not have any payload.
    ///
418 419 420
    /// \param u universe (ignored).
    /// \param type option-type (ignored).
    /// \param buf option-buffer (ignored).
421
    /// \return instance of RAPID_COMMIT option..
422 423 424 425
    static dhcp::OptionPtr factoryRapidCommit6(dhcp::Option::Universe u,
                                               uint16_t type,
                                               const dhcp::OptionBuffer& buf);

426

427 428
    /// \brief Factory function to create DHCPv4 Request List option.
    ///
429
    /// This factory function creates DHCPv4 PARAMETER_REQUEST_LIST option
430
    /// instance with the following set of requested options:
431 432 433 434 435 436 437 438
    /// - DHO_SUBNET_MASK,
    /// - DHO_BROADCAST_ADDRESS,
    /// - DHO_TIME_OFFSET,
    /// - DHO_ROUTERS,
    /// - DHO_DOMAIN_NAME,
    /// - DHO_DOMAIN_NAME_SERVERS,
    /// - DHO_HOST_NAME.
    ///
439 440 441
    /// \param u universe (ignored).
    /// \param type option-type (ignored).
    /// \param buf option-buffer (ignored).
442 443
    /// \return instance o the generic option.
     static dhcp::OptionPtr factoryRequestList4(dhcp::Option::Universe u,
444 445 446
                                               uint16_t type,
                                               const dhcp::OptionBuffer& buf);

447 448 449 450 451 452 453 454 455 456
    /// \brief Generate DHCPv4 client identifier from HW address.
    ///
    /// This method generates DHCPv4 client identifier option from a
    /// HW address.
    ///
    /// \param hwaddr HW address.
    ///
    /// \return Pointer to an instance of the generated option.
    dhcp::OptionPtr generateClientId(const dhcp::HWAddrPtr& hwaddr) const;

457 458 459
    /// \brief Generate DUID.
    ///
    /// Method generates unique DUID. The number of DUIDs it can generate
460
    /// depends on the number of simulated clients, which is specified
461
    /// from the command line. It uses \ref CommandOptions object to retrieve
462
    /// number of clients. Since the last six octets of DUID are constructed
463 464 465
    /// from the MAC address, this function uses \ref generateMacAddress
    /// internally to randomize the DUID.
    ///
466 467
    /// \todo add support for other types of DUID.
    ///
468 469
    /// \param [out] randomized number of bytes randomized (initial value
    /// is ignored).
470 471
    /// \throw isc::BadValue if \ref generateMacAddress throws.
    /// \return vector representing DUID.
472
    std::vector<uint8_t> generateDuid(uint8_t& randomized);
473

474 475 476 477 478 479 480
    /// \brief Generate MAC address.
    ///
    /// This method generates MAC address. The number of unique
    /// MAC addresses it can generate is determined by the number
    /// simulated DHCP clients specified from command line. It uses
    /// \ref CommandOptions object to retrieve number of clients.
    /// Based on this the random value is generated and added to
481
    /// the MAC address template (default MAC address).
482
    ///
483 484
    /// \param [out] randomized number of bytes randomized (initial
    /// value is ignored).
485
    /// \throw isc::BadValue if MAC address template (default or specified
486
    /// from the command line) has invalid size (expected 6 octets).
487
    /// \return generated MAC address.
488
    std::vector<uint8_t> generateMacAddress(uint8_t& randomized);
489

490 491
    /// \brief generate transaction id.
    ///
492 493 494
    /// Generate transaction id value (32-bit for DHCPv4,
    /// 24-bit for DHCPv6).
    ///
495 496
    /// \return generated transaction id.
    uint32_t generateTransid() {
497
        return (transid_gen_->generate());
498 499
    }

500 501 502 503 504 505 506 507 508
    /// \brief Returns a timeout for packet reception.
    ///
    /// The calculation is based on the value of the timestamp
    /// when the next set of packets is to be sent. If no packet is
    /// received until then, new packets are sent.
    ///
    /// \return A current timeout in microseconds.
    uint32_t getCurrentTimeout() const;

509 510 511 512 513
    /// \brief Return template buffer.
    ///
    /// Method returns template buffer at specified index.
    ///
    /// \param idx index of template buffer.
514 515
    /// \throw isc::OutOfRange if buffer index out of bounds.
    /// \return reference to template buffer.
516 517 518 519 520
    TemplateBuffer getTemplateBuffer(const size_t idx) const;

    /// \brief Reads packet templates from files.
    ///
    /// Method iterates through all specified template files, reads
521 522
    /// their content and stores it in class internal buffers. Template
    /// file names are specified from the command line with -T option.
523
    ///
524 525 526 527
    /// \throw isc::BadValue if any of the template files does not exist,
    /// contains characters other than hexadecimal digits or spaces.
    /// \throw OutOfRange if any of the template files is empty or has
    /// odd number of hexadecimal digits.
528 529
    void initPacketTemplates();

530 531 532 533 534 535
    /// \brief Initializes Statistics Manager.
    ///
    /// This function initializes Statistics Manager. If there is
    /// the one initialized already it is released.
    void initializeStatsMgr();

536 537 538
    /// \brief Open socket to communicate with DHCP server.
    ///
    /// Method opens socket and binds it to local address. Function will
539
    /// use either interface name, local address or server address
540 541 542
    /// to create a socket, depending on what is available (specified
    /// from the command line). If socket can't be created for any
    /// reason, exception is thrown.
543 544
    /// If destination address is broadcast (for DHCPv4) or multicast
    /// (for DHCPv6) than broadcast or multicast option is set on
545
    /// the socket. Opened socket is registered and managed by IfaceMgr.
546
    ///
547 548 549
    /// \throw isc::BadValue if socket can't be created for given
    /// interface, local address or remote address.
    /// \throw isc::InvalidOperation if broadcast option can't be
550
    /// set for the v4 socket or if multicast option can't be set
551
    /// for the v6 socket.
552
    /// \throw isc::Unexpected if internal unexpected error occurred.
553
    /// \return socket descriptor.
554
    int openSocket() const;
555

556 557 558 559 560 561
    /// \brief Print intermediate statistics.
    ///
    /// Print brief statistics regarding number of sent packets,
    /// received packets and dropped packets so far.
    void printIntermediateStats();

562 563 564 565 566
    /// \brief Print rate statistics.
    ///
    /// Method print packet exchange rate statistics.
    void printRate() const;

567 568 569 570 571 572 573
    /// \brief Print performance statistics.
    ///
    /// Method prints performance statistics.
    /// \throws isc::InvalidOperation if Statistics Manager was
    /// not initialized.
    void printStats() const;

574
    /// \brief Process received DHCPv4 packet.
575
    ///
576 577 578
    /// Method performs processing of the received DHCPv4 packet,
    /// updates statistics and responds to the server if required,
    /// e.g. when OFFER packet arrives, this function will initiate
579 580
    /// REQUEST message to the server.
    ///
581 582 583 584 585
    /// \warning this method does not check if provided socket is
    /// valid (specifically if v4 socket for received v4 packet).
    ///
    /// \param [in] socket socket to be used.
    /// \param [in] pkt4 object representing DHCPv4 packet received.
586
    /// \throw isc::BadValue if unknown message type received.
587
    /// \throw isc::Unexpected if unexpected error occurred.
588 589
    void processReceivedPacket4(const TestControlSocket& socket,
                                const dhcp::Pkt4Ptr& pkt4);
590

591
    /// \brief Process received DHCPv6 packet.
592
    ///
593
    /// Method performs processing of the received DHCPv6 packet,
594
    /// updates statistics and responds to the server if required,
595
    /// e.g. when ADVERTISE packet arrives, this function will initiate
596 597
    /// REQUEST message to the server.
    ///
598 599 600 601 602
    /// \warning this method does not check if provided socket is
    /// valid (specifically if v4 socket for received v4 packet).
    ///
    /// \param [in] socket socket to be used.
    /// \param [in] pkt6 object representing DHCPv6 packet received.
603
    /// \throw isc::BadValue if unknown message type received.
604
    /// \throw isc::Unexpected if unexpected error occurred.
605 606
    void processReceivedPacket6(const TestControlSocket& socket,
                                const dhcp::Pkt6Ptr& pkt6);
607

608 609 610
    /// \brief Receive DHCPv4 or DHCPv6 packets from the server.
    ///
    /// Method receives DHCPv4 or DHCPv6 packets from the server.
611 612
    /// This function will call \ref processReceivedPacket4 or
    /// \ref processReceivedPacket6 depending if DHCPv4 or DHCPv6 packet
613 614
    /// has arrived.
    ///
615 616 617
    /// \warning this method does not check if provided socket is
    /// valid. Ensure that it is valid prior to calling it.
    ///
618
    /// \param socket socket to be used.
619
    /// \throw isc::BadValue if unknown message type received.
620
    /// \throw isc::Unexpected if unexpected error occurred.
621 622
    /// \return number of received packets.
    uint64_t receivePackets(const TestControlSocket& socket);
623

624 625 626 627
    /// \brief Register option factory functions for DHCPv4
    ///
    /// Method registers option factory functions for DHCPv4.
    /// These functions are called to create instances of DHCPv4
628
    /// options. Call \ref dhcp::Option::factory to invoke factory
629 630
    /// function for particular option. Don't use this function directly.
    /// Use \ref registerOptionFactories instead.
631 632
    void registerOptionFactories4() const;

633 634 635 636
    /// \brief Register option factory functions for DHCPv6
    ///
    /// Method registers option factory functions for DHCPv6.
    /// These functions are called to create instances of DHCPv6
637
    /// options. Call \ref dhcp::Option::factory to invoke factory
638 639
    /// function for particular option. Don't use this function directly.
    /// Use \ref registerOptionFactories instead.
640 641
    void registerOptionFactories6() const;

642 643 644
    /// \brief Register option factory functions for DHCPv4 or DHCPv6.
    ///
    /// Method registers option factory functions for DHCPv4 or DHCPv6,
645
    /// depending in which mode test is currently running.
646 647
    void registerOptionFactories() const;

648

649
    /// \brief Resets internal state of the object.
650
    ///
651
    /// Method resets internal state of the object. It has to be
652 653 654
    /// called before new test is started.
    void reset();

655 656 657 658 659 660 661 662
    /// \brief Save the first DHCPv4 sent packet of the specified type.
    ///
    /// This method saves first packet of the specified being sent
    /// to the server if user requested diagnostics flag 'T'. In
    /// such case program has to print contents of selected packets
    /// being sent to the server. It collects first packets of each
    /// type and keeps them around until test finishes. Then they
    /// are printed to the user. If packet of specified type has
663
    /// been already stored this function performs no operation.
664
    /// This function does not perform sanity check if packet
665 666 667 668 669 670 671 672 673 674 675 676 677
    /// pointer is valid. Make sure it is before calling it.
    ///
    /// \param pkt packet to be stored.
    inline void saveFirstPacket(const dhcp::Pkt4Ptr& pkt);

    /// \brief Save the first DHCPv6 sent packet of the specified type.
    ///
    /// This method saves first packet of the specified being sent
    /// to the server if user requested diagnostics flag 'T'. In
    /// such case program has to print contents of selected packets
    /// being sent to the server. It collects first packets of each
    /// type and keeps them around until test finishes. Then they
    /// are printed to the user. If packet of specified type has
678
    /// been already stored this function performs no operation.
679
    /// This function does not perform sanity check if packet
680 681 682 683 684
    /// pointer is valid. Make sure it is before calling it.
    ///
    /// \param pkt packet to be stored.
    inline void saveFirstPacket(const dhcp::Pkt6Ptr& pkt);

685 686 687 688 689 690
    /// \brief Send DHCPv4 DISCOVER message.
    ///
    /// Method creates and sends DHCPv4 DISCOVER message to the server
    /// with the following options:
    /// - MESSAGE_TYPE set to DHCPDISCOVER
    /// - PARAMETER_REQUEST_LIST with the same list of requested options
691
    /// as described in \ref factoryRequestList4.
692 693 694
    /// The transaction id and MAC address are randomly generated for
    /// the message. Range of unique MAC addresses generated depends
    /// on the number of clients specified from the command line.
695 696
    /// Copy of sent packet is stored in the stats_mgr4_ object to
    /// update statistics.
697 698
    ///
    /// \param socket socket to be used to send the message.
699
    /// \param preload preload mode, packets not included in statistics.
700
    ///
701 702
    /// \throw isc::Unexpected if failed to create new packet instance.
    /// \throw isc::BadValue if MAC address has invalid length.
703
    /// \throw isc::dhcp::SocketWriteError if failed to send the packet.
704 705
    void sendDiscover4(const TestControlSocket& socket,
                       const bool preload = false);
706

707 708 709
    /// \brief Send DHCPv4 DISCOVER message from template.
    ///
    /// Method sends DHCPv4 DISCOVER message from template. The
710
    /// template data is expected to be in binary format. Provided
711 712
    /// buffer is copied and parts of it are replaced with actual
    /// data (e.g. MAC address, transaction id etc.).
713 714
    /// Copy of sent packet is stored in the stats_mgr4_ object to
    /// update statistics.
715 716 717 718
    ///
    /// \param socket socket to be used to send the message.
    /// \param template_buf buffer holding template packet.
    /// \param preload preload mode, packets not included in statistics.
719
    ///
720
    /// \throw isc::OutOfRange if randomization offset is out of bounds.
721
    /// \throw isc::dhcp::SocketWriteError if failed to send the packet.
722 723 724 725
    void sendDiscover4(const TestControlSocket& socket,
                       const std::vector<uint8_t>& template_buf,
                       const bool preload = false);

726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750
    /// \brief Send number of packets to initiate new exchanges.
    ///
    /// Method initiates the new DHCP exchanges by sending number
    /// of DISCOVER (DHCPv4) or SOLICIT (DHCPv6) packets. If preload
    /// mode was requested sent packets will not be counted in
    /// the statistics. The responses from the server will be
    /// received and counted as orphans because corresponding sent
    /// packets are not included in StatsMgr for match.
    /// When preload mode is disabled and diagnostics flag 'i' is
    /// specified then function will be trying to receive late packets
    /// before new packets are sent to the server. Statistics of
    /// late received packets is updated accordingly.
    ///
    /// \todo do not count responses in preload mode as orphans.
    ///
    /// \param socket socket to be used to send packets.
    /// \param packets_num number of packets to be sent.
    /// \param preload preload mode, packets not included in statistics.
    /// \throw isc::Unexpected if thrown by packet sending method.
    /// \throw isc::InvalidOperation if thrown by packet sending method.
    /// \throw isc::OutOfRange if thrown by packet sending method.
    void sendPackets(const TestControlSocket &socket,
                     const uint64_t packets_num,
                     const bool preload = false);

751 752 753 754 755 756 757 758 759
    /// \brief Send number of DHCPREQUEST (renew) messages to a server.
    ///
    /// \param socket An object representing socket to be used to send packets.
    /// \param msg_num A number of messages to be sent.
    ///
    /// \return A number of messages actually sent.
    uint64_t sendMultipleRequests(const TestControlSocket& socket,
                                  const uint64_t msg_num);

760
    /// \brief Send number of DHCPv6 Renew or Release messages to the server.
Marcin Siodelski's avatar
Marcin Siodelski committed
761 762
    ///
    /// \param socket An object representing socket to be used to send packets.
763 764 765
    /// \param msg_type A type of the messages to be sent (DHCPV6_RENEW or
    /// DHCPV6_RELEASE).
    /// \param msg_num A number of messages to be sent.
Marcin Siodelski's avatar
Marcin Siodelski committed
766
    ///
767 768 769 770
    /// \return A number of messages actually sent.
    uint64_t sendMultipleMessages6(const TestControlSocket& socket,
                                   const uint32_t msg_type,
                                   const uint64_t msg_num);
Marcin Siodelski's avatar
Marcin Siodelski committed
771

772 773 774 775 776 777 778 779
    /// \brief Send DHCPv4 renew (DHCPREQUEST) using specified socket.
    ///
    /// \param socket An object encapsulating socket to be used to send
    /// a packet.
    ///
    /// \return true if the message has been sent, false otherwise.
    bool sendRequestFromAck(const TestControlSocket& socket);

780
    /// \brief Send DHCPv6 Renew or Release message using specified socket.
Marcin Siodelski's avatar
Marcin Siodelski committed
781
    ///
782
    /// This method will select an existing lease from the Reply packet cache
783 784
    /// If there is no lease that can be renewed or released this method will
    /// return false.
Marcin Siodelski's avatar
Marcin Siodelski committed
785
    ///
786 787
    /// \param msg_type A type of the message to be sent (DHCPV6_RENEW or
    /// DHCPV6_RELEASE).
Marcin Siodelski's avatar
Marcin Siodelski committed
788 789 790
    /// \param socket An object encapsulating socket to be used to send
    /// a packet.
    ///
791 792 793
    /// \return true if the message has been sent, false otherwise.
    bool sendMessageFromReply(const uint16_t msg_type,
                              const TestControlSocket& socket);
Marcin Siodelski's avatar
Marcin Siodelski committed
794

795 796 797
    /// \brief Send DHCPv4 REQUEST message.
    ///
    /// Method creates and sends DHCPv4 REQUEST message to the server.
798 799
    /// Copy of sent packet is stored in the stats_mgr4_ object to
    /// update statistics.
800 801
    ///
    /// \param socket socket to be used to send message.
802
    /// \param discover_pkt4 DISCOVER packet sent.
803
    /// \param offer_pkt4 OFFER packet object.
804
    ///
805
    /// \throw isc::Unexpected if unexpected error occurred.
806 807
    /// \throw isc::InvalidOperation if Statistics Manager has not been
    /// initialized.
808
    /// \throw isc::dhcp::SocketWriteError if failed to send the packet.
809
    void sendRequest4(const TestControlSocket& socket,
810 811 812 813 814 815
                      const dhcp::Pkt4Ptr& discover_pkt4,
                      const dhcp::Pkt4Ptr& offer_pkt4);

    /// \brief Send DHCPv4 REQUEST message from template.
    ///
    /// Method sends DHCPv4 REQUEST message from template.
816 817
    /// Copy of sent packet is stored in the stats_mgr4_ object to
    /// update statistics.
818 819 820 821 822
    ///
    /// \param socket socket to be used to send message.
    /// \param template_buf buffer holding template packet.
    /// \param discover_pkt4 DISCOVER packet sent.
    /// \param offer_pkt4 OFFER packet received.
823 824
    ///
    /// \throw isc::dhcp::SocketWriteError if failed to send the packet.
825 826 827
    void sendRequest4(const TestControlSocket& socket,
                      const std::vector<uint8_t>& template_buf,
                      const dhcp::Pkt4Ptr& discover_pkt4,
828 829 830
                      const dhcp::Pkt4Ptr& offer_pkt4);

    /// \brief Send DHCPv6 REQUEST message.
831 832 833 834 835 836
    ///
    /// Method creates and sends DHCPv6 REQUEST message to the server
    /// with the following options:
    /// - D6O_ELAPSED_TIME
    /// - D6O_CLIENTID
    /// - D6O_SERVERID
837 838
    /// Copy of sent packet is stored in the stats_mgr6_ object to
    /// update statistics.
839 840 841
    ///
    /// \param socket socket to be used to send message.
    /// \param advertise_pkt6 ADVERTISE packet object.
842
    /// \throw isc::Unexpected if unexpected error occurred.
843 844
    /// \throw isc::InvalidOperation if Statistics Manager has not been
    /// initialized.
845 846
    ///
    /// \throw isc::dhcp::SocketWriteError if failed to send the packet.
847 848 849
    void sendRequest6(const TestControlSocket& socket,
                      const dhcp::Pkt6Ptr& advertise_pkt6);

850 851 852
    /// \brief Send DHCPv6 REQUEST message from template.
    ///
    /// Method sends DHCPv6 REQUEST message from template.
853 854
    /// Copy of sent packet is stored in the stats_mgr6_ object to
    /// update statistics.
855 856 857 858
    ///
    /// \param socket socket to be used to send message.
    /// \param template_buf packet template buffer.
    /// \param advertise_pkt6 ADVERTISE packet object.
859 860
    ///
    /// \throw isc::dhcp::SocketWriteError if failed to send the packet.
861 862 863 864
    void sendRequest6(const TestControlSocket& socket,
                      const std::vector<uint8_t>& template_buf,
                      const dhcp::Pkt6Ptr& advertise_pkt6);

865 866 867 868 869 870 871 872 873
    /// \brief Send DHCPv6 SOLICIT message.
    ///
    /// Method creates and sends DHCPv6 SOLICIT message to the server
    /// with the following options:
    /// - D6O_ELAPSED_TIME,
    /// - D6O_RAPID_COMMIT if rapid commit is requested in command line,
    /// - D6O_CLIENTID,
    /// - D6O_ORO (Option Request Option),
    /// - D6O_IA_NA.
874 875
    /// Copy of sent packet is stored in the stats_mgr6_ object to
    /// update statistics.
876 877
    ///
    /// \param socket socket to be used to send the message.
878
    /// \param preload mode, packets not included in statistics.
879
    ///
880
    /// \throw isc::Unexpected if failed to create new packet instance.
881
    /// \throw isc::dhcp::SocketWriteError if failed to send the packet.
882 883
    void sendSolicit6(const TestControlSocket& socket,
                      const bool preload = false);
884

885 886 887
    /// \brief Send DHCPv6 SOLICIT message from template.
    ///
    /// Method sends DHCPv6 SOLICIT message from template.
888 889
    /// Copy of sent packet is stored in the stats_mgr6_ object to
    /// update statistics.
890 891 892 893
    ///
    /// \param socket socket to be used to send the message.
    /// \param template_buf packet template buffer.
    /// \param preload mode, packets not included in statistics.
894 895
    ///
    /// \throw isc::dhcp::SocketWriteError if failed to send the packet.
896 897 898 899
    void sendSolicit6(const TestControlSocket& socket,
                      const std::vector<uint8_t>& template_buf,
                      const bool preload = false);

900
    /// \brief Set default DHCPv4 packet parameters.
901
    ///
902
    /// This method sets default parameters on the DHCPv4 packet:
903 904 905 906 907 908
    /// - interface name,
    /// - local port = 68 (DHCP client port),
    /// - remote port = 67 (DHCP server port),
    /// - server's address,
    /// - GIADDR = local address where socket is bound to,
    /// - hops = 1 (pretending that we are a relay)
909
    ///
910
    /// \param socket socket used to send the packet.
911
    /// \param pkt reference to packet to be configured.
912
    void setDefaults4(const TestControlSocket& socket,
913
                      const dhcp::Pkt4Ptr& pkt);
914

915 916 917 918 919 920 921 922 923 924 925 926
    /// \brief Set default DHCPv6 packet parameters.
    ///
    /// This method sets default parameters on the DHCPv6 packet:
    /// - interface name,
    /// - interface index,
    /// - local port,
    /// - remote port,
    /// - local address,
    /// - remote address (server).
    ///
    /// \param socket socket used to send the packet.
    /// \param pkt reference to packet to be configured.
927
    void setDefaults6(const TestControlSocket& socket,
928
                      const dhcp::Pkt6Ptr& pkt);
929

930
    /// \brief Find if diagnostic flag has been set.
931 932 933 934 935
    ///
    /// \param diag diagnostic flag (a,e,i,s,r,t,T).
    /// \return true if diagnostics flag has been set.
    bool testDiags(const char diag) const;

936 937 938
protected:

    /// \brief Increments counter of late sent messages if required.
939
    ///
940 941 942 943
    /// This function checks if the message or set of messages of a given type,
    /// were sent later than their due time. If they were sent late, it is
    /// an indication that the perfdhcp doesn't catch up with the desired rate
    /// for sending messages.
944
    ///
945 946 947
    /// \param rate_control An object tracking due times for a particular
    /// type of messages.
    void checkLateMessages(RateControl& rate_control);
948

949 950 951 952 953 954 955 956 957 958
    /// \brief Copies IA_NA or IA_PD option from one packet to another.
    ///
    /// This function checks the lease-type specified in the command line
    /// with option -e<lease-type>. If 'address-only' value has been specified
    /// this function expects that IA_NA option is present in the packet
    /// encapsulated by pkt_from object. If 'prefix-only' value has been
    /// specified, this function expects that IA_PD option is present in the
    /// packet encapsulated by pkt_to object.
    ///
    /// \param [in] pkt_from A packet from which options should be copied.
959
    /// \param [out] pkt_to A packet to which options should be copied.
960 961 962 963 964 965 966
    ///
    /// \throw isc::perfdhcp::OptionNotFound if a required option is not
    /// found in the packet from which options should be copied.
    /// \throw isc::BadValue if any of the specified pointers to packets
    /// is NULL.
    void copyIaOptions(const dhcp::Pkt6Ptr& pkt_from, dhcp::Pkt6Ptr& pkt_to);

967 968
    /// \brief Convert binary value to hex string.
    ///
969 970
    /// \todo Consider moving this function to src/lib/util.
    ///
971 972 973 974
    /// \param b byte to convert.
    /// \return hex string.
    std::string byte2Hex(const uint8_t b) const;

975 976
    /// \brief Calculate elapsed time between two packets.
    ///
977 978 979 980 981
    /// This function calculates the time elapsed between two packets. If
    /// the timestamp of the pkt2 is greater than timestamp of the pkt1,
    /// the positive value is returned. If the pkt2 timestamp is equal or
    /// less than pkt1 timestamp, 0 is returned.
    ///
982
    /// \tparam T Pkt4Ptr or Pkt6Ptr class.
983 984
    /// \param pkt1 first packet.
    /// \param pkt2 second packet.
985
    /// \throw InvalidOperation if packet timestamps are invalid.
986 987
    /// \return elapsed time in milliseconds between pkt1 and pkt2.
    template<class T>
988
    uint32_t getElapsedTime(const T& pkt1, const T& pkt2);
989

990
    /// \brief Return elapsed time offset in a packet.
991 992 993 994
    ///
    /// \return elapsed time offset in packet.
    int getElapsedTimeOffset() const;

995
    /// \brief Return randomization offset in a packet.
996 997 998 999
    ///
    /// \return randomization offset in packet.
    int getRandomOffset(const int arg_idx) const;

1000
    /// \brief Return requested ip offset in a packet.
1001
    ///
1002
    /// \return randomization offset in a packet.
1003 1004
    int getRequestedIpOffset() const;

1005
    /// \brief Return server id offset in a packet.
1006 1007 1008 1009
    ///
    /// \return server id offset in packet.
    int getServerIdOffset() const;

1010
    /// \brief Return transaction id offset in a packet.
1011 1012
    ///
    /// \param arg_idx command line argument index to be used.
1013
    /// If multiple -X parameters specified it points to the
1014 1015 1016 1017
    /// one to be used.
    /// \return transaction id offset in packet.
    int getTransactionIdOffset(const int arg_idx) const;

1018 1019 1020 1021 1022
    /// \brief Get number of received packets.
    ///
    /// Get the number of received packets from the Statistics Manager.
    /// Function may throw if Statistics Manager object is not
    /// initialized.
1023 1024 1025 1026 1027 1028
    ///
    /// \note The method parameter is non-const to suppress the cppcheck
    /// warning about the object being passed by value. However, passing
    /// an enum by reference doesn't make much sense. At the same time,
    /// removing the constness should be pretty safe for this function.
    ///
1029 1030
    /// \param xchg_type packet exchange type.
    /// \return number of received packets.
1031
    uint64_t getRcvdPacketsNum(ExchangeType xchg_type) const;
1032 1033 1034 1035 1036 1037

    /// \brief Get number of sent packets.
    ///
    /// Get the number of sent packets from the Statistics Manager.
    /// Function may throw if Statistics Manager object is not
    /// initialized.
1038 1039 1040 1041 1042