summaryrefslogtreecommitdiffstats
path: root/src/bin/dhcp4/tests/dhcp4_client.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/bin/dhcp4/tests/dhcp4_client.h')
-rw-r--r--src/bin/dhcp4/tests/dhcp4_client.h533
1 files changed, 533 insertions, 0 deletions
diff --git a/src/bin/dhcp4/tests/dhcp4_client.h b/src/bin/dhcp4/tests/dhcp4_client.h
new file mode 100644
index 0000000..a7265a4
--- /dev/null
+++ b/src/bin/dhcp4/tests/dhcp4_client.h
@@ -0,0 +1,533 @@
+// Copyright (C) 2014-2020 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 DHCP4_CLIENT_H
+#define DHCP4_CLIENT_H
+
+#include <asiolink/io_address.h>
+#include <dhcp/hwaddr.h>
+#include <dhcp/option.h>
+#include <dhcp/pkt4.h>
+#include <dhcp4/tests/dhcp4_test_utils.h>
+#include <util/optional.h>
+#include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
+#include <set>
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+/// @brief General error emitted by the DHCP4 test client.
+class Dhcp4ClientError : public isc::Exception {
+public:
+ Dhcp4ClientError(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) { };
+};
+
+/// @brief DHCPv4 client used for unit testing.
+///
+/// This class implements a DHCPv4 "client" which interoperates with the
+/// @c NakedDhcpv4Srv class. It calls @c NakedDhcpv4Srv::fakeReceive to
+/// deliver client messages to the server for processing. The server places
+/// the response in the @c NakedDhcpv4Srv::fake_sent_ container. The client
+/// pops messages from this container which simulates reception of the
+/// response from the server.
+///
+/// The client maintains the leases it acquired from the server.
+///
+/// The client exposes a set of functions which simulate different exchange
+/// types between the client and the server. It also provides the access to
+/// the objects encapsulating responses from the server so as it is possible
+/// to verify from the unit test that the server's response is correct.
+class Dhcp4Client : public boost::noncopyable {
+public:
+
+ /// @brief States of the DHCP client.
+ enum State {
+ SELECTING,
+ INIT_REBOOT,
+ RENEWING,
+ REBINDING
+ };
+
+ /// @brief Holds the DHCPv4 messages taking part in transaction between
+ /// the client and the server.
+ struct Context {
+ /// @brief Holds the last sent message from the client to the server.
+ Pkt4Ptr query_;
+ /// @brief Holds the last sent message by the server to the client.
+ Pkt4Ptr response_;
+ };
+
+ /// @brief Holds the configuration of the client received from the
+ /// DHCP server.
+ struct Configuration {
+ /// @brief Holds IP addresses received in the Routers option.
+ Option4AddrLst::AddressContainer routers_;
+ /// @brief Holds IP addresses received in the DNS Servers option.
+ Option4AddrLst::AddressContainer dns_servers_;
+ /// @brief Holds IP addresses received in the Log Servers option.
+ Option4AddrLst::AddressContainer log_servers_;
+ /// @brief Holds IP addresses received in the Quotes Servers option.
+ Option4AddrLst::AddressContainer quotes_servers_;
+ /// @brief Vendor Specific options
+ OptionCollection vendor_suboptions_;
+ /// @brief Holds a lease obtained by the client.
+ Lease4 lease_;
+ /// @brief Holds server id of the server which responded to the client's
+ /// request.
+ asiolink::IOAddress serverid_;
+ /// @brief Holds returned siaddr.
+ asiolink::IOAddress siaddr_;
+ /// @brief Holds returned sname.
+ std::string sname_;
+ /// @brief Holds returned (boot)file.
+ std::string boot_file_name_;
+
+ /// @brief Constructor.
+ Configuration();
+
+ /// @brief Sets configuration values to defaults.
+ void reset();
+ };
+
+ /// @brief Creates a new client.
+ ///
+ /// @param Initial client's state.
+ Dhcp4Client(const State& state = SELECTING);
+
+ /// @brief Creates a new client that communicates with a specified server.
+ ///
+ /// @param srv An instance of the DHCPv4 server to be used.
+ /// @param state Initial client's state.
+ Dhcp4Client(boost::shared_ptr<NakedDhcpv4Srv> srv,
+ const State& state = SELECTING);
+
+ /// @brief Creates a lease for the client using the specified address
+ /// and valid lifetime.
+ ///
+ /// This method creates the lease using the specified address and
+ /// valid lease lifetime. The client will use this lease in any
+ /// future communication with the DHCP server. One of the use cases
+ /// for this method is to pre-configure the client with the explicitly
+ /// given address before it sends the DHCPINFORM to the DHCP server.
+ /// The client will inject the leased address into the ciaddr field
+ /// of the DHCPINFORM message.
+ ///
+ /// @param addr Lease address.
+ /// @param valid_lft Valid lifetime.
+ void createLease(const asiolink::IOAddress& addr, const uint32_t valid_lft);
+
+ /// @brief Sends DHCPDISCOVER message to the server and receives response.
+ ///
+ /// The message being sent to the server includes Parameter Request List
+ /// option if any options to be requested have been specified using the
+ /// @c requestOptions or @c requestOption methods.
+ ///
+ /// The configuration returned by the server in the DHCPOFFER message is
+ /// NOT stored in the client configuration: @c config_.
+ ///
+ /// @param requested_addr A pointer to the IP Address to be sent in the
+ /// Requested IP Address option or NULL if the option should not be
+ /// included.
+ void doDiscover(const boost::shared_ptr<asiolink::IOAddress>&
+ requested_addr = boost::shared_ptr<asiolink::IOAddress>());
+
+ /// @brief Perform 4-way exchange with a server.
+ ///
+ /// This method calls @c doDiscover and @c doRequest to perform the 4-way
+ /// exchange with the server.
+ ///
+ /// @param requested_addr A pointer to the address to be requested using the
+ /// Requested IP Address option.
+ void doDORA(const boost::shared_ptr<asiolink::IOAddress>&
+ requested_addr = boost::shared_ptr<asiolink::IOAddress>());
+
+ /// @brief Sends DHCPINFORM message to the server and receives response.
+ ///
+ /// This function simulates sending the DHCPINFORM message to the server
+ /// and receiving server's response (if any). The server's response and the
+ /// message sent to the server is stored in the context structure and can
+ /// be accessed using @c getContext function.
+ ///
+ /// The configuration returned by the server is stored in the
+ /// @c config_ public member and can be accessed directly.
+ ///
+ /// @param set_ciaddr Indicates if the ciaddr should be set for an
+ /// outgoing message and defaults to true. Note, that the RFC2131 mandates
+ /// setting the ciaddr for DHCPINFORM but the server may still want to
+ /// respond if the ciaddr is not set.
+ ///
+ /// @throw This function doesn't thrown exceptions on its own, but it calls
+ /// functions that are not exception safe, so it may emit an exception if
+ /// an error occurs.
+ void doInform(const bool set_ciaddr = true);
+
+ /// @brief Sends DHCPRELEASE Message to the server.
+ ///
+ /// This method simulates sending the DHCPRELEASE message to the server.
+ /// The released lease is removed from the client's configuration.
+ void doRelease();
+
+
+ /// @brief Sends DHCPDECLINE Message to the server.
+ ///
+ /// This method simulates sending the DHCPDECLINE message to the server.
+ /// The released lease is removed from the client's configuration.
+ void doDecline();
+
+ /// @brief Sends DHCPREQUEST Message to the server and receives a response.
+ ///
+ /// This method simulates sending the DHCPREQUEST message to the server and
+ /// receiving a response. The DHCPREQUEST message can be used by the client
+ /// being in various states:
+ /// - SELECTING - client is trying to obtain a new lease and it has selected
+ /// the server using the DHCPDISCOVER.
+ /// - INIT-REBOOT - client cached an address it was previously using and is
+ /// now trying to verify if this address is still valid.
+ /// - RENEW - client's renewal timer has passed and the client is trying to
+ /// extend the lifetime of the lease.
+ /// - REBIND - client's rebind timer has passed and the client is trying to
+ /// extend the lifetime of the lease from any server.
+ ///
+ /// Depending on the state that the client is in, different combinations of
+ /// - ciaddr
+ /// - Requested IP Address option
+ /// - server identifier
+ /// are used (as per RFC2131, section 4.3.2). Therefore, the unit tests
+ /// must set the appropriate state of the client prior to calling this
+ /// method using the @c setState function.
+ ///
+ /// When the server returns the DHCPACK the configuration carried in the
+ /// DHCPACK message is applied and can be obtained from the @c config_.
+ void doRequest();
+
+ /// @brief Receives a response from the server.
+ ///
+ /// This method is useful to receive response from the server after
+ /// parking a packet. In this case, the packet is not received as a
+ /// result of initial exchange, e.g. @c doRequest. The test can call
+ /// this method to complete the transaction when it expects that the
+ /// packet has been unparked.
+ void receiveResponse();
+
+ /// @brief Generates a hardware address used by the client.
+ ///
+ /// It assigns random values to the bytes of the hardware address.
+ ///
+ /// @param htype hardware address type. Currently the only type
+ /// supported is Ethernet hardware address.
+ ///
+ /// @return Pointer to the generated hardware address.
+ HWAddrPtr generateHWAddr(const uint8_t htype = HTYPE_ETHER) const;
+
+ /// @brief Returns HW address used by the client.
+ HWAddrPtr getHWAddress() const {
+ return (hwaddr_);
+ }
+
+ /// @brief Returns current context.
+ const Context& getContext() const {
+ return (context_);
+ }
+
+ /// @brief Returns the server that the client is communicating with.
+ boost::shared_ptr<NakedDhcpv4Srv> getServer() const {
+ return (srv_);
+ }
+
+ /// @brief Creates the client id from the client id in the textual format.
+ ///
+ /// The generated client id will be added to the client's messages to the
+ /// server.
+ ///
+ /// @param clientid Client id in the textual format. Use the empty client id
+ /// value to not include the client id.
+ void includeClientId(const std::string& clientid);
+
+ /// @brief Creates an instance of the Client FQDN option to be included
+ /// in the client's message.
+ ///
+ /// @param flags Flags.
+ /// @param fqdn_name Name in the textual format.
+ /// @param fqdn_type Type of the name (fully qualified or partial).
+ void includeFQDN(const uint8_t flags, const std::string& fqdn_name,
+ Option4ClientFqdn::DomainNameType fqdn_type);
+
+ /// @brief Creates an instance of the Hostname option to be included
+ /// in the client's message.
+ ///
+ /// @param name Name to be stored in the option.
+ void includeHostname(const std::string& name);
+
+ /// @brief Modifies the client's HW address (adds one to it).
+ ///
+ /// The HW address should be modified to test negative scenarios when the
+ /// client acquires a lease and tries to renew it with a different HW
+ /// address. The server should detect the HW address mismatch and react
+ /// accordingly.
+ ///
+ /// The HW address modification affects the value returned by the
+ /// @c Dhcp4Client::getHWAddress.
+ void modifyHWAddr();
+
+ /// @brief Specify an option to be requested by a client.
+ ///
+ /// This function adds option code to the collection of option
+ /// codes to be requested by a client.
+ ///
+ /// @param option Option code to be requested. The value of 0 is
+ /// ignored and the function is no-op.
+ void requestOption(const uint8_t option);
+
+ /// @brief Specifies options to be requested by the client.
+ ///
+ /// This function configures the client to request options having
+ /// specified codes using Parameter Request List option. The default
+ /// value of 0 specify that the option is not requested.
+ ///
+ /// If there are options specified to be requested before the function
+ /// is called, the new option codes override previously specified ones.
+ /// In order to clear the list of requested options call
+ /// @c requestOptions(0).
+ ///
+ /// @param option1 First option to be requested.
+ /// @param option2 Second option to be requested (optional).
+ /// @param option3 Third option to be requested (optional).
+ void requestOptions(const uint8_t option1,
+ const uint8_t option2 = 0,
+ const uint8_t option3 = 0);
+
+ /// @brief Sets circuit-id value to be included in the circuit-id
+ /// sub option of the RAI option.
+ ///
+ /// @param circuit_id New circuit-id value.
+ void setCircuitId(const std::string& circuit_id) {
+ circuit_id_ = circuit_id;
+ }
+
+ /// @brief Sets destination address for the messages being sent by the
+ /// client.
+ ///
+ /// By default, the client uses broadcast address 255.255.255.255 to
+ /// communicate with the server. In certain cases it may be desired
+ /// that different address is used. This function sets the new address
+ /// for all future exchanges with the server.
+ ///
+ /// @param dest_addr New destination address.
+ void setDestAddress(const asiolink::IOAddress& dest_addr) {
+ dest_addr_ = dest_addr;
+ }
+
+ /// @brief Sets the explicit hardware address for the client.
+ ///
+ /// @param hwaddr_str String representation of the HW address. Use an
+ /// empty string to set the NULL hardware address.
+ void setHWAddress(const std::string& hwaddr_str);
+
+ /// @brief Sets the interface over which the messages should be sent.
+ ///
+ /// @param iface_name Name of the interface over which the messages should
+ /// be sent.
+ void setIfaceName(const std::string& iface_name) {
+ iface_name_ = iface_name;
+ }
+
+ /// @brief Sets the interface over which the messages should be sent.
+ ///
+ /// @param iface_index Index of the interface over which the
+ /// messages should be sent.
+ void setIfaceIndex(uint32_t iface_index) {
+ iface_index_ = iface_index;
+ }
+
+ /// @brief Sets client state.
+ ///
+ /// Depending on the current state the client's behavior is different
+ /// when sending Request messages as per RFC2131, section 4.3.2.
+ ///
+ /// @param state New client's state.
+ void setState(const State& state) {
+ state_ = state;
+ }
+
+ /// @brief Simulate sending messages through a relay.
+ ///
+ /// @param use Parameter which 'true' value indicates that client should
+ /// simulate sending messages via relay.
+ /// @param relay_addr Relay address
+ /// @param sf_relay_addr Server facing relay address.
+ void useRelay(const bool use = true,
+ const asiolink::IOAddress& relay_addr =
+ asiolink::IOAddress("192.0.2.2"),
+ const asiolink::IOAddress& sf_relay_addr =
+ asiolink::IOAddress("10.0.0.2")) {
+ use_relay_ = use;
+ relay_addr_ = relay_addr;
+ server_facing_relay_addr_ = sf_relay_addr;
+ }
+
+ /// @brief Current client's configuration obtained from the server.
+ Configuration config_;
+
+ /// @brief Specific ciaddr to be used in client's messages.
+ ///
+ /// If this value is "unspecified" the default values will be used
+ /// by the client. If this value is specified, it will override ciaddr
+ /// in the client's messages.
+ isc::util::Optional<asiolink::IOAddress> ciaddr_;
+
+ /// @brief Adds extra option (an option the client will always send)
+ ///
+ /// @param opt additional option to be sent
+ void addExtraOption(const OptionPtr& opt);
+
+ /// @brief Add a client class.
+ ///
+ /// @param client_class name of the class to be added.
+ void addClass(const ClientClass& client_class);
+
+private:
+ /// @brief Appends extra options, previously added with addExtraOption()
+ ///
+ /// @brief Copies options from extra_options_ into outgoing message
+ void appendExtraOptions();
+
+ /// @brief Appends extra classes, previously added with addClass()
+ ///
+ /// @brief Add client classes from classes_ to incoming message
+ void appendClasses();
+
+ /// @brief Creates and adds Requested IP Address option to the client's
+ /// query.
+ ///
+ /// @param addr Address to be added in the Requested IP Address option.
+ void addRequestedAddress(const asiolink::IOAddress& addr);
+
+ /// @brief Stores configuration received from the server.
+ ///
+ /// This methods stores the configuration obtained from the DHCP server
+ /// in the @c Configuration structure. This configuration includes:
+ /// - obtained lease
+ /// - server id of the server that provided the configuration
+ /// - lease
+ /// - selected options (used by unit tests):
+ /// - DNS Servers
+ /// - Routers
+ /// - Log Servers
+ /// - Quotes Servers
+ void applyConfiguration();
+
+ /// @brief Creates client's side DHCP message.
+ ///
+ /// @param msg_type Type of the message to be created.
+ /// @return An instance of the message created.
+ Pkt4Ptr createMsg(const uint8_t msg_type);
+
+ /// @brief Includes the Client Identifier option in the client's message.
+ ///
+ /// This function creates an instance of the Client Identifier option
+ /// if the client identifier has been specified and includes this
+ /// option in the client's message to the server.
+ void appendClientId();
+
+ /// @brief Includes the Server Identifier option in the client's message.
+ ///
+ /// This function creates an instance of the Server Identifier option.
+ /// It uses whatever information is stored in config_.serverid_.
+ void appendServerId();
+
+ /// @brief Includes FQDN or Hostname option in the client's message.
+ ///
+ /// This method checks if @c fqdn_ or @c hostname_ is specified and
+ /// includes it in the client's message. If both are specified, the
+ /// @c fqdn_ will be used.
+ void appendName();
+
+ /// @brief Include PRL Option in the query message.
+ ///
+ /// This function creates the instance of the PRL (Parameter Request List)
+ /// option and adds option codes from the @c requested_options_ to it.
+ /// It later adds the PRL option to the @c context_.query_ message
+ /// if it is non-NULL.
+ void appendPRL();
+
+ /// @brief Simulates reception of the message from the server.
+ ///
+ /// @return Received message.
+ Pkt4Ptr receiveOneMsg();
+
+ /// @brief Simulates sending a message to the server.
+ ///
+ /// This function instantly triggers processing of the message by the
+ /// server. The server's response can be gathered by invoking the
+ /// @c receiveOneMsg function.
+ ///
+ /// @param msg Message to be sent.
+ void sendMsg(const Pkt4Ptr& msg);
+
+ /// @brief Current context (sent and received message).
+ Context context_;
+
+ /// @biref Current transaction id (altered on each send).
+ uint32_t curr_transid_;
+
+ /// @brief Currently used destination address.
+ asiolink::IOAddress dest_addr_;
+
+ /// @brief FQDN requested by the client.
+ Option4ClientFqdnPtr fqdn_;
+
+ /// @brief Hostname requested by the client.
+ OptionStringPtr hostname_;
+
+ /// @brief Current hardware address of the client.
+ HWAddrPtr hwaddr_;
+
+ /// @brief Current client identifier.
+ ClientIdPtr clientid_;
+
+ /// @brief Interface to be used to send the messages (name).
+ std::string iface_name_;
+
+ /// @brief Interface to be used to send the messages (index).
+ uint32_t iface_index_;
+
+ /// @brief Relay address to use.
+ asiolink::IOAddress relay_addr_;
+
+ /// @brief Collection of options codes to be requested by the client.
+ std::set<uint8_t> requested_options_;
+
+ /// @brief Address of the relay interface connected to the server.
+ asiolink::IOAddress server_facing_relay_addr_;
+
+ /// @brief Pointer to the server that the client is communicating with.
+ boost::shared_ptr<NakedDhcpv4Srv> srv_;
+
+ /// @brief Current state of the client.
+ State state_;
+
+ /// @brief Enable relaying messages to the server.
+ bool use_relay_;
+
+ /// @brief Specifies value to be inserted into circuit-id sub option
+ /// of the RAI option.
+ std::string circuit_id_;
+
+ /// @brief Extra options the client will send.
+ OptionCollection extra_options_;
+
+ /// @brief Extra classes to add to the query.
+ ClientClasses classes_;
+};
+
+} // end of namespace isc::dhcp::test
+} // end of namespace isc::dhcp
+} // end of namespace isc
+
+#endif // DHCP4_CLIENT_H