diff options
Diffstat (limited to 'src/bin/perfdhcp/test_control.h')
-rw-r--r-- | src/bin/perfdhcp/test_control.h | 1116 |
1 files changed, 1116 insertions, 0 deletions
diff --git a/src/bin/perfdhcp/test_control.h b/src/bin/perfdhcp/test_control.h new file mode 100644 index 0000000..17700ce --- /dev/null +++ b/src/bin/perfdhcp/test_control.h @@ -0,0 +1,1116 @@ +// Copyright (C) 2012-2022 Internet Systems Consortium, Inc. ("ISC") +// +// 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/. + +#ifndef TEST_CONTROL_H +#define TEST_CONTROL_H + +#include <perfdhcp/packet_storage.h> +#include <perfdhcp/rate_control.h> +#include <perfdhcp/stats_mgr.h> +#include <perfdhcp/receiver.h> +#include <perfdhcp/command_options.h> +#include <perfdhcp/perf_socket.h> +#include <perfdhcp/random_number_generator.h> + +#include <dhcp/iface_mgr.h> +#include <dhcp/dhcp4.h> +#include <dhcp/dhcp6.h> +#include <dhcp/pkt4.h> +#include <dhcp/pkt6.h> + +#include <boost/noncopyable.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/date_time/posix_time/posix_time.hpp> + +#include <string> +#include <vector> +#include <unordered_map> + +namespace isc { +namespace perfdhcp { + +/// 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; + +/// \brief Test Control class. +/// +/// This class is used to run the performance test with +/// with \ref TestControl::runWrapped function. This function can be executed +/// multiple times if desired because it resets TestControl's internal +/// state every time it is executed. Prior to running \ref TestControl::runWrapped, +/// one must make sure to parse command line options by calling +/// \ref CommandOptions::parse. Failing to do this will result in an exception. +/// +/// The following major stages of the test are performed by this class: +/// - set default transaction id and MAC address generators - the generator +/// is an object of \ref TestControl::NumberGenerator type and it provides +/// 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, +/// - 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: +/// - calculate how many packets must be send to satisfy desired rate, +/// - receive incoming packets from the server, +/// - check the exit conditions - terminate the program if the exit criteria +/// are fulfilled, e.g. reached maximum number of packet drops, +/// - send the number of packets appropriate to satisfy the desired rate, +/// - optionally print intermediate reports, +/// - print statistics, e.g. achieved rate, +/// - optionally print some diagnostics. +/// +/// With the '-w' command line option user may specify the external application +/// or script to be executed. This is executed twice, first when the test starts +/// 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'. +/// +/// The application is executed by calling fork() to fork the current perfdhcp +/// process and then call execlp() to replace the current process image with +/// the new one. +/// +/// Option factory functions are registered using +/// \ref dhcp::LibDHCP::OptionFactoryRegister. Registered factory functions +/// provide a way to create options of the same type in the same way. +/// When a new option instance is needed, the corresponding factory +/// function is called to create it. This is done by calling +/// \ref dhcp::Option::factory with DHCP message type specified as one of +/// parameters. Some of the parameters passed to factory function +/// may be ignored (e.g. option buffer). +/// 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. +class TestControl : public boost::noncopyable { +public: + /// \brief Default constructor. + TestControl(CommandOptions& options, BasePerfSocket& socket); + + /// Packet template buffer. + typedef std::vector<uint8_t> TemplateBuffer; + /// Packet template buffers list. + typedef std::vector<TemplateBuffer> TemplateBufferCollection; + + /// @brief Delay the exit by a fixed given time to catch up to all exchanges + /// that were already started. + /// @return true if need to wait, false = ok to exit now + bool waitToExit(); + + /// @brief Checks if all expected packets were already received + bool haveAllPacketsBeenReceived() const; + + /// \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 + /// (e.g. sequential or based on random function). + class NumberGenerator { + public: + + /// \brief Destructor. + virtual ~NumberGenerator() { } + + /// \brief Generate number. + /// + /// \return Generate number. + virtual uint32_t generate() = 0; + }; + + /// The default generator pointer. + typedef boost::shared_ptr<NumberGenerator> NumberGeneratorPtr; + + /// \brief Sequential numbers generator class. + class SequentialGenerator : public NumberGenerator { + public: + /// \brief Constructor. + /// + /// \param range maximum number generated. If 0 is given then + /// range defaults to maximum uint32_t value. + SequentialGenerator(uint32_t range = 0xFFFFFFFF) : + NumberGenerator(), + num_(0), + range_(range) { + if (range_ == 0) { + range_ = 0xFFFFFFFF; + } + } + + /// \brief Generate number sequentially. + /// + /// \return generated number. + virtual uint32_t generate() { + uint32_t num = num_; + num_ = (num_ + 1) % range_; + return (num); + } + private: + uint32_t num_; ///< Current number. + uint32_t range_; ///< Number of unique numbers generated. + }; + + /// \brief Length of the Ethernet HW address (MAC) in bytes. + /// + /// \todo Make this variable length as there are cases when HW + /// address is longer than this (e.g. 20 bytes). + static const uint8_t HW_ETHER_LEN = 6; + + /// \brief Set new transaction id generator. + /// + /// \param generator generator object to be used. + void setTransidGenerator(const NumberGeneratorPtr& generator) { + transid_gen_.reset(); + transid_gen_ = generator; + } + + /// \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; + } + + /// \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. + /// @todo Instead of cleaning packets periodically we could + /// just stop adding new packets when the certain threshold + /// has been reached. + void cleanCachedPackets(); + + /// \brief Get interrupted flag. + bool interrupted() const { return interrupted_; } + + /// \brief Get stats manager. + StatsMgr& getStatsMgr() { return stats_mgr_; }; + + /// \brief Start receiver. + void start() { receiver_.start(); } + + /// \brief Stop receiver. + void stop() { receiver_.stop(); } + + /// \brief Run wrapped command. + /// + /// \param do_stop execute wrapped command with "stop" argument. + void runWrapped(bool do_stop = false) const; + + /// \brief Get received server id flag. + bool serverIdReceived() const { return first_packet_serverid_.size() > 0; } + + /// \brief Get received server id. + std::string getServerId() const { return vector2Hex(first_packet_serverid_); } + + /// \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 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 uint64_t packets_num, + const bool preload = false); + + /// \brief Send number of DHCPREQUEST (renew) messages to a server. + /// + /// \param msg_type A type of the messages to be sent (DHCPREQUEST or + /// DHCPRELEASE). + /// \param msg_num A number of messages to be sent. + /// + /// \return A number of messages actually sent. + uint64_t sendMultipleMessages4(const uint32_t msg_type, + const uint64_t msg_num); + + /// \brief Send number of DHCPv6 Renew or Release messages to the server. + /// + /// \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. + /// + /// \return A number of messages actually sent. + uint64_t sendMultipleMessages6(const uint32_t msg_type, + const uint64_t msg_num); + + /// \brief Pull packets from receiver and process them. + /// + /// It runs in a loop until there are no packets in receiver. + unsigned int consumeReceivedPackets(); + + /// \brief Print intermediate statistics. + /// + /// Print brief statistics regarding number of sent packets, + /// received packets and dropped packets so far. + void printIntermediateStats(); + + /// \brief Print performance statistics. + /// + /// Method prints performance statistics. + /// \throws isc::InvalidOperation if Statistics Manager was + /// not initialized. + void printStats() const; + + /// \brief Print templates information. + /// + /// Method prints information about data offsets + /// in packet templates and their contents. + void printTemplates() const; + + /// \brief Get set of unique replied addresses. + std::set<std::string>& getAllUniqueAddrReply() { + return unique_reply_address_; + } + + /// \brief Get set of unique advertised addresses. + std::set<std::string>& getAllUniqueAddrAdvert() { + return unique_address_; + } + + /// \brief Convert binary value to hex string. + /// + /// \todo Consider moving this function to src/lib/util. + /// + /// \param b byte to convert. + /// \return hex string. + static std::string byte2Hex(const uint8_t b); + + /// \brief Convert vector in hexadecimal string. + /// + /// \todo Consider moving this function to src/lib/util. + /// + /// \param vec vector to be converted. + /// \param separator separator. + static std::string vector2Hex(const std::vector<uint8_t>& vec, + const std::string& separator = ""); + + /// \brief Initialized at first exit condition with the time perfdhcp + /// should exit + boost::posix_time::ptime exit_time_; + + // We would really like following methods and members to be private but + // 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. +protected: + /// Generate uniformly distributed integers in range of [min, max] + UniformRandomIntegerGenerator number_generator_; + + /// \brief Creates DHCPREQUEST from a DHCPACK message. + /// + /// @param msg_type the message type to be created (DHCPREQUEST or DHCPRELEASE) + /// \param ack An instance of the DHCPACK message to be used to + /// create a new message. + /// + /// \return Pointer to the created message. + dhcp::Pkt4Ptr createMessageFromAck(const uint16_t msg_type, + const dhcp::Pkt4Ptr& ack); + + /// \brief Creates DHCPv6 message from the Reply packet. + /// + /// This function creates DHCPv6 Renew or Release message using the + /// data from the Reply message by copying options from the Reply + /// message. + /// + /// \param msg_type A type of the message to be created. + /// \param reply An instance of the Reply packet which contents should + /// be used to create an instance of the new message. + /// + /// \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); + + /// \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. + /// + /// \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. + /// \throw if elapsed time buffer size is neither 2 nor 0. + /// \return instance o the option. + static dhcp::OptionPtr + factoryElapsedTime6(dhcp::Option::Universe u, + uint16_t type, + const dhcp::OptionBuffer& buf); + + /// \brief Factory function to create generic option. + /// + /// This factory function creates option with specified universe, + /// type and buf. It does not have any additional logic validating + /// the buffer contents, size etc. + /// + /// \param u universe (V6 or V4). + /// \param type option-type (ignored). + /// \param buf option-buffer. + /// \return instance o the option. + static dhcp::OptionPtr factoryGeneric(dhcp::Option::Universe u, + uint16_t type, + const dhcp::OptionBuffer& buf); + + /// \brief Factory function to create IA_NA option. + /// + /// This factory function creates DHCPv6 IA_NA option instance. + /// + /// \todo add support for IA Address options. + /// + /// \param u universe (ignored). + /// \param type option-type (ignored). + /// \param buf option-buffer carrying IANA suboptions. + /// \return instance of IA_NA option. + static dhcp::OptionPtr factoryIana6(dhcp::Option::Universe u, + uint16_t type, + const dhcp::OptionBuffer& buf); + + /// \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); + + /// \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 + /// + /// \param u universe (ignored). + /// \param type option-type (ignored). + /// \param buf option-buffer (ignored). + /// \return instance of ORO option. + static dhcp::OptionPtr + factoryOptionRequestOption6(dhcp::Option::Universe u, + uint16_t type, + const dhcp::OptionBuffer& buf); + + /// \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. + /// + /// \param u universe (ignored). + /// \param type option-type (ignored). + /// \param buf option-buffer (ignored). + /// \return instance of RAPID_COMMIT option.. + static dhcp::OptionPtr factoryRapidCommit6(dhcp::Option::Universe u, + uint16_t type, + const dhcp::OptionBuffer& buf); + + + /// \brief Factory function to create DHCPv4 Request List option. + /// + /// This factory function creates DHCPv4 PARAMETER_REQUEST_LIST option + /// instance with the following set of requested options: + /// - DHO_SUBNET_MASK, + /// - DHO_BROADCAST_ADDRESS, + /// - DHO_TIME_OFFSET, + /// - DHO_ROUTERS, + /// - DHO_DOMAIN_NAME, + /// - DHO_DOMAIN_NAME_SERVERS, + /// - DHO_HOST_NAME. + /// + /// \param u universe (ignored). + /// \param type option-type (ignored). + /// \param buf option-buffer (ignored). + /// \return instance o the generic option. + static dhcp::OptionPtr factoryRequestList4(dhcp::Option::Universe u, + uint16_t type, + const dhcp::OptionBuffer& buf); + + /// \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; + + /// \brief Generate DUID. + /// + /// Method generates unique DUID. The number of DUIDs it can generate + /// depends on the number of simulated clients, which is specified + /// from the command line. It uses \ref CommandOptions object to retrieve + /// number of clients. Since the last six octets of DUID are constructed + /// from the MAC address, this function uses \ref generateMacAddress + /// internally to randomize the DUID. + /// + /// \todo add support for other types of DUID. + /// + /// \param [out] randomized number of bytes randomized (initial value + /// is ignored). + /// \throw isc::BadValue if \ref generateMacAddress throws. + /// \return vector representing DUID. + std::vector<uint8_t> generateDuid(uint8_t& randomized); + + /// \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 + /// the MAC address template (default MAC address). + /// + /// \param [out] randomized number of bytes randomized (initial + /// value is ignored). + /// \throw isc::BadValue if MAC address template (default or specified + /// from the command line) has invalid size (expected 6 octets). + /// \return generated MAC address. + std::vector<uint8_t> generateMacAddress(uint8_t& randomized); + + /// \brief generate transaction id. + /// + /// Generate transaction id value (32-bit for DHCPv4, + /// 24-bit for DHCPv6). + /// + /// \return generated transaction id. + uint32_t generateTransid() { + return (transid_gen_->generate()); + } + + /// \brief Return template buffer. + /// + /// Method returns template buffer at specified index. + /// + /// \param idx index of template buffer. + /// \throw isc::OutOfRange if buffer index out of bounds. + /// \return reference to template buffer. + TemplateBuffer getTemplateBuffer(const size_t idx) const; + + /// \brief Reads packet templates from files. + /// + /// Method iterates through all specified template files, reads + /// their content and stores it in class internal buffers. Template + /// file names are specified from the command line with -T option. + /// + /// \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. + void initPacketTemplates(); + + /// \brief Print rate statistics. + /// + /// Method print packet exchange rate statistics. + void printRate() const; + + /// \brief Process received DHCPv4 packet. + /// + /// 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 + /// REQUEST message to the server. + /// + /// \warning this method does not check if provided socket is + /// valid (specifically if v4 socket for received v4 packet). + /// + /// \param [in] pkt4 object representing DHCPv4 packet received. + /// \throw isc::BadValue if unknown message type received. + /// \throw isc::Unexpected if unexpected error occurred. + void processReceivedPacket4(const dhcp::Pkt4Ptr& pkt4); + + /// \brief Process IA in received DHCPv6 packet. + /// + /// Process IA in received message to check if it contain proper + /// address and/or prefix + /// + /// \param [in] pkt6 object representing DHCPv6 packet received. + /// \return true if the message include correct IA, false otherwise. + bool validateIA(const dhcp::Pkt6Ptr& pkt6); + + /// \brief Process received v6 addresses uniqueness. + /// + /// Generate list of addresses and check for uniqueness. + /// + /// \param pkt6 object representing received DHCPv6 packet + /// \param xchg_type ExchangeType enum value. + void address6Uniqueness(const dhcp::Pkt6Ptr& pkt6, ExchangeType xchg_type); + + /// \brief Process received v4 addresses uniqueness. + /// + /// Generate list of addresses and check for uniqueness. + /// + /// \param pkt4 object representing received DHCPv4 packet + /// \param xchg_type ExchangeType enum value. + void address4Uniqueness(const dhcp::Pkt4Ptr& pkt4, ExchangeType xchg_type); + + /// \brief add unique address to already assigned list. + /// + /// Add address and/or prefix to unique set if it's not already there, + /// otherwise increment the number of non unique addresses. + /// + /// \param current set of addresses that should be added to unique list + /// \param xchg_type ExchangeType enum value. + void addUniqeAddr(const std::set<std::string>& current, ExchangeType xchg_type) { + switch(xchg_type) { + case ExchangeType::SA: { + for (auto current_it = current.begin(); + current_it != current.end(); ++current_it) { + // addresses should be unique cross packets + auto ret = unique_address_.emplace(*current_it); + if (!ret.second) { + stats_mgr_.updateNonUniqueAddrNum(ExchangeType::SA); + } + } + break; + } + case ExchangeType::RR: { + for (auto current_it = current.begin(); + current_it != current.end(); ++current_it) { + // addresses should be unique cross packets + auto ret = unique_reply_address_.emplace(*current_it); + if (!ret.second) { + stats_mgr_.updateNonUniqueAddrNum(ExchangeType::RR); + } + } + break; + } + case ExchangeType::RLA: + case ExchangeType::RL: { + removeUniqueAddr(current); + break; + } + case ExchangeType::DO: { + for (auto current_it = current.begin(); + current_it != current.end(); ++current_it) { + // addresses should be unique cross packets + auto ret = unique_address_.emplace(*current_it); + if (!ret.second) { + stats_mgr_.updateNonUniqueAddrNum(ExchangeType::DO); + } + } + break; + } + case ExchangeType::RA: { + for (auto current_it = current.begin(); + current_it != current.end(); ++current_it) { + // addresses should be unique cross packets + auto ret = unique_reply_address_.emplace(*current_it); + if (!ret.second) { + stats_mgr_.updateNonUniqueAddrNum(ExchangeType::RA); + } + } + break; + } + case ExchangeType::RNA: + case ExchangeType::RN: + default: + break; + } + } + + /// \brief remove unique address from list. + /// + /// If address is released we should remove it from both + /// advertised (offered) and assigned sets. + /// + /// \param addr holding value of unique address. + void removeUniqueAddr(const std::set<std::string>& addr) { + for (auto addr_it = addr.begin(); addr_it != addr.end(); ++addr_it) { + auto it = unique_address_.find(*addr_it); + if (it != unique_address_.end()) { + unique_address_.erase(it); + } + + auto it2 = unique_reply_address_.find(*addr_it); + if (it2 != unique_reply_address_.end()) { + unique_reply_address_.erase(it2); + } + } + } + + /// \brief Process received DHCPv6 packet. + /// + /// Method performs processing of the received DHCPv6 packet, + /// updates statistics and responds to the server if required, + /// e.g. when ADVERTISE packet arrives, this function will initiate + /// REQUEST message to the server. + /// + /// \param [in] pkt6 object representing DHCPv6 packet received. + /// \throw isc::BadValue if unknown message type received. + /// \throw isc::Unexpected if unexpected error occurred. + void processReceivedPacket6(const dhcp::Pkt6Ptr& pkt6); + + /// \brief Register option factory functions for DHCPv4. + /// + /// Method registers option factory functions for DHCPv4. + /// These functions are called to create instances of DHCPv4 + /// options. Call \ref dhcp::Option::factory to invoke factory + /// function for particular option. Don't use this function directly. + /// Use \ref registerOptionFactories instead. + void registerOptionFactories4() const; + + /// \brief Register option factory functions for DHCPv6. + /// + /// Method registers option factory functions for DHCPv6. + /// These functions are called to create instances of DHCPv6 + /// options. Call \ref dhcp::Option::factory to invoke factory + /// function for particular option. Don't use this function directly. + /// Use \ref registerOptionFactories instead. + void registerOptionFactories6() const; + + /// \brief Register option factory functions for DHCPv4 or DHCPv6. + /// + /// Method registers option factory functions for DHCPv4 or DHCPv6, + /// depending in which mode test is currently running. + void registerOptionFactories() const; + + /// \brief Resets internal state of the object. + /// + /// Method resets internal state of the object. It has to be + /// called before new test is started. + void reset(); + + /// \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 + /// been already stored this function performs no operation. + /// This function does not perform sanity check if packet + /// 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 + /// been already stored this function performs no operation. + /// This function does not perform sanity check if packet + /// pointer is valid. Make sure it is before calling it. + /// + /// \param pkt packet to be stored. + inline void saveFirstPacket(const dhcp::Pkt6Ptr& pkt); + + /// \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 + /// as described in \ref factoryRequestList4. + /// 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. + /// Copy of sent packet is stored in the stats_mgr_ object to + /// update statistics. + /// + /// \param preload preload mode, packets not included in statistics. + /// + /// \throw isc::Unexpected if failed to create new packet instance. + /// \throw isc::BadValue if MAC address has invalid length. + /// \throw isc::dhcp::SocketWriteError if failed to send the packet. + void sendDiscover4(const bool preload = false); + + /// \brief Send DHCPv4 DISCOVER message from template. + /// + /// Method sends DHCPv4 DISCOVER message from template. The + /// template data is expected to be in binary format. Provided + /// buffer is copied and parts of it are replaced with actual + /// data (e.g. MAC address, transaction id etc.). + /// Copy of sent packet is stored in the stats_mgr_ object to + /// update statistics. + /// + /// \param template_buf buffer holding template packet. + /// \param preload preload mode, packets not included in statistics. + /// + /// \throw isc::OutOfRange if randomization offset is out of bounds. + /// \throw isc::dhcp::SocketWriteError if failed to send the packet. + void sendDiscover4(const std::vector<uint8_t>& template_buf, + const bool preload = false); + + /// \brief Send DHCPv4 renew (DHCPREQUEST). + /// + /// \param msg_type A type of the message to be sent (DHCPREQUEST or + /// DHCPRELEASE). + /// + /// \return true if the message has been sent, false otherwise. + bool sendMessageFromAck(const uint16_t msg_type); + + /// \brief Send DHCPv6 Renew or Release message. + /// + /// This method will select an existing lease from the Reply packet cache + /// If there is no lease that can be renewed or released this method will + /// return false. + /// + /// \param msg_type A type of the message to be sent (DHCPV6_RENEW or + /// DHCPV6_RELEASE). + /// + /// \return true if the message has been sent, false otherwise. + bool sendMessageFromReply(const uint16_t msg_type); + + /// \brief Send DHCPv4 REQUEST message. + /// + /// Method creates and sends DHCPv4 REQUEST message to the server. + /// Copy of sent packet is stored in the stats_mgr_ object to + /// update statistics. + /// + /// \param discover_pkt4 DISCOVER packet sent. + /// \param offer_pkt4 OFFER packet object. + /// + /// \throw isc::Unexpected if unexpected error occurred. + /// \throw isc::InvalidOperation if Statistics Manager has not been + /// initialized. + /// \throw isc::dhcp::SocketWriteError if failed to send the packet. + void sendRequest4(const dhcp::Pkt4Ptr& discover_pkt4, + const dhcp::Pkt4Ptr& offer_pkt4); + + /// \brief Send DHCPv4 REQUEST message from template. + /// + /// Method sends DHCPv4 REQUEST message from template. + /// Copy of sent packet is stored in the stats_mgr_ object to + /// update statistics. + /// + /// \param template_buf buffer holding template packet. + /// \param discover_pkt4 DISCOVER packet sent. + /// \param offer_pkt4 OFFER packet received. + /// + /// \throw isc::dhcp::SocketWriteError if failed to send the packet. + void sendRequest4(const std::vector<uint8_t>& template_buf, + const dhcp::Pkt4Ptr& discover_pkt4, + const dhcp::Pkt4Ptr& offer_pkt4); + + /// \brief Send DHCPv6 REQUEST message. + /// + /// Method creates and sends DHCPv6 REQUEST message to the server + /// with the following options: + /// - D6O_ELAPSED_TIME + /// - D6O_CLIENTID + /// - D6O_SERVERID + /// Copy of sent packet is stored in the stats_mgr_ object to + /// update statistics. + /// + /// \param advertise_pkt6 ADVERTISE packet object. + /// \throw isc::Unexpected if unexpected error occurred. + /// \throw isc::InvalidOperation if Statistics Manager has not been + /// initialized. + /// + /// \throw isc::dhcp::SocketWriteError if failed to send the packet. + void sendRequest6(const dhcp::Pkt6Ptr& advertise_pkt6); + + /// \brief Send DHCPv6 REQUEST message from template. + /// + /// Method sends DHCPv6 REQUEST message from template. + /// Copy of sent packet is stored in the stats_mgr_ object to + /// update statistics. + /// + /// \param template_buf packet template buffer. + /// \param advertise_pkt6 ADVERTISE packet object. + /// + /// \throw isc::dhcp::SocketWriteError if failed to send the packet. + void sendRequest6(const std::vector<uint8_t>& template_buf, + const dhcp::Pkt6Ptr& advertise_pkt6); + + /// \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. + /// Copy of sent packet is stored in the stats_mgr_ object to + /// update statistics. + /// + /// \param preload mode, packets not included in statistics. + /// + /// \throw isc::Unexpected if failed to create new packet instance. + /// \throw isc::dhcp::SocketWriteError if failed to send the packet. + void sendSolicit6(const bool preload = false); + + /// \brief Send DHCPv6 SOLICIT message from template. + /// + /// Method sends DHCPv6 SOLICIT message from template. + /// Copy of sent packet is stored in the stats_mgr_ object to + /// update statistics. + /// + /// \param template_buf packet template buffer. + /// \param preload mode, packets not included in statistics. + /// + /// \throw isc::dhcp::SocketWriteError if failed to send the packet. + void sendSolicit6(const std::vector<uint8_t>& template_buf, + const bool preload = false); + + /// \brief Set default DHCPv4 packet parameters. + /// + /// This method sets default parameters on the DHCPv4 packet: + /// - 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) + /// + /// \param pkt reference to packet to be configured. + void setDefaults4(const dhcp::Pkt4Ptr& pkt); + + /// \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 pkt reference to packet to be configured. + void setDefaults6(const dhcp::Pkt6Ptr& pkt); + + /// @brief Inserts extra options specified by user. + /// + /// Note: addExtraOpts for v4 and v6 could easily be turned into a template. + /// However, this would require putting code here that uses CommandOptions, + /// and that would create dependency between test_control.h and + /// command_options.h. + /// + /// @param pkt4 options will be added here. + void addExtraOpts(const dhcp::Pkt4Ptr& pkt4); + + /// @brief Inserts extra options specified by user. + /// + /// Note: addExtraOpts for v4 and v6 could easily be turned into a template. + /// However, this would require putting code here that uses CommandOptions, + /// and that would create dependency between test_control.h and + /// command_options.h. + /// + /// @param pkt6 options will be added here. + void addExtraOpts(const dhcp::Pkt6Ptr& pkt6); + + /// \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. + /// \param [out] pkt_to A packet to which options should be copied. + /// + /// \throw isc::NotFound 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); + + /// \brief Calculate elapsed time between two packets. + /// + /// 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. + /// + /// \tparam T Pkt4Ptr or Pkt6Ptr class. + /// \param pkt1 first packet. + /// \param pkt2 second packet. + /// \throw InvalidOperation if packet timestamps are invalid. + /// \return elapsed time in milliseconds between pkt1 and pkt2. + template<class T> + uint32_t getElapsedTime(const T& pkt1, const T& pkt2); + + /// \brief Return elapsed time offset in a packet. + /// + /// \return elapsed time offset in packet. + int getElapsedTimeOffset() const; + + /// \brief Return randomization offset in a packet. + /// + /// \return randomization offset in packet. + int getRandomOffset(const int arg_idx) const; + + /// \brief Return requested ip offset in a packet. + /// + /// \return randomization offset in a packet. + int getRequestedIpOffset() const; + + /// \brief Return server id offset in a packet. + /// + /// \return server id offset in packet. + int getServerIdOffset() const; + + /// \brief Return transaction id offset in a packet. + /// + /// \param arg_idx command line argument index to be used. + /// If multiple -X parameters specified it points to the + /// one to be used. + /// \return transaction id offset in packet. + int getTransactionIdOffset(const int arg_idx) const; + + /// \brief Handle child signal. + /// + /// Function handles child signal by waiting for + /// the process to complete. + /// + /// \param sig signal (ignored). + static void handleChild(int sig); + + /// \brief Handle interrupt signal. + /// + /// Function sets flag indicating that program has been + /// interrupted. + /// + /// \param sig signal (ignored). + static void handleInterrupt(int sig); + + /// \brief Print main diagnostics data. + /// + /// Method prints main diagnostics data. + void printDiagnostics() const; + + /// \brief Print template information. + /// + /// \param packet_type packet type. + void printTemplate(const uint8_t packet_type) const; + + /// \brief Read DHCP message template from file. + /// + /// Method reads DHCP message template from file and + /// converts it to binary format. Read data is appended + /// to template_buffers_ vector. + /// + /// \param file_name name of the packet template file. + /// \throw isc::OutOfRange if file is empty or has odd number + /// of hexadecimal digits. + /// \throw isc::BadValue if file contains characters other than + /// spaces or hexadecimal digits. + void readPacketTemplate(const std::string& file_name); + + /// \brief Keep addresses and prefixes from advertise msg for uniqueness checks. + std::set<std::string> unique_address_; + + /// \brief Keep addresses and prefixes from reply msg for uniqueness checks. + std::set<std::string> unique_reply_address_; + + /// \brief Socket used for DHCP traffic. + BasePerfSocket &socket_; + + /// \brief Receiver used to receive DHCP traffic. + Receiver receiver_; + + /// \brief Last intermediate report time. + boost::posix_time::ptime last_report_; + + /// \brief Statistics Manager. + StatsMgr stats_mgr_; + + /// \brief Storage for DHCPACK messages. + PacketStorage<dhcp::Pkt4> ack_storage_; + + /// \brief Storage for reply messages. + PacketStorage<dhcp::Pkt6> reply_storage_; + + /// \brief Transaction id generator. + NumberGeneratorPtr transid_gen_; + + /// \brief Numbers generator for MAC address. + NumberGeneratorPtr macaddr_gen_; + + /// \brief Buffer holding server id received in first packet + dhcp::OptionBuffer first_packet_serverid_; + + /// \brief Packet template buffers. + TemplateBufferCollection template_buffers_; + + /// First packets send. They are used at the end of the test + /// to print packet templates when diagnostics flag T is specified. + + /// \brief Template for v4. + std::map<uint8_t, dhcp::Pkt4Ptr> template_packets_v4_; + + /// \brief Template for v6. + std::map<uint8_t, dhcp::Pkt6Ptr> template_packets_v6_; + + /// \brief Program interrupted flag. + static bool interrupted_; + + /// \brief Command options. + CommandOptions& options_; +}; + +} // namespace perfdhcp +} // namespace isc + +#endif // TEST_CONTROL_H |