#include <dhcpsrv/lease.h>
#include <util/pointer_util.h>
#include <sstream>
+#include <iostream>
using namespace isc::util;
using namespace std;
namespace isc {
namespace dhcp {
+const uint32_t Lease::STATE_DEFAULT = 0x1;
+const uint32_t Lease::STATE_DECLINED = 0x2;
+const uint32_t Lease::STATE_EXPIRED_RECLAIMED = 0x4;
+
+const unsigned int Lease::BASIC_STATES_NUM = 3;
+
+
Lease::Lease(const isc::asiolink::IOAddress& addr, uint32_t t1, uint32_t t2,
uint32_t valid_lft, SubnetID subnet_id, time_t cltt,
const bool fqdn_fwd, const bool fqdn_rev,
const std::string& hostname, const HWAddrPtr& hwaddr)
:addr_(addr), t1_(t1), t2_(t2), valid_lft_(valid_lft), cltt_(cltt),
subnet_id_(subnet_id), fixed_(false), hostname_(hostname),
- fqdn_fwd_(fqdn_fwd), fqdn_rev_(fqdn_rev), hwaddr_(hwaddr) {
+ fqdn_fwd_(fqdn_fwd), fqdn_rev_(fqdn_rev), hwaddr_(hwaddr),
+ state_(STATE_DEFAULT) {
}
}
}
-bool Lease::expired() const {
+std::string
+Lease::basicStatesToText(const uint32_t state) {
+ // Stream will hold comma separated list of states.
+ std::ostringstream s;
+ // Iterate over all defined states.
+ for (int i = 0; i < BASIC_STATES_NUM; ++i) {
+ // Test each bit of the lease state to see if it is set.
+ uint32_t single_state = (state & (static_cast<uint32_t>(1) << i));
+ // Only proceed if the bit is set.
+ if (single_state > 0) {
+ // Comma sign is applied only if any state has been found.
+ if (static_cast<int>(s.tellp() > 0)) {
+ s << ",";
+ }
+ // Check which bit is set and append state name.
+ switch (single_state) {
+ case STATE_DEFAULT:
+ s << "default";
+ break;
+
+ case STATE_DECLINED:
+ s << "declined";
+ break;
+
+ case STATE_EXPIRED_RECLAIMED:
+ s << "expired-reclaimed";
+ break;
+
+ default:
+ // This shouldn't really happen.
+ s << "unknown";
+ }
+ }
+ }
+
+ return (s.tellp() > 0 ? s.str() : "(not set)");
+}
+
+bool
+Lease::expired() const {
+ return (getExpirationTime() < time(NULL));
+}
+
+bool
+Lease::stateExpiredReclaimed() const {
+ return ((state_ & STATE_EXPIRED_RECLAIMED) != 0);
+}
- // Let's use int64 to avoid problems with negative/large uint32 values
- int64_t expire_time = cltt_ + valid_lft_;
- return (expire_time < time(NULL));
+int64_t
+Lease::getExpirationTime() const {
+ return (static_cast<int64_t>(cltt_) + valid_lft_);
}
bool
Lease4::Lease4(const Lease4& other)
: Lease(other.addr_, other.t1_, other.t2_, other.valid_lft_,
other.subnet_id_, other.cltt_, other.fqdn_fwd_,
- other.fqdn_rev_, other.hostname_, other.hwaddr_),
- ext_(other.ext_) {
+ other.fqdn_rev_, other.hostname_, other.hwaddr_) {
- fixed_ = other.fixed_;
+ // Copy over fields derived from Lease.
+ ext_ = other.ext_;
comments_ = other.comments_;
+ fixed_ = other.fixed_;
+ state_ = other.state_;
// Copy the hardware address if it is defined.
if (other.hwaddr_) {
fqdn_rev_ = other.fqdn_rev_;
comments_ = other.comments_;
ext_ = other.ext_;
+ state_ = other.state_;
// Copy the hardware address if it is defined.
if (other.hwaddr_) {
<< "Valid life: " << valid_lft_ << "\n"
<< "Cltt: " << cltt_ << "\n"
<< "Hardware addr: " << (hwaddr_?hwaddr_->toText(false):"(none)") << "\n"
- << "Subnet ID: " << subnet_id_ << "\n";
+ << "Subnet ID: " << subnet_id_ << "\n"
+ << "State: " << statesToText(state_) << "\n";
return (stream.str());
}
<< "Cltt: " << cltt_ << "\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"
+ << "State: " << statesToText(state_) << "\n";
return (stream.str());
}
hostname_ == other.hostname_ &&
fqdn_fwd_ == other.fqdn_fwd_ &&
fqdn_rev_ == other.fqdn_rev_ &&
- comments_ == other.comments_);
+ comments_ == other.comments_ &&
+ state_ == other.state_);
}
bool
hostname_ == other.hostname_ &&
fqdn_fwd_ == other.fqdn_fwd_ &&
fqdn_rev_ == other.fqdn_rev_ &&
- comments_ == other.comments_);
+ comments_ == other.comments_ &&
+ state_ == other.state_);
}
std::ostream&
/// @return text decription
static std::string typeToText(Type type);
+ /// @name Common lease states constants.
+ //@{
+ ///
+ /// @brief A lease in the default state.
+ static const uint32_t STATE_DEFAULT;
+
+ /// @brief Declined lease.
+ static const uint32_t STATE_DECLINED;
+
+ /// @brief Expired and reclaimed lease.
+ static const uint32_t STATE_EXPIRED_RECLAIMED;
+
+ /// @brief Number of common states for DHCPv4 and DHCPv6.
+ ///
+ /// This constants holds the number of states used for both DHCPv4 and
+ /// DHCPv6 leases. If new states are defined, this value must be adjusted
+ /// accordingly.
+ static const unsigned int BASIC_STATES_NUM;
+ //@}
+
+ /// @brief Returns name(s) of the basic lease state(s).
+ ///
+ /// @param state A numeric value holding a state information. Multiple bits
+ /// may be set to indicate that the lease is in multiple states.
+ ///
+ /// @return Comma separated list of state names.
+ static std::string basicStatesToText(const uint32_t state);
+
/// @brief Constructor
///
/// @param addr IP address
/// system administrator.
std::string comments_;
+ /// @brief Bitfield holding lease state(s).
+ ///
+ /// This is a bitfield which holds the lease state. Typically, a lease
+ /// remains in a single state, but it is possible set multiple states
+ /// for a single lease. In this case, multiple bits of this bitfield
+ /// will be set.
+ uint32_t state_;
+
/// @brief Convert Lease to Printable Form
///
/// @return String form of the lease
/// @return true if the lease is expired
bool expired() const;
+ /// @brief Indicates if the lease is in the "expired-reclaimed" state.
+ ///
+ /// @return true if the lease is in the "expired-reclaimed" state, false
+ /// otherwise.
+ bool stateExpiredReclaimed() const;
+
/// @brief Returns true if the other lease has equal FQDN data.
///
/// @param other Lease which FQDN data is to be compared with our lease.
///
/// @return const reference to the hardware address
const std::vector<uint8_t>& getHWAddrVector() const;
+
+ /// @brief Returns lease expiration time.
+ ///
+ /// The lease expiration time is a sum of a client last transmission time
+ /// and valid lifetime.
+ int64_t getExpirationTime() const;
};
/// @brief Structure that holds a lease for IPv4 address
/// @param other the @c Lease4 object to be copied.
Lease4(const Lease4& other);
+ /// @brief Returns name of the lease states specific to DHCPv4.
+ ///
+ /// @todo Currently it simply returns common states for DHCPv4 and DHCPv6.
+ /// This method will have to be extended to handle DHCPv4 specific states
+ /// when they are defined.
+ ///
+ /// @param state Numeric value holding lease states.
+ /// @return Comma separated list of lease state names.
+ static std::string statesToText(const uint32_t state) {
+ return (Lease::basicStatesToText(state));
+ }
+
+
/// @brief Returns a client identifier.
///
/// @warning Since the function returns the reference to a vector (not a
/// Initialize fields that don't have a default constructor.
Lease6();
+ /// @brief Returns name of the lease states specific to DHCPv6.
+ ///
+ /// @todo Currently it simply returns common states for DHCPv4 and DHCPv6.
+ /// This method will have to be extended to handle DHCPv6 specific states
+ /// when they are defined.
+ ///
+ /// @param state Numeric value holding lease states.
+ /// @return Comma separated list of lease state names.
+ static std::string statesToText(const uint32_t state) {
+ return (Lease::basicStatesToText(state));
+ }
+
/// @brief Returns a reference to a vector representing a DUID.
///
/// @warning Since the function returns the reference to a vector (not a
EXPECT_TRUE(lease.fqdn_fwd_);
EXPECT_TRUE(lease.fqdn_rev_);
EXPECT_TRUE(lease.comments_.empty());
+ EXPECT_EQ(Lease::STATE_DEFAULT, lease.state_);
}
}
Lease4 lease(0xffffffff, hwaddr_, clientid_, VALID_LIFETIME, 0, 0, current_time,
SUBNET_ID);
+ // Declined is a non-default state. We'll see if the state will be copied
+ // or the default state will be set for the copied lease.
+ lease.state_ = Lease::STATE_DECLINED;
+
// Use copy constructor to copy the lease.
Lease4 copied_lease(lease);
Lease4 lease(0xffffffff, hwaddr_, clientid_, VALID_LIFETIME, 0, 0, current_time,
SUBNET_ID);
- // Use assignment operator to assign the lease.
- Lease4 copied_lease = lease;
+ // Declined is a non-default state. We'll see if the state will be copied
+ // or the default state will be set for the copied lease.
+ lease.state_ = Lease::STATE_DECLINED;
+
+ // Create a default lease.
+ Lease4 copied_lease;
+ // Use assignment operator to assign new lease.
+ copied_lease = lease;
// Both leases should be now equal. When doing this check we assume that
// the equality operator works correctly.
lease1.comments_ = lease2.comments_;
EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ lease1.state_ += 1;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease2.state_ += 1;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
}
// Verify that the client id can be returned as a vector object and if client
<< "Cltt: 12345678\n"
<< "Hardware addr: " << hwaddr_->toText(false) << "\n"
<< "Client id: " << clientid_->toText() << "\n"
- << "Subnet ID: 789\n";
+ << "Subnet ID: 789\n"
+ << "State: default\n";
EXPECT_EQ(expected.str(), lease.toText());
<< "Cltt: 12345678\n"
<< "Hardware addr: (none)\n"
<< "Client id: (none)\n"
- << "Subnet ID: 789\n";
+ << "Subnet ID: 789\n"
+ << "State: default\n";
EXPECT_EQ(expected.str(), lease.toText());
}
+// Verify that the lease states are correctly returned in the textual format.
+TEST_F(Lease4Test, stateToText) {
+ EXPECT_EQ("default", Lease4::statesToText(Lease::STATE_DEFAULT));
+ EXPECT_EQ("declined", Lease4::statesToText(Lease::STATE_DECLINED));
+ EXPECT_EQ("expired-reclaimed", Lease4::statesToText(Lease::STATE_EXPIRED_RECLAIMED));
+
+ // Try multiple states.
+ EXPECT_EQ("default,declined,expired-reclaimed",
+ Lease4::statesToText(Lease::STATE_DEFAULT | Lease::STATE_DECLINED |
+ Lease::STATE_EXPIRED_RECLAIMED));
+
+ // Try undefined state.
+ EXPECT_EQ("(not set)", Lease4::statesToText(0));
+}
+
/// @brief Creates an instance of the lease with certain FQDN data.
///
/// @param hostname Hostname.
// Lease6 is also defined in lease_mgr.h, so is tested in this file as well.
// This test checks if the Lease6 structure can be instantiated correctly
-TEST(Lease6, Lease6ConstructorDefault) {
+TEST(Lease6Test, Lease6ConstructorDefault) {
// check a variety of addresses with different bits set.
const char* ADDRESS[] = {
// This test verifies that the Lease6 constructor which accepts FQDN data,
// sets the data correctly for the lease.
-TEST(Lease6, Lease6ConstructorWithFQDN) {
+TEST(Lease6Test, Lease6ConstructorWithFQDN) {
// check a variety of addresses with different bits set.
const char* ADDRESS[] = {
/// Checks that the operator==() correctly compares two leases for equality.
/// As operator!=() is also defined for this class, every check on operator==()
/// is followed by the reverse check on operator!=().
-TEST(Lease6, OperatorEquals) {
+TEST(Lease6Test, OperatorEquals) {
// check a variety of addresses with different bits set.
const IOAddress addr("2001:db8:1::456");
lease1.comments_ = lease2.comments_;
EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
EXPECT_FALSE(lease1 != lease2); // ... leases equal
+
+ lease1.state_ += 1;
+ EXPECT_FALSE(lease1 == lease2);
+ EXPECT_TRUE(lease1 != lease2);
+ lease2.state_ += 1;
+ EXPECT_TRUE(lease1 == lease2); // Check that the reversion has made the
+ EXPECT_FALSE(lease1 != lease2); // ... leases equal
}
// Checks if lease expiration is calculated properly
-TEST(Lease6, Lease6Expired) {
+TEST(Lease6Test, Lease6Expired) {
const IOAddress addr("2001:db8:1::456");
const uint8_t duid_array[] = {0, 1, 2, 3, 4, 5, 6, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
const DuidPtr duid(new DUID(duid_array, sizeof(duid_array)));
// Verify that the DUID can be returned as a vector object and if DUID is NULL
// the empty vector is returned.
-TEST(Lease6, getDuidVector) {
+TEST(Lease6Test, getDuidVector) {
// Create a lease.
Lease6 lease;
// By default, the lease should have client id set to NULL. If it doesn't,
}
// Verify the behavior of the function which checks FQDN data for equality.
-TEST(Lease6, hasIdenticalFqdn) {
+TEST(Lease6Test, hasIdenticalFqdn) {
Lease6 lease = createLease6("myhost.example.com.", true, true);
EXPECT_TRUE(lease.hasIdenticalFqdn(createLease6("myhost.example.com.",
true, true)));
}
// Verify that toText() method reports Lease4 structure properly.
-TEST(Lease6, toText) {
+TEST(Lease6Test, toText) {
HWAddrPtr hwaddr(new HWAddr(HWADDR, sizeof(HWADDR), HTYPE_ETHER));
Lease6 lease(Lease::TYPE_NA, IOAddress("2001:db8::1"), duid, 123456,
400, 800, 100, 200, 5678, hwaddr, 128);
lease.cltt_ = 12345678;
+ lease.state_ = Lease::STATE_DECLINED;
std::stringstream expected;
expected << "Type: IA_NA(" << static_cast<int>(Lease::TYPE_NA) << ")\n"
<< "Valid life: 800\n"
<< "Cltt: 12345678\n"
<< "Hardware addr: " << hwaddr->toText(false) << "\n"
- << "Subnet ID: 5678\n";
+ << "Subnet ID: 5678\n"
+ << "State: declined\n";
EXPECT_EQ(expected.str(), lease.toText());
<< "Valid life: 800\n"
<< "Cltt: 12345678\n"
<< "Hardware addr: (none)\n"
- << "Subnet ID: 5678\n";
+ << "Subnet ID: 5678\n"
+ << "State: declined\n";
EXPECT_EQ(expected.str(), lease.toText());
}
+// Verify that the lease states are correctly returned in the textual format.
+TEST(Lease6Test, stateToText) {
+ EXPECT_EQ("default", Lease6::statesToText(Lease::STATE_DEFAULT));
+ EXPECT_EQ("declined", Lease6::statesToText(Lease::STATE_DECLINED));
+ EXPECT_EQ("expired-reclaimed", Lease6::statesToText(Lease::STATE_EXPIRED_RECLAIMED));
+
+ // Try multiple states.
+ EXPECT_EQ("default,declined,expired-reclaimed",
+ Lease6::statesToText(Lease::STATE_DEFAULT | Lease::STATE_DECLINED |
+ Lease::STATE_EXPIRED_RECLAIMED));
+
+ // Try undefined state.
+ EXPECT_EQ("(not set)", Lease6::statesToText(0));
+}
+
+
}; // end of anonymous namespace