IfaceMgr::instance().openSockets4();
}
- /// @brief Test that server doesn't allocate the lease for a client
- /// which has the same address or client identifier as another client.
+ /// @brief Test that server returns the same lease for the client which is
+ /// sometimes using client identifier, sometimes not.
///
/// This test checks the server's behavior in the following scenario:
- /// - Client A identifies itself to the server using the hardware address
- /// and client identifier or only one of those.
- /// - Client A performs the 4-way exchange and obtains a lease from the server.
- /// - Client B uses the same HW address or client identifier as the client A.
- /// - Client B uses both HW address and client identifier if the client A is using
- /// only one of them. Client B uses one of the HW address or client
- /// identifier if the client A is using both.
- /// - Client B sends the DHCPDISCOVER to the server.
- /// The server determines that there is a lease for the client A using the
- /// same HW address as the client B. Server discards the client's message and
- /// doesn't offer the lease for the client B to prevent allocation of the
- /// lease without a unique identifier.
- /// - The client sends the DHCPREQUEST and the server sends the DHCPNAK for the
- /// same reason.
- /// - The client A renews its address successfully.
+ /// - Client identifies itself to the server using HW address, and may use
+ /// client identifier.
+ /// - Client performs the 4-way exchange and obtains a lease from the server.
+ /// - If the client identifier was in use when the client has acquired the lease,
+ /// the client uses null client identifier in the next exchange with the server.
+ /// - If the client identifier was not in use when the client has acquired the
+ /// lease, the client uses client identifier in the next exchange with the
+ /// server.
+ /// - When the client contacts the server for the second time using the
+ /// DHCPDISCOVER the server determines (using HW address) that the client
+ /// already has a lease and returns this lease to the client.
+ /// - The client renews the existing lease.
///
- /// The specific test cases using this test must make sure that one of the
- /// provided parameters is an empty string. This simulates the situation where
- /// one of the clients has only one of the identifiers and the other one has
- /// two.
- ///
- /// @param hwaddr_a HW address of client A.
- /// @param clientid_a Client id of client A.
- /// @param hwaddr_b HW address of client B.
- /// @param clientid_b Client id of client B.
- void oneAllocationOverlapTest(const std::string& hwaddr_a,
- const std::string& clientid_a,
- const std::string& hwaddr_b,
+ /// @param clientid_a Client identifier when the client initially allocates
+ /// the lease. An empty value means "no client identifier".
+ /// @param clientid_b Client identifier when the client sends the DHCPDISCOVER
+ /// and then DHCPREQUEST to renew lease.
+ void oneAllocationOverlapTest(const std::string& clientid_a,
const std::string& clientid_b);
- /// @brief Test that server can allocate the lease for a client having
- /// the same HW Address or client id as another client.
- ///
- /// This test checks the server behavior in the following situation:
- /// - Client A identifies itself to the server using client identifier
- /// and the hardware address and requests allocation of the new lease.
- /// - Server allocates the lease to the client.
- /// - Client B has a different hardware address or client identifier than
- /// the client A, but the other identifier is equal to the corresponding
- /// identifier of the client A.
- /// - Client B sends DHCPDISCOVER.
- /// - Server should determine that the client B is not client A, because
- /// it is using a different hadrware address or client identifier.
- /// As a consequence, the server should offer a different address to the
- /// client B.
- /// - The client B performs the 4-way exchange again, and the server
- /// allocates a new address to the client, which should be different
- /// than the address used by the client A.
- /// - Client B is in the renewing state and it successfully renews its
- /// address.
- /// - The client A also renews its address successfully.
- ///
- /// @param hwaddr_a HW address of client A.
- /// @param clientid_a Client id of client A.
- /// @param hwaddr_b HW address of client B.
- /// @param clientid_b Client id of client B.
- void twoAllocationsOverlapTest(const std::string& hwaddr_a,
- const std::string& clientid_a,
- const std::string& hwaddr_b,
- const std::string& clientid_b);
-
/// @brief Interface Manager's fake configuration control.
IfaceMgrTestConfig iface_mgr_test_config_;
}
void
-DORATest::twoAllocationsOverlapTest(const std::string& hwaddr_a,
- const std::string& clientid_a,
- const std::string& hwaddr_b,
- const std::string& clientid_b) {
+DORATest::oneAllocationOverlapTest(const std::string& clientid_a,
+ const std::string& clientid_b) {
+ // Allocate a lease by client using the 4-way exchange.
+ Dhcp4Client client(Dhcp4Client::SELECTING);
+ client.includeClientId(clientid_a);
+ client.setHWAddress("01:02:03:04:05:06");
+ configure(DORA_CONFIGS[0], *client.getServer());
+ ASSERT_NO_THROW(client.doDORA());
+ // 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()));
+ Lease4Ptr lease_a = LeaseMgrFactory::instance().getLease4(client.config_.lease_.addr_);
+ ASSERT_TRUE(lease_a);
+ // Remember the allocated address.
+ IOAddress yiaddr = lease_a->addr_;
+
+ // Change client identifier. If parameters clientid_a and clientid_b
+ // are specified correctly, this removes the client identifier from
+ // client's requests if the lease has been acquired with the client
+ // identifier, or adds the client identifier otherwise.
+ client.includeClientId(clientid_b);
+
+ // Check if the server will offer the same address.
+ ASSERT_NO_THROW(client.doDiscover());
+ resp = client.getContext().response_;
+ ASSERT_TRUE(resp);
+ EXPECT_EQ(yiaddr, resp->getYiaddr());
+
+ // Client should also be able to renew its address.
+ client.setState(Dhcp4Client::RENEWING);
+ ASSERT_NO_THROW(client.doRequest());
+ ASSERT_TRUE(client.getContext().response_);
+ resp = client.getContext().response_;
+ ASSERT_EQ(DHCPACK, static_cast<int>(resp->getType()));
+ ASSERT_EQ(yiaddr, client.config_.lease_.addr_);
+}
+
+// This test checks the server behavior in the following situation:
+// - Client A identifies itself to the server using client identifier
+// and the hardware address and requests allocation of the new lease.
+// - Server allocates the lease to the client.
+// - Client B has the same hardware address but is using a different
+// client identifier then Client A.
+// - Client B sends DHCPDISCOVER.
+// - Server should determine that the client B is not client A, because
+// it is using a different client identifier, even though they use the
+// same HW address. As a consequence, the server should offer a
+// different address to the client B.
+// - The client B performs the 4-way exchange again and the server
+// allocates a new address to the client, which should be different
+// than the address used by the client A.
+// - Client B is in the renewing state and it successfully renews its
+// address.
+// - Client A also renews its address successfully.
+TEST_F(DORATest, twoAllocationsOverlap) {
// Allocate a lease by client A using the 4-way exchange.
Dhcp4Client client_a(Dhcp4Client::SELECTING);
- client_a.includeClientId(clientid_a);
- client_a.setHWAddress(hwaddr_a);
+ client_a.includeClientId("12:34");
+ client_a.setHWAddress("01:02:03:04:05:06");
configure(DORA_CONFIGS[0], *client_a.getServer());
ASSERT_NO_THROW(client_a.doDORA());
// Make sure that the server responded.
// Create client B.
Dhcp4Client client_b(client_a.getServer(), Dhcp4Client::SELECTING);
- client_b.setHWAddress(hwaddr_b);
- client_b.includeClientId(clientid_b);
+ client_b.setHWAddress("01:02:03:04:05:06");
+ client_b.includeClientId("45:67");
// Send DHCPDISCOVER and expect the response.
ASSERT_NO_THROW(client_b.doDiscover());
Pkt4Ptr resp_b = client_b.getContext().response_;
ASSERT_NE(client_a.config_.lease_.addr_, client_b.config_.lease_.addr_);
}
-void
-DORATest::oneAllocationOverlapTest(const std::string& hwaddr_a,
- const std::string& clientid_a,
- const std::string& hwaddr_b,
- const std::string& clientid_b) {
- // Allocate a lease by client A using the 4-way exchange.
- Dhcp4Client client_a(Dhcp4Client::SELECTING);
- client_a.includeClientId(clientid_a);
- client_a.setHWAddress(hwaddr_a);
- configure(DORA_CONFIGS[0], *client_a.getServer());
- ASSERT_NO_THROW(client_a.doDORA());
- // Make sure that the server responded.
- ASSERT_TRUE(client_a.getContext().response_);
- Pkt4Ptr resp_a = client_a.getContext().response_;
- // Make sure that the server has responded with DHCPACK.
- ASSERT_EQ(DHCPACK, static_cast<int>(resp_a->getType()));
- Lease4Ptr lease_a = LeaseMgrFactory::instance().getLease4(client_a.config_.lease_.addr_);
- ASSERT_TRUE(lease_a);
-
- // Client B sends a DHCPDISCOVER.
- Dhcp4Client client_b(client_a.getServer(), Dhcp4Client::SELECTING);
- client_b.setHWAddress(hwaddr_b);
- client_b.includeClientId(clientid_b);
- // Client A and Client B have one common identifier (HW address
- // or client identifier) and one of them is missing the other
- // identifier. The allocation engine can't offer an address for
- // the client which has the same identifier as the other client and
- // which doesn't have any other (unique) identifier. It should
- // discard the DHCPDISCOVER.
- ASSERT_NO_THROW(client_b.doDiscover());
- Pkt4Ptr resp_b = client_b.getContext().response_;
- ASSERT_FALSE(resp_b);
-
- // Now repeat the same test but send the DHCPREQUEST. This time the
- // server should send the DHCPNAK.
- client_b.config_.lease_.addr_ = IOAddress::IPV4_ZERO_ADDRESS();
- client_b.setState(Dhcp4Client::INIT_REBOOT);
- ASSERT_NO_THROW(client_b.doRequest());
- resp_b = client_b.getContext().response_;
- ASSERT_TRUE(resp_b);
- ASSERT_EQ(DHCPNAK, static_cast<int>(resp_b->getType()));
-
- // Client A should also be able to renew its address.
- client_a.setState(Dhcp4Client::RENEWING);
- ASSERT_NO_THROW(client_a.doRequest());
- ASSERT_TRUE(client_a.getContext().response_);
- resp_b = client_a.getContext().response_;
- ASSERT_EQ(DHCPACK, static_cast<int>(resp_b->getType()));
- ASSERT_NE(client_a.config_.lease_.addr_, client_b.config_.lease_.addr_);
-}
-
-// This test checks the server behavior in the following situation:
-// - Client A identifies itself to the server using client identifier
-// and the hardware address and requests allocation of the new lease.
-// - Server allocates the lease to the client.
-// - Client B has a different hardware address but is using the same
-// client identifier as Client A.
-// - Client B sends DHCPDISCOVER.
-// - Server should determine that the client B is not client A, because
-// it is using a different hadrware address, even though they use the
-// same client identifier. As a consequence, the server should offer
-// a different address to the client B.
-// - The client B performs the 4-way exchange again and the server
-// allocates a new address to the client, which should be different
-// than the address used by the client A.
-// - Client B is in the renewing state and it successfully renews its
-// address.
-// - Client A also renews its address successfully.
-TEST_F(DORATest, twoAllocationsOverlap1) {
- twoAllocationsOverlapTest("01:02:03:04:05:06", "12:34",
- "02:02:03:03:04:04", "12:34");
-}
-
-// This test is similar to twoAllocationsOverlap1, but the
-// clients differ by client identifier.
-TEST_F(DORATest, twoAllocationsOverlap2) {
- twoAllocationsOverlapTest("01:02:03:04:05:06", "12:34",
- "01:02:03:04:05:06", "22:34");
-}
-
// This test checks the server behavior in the following situation:
// - Client A identifies itself to the server using the hardware address
// and client identifier.
// same reason.
// - Client A renews its address successfully.
TEST_F(DORATest, oneAllocationOverlap1) {
- oneAllocationOverlapTest("01:02:03:04:05:06", "12:34",
- "01:02:03:04:05:06", "");
+ oneAllocationOverlapTest("12:34", "");
}
// This test is similar to oneAllocationOverlap2 but this time the client A
// uses no client identifier, and the client B uses the HW address and the
// client identifier. The server behaves as previously.
TEST_F(DORATest, oneAllocationOverlap2) {
- oneAllocationOverlapTest("01:02:03:04:05:06", "",
- "01:02:03:04:05:06", "12:34");
+ oneAllocationOverlapTest("", "12:34");
}
// This is a simple test for the host reservation. It creates a reservation
return (ctx.host_ && !ctx.host_->getIPv4Reservation().isV4Zero());
}
-/// @brief Check if there is a lease for the client which message is processed.
+/// @brief Finds existing lease in the database.
///
-/// This function searches the lease database to find existing lease for the client.
-/// It finds the lease using the client's HW address first. If the lease exists and
-/// appears to belong to the client the lease is returned. Otherwise, the function
-/// will search for the lease using the client identifier (if supplied). If the
-/// lease exists and appears to belong to the client, it is returned.
+/// This function searches for the lease in the database which belongs to the
+/// client requesting allocation. If the client has supplied the client
+/// identifier this identifier is used to look up the lease. If the lease is
+/// not found using the client identifier, an additional lookup is performed
+/// using the HW address, if supplied. If the lease is found using the HW
+/// address, the function also checks if the lease belongs to the client, i.e.
+/// there is no conflict between the client identifiers.
///
-/// This function also identifies the conflicts between existing leases and the
-/// lease to be allocated for the client, when the client is using a HW address
-/// or client identifier which is already in use by the client having a lease in
-/// the database. If the client uses an identifier which is already used by another
-/// client and no other unique identifier which could be used to identify the client's
-/// lease this function signals the conflict by returning 'true'.
-///
-/// @param ctx Client context.
-/// @param [out] client_lease Client's lease found in the database.
-///
-/// @return true if there is a conflict of identifiers (HW address or client id)
-/// between the client which message is being processed and the client which has
-/// a lease in the database. When the value 'true' is returned, the caller should
-/// cease the lease allocation for the client.
-bool matchClientLease(const AllocEngine::ClientContext4& ctx, Lease4Ptr& client_lease) {
- // Obtain the sole instance of the LeaseMgr.
+/// @param ctx Context holding data extracted from the client's message,
+/// including the HW address and client identifier.
+/// @param [out] client_lease A pointer to the lease returned by this function
+/// or null value if no has been lease found.
+void findClientLease(const AllocEngine::ClientContext4& ctx, Lease4Ptr& client_lease) {
LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
-
- // The server should hand out existing lease to the client, so we have to check
- // if there is one. First, try to use the client's HW address.
- client_lease = lease_mgr.getLease4(*ctx.hwaddr_, ctx.subnet_->getID());
- // If there is no lease for this HW address or the lease doesn't seem to be ours,
- // we will have to use the client identifier. Note that in some situations two
- // clients may use the same HW address so even if we find the lease for the HW
- // address it doesn't mean it is ours, because client identifier may not match.
- if (ctx.clientid_ && ((!client_lease) || (client_lease && !ctx.myLease(*client_lease)))) {
- // Check if the lease is in conflict with the lease that we want to allocate.
- // If the lease is in conflict because of using overlapping HW address or
- // client identifier, we can't allocate the lease for this client.
- if (client_lease && ctx.isInConflict(*client_lease)) {
- return (true);
- }
- // There is no lease or the lease we found is not conflicting with the lease
- // which we have found for the HW address, so there is still a chance that
- // we will allocate the lease. Check if there is a lease using the client
- // identifier.
+ // If client identifier has been supplied, use it to lookup the lease. This
+ // search will return no lease if the client doesn't have any lease in the
+ // database or if the client didn't use client identifier to allocate the
+ // existing lease (this include cases when the server was explicitly
+ // configured to ignore client identifier).
+ if (ctx.clientid_) {
client_lease = lease_mgr.getLease4(*ctx.clientid_, ctx.subnet_->getID());
}
- // Check if the lease we have found belongs to us.
- if (client_lease && !ctx.myLease(*client_lease)) {
- // If the lease doesn't belong to us, check if we can add new lease for
- // the client which message we're processing, or its identifiers are
- // in conflict with this lease.
- if (ctx.isInConflict(*client_lease)) {
- return (true);
+ // If no lease found using the client identifier, try the lookup using
+ // the HW address.
+ if (!client_lease && ctx.hwaddr_) {
+ client_lease = lease_mgr.getLease4(*ctx.hwaddr_, ctx.subnet_->getID());
+ // This lookup may return the lease which has conflicting client
+ // identifier and thus is considered to belong to someone else.
+ // If this is the case, we need to toss the result and force the
+ // Allocation Engine to allocate another lease.
+ if (client_lease && !client_lease->belongsToClient(ctx.hwaddr_, ctx.clientid_)) {
+ client_lease.reset();
}
- // If there is no conflict we can proceed and try to find the appropriate
- // lease but we don't use the one we found, because it is assigned to
- // someone else. Reset the pointer to indicate that we're not
- // renewing this lease.
- client_lease.reset();
}
-
- return (false);
}
} // end of anonymous namespace
fake_allocation_(fake_allocation), old_lease_(), host_() {
}
-
-
-bool
-AllocEngine::ClientContext4::myLease(const Lease4& lease) const {
- if ((!hwaddr_ && lease.hwaddr_) || (hwaddr_ && !lease.hwaddr_)) {
- return (false);
- }
-
- if ((hwaddr_ && lease.hwaddr_) && (hwaddr_->hwaddr_ != lease.hwaddr_->hwaddr_)) {
- return (false);
- }
-
- if ((!clientid_ && lease.client_id_) || (clientid_ && !lease.client_id_)) {
- return (false);
- }
-
- if ((clientid_ && lease.client_id_) && (*clientid_ != *lease.client_id_)) {
- return (false);
- }
-
- return (true);
-}
-
-bool
-AllocEngine::ClientContext4::isInConflict(const Lease4& lease) const {
- return ((!(hwaddr_ && lease.hwaddr_) && (clientid_ && lease.client_id_) &&
- (*clientid_ == *lease.client_id_)) ||
- (!(clientid_ && lease.client_id_) && (hwaddr_ && lease.hwaddr_) &&
- (hwaddr_->hwaddr_ == lease.hwaddr_->hwaddr_)));
-}
-
Lease4Ptr
AllocEngine::allocateLease4(ClientContext4& ctx) {
// The NULL pointer indicates that the old lease didn't exist. It may
// if there is a conflict with existing lease and the allocation should
// not be continued.
Lease4Ptr client_lease;
- bool conflict = matchClientLease(ctx, client_lease);
- if (conflict) {
- return (Lease4Ptr());
- }
+ findClientLease(ctx, client_lease);
// new_lease will hold the pointer to the lease that we will offer to the
// caller.
// if there is a conflict with existing lease and the allocation should
// not be continued.
Lease4Ptr client_lease;
- bool conflict = matchClientLease(ctx, client_lease);
- if (conflict) {
- return (Lease4Ptr());
- }
+ findClientLease(ctx, client_lease);
// Obtain the sole instance of the LeaseMgr.
LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
// if the address is in use by our client or another client.
// If it is in use by another client, the address can't be
// allocated.
- if (existing && !existing->expired() && !ctx.myLease(*existing)) {
+ if (existing && !existing->expired() &&
+ !existing->belongsToClient(ctx.hwaddr_, ctx.clientid_)) {
return (Lease4Ptr());
}
EXPECT_TRUE(*ctx.old_lease_ == original_lease);
}
+// This test checks that the Allocation Engine correcly identifies the
+// existing client's lease in the lease database, using the client
+// identifier and HW address.
+TEST_F(AllocEngine4Test, identifyClientLease) {
+ Lease4Ptr lease(new Lease4(IOAddress("192.0.2.101"), hwaddr_, clientid_,
+ 100, 30, 60, time(NULL), subnet_->getID()));
+ LeaseMgrFactory::instance().addLease(lease);
+
+ AllocEngine engine(AllocEngine::ALLOC_ITERATIVE, 100, false);
+ AllocEngine::ClientContext4 ctx(subnet_, clientid_, hwaddr_,
+ IOAddress::IPV4_ZERO_ADDRESS(),
+ false, false, "", true);
+
+ Lease4Ptr identified_lease = engine.allocateLease4(ctx);
+ ASSERT_TRUE(identified_lease);
+ EXPECT_EQ("192.0.2.101", identified_lease->addr_.toText());
+
+ ctx.hwaddr_ = hwaddr2_;
+ identified_lease = engine.allocateLease4(ctx);
+ ASSERT_TRUE(identified_lease);
+ EXPECT_EQ("192.0.2.101", identified_lease->addr_.toText());
+
+ ctx.hwaddr_ = hwaddr_;
+ ctx.clientid_ = clientid2_;
+ identified_lease = engine.allocateLease4(ctx);
+ ASSERT_TRUE(identified_lease);
+ EXPECT_NE(identified_lease->addr_.toText(), "192.0.2.101");
+
+ ctx.clientid_.reset();
+ identified_lease = engine.allocateLease4(ctx);
+ ASSERT_TRUE(identified_lease);
+ EXPECT_EQ("192.0.2.101", identified_lease->addr_.toText());
+
+ ctx.hwaddr_ = hwaddr2_;
+ identified_lease = engine.allocateLease4(ctx);
+ ASSERT_TRUE(identified_lease);
+ EXPECT_NE(identified_lease->addr_.toText(), "192.0.2.101");
+
+ lease->client_id_.reset();
+ ASSERT_NO_THROW(LeaseMgrFactory::instance().updateLease4(lease));
+
+ ctx.hwaddr_ = hwaddr_;
+ ctx.clientid_ = clientid_;
+ identified_lease = engine.allocateLease4(ctx);
+ ASSERT_TRUE(identified_lease);
+ EXPECT_EQ("192.0.2.101", identified_lease->addr_.toText());
+
+ ctx.clientid_.reset();
+ identified_lease = engine.allocateLease4(ctx);
+ ASSERT_TRUE(identified_lease);
+ EXPECT_EQ("192.0.2.101", identified_lease->addr_.toText());
+
+ ctx.clientid_ = clientid_;
+ ctx.hwaddr_ = hwaddr2_;
+ identified_lease = engine.allocateLease4(ctx);
+ ASSERT_TRUE(identified_lease);
+ EXPECT_NE(identified_lease->addr_.toText(), "192.0.2.101");
+}
+
// This test checks that when the client requests the address which belongs
// to another client, the allocation engine returns NULL (for the
// DHCPREQUEST case) or a lease for the address which belongs to this
CfgMgr::instance().commit();
// Create a lease for the client for a different address than reserved.
- Lease4Ptr lease(new Lease4(IOAddress("192.0.2.101"), hwaddr_, 0, 0,
+ Lease4Ptr lease(new Lease4(IOAddress("192.0.2.101"), hwaddr_, ClientIdPtr(),
100, 30, 60, time(NULL), subnet_->getID(),
false, false, ""));
LeaseMgrFactory::instance().addLease(lease);
AllocEngine::ClientContext4 ctx2(subnet_, clientid_, hwaddr_,
IOAddress("192.0.2.101"), false, false,
"", false);
+ AllocEngine::findReservation(ctx2);
allocated_lease = engine.allocateLease4(ctx2);
// The client has reservation so the server wants to allocate a
// reserved address and doesn't want to renew the address that the