diff options
author | Francis Dupont <fdupont@isc.org> | 2020-11-13 13:03:48 +0100 |
---|---|---|
committer | Francis Dupont <fdupont@isc.org> | 2021-01-11 16:05:46 +0100 |
commit | 65b7546e3246915bd6d68336e7fea3199b2ca4b0 (patch) | |
tree | 6eb9eb9d4b262c5b2779f5ecc78043105ce62ded | |
parent | [#1418] Checkpoint: todo v6 server UT and doc (diff) | |
download | kea-65b7546e3246915bd6d68336e7fea3199b2ca4b0.tar.xz kea-65b7546e3246915bd6d68336e7fea3199b2ca4b0.zip |
[#1418] Updated v6 tests and doc
-rw-r--r-- | doc/sphinx/arm/dhcp4-srv.rst | 61 | ||||
-rw-r--r-- | doc/sphinx/arm/dhcp6-srv.rst | 59 | ||||
-rw-r--r-- | src/bin/dhcp4/tests/dhcp4_srv_unittest.cc | 4 | ||||
-rw-r--r-- | src/bin/dhcp4/tests/hooks_unittest.cc | 4 | ||||
-rw-r--r-- | src/bin/dhcp6/dhcp6_srv.cc | 53 | ||||
-rw-r--r-- | src/bin/dhcp6/tests/dhcp6_srv_unittest.cc | 423 | ||||
-rw-r--r-- | src/bin/dhcp6/tests/hooks_unittest.cc | 182 | ||||
-rw-r--r-- | src/lib/dhcpsrv/libdhcpsrv.dox | 8 | ||||
-rw-r--r-- | src/lib/dhcpsrv/tests/alloc_engine4_unittest.cc | 6 | ||||
-rw-r--r-- | src/lib/dhcpsrv/tests/alloc_engine6_unittest.cc | 137 |
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. |