summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFrancis Dupont <fdupont@isc.org>2020-11-13 13:03:48 +0100
committerFrancis Dupont <fdupont@isc.org>2021-01-11 16:05:46 +0100
commit65b7546e3246915bd6d68336e7fea3199b2ca4b0 (patch)
tree6eb9eb9d4b262c5b2779f5ecc78043105ce62ded
parent[#1418] Checkpoint: todo v6 server UT and doc (diff)
downloadkea-65b7546e3246915bd6d68336e7fea3199b2ca4b0.tar.xz
kea-65b7546e3246915bd6d68336e7fea3199b2ca4b0.zip
[#1418] Updated v6 tests and doc
-rw-r--r--doc/sphinx/arm/dhcp4-srv.rst61
-rw-r--r--doc/sphinx/arm/dhcp6-srv.rst59
-rw-r--r--src/bin/dhcp4/tests/dhcp4_srv_unittest.cc4
-rw-r--r--src/bin/dhcp4/tests/hooks_unittest.cc4
-rw-r--r--src/bin/dhcp6/dhcp6_srv.cc53
-rw-r--r--src/bin/dhcp6/tests/dhcp6_srv_unittest.cc423
-rw-r--r--src/bin/dhcp6/tests/hooks_unittest.cc182
-rw-r--r--src/lib/dhcpsrv/libdhcpsrv.dox8
-rw-r--r--src/lib/dhcpsrv/tests/alloc_engine4_unittest.cc6
-rw-r--r--src/lib/dhcpsrv/tests/alloc_engine6_unittest.cc137
10 files changed, 904 insertions, 33 deletions
diff --git a/doc/sphinx/arm/dhcp4-srv.rst b/doc/sphinx/arm/dhcp4-srv.rst
index 214c44bbc9..a2f021a0b5 100644
--- a/doc/sphinx/arm/dhcp4-srv.rst
+++ b/doc/sphinx/arm/dhcp4-srv.rst
@@ -3994,6 +3994,63 @@ enable the option for the whole subnet, the following configuration can be used:
}
],
+Lease Caching
+-------------
+
+Clients that attempt renewal frequently can cause the server to update
+and write the database frequently resulting in a performance impact on
+the server. The cache parameters instructs the DHCP server to avoid
+updating leases too frequently thus avoiding this behavior. Instead
+the server assigns the same lease (i.e. reuses it) with no
+modifications except for CLTT (Client Last Transmission Time)
+which does not require disk operations.
+
+The two parameters are the ``cache-threshold`` double and the
+``cache-max-age`` integer and have no default, i.e. the lease caching
+feature must be explicitly enabled. These parameters can be configured
+at the global, shared network and subnet levels. The subnet level has
+the precedence on the shared network level, the global level is used
+as last resort. For example:
+
+::
+
+ "subnet4": [
+ {
+ "pools": [ { "pool": "192.0.2.1 - 192.0.2.200" } ],
+ "subnet": "192.0.2.0/24",
+ "cache-threshold": .25,
+ "cache-max-age": 600,
+ "valid-lifetime": 2000,
+ ...
+ }
+ ],
+
+When an already assigned lease can fulfill a client query:
+
+ - any important change e.g. for DDNS parameter, hostname or
+ valid lifetime reduction makes the lease not reusable
+
+ - lease age i.e. the difference between the creation or last modification
+ time and the current time is computed (elapsed duration)
+
+ - if the ``cache-max-age`` is configured, it is compared with the age
+ and too old leases are not reusable (this means that the value 0
+ for ``cache-max-age`` disables the lease cache feature)
+
+ - if the ``cache-threshold`` is configured and is between 0. and 1.
+ it expresses the percentage of the lease valid lifetime which is
+ allowed for the lease age. Values below and including 0. and
+ values greater than 1. disables the lease cache feature.
+
+In the example a lease with a valid lifetime of 2000 seconds can be
+reused if it was committed less than 500 seconds ago. With a lifetime
+of 3000 seconds the maximum age of 600 seconds applies.
+
+In responses the ``dhcp-lease-time`` option is set to the remaining
+valid lifetime i.e. the expiration date does not change. Other options
+based on the valid lifetime e.g. ``dhcp-renewal-time`` and
+``dhcp-rebinding-time`` also use the remaining lifetime.
+
.. _host-reservation-v4:
Host Reservation in DHCPv4
@@ -6749,6 +6806,10 @@ used by all servers connecting to the configuration database.
+-----------------------------+----------------------------+-------------+-------------+-------------+
| boot-file-name | yes | yes | yes | n/a |
+-----------------------------+----------------------------+-------------+-------------+-------------+
+ | cache-max-age | yes | todo | todo | n/a |
+ +-----------------------------+----------------------------+-------------+-------------+-------------+
+ | cache-threshold | yes | todo | todo | n/a |
+ +-----------------------------+----------------------------+-------------+-------------+-------------+
| calculate-tee-times | yes | yes | yes | n/a |
+-----------------------------+----------------------------+-------------+-------------+-------------+
| client-class | n/a | yes | yes | yes |
diff --git a/doc/sphinx/arm/dhcp6-srv.rst b/doc/sphinx/arm/dhcp6-srv.rst
index efdb8310d7..1268a63f57 100644
--- a/doc/sphinx/arm/dhcp6-srv.rst
+++ b/doc/sphinx/arm/dhcp6-srv.rst
@@ -3467,6 +3467,61 @@ Our tests reported best results when:
storing leases. In our case it's 11 * 6 = 66.
+Lease Caching
+-------------
+
+Clients that attempt renewal frequently can cause the server to update
+and write the database frequently resulting in a performance impact on
+the server. The cache parameters instructs the DHCP server to avoid
+updating leases too frequently thus avoiding this behavior. Instead
+the server assigns the same lease (i.e. reuses it) with no
+modifications except for CLTT (Client Last Transmission Time)
+which does not require disk operations.
+
+The two parameters are the ``cache-threshold`` double and the
+``cache-max-age`` integer and have no default, i.e. the lease caching
+feature must be explicitly enabled. These parameters can be configured
+at the global, shared network and subnet levels. The subnet level has
+the precedence on the shared network level, the global level is used
+as last resort. For example:
+
+::
+
+ "subnet6": [
+ {
+ "subnet": "2001:db8:1:1::/64",
+ "pools": [ { "pool": "2001:db8:1:1::1:0/112" } ],
+ "cache-threshold": .25,
+ "cache-max-age": 600,
+ "valid-lifetime": 2000,
+ ...
+ }
+ ],
+
+When an already assigned lease can fulfill a client query:
+
+ - any important change e.g. for DDNS parameter, hostname, or
+ preferred or valid lifetime reduction makes the lease not reusable
+
+ - lease age i.e. the difference between the creation or last modification
+ time and the current time is computed (elapsed duration)
+
+ - if the ``cache-max-age`` is configured, it is compared with the age
+ and too old leases are not reusable (this means that the value 0
+ for ``cache-max-age`` disables the lease cache feature)
+
+ - if the ``cache-threshold`` is configured and is between 0. and 1.
+ it expresses the percentage of the lease valid lifetime which is
+ allowed for the lease age. Values below and including 0. and
+ values greater than 1. disables the lease cache feature.
+
+In the example a lease with a valid lifetime of 2000 seconds can be
+reused if it was committed less than 500 seconds ago. With a lifetime
+of 3000 seconds the maximum age of 600 seconds applies.
+
+In responses used preferred and valid lifetimes are the remaining
+values i.e. the expiration dates do not change.
+
.. _host-reservation-v6:
Host Reservation in DHCPv6
@@ -6731,6 +6786,10 @@ the global DHCPv6 options (``option-data``) are modified using
| | | Network | | | Delegation |
| | | | | | Pool |
+=============================+============================+===========+===========+===========+============+
+ | cache-max-age | yes | todo | todo | n/a | n/a |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+------------+
+ | cache-threshold | yes | todo | todo | n/a | n/a |
+ +-----------------------------+----------------------------+-----------+-----------+-----------+------------+
| calculate-tee-times | yes | yes | yes | n/a | n/a |
+-----------------------------+----------------------------+-----------+-----------+-----------+------------+
| client-class | n/a | yes | yes | yes | yes |
diff --git a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
index 586ead2042..e8cb29565b 100644
--- a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
+++ b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
@@ -1766,7 +1766,7 @@ TEST_F(Dhcpv4SrvTest, DiscoverCache) {
ASSERT_TRUE(l);
// Check that preferred, valid and cltt really set.
- // Constructed lease looks as if it was assigned 10 seconds ago
+ // Constructed lease looks as if it was assigned 100 seconds ago
EXPECT_EQ(l->valid_lft_, temp_valid);
EXPECT_EQ(l->cltt_, temp_timestamp);
@@ -2273,7 +2273,7 @@ TEST_F(Dhcpv4SrvTest, RenewCache) {
ASSERT_TRUE(l);
// Check that preferred, valid and cltt really set.
- // Constructed lease looks as if it was assigned 10 seconds ago
+ // Constructed lease looks as if it was assigned 100 seconds ago
EXPECT_EQ(l->valid_lft_, temp_valid);
EXPECT_EQ(l->cltt_, temp_timestamp);
diff --git a/src/bin/dhcp4/tests/hooks_unittest.cc b/src/bin/dhcp4/tests/hooks_unittest.cc
index 3ac2411840..3f1141b5e4 100644
--- a/src/bin/dhcp4/tests/hooks_unittest.cc
+++ b/src/bin/dhcp4/tests/hooks_unittest.cc
@@ -2040,7 +2040,7 @@ TEST_F(HooksDhcpv4SrvTest, leases4CommittedRequest) {
}
// This test verifies that the callout installed on the leases4_committed hook
-// point is executed as a result of DHCPREQUEST message sent to reuse an
+// point is executed as a result of DHCPREQUEST message sent to reuse
// an existing lease.
TEST_F(HooksDhcpv4SrvTest, leases4CommittedCache) {
IfaceMgrTestConfig test_config(true);
@@ -2098,7 +2098,7 @@ TEST_F(HooksDhcpv4SrvTest, leases4CommittedCache) {
// Check that the callback called is indeed the one we installed
EXPECT_EQ("leases4_committed", callback_name_);
- // Renewed lease should not be present because it was renewed.
+ // Renewed lease should not be present because it was reused.
EXPECT_FALSE(callback_lease4_);
// Deleted lease must not be present, because it renews the same address.
diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc
index 5ddfe3f6a2..13d4040f60 100644
--- a/src/bin/dhcp6/dhcp6_srv.cc
+++ b/src/bin/dhcp6/dhcp6_srv.cc
@@ -2344,11 +2344,7 @@ Dhcpv6Srv::extendIA_NA(const Pkt6Ptr& query, const Pkt6Ptr& answer,
uint32_t min_preferred_lft = std::numeric_limits<uint32_t>::max();
// For all leases we have now, add the IAADDR with non-zero lifetimes.
- for (Lease6Collection::const_iterator l = leases.begin(); l != leases.end(); ++l) {
- Option6IAAddrPtr iaaddr(new Option6IAAddr(D6O_IAADDR,
- (*l)->addr_, (*l)->preferred_lft_, (*l)->valid_lft_));
- ia_rsp->addOption(iaaddr);
-
+ for (Lease6Collection::iterator l = leases.begin(); l != leases.end(); ++l) {
if ((*l)->remaining_valid_lft_ == 0) {
LOG_INFO(lease6_logger, DHCP6_LEASE_RENEW)
.arg(query->getLabel())
@@ -2364,6 +2360,10 @@ Dhcpv6Srv::extendIA_NA(const Pkt6Ptr& query, const Pkt6Ptr& answer,
.arg(Lease::lifetimeToText((*l)->valid_lft_));
}
+ Option6IAAddrPtr iaaddr(new Option6IAAddr(D6O_IAADDR,
+ (*l)->addr_, (*l)->preferred_lft_, (*l)->valid_lft_));
+ ia_rsp->addOption(iaaddr);
+
// Check for new minimum lease time
if (((*l)->preferred_lft_ > 0) && (min_preferred_lft > (*l)->preferred_lft_)) {
min_preferred_lft = (*l)->preferred_lft_;
@@ -2376,8 +2376,8 @@ Dhcpv6Srv::extendIA_NA(const Pkt6Ptr& query, const Pkt6Ptr& answer,
}
// For the leases that we just retired, send the addresses with 0 lifetimes.
- for (Lease6Collection::const_iterator l = ctx.currentIA().old_leases_.begin();
- l != ctx.currentIA().old_leases_.end(); ++l) {
+ for (Lease6Collection::iterator l = ctx.currentIA().old_leases_.begin();
+ l != ctx.currentIA().old_leases_.end(); ++l) {
// Send an address with zero lifetimes only when this lease belonged to
// this client. Do not send it when we're reusing an old lease that belonged
@@ -2532,7 +2532,23 @@ Dhcpv6Srv::extendIA_PD(const Pkt6Ptr& query,
// for calculating T1 and T2.
uint32_t min_preferred_lft = std::numeric_limits<uint32_t>::max();
- for (Lease6Collection::const_iterator l = leases.begin(); l != leases.end(); ++l) {
+ for (Lease6Collection::iterator l = leases.begin(); l != leases.end(); ++l) {
+ if ((*l)->remaining_valid_lft_ == 0) {
+ LOG_INFO(lease6_logger, DHCP6_PD_LEASE_RENEW)
+ .arg(query->getLabel())
+ .arg((*l)->addr_.toText())
+ .arg(static_cast<int>((*l)->prefixlen_))
+ .arg(ia->getIAID());
+ } else {
+ (*l)->valid_lft_ = (*l)->remaining_valid_lft_;
+ (*l)->preferred_lft_ = (*l)->remaining_preferred_lft_;
+ LOG_INFO(lease6_logger, DHCP6_PD_LEASE_REUSE)
+ .arg(query->getLabel())
+ .arg((*l)->addr_.toText())
+ .arg(static_cast<int>((*l)->prefixlen_))
+ .arg(ia->getIAID())
+ .arg(Lease::lifetimeToText((*l)->valid_lft_));
+ }
Option6IAPrefixPtr prf(new Option6IAPrefix(D6O_IAPREFIX,
(*l)->addr_, (*l)->prefixlen_,
@@ -2553,23 +2569,6 @@ Dhcpv6Srv::extendIA_PD(const Pkt6Ptr& query,
}
}
- if ((*l)->remaining_valid_lft_ == 0) {
- LOG_INFO(lease6_logger, DHCP6_PD_LEASE_RENEW)
- .arg(query->getLabel())
- .arg((*l)->addr_.toText())
- .arg(static_cast<int>((*l)->prefixlen_))
- .arg(ia->getIAID());
- } else {
- (*l)->valid_lft_ = (*l)->remaining_valid_lft_;
- (*l)->preferred_lft_ = (*l)->remaining_preferred_lft_;
- LOG_INFO(lease6_logger, DHCP6_PD_LEASE_REUSE)
- .arg(query->getLabel())
- .arg((*l)->addr_.toText())
- .arg(static_cast<int>((*l)->prefixlen_))
- .arg(ia->getIAID())
- .arg(Lease::lifetimeToText((*l)->valid_lft_));
- }
-
// Check for new minimum lease time
if (((*l)->preferred_lft_ > 0) && ((*l)->preferred_lft_ < min_preferred_lft)) {
min_preferred_lft = (*l)->preferred_lft_;
@@ -2582,8 +2581,8 @@ Dhcpv6Srv::extendIA_PD(const Pkt6Ptr& query,
}
/// For the leases that we just retired, send the prefixes with 0 lifetimes.
- for (Lease6Collection::const_iterator l = ctx.currentIA().old_leases_.begin();
- l != ctx.currentIA().old_leases_.end(); ++l) {
+ for (Lease6Collection::iterator l = ctx.currentIA().old_leases_.begin();
+ l != ctx.currentIA().old_leases_.end(); ++l) {
// Send a prefix with zero lifetimes only when this lease belonged to
// this client. Do not send it when we're reusing an old lease that belonged
diff --git a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
index e516279cc0..5284cfcf93 100644
--- a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
+++ b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
@@ -997,6 +997,147 @@ TEST_F(Dhcpv6SrvTest, ManySolicits) {
cout << "Offered address to client3=" << addr3->getAddress() << endl;
}
+// This test verifies that incoming SOLICIT can't reuse an existing lease
+// and simply return it, i.e. fake allocation ignores the cache feature.
+TEST_F(Dhcpv6SrvTest, SolicitCache) {
+ NakedDhcpv6Srv srv(0);
+
+ // Enable lease reuse.
+ subnet_->setCacheThreshold(.1);
+
+ const IOAddress addr("2001:db8:1:1::cafe:babe");
+ const uint32_t iaid = 234;
+ const uint32_t pref = subnet_->getPreferred();
+ const uint32_t valid = subnet_->getValid();
+ const int delta = 100;
+ const time_t timestamp = time(NULL) - delta;
+
+ // Generate client-id also duid_.
+ OptionPtr clientid = generateClientId();
+
+ // Check that the address we are about to use is indeed in pool.
+ ASSERT_TRUE(subnet_->inPool(Lease::TYPE_NA, addr));
+
+ Lease6Ptr used(new Lease6(Lease::TYPE_NA, addr, duid_, iaid, pref, valid,
+ subnet_->getID()));
+ used->cltt_ = timestamp;
+ ASSERT_TRUE(LeaseMgrFactory::instance().addLease(used));
+
+ // Check that the lease is really in the database.
+ Lease6Ptr l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, addr);
+ ASSERT_TRUE(l);
+
+ // Check that preferred, valid and cltt really set.
+ // Constructed lease looks as if it was assigned 100 seconds ago.
+ EXPECT_EQ(l->preferred_lft_, pref);
+ EXPECT_EQ(l->valid_lft_, valid);
+ EXPECT_EQ(l->cltt_, timestamp);
+
+ // Let's create a SOLICIT.
+ Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
+ sol->setRemoteAddr(IOAddress("fe80::abcd"));
+ sol->setIface("eth0");
+ sol->setIndex(ETH0_INDEX);
+ sol->addOption(generateIA(D6O_IA_NA, 234, 1500, 3000));
+ sol->addOption(clientid);
+
+ // Pass it to the server and get an advertise
+ AllocEngine::ClientContext6 ctx;
+ bool drop = false;
+ srv.initContext(sol, ctx, drop);
+ ASSERT_FALSE(drop);
+ Pkt6Ptr reply = srv.processSolicit(ctx);
+
+ // check if we get response at all
+ checkResponse(reply, DHCPV6_ADVERTISE, 1234);
+
+ // check that IA_NA was returned and that there's an address included
+ boost::shared_ptr<Option6IAAddr> iaaddr =
+ checkIA_NA(reply, 234, subnet_->getT1(), subnet_->getT2());
+ ASSERT_TRUE(iaaddr);
+
+ // Check the address.
+ EXPECT_EQ(addr, iaaddr->getAddress());
+ EXPECT_EQ(pref, iaaddr->getPreferred());
+ EXPECT_EQ(valid, iaaddr->getValid());
+
+ // check DUIDs
+ checkServerId(reply, srv.getServerID());
+ checkClientId(reply, clientid);
+}
+
+// This test verifies that incoming SOLICIT can't reuse an existing lease
+// and simply return it, i.e. fake allocation ignores the cache feature.
+// Prefix variant.
+TEST_F(Dhcpv6SrvTest, pdSolicitCache) {
+ NakedDhcpv6Srv srv(0);
+
+ // Enable lease reuse.
+ subnet_->setCacheThreshold(.1);
+
+ const IOAddress prefix("2001:db8:1:2::");
+ const uint8_t prefixlen = pd_pool_->getLength();
+ const uint32_t iaid = 234;
+ const uint32_t pref = subnet_->getPreferred();
+ const uint32_t valid = subnet_->getValid();
+ const int delta = 100;
+ const time_t timestamp = time(NULL) - delta;
+
+ // Generate client-id also duid_.
+ OptionPtr clientid = generateClientId();
+
+ // Check that the prefix we are about to use is indeed in pool.
+ ASSERT_TRUE(subnet_->inPool(Lease::TYPE_PD, prefix));
+
+ Lease6Ptr used(new Lease6(Lease::TYPE_PD, prefix, duid_, iaid, pref, valid,
+ subnet_->getID(), HWAddrPtr(), prefixlen));
+ used->cltt_ = timestamp;
+ ASSERT_TRUE(LeaseMgrFactory::instance().addLease(used));
+
+ // Check that the lease is really in the database.
+ Lease6Ptr l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_PD, prefix);
+ ASSERT_TRUE(l);
+
+ // Check that preferred, valid and cltt really set.
+ // Constructed lease looks as if it was assigned 100 seconds ago.
+ EXPECT_EQ(l->preferred_lft_, pref);
+ EXPECT_EQ(l->valid_lft_, valid);
+ EXPECT_EQ(l->cltt_, timestamp);
+
+ // Let's create a SOLICIT.
+ Pkt6Ptr sol = Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234));
+ sol->setRemoteAddr(IOAddress("fe80::abcd"));
+ sol->setIface("eth0");
+ sol->setIndex(ETH0_INDEX);
+ sol->addOption(generateIA(D6O_IA_PD, 234, 1500, 3000));
+ sol->addOption(clientid);
+
+ // Pass it to the server and get an advertise
+ AllocEngine::ClientContext6 ctx;
+ bool drop = false;
+ srv.initContext(sol, ctx, drop);
+ ASSERT_FALSE(drop);
+ Pkt6Ptr reply = srv.processSolicit(ctx);
+
+ // check if we get response at all
+ checkResponse(reply, DHCPV6_ADVERTISE, 1234);
+
+ // check that IA_PD was returned and that there's a prefix included
+ boost::shared_ptr<Option6IAPrefix> iapref =
+ checkIA_PD(reply, 234, subnet_->getT1(), subnet_->getT2());
+ ASSERT_TRUE(iapref);
+
+ // Check the prefix.
+ EXPECT_EQ(prefix, iapref->getAddress());
+ EXPECT_EQ(prefixlen, iapref->getLength());
+ EXPECT_EQ(pref, iapref->getPreferred());
+ EXPECT_EQ(valid, iapref->getValid());
+
+ // check DUIDs
+ checkServerId(reply, srv.getServerID());
+ checkClientId(reply, clientid);
+}
+
// This test verifies that incoming REQUEST can be handled properly, that a
// REPLY is generated, that the response has an address and that address
// really belongs to the configured pool.
@@ -1222,6 +1363,147 @@ TEST_F(Dhcpv6SrvTest, ManyRequests) {
cout << "Assigned address to client3=" << addr3->getAddress() << endl;
}
+// This test verifies that incoming REQUEST can reuse an existing lease.
+TEST_F(Dhcpv6SrvTest, RequestCache) {
+ NakedDhcpv6Srv srv(0);
+
+ // Enable lease reuse.
+ subnet_->setCacheThreshold(.1);
+
+ const IOAddress addr("2001:db8:1:1::cafe:babe");
+ const uint32_t iaid = 234;
+ const uint32_t pref = subnet_->getPreferred();
+ const uint32_t valid = subnet_->getValid();
+ const int delta = 100;
+ const time_t timestamp = time(NULL) - delta;
+
+ // Generate client-id also duid_.
+ OptionPtr clientid = generateClientId();
+
+ // Check that the address we are about to use is indeed in pool.
+ ASSERT_TRUE(subnet_->inPool(Lease::TYPE_NA, addr));
+
+ Lease6Ptr used(new Lease6(Lease::TYPE_NA, addr, duid_, iaid, pref, valid,
+ subnet_->getID()));
+ used->cltt_ = timestamp;
+ ASSERT_TRUE(LeaseMgrFactory::instance().addLease(used));
+
+ // Check that the lease is really in the database.
+ Lease6Ptr l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, addr);
+ ASSERT_TRUE(l);
+
+ // Check that preferred, valid and cltt really set.
+ // Constructed lease looks as if it was assigned 100 seconds ago.
+ EXPECT_EQ(l->preferred_lft_, pref);
+ EXPECT_EQ(l->valid_lft_, valid);
+ EXPECT_EQ(l->cltt_, timestamp);
+
+ // Let's create a REQUEST.
+ Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234));
+ req->setRemoteAddr(IOAddress("fe80::abcd"));
+ req->setIface("eth0");
+ req->setIndex(ETH0_INDEX);
+ req->addOption(createIA(Lease::TYPE_NA, addr, 128, iaid));
+ req->addOption(clientid);
+ req->addOption(srv.getServerID());
+
+ // Pass it to the server and get an advertise
+ AllocEngine::ClientContext6 ctx;
+ bool drop = false;
+ srv.initContext(req, ctx, drop);
+ ASSERT_FALSE(drop);
+ Pkt6Ptr reply = srv.processRequest(ctx);
+
+ // check if we get response at all
+ checkResponse(reply, DHCPV6_REPLY, 1234);
+
+ // check that IA_NA was returned and that there's an address included
+ boost::shared_ptr<Option6IAAddr> iaaddr =
+ checkIA_NA(reply, 234, subnet_->getT1(), subnet_->getT2());
+ ASSERT_TRUE(iaaddr);
+
+ // Check the address.
+ EXPECT_EQ(addr, iaaddr->getAddress());
+ EXPECT_EQ(pref - delta, iaaddr->getPreferred());
+ EXPECT_EQ(valid - delta, iaaddr->getValid());
+
+ // check DUIDs
+ checkServerId(reply, srv.getServerID());
+ checkClientId(reply, clientid);
+}
+
+// This test verifies that incoming REQUEST can reuse an existing lease.
+// Prefix variant.
+TEST_F(Dhcpv6SrvTest, pdRequestCache) {
+ NakedDhcpv6Srv srv(0);
+
+ // Enable lease reuse.
+ subnet_->setCacheThreshold(.1);
+
+ const IOAddress prefix("2001:db8:1:2::");
+ const uint8_t prefixlen = pd_pool_->getLength();
+ const uint32_t iaid = 234;
+ const uint32_t pref = subnet_->getPreferred();
+ const uint32_t valid = subnet_->getValid();
+ const int delta = 100;
+ const time_t timestamp = time(NULL) - delta;
+
+ // Generate client-id also duid_.
+ OptionPtr clientid = generateClientId();
+
+ // Check that the prefix we are about to use is indeed in pool.
+ ASSERT_TRUE(subnet_->inPool(Lease::TYPE_PD, prefix));
+
+ Lease6Ptr used(new Lease6(Lease::TYPE_PD, prefix, duid_, iaid, pref, valid,
+ subnet_->getID(), HWAddrPtr(), prefixlen));
+ used->cltt_ = timestamp;
+ ASSERT_TRUE(LeaseMgrFactory::instance().addLease(used));
+
+ // Check that the lease is really in the database.
+ Lease6Ptr l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_PD, prefix);
+ ASSERT_TRUE(l);
+
+ // Check that preferred, valid and cltt really set.
+ // Constructed lease looks as if it was assigned 100 seconds ago.
+ EXPECT_EQ(l->preferred_lft_, pref);
+ EXPECT_EQ(l->valid_lft_, valid);
+ EXPECT_EQ(l->cltt_, timestamp);
+
+ // Let's create a REQUEST.
+ Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234));
+ req->setRemoteAddr(IOAddress("fe80::abcd"));
+ req->setIface("eth0");
+ req->setIndex(ETH0_INDEX);
+ req->addOption(createIA(Lease::TYPE_PD, prefix, prefixlen, iaid));
+ req->addOption(clientid);
+ req->addOption(srv.getServerID());
+
+ // Pass it to the server and get an advertise
+ AllocEngine::ClientContext6 ctx;
+ bool drop = false;
+ srv.initContext(req, ctx, drop);
+ ASSERT_FALSE(drop);
+ Pkt6Ptr reply = srv.processRequest(ctx);
+
+ // check if we get response at all
+ checkResponse(reply, DHCPV6_REPLY, 1234);
+
+ // check that IA_PD was returned and that there's a prefix included
+ boost::shared_ptr<Option6IAPrefix> iapref =
+ checkIA_PD(reply, 234, subnet_->getT1(), subnet_->getT2());
+ ASSERT_TRUE(iapref);
+
+ // Check the prefix.
+ EXPECT_EQ(prefix, iapref->getAddress());
+ EXPECT_EQ(prefixlen, iapref->getLength());
+ EXPECT_EQ(pref - delta, iapref->getPreferred());
+ EXPECT_EQ(valid - delta, iapref->getValid());
+
+ // check DUIDs
+ checkServerId(reply, srv.getServerID());
+ checkClientId(reply, clientid);
+}
+
// This test verifies that incoming (positive) RENEW can be handled properly, that a
// REPLY is generated, that the response has an address and that address
// really belongs to the configured pool and that lease is actually renewed.
@@ -1365,6 +1647,147 @@ TEST_F(Dhcpv6SrvTest, maxLifetimeReuseExpired) {
true, true, 5000, 6000, 4000, 5000);
}
+// This test verifies that incoming RENEW can reuse an existing lease.
+TEST_F(Dhcpv6SrvTest, RenewCache) {
+ NakedDhcpv6Srv srv(0);
+
+ // Enable lease reuse.
+ subnet_->setCacheThreshold(.1);
+
+ const IOAddress addr("2001:db8:1:1::cafe:babe");
+ const uint32_t iaid = 234;
+ const uint32_t pref = subnet_->getPreferred();
+ const uint32_t valid = subnet_->getValid();
+ const int delta = 100;
+ const time_t timestamp = time(NULL) - delta;
+
+ // Generate client-id also duid_.
+ OptionPtr clientid = generateClientId();
+
+ // Check that the address we are about to use is indeed in pool.
+ ASSERT_TRUE(subnet_->inPool(Lease::TYPE_NA, addr));
+
+ Lease6Ptr used(new Lease6(Lease::TYPE_NA, addr, duid_, iaid, pref, valid,
+ subnet_->getID()));
+ used->cltt_ = timestamp;
+ ASSERT_TRUE(LeaseMgrFactory::instance().addLease(used));
+
+ // Check that the lease is really in the database.
+ Lease6Ptr l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, addr);
+ ASSERT_TRUE(l);
+
+ // Check that preferred, valid and cltt really set.
+ // Constructed lease looks as if it was assigned 100 seconds ago.
+ EXPECT_EQ(l->preferred_lft_, pref);
+ EXPECT_EQ(l->valid_lft_, valid);
+ EXPECT_EQ(l->cltt_, timestamp);
+
+ // Let's create a RENEW.
+ Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RENEW, 1234));
+ req->setRemoteAddr(IOAddress("fe80::abcd"));
+ req->setIface("eth0");
+ req->setIndex(ETH0_INDEX);
+ req->addOption(createIA(Lease::TYPE_NA, addr, 128, iaid));
+ req->addOption(clientid);
+ req->addOption(srv.getServerID());
+
+ // Pass it to the server and get an advertise
+ AllocEngine::ClientContext6 ctx;
+ bool drop = false;
+ srv.initContext(req, ctx, drop);
+ ASSERT_FALSE(drop);
+ Pkt6Ptr reply = srv.processRenew(ctx);
+
+ // check if we get response at all
+ checkResponse(reply, DHCPV6_REPLY, 1234);
+
+ // check that IA_NA was returned and that there's an address included
+ boost::shared_ptr<Option6IAAddr> iaaddr =
+ checkIA_NA(reply, 234, subnet_->getT1(), subnet_->getT2());
+ ASSERT_TRUE(iaaddr);
+
+ // Check the address.
+ EXPECT_EQ(addr, iaaddr->getAddress());
+ EXPECT_EQ(pref - delta, iaaddr->getPreferred());
+ EXPECT_EQ(valid - delta, iaaddr->getValid());
+
+ // check DUIDs
+ checkServerId(reply, srv.getServerID());
+ checkClientId(reply, clientid);
+}
+
+// This test verifies that incoming RENEW can reuse an existing lease.
+// Prefix variant.
+TEST_F(Dhcpv6SrvTest, pdRenewCache) {
+ NakedDhcpv6Srv srv(0);
+
+ // Enable lease reuse.
+ subnet_->setCacheThreshold(.1);
+
+ const IOAddress prefix("2001:db8:1:2::");
+ const uint8_t prefixlen = pd_pool_->getLength();
+ const uint32_t iaid = 234;
+ const uint32_t pref = subnet_->getPreferred();
+ const uint32_t valid = subnet_->getValid();
+ const int delta = 100;
+ const time_t timestamp = time(NULL) - delta;
+
+ // Generate client-id also duid_.
+ OptionPtr clientid = generateClientId();
+
+ // Check that the prefix we are about to use is indeed in pool.
+ ASSERT_TRUE(subnet_->inPool(Lease::TYPE_PD, prefix));
+
+ Lease6Ptr used(new Lease6(Lease::TYPE_PD, prefix, duid_, iaid, pref, valid,
+ subnet_->getID(), HWAddrPtr(), prefixlen));
+ used->cltt_ = timestamp;
+ ASSERT_TRUE(LeaseMgrFactory::instance().addLease(used));
+
+ // Check that the lease is really in the database.
+ Lease6Ptr l = LeaseMgrFactory::instance().getLease6(Lease::TYPE_PD, prefix);
+ ASSERT_TRUE(l);
+
+ // Check that preferred, valid and cltt really set.
+ // Constructed lease looks as if it was assigned 100 seconds ago.
+ EXPECT_EQ(l->preferred_lft_, pref);
+ EXPECT_EQ(l->valid_lft_, valid);
+ EXPECT_EQ(l->cltt_, timestamp);
+
+ // Let's create a RENEW.
+ Pkt6Ptr req = Pkt6Ptr(new Pkt6(DHCPV6_RENEW, 1234));
+ req->setRemoteAddr(IOAddress("fe80::abcd"));
+ req->setIface("eth0");
+ req->setIndex(ETH0_INDEX);
+ req->addOption(createIA(Lease::TYPE_PD, prefix, prefixlen, iaid));
+ req->addOption(clientid);
+ req->addOption(srv.getServerID());
+
+ // Pass it to the server and get an advertise
+ AllocEngine::ClientContext6 ctx;
+ bool drop = false;
+ srv.initContext(req, ctx, drop);
+ ASSERT_FALSE(drop);
+ Pkt6Ptr reply = srv.processRenew(ctx);
+
+ // check if we get response at all
+ checkResponse(reply, DHCPV6_REPLY, 1234);
+
+ // check that IA_PD was returned and that there's a prefix included
+ boost::shared_ptr<Option6IAPrefix> iapref =
+ checkIA_PD(reply, 234, subnet_->getT1(), subnet_->getT2());
+ ASSERT_TRUE(iapref);
+
+ // Check the prefix.
+ EXPECT_EQ(prefix, iapref->getAddress());
+ EXPECT_EQ(prefixlen, iapref->getLength());
+ EXPECT_EQ(pref - delta, iapref->getPreferred());
+ EXPECT_EQ(valid - delta, iapref->getValid());
+
+ // check DUIDs
+ checkServerId(reply, srv.getServerID());
+ checkClientId(reply, clientid);
+}
+
// This test verifies that incoming (positive) RELEASE with address can be
// handled properly, that a REPLY is generated, that the response has status
// code and that the lease is indeed removed from the database.
diff --git a/src/bin/dhcp6/tests/hooks_unittest.cc b/src/bin/dhcp6/tests/hooks_unittest.cc
index 2629fcda68..a3e0a291fc 100644
--- a/src/bin/dhcp6/tests/hooks_unittest.cc
+++ b/src/bin/dhcp6/tests/hooks_unittest.cc
@@ -2474,6 +2474,188 @@ TEST_F(HooksDhcpv6SrvTest, leases6CommittedRequestPrefix) {
checkCalloutHandleReset(client.getContext().query_);
}
+// This test verifies that the callout installed on the leases6_committed hook
+// point is executed as a result of REQUEST message sent to reuse an
+// existing lease.
+TEST_F(HooksDhcpv6SrvTest, leases6CommittedCache) {
+ IfaceMgrTestConfig test_config(true);
+
+ string config = "{ \"interfaces-config\": {"
+ " \"interfaces\": [ \"*\" ]"
+ "},"
+ "\"preferred-lifetime\": 3000,"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, "
+ "\"subnet6\": [ { "
+ " \"pools\": [ { \"pool\": \"2001:db8:1::/64\" } ],"
+ " \"subnet\": \"2001:db8:1::/48\", "
+ " \"interface\": \"eth1\", "
+ " \"cache-threshold\": .25 "
+ " } ],"
+ "\"valid-lifetime\": 4000 }";
+
+ Dhcp6Client client;
+ client.setInterface("eth1");
+ client.requestAddress(0xabca, IOAddress("2001:db8:1::28"));
+
+ ASSERT_NO_THROW(configure(config, *client.getServer()));
+
+ ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+ "leases6_committed", leases6_committed_callout));
+
+ ASSERT_NO_THROW(client.doSARR());
+
+ // Make sure that we received a response
+ ASSERT_TRUE(client.getContext().response_);
+
+ // Check that the callback called is indeed the one we installed
+ EXPECT_EQ("leases6_committed", callback_name_);
+
+ // Check if all expected parameters were really received
+ vector<string> expected_argument_names;
+ expected_argument_names.push_back("query6");
+ expected_argument_names.push_back("deleted_leases6");
+ expected_argument_names.push_back("leases6");
+
+ sort(expected_argument_names.begin(), expected_argument_names.end());
+ EXPECT_TRUE(callback_argument_names_ == expected_argument_names);
+
+ // Newly allocated lease should be returned.
+ ASSERT_TRUE(callback_new_leases6_);
+ ASSERT_EQ(1, callback_new_leases6_->size());
+ Lease6Ptr lease = callback_new_leases6_->at(0);
+ ASSERT_TRUE(lease);
+ EXPECT_EQ("2001:db8:1::28", lease->addr_.toText());
+
+ // Deleted lease must not be present, because it is a new allocation.
+ ASSERT_TRUE(callback_deleted_leases6_);
+ EXPECT_TRUE(callback_deleted_leases6_->empty());
+
+ // Pkt passed to a callout must be configured to copy retrieved options.
+ EXPECT_TRUE(callback_qry_options_copy_);
+
+ // Check if the callout handle state was reset after the callout.
+ checkCalloutHandleReset(client.getContext().query_);
+
+ resetCalloutBuffers();
+
+ // Request the lease and make sure that the callout has been executed.
+ ASSERT_NO_THROW(client.doRequest());
+
+ // Make sure that we received a response
+ ASSERT_TRUE(client.getContext().response_);
+
+ // Check that the callback called is indeed the one we installed
+ EXPECT_EQ("leases6_committed", callback_name_);
+
+ // Requested lease should not be present, because it is reused.
+ ASSERT_TRUE(callback_new_leases6_);
+ EXPECT_TRUE(callback_new_leases6_->empty());
+
+ // Deleted lease must not be present, because it is renewed.
+ ASSERT_TRUE(callback_deleted_leases6_);
+ EXPECT_TRUE(callback_deleted_leases6_->empty());
+
+ // Pkt passed to a callout must be configured to copy retrieved options.
+ EXPECT_TRUE(callback_qry_options_copy_);
+
+ // Check if the callout handle state was reset after the callout.
+ checkCalloutHandleReset(client.getContext().query_);
+}
+
+// This test verifies that the callout installed on the leases6_committed hook
+// point is executed as a result of REQUEST message sent to reuse an
+// existing lease. Prefix variant.
+TEST_F(HooksDhcpv6SrvTest, leases6CommittedCachePrefix) {
+ IfaceMgrTestConfig test_config(true);
+
+ string config = "{ \"interfaces-config\": {"
+ " \"interfaces\": [ \"*\" ]"
+ "},"
+ "\"preferred-lifetime\": 3000,"
+ "\"rebind-timer\": 2000, "
+ "\"renew-timer\": 1000, "
+ "\"subnet6\": [ { "
+ " \"pd-pools\": [ {"
+ " \"prefix\": \"2001:db8:1::\", "
+ " \"prefix-len\": 56, "
+ " \"delegated-len\": 64 } ], "
+ " \"subnet\": \"2001:db8:1::/48\", "
+ " \"interface\": \"eth1\", "
+ " \"cache-threshold\": .25 "
+ " } ],"
+ "\"valid-lifetime\": 4000 }";
+
+ Dhcp6Client client;
+ client.setInterface("eth1");
+ client.requestPrefix(0xabca, 64, IOAddress("2001:db8:1:28::"));
+
+ ASSERT_NO_THROW(configure(config, *client.getServer()));
+
+ ASSERT_NO_THROW(HooksManager::preCalloutsLibraryHandle().registerCallout(
+ "leases6_committed", leases6_committed_callout));
+
+ ASSERT_NO_THROW(client.doSARR());
+
+ // Make sure that we received a response
+ ASSERT_TRUE(client.getContext().response_);
+
+ // Check that the callback called is indeed the one we installed
+ EXPECT_EQ("leases6_committed", callback_name_);
+
+ // Check if all expected parameters were really received
+ vector<string> expected_argument_names;
+ expected_argument_names.push_back("query6");
+ expected_argument_names.push_back("deleted_leases6");
+ expected_argument_names.push_back("leases6");
+
+ sort(expected_argument_names.begin(), expected_argument_names.end());
+ EXPECT_TRUE(callback_argument_names_ == expected_argument_names);
+
+ // Newly allocated lease should be returned.
+ ASSERT_TRUE(callback_new_leases6_);
+ ASSERT_EQ(1, callback_new_leases6_->size());
+ Lease6Ptr lease = callback_new_leases6_->at(0);
+ ASSERT_TRUE(lease);
+ EXPECT_EQ("2001:db8:1:28::", lease->addr_.toText());
+ EXPECT_EQ(64, lease->prefixlen_);
+
+ // Deleted lease must not be present, because it is a new allocation.
+ ASSERT_TRUE(callback_deleted_leases6_);
+ EXPECT_TRUE(callback_deleted_leases6_->empty());
+
+ // Pkt passed to a callout must be configured to copy retrieved options.
+ EXPECT_TRUE(callback_qry_options_copy_);
+
+ // Check if the callout handle state was reset after the callout.
+ checkCalloutHandleReset(client.getContext().query_);
+
+ resetCalloutBuffers();
+
+ // Request the lease and make sure that the callout has been executed.
+ ASSERT_NO_THROW(client.doRequest());
+
+ // Make sure that we received a response
+ ASSERT_TRUE(client.getContext().response_);
+
+ // Check that the callback called is indeed the one we installed
+ EXPECT_EQ("leases6_committed", callback_name_);
+
+ // Requested lease should not be present, because it is reused.
+ ASSERT_TRUE(callback_new_leases6_);
+ EXPECT_TRUE(callback_new_leases6_->empty());
+
+ // Deleted lease must not be present, because it is renewed.
+ ASSERT_TRUE(callback_deleted_leases6_);
+ EXPECT_TRUE(callback_deleted_leases6_->empty());
+
+ // Pkt passed to a callout must be configured to copy retrieved options.
+ EXPECT_TRUE(callback_qry_options_copy_);
+
+ // Check if the callout handle state was reset after the callout.
+ checkCalloutHandleReset(client.getContext().query_);
+}
+
// This test verifies that it is possible to park a packet as a result of
// the leases6_committed callouts.
TEST_F(HooksDhcpv6SrvTest, leases6CommittedParkRequests) {
diff --git a/src/lib/dhcpsrv/libdhcpsrv.dox b/src/lib/dhcpsrv/libdhcpsrv.dox
index 01872fd9d2..b5453614c2 100644
--- a/src/lib/dhcpsrv/libdhcpsrv.dox
+++ b/src/lib/dhcpsrv/libdhcpsrv.dox
@@ -272,6 +272,14 @@ was made. The client will be offered/allocated a reserved address
the next time it retries sending a DHCPDISCOVER/DHCPREQUEST message to
the server.
+@subsection allocEngineReuse Allocation Engine Cache
+
+The allocation engine provides a cache-like feature: when a suitable
+lease already exists for a client if its age is small enough compared
+to the valid lifetime (threshold parameter) and below a configured maximum
+(max age parameter) the lease can be reused. A reusable lease is marked
+by a not zero remaining_valid_lft_ value.
+
@section timerManager Timer Manager
The @c isc::dhcp::TimerMgr is a singleton class used throughout the
diff --git a/src/lib/dhcpsrv/tests/alloc_engine4_unittest.cc b/src/lib/dhcpsrv/tests/alloc_engine4_unittest.cc
index f557f17d47..9f3ce773d6 100644
--- a/src/lib/dhcpsrv/tests/alloc_engine4_unittest.cc
+++ b/src/lib/dhcpsrv/tests/alloc_engine4_unittest.cc
@@ -4084,6 +4084,9 @@ TEST_F(AllocEngine4Test, requestCacheBadMaxAge4) {
// Set valid lifetime to 500.
subnet_->setValid(500);
+ // Set the threshold to 25%.
+ subnet_->setCacheThreshold(.25);
+
// Set the max age to 50.
subnet_->setCacheMaxAge(50);
@@ -4202,6 +4205,9 @@ TEST_F(AllocEngine4Test, discoverCacheRevDDNS4) {
// Set the threshold to 10%.
subnet_->setCacheThreshold(.1);
+ // Set the max age to 200.
+ subnet_->setCacheMaxAge(200);
+
IOAddress addr("192.0.2.105");
time_t now = time(NULL) - 100; // Allocated 100 seconds ago.
Lease4Ptr lease(new Lease4(addr, hwaddr_, clientid_,
diff --git a/src/lib/dhcpsrv/tests/alloc_engine6_unittest.cc b/src/lib/dhcpsrv/tests/alloc_engine6_unittest.cc
index 940c6718f1..ece87a1dc0 100644
--- a/src/lib/dhcpsrv/tests/alloc_engine6_unittest.cc
+++ b/src/lib/dhcpsrv/tests/alloc_engine6_unittest.cc
@@ -4394,8 +4394,46 @@ TEST_F(AllocEngine6ExtendedInfoTest, reuseExpiredLease6) {
<< " actual: " << *(lease->getContext()) << std::endl;
}
-// V6 engine does not allocate on fake allocation / solicit so the cache
-// feature does nothing visible in this case.
+// Checks whether fake allocation does not use the cache feature.
+TEST_F(AllocEngine6Test, solicitNoCache) {
+ boost::scoped_ptr<AllocEngine> engine;
+ ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+ ASSERT_TRUE(engine);
+
+ // Set the threshold to 25%.
+ subnet_->setCacheThreshold(.25);
+
+ IOAddress addr("2001:db8:1::15");
+ time_t now = time(NULL) - 100; // Allocated 100 seconds ago.
+ Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, duid_, iaid_,
+ 300, 400, subnet_->getID()));
+ lease->cltt_ = now;
+ ASSERT_FALSE(lease->expired());
+ // Copy the lease, so as it can be compared with.
+ Lease6Ptr original_lease(new Lease6(*lease));
+ ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
+
+ // Create a context for fake allocation..
+ Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 1234));
+ AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", true,
+ query);
+ ctx.currentIA().iaid_ = iaid_;
+ ctx.currentIA().addHint(addr);
+
+ EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
+ EXPECT_EQ(addr, lease->addr_);
+ EXPECT_EQ(128, lease->prefixlen_);
+
+ // The lease was not reused.
+ EXPECT_EQ(0, lease->remaining_valid_lft_);
+
+ // Check the lease was not updated in the database.
+ Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->type_,
+ lease->addr_);
+ ASSERT_TRUE(from_mgr);
+
+ detailCompareLease(original_lease, from_mgr);
+}
// Checks whether a lease can be reused (request) using cache threshold.
TEST_F(AllocEngine6Test, requestCacheThreshold6) {
@@ -4718,6 +4756,9 @@ TEST_F(AllocEngine6Test, requestCacheBadThreshold6) {
// Set the threshold to 10%.
subnet_->setCacheThreshold(.1);
+ // Set the max age to 150.
+ subnet_->setCacheMaxAge(150);
+
IOAddress addr("2001:db8:1::15");
time_t now = time(NULL) - 100; // Allocated 100 seconds ago.
Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, duid_, iaid_,
@@ -4758,6 +4799,98 @@ TEST_F(AllocEngine6Test, renewCacheBadThreshold6) {
// Set the threshold to 10%.
subnet_->setCacheThreshold(.1);
+ // Set the max age to 150.
+ subnet_->setCacheMaxAge(150);
+
+ IOAddress prefix("2001:db8:1:2::");
+ uint8_t prefixlen = 80;
+ time_t now = time(NULL) - 100; // Allocated 100 seconds ago.
+ Lease6Ptr lease(new Lease6(Lease::TYPE_PD, prefix, duid_, iaid_,
+ 300, 400, subnet_->getID(),
+ HWAddrPtr(), prefixlen));
+ lease->cltt_ = now;
+ ASSERT_FALSE(lease->expired());
+ ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
+
+ // Create a context for renew.
+ Pkt6Ptr query(new Pkt6(DHCPV6_RENEW, 1234));
+ AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false,
+ query);
+ ctx.currentIA().type_ = Lease::TYPE_PD;
+ ctx.currentIA().iaid_ = iaid_;
+ ctx.currentIA().addHint(prefix, prefixlen);
+
+ EXPECT_NO_THROW(lease = expectOneLease(engine->renewLeases6(ctx)));
+ EXPECT_EQ(prefix, lease->addr_);
+ EXPECT_EQ(prefixlen, lease->prefixlen_);
+
+ // The lease was not reused.
+ EXPECT_EQ(0, lease->remaining_valid_lft_);
+
+ // Check the lease was updated in the database.
+ Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->type_,
+ lease->addr_);
+ ASSERT_TRUE(from_mgr);
+
+ detailCompareLease(lease, from_mgr);
+}
+
+// Checks whether a lease can't be reused (request) using too small
+// cache max age.
+TEST_F(AllocEngine6Test, requestCacheBadMaxAge6) {
+ boost::scoped_ptr<AllocEngine> engine;
+ ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+ ASSERT_TRUE(engine);
+
+ // Set the threshold to 25%.
+ subnet_->setCacheThreshold(.25);
+
+ // Set the max age to 50.
+ subnet_->setCacheMaxAge(50);
+
+ IOAddress addr("2001:db8:1::15");
+ time_t now = time(NULL) - 100; // Allocated 100 seconds ago.
+ Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, duid_, iaid_,
+ 300, 400, subnet_->getID()));
+ lease->cltt_ = now;
+ ASSERT_FALSE(lease->expired());
+ ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
+
+ // Create a context for request.
+ Pkt6Ptr query(new Pkt6(DHCPV6_REQUEST, 1234));
+ AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false,
+ query);
+ ctx.currentIA().iaid_ = iaid_;
+ ctx.currentIA().addHint(addr);
+
+ EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
+ EXPECT_EQ(addr, lease->addr_);
+ EXPECT_EQ(128, lease->prefixlen_);
+
+ // The lease was not reused.
+ EXPECT_EQ(0, lease->remaining_valid_lft_);
+
+ // Check the lease was updated in the database.
+ Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->type_,
+ lease->addr_);
+ ASSERT_TRUE(from_mgr);
+
+ detailCompareLease(lease, from_mgr);
+}
+
+// Checks whether a lease can't be reused (renew) using too small
+// cache max age.
+TEST_F(AllocEngine6Test, renewCacheBadMaxAge6) {
+ boost::scoped_ptr<AllocEngine> engine;
+ ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+ ASSERT_TRUE(engine);
+
+ // Set the threshold to 25%.
+ subnet_->setCacheThreshold(.25);
+
+ // Set the max age to 50.
+ subnet_->setCacheMaxAge(50);
+
IOAddress prefix("2001:db8:1:2::");
uint8_t prefixlen = 80;
time_t now = time(NULL) - 100; // Allocated 100 seconds ago.