summaryrefslogtreecommitdiffstats
path: root/src/lib/dhcpsrv/tracking_lease_mgr.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/dhcpsrv/tracking_lease_mgr.h')
-rw-r--r--src/lib/dhcpsrv/tracking_lease_mgr.h308
1 files changed, 308 insertions, 0 deletions
diff --git a/src/lib/dhcpsrv/tracking_lease_mgr.h b/src/lib/dhcpsrv/tracking_lease_mgr.h
new file mode 100644
index 0000000..fe21177
--- /dev/null
+++ b/src/lib/dhcpsrv/tracking_lease_mgr.h
@@ -0,0 +1,308 @@
+// Copyright (C) 2023 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/.
+
+#ifndef TRACKING_LEASE_MGR_H
+#define TRACKING_LEASE_MGR_H
+
+#include <asiolink/io_address.h>
+#include <dhcpsrv/lease_mgr.h>
+#include <boost/multi_index_container.hpp>
+#include <boost/multi_index/composite_key.hpp>
+#include <boost/multi_index/indexed_by.hpp>
+#include <boost/multi_index/member.hpp>
+#include <boost/multi_index/hashed_index.hpp>
+#include <boost/multi_index/ordered_index.hpp>
+#include <boost/multi_index/sequenced_index.hpp>
+#include <boost/shared_ptr.hpp>
+#include <functional>
+#include <string>
+#include <unordered_set>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Introduces callbacks into the @c LeaseMgr.
+///
+/// The LeaseMgr is a central point of lease management and is aware of all
+/// lease changes within the server instance. Thus, it is a good opportunity
+/// to allow installing callbacks in the @c LeaseMgr to track all lease
+/// changes. An allocator maintaining a list of free leases (FLQ allocator)
+/// can benefit from it by/ installing the callbacks that add or remove
+/// free leases from this list, depending on the lease manager's activity.
+/// The callbacks are invoked regardless if the lease changes result from a
+/// normal lease allocation or a control command. The callbacks can also be
+/// useful for maintaining a log of lease changes. Such a log could be made
+/// available externally and consumed by another application (e.g., Stork).
+///
+/// The lease manager can track three types of calls: new lease insertion
+/// (add), an existing lease update, and lease deletion. Even though the
+/// lease reclamation is similar to deleting a lease because it becomes free,
+/// it is a lease update from the lease manager's standpoint. Currently,
+/// the lease manager cannot track the deletion of the reclaimed leases
+/// (i.e., the leases in the expired-reclaimed state).
+///
+/// The lease backends should call the suitable tracking functions:
+/// @c trackAddLease, @c trackUpdateLease, and @c trackDeleteLease.
+/// However, the backends must ensure that there are no race conditions
+/// between modifying the lease in the database and running the callbacks.
+/// Suppose that two threads modify the same lease. Thread A inserts the
+/// lease in the database, and thread B removes it. The callback for
+/// thread A should be invoked before the callback for thread B. If they
+/// are invoked in reverse order, it can result in an inconsistent state
+/// in the free lease queue allocator because the allocator should record
+/// the lease as available after the thread B callback. The reverse
+/// invocation order would result in marking the lease as unavailable for
+/// allocation after both callbacks.
+///
+/// The race condition does not occur for the Memfile backend because it
+/// guards entire functions with a mutex. However, the SQL backends rely
+/// on the database to guard against concurrent writes. In these cases,
+/// the backend must protect against the reverse order of callbacks. They
+/// should use the lease locking mechanism introduced in the
+/// @c TrackingLeaseMgr.
+///
+/// The lease locking is modeled on an @c unordered_set container holding
+/// the leases with the ongoing allocations. The leases are inserted into
+/// this container by the @c tryLock function. If another thread has already
+/// locked the lease, this function returns @c false to indicate an
+/// unsuccessful attempt. In this case, the thread should resign from updating
+/// the lease and return early. It can result in a lease allocation failure,
+/// but two concurrent threads extremely rarely work on allocating a lease for
+/// the same client. A passive wait could be another option here, but it is a
+/// much more complicated solution for a bit of gain.
+class TrackingLeaseMgr : public LeaseMgr {
+public:
+
+ /// The @c LeaseMgrFactory manages the @c LeaseMgr instances and has
+ /// to be able to move installed callbacks between them. No other external
+ /// class can have access to the callbacks container. Thus, we can't make
+ /// the container public. The friend declaration deals with it cleanly.
+ friend class LeaseMgrFactory;
+
+ /// @brief An enumeration differentiating between lease write operations.
+ typedef enum {
+ TRACK_ADD_LEASE,
+ TRACK_UPDATE_LEASE,
+ TRACK_DELETE_LEASE
+ } CallbackType;
+
+ /// @brief Type of a callback function invoked upon a lease insertion,
+ /// update or deletion.
+ ///
+ /// The first argument is a pointer to the lease for which the callback
+ /// is invoked.
+ typedef std::function<void(LeasePtr)> CallbackFn;
+
+ /// @brief A structure representing a registered callback.
+ ///
+ /// It associates the callback with a type, its owner, subnet
+ /// identifier, and a lease type. The owner is a string specified
+ /// by the registration function caller. There must be at most one
+ /// callback registered for the particular owner, subnet identifier
+ /// and the lease type.
+ typedef struct {
+ /// @brief Callback type (i.e., lease add, update, delete).
+ CallbackType type;
+
+ /// @brief An entity owning callback registration (e.g., FLQ allocator).
+ std::string owner;
+
+ /// Subnet identifier associated with the callback.
+ SubnetID subnet_id;
+
+ /// @brief Lease types for which the callback should be invoked.
+ Lease::Type lease_type;
+
+ /// @brief Callback function.
+ CallbackFn fn;
+ } Callback;
+
+protected:
+
+ /// @brief A multi-index container holding registered callbacks.
+ ///
+ /// The callbacks are accessible via two indexes. The first composite index
+ /// filters the callbacks by the callback type (i.e., lease add, update or delete)
+ /// and the subnet id. The second index filters the callbacks by the subnet id
+ /// and the lease type.
+ typedef boost::multi_index_container<
+ Callback,
+ boost::multi_index::indexed_by<
+ boost::multi_index::ordered_non_unique<
+ boost::multi_index::composite_key<
+ Callback,
+ boost::multi_index::member<Callback, CallbackType, &Callback::type>,
+ boost::multi_index::member<Callback, SubnetID, &Callback::subnet_id>,
+ boost::multi_index::member<Callback, Lease::Type, &Callback::lease_type>
+ >
+ >,
+ boost::multi_index::ordered_non_unique<
+ boost::multi_index::composite_key<
+ Callback,
+ boost::multi_index::member<Callback, SubnetID, &Callback::subnet_id>,
+ boost::multi_index::member<Callback, Lease::Type, &Callback::lease_type>
+ >
+ >
+ >
+ > CallbackContainer;
+
+ /// @brief Pointer to the callback container.
+ typedef boost::shared_ptr<CallbackContainer> CallbackContainerPtr;
+
+ /// @brief Constructor.
+ TrackingLeaseMgr();
+
+ /// @brief Attempts to lock a lease.
+ ///
+ /// If a lease is successfully locked, no other thread can lock it. It protects
+ /// against running the callbacks out of order when two threads modify the same
+ /// lease. Such a locking should only be used when the lease allocation followed by
+ /// the callbacks invocation are not protected by some other synchronization
+ /// mechanism. In particular, the Memfile backend uses a mutex for locking in the
+ /// lease allocation functions. In this case, it is unnecessary to apply a lock at the
+ /// lease level. The SQL backends rely on the database locking mechanisms to prevent
+ /// the concurrent updates of the same lease. These backends must use the lease locking
+ /// to ensure the correct callbacks invocation order.
+ ///
+ /// This function is not thread-safe and must be invoked in a thread-safe context.
+ ///
+ /// @param lease a lease instance for which the lock should be attempted.
+ /// @return true when locking was successful, false otherwise. In the latter case,
+ /// the thread should stop a lease allocation or deallocation attempt.
+ bool tryLock(const LeasePtr& lease);
+
+ /// @brief Attempts to unlock a lease.
+ ///
+ /// This function is not thread-safe and must be invoked in a thread-safe context.
+ ///
+ /// @param lease a lease instance for which unlocking should be attempted.
+ void unlock(const LeasePtr& lease);
+
+public:
+
+ /// @brief Checks if the lease is locked.
+ ///
+ /// This function is useful in the unit tests.
+ ///
+ /// @return true if the lease is locked, false otherwise.
+ bool isLocked(const LeasePtr& lease);
+
+protected:
+
+ /// @brief Invokes the callbacks when a new lease is added.
+ ///
+ /// It executes all callbacks of the @c TRACK_ADD_LEASE type for a subnet id of 0
+ /// and the subnet id associated with the lease.
+ ///
+ /// The callbacks execution order is not guaranteed.
+ ///
+ /// @param lease new lease instance.
+ void trackAddLease(const LeasePtr& lease);
+
+ /// @brief Invokes the callbacks when a lease is updated.
+ ///
+ /// It executes all callbacks of the @c TRACK_UPDATE_LEASE type for a subnet id of 0
+ /// and the subnet id associated with the lease.
+ ///
+ /// The callbacks execution order is not guaranteed.
+ ///
+ /// @param lease updated lease instance.
+ void trackUpdateLease(const LeasePtr& lease);
+
+ /// @brief Invokes the callbacks when a lease is deleted.
+ ///
+ /// It executes all callbacks of the @c TRACK_DELETE_LEASE type for a subnet id of 0
+ /// and the subnet id associated with the lease.
+ ///
+ /// The callbacks execution order is not guaranteed.
+ ///
+ /// @param lease deleted lease instance.
+ void trackDeleteLease(const LeasePtr& lease);
+
+public:
+
+ /// @brief Registers a callback function for a subnet.
+ ///
+ /// @param type callback type.
+ /// @param owner callback owner identifier.
+ /// @param subnet_id subnet identifier; it can be set to 0 if the callback should be
+ /// called for all subnets.
+ /// @param lease_type a lease type.
+ /// @param callback_fn callback function instance.
+ /// @throw InvalidOperation when the callback has been already registered for the given owner and
+ /// the subnet identifier.
+ void registerCallback(CallbackType type, std::string owner, SubnetID subnet_id,
+ Lease::Type lease_type, CallbackFn callback_fn);
+
+ /// @brief Registers a callback function for all subnets.
+ ///
+ /// @param type callback type.
+ /// @param owner callback owner identifier.
+ /// @param lease_type a lease type.
+ /// @param callback_fn callback function instance.
+ /// @throw InvalidOperation when the callback has been already registered for the given owner and
+ /// all subnets.
+ void registerCallback(CallbackType type, std::string owner, Lease::Type lease_type,
+ CallbackFn callback_fn);
+
+ /// @brief Unregisters all callbacks for a given subnet identifier.
+ ///
+ /// @param subnet_id a subnet identifier.
+ /// @param lease_type a lease type.
+ void unregisterCallbacks(SubnetID subnet_id, Lease::Type lease_type);
+
+ /// @brief Unregisters all callbacks.
+ void unregisterAllCallbacks();
+
+ /// @brief Checks if any callbacks have been registered.
+ ///
+ /// It is a quick check to be performed by the backends whether or not
+ /// the callbacks mechanism is used.
+ ///
+ /// @return true if any callbacks have been registered.
+ bool hasCallbacks() const;
+
+protected:
+
+ /// @brief Converts callback type to string for logging purposes.
+ ///
+ /// @param type callback type.
+ /// @return callback type name or the 'unknown' string.
+ static std::string callbackTypeToString(CallbackType type);
+
+ /// @brief Runs registered callbacks of the particular type.
+ ///
+ /// The specified lease instance contains the subnet identifier used to
+ /// filter the callbacks to be invoked.
+ ///
+ /// @param type callback type.
+ /// @param lease lease instance for which the callbacks are invoked.
+ void runCallbacks(CallbackType type, const LeasePtr& lease);
+
+ /// @brief Runs registered callbacks of the particular type for a subnet id.
+ ///
+ /// It is called internally by the @c runCallbacks function.
+ ///
+ /// @param type callback type.
+ /// @param subnet_id subnet identifier for which the callbacks are invoked.
+ /// @param lease lease instance for which the callbacks are invoked.
+ void runCallbacksForSubnetID(CallbackType type, SubnetID subnet_id,
+ const LeasePtr& lease);
+
+ /// @brief The multi-index container holding registered callbacks.
+ CallbackContainerPtr callbacks_;
+
+ /// @brief A set of locked leases.
+ ///
+ /// It is empty if locking is not used (e.g. Memfile backend) or when there
+ /// are no ongoing allocations.
+ std::unordered_set<asiolink::IOAddress, asiolink::IOAddress::Hash> locked_leases_;
+};
+
+} // end of namespace isc::dhcp
+} // end of namespace isc
+
+#endif // TRACKING_LEASE_MGR_H