]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[3390] Implemented tests for DHCPINFORM processing.
authorMarcin Siodelski <marcin@isc.org>
Tue, 15 Jul 2014 10:05:48 +0000 (12:05 +0200)
committerMarcin Siodelski <marcin@isc.org>
Tue, 15 Jul 2014 10:05:48 +0000 (12:05 +0200)
src/bin/dhcp4/tests/Makefile.am
src/bin/dhcp4/tests/dhcp4_client.cc
src/bin/dhcp4/tests/dhcp4_client.h
src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
src/bin/dhcp4/tests/dhcp4_test_utils.cc
src/bin/dhcp4/tests/dhcp4_test_utils.h
src/bin/dhcp4/tests/inform_unittest.cc [new file with mode: 0644]

index dba725e03fb1faaaeecdd9940f83b67dcbbd5b95..d80b902b2f05d1c82ec6ffd38b99cd98b820778a 100644 (file)
@@ -89,6 +89,7 @@ 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
+dhcp4_unittests_SOURCES += inform_unittest.cc
 
 if CONFIG_BACKEND_BUNDY
 # For Bundy backend, we only need to run the usual tests. There are no
index 1930923650467745c4be3580f8ba5497e6f63641..dd9c3b8bf6c3eac8a80539dff0ef1004ec95d000 100644 (file)
 // PERFORMANCE OF THIS SOFTWARE.
 
 #include <dhcp/dhcp4.h>
+#include <dhcp/option.h>
+#include <dhcp/option_int_array.h>
+#include <dhcpsrv/lease.h>
 #include <dhcp4/tests/dhcp4_client.h>
