Browse Source

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

Marcin Siodelski 10 years ago
parent
commit
e602856372

+ 42 - 3
src/lib/dhcpsrv/lease.cc

@@ -1,4 +1,4 @@
-// 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
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
 // purpose with or without fee is hereby granted, provided that the above
@@ -13,8 +13,10 @@
 // PERFORMANCE OF THIS SOFTWARE.
 // PERFORMANCE OF THIS SOFTWARE.
 
 
 #include <dhcpsrv/lease.h>
 #include <dhcpsrv/lease.h>
+#include <util/pointer_util.h>
 #include <sstream>
 #include <sstream>
 
 
+using namespace isc::util;
 using namespace std;
 using namespace std;
 
 
 namespace isc {
 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) {
      fqdn_fwd_(fqdn_fwd), fqdn_rev_(fqdn_rev), hwaddr_(hwaddr) {
 }
 }
 
 
+
 std::string
 std::string
 Lease::typeToText(Lease::Type type) {
 Lease::typeToText(Lease::Type type) {
    switch (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>&
 const std::vector<uint8_t>&
 Lease4::getClientIdVector() const {
 Lease4::getClientIdVector() const {
     if(!client_id_) {
     if(!client_id_) {
@@ -139,6 +161,23 @@ Lease4::matches(const Lease4& other) const {
              *hwaddr_ == *other.hwaddr_) );
              *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&
 Lease4::operator=(const Lease4& other) {
 Lease4::operator=(const Lease4& other) {
     if (this != &other) {
     if (this != &other) {
@@ -241,13 +280,13 @@ std::string
 Lease4::toText() const {
 Lease4::toText() const {
     ostringstream stream;
     ostringstream stream;
 
 
-    /// @todo: print out client-id (if present)
     stream << "Address:       " << addr_ << "\n"
     stream << "Address:       " << addr_ << "\n"
            << "Valid life:    " << valid_lft_ << "\n"
            << "Valid life:    " << valid_lft_ << "\n"
            << "T1:            " << t1_ << "\n"
            << "T1:            " << t1_ << "\n"
            << "T2:            " << t2_ << "\n"
            << "T2:            " << t2_ << "\n"
            << "Cltt:          " << cltt_ << "\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";
            << "Subnet ID:     " << subnet_id_ << "\n";
 
 
     return (stream.str());
     return (stream.str());

+ 88 - 0
src/lib/dhcpsrv/lease.h

@@ -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
     /// @brief Default constructor
     ///
     ///
     /// Initialize fields that don't have a 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.
     /// @return true if the selected parameters of the two leases match.
     bool matches(const Lease4& other) const;
     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.
     /// @brief Assignment operator.
     ///
     ///
     /// @param other the @c Lease4 object to be assigned.
     /// @param other the @c Lease4 object to be assigned.

+ 124 - 52
src/lib/dhcpsrv/tests/lease_unittest.cc

@@ -57,27 +57,24 @@ Lease4 createLease4(const std::string& hostname, const bool fqdn_fwd,
 class Lease4Test : public ::testing::Test {
 class Lease4Test : public ::testing::Test {
 public:
 public:
 
 
-    /// Default constructor
+    /// @brief Default constructor
     ///
     ///
     /// Currently it only initializes hardware address.
     /// Currently it only initializes hardware address.
     Lease4Test() {
     Lease4Test() {
         hwaddr_.reset(new HWAddr(HWADDR, sizeof(HWADDR), HTYPE_ETHER));
         hwaddr_.reset(new HWAddr(HWADDR, sizeof(HWADDR), HTYPE_ETHER));
+        clientid_.reset(new ClientId(CLIENTID, sizeof(CLIENTID)));
     }
     }
 
 
     /// Hardware address, used by tests.
     /// Hardware address, used by tests.
     HWAddrPtr hwaddr_;
     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) {
 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);
     const time_t current_time = time(NULL);
 
 
     // Other random constants.
     // Other random constants.
@@ -93,15 +90,14 @@ TEST_F(Lease4Test, constructor) {
     for (int i = 0; i < sizeof(ADDRESS) / sizeof(ADDRESS[0]); ++i) {
     for (int i = 0; i < sizeof(ADDRESS) / sizeof(ADDRESS[0]); ++i) {
 
 
         // Create the lease
         // 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,
                      current_time, SUBNET_ID, true, true,
                      "hostname.example.com.");
                      "hostname.example.com.");
 
 
         EXPECT_EQ(ADDRESS[i], static_cast<uint32_t>(lease.addr_));
         EXPECT_EQ(ADDRESS[i], static_cast<uint32_t>(lease.addr_));
         EXPECT_EQ(0, lease.ext_);
         EXPECT_EQ(0, lease.ext_);
         EXPECT_TRUE(hwaddr_ == lease.hwaddr_);
         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.t1_);
         EXPECT_EQ(0, lease.t2_);
         EXPECT_EQ(0, lease.t2_);
         EXPECT_EQ(VALID_LIFETIME, lease.valid_lft_);
         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.
 // This test verfies that copy constructor copies Lease4 fields correctly.
 TEST_F(Lease4Test, copyConstructor) {
 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);
     const time_t current_time = time(NULL);
 
 
     // Other random constants.
     // Other random constants.
@@ -130,8 +122,7 @@ TEST_F(Lease4Test, copyConstructor) {
     const uint32_t VALID_LIFETIME = 500;
     const uint32_t VALID_LIFETIME = 500;
 
 
     // Create the lease
     // 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);
                  SUBNET_ID);
 
 
     // Use copy constructor to copy the lease.
     // Use copy constructor to copy the lease.
@@ -160,12 +151,7 @@ TEST_F(Lease4Test, copyConstructor) {
 // correctly.
 // correctly.
 TEST_F(Lease4Test, operatorAssign) {
 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);
     const time_t current_time = time(NULL);
 
 
     // Other random constants.
     // Other random constants.
@@ -173,8 +159,7 @@ TEST_F(Lease4Test, operatorAssign) {
     const uint32_t VALID_LIFETIME = 500;
     const uint32_t VALID_LIFETIME = 500;
 
 
     // Create the lease
     // 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);
                  SUBNET_ID);
 
 
     // Use assignment operator to assign the lease.
     // 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
     // Create two leases which share the same address, HW address, client id
     // and ext_ value.
     // and ext_ value.
     const time_t current_time = time(NULL);
     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.hostname_ = "lease1.example.com.";
     lease1.fqdn_fwd_ = true;
     lease1.fqdn_fwd_ = true;
     lease1.fqdn_rev_ = true;
     lease1.fqdn_rev_ = true;
@@ -251,6 +235,96 @@ TEST_F(Lease4Test, matches) {
     lease1.ext_ = lease2.ext_;
     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
 /// @brief Lease4 Equality Test
 ///
 ///
 /// Checks that the operator==() correctly compares two leases for equality.
 /// Checks that the operator==() correctly compares two leases for equality.
@@ -261,27 +335,22 @@ TEST_F(Lease4Test, operatorEquals) {
     // Random values for the tests
     // Random values for the tests
     const uint32_t ADDRESS = 0x01020304;
     const uint32_t ADDRESS = 0x01020304;
     const uint8_t HWADDR[] = {0x08, 0x00, 0x2b, 0x02, 0x3f, 0x4e};
     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 time_t current_time = time(NULL);
     const uint32_t SUBNET_ID = 42;
     const uint32_t SUBNET_ID = 42;
     const uint32_t VALID_LIFETIME = 500;
     const uint32_t VALID_LIFETIME = 500;
 
 
     // Check when the leases are equal.
     // 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);
                   0, SUBNET_ID);
 
 
     // We need to make an explicit copy. Otherwise the second lease will just
     // 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_));
     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_TRUE(lease1 == lease2);
     EXPECT_FALSE(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_TRUE(lease1 == lease2);  // Check that the reversion has made the
     EXPECT_FALSE(lease1 != lease2); // ... leases equal
     EXPECT_FALSE(lease1 != lease2); // ... leases equal
 
 
+    std::vector<uint8_t> clientid_vec = clientid_->getClientId();
     ++clientid_vec[0];
     ++clientid_vec[0];
     lease1.client_id_.reset(new ClientId(clientid_vec));
     lease1.client_id_.reset(new ClientId(clientid_vec));
     EXPECT_FALSE(lease1 == lease2);
     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
 // Verify that the client id can be returned as a vector object and if client
 // id is NULL the empty vector is returned.
 // id is NULL the empty vector is returned.
-TEST(Lease4, getClientIdVector) {
+TEST_F(Lease4Test, getClientIdVector) {
     // Create a lease.
     // Create a lease.
     Lease4 lease;
     Lease4 lease;
     // By default, the lease should have client id set to NULL. If it doesn't,
     // 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_);
     ASSERT_FALSE(lease.client_id_);
     // When client id is NULL the vector returned should be empty.
     // When client id is NULL the vector returned should be empty.
     EXPECT_TRUE(lease.getClientIdVector().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
     // 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.
     // the one that has been used to set the client id for the lease.
     std::vector<uint8_t> returned_vec = lease.getClientIdVector();
     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.
 // 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);
     Lease4 lease = createLease4("myhost.example.com.", true, true);
     EXPECT_TRUE(lease.hasIdenticalFqdn(createLease4("myhost.example.com.",
     EXPECT_TRUE(lease.hasIdenticalFqdn(createLease4("myhost.example.com.",
                                                      true, true)));
                                                      true, true)));
@@ -429,8 +498,8 @@ TEST(Lease4, hasIdenticalFqdn) {
 TEST_F(Lease4Test, toText) {
 TEST_F(Lease4Test, toText) {
 
 
     const time_t current_time = 12345678;
     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;
     std::stringstream expected;
     expected << "Address:       192.0.2.3\n"
     expected << "Address:       192.0.2.3\n"
@@ -439,12 +508,14 @@ TEST_F(Lease4Test, toText) {
              << "T2:            456\n"
              << "T2:            456\n"
              << "Cltt:          12345678\n"
              << "Cltt:          12345678\n"
              << "Hardware addr: " << hwaddr_->toText(false) << "\n"
              << "Hardware addr: " << hwaddr_->toText(false) << "\n"
+             << "Client id:     " << clientid_->toText() << "\n"
              << "Subnet ID:     789\n";
              << "Subnet ID:     789\n";
 
 
     EXPECT_EQ(expected.str(), lease.toText());
     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.hwaddr_.reset();
+    lease.client_id_.reset();
     expected.str("");
     expected.str("");
     expected << "Address:       192.0.2.3\n"
     expected << "Address:       192.0.2.3\n"
              << "Valid life:    3600\n"
              << "Valid life:    3600\n"
@@ -452,6 +523,7 @@ TEST_F(Lease4Test, toText) {
              << "T2:            456\n"
              << "T2:            456\n"
              << "Cltt:          12345678\n"
              << "Cltt:          12345678\n"
              << "Hardware addr: (none)\n"
              << "Hardware addr: (none)\n"
+             << "Client id:     (none)\n"
              << "Subnet ID:     789\n";
              << "Subnet ID:     789\n";
     EXPECT_EQ(expected.str(), lease.toText());
     EXPECT_EQ(expected.str(), lease.toText());
 }
 }

+ 1 - 0
src/lib/util/Makefile.am

@@ -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 += memory_segment_local.h memory_segment_local.cc
 libkea_util_la_SOURCES += optional_value.h
 libkea_util_la_SOURCES += optional_value.h
 libkea_util_la_SOURCES += pid_file.h pid_file.cc
 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 += process_spawn.h process_spawn.cc
 libkea_util_la_SOURCES += range_utilities.h
 libkea_util_la_SOURCES += range_utilities.h
 libkea_util_la_SOURCES += signal_set.cc signal_set.h
 libkea_util_la_SOURCES += signal_set.cc signal_set.h

+ 39 - 0
src/lib/util/pointer_util.h

@@ -0,0 +1,39 @@
+// 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