From: Marcin Siodelski Date: Tue, 15 Jul 2014 10:05:48 +0000 (+0200) Subject: [3390] Implemented tests for DHCPINFORM processing. X-Git-Tag: trac3482_base~79^2~7 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=852fb7c13332d024cdab8e594336b1cfadba757c;p=thirdparty%2Fkea.git [3390] Implemented tests for DHCPINFORM processing. --- diff --git a/src/bin/dhcp4/tests/Makefile.am b/src/bin/dhcp4/tests/Makefile.am index dba725e03f..d80b902b2f 100644 --- a/src/bin/dhcp4/tests/Makefile.am +++ b/src/bin/dhcp4/tests/Makefile.am @@ -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 diff --git a/src/bin/dhcp4/tests/dhcp4_client.cc b/src/bin/dhcp4/tests/dhcp4_client.cc index 1930923650..dd9c3b8bf6 100644 --- a/src/bin/dhcp4/tests/dhcp4_client.cc +++ b/src/bin/dhcp4/tests/dhcp4_client.cc @@ -13,30 +13,92 @@ // PERFORMANCE OF THIS SOFTWARE. #include +#include +#include +#include #include +#include #include 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(new NakedDhcpv4Srv(0))), use_relay_(false) { } Dhcp4Client::Dhcp4Client(boost::shared_ptr& 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::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 + (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 (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); diff --git a/src/bin/dhcp4/tests/dhcp4_client.h b/src/bin/dhcp4/tests/dhcp4_client.h index 67d2f78914..f1eefe7c47 100644 --- a/src/bin/dhcp4/tests/dhcp4_client.h +++ b/src/bin/dhcp4/tests/dhcp4_client.h @@ -21,6 +21,7 @@ #include #include #include +#include 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& 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 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 srv_; diff --git a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc index 839d8fd19d..f134a694ce 100644 --- a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc +++ b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc @@ -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. diff --git a/src/bin/dhcp4/tests/dhcp4_test_utils.cc b/src/bin/dhcp4/tests/dhcp4_test_utils.cc index c641f5bb07..d5d0ef9530 100644 --- a/src/bin/dhcp4/tests/dhcp4_test_utils.cc +++ b/src/bin/dhcp4/tests/dhcp4_test_utils.cc @@ -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 diff --git a/src/bin/dhcp4/tests/dhcp4_test_utils.h b/src/bin/dhcp4/tests/dhcp4_test_utils.h index d7513da24c..5ca9526aac 100644 --- a/src/bin/dhcp4/tests/dhcp4_test_utils.h +++ b/src/bin/dhcp4/tests/dhcp4_test_utils.h @@ -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 index 0000000000..52450d6916 --- /dev/null +++ b/src/bin/dhcp4/tests/inform_unittest.cc @@ -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 +#include +#include +#include +#include +#include +#include + +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(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(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(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(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(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(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