"},\n"
"\"valid-lifetime\": 600,\n"
"\"ip-reservations-unique\": false,\n"
+ "\"host-reservation-identifiers\": [ \"hw-address\" ],\n"
"\"subnet4\": [\n"
" {\n"
" \"subnet\": \"10.0.0.0/24\",\n"
ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
EXPECT_EQ(second_address, resp->getYiaddr().toText());
}
+
+ /// @brief Test that two clients having reservations for the same IP
+ /// address are offered the reserved lease.
+ ///
+ /// This test verifies the case when two clients have reservations for
+ /// the same IP address. The first client sends DHCPDICOVER and is
+ /// offered the reserved address. At the same time, the second client
+ /// having the reservation for the same IP address performs 4-way
+ /// exchange using the reserved address as a hint in DHCPDISCOVER.
+ /// The client gets the lease for this address. This test verifies
+ /// that the allocation engine correctly identifies that the second
+ /// client has a reservation for this address. In order to verify
+ /// that the allocation engine must fetch all reservations for the
+ /// reserved address and verifies that one of them belongs to the
+ /// second client.
+ ///
+ /// @param hw_address1 Hardware address of the first client having
+ /// the reservation.
+ /// @param hw_address2 Hardware address of the second client having
+ /// the reservation.
+ void testMultipleClientsRace(const std::string& hw_address1,
+ const std::string& hw_address2) {
+ // Create first client having the reservation.
+ Dhcp4Client client1(Dhcp4Client::SELECTING);
+ client1.setHWAddress(hw_address1);
+
+ // Configure the server.
+ ASSERT_NO_FATAL_FAILURE(configure(CONFIGS[7], *client1.getServer()));
+
+ // Sends DHCPDISCOVER and make sure the client is offered the
+ // reserved IP address.
+ client1.doDiscover(boost::make_shared<IOAddress>("10.0.0.123"));
+ ASSERT_TRUE(client1.getContext().response_);
+ Pkt4Ptr resp = client1.getContext().response_;
+ ASSERT_EQ(DHCPOFFER, static_cast<int>(resp->getType()));
+ EXPECT_EQ("10.0.0.123", resp->getYiaddr().toText());
+
+ // Create the second client matching the second reservation for
+ // the given IP address.
+ Dhcp4Client client2(client1.getServer(), Dhcp4Client::SELECTING);
+ client2.setHWAddress(hw_address2);
+
+ // Make sure that the second client gets the reserved lease.
+ client2.doDORA(boost::make_shared<IOAddress>("10.0.0.123"));
+ ASSERT_TRUE(client2.getContext().response_);
+ resp = client2.getContext().response_;
+ ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
+ EXPECT_EQ("10.0.0.123", resp->getYiaddr().toText());
+ }
};
// Verifies that a client, which fails to match to a global
// Verifies that if the server is configured to allow for specifying
// multiple reservations for the same IP address the first client
-// matching the reservation will be given this address.
-TEST_F(HostTest, oneOfMultiple) {
- Dhcp4Client client(Dhcp4Client::SELECTING);
+// matching the reservation will be given this address. The second
+// client will be given a different lease.
+TEST_F(HostTest, firstClientGetsReservedAddress) {
+ // Create a client which has MAC address matching the reservation.
+ Dhcp4Client client1(Dhcp4Client::SELECTING);
+ client1.setHWAddress("aa:bb:cc:dd:ee:fe");
+ // Do 4-way exchange for this client to get the reserved address.
+ runDoraTest(CONFIGS[7], client1, "", "10.0.0.123");
+
+ // Create another client that has a reservation for the same
+ // IP address.
+ Dhcp4Client client2(client1.getServer(), Dhcp4Client::SELECTING);
+ client2.setHWAddress("aa:bb:cc:dd:ee:ff");
+ // Do 4-way exchange with client2.
+ ASSERT_NO_THROW(client2.doDORA());
+
+ // Make sure that the server responded with DHCPACK.
+ ASSERT_TRUE(client2.getContext().response_);
+ Pkt4Ptr resp = client2.getContext().response_;
+ ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
+
+ // Even though the client has reservation for this address the
+ // server should not assign this address because another client
+ // has taken it already.
+ EXPECT_NE("10.0.0.123", resp->getYiaddr().toText());
+
+ // If the client1 releases the reserved lease, the client2 should acquire it.
+ ASSERT_NO_THROW(client1.doRelease());
+
+ // Client2 attempts to renew the currently used lease, but should get the
+ // DHCPNAK.
+ client2.setState(Dhcp4Client::RENEWING);
+ ASSERT_NO_THROW(client2.doRequest());
+ resp = client2.getContext().response_;
+ ASSERT_TRUE(resp);
+ ASSERT_EQ(DHCPNAK, static_cast<int>(resp->getType()));
+
+ // The client falls back to 4-way exchange and gets the reserved address.
+ client2.setState(Dhcp4Client::SELECTING);
+ ASSERT_NO_THROW(client2.doDORA());
+ resp = client2.getContext().response_;
+ ASSERT_TRUE(resp);
+ ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
+ EXPECT_EQ("10.0.0.123", resp->getYiaddr().toText());
+}
- // Hardware address matches all reservations
- client.setHWAddress("aa:bb:cc:dd:ee:fe");
+// This test verifies the case when two clients have reservations for
+// the same IP address. The first client sends DHCPDICOVER and is
+// offered the reserved address. At the same time, the second client
+// having the reservation for the same IP address performs 4-way
+// exchange using the reserved address as a hint in DHCPDISCOVER.
+// The client gets the lease for this address. This test verifies
+// that the allocation engine correctly identifies that the second
+// client has a reservation for this address. In order to verify
+// that the allocation engine must fetch all reservations for the
+// reserved address and verifies that one of them belongs to the
+// second client.
+TEST_F(HostTest, multipleClientsRace1) {
+ ASSERT_NO_FATAL_FAILURE(testMultipleClientsRace("aa:bb:cc:dd:ee:fe",
+ "aa:bb:cc:dd:ee:ff"));
+}
- runDoraTest(CONFIGS[7], client, "", "10.0.0.123");
+// This is a second variant of the multipleClientsRace1. The test is almost
+// the same but the client matching the second reservation sends DHCPDISCOVER
+// first and then the client having the first reservation performs 4-way
+// exchange. This is to ensure that the order in which reservations are
+// defined does not matter.
+TEST_F(HostTest, multipleClientsRace2) {
+ ASSERT_NO_FATAL_FAILURE(testMultipleClientsRace("aa:bb:cc:dd:ee:ff",
+ "aa:bb:cc:dd:ee:fe"));
}
} // end of anonymous namespace
" \"interface\": \"eth0\"\n"
" }\n"
"]\n"
+ "}",
+
+ // Configuration 14 multiple reservations for the same delegated prefix.
+ "{ \"interfaces-config\": {\n"
+ " \"interfaces\": [ \"*\" ]\n"
+ "},\n"
+ "\"valid-lifetime\": 4000,\n"
+ "\"ip-reservations-unique\": false,\n"
+ "\"subnet6\": [\n"
+ " {\n"
+ " \"subnet\": \"2001:db8:1::/64\",\n"
+ " \"id\": 10,"
+ " \"reservations\": [\n"
+ " {\n"
+ " \"duid\": \"01:02:03:04\",\n"
+ " \"prefixes\": [ \"3000::5a:0/112\" ]\n"
+ " },\n"
+ " {\n"
+ " \"duid\": \"01:02:03:05\",\n"
+ " \"prefixes\": [ \"3000::5a:0/112\" ]\n"
+ " }\n"
+ " ],\n"
+ " \"pd-pools\": ["
+ " {\n"
+ " \"prefix\": \"3000::\",\n"
+ " \"prefix-len\": 64,\n"
+ " \"delegated-len\": 112\n"
+ " }\n"
+ " ],\n"
+ " \"interface\": \"eth0\"\n"
+ " }\n"
+ "]\n"
"}"
};
const std::string& first_address = "2001:db8:1::10",
const std::string& second_address = "2001:db8:2::10");
+ /// @brief Test that two clients having reservations for the same IP
+ /// address are offered the reserved lease.
+ ///
+ /// This test verifies the case when two clients have reservations for
+ /// the same IP address. The first client sends Solicit and is offered
+ /// the reserved address. At the same time, the second client having
+ /// the reservation for the same IP address performs 4-way exchange
+ /// using the reserved address as a hint in Solicit.
+ /// The client gets the lease for this address. This test verifies
+ /// that the allocation engine correctly identifies that the second
+ /// client has a reservation for this address.
+ ///
+ /// @param duid1 Hardware address of the first client having the
+ /// reservation.
+ /// @param duid2 Hardware address of the second client having the
+ /// reservation.
+ void testMultipleClientsRace(const std::string& duid1,
+ const std::string& duid2);
+
/// @brief Configures client to include 6 IAs without hints.
///
/// This method configures the client to include 3 IA_NAs and
EXPECT_EQ(second_address, lease_client.addr_.toText());
}
+void
+HostTest::testMultipleClientsRace(const std::string& duid1,
+ const std::string& duid2) {
+ Dhcp6Client client1;
+ client1.setDUID(duid1);
+ ASSERT_NO_THROW(configure(CONFIGS[13], *client1.getServer()));
+ // client1 performs 4-way exchange to get the reserved lease.
+ requestIA(client1, Hint(IAID(1), "2001:db8:1::15"));
+ ASSERT_NO_THROW(client1.doSARR());
+
+ // Make sure the client has obtained reserved lease.
+ ASSERT_TRUE(client1.hasLeaseForAddress(IOAddress("2001:db8:1::15"), IAID(1)));
+
+ // Create another client that has a reservation for the same
+ // IP address.
+ Dhcp6Client client2(client1.getServer());
+ client2.setDUID(duid2);
+ requestIA(client2, Hint(IAID(1), "2001:db8:1::15"));
+
+ // client2 performs 4-way exchange.
+ ASSERT_NO_THROW(client2.doSARR());
+
+ // Make sure the client didn't get the reserved lease. This lease has been
+ // already taken by the client1.
+ EXPECT_FALSE(client2.hasLeaseForAddress(IOAddress("2001:db8:1::15"), IAID(1)));
+
+ // Make sure the client2 got a lease from the configured pool.
+ EXPECT_TRUE(client2.hasLeaseForAddressRange(IOAddress("2001:db8:1::10"),
+ IOAddress("2001:db8:1::200")));
+}
+
void
HostTest::requestEmptyIAs(Dhcp6Client& client) {
// Create IAs with IAIDs between 1 and 6.
// Verifies that if the server is configured to allow for specifying
// multiple reservations for the same IP address the first client
-// matching the reservation will be given this address.
-TEST_F(HostTest, oneOfMultiple) {
+// matching the reservation will be given this address. The second
+// client will be given a different lease.
+TEST_F(HostTest, firstClientGetsReservedAddress) {
+ // Create a client which has DUID matching the reservation.
Dhcp6Client client1;
client1.setDUID("01:02:03:04");
-
ASSERT_NO_THROW(configure(CONFIGS[13], *client1.getServer()));
-
- // First client performs 4-way exchange and obtains an address and
- // prefix indicated in hints.
+ // client1 performs 4-way exchange to get the reserved lease.
requestIA(client1, Hint(IAID(1), "2001:db8:1::10"));
-
ASSERT_NO_THROW(client1.doSARR());
- // Make sure the client has obtained requested leases.
+ // Make sure the client has obtained reserved lease.
ASSERT_TRUE(client1.hasLeaseForAddress(IOAddress("2001:db8:1::15"), IAID(1)));
+
+ // Create another client that has a reservation for the same
+ // IP address.
+ Dhcp6Client client2(client1.getServer());
+ client2.setDUID("01:02:03:05");
+ requestIA(client2, Hint(IAID(1), "2001:db8:1::10"));
+
+ // client2 performs 4-way exchange.
+ ASSERT_NO_THROW(client2.doSARR());
+
+ // Make sure the client didn't get the reserved lease. This lease has been
+ // already taken by the client1.
+ EXPECT_FALSE(client2.hasLeaseForAddress(IOAddress("2001:db8:1::15"), IAID(1)));
+
+ // Make sure the client2 got a lease from the configured pool.
+ auto leases = client2.getLeasesByAddressRange(IOAddress("2001:db8:1::10"),
+ IOAddress("2001:db8:1::200"));
+ EXPECT_EQ(1, leases.size());
+
+ // Verify that the client1 can renew the lease.
+ ASSERT_NO_THROW(client1.doRenew());
+ EXPECT_TRUE(client1.hasLeaseForAddress(IOAddress("2001:db8:1::15"), IAID(1)));
+
+ // The client2 should also renew the lease.
+ ASSERT_NO_THROW(client2.doRenew());
+ EXPECT_FALSE(client2.hasLeaseForAddress(IOAddress("2001:db8:1::15"), IAID(1)));
+ leases = client2.getLeasesByAddressRange(IOAddress("2001:db8:1::10"),
+ IOAddress("2001:db8:1::200"));
+ EXPECT_EQ(1, leases.size());
+
+ // If the client1 releases the reserved lease, the client2 should acquire it.
+ ASSERT_NO_THROW(client1.doRelease());
+ ASSERT_NO_THROW(client2.doRenew());
+ EXPECT_TRUE(client2.hasLeaseForAddress(IOAddress("2001:db8:1::15"), IAID(1)));
+}
+
+// Verifies that if the server is configured to allow for specifying
+// multiple reservations for the same delegated prefix the first client
+// matching the reservation will be given this prefix. The second
+// client will be given a different lease.
+TEST_F(HostTest, firstClientGetsReservedPrefix) {
+ // Create a client which has DUID matching the reservation.
+ Dhcp6Client client1;
+ client1.setDUID("01:02:03:04");
+ ASSERT_NO_THROW(configure(CONFIGS[14], *client1.getServer()));
+ // client1 performs 4-way exchange to get the reserved lease.
+ client1.requestPrefix(1);
+ ASSERT_NO_THROW(client1.doSARR());
+
+ // Make sure the client has obtained reserved lease.
+ ASSERT_TRUE(client1.hasLeaseForPrefix(IOAddress("3000::5a:0"), 112, IAID(1)));
+
+ // Create another client that has a reservation for the same
+ // IP address.
+ Dhcp6Client client2(client1.getServer());
+ client2.setDUID("01:02:03:05");
+ client2.requestPrefix(1);
+
+ // client2 performs 4-way exchange.
+ ASSERT_NO_THROW(client2.doSARR());
+
+ // Make sure the client didn't get the reserved lease. This lease has been
+ // already taken by the client1.
+ EXPECT_FALSE(client2.hasLeaseForPrefix(IOAddress("3000::5a:0"), 112, IAID(1)));
+
+ // Make sure the client2 got a lease from the configured pool.
+ EXPECT_TRUE(client2.hasLeaseForPrefixPool(IOAddress("3000::"), 64, 112));
+
+ // Verify that the client1 can renew the lease.
+ ASSERT_NO_THROW(client1.doRenew());
+ EXPECT_TRUE(client1.hasLeaseForPrefix(IOAddress("3000::5a:0"), 112, IAID(1)));
+
+ // The client2 should also renew the lease.
+ ASSERT_NO_THROW(client2.doRenew());
+ EXPECT_TRUE(client2.hasLeaseForPrefixPool(IOAddress("3000::"), 64, 112));
+
+ // If the client1 releases the reserved lease, the client2 should acquire it.
+ ASSERT_NO_THROW(client1.doRelease());
+ ASSERT_NO_THROW(client2.doRenew());
+ EXPECT_TRUE(client2.hasLeaseForPrefix(IOAddress("3000::5a:0"), 112, IAID(1)));
+}
+
+/// This test verifies the case when two clients have reservations for
+/// the same IP address. The first client sends Solicit and is offered
+/// the reserved address. At the same time, the second client having
+/// the reservation for the same IP address performs 4-way exchange
+/// using the reserved address as a hint in Solicit.
+/// The client gets the lease for this address. This test verifies
+/// that the allocation engine correctly identifies that the second
+/// client has a reservation for this address.
+TEST_F(HostTest, multipleClientsRace1) {
+ ASSERT_NO_FATAL_FAILURE(testMultipleClientsRace("01:02:03:04", "01:02:03:05"));
+}
+
+// This is a second variant of the multipleClientsRace1. The test is almost
+// the same but the client matching the second reservation sends Solicit
+// first and then the client having the first reservation performs 4-way
+// exchange. This is to ensure that the order in which reservations are
+// defined does not matter.
+TEST_F(HostTest, multipleClientsRace2) {
+ ASSERT_NO_FATAL_FAILURE(testMultipleClientsRace("01:02:03:05", "01:02:03:04"));
}
} // end of anonymous namespace
namespace {
+/// @brief Find reservations for the given subnet and address or delegated prefix.
+///
+/// @param subnet_id Subnet identifier for which the reservations should
+/// be found.
+/// @param address Address or delegated prefix for which the reservations
+/// should be found.
+///
+/// @return Collection of host reservations.
+ConstHostCollection
+getIPv6Resrv(const SubnetID& subnet_id, const IOAddress& address) {
+ ConstHostCollection reserved;
+ // The global parameter ip-reservations-unique controls whether it is allowed
+ // to specify multiple reservations for the same IP address or delegated prefix
+ // or IP reservations must be unique. Some host backends do not support the
+ // former, thus we can't always use getAll6 calls to get the reservations
+ // for the given IP. When we're in the default mode, when IP reservations
+ // are unique, we should call get6 (supported by all backends). If we're in
+ // the mode in which non-unique reservations are allowed the backends which
+ // don't support it are not used and we can safely call getAll6.
+ if (CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->getIPReservationsUnique()) {
+ auto host = HostMgr::instance().get6(subnet_id, address);
+ if (host) {
+ reserved.push_back(host);
+ }
+ } else {
+ auto hosts = HostMgr::instance().getAll6(subnet_id, address);
+ reserved.insert(reserved.end(), hosts.begin(), hosts.end());
+ }
+ return (reserved);
+}
+
/// @brief Checks if the specified address belongs to one of the subnets
/// within a shared network.
///
// else. There is no need to check for whom it is reserved, because if
// it has been reserved for us we would have already allocated a lease.
- ConstHostPtr host;
+ ConstHostCollection hosts;
if (hr_mode != Network::HR_DISABLED) {
- host = HostMgr::instance().get6(subnet->getID(), hint);
+ hosts = getIPv6Resrv(subnet->getID(), hint);
}
- if (!host) {
+ if (hosts.empty()) {
// If the in-pool reservations are disabled, or there is no
// reservation for a given hint, we're good to go.
} else if (lease->expired()) {
// If the lease is expired, we may likely reuse it, but...
- ConstHostPtr host;
+ ConstHostCollection hosts;
if (hr_mode != Network::HR_DISABLED) {
- host = HostMgr::instance().get6(subnet->getID(), hint);
+ hosts = getIPv6Resrv(subnet->getID(), hint);
}
// Let's check if there is a reservation for this address.
- if (!host) {
+ if (hosts.empty()) {
// Copy an existing, expired lease so as it can be returned
// to the caller.
}
// First check for reservation when it is the choice.
- if (check_reservation_first && (hr_mode == Network::HR_ALL) &&
- HostMgr::instance().get6(subnet->getID(), candidate)) {
- // Don't allocate.
- continue;
+ if (check_reservation_first && (hr_mode == Network::HR_ALL)) {
+ auto hosts = getIPv6Resrv(subnet->getID(), candidate);
+ if (!hosts.empty()) {
+ // Don't allocate.
+ continue;
+ }
}
// Check if the resource is busy i.e. can be being allocated
/// In-pool reservations: Check if this address is reserved for someone
/// else. There is no need to check for whom it is reserved, because if
/// it has been reserved for us we would have already allocated a lease.
- if (!check_reservation_first && (hr_mode == Network::HR_ALL) &&
- HostMgr::instance().get6(subnet->getID(), candidate)) {
-
- // Don't allocate.
- continue;
+ if (!check_reservation_first && (hr_mode == Network::HR_ALL)) {
+ auto hosts = getIPv6Resrv(subnet->getID(), candidate);
+ if (!hosts.empty()) {
+ // Don't allocate.
+ continue;
+ }
}
// there's no existing lease for selected candidate, so it is
// allocation attempts.
} else if (existing->expired()) {
// Make sure it's not reserved.
- if (!check_reservation_first && (hr_mode == Network::HR_ALL) &&
- HostMgr::instance().get6(subnet->getID(), candidate)) {
- // Don't allocate.
- continue;
+ if (!check_reservation_first && (hr_mode == Network::HR_ALL)) {
+ auto hosts = getIPv6Resrv(subnet->getID(), candidate);
+ if (!hosts.empty()) {
+ // Don't allocate.
+ continue;
+ }
}
// Copy an existing, expired lease so as it can be returned
// We have to make a bit more expensive operation here to retrieve
// the reservation for the candidate lease and see if it is
// reserved for someone else.
- ConstHostPtr host = HostMgr::instance().get6(ctx.subnet_->getID(),
- candidate->addr_);
+ auto hosts = getIPv6Resrv(ctx.subnet_->getID(), candidate->addr_);
// If lease is not reserved to someone else, it means that it can
// be allocated to us from a dynamic pool, but we must check if
// this lease belongs to any pool. If it does, we can proceed to
// checking the next lease.
- if (!host && inAllowedPool(ctx, candidate->type_,
- candidate->addr_, false)) {
+ if (hosts.empty() && inAllowedPool(ctx, candidate->type_,
+ candidate->addr_, false)) {
continue;
}
- if (host) {
+ if (!hosts.empty()) {
// Ok, we have a problem. This host has a lease that is reserved
// for someone else. We need to recover from this.
- if (ctx.currentIA().type_ == Lease::TYPE_NA) {
- LOG_INFO(alloc_engine_logger, ALLOC_ENGINE_V6_REVOKED_ADDR_LEASE)
- .arg(candidate->addr_.toText()).arg(ctx.duid_->toText())
- .arg(host->getIdentifierAsText());
+ if (hosts.size() == 1) {
+ if (ctx.currentIA().type_ == Lease::TYPE_NA) {
+ LOG_INFO(alloc_engine_logger, ALLOC_ENGINE_V6_REVOKED_ADDR_LEASE)
+ .arg(candidate->addr_.toText()).arg(ctx.duid_->toText())
+ .arg(hosts.front()->getIdentifierAsText());
+ } else {
+ LOG_INFO(alloc_engine_logger, ALLOC_ENGINE_V6_REVOKED_PREFIX_LEASE)
+ .arg(candidate->addr_.toText())
+ .arg(static_cast<int>(candidate->prefixlen_))
+ .arg(ctx.duid_->toText())
+ .arg(hosts.front()->getIdentifierAsText());
+ }
} else {
- LOG_INFO(alloc_engine_logger, ALLOC_ENGINE_V6_REVOKED_PREFIX_LEASE)
- .arg(candidate->addr_.toText())
- .arg(static_cast<int>(candidate->prefixlen_))
- .arg(ctx.duid_->toText())
- .arg(host->getIdentifierAsText());
+ if (ctx.currentIA().type_ == Lease::TYPE_NA) {
+ LOG_INFO(alloc_engine_logger, ALLOC_ENGINE_V6_REVOKED_SHARED_ADDR_LEASE)
+ .arg(candidate->addr_.toText()).arg(ctx.duid_->toText())
+ .arg(hosts.size());
+ } else {
+ LOG_INFO(alloc_engine_logger, ALLOC_ENGINE_V6_REVOKED_SHARED_PREFIX_LEASE)
+ .arg(candidate->addr_.toText())
+ .arg(static_cast<int>(candidate->prefixlen_))
+ .arg(ctx.duid_->toText())
+ .arg(hosts.size());
+ }
}
}
((ctx.subnet_->getHostReservationMode() == Network::HR_ALL) ||
((ctx.subnet_->getHostReservationMode() == Network::HR_OUT_OF_POOL) &&
(!ctx.subnet_->inPool(Lease::TYPE_V4, address))))) {
- ConstHostPtr host = HostMgr::instance().get4(ctx.subnet_->getID(), address);
- if (host) {
+ // The global parameter ip-reservations-unique controls whether it is allowed
+ // to specify multiple reservations for the same IP address or delegated prefix
+ // or IP reservations must be unique. Some host backends do not support the
+ // former, thus we can't always use getAll4 calls to get the reservations
+ // for the given IP. When we're in the default mode, when IP reservations
+ // are unique, we should call get4 (supported by all backends). If we're in
+ // the mode in which non-unique reservations are allowed the backends which
+ // don't support it are not used and we can safely call getAll4.
+ ConstHostCollection hosts;
+ if (CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->getIPReservationsUnique()) {
+ // Reservations are unique. It is safe to call get4 to get the unique host.
+ ConstHostPtr host = HostMgr::instance().get4(ctx.subnet_->getID(), address);
+ if (host) {
+ hosts.push_back(host);
+ }
+ } else {
+ // Reservations can be non-unique. Need to get all reservations for that address.
+ hosts = HostMgr::instance().getAll4(ctx.subnet_->getID(), address);
+ }
+
+ for (auto host : hosts) {
for (auto id = ctx.host_identifiers_.cbegin(); id != ctx.host_identifiers_.cend();
++id) {
- if (id->first == host->getIdentifierType()) {
- return (id->second != host->getIdentifier());
+ // If we find the matching host we know that this address is reserved
+ // for us and we can return immediatelly.
+ if (id->first == host->getIdentifierType() &&
+ id->second == host->getIdentifier()) {
+ return (false);
}
}
- return (true);
}
+ // We didn't find a matching host. If there are any reservations it means that
+ // address is reserved for another client or multiple clients. If there are
+ // no reservations address is not reserved for another client.
+ return (!hosts.empty());
}
return (false);
}
-// File created from ../../../src/lib/dhcpsrv/alloc_engine_messages.mes on Mon Sep 28 2020 14:52
+// File created from ../../../src/lib/dhcpsrv/alloc_engine_messages.mes on Tue Oct 06 2020 05:22
#include <cstddef>
#include <log/message_types.h>
extern const isc::log::MessageID ALLOC_ENGINE_V6_REUSE_EXPIRED_LEASE_DATA = "ALLOC_ENGINE_V6_REUSE_EXPIRED_LEASE_DATA";
extern const isc::log::MessageID ALLOC_ENGINE_V6_REVOKED_ADDR_LEASE = "ALLOC_ENGINE_V6_REVOKED_ADDR_LEASE";
extern const isc::log::MessageID ALLOC_ENGINE_V6_REVOKED_PREFIX_LEASE = "ALLOC_ENGINE_V6_REVOKED_PREFIX_LEASE";
+extern const isc::log::MessageID ALLOC_ENGINE_V6_REVOKED_SHARED_ADDR_LEASE = "ALLOC_ENGINE_V6_REVOKED_SHARED_ADDR_LEASE";
+extern const isc::log::MessageID ALLOC_ENGINE_V6_REVOKED_SHARED_PREFIX_LEASE = "ALLOC_ENGINE_V6_REVOKED_SHARED_PREFIX_LEASE";
} // namespace dhcp
} // namespace isc
"ALLOC_ENGINE_V6_RENEW_REMOVE_UNRESERVED", "dynamically allocating leases for the renewing client %1",
"ALLOC_ENGINE_V6_REUSE_EXPIRED_LEASE_DATA", "%1: reusing expired lease, updated lease information: %2",
"ALLOC_ENGINE_V6_REVOKED_ADDR_LEASE", "address %1 was revoked from client %2 as it is reserved for client %3",
- "ALLOC_ENGINE_V6_REVOKED_PREFIX_LEASE", "Prefix %1/%2 was revoked from client %3 as it is reserved for client %4",
+ "ALLOC_ENGINE_V6_REVOKED_PREFIX_LEASE", "prefix %1/%2 was revoked from client %3 as it is reserved for client %4",
+ "ALLOC_ENGINE_V6_REVOKED_SHARED_ADDR_LEASE", "address %1 was revoked from client %2 as it is reserved for %3 other clients",
+ "ALLOC_ENGINE_V6_REVOKED_SHARED_PREFIX_LEASE", "prefix %1/%2 was revoked from client %3 as it is reserved for %4 other clients",
NULL
};
-// File created from ../../../src/lib/dhcpsrv/alloc_engine_messages.mes on Mon Sep 28 2020 14:52
+// File created from ../../../src/lib/dhcpsrv/alloc_engine_messages.mes on Tue Oct 06 2020 05:22
#ifndef ALLOC_ENGINE_MESSAGES_H
#define ALLOC_ENGINE_MESSAGES_H
extern const isc::log::MessageID ALLOC_ENGINE_V6_REUSE_EXPIRED_LEASE_DATA;
extern const isc::log::MessageID ALLOC_ENGINE_V6_REVOKED_ADDR_LEASE;
extern const isc::log::MessageID ALLOC_ENGINE_V6_REVOKED_PREFIX_LEASE;
+extern const isc::log::MessageID ALLOC_ENGINE_V6_REVOKED_SHARED_ADDR_LEASE;
+extern const isc::log::MessageID ALLOC_ENGINE_V6_REVOKED_SHARED_PREFIX_LEASE;
} // namespace dhcp
} // namespace isc
-# Copyright (C) 2015-2019 Internet Systems Consortium, Inc. ("ISC")
+# Copyright (C) 2015-2020 Internet Systems Consortium, Inc. ("ISC")
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
address that is currently in use by another client. The server will fully
recover from this situation, but clients will change their addresses.
-% ALLOC_ENGINE_V6_REVOKED_PREFIX_LEASE Prefix %1/%2 was revoked from client %3 as it is reserved for client %4
+% ALLOC_ENGINE_V6_REVOKED_PREFIX_LEASE prefix %1/%2 was revoked from client %3 as it is reserved for client %4
This informational message is an indication that the specified IPv6
prefix was used by client A but it is now reserved for client B. Client
A has been told to stop using it so that it can be leased to client B.
in cases such as the system administrator adding a reservation for an
address that is currently in use by another client. The server will fully
recover from this situation, but clients will change their prefixes.
+
+% ALLOC_ENGINE_V6_REVOKED_SHARED_ADDR_LEASE address %1 was revoked from client %2 as it is reserved for %3 other clients
+This informational message is an indication that the specified IPv6
+address was used by client A but it is now reserved for multiple other
+clients. Client A has been told to stop using it so that it can be
+leased to one of the clients having the reservation for it. This is a
+normal occurrence during conflict resolution, which can occur in cases
+such as the system administrator adding reservations for an address
+that is currently in use by another client. The server will fully
+recover from this situation, but clients will change their addresses.
+
+% ALLOC_ENGINE_V6_REVOKED_SHARED_PREFIX_LEASE prefix %1/%2 was revoked from client %3 as it is reserved for %4 other clients
+This informational message is an indication that the specified IPv6
+prefix was used by client A but it is now reserved for multiple other
+clients. Client A has been told to stop using it so that it can be
+leased to one of the clients having the reservation for it. This is a
+normal occurrence during conflict resolution, which can occur in cases
+such as the system administrator adding reservations for an address
+that is currently in use by another client. The server will fully
+recover from this situation, but clients will change their prefixes.
std::list<std::string> getHostDbAccessStringList() const;
/// @brief Modifies the setting imposing whether the IP reservations
- /// are unique or can be non-unique.
+ /// are unique or can be non unique.
///
/// This flag can be set to @c false when the server is explicitly
/// configured to allow multiple hosts to have the same IP reservation.
ip_reservations_unique_ = unique;
}
+ /// @brief Returns the setting indicating if the IP reservations are
+ /// unique or can be non unique.
+ ///
+ /// @return true if the IP reservations must be unique or false if
+ /// the reservations can be non unique.
+ bool getIPReservationsUnique() const {
+ return (ip_reservations_unique_);
+ }
+
/// @brief Creates instance of lease manager and host data sources
/// according to the configuration specified.
void createManagers();