From: Marcin Siodelski Date: Mon, 14 Jul 2014 08:04:58 +0000 (+0200) Subject: [3390] Initial implementation of the dhcp4 test client. X-Git-Tag: trac3482_base~79^2~8 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=fad91c5b8de5a468ff4405dbf229541f9bbab62b;p=thirdparty%2Fkea.git [3390] Initial implementation of the dhcp4 test client. --- diff --git a/src/bin/dhcp4/tests/Makefile.am b/src/bin/dhcp4/tests/Makefile.am index 737fd82732..dba725e03f 100644 --- a/src/bin/dhcp4/tests/Makefile.am +++ b/src/bin/dhcp4/tests/Makefile.am @@ -88,6 +88,7 @@ dhcp4_unittests_SOURCES += ctrl_dhcp4_srv_unittest.cc dhcp4_unittests_SOURCES += config_parser_unittest.cc dhcp4_unittests_SOURCES += fqdn_unittest.cc dhcp4_unittests_SOURCES += marker_file.cc +dhcp4_unittests_SOURCES += dhcp4_client.cc dhcp4_client.h if CONFIG_BACKEND_BUNDY # For Bundy backend, we only need to run the usual tests. There are no diff --git a/src/bin/dhcp4/tests/dhcp4_client.cc b/src/bin/dhcp4/tests/dhcp4_client.cc new file mode 100644 index 0000000000..1930923650 --- /dev/null +++ b/src/bin/dhcp4/tests/dhcp4_client.cc @@ -0,0 +1,110 @@ +// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include +#include +#include + +namespace isc { +namespace dhcp { +namespace test { + +Dhcp4Client::Dhcp4Client() : + curr_transid_(0), + dest_addr_("255.255.255.255"), + hwaddr_(generateHWAddr()), + relay_addr_("10.0.0.2"), + srv_(boost::shared_ptr(new NakedDhcpv4Srv(0))), + use_relay_(false) { +} + +Dhcp4Client::Dhcp4Client(boost::shared_ptr& srv) : + curr_transid_(0), + dest_addr_("255.255.255.255"), + hwaddr_(generateHWAddr()), + relay_addr_("10.0.0.2"), + srv_(srv), + use_relay_(false) { +} +Pkt4Ptr +Dhcp4Client::createMsg(const uint8_t msg_type) { + Pkt4Ptr msg(new Pkt4(msg_type, curr_transid_++)); + msg->setHWAddr(hwaddr_); + return (msg); +} + +void +Dhcp4Client::doInform() { + context_.query_ = createMsg(DHCPINFORM); + sendMsg(context_.query_); + context_.response_ = receiveOneMsg(); +} + +HWAddrPtr +Dhcp4Client::generateHWAddr(const uint8_t htype) const { + if (htype != HTYPE_ETHER) { + isc_throw(isc::NotImplemented, + "The harware address type " << static_cast(htype) + << " is currently not supported"); + } + std::vector hwaddr; + // Generate ethernet hardware address by assigning random byte values. + for (int i = 0; i < HWAddr::ETHERNET_HWADDR_LEN; ++i) { + hwaddr.push_back(static_cast(rand() % 255)); + } + return (HWAddrPtr(new HWAddr(hwaddr, htype))); +} + +void +Dhcp4Client::modifyHWAddr() { + if (!hwaddr_) { + hwaddr_ = generateHWAddr(); + return; + } + // Modify the HW address by adding 1 to its last byte. + ++hwaddr_->hwaddr_[hwaddr_->hwaddr_.size() - 1]; +} + +Pkt4Ptr +Dhcp4Client::receiveOneMsg() { + // Return empty pointer if server hasn't responded. + if (srv_->fake_sent_.empty()) { + return (Pkt4Ptr()); + } + Pkt4Ptr msg = srv_->fake_sent_.front(); + srv_->fake_sent_.pop_front(); + return (msg); +} + +void +Dhcp4Client::sendMsg(const Pkt4Ptr& msg) { + srv_->shutdown_ = false; + if (use_relay_) { + msg->setGiaddr(relay_addr_); + } + // Repack the message to simulate wire-data parsing. + msg->pack(); + Pkt4Ptr msg_copy(new Pkt4(static_cast + (msg->getBuffer().getData()), + msg->getBuffer().getLength())); + msg_copy->setRemoteAddr(asiolink::IOAddress("0.0.0.0")); + msg_copy->setLocalAddr(dest_addr_); + msg_copy->setIface("eth0"); + srv_->fakeReceive(msg_copy); + srv_->run(); +} + +} // end of namespace isc::dhcp::test +} // end of namespace isc::dhcp +} // end of namespace isc diff --git a/src/bin/dhcp4/tests/dhcp4_client.h b/src/bin/dhcp4/tests/dhcp4_client.h new file mode 100644 index 0000000000..67d2f78914 --- /dev/null +++ b/src/bin/dhcp4/tests/dhcp4_client.h @@ -0,0 +1,180 @@ +// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#ifndef DHCP4_CLIENT_H +#define DHCP4_CLIENT_H + +#include +#include +#include +#include +#include +#include + +namespace isc { +namespace dhcp { +namespace test { + +/// @brief DHCPv4 client used for unit testing. +/// +/// This class implements a DHCPv4 "client" which interoperates with the +/// @c NakedDhcpv4Srv class. It calls @c NakedDhcpv4Srv::fakeRecive 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 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 Creates a new client. + Dhcp4Client(); + + /// @brief Creates a new client that communicates with a specified server. + /// + /// @param srv An instance of the DHCPv4 server to be used. + Dhcp4Client(boost::shared_ptr& srv); + + /// @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). + /// + /// @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(); + + /// @brief Generates a hardware address used by the client. + /// + /// It assiges random values to the bytes of the harware 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; + + /// @brief Returns current context. + const Context& getContext() const { + return (context_); + } + + /// @brief Returns the server that the client is communicating with. + boost::shared_ptr getServer() const { + return (srv_); + } + + /// @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 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 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 + void useRelay(const bool use = true, + const asiolink::IOAddress& relay_addr = + asiolink::IOAddress("10.0.0.2")) { + use_relay_ = use; + relay_addr_ = relay_addr; + } + +private: + + /// @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 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 use destination address. + asiolink::IOAddress dest_addr_; + + /// @brief Current hardware address of the client. + HWAddrPtr hwaddr_; + + /// @brief Relay address to use. + asiolink::IOAddress relay_addr_; + + /// @brief Pointer to the server that the client is communicating with. + boost::shared_ptr srv_; + + /// @brief Enable relaying messages to the server. + bool use_relay_; +}; + +} // end of namespace isc::dhcp::test +} // end of namespace isc::dhcp +} // end of namespace isc + +#endif // DHCP4_CLIENT diff --git a/src/bin/dhcp4/tests/dhcp4_test_utils.h b/src/bin/dhcp4/tests/dhcp4_test_utils.h index d3d2f1d64a..d7513da24c 100644 --- a/src/bin/dhcp4/tests/dhcp4_test_utils.h +++ b/src/bin/dhcp4/tests/dhcp4_test_utils.h @@ -204,6 +204,7 @@ public: using Dhcpv4Srv::acceptMessageType; using Dhcpv4Srv::selectSubnet; using Dhcpv4Srv::VENDOR_CLASS_PREFIX; + using Dhcpv4Srv::shutdown_; }; class Dhcpv4SrvTest : public ::testing::Test {