+#include <boost/pointer_cast.hpp>
 #include <cstdlib>
 
 namespace isc {
 namespace dhcp {
 namespace test {
 
+Dhcp4Client::Configuration::Configuration()
+    : routers_(), dns_servers_(), serverid_("0.0.0.0") {
+    reset();
+}
+
+void
+Dhcp4Client::Configuration::reset() {
+    routers_.clear();
+    dns_servers_.clear();
+    serverid_ = asiolink::IOAddress("0.0.0.0");
+}
+
 Dhcp4Client::Dhcp4Client() :
+    config_(),
     curr_transid_(0),
     dest_addr_("255.255.255.255"),
     hwaddr_(generateHWAddr()),
-    relay_addr_("10.0.0.2"),
+    relay_addr_("192.0.2.2"),
+    requested_options_(),
+    server_facing_relay_addr_("10.0.0.2"),
     srv_(boost::shared_ptr<NakedDhcpv4Srv>(new NakedDhcpv4Srv(0))),
     use_relay_(false) {
 }
 
 Dhcp4Client::Dhcp4Client(boost::shared_ptr<NakedDhcpv4Srv>& srv) :
+    config_(),
     curr_transid_(0),
     dest_addr_("255.255.255.255"),
     hwaddr_(generateHWAddr()),
-    relay_addr_("10.0.0.2"),
+    relay_addr_("192.0.2.2"),
+    requested_options_(),
+    server_facing_relay_addr_("10.0.0.2"),
     srv_(srv),
     use_relay_(false) {
 }
+
+void
+Dhcp4Client::applyConfiguration() {
+    Pkt4Ptr resp = context_.response_;
+    if (!resp) {
+        return;
+    }
+
+    config_.reset();
+
+    Option4AddrLstPtr opt_routers = boost::dynamic_pointer_cast<
+        Option4AddrLst>(resp->getOption(DHO_ROUTERS));
+    if (opt_routers) {
+        config_.routers_ = opt_routers->getAddresses();
+    }
+
+    Option4AddrLstPtr opt_dns_servers = boost::dynamic_pointer_cast<
+        Option4AddrLst>(resp->getOption(DHO_DOMAIN_NAME_SERVERS));
+    if (opt_dns_servers) {
+        config_.dns_servers_ = opt_dns_servers->getAddresses();
+    }
+
+    OptionCustomPtr opt_serverid = boost::dynamic_pointer_cast<
+        OptionCustom>(resp->getOption(DHO_DHCP_SERVER_IDENTIFIER));
+    if (opt_serverid) {
+        config_.serverid_ = opt_serverid->readAddress();
+    }
+
+    /// @todo Other possible configuration.
+}
+
+void
+Dhcp4Client::createLease(const asiolink::IOAddress& addr,
+                         const uint32_t valid_lft) {
+    Lease4 lease(addr, &hwaddr_->hwaddr_[0], hwaddr_->hwaddr_.size(),
+                 0, 0, valid_lft, valid_lft / 2, valid_lft,
+                 time(NULL), false, false, "");
+    config_.lease_ = lease;
+}
+
 Pkt4Ptr
 Dhcp4Client::createMsg(const uint8_t msg_type) {
     Pkt4Ptr msg(new Pkt4(msg_type, curr_transid_++));
@@ -45,10 +107,39 @@ Dhcp4Client::createMsg(const uint8_t msg_type) {
 }
 
 void
-Dhcp4Client::doInform() {
+Dhcp4Client::doInform(const bool set_ciaddr) {
     context_.query_ = createMsg(DHCPINFORM);
+    // Request options if any.
+    if (!requested_options_.empty()) {
+        // Include Parameter Request List if at least one option code
+        // has been specified to be requested.
+        OptionUint8ArrayPtr prl(new OptionUint8Array(Option::V4,
+                                  DHO_DHCP_PARAMETER_REQUEST_LIST));
+        for (std::set<uint8_t>::const_iterator opt = requested_options_.begin();
+             opt != requested_options_.end(); ++opt) {
+            prl->addValue(*opt);
+        }
+        context_.query_->addOption(prl);
+    }
+    // The client sending a DHCPINFORM message has an IP address obtained
+    // by some other means, e.g. static configuration. The lease which we
+    // are using here is most likely set by the createLease method.
+    if (set_ciaddr) {
+        context_.query_->setCiaddr(config_.lease_.addr_);
+    }
+    context_.query_->setLocalAddr(config_.lease_.addr_);
+    // Send the message to the server.
     sendMsg(context_.query_);
+    // Expect response. If there is no response, return.
     context_.response_ = receiveOneMsg();
+    if (!context_.response_) {
+        return;
+    }
+    // If DHCPACK has been returned by the server, use the returned
+    // configuration.
+    if (context_.response_->getType() == DHCPACK) {
+        applyConfiguration();
+    }
 }
 
 HWAddrPtr
@@ -76,6 +167,23 @@ Dhcp4Client::modifyHWAddr() {
     ++hwaddr_->hwaddr_[hwaddr_->hwaddr_.size() - 1];
 }
 
+void
+Dhcp4Client::requestOption(const uint8_t option) {
+    if (option != 0) {
+        requested_options_.insert(option);
+    }
+}
+
+void
+Dhcp4Client::requestOptions(const uint8_t option1, const uint8_t option2,
+                            const uint8_t option3) {
+    requested_options_.clear();
+    requestOption(option1);
+    requestOption(option2);
+    requestOption(option3);
+}
+
+
 Pkt4Ptr
 Dhcp4Client::receiveOneMsg() {
     // Return empty pointer if server hasn't responded.
@@ -84,21 +192,35 @@ Dhcp4Client::receiveOneMsg() {
     }
     Pkt4Ptr msg = srv_->fake_sent_.front();
     srv_->fake_sent_.pop_front();
-    return (msg);
+
+    // Copy the original message to simulate reception over the wire.
+    msg->pack();
+    Pkt4Ptr msg_copy(new Pkt4(static_cast<const uint8_t*>
+                              (msg->getBuffer().getData()),
+                              msg->getBuffer().getLength()));
+    msg_copy->setRemoteAddr(msg->getLocalAddr());
+    msg_copy->setLocalAddr(msg->getRemoteAddr());
+    msg_copy->setIface(msg->getIface());
+
+    msg_copy->unpack();
+
+    return (msg_copy);
 }
 
 void
 Dhcp4Client::sendMsg(const Pkt4Ptr& msg) {
     srv_->shutdown_ = false;
     if (use_relay_) {
+        msg->setHops(1);
         msg->setGiaddr(relay_addr_);
+        msg->setLocalAddr(server_facing_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->setRemoteAddr(msg->getLocalAddr());
     msg_copy->setLocalAddr(dest_addr_);
     msg_copy->setIface("eth0");
     srv_->fakeReceive(msg_copy);
index 67d2f78914bec4008422d94bea14f4687a6999f7..f1eefe7c47f0066e240172408f36f5818afc2279 100644 (file)
@@ -21,6 +21,7 @@
 #include <dhcp4/tests/dhcp4_test_utils.h>
 #include <boost/noncopyable.hpp>
 #include <boost/shared_ptr.hpp>
+#include <set>
 
 namespace isc {
 namespace dhcp {
@@ -50,7 +51,21 @@ public:
         /// @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_;    };
+        Pkt4Ptr response_;
+    };
+
+    /// @brief Holds the configuration of the client received from the
+    /// DHCP server.
+    struct Configuration {
+        Option4AddrLst::AddressContainer routers_;
+        Option4AddrLst::AddressContainer dns_servers_;
+        Lease4 lease_;
+        asiolink::IOAddress serverid_;
+
+        Configuration();
+
+        void reset();
+    };
 
     /// @brief Creates a new client.
     Dhcp4Client();
@@ -60,15 +75,35 @@ public:
     /// @param srv An instance of the DHCPv4 server to be used.
     Dhcp4Client(boost::shared_ptr<NakedDhcpv4Srv>& srv);
 
+    /// @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 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).
     ///
+    /// @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();
+    void doInform(const bool set_ciaddr = true);
 
     /// @brief Generates a hardware address used by the client.
     ///
@@ -104,6 +139,33 @@ public:
     /// @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 destination address for the messages being sent by the
     /// client.
     ///
@@ -124,13 +186,22 @@ public:
     /// @param relay_addr 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_;
+
 private:
 
+    /// @brief Stores configuration received from the server.
+    void applyConfiguration();
+
     /// @brief Creates client's side DHCP message.
     ///
     /// @param msg_type Type of the message to be created.
@@ -166,6 +237,12 @@ private:
     /// @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_;
 
index 839d8fd19d5a638e3201dc68ed20b9ccf12be4c4..f134a694ce76820d2f127523c34cbf37c5dfac6b 100644 (file)
@@ -480,20 +480,6 @@ TEST_F(Dhcpv4SrvTest, processDecline) {
     EXPECT_NO_THROW(srv.processDecline(pkt));
 }
 
-TEST_F(Dhcpv4SrvTest, processInform) {
-    NakedDhcpv4Srv srv;
-    Pkt4Ptr pkt(new Pkt4(DHCPINFORM, 1234));
-
-    // Should not throw
-    EXPECT_NO_THROW(srv.processInform(pkt));
-
-    // Should return something
-    EXPECT_TRUE(srv.processInform(pkt));
-
-    // @todo Implement more reasonable tests before starting
-    // work on processSomething() method.
-}
-
 TEST_F(Dhcpv4SrvTest, serverReceivedPacketName) {
     // Check all possible packet types
     for (int itype = 0; itype < 256; ++itype) {
@@ -3587,6 +3573,7 @@ TEST_F(Dhcpv4SrvTest, acceptDirectRequest) {
     // message is considered malformed and the accept() function should
     // return false.
     pkt->setGiaddr(IOAddress("192.0.10.1"));
+    pkt->setRemoteAddr(IOAddress("0.0.0.0"));
     pkt->setLocalAddr(IOAddress("192.0.2.3"));
     pkt->setIface("eth1");
     EXPECT_FALSE(srv.accept(pkt));
@@ -3621,6 +3608,20 @@ TEST_F(Dhcpv4SrvTest, acceptDirectRequest) {
     pkt->setLocalAddr(IOAddress("10.0.0.1"));
     EXPECT_TRUE(srv.accept(pkt));
 
+    // For the DHCPINFORM the ciaddr should be set or at least the source
+    // address.
+    pkt->setType(DHCPINFORM);
+    pkt->setRemoteAddr(IOAddress("10.0.0.101"));
+    EXPECT_TRUE(srv.accept(pkt));
+
+    // When neither ciaddr nor source addres is present, the packet should
+    // be dropped.
+    pkt->setRemoteAddr(IOAddress("0.0.0.0"));
+    EXPECT_FALSE(srv.accept(pkt));
+
+    // When ciaddr is set, the packet should be accepted.
+    pkt->setCiaddr(IOAddress("10.0.0.1"));
+    EXPECT_TRUE(srv.accept(pkt));
 }
 
 // This test checks that the server rejects a message with invalid type.
index c641f5bb0710e731fc1ae8adf0da709e808cf930..d5d0ef95301801b82ea0979685ff1aa615480a33 100644 (file)
@@ -563,16 +563,22 @@ Dhcpv4SrvTest::testDiscoverRequest(const uint8_t msg_type) {
 
 void
 Dhcpv4SrvTest::configure(const std::string& config) {
+    configure(config, srv_);
+}
+
+void
+Dhcpv4SrvTest::configure(const std::string& config, NakedDhcpv4Srv& srv) {
     ElementPtr json = Element::fromJSON(config);
     ConstElementPtr status;
 
     // Configure the server and make sure the config is accepted
-    EXPECT_NO_THROW(status = configureDhcp4Server(srv_, json));
+    EXPECT_NO_THROW(status = configureDhcp4Server(srv, json));
     ASSERT_TRUE(status);
     int rcode;
     ConstElementPtr comment = config::parseAnswer(rcode, status);
     ASSERT_EQ(0, rcode);
-}
+ }
+
 
 
 }; // end of isc::dhcp::test namespace
index d7513da24c325ce2a8dba21e9c32fd3247cb8322..5ca9526aac2d9196e6cd46f045722b5b0fbdc9dc 100644 (file)
@@ -407,6 +407,12 @@ public:
     /// @param config String holding server configuration in JSON format.
     void configure(const std::string& config);
 
+    /// @brief Configure specified DHCP server using JSON string.
+    ///
+    /// @param config String holding server configuration in JSON format.
+    /// @param srv Instance of the server to be configured.
+    void configure(const std::string& config, NakedDhcpv4Srv& srv);
+
     /// @brief This function cleans up after the test.
     virtual void TearDown();
 
diff --git a/src/bin/dhcp4/tests/inform_unittest.cc b/src/bin/dhcp4/tests/inform_unittest.cc
new file mode 100644 (file)
index 0000000..52450d6
--- /dev/null
@@ -0,0 +1,328 @@
+// 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 <config.h>
+#include <asiolink/io_address.h>
+#include <cc/data.h>
+#include <dhcp/dhcp4.h>
+#include <dhcp/tests/iface_mgr_test_config.h>
+#include <dhcp4/tests/dhcp4_test_utils.h>
+#include <dhcp4/tests/dhcp4_client.h>
+
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::data;
+using namespace isc::dhcp;
+using namespace isc::dhcp::test;
+
+namespace {
+
+/// @brief Set of JSON configurations used throughout the Rebind tests.
+///
+/// - Configuration 0:
+///   - Used for testing direct traffic
+///   - 1 subnet: 10.0.0.0/24
+///   - 1 pool: 10.0.0.10-10.0.0.100
+///   - Router option present: 10.0.0.200 and 10.0.0.201
+///   - Domain Name Server option present: 10.0.0.202, 10.0.0.203.
+///
+/// - Configuration 1:
+///   - Use for testing relayed messages
+///   - 1 subnet: 192.0.2.0/24
+///   - Router option present: 192.0.2.200 and 192.0.2.201
+///   - Domain Name Server option present: 192.0.2.202, 192.0.2.203.
+const char* INFORM_CONFIGS[] = {
+// Configuration 0
+    "{ \"interfaces\": [ \"all\" ],"
+        "\"valid-lifetime\": 600,"
+        "\"subnet4\": [ { "
+        "    \"subnet\": \"10.0.0.0/24\", "
+        "    \"pool\": [ \"10.0.0.10-10.0.0.100\" ],"
+        "    \"option-data\": [ {"
+        "        \"name\": \"routers\","
+        "        \"code\": 3,"
+        "        \"data\": \"10.0.0.200,10.0.0.201\","
+        "        \"csv-format\": true,"
+        "        \"space\": \"dhcp4\""
+        "    },"
+        "    {"
+        "        \"name\": \"domain-name-servers\","
+        "        \"code\": 6,"
+        "        \"data\": \"10.0.0.202,10.0.0.203\","
+        "        \"csv-format\": true,"
+        "        \"space\": \"dhcp4\""
+
+        "    } ]"
+        " } ]"
+    "}",
+
+// Configuration 1
+    "{ \"interfaces\": [ \"all\" ],"
+        "\"valid-lifetime\": 600,"
+        "\"subnet4\": [ { "
+        "    \"subnet\": \"192.0.2.0/24\", "
+        "    \"option-data\": [ {"
+        "        \"name\": \"routers\","
+        "        \"code\": 3,"
+        "        \"data\": \"192.0.2.200,192.0.2.201\","
+        "        \"csv-format\": true,"
+        "        \"space\": \"dhcp4\""
+        "    },"
+        "    {"
+        "        \"name\": \"domain-name-servers\","
+        "        \"code\": 6,"
+        "        \"data\": \"192.0.2.202,192.0.2.203\","
+        "        \"csv-format\": true,"
+        "        \"space\": \"dhcp4\""
+
+        "    } ]"
+        " } ]"
+    "}"
+};
+
+/// @brief Test fixture class for testing DHCPINFORM.
+class InformTest : public Dhcpv4SrvTest {
+public:
+
+    /// @brief Constructor.
+    ///
+    /// Sets up fake interfaces.
+    InformTest()
+        : Dhcpv4SrvTest(),
+          iface_mgr_test_config_(true) {
+        IfaceMgr::instance().openSockets4();
+    }
+
+    /// @brief Interface Manager's fake configuration control.
+    IfaceMgrTestConfig iface_mgr_test_config_;
+
+};
+
+// Test that directly connected client's DHCPINFORM message is processed and
+// DHCPACK message is sent back.
+TEST_F(InformTest, directClientBroadcast) {
+    Dhcp4Client client;
+    // Configure DHCP server.
+    configure(INFORM_CONFIGS[0], *client.getServer());
+    // Request some configuration when DHCPINFORM is sent.
+    client.requestOptions(DHO_DOMAIN_NAME_SERVERS, DHO_ROUTERS);
+    // Preconfigure the client with the IP address.
+    client.createLease(IOAddress("10.0.0.56"), 600);
+    // Send DHCPINFORM message to the server.
+    client.doInform();
+    //    ASSERT_NO_THROW(client.doInform());
+    // Make sure that the server responded.
+    ASSERT_TRUE(client.getContext().response_);
+    Pkt4Ptr resp = client.getContext().response_;
+    // Make sure that the server has responded with DHCPACK.
+    ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
+    // Response should have been unicast to the ciaddr.
+    EXPECT_EQ(IOAddress("10.0.0.56"), resp->getLocalAddr());
+    // Response must not be relayed.
+    EXPECT_FALSE(resp->isRelayed());
+    // Make sure that the server id is present.
+    EXPECT_EQ("10.0.0.1", client.config_.serverid_.toText());
+    // Make sure that the Routers option has been received.
+    ASSERT_EQ(2, client.config_.routers_.size());
+    EXPECT_EQ("10.0.0.200", client.config_.routers_[0].toText());
+    EXPECT_EQ("10.0.0.201", client.config_.routers_[1].toText());
+    // Make sure that the DNS Servers option has been received.
+    ASSERT_EQ(2, client.config_.dns_servers_.size());
+    EXPECT_EQ("10.0.0.202", client.config_.dns_servers_[0].toText());
+    EXPECT_EQ("10.0.0.203", client.config_.dns_servers_[1].toText());
+
+    // Check that we can send another DHCPINFORM message using
+    // different ciaddr and we will get the configuration.
+    client.createLease(IOAddress("10.0.0.12"), 600);
+    // This time do not request DNS Servers option and it should not
+    // be returned.
+    client.requestOptions(DHO_ROUTERS);
+    // Send DHCPINFORM.
+    ASSERT_NO_THROW(client.doInform());
+    ASSERT_TRUE(client.getContext().response_);
+    resp = client.getContext().response_;
+    // Make sure that the server has responded with DHCPACK.
+    ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
+    // Response should have been unicast to the ciaddr.
+    EXPECT_EQ(IOAddress("10.0.0.12"), resp->getLocalAddr());
+    // Response must not be relayed.
+    EXPECT_FALSE(resp->isRelayed());
+    // Make sure that the server id is present.
+    EXPECT_EQ("10.0.0.1", client.config_.serverid_.toText());
+    // Make sure that the Routers option has been received.
+    ASSERT_EQ(2, client.config_.routers_.size());
+    EXPECT_EQ("10.0.0.200", client.config_.routers_[0].toText());
+    EXPECT_EQ("10.0.0.201", client.config_.routers_[1].toText());
+    // Make sure that the DNS Servers option has been received.
+    ASSERT_TRUE(client.config_.dns_servers_.empty());
+}
+
+// This test checks that the server drops DHCPINFORM message when the
+// source address and ciaddr is 0.
+TEST_F(InformTest, directClientBroadcastNoAddress) {
+    Dhcp4Client client;
+    // Configure DHCP server.
+    configure(INFORM_CONFIGS[0], *client.getServer());
+    // Request some configuration when DHCPINFORM is sent.
+    client.requestOptions(DHO_DOMAIN_NAME_SERVERS, DHO_ROUTERS);
+    // Send DHCPINFORM message to the server.
+    ASSERT_NO_THROW(client.doInform());
+    // Make sure that the server dropped the message.
+    ASSERT_FALSE(client.getContext().response_);
+}
+
+// Test that client's DHCPINFORM message sent to a unicast address
+// is received and processed by the server and that the DHCPACK is
+// is sent.
+TEST_F(InformTest, directClientUnicast) {
+    Dhcp4Client client;
+    // Configure DHCP server.
+    configure(INFORM_CONFIGS[0], *client.getServer());
+    // Request some configuration when DHCPINFORM is sent.
+    client.requestOptions(DHO_DOMAIN_NAME_SERVERS, DHO_ROUTERS);
+    // Preconfigure the client with the IP address.
+    client.createLease(IOAddress("10.0.0.56"), 600);
+    // Set remote address to unicast.
+    client.setDestAddress(IOAddress("10.0.0.1"));
+    // Send DHCPINFORM message to the server.
+    ASSERT_NO_THROW(client.doInform());
+    // Make sure that the server responded.
+    ASSERT_TRUE(client.getContext().response_);
+    Pkt4Ptr resp = client.getContext().response_;
+    // Make sure that the server has responded with DHCPACK.
+    ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
+    // Response should have been unicast to the ciaddr.
+    EXPECT_EQ(IOAddress("10.0.0.56"), resp->getLocalAddr());
+    // Response must not be relayed.
+    EXPECT_FALSE(resp->isRelayed());
+    // Make sure that the server id is present.
+    EXPECT_EQ("10.0.0.1", client.config_.serverid_.toText());
+    // Make sure that the Routers option has been received.
+    ASSERT_EQ(2, client.config_.routers_.size());
+    EXPECT_EQ("10.0.0.200", client.config_.routers_[0].toText());
+    EXPECT_EQ("10.0.0.201", client.config_.routers_[1].toText());
+    // Make sure that the DNS Servers option has been received.
+    ASSERT_EQ(2, client.config_.dns_servers_.size());
+    EXPECT_EQ("10.0.0.202", client.config_.dns_servers_[0].toText());
+    EXPECT_EQ("10.0.0.203", client.config_.dns_servers_[1].toText());
+}
+
+// This test checks that the server responds to the source address of the
+// packet received from the directly connected client if the client didn't
+// set the ciaddr.
+TEST_F(InformTest, directClientNoCiaddr) {
+    Dhcp4Client client;
+    // Configure DHCP server.
+    configure(INFORM_CONFIGS[0], *client.getServer());
+    // Request some configuration when DHCPINFORM is sent.
+    client.requestOptions(DHO_DOMAIN_NAME_SERVERS, DHO_ROUTERS);
+    // Preconfigure the client with the IP address.
+    client.createLease(IOAddress("10.0.0.56"), 600);
+    // Send DHCPINFORM message (with ciaddr not set) to the server.
+    ASSERT_NO_THROW(client.doInform(false));
+    // Make sure that the server responded.
+    ASSERT_TRUE(client.getContext().response_);
+    Pkt4Ptr resp = client.getContext().response_;
+    // Make sure that the server has responded with DHCPACK.
+    ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
+    // Response should have been unicast to the ciaddr.
+    EXPECT_EQ(IOAddress("10.0.0.56"), resp->getLocalAddr());
+    // Response must not be relayed.
+    EXPECT_FALSE(resp->isRelayed());
+    // Make sure that the server id is present.
+    EXPECT_EQ("10.0.0.1", client.config_.serverid_.toText());
+    // Make sure that the Routers option has been received.
+    ASSERT_EQ(2, client.config_.routers_.size());
+    EXPECT_EQ("10.0.0.200", client.config_.routers_[0].toText());
+    EXPECT_EQ("10.0.0.201", client.config_.routers_[1].toText());
+    // Make sure that the DNS Servers option has been received.
+    ASSERT_EQ(2, client.config_.dns_servers_.size());
+    EXPECT_EQ("10.0.0.202", client.config_.dns_servers_[0].toText());
+    EXPECT_EQ("10.0.0.203", client.config_.dns_servers_[1].toText());
+}
+
+// This test checks that the server receiving DHCPINFORM via relay, unicasts the
+// DHCPACK to the client (ciaddr).
+TEST_F(InformTest, relayedClient) {
+    Dhcp4Client client;
+    // Configure DHCP server.
+    configure(INFORM_CONFIGS[1], *client.getServer());
+    // Message is relayed.
+    client.useRelay();
+    // Request some configuration when DHCPINFORM is sent.
+    client.requestOptions(DHO_DOMAIN_NAME_SERVERS, DHO_ROUTERS);
+    // Preconfigure the client with the IP address.
+    client.createLease(IOAddress("192.0.2.56"), 600);
+    // Send DHCPINFORM message to the server.
+    ASSERT_NO_THROW(client.doInform());
+    // Make sure that the server responded.
+    ASSERT_TRUE(client.getContext().response_);
+    Pkt4Ptr resp = client.getContext().response_;
+    // Make sure that the server has responded with DHCPACK.
+    ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
+    // Response should have been unicast to the ciaddr.
+    EXPECT_EQ(IOAddress("192.0.2.56"), resp->getLocalAddr());
+    // Response is unicast to the client, so it must not be relayed.
+    EXPECT_FALSE(resp->isRelayed());
+    // Make sure that the server id is present.
+    EXPECT_EQ("10.0.0.1", client.config_.serverid_.toText());
+    // Make sure that the Routers option has been received.
+    ASSERT_EQ(2, client.config_.routers_.size());
+    EXPECT_EQ("192.0.2.200", client.config_.routers_[0].toText());
+    EXPECT_EQ("192.0.2.201", client.config_.routers_[1].toText());
+    // Make sure that the DNS Servers option has been received.
+    ASSERT_EQ(2, client.config_.dns_servers_.size());
+    EXPECT_EQ("192.0.2.202", client.config_.dns_servers_[0].toText());
+    EXPECT_EQ("192.0.2.203", client.config_.dns_servers_[1].toText());
+}
+
+// This test checks that the server can respond to the DHCPINFORM message
+// received via relay when the ciaddr is not set.
+TEST_F(InformTest, relayedClientNoCiaddr) {
+    Dhcp4Client client;
+    // Configure DHCP server.
+    configure(INFORM_CONFIGS[1], *client.getServer());
+    // Message is relayed.
+    client.useRelay();
+    // Request some configuration when DHCPINFORM is sent.
+    client.requestOptions(DHO_DOMAIN_NAME_SERVERS, DHO_ROUTERS);
+    // Send DHCPINFORM message to the server.
+    ASSERT_NO_THROW(client.doInform());
+    // Make sure that the server responded.
+    ASSERT_TRUE(client.getContext().response_);
+    Pkt4Ptr resp = client.getContext().response_;
+    // Make sure that the server has responded with DHCPACK.
+    ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
+    // Response should go through a relay as there is no ciaddr.
+    EXPECT_EQ(IOAddress("192.0.2.2"), resp->getLocalAddr());
+    EXPECT_EQ(IOAddress("192.0.2.2"), resp->getGiaddr());
+    EXPECT_EQ(1, resp->getHops());
+    // In the case when the client didn't set the ciaddr and the message
+    // was received via relay the server sets the Broadcast flag to help
+    // the relay forwarding the message (without yiaddr) to the client.
+    EXPECT_EQ(BOOTP_BROADCAST, resp->getFlags() & BOOTP_BROADCAST);
+    // Make sure that the server id is present.
+    EXPECT_EQ("10.0.0.1", client.config_.serverid_.toText());
+    // Make sure that the Routers option has been received.
+    ASSERT_EQ(2, client.config_.routers_.size());
+    EXPECT_EQ("192.0.2.200", client.config_.routers_[0].toText());
+    EXPECT_EQ("192.0.2.201", client.config_.routers_[1].toText());
+    // Make sure that the DNS Servers option has been received.
+    ASSERT_EQ(2, client.config_.dns_servers_.size());
+    EXPECT_EQ("192.0.2.202", client.config_.dns_servers_[0].toText());
+    EXPECT_EQ("192.0.2.203", client.config_.dns_servers_[1].toText());
+}
+
+
+} // end of anonymous namespace