2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-29 04:57:52 +00:00

[#890] make allocation engine thread safe

This commit is contained in:
Razvan Becheriu 2020-01-28 17:06:21 +02:00
parent f132d56049
commit 07954313dd
3 changed files with 122 additions and 70 deletions

View File

@ -34,18 +34,20 @@
#include <algorithm>
#include <cstring>
#include <sstream>
#include <limits>
#include <vector>
#include <sstream>
#include <stdint.h>
#include <string.h>
#include <utility>
#include <vector>
using namespace isc::asiolink;
using namespace isc::dhcp;
using namespace isc::dhcp_ddns;
using namespace isc::hooks;
using namespace isc::stats;
using namespace isc::util;
namespace {
@ -87,7 +89,7 @@ namespace isc {
namespace dhcp {
AllocEngine::IterativeAllocator::IterativeAllocator(Lease::Type lease_type)
:Allocator(lease_type) {
: Allocator(lease_type) {
}
isc::asiolink::IOAddress
@ -106,17 +108,14 @@ AllocEngine::IterativeAllocator::increasePrefix(const isc::asiolink::IOAddress&
<< prefix_len);
}
// Brief explanation what happens here:
// http://www.youtube.com/watch?v=NFQCYpIHLNQ
uint8_t n_bytes = (prefix_len - 1)/8;
uint8_t n_bits = 8 - (prefix_len - n_bytes*8);
uint8_t mask = 1 << n_bits;
// Longer explanation: n_bytes specifies number of full bytes that are
// in-prefix. They can also be used as an offset for the first byte that
// is not in prefix. n_bits specifies number of bits on the last byte that
// is (often partially) in prefix. For example for a /125 prefix, the values
// Explanation: n_bytes specifies number of full bytes that are in-prefix.
// They can also be used as an offset for the first byte that is not in
// prefix. n_bits specifies number of bits on the last byte that is
// (often partially) in prefix. For example for a /125 prefix, the values
// are 15 and 3, respectively. Mask is a bitmask that has the least
// significant bit from the prefix set.
@ -158,11 +157,10 @@ AllocEngine::IterativeAllocator::increaseAddress(const isc::asiolink::IOAddress&
}
isc::asiolink::IOAddress
AllocEngine::IterativeAllocator::pickAddress(const SubnetPtr& subnet,
const ClientClasses& client_classes,
const DuidPtr&,
const IOAddress&) {
AllocEngine::IterativeAllocator::pickAddressInternal(const SubnetPtr& subnet,
const ClientClasses& client_classes,
const DuidPtr&,
const IOAddress&) {
// Is this prefix allocation?
bool prefix = pool_type_ == Lease::TYPE_PD;
uint8_t prefix_len = 0;
@ -287,28 +285,28 @@ AllocEngine::IterativeAllocator::pickAddress(const SubnetPtr& subnet,
}
AllocEngine::HashedAllocator::HashedAllocator(Lease::Type lease_type)
:Allocator(lease_type) {
: Allocator(lease_type) {
isc_throw(NotImplemented, "Hashed allocator is not implemented");
}
isc::asiolink::IOAddress
AllocEngine::HashedAllocator::pickAddress(const SubnetPtr&,
const ClientClasses&,
const DuidPtr&,
const IOAddress&) {
AllocEngine::HashedAllocator::pickAddressInternal(const SubnetPtr&,
const ClientClasses&,
const DuidPtr&,
const IOAddress&) {
isc_throw(NotImplemented, "Hashed allocator is not implemented");
}
AllocEngine::RandomAllocator::RandomAllocator(Lease::Type lease_type)
:Allocator(lease_type) {
: Allocator(lease_type) {
isc_throw(NotImplemented, "Random allocator is not implemented");
}
isc::asiolink::IOAddress
AllocEngine::RandomAllocator::pickAddress(const SubnetPtr&,
const ClientClasses&,
const DuidPtr&,
const IOAddress&) {
AllocEngine::RandomAllocator::pickAddressInternal(const SubnetPtr&,
const ClientClasses&,
const DuidPtr&,
const IOAddress&) {
isc_throw(NotImplemented, "Random allocator is not implemented");
}
@ -1058,7 +1056,7 @@ AllocEngine::allocateUnreservedLeases6(ClientContext6& ctx) {
}
Lease6Ptr existing = LeaseMgrFactory::instance().getLease6(ctx.currentIA().type_,
candidate);
candidate);
if (!existing) {
// there's no existing lease for selected candidate, so it is
@ -3375,9 +3373,6 @@ AllocEngine::requestLease4(AllocEngine::ClientContext4& ctx) {
Lease4Ptr client_lease;
findClientLease(ctx, client_lease);
// Obtain the sole instance of the LeaseMgr.
LeaseMgr& lease_mgr = LeaseMgrFactory::instance();
// When the client sends the DHCPREQUEST, it should always specify the
// address which it is requesting or renewing. That is, the client should
// either use the requested IP address option or set the ciaddr. However,
@ -3539,7 +3534,7 @@ AllocEngine::requestLease4(AllocEngine::ClientContext4& ctx) {
.arg(ctx.query_->getLabel())
.arg(client_lease->addr_.toText());
if (lease_mgr.deleteLease(client_lease)) {
if (LeaseMgrFactory::instance().deleteLease(client_lease)) {
// Need to decrease statistic for assigned addresses.
StatsMgr::instance().addValue(
StatsMgr::generateName("subnet", client_lease->subnet_id_,
@ -3583,14 +3578,12 @@ AllocEngine::createLease4(const ClientContext4& ctx, const IOAddress& addr,
time_t now = time(NULL);
// @todo: remove this kludge?
std::vector<uint8_t> local_copy;
if (ctx.clientid_ && ctx.subnet_->getMatchClientId()) {
local_copy = ctx.clientid_->getDuid();
ClientIdPtr client_id;
if (ctx.subnet_->getMatchClientId()) {
client_id = ctx.clientid_;
}
const uint8_t* local_copy0 = local_copy.empty() ? 0 : &local_copy[0];
Lease4Ptr lease(new Lease4(addr, ctx.hwaddr_, local_copy0, local_copy.size(),
Lease4Ptr lease(new Lease4(addr, ctx.hwaddr_, client_id,
valid_lft, now, ctx.subnet_->getID()));
// Set FQDN specific lease parameters.

View File

@ -22,6 +22,7 @@
#include <dhcpsrv/lease_mgr.h>
#include <dhcpsrv/srv_config.h>
#include <hooks/callout_handle.h>
#include <util/multi_threading_mgr.h>
#include <boost/function.hpp>
#include <boost/shared_ptr.hpp>
@ -29,6 +30,7 @@
#include <list>
#include <map>
#include <mutex>
#include <set>
#include <utility>
@ -88,30 +90,44 @@ protected:
/// @param subnet next address will be returned from pool of that subnet
/// @param client_classes list of classes client belongs to
/// @param duid Client's DUID
/// @param hint client's hint
/// @param hint Client's hint
///
/// @return the next address
virtual isc::asiolink::IOAddress
pickAddress(const SubnetPtr& subnet,
const ClientClasses& client_classes,
const DuidPtr& duid,
const isc::asiolink::IOAddress& hint) = 0;
const isc::asiolink::IOAddress& hint) {
return util::MultiThreadingMgr::call(
mutex_, [&]() {return pickAddressInternal(subnet, client_classes, duid, hint);});
}
/// @brief Default constructor.
///
/// Specifies which type of leases this allocator will assign
/// @param pool_type specifies pool type (addresses, temp. addr or prefixes)
Allocator(Lease::Type pool_type)
:pool_type_(pool_type) {
Allocator(Lease::Type pool_type) : pool_type_(pool_type) {
}
/// @brief virtual destructor
virtual ~Allocator() {
}
private:
virtual isc::asiolink::IOAddress
pickAddressInternal(const SubnetPtr& subnet,
const ClientClasses& client_classes,
const DuidPtr& duid,
const isc::asiolink::IOAddress& hint) = 0;
protected:
/// @brief defines pool type allocation
Lease::Type pool_type_;
private:
std::mutex mutex_;
};
/// defines a pointer to allocator
@ -132,18 +148,22 @@ protected:
/// @param type - specifies allocation type
IterativeAllocator(Lease::Type type);
private:
/// @brief returns the next address from pools in a subnet
///
/// @param subnet next address will be returned from pool of that subnet
/// @param client_classes list of classes client belongs to
/// @param duid Client's DUID (ignored)
/// @param hint client's hint (ignored)
/// @param hint Client's hint (ignored)
///
/// @return the next address
virtual isc::asiolink::IOAddress
pickAddress(const SubnetPtr& subnet,
const ClientClasses& client_classes,
const DuidPtr& duid,
const isc::asiolink::IOAddress& hint);
pickAddressInternal(const SubnetPtr& subnet,
const ClientClasses& client_classes,
const DuidPtr& duid,
const isc::asiolink::IOAddress& hint);
protected:
/// @brief Returns the next prefix
@ -155,6 +175,7 @@ protected:
///
/// @param prefix prefix to be increased
/// @param prefix_len length of the prefix to be increased
///
/// @return result prefix
static isc::asiolink::IOAddress
increasePrefix(const isc::asiolink::IOAddress& prefix,
@ -168,11 +189,11 @@ protected:
/// @param address address or prefix to be increased
/// @param prefix true when the previous argument is a prefix
/// @param prefix_len length of the prefix
///
/// @return result address or prefix
static isc::asiolink::IOAddress
increaseAddress(const isc::asiolink::IOAddress& address,
bool prefix, const uint8_t prefix_len);
};
/// @brief Address/prefix allocator that gets an address based on a hash
@ -185,6 +206,8 @@ protected:
/// @param type - specifies allocation type
HashedAllocator(Lease::Type type);
private:
/// @brief returns an address based on hash calculated from client's DUID.
///
/// @todo: Implement this method
@ -193,12 +216,13 @@ protected:
/// @param client_classes list of classes client belongs to
/// @param duid Client's DUID
/// @param hint a hint (last address that was picked)
///
/// @return selected address
virtual isc::asiolink::IOAddress
pickAddress(const SubnetPtr& subnet,
const ClientClasses& client_classes,
const DuidPtr& duid,
const isc::asiolink::IOAddress& hint);
pickAddressInternal(const SubnetPtr& subnet,
const ClientClasses& client_classes,
const DuidPtr& duid,
const isc::asiolink::IOAddress& hint);
};
/// @brief Random allocator that picks address randomly
@ -211,6 +235,8 @@ protected:
/// @param type - specifies allocation type
RandomAllocator(Lease::Type type);
private:
/// @brief returns a random address from pool of specified subnet
///
/// @todo: Implement this method
@ -219,12 +245,13 @@ protected:
/// @param client_classes list of classes client belongs to
/// @param duid Client's DUID (ignored)
/// @param hint the last address that was picked (ignored)
///
/// @return a random address from the pool
virtual isc::asiolink::IOAddress
pickAddress(const SubnetPtr& subnet,
const ClientClasses& client_classes,
const DuidPtr& duid,
const isc::asiolink::IOAddress& hint);
pickAddressInternal(const SubnetPtr& subnet,
const ClientClasses& client_classes,
const DuidPtr& duid,
const isc::asiolink::IOAddress& hint);
};
public:
@ -255,7 +282,9 @@ public:
/// @brief Returns allocator for a given pool type
///
/// @param type type of pool (V4, IA, TA or PD)
///
/// @throw BadValue if allocator for a given type is missing
///
/// @return pointer to allocator handling a given resource types
AllocatorPtr getAllocator(Lease::Type type);
@ -334,6 +363,7 @@ public:
/// @brief Compares two @c AllocEngine::Resource objects for equality.
///
/// @param other object to be compared with this object
///
/// @return true if objects are equal, false otherwise.
bool equals(const Resource& other) const {
return (address_ == other.address_ &&
@ -343,6 +373,7 @@ public:
/// @brief Equality operator.
///
/// @param other object to be compared with this object
///
/// @return true if objects are equal, false otherwise.
bool operator==(const Resource& other) const {
return (equals(other));
@ -370,8 +401,9 @@ public:
/// @brief Compare operator
///
/// @note Only the address/prefix part of resources is used.
/// @param lhr Left hand resource objet
/// @param rhr Right hand resource objet
/// @param lhr Left hand resource object
/// @param rhr Right hand resource object
///
/// @return true if lhr is less than rhr, false otherwise
bool operator() (const Resource& lhr, const Resource& rhr) const {
if (lhr.getAddress() == rhr.getAddress()) {
@ -541,12 +573,14 @@ public:
/// @brief Convenience method adding new hint from IAADDR option.
///
/// @param iaaddr Pointer to IAADDR.
///
/// @throw BadValue if iaaddr is null.
void addHint(const Option6IAAddrPtr& iaaddr);
/// @brief Convenience method adding new hint from IAPREFIX option.
///
/// @param iaprefix Pointer to IAPREFIX.
///
/// @throw BadValue if iaprefix is null.
void addHint(const Option6IAPrefixPtr& iaprefix);
};
@ -908,12 +942,14 @@ public:
/// pointer if no matches are found.
///
/// @param ctx Client context holding various information about the client.
///
/// @return Pointer to the reservation found, or an empty pointer.
static ConstHostPtr findGlobalReservation(ClientContext6& ctx);
/// @brief Creates an IPv6Resrv instance from a Lease6
///
/// @param lease Reference to the Lease6
///
/// @return The newly formed IPv6Resrv instance
static IPv6Resrv makeIPv6Resrv(const Lease6& lease) {
if (lease.type_ == Lease::TYPE_NA) {
@ -941,10 +977,10 @@ private:
/// @param [out] callout_status callout returned by the lease6_select
///
/// The following fields of the ctx structure are used:
/// @ref ClientContext6::subnet_ subnet the lease is allocated from
/// @ref ClientContext6::duid_ client's DUID
/// @ref ClientContext6::subnet_ Subnet the lease is allocated from
/// @ref ClientContext6::duid_ Client's DUID
/// @ref ClientContext6::iaid_ IAID from the IA_NA container the client sent to us
/// @ref ClientContext6::type_ lease type (IA, TA or PD)
/// @ref ClientContext6::type_ Lease type (IA, TA or PD)
/// @ref ClientContext6::fwd_dns_update_ A boolean value which indicates that server takes
/// responsibility for the forward DNS Update for this lease
/// (if true).
@ -958,6 +994,7 @@ private:
/// registered)
/// @ref ClientContext6::fake_allocation_ is this real i.e. REQUEST (false) or just picking
/// an address for SOLICIT that is not really allocated (true)
///
/// @return allocated lease (or NULL in the unlikely case of the lease just
/// became unavailable)
Lease6Ptr createLease6(ClientContext6& ctx,
@ -973,6 +1010,7 @@ private:
/// This may change in the future.
///
/// @param ctx client context that contains all details (subnet, client-id, etc.)
///
/// @return collection of newly allocated leases
Lease6Collection allocateUnreservedLeases6(ClientContext6& ctx);
@ -1061,8 +1099,8 @@ private:
/// @param [out] callout_status callout returned by the lease6_select
///
/// The following parameters are used from the ctx structure:
/// @ref ClientContext6::subnet_ subnet the lease is allocated from
/// @ref ClientContext6::duid_ client's DUID
/// @ref ClientContext6::subnet_ Subnet the lease is allocated from
/// @ref ClientContext6::duid_ Client's DUID
/// @ref ClientContext6::iaid_ IAID from the IA_NA container the client sent to us
/// @ref ClientContext6::fwd_dns_update_ A boolean value which indicates that server takes
/// responsibility for the forward DNS Update for this lease
@ -1078,6 +1116,7 @@ private:
/// allocated (true)
///
/// @return refreshed lease
///
/// @throw BadValue if trying to recycle lease that is still valid
Lease6Ptr
reuseExpiredLease(Lease6Ptr& expired,
@ -1109,6 +1148,7 @@ private:
/// @brief Utility function that removes all leases with a specified address
/// @param container A collection of Lease6 pointers
/// @param addr address to be removed
///
/// @return true if removed (false otherwise)
static bool
removeLeases(Lease6Collection& container,
@ -1232,6 +1272,7 @@ private:
/// - call lease4_recover hook
///
/// @param lease Lease to be reclaimed from Declined state
///
/// @return true if it's ok to remove the lease (false = hooks status says
/// to keep it)
bool reclaimDeclined(const Lease4Ptr& lease);
@ -1245,6 +1286,7 @@ private:
/// - call lease6_recover hook
///
/// @param lease Lease to be reclaimed from Declined state
///
/// @return true if it's ok to remove the lease (false = hooks status says
/// to keep it)
bool reclaimDeclined(const Lease6Ptr& lease);
@ -1518,6 +1560,7 @@ public:
/// matches are found.
///
/// @param ctx Client context holding various information about the client.
///
/// @return Pointer to the reservation found, or an empty pointer.
static ConstHostPtr findGlobalReservation(ClientContext4& ctx);
@ -1585,7 +1628,7 @@ private:
/// -# If the client is not requesting any specific address, allocate
/// the address from the dynamic pool.
///
/// @throws various exceptions if the allocation goes wrong.
/// @throw various exceptions if the allocation goes wrong.
///
/// @param ctx Client context holding the data extracted from the
/// client's message.
@ -1620,6 +1663,7 @@ private:
/// - @ref ClientContext4::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
/// become unavailable)
Lease4Ptr createLease4(const ClientContext4& ctx,
@ -1654,6 +1698,7 @@ private:
/// @param [out] callout_status callout returned by the lease4_select
///
/// @return Updated lease instance.
///
/// @throw BadValue if trying to reuse a lease which is still valid or
/// when the provided parameters are invalid.
Lease4Ptr

View File

@ -8,6 +8,7 @@
#define MULTI_THREADING_MGR_H
#include <boost/noncopyable.hpp>
#include <mutex>
namespace isc {
namespace util {
@ -28,18 +29,14 @@ namespace util {
/// For instance for a class protected by its mutex:
/// @code
/// namespace locked {
/// void foo() { ... }
/// int foo() { ... }
/// } // end of locked namespace
///
/// void foo() {
/// if (MultiThreadingMgr::instance().getMode()) {
/// lock_guard<mutex> lock(mutex_);
/// locked::foo();
/// } else {
/// locked::foo();
/// }
/// int foo() {
/// return MultiThreadingMgr::call(mutex_, []() {return locked::foo()});
/// }
/// @endcode
class MultiThreadingMgr : public boost::noncopyable {
public:
@ -62,6 +59,23 @@ public:
/// @param enabled The new mode.
void setMode(bool enabled);
/// @brief Call a Functor in MT or ST mode
///
/// @tparam Lockable a lock which is used to create a thread safe context
/// @tparam Callable a functor which will be called in MT or ST mode
/// @param lk the lock object to perform lock in MT mode
/// @param f the functor to call
/// @result the result of the functor call
template<typename Lockable, typename Callable>
static auto call(Lockable& lk, const Callable& f) -> decltype(f()) {
if (MultiThreadingMgr::instance().getMode()) {
std::lock_guard<Lockable> lock(lk);
return f();
} else {
return f();
}
}
protected:
/// @brief Constructor.