}
}
+ /// @brief Constructor.
+ ///
+ /// @param address IPv4 address.
+ /// @param hw_address Pointer to client's HW addresss.
+ /// @param client_id pointer to the client id structure.
+ /// @param valid_lifetime Valid lifetime value.
+ /// @param t1 Renew timer.
+ /// @param t2 Rebind timer.
+ /// @param cltt Timestamp when the lease is acquired, renewed.
+ /// @param subnet_id Subnet identifier.
+ /// @param fqdn_fwd Forward DNS update performed.
+ /// @param fqdn_rev Reverse DNS update performed.
+ /// @param hostname Client's name for the DNS update..
+ Lease4(const isc::asiolink::IOAddress& address,
+ const HWAddrPtr& hw_address,
+ const ClientIdPtr& client_id,
+ const uint32_t valid_lifetime,
+ const uint32_t t1,
+ const uint32_t t2,
+ const time_t cltt,
+ const SubnetID subnet_id,
+ const bool fqdn_fwd = false,
+ const bool fqdn_rev = false,
+ const std::string& hostname = "");
+
+
/// @brief Default constructor
///
/// Initialize fields that don't have a default constructor.
/// @return true if the selected parameters of the two leases match.
bool matches(const Lease4& other) const;
+ /// @brief Check if the lease belongs to the client with the given
+ /// identifiers.
+ ///
+ /// This method checks if the lease belongs to the client using the
+ /// specified HW address and/or client identifier. Note that any of the
+ /// pointers passed to this method may be set to null, in which case
+ /// they are treated as unspecified and are not used for matching the
+ /// client with the lease.
+ ///
+ /// According to the DHCPv4 specifications, the client identifier takes
+ /// precedence over the HW address when identifying the lease for the
+ /// client on the server side. In particular, the RFC4361 introduces the
+ /// use of DUID for DHCPv4 which should be a stable identifier for the
+ /// client. The use of stable identifier allows for the correlation of the
+ /// DHCPv4 and DHCPv6 clients in the dual stack networks. It also allows
+ /// for allocating the same lease to the client which hardware (and thus
+ /// MAC address) has changed.
+ ///
+ /// By default, Kea respects the precedence of the client identifier over
+ /// MAC address and when this method finds the match of the client
+ /// identifier with the client identifier stored in the lease, it will
+ /// treat the lease as the lease of this client, even when the HW
+ /// address doesn't match.
+ ///
+ /// The HW address is used for matching the client with the lease only
+ /// when the lease is not associated with any client identifier (client
+ /// identifier for the lease is null) or when the client identifier
+ /// parameter passed to this method is null. This facilitates the following
+ /// cases:
+ /// - client didn't generate client identifier and is only using the chaddr
+ /// field to identify itself.
+ /// - server's administrator configured the server to ignore client identifier,
+ /// the client obtained the new lease, and the administrator reconfigured
+ /// the server to NOT ignore the client identifier. The client is trying
+ /// to renew its lease and both the client identifier and HW address is
+ /// used for matching the lease which doesn't have the record of the
+ /// client identifier.
+ /// - client obtained the lease using the HW address and client identifier,
+ /// the server's administrator configured the server to ignore the client
+ /// identifier, and the client returns to renew the lease. This time, the
+ /// lease has a record of both client identifier and the HW address but
+ /// only the HW address is used for matching the client to the lease.
+ ///
+ /// Note that the typical case when the server's administrator may want to
+ /// force ignoring client identifier passed in the client's message is when
+ /// the client is performing multi-stage boot. In such case, the client
+ /// identifiers may change on various stages of the boot, but the HW address
+ /// will remain stable. The server's administrator prefers to use the
+ /// HW address for client identification in this case.
+ ///
+ /// It may also be useful to ignore client identifiers to mitigate the
+ /// problem of broken client implementations which generate new client
+ /// identifiers every time they connect to the network.
+ ///
+ /// @param hw_address Pointer to the HW address of the client.
+ /// @param client_id Pointer to the client identifier structure.
+ ///
+ /// @return true if the lease belongs to the client using the specified
+ /// hardware address and/or client identifier.
+ bool belongsToClient(const HWAddrPtr& hw_address,
+ const ClientIdPtr& client_id) const;
+
/// @brief Assignment operator.
///
/// @param other the @c Lease4 object to be assigned.
class Lease4Test : public ::testing::Test {
public:
- /// Default constructor
+ /// @brief Default constructor
///
/// Currently it only initializes hardware address.
Lease4Test() {
hwaddr_.reset(new HWAddr(HWADDR, sizeof(HWADDR), HTYPE_ETHER));
+ clientid_.reset(new ClientId(CLIENTID, sizeof(CLIENTID)));
}
/// Hardware address, used by tests.
HWAddrPtr hwaddr_;
+
+ /// Pointer to the client identifier used by tests.
+ ClientIdPtr clientid_;
};
-/// Lease4 is also defined in lease_mgr.h, so is tested in this file as well.
-// This test checks if the Lease4 structure can be instantiated correctly
+// This test checks if the Lease4 structure can be instantiated correctly.
TEST_F(Lease4Test, constructor) {
-
- // Random values for the tests
- const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54};
- std::vector<uint8_t> clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID));
- ClientId clientid(clientid_vec);
-
- // ...and a time
+ // Get current time for the use in Lease.
const time_t current_time = time(NULL);
// Other random constants.
for (int i = 0; i < sizeof(ADDRESS) / sizeof(ADDRESS[0]); ++i) {
// Create the lease
- Lease4 lease(ADDRESS[i], hwaddr_,
- CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, 0, 0,
+ Lease4 lease(ADDRESS[i], hwaddr_, clientid_, VALID_LIFETIME, 0, 0,
current_time, SUBNET_ID, true, true,
"hostname.example.com.");
EXPECT_EQ(ADDRESS[i], static_cast<uint32_t>(lease.addr_));
EXPECT_EQ(0, lease.ext_);
EXPECT_TRUE(hwaddr_ == lease.hwaddr_);
- EXPECT_TRUE(clientid == *lease.client_id_);
+ EXPECT_TRUE(*clientid_ == *lease.client_id_);
EXPECT_EQ(0, lease.t1_);
EXPECT_EQ(0, lease.t2_);
EXPECT_EQ(VALID_LIFETIME, lease.valid_lft_);
// This test verfies that copy constructor copies Lease4 fields correctly.
TEST_F(Lease4Test, copyConstructor) {
- const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54};
- std::vector<uint8_t> clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID));
- ClientId clientid(clientid_vec);
-
- // ...and a time
+ // Get current time for the use in Lease4.
const time_t current_time = time(NULL);
// Other random constants.
const uint32_t VALID_LIFETIME = 500;
// Create the lease
- Lease4 lease(0xffffffff, hwaddr_,
- CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, 0, 0, current_time,
+ Lease4 lease(0xffffffff, hwaddr_, clientid_, VALID_LIFETIME, 0, 0, current_time,
SUBNET_ID);
// Use copy constructor to copy the lease.
// correctly.
TEST_F(Lease4Test, operatorAssign) {
- // Random values for the tests
- const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54};
- std::vector<uint8_t> clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID));
- ClientId clientid(clientid_vec);
-
- // ...and a time
+ // Get the current time for the use in Lease4.
const time_t current_time = time(NULL);
// Other random constants.
const uint32_t VALID_LIFETIME = 500;
// Create the lease
- Lease4 lease(0xffffffff, hwaddr_,
- CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, 0, 0, current_time,
+ Lease4 lease(0xffffffff, hwaddr_, clientid_, VALID_LIFETIME, 0, 0, current_time,
SUBNET_ID);
// Use assignment operator to assign the lease.
// Create two leases which share the same address, HW address, client id
// and ext_ value.
const time_t current_time = time(NULL);
- Lease4 lease1(IOAddress("192.0.2.3"), hwaddr_, CLIENTID,
- sizeof(CLIENTID), VALID_LIFETIME, current_time, 0, 0,
- SUBNET_ID);
+ Lease4 lease1(IOAddress("192.0.2.3"), hwaddr_, clientid_, VALID_LIFETIME,
+ current_time, 0, 0, SUBNET_ID);
lease1.hostname_ = "lease1.example.com.";
lease1.fqdn_fwd_ = true;
lease1.fqdn_rev_ = true;
lease1.ext_ = lease2.ext_;
}
+// This test verifies that it is correctly determined when the lease
+// belongs to the particular client identified by the client identifier
+// and hw address.
+TEST_F(Lease4Test, leaseBelongsToClient) {
+ // Create the lease with MAC address and Client Identifier.
+ Lease4 lease(IOAddress("192.0.2.1"),
+ HWAddrPtr(new HWAddr(HWAddr::fromText("00:01:02:03:04:05", HTYPE_ETHER))),
+ ClientId::fromText("01:02:03:04"),
+ 60, time(NULL), 0, 0, 1);
+
+ // Create HW address and Client Id objects which match those held by the
+ // lease. This is a full match, so the lease belongs to the client.
+ HWAddrPtr hwaddr(new HWAddr(HWAddr::fromText("00:01:02:03:04:05", HTYPE_ETHER)));
+ ClientIdPtr clientid = ClientId::fromText("01:02:03:04");
+ EXPECT_TRUE(lease.belongsToClient(hwaddr, clientid));
+
+ // Modify the HW address. The lease should still belong to the client
+ // because the client identifier matches.
+ hwaddr.reset(new HWAddr(HWAddr::fromText("00:01:02:03:04:06", HTYPE_ETHER)));
+ EXPECT_TRUE(lease.belongsToClient(hwaddr, clientid));
+
+ // Use the correct HW address, but modify the client identifier. The client
+ // identifier must match if present. If it doesn't match, the lease doesn't
+ // belong to the client.
+ hwaddr.reset(new HWAddr(HWAddr::fromText("00:01:02:03:04:05", HTYPE_ETHER)));
+ clientid = ClientId::fromText("01:02:03:05");
+ EXPECT_FALSE(lease.belongsToClient(hwaddr, clientid));
+
+ // Set client id to null and leave only HW address. It coveres the case when
+ // the client id is ignored by the server (e.g. multi-stage boot case), but
+ // the client has a lease already.
+ clientid.reset();
+ EXPECT_TRUE(lease.belongsToClient(hwaddr, clientid));
+
+ // Now use the correct client id and the null HW address. The client id
+ // should be sufficient to match the lease.
+ clientid = ClientId::fromText("01:02:03:04");
+ hwaddr.reset();
+ EXPECT_TRUE(lease.belongsToClient(hwaddr, clientid));
+
+ // Use both HW address and client id set to null. There must be no match.
+ hwaddr.reset();
+ clientid.reset();
+ EXPECT_FALSE(lease.belongsToClient(hwaddr, clientid));
+
+ // This time both HW address and client identifier are different than
+ // those held by the lease. There should be no match.
+ hwaddr.reset(new HWAddr(HWAddr::fromText("00:01:02:03:04:06", HTYPE_ETHER)));
+ clientid = ClientId::fromText("01:02:03:05");
+ EXPECT_FALSE(lease.belongsToClient(hwaddr, clientid));
+
+ // Use the correct HW address and client id to match the lease, but set the
+ // client id for the lease to null. This covers the case when the lease
+ // has been acquired without client identifier but then the client is
+ // renewing using some client identifier (or server is configured to
+ // not ignore the client identifier). The client should obtain the lease.
+ hwaddr.reset(new HWAddr(HWAddr::fromText("00:01:02:03:04:05", HTYPE_ETHER)));
+ lease.client_id_.reset();
+ EXPECT_TRUE(lease.belongsToClient(hwaddr, clientid));
+
+ // Change HW address. This time, the HW address doesn't match and client id
+ // can't be compared so it is considered as no match.
+ hwaddr.reset(new HWAddr(HWAddr::fromText("00:01:02:03:04:06", HTYPE_ETHER)));
+ EXPECT_FALSE(lease.belongsToClient(hwaddr, clientid));
+
+ // The lease now holds the client id by the HW address is null for the lease.
+ // Also, we're using correct client id to match the lease and some HW address.
+ // There should be a match because client id is ok.
+ lease.client_id_ = ClientId::fromText("01:02:03:04");
+ lease.hwaddr_.reset();
+ clientid = ClientId::fromText("01:02:03:04");
+ hwaddr.reset(new HWAddr(HWAddr::fromText("00:01:02:03:04:05", HTYPE_ETHER)));
+ EXPECT_TRUE(lease.belongsToClient(hwaddr, clientid));
+
+ // If client id is wrong, there should be no match.
+ clientid = ClientId::fromText("01:02:03:05");
+ EXPECT_FALSE(lease.belongsToClient(hwaddr, clientid));
+
+ // Setting HW address to null shouldn't matter and we should still have a
+ // match if the client id is correct.
+ clientid = ClientId::fromText("01:02:03:04");
+ hwaddr.reset();
+ EXPECT_TRUE(lease.belongsToClient(hwaddr, clientid));
+
+ // But with the null HW address and non-matching client id there should be
+ // no match whatsoever.
+ clientid = ClientId::fromText("01:02:03:06");
+ EXPECT_FALSE(lease.belongsToClient(hwaddr, clientid));
+}
+
/// @brief Lease4 Equality Test
///
/// Checks that the operator==() correctly compares two leases for equality.
// Random values for the tests
const uint32_t ADDRESS = 0x01020304;
const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e};
- std::vector<uint8_t> hwaddr(HWADDR, HWADDR + sizeof(HWADDR));
- const uint8_t CLIENTID[] = {0x17, 0x34, 0xe2, 0xff, 0x09, 0x92, 0x54};
- std::vector<uint8_t> clientid_vec(CLIENTID, CLIENTID + sizeof(CLIENTID));
- ClientId clientid(clientid_vec);
const time_t current_time = time(NULL);
const uint32_t SUBNET_ID = 42;
const uint32_t VALID_LIFETIME = 500;
// Check when the leases are equal.
- Lease4 lease1(ADDRESS, hwaddr_,
- CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, current_time, 0,
+ Lease4 lease1(ADDRESS, hwaddr_, clientid_, VALID_LIFETIME, current_time, 0,
0, SUBNET_ID);
// We need to make an explicit copy. Otherwise the second lease will just
- // store a pointer and we'll have two leases pointing to a single HWAddr.
- // That would make modifications to only one impossible.
+ // store a pointer and we'll have two leases pointing to a single HWAddr
+ // or client. That would make modifications to only one impossible.
HWAddrPtr hwcopy(new HWAddr(*hwaddr_));
+ ClientIdPtr clientid_copy(new ClientId(*clientid_));
- Lease4 lease2(ADDRESS, hwcopy,
- CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, current_time, 0, 0,
- SUBNET_ID);
+ Lease4 lease2(ADDRESS, hwcopy, clientid_copy, VALID_LIFETIME, current_time,
+ 0, 0, SUBNET_ID);
EXPECT_TRUE(lease1 == lease2);
EXPECT_FALSE(lease1 != lease2);
EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
EXPECT_FALSE(lease1 != lease2); // ... leases equal
+ std::vector<uint8_t> clientid_vec = clientid_->getClientId();
++clientid_vec[0];
lease1.client_id_.reset(new ClientId(clientid_vec));
EXPECT_FALSE(lease1 == lease2);
// Verify that the client id can be returned as a vector object and if client
// id is NULL the empty vector is returned.
-TEST(Lease4, getClientIdVector) {
+TEST_F(Lease4Test, getClientIdVector) {
// Create a lease.
Lease4 lease;
// By default, the lease should have client id set to NULL. If it doesn't,
ASSERT_FALSE(lease.client_id_);
// When client id is NULL the vector returned should be empty.
EXPECT_TRUE(lease.getClientIdVector().empty());
- // Now, let's set the non NULL client id. Fill it with the 8 bytes, each
- // holding a value of 0x42.
- std::vector<uint8_t> client_id_vec(8, 0x42);
- lease.client_id_ = ClientIdPtr(new ClientId(client_id_vec));
+
+ // Initialize client identifier to non-null value.
+ lease.client_id_ = clientid_;
// Check that the returned vector, encapsulating client id is equal to
// the one that has been used to set the client id for the lease.
std::vector<uint8_t> returned_vec = lease.getClientIdVector();
- EXPECT_TRUE(returned_vec == client_id_vec);
+ EXPECT_TRUE(returned_vec == clientid_->getClientId());
}
// Verify the behavior of the function which checks FQDN data for equality.
-TEST(Lease4, hasIdenticalFqdn) {
+TEST_F(Lease4Test, hasIdenticalFqdn) {
Lease4 lease = createLease4("myhost.example.com.", true, true);
EXPECT_TRUE(lease.hasIdenticalFqdn(createLease4("myhost.example.com.",
true, true)));
TEST_F(Lease4Test, toText) {
const time_t current_time = 12345678;
- Lease4 lease(IOAddress("192.0.2.3"), hwaddr_, CLIENTID, sizeof(CLIENTID),
- 3600, 123, 456, current_time, 789);
+ Lease4 lease(IOAddress("192.0.2.3"), hwaddr_, clientid_, 3600, 123,
+ 456, current_time, 789);
std::stringstream expected;
expected << "Address: 192.0.2.3\n"
<< "T2: 456\n"
<< "Cltt: 12345678\n"
<< "Hardware addr: " << hwaddr_->toText(false) << "\n"
+ << "Client id: " << clientid_->toText() << "\n"
<< "Subnet ID: 789\n";
EXPECT_EQ(expected.str(), lease.toText());
- // Now let's try with a lease without hardware address.
+ // Now let's try with a lease without hardware address and client identifier.
lease.hwaddr_.reset();
+ lease.client_id_.reset();
expected.str("");
expected << "Address: 192.0.2.3\n"
<< "Valid life: 3600\n"
<< "T2: 456\n"
<< "Cltt: 12345678\n"
<< "Hardware addr: (none)\n"
+ << "Client id: (none)\n"
<< "Subnet ID: 789\n";
EXPECT_EQ(expected.str(), lease.toText());
}