Commit 95b09ff5 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[master] Merge branch 'trac3694'

Conflicts:
	src/lib/dhcpsrv/alloc_engine.cc
	src/lib/dhcpsrv/alloc_engine.h
	src/lib/dhcpsrv/tests/alloc_engine4_unittest.cc
parents 86f0a1c7 76a78c53
......@@ -2046,70 +2046,73 @@ temporarily override a list of interface names and listen on all interfaces.
<section id="reservation4-conflict">
<title>Conflicts in DHCPv4 reservations</title>
<para>As reservations and lease information are stored separately,
conflicts may arrise. Consider the following series of events. The server
has configured dynamic pool of addresses from the range of 192.0.2.10 to
192.0.2.20. Host A requests an address and gets 19.0.2.10. Now the system
administrator decides to reserve an address for host B. He decides to
<para>As the reservations and lease information are stored separately,
conflicts may arise. Consider the following series of events. The server
has configured the dynamic pool of addresses from the range of 192.0.2.10 to
192.0.2.20. The Host A requests an address and gets 19.0.2.10. Now the system
administrator decides to reserve the address for the Host B. He decides to
reserve 192.0.2.10 for that purpose. In general, reserving an address that
is currently assigned to someone else is not recommended, but there are
valid use cases where such an operation is warranted.</para>
<para>The server now has a conflict to resolve. Let's analyze the
situation here. If the host B boots up and requests an address, the server is
not able to assign the reserved address 192.0.2.10. A naive approach would
to be immediately remove the lease for host A and create a new one for
host B. That would not solve the problem, though, because as soon as host
B get the address, it will detect that the address is already in use by
someone else (host A) and would send Decline. Therefore in this situation,
the server has to temporarily assign a different address (not matching
what has been reserved) to host B.</para>
situation here. If the Host B boots up and requests an address, the server is
not able to assign the reserved address 192.0.2.10 for the Host B. A naive
approach would to be immediately remove the existing lease for the Host A
and create a new one for the Host B. That would not solve the problem,
though, because as soon as the Host B gets the address, it will detect
that the address is already in use by the Host A and would send
the DHCPDECLINE message. Therefore, in this situation, the server has
to temporarily assign a different address (not matching what has been
reserved) to the Host B.</para>
<!-- let's keep this text around. It describes how that is working in v6
<para>When the host A renews its address, the server will discover that
<para>When the Host A renews its address, the server will discover that
the address being renewed is now reserved for someone else (host
B). Therefore the server will remove the lease and will inform the host A
that it is no longer allowed to use it by sending NAK message. Host A
B). Therefore the server will remove the lease and will inform the Host A
that it is no longer allowed to use it by sending DHCPNAK message. Host A
will then revert to server discovery and will eventually get a different
address. The address 192.0.2.10 is now no longer used. When host B tries
to renew its temporary address, the server will detect that it has a valid
lease, but there is a reservation for a different address. The server will
send NAK to inform host B that its address is no longer usable. The
server will also remove its temporary lease. It will revert to the server
discovery phase and will eventually send a REQUEST message. This time the
server will find out that there is a reservation for that host and the
reserved address 192.0.2.10 is not used, so it will be granted.</para> -->
<para>When the host A renews its address, the server will discover that
the address being renewed is now reserved for someone else (host
B). Therefore the server will inform the host A that it is no longer
allowed to use it by sending NAK message. The server will remove the
lease, though, as there's small chance that the NAK may be lost if the
to renew its temporarily assigned address, the server will detect that
it has a valid lease, but there is a reservation for a different address.
The server will send DHCPNAK to inform host B that its address is no
longer usable. The server will also remove its temporary lease. It will
revert to the server discovery phase and will eventually send a
DHCPREQUEST message. This time the server will find out that there is a
reservation for that host and the reserved address 192.0.2.10 is not used,
so it will be granted.</para> -->
<para>When the Host A renews its address, the server will discover that
the address being renewed is now reserved for another host - the Host
B. Therefore the server will inform the Host A that it is no longer
allowed to use it by sending DHCPNAK message. The server will not remove the
lease, though, as there's small chance that the DHCPNAK may be lost if the
network is lossy. If that happens, the client will not receive any
responses, so it will retransmit its Request packet. Once the NAK is
received by the host A, it will then revert to the server discovery and
will eventually get a different address. Besides allocating a new lease,
the server will also remove the old one. As a result the address
192.0.2.10 will be no longer used. When host B tries to renew its
temporary address, the server will detect that it has a valid lease, but
there is a reservation for a different address. The server will send NAK
to inform host B that its address is no longer usable, but will keep its
least (again, the NAK may be lost, so the server will keep it, until the
client gets back for a new address). The host B will revert to the server
discovery phase and will eventually send a REQUEST message. This time the
server will find out that there is a reservation for that host and the
reserved address 192.0.2.10 is not used, so it will be granted. It will
also remove the lease for the temporary address that the host B previously
had.</para>
responses, so it will retransmit its DHCPREQUEST packet. Once the
DHCPNAK is received by the Host A, it will then revert to the server
discovery and will eventually get a different address. Besides
allocating a new lease, the server will also remove the old one. As
a result, the address 192.0.2.10 will be no longer used. When Host B
tries to renew its temporarily assigned address, the server will detect
that it has a valid lease, but there is a reservation for a different
address. The server will send DHCPNAK to inform Host B that its address
is no longer usable, but will keep its lease (again, the DHCPNAK may be
lost, so the server will keep it, until the client returns for a new
address). The Host B will revert to the server discovery phase and will
eventually send a DHCPREQUEST message. This time the server will find
out that there is a reservation for that host and the reserved address
192.0.2.10 is not used, so it will be granted. It will also remove the
lease for the temporarily assigned address that the Host B previously
obtained.</para>
<para>This recovery will succeed, even if other hosts will attempt to get
the reserved address. Had the host C requested address 192.0.2.10 after
the reservation was made, the server will either propose a different
address (when responding to DISCOVER) or would send NAK (when responding
to REQUEST).</para>
the reserved address. Had the Host C requested address 192.0.2.10 after
the reservation was made, the server will either offer a different
address (when responding to DHCPDISCOVER) or would send DHCPNAK
(when responding to DHCPREQUEST).</para>
<para>This recovery mechanism allows the server to fully recover from a
case where reservations conflict with existing leases. This procedure
case where reservations conflict with the existing leases. This procedure
takes time and will roughly take as long as renew-timer value specified.
The best way to avoid such recovery is to not define new reservations that
conflict with existing leases. Another recommendation is to use
......
// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2014-2015 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
......@@ -49,6 +49,7 @@ Dhcp4Client::Dhcp4Client(const Dhcp4Client::State& state) :
curr_transid_(0),
dest_addr_("255.255.255.255"),
hwaddr_(generateHWAddr()),
iface_name_("eth0"),
relay_addr_("192.0.2.2"),
requested_options_(),
server_facing_relay_addr_("10.0.0.2"),
......@@ -64,6 +65,7 @@ Dhcp4Client::Dhcp4Client(boost::shared_ptr<NakedDhcpv4Srv> srv,
curr_transid_(0),
dest_addr_("255.255.255.255"),
hwaddr_(generateHWAddr()),
iface_name_("eth0"),
relay_addr_("192.0.2.2"),
requested_options_(),
server_facing_relay_addr_("10.0.0.2"),
......@@ -344,7 +346,7 @@ Dhcp4Client::sendMsg(const Pkt4Ptr& msg) {
msg->getBuffer().getLength()));
msg_copy->setRemoteAddr(msg->getLocalAddr());
msg_copy->setLocalAddr(dest_addr_);
msg_copy->setIface("eth0");
msg_copy->setIface(iface_name_);
srv_->fakeReceive(msg_copy);
srv_->run();
}
......
// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2014-2015 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
......@@ -273,6 +273,14 @@ public:
/// @param hwaddr_str String representation of the HW address.
void setHWAddress(const std::string& hwaddr_str);
/// @brief Sets the interface over which the messages should be sent.
///
/// @param iface_name Name of the interface over which the messages should
/// be sent.
void setIfaceName(const std::string& iface_name) {
iface_name_ = iface_name;
}
/// @brief Sets client state.
///
/// Depending on the current state the client's behavior is different
......@@ -371,6 +379,9 @@ private:
/// @brief Current hardware address of the client.
HWAddrPtr hwaddr_;
/// @brief Interface to be used to send the messages.
std::string iface_name_;
/// @brief Relay address to use.
asiolink::IOAddress relay_addr_;
......
......@@ -845,6 +845,7 @@ TEST_F(Dhcpv4SrvTest, RenewBasic) {
req->setYiaddr(addr);
req->setCiaddr(addr); // client's address
req->setIface("eth0");
req->setHWAddr(hwaddr2);
req->addOption(clientid);
req->addOption(srv->getServerID());
......@@ -2782,6 +2783,7 @@ TEST_F(HooksDhcpv4SrvTest, lease4RenewSimple) {
req->setYiaddr(addr);
req->setCiaddr(addr); // client's address
req->setIface("eth0");
req->setHWAddr(hwaddr2);
req->addOption(clientid);
req->addOption(srv_->getServerID());
......@@ -2877,6 +2879,7 @@ TEST_F(HooksDhcpv4SrvTest, lease4RenewSkip) {
req->setYiaddr(addr);
req->setCiaddr(addr); // client's address
req->setIface("eth0");
req->setHWAddr(hwaddr2);
req->addOption(clientid);
req->addOption(srv_->getServerID());
......
......@@ -21,6 +21,7 @@
#include <dhcpsrv/lease_mgr_factory.h>
#include <dhcpsrv/subnet.h>
#include <dhcp4/json_config_parser.h>
#include <dhcp4/tests/dhcp4_client.h>
#include <dhcp4/tests/dhcp4_test_utils.h>
#include <gtest/gtest.h>
#include <string>
......@@ -306,51 +307,23 @@ TEST_F(DirectClientTest, renew) {
ASSERT_NO_THROW(IfaceMgr::instance().openSockets4());
// Add a subnet.
ASSERT_NO_FATAL_FAILURE(configureSubnet("10.0.0.0"));
// Make sure that the subnet has been really added. Also, the subnet
// will be needed to create a lease for a client.
Subnet4Ptr subnet = CfgMgr::instance().getCurrentCfg()->
getCfgSubnets4()->selectSubnet(IOAddress("10.0.0.10"));
// Create a lease for a client that we will later renewed. By explicitly
// creating a lease we will get to know the lease parameters, such as
// leased address etc.
const uint8_t hwaddr_data[] = { 1, 2, 3, 4, 5, 6 };
HWAddrPtr hwaddr(new HWAddr(hwaddr_data, sizeof(hwaddr_data), HTYPE_ETHER));
Lease4Ptr lease(new Lease4(IOAddress("10.0.0.10"), hwaddr,
&generateClientId()->getData()[0],
generateClientId()->getData().size(),
100, 50, 75, time(NULL),
subnet->getID()));
LeaseMgrFactory::instance().addLease(lease);
// Create a Request to renew client's lease. The renew request is unicast
// through eth1. Note, that in case of renewal the client unicasts its
// Request and sets the ciaddr. The server is supposed to use ciaddr to
// pick the subnet for the client. In order to make sure that the server
// uses ciaddr, we simulate reception of the packet through eth1, for which
// there is no subnet for directly connected clients.
Pkt4Ptr req = Pkt4Ptr(new Pkt4(DHCPREQUEST, 1234));
req->setCiaddr(IOAddress("10.0.0.10"));
req = createClientMessage(req, "eth1");
req->setLocalAddr(IOAddress("10.0.0.1"));
req->setRemoteAddr(req->getCiaddr());
srv_.fakeReceive(req);
// Create the DHCPv4 client.
Dhcp4Client client;
client.useRelay(false);
// Process clients' messages.
srv_.run();
// Obtain the lease using the 4-way exchange.
ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<IOAddress>(new IOAddress("10.0.0.10"))));
ASSERT_EQ("10.0.0.10", client.config_.lease_.addr_.toText());
// Check that the server did send response.
ASSERT_EQ(1, srv_.fake_sent_.size());
Pkt4Ptr response = srv_.fake_sent_.front();
ASSERT_TRUE(response);
ASSERT_EQ(DHCPACK, response->getType());
// Check that the offered address belongs to the suitable subnet.
subnet = CfgMgr::instance().getCurrentCfg()->
getCfgSubnets4()->selectSubnet(response->getYiaddr());
ASSERT_TRUE(subnet);
EXPECT_EQ("10.0.0.0", subnet->get().first.toText());
// Put the client into the renewing state.
client.setState(Dhcp4Client::RENEWING);
// Renew, and make sure we have obtained the same address.
ASSERT_NO_THROW(client.doRequest());
ASSERT_TRUE(client.getContext().response_);
EXPECT_EQ(DHCPACK, static_cast<int>(client.getContext().response_->getType()));
EXPECT_EQ("10.0.0.10", client.config_.lease_.addr_.toText());
}
// This test verifies that when a client in the Rebinding state broadcasts
......@@ -366,58 +339,31 @@ TEST_F(DirectClientTest, rebind) {
ASSERT_NO_THROW(IfaceMgr::instance().openSockets4());
// Add a subnet.
ASSERT_NO_FATAL_FAILURE(configureSubnet("10.0.0.0"));
// Make sure that the subnet has been really added. Also, the subnet
// will be needed to create a lease for a client.
Subnet4Ptr subnet = CfgMgr::instance().getCurrentCfg()->
getCfgSubnets4()->selectSubnet(IOAddress("10.0.0.10"));
// Create a lease, which will be later renewed. By explicitly creating a
// lease we will know the lease parameters, such as leased address etc.
const uint8_t hwaddr_data[] = { 1, 2, 3, 4, 5, 6 };
HWAddrPtr hwaddr(new HWAddr(hwaddr_data, sizeof(hwaddr_data), HTYPE_ETHER));
Lease4Ptr lease(new Lease4(IOAddress("10.0.0.10"), hwaddr,
&generateClientId()->getData()[0],
generateClientId()->getData().size(),
100, 50, 75, time(NULL),
subnet->getID()));
LeaseMgrFactory::instance().addLease(lease);
// Broadcast Request through an interface for which there is no subnet
// configured. This message should be discarded by the server.
Pkt4Ptr req = Pkt4Ptr(new Pkt4(DHCPREQUEST, 1234));
req->setCiaddr(IOAddress("10.0.0.10"));
req = createClientMessage(req, "eth1");
req->setRemoteAddr(req->getCiaddr());
srv_.fakeReceive(req);
// Broadcast another Request through an interface for which there is
// a subnet configured. The server should generate a response.
req = Pkt4Ptr(new Pkt4(DHCPREQUEST, 5678));
req->setCiaddr(IOAddress("10.0.0.10"));
req = createClientMessage(req, "eth0");
req->setRemoteAddr(req->getCiaddr());
// Create the DHCPv4 client.
Dhcp4Client client;
client.useRelay(false);
srv_.fakeReceive(req);
// Process clients' messages.
srv_.run();
// Check that the server did send exactly one response.
ASSERT_EQ(1, srv_.fake_sent_.size());
Pkt4Ptr response = srv_.fake_sent_.front();
ASSERT_TRUE(response);
// Obtain the lease using the 4-way exchange.
ASSERT_NO_THROW(client.doDORA(boost::shared_ptr<IOAddress>(new IOAddress("10.0.0.10"))));
ASSERT_EQ("10.0.0.10", client.config_.lease_.addr_.toText());
// Make sure that the server responded with ACK, not NAK.
ASSERT_EQ(DHCPACK, response->getType());
// Make sure that the response is generated for the second Request
// (transmitted over eth0).
EXPECT_EQ(5678, response->getTransid());
// Check that the offered address belongs to the suitable subnet.
subnet = CfgMgr::instance().getCurrentCfg()->
getCfgSubnets4()->selectSubnet(response->getYiaddr());
ASSERT_TRUE(subnet);
EXPECT_EQ("10.0.0.0", subnet->get().first.toText());
// Put the client into the rebinding state.
client.setState(Dhcp4Client::REBINDING);
// Broadcast Request through an interface for which there is no subnet
// configured. This message should be discarded by the server.
client.setIfaceName("eth1");
ASSERT_NO_THROW(client.doRequest());
EXPECT_FALSE(client.getContext().response_);
// Send Rebind over the correct interface, and make sure we have obtained
// the same address.
client.setIfaceName("eth0");
ASSERT_NO_THROW(client.doRequest());
ASSERT_TRUE(client.getContext().response_);
EXPECT_EQ(DHCPACK, static_cast<int>(client.getContext().response_->getType()));
EXPECT_EQ("10.0.0.10", client.config_.lease_.addr_.toText());
}
}
......@@ -508,19 +508,21 @@ TEST_F(DORATest, reservation) {
// 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
// address which is in use by Client A and offers an address different
// than reserved.
// 15. Client B requests the allocation of the offered address and the server
// allocates this address.
// 16. Client A renews the lease.
// 17. 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 exchange 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
// 18. Client B uses 4-way exchange to obtain the reserved lease but the lease
// for the Client A hasn't been removed yet. Client B is assigned the same
// address it has been using.
// 19. Client A uses 4-way exchange to allocate a new lease.
// 20. 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.
// 21. Client B uses 4-way exchange to obtain a new lease.
// 22. The server finally allocates a reserved address to the Client B.
TEST_F(DORATest, reservationsWithConflicts) {
Dhcp4Client client(Dhcp4Client::SELECTING);
// Configure DHCP server.
......@@ -629,10 +631,13 @@ TEST_F(DORATest, reservationsWithConflicts) {
// 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_);
// in use by Client A so it offers a different address.
ASSERT_NO_THROW(clientB.doDORA(boost::shared_ptr<
IOAddress>(new IOAddress("0.0.0.0"))));
ASSERT_TRUE(clientB.getContext().response_);
ASSERT_EQ(DHCPACK, static_cast<int>(clientB.getContext().response_->getType()));
IOAddress client_b_addr = clientB.config_.lease_.addr_;
ASSERT_NE(client_b_addr, in_pool_addr);
// Client A renews the lease.
client.setState(Dhcp4Client::RENEWING);
......@@ -644,15 +649,29 @@ TEST_F(DORATest, reservationsWithConflicts) {
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.
// Client B performs 4-way exchange but still gets an address from the
// dynamic pool, 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"))));
ASSERT_NO_THROW(clientB.doDORA(boost::shared_ptr<
IOAddress>(new IOAddress("0.0.0.0"))));
// Make sure that the server responded.
ASSERT_FALSE(clientB.getContext().response_);
ASSERT_TRUE(clientB.getContext().response_);
ASSERT_EQ(DHCPACK, static_cast<int>(clientB.getContext().response_->getType()));
ASSERT_NE(clientB.config_.lease_.addr_, in_pool_addr);
ASSERT_EQ(client_b_addr, clientB.config_.lease_.addr_);
// Client B renews its lease.
clientB.setState(Dhcp4Client::RENEWING);
clientB.setDestAddress(IOAddress("10.0.0.1"));
ASSERT_NO_THROW(clientB.doRequest());
// The server should renew the client's B lease because the address
// reserved for client B is still in use by the client A.
ASSERT_TRUE(clientB.getContext().response_);
EXPECT_EQ(DHCPACK, static_cast<int>(clientB.getContext().response_->getType()));
ASSERT_NE(clientB.config_.lease_.addr_, in_pool_addr);
ASSERT_EQ(client_b_addr, clientB.config_.lease_.addr_);
// Client A performs 4-way exchange.
client.setState(Dhcp4Client::SELECTING);
......@@ -667,12 +686,20 @@ TEST_F(DORATest, reservationsWithConflicts) {
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());
ASSERT_NE(client.config_.lease_.addr_, in_pool_addr);
subnet = CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->
selectSubnet(client.config_.lease_.addr_);
ASSERT_TRUE(subnet);
ASSERT_TRUE(subnet->inPool(Lease::TYPE_V4, client.config_.lease_.addr_));
// Client B renews again.
ASSERT_NO_THROW(clientB.doRequest());
// The client B should now receive the DHCPNAK from the server because
// the reserved address is now available and the client should
// revert to the DHCPDISCOVER to obtain it.
ASSERT_TRUE(clientB.getContext().response_);
EXPECT_EQ(DHCPNAK, static_cast<int>(clientB.getContext().response_->getType()));
// Client B performs 4-way exchange and obtains a lease for the
// reserved address.
clientB.setState(Dhcp4Client::SELECTING);
......
......@@ -103,6 +103,21 @@ public:
return (asio_address_.is_v4());
}
/// \brief Convenience function to check if it is an IPv4 zero address.
///
/// \return true if the address is the zero IPv4 address.
bool isV4Zero() const {
return (equals(IPV4_ZERO_ADDRESS()));
}
/// \brief Convenience function to check if it is an IPv4 broadcast
/// address.
///
/// \return true if the address is the broadcast IPv4 address.
bool isV4Bcast() const {
return (equals(IPV4_BCAST_ADDRESS()));
}
/// \brief Convenience function to check for an IPv6 address
///
/// \return true if the address is a V6 address
......@@ -110,6 +125,13 @@ public:
return (asio_address_.is_v6());
}
/// \brief Convenience function to check if it is an IPv4 zero address.
///
/// \return true if the address is the zero IPv4 address.
bool isV6Zero() const {
return (equals(IPV6_ZERO_ADDRESS()));
}
/// \brief checks whether and address is IPv6 and is link-local
///
/// \return true if the address is IPv6 link-local, false otherwise
......
......@@ -119,6 +119,40 @@ TEST(IOAddressTest, isV4) {
EXPECT_FALSE(address6.isV4());
}
TEST(IOAddressTest, isV4Zero) {
// 0.0.0.0
const IOAddress address_zero("0.0.0.0");
EXPECT_TRUE(address_zero.isV4Zero());
// :: (v6 zero address)
const IOAddress address_zero_v6("::");
EXPECT_FALSE(address_zero_v6.isV4Zero());
// 192.0.2.3
const IOAddress address_non_zero("192.0.2.3");
EXPECT_FALSE(address_non_zero.isV4Zero());
// 0.0.0.100
const IOAddress address_non_zero1("0.0.0.100");
EXPECT_FALSE(address_non_zero1.isV4Zero());
// 64.0.0.0
const IOAddress address_non_zero2("64.0.0.0");
EXPECT_FALSE(address_non_zero2.isV4Zero());
}
TEST(IOAddressTest, isV4Bcast) {
// 255.255.255.255
const IOAddress address_bcast("255.255.255.255");
EXPECT_TRUE(address_bcast.isV4Bcast());
// 10.2.3.4
const IOAddress address_non_bcast("10.2.3.4");
EXPECT_FALSE(address_non_bcast.isV4Bcast());
// 255.255.255.23
const IOAddress address_non_bcast1("255.255.255.23");
EXPECT_FALSE(address_non_bcast1.isV4Bcast());
// 123.255.255.255
const IOAddress address_non_bcast2("123.255.255.255");
EXPECT_FALSE(address_non_bcast2.isV4Bcast());
}
TEST(IOAddressTest, isV6) {
const IOAddress address4("192.0.2.1");
const IOAddress address6("2001:db8:1::dead:beef");
......@@ -127,6 +161,21 @@ TEST(IOAddressTest, isV6) {
EXPECT_TRUE(address6.isV6());
}
TEST(IOAddressTest, isV6Zero) {
// ::
const IOAddress address_zero("::");
EXPECT_TRUE(address_zero.isV6Zero());
// 0.0.0.0
const IOAddress address_non_zero("0.0.0.0");
EXPECT_FALSE(address_non_zero.isV6Zero());
// ::ff
const IOAddress address_non_zero1("::ff");
EXPECT_FALSE(address_non_zero1.isV6Zero());
// ff::
const IOAddress address_non_zero2("ff::");
EXPECT_FALSE(address_non_zero2.isV6Zero());
}
TEST(IOAddressTest, uint32) {
IOAddress addr1("192.0.2.5");
......
This diff is collapsed.
This diff is collapsed.
......@@ -179,6 +179,12 @@ the database access parameters are changed: in the latter case, the
server closes the currently open database, and opens a database using
the new parameters.
% DHCPSRV_DISCOVER_ADDRESS_CONFLICT conflicting reservation for address %1 with existing lease %2
This warning message is issued when the DHCP server finds that the
address reserved for the client can't be offered because this address
is currently allocated to another client. The server will try to allocate
a different address to the client to use until the conflict is resolved.
% DHCPSRV_DHCP_DDNS_ERROR_EXCEPTION error handler for DHCP_DDNS IO generated an expected exception: %1
This is an error message that occurs when an attempt to send a request to
kea-dhcp-ddns fails there registered error handler threw an uncaught exception.
......
......@@ -198,7 +198,7 @@ public:
/// and a subnet
///
/// There can be at most one lease for a given HW address in a single
/// pool, so this method with either return a single lease or NULL.
/// pool, so this method will either return a single lease or NULL.
///
/// @param hwaddr hardware address of the client
/// @param subnet_id identifier of the subnet that lease must belong to
......@@ -235,8 +235,8 @@ public:
/// @brief Returns existing IPv4 lease for specified client-id
///
/// There can be at most one lease for a given HW address in a single
/// pool, so this method with either return a single lease or NULL.
/// There can be at most one lease for a given client-id in a single
/// pool, so this method will either return a single lease or NULL.
///
/// @param clientid client identifier
/// @param subnet_id identifier of the subnet that lease must belong to
......
......@@ -88,7 +88,7 @@ typedef boost::multi_index_container<