Commit 32e13fbf authored by Tomek Mrugalski's avatar Tomek Mrugalski 🛰

[2320] Allocation Engine for IPv4 implemented

parent 92e4f34d
......@@ -260,6 +260,114 @@ AllocEngine::allocateAddress6(const Subnet6Ptr& subnet,
<< " 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,
......@@ -300,6 +408,45 @@ Lease6Ptr AllocEngine::reuseExpiredLease(Lease6Ptr& expired,
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,
......@@ -338,6 +485,48 @@ Lease6Ptr AllocEngine::createLease6(const Subnet6Ptr& subnet,
}
}
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
}
......
......@@ -17,6 +17,7 @@
#include <asiolink/io_address.h>
#include <dhcp/duid.h>
#include <dhcp/hwaddr.h>
#include <dhcpsrv/subnet.h>
#include <dhcpsrv/lease_mgr.h>
......@@ -182,13 +183,15 @@ protected:
///
/// @param subnet subnet the allocation should come from
/// @param clientid Client identifier
/// @param hwaddr client's hardware address info
/// @param hint a hint that the client provided
/// @param fake_allocation is this real i.e. REQUEST (false) or just picking
/// an address for DISCOVER that is not really allocated (true)
/// @return Allocated IPv4 lease (or NULL if allocation failed)
Lease4Ptr
allocateAddress4(const SubnetPtr& subnet,
const DuidPtr& clientid,
const ClientIdPtr& clientid,
const HWAddrPtr& hwaddr,
const isc::asiolink::IOAddress& hint,
bool fake_allocation);
......@@ -224,12 +227,14 @@ private:
///
/// @param subnet subnet the lease is allocated from
/// @param clientid client identifier
/// @param hwaddr client's hardware address
/// @param addr an address that was selected and is confirmed to be available
/// @param fake_allocation is this real i.e. REQUEST (false) or just picking
/// an address for DISCOVER that is not really allocated (true)
/// @return allocated lease (or NULL in the unlikely case of the lease just
/// becomed unavailable)
Lease4Ptr createLease4(const Subnet4Ptr& subnet, const DuidPtr& clientid,
Lease4Ptr createLease4(const SubnetPtr& subnet, const DuidPtr& clientid,
const HWAddrPtr& hwaddr,
const isc::asiolink::IOAddress& addr,
bool fake_allocation = false);
......@@ -260,12 +265,14 @@ private:
/// @param expired old, expired lease
/// @param subnet subnet the lease is allocated from
/// @param clientid client identifier
/// @param hwaddr client's hardware address
/// @param fake_allocation is this real i.e. REQUEST (false) or just picking
/// an address for DISCOVER that is not really allocated (true)
/// @return refreshed lease
/// @throw BadValue if trying to recycle lease that is still valid
Lease4Ptr reuseExpiredLease(Lease4Ptr& expired, const Subnet4Ptr& subnet,
const DuidPtr& clientid,
Lease4Ptr reuseExpiredLease(Lease4Ptr& expired, const SubnetPtr& subnet,
const ClientIdPtr& clientid,
const HWAddrPtr& hwaddr,
bool fake_allocation = false);
/// @brief reuses expired IPv6 lease
......
......@@ -233,10 +233,10 @@ struct Lease4 : public Lease {
/// @param valid_lft Lifetime of the lease
/// @param cltt Client last transmission time
/// @param subnet_id Subnet identification
Lease4(uint32_t addr, const uint8_t* hwaddr, size_t hwaddr_len,
Lease4(const isc::asiolink::IOAddress& addr, const uint8_t* hwaddr, size_t hwaddr_len,
const uint8_t* clientid, size_t clientid_len, uint32_t valid_lft,
time_t cltt, uint32_t subnet_id)
: Lease(addr, 0, 0, valid_lft, subnet_id, cltt),
uint32_t t1, uint32_t t2, time_t cltt, uint32_t subnet_id)
: Lease(addr, t1, t2, valid_lft, subnet_id, cltt),
ext_(0), hwaddr_(hwaddr, hwaddr + hwaddr_len),
client_id_(new ClientId(clientid, clientid_len)) {
}
......@@ -370,6 +370,7 @@ typedef std::vector<Lease6Ptr> Lease6Collection;
class LeaseMgr {
public:
/// Client hardware address
/// @todo: migrate to HWAddr structure
typedef std::vector<uint8_t> HWAddr;
/// Database configuration parameter map
......
......@@ -13,6 +13,7 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <dhcpsrv/memfile_lease_mgr.h>
#include <exceptions/exceptions.h>
#include <iostream>
......@@ -27,8 +28,13 @@ Memfile_LeaseMgr::Memfile_LeaseMgr(const ParameterMap& parameters)
Memfile_LeaseMgr::~Memfile_LeaseMgr() {
}
bool Memfile_LeaseMgr::addLease(const Lease4Ptr&) {
return (false);
bool Memfile_LeaseMgr::addLease(const Lease4Ptr& lease) {
if (getLease4(lease->addr_)) {
// there is a lease with specified address already
return (false);
}
storage4_.insert(lease);
return (true);
}
bool Memfile_LeaseMgr::addLease(const Lease6Ptr& lease) {
......@@ -40,27 +46,51 @@ bool Memfile_LeaseMgr::addLease(const Lease6Ptr& lease) {
return (true);
}
Lease4Ptr Memfile_LeaseMgr::getLease4(const isc::asiolink::IOAddress&) const {
return (Lease4Ptr());
Lease4Ptr Memfile_LeaseMgr::getLease4(const isc::asiolink::IOAddress& addr) const {
Lease4Storage::iterator l = storage4_.find(addr);
if (l == storage4_.end()) {
return (Lease4Ptr());
} else {
return (*l);
}
}
Lease4Collection Memfile_LeaseMgr::getLease4(const HWAddr& ) const {
isc_throw(NotImplemented, "getLease4(HWaddr x) method not implemented yet");
return (Lease4Collection());
}
Lease4Ptr Memfile_LeaseMgr::getLease4(const HWAddr&,
SubnetID) const {
Lease4Ptr Memfile_LeaseMgr::getLease4(const HWAddr& hwaddr,
SubnetID id) const {
Lease4Storage::iterator l;
for (l = storage4_.begin(); l != storage4_.end(); ++l) {
if ( ((*l)->hwaddr_ == hwaddr) &&
((*l)->subnet_id_ == id)) {
return (*l);
}
}
// not found
return (Lease4Ptr());
}
Lease4Ptr Memfile_LeaseMgr::getLease4(const ClientId&,
SubnetID) const {
Lease4Ptr Memfile_LeaseMgr::getLease4(const ClientId& client_id,
SubnetID subnet_id) const {
Lease4Storage::iterator l;
for (l = storage4_.begin(); l != storage4_.end(); ++l) {
if ( (*(*l)->client_id_ == client_id) &&
((*l)->subnet_id_ == subnet_id)) {
return (*l);
}
}
// not found
return (Lease4Ptr());
}
Lease4Collection Memfile_LeaseMgr::getLease4(const ClientId& ) const {
return (Lease4Collection());
isc_throw(NotImplemented, "getLease4(ClientId) not implemented");
}
Lease6Ptr Memfile_LeaseMgr::getLease6(const isc::asiolink::IOAddress& addr) const {
......@@ -98,11 +128,18 @@ void Memfile_LeaseMgr::updateLease6(const Lease6Ptr& ) {
bool Memfile_LeaseMgr::deleteLease(const isc::asiolink::IOAddress& addr) {
if (addr.isV4()) {
// V4 not implemented yet
return (false);
// v4 lease
Lease4Storage::iterator l = storage4_.find(addr);
if (l == storage4_.end()) {
// No such lease
return (false);
} else {
storage4_.erase(l);
return (true);
}
} else {
// V6 lease
// v6 lease
Lease6Storage::iterator l = storage6_.find(addr);
if (l == storage6_.end()) {
// No such lease
......
......@@ -231,6 +231,22 @@ protected:
>
> Lease6Storage; // Let the whole contraption be called Lease6Storage.
typedef boost::multi_index_container< // this is a multi-index container...
Lease4Ptr, // it will hold shared_ptr to leases6
boost::multi_index::indexed_by< // and will be sorted by
// IPv6 address that are unique. That particular key is a member
// of the Lease6 structure, is of type IOAddress and can be accessed
// by doing &Lease6::addr_
boost::multi_index::ordered_unique<
boost::multi_index::member<Lease, isc::asiolink::IOAddress, &Lease::addr_>
>
>
> Lease4Storage; // Let the whole contraption be called Lease6Storage.
/// @brief stores IPv4 leases
Lease4Storage storage4_;
/// @brief stores IPv6 leases
Lease6Storage storage6_;
};
......
......@@ -448,9 +448,10 @@ public:
time_t cltt = 0;
MySqlLeaseMgr::convertFromDatabaseTime(expire_, valid_lifetime_, cltt);
// note that T1 and T2 are not stored
return (Lease4Ptr(new Lease4(addr4_, hwaddr_buffer_, hwaddr_length_,
client_id_buffer_, client_id_length_,
valid_lifetime_, cltt, subnet_id_)));
valid_lifetime_, 0, 0, cltt, subnet_id_)));
}
/// @brief Return columns in error
......
......@@ -39,9 +39,14 @@ namespace {
class CfgMgrTest : public ::testing::Test {
public:
CfgMgrTest() {
// make sure we start with a clean configuration
CfgMgr::instance().deleteSubnets4();
CfgMgr::instance().deleteSubnets6();
}
~CfgMgrTest() {
// clean up after the test
CfgMgr::instance().deleteSubnets4();
CfgMgr::instance().deleteSubnets6();
}
};
......
......@@ -272,7 +272,7 @@ TEST(Lease4, Lease4Constructor) {
// Create the lease
Lease4 lease(ADDRESS[i], HWADDR, sizeof(HWADDR),
CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, current_time,
CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, 0, 0, current_time,
SUBNET_ID);
EXPECT_EQ(ADDRESS[i], static_cast<uint32_t>(lease.addr_));
......@@ -312,10 +312,10 @@ TEST(Lease4, OperatorEquals) {
// Check when the leases are equal.
Lease4 lease1(ADDRESS, HWADDR, sizeof(HWADDR),
CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, current_time,
CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, current_time, 0, 0,
SUBNET_ID);
Lease4 lease2(ADDRESS, HWADDR, sizeof(HWADDR),
CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, current_time,
CLIENTID, sizeof(CLIENTID), VALID_LIFETIME, current_time, 0, 0,
SUBNET_ID);
EXPECT_TRUE(lease1 == lease2);
EXPECT_FALSE(lease1 != lease2);
......
......@@ -1414,9 +1414,7 @@ TestControl::sendRequest4(const TestControlSocket& socket,
setDefaults4(socket, pkt4);
// Set hardware address
const uint8_t* chaddr = offer_pkt4->getChaddr();
std::vector<uint8_t> mac_address(chaddr, chaddr + HW_ETHER_LEN);
pkt4->setHWAddr(HTYPE_ETHER, mac_address.size(), mac_address);
pkt4->setHWAddr(offer_pkt4->getHWAddr());
// Set elapsed time.
uint32_t elapsed_time = getElapsedTime<Pkt4Ptr>(discover_pkt4, offer_pkt4);
pkt4->setSecs(static_cast<uint16_t>(elapsed_time / 1000));
......@@ -1461,8 +1459,10 @@ TestControl::sendRequest4(const TestControlSocket& socket,
transid));
// Set hardware address from OFFER packet received.
const uint8_t* chaddr = offer_pkt4->getChaddr();
std::vector<uint8_t> mac_address(chaddr, chaddr + HW_ETHER_LEN);
HWAddrPtr hwaddr = offer_pkt4->getHWAddr();
std::vector<uint8_t> mac_address(HW_ETHER_LEN, 0);
uint8_t hw_len = hwaddr->hwaddr_.size();
memcpy(&mac_address[0], &hwaddr->hwaddr_[0], hw_len > 16 ? 16 : hw_len);
pkt4->writeAt(rand_offset, mac_address.begin(), mac_address.end());
// Set elapsed time.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment