<< " tries");
}
+Lease4Ptr
+AllocEngine::allocateAddress4(const SubnetPtr& subnet,
+ const ClientIdPtr& clientid,
+ const HWAddrPtr& hwaddr,
+ const IOAddress& hint,
+ bool fake_allocation /* = false */ ) {
+
+ // That check is not necessary. We create allocator in AllocEngine
+ // constructor
+ if (!allocator_) {
+ isc_throw(InvalidOperation, "No allocator selected");
+ }
+
+ // check if there's existing lease for that subnet/clientid/hwaddr combination.
+ Lease4Ptr existing = LeaseMgrFactory::instance().getLease4(hwaddr->hwaddr_, subnet->getID());
+ if (existing) {
+ // we have a lease already. This is a returning client, probably after
+ // his reboot.
+ return (existing);
+ }
+
+ existing = LeaseMgrFactory::instance().getLease4(*clientid, subnet->getID());
+ if (existing) {
+ // we have a lease already. This is a returning client, probably after
+ // his reboot.
+
+ // @todo: produce a warning. We haven't found him using MAC address, but
+ // we found him using client-id
+ return (existing);
+ }
+
+ // check if the hint is in pool and is available
+ if (subnet->inPool(hint)) {
+ existing = LeaseMgrFactory::instance().getLease4(hint);
+ if (!existing) {
+ /// @todo: check if the hint is reserved once we have host support
+ /// implemented
+
+ // the hint is valid and not currently used, let's create a lease for it
+ Lease4Ptr lease = createLease4(subnet, clientid, hwaddr, hint, fake_allocation);
+
+ // It can happen that the lease allocation failed (we could have lost
+ // the race condition. That means that the hint is lo longer usable and
+ // we need to continue the regular allocation path.
+ if (lease) {
+ return (lease);
+ }
+ } else {
+ if (existing->expired()) {
+ return (reuseExpiredLease(existing, subnet, clientid, hwaddr,
+ fake_allocation));
+ }
+
+ }
+ }
+
+ // Hint is in the pool but is not available. Search the pool until first of
+ // the following occurs:
+ // - we find a free address
+ // - we find an address for which the lease has expired
+ // - we exhaust number of tries
+ //
+ // @todo: Current code does not handle pool exhaustion well. It will be
+ // improved. Current problems:
+ // 1. with attempts set to too large value (e.g. 1000) and a small pool (e.g.
+ // 10 addresses), we will iterate over it 100 times before giving up
+ // 2. attempts 0 mean unlimited (this is really UINT_MAX, not infinite)
+ // 3. the whole concept of infinite attempts is just asking for infinite loop
+ // We may consider some form or reference counting (this pool has X addresses
+ // left), but this has one major problem. We exactly control allocation
+ // moment, but we currently do not control expiration time at all
+
+ unsigned int i = attempts_;
+ do {
+ IOAddress candidate = allocator_->pickAddress(subnet, clientid, hint);
+
+ /// @todo: check if the address is reserved once we have host support
+ /// implemented
+
+ Lease4Ptr existing = LeaseMgrFactory::instance().getLease4(candidate);
+ if (!existing) {
+ // there's no existing lease for selected candidate, so it is
+ // free. Let's allocate it.
+ Lease4Ptr lease = createLease4(subnet, clientid, hwaddr, candidate,
+ fake_allocation);
+ if (lease) {
+ return (lease);
+ }
+
+ // Although the address was free just microseconds ago, it may have
+ // been taken just now. If the lease insertion fails, we continue
+ // allocation attempts.
+ } else {
+ if (existing->expired()) {
+ return (reuseExpiredLease(existing, subnet, clientid, hwaddr,
+ fake_allocation));
+ }
+ }
+
+ // continue trying allocation until we run out of attempts
+ // (or attempts are set to 0, which means infinite)
+ --i;
+ } while ( i || !attempts_);
+
+ isc_throw(AllocFailed, "Failed to allocate address after " << attempts_
+ << " tries");
+}
+
Lease6Ptr AllocEngine::reuseExpiredLease(Lease6Ptr& expired,
const Subnet6Ptr& subnet,
const DuidPtr& duid,
return (expired);
}
+Lease4Ptr AllocEngine::reuseExpiredLease(Lease4Ptr& expired,
+ const SubnetPtr& subnet,
+ const ClientIdPtr& clientid,
+ const HWAddrPtr& hwaddr,
+ bool fake_allocation /*= false */ ) {
+
+ if (!expired->expired()) {
+ isc_throw(BadValue, "Attempt to recycle lease that is still valid");
+ }
+
+ // address, lease type and prefixlen (0) stay the same
+ expired->client_id_ = clientid;
+ expired->hwaddr_ = hwaddr->hwaddr_;
+ expired->valid_lft_ = subnet->getValid();
+ expired->t1_ = subnet->getT1();
+ expired->t2_ = subnet->getT2();
+ expired->cltt_ = time(NULL);
+ expired->subnet_id_ = subnet->getID();
+ expired->fixed_ = false;
+ expired->hostname_ = std::string("");
+ expired->fqdn_fwd_ = false;
+ expired->fqdn_rev_ = false;
+
+ /// @todo: log here that the lease was reused (there's ticket #2524 for
+ /// logging in libdhcpsrv)
+
+ if (!fake_allocation) {
+ // for REQUEST we do update the lease
+ LeaseMgrFactory::instance().updateLease4(expired);
+ }
+
+ // We do nothing for SOLICIT. We'll just update database when
+ // the client gets back to us with REQUEST message.
+
+ // it's not really expired at this stage anymore - let's return it as
+ // an updated lease
+ return (expired);
+}
+
Lease6Ptr AllocEngine::createLease6(const Subnet6Ptr& subnet,
const DuidPtr& duid,
uint32_t iaid,
}
}
+Lease4Ptr AllocEngine::createLease4(const SubnetPtr& subnet,
+ const DuidPtr& clientid,
+ const HWAddrPtr& hwaddr,
+ const IOAddress& addr,
+ bool fake_allocation /*= false */ ) {
+ if (!hwaddr) {
+ isc_throw(BadValue, "Can't create a lease with NULL HW address");
+ }
+ time_t now = time(NULL);
+ Lease4Ptr lease(new Lease4(addr, &hwaddr->hwaddr_[0], hwaddr->hwaddr_.size(),
+ &clientid->getDuid()[0], clientid->getDuid().size(),
+ subnet->getValid(), subnet->getT1(), subnet->getT2(),
+ now, subnet->getID()));
+
+ if (!fake_allocation) {
+ // That is a real (REQUEST) allocation
+ bool status = LeaseMgrFactory::instance().addLease(lease);
+
+ if (status) {
+ return (lease);
+ } else {
+ // One of many failures with LeaseMgr (e.g. lost connection to the
+ // database, database failed etc.). One notable case for that
+ // is that we are working in multi-process mode and we lost a race
+ // (some other process got that address first)
+ return (Lease4Ptr());
+ }
+ } else {
+ // That is only fake (DISCOVER) allocation
+
+ // It is for OFFER only. We should not insert the lease into LeaseMgr,
+ // but rather check that we could have inserted it.
+ Lease4Ptr existing = LeaseMgrFactory::instance().getLease4(addr);
+ if (!existing) {
+ return (lease);
+ } else {
+ return (Lease4Ptr());
+ }
+ }
+}
+
+
AllocEngine::~AllocEngine() {
// no need to delete allocator. smart_ptr will do the trick for us
}
#include <asiolink/io_address.h>
#include <dhcp/duid.h>
+#include <dhcp/dhcp4.h>
#include <dhcpsrv/alloc_engine.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/lease_mgr.h>
using AllocEngine::IterativeAllocator;
};
-// empty class for now, but may be extended once Addr6 becomes bigger
-class AllocEngineTest : public ::testing::Test {
+class AllocEngine6Test : public ::testing::Test {
public:
- AllocEngineTest() {
- duid_ = boost::shared_ptr<DUID>(new DUID(vector<uint8_t>(8, 0x42)));
+ AllocEngine6Test() {
+ duid_ = DuidPtr(new DUID(vector<uint8_t>(8, 0x42)));
iaid_ = 42;
// instantiate cfg_mgr
// @todo: check cltt
}
- ~AllocEngineTest() {
+ ~AllocEngine6Test() {
factory_.destroy();
}
LeaseMgrFactory factory_;
};
+class AllocEngine4Test : public ::testing::Test {
+public:
+ AllocEngine4Test() {
+ clientid_ = ClientIdPtr(new ClientId(vector<uint8_t>(8, 0x44)));
+ static uint8_t mac[] = { 0, 1, 22, 33, 44, 55};
+
+ // Let's use odd hardware type to check if there is no Ethernet
+ // hardcoded anywhere.
+ hwaddr_ = HWAddrPtr(new HWAddr(mac, sizeof(mac), HTYPE_FDDI));
+
+ // instantiate cfg_mgr
+ CfgMgr& cfg_mgr = CfgMgr::instance();
+
+ subnet_ = Subnet4Ptr(new Subnet4(IOAddress("192.0.2.0"), 24, 1, 2, 3));
+ pool_ = Pool4Ptr(new Pool4(IOAddress("192.0.2.100"),
+ IOAddress("192.0.2.109")));
+ subnet_->addPool(pool_);
+ cfg_mgr.addSubnet4(subnet_);
+
+ factory_.create("type=memfile");
+ }
+
+ void checkLease4(const Lease4Ptr& lease) {
+ // that is belongs to the right subnet
+ EXPECT_EQ(lease->subnet_id_, subnet_->getID());
+ EXPECT_TRUE(subnet_->inRange(lease->addr_));
+ EXPECT_TRUE(subnet_->inPool(lease->addr_));
+
+ // that it have proper parameters
+ EXPECT_EQ(subnet_->getValid(), lease->valid_lft_);
+ EXPECT_EQ(subnet_->getT1(), lease->t1_);
+ EXPECT_EQ(subnet_->getT2(), lease->t2_);
+ EXPECT_TRUE(false == lease->fqdn_fwd_);
+ EXPECT_TRUE(false == lease->fqdn_rev_);
+ EXPECT_TRUE(*lease->client_id_ == *clientid_);
+ EXPECT_TRUE(lease->hwaddr_ == hwaddr_->hwaddr_);
+ // @todo: check cltt
+ }
+
+ ~AllocEngine4Test() {
+ factory_.destroy();
+ }
+
+ ClientIdPtr clientid_;
+ HWAddrPtr hwaddr_;
+ Subnet4Ptr subnet_;
+ Pool4Ptr pool_;
+ LeaseMgrFactory factory_;
+};
+
// This test checks if the Allocation Engine can be instantiated and that it
// parses parameters string properly.
-TEST_F(AllocEngineTest, constructor) {
+TEST_F(AllocEngine6Test, constructor) {
boost::scoped_ptr<AllocEngine> x;
// Hashed and random allocators are not supported yet
}
// This test checks if the simple allocation can succeed
-TEST_F(AllocEngineTest, simpleAlloc) {
+TEST_F(AllocEngine6Test, simpleAlloc6) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
ASSERT_TRUE(engine);
}
// This test checks if the fake allocation (for SOLICIT) can succeed
-TEST_F(AllocEngineTest, fakeAlloc) {
+TEST_F(AllocEngine6Test, fakeAlloc6) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
ASSERT_TRUE(engine);
// This test checks if the allocation with a hint that is valid (in range,
// in pool and free) can succeed
-TEST_F(AllocEngineTest, allocWithValidHint) {
+TEST_F(AllocEngine6Test, allocWithValidHint6) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
ASSERT_TRUE(engine);
// This test checks if the allocation with a hint that is in range,
// in pool, but is currently used) can succeed
-TEST_F(AllocEngineTest, allocWithUsedHint) {
+TEST_F(AllocEngine6Test, allocWithUsedHint6) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
ASSERT_TRUE(engine);
// let's create a lease and put it in the LeaseMgr
DuidPtr duid2 = boost::shared_ptr<DUID>(new DUID(vector<uint8_t>(8, 0xff)));
+ time_t now = time(NULL);
Lease6Ptr used(new Lease6(Lease6::LEASE_IA_NA, IOAddress("2001:db8:1::1f"),
- duid2, 1, 2, 3, 4, 5, subnet_->getID()));
+ duid2, 1, 2, 3, 4, now, subnet_->getID()));
ASSERT_TRUE(LeaseMgrFactory::instance().addLease(used));
// another client comes in and request an address that is in pool, but
// 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(AllocEngineTest, allocBogusHint) {
+TEST_F(AllocEngine6Test, allocBogusHint6) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
ASSERT_TRUE(engine);
// This test verifies that the allocator picks addresses that belong to the
// pool
-TEST_F(AllocEngineTest, IterativeAllocator) {
+TEST_F(AllocEngine6Test, IterativeAllocator) {
boost::scoped_ptr<NakedAllocEngine::Allocator>
alloc(new NakedAllocEngine::IterativeAllocator());
// This test verifies that the iterative allocator really walks over all addresses
// in all pools in specified subnet. It also must not pick the same address twice
// unless it runs out of pool space and must start over.
-TEST_F(AllocEngineTest, IterativeAllocator_manyPools) {
+TEST_F(AllocEngine6Test, IterativeAllocator_manyPools6) {
NakedAllocEngine::IterativeAllocator* alloc = new NakedAllocEngine::IterativeAllocator();
// let's start from 2, as there is 2001:db8:1::10 - 2001:db8:1::20 pool already.
}
// This test checks if really small pools are working
-TEST_F(AllocEngineTest, smallPool) {
+TEST_F(AllocEngine6Test, smallPool6) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
ASSERT_TRUE(engine);
// This test checks if all addresses in a pool are currently used, the attempt
// to find out a new lease fails.
-TEST_F(AllocEngineTest, outOfAddresses) {
+TEST_F(AllocEngine6Test, outOfAddresses6) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
ASSERT_TRUE(engine);
}
// This test checks if an expired lease can be reused in SOLICIT (fake allocation)
-TEST_F(AllocEngineTest, solicitReuseExpiredLease) {
+TEST_F(AllocEngine6Test, solicitReuseExpiredLease6) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
ASSERT_TRUE(engine);
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
lease = engine->allocateAddress6(subnet_, duid_, iaid_, IOAddress("::"),
true);
}
// This test checks if an expired lease can be reused in REQUEST (actual allocation)
-TEST_F(AllocEngineTest, requestReuseExpiredLease) {
+TEST_F(AllocEngine6Test, requestReuseExpiredLease6) {
boost::scoped_ptr<AllocEngine> engine;
ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
ASSERT_TRUE(engine);
detailCompareLease(lease, from_mgr);
}
+// --- IPv4 ---
+
+// This test checks if the simple IPv4 allocation can succeed
+TEST_F(AllocEngine4Test, simpleAlloc4) {
+ boost::scoped_ptr<AllocEngine> engine;
+ ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+ ASSERT_TRUE(engine);
+
+ Lease4Ptr lease = engine->allocateAddress4(subnet_, clientid_, hwaddr_,
+ IOAddress("0.0.0.0"), false);
+
+ // check that we got a lease
+ ASSERT_TRUE(lease);
+
+ // do all checks on the lease
+ checkLease4(lease);
+
+ // Check that the lease is indeed in LeaseMgr
+ Lease4Ptr from_mgr = LeaseMgrFactory::instance().getLease4(lease->addr_);
+ ASSERT_TRUE(from_mgr);
+
+ // Now check that the lease in LeaseMgr has the same parameters
+ detailCompareLease(lease, from_mgr);
+}
+
+// This test checks if the fake allocation (for DISCOVER) can succeed
+TEST_F(AllocEngine4Test, fakeAlloc4) {
+ boost::scoped_ptr<AllocEngine> engine;
+ ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+ ASSERT_TRUE(engine);
+
+ Lease4Ptr lease = engine->allocateAddress4(subnet_, clientid_, hwaddr_,
+ IOAddress("0.0.0.0"), true);
+
+ // check that we got a lease
+ ASSERT_TRUE(lease);
+
+ // do all checks on the lease
+ checkLease4(lease);
+
+ // Check that the lease is NOT in LeaseMgr
+ Lease4Ptr from_mgr = LeaseMgrFactory::instance().getLease4(lease->addr_);
+ ASSERT_FALSE(from_mgr);
+}
+
+
+// This test checks if the allocation with a hint that is valid (in range,
+// in pool and free) can succeed
+TEST_F(AllocEngine4Test, allocWithValidHint4) {
+ boost::scoped_ptr<AllocEngine> engine;
+ ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+ ASSERT_TRUE(engine);
+
+ Lease4Ptr lease = engine->allocateAddress4(subnet_, clientid_, hwaddr_,
+ IOAddress("192.0.2.105"),
+ false);
+
+ // check that we got a lease
+ ASSERT_TRUE(lease);
+
+ // we should get what we asked for
+ EXPECT_EQ(lease->addr_.toText(), "192.0.2.105");
+
+ // do all checks on the lease
+ checkLease4(lease);
+
+ // Check that the lease is indeed in LeaseMgr
+ Lease4Ptr from_mgr = LeaseMgrFactory::instance().getLease4(lease->addr_);
+ ASSERT_TRUE(from_mgr);
+
+ // Now check that the lease in LeaseMgr has the same parameters
+ detailCompareLease(lease, from_mgr);
+}
+
+
+// This test checks if the allocation with a hint that is in range,
+// in pool, but is currently used) can succeed
+TEST_F(AllocEngine4Test, allocWithUsedHint4) {
+ boost::scoped_ptr<AllocEngine> engine;
+ ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+ ASSERT_TRUE(engine);
+
+ // let's create a lease and put it in the LeaseMgr
+ uint8_t hwaddr2[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
+ uint8_t clientid2[] = { 8, 7, 6, 5, 4, 3, 2, 1 };
+ time_t now = time(NULL);
+ Lease4Ptr used(new Lease4(IOAddress("192.0.2.106"), hwaddr2, sizeof(hwaddr2),
+ clientid2, sizeof(clientid2), 1, 2, 3, now, subnet_->getID()));
+ ASSERT_TRUE(LeaseMgrFactory::instance().addLease(used));
+
+ // another client comes in and request an address that is in pool, but
+ // unfortunately it is used already. The same address must not be allocated
+ // twice.
+ Lease4Ptr lease = engine->allocateAddress4(subnet_, clientid_, hwaddr_,
+ IOAddress("192.0.2.106"),
+ false);
+ // check that we got a lease
+ ASSERT_TRUE(lease);
+
+ // allocated address must be different
+ EXPECT_TRUE(used->addr_.toText() != lease->addr_.toText());
+
+ // we should NOT get what we asked for, because it is used already
+ EXPECT_TRUE(lease->addr_.toText() != "192.0.2.106");
+
+ // do all checks on the lease
+ checkLease4(lease);
+
+ // Check that the lease is indeed in LeaseMgr
+ Lease4Ptr from_mgr = LeaseMgrFactory::instance().getLease4(lease->addr_);
+ ASSERT_TRUE(from_mgr);
+
+ // Now check that the lease in LeaseMgr has the same parameters
+ detailCompareLease(lease, from_mgr);
+}
+
+
+// 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(AllocEngine4Test, allocBogusHint4) {
+ boost::scoped_ptr<AllocEngine> engine;
+ ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+ ASSERT_TRUE(engine);
+
+ // Client would like to get a 3000::abc lease, which does not belong to any
+ // supported lease. Allocation engine should ignore it and carry on
+ // with the normal allocation
+ Lease4Ptr lease = engine->allocateAddress4(subnet_, clientid_, hwaddr_,
+ IOAddress("10.1.1.1"),
+ false);
+ // check that we got a lease
+ ASSERT_TRUE(lease);
+
+ // we should NOT get what we asked for, because it is used already
+ EXPECT_TRUE(lease->addr_.toText() != "10.1.1.1");
+
+ // do all checks on the lease
+ checkLease4(lease);
+
+ // Check that the lease is indeed in LeaseMgr
+ Lease4Ptr from_mgr = LeaseMgrFactory::instance().getLease4(lease->addr_);
+ ASSERT_TRUE(from_mgr);
+
+ // Now check that the lease in LeaseMgr has the same parameters
+ detailCompareLease(lease, from_mgr);
+}
+
+
+// This test verifies that the allocator picks addresses that belong to the
+// pool
+TEST_F(AllocEngine4Test, IterativeAllocator) {
+ boost::scoped_ptr<NakedAllocEngine::Allocator>
+ alloc(new NakedAllocEngine::IterativeAllocator());
+
+ for (int i = 0; i < 1000; ++i) {
+ IOAddress candidate = alloc->pickAddress(subnet_, clientid_,
+ IOAddress("0.0.0.0"));
+ EXPECT_TRUE(subnet_->inPool(candidate));
+ }
+}
+
+
+// This test verifies that the iterative allocator really walks over all addresses
+// in all pools in specified subnet. It also must not pick the same address twice
+// unless it runs out of pool space and must start over.
+TEST_F(AllocEngine4Test, IterativeAllocator_manyPools4) {
+ NakedAllocEngine::IterativeAllocator* alloc = new NakedAllocEngine::IterativeAllocator();
+
+ // let's start from 2, as there is 2001:db8:1::10 - 2001:db8:1::20 pool already.
+ for (int i = 2; i < 10; ++i) {
+ stringstream min, max;
+
+ min << "192.0.2." << i*10 + 1;
+ max << "192.0.2." << i*10 + 9;
+
+ Pool4Ptr pool(new Pool4(IOAddress(min.str()),
+ IOAddress(max.str())));
+ // cout << "Adding pool: " << min.str() << "-" << max.str() << endl;
+ subnet_->addPool(pool);
+ }
+
+ int total = 10 + 8*9; // first pool (.100 - .109) has 10 addresses in it,
+ // there are 8 extra pools with 9 addresses in each.
+
+ // Let's keep picked addresses here and check their uniqueness.
+ std::map<IOAddress, int> generated_addrs;
+ int cnt = 0;
+ while (++cnt) {
+ IOAddress candidate = alloc->pickAddress(subnet_, clientid_, IOAddress("0.0.0.0"));
+ EXPECT_TRUE(subnet_->inPool(candidate));
+
+ // One way to easily verify that the iterative allocator really works is
+ // to uncomment the following line and observe its output that it
+ // covers all defined subnets.
+ // cout << candidate.toText() << endl;
+
+ if (generated_addrs.find(candidate) == generated_addrs.end()) {
+ // we haven't had this
+ generated_addrs[candidate] = 0;
+ } else {
+ // we have seen this address before. That should mean that we
+ // iterated over all addresses.
+ if (generated_addrs.size() == total) {
+ // we have exactly the number of address in all pools
+ break;
+ }
+ ADD_FAILURE() << "Too many or not enough unique addresses generated.";
+ break;
+ }
+
+ if ( cnt>total ) {
+ ADD_FAILURE() << "Too many unique addresses generated.";
+ break;
+ }
+ }
+
+ delete alloc;
+}
+
+
+// This test checks if really small pools are working
+TEST_F(AllocEngine4Test, smallPool4) {
+ boost::scoped_ptr<AllocEngine> engine;
+ ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+ ASSERT_TRUE(engine);
+
+ IOAddress addr("192.0.2.17");
+ CfgMgr& cfg_mgr = CfgMgr::instance();
+ cfg_mgr.deleteSubnets4(); // Get rid of the default test configuration
+
+ // Create configuration similar to other tests, but with a single address pool
+ subnet_ = Subnet4Ptr(new Subnet4(IOAddress("192.0.2.0"), 24, 1, 2, 3));
+ pool_ = Pool4Ptr(new Pool4(addr, addr)); // just a single address
+ subnet_->addPool(pool_);
+ cfg_mgr.addSubnet4(subnet_);
+
+ Lease4Ptr lease = engine->allocateAddress4(subnet_, clientid_, hwaddr_, IOAddress("0.0.0.0"),
+ false);
+
+ // Check that we got that single lease
+ ASSERT_TRUE(lease);
+
+ EXPECT_EQ("192.0.2.17", lease->addr_.toText());
+
+ // do all checks on the lease
+ checkLease4(lease);
+
+ // Check that the lease is indeed in LeaseMgr
+ Lease4Ptr from_mgr = LeaseMgrFactory::instance().getLease4(lease->addr_);
+ ASSERT_TRUE(from_mgr);
+
+ // Now check that the lease in LeaseMgr has the same parameters
+ detailCompareLease(lease, from_mgr);
+}
+
+// This test checks if all addresses in a pool are currently used, the attempt
+// to find out a new lease fails.
+TEST_F(AllocEngine4Test, outOfAddresses4) {
+ boost::scoped_ptr<AllocEngine> engine;
+ ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+ ASSERT_TRUE(engine);
+
+ IOAddress addr("192.0.2.17");
+ CfgMgr& cfg_mgr = CfgMgr::instance();
+ cfg_mgr.deleteSubnets4(); // Get rid of the default test configuration
+
+ // Create configuration similar to other tests, but with a single address pool
+ subnet_ = Subnet4Ptr(new Subnet4(IOAddress("192.0.2.0"), 24, 1, 2, 3));
+ pool_ = Pool4Ptr(new Pool4(addr, addr)); // just a single address
+ subnet_->addPool(pool_);
+ cfg_mgr.addSubnet4(subnet_);
+
+ // Just a different hw/client-id for the second client
+ uint8_t hwaddr2[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
+ uint8_t clientid2[] = { 8, 7, 6, 5, 4, 3, 2, 1 };
+ time_t now = time(NULL);
+ Lease4Ptr lease(new Lease4(addr, hwaddr2, sizeof(hwaddr2), clientid2, sizeof(clientid2),
+ 501, 502, 503, now, subnet_->getID()));
+ lease->cltt_ = time(NULL) - 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
+
+ EXPECT_THROW(engine->allocateAddress4(subnet_, clientid_, hwaddr_, IOAddress("0.0.0.0"),false),
+ AllocFailed);
+}
+
+// This test checks if an expired lease can be reused in DISCOVER (fake allocation)
+TEST_F(AllocEngine4Test, discoverReuseExpiredLease4) {
+ boost::scoped_ptr<AllocEngine> engine;
+ ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+ ASSERT_TRUE(engine);
+
+ IOAddress addr("192.0.2.15");
+ CfgMgr& cfg_mgr = CfgMgr::instance();
+ cfg_mgr.deleteSubnets4(); // Get rid of the default test configuration
+
+ // Create configuration similar to other tests, but with a single address pool
+ subnet_ = Subnet4Ptr(new Subnet4(IOAddress("192.0.2.0"), 24, 1, 2, 3));
+ pool_ = Pool4Ptr(new Pool4(addr, addr)); // just a single address
+ subnet_->addPool(pool_);
+ cfg_mgr.addSubnet4(subnet_);
+
+ // Just a different hw/client-id for the second client
+ uint8_t hwaddr2[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
+ uint8_t clientid2[] = { 8, 7, 6, 5, 4, 3, 2, 1 };
+ time_t now = time(NULL) - 500; // Allocated 500 seconds ago
+ Lease4Ptr lease(new Lease4(addr, clientid2, sizeof(clientid2), hwaddr2, sizeof(hwaddr2),
+ 495, 100, 200, now, subnet_->getID()));
+ // lease was assigned 500 seconds ago, but its valid lifetime is 495, so it
+ // is expired already
+ ASSERT_TRUE(lease->expired());
+ ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
+
+ // CASE 1: Asking for any address
+ lease = engine->allocateAddress4(subnet_, clientid_, hwaddr_, IOAddress("0.0.0.0"),
+ true);
+ // Check that we got that single lease
+ ASSERT_TRUE(lease);
+ EXPECT_EQ(addr.toText(), lease->addr_.toText());
+
+ // Do all checks on the lease (if subnet-id, preferred/valid times are ok etc.)
+ checkLease4(lease);
+
+ // CASE 2: Asking specifically for this address
+ lease = engine->allocateAddress4(subnet_, clientid_, hwaddr_, IOAddress(addr.toText()),
+ true);
+ // Check that we got that single lease
+ ASSERT_TRUE(lease);
+ EXPECT_EQ(addr.toText(), lease->addr_.toText());
+}
+
+// This test checks if an expired lease can be reused in REQUEST (actual allocation)
+TEST_F(AllocEngine4Test, requestReuseExpiredLease4) {
+ boost::scoped_ptr<AllocEngine> engine;
+ ASSERT_NO_THROW(engine.reset(new AllocEngine(AllocEngine::ALLOC_ITERATIVE, 100)));
+ ASSERT_TRUE(engine);
+
+ IOAddress addr("192.0.2.105");
+
+ // Just a different hw/client-id for the second client
+ uint8_t hwaddr2[] = { 0, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};
+ uint8_t clientid2[] = { 8, 7, 6, 5, 4, 3, 2, 1 };
+ time_t now = time(NULL) - 500; // Allocated 500 seconds ago
+ Lease4Ptr lease(new Lease4(addr, clientid2, sizeof(clientid2), hwaddr2, sizeof(hwaddr2),
+ 495, 100, 200, now, subnet_->getID()));
+ // lease was assigned 500 seconds ago, but its valid lifetime is 495, so it
+ // is expired already
+ ASSERT_TRUE(lease->expired());
+ ASSERT_TRUE(LeaseMgrFactory::instance().addLease(lease));
+
+ // A client comes along, asking specifically for this address
+ lease = engine->allocateAddress4(subnet_, clientid_, hwaddr_,
+ IOAddress(addr.toText()), false);
+
+ // Check that he got that single lease
+ ASSERT_TRUE(lease);
+ EXPECT_EQ(addr.toText(), lease->addr_.toText());
+
+ // Check that the lease is indeed updated in LeaseMgr
+ Lease4Ptr from_mgr = LeaseMgrFactory::instance().getLease4(addr);
+ ASSERT_TRUE(from_mgr);
+
+ // Now check that the lease in LeaseMgr has the same parameters
+ detailCompareLease(lease, from_mgr);
+}
+
}; // end of anonymous namespace