// 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_++));
}
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
++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.
}
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);
#include <dhcp4/tests/dhcp4_test_utils.h>
#include <boost/noncopyable.hpp>
#include <boost/shared_ptr.hpp>
+#include <set>
namespace isc {
namespace dhcp {
/// @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();
/// @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.
///
/// @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.
///
/// @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.
/// @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_;
--- /dev/null
+// 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