2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-22 01:49:48 +00:00
kea/src/lib/dhcpsrv/tests/alloc_engine6_unittest.cc
2025-08-20 17:39:16 +02:00

6682 lines
262 KiB
C++

// Copyright (C) 2015-2025 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <config.h>
#include <dhcp/pkt4.h>
#include <dhcp/pkt6.h>
#include <dhcpsrv/allocator.h>
#include <dhcpsrv/host_mgr.h>
#include <dhcpsrv/parsers/client_class_def_parser.h>
#include <dhcpsrv/testutils/alloc_engine_utils.h>
#include <dhcpsrv/testutils/test_utils.h>
#include <eval/eval_context.h>
#include <stats/stats_mgr.h>
#include <testutils/gtest_utils.h>
#include <boost/pointer_cast.hpp>
using namespace std;
using namespace isc::hooks;
using namespace isc::asiolink;
using namespace isc::stats;
using namespace isc::data;
using namespace isc::util;
namespace isc {
namespace dhcp {
namespace test {
// Convenience values to improve readibility.
const bool IN_SUBNET = true;
const bool IN_POOL = true;
// Test convenience method adding hints to IA context.
TEST(ClientContext6Test, addHint) {
AllocEngine::ClientContext6 ctx;
ctx.currentIA().addHint(IOAddress("2001:db8:1::1"));
ctx.currentIA().addHint(IOAddress("3000:1::"), 64);
ctx.currentIA().addHint(IOAddress("3001:2::"), 64, 100, 200);
ASSERT_EQ(3, ctx.currentIA().hints_.size());
EXPECT_EQ("2001:db8:1::1", ctx.currentIA().hints_[0].getAddress().toText());
EXPECT_EQ("3000:1::", ctx.currentIA().hints_[1].getAddress().toText());
EXPECT_EQ("3001:2::", ctx.currentIA().hints_[2].getAddress().toText());
EXPECT_EQ(100, ctx.currentIA().hints_[2].getPreferred());
EXPECT_EQ(200, ctx.currentIA().hints_[2].getValid());
}
// Test convenience method adding allocated prefixes and addresses to
// a context.
TEST(ClientContext6Test, addAllocatedResource) {
AllocEngine::ClientContext6 ctx;
ctx.addAllocatedResource(IOAddress("2001:db8:1::1"));
ctx.addAllocatedResource(IOAddress("3000:1::"), 64);
ASSERT_EQ(2, ctx.allocated_resources_.size());
EXPECT_TRUE(ctx.isAllocated(IOAddress("2001:db8:1::1")));
EXPECT_TRUE(ctx.isAllocated(IOAddress("3000:1::"), 64));
}
// This test checks if the v6 Allocation Engine can be instantiated, parses
// parameters string and allocators are created.
TEST_F(AllocEngine6Test, constructor) {
boost::scoped_ptr<AllocEngine> x;
ASSERT_NO_THROW(x.reset(new AllocEngine(100)));
}
// This test checks if two simple IPv6 allocations succeed and that the
// statistics is properly updated. Prior to the second allocation it
// resets the pointer to the last allocated address within the address
// pool. This causes the engine to walk over the already allocated
// address and then pick the first available address for the second
// allocation. Because the allocation engine checks the callouts next
// step status after each attempt to allocate an address, this test
// also sets this status to non-default value prior to the second
// allocation attempt, to make sure that this unexpected status will
// not interfere with the allocation.
TEST_F(AllocEngine6Test, simpleAlloc6) {
// Assigned count should be zero.
EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID()));
// Get the cumulative count of assigned addresses.
int64_t cumulative = getStatistics("cumulative-assigned-nas",
subnet_->getID());
int64_t glbl_cumulative = getStatistics("cumulative-assigned-nas");
simpleAlloc6Test(pool_, IOAddress("::"), false);
// We should have bumped the assigned counter by 1
EXPECT_TRUE(testStatistics("assigned-nas", 1, subnet_->getID()));
cumulative += 1;
EXPECT_TRUE(testStatistics("cumulative-assigned-nas",
cumulative, subnet_->getID()));
glbl_cumulative += 1;
EXPECT_TRUE(testStatistics("cumulative-assigned-nas", glbl_cumulative));
// Reset last allocated address to check that the other client will
// be refused the already allocated address and will get the one
// available.
auto allocation_state = boost::dynamic_pointer_cast<PoolIterativeAllocationState>(pool_->getAllocationState());
allocation_state->resetLastAllocated();
// Simulate another client. This client should be assigned a different
// address.
DuidPtr duid(new DUID(std::vector<uint8_t>(8, 0x84)));
simpleAlloc6Test(pool_, duid, IOAddress("::"), false);
// We should have bumped the assigned counter by 2
EXPECT_TRUE(testStatistics("assigned-nas", 2, subnet_->getID()));
cumulative += 1;
EXPECT_TRUE(testStatistics("cumulative-assigned-nas",
cumulative, subnet_->getID()));
glbl_cumulative += 1;
EXPECT_TRUE(testStatistics("cumulative-assigned-nas", glbl_cumulative));
}
// This test checks that simple allocation uses default lifetimes.
TEST_F(AllocEngine6Test, defaultAlloc6) {
simpleAlloc6Test(pool_, IOAddress("::"), 0, 0, 300, 400);
}
// This test checks that simple allocation uses specified lifetimes.
TEST_F(AllocEngine6Test, hintAlloc6) {
simpleAlloc6Test(pd_pool_, IOAddress("::"), 301, 399, 301, 399);
}
// This test checks that simple allocation uses min lifetimes.
TEST_F(AllocEngine6Test, minAlloc6) {
simpleAlloc6Test(pool_, IOAddress("::"), 100, 200, 200, 300);
}
// This test checks that simple allocation uses max lifetimes.
TEST_F(AllocEngine6Test, maxAlloc6) {
simpleAlloc6Test(pd_pool_, IOAddress("::"), 500, 600, 400, 500);
}
// This test checks if the simple PD allocation (REQUEST) can succeed
// and the stats counter is properly bumped by 1
TEST_F(AllocEngine6Test, pdSimpleAlloc6) {
// Assigned count should be zero.
EXPECT_TRUE(testStatistics("assigned-pds", 0, subnet_->getID()));
// Get the cumulative count of assigned prefixes.
int64_t cumulative = getStatistics("cumulative-assigned-pds",
subnet_->getID());
int64_t glbl_cumulative = getStatistics("cumulative-assigned-pds");
simpleAlloc6Test(pd_pool_, IOAddress("::"), false);
// We should have bumped the assigned counter by 1
EXPECT_TRUE(testStatistics("assigned-pds", 1, subnet_->getID()));
cumulative += 1;
EXPECT_TRUE(testStatistics("cumulative-assigned-pds",
cumulative, subnet_->getID()));
glbl_cumulative += 1;
EXPECT_TRUE(testStatistics("cumulative-assigned-pds", glbl_cumulative));
}
// This test checks if the fake allocation (for SOLICIT) can succeed
// and the stats counter isn't bumped
TEST_F(AllocEngine6Test, fakeAlloc6) {
// Assigned count should be zero.
EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID()));
// Get the cumulative count of assigned addresses.
int64_t cumulative = getStatistics("cumulative-assigned-nas",
subnet_->getID());
int64_t glbl_cumulative = getStatistics("cumulative-assigned-nas");
simpleAlloc6Test(pool_, IOAddress("::"), true);
// We should not have bumped the assigned counter.
EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID()));
EXPECT_TRUE(testStatistics("cumulative-assigned-nas",
cumulative, subnet_->getID()));
EXPECT_TRUE(testStatistics("cumulative-assigned-nas", glbl_cumulative));
}
// This test checks if the fake PD allocation (for SOLICIT) can succeed
// and the stats counter isn't bumped
TEST_F(AllocEngine6Test, pdFakeAlloc6) {
// Assigned count should be zero.
EXPECT_TRUE(testStatistics("assigned-pds", 0, subnet_->getID()));
// Get the cumulative count of assigned prefixes.
int64_t cumulative = getStatistics("cumulative-assigned-pds",
subnet_->getID());
int64_t glbl_cumulative = getStatistics("cumulative-assigned-pds");
simpleAlloc6Test(pd_pool_, IOAddress("::"), true);
// We should not have bumped the assigned counter
EXPECT_TRUE(testStatistics("assigned-pds", 0, subnet_->getID()));
EXPECT_TRUE(testStatistics("cumulative-assigned-pds",
cumulative, subnet_->getID()));
EXPECT_TRUE(testStatistics("cumulative-assigned-pds", glbl_cumulative));
}
// This test checks if the allocation with a hint that is valid (in range,
// in pool and free) can succeed
TEST_F(AllocEngine6Test, allocWithValidHint6) {
Lease6Ptr lease = simpleAlloc6Test(pool_, IOAddress("2001:db8:1::15"),
false);
ASSERT_TRUE(lease);
// We should get what we asked for
EXPECT_EQ("2001:db8:1::15", lease->addr_.toText());
}
// This test checks if the address allocation with a hint that is in range,
// in pool, but is currently used, can succeed
TEST_F(AllocEngine6Test, allocWithUsedHint6) {
allocWithUsedHintTest(Lease::TYPE_NA,
IOAddress("2001:db8:1::1f"), // allocate this as used
IOAddress("2001:db8:1::1f"), // request this addr
128);
}
// This test checks if the PD allocation with a hint that is in range,
// in pool, but is currently used, can succeed
TEST_F(AllocEngine6Test, pdAllocWithUsedHint6) {
allocWithUsedHintTest(Lease::TYPE_PD,
IOAddress("2001:db8:1:2::"), // allocate this prefix as used
IOAddress("2001:db8:1:2::"), // request this prefix
80);
}
// This test checks if the allocation with a hint that is out the blue
// can succeed. The invalid hint should be ignored completely.
TEST_F(AllocEngine6Test, allocBogusHint6) {
allocBogusHint6(Lease::TYPE_NA, IOAddress("3000::abc"), 128);
}
// This test checks if the allocation with a hint that is out the blue
// can succeed. The invalid hint should be ignored completely.
TEST_F(AllocEngine6Test, pdAllocBogusHint6) {
allocBogusHint6(Lease::TYPE_PD, IOAddress("3000::abc"), 80);
}
// This test checks that null values are handled properly
TEST_F(AllocEngine6Test, allocateAddress6Nulls) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
// Allocations without subnet are not allowed
Lease6Ptr lease;
AllocEngine::ClientContext6 ctx1(Subnet6Ptr(), duid_, false, false, "", false,
Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234)));
ctx1.currentIA().iaid_ = iaid_;
EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx1)));
ASSERT_FALSE(lease);
EXPECT_EQ(0, getStatistics("v6-allocation-fail"));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-shared-network"));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-subnet"));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-no-pools"));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-classes"));
EXPECT_EQ(0, getStatistics("v6-allocation-fail", subnet_->getID()));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-shared-network", subnet_->getID()));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-subnet", subnet_->getID()));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-no-pools", subnet_->getID()));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-classes", subnet_->getID()));
// Allocations without DUID are not allowed either
AllocEngine::ClientContext6 ctx2(subnet_, DuidPtr(), false, false, "", false,
Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234)));
ctx2.currentIA().iaid_ = iaid_;
EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx2)));
ASSERT_FALSE(lease);
EXPECT_EQ(0, getStatistics("v6-allocation-fail"));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-shared-network"));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-subnet"));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-no-pools"));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-classes"));
EXPECT_EQ(0, getStatistics("v6-allocation-fail", subnet_->getID()));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-shared-network", subnet_->getID()));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-subnet", subnet_->getID()));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-no-pools", subnet_->getID()));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-classes", subnet_->getID()));
}
// This test checks if really small pools are working
TEST_F(AllocEngine6Test, smallPool6) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
IOAddress addr("2001:db8:1::ad");
// Create a subnet with a pool that has one address.
initSubnet(IOAddress("2001:db8:1::"), addr, addr);
// Initialize FQDN for a lease.
initFqdn("myhost.example.com", true, true);
Lease6Ptr lease;
AllocEngine::ClientContext6 ctx(subnet_, duid_, fqdn_fwd_, fqdn_rev_,
hostname_, false,
Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234)));
ctx.currentIA().iaid_ = iaid_;
EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
// Check that we got that single lease
ASSERT_TRUE(lease);
EXPECT_EQ("2001:db8:1::ad", lease->addr_.toText());
// Do all checks on the lease
checkLease6(duid_, lease, Lease::TYPE_NA, 128);
// Check that the lease is indeed in LeaseMgr
Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease->type_,
lease->addr_);
ASSERT_TRUE(from_mgr);
// Now check that the lease in LeaseMgr has the same parameters
detailCompareLease(lease, from_mgr);
// This is a new lease allocation. The old lease corresponding to a newly
// allocated lease should be null.
ASSERT_TRUE(ctx.currentIA().old_leases_.empty());
}
// This test checks if all addresses in a pool are currently used, the attempt
// to find out a new lease fails.
TEST_F(AllocEngine6Test, outOfAddresses6) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
IOAddress addr("2001:db8:1::ad");
CfgMgr& cfg_mgr = CfgMgr::instance();
cfg_mgr.clear(); // Get rid of the default test configuration
// Create configuration similar to other tests, but with a single address pool
subnet_ = Subnet6::create(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4, SubnetID(10));
pool_ = Pool6Ptr(new Pool6(Lease::TYPE_NA, addr, addr)); // just a single address
subnet_->addPool(pool_);
cfg_mgr.getStagingCfg()->getCfgSubnets6()->add(subnet_);
// Just a different duid
DuidPtr other_duid = DuidPtr(new DUID(vector<uint8_t>(12, 0xff)));
const uint32_t other_iaid = 3568;
Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, other_duid, other_iaid,
501, 502, subnet_->getID(), HWAddrPtr()));
lease->cltt_ = time(0) - 10; // Allocated 10 seconds ago
ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
// There is just a single address in the pool and allocated it to someone
// else, so the allocation should fail
Pkt6Ptr query(new Pkt6(DHCPV6_REQUEST, 1234));
AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false,
query);
ctx.currentIA().iaid_ = iaid_;
Lease6Ptr lease2;
EXPECT_NO_THROW(lease2 = expectOneLease(engine->allocateLeases6(ctx)));
ASSERT_FALSE(lease2);
EXPECT_EQ(1, getStatistics("v6-allocation-fail"));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-shared-network"));
EXPECT_EQ(1, getStatistics("v6-allocation-fail-subnet"));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-no-pools"));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-classes"));
EXPECT_EQ(1, getStatistics("v6-allocation-fail", subnet_->getID()));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-shared-network", subnet_->getID()));
EXPECT_EQ(1, getStatistics("v6-allocation-fail-subnet", subnet_->getID()));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-no-pools", subnet_->getID()));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-classes", subnet_->getID()));
}
// This test checks if an expired lease can be reused in SOLICIT (fake allocation)
TEST_F(AllocEngine6Test, solicitReuseExpiredLease6) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
IOAddress addr("2001:db8:1::ad");
// Create one subnet with a pool holding one address.
initSubnet(IOAddress("2001:db8:1::"), addr, addr);
// Initialize FQDN data for the lease.
initFqdn("myhost.example.com", true, true);
// Verify the all of relevant stats are zero.
EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID()));
EXPECT_TRUE(testStatistics("reclaimed-leases", 0));
EXPECT_TRUE(testStatistics("reclaimed-leases", 0, subnet_->getID()));
// Get the cumulative count of assigned addresses.
int64_t cumulative = getStatistics("cumulative-assigned-nas",
subnet_->getID());
int64_t glbl_cumulative = getStatistics("cumulative-assigned-nas");
// Just a different duid
DuidPtr other_duid = DuidPtr(new DUID(vector<uint8_t>(12, 0xff)));
const uint32_t other_iaid = 3568;
Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, other_duid, other_iaid,
501, 502, subnet_->getID(), HWAddrPtr()));
lease->cltt_ = time(0) - 500; // Allocated 500 seconds ago
lease->valid_lft_ = 495; // Lease was valid for 495 seconds
ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
// Make sure that we really created expired lease
ASSERT_TRUE(lease->expired());
// CASE 1: Asking for any address
AllocEngine::ClientContext6 ctx1(subnet_, duid_, fqdn_fwd_, fqdn_rev_, hostname_,
true, Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234)));
ctx1.currentIA().iaid_ = iaid_;
EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx1)));
// Check that we got that single lease
ASSERT_TRUE(lease);
EXPECT_EQ(addr, lease->addr_);
// Do all checks on the lease (if subnet-id, preferred/valid times are ok etc.)
checkLease6(duid_, lease, Lease::TYPE_NA, 128);
// CASE 2: Asking specifically for this address
AllocEngine::ClientContext6 ctx2(subnet_, duid_, false, false, "", true,
Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234)));
ctx2.currentIA().iaid_ = iaid_;
ctx2.currentIA().addHint(addr);
EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx2)));
// Check that we got that single lease
ASSERT_TRUE(lease);
EXPECT_EQ(addr, lease->addr_);
// Verify the none of relevant stats were altered.
EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID()));
EXPECT_TRUE(testStatistics("cumulative-assigned-nas",
cumulative, subnet_->getID()));
EXPECT_TRUE(testStatistics("cumulative-assigned-nas", glbl_cumulative));
EXPECT_TRUE(testStatistics("reclaimed-leases", 0));
EXPECT_TRUE(testStatistics("reclaimed-leases", 0, subnet_->getID()));
}
// This test checks if an expired lease can be reused using default lifetimes.
TEST_F(AllocEngine6Test, defaultReuseExpiredLease6) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
IOAddress addr("2001:db8:1::ad");
// Create one subnet with a pool holding one address.
initSubnet(IOAddress("2001:db8:1::"), addr, addr);
subnet_->setPreferred(Triplet<uint32_t>(200, 300, 400));
subnet_->setValid(Triplet<uint32_t>(300, 400, 500));
// Initialize FQDN data for the lease.
initFqdn("myhost.example.com", true, true);
// Just a different duid
DuidPtr other_duid = DuidPtr(new DUID(vector<uint8_t>(12, 0xff)));
const uint32_t other_iaid = 3568;
Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, other_duid, other_iaid,
501, 502, subnet_->getID(), HWAddrPtr()));
lease->cltt_ = time(0) - 500; // Allocated 500 seconds ago
lease->valid_lft_ = 495; // Lease was valid for 495 seconds
ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
// Make sure that we really created expired lease
ASSERT_TRUE(lease->expired());
// Asking specifically for this address with zero lifetimes
AllocEngine::ClientContext6 ctx2(subnet_, duid_, false, false, "", true,
Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234)));
ctx2.currentIA().iaid_ = iaid_;
ctx2.currentIA().addHint(addr, 128, 0, 0);
EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx2)));
// Check that we got that single lease
ASSERT_TRUE(lease);
EXPECT_EQ(addr, lease->addr_);
// Check lifetimes: defaults are expected.
EXPECT_EQ(300, lease->preferred_lft_);
EXPECT_EQ(400, lease->valid_lft_);
}
// This test checks if an expired lease can be reused using specified lifetimes.
TEST_F(AllocEngine6Test, hintReuseExpiredLease6) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
IOAddress addr("2001:db8:1::ad");
// Create one subnet with a pool holding one address.
initSubnet(IOAddress("2001:db8:1::"), addr, addr);
subnet_->setPreferred(Triplet<uint32_t>(200, 300, 400));
subnet_->setValid(Triplet<uint32_t>(300, 400, 500));
// Initialize FQDN data for the lease.
initFqdn("myhost.example.com", true, true);
// Just a different duid
DuidPtr other_duid = DuidPtr(new DUID(vector<uint8_t>(12, 0xff)));
const uint32_t other_iaid = 3568;
Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, other_duid, other_iaid,
501, 502, subnet_->getID(), HWAddrPtr()));
lease->cltt_ = time(0) - 500; // Allocated 500 seconds ago
lease->valid_lft_ = 495; // Lease was valid for 495 seconds
ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
// Make sure that we really created expired lease
ASSERT_TRUE(lease->expired());
// Asking specifically for this address with zero lifetimes
AllocEngine::ClientContext6 ctx2(subnet_, duid_, false, false, "", true,
Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234)));
ctx2.currentIA().iaid_ = iaid_;
ctx2.currentIA().addHint(addr, 128, 299, 401);
EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx2)));
// Check that we got that single lease
ASSERT_TRUE(lease);
EXPECT_EQ(addr, lease->addr_);
// Check lifetimes: specified values are expected.
EXPECT_EQ(299, lease->preferred_lft_);
EXPECT_EQ(401, lease->valid_lft_);
}
// This test checks if an expired lease can be reused using min lifetimes.
TEST_F(AllocEngine6Test, minReuseExpiredLease6) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
IOAddress addr("2001:db8:1::ad");
// Create one subnet with a pool holding one address.
initSubnet(IOAddress("2001:db8:1::"), addr, addr);
subnet_->setPreferred(Triplet<uint32_t>(200, 300, 400));
subnet_->setValid(Triplet<uint32_t>(300, 400, 500));
// Initialize FQDN data for the lease.
initFqdn("myhost.example.com", true, true);
// Just a different duid
DuidPtr other_duid = DuidPtr(new DUID(vector<uint8_t>(12, 0xff)));
const uint32_t other_iaid = 3568;
Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, other_duid, other_iaid,
501, 502, subnet_->getID(), HWAddrPtr()));
lease->cltt_ = time(0) - 500; // Allocated 500 seconds ago
lease->valid_lft_ = 495; // Lease was valid for 495 seconds
ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
// Make sure that we really created expired lease
ASSERT_TRUE(lease->expired());
// Asking specifically for this address with zero lifetimes
AllocEngine::ClientContext6 ctx2(subnet_, duid_, false, false, "", true,
Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234)));
ctx2.currentIA().iaid_ = iaid_;
ctx2.currentIA().addHint(addr, 128, 100, 200);
EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx2)));
// Check that we got that single lease
ASSERT_TRUE(lease);
EXPECT_EQ(addr, lease->addr_);
// Check lifetimes: min values are expected.
EXPECT_EQ(200, lease->preferred_lft_);
EXPECT_EQ(300, lease->valid_lft_);
}
// This test checks if an expired lease can be reused using max lifetimes.
TEST_F(AllocEngine6Test, maxReuseExpiredLease6) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
IOAddress addr("2001:db8:1::ad");
// Create one subnet with a pool holding one address.
initSubnet(IOAddress("2001:db8:1::"), addr, addr);
subnet_->setPreferred(Triplet<uint32_t>(200, 300, 400));
subnet_->setValid(Triplet<uint32_t>(300, 400, 500));
// Initialize FQDN data for the lease.
initFqdn("myhost.example.com", true, true);
// Just a different duid
DuidPtr other_duid = DuidPtr(new DUID(vector<uint8_t>(12, 0xff)));
const uint32_t other_iaid = 3568;
Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, other_duid, other_iaid,
501, 502, subnet_->getID(), HWAddrPtr()));
lease->cltt_ = time(0) - 500; // Allocated 500 seconds ago
lease->valid_lft_ = 495; // Lease was valid for 495 seconds
ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
// Make sure that we really created expired lease
ASSERT_TRUE(lease->expired());
// Asking specifically for this address with zero lifetimes
AllocEngine::ClientContext6 ctx2(subnet_, duid_, false, false, "", true,
Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234)));
ctx2.currentIA().iaid_ = iaid_;
ctx2.currentIA().addHint(addr, 128, 500, 600);
EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx2)));
// Check that we got that single lease
ASSERT_TRUE(lease);
EXPECT_EQ(addr, lease->addr_);
// Check lifetimes: max values are expected.
EXPECT_EQ(400, lease->preferred_lft_);
EXPECT_EQ(500, lease->valid_lft_);
}
// This test checks if an expired lease can be reused using class lifetimes.
// Verifies getLifetimes() is invoked by v6 resuseExpiredLease().
TEST_F(AllocEngine6Test, classReuseExpiredLease6) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
IOAddress addr("2001:db8:1::ad");
// Let's make a classes with valid-lifetime and add it to the dictionary.
ClientClassDictionaryPtr dictionary = CfgMgr::instance().getStagingCfg()->getClientClassDictionary();
ClientClassDefPtr class_def(new ClientClassDef("valid_one", ExpressionPtr()));
Triplet<uint32_t> valid_one(600, 700, 800);
class_def->setValid(valid_one);
dictionary->addClass(class_def);
// Create one subnet with a pool holding one address.
initSubnet(IOAddress("2001:db8:1::"), addr, addr);
subnet_->setPreferred(Triplet<uint32_t>(100, 200, 300));
subnet_->setValid(Triplet<uint32_t>(300, 400, 500));
// Initialize FQDN data for the lease.
initFqdn("myhost.example.com", true, true);
// Just a different duid
DuidPtr other_duid = DuidPtr(new DUID(vector<uint8_t>(12, 0xff)));
const uint32_t other_iaid = 3568;
Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, other_duid, other_iaid,
501, 502, subnet_->getID(), HWAddrPtr()));
lease->cltt_ = time(0) - 500; // Allocated 500 seconds ago
lease->valid_lft_ = 495; // Lease was valid for 495 seconds
ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
// Make sure that we really created expired lease
ASSERT_TRUE(lease->expired());
// Asking specifically for this address with zero lifetimes
AllocEngine::ClientContext6 ctx2(subnet_, duid_, false, false, "", true,
Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234)));
ctx2.currentIA().iaid_ = iaid_;
ctx2.currentIA().addHint(addr, 128, 0, 0);
// Add the class name to the context.
ctx2.query_->addClass("valid_one");
EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx2)));
// Check that we got that single lease
ASSERT_TRUE(lease);
EXPECT_EQ(addr, lease->addr_);
// Check lifetimes: specified values are expected.
EXPECT_EQ(200, lease->preferred_lft_);
EXPECT_EQ(700, lease->valid_lft_);
}
// This test checks an expired registered lease can't be reused in SOLICT.
TEST_F(AllocEngine6Test, solicitReuseExpiredRegisteredLease6) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
IOAddress addr("2001:db8:1::ad");
// Create one subnet with a pool holding one address.
initSubnet(IOAddress("2001:db8:1::"), addr, addr);
// Initialize FQDN data for the lease.
initFqdn("myhost.example.com", true, true);
// Just a different duid
DuidPtr other_duid = DuidPtr(new DUID(vector<uint8_t>(12, 0xff)));
const uint32_t other_iaid = 3568;
Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, other_duid, other_iaid,
501, 502, subnet_->getID(), HWAddrPtr()));
lease->state_ = Lease::STATE_REGISTERED;
lease->cltt_ = time(0) - 500; // Registered 500 seconds ago
lease->valid_lft_ = 495; // Lease was valid for 495 seconds
ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
// Make sure that we really created expired lease
ASSERT_TRUE(lease->expired());
// CASE 1: Asking for any address
AllocEngine::ClientContext6 ctx1(subnet_, duid_, fqdn_fwd_, fqdn_rev_, hostname_,
true, Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234)));
ctx1.currentIA().iaid_ = iaid_;
EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx1)));
// Check that we did not get this single lease.
EXPECT_FALSE(lease);
// CASE 2: Asking specifically for this address
AllocEngine::ClientContext6 ctx2(subnet_, duid_, false, false, "", true,
Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234)));
ctx2.currentIA().iaid_ = iaid_;
ctx2.currentIA().addHint(addr);
EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx2)));
// Check that we did not get this single lease.
EXPECT_FALSE(lease);
}
// This test checks a registered lease can't be reused in SOLICT.
TEST_F(AllocEngine6Test, solicitReuseRegisteredLease6) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
IOAddress addr("2001:db8:1::ad");
// Create one subnet with a pool holding one address.
initSubnet(IOAddress("2001:db8:1::"), addr, addr);
// Initialize FQDN data for the lease.
initFqdn("myhost.example.com", true, true);
Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, duid_, iaid_,
501, 502, subnet_->getID(), HWAddrPtr()));
lease->state_ = Lease::STATE_REGISTERED;
lease->cltt_ = time(0) - 200; // Registered 200 seconds ago
lease->valid_lft_ = 400; // Lease was valid for 400 seconds
ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
// CASE 1: Asking for any address
AllocEngine::ClientContext6 ctx1(subnet_, duid_, fqdn_fwd_, fqdn_rev_, hostname_,
true, Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234)));
ctx1.currentIA().iaid_ = iaid_;
EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx1)));
// Check that we did not get this single lease.
EXPECT_FALSE(lease);
// CASE 2: Asking specifically for this address
AllocEngine::ClientContext6 ctx2(subnet_, duid_, false, false, "", true,
Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234)));
ctx2.currentIA().iaid_ = iaid_;
ctx2.currentIA().addHint(addr);
EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx2)));
// Check that we did not get this single lease.
EXPECT_FALSE(lease);
}
// This test checks if an expired lease can be reused in REQUEST (actual allocation)
TEST_F(AllocEngine6Test, requestReuseExpiredLease6) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
IOAddress addr("2001:db8:1::ad");
CfgMgr& cfg_mgr = CfgMgr::instance();
cfg_mgr.clear(); // Get rid of the default test configuration
// Create configuration similar to other tests, but with a single address pool
subnet_ = Subnet6::create(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4, SubnetID(10));
pool_ = Pool6Ptr(new Pool6(Lease::TYPE_NA, addr, addr)); // just a single address
subnet_->addPool(pool_);
cfg_mgr.getStagingCfg()->getCfgSubnets6()->add(subnet_);
cfg_mgr.commit();
int64_t cumulative = getStatistics("cumulative-assigned-nas",
subnet_->getID());
int64_t glbl_cumulative = getStatistics("cumulative-assigned-nas");
// Let's create an expired lease
DuidPtr other_duid = DuidPtr(new DUID(vector<uint8_t>(12, 0xff)));
const uint32_t other_iaid = 3568;
const SubnetID other_subnetid = 999;
Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, other_duid, other_iaid,
501, 502, other_subnetid, HWAddrPtr()));
int64_t other_cumulative =
getStatistics("cumulative-assigned-nas", other_subnetid);
lease->cltt_ = time(0) - 500; // Allocated 500 seconds ago
lease->valid_lft_ = 495; // Lease was valid for 495 seconds
lease->fqdn_fwd_ = true;
lease->fqdn_rev_ = true;
lease->hostname_ = "myhost.example.com.";
ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
// A client comes along, asking specifically for this address
AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false,
Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234)));
ctx.currentIA().iaid_ = iaid_;
ctx.currentIA().addHint(addr);
EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
// Check that he got that single lease
ASSERT_TRUE(lease);
EXPECT_EQ(addr, lease->addr_);
// This reactivated lease should have updated FQDN data.
EXPECT_TRUE(lease->hostname_.empty());
EXPECT_FALSE(lease->fqdn_fwd_);
EXPECT_FALSE(lease->fqdn_rev_);
// Check that the old lease has been returned.
Lease6Ptr old_lease = expectOneLease(ctx.currentIA().old_leases_);
ASSERT_TRUE(old_lease);
// It should at least have the same IPv6 address.
EXPECT_EQ(lease->addr_, old_lease->addr_);
// Check that it carries not updated FQDN data.
EXPECT_EQ("myhost.example.com.", old_lease->hostname_);
EXPECT_TRUE(old_lease->fqdn_fwd_);
EXPECT_TRUE(old_lease->fqdn_rev_);
// Check that the lease is indeed updated in LeaseMgr
Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
addr);
ASSERT_TRUE(from_mgr);
// Now check that the lease in LeaseMgr has the same parameters
detailCompareLease(lease, from_mgr);
// Verify the stats got adjusted correctly
EXPECT_TRUE(testStatistics("assigned-nas", 1, subnet_->getID()));
cumulative += 1;
EXPECT_TRUE(testStatistics("cumulative-assigned-nas",
cumulative, subnet_->getID()));
glbl_cumulative += 1;
EXPECT_TRUE(testStatistics("cumulative-assigned-nas", glbl_cumulative));
EXPECT_TRUE(testStatistics("assigned-nas", -1, other_subnetid));
EXPECT_FALSE(testStatistics("cumulative-assigned-nas",
other_cumulative, other_subnetid, false));
EXPECT_TRUE(testStatistics("reclaimed-leases", 1));
EXPECT_TRUE(testStatistics("reclaimed-leases", 0, subnet_->getID()));
EXPECT_TRUE(testStatistics("reclaimed-leases", 1, other_subnetid));
}
// This test checks checks an expired registered lease can't be reused in REQUEST.
TEST_F(AllocEngine6Test, requestReuseExpiredRegisteredLease6) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
IOAddress addr("2001:db8:1::ad");
CfgMgr& cfg_mgr = CfgMgr::instance();
cfg_mgr.clear(); // Get rid of the default test configuration
// Create configuration similar to other tests, but with a single address pool
subnet_ = Subnet6::create(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4, SubnetID(10));
pool_ = Pool6Ptr(new Pool6(Lease::TYPE_NA, addr, addr)); // just a single address
subnet_->addPool(pool_);
cfg_mgr.getStagingCfg()->getCfgSubnets6()->add(subnet_);
cfg_mgr.commit();
// Let's create an expired registered lease
DuidPtr other_duid = DuidPtr(new DUID(vector<uint8_t>(12, 0xff)));
const uint32_t other_iaid = 3568;
const SubnetID other_subnetid = 999;
Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, other_duid, other_iaid,
501, 502, other_subnetid, HWAddrPtr()));
lease->state_ = Lease::STATE_REGISTERED;
lease->cltt_ = time(0) - 500; // Registered 500 seconds ago
lease->valid_lft_ = 495; // Lease was valid for 495 seconds
lease->fqdn_fwd_ = true;
lease->fqdn_rev_ = true;
lease->hostname_ = "myhost.example.com.";
ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
// A client comes along, asking specifically for this address
AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false,
Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234)));
ctx.currentIA().iaid_ = iaid_;
ctx.currentIA().addHint(addr);
EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
// Check that we did not get this single lease.
EXPECT_FALSE(lease);
// Check that no old lease has been returned.
Lease6Ptr old_lease = expectOneLease(ctx.currentIA().old_leases_);
EXPECT_FALSE(old_lease);
}
// This test checks a registered lease can't be reused in REQUEST.
TEST_F(AllocEngine6Test, requestReuseRegisteredLease6) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
IOAddress addr("2001:db8:1::ad");
CfgMgr& cfg_mgr = CfgMgr::instance();
cfg_mgr.clear(); // Get rid of the default test configuration
// Create configuration similar to other tests, but with a single address pool
subnet_ = Subnet6::create(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4, SubnetID(10));
pool_ = Pool6Ptr(new Pool6(Lease::TYPE_NA, addr, addr)); // just a single address
subnet_->addPool(pool_);
cfg_mgr.getStagingCfg()->getCfgSubnets6()->add(subnet_);
cfg_mgr.commit();
// Let's create a registered lease
const SubnetID other_subnetid = 999;
Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, duid_, iaid_,
501, 502, other_subnetid, HWAddrPtr()));
lease->state_ = Lease::STATE_REGISTERED;
lease->cltt_ = time(0) - 200; // Registered 200 seconds ago
lease->valid_lft_ = 400; // Lease was valid for 400 seconds
lease->fqdn_fwd_ = true;
lease->fqdn_rev_ = true;
lease->hostname_ = "myhost.example.com.";
ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
// A client comes along, asking specifically for this address
AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false,
Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234)));
ctx.currentIA().iaid_ = iaid_;
ctx.currentIA().addHint(addr);
EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
// Check that we did not get this single lease.
EXPECT_FALSE(lease);
// Check that no old lease has been returned.
Lease6Ptr old_lease = expectOneLease(ctx.currentIA().old_leases_);
EXPECT_FALSE(old_lease);
}
// This test checks if a released lease can be reused in REQUEST (actual allocation)
TEST_F(AllocEngine6Test, requestReuseReleasedLease6) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
IOAddress addr("2001:db8:1::ad");
CfgMgr& cfg_mgr = CfgMgr::instance();
cfg_mgr.clear(); // Get rid of the default test configuration
// Create configuration similar to other tests, but with a single address pool
subnet_ = Subnet6::create(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4, SubnetID(10));
pool_ = Pool6Ptr(new Pool6(Lease::TYPE_NA, addr, addr)); // just a single address
subnet_->addPool(pool_);
cfg_mgr.getStagingCfg()->getCfgSubnets6()->add(subnet_);
cfg_mgr.commit();
int64_t cumulative = getStatistics("cumulative-assigned-nas",
subnet_->getID());
int64_t glbl_cumulative = getStatistics("cumulative-assigned-nas");
// Let's create a released lease
DuidPtr other_duid = DuidPtr(new DUID(vector<uint8_t>(12, 0xff)));
const uint32_t other_iaid = 3568;
const SubnetID other_subnetid = 999;
Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, other_duid, other_iaid,
501, 502, other_subnetid, HWAddrPtr()));
lease->state_ = Lease6::STATE_RELEASED;
int64_t other_cumulative =
getStatistics("cumulative-assigned-nas", other_subnetid);
lease->cltt_ = time(0) - 500; // Allocated 500 seconds ago
lease->valid_lft_ = 495; // Lease was valid for 495 seconds
lease->fqdn_fwd_ = true;
lease->fqdn_rev_ = true;
lease->hostname_ = "myhost.example.com.";
ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
// A client comes along, asking specifically for this address
AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false,
Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234)));
ctx.currentIA().iaid_ = iaid_;
ctx.currentIA().addHint(addr);
EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
// Check that he got that single lease
ASSERT_TRUE(lease);
EXPECT_EQ(addr, lease->addr_);
// This reactivated lease should have updated FQDN data.
EXPECT_TRUE(lease->hostname_.empty());
EXPECT_FALSE(lease->fqdn_fwd_);
EXPECT_FALSE(lease->fqdn_rev_);
EXPECT_EQ(Lease6::STATE_DEFAULT, lease->state_);
// Check that the old lease has been returned.
Lease6Ptr old_lease = expectOneLease(ctx.currentIA().old_leases_);
ASSERT_TRUE(old_lease);
// It should at least have the same IPv6 address.
EXPECT_EQ(lease->addr_, old_lease->addr_);
// Check that it carries not updated FQDN data.
EXPECT_EQ("myhost.example.com.", old_lease->hostname_);
EXPECT_TRUE(old_lease->fqdn_fwd_);
EXPECT_TRUE(old_lease->fqdn_rev_);
EXPECT_EQ(Lease6::STATE_RELEASED, old_lease->state_);
// Check that the lease is indeed updated in LeaseMgr
Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
addr);
ASSERT_TRUE(from_mgr);
EXPECT_EQ(Lease6::STATE_DEFAULT, from_mgr->state_);
// Now check that the lease in LeaseMgr has the same parameters
detailCompareLease(lease, from_mgr);
// Verify the stats got adjusted correctly
EXPECT_TRUE(testStatistics("assigned-nas", 1, subnet_->getID()));
cumulative += 1;
EXPECT_TRUE(testStatistics("cumulative-assigned-nas",
cumulative, subnet_->getID()));
glbl_cumulative += 1;
EXPECT_TRUE(testStatistics("cumulative-assigned-nas", glbl_cumulative));
EXPECT_FALSE(testStatistics("cumulative-assigned-nas",
other_cumulative, other_subnetid, false));
EXPECT_TRUE(testStatistics("reclaimed-leases", 1));
EXPECT_TRUE(testStatistics("reclaimed-leases", 0, subnet_->getID()));
EXPECT_TRUE(testStatistics("reclaimed-leases", 1, other_subnetid));
}
// Checks if the lease lifetime is extended when the client sends the
// Request.
TEST_F(AllocEngine6Test, requestExtendLeaseLifetime) {
// Create a lease for the client.
Lease6Ptr lease(new Lease6(Lease::TYPE_NA, IOAddress("2001:db8:1::15"),
duid_, iaid_, 300, 400,
subnet_->getID(), HWAddrPtr()));
// Allocated 200 seconds ago - half of the lifetime.
time_t lease_cltt = time(0) - 200;
lease->cltt_ = lease_cltt;
ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
// Client should receive a lease.
Lease6Ptr new_lease = simpleAlloc6Test(pool_, IOAddress("::"), false);
ASSERT_TRUE(new_lease);
// And the lease lifetime should be extended.
EXPECT_GT(new_lease->cltt_, lease_cltt)
<< "Lease lifetime was not extended, but it should";
}
// Checks if the lease lifetime is extended when the client sends the
// Request and the client has a reservation for the lease.
TEST_F(AllocEngine6Test, requestExtendLeaseLifetimeForReservation) {
// Create reservation for the client. This is in-pool reservation,
// as the pool is 2001:db8:1::10 - 2001:db8:1::20.
createHost6(true, IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::1c"), 128);
// Create a lease for the client.
Lease6Ptr lease(new Lease6(Lease::TYPE_NA, IOAddress("2001:db8:1::1c"),
duid_, iaid_, 300, 400,
subnet_->getID(), HWAddrPtr()));
// Allocated 200 seconds ago - half of the lifetime.
time_t lease_cltt = time(0) - 200;
lease->cltt_ = lease_cltt;
ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
// Client should receive a lease.
Lease6Ptr new_lease = simpleAlloc6Test(pool_, IOAddress("::"), false);
ASSERT_TRUE(new_lease);
// And the lease lifetime should be extended.
EXPECT_GT(new_lease->cltt_, lease_cltt)
<< "Lease lifetime was not extended, but it should";
}
// Checks if the lease lifetime is extended when the client sends the
// Renew.
TEST_F(AllocEngine6Test, renewExtendLeaseLifetime) {
// Create a lease for the client.
Lease6Ptr lease(new Lease6(Lease::TYPE_NA, IOAddress("2001:db8:1::15"),
duid_, iaid_, 300, 400,
subnet_->getID(), HWAddrPtr()));
// Allocated 200 seconds ago - half of the lifetime.
time_t lease_cltt = time(0) - 200;
lease->cltt_ = lease_cltt;
ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
AllocEngine engine(100);
// This is what the client will send in his renew message.
AllocEngine::HintContainer hints;
hints.push_back(AllocEngine::Resource(IOAddress("2001:db8:1::15"), 128));
// Client should receive a lease.
Lease6Collection renewed = renewTest(engine, pool_, hints, IN_SUBNET, IN_POOL);
ASSERT_EQ(1, renewed.size());
// And the lease lifetime should be extended.
EXPECT_GT(renewed[0]->cltt_, lease_cltt)
<< "Lease lifetime was not extended, but it should";
}
// Checks a registered lease can't be renewed.
TEST_F(AllocEngine6Test, renewRegisteredLease6) {
// Create a registered lease for the client.
IOAddress addr("2001:db8:1::15");
Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr,
duid_, iaid_, 300, 400,
subnet_->getID(), HWAddrPtr()));
lease->state_ = Lease::STATE_REGISTERED;
// Allocated 200 seconds ago - half of the lifetime.
time_t lease_cltt = time(0) - 200;
lease->cltt_ = lease_cltt;
ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
AllocEngine engine(100);
// This is what the client will send in his renew message.
AllocEngine::HintContainer hints;
hints.push_back(AllocEngine::Resource(addr, 128));
// Get renewed leases.
Lease6Collection renewed = renewTest(engine, pool_, hints, IN_SUBNET, IN_POOL);
for (auto const& l : renewed) {
// Not registered.
EXPECT_EQ(0, l->state_);
// Another address.
EXPECT_NE(addr, l->addr_);
}
}
// Checks that a renewed lease uses default lifetimes.
TEST_F(AllocEngine6Test, defaultRenewLeaseLifetime) {
// Create a lease for the client.
Lease6Ptr lease(new Lease6(Lease::TYPE_NA, IOAddress("2001:db8:1::15"),
duid_, iaid_, 300, 400,
subnet_->getID(), HWAddrPtr()));
// Allocated 200 seconds ago - half of the lifetime.
time_t lease_cltt = time(0) - 200;
lease->cltt_ = lease_cltt;
ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
AllocEngine engine(100);
subnet_->setPreferred(Triplet<uint32_t>(200, 300, 400));
subnet_->setValid(Triplet<uint32_t>(300, 400, 500));
// This is what the client will send in his renew message.
AllocEngine::HintContainer hints;
hints.push_back(AllocEngine::Resource(IOAddress("2001:db8:1::15"),
128, 0, 0));
// Client should receive a lease.
Lease6Collection renewed = renewTest(engine, pool_, hints, IN_SUBNET, IN_POOL);
ASSERT_EQ(1, renewed.size());
// And the lease lifetime should be extended.
EXPECT_GT(renewed[0]->cltt_, lease_cltt)
<< "Lease lifetime was not extended, but it should";
// Checks that default values are used for lifetimes.
EXPECT_EQ(300, renewed[0]->preferred_lft_);
EXPECT_EQ(400, renewed[0]->valid_lft_);
}
// Checks that a renewed lease uses specified lifetimes.
TEST_F(AllocEngine6Test, hintRenewLeaseLifetime) {
// Create a lease for the client.
Lease6Ptr lease(new Lease6(Lease::TYPE_NA, IOAddress("2001:db8:1::15"),
duid_, iaid_, 300, 400,
subnet_->getID(), HWAddrPtr()));
// Allocated 200 seconds ago - half of the lifetime.
time_t lease_cltt = time(0) - 200;
lease->cltt_ = lease_cltt;
ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
AllocEngine engine(100);
subnet_->setPreferred(Triplet<uint32_t>(200, 300, 400));
subnet_->setValid(Triplet<uint32_t>(300, 400, 500));
// This is what the client will send in his renew message.
AllocEngine::HintContainer hints;
hints.push_back(AllocEngine::Resource(IOAddress("2001:db8:1::15"),
128, 301, 399));
// Client should receive a lease.
Lease6Collection renewed = renewTest(engine, pool_, hints, IN_SUBNET, IN_POOL);
ASSERT_EQ(1, renewed.size());
// And the lease lifetime should be extended.
EXPECT_GT(renewed[0]->cltt_, lease_cltt)
<< "Lease lifetime was not extended, but it should";
// Checks that specified values are used for lifetimes.
EXPECT_EQ(301, renewed[0]->preferred_lft_);
EXPECT_EQ(399, renewed[0]->valid_lft_);
}
// Checks that a renewed lease uses min lifetimes.
TEST_F(AllocEngine6Test, minRenewLeaseLifetime) {
// Create a lease for the client.
Lease6Ptr lease(new Lease6(Lease::TYPE_NA, IOAddress("2001:db8:1::15"),
duid_, iaid_, 300, 400,
subnet_->getID(), HWAddrPtr()));
// Allocated 200 seconds ago - half of the lifetime.
time_t lease_cltt = time(0) - 200;
lease->cltt_ = lease_cltt;
ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
AllocEngine engine(100);
subnet_->setPreferred(Triplet<uint32_t>(200, 300, 400));
subnet_->setValid(Triplet<uint32_t>(300, 400, 500));
// This is what the client will send in his renew message.
AllocEngine::HintContainer hints;
hints.push_back(AllocEngine::Resource(IOAddress("2001:db8:1::15"),
128, 100, 200));
// Client should receive a lease.
Lease6Collection renewed = renewTest(engine, pool_, hints, IN_SUBNET, IN_POOL);
ASSERT_EQ(1, renewed.size());
// And the lease lifetime should be extended.
EXPECT_GT(renewed[0]->cltt_, lease_cltt)
<< "Lease lifetime was not extended, but it should";
// Checks that min values are used for lifetimes.
EXPECT_EQ(200, renewed[0]->preferred_lft_);
EXPECT_EQ(300, renewed[0]->valid_lft_);
}
// Checks that a renewed lease uses max lifetimes.
TEST_F(AllocEngine6Test, maxRenewLeaseLifetime) {
// Create a lease for the client.
Lease6Ptr lease(new Lease6(Lease::TYPE_NA, IOAddress("2001:db8:1::15"),
duid_, iaid_, 300, 400,
subnet_->getID(), HWAddrPtr()));
// Allocated 200 seconds ago - half of the lifetime.
time_t lease_cltt = time(0) - 200;
lease->cltt_ = lease_cltt;
ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
AllocEngine engine(100);
subnet_->setPreferred(Triplet<uint32_t>(200, 300, 400));
subnet_->setValid(Triplet<uint32_t>(300, 400, 500));
// This is what the client will send in his renew message.
AllocEngine::HintContainer hints;
hints.push_back(AllocEngine::Resource(IOAddress("2001:db8:1::15"),
128, 500, 600));
// Client should receive a lease.
Lease6Collection renewed = renewTest(engine, pool_, hints, IN_SUBNET, IN_POOL);
ASSERT_EQ(1, renewed.size());
// And the lease lifetime should be extended.
EXPECT_GT(renewed[0]->cltt_, lease_cltt)
<< "Lease lifetime was not extended, but it should";
// Checks that max values are used for lifetimes.
EXPECT_EQ(400, renewed[0]->preferred_lft_);
EXPECT_EQ(500, renewed[0]->valid_lft_);
}
// Verifies renewal uses class life times via getLifetimes() in invoked by
// extendLease6().
TEST_F(AllocEngine6Test, renewClassLeaseLifetime) {
// Create a lease for the client.
Lease6Ptr lease(new Lease6(Lease::TYPE_NA, IOAddress("2001:db8:1::15"),
duid_, iaid_, 300, 400,
subnet_->getID(), HWAddrPtr()));
// Allocated 200 seconds ago - half of the lifetime.
time_t lease_cltt = time(0) - 200;
lease->cltt_ = lease_cltt;
ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
AllocEngine engine(100);
// This is what the client will send in his renew message.
AllocEngine::HintContainer hints;
hints.push_back(AllocEngine::Resource(IOAddress("2001:db8:1::15"), 128));
Lease::Type type = Lease::TYPE_NA;
Pkt6Ptr query(new Pkt6(DHCPV6_RENEW, 1234));
AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "",
false, query);
ctx.currentIA().hints_ = hints;
ctx.currentIA().iaid_ = iaid_;
ctx.currentIA().type_ = type;
// Add a client class with both valid and preferred lifetime values.
ClientClassDefPtr class_def(new ClientClassDef("lft_class", ExpressionPtr()));
Triplet<uint32_t> valid_lft(600, 700, 800);
class_def->setValid(valid_lft);
Triplet<uint32_t> preferred_lft(650, 750, 850);
class_def->setPreferred(preferred_lft);
CfgMgr::instance().getCurrentCfg()->getClientClassDictionary()->addClass(class_def);
ctx.query_->addClass(class_def->getName());
findReservation(engine, ctx);
Lease6Collection renewed = engine.renewLeases6(ctx);
ASSERT_EQ(1, renewed.size());
// And the lease lifetime should be extended.
EXPECT_GT(renewed[0]->cltt_, lease_cltt)
<< "Lease lifetime was not extended, but it should";
// Verify life times came from the class. Preferred should get adjusted.
EXPECT_EQ(renewed[0]->preferred_lft_, 437);
EXPECT_EQ(renewed[0]->valid_lft_, 700);
}
// Checks if a released lease is renewed and that its state is set to default.
TEST_F(AllocEngine6Test, renewReleasedLease) {
// Create a released lease for the client.
Lease6Ptr lease(new Lease6(Lease::TYPE_NA, IOAddress("2001:db8:1::15"),
duid_, iaid_, 300, 400,
subnet_->getID(), HWAddrPtr()));
lease->state_ = Lease6::STATE_RELEASED;
// Allocated 200 seconds ago - half of the lifetime.
time_t lease_cltt = time(0) - 200;
lease->cltt_ = lease_cltt;
ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
AllocEngine engine(100);
// This is what the client will send in his renew message.
AllocEngine::HintContainer hints;
hints.push_back(AllocEngine::Resource(IOAddress("2001:db8:1::15"), 128));
// Client should renew the lease.
Lease6Collection renewed = renewTest(engine, pool_, hints, IN_SUBNET, IN_POOL);
ASSERT_EQ(1, renewed.size());
// And the lease lifetime should be extended.
EXPECT_GT(renewed[0]->cltt_, lease_cltt)
<< "Lease lifetime was not extended, but it should";
// The lease should have the default state.
EXPECT_EQ(Lease6::STATE_DEFAULT, renewed[0]->state_);
}
// Checks if a released lease is re-allocated and that its state set to default.
TEST_F(AllocEngine6Test, reallocReleasedLease) {
// Create a released lease for the client.
Lease6Ptr lease(new Lease6(Lease::TYPE_NA, IOAddress("2001:db8:1::15"),
duid_, iaid_, 300, 400,
subnet_->getID(), HWAddrPtr()));
lease->state_ = Lease6::STATE_RELEASED;
// Allocated 200 seconds ago - half of the lifetime.
time_t lease_cltt = time(0) - 200;
lease->cltt_ = lease_cltt;
ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
AllocEngine engine(100);
// Reallocate the released lease.
Lease6Ptr renewed = simpleAlloc6Test(pool_, IOAddress("::"), false);
ASSERT_TRUE(renewed);
// And the lease lifetime should be extended.
EXPECT_GT(renewed->cltt_, lease_cltt)
<< "Lease lifetime was not extended, but it should";
// The lease should have the default state.
EXPECT_EQ(Lease6::STATE_DEFAULT, renewed->state_);
}
// Renew and the client has a reservation for the lease.
TEST_F(AllocEngine6Test, renewExtendLeaseLifetimeForReservation) {
// Create reservation for the client. This is in-pool reservation,
// as the pool is 2001:db8:1::10 - 2001:db8:1::20.
createHost6(true, IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::15"), 128);
// Create a lease for the client.
Lease6Ptr lease(new Lease6(Lease::TYPE_NA, IOAddress("2001:db8:1::15"),
duid_, iaid_, 300, 400,
subnet_->getID(), HWAddrPtr()));
// Allocated 200 seconds ago - half of the lifetime.
time_t lease_cltt = time(0) - 200;
lease->cltt_ = lease_cltt;
ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
AllocEngine engine(100);
// This is what the client will send in his renew message.
AllocEngine::HintContainer hints;
hints.push_back(AllocEngine::Resource(IOAddress("2001:db8:1::15"), 128));
// Client should receive a lease.
Lease6Collection renewed = renewTest(engine, pool_, hints, IN_SUBNET, IN_POOL);
ASSERT_EQ(1, renewed.size());
// And the lease lifetime should be extended.
EXPECT_GT(renewed[0]->cltt_, lease_cltt)
<< "Lease lifetime was not extended, but it should";
}
// --- v6 host reservation ---
// Checks that a client gets the address reserved (in-pool case)
// This test checks the behavior of the allocation engine in the following
// scenario:
// - Client has no lease in the database.
// - Client has an in-pool reservation.
// - Client sends SOLICIT without any hints.
// - Client is allocated a reserved address.
//
// Note that a DHCPv6 client can, but doesn't have to send any hints in its
// Solicit message.
TEST_F(AllocEngine6Test, reservedAddressInPoolSolicitNoHint) {
// Create reservation for the client. This is in-pool reservation,
// as the pool is 2001:db8:1::10 - 2001:db8:1::20.
createHost6(true, IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::1c"), 128);
AllocEngine engine(100);
}
// Checks that a client gets the address reserved (in-pool case)
// This test checks the behavior of the allocation engine in the following
// scenario:
// - Client has no lease in the database.
// - Client has an in-pool reservation.
// - Client sends REQUEST without any hints.
// - Client is allocated a reserved address.
//
// Note that DHCPv6 client must send an address in REQUEST that the server
// offered in Advertise. Nevertheless, the client may ignore this requirement.
TEST_F(AllocEngine6Test, reservedAddressInPoolRequestNoHint) {
// Create reservation for the client. This is in-pool reservation,
// as the pool is 2001:db8:1::10 - 2001:db8:1::20.
createHost6(true, IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::1c"), 128);
AllocEngine engine(100);
// Assigned count should be zero.
EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID()));
// Get the cumulative count of assigned addresses.
int64_t cumulative = getStatistics("cumulative-assigned-nas",
subnet_->getID());
int64_t glbl_cumulative = getStatistics("cumulative-assigned-nas");
Lease6Ptr lease = simpleAlloc6Test(pool_, IOAddress("::"), false);
ASSERT_TRUE(lease);
EXPECT_EQ("2001:db8:1::1c", lease->addr_.toText());
// Assigned count should have been incremented by one.
EXPECT_TRUE(testStatistics("assigned-nas", 1, subnet_->getID()));
cumulative += 1;
EXPECT_TRUE(testStatistics("cumulative-assigned-nas",
cumulative, subnet_->getID()));
glbl_cumulative += 1;
EXPECT_TRUE(testStatistics("cumulative-assigned-nas", glbl_cumulative));
}
// Checks that a client gets the address reserved (in-pool case)
// This test checks the behavior of the allocation engine in the following
// scenario:
// - Client has no lease in the database.
// - Client has an in-pool reservation.
// - Client sends SOLICIT with a hint that does not match reservation
// - Client is allocated a reserved address, not the hint.
//
// Note that DHCPv6 client can, but don't have to send any hints in its
// Solicit message.
TEST_F(AllocEngine6Test, reservedAddressInPoolSolicitValidHint) {
// Create reservation for the client. This is in-pool reservation,
// as the pool is 2001:db8:1::10 - 2001:db8:1::20.
createHost6(true, IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::1c"), 128);
AllocEngine engine(100);
// Assigned count should be zero.
EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID()));
// Get the cumulative count of assigned addresses.
int64_t cumulative = getStatistics("cumulative-assigned-nas",
subnet_->getID());
int64_t glbl_cumulative = getStatistics("cumulative-assigned-nas");
// Let's pretend the client sends hint 2001:db8:1::10.
Lease6Ptr lease = simpleAlloc6Test(pool_, IOAddress("2001:db8:1::10"), true);
ASSERT_TRUE(lease);
// The hint should be ignored and the reserved address should be assigned
EXPECT_EQ("2001:db8:1::1c", lease->addr_.toText());
// Assigned count should not have been incremented.
EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID()));
EXPECT_TRUE(testStatistics("cumulative-assigned-nas",
cumulative, subnet_->getID()));
EXPECT_TRUE(testStatistics("cumulative-assigned-nas", glbl_cumulative));
}
// Checks that a client gets the address reserved (in-pool case)
// This test checks the behavior of the allocation engine in the following
// scenario:
// - Client has no lease in the database.
// - Client has an in-pool reservation.
// - Client sends REQUEST with a hint that does not match reservation
// - Client is allocated a reserved address, not the hint.
//
// Note that DHCPv6 client must send an address in REQUEST that the server
// offered in Advertise. Nevertheless, the client may ignore this requirement.
TEST_F(AllocEngine6Test, reservedAddressInPoolRequestValidHint) {
// Create reservation for the client This is in-pool reservation,
// as the pool is 2001:db8:1::10 - 2001:db8:1::20.
createHost6(true, IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::1c"), 128);
AllocEngine engine(100);
// Assigned count should be zero.
EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID()));
// Get the cumulative count of assigned addresses.
int64_t cumulative = getStatistics("cumulative-assigned-nas",
subnet_->getID());
int64_t glbl_cumulative = getStatistics("cumulative-assigned-nas");
// Let's pretend the client sends hint 2001:db8:1::10.
Lease6Ptr lease = simpleAlloc6Test(pool_, IOAddress("2001:db8:1::10"), false);
ASSERT_TRUE(lease);
// The hint should be ignored and the reserved address should be assigned
EXPECT_EQ("2001:db8:1::1c", lease->addr_.toText());
// Assigned count should have been incremented.
EXPECT_TRUE(testStatistics("assigned-nas", 1, subnet_->getID()));
cumulative += 1;
EXPECT_TRUE(testStatistics("cumulative-assigned-nas",
cumulative, subnet_->getID()));
glbl_cumulative += 1;
EXPECT_TRUE(testStatistics("cumulative-assigned-nas", glbl_cumulative));
}
// Checks that a client gets the address reserved (in-pool case)
// This test checks the behavior of the allocation engine in the following
// scenario:
// - Client has no lease in the database.
// - Client has an in-pool reservation.
// - Client sends SOLICIT with a hint that does matches reservation
// - Client is allocated a reserved address, not the hint.
//
// Note that DHCPv6 client can, but don't have to send any hints in its
// Solicit message.
TEST_F(AllocEngine6Test, reservedAddressInPoolSolicitMatchingHint) {
// Create reservation for the client. This is in-pool reservation,
// as the pool is 2001:db8:1::10 - 2001:db8:1::20.
createHost6(true, IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::1c"), 128);
AllocEngine engine(100);
// Assigned count should be zero.
EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID()));
// Get the cumulative count of assigned addresses.
int64_t cumulative = getStatistics("cumulative-assigned-nas",
subnet_->getID());
int64_t glbl_cumulative = getStatistics("cumulative-assigned-nas");
// Let's pretend the client sends hint 2001:db8:1::10.
Lease6Ptr lease = simpleAlloc6Test(pool_, IOAddress("2001:db8:1::1c"), true);
ASSERT_TRUE(lease);
// The hint should be ignored and the reserved address should be assigned
EXPECT_EQ("2001:db8:1::1c", lease->addr_.toText());
// Assigned count should not have been incremented.
EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID()));
EXPECT_TRUE(testStatistics("cumulative-assigned-nas",
cumulative, subnet_->getID()));
EXPECT_TRUE(testStatistics("cumulative-assigned-nas", glbl_cumulative));
}
// Checks that a client gets the address reserved (in-pool case)
// This test checks the behavior of the allocation engine in the following
// scenario:
// - Client has no lease in the database.
// - Client has an in-pool reservation.
// - Client sends REQUEST with a hint that does not match reservation
// - Client is allocated a reserved address, not the hint.
//
// Note that DHCPv6 client must send an address in REQUEST that the server
// offered in Advertise. Nevertheless, the client may ignore this requirement.
TEST_F(AllocEngine6Test, reservedAddressInPoolRequestMatchingHint) {
// Create reservation for the client. This is in-pool reservation,
// as the pool is 2001:db8:1::10 - 2001:db8:1::20.
createHost6(true, IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::1c"), 128);
AllocEngine engine(100);
// Assigned count should be zero.
EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID()));
// Get the cumulative count of assigned addresses.
int64_t cumulative = getStatistics("cumulative-assigned-nas",
subnet_->getID());
int64_t glbl_cumulative = getStatistics("cumulative-assigned-nas");
// Let's pretend the client sends hint 2001:db8:1::10.
Lease6Ptr lease = simpleAlloc6Test(pool_, IOAddress("2001:db8:1::1c"), false);
ASSERT_TRUE(lease);
// The hint should be ignored and the reserved address should be assigned
EXPECT_EQ("2001:db8:1::1c", lease->addr_.toText());
// Assigned count should have been incremented.
EXPECT_TRUE(testStatistics("assigned-nas", 1, subnet_->getID()));
cumulative += 1;
EXPECT_TRUE(testStatistics("cumulative-assigned-nas",
cumulative, subnet_->getID()));
glbl_cumulative += 1;
EXPECT_TRUE(testStatistics("cumulative-assigned-nas", glbl_cumulative));
}
// Checks that a client gets the address reserved (out-of-pool case)
// This test checks the behavior of the allocation engine in the following
// scenario:
// - Client has no lease in the database.
// - Client has an out-of-pool reservation.
// - Client sends SOLICIT without any hints.
// - Client is allocated a reserved address.
//
// Note that DHCPv6 client can, but don't have to send any hints in its
// Solicit message.
TEST_F(AllocEngine6Test, reservedAddressOutOfPoolSolicitNoHint) {
// Create reservation for the client. This is out-of-pool reservation,
// as the pool is 2001:db8:1::10 - 2001:db8:1::20.
createHost6(true, IPv6Resrv::TYPE_NA, IOAddress("2001:db8::abcd"), 128);
AllocEngine engine(100);
// Assigned count should be zero.
EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID()));
// Get the cumulative count of assigned addresses.
int64_t cumulative = getStatistics("cumulative-assigned-nas",
subnet_->getID());
int64_t glbl_cumulative = getStatistics("cumulative-assigned-nas");
Lease6Ptr lease = simpleAlloc6Test(pool_, IOAddress("::"), true, false);
ASSERT_TRUE(lease);
EXPECT_EQ("2001:db8::abcd", lease->addr_.toText());
// Assigned count should not have been incremented.
EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID()));
EXPECT_TRUE(testStatistics("cumulative-assigned-nas",
cumulative, subnet_->getID()));
EXPECT_TRUE(testStatistics("cumulative-assigned-nas", glbl_cumulative));
}
// Checks that a client gets the address reserved (out-of-pool case)
// This test checks the behavior of the allocation engine in the following
// scenario:
// - Client has no lease in the database.
// - Client has an out-of-pool reservation.
// - Client sends REQUEST without any hints.
// - Client is allocated a reserved address.
//
// Note that DHCPv6 client must send an address in REQUEST that the server
// offered in Advertise. Nevertheless, the client may ignore this requirement.
TEST_F(AllocEngine6Test, reservedAddressOutOfPoolRequestNoHint) {
// Create reservation for the client. This is out-of-pool reservation,
// as the pool is 2001:db8:1::10 - 2001:db8:1::20.
createHost6(true, IPv6Resrv::TYPE_NA, IOAddress("2001:db8::abcd"), 128);
AllocEngine engine(100);
// Assigned count should be zero.
EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID()));
// Get the cumulative count of assigned addresses.
int64_t cumulative = getStatistics("cumulative-assigned-nas",
subnet_->getID());
int64_t glbl_cumulative = getStatistics("cumulative-assigned-nas");
Lease6Ptr lease = simpleAlloc6Test(pool_, IOAddress("::"), false, false);
ASSERT_TRUE(lease);
EXPECT_EQ("2001:db8::abcd", lease->addr_.toText());
// We should not have bumped the address counter
EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID()));
EXPECT_TRUE(testStatistics("cumulative-assigned-nas",
cumulative, subnet_->getID()));
EXPECT_TRUE(testStatistics("cumulative-assigned-nas", glbl_cumulative));
}
// Checks that a client gets the address reserved (in-pool case)
// This test checks the behavior of the allocation engine in the following
// scenario:
// - Client has no lease in the database.
// - Client has an in-pool reservation.
// - Client sends SOLICIT with a hint that does not match reservation
// - Client is allocated a reserved address, not the hint.
//
// Note that DHCPv6 client can, but don't have to send any hints in its
// Solicit message.
TEST_F(AllocEngine6Test, reservedAddressOutOfPoolSolicitValidHint) {
// Create reservation for the client. This is out-of-pool reservation,
// as the pool is 2001:db8:1::10 - 2001:db8:1::20.
createHost6(true, IPv6Resrv::TYPE_NA, IOAddress("2001:db8::abcd"), 128);
AllocEngine engine(100);
// Assigned count should be zero.
EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID()));
// Get the cumulative count of assigned addresses.
int64_t cumulative = getStatistics("cumulative-assigned-nas",
subnet_->getID());
int64_t glbl_cumulative = getStatistics("cumulative-assigned-nas");
// Let's pretend the client sends hint 2001:db8:1::10.
Lease6Ptr lease = simpleAlloc6Test(pool_, IOAddress("2001:db8:1::10"), true, false);
ASSERT_TRUE(lease);
// The hint should be ignored and the reserved address should be assigned
EXPECT_EQ("2001:db8::abcd", lease->addr_.toText());
// We should not have bumped the address counter
EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID()));
EXPECT_TRUE(testStatistics("cumulative-assigned-nas",
cumulative, subnet_->getID()));
EXPECT_TRUE(testStatistics("cumulative-assigned-nas", glbl_cumulative));
}
// Checks that a client gets the address reserved (out-of-pool case)
// This test checks the behavior of the allocation engine in the following
// scenario:
// - Client has no lease in the database.
// - Client has an out-of-pool reservation.
// - Client sends REQUEST with a hint that does not match reservation
// - Client is allocated a reserved address, not the hint.
//
// Note that DHCPv6 client must send an address in REQUEST that the server
// offered in Advertise. Nevertheless, the client may ignore this requirement.
TEST_F(AllocEngine6Test, reservedAddressOutOfPoolRequestValidHint) {
// Create reservation for the client. This is out-of-pool reservation,
// as the pool is 2001:db8:1::10 - 2001:db8:1::20.
createHost6(true, IPv6Resrv::TYPE_NA, IOAddress("2001:db8::abcd"), 128);
AllocEngine engine(100);
// Assigned count should be zero.
EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID()));
// Get the cumulative count of assigned addresses.
int64_t cumulative = getStatistics("cumulative-assigned-nas",
subnet_->getID());
int64_t glbl_cumulative = getStatistics("cumulative-assigned-nas");
// Let's pretend the client sends hint 2001:db8:1::10.
Lease6Ptr lease = simpleAlloc6Test(pool_, IOAddress("2001:db8:1::10"), false, false);
ASSERT_TRUE(lease);
// The hint should be ignored and the reserved address should be assigned
EXPECT_EQ("2001:db8::abcd", lease->addr_.toText());
// We should not have bumped the address counter
EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID()));
EXPECT_TRUE(testStatistics("cumulative-assigned-nas",
cumulative, subnet_->getID()));
EXPECT_TRUE(testStatistics("cumulative-assigned-nas", glbl_cumulative));
}
// Checks that a client gets the address reserved (out-of-pool case)
// This test checks the behavior of the allocation engine in the following
// scenario:
// - Client has no lease in the database.
// - Client has an out-of-pool reservation.
// - Client sends SOLICIT with a hint that does matches reservation
// - Client is allocated a reserved address, not the hint.
//
// Note that DHCPv6 client can, but don't have to send any hints in its
// Solicit message.
TEST_F(AllocEngine6Test, reservedAddressOutOfPoolSolicitMatchingHint) {
// Create reservation for the client. This is out-of-pool reservation,
// as the pool is 2001:db8:1::10 - 2001:db8:1::20.
createHost6(true, IPv6Resrv::TYPE_NA, IOAddress("2001:db8::abcd"), 128);
AllocEngine engine(100);
// Assigned count should be zero.
EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID()));
// Get the cumulative count of assigned addresses.
int64_t cumulative = getStatistics("cumulative-assigned-nas",
subnet_->getID());
int64_t glbl_cumulative = getStatistics("cumulative-assigned-nas");
// Let's pretend the client sends hint 2001:db8:1::10.
Lease6Ptr lease = simpleAlloc6Test(pool_, IOAddress("2001:db8:1::1c"), true, false);
ASSERT_TRUE(lease);
// The hint should be ignored and the reserved address should be assigned
EXPECT_EQ("2001:db8::abcd", lease->addr_.toText());
// We should not have bumped the address counter
EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID()));
EXPECT_TRUE(testStatistics("cumulative-assigned-nas",
cumulative, subnet_->getID()));
EXPECT_TRUE(testStatistics("cumulative-assigned-nas", glbl_cumulative));
}
// Checks that a client gets the address reserved (out-of-pool case)
// This test checks the behavior of the allocation engine in the following
// scenario:
// - Client has no lease in the database.
// - Client has an out-of-pool reservation.
// - Client sends REQUEST with a hint that does not match reservation
// - Client is allocated a reserved address, not the hint.
//
// Note that DHCPv6 client must send an address in REQUEST that the server
// offered in Advertise. Nevertheless, the client may ignore this requirement.
TEST_F(AllocEngine6Test, reservedAddressOutOfPoolRequestMatchingHint) {
// Create reservation for the client. This is out-of-pool reservation,
// as the pool is 2001:db8:1::10 - 2001:db8:1::20.
createHost6(true, IPv6Resrv::TYPE_NA, IOAddress("2001:db8::abcd"), 128);
AllocEngine engine(100);
// Assigned count should be zero.
EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID()));
// Get the cumulative count of assigned addresses.
int64_t cumulative = getStatistics("cumulative-assigned-nas",
subnet_->getID());
int64_t glbl_cumulative = getStatistics("cumulative-assigned-nas");
// Let's pretend the client sends hint 2001:db8:1::10.
Lease6Ptr lease = simpleAlloc6Test(pool_, IOAddress("2001:db8:1::1c"), false, false);
ASSERT_TRUE(lease);
// The hint should be ignored and the reserved address should be assigned
EXPECT_EQ("2001:db8::abcd", lease->addr_.toText());
// We should not have bumped the address counter
EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID()));
EXPECT_TRUE(testStatistics("cumulative-assigned-nas",
cumulative, subnet_->getID()));
EXPECT_TRUE(testStatistics("cumulative-assigned-nas", glbl_cumulative));
}
// In the following situation:
// - client is assigned an address A
// - HR is made for *this* client to get B
// - client tries to get address A:
// Check that his existing lease for lease A is removed
// Check that he is assigned a new lease for B
// - verify that the number of assigned address behaves as expected
TEST_F(AllocEngine6Test, reservedAddressInPoolReassignedThis) {
AllocEngine engine(100);
// Assigned count should be zero.
EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID()));
// Get the cumulative count of assigned addresses.
int64_t cumulative = getStatistics("cumulative-assigned-nas",
subnet_->getID());
int64_t glbl_cumulative = getStatistics("cumulative-assigned-nas");
// Client gets an address
Lease6Ptr lease1 = simpleAlloc6Test(pool_, IOAddress("::"), false);
ASSERT_TRUE(lease1);
// We should have bumped the address counter
EXPECT_TRUE(testStatistics("assigned-nas", 1, subnet_->getID()));
cumulative += 1;
EXPECT_TRUE(testStatistics("cumulative-assigned-nas",
cumulative, subnet_->getID()));
glbl_cumulative += 1;
EXPECT_TRUE(testStatistics("cumulative-assigned-nas", glbl_cumulative));
// Just check that if the client requests again, it will get the same
// address.
Lease6Ptr lease2 = simpleAlloc6Test(pool_, lease1->addr_, false);
ASSERT_TRUE(lease2);
detailCompareLease(lease1, lease2);
// We should not have bumped the address counter again
EXPECT_TRUE(testStatistics("assigned-nas", 1, subnet_->getID()));
EXPECT_TRUE(testStatistics("cumulative-assigned-nas",
cumulative, subnet_->getID()));
EXPECT_TRUE(testStatistics("cumulative-assigned-nas", glbl_cumulative));
// Now admin creates a reservation for this client. This is in-pool
// reservation, as the pool is 2001:db8:1::10 - 2001:db8:1::20.
createHost6(true, IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::1c"), 128);
// Just check that this time the client will get.
Lease6Ptr lease3 = simpleAlloc6Test(pool_, lease1->addr_, false);
ASSERT_TRUE(lease3);
// Check that previous lease was not used anymore.
EXPECT_NE(lease1->addr_.toText(), lease3->addr_.toText());
// Check that the reserved address was indeed assigned.
EXPECT_EQ("2001:db8:1::1c", lease3->addr_.toText());
// Check that the old lease is gone.
Lease6Ptr old = LeaseMgrFactory::instance().getLease6(lease1->type_,
lease1->addr_);
EXPECT_FALSE(old);
// Check that the reserved lease is in the database.
Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease1->type_,
IOAddress("2001:db8:1::1c"));
ASSERT_TRUE(from_mgr);
// Now check that the lease in LeaseMgr has the same parameters
detailCompareLease(lease3, from_mgr);
// Lastly check to see that the address counter is still 1, we should have
// have decremented it on the implied release and incremented it on the reserved
EXPECT_TRUE(testStatistics("assigned-nas", 1, subnet_->getID()));
cumulative += 1;
EXPECT_TRUE(testStatistics("cumulative-assigned-nas",
cumulative, subnet_->getID()));
glbl_cumulative += 1;
EXPECT_TRUE(testStatistics("cumulative-assigned-nas", glbl_cumulative));
}
// In the following situation:
// - client X is assigned an address A
// - HR is made for client Y (*other* client) to get A
// - client X tries to get address A:
// Check that his existing lease for lease A is removed
// Check that he is assigned a new lease
TEST_F(AllocEngine6Test, reservedAddressInPoolReassignedOther) {
AllocEngine engine(100);
// Assigned count should be zero.
EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID()));
// Get the cumulative count of assigned addresses.
int64_t cumulative = getStatistics("cumulative-assigned-nas",
subnet_->getID());
int64_t glbl_cumulative = getStatistics("cumulative-assigned-nas");
// Client gets an address
Lease6Ptr lease1 = simpleAlloc6Test(pool_, IOAddress("::"), false);
ASSERT_TRUE(lease1);
// We should have bumped the address counter
EXPECT_TRUE(testStatistics("assigned-nas", 1, subnet_->getID()));
cumulative += 1;
EXPECT_TRUE(testStatistics("cumulative-assigned-nas",
cumulative, subnet_->getID()));
glbl_cumulative += 1;
EXPECT_TRUE(testStatistics("cumulative-assigned-nas", glbl_cumulative));
// Just check that if the client requests again, it will get the same
// address.
Lease6Ptr lease2 = simpleAlloc6Test(pool_, lease1->addr_, false);
ASSERT_TRUE(lease2);
detailCompareLease(lease1, lease2);
// We should not have bumped the address counter again
EXPECT_TRUE(testStatistics("assigned-nas", 1, subnet_->getID()));
EXPECT_TRUE(testStatistics("cumulative-assigned-nas",
cumulative, subnet_->getID()));
EXPECT_TRUE(testStatistics("cumulative-assigned-nas", glbl_cumulative));
// Now admin creates a reservation for this client. Let's use the
// address client X just received. Let's generate a host, but don't add it
// to the HostMgr yet.
HostPtr host = createHost6(false, IPv6Resrv::TYPE_NA, lease1->addr_, 128);
// We need to tweak reservation id: use a different DUID for client Y
vector<uint8_t> other_duid(8, 0x45);
host->setIdentifier(&other_duid[0], other_duid.size(), Host::IDENT_DUID);
// Ok, now add it to the HostMgr
addHost(host);
// Just check that this time the client will get a different lease.
Lease6Ptr lease3 = simpleAlloc6Test(pool_, lease1->addr_, false);
ASSERT_TRUE(lease3);
// Check that previous lease was not used anymore.
EXPECT_NE(lease1->addr_.toText(), lease3->addr_.toText());
// Check that the old lease is gone.
Lease6Ptr old = LeaseMgrFactory::instance().getLease6(lease1->type_,
lease1->addr_);
EXPECT_FALSE(old);
// Check that the reserved lease is in the database.
Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(lease1->type_,
lease3->addr_);
ASSERT_TRUE(from_mgr);
// Now check that the lease in LeaseMgr has the same parameters
detailCompareLease(lease3, from_mgr);
// Lastly check to see that the address counter is still 1 we should have
// have decremented it on the implied release and incremented it on the reserved
EXPECT_TRUE(testStatistics("assigned-nas", 1, subnet_->getID()));
cumulative += 1;
EXPECT_TRUE(testStatistics("cumulative-assigned-nas",
cumulative, subnet_->getID()));
glbl_cumulative += 1;
EXPECT_TRUE(testStatistics("cumulative-assigned-nas", glbl_cumulative));
}
// Checks that a reserved address for client A is not assigned when
// other clients are requesting addresses. The scenario is as follows:
// we use a regular pool with 17 addresses in it. One of them is
// reserved for client A. Now we try to allocate addresses for 30 clients
// (A is not one of them). The first 16 attempts should succeed. Then
// we run out of addresses and remaining 14 clients will get nothing.
// Finally, we check that client A still can get his reserved address.
TEST_F(AllocEngine6Test, reservedAddress) {
AllocEngine engine(100);
// Create reservation for the client. This is in-pool reservation,
// as the pool is 2001:db8:1::10 - 2001:db8:1::20.
createHost6(true, IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::12"), 128);
// Let's generate 30 DUIDs, each of them 16 bytes long
vector<DuidPtr> clients;
for (int i = 0; i < 30; i++) {
vector<uint8_t> data(16, i);
clients.push_back(DuidPtr(new DUID(data)));
}
// The default pool is 2001:db8:1::10 to 2001:db8:1::20. There's 17
// addresses in it. One of them is reserved, so this means that we should
// get 16 successes and 14 (30-16) failures.
int success = 0;
int failure = 0;
for (int i = 0; i < 30; i++) {
Pkt6Ptr query(new Pkt6(DHCPV6_REQUEST, 1234));
AllocEngine::ClientContext6 ctx(subnet_, clients[i], false, false, "",
false, query);
ctx.currentIA().iaid_ = iaid_;
findReservation(engine, ctx);
Lease6Collection leases = engine.allocateLeases6(ctx);
if (leases.empty()) {
failure++;
std::cout << "Alloc for client " << (int)i << " failed." << std::endl;
EXPECT_EQ(failure, getStatistics("v6-allocation-fail"));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-shared-network"));
EXPECT_EQ(failure, getStatistics("v6-allocation-fail-subnet"));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-no-pools"));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-classes"));
EXPECT_EQ(failure, getStatistics("v6-allocation-fail", subnet_->getID()));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-shared-network", subnet_->getID()));
EXPECT_EQ(failure, getStatistics("v6-allocation-fail-subnet", subnet_->getID()));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-no-pools", subnet_->getID()));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-classes", subnet_->getID()));
} else {
success++;
std::cout << "Alloc for client " << (int)i << " succeeded:"
<< leases[0]->addr_.toText() << std::endl;
// The assigned addresses must not match the one reserved.
EXPECT_NE("2001:db8:1::12", leases[0]->addr_.toText());
}
}
EXPECT_EQ(16, success);
EXPECT_EQ(14, failure);
// We're now pretty sure that any clients other than the reserved address
// will not get any service. Now let's check if the client that has the
// address reserved, will get it (despite the pool being depleted).
AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false,
Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234)));
ctx.currentIA().iaid_ = iaid_;
findReservation(engine, ctx);
Lease6Collection leases = engine.allocateLeases6(ctx);
ASSERT_EQ(1, leases.size());
EXPECT_EQ("2001:db8:1::12", leases[0]->addr_.toText());
}
// Checks if the allocateLeases throws exceptions for invalid input data.
TEST_F(AllocEngine6Test, allocateLeasesInvalidData) {
AllocEngine engine(100);
// That looks like a valid context.
AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false,
Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234)));
ctx.currentIA().iaid_ = iaid_;
Lease6Collection leases;
// Let's break it!
ctx.subnet_.reset();
// Subnet is required for allocation, so we should get no leases.
EXPECT_NO_THROW(leases = engine.allocateLeases6(ctx));
ASSERT_TRUE(leases.empty());
EXPECT_EQ(0, getStatistics("v6-allocation-fail"));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-shared-network"));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-subnet"));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-no-pools"));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-classes"));
EXPECT_EQ(0, getStatistics("v6-allocation-fail", subnet_->getID()));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-shared-network", subnet_->getID()));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-subnet", subnet_->getID()));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-no-pools", subnet_->getID()));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-classes", subnet_->getID()));
// Let's fix this and break it in a different way.
ctx.subnet_ = subnet_;
ctx.duid_.reset();
// We must know who we're allocating for. No duid = no service.
EXPECT_NO_THROW(leases = engine.allocateLeases6(ctx));
ASSERT_TRUE(leases.empty());
EXPECT_EQ(0, getStatistics("v6-allocation-fail"));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-shared-network"));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-subnet"));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-no-pools"));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-classes"));
EXPECT_EQ(0, getStatistics("v6-allocation-fail", subnet_->getID()));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-shared-network", subnet_->getID()));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-subnet", subnet_->getID()));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-no-pools", subnet_->getID()));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-classes", subnet_->getID()));
}
// Checks whether an address can be renewed (simple case, no reservation tricks)
TEST_F(AllocEngine6Test, addressRenewal) {
AllocEngine engine(100);
// Assigned count should zero.
EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID()));
// Get the cumulative count of assigned addresses.
int64_t cumulative = getStatistics("cumulative-assigned-nas",
subnet_->getID());
int64_t glbl_cumulative = getStatistics("cumulative-assigned-nas");
Lease6Collection leases;
leases = allocateTest(engine, pool_, IOAddress("::"), false, true);
ASSERT_EQ(1, leases.size());
// Assigned count should be one.
EXPECT_TRUE(testStatistics("assigned-nas", 1, subnet_->getID()));
cumulative += 1;
EXPECT_TRUE(testStatistics("cumulative-assigned-nas",
cumulative, subnet_->getID()));
glbl_cumulative += 1;
EXPECT_TRUE(testStatistics("cumulative-assigned-nas", glbl_cumulative));
// This is what the client will send in his renew message.
AllocEngine::HintContainer hints;
hints.push_back(AllocEngine::Resource(leases[0]->addr_, 128));
Lease6Collection renewed = renewTest(engine, pool_, hints, IN_SUBNET, IN_POOL);
ASSERT_EQ(1, renewed.size());
// Check that the lease was indeed renewed and hasn't changed
// (i.e. the same address, preferred and valid lifetimes)
/// @todo: use leaseCompare, but ignore cltt_
EXPECT_EQ(leases[0]->addr_, renewed[0]->addr_);
EXPECT_EQ(leases[0]->type_, renewed[0]->type_);
EXPECT_EQ(leases[0]->preferred_lft_, renewed[0]->preferred_lft_);
EXPECT_EQ(leases[0]->valid_lft_, renewed[0]->valid_lft_);
// Assigned count should still be one.
EXPECT_TRUE(testStatistics("assigned-nas", 1, subnet_->getID()));
EXPECT_TRUE(testStatistics("cumulative-assigned-nas",
cumulative, subnet_->getID()));
EXPECT_TRUE(testStatistics("cumulative-assigned-nas", glbl_cumulative));
}
// Checks whether an address can be renewed (in-pool reservation)
TEST_F(AllocEngine6Test, reservedAddressRenewal) {
// Create reservation for the client. This is in-pool reservation,
// as the pool is 2001:db8:1::10 - 2001:db8:1::20.
createHost6(true, IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::1c"), 128);
AllocEngine engine(100);
// Assigned count should zero.
EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID()));
// Get the cumulative count of assigned addresses.
int64_t cumulative = getStatistics("cumulative-assigned-nas",
subnet_->getID());
int64_t glbl_cumulative = getStatistics("cumulative-assigned-nas");
Lease6Collection leases;
leases = allocateTest(engine, pool_, IOAddress("::"), false, true);
ASSERT_EQ(1, leases.size());
ASSERT_EQ("2001:db8:1::1c", leases[0]->addr_.toText());
// Assigned count should be one.
EXPECT_TRUE(testStatistics("assigned-nas", 1, subnet_->getID()));
cumulative += 1;
EXPECT_TRUE(testStatistics("cumulative-assigned-nas",
cumulative, subnet_->getID()));
glbl_cumulative += 1;
EXPECT_TRUE(testStatistics("cumulative-assigned-nas", glbl_cumulative));
// This is what the client will send in his renew message.
AllocEngine::HintContainer hints;
hints.push_back(AllocEngine::Resource(leases[0]->addr_, 128));
Lease6Collection renewed = renewTest(engine, pool_, hints, IN_SUBNET, IN_POOL);
ASSERT_EQ(1, renewed.size());
ASSERT_EQ("2001:db8:1::1c", leases[0]->addr_.toText());
// Assigned count should still be one.
EXPECT_TRUE(testStatistics("assigned-nas", 1, subnet_->getID()));
EXPECT_TRUE(testStatistics("cumulative-assigned-nas",
cumulative, subnet_->getID()));
EXPECT_TRUE(testStatistics("cumulative-assigned-nas", glbl_cumulative));
}
// Checks whether a single host can have more than one reservation.
//
/// @todo: as of #3677, this does not work. When processing solicit with two
/// IA_NAs and two reservations, there currently no way to indicate that
/// the first reservation should be used for the first IA and the second
/// reservation for the second IA. This works for Requests and Renews, though.
/// In both of those messages, when processing of the first IA is complete,
/// we have a lease in the database. Based on that, when processing the second
/// IA we can detect that the first reserved address is in use already and
/// use the second reservation.
TEST_F(AllocEngine6Test, DISABLED_reserved2AddressesSolicit) {
// Create reservation for the client. This is in-pool reservation,
// as the pool is 2001:db8:1::10 - 2001:db8:1::20.
// Two addresses are reserved: 2001:db8:1::babe and 2001:db8:1::cafe
HostPtr host = createHost6(true, IPv6Resrv::TYPE_NA,
IOAddress("2001:db8:1::babe"), 128);
IPv6Resrv resv2(IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::cafe"), 128);
host->addReservation(resv2);
CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
CfgMgr::instance().commit();
AllocEngine engine(100);
AllocEngine::ClientContext6 ctx1(subnet_, duid_, false, false, "", true,
Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234)));
ctx1.currentIA().iaid_ = iaid_;
ctx1.currentIA().type_ = pool_->getType();
Lease6Collection leases1;
findReservation(engine, ctx1);
EXPECT_NO_THROW(leases1 = engine.allocateLeases6(ctx1));
ASSERT_EQ(1, leases1.size());
EXPECT_EQ("2001:db8:1::babe", leases1[0]->addr_.toText());
// Double check that repeating the same duid/type/iaid will end up with
// the same address.
AllocEngine::ClientContext6 ctx2(subnet_, duid_, false, false, "", true,
Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234)));
ctx2.currentIA().iaid_ = iaid_;
ctx2.currentIA().type_ = pool_->getType();
Lease6Collection leases2;
findReservation(engine, ctx2);
EXPECT_NO_THROW(leases2 = engine.allocateLeases6(ctx2));
EXPECT_EQ(1, leases2.size());
EXPECT_EQ("2001:db8:1::babe", leases2[0]->addr_.toText());
// Ok, now the tricky part. Request allocation for the same duid and type, but
// different iaid. The second address should be assigned.
AllocEngine::ClientContext6 ctx3(subnet_, duid_, false, false, "", true,
Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234)));
ctx3.currentIA().iaid_ = iaid_ + 1;
ctx3.currentIA().type_ = pool_->getType();
Lease6Collection leases3;
findReservation(engine, ctx3);
EXPECT_NO_THROW(leases3 = engine.allocateLeases6(ctx3));
ASSERT_EQ(1, leases3.size());
EXPECT_EQ("2001:db8:1::cafe", leases3[0]->addr_.toText());
}
// Checks whether a single host can have more than one reservation.
TEST_F(AllocEngine6Test, reserved2Addresses) {
// Create reservation for the client. This is in-pool reservation,
// as the pool is 2001:db8:1::10 - 2001:db8:1::20.
// Two addresses are reserved: 2001:db8:1::babe and 2001:db8:1::cafe
HostPtr host = createHost6(true, IPv6Resrv::TYPE_NA,
IOAddress("2001:db8:1::babe"), 128);
IPv6Resrv resv2(IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::cafe"), 128);
host->addReservation(resv2);
CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
CfgMgr::instance().commit();
AllocEngine engine(100);
AllocEngine::ClientContext6 ctx1(subnet_, duid_, false, false, "", false,
Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234)));
ctx1.currentIA().iaid_ = iaid_;
ctx1.currentIA().type_ = pool_->getType();
Lease6Collection leases1;
findReservation(engine, ctx1);
EXPECT_NO_THROW(leases1 = engine.allocateLeases6(ctx1));
ASSERT_EQ(1, leases1.size());
EXPECT_EQ("2001:db8:1::babe", leases1[0]->addr_.toText());
// Double check that repeating the same duid/type/iaid will end up with
// the same address.
AllocEngine::ClientContext6 ctx2(subnet_, duid_, false, false, "", false,
Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234)));
ctx2.currentIA().iaid_ = iaid_;
ctx2.currentIA().type_ = pool_->getType();
Lease6Collection leases2;
findReservation(engine, ctx2);
EXPECT_NO_THROW(leases2 = engine.allocateLeases6(ctx2));
EXPECT_EQ(1, leases2.size());
EXPECT_EQ("2001:db8:1::babe", leases2[0]->addr_.toText());
// Ok, now the tricky part. Request allocation for the same duid and type, but
// different iaid. The second address should be assigned.
AllocEngine::ClientContext6 ctx3(subnet_, duid_, false, false, "", false,
Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234)));
ctx3.currentIA().iaid_ = iaid_ + 1;
ctx3.currentIA().type_ = pool_->getType();
Lease6Collection leases3;
findReservation(engine, ctx3);
EXPECT_NO_THROW(leases3 = engine.allocateLeases6(ctx3));
ASSERT_EQ(1, leases3.size());
EXPECT_EQ("2001:db8:1::cafe", leases3[0]->addr_.toText());
}
// Checks whether address can change during renew (if there is a new
// reservation for this client)
TEST_F(AllocEngine6Test, reservedAddressRenewChange) {
AllocEngine engine(100);
Lease6Collection leases;
leases = allocateTest(engine, pool_, IOAddress("::"), false, true);
ASSERT_EQ(1, leases.size());
ASSERT_NE("2001:db8:1::1c", leases[0]->addr_.toText());
// This is what the client will send in his renew message.
AllocEngine::HintContainer hints;
hints.push_back(AllocEngine::Resource(leases[0]->addr_, 128));
// Create reservation for the client. This is in-pool reservation,
// as the pool is 2001:db8:1::10 - 2001:db8:1::20.
createHost6(true, IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::1c"), 128);
Lease6Collection renewed = renewTest(engine, pool_, hints, IN_SUBNET, IN_POOL);
ASSERT_EQ(1, renewed.size());
ASSERT_EQ("2001:db8:1::1c", renewed[0]->addr_.toText());
}
// Checks whether address can change during renew (if there is a new
// reservation for this address for another client)
TEST_F(AllocEngine6Test, reservedAddressRenewReserved) {
AllocEngine engine(100);
Lease6Collection leases;
leases = allocateTest(engine, pool_, IOAddress("::"), false, true);
ASSERT_EQ(1, leases.size());
// This is what the client will send in his renew message.
AllocEngine::HintContainer hints;
hints.push_back(AllocEngine::Resource(leases[0]->addr_, 128));
// Create reservation for this address, but for another client.
// This is in-pool reservation, as the pool is 2001:db8:1::10 - 2001:db8:1::20.
HostPtr host = createHost6(false, IPv6Resrv::TYPE_NA, leases[0]->addr_, 128);
// We need to tweak reservation id: use a different DUID for client Y
vector<uint8_t> other_duid(8, 0x45);
host->setIdentifier(&other_duid[0], other_duid.size(), Host::IDENT_DUID);
// Ok, now add it to the HostMgr
CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
CfgMgr::instance().commit();
Lease6Collection renewed = renewTest(engine, pool_, hints, IN_SUBNET, IN_POOL);
ASSERT_EQ(1, renewed.size());
// Check that we no longer have the reserved address.
ASSERT_NE(leases[0]->addr_.toText(), renewed[0]->addr_.toText());
// Check that the lease for the now reserved address is no longer in
// the lease database.
Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
leases[0]->addr_);
EXPECT_FALSE(from_mgr);
}
/// @todo: The following methods are tested indirectly by allocateLeases6()
/// tests, but could use more direct testing:
/// - AllocEngine::allocateUnreservedLeases6
/// - AllocEngine::allocateReservedLeases6
/// - AllocEngine::removeNonmatchingReservedLeases6
/// - AllocEngine::removeLeases
/// - AllocEngine::removeNonreservedLeases6
// Checks that a client gets the address reserved (in-pool case)
// This test checks the behavior of the allocation engine in the following
// scenario:
// - Client has no lease in the database.
// - Client has an in-pool reservation.
// - Client sends SOLICIT without any hints.
// - Client is allocated a reserved address.
//
// Note that DHCPv6 client can, but don't have to send any hints in its
// Solicit message.
TEST_F(AllocEngine6Test, reservedAddressByMacInPoolSolicitNoHint) {
// Create reservation for the client. This is in-pool reservation,
// as the pool is 2001:db8:1::10 - 2001:db8:1::20.
createHost6HWAddr(true, IPv6Resrv::TYPE_NA, hwaddr_,
IOAddress("2001:db8:1::1c"), 128);
AllocEngine engine(100);
Lease6Ptr lease = simpleAlloc6Test(pool_, IOAddress("::"), true);
ASSERT_TRUE(lease);
EXPECT_EQ("2001:db8:1::1c", lease->addr_.toText());
}
// Checks that a client gets the address reserved (in-pool case)
// This test checks the behavior of the allocation engine in the following
// scenario:
// - Client has no lease in the database.
// - Client has an in-pool reservation.
// - Client sends REQUEST without any hints.
// - Client is allocated a reserved address.
//
// Note that DHCPv6 client must send an address in REQUEST that the server
// offered in Advertise. Nevertheless, the client may ignore this requirement.
TEST_F(AllocEngine6Test, reservedAddressByMacInPoolRequestNoHint) {
// Create reservation for the client. This is in-pool reservation,
// as the pool is 2001:db8:1::10 - 2001:db8:1::20.
createHost6HWAddr(true, IPv6Resrv::TYPE_NA, hwaddr_,
IOAddress("2001:db8:1::1c"), 128);
AllocEngine engine(100);
Lease6Ptr lease = simpleAlloc6Test(pool_, IOAddress("::"), false);
ASSERT_TRUE(lease);
EXPECT_EQ("2001:db8:1::1c", lease->addr_.toText());
}
// Checks that a client gets the address reserved (in-pool case)
// This test checks the behavior of the allocation engine in the following
// scenario:
// - Client has no lease in the database.
// - Client has an in-pool reservation.
// - Client sends SOLICIT with a hint that does not match reservation
// - Client is allocated a reserved address, not the hint.
//
// Note that DHCPv6 client can, but don't have to send any hints in its
// Solicit message.
TEST_F(AllocEngine6Test, reservedAddressByMacInPoolSolicitValidHint) {
// Create reservation for the client. This is in-pool reservation,
// as the pool is 2001:db8:1::10 - 2001:db8:1::20.
createHost6HWAddr(true, IPv6Resrv::TYPE_NA, hwaddr_,
IOAddress("2001:db8:1::1c"), 128);
AllocEngine engine(100);
// Let's pretend the client sends hint 2001:db8:1::10.
Lease6Ptr lease = simpleAlloc6Test(pool_, IOAddress("2001:db8:1::10"), true);
ASSERT_TRUE(lease);
// The hint should be ignored and the reserved address should be assigned
EXPECT_EQ("2001:db8:1::1c", lease->addr_.toText());
}
// Checks that a client gets the address reserved (in-pool case)
// This test checks the behavior of the allocation engine in the following
// scenario:
// - Client has no lease in the database.
// - Client has an in-pool reservation.
// - Client sends REQUEST with a hint that does not match reservation
// - Client is allocated a reserved address, not the hint.
//
// Note that DHCPv6 client must send an address in REQUEST that the server
// offered in Advertise. Nevertheless, the client may ignore this requirement.
TEST_F(AllocEngine6Test, reservedAddressByMacInPoolRequestValidHint) {
// Create reservation for the client This is in-pool reservation,
// as the pool is 2001:db8:1::10 - 2001:db8:1::20.
createHost6HWAddr(true, IPv6Resrv::TYPE_NA, hwaddr_,
IOAddress("2001:db8:1::1c"), 128);
AllocEngine engine(100);
// Let's pretend the client sends hint 2001:db8:1::10.
Lease6Ptr lease = simpleAlloc6Test(pool_, IOAddress("2001:db8:1::10"), false);
ASSERT_TRUE(lease);
// The hint should be ignored and the reserved address should be assigned
EXPECT_EQ("2001:db8:1::1c", lease->addr_.toText());
}
// This test checks that the allocation engine can delegate the long prefix.
// The pool with prefix of 64 and with long delegated prefix has a very
// high capacity. The number of attempts that the allocation engine makes
// to allocate the prefix for high capacity pools is equal to the capacity
// value. This test verifies that the prefix can be allocated in that
// case.
TEST_F(AllocEngine6Test, largePdPool) {
AllocEngine engine(0);
// Remove the default PD pool.
subnet_->delPools(Lease::TYPE_PD);
// Configure the PD pool with the prefix length of /80 and the delegated
// length /96.
Pool6Ptr pool(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:1:2::"), 80, 96));
subnet_->addPool(pool);
// We should have got exactly one lease.
Lease6Collection leases = allocateTest(engine, pool, IOAddress("::"),
false, true);
ASSERT_EQ(1, leases.size());
}
// This test checks that the allocation engine can pick a pool which has smaller
// delegated prefix length than the hint.
TEST_F(AllocEngine6Test, largePdPoolPreferLower) {
AllocEngine engine(0);
// Remove the default PD pool.
subnet_->delPools(Lease::TYPE_PD);
// Configure the PD pool with the prefix length of /80 and the delegated
// length /96.
Pool6Ptr pool(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:1:2::"), 80, 96));
subnet_->addPool(pool);
Pool6Ptr pool2(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:1:3::"), 72, 80));
subnet_->addPool(pool2);
// We should have got exactly one lease.
// Even though the hint is from the first pool, the second pool is preferred.
Lease6Collection leases = allocateTest(engine, pool2, IOAddress("2001:db8:1:2::"),
false, true, 92);
ASSERT_EQ(1, leases.size());
}
// This test checks that the allocation engine can pick a pool which has smaller
// delegated prefix length than the hint. However the already present lease in
// the database is used and the hint delegated length is ignored.
TEST_F(AllocEngine6Test, largePdPoolPreferExistingInsteadOfLower) {
AllocEngine engine(0);
// Remove the default PD pool.
subnet_->delPools(Lease::TYPE_PD);
// Configure the PD pool with the prefix length of /80 and the delegated
// length /96.
Pool6Ptr pool(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:1:2::"), 80, 96));
subnet_->addPool(pool);
Pool6Ptr pool2(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:1:3::"), 72, 80));
subnet_->addPool(pool2);
// Let's create a lease and put it in the LeaseMgr
// Even if the prefix length in the hint does not match, the allocation
// engine should use the existing lease.
Lease6Ptr used(new Lease6(Lease::TYPE_PD, IOAddress("2001:db8:1:2::"),
duid_, iaid_, 300, 400, subnet_->getID(), HWAddrPtr(), 96));
ASSERT_TRUE(LeaseMgrFactory::instance().addLease(used));
// We should have got exactly one lease.
Lease6Collection leases = allocateTest(engine, pool, IOAddress("2001:db8:1:3::"),
false, true, 92);
ASSERT_EQ(1, leases.size());
}
// This test checks that the allocation engine can pick a pool which has exact
// delegated prefix length as the hint.
TEST_F(AllocEngine6Test, largePdPoolPreferEqual) {
AllocEngine engine(0);
// Remove the default PD pool.
subnet_->delPools(Lease::TYPE_PD);
// Configure the PD pool with the prefix length of /80 and the delegated
// length /96.
Pool6Ptr pool(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:1:2::"), 80, 96));
subnet_->addPool(pool);
Pool6Ptr pool2(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:1:3::"), 72, 80));
subnet_->addPool(pool2);
// We should have got exactly one lease.
// Even though the hint is from the first pool, the second pool is preferred.
Lease6Collection leases = allocateTest(engine, pool2, IOAddress("2001:db8:1:2::"),
false, true, 80);
ASSERT_EQ(1, leases.size());
}
// This test checks that the allocation engine can pick a pool which has exact
// delegated prefix length as the hint. However the already present lease in
// the database is used and the hint delegated length is ignored.
TEST_F(AllocEngine6Test, largePdPoolPreferExistingInsteadOfEqual) {
AllocEngine engine(0);
// Remove the default PD pool.
subnet_->delPools(Lease::TYPE_PD);
// Configure the PD pool with the prefix length of /80 and the delegated
// length /96.
Pool6Ptr pool(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:1:2::"), 80, 96));
subnet_->addPool(pool);
Pool6Ptr pool2(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:1:3::"), 72, 80));
subnet_->addPool(pool2);
// Let's create a lease and put it in the LeaseMgr
// Even if the prefix length in the hint does not match, the allocation
// engine should use the existing lease.
Lease6Ptr used(new Lease6(Lease::TYPE_PD, IOAddress("2001:db8:1:2::"),
duid_, iaid_, 300, 400, subnet_->getID(), HWAddrPtr(), 96));
ASSERT_TRUE(LeaseMgrFactory::instance().addLease(used));
// We should have got exactly one lease.
Lease6Collection leases = allocateTest(engine, pool, IOAddress("2001:db8:1:3::"),
false, true, 80);
ASSERT_EQ(1, leases.size());
}
// This test checks that the allocation engine can pick a pool which has greater
// delegated prefix length than the hint.
TEST_F(AllocEngine6Test, largePdPoolPreferHigher) {
AllocEngine engine(0);
// Remove the default PD pool.
subnet_->delPools(Lease::TYPE_PD);
// Configure the PD pool with the prefix length of /80 and the delegated
// length /96.
Pool6Ptr pool(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:1:2::"), 80, 96));
subnet_->addPool(pool);
Pool6Ptr pool2(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:1:3::"), 72, 80));
subnet_->addPool(pool2);
// We should have got exactly one lease.
// Even though the first pool also matches the condition, because of the hint,
// the second pool is preferred.
Lease6Collection leases = allocateTest(engine, pool2, IOAddress("2001:db8:1:3::"),
false, true, 64);
ASSERT_EQ(1, leases.size());
}
// This test checks that the allocation engine can pick a pool which has greater
// delegated prefix length than the hint. However the already present lease in
// the database is used and the hint delegated length is ignored.
TEST_F(AllocEngine6Test, largePdPoolPreferExistingInsteadOfHigher) {
AllocEngine engine(0);
// Remove the default PD pool.
subnet_->delPools(Lease::TYPE_PD);
// Configure the PD pool with the prefix length of /80 and the delegated
// length /96.
Pool6Ptr pool(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:1:2::"), 80, 96));
subnet_->addPool(pool);
Pool6Ptr pool2(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8:1:3::"), 72, 80));
subnet_->addPool(pool2);
// Let's create a lease and put it in the LeaseMgr
// Even if the prefix length in the hint does not match, the allocation
// engine should use the existing lease.
Lease6Ptr used(new Lease6(Lease::TYPE_PD, IOAddress("2001:db8:1:2::"),
duid_, iaid_, 300, 400, subnet_->getID(), HWAddrPtr(), 96));
ASSERT_TRUE(LeaseMgrFactory::instance().addLease(used));
// We should have got exactly one lease.
Lease6Collection leases = allocateTest(engine, pool, IOAddress("2001:db8:1:3::"),
false, true, 64);
ASSERT_EQ(1, leases.size());
}
// This test checks that the allocation engine can delegate addresses
// from ridiculously large pool. The configuration provides 2^80 or
// 1208925819614629174706176 addresses. We used to have a bug that would
// confuse the allocation engine if the number of available addresses
// was larger than 2^32.
TEST_F(AllocEngine6Test, largePoolOver32bits) {
AllocEngine engine(0);
// Configure 2001:db8::/32 subnet
subnet_ = Subnet6::create(IOAddress("2001:db8::"), 32, 1, 2, 3, 4, SubnetID(10));
// Configure the NA pool of /48. So there are 2^80 addresses there. Make
// sure that we still can handle cases where number of available addresses
// is over max_uint64
Pool6Ptr pool(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1::"), 48));
subnet_->addPool(pool);
// We should have got exactly one lease.
Lease6Collection leases = allocateTest(engine, pool, IOAddress("::"),
false, true);
ASSERT_EQ(1, leases.size());
}
// This test verifies that it is possible to override the number of allocation
// attempts made by the allocation engine for a single lease.
TEST_F(AllocEngine6Test, largeAllocationAttemptsOverride) {
// Remove the default NA pools.
subnet_->delPools(Lease::TYPE_NA);
subnet_->delPools(Lease::TYPE_PD);
// Add exactly one pool with many addresses.
Pool6Ptr pool(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1::"), 56));
subnet_->addPool(pool);
// Allocate 5 addresses from the pool configured.
for (int i = 0; i < 5; ++i) {
DuidPtr duid = DuidPtr(new DUID(vector<uint8_t>(12,
static_cast<uint8_t>(i))));
// Get the unique IAID.
const uint32_t iaid = 3568 + i;
// Construct the unique address from the pool.
std::ostringstream address;
address << "2001:db8:1::";
address << i;
// Allocate the lease.
Lease6Ptr lease(new Lease6(Lease::TYPE_NA, IOAddress(address.str()),
duid, iaid, 501, 502, subnet_->getID(), HWAddrPtr()));
ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
}
// Try to use the allocation engine to allocate the lease. The iterative
// allocator will pick the addresses already allocated until it finds the
// available address. Since, we have restricted the number of attempts the
// allocation should fail.
AllocEngine engine(3);
Lease6Collection leases = allocateTest(engine, pool_, IOAddress("::"),
false, true);
ASSERT_TRUE(leases.empty());
EXPECT_EQ(1, getStatistics("v6-allocation-fail"));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-shared-network"));
EXPECT_EQ(1, getStatistics("v6-allocation-fail-subnet"));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-no-pools"));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-classes"));
EXPECT_EQ(1, getStatistics("v6-allocation-fail", subnet_->getID()));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-shared-network", subnet_->getID()));
EXPECT_EQ(1, getStatistics("v6-allocation-fail-subnet", subnet_->getID()));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-no-pools", subnet_->getID()));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-classes", subnet_->getID()));
// This time, lets allow more attempts, and expect that the allocation will
// be successful.
AllocEngine engine2(6);
leases = allocateTest(engine2, pool_, IOAddress("::"), false, true);
ASSERT_EQ(1, leases.size());
}
// This test checks if an expired declined lease can be reused in SOLICIT (fake allocation)
TEST_F(AllocEngine6Test, solicitReuseDeclinedLease6) {
AllocEnginePtr engine(new AllocEngine(100));
ASSERT_TRUE(engine);
// Now prepare a configuration with single address pool.
// Create one subnet with a pool holding one address.
string addr_txt("2001:db8:1::ad");
IOAddress addr(addr_txt);
initSubnet(IOAddress("2001:db8:1::"), addr, addr);
// Use information that is different than what we'll request
Lease6Ptr declined = generateDeclinedLease(addr_txt, 100, -10);
ASSERT_TRUE(declined->expired());
// CASE 1: Asking for any address
Lease6Ptr assigned;
testReuseLease6(engine, declined, "::", true, SHOULD_PASS, assigned);
// Check that we got that single lease
ASSERT_TRUE(assigned);
EXPECT_EQ(addr, assigned->addr_);
// Do all checks on the lease (if subnet-id, preferred/valid times are ok etc.)
checkLease6(duid_, assigned, Lease::TYPE_NA, 128);
// CASE 2: Asking specifically for this address
testReuseLease6(engine, declined, addr_txt, true, SHOULD_PASS, assigned);
// Check that we got that single lease
ASSERT_TRUE(assigned);
EXPECT_EQ(addr, assigned->addr_);
}
// This test checks if an expired declined lease can be reused when responding
// to REQUEST (actual allocation)
TEST_F(AllocEngine6Test, requestReuseDeclinedLease6) {
AllocEnginePtr engine(new AllocEngine(100));
ASSERT_TRUE(engine);
// Now prepare a configuration with single address pool.
string addr_txt("2001:db8::7");
IOAddress addr(addr_txt);
initSubnet(IOAddress("2001:db8::"), addr, addr);
// Now create a declined lease, decline it and rewind its cltt, so it
// is expired.
Lease6Ptr declined = generateDeclinedLease(addr_txt, 100, -10);
// Asking specifically for this address
Lease6Ptr assigned;
testReuseLease6(engine, declined, addr_txt, false, SHOULD_PASS, assigned);
// Check that we got it.
ASSERT_TRUE(assigned);
EXPECT_EQ(addr, assigned->addr_);
// Check that the lease is indeed updated in LeaseMgr
Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
addr);
ASSERT_TRUE(from_mgr);
// Now check that the lease in LeaseMgr has the same parameters
detailCompareLease(assigned, from_mgr);
}
// This test checks if statistics are not updated when expired declined lease
// is reused when responding to SOLICIT (fake allocation)
TEST_F(AllocEngine6Test, solicitReuseDeclinedLease6Stats) {
// Now prepare for SOLICIT processing
AllocEnginePtr engine(new AllocEngine(100));
ASSERT_TRUE(engine);
// Now prepare a configuration with single address pool.
string addr_txt("2001:db8:1::1");
IOAddress addr(addr_txt);
initSubnet(IOAddress("2001:db8:1::"), addr, addr);
// Stats should be zero.
EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID()));
EXPECT_TRUE(testStatistics("declined-addresses", 0));
EXPECT_TRUE(testStatistics("reclaimed-declined-addresses", 0));
EXPECT_TRUE(testStatistics("declined-addresses", 0, subnet_->getID()));
EXPECT_TRUE(testStatistics("reclaimed-declined-addresses", 0, subnet_->getID()));
// Get the cumulative count of assigned addresses.
int64_t cumulative = getStatistics("cumulative-assigned-nas",
subnet_->getID());
int64_t glbl_cumulative = getStatistics("cumulative-assigned-nas");
// Now create a declined lease, decline it and rewind its cltt, so it
// is expired.
Lease6Ptr declined = generateDeclinedLease(addr_txt, 100, -10);
// Ask for any address. There's only one address in the pool, so it doesn't
// matter much.
Lease6Ptr assigned;
testReuseLease6(engine, declined, "::", true, SHOULD_PASS, assigned);
// Check that the stats were not modified
EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID()));
EXPECT_TRUE(testStatistics("cumulative-assigned-nas",
cumulative, subnet_->getID()));
EXPECT_TRUE(testStatistics("cumulative-assigned-nas", glbl_cumulative));
EXPECT_TRUE(testStatistics("declined-addresses", 0));
EXPECT_TRUE(testStatistics("reclaimed-declined-addresses", 0));
EXPECT_TRUE(testStatistics("declined-addresses", 0, subnet_->getID()));
EXPECT_TRUE(testStatistics("reclaimed-declined-addresses", 0, subnet_->getID()));
}
// This test checks if statistics are updated when expired declined lease
// is reused when responding to REQUEST (actual allocation)
TEST_F(AllocEngine6Test, requestReuseDeclinedLease6Stats) {
// Prepare for REQUEST processing.
AllocEnginePtr engine(new AllocEngine(100));
ASSERT_TRUE(engine);
// Now prepare a configuration with single address pool.
string addr_txt("2001:db8::1");
IOAddress addr(addr_txt);
initSubnet(IOAddress("2001:db8::"), addr, addr);
// Stats should be zero.
EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID()));
EXPECT_TRUE(testStatistics("declined-addresses", 0));
EXPECT_TRUE(testStatistics("reclaimed-declined-addresses", 0));
EXPECT_TRUE(testStatistics("declined-addresses", 0, subnet_->getID()));
EXPECT_TRUE(testStatistics("reclaimed-declined-addresses", 0, subnet_->getID()));
// Get the cumulative count of assigned addresses.
int64_t cumulative = getStatistics("cumulative-assigned-nas",
subnet_->getID());
int64_t glbl_cumulative = getStatistics("cumulative-assigned-nas");
// Now create a declined lease, decline it and rewind its cltt, so it
// is expired.
Lease6Ptr declined = generateDeclinedLease(addr_txt, 100, -10);
// Ask for any address. There's only one address in the pool, so it doesn't
// matter much.
Lease6Ptr assigned;
testReuseLease6(engine, declined, "::", false, SHOULD_PASS, assigned);
// Check that the stats were modified as expected.
// assigned-nas should NOT get incremented. Currently we do not adjust assigned
// counts when we declines
// declined-addresses will -1, as the artificial creation of declined lease
// doesn't increment it from zero. reclaimed-declined-addresses will be 1
// because the leases are implicitly reclaimed before they can be assigned.
EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID()));
cumulative += 1;
EXPECT_TRUE(testStatistics("cumulative-assigned-nas",
cumulative, subnet_->getID()));
glbl_cumulative += 1;
EXPECT_TRUE(testStatistics("cumulative-assigned-nas", glbl_cumulative));
EXPECT_TRUE(testStatistics("declined-addresses", -1));
EXPECT_TRUE(testStatistics("reclaimed-declined-addresses", 1));
EXPECT_TRUE(testStatistics("declined-addresses", -1, subnet_->getID()));
EXPECT_TRUE(testStatistics("reclaimed-declined-addresses", 1, subnet_->getID()));
}
// This test checks if an expired-reclaimed lease can be reused by
// a returning client via REQUEST, rather than renew/rebind. This
// would be typical of cable modem clients which do not retain lease
// data across reboots.
TEST_F(AllocEngine6Test, reuseReclaimedExpiredViaRequest) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
IOAddress addr("2001:db8:1::ad");
CfgMgr& cfg_mgr = CfgMgr::instance();
cfg_mgr.clear(); // Get rid of the default test configuration
// Create configuration similar to other tests, but with a single address pool
subnet_ = Subnet6::create(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4, SubnetID(10));
pool_ = Pool6Ptr(new Pool6(Lease::TYPE_NA, addr, addr)); // just a single address
subnet_->addPool(pool_);
cfg_mgr.getStagingCfg()->getCfgSubnets6()->add(subnet_);
cfg_mgr.commit();
// Verify relevant stats are zero.
EXPECT_TRUE(testStatistics("assigned-nas", 0, subnet_->getID()));
EXPECT_TRUE(testStatistics("reclaimed-leases", 0));
EXPECT_TRUE(testStatistics("reclaimed-leases", 0, subnet_->getID()));
// Get the cumulative count of assigned addresses.
int64_t cumulative = getStatistics("cumulative-assigned-nas",
subnet_->getID());
int64_t glbl_cumulative = getStatistics("cumulative-assigned-nas");
// Let's create an expired lease
Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, duid_, iaid_,
501, 502, subnet_->getID(), HWAddrPtr()));
lease->cltt_ = time(0) - 500; // Allocated 500 seconds ago
lease->valid_lft_ = 495; // Lease was valid for 495 seconds
lease->fqdn_fwd_ = true;
lease->fqdn_rev_ = true;
lease->hostname_ = "myhost.example.com.";
lease->state_ = Lease::STATE_EXPIRED_RECLAIMED;
ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
// Verify that the lease state is indeed expired-reclaimed
EXPECT_EQ(lease->state_, Lease::STATE_EXPIRED_RECLAIMED);
// Same client comes along and issues a request
AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false,
Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234)));
EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
// Check that he got the original lease back.
ASSERT_TRUE(lease);
EXPECT_EQ(addr, lease->addr_);
// Check that the lease is indeed updated in LeaseMgr
Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
addr);
ASSERT_TRUE(from_mgr);
// Now check that the lease in LeaseMgr has the same parameters
detailCompareLease(lease, from_mgr);
// Verify that the lease state has been set back to the default.
EXPECT_EQ(lease->state_, Lease::STATE_DEFAULT);
// Verify assigned-nas got bumped. Reclaimed stats should still
// be zero as we artificially marked it reclaimed.
EXPECT_TRUE(testStatistics("assigned-nas", 1, subnet_->getID()));
cumulative += 1;
EXPECT_TRUE(testStatistics("cumulative-assigned-nas",
cumulative, subnet_->getID()));
glbl_cumulative += 1;
EXPECT_TRUE(testStatistics("cumulative-assigned-nas", glbl_cumulative));
EXPECT_TRUE(testStatistics("reclaimed-leases", 0));
EXPECT_TRUE(testStatistics("reclaimed-leases", 0, subnet_->getID()));
}
/// @brief This test class is dedicated to testing shared networks
///
/// It uses one common configuration:
/// 1 shared network with 2 subnets:
/// - 2001:db8:1::/56 subnet with a small pool of single address
/// - 2001:db8:1::/56 subnet with pool with 64K addresses.
class SharedNetworkAlloc6Test : public AllocEngine6Test {
public:
SharedNetworkAlloc6Test() : engine_(0) {
subnet1_ = Subnet6::create(IOAddress("2001:db8:1::"), 56, 1, 2, 3, 4, SubnetID(10));
subnet2_ = Subnet6::create(IOAddress("2001:db8:2::"), 56, 1, 2, 3, 4, SubnetID(20));
pool1_.reset(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:1::1"),
IOAddress("2001:db8:1::1")));
pool2_.reset(new Pool6(Lease::TYPE_NA, IOAddress("2001:db8:2::"),
IOAddress("2001:db8:2::FF")));
subnet1_->addPool(pool1_);
subnet2_->addPool(pool2_);
// Both subnets belong to the same network so they can be used
// interchangeably.
network_.reset(new SharedNetwork6("test_network"));
network_->add(subnet1_);
network_->add(subnet2_);
}
Lease6Ptr
insertLease(std::string addr, SubnetID subnet_id) {
Lease6Ptr lease(new Lease6(Lease::TYPE_NA, IOAddress(addr), duid_, iaid_,
501, 502, subnet_->getID(), HWAddrPtr()));
lease->cltt_ = time(0) - 10; // Allocated 10 seconds ago
if (!LeaseMgrFactory::instance().addLease(lease)) {
ADD_FAILURE() << "Failed to add a lease for address " << addr
<< " in subnet with subnet-id " << subnet_id;
return (Lease6Ptr());
}
return (lease);
}
/// Convenience pointers to configuration elements. These are initialized
/// in the constructor and are used throughout the tests.
AllocEngine engine_;
Subnet6Ptr subnet1_;
Subnet6Ptr subnet2_;
Pool6Ptr pool1_;
Pool6Ptr pool2_;
SharedNetwork6Ptr network_;
};
// This test verifies that the server can offer an address from a
// subnet and the introduction of shared network doesn't break anything here.
TEST_F(SharedNetworkAlloc6Test, solicitSharedNetworkSimple) {
// Create context which will be used to try to allocate leases from the
// shared network. The context points to subnet1, which address space
// is exhausted. We expect the allocation engine to find another subnet
// within the same shared network and offer an address from there.
Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 1234));
AllocEngine::ClientContext6 ctx(subnet1_, duid_, false, false, "", true,
query);
ctx.currentIA().iaid_ = iaid_;
Lease6Ptr lease;
ASSERT_NO_THROW(lease = expectOneLease(engine_.allocateLeases6(ctx)));
ASSERT_TRUE(lease);
ASSERT_TRUE(subnet1_->inRange(lease->addr_));
}
// This test verifies that the server can pick a subnet from shared subnets
// based on hints.
TEST_F(SharedNetworkAlloc6Test, solicitSharedNetworkHint) {
// Create context which will be used to try to allocate leases from the
// shared network. There's a hint that points to the subnet2. The
// shared network mechanism should be able to pick the second subnet
// based on it.
Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 1234));
AllocEngine::ClientContext6 ctx(subnet1_, duid_, false, false, "", true,
query);
ctx.currentIA().iaid_ = iaid_;
ctx.currentIA().addHint(IOAddress("2001:db8:2::12"));
Lease6Ptr lease;
ASSERT_NO_THROW(lease = expectOneLease(engine_.allocateLeases6(ctx)));
ASSERT_TRUE(lease);
// The second subnet should be selected.
ASSERT_TRUE(subnet2_->inRange(lease->addr_));
}
// This test verifies that the client is offered an address from an
// alternative subnet within shared network when the address pool is
// exhausted in the first address pool.
TEST_F(SharedNetworkAlloc6Test, solicitSharedNetworkOutOfAddresses) {
// Create a lease for a single address in the first address pool. The
// pool is now exhausted.
DuidPtr other_duid(new DUID(vector<uint8_t>(12, 0xff)));
const uint32_t other_iaid = 3568;
Lease6Ptr lease(new Lease6(Lease::TYPE_NA, IOAddress("2001:db8:1::1"),
other_duid, other_iaid, 501, 502,
subnet1_->getID(), HWAddrPtr()));
lease->cltt_ = time(0) - 10; // Allocated 10 seconds ago
ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
// Create context which will be used to try to allocate leases from the
// shared network. The context points to subnet1, which address space
// is exhausted. We expect the allocation engine to find another subnet
// within the same shared network and offer an address from there.
Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 1234));
AllocEngine::ClientContext6 ctx(subnet1_, duid_, false, false, "", true,
query);
ctx.currentIA().iaid_ = iaid_;
Lease6Ptr lease2;
ASSERT_NO_THROW(lease2 = expectOneLease(engine_.allocateLeases6(ctx)));
ASSERT_TRUE(lease2);
ASSERT_TRUE(subnet2_->inRange(lease2->addr_));
// The client having a lease should be offered this lease, even if
// the client starts allocation from the second subnet. The code should
// determine that the client has a lease in subnet1 and use this subnet
// instead.
AllocEngine::ClientContext6 ctx2(subnet2_, other_duid, false, false, "",
true, query);
ctx2.currentIA().iaid_ = other_iaid;
ASSERT_NO_THROW(lease2 = expectOneLease(engine_.allocateLeases6(ctx2)));
ASSERT_TRUE(lease2);
ASSERT_EQ("2001:db8:1::1", lease2->addr_.toText());
// Delete the lease in the first subnet.
ASSERT_TRUE(LeaseMgrFactory::instance().deleteLease(lease));
// Now, try requesting this address by providing a hint. The engine
// should try to honor the hint even though we start from the subnet2.
ctx.subnet_ = subnet2_;
ctx.currentIA().addHint(IOAddress("2001:db8:1::1"));
ASSERT_NO_THROW(lease2 = expectOneLease(engine_.allocateLeases6(ctx)));
ASSERT_TRUE(lease2);
ASSERT_TRUE(subnet1_->inRange(lease2->addr_));
}
// This test verifies that the server can offer an address from a
// different subnet than orginally selected, when the address pool in
// the first subnet is exhausted.
TEST_F(SharedNetworkAlloc6Test, solicitSharedNetworkClassification) {
// Try to offer address from subnet1. There is an address available so
// it should be offered.
Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 1234));
AllocEngine::ClientContext6 ctx(subnet1_, duid_, false, false, "", true,
query);
ctx.currentIA().iaid_ = iaid_;
Lease6Ptr lease;
ASSERT_NO_THROW(lease = expectOneLease(engine_.allocateLeases6(ctx)));
ASSERT_TRUE(lease);
ASSERT_TRUE(subnet1_->inRange(lease->addr_));
// Apply restrictions on the subnet1. This should be only assigned
// to clients belonging to cable-modem class.
subnet1_->allowClientClass("cable-modem");
// The allocation engine should determine that the subnet1 is not
// available for the client not belonging to the cable-modem class.
// Instead, it should offer an address from subnet2 that belongs
// to the same shared network.
AllocEngine::ClientContext6 ctx2(subnet1_, duid_, false, false, "", true,
query);
ctx2.currentIA().iaid_ = iaid_;
ctx2.query_ = query;
ASSERT_NO_THROW(lease = expectOneLease(engine_.allocateLeases6(ctx2)));
ASSERT_TRUE(lease);
ASSERT_TRUE(subnet2_->inRange(lease->addr_));
AllocEngine::ClientContext6 ctx3(subnet1_, duid_, false, false, "", true,
query);
ctx3.currentIA().iaid_ = iaid_;
ctx3.query_ = query;
// Create host reservation in the first subnet for this client. The
// allocation engine should not assign reserved address to the client
// because client classification doesn't allow that.
subnet_ = subnet1_;
createHost6(true, IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::1"), 128);
AllocEngine::findReservation(ctx3);
ASSERT_NO_THROW(lease = expectOneLease(engine_.allocateLeases6(ctx)));
ASSERT_TRUE(lease);
ASSERT_TRUE(subnet2_->inRange(lease->addr_));
AllocEngine::ClientContext6 ctx4(subnet1_, duid_, false, false, "", true,
query);
ctx4.currentIA().iaid_ = iaid_;
ctx4.query_ = query;
// Assign cable-modem class and try again. This time, we should
// offer an address from the subnet1_.
ctx4.query_->addClass(ClientClass("cable-modem"));
AllocEngine::findReservation(ctx4);
ASSERT_NO_THROW(lease = expectOneLease(engine_.allocateLeases6(ctx4)));
ASSERT_TRUE(lease);
EXPECT_EQ("2001:db8:1::1", lease->addr_.toText());
}
// This test verifies that the server can offer an address from a
// different subnet than orginally selected, when the address pool in
// the first subnet requires another class.
TEST_F(SharedNetworkAlloc6Test, solicitSharedNetworkPoolClassification) {
// Try to offer address from subnet1. There is an address available so
// it should be offered.
Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 1234));
AllocEngine::ClientContext6 ctx(subnet1_, duid_, false, false, "", true,
query);
ctx.currentIA().iaid_ = iaid_;
Lease6Ptr lease;
ASSERT_NO_THROW(lease = expectOneLease(engine_.allocateLeases6(ctx)));
ASSERT_TRUE(lease);
ASSERT_TRUE(subnet1_->inRange(lease->addr_));
// Apply restrictions on the pool1. This should be only assigned
// to clients belonging to cable-modem class.
pool1_->allowClientClass("cable-modem");
// The allocation engine should determine that the pool1 is not
// available for the client not belonging to the cable-modem class.
// Instead, it should offer an address from subnet2 that belongs
// to the same shared network.
AllocEngine::ClientContext6 ctx2(subnet1_, duid_, false, false, "", true,
query);
ctx2.currentIA().iaid_ = iaid_;
ctx2.query_ = query;
ASSERT_NO_THROW(lease = expectOneLease(engine_.allocateLeases6(ctx2)));
ASSERT_TRUE(lease);
ASSERT_TRUE(subnet2_->inRange(lease->addr_));
AllocEngine::ClientContext6 ctx3(subnet1_, duid_, false, false, "", true,
query);
ctx3.currentIA().iaid_ = iaid_;
ctx3.query_ = query;
AllocEngine::ClientContext6 ctx4(subnet1_, duid_, false, false, "", true,
query);
ctx4.currentIA().iaid_ = iaid_;
ctx4.query_ = query;
// Assign cable-modem class and try again. This time, we should
// offer an address from the pool1_.
ctx4.query_->addClass(ClientClass("cable-modem"));
// Restrict access to pool2 for this client, to make sure that the
// server doesn't accidentally get a lease from this pool.
pool2_->allowClientClass("telephone");
AllocEngine::findReservation(ctx4);
ASSERT_NO_THROW(lease = expectOneLease(engine_.allocateLeases6(ctx4)));
ASSERT_TRUE(lease);
EXPECT_EQ("2001:db8:1::1", lease->addr_.toText());
}
// This test verifies that the client is offered a reserved address
// even if this address belongs to another subnet within the same
// shared network.
TEST_F(SharedNetworkAlloc6Test, solicitSharedNetworkReservations) {
EXPECT_FALSE(HostMgr::instance().getDisableSingleQuery());
// Create reservation for the client in the second subnet.
subnet_ = subnet2_;
createHost6(true, IPv6Resrv::TYPE_NA, IOAddress("2001:db8:2::15"), 128);
// Start allocation from subnet1_. The engine should determine that the
// client has reservations in subnet2_ and should rather assign reserved
// addresses.
Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 1234));
AllocEngine::ClientContext6 ctx(subnet1_, duid_, false, false, "", true,
query);
ctx.currentIA().iaid_ = iaid_;
// Find reservations for this subnet/shared network.
AllocEngine::findReservation(ctx);
Lease6Ptr lease;
ASSERT_NO_THROW(lease = expectOneLease(engine_.allocateLeases6(ctx)));
ASSERT_TRUE(lease);
ASSERT_EQ("2001:db8:2::15", lease->addr_.toText());
}
// This test verifies that the client is offered a reserved address
// even if this address belongs to another subnet within the same
// shared network. Host lookups returning a collection are disabled.
// As it is only an optimization the behavior (so the test) must stay
// unchanged.
TEST_F(SharedNetworkAlloc6Test, solicitSharedNetworkReservationsNoColl) {
// Disable host lookups returning a collection.
ASSERT_FALSE(HostMgr::instance().getDisableSingleQuery());
HostMgr::instance().setDisableSingleQuery(true);
// Create reservation for the client in the second subnet.
subnet_ = subnet2_;
createHost6(true, IPv6Resrv::TYPE_NA, IOAddress("2001:db8:2::15"), 128);
// Start allocation from subnet1_. The engine should determine that the
// client has reservations in subnet2_ and should rather assign reserved
// addresses.
Pkt6Ptr query(new Pkt6(DHCPV6_SOLICIT, 1234));
AllocEngine::ClientContext6 ctx(subnet1_, duid_, false, false, "", true,
query);
ctx.currentIA().iaid_ = iaid_;
// Find reservations for this subnet/shared network.
AllocEngine::findReservation(ctx);
Lease6Ptr lease;
ASSERT_NO_THROW(lease = expectOneLease(engine_.allocateLeases6(ctx)));
ASSERT_TRUE(lease);
ASSERT_EQ("2001:db8:2::15", lease->addr_.toText());
}
// This test verifies that the client is allocated a reserved address
// even if this address belongs to another subnet within the same
// shared network.
TEST_F(SharedNetworkAlloc6Test, requestSharedNetworkReservations) {
EXPECT_FALSE(HostMgr::instance().getDisableSingleQuery());
// Create reservation for the client in the second subnet.
subnet_ = subnet2_;
createHost6(true, IPv6Resrv::TYPE_NA, IOAddress("2001:db8:2::15"), 128);
// Start allocation from subnet1_. The engine should determine that the
// client has reservations in subnet2_ and should rather assign reserved
// addresses.
Pkt6Ptr query(new Pkt6(DHCPV6_REQUEST, 1234));
AllocEngine::ClientContext6 ctx(subnet1_, duid_, false, false, "", false,
query);
ctx.currentIA().iaid_ = iaid_;
// Find reservations for this subnet/shared network.
AllocEngine::findReservation(ctx);
Lease6Ptr lease;
ASSERT_NO_THROW(lease = expectOneLease(engine_.allocateLeases6(ctx)));
ASSERT_TRUE(lease);
ASSERT_EQ("2001:db8:2::15", lease->addr_.toText());
}
// This test verifies that the client is allocated a reserved address
// even if this address belongs to another subnet within the same
// shared network. Host lookups returning a collection are disabled.
// As it is only an optimization the behavior (so the test) must stay
// unchanged.
TEST_F(SharedNetworkAlloc6Test, requestSharedNetworkReservationsNoColl) {
// Disable host lookups returning a collection.
ASSERT_FALSE(HostMgr::instance().getDisableSingleQuery());
HostMgr::instance().setDisableSingleQuery(true);
// Create reservation for the client in the second subnet.
subnet_ = subnet2_;
createHost6(true, IPv6Resrv::TYPE_NA, IOAddress("2001:db8:2::15"), 128);
// Start allocation from subnet1_. The engine should determine that the
// client has reservations in subnet2_ and should rather assign reserved
// addresses.
Pkt6Ptr query(new Pkt6(DHCPV6_REQUEST, 1234));
AllocEngine::ClientContext6 ctx(subnet1_, duid_, false, false, "", false,
query);
ctx.currentIA().iaid_ = iaid_;
// Find reservations for this subnet/shared network.
AllocEngine::findReservation(ctx);
Lease6Ptr lease;
ASSERT_NO_THROW(lease = expectOneLease(engine_.allocateLeases6(ctx)));
ASSERT_TRUE(lease);
ASSERT_EQ("2001:db8:2::15", lease->addr_.toText());
}
// This test verifies that client is assigned an existing lease from a
// shared network, regardless of the default subnet. It also verifies that
// the client is assigned a reserved address from a shared network which
// replaces existing lease within this shared network.
TEST_F(SharedNetworkAlloc6Test, requestSharedNetworkExistingLeases) {
// Get the cumulative count of assigned addresses.
int64_t cumulative = getStatistics("cumulative-assigned-nas",
subnet2_->getID());
int64_t glbl_cumulative = getStatistics("cumulative-assigned-nas");
// Create a lease in subnet 2 for this client. The lease is in expired
// reclaimed state initially to allow for checking whether the lease
// gets renewed.
Lease6Ptr lease(new Lease6(Lease::TYPE_NA, IOAddress("2001:db8:2::1"),
duid_, iaid_, 501, 502,
subnet2_->getID(), HWAddrPtr()));
lease->state_ = Lease::STATE_EXPIRED_RECLAIMED;
ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
// Create context which will be used to try to allocate leases from the
// shared network. The context points to subnet 1 initially, but the
// allocation engine should determine that there are existing leases
// in subnet 2 and renew those.
Pkt6Ptr query(new Pkt6(DHCPV6_REQUEST, 1234));
AllocEngine::ClientContext6 ctx(subnet1_, duid_, false, false, "", false,
query);
ctx.currentIA().iaid_ = iaid_;
// Check that we have been allocated the existing lease.
Lease6Ptr lease2;
ASSERT_NO_THROW(lease2 = expectOneLease(engine_.allocateLeases6(ctx)));
ASSERT_TRUE(lease2);
EXPECT_EQ("2001:db8:2::1", lease2->addr_.toText());
// Statistics should be bumped when the lease is re-assigned.
EXPECT_TRUE(testStatistics("assigned-nas", 1, subnet2_->getID()));
cumulative += 1;
EXPECT_TRUE(testStatistics("cumulative-assigned-nas",
cumulative, subnet2_->getID()));
glbl_cumulative += 1;
EXPECT_TRUE(testStatistics("cumulative-assigned-nas", glbl_cumulative));
// Another interesting case is when there is a reservation in a different
// subnet than the one from which the ease has been assigned.
subnet_ = subnet1_;
createHost6(true, IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::1"), 128);
// The reserved lease should take precedence.
ctx.subnet_ = subnet1_;
ctx.currentIA().iaid_ = iaid_;
AllocEngine::findReservation(ctx);
ASSERT_NO_THROW(lease2 = expectOneLease(engine_.allocateLeases6(ctx)));
ASSERT_TRUE(lease2);
EXPECT_EQ("2001:db8:1::1", lease2->addr_.toText());
// The previous lease should have been removed.
ASSERT_EQ(1, ctx.currentIA().old_leases_.size());
EXPECT_EQ("2001:db8:2::1", ctx.currentIA().old_leases_[0]->addr_.toText());
}
// This test verifies that the server can offer an address from a shared
// subnet if there's at least 1 address left there, but will not offer
// anything if both subnets are completely full.
TEST_F(SharedNetworkAlloc6Test, requestRunningOut) {
// Allocate everything in subnet1
insertLease("2001:db8:1::1", subnet1_->getID());
// Allocate everything, except one address in subnet2.
for (int i = 0; i < 255; i++) {
stringstream tmp;
tmp << "2001:db8:2::" << hex << i;
insertLease(tmp.str(), subnet2_->getID());
}
// Create context which will be used to try to allocate leases from the
// shared network. The context points to subnet 1 initially, but the
// allocation engine should determine that there are existing leases
// in subnet 2 and renew those.
Pkt6Ptr query(new Pkt6(DHCPV6_REQUEST, 1234));
AllocEngine::ClientContext6 ctx1(subnet1_, duid_, false, false, "", false,
query);
ctx1.currentIA().iaid_ = iaid_;
// Check that we have been allocated the existing lease (there's only
// one lease left, so we know exactly which one will be given out.
Lease6Ptr lease;
ASSERT_NO_THROW(lease = expectOneLease(engine_.allocateLeases6(ctx1)));
ASSERT_TRUE(lease);
EXPECT_EQ("2001:db8:2::ff", lease->addr_.toText());
// Ok, now try for another client. We should be completely full.
DuidPtr other_duid(new DUID(vector<uint8_t>(12, 0xff)));
AllocEngine::ClientContext6 ctx2(subnet2_, other_duid, false, false, "", false,
query);
Lease6Collection leases = engine_.allocateLeases6(ctx2);
// Bugger off, we're full!
ASSERT_TRUE(leases.empty());
EXPECT_EQ(1, getStatistics("v6-allocation-fail"));
EXPECT_EQ(1, getStatistics("v6-allocation-fail-shared-network"));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-subnet"));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-no-pools"));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-classes"));
EXPECT_EQ(1, getStatistics("v6-allocation-fail", subnet2_->getID()));
EXPECT_EQ(1, getStatistics("v6-allocation-fail-shared-network", subnet2_->getID()));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-subnet", subnet2_->getID()));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-no-pools", subnet2_->getID()));
EXPECT_EQ(0, getStatistics("v6-allocation-fail-classes", subnet2_->getID()));
}
// Verifies that client with a hostname reservation can
// 1. Get a dynamic lease
// 2. Renew the same lease via REQUEST (calls allocateLease6)
// 3. Renew the same lease via RENEW/REBIND (calls renewLeases6)
// renew a dynamic lease from their selected subnet.
TEST_F(AllocEngine6Test, hostDynamicAddress) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
HostPtr host(new Host(&duid_->getDuid()[0], duid_->getDuid().size(),
Host::IDENT_DUID, SUBNET_ID_UNUSED, subnet_->getID(),
asiolink::IOAddress("0.0.0.0")));
host->setHostname("host1");
CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
CfgMgr::instance().commit();
subnet_->setReservationsInSubnet(true);
// Create context which will be used to try to allocate leases
Pkt6Ptr query(new Pkt6(DHCPV6_REQUEST, 1234));
AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false, query);
ctx.currentIA().iaid_ = iaid_;
// Look up the reservation.
findReservation(*engine, ctx);
// Make sure we found our host.
ConstHostPtr current = ctx.currentHost();
ASSERT_TRUE(current);
ASSERT_EQ("host1", current->getHostname());
// Check that we have been allocated a dynamic address.
Lease6Ptr lease;
ASSERT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
ASSERT_TRUE(lease);
EXPECT_EQ("2001:db8:1::10", lease->addr_.toText());
// We're going to rollback the clock a little so we can verify a renewal.
ASSERT_NO_FATAL_FAILURE(rollbackPersistedCltt(lease));
// This is what the client will send in his renew message.
AllocEngine::HintContainer hints;
hints.push_back(AllocEngine::Resource(IOAddress("2001:db8:1::10"), 128));
// Set test fixture hostname_ to the expected value. This gets checked in
// renewTest.
hostname_ = "host1";
// Client should receive a lease.
Lease6Collection renewed = renewTest(*engine, pool_, hints, IN_SUBNET, IN_POOL);
ASSERT_EQ(1, renewed.size());
Lease6Ptr renewed_lease = renewed[0];
EXPECT_EQ("2001:db8:1::10", renewed_lease->addr_.toText());
// And the lease lifetime should be extended.
EXPECT_GT(renewed_lease->cltt_, lease->cltt_)
<< "Lease lifetime was not extended, but it should";
// Now let's verify that if the client returns via SARR, they get the same
// lease and the cltt gets extended.
// First, we're going to rollback the clock again so we can verify the
// allocation updates the expiry.
ASSERT_NO_FATAL_FAILURE(rollbackPersistedCltt(renewed_lease));
// Create a new context which will be used to try to allocate leases
query.reset(new Pkt6(DHCPV6_REQUEST, 1234));
AllocEngine::ClientContext6 ctx2(subnet_, duid_, false, false, "", false, query);
ctx2.currentIA().iaid_ = iaid_;
// Look up the reservation.
findReservation(*engine, ctx2);
// Make sure we found our host.
current = ctx2.currentHost();
ASSERT_TRUE(current);
ASSERT_EQ("host1", current->getHostname());
// Check that we have been allocated the original dynamic address.
Lease6Ptr lease2;
ASSERT_NO_THROW(lease2 = expectOneLease(engine->allocateLeases6(ctx2)));
ASSERT_TRUE(lease2);
EXPECT_EQ("2001:db8:1::10", lease2->addr_.toText());
// And the lease lifetime should be extended.
EXPECT_GT(lease2->cltt_, renewed_lease->cltt_)
<< "Lease lifetime was not extended, but it should";
}
// Verifies that client with a global hostname reservation can:
// 1. Get a dynamic lease
// 2. Renew the same lease via REQUEST (calls allocateLease6)
// 3. Renew the same lease via RENEW/REBIND (calls renewLeases6)
TEST_F(AllocEngine6Test, globalHostDynamicAddress) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
HostPtr host(new Host(&duid_->getDuid()[0], duid_->getDuid().size(),
Host::IDENT_DUID, SUBNET_ID_UNUSED, SUBNET_ID_GLOBAL,
asiolink::IOAddress("0.0.0.0")));
host->setHostname("ghost1");
CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
CfgMgr::instance().commit();
subnet_->setReservationsGlobal(true);
// Create context which will be used to try to allocate leases
Pkt6Ptr query(new Pkt6(DHCPV6_REQUEST, 1234));
AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false, query);
ctx.currentIA().iaid_ = iaid_;
// Look up the reservation.
findReservation(*engine, ctx);
// Make sure we found our host.
ConstHostPtr current = ctx.currentHost();
ASSERT_TRUE(current);
ASSERT_EQ("ghost1", current->getHostname());
// Check that we have been allocated a dynamic address.
Lease6Ptr lease;
ASSERT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
ASSERT_TRUE(lease);
EXPECT_EQ("2001:db8:1::10", lease->addr_.toText());
// We're going to rollback the clock a little so we can verify a renewal.
ASSERT_NO_FATAL_FAILURE(rollbackPersistedCltt(lease));
// This is what the client will send in his renew message.
AllocEngine::HintContainer hints;
hints.push_back(AllocEngine::Resource(IOAddress("2001:db8:1::10"), 128));
// Set test fixture hostname_ to the expected value. This gets checked in
// renewTest.
hostname_ = "ghost1";
// Client should receive a lease.
Lease6Collection renewed = renewTest(*engine, pool_, hints, IN_SUBNET, IN_POOL);
ASSERT_EQ(1, renewed.size());
// And the lease lifetime should be extended.
ASSERT_GT(renewed[0]->cltt_, lease->cltt_)
<< "Lease lifetime was not extended, but it should";
// Now let's verify that if the client returns via SARR, they get the same
// lease. Create a new context which will be used to try to allocate leases
// First, we're going to rollback the clock again so we can verify the
// allocation updates the expiry.
Lease6Ptr renewed_lease = renewed[0];
ASSERT_NO_FATAL_FAILURE(rollbackPersistedCltt(renewed_lease));
query.reset(new Pkt6(DHCPV6_REQUEST, 1234));
AllocEngine::ClientContext6 ctx2(subnet_, duid_, false, false, "", false, query);
ctx2.currentIA().iaid_ = iaid_;
// Look up the reservation.
findReservation(*engine, ctx2);
// Make sure we found our host.
current = ctx2.currentHost();
ASSERT_TRUE(current);
ASSERT_EQ("ghost1", current->getHostname());
// Check that we have been allocated a dynamic address.
Lease6Ptr lease2;
ASSERT_NO_THROW(lease2 = expectOneLease(engine->allocateLeases6(ctx2)));
ASSERT_TRUE(lease2);
EXPECT_EQ("2001:db8:1::10", lease2->addr_.toText());
// And the lease lifetime should be extended.
EXPECT_GT(lease2->cltt_, renewed_lease->cltt_)
<< "Lease lifetime was not extended, but it should";
}
// Verifies that client with a globally reserved address that is
// outside the selected subnet is given a dynamic address instead.
TEST_F(AllocEngine6Test, globalHostReservedAddress) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
HostPtr host(new Host(&duid_->getDuid()[0], duid_->getDuid().size(),
Host::IDENT_DUID, SUBNET_ID_UNUSED, SUBNET_ID_GLOBAL,
asiolink::IOAddress("0.0.0.0")));
host->setHostname("ghost1");
IPv6Resrv resv(IPv6Resrv::TYPE_NA, asiolink::IOAddress("3001::1"), 128);
host->addReservation(resv);
CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
CfgMgr::instance().commit();
subnet_->setReservationsGlobal(true);
// Create context which will be used to try to allocate leases
Pkt6Ptr query(new Pkt6(DHCPV6_REQUEST, 1234));
AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false, query);
ctx.currentIA().iaid_ = iaid_;
// Look up the reservation.
findReservation(*engine, ctx);
// Make sure we found our host.
ConstHostPtr current = ctx.currentHost();
ASSERT_TRUE(current);
ASSERT_EQ("ghost1", current->getHostname());
// Check that we have been allocated a dynamic address.
Lease6Ptr lease;
ASSERT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
ASSERT_TRUE(lease);
EXPECT_NE("3001::1", lease->addr_.toText());
EXPECT_TRUE(subnet_->inRange(lease->addr_))
<< " address not in range: " << lease->addr_.toText();
ASSERT_NO_FATAL_FAILURE(rollbackPersistedCltt(lease));
EXPECT_NO_THROW(LeaseMgrFactory::instance().updateLease6(lease));
// This is what the client will send in his renew message.
AllocEngine::HintContainer hints;
hints.push_back(AllocEngine::Resource(IOAddress(lease->addr_.toText()), 128));
// Set test fixture hostname_ to the expected value. This gets checked in
// renewTest.
hostname_ = "ghost1";
// Client should receive a lease.
Lease6Collection renewed = renewTest(*engine, pool_, hints, IN_SUBNET, IN_POOL);
ASSERT_EQ(1, renewed.size());
// And the lease lifetime should be extended.
EXPECT_GT(renewed[0]->cltt_, lease->cltt_)
<< "Lease lifetime was not extended, but it should";
}
// Verifies that client with a globally reserved address that is
// inside the selected subnet is given that address.
TEST_F(AllocEngine6Test, globalHostReservedMatchingAddress) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
HostPtr host(new Host(&duid_->getDuid()[0], duid_->getDuid().size(),
Host::IDENT_DUID, SUBNET_ID_UNUSED, SUBNET_ID_GLOBAL,
asiolink::IOAddress("0.0.0.0")));
host->setHostname("ghost1");
IPv6Resrv resv(IPv6Resrv::TYPE_NA, asiolink::IOAddress("2001:db8:1::a"), 128);
host->addReservation(resv);
CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
CfgMgr::instance().commit();
subnet_->setReservationsGlobal(true);
// Create context which will be used to try to allocate leases
Pkt6Ptr query(new Pkt6(DHCPV6_REQUEST, 1234));
AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false, query);
ctx.currentIA().iaid_ = iaid_;
// Look up the reservation.
findReservation(*engine, ctx);
// Make sure we found our host.
ConstHostPtr current = ctx.currentHost();
ASSERT_TRUE(current);
ASSERT_EQ("ghost1", current->getHostname());
// Check that we have been allocated the fixed address.
Lease6Ptr lease;
ASSERT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
ASSERT_TRUE(lease);
EXPECT_EQ("2001:db8:1::a", lease->addr_.toText());
// We're going to rollback the clock a little so we can verify a renewal.
//--lease->cltt_;
ASSERT_NO_FATAL_FAILURE(rollbackPersistedCltt(lease));
// We're going to rollback the clock a little so we can verify a renewal.
--lease->cltt_;
EXPECT_NO_THROW(LeaseMgrFactory::instance().updateLease6(lease));
// This is what the client will send in his renew message.
AllocEngine::HintContainer hints;
hints.push_back(AllocEngine::Resource(IOAddress("2008:db8:1::a"), 128));
// Set test fixture hostname_ to the expected value. This gets checked in
// renewTest.
hostname_ = "ghost1";
// Client should receive a lease.
Lease6Collection renewed = renewTest(*engine, pool_, hints, IN_SUBNET, !IN_POOL);
ASSERT_EQ(1, renewed.size());
// And the lease lifetime should be extended.
EXPECT_GT(renewed[0]->cltt_, lease->cltt_)
<< "Lease lifetime was not extended, but it should";
}
// Verifies that client with a global prefix reservation can get and
// renew a lease for an arbitrary prefix.
TEST_F(AllocEngine6Test, globalHostReservedPrefix) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
HostPtr host(new Host(&duid_->getDuid()[0], duid_->getDuid().size(),
Host::IDENT_DUID, SUBNET_ID_UNUSED, SUBNET_ID_GLOBAL,
asiolink::IOAddress("0.0.0.0")));
host->setHostname("ghost1");
IPv6Resrv resv(IPv6Resrv::TYPE_PD, asiolink::IOAddress("3001::"), 64);
host->addReservation(resv);
CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
CfgMgr::instance().commit();
subnet_->setReservationsGlobal(true);
// Create context which will be used to try to allocate leases
Pkt6Ptr query(new Pkt6(DHCPV6_REQUEST, 1234));
AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false, query);
ctx.currentIA().type_ = Lease::TYPE_PD;
ctx.currentIA().iaid_ = iaid_;
// Look up the reservation.
findReservation(*engine, ctx);
// Make sure we found our host.
ConstHostPtr current = ctx.currentHost();
ASSERT_TRUE(current);
ASSERT_EQ("ghost1", current->getHostname());
// Check that we have been allocated the fixed address.
Lease6Ptr lease;
ASSERT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
ASSERT_TRUE(lease);
EXPECT_EQ("3001::", lease->addr_.toText());
// We're going to rollback the clock a little so we can verify a renewal.
--lease->cltt_;
EXPECT_NO_THROW(LeaseMgrFactory::instance().updateLease6(lease));
// This is what the client will send in his renew message.
AllocEngine::HintContainer hints;
hints.push_back(AllocEngine::Resource(IOAddress("3001::"), 64));
// Set test fixture hostname_ to the expected value. This gets checked via
// renewTest.
hostname_ = "ghost1";
// We need a PD pool to fake renew_test
Pool6Ptr dummy_pool(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8::"), 64, 64));
// Client should receive a lease.
Lease6Collection renewed = renewTest(*engine, dummy_pool, hints, !IN_SUBNET, !IN_POOL);
ASSERT_EQ(1, renewed.size());
// And the lease lifetime should be extended.
EXPECT_GT(renewed[0]->cltt_, lease->cltt_)
<< "Lease lifetime was not extended, but it should";
}
// Verifies that client with a global prefix reservation can get and
// renew a lease for an arbitrary prefix even if using a wrong hint prefix
// length.
TEST_F(AllocEngine6Test, globalHostReservedPrefixDifferentPrefixLen) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
HostPtr host(new Host(&duid_->getDuid()[0], duid_->getDuid().size(),
Host::IDENT_DUID, SUBNET_ID_UNUSED, SUBNET_ID_GLOBAL,
asiolink::IOAddress("0.0.0.0")));
host->setHostname("ghost1");
IPv6Resrv resv(IPv6Resrv::TYPE_PD, asiolink::IOAddress("3001::"), 64);
host->addReservation(resv);
CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
CfgMgr::instance().commit();
subnet_->setReservationsGlobal(true);
// Create context which will be used to try to allocate leases
Pkt6Ptr query(new Pkt6(DHCPV6_REQUEST, 1234));
AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false, query);
ctx.currentIA().type_ = Lease::TYPE_PD;
ctx.currentIA().iaid_ = iaid_;
// Using a different prefix length in the hint should have no effect
ctx.currentIA().addHint(asiolink::IOAddress("3001::"), 32);
// Look up the reservation.
findReservation(*engine, ctx);
// Make sure we found our host.
ConstHostPtr current = ctx.currentHost();
ASSERT_TRUE(current);
ASSERT_EQ("ghost1", current->getHostname());
// Check that we have been allocated the fixed address.
Lease6Ptr lease;
ASSERT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
ASSERT_TRUE(lease);
EXPECT_EQ("3001::", lease->addr_.toText());
// We're going to rollback the clock a little so we can verify a renewal.
--lease->cltt_;
EXPECT_NO_THROW(LeaseMgrFactory::instance().updateLease6(lease));
// This is what the client will send in his renew message.
AllocEngine::HintContainer hints;
hints.push_back(AllocEngine::Resource(IOAddress("3001::"), 64));
// Set test fixture hostname_ to the expected value. This gets checked via
// renewTest.
hostname_ = "ghost1";
// We need a PD pool to fake renew_test
Pool6Ptr dummy_pool(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8::"), 64, 64));
// Client should receive a lease.
Lease6Collection renewed = renewTest(*engine, dummy_pool, hints, !IN_SUBNET, !IN_POOL);
ASSERT_EQ(1, renewed.size());
// And the lease lifetime should be extended.
EXPECT_GT(renewed[0]->cltt_, lease->cltt_)
<< "Lease lifetime was not extended, but it should";
}
// Verifies that client with a subnet address reservation can get and
// renew a lease for an address in the subnet.
TEST_F(AllocEngine6Test, mixedHostReservedAddress) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
HostPtr host(new Host(&duid_->getDuid()[0], duid_->getDuid().size(),
Host::IDENT_DUID, SUBNET_ID_UNUSED, subnet_->getID(),
asiolink::IOAddress("0.0.0.0")));
host->setHostname("mhost1");
IPv6Resrv resv(IPv6Resrv::TYPE_NA, asiolink::IOAddress("2001:db8:1::1c"), 128);
host->addReservation(resv);
CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
CfgMgr::instance().commit();
subnet_->setReservationsGlobal(true);
subnet_->setReservationsInSubnet(true);
subnet_->setReservationsOutOfPool(false);
// Create context which will be used to try to allocate leases
Pkt6Ptr query(new Pkt6(DHCPV6_REQUEST, 1234));
AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false, query);
ctx.currentIA().iaid_ = iaid_;
// Look up the reservation.
findReservation(*engine, ctx);
// Make sure we found our host.
ConstHostPtr current = ctx.currentHost();
ASSERT_TRUE(current);
ASSERT_EQ("mhost1", current->getHostname());
// Check that we have been allocated the fixed address.
Lease6Ptr lease;
ASSERT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
ASSERT_TRUE(lease);
EXPECT_EQ("2001:db8:1::1c", lease->addr_.toText());
// We're going to rollback the clock a little so we can verify a renewal.
--lease->cltt_;
EXPECT_NO_THROW(LeaseMgrFactory::instance().updateLease6(lease));
// This is what the client will send in his renew message.
AllocEngine::HintContainer hints;
hints.push_back(AllocEngine::Resource(IOAddress("2001:db8:1::1c"), 128));
// Set test fixture hostname_ to the expected value. This gets checked in
// renewTest.
hostname_ = "mhost1";
// Client should receive a lease.
Lease6Collection renewed = renewTest(*engine, pool_, hints, IN_SUBNET, IN_POOL);
ASSERT_EQ(1, renewed.size());
// And the lease lifetime should be extended.
EXPECT_GT(renewed[0]->cltt_, lease->cltt_)
<< "Lease lifetime was not extended, but it should";
}
// Verifies that client with a subnet prefix reservation can get and
// renew a lease for a prefix in the subnet even if using a wrong hint prefix
// length.
TEST_F(AllocEngine6Test, mixedHostReservedPrefix) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
HostPtr host(new Host(&duid_->getDuid()[0], duid_->getDuid().size(),
Host::IDENT_DUID, SUBNET_ID_UNUSED, subnet_->getID(),
asiolink::IOAddress("0.0.0.0")));
host->setHostname("mhost1");
IPv6Resrv resv(IPv6Resrv::TYPE_PD, asiolink::IOAddress("2001:db8:1:2::"), 64);
host->addReservation(resv);
CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
CfgMgr::instance().commit();
subnet_->setReservationsGlobal(true);
subnet_->setReservationsInSubnet(true);
// Create context which will be used to try to allocate leases
Pkt6Ptr query(new Pkt6(DHCPV6_REQUEST, 1234));
AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false, query);
ctx.currentIA().type_ = Lease::TYPE_PD;
ctx.currentIA().iaid_ = iaid_;
// Using a different prefix length in the hint should have no effect
ctx.currentIA().addHint(asiolink::IOAddress("2001:db8:1:2::"), 32);
// Look up the reservation.
findReservation(*engine, ctx);
// Make sure we found our host.
ConstHostPtr current = ctx.currentHost();
ASSERT_TRUE(current);
ASSERT_EQ("mhost1", current->getHostname());
// Check that we have been allocated the fixed prefix.
Lease6Ptr lease;
ASSERT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
ASSERT_TRUE(lease);
EXPECT_EQ("2001:db8:1:2::", lease->addr_.toText());
// We're going to rollback the clock a little so we can verify a renewal.
--lease->cltt_;
EXPECT_NO_THROW(LeaseMgrFactory::instance().updateLease6(lease));
// This is what the client will send in his renew message.
AllocEngine::HintContainer hints;
hints.push_back(AllocEngine::Resource(IOAddress("2001:db8:1:2::"), 64));
// Set test fixture hostname_ to the expected value. This gets checked via
// renewTest.
hostname_ = "mhost1";
// We need a PD pool to fake renew_test
Pool6Ptr dummy_pool(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8::"), 64, 64));
// Client should receive a lease.
Lease6Collection renewed = renewTest(*engine, dummy_pool, hints, IN_SUBNET, IN_POOL);
ASSERT_EQ(1, renewed.size());
// And the lease lifetime should be extended.
EXPECT_GT(renewed[0]->cltt_, lease->cltt_)
<< "Lease lifetime was not extended, but it should";
}
// Verifies that client with a subnet prefix reservation can get and
// renew a lease for a prefix in the subnet.
TEST_F(AllocEngine6Test, mixedHostReservedPrefixDifferentPrefixLen) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
HostPtr host(new Host(&duid_->getDuid()[0], duid_->getDuid().size(),
Host::IDENT_DUID, SUBNET_ID_UNUSED, subnet_->getID(),
asiolink::IOAddress("0.0.0.0")));
host->setHostname("mhost1");
IPv6Resrv resv(IPv6Resrv::TYPE_PD, asiolink::IOAddress("2001:db8:1:2::"), 64);
host->addReservation(resv);
CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
CfgMgr::instance().commit();
subnet_->setReservationsGlobal(true);
subnet_->setReservationsInSubnet(true);
// Create context which will be used to try to allocate leases
Pkt6Ptr query(new Pkt6(DHCPV6_REQUEST, 1234));
AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false, query);
ctx.currentIA().type_ = Lease::TYPE_PD;
ctx.currentIA().iaid_ = iaid_;
// Look up the reservation.
findReservation(*engine, ctx);
// Make sure we found our host.
ConstHostPtr current = ctx.currentHost();
ASSERT_TRUE(current);
ASSERT_EQ("mhost1", current->getHostname());
// Check that we have been allocated the fixed prefix.
Lease6Ptr lease;
ASSERT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
ASSERT_TRUE(lease);
EXPECT_EQ("2001:db8:1:2::", lease->addr_.toText());
// We're going to rollback the clock a little so we can verify a renewal.
--lease->cltt_;
EXPECT_NO_THROW(LeaseMgrFactory::instance().updateLease6(lease));
// This is what the client will send in his renew message.
AllocEngine::HintContainer hints;
hints.push_back(AllocEngine::Resource(IOAddress("2001:db8:1:2::"), 64));
// Set test fixture hostname_ to the expected value. This gets checked via
// renewTest.
hostname_ = "mhost1";
// We need a PD pool to fake renew_test
Pool6Ptr dummy_pool(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8::"), 64, 64));
// Client should receive a lease.
Lease6Collection renewed = renewTest(*engine, dummy_pool, hints, IN_SUBNET, IN_POOL);
ASSERT_EQ(1, renewed.size());
// And the lease lifetime should be extended.
EXPECT_GT(renewed[0]->cltt_, lease->cltt_)
<< "Lease lifetime was not extended, but it should";
}
// Verifies that client with a subnet and a global address reservation
// can get and renew a lease for an address in the subnet.
TEST_F(AllocEngine6Test, bothHostReservedAddress) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
HostPtr ghost(new Host(&duid_->getDuid()[0], duid_->getDuid().size(),
Host::IDENT_DUID, SUBNET_ID_UNUSED, SUBNET_ID_GLOBAL,
asiolink::IOAddress("0.0.0.0")));
ghost->setHostname("ghost1");
IPv6Resrv gresv(IPv6Resrv::TYPE_NA, asiolink::IOAddress("3001::1"), 128);
ghost->addReservation(gresv);
CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(ghost);
HostPtr host(new Host(&duid_->getDuid()[0], duid_->getDuid().size(),
Host::IDENT_DUID, SUBNET_ID_UNUSED, subnet_->getID(),
asiolink::IOAddress("0.0.0.0")));
host->setHostname("mhost1");
IPv6Resrv resv(IPv6Resrv::TYPE_NA, asiolink::IOAddress("2001:db8:1::1c"), 128);
host->addReservation(resv);
CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
CfgMgr::instance().commit();
subnet_->setReservationsGlobal(true);
subnet_->setReservationsInSubnet(true);
subnet_->setReservationsOutOfPool(false);
// Create context which will be used to try to allocate leases
Pkt6Ptr query(new Pkt6(DHCPV6_REQUEST, 1234));
AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false, query);
ctx.currentIA().iaid_ = iaid_;
// Look up the reservation.
findReservation(*engine, ctx);
// Make sure we found our host.
ConstHostPtr current = ctx.currentHost();
ASSERT_TRUE(current);
ASSERT_EQ("mhost1", current->getHostname());
// Check that we have been allocated the fixed address.
Lease6Ptr lease;
ASSERT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
ASSERT_TRUE(lease);
EXPECT_EQ("2001:db8:1::1c", lease->addr_.toText());
// We're going to rollback the clock a little so we can verify a renewal.
--lease->cltt_;
EXPECT_NO_THROW(LeaseMgrFactory::instance().updateLease6(lease));
// This is what the client will send in his renew message.
AllocEngine::HintContainer hints;
hints.push_back(AllocEngine::Resource(IOAddress("2001:db8:1::1c"), 128));
// Set test fixture hostname_ to the expected value. This gets checked in
// renewTest.
hostname_ = "mhost1";
// Client should receive a lease.
Lease6Collection renewed = renewTest(*engine, pool_, hints, IN_SUBNET, IN_POOL);
ASSERT_EQ(1, renewed.size());
// And the lease lifetime should be extended.
EXPECT_GT(renewed[0]->cltt_, lease->cltt_)
<< "Lease lifetime was not extended, but it should";
}
// Verifies that client with a subnet and a global prefix reservation
// can get and renew a lease for a prefix in the subnet.
TEST_F(AllocEngine6Test, bothHostReservedPrefix) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
HostPtr ghost(new Host(&duid_->getDuid()[0], duid_->getDuid().size(),
Host::IDENT_DUID, SUBNET_ID_UNUSED, SUBNET_ID_GLOBAL,
asiolink::IOAddress("0.0.0.0")));
ghost->setHostname("ghost1");
IPv6Resrv gresv(IPv6Resrv::TYPE_PD, asiolink::IOAddress("3001::"), 64);
ghost->addReservation(gresv);
CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(ghost);
HostPtr host(new Host(&duid_->getDuid()[0], duid_->getDuid().size(),
Host::IDENT_DUID, SUBNET_ID_UNUSED, subnet_->getID(),
asiolink::IOAddress("0.0.0.0")));
host->setHostname("mhost1");
IPv6Resrv resv(IPv6Resrv::TYPE_PD, asiolink::IOAddress("2001:db8:1:2::"), 64);
host->addReservation(resv);
CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
CfgMgr::instance().commit();
subnet_->setReservationsGlobal(true);
subnet_->setReservationsInSubnet(true);
// Create context which will be used to try to allocate leases
Pkt6Ptr query(new Pkt6(DHCPV6_REQUEST, 1234));
AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false, query);
ctx.currentIA().type_ = Lease::TYPE_PD;
ctx.currentIA().iaid_ = iaid_;
// Look up the reservation.
findReservation(*engine, ctx);
// Make sure we found our host.
ConstHostPtr current = ctx.currentHost();
ASSERT_TRUE(current);
ASSERT_EQ("mhost1", current->getHostname());
// Check that we have been allocated the fixed prefix.
Lease6Ptr lease;
ASSERT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
ASSERT_TRUE(lease);
EXPECT_EQ("2001:db8:1:2::", lease->addr_.toText());
// We're going to rollback the clock a little so we can verify a renewal.
--lease->cltt_;
EXPECT_NO_THROW(LeaseMgrFactory::instance().updateLease6(lease));
// This is what the client will send in his renew message.
AllocEngine::HintContainer hints;
hints.push_back(AllocEngine::Resource(IOAddress("2001:db8:1:2::"), 64));
// Set test fixture hostname_ to the expected value. This gets checked via
// renewTest.
hostname_ = "mhost1";
// We need a PD pool to fake renew_test
Pool6Ptr dummy_pool(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8::"), 64, 64));
// Client should receive a lease.
Lease6Collection renewed = renewTest(*engine, dummy_pool, hints, IN_SUBNET, IN_POOL);
ASSERT_EQ(1, renewed.size());
// And the lease lifetime should be extended.
EXPECT_GT(renewed[0]->cltt_, lease->cltt_)
<< "Lease lifetime was not extended, but it should";
}
// Verifies that client with a subnet and a global prefix reservation
// can get and renew a lease for a prefix in the subnet even if using a wrong
// hint prefix length.
TEST_F(AllocEngine6Test, bothHostReservedPrefixDifferentPrefixLen) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
HostPtr ghost(new Host(&duid_->getDuid()[0], duid_->getDuid().size(),
Host::IDENT_DUID, SUBNET_ID_UNUSED, SUBNET_ID_GLOBAL,
asiolink::IOAddress("0.0.0.0")));
ghost->setHostname("ghost1");
IPv6Resrv gresv(IPv6Resrv::TYPE_PD, asiolink::IOAddress("3001::"), 64);
ghost->addReservation(gresv);
CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(ghost);
HostPtr host(new Host(&duid_->getDuid()[0], duid_->getDuid().size(),
Host::IDENT_DUID, SUBNET_ID_UNUSED, subnet_->getID(),
asiolink::IOAddress("0.0.0.0")));
host->setHostname("mhost1");
IPv6Resrv resv(IPv6Resrv::TYPE_PD, asiolink::IOAddress("2001:db8:1:2::"), 64);
host->addReservation(resv);
CfgMgr::instance().getStagingCfg()->getCfgHosts()->add(host);
CfgMgr::instance().commit();
subnet_->setReservationsGlobal(true);
subnet_->setReservationsInSubnet(true);
// Create context which will be used to try to allocate leases
Pkt6Ptr query(new Pkt6(DHCPV6_REQUEST, 1234));
AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false, query);
ctx.currentIA().type_ = Lease::TYPE_PD;
ctx.currentIA().iaid_ = iaid_;
// Using a different prefix length in the hint should have no effect
ctx.currentIA().addHint(asiolink::IOAddress("2001:db8:1:2::"), 32);
// Look up the reservation.
findReservation(*engine, ctx);
// Make sure we found our host.
ConstHostPtr current = ctx.currentHost();
ASSERT_TRUE(current);
ASSERT_EQ("mhost1", current->getHostname());
// Check that we have been allocated the fixed prefix.
Lease6Ptr lease;
ASSERT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
ASSERT_TRUE(lease);
EXPECT_EQ("2001:db8:1:2::", lease->addr_.toText());
// We're going to rollback the clock a little so we can verify a renewal.
--lease->cltt_;
EXPECT_NO_THROW(LeaseMgrFactory::instance().updateLease6(lease));
// This is what the client will send in his renew message.
AllocEngine::HintContainer hints;
hints.push_back(AllocEngine::Resource(IOAddress("2001:db8:1:2::"), 64));
// Set test fixture hostname_ to the expected value. This gets checked via
// renewTest.
hostname_ = "mhost1";
// We need a PD pool to fake renew_test
Pool6Ptr dummy_pool(new Pool6(Lease::TYPE_PD, IOAddress("2001:db8::"), 64, 64));
// Client should receive a lease.
Lease6Collection renewed = renewTest(*engine, dummy_pool, hints, IN_SUBNET, IN_POOL);
ASSERT_EQ(1, renewed.size());
// And the lease lifetime should be extended.
EXPECT_GT(renewed[0]->cltt_, lease->cltt_)
<< "Lease lifetime was not extended, but it should";
}
/// @brief Test fixture class for testing storage of extended lease data.
/// It primarily creates several configuration items common to the
/// extended info tests.
class AllocEngine6ExtendedInfoTest : public AllocEngine6Test {
public:
/// @brief Constructor
AllocEngine6ExtendedInfoTest()
: engine_(100),
duid1_(), duid2_(), duid3_(), relay1_(), relay2_(), relay3_(),
duid1_addr_("::"), duid2_addr_("::") {
duid1_.reset(new DUID(std::vector<uint8_t>(8, 0x84)));
duid2_.reset(new DUID(std::vector<uint8_t>(8, 0x74)));
duid3_.reset(new DUID(std::vector<uint8_t>(8, 0x64)));
relay1_.msg_type_ = DHCPV6_RELAY_FORW;
relay1_.hop_count_ = 33;
relay1_.linkaddr_ = IOAddress("2001:db8::1");
relay1_.peeraddr_ = IOAddress("2001:db8::2");
relay1_.relay_msg_len_ = 0;
uint8_t relay_opt_data[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
vector<uint8_t> relay_data(relay_opt_data,
relay_opt_data + sizeof(relay_opt_data));
OptionPtr optRelay1(new Option(Option::V6, 200, relay_data));
relay1_.options_.insert(make_pair(optRelay1->getType(), optRelay1));
relay2_.msg_type_ = DHCPV6_RELAY_FORW;
relay2_.hop_count_ = 77;
relay2_.linkaddr_ = IOAddress("2001:db8::3");
relay2_.peeraddr_ = IOAddress("2001:db8::4");
relay2_.relay_msg_len_ = 0;
relay3_.msg_type_ = DHCPV6_RELAY_FORW;
relay3_.hop_count_ = 100;
relay3_.linkaddr_ = IOAddress("2001:db8::5");
relay3_.peeraddr_ = IOAddress("2001:db8::6");
relay1_.relay_msg_len_ = 0;
vector<uint8_t> remote_id_data({1, 2, 3, 4, 5, 6});
OptionPtr remote_id(new Option(Option::V6, D6O_REMOTE_ID, remote_id_data));
relay3_.options_.insert(make_pair(remote_id->getType(), remote_id));
OptionPtr relay_id(new Option(Option::V6, D6O_RELAY_ID, duid3_->getDuid()));
relay3_.options_.insert(make_pair(relay_id->getType(), relay_id));
duid1_addr_ = IOAddress("2001:db8:1::10");
duid2_addr_ = IOAddress("2001:db8:1::11");
// Create the allocation engine, context and lease.
NakedAllocEngine engine(100);
}
/// Configuration elements. These are initialized in the constructor
/// and are used throughout the tests.
NakedAllocEngine engine_;
DuidPtr duid1_;
DuidPtr duid2_;
DuidPtr duid3_;
Pkt6::RelayInfo relay1_;
Pkt6::RelayInfo relay2_;
Pkt6::RelayInfo relay3_;
IOAddress duid1_addr_;
IOAddress duid2_addr_;
};
// Exercises AllocEnginer6Test::updateExtendedInfo6() through various
// permutations of client packet content.
TEST_F(AllocEngine6ExtendedInfoTest, updateExtendedInfo6) {
// Structure that defines a test scenario.
struct Scenario {
std::string description_; // test description
std::string orig_context_json_; // user context the lease begins with
std::vector<Pkt6::RelayInfo> relays_; // vector of relays from pkt
std::string exp_context_json_; // expected user context on the lease
bool exp_ret; // expected returned value
};
// Test scenarios.
std::vector<Scenario> scenarios {
{
"no context, no relay",
"",
{},
"",
false
},
{
"some original context, no relay",
"{\"foo\": 123}",
{},
"{\"foo\": 123}",
false
},
{
"no original context, one relay",
"",
{ relay1_ },
"{ \"ISC\": { \"relay-info\": [ { \"hop\": 33, \"link\": \"2001:db8::1\","
" \"options\": \"0x00C800080102030405060708\", \"peer\": \"2001:db8::2\" } ] } }",
true
},
{
"no original context, one relay with remote and relay ids",
"",
{ relay3_ },
"{ \"ISC\": { \"relay-info\": [ { \"hop\": 100, \"link\": \"2001:db8::5\","
" \"options\": \"0x00250006010203040506003500086464646464646464\","
" \"remote-id\": \"010203040506\","
" \"relay-id\": \"6464646464646464\","
" \"peer\": \"2001:db8::6\" } ] } }",
true
},
{
"some original context, one relay",
"{\"foo\": 123, \"ISC\": {\"bar\": 456}}",
{ relay1_ },
"{ \"ISC\": { \"relay-info\": [ { \"hop\": 33, \"link\": \"2001:db8::1\","
" \"options\": \"0x00C800080102030405060708\", \"peer\": \"2001:db8::2\" } ],"
"\"bar\": 456 }, \"foo\": 123 }",
true
},
{
"bad original context, one relay",
"[\"foo\"]",
{ relay1_ },
"{ \"ISC\": { \"relay-info\": [ { \"hop\": 33, \"link\": \"2001:db8::1\","
" \"options\": \"0x00C800080102030405060708\", \"peer\": \"2001:db8::2\" } ] } }",
true
},
{
"some original context, one relay",
"{\"foo\": 123, \"ISC\":[\"bar\"]}",
{ relay1_ },
"{ \"ISC\": { \"relay-info\": [ { \"hop\": 33, \"link\": \"2001:db8::1\","
" \"options\": \"0x00C800080102030405060708\", \"peer\": \"2001:db8::2\" } ] },"
" \"foo\": 123 }",
true
},
{
"some original context, one relay with remote and relay ids",
"{\"foo\": 123}",
{ relay3_ },
"{ \"ISC\": { \"relay-info\": [ { \"hop\": 100, \"link\": \"2001:db8::5\","
" \"options\": \"0x00250006010203040506003500086464646464646464\","
" \"remote-id\": \"010203040506\","
" \"relay-id\": \"6464646464646464\","
" \"peer\": \"2001:db8::6\" } ] }, \"foo\": 123 }",
true
},
{
"no original context, two relay-info",
"",
{ relay1_, relay2_ },
"{ \"ISC\": { \"relay-info\": [ { \"hop\": 33, \"link\": \"2001:db8::1\","
" \"options\": \"0x00C800080102030405060708\", \"peer\": \"2001:db8::2\" },"
" {\"hop\": 77, \"link\": \"2001:db8::3\", \"peer\": \"2001:db8::4\" } ] } }",
true
},
{
"no original context, two relay-info, second with remote and relay ids",
"",
{ relay1_, relay3_ },
"{ \"ISC\": { \"relay-info\": [ { \"hop\": 33, \"link\": \"2001:db8::1\","
" \"options\": \"0x00C800080102030405060708\", \"peer\": \"2001:db8::2\" },"
" { \"hop\": 100, \"link\": \"2001:db8::5\","
" \"options\": \"0x00250006010203040506003500086464646464646464\","
" \"remote-id\": \"010203040506\","
" \"relay-id\": \"6464646464646464\","
" \"peer\": \"2001:db8::6\" } ] } }",
true
},
{
"original relay context, no relay",
"{ \"ISC\": { \"relay-info\": [ { \"hop\": 33, \"link\": \"2001:db8::1\","
" \"options\": \"0x00C800080102030405060708\", \"peer\": \"2001:db8::2\" } ] } }",
{},
"{ \"ISC\": { \"relay-info\": [ { \"hop\": 33, \"link\": \"2001:db8::1\","
" \"options\": \"0x00C800080102030405060708\", \"peer\": \"2001:db8::2\" } ] } }",
false
},
{
"original relay context, different relay",
"{ \"ISC\": { \"relay-info\": [ { \"hop\": 33, \"link\": \"2001:db8::1\","
" \"options\": \"0x00C800080102030405060708\", \"peer\": \"2001:db8::2\" } ] } }",
{ relay2_ },
"{ \"ISC\": { \"relay-info\": [ { \"hop\": 77, \"link\": \"2001:db8::3\","
" \"peer\": \"2001:db8::4\" } ] } }",
true
},
{
"original relay context, different relay with remote and relay ids",
"{ \"ISC\": { \"relay-info\": [ { \"hop\": 33, \"link\": \"2001:db8::1\","
" \"options\": \"0x00C800080102030405060708\", \"peer\": \"2001:db8::2\" } ] } }",
{ relay3_ },
"{ \"ISC\": { \"relay-info\": [ { \"hop\": 100, \"link\": \"2001:db8::5\","
" \"options\": \"0x00250006010203040506003500086464646464646464\","
" \"remote-id\": \"010203040506\","
" \"relay-id\": \"6464646464646464\","
" \"peer\": \"2001:db8::6\" } ] } }",
true
}
};
// Allocate a lease.
Lease6Ptr lease;
AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false,
Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 1234)));
ASSERT_NO_THROW(lease = expectOneLease(engine_.allocateLeases6(ctx)));
ASSERT_TRUE(lease);
// All scenarios require storage to be enabled.
subnet_->setStoreExtendedInfo(true);
// Verify that the lease begins with no user context.
ConstElementPtr user_context = lease->getContext();
ASSERT_FALSE(user_context);
// Iterate over the test scenarios.
ElementPtr orig_context;
ElementPtr exp_context;
for (auto const& scenario : scenarios) {
SCOPED_TRACE(scenario.description_);
// Create the original user context from JSON.
if (scenario.orig_context_json_.empty()) {
orig_context.reset();
} else {
ASSERT_NO_THROW(orig_context = Element::fromJSON(scenario.orig_context_json_))
<< "invalid orig_context_json_, test is broken";
}
// Create the expected user context from JSON.
if (scenario.exp_context_json_.empty()) {
exp_context.reset();
} else {
ASSERT_NO_THROW(exp_context = Element::fromJSON(scenario.exp_context_json_))
<< "invalid exp_context_json_, test is broken";
}
// Initialize lease's user context.
lease->setContext(orig_context);
if (!orig_context) {
ASSERT_FALSE(lease->getContext());
} else {
ASSERT_TRUE(lease->getContext());
ASSERT_TRUE(orig_context->equals(*(lease->getContext())));
}
// Set the client packet relay vector from the scenario.
ctx.query_->relay_info_ = scenario.relays_;
// Call AllocEngine::updateLease6ExtendeInfo().
ASSERT_NO_THROW_LOG(engine_.callUpdateLease6ExtendedInfo(lease, ctx));
bool ret = (lease->extended_info_action_ == Lease6::ACTION_UPDATE);
// Reset the lease action.
lease->extended_info_action_ = Lease6::ACTION_IGNORE;
ASSERT_EQ(scenario.exp_ret, ret);
// Verify the lease has the expected user context content.
if (!exp_context) {
ASSERT_FALSE(lease->getContext());
} else {
ASSERT_TRUE(lease->getContext());
ASSERT_TRUE(exp_context->equals(*(lease->getContext())))
<< "expected: " << *(exp_context) << std::endl
<< " actual: " << *(lease->getContext()) << std::endl;
}
}
}
// Verifies that the extended data (RelayInfos for now) is
// added to a V6 lease when leases are created and/or renewed,
// when store-extended-info is true.
TEST_F(AllocEngine6ExtendedInfoTest, storeExtendedInfoEnabled6) {
// Structure that defines a test scenario.
struct Scenario {
std::string description_; // test description
DuidPtr duid_; // client DUID
std::vector<Pkt6::RelayInfo> relays_; // vector of relays from pkt
std::string exp_context_json_; // expected user context on the lease
IOAddress exp_address_; // expected lease address
};
// Test scenarios.
std::vector<Scenario> scenarios {
{
"create client one without relays",
duid1_,
{},
"",
duid1_addr_
},
{
"renew client one without relays",
DuidPtr(),
{},
"",
duid1_addr_
},
{
"create client two with relays",
duid2_,
{ relay1_, relay2_ },
"{ \"ISC\": { \"relay-info\": [ { \"hop\": 33, \"link\": \"2001:db8::1\","
" \"options\": \"0x00C800080102030405060708\", \"peer\": \"2001:db8::2\" },"
" { \"hop\": 77, \"link\": \"2001:db8::3\", \"peer\": \"2001:db8::4\" } ] } }",
duid2_addr_
},
{
"renew client two without rai",
DuidPtr(),
{},
"{ \"ISC\": { \"relay-info\": [ { \"hop\": 33, \"link\": \"2001:db8::1\","
" \"options\": \"0x00C800080102030405060708\", \"peer\": \"2001:db8::2\" },"
" { \"hop\": 77, \"link\": \"2001:db8::3\", \"peer\": \"2001:db8::4\" } ] } }",
duid2_addr_
}};
// All of the scenarios require storage to be enabled.
subnet_->setStoreExtendedInfo(true);
// Iterate over the test scenarios.
DuidPtr current_duid;
for (auto const& scenario : scenarios) {
SCOPED_TRACE(scenario.description_);
ElementPtr exp_context;
// Create the expected user context from JSON.
if (!scenario.exp_context_json_.empty()) {
ASSERT_NO_THROW(exp_context = Element::fromJSON(scenario.exp_context_json_))
<< "invalid exp_context_json_, test is broken";
}
Pkt6Ptr pkt;
if (scenario.duid_) {
current_duid = scenario.duid_;
pkt.reset(new Pkt6(DHCPV6_REQUEST, 1234));
} else {
pkt.reset(new Pkt6(DHCPV6_RENEW, 1234));
}
// Set packet relay vector from the scenario.
pkt->relay_info_ = scenario.relays_;
// Create the context;
AllocEngine::ClientContext6 ctx(subnet_, current_duid, false, false, "", false, pkt);
// Create or renew the lease.
Lease6Ptr lease;
ASSERT_NO_THROW(lease = expectOneLease(engine_.allocateLeases6(ctx)));
ASSERT_TRUE(lease);
EXPECT_EQ(scenario.exp_address_, lease->addr_);
// Verify the lease has the expected user context content.
if (!exp_context) {
ASSERT_FALSE(lease->getContext());
} else {
ASSERT_TRUE(lease->getContext());
ASSERT_TRUE(exp_context->equals(*(lease->getContext())))
<< "expected: " << *(exp_context) << std::endl
<< " actual: " << *(lease->getContext()) << std::endl;
}
}
}
// Verifies that the extended data (RelayInfos for now) is
// not added to a V6 lease when leases are created and/or renewed,
// when store-extended-info is false.
TEST_F(AllocEngine6ExtendedInfoTest, storeExtendedInfoDisabled6) {
// Structure that defines a test scenario.
struct Scenario {
std::string description_; // test description
DuidPtr duid_; // client DUID
std::vector<Pkt6::RelayInfo> relays_; // vector of relays from pkt
IOAddress exp_address_; // expected lease address
};
// Test scenarios.
std::vector<Scenario> scenarios {
{
"create client one without relays",
duid1_,
{},
duid1_addr_
},
{
"renew client one without relays",
DuidPtr(),
{},
duid1_addr_
},
{
"create client two with relays",
duid2_,
{ relay1_, relay2_ },
duid2_addr_
},
{
"renew client two with relays",
DuidPtr(),
{ relay1_, relay2_ },
duid2_addr_
}
};
// All of the scenarios require storage to be disabled.
subnet_->setStoreExtendedInfo(false);
// Iterate over the test scenarios.
DuidPtr current_duid;
for (auto const& scenario : scenarios) {
SCOPED_TRACE(scenario.description_);
Pkt6Ptr pkt;
if (scenario.duid_) {
current_duid = scenario.duid_;
pkt.reset(new Pkt6(DHCPV6_REQUEST, 1234));
} else {
pkt.reset(new Pkt6(DHCPV6_RENEW, 1234));
}
// Set packet relay vector from the scenario.
pkt->relay_info_ = scenario.relays_;
// Create the context;
AllocEngine::ClientContext6 ctx(subnet_, current_duid, false, false, "", false, pkt);
// Create or renew the lease.
Lease6Ptr lease;
ASSERT_NO_THROW(lease = expectOneLease(engine_.allocateLeases6(ctx)));
ASSERT_TRUE(lease);
EXPECT_EQ(scenario.exp_address_, lease->addr_);
// Verify the lease had no user context content.
ASSERT_FALSE(lease->getContext());
}
}
// Verifies that the extended data (RelayInfos for now) is
// added to a V6 lease when an expired lease is reused and
// store-extended-info is true. We don't bother testing the
// disabled case as this is tested thoroughly elsewhere.
TEST_F(AllocEngine6ExtendedInfoTest, reuseExpiredLease6) {
// Create one subnet with a pool holding one address.
IOAddress addr("2001:db8:1::ad");
initSubnet(IOAddress("2001:db8:1::"), addr, addr);
subnet_->setPreferred(Triplet<uint32_t>(200, 300, 400));
subnet_->setValid(Triplet<uint32_t>(300, 400, 500));
// Create an expired lease for duid1_.
Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, duid1_, 1234,
501, 502, subnet_->getID(), HWAddrPtr()));
lease->cltt_ = time(0) - 500; // Allocated 500 seconds ago
lease->valid_lft_ = 495; // Lease was valid for 495 seconds
ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
// Make sure that we really created expired lease
ASSERT_TRUE(lease->expired());
// Asking specifically for this address with zero lifetimes
AllocEngine::ClientContext6 ctx(subnet_, duid2_, false, false, "", false,
Pkt6Ptr(new Pkt6(DHCPV6_REQUEST, 5678)));
ctx.currentIA().iaid_ = iaid_;
ctx.currentIA().addHint(addr, 128, 0, 0);
// Add a relay to the packet relay vector.
ctx.query_->relay_info_.push_back(relay1_);
// Enable extended info storage.
subnet_->setStoreExtendedInfo(true);
// Reuse the expired lease.
EXPECT_NO_THROW(lease = expectOneLease(engine_.allocateLeases6(ctx)));
// Check that we got that single lease
ASSERT_TRUE(lease);
EXPECT_EQ(addr, lease->addr_);
// Now let's verify that the extended info is in the user-context.
ASSERT_TRUE(lease->getContext());
std::string exp_content_json =
"{ \"ISC\": { \"relay-info\": [ { \"hop\": 33, \"link\": \"2001:db8::1\","
" \"options\": \"0x00C800080102030405060708\", \"peer\": \"2001:db8::2\" } ] } }";
ConstElementPtr exp_context;
ASSERT_NO_THROW(exp_context = Element::fromJSON(exp_content_json))
<< "invalid exp_context_json_, test is broken";
ASSERT_TRUE(exp_context->equals(*(lease->getContext())))
<< "expected: " << *(exp_context) << std::endl
<< " actual: " << *(lease->getContext()) << std::endl;
}
// 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(100)));
ASSERT_TRUE(engine);
// Set the threshold to 25%.
subnet_->setCacheThreshold(.25);
IOAddress addr("2001:db8:1::15");
time_t now = time(0) - 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)));
ASSERT_TRUE(lease);
EXPECT_EQ(addr, lease->addr_);
EXPECT_EQ(128, lease->prefixlen_);
// The lease was not reused.
EXPECT_EQ(0, lease->reuseable_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) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
// Set the threshold to 33%.
subnet_->setCacheThreshold(.33);
IOAddress addr("2001:db8:1::15");
time_t now = time(0) - 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 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)));
ASSERT_TRUE(lease);
EXPECT_EQ(addr, lease->addr_);
EXPECT_EQ(128, lease->prefixlen_);
// The lease was reused.
time_t age = lease->cltt_ - now;
EXPECT_GE(age, 100);
EXPECT_LE(age, 110);
EXPECT_EQ(400 - age, lease->reuseable_valid_lft_);
EXPECT_EQ(300 - age, lease->reuseable_preferred_lft_);
// Check other lease parameters.
EXPECT_TRUE(*lease->duid_ == *duid_);
EXPECT_TRUE(ctx.isAllocated(addr));
// 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 (renew) using cache threshold.
TEST_F(AllocEngine6Test, renewCacheThreshold6) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
// Set the threshold to 25%.
subnet_->setCacheThreshold(.25);
IOAddress prefix("2001:db8:1:2::");
uint8_t prefixlen = 80;
time_t now = time(0) - 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());
// 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 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)));
ASSERT_TRUE(lease);
EXPECT_EQ(prefix, lease->addr_);
EXPECT_EQ(prefixlen, lease->prefixlen_);
// The lease was reused.
time_t age = lease->cltt_ - now;
EXPECT_GE(age, 100);
EXPECT_LE(age, 110);
EXPECT_EQ(400 - age, lease->reuseable_valid_lft_);
EXPECT_EQ(300 - age, lease->reuseable_preferred_lft_);
// Check other lease parameters.
EXPECT_TRUE(*lease->duid_ == *duid_);
EXPECT_TRUE(ctx.isAllocated(prefix, prefixlen));
// 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 max age.
TEST_F(AllocEngine6Test, requestCacheMaxAge6) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
// Set the max age to 150.
subnet_->setCacheMaxAge(150);
IOAddress addr("2001:db8:1::15");
time_t now = time(0) - 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 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)));
ASSERT_TRUE(lease);
EXPECT_EQ(addr, lease->addr_);
EXPECT_EQ(128, lease->prefixlen_);
// The lease was reused.
time_t age = lease->cltt_ - now;
EXPECT_GE(age, 100);
EXPECT_LE(age, 110);
EXPECT_EQ(400 - age, lease->reuseable_valid_lft_);
EXPECT_EQ(300 - age, lease->reuseable_preferred_lft_);
// Check other lease parameters.
EXPECT_TRUE(*lease->duid_ == *duid_);
EXPECT_TRUE(ctx.isAllocated(addr));
// 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 (renew) using cache max age.
TEST_F(AllocEngine6Test, renewCacheMaxAge6) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
// Set the max age to 150.
subnet_->setCacheMaxAge(150);
IOAddress prefix("2001:db8:1:2::");
uint8_t prefixlen = 80;
time_t now = time(0) - 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());
// 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 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)));
ASSERT_TRUE(lease);
EXPECT_EQ(prefix, lease->addr_);
EXPECT_EQ(prefixlen, lease->prefixlen_);
// The lease was reused.
time_t age = lease->cltt_ - now;
EXPECT_GE(age, 100);
EXPECT_LE(age, 110);
EXPECT_EQ(400 - age, lease->reuseable_valid_lft_);
EXPECT_EQ(300 - age, lease->reuseable_preferred_lft_);
// Check other lease parameters.
EXPECT_TRUE(*lease->duid_ == *duid_);
EXPECT_TRUE(ctx.isAllocated(prefix, prefixlen));
// 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 both cache threshold
// and max age.
TEST_F(AllocEngine6Test, requestCacheBoth6) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
// Set the threshold to 25%.
subnet_->setCacheThreshold(.25);
// Set the max age to 150.
subnet_->setCacheMaxAge(150);
IOAddress addr("2001:db8:1::15");
time_t now = time(0) - 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 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)));
ASSERT_TRUE(lease);
EXPECT_EQ(addr, lease->addr_);
EXPECT_EQ(128, lease->prefixlen_);
// The lease was reused.
time_t age = lease->cltt_ - now;
EXPECT_GE(age, 100);
EXPECT_LE(age, 110);
EXPECT_EQ(400 - age, lease->reuseable_valid_lft_);
EXPECT_EQ(300 - age, lease->reuseable_preferred_lft_);
// Check other lease parameters.
EXPECT_TRUE(*lease->duid_ == *duid_);
EXPECT_TRUE(ctx.isAllocated(addr));
// 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 (renew) using both cache threshold
// and max age.
TEST_F(AllocEngine6Test, renewCacheBoth6) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
// Set the threshold to 25%.
subnet_->setCacheThreshold(.25);
// Set the max age to 150.
subnet_->setCacheMaxAge(150);
IOAddress prefix("2001:db8:1:2::");
uint8_t prefixlen = 80;
time_t now = time(0) - 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());
// 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 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)));
ASSERT_TRUE(lease);
EXPECT_EQ(prefix, lease->addr_);
EXPECT_EQ(prefixlen, lease->prefixlen_);
// The lease was reused.
time_t age = lease->cltt_ - now;
EXPECT_GE(age, 100);
EXPECT_LE(age, 110);
EXPECT_EQ(400 - age, lease->reuseable_valid_lft_);
EXPECT_EQ(300 - age, lease->reuseable_preferred_lft_);
// Check other lease parameters.
EXPECT_TRUE(*lease->duid_ == *duid_);
EXPECT_TRUE(ctx.isAllocated(prefix, prefixlen));
// 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't be reused (request) using too small
// cache threshold.
TEST_F(AllocEngine6Test, requestCacheBadThreshold6) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
// 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(0) - 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)));
ASSERT_TRUE(lease);
EXPECT_EQ(addr, lease->addr_);
EXPECT_EQ(128, lease->prefixlen_);
// The lease was not reused.
EXPECT_EQ(0, lease->reuseable_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 threshold.
TEST_F(AllocEngine6Test, renewCacheBadThreshold6) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
// 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(0) - 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)));
ASSERT_TRUE(lease);
EXPECT_EQ(prefix, lease->addr_);
EXPECT_EQ(prefixlen, lease->prefixlen_);
// The lease was not reused.
EXPECT_EQ(0, lease->reuseable_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(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(0) - 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)));
ASSERT_TRUE(lease);
EXPECT_EQ(addr, lease->addr_);
EXPECT_EQ(128, lease->prefixlen_);
// The lease was not reused.
EXPECT_EQ(0, lease->reuseable_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(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(0) - 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)));
ASSERT_TRUE(lease);
EXPECT_EQ(prefix, lease->addr_);
EXPECT_EQ(prefixlen, lease->prefixlen_);
// The lease was not reused.
EXPECT_EQ(0, lease->reuseable_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) when the valid
// lifetime was reduced.
// This works only when the lifetime is recomputed.
TEST_F(AllocEngine6Test, renewCacheReducedValid6) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
// Set valid lifetime to 200.
subnet_->setValid(200);
// Set the threshold to 25%.
subnet_->setCacheThreshold(.25);
IOAddress addr("2001:db8:1::15");
time_t now = time(0) - 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 renew.
Pkt6Ptr query(new Pkt6(DHCPV6_RENEW, 1234));
AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", false,
query);
ctx.currentIA().iaid_ = iaid_;
ctx.currentIA().addHint(addr);
EXPECT_NO_THROW(lease = expectOneLease(engine->renewLeases6(ctx)));
ASSERT_TRUE(lease);
EXPECT_EQ(addr, lease->addr_);
EXPECT_EQ(128, lease->prefixlen_);
// The lease was not reused.
EXPECT_EQ(0, lease->reuseable_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) when the preferred
// lifetime was reduced.
// This works only when the lifetime is recomputed.
TEST_F(AllocEngine6Test, renewCacheReducedPreferred6) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
// Set preferred lifetime to 100.
subnet_->setPreferred(100);
// Set the threshold to 25%.
subnet_->setCacheThreshold(.25);
IOAddress prefix("2001:db8:1:2::");
uint8_t prefixlen = 80;
time_t now = time(0) - 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)));
ASSERT_TRUE(lease);
EXPECT_EQ(prefix, lease->addr_);
EXPECT_EQ(prefixlen, lease->prefixlen_);
// The lease was not reused.
EXPECT_EQ(0, lease->reuseable_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) when DDNS parameter changed.
TEST_F(AllocEngine6Test, requestCacheFwdDDNS6) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
// Set the threshold to 25%.
subnet_->setCacheThreshold(.25);
IOAddress addr("2001:db8:1::15");
time_t now = time(0) - 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_, true, false, "", false,
query);
ctx.currentIA().iaid_ = iaid_;
ctx.currentIA().addHint(addr);
EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
ASSERT_TRUE(lease);
EXPECT_EQ(addr, lease->addr_);
EXPECT_EQ(128, lease->prefixlen_);
// The lease was not reused.
EXPECT_EQ(0, lease->reuseable_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) when DDNS parameter changed.
TEST_F(AllocEngine6Test, renewCacheFwdDDNS6) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
// Set the threshold to 25%.
subnet_->setCacheThreshold(.25);
IOAddress prefix("2001:db8:1:2::");
uint8_t prefixlen = 80;
time_t now = time(0) - 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_, true, 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)));
ASSERT_TRUE(lease);
EXPECT_EQ(prefix, lease->addr_);
EXPECT_EQ(prefixlen, lease->prefixlen_);
// The lease was not reused.
EXPECT_EQ(0, lease->reuseable_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) when DDNS parameter changed.
TEST_F(AllocEngine6Test, requestCacheRevDDNS6) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
// Set the threshold to 25%.
subnet_->setCacheThreshold(.25);
IOAddress addr("2001:db8:1::15");
time_t now = time(0) - 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, true, "", false,
query);
ctx.currentIA().iaid_ = iaid_;
ctx.currentIA().addHint(addr);
EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
ASSERT_TRUE(lease);
EXPECT_EQ(addr, lease->addr_);
EXPECT_EQ(128, lease->prefixlen_);
// The lease was not reused.
EXPECT_EQ(0, lease->reuseable_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) when DDNS parameter changed.
TEST_F(AllocEngine6Test, renewCacheRevDDNS6) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
// Set the threshold to 25%.
subnet_->setCacheThreshold(.25);
IOAddress prefix("2001:db8:1:2::");
uint8_t prefixlen = 80;
time_t now = time(0) - 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, true, "", 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)));
ASSERT_TRUE(lease);
EXPECT_EQ(prefix, lease->addr_);
EXPECT_EQ(prefixlen, lease->prefixlen_);
// The lease was not reused.
EXPECT_EQ(0, lease->reuseable_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) when hostname changed.
TEST_F(AllocEngine6Test, requestCacheHostname6) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
// Set the threshold to 25%.
subnet_->setCacheThreshold(.25);
IOAddress addr("2001:db8:1::15");
time_t now = time(0) - 100; // Allocated 100 seconds ago.
Lease6Ptr lease(new Lease6(Lease::TYPE_NA, addr, duid_, iaid_,
300, 400, subnet_->getID(),
false, false, "foo"));
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, "bar", false,
query);
ctx.currentIA().iaid_ = iaid_;
ctx.currentIA().addHint(addr);
EXPECT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
ASSERT_TRUE(lease);
EXPECT_EQ(addr, lease->addr_);
EXPECT_EQ(128, lease->prefixlen_);
// The lease was not reused.
EXPECT_EQ(0, lease->reuseable_valid_lft_);
EXPECT_EQ("bar", lease->hostname_);
// 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) when hostname changed.
TEST_F(AllocEngine6Test, renewCacheHostname6) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
// Set the threshold to 25%.
subnet_->setCacheThreshold(.25);
IOAddress prefix("2001:db8:1:2::");
uint8_t prefixlen = 80;
time_t now = time(0) - 100; // Allocated 100 seconds ago.
Lease6Ptr lease(new Lease6(Lease::TYPE_PD, prefix, duid_, iaid_,
300, 400, subnet_->getID(),
false, false, "foo",
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, "bar", 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)));
ASSERT_TRUE(lease);
EXPECT_EQ(prefix, lease->addr_);
EXPECT_EQ(prefixlen, lease->prefixlen_);
// The lease was not reused.
EXPECT_EQ(0, lease->reuseable_valid_lft_);
EXPECT_EQ("bar", lease->hostname_);
// 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);
}
// Verifies that AllocEngine::getLifetimes6() returns the appropriate
// valid lifetime value based on the context content.
TEST_F(AllocEngine6Test, getValidLifetime) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
// Let's make three classes, two with valid-lifetime and one without,
// and add them to the dictionary.
ClientClassDictionaryPtr dictionary = CfgMgr::instance().getStagingCfg()->getClientClassDictionary();
ClientClassDefPtr class_def(new ClientClassDef("valid_one", ExpressionPtr()));
Triplet<uint32_t> valid_one(50, 100, 150);
class_def->setValid(valid_one);
dictionary->addClass(class_def);
class_def.reset(new ClientClassDef("valid_two", ExpressionPtr()));
Triplet<uint32_t>valid_two(200, 250, 300);
class_def->setValid(valid_two);
dictionary->addClass(class_def);
class_def.reset(new ClientClassDef("valid_unspec", ExpressionPtr()));
dictionary->addClass(class_def);
// Commit our class changes.
CfgMgr::instance().commit();
// Update the subnet's triplet to something more useful.
subnet_->setValid(Triplet<uint32_t>(500, 1000, 1500));
// Describes a test scenario.
struct Scenario {
std::string desc_; // descriptive text for logging
std::vector<std::string> classes_; // class list of assigned classes
uint32_t requested_lft_; // use as option 51 is > 0
uint32_t exp_valid_; // expected lifetime
};
// Scenarios to test.
std::vector<Scenario> scenarios = {
{
"no classes, no hint",
{},
0,
subnet_->getValid()
},
{
"no classes, hint",
{},
subnet_->getValid().getMin() + 50,
subnet_->getValid().getMin() + 50
},
{
"no classes, hint too small",
{},
subnet_->getValid().getMin() - 50,
subnet_->getValid().getMin()
},
{
"no classes, hint too big",
{},
subnet_->getValid().getMax() + 50,
subnet_->getValid().getMax()
},
{
"class unspecified, no hint",
{ "valid_unspec" },
0,
subnet_->getValid()
},
{
"from last class, no hint",
{ "valid_unspec", "valid_one" },
0,
valid_one.get()
},
{
"from first class, no hint",
{ "valid_two", "valid_one" },
0,
valid_two.get()
},
{
"class plus hint",
{ "valid_one" },
valid_one.getMin() + 25,
valid_one.getMin() + 25
},
{
"class plus hint too small",
{ "valid_one" },
valid_one.getMin() - 25,
valid_one.getMin()
},
{
"class plus hint too big",
{ "valid_one" },
valid_one.getMax() + 25,
valid_one.getMax()
}
};
// Iterate over the scenarios and verify the correct outcome.
for (auto const& scenario : scenarios) {
SCOPED_TRACE(scenario.desc_); {
// Create a context;
AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", true,
Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234)));
// Add client classes (if any)
for (auto const& class_name : scenario.classes_) {
ctx.query_->addClass(class_name);
}
// Add hint
ctx.currentIA().iaid_ = iaid_;
// prefix, prefixlen, preferred, valid
ctx.currentIA().addHint(IOAddress("::"), 128, 0, scenario.requested_lft_);
Lease6Ptr lease;
ASSERT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
ASSERT_TRUE(lease);
EXPECT_EQ(lease->valid_lft_, scenario.exp_valid_);
}
}
}
// Verifies that AllocEngine::getLifetimes6() returns the appropriate
// valid lifetime value based on the context content.
TEST_F(AllocEngine6Test, getTemplateClassValidLifetime) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
// Let's make three classes, two with valid-lifetime and one without,
// and add them to the dictionary.
ClientClassDictionaryPtr dictionary = CfgMgr::instance().getStagingCfg()->getClientClassDictionary();
ExpressionPtr match_expr;
ExpressionParser parser;
ElementPtr test_cfg = Element::create("'valid_one_value'");
parser.parse(match_expr, test_cfg, AF_INET6, EvalContext::acceptAll, EvalContext::PARSER_STRING);
ClientClassDefPtr class_def(new TemplateClientClassDef("valid_one", match_expr));
Triplet<uint32_t> valid_one(50, 100, 150);
class_def->setValid(valid_one);
dictionary->addClass(class_def);
test_cfg = Element::create("'valid_two_value'");
parser.parse(match_expr, test_cfg, AF_INET6, EvalContext::acceptAll, EvalContext::PARSER_STRING);
class_def.reset(new TemplateClientClassDef("valid_two", match_expr));
Triplet<uint32_t>valid_two(200, 250, 300);
class_def->setValid(valid_two);
dictionary->addClass(class_def);
test_cfg = Element::create("'valid_unspec_value'");
parser.parse(match_expr, test_cfg, AF_INET6, EvalContext::acceptAll, EvalContext::PARSER_STRING);
class_def.reset(new TemplateClientClassDef("valid_unspec", match_expr));
dictionary->addClass(class_def);
// Commit our class changes.
CfgMgr::instance().commit();
// Update the subnet's triplet to something more useful.
subnet_->setValid(Triplet<uint32_t>(500, 1000, 1500));
// Describes a test scenario.
struct Scenario {
std::string desc_; // descriptive text for logging
std::vector<std::string> classes_; // class list of assigned classes
uint32_t requested_lft_; // use as option 51 is > 0
uint32_t exp_valid_; // expected lifetime
};
// Scenarios to test.
std::vector<Scenario> scenarios = {
{
"no classes, no hint",
{},
0,
subnet_->getValid()
},
{
"no classes, hint",
{},
subnet_->getValid().getMin() + 50,
subnet_->getValid().getMin() + 50
},
{
"no classes, hint too small",
{},
subnet_->getValid().getMin() - 50,
subnet_->getValid().getMin()
},
{
"no classes, hint too big",
{},
subnet_->getValid().getMax() + 50,
subnet_->getValid().getMax()
},
{
"class unspecified, no hint",
{ "valid_unspec" },
0,
subnet_->getValid()
},
{
"from last class, no hint",
{ "valid_unspec", "valid_one" },
0,
valid_one.get()
},
{
"from first class, no hint",
{ "valid_two", "valid_one" },
0,
valid_two.get()
},
{
"class plus hint",
{ "valid_one" },
valid_one.getMin() + 25,
valid_one.getMin() + 25
},
{
"class plus hint too small",
{ "valid_one" },
valid_one.getMin() - 25,
valid_one.getMin()
},
{
"class plus hint too big",
{ "valid_one" },
valid_one.getMax() + 25,
valid_one.getMax()
}
};
// Iterate over the scenarios and verify the correct outcome.
for (auto const& scenario : scenarios) {
SCOPED_TRACE(scenario.desc_); {
// Create a context;
AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", true,
Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234)));
// Add client classes (if any)
for (auto const& class_name : scenario.classes_) {
ctx.query_->addClass(class_name);
}
// Add hint
ctx.currentIA().iaid_ = iaid_;
// prefix, prefixlen, preferred, valid
ctx.currentIA().addHint(IOAddress("::"), 128, 0, scenario.requested_lft_);
Lease6Ptr lease;
ASSERT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
ASSERT_TRUE(lease);
EXPECT_EQ(lease->valid_lft_, scenario.exp_valid_);
}
}
}
// Verifies that AllocEngine::getLifetimes6() returns the appropriate
// preferred lifetime value based on the context content.
TEST_F(AllocEngine6Test, getPreferredLifetime) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
// Let's make three classes, two with preferred-lifetime and one without,
// and add them to the dictionary.
ClientClassDictionaryPtr dictionary = CfgMgr::instance().getStagingCfg()->getClientClassDictionary();
ClientClassDefPtr class_def(new ClientClassDef("preferred_one", ExpressionPtr()));
Triplet<uint32_t> preferred_one(50, 100, 150);
class_def->setPreferred(preferred_one);
dictionary->addClass(class_def);
class_def.reset(new ClientClassDef("preferred_two", ExpressionPtr()));
Triplet<uint32_t>preferred_two(200, 250, 300);
class_def->setPreferred(preferred_two);
dictionary->addClass(class_def);
class_def.reset(new ClientClassDef("preferred_unspec", ExpressionPtr()));
dictionary->addClass(class_def);
// Commit our class changes.
CfgMgr::instance().commit();
// Ensure subnet valid-lft is 400.
subnet_->setValid(Triplet<uint32_t>(400, 400, 400));
// Convenience Triplet values.
Triplet<uint32_t> unspecified;
Triplet<uint32_t> specified(Triplet<uint32_t>(301, 350, 450));
// Describes a test scenario.
struct Scenario {
std::string desc_; // descriptive text for logging
std::vector<std::string> classes_; // class list of assigned classes
Triplet<uint32_t> subnet_pref_; // subnet's preferred lifetime triplet.
uint32_t requested_lft_; // use as option 51 is > 0
uint32_t exp_preferred_; // expected lifetime
};
// Scenarios to test.
std::vector<Scenario> scenarios = {
{
"no classes, no hint, subnet specified",
{},
specified,
0,
specified
},
{
"no classes, no hint, subnet unspecified",
{},
unspecified,
0,
(subnet_->getValid() * 5 / 8)
},
{
"no classes, hint",
{},
specified,
subnet_->getPreferred().getMin() + 50,
subnet_->getPreferred().getMin() + 50
},
{
"no classes, hint too small",
{},
specified,
specified.getMin() - 50,
specified.getMin()
},
{
"no classes, hint too big",
{},
specified,
specified.getMax() + 50,
(subnet_->getValid() * 5 / 8)
},
{
"class unspecified, no hint",
{ "preferred_unspec" },
specified,
0,
specified
},
{
"from last class, no hint",
{ "preferred_unspec", "preferred_one" },
specified,
0,
preferred_one.get()
},
{
"from first class, no hint",
{ "preferred_two", "preferred_one" },
specified,
0,
preferred_two.get()
},
{
"class plus hint",
{ "preferred_one" },
specified,
preferred_one.getMin() + 25,
preferred_one.getMin() + 25
},
{
"class plus hint too small",
{ "preferred_one" },
specified,
preferred_one.getMin() - 25,
preferred_one.getMin()
},
{
"class plus hint too big",
{ "preferred_one" },
specified,
preferred_one.getMax() + 25,
preferred_one.getMax()
}
};
// Iterate over the scenarios and verify the correct outcome.
for (auto const& scenario : scenarios) {
SCOPED_TRACE(scenario.desc_); {
// Set the subnet's preferred-lifetime triplet.
subnet_->setPreferred(scenario.subnet_pref_);
// Create a context;
AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", true,
Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234)));
// Add client classes (if any)
for (auto const& class_name : scenario.classes_) {
string subclass(TemplateClientClassDef::SPAWN_CLASS_PREFIX);
subclass += class_name;
subclass += "_value";
ctx.query_->addSubClass(class_name, subclass);
}
// Add hint
ctx.currentIA().iaid_ = iaid_;
// prefix, prefixlen, preferred, valid
ctx.currentIA().addHint(IOAddress("::"), 128, scenario.requested_lft_, 0);
Lease6Ptr lease;
ASSERT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
ASSERT_TRUE(lease);
EXPECT_EQ(lease->preferred_lft_, scenario.exp_preferred_);
}
}
}
// Verifies that AllocEngine::getLifetimes6() returns the appropriate
// preferred lifetime value based on the context content.
TEST_F(AllocEngine6Test, getTemplateClassPreferredLifetime) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
// Let's make three classes, two with preferred-lifetime and one without,
// and add them to the dictionary.
ClientClassDictionaryPtr dictionary = CfgMgr::instance().getStagingCfg()->getClientClassDictionary();
ExpressionPtr match_expr;
ExpressionParser parser;
ElementPtr test_cfg = Element::create("'preferred_one_value'");
parser.parse(match_expr, test_cfg, AF_INET6, EvalContext::acceptAll, EvalContext::PARSER_STRING);
ClientClassDefPtr class_def(new TemplateClientClassDef("preferred_one", match_expr));
Triplet<uint32_t> preferred_one(50, 100, 150);
class_def->setPreferred(preferred_one);
dictionary->addClass(class_def);
test_cfg = Element::create("'preferred_two_value'");
parser.parse(match_expr, test_cfg, AF_INET6, EvalContext::acceptAll, EvalContext::PARSER_STRING);
class_def.reset(new TemplateClientClassDef("preferred_two", match_expr));
Triplet<uint32_t>preferred_two(200, 250, 300);
class_def->setPreferred(preferred_two);
dictionary->addClass(class_def);
test_cfg = Element::create("'preferred_unspec_value'");
parser.parse(match_expr, test_cfg, AF_INET6, EvalContext::acceptAll, EvalContext::PARSER_STRING);
class_def.reset(new TemplateClientClassDef("preferred_unspec", match_expr));
dictionary->addClass(class_def);
// Commit our class changes.
CfgMgr::instance().commit();
// Update the subnet's triplet to something more useful. Note max is
// intentionally larger than valid-lft.
subnet_->setPreferred(Triplet<uint32_t>(301, 350, 450));
// Describes a test scenario.
struct Scenario {
std::string desc_; // descriptive text for logging
std::vector<std::string> classes_; // class list of assigned classes
uint32_t requested_lft_; // use as option 51 is > 0
uint32_t exp_preferred_; // expected lifetime
};
// Scenarios to test.
std::vector<Scenario> scenarios = {
{
"no classes, no hint",
{},
0,
subnet_->getPreferred()
},
{
"no classes, hint",
{},
subnet_->getPreferred().getMin() + 50,
subnet_->getPreferred().getMin() + 50
},
{
"no classes, hint too small",
{},
subnet_->getPreferred().getMin() - 50,
subnet_->getPreferred().getMin()
},
{
"no classes, hint too big",
{},
subnet_->getPreferred().getMax() + 50,
(subnet_->getValid() * 5 / 8)
},
{
"class unspecified, no hint",
{ "preferred_unspec" },
0,
subnet_->getPreferred()
},
{
"from last class, no hint",
{ "preferred_unspec", "preferred_one" },
0,
preferred_one.get()
},
{
"from first class, no hint",
{ "preferred_two", "preferred_one" },
0,
preferred_two.get()
},
{
"class plus hint",
{ "preferred_one" },
preferred_one.getMin() + 25,
preferred_one.getMin() + 25
},
{
"class plus hint too small",
{ "preferred_one" },
preferred_one.getMin() - 25,
preferred_one.getMin()
},
{
"class plus hint too big",
{ "preferred_one" },
preferred_one.getMax() + 25,
preferred_one.getMax()
}
};
// Iterate over the scenarios and verify the correct outcome.
for (auto const& scenario : scenarios) {
SCOPED_TRACE(scenario.desc_); {
// Create a context;
AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", true,
Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234)));
// Add client classes (if any)
for (auto const& class_name : scenario.classes_) {
string subclass(TemplateClientClassDef::SPAWN_CLASS_PREFIX);
subclass += class_name;
subclass += "_value";
ctx.query_->addSubClass(class_name, subclass);
}
// Add hint
ctx.currentIA().iaid_ = iaid_;
// prefix, prefixlen, preferred, valid
ctx.currentIA().addHint(IOAddress("::"), 128, scenario.requested_lft_, 0);
Lease6Ptr lease;
ASSERT_NO_THROW(lease = expectOneLease(engine->allocateLeases6(ctx)));
ASSERT_TRUE(lease);
EXPECT_EQ(lease->preferred_lft_, scenario.exp_preferred_);
}
}
}
// Verifies that AllocEngine::getMinLifetimes6() returns the appropriate
// valid lifetime value based on the context content.
TEST_F(AllocEngine6Test, getMinValidLifetime) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
// Let's make three classes, two with valid-lifetime and one without,
// and add them to the dictionary.
ClientClassDictionaryPtr dictionary = CfgMgr::instance().getStagingCfg()->getClientClassDictionary();
ExpressionPtr match_expr;
ExpressionParser parser;
ElementPtr test_cfg = Element::create("'valid_one_value'");
parser.parse(match_expr, test_cfg, AF_INET6, EvalContext::acceptAll, EvalContext::PARSER_STRING);
ClientClassDefPtr class_def(new TemplateClientClassDef("valid_one", match_expr));
Triplet<uint32_t> valid_one(50, 100, 150);
class_def->setValid(valid_one);
dictionary->addClass(class_def);
test_cfg = Element::create("'valid_two_value'");
parser.parse(match_expr, test_cfg, AF_INET6, EvalContext::acceptAll, EvalContext::PARSER_STRING);
class_def.reset(new TemplateClientClassDef("valid_two", match_expr));
Triplet<uint32_t>valid_two(200, 250, 300);
class_def->setValid(valid_two);
dictionary->addClass(class_def);
test_cfg = Element::create("'valid_unspec_value'");
parser.parse(match_expr, test_cfg, AF_INET6, EvalContext::acceptAll, EvalContext::PARSER_STRING);
class_def.reset(new TemplateClientClassDef("valid_unspec", match_expr));
dictionary->addClass(class_def);
// Commit our class changes.
CfgMgr::instance().commit();
// Update the subnet's triplet to something more useful.
subnet_->setValid(Triplet<uint32_t>(500, 1000, 1500));
// Describes a test scenario.
struct Scenario {
std::string desc_; // descriptive text for logging
std::vector<std::string> classes_; // class list of assigned classes
uint32_t requested_lft_; // use as option 51 is > 0
uint32_t remaining_lft_; // remaining valid lifetime
uint32_t exp_valid_; // expected lifetime
};
// Scenarios to test.
std::vector<Scenario> scenarios = {
{
"no classes, no hint, remain 0",
{},
0,
0,
subnet_->getValid().getMin()
},
{
"no classes, no hint, remain too small",
{},
0,
100,
subnet_->getValid().getMin()
},
{
"no classes, no hint, remain",
{},
0,
800,
800
},
{
"no classes, hint, remain 0",
{},
800,
0,
subnet_->getValid().getMin()
},
{
"class unspecified, no hint, remain 0",
{ "valid_unspec" },
0,
0,
subnet_->getValid().getMin()
},
{
"from last class, no hint, remain 0",
{ "valid_unspec", "valid_one" },
0,
0,
valid_one.getMin()
},
{
"from first class, no hint, remain 0",
{ "valid_two", "valid_one" },
0,
0,
valid_two.getMin()
},
{
"class plus remain too small",
{ "valid_one" },
0,
10,
valid_one.getMin()
},
{
"class plus remain",
{ "valid_one" },
0,
100,
100
}
};
// Iterate over the scenarios and verify the correct outcome.
for (auto const& scenario : scenarios) {
SCOPED_TRACE(scenario.desc_); {
// Create a context;
AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", true,
Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234)));
// Add client classes (if any)
for (auto const& class_name : scenario.classes_) {
ctx.query_->addClass(class_name);
}
// Add hint
ctx.currentIA().iaid_ = iaid_;
// prefix, prefixlen, preferred, valid
ctx.currentIA().addHint(IOAddress("::"), 128, 0, scenario.requested_lft_);
uint32_t valid = scenario.remaining_lft_;
uint32_t preferred = 0;
engine->getMinLifetimes6(ctx, preferred, valid);
EXPECT_EQ(valid, scenario.exp_valid_);
}
}
}
// Verifies that AllocEngine::getMinLifetimes6() returns the appropriate
// preferred lifetime value based on the context content.
TEST_F(AllocEngine6Test, getMinPreferredLifetime) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(100)));
ASSERT_TRUE(engine);
// Let's make three classes, two with preferred-lifetime and one without,
// and add them to the dictionary.
ClientClassDictionaryPtr dictionary = CfgMgr::instance().getStagingCfg()->getClientClassDictionary();
ExpressionPtr match_expr;
ExpressionParser parser;
ElementPtr test_cfg = Element::create("'preferred_one_value'");
parser.parse(match_expr, test_cfg, AF_INET6, EvalContext::acceptAll, EvalContext::PARSER_STRING);
ClientClassDefPtr class_def(new TemplateClientClassDef("preferred_one", match_expr));
Triplet<uint32_t> preferred_one(50, 100, 150);
class_def->setPreferred(preferred_one);
dictionary->addClass(class_def);
test_cfg = Element::create("'preferred_two_value'");
parser.parse(match_expr, test_cfg, AF_INET6, EvalContext::acceptAll, EvalContext::PARSER_STRING);
class_def.reset(new TemplateClientClassDef("preferred_two", match_expr));
Triplet<uint32_t>preferred_two(200, 250, 300);
class_def->setPreferred(preferred_two);
dictionary->addClass(class_def);
test_cfg = Element::create("'preferred_unspec_value'");
parser.parse(match_expr, test_cfg, AF_INET6, EvalContext::acceptAll, EvalContext::PARSER_STRING);
class_def.reset(new TemplateClientClassDef("preferred_unspec", match_expr));
dictionary->addClass(class_def);
// Commit our class changes.
CfgMgr::instance().commit();
// Update the subnet's triplet to something more useful. Note that
// valid is 400 for the subnet.
subnet_->setPreferred(Triplet<uint32_t>(300, 350, 450));
// Describes a test scenario.
struct Scenario {
std::string desc_; // descriptive text for logging
std::vector<std::string> classes_; // class list of assigned classes
uint32_t requested_lft_; // use as option 51 is > 0
uint32_t remaining_lft_; // remaining preferred lifetime
uint32_t exp_preferred_; // expected lifetime
};
// Scenarios to test.
std::vector<Scenario> scenarios = {
{
"no classes, no hint, remain 0",
{},
0,
0,
subnet_->getPreferred().getMin()
},
{
"no classes, no hint, remain too small",
{},
0,
100,
subnet_->getPreferred().getMin()
},
{
"no classes, no hint, remain",
{},
0,
350,
350
},
{
"no classes, no hint, remain too big",
{},
0,
500,
subnet_->getValid().getMin() * 5 / 8
},
{
"no classes, hint, remain 0",
{},
800,
0,
subnet_->getPreferred().getMin()
},
{
"class unspecified, no hint, remain 0",
{ "preferred_unspec" },
0,
0,
subnet_->getPreferred().getMin()
},
{
"from last class, no hint, remain 0",
{ "preferred_unspec", "preferred_one" },
0,
0,
preferred_one.getMin()
},
{
"from first class, no hint, remain 0",
{ "preferred_two", "preferred_one" },
0,
0,
preferred_two.getMin()
},
{
"class plus remain too small",
{ "preferred_one" },
0,
10,
preferred_one.getMin()
},
{
"class plus remain",
{ "preferred_one" },
0,
100,
100
}
};
// Iterate over the scenarios and verify the correct outcome.
for (auto const& scenario : scenarios) {
SCOPED_TRACE(scenario.desc_); {
// Create a context;
AllocEngine::ClientContext6 ctx(subnet_, duid_, false, false, "", true,
Pkt6Ptr(new Pkt6(DHCPV6_SOLICIT, 1234)));
// Add client classes (if any)
for (auto const& class_name : scenario.classes_) {
string subclass(TemplateClientClassDef::SPAWN_CLASS_PREFIX);
subclass += class_name;
subclass += "_value";
ctx.query_->addSubClass(class_name, subclass);
}
// Add hint
ctx.currentIA().iaid_ = iaid_;
// prefix, prefixlen, preferred, valid
ctx.currentIA().addHint(IOAddress("::"), 128, scenario.requested_lft_, 0);
uint32_t valid = 0;
uint32_t preferred = scenario.remaining_lft_;
engine->getMinLifetimes6(ctx, preferred, valid);
EXPECT_EQ(preferred, scenario.exp_preferred_);
}
}
}
// Verifies that AllocEngine::getRemaining retuns the remaining lifetime values.
TEST_F(AllocEngine6Test, getRemaining) {
// No Lease.
uint32_t valid(1);
uint32_t preferred(1);
Lease6Ptr lease;
AllocEngine::getRemaining(lease, preferred, valid);
EXPECT_EQ(0, valid);
EXPECT_EQ(0, preferred);
// Unexpected state.
valid = 1;
preferred = 1;
DuidPtr duid(new DUID(vector<uint8_t>(12, 0xff)));
const uint32_t iaid = 3568;
time_t now = time(0);
lease.reset(new Lease6(Lease::TYPE_NA, IOAddress("2001:db8:1::1"), duid,
iaid, 30, 50, 1));
lease->state_ = Lease::STATE_DECLINED;
AllocEngine::getRemaining(lease, preferred, valid);
EXPECT_EQ(0, valid);
EXPECT_EQ(0, preferred);
// Time going backward.
valid = 1;
preferred = 1;
lease->state_ = Lease::STATE_DEFAULT;
lease->cltt_ = lease->current_cltt_ = now + 100;
lease->valid_lft_ = lease->current_valid_lft_ = 50;
AllocEngine::getRemaining(lease, preferred, valid);
EXPECT_EQ(0, valid);
EXPECT_EQ(0, preferred);
// Already expired.
valid = 1;
preferred = 1;
lease->cltt_ = lease->current_cltt_ = now - 100;
AllocEngine::getRemaining(lease, preferred, valid);
EXPECT_EQ(0, valid);
EXPECT_EQ(0, preferred);
// Valid case.
now = time(0);
lease->cltt_ = lease->current_cltt_ = now - 10;
AllocEngine::getRemaining(lease, preferred, valid);
EXPECT_NEAR(40, valid, 1);
EXPECT_NEAR(20, preferred, 1);
// No longer preferred.
now = time(0);
lease->cltt_ = lease->current_cltt_ = now - 40;
AllocEngine::getRemaining(lease, preferred, valid);
EXPECT_NEAR(10, valid, 1);
EXPECT_EQ(0, preferred);
}
} // namespace test
} // namespace dhcp
} // namespace isc