Commit 7b192fe3 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[master] Merge branch 'trac3564'

parents 35070d0d b1cd4b11
......@@ -5,6 +5,7 @@ EXTRA_DIST = version.ent.in differences.txt Doxyfile Doxyfile-xml
nobase_dist_doc_DATA = examples/kea4/single-subnet.json
nobase_dist_doc_DATA += examples/kea4/several-subnets.json
nobase_dist_doc_DATA += examples/kea4/multiple-options.json
nobase_dist_doc_DATA += examples/kea4/reservations.json
nobase_dist_doc_DATA += examples/kea6/simple.json
nobase_dist_doc_DATA += examples/kea6/several-subnets.json
nobase_dist_doc_DATA += examples/kea6/multiple-options.json
......
# This is an example configuration file for the DHCPv4 server in Kea.
# It contains one subnet in which there are two static address reservations
# for the clients identified by the MAC addresses.
{ "Dhcp4":
{
# Kea is told to listen on eth0 interface only.
"interfaces": [ "eth0" ],
# We need to specify lease type. As of May 2014, three backends are supported:
# memfile, mysql and pgsql. We'll just use memfile, because it doesn't require
# any prior set up.
"lease-database": {
"type": "memfile"
},
# Addresses will be assigned with valid lifetimes being 4000. Client
# is told to start renewing after 1000 seconds. If the server does not respond
# after 2000 seconds since the lease was granted, client is supposed
# to start REBIND procedure (emergency renewal that allows switching
# to a different server).
"valid-lifetime": 4000,
# Renew and rebind timers are commented out. This implies that options
# 58 and 59 will not be sent to the client. In this case it is up to
# the client to pick the timer values according to RFC2131. Uncomment the
# timers to send these options to the client.
# "renew-timer": 1000,
# "rebind-timer": 2000,
# Define a subnet with two reservations for the 192.0.2.202 and
# 192.0.2.100 address. Note that the latter is a reservation for the
# address which is within the range of the pool of the dynamically
# allocated address. The server will exclude this address from this
# pool and only assign it to the client which has a reservation for
# it.
"subnet4": [
{
"pools": [ { "pool": "192.0.2.1 - 192.0.2.200" } ],
"subnet": "192.0.2.0/24",
"interface": "eth0",
"reservations": [
{
"hw-address": "1a:1b:1c:1d:1e:1f",
"ip-address": "192.0.2.202"
},
{
"hw-address": "0a:0b:0c:0d:0e:0f",
"ip-address": "192.0.2.100"
}
]
}
]
},
# The following configures logging. It assumes that messages with at least
# informational level (info, warn, error) will will be logged to stdout.
"Logging": {
"loggers": [
{
"name": "kea-dhcp4",
"output_options": [
{
"output": "stdout"
}
],
"severity": "INFO"
}
]
}
}
......@@ -957,14 +957,17 @@ Dhcpv4Srv::assignLease(const Pkt4Ptr& question, Pkt4Ptr& answer) {
OptionCustomPtr opt_serverid = boost::dynamic_pointer_cast<
OptionCustom>(question->getOption(DHO_DHCP_SERVER_IDENTIFIER));
// Try to get the Requested IP Address option and use the address as a hint
// for the allocation engine. If the server doesn't already have a lease
// for this client it will try to allocate the one requested.
// Check if the client has sent a requested IP address option or
// ciaddr.
OptionCustomPtr opt_requested_address = boost::dynamic_pointer_cast<
OptionCustom>(question->getOption(DHO_DHCP_REQUESTED_ADDRESS));
IOAddress hint("0.0.0.0");
if (opt_requested_address) {
hint = opt_requested_address->readAddress();
} else if (question->getCiaddr() != IOAddress("0.0.0.0")) {
hint = question->getCiaddr();
}
HWAddrPtr hwaddr = question->getHWAddr();
......@@ -1309,6 +1312,11 @@ Dhcpv4Srv::processDiscover(Pkt4Ptr& discover) {
// include in the response. If client did not request
// them we append them for him.
appendBasicOptions(discover, offer);
} else {
// If the server can't offer an address, it drops the packet.
return (Pkt4Ptr());
}
// Set the src/dest IP address, port and interface for the outgoing
......
......@@ -57,7 +57,7 @@ Dhcp4Client::Dhcp4Client(const Dhcp4Client::State& state) :
use_relay_(false) {
}
Dhcp4Client::Dhcp4Client(boost::shared_ptr<NakedDhcpv4Srv>& srv,
Dhcp4Client::Dhcp4Client(boost::shared_ptr<NakedDhcpv4Srv> srv,
const Dhcp4Client::State& state) :
config_(),
ciaddr_(IOAddress("0.0.0.0")),
......@@ -349,6 +349,11 @@ Dhcp4Client::sendMsg(const Pkt4Ptr& msg) {
srv_->run();
}
void
Dhcp4Client::setHWAddress(const std::string& hwaddr_str) {
hwaddr_.reset(new HWAddr(HWAddr::fromText(hwaddr_str)));
}
} // end of namespace isc::dhcp::test
} // end of namespace isc::dhcp
} // end of namespace isc
......@@ -103,7 +103,7 @@ public:
///
/// @param srv An instance of the DHCPv4 server to be used.
/// @param state Initial client's state.
Dhcp4Client(boost::shared_ptr<NakedDhcpv4Srv>& srv,
Dhcp4Client(boost::shared_ptr<NakedDhcpv4Srv> srv,
const State& state = SELECTING);
/// @brief Creates a lease for the client using the specified address
......@@ -203,7 +203,9 @@ public:
HWAddrPtr generateHWAddr(const uint8_t htype = HTYPE_ETHER) const;
/// @brief Returns HW address used by the client.
HWAddrPtr getHWAddress() const;
HWAddrPtr getHWAddress() const {
return (hwaddr_);
}
/// @brief Returns current context.
const Context& getContext() const {
......@@ -266,6 +268,11 @@ public:
dest_addr_ = dest_addr;
}
/// @brief Sets the explicit hardware address for the client.
///
/// @param hwaddr_str String representation of the HW address.
void setHWAddress(const std::string& hwaddr_str);
/// @brief Sets client state.
///
/// Depending on the current state the client's behavior is different
......
......@@ -1136,6 +1136,25 @@ TEST_F(Dhcpv4SrvTest, relayAgentInfoEcho) {
NakedDhcpv4Srv srv(0);
// Use of the captured DHCPDISCOVER packet requires that
// subnet 10.254.226.0/24 is in use, because this packet
// contains the giaddr which belongs to this subnet and
// this giaddr is used to select the subnet
std::string config = "{ \"interfaces\": [ \"*\" ],"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"subnet4\": [ { "
" \"pools\": [ { \"pool\": \"10.254.226.0/25\" } ],"
" \"subnet\": \"10.254.226.0/24\", "
" \"rebind-timer\": 2000, "
" \"renew-timer\": 1000, "
" \"valid-lifetime\": 4000,"
" \"interface\": \"eth0\" "
" } ],"
"\"valid-lifetime\": 4000 }";
configure(config);
// Let's create a relayed DISCOVER. This particular relayed DISCOVER has
// added option 82 (relay agent info) with 3 suboptions. The server
// is supposed to echo it back in its response.
......@@ -1610,7 +1629,7 @@ public:
192, 0, 2, 1, // ciaddr
1, 2, 3, 4, // yiaddr
192, 0, 2, 255, // siaddr
255, 255, 255, 255, // giaddr
192, 0, 2, 50, // giaddr
};
// Initialize the vector with the header fields defined above.
......@@ -2041,7 +2060,6 @@ TEST_F(HooksDhcpv4SrvTest, buffer4ReceiveValueChange) {
IfaceMgrTestConfig test_config(true);
IfaceMgr::instance().openSockets4();
// Install callback that modifies MAC addr of incoming packet
EXPECT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
"buffer4_receive", buffer4_receive_change_hwaddr));
......
......@@ -283,10 +283,11 @@ void Dhcpv4SrvTest::checkAddressParams(const Pkt4Ptr& rsp,
}
}
void Dhcpv4SrvTest::checkResponse(const Pkt4Ptr& rsp, uint8_t expected_message_type,
void Dhcpv4SrvTest::checkResponse(const Pkt4Ptr& rsp, int expected_message_type,
uint32_t expected_transid) {
ASSERT_TRUE(rsp);
EXPECT_EQ(expected_message_type, rsp->getType());
EXPECT_EQ(expected_message_type,
static_cast<int>(rsp->getType()));
EXPECT_EQ(expected_transid, rsp->getTransid());
}
......@@ -544,31 +545,33 @@ Dhcpv4SrvTest::testDiscoverRequest(const uint8_t msg_type) {
if (msg_type == DHCPDISCOVER) {
ASSERT_NO_THROW(rsp = srv->processDiscover(received));
// Should return non-NULL packet.
ASSERT_TRUE(rsp);
// Should return NULL packet.
ASSERT_FALSE(rsp);
} else {
ASSERT_NO_THROW(rsp = srv->processRequest(received));
// Should return non-NULL packet.
ASSERT_TRUE(rsp);
// We should get the NAK packet with yiaddr set to 0.
EXPECT_EQ(DHCPNAK, rsp->getType());
ASSERT_EQ("0.0.0.0", rsp->getYiaddr().toText());
// Make sure that none of the requested options is returned in NAK.
// Also options such as Routers or Subnet Mask should not be there,
// because lease hasn't been acquired.
EXPECT_TRUE(noRequestedOptions(rsp));
EXPECT_TRUE(noBasicOptions(rsp));
}
// We should get the NAK packet with yiaddr set to 0.
EXPECT_EQ(DHCPNAK, rsp->getType());
ASSERT_EQ("0.0.0.0", rsp->getYiaddr().toText());
// Make sure that none of the requested options is returned in NAK.
// Also options such as Routers or Subnet Mask should not be there,
// because lease hasn't been acquired.
EXPECT_TRUE(noRequestedOptions(rsp));
EXPECT_TRUE(noBasicOptions(rsp));
}
void
Dhcpv4SrvTest::configure(const std::string& config) {
configure(config, srv_);
Dhcpv4SrvTest::configure(const std::string& config, const bool commit) {
configure(config, srv_, commit);
}
void
Dhcpv4SrvTest::configure(const std::string& config, NakedDhcpv4Srv& srv) {
Dhcpv4SrvTest::configure(const std::string& config, NakedDhcpv4Srv& srv,
const bool commit) {
ElementPtr json = Element::fromJSON(config);
ConstElementPtr status;
......@@ -579,7 +582,9 @@ Dhcpv4SrvTest::configure(const std::string& config, NakedDhcpv4Srv& srv) {
ConstElementPtr comment = config::parseAnswer(rcode, status);
ASSERT_EQ(0, rcode);
CfgMgr::instance().commit();
if (commit) {
CfgMgr::instance().commit();
}
}
......
......@@ -307,7 +307,7 @@ public:
/// @param rsp response packet to be validated
/// @param expected_message_type expected message type
/// @param expected_transid expected transaction-id
void checkResponse(const Pkt4Ptr& rsp, uint8_t expected_message_type,
void checkResponse(const Pkt4Ptr& rsp, int expected_message_type,
uint32_t expected_transid);
/// @brief Checks if the lease sent to client is present in the database
......@@ -405,13 +405,18 @@ public:
/// @brief Runs DHCPv4 configuration from the JSON string.
///
/// @param config String holding server configuration in JSON format.
void configure(const std::string& config);
/// @param commit A boolean flag indicating if the new configuration
/// should be committed (if true), or not (if false).
void configure(const std::string& config, const bool commit = true);
/// @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);
/// @param commit A boolean flag indicating if the new configuration
/// should be committed (if true), or not (if false).
void configure(const std::string& config, NakedDhcpv4Srv& srv,
const bool commit = true);
/// @brief This function cleans up after the test.
virtual void TearDown();
......
......@@ -17,6 +17,10 @@
#include <cc/data.h>
#include <dhcp/dhcp4.h>
#include <dhcp/tests/iface_mgr_test_config.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/host.h>
#include <dhcpsrv/host_mgr.h>
#include <dhcpsrv/subnet_id.h>
#include <dhcp4/tests/dhcp4_test_utils.h>
#include <dhcp4/tests/dhcp4_client.h>
#include <boost/shared_ptr.hpp>
......@@ -47,12 +51,19 @@ namespace {
/// - Domain Name Server option present: 192.0.2.202, 192.0.2.203.
/// - Log Servers option present: 192.0.2.200 and 192.0.2.201
/// - Quotes Servers option present: 192.0.2.202, 192.0.2.203.
///
/// - Configuration 2:
/// - Use for testing simple scenarios with host reservations
/// - 1 subnet: 10.0.0.0/24
/// - One reservation for the client using MAC address:
/// aa:bb:cc:dd:ee:ff, reserved address 10.0.0.7
const char* DORA_CONFIGS[] = {
// Configuration 0
"{ \"interfaces\": [ \"*\" ],"
"\"valid-lifetime\": 600,"
"\"subnet4\": [ { "
" \"subnet\": \"10.0.0.0/24\", "
" \"id\": 1,"
" \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ],"
" \"option-data\": [ {"
" \"name\": \"routers\","
......@@ -119,6 +130,21 @@ const char* DORA_CONFIGS[] = {
" \"space\": \"dhcp4\""
" } ]"
" } ]"
"}",
// Configuration 2
"{ \"interfaces\": [ \"*\" ],"
"\"valid-lifetime\": 600,"
"\"subnet4\": [ { "
" \"subnet\": \"10.0.0.0/24\", "
" \"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ],"
" \"reservations\": [ "
" {"
" \"hw-address\": \"aa:bb:cc:dd:ee:ff\","
" \"ip-address\": \"10.0.0.7\""
" }"
" ]"
"} ]"
"}"
};
......@@ -402,4 +428,246 @@ TEST_F(DORATest, ciaddr) {
EXPECT_EQ("0.0.0.0", resp->getCiaddr().toText());
}
// This is a simple test for the host reservation. It creates a reservation
// for an address for a single client, identified by the HW address. The
// test verifies that the client using this HW address will obtain a
// lease for the reserved address. It also checks that the client using
// a different HW address will obtain an address from the dynamic pool.
TEST_F(DORATest, reservation) {
// Client A is a one which will have a reservation.
Dhcp4Client clientA(Dhcp4Client::SELECTING);
// Set explicit HW address so as it matches the reservation in the
// configuration used below.
clientA.setHWAddress("aa:bb:cc:dd:ee:ff");
// Configure DHCP server.
configure(DORA_CONFIGS[2], *clientA.getServer());
// Client A performs 4-way exchange and should obtain a reserved
// address.
ASSERT_NO_THROW(clientA.doDORA(boost::shared_ptr<
IOAddress>(new IOAddress("0.0.0.0"))));
// Make sure that the server responded.
ASSERT_TRUE(clientA.getContext().response_);
Pkt4Ptr resp = clientA.getContext().response_;
// Make sure that the server has responded with DHCPACK.
ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
// Make sure that the client has got the lease for the reserved address.
ASSERT_EQ("10.0.0.7", clientA.config_.lease_.addr_.toText());
// Client B uses the same server as Client A.
Dhcp4Client clientB(clientA.getServer(), Dhcp4Client::SELECTING);
// Client B has no reservation so it should get the lease from
// the dynamic pool.
ASSERT_NO_THROW(clientB.doDORA(boost::shared_ptr<
IOAddress>(new IOAddress("0.0.0.0"))));
// Make sure that the server responded.
ASSERT_TRUE(clientB.getContext().response_);
resp = clientB.getContext().response_;
// Make sure that the server has responded with DHCPACK.
ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
// Obtain the subnet to which the returned address belongs.
Subnet4Ptr subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->
selectSubnet(clientB.config_.lease_.addr_);
ASSERT_TRUE(subnet);
// Make sure that the address has been allocated from the dynamic pool.
ASSERT_TRUE(subnet->inPool(Lease::TYPE_V4, clientB.config_.lease_.addr_));
}
// This test checks the following scenario:
// 1. Client A performs 4-way exchange and obrains a lease from the dynamic pool.
// 2. Reservation is created for the client A using an address out of the dynamic
// pool.
// 3. Client A renews the lease.
// 4. Server responds with DHCPNAK to indicate that the client should stop using
// an address for which it has a lease. Server doesn't want to renew an
// address for which the client doesn't have a reservation, while it has
// a reservation for a different address.
// 5. Client A receives a DHCPNAK and returns to the DHCP server discovery.
// 6. Client A performs a 4-way exchange with a server and the server allocates
// a reserved address to the Client A.
// 7. Client A renews the allocated address and the server returns a DHCPACK.
// 8. Reservation for the Client A is removed.
// 9. Client A renews the (previously reserved) lease and the server returns
// DHCPNAK because the address in use is neither reserved nor belongs to
// the dynamic pool.
// 10. Client A returns to the DHCP server discovery.
// 11. Client A uses 4-way exchange to obtain a lease from the dynamic pool.
// 12. The new address that the Client A is using is reserved for Client B.
// Client A still holds this address.
// 13. Client B uses 4-way exchange to obtain a new lease.
// 14. The server determines that the Client B has a reservation for the
// address which is in use by Client A. The server drops the client's
// DHCPDISCOVER message.
// 15. Client A renews the lease.
// 16. The server determines that the address that Client A is using is reserved
// for Client B. The server returns DHCPNAK to the Client A.
// 17. Client B uses 4-way echange to obtain the reserved lease but the lease
// for the Client A hasn't been removed yet. Client B's DHCPDISCOVER
// message is dropped again.
// 18. Client A uses 4-way exchange to allocate a new lease.
// 19. The server allocates a new lease from the dynamic pool but it avoids
// allocating the address reserved for the Client B.
// 20. Client B uses 4-way exchange to obtain a new lease.
// 21. The server finally allocates a reserved address to the Client B.
TEST_F(DORATest, reservationsWithConflicts) {
Dhcp4Client client(Dhcp4Client::SELECTING);
// Configure DHCP server.
configure(DORA_CONFIGS[0], *client.getServer());
// Client A performs 4-way exchange and obrains a lease from the
// dynamic pool.
ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<
IOAddress>(new IOAddress("10.0.0.50"))));
// 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()));
// Make sure that the client has got the lease with the requested address.
ASSERT_EQ("10.0.0.50", client.config_.lease_.addr_.toText());
configure(DORA_CONFIGS[0], false);
// Reservation is created for the client A using an address out of the
// dynamic pool.
HostPtr host(new Host(&client.getHWAddress()->hwaddr_[0],
client.getHWAddress()->hwaddr_.size(),
Host::IDENT_HWADDR, SubnetID(1),
SubnetID(0), IOAddress("10.0.0.9")));
CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
CfgMgr::instance().commit();
// Let's transition the client to Renewing state.
client.setState(Dhcp4Client::RENEWING);
// Set the unicast destination address to indicate that it is a renewal.
client.setDestAddress(IOAddress("10.0.0.1"));
ASSERT_NO_THROW(client.doRequest());
// Client should get the DHCPNAK from the server because the client has
// a reservation for a different address that it is trying to renew.
resp = client.getContext().response_;
ASSERT_EQ(DHCPNAK, static_cast<int>(resp->getType()));
// A conforming client would go back to the server discovery.
client.setState(Dhcp4Client::SELECTING);
// Obtain a lease from the server using the 4-way exchange.
ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<
IOAddress>(new IOAddress("0.0.0.0"))));
// Make sure that the server responded.
ASSERT_TRUE(client.getContext().response_);
resp = client.getContext().response_;
// Make sure that the server has responded with DHCPACK with a reserved
// address
ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
ASSERT_EQ("10.0.0.9", client.config_.lease_.addr_.toText());
// Client A renews the allocated address.
client.setState(Dhcp4Client::RENEWING);
// Set the unicast destination address to indicate that it is a renewal.
client.setDestAddress(IOAddress("10.0.0.1"));
ASSERT_NO_THROW(client.doRequest());
// Make sure the server responded and renewed the client's address.
resp = client.getContext().response_;
ASSERT_EQ("10.0.0.9", client.config_.lease_.addr_.toText());
// By reconfiguring the server, we remove the existing reservations.
configure(DORA_CONFIGS[0]);
// Try to renew the existing lease again.
ASSERT_NO_THROW(client.doRequest());
// The reservation has been removed, so the server should respond with
// a DHCPNAK because the address that the client is using doesn't belong
// to a dynamic pool.
resp = client.getContext().response_;
ASSERT_EQ(DHCPNAK, static_cast<int>(resp->getType()));
// A conforming client would go back to the server discovery.
client.setState(Dhcp4Client::SELECTING);
// Obtain a lease from the server using the 4-way exchange.
ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<
IOAddress>(new IOAddress("0.0.0.0"))));
// Make sure that the server responded.
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()));
// Obtain the subnet to which the returned address belongs.
Subnet4Ptr subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->
selectSubnet(client.config_.lease_.addr_);
ASSERT_TRUE(subnet);
// Make sure that the address has been allocated from the dynamic pool.
ASSERT_TRUE(subnet->inPool(Lease::TYPE_V4, client.config_.lease_.addr_));
// Remember the address allocated in the dynamic pool.
IOAddress in_pool_addr = client.config_.lease_.addr_;
// Create Client B.
Dhcp4Client clientB(client.getServer());
clientB.modifyHWAddr();
// Create reservation for the Client B, for the address that the
// Client A is using.
configure(DORA_CONFIGS[0], false);
host.reset(new Host(&clientB.getHWAddress()->hwaddr_[0],
clientB.getHWAddress()->hwaddr_.size(),
Host::IDENT_HWADDR, SubnetID(1),
SubnetID(0), in_pool_addr));
CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
CfgMgr::instance().commit();
// Client B performs a DHCPDISCOVER.
clientB.setState(Dhcp4Client::SELECTING);
// The server determines that the address reserved for Client B is
// in use by Client A so it drops the message from the Client B.
ASSERT_NO_THROW(clientB.doDiscover(boost::shared_ptr<
IOAddress>(new IOAddress("0.0.0.0"))));
ASSERT_FALSE(clientB.getContext().response_);
// Client A renews the lease.
client.setState(Dhcp4Client::RENEWING);
// Set the unicast destination address to indicate that it is a renewal.
client.setDestAddress(IOAddress(in_pool_addr));
ASSERT_NO_THROW(client.doRequest());
// Client A should get a DHCPNAK because it is using an address reserved
// for Client B.
resp = client.getContext().response_;
ASSERT_EQ(DHCPNAK, static_cast<int>(resp->getType()));
// Client B performs 4-way exchange but still doesn't get an address
// because Client A hasn't obtained a new lease, so it is still using
// an address reserved for Client B.
clientB.setState(Dhcp4Client::SELECTING);
// Obtain a lease from the server using the 4-way exchange.
ASSERT_NO_THROW(clientB.doDiscover(boost::shared_ptr<
IOAddress>(new IOAddress("0.0.0.0"))));
// Make sure that the server responded.
ASSERT_FALSE(clientB.getContext().response_);
// Client A performs 4-way exchange.
client.setState(Dhcp4Client::SELECTING);
// Obtain a lease from the server using the 4-way exchange.
ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<
IOAddress>(new IOAddress("0.0.0.0"))));
// Make sure that the server responded.
ASSERT_TRUE(client.getContext().response_);
resp = client.getContext().response_;
ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
// The server should have assigned a different address than the one
// reserved for the Client B.
ASSERT_NE(client.config_.lease_.addr_.toText(), in_pool_addr.toText());
subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->