]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[3390] Initial implementation of the dhcp4 test client.
authorMarcin Siodelski <marcin@isc.org>
Mon, 14 Jul 2014 08:04:58 +0000 (10:04 +0200)
committerMarcin Siodelski <marcin@isc.org>
Mon, 14 Jul 2014 08:04:58 +0000 (10:04 +0200)
src/bin/dhcp4/tests/Makefile.am
src/bin/dhcp4/tests/dhcp4_client.cc [new file with mode: 0644]
src/bin/dhcp4/tests/dhcp4_client.h [new file with mode: 0644]
src/bin/dhcp4/tests/dhcp4_test_utils.h

index 737fd82732c25b3c4f48ec20d5c9dfee952a8254..dba725e03fb1faaaeecdd9940f83b67dcbbd5b95 100644 (file)
@@ -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 (file)
index 0000000..1930923
--- /dev/null
@@ -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 <dhcp/dhcp4.h>
+#include <dhcp4/tests/dhcp4_client.h>
+#include <cstdlib>
+
+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<NakedDhcpv4Srv>(new NakedDhcpv4Srv(0))),
+    use_relay_(false) {
+}
+
+Dhcp4Client::Dhcp4Client(boost::shared_ptr<NakedDhcpv4Srv>& 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<int>(htype)
+                  << " is currently not supported");
+    }
+    std::vector<uint8_t> 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<uint8_t>(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<const uint8_t*>
+                              (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 (file)
index 0000000..67d2f78
--- /dev/null
@@ -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 <asiolink/io_address.h>
+#include <dhcp/hwaddr.h>
+#include <dhcp/pkt4.h>
+#include <dhcp4/tests/dhcp4_test_utils.h>
+#include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
+
+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<NakedDhcpv4Srv>& 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<NakedDhcpv4Srv> 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<NakedDhcpv4Srv> 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
index d3d2f1d64aeaef747c61e4f3f6514c08c896e6d2..d7513da24c325ce2a8dba21e9c32fd3247cb8322 100644 (file)
@@ -204,6 +204,7 @@ public:
     using Dhcpv4Srv::acceptMessageType;
     using Dhcpv4Srv::selectSubnet;
     using Dhcpv4Srv::VENDOR_CLASS_PREFIX;
+    using Dhcpv4Srv::shutdown_;
 };
 
 class Dhcpv4SrvTest : public ::testing::Test {