Commit e6028563 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[3747] Added method which checks if lease belongs to the client.

parent 5e4674e2
// Copyright (C) 2012-2014 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2012-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
......@@ -13,8 +13,10 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <dhcpsrv/lease.h>
#include <util/pointer_util.h>
#include <sstream>
using namespace isc::util;
using namespace std;
namespace isc {
......@@ -29,6 +31,7 @@ Lease::Lease(const isc::asiolink::IOAddress& addr, uint32_t t1, uint32_t t2,
fqdn_fwd_(fqdn_fwd), fqdn_rev_(fqdn_rev), hwaddr_(hwaddr) {
}
std::string
Lease::typeToText(Lease::Type type) {
switch (type) {
......@@ -88,6 +91,25 @@ Lease4::Lease4(const Lease4& other)
}
}
Lease4::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,
const bool fqdn_rev,
const std::string& hostname)
: Lease(address, t1, t2, valid_lifetime, subnet_id, cltt, fqdn_fwd,
fqdn_rev, hostname, hw_address),
ext_(0), client_id_(client_id) {
}
const std::vector<uint8_t>&
Lease4::getClientIdVector() const {
if(!client_id_) {
......@@ -139,6 +161,23 @@ Lease4::matches(const Lease4& other) const {
*hwaddr_ == *other.hwaddr_) );
}
bool
Lease4::belongsToClient(const HWAddrPtr& hw_address,
const ClientIdPtr& client_id) const {
// If client id matches, lease matches.
if (equalValues(client_id, client_id_)) {
return (true);
} else if (!client_id || !client_id_) {
// If client id is unspecified, use HW address.
if (equalValues(hw_address, hwaddr_)) {
return (true);
}
}
return (false);
}
Lease4&
Lease4::operator=(const Lease4& other) {
if (this != &other) {
......@@ -241,13 +280,13 @@ std::string
Lease4::toText() const {
ostringstream stream;
/// @todo: print out client-id (if present)
stream << "Address: " << addr_ << "\n"
<< "Valid life: " << valid_lft_ << "\n"
<< "T1: " << t1_ << "\n"
<< "T2: " << t2_ << "\n"
<< "Cltt: " << cltt_ << "\n"
<< "Hardware addr: " << (hwaddr_?hwaddr_->toText(false):"(none)") << "\n"
<< "Hardware addr: " << (hwaddr_ ? hwaddr_->toText(false) : "(none)") << "\n"
<< "Client id: " << (client_id_ ? client_id_->toText() : "(none)") << "\n"
<< "Subnet ID: " << subnet_id_ << "\n";
return (stream.str());
......
......@@ -217,6 +217,32 @@ struct Lease4 : public Lease {
}
}
/// @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.
......@@ -253,6 +279,68 @@ struct Lease4 : public Lease {
/// @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.
......
......@@ -57,27 +57,24 @@ Lease4 createLease4(const std::string& hostname, const bool fqdn_fwd,
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.
......@@ -93,15 +90,14 @@ TEST_F(Lease4Test, constructor) {
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_);
......@@ -118,11 +114,7 @@ TEST_F(Lease4Test, constructor) {
// 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.
......@@ -130,8 +122,7 @@ TEST_F(Lease4Test, copyConstructor) {
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.
......@@ -160,12 +151,7 @@ TEST_F(Lease4Test, copyConstructor) {
// 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.
......@@ -173,8 +159,7 @@ TEST_F(Lease4Test, operatorAssign) {
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.
......@@ -205,9 +190,8 @@ TEST_F(Lease4Test, matches) {
// 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;
......@@ -251,6 +235,96 @@ TEST_F(Lease4Test, matches) {
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.
......@@ -261,27 +335,22 @@ TEST_F(Lease4Test, operatorEquals) {
// 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);
......@@ -308,6 +377,7 @@ TEST_F(Lease4Test, operatorEquals) {
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);
......@@ -390,7 +460,7 @@ TEST_F(Lease4Test, operatorEquals) {
// 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,
......@@ -398,18 +468,17 @@ TEST(Lease4, getClientIdVector) {
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)));
......@@ -429,8 +498,8 @@ TEST(Lease4, hasIdenticalFqdn) {
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"
......@@ -439,12 +508,14 @@ TEST_F(Lease4Test, toText) {
<< "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"
......@@ -452,6 +523,7 @@ TEST_F(Lease4Test, toText) {
<< "T2: 456\n"
<< "Cltt: 12345678\n"
<< "Hardware addr: (none)\n"
<< "Client id: (none)\n"
<< "Subnet ID: 789\n";
EXPECT_EQ(expected.str(), lease.toText());
}
......
......@@ -19,6 +19,7 @@ libkea_util_la_SOURCES += memory_segment.h
libkea_util_la_SOURCES += memory_segment_local.h memory_segment_local.cc
libkea_util_la_SOURCES += optional_value.h
libkea_util_la_SOURCES += pid_file.h pid_file.cc
libkea_util_la_SOURCES += pointer_util.h
libkea_util_la_SOURCES += process_spawn.h process_spawn.cc
libkea_util_la_SOURCES += range_utilities.h
libkea_util_la_SOURCES += signal_set.cc signal_set.h
......
// Copyright (C) 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
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#ifndef POINTER_UTIL_H
#define POINTER_UTIL_H
namespace isc {
namespace util {
/// @brief This function checks if two pointers are non-null and values
/// are equal.
///
/// This function performs the typical comparison of values encapsulated by
/// the smart pointers, with checking if the pointers are non-null.
///
/// @tparam T Pointer type, e.g. boost::shared_ptr, or boost::scoped_ptr.
///
/// @return true only if both pointers are non-null and the values which they
/// point to are equal.
template<typename T>
bool equalValues(const T& ptr1, const T& ptr2) {
return (ptr1 && ptr2 && (*ptr1 == *ptr2));
}
} // end of namespace isc::util
} // end of namespace isc
#endif
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment