Kea 2.7.6
|
This library contains code used for the DHCPv4 and DHCPv6 servers' operations, including the "Lease Manager" that manages information about leases and the "Configuration Manager" that stores the servers' configuration etc. The code here is server specific. For generic (useful to the server, client, relay and other tools like perfdhcp) code, please see libkea-dhcp++ - Low Level DHCP Library.
This library contains several crucial elements for the operation of the DHCP server:
The global parameters handle direct (vs using a search in a name to value table) access to global scalar (i.e. not list or map) parameter values.
This is related to the procedure to add a new global scalar parameter to the DHCPv4 or DHCPv6 (DHCPvX below) server implementation:
Note there is nothing to update for a global parameter in the configuration backend: no new column in database schemas, no code in hooks. Of course this does not apply to parameters which exist at not global level too.
LeaseMgr provides a common, unified abstract API for all database backends. All backends are derived from the base class isc::dhcp::LeaseMgr. Currently Kea supports three backends, implemented in the following classes:
Configuration Manager (isc::dhcp::CfgMgr) is a singleton object which holds configuration information necessary for the operation of Kea daemons. A complete collection of information for the daemon is stored in the isc::dhcp::SrvConfig object. Internally, the Configuration Manager holds a list of isc::dhcp::SrvConfig objects, from which one is marked as "current configuration".
When the server starts up or is being reconfigured a new isc::dhcp::SrvConfig object, referred to as "staging configuration", is created. The staging configuration is held at the tip of the list of configurations. The object can be accessed by calling the isc::dhcp::CfgMgr::getStagingCfg. This object can be accessed from different stages of the configuration parsing and modified as needed. Modifications of the staging configuration do not affect the current configuration. The staging configuration is unused until the isc::dhcp::CfgMgr::commit function is called. This exception safe method marks the staging object as "current configuration". The const pointer to the current configuration can be accessed by calling a isc::dhcp::CfgMgr::getCurrentCfg.
The staging configuration can be discarded at any time before it is committed by calling the isc::dhcp::CfgMgr::rollback. This removes the isc::dhcp::SrvConfig object from the Configuration Manager. When the isc::dhcp::CfgMgr::getStagingCfg is called again a fresh/default isc::dhcp::SrvConfig object is returned.
The Configuration Manager stores previous configurations, i.e. configurations which occurred prior to the most current configuration. This is currently unused (except for unit tests) by the daemons, but in the future this mechanism can be used to trigger a rollover of the server configuration to a last good configuration that the administrator prefers.
The previous configurations are identified by the value which specifies a distance between the current configuration and the previous configuration. For example: the value of 1 identifies an immediate predecessor of the current configuration, the value of 2 identifies the one that occurred before it etc.
All configuration classes are derived from the abstract base class isc::data::CfgToElement and define the toElement virtual method which returns a isc::data::ConstElementPtr which must be parsed into the same object, i.e. fulfill this property:
Host Manager implemented by the isc::dhcp::HostMgr is a singleton object which provides means to retrieve resources statically assigned to the DHCP clients, such as IP addresses, prefixes or hostnames. The statically assigned resources are called reservations (or host reservations) and they are represented in the code by the isc::dhcp::Host class.
The reservations can be specified in the configuration file or in some other storage (typically in a database). A dedicated object, called host data source, is needed to retrieve the host reservations from the database. This object must implement the isc::dhcp::BaseHostDataSource interface and its implementation is specific to the type of storage holding the reservations. For example, the host data source managing host reservations in the MySQL database is required to establish connection to the MySQL database and issue specific queries. A factory method creating an instance of a base host data source object must be registered (at global object initialization for built-in backends, dynamically for backends loaded at run-time). See host_data_source_factory.cc for example code that registers MySQL and PostgreSQL. Note, that this instance is created as "alternate host data source" as opposed to the primary data source which returns host reservations specified in the configuration file. The primary data source is implemented internally in the isc::dhcp::HostMgr and uses the configuration data structures held by the isc::dhcp::CfgMgr to retrieve the reservations. In general, the isc::dhcp::HostMgr first searches for the reservations using the primary data source and falls back to the use of alternate data source when nothing has been found. For those methods which are meant to return multiple reservations (e.g. find all reservations for the particular client), the isc::dhcp::HostMgr will use both primary and alternate data source (if present) and concatenate results.
For more information about the isc::dhcp::HostMgr please refer to its documentation.
Storing and retrieving host reservations within a PostgreSQL schema is provided by the class, isc::dhcp::PgSqlHostDataSource, a derivation of isc::dhcp::BaseHostDataSource and is depicted in the following class diagram:
The isc::dhcp::CfgOption object holds a collection of options being sent to the client. Since each subnet comes with a distinct set of options, every isc::dhcp::Subnet object holds its own copy of the isc::dhcp::CfgOption object with specific options.
The DHCP server also allows for configuration of "global" options which are shared by all subnets. The rule here is that if a particular option appears in the global options set and the subnet specific options set, the subnet specific option takes precedence. The global options configuration is held in the dedicated instance of the isc::dhcp::CfgOption class. This instance is owned by the isc::dhcp::SrvConfig class.
When the new configuration is parsed, the global options are merged into the isc::dhcp::CfgOption instances for all subnets. This is causing some overhead during the reconfiguration of the server but on the other hand it avoids the lookup of options in two places (among subnet specific options and global options) during each packet processing.
One of the benefits of keeping a separate set of global options is that there may be cases when the server administrator doesn't specify any subnet configuration and only wants global options to be used. This is the case, when the DHCP server is used for stateless configuration, i.e. client's are not allocated an address or prefix, and only stateless configuration is handed out.
The Allocation Engine (isc::dhcp::AllocEngine) is one of the core Kea modules. It uses the data from the received client DHCP messages to find an available lease and allocate it to the client. Finding a free lease is very complex because the engine has to consider the client's hints, whether or not the client has a static reservation, classes associated with the client, and many more. If the engine cannot allocate a lease indicated in the hint and the client has no static reservations, it has to find an available lease in the configured address or prefix delegation pools. It is the responsibility of the allocator (isc::dhcp::Allocator).
Allocators are implemented in C++ classes with a well-defined interface. Each allocator uses a different algorithm for selecting a lease from the configured pools:
The following allocators are not implemented in Kea but can be considered in the future:
Allocation Engine has been extended to support different types of leases. Four types are supported: TYPE_V4 (IPv4 addresses), TYPE_NA (normal IPv6 addresses), TYPE_TA (temporary IPv6 addresses) and TYPE_PD (delegated prefixes). Support for TYPE_TA is partial. Some routines are able to handle it, while other are not. The major missing piece is the RandomAllocator, so there is no way to randomly generate an address. This defeats the purpose of using temporary addresses for now.
The Allocation Engine supports allocation of the IPv6 addresses and prefixes. For a prefix pool, the iterative allocator "walks over" every available pool. It is similar to how it iterates over address pool, but instead of increasing address by just one, it walks over the whole delegated prefix length in one step. This is implemented in isc::dhcp::AllocEngine::IterativeAllocator::increasePrefix(). Functionally the increaseAddress(addr) call is equivalent to increasePrefix(addr, 128) (increasing by a /128 prefix, i.e. a single address). However, both methods are kept, because increaseAddress() is faster and this is a routine that may be called many hundred thousands times per second.
The Allocation Engine supports allocation of statically assigned addresses to the DHCPv4 clients, a.k.a. Host Reservation.
When the server receives a DHCPDISCOVER or DHCPREQUEST from the client it calls isc::dhcp::AllocEngine::allocateLease4 to obtain the suitable lease for the client. If the Allocation Engine determines that the particular client has a reservation it will try to allocate a reserved address for it. If the client requested allocation or renewal of a different address, the Allocation Engine will respond with a NULL lease to indicate that the address desired by the client could not be assigned. The DHCP server should send a DHCPNAK to the client and the client should fall back to the DHCP server discovery. When the client sends DHCPDISCOVER, the Allocation Engine offers the reserved address and the client should request the offered address in subsequent DHCPREQUEST messages.
There are cases when the Allocation Engine is unable to assign the reserved address for the client. This includes the situations when the address had been previously reserved for another client or the address had been assigned out of the dynamic address pool. Such address may still remain in use of the client which obtained it first and the Allocation Engine must not assign it to the client for which it is reserved until the client using this address releases or the server assigns a different address for it.
In order to resolve this conflict the Allocation Engine will refuse to renew the lease for the client using the address not reserved for it. This client should fall back to the 4-way exchange and the Allocation Engine will assign a different address. As a result, the reserved address will be freed for the use of the client for which the reservation was made. The client will be offered/allocated a reserved address the next time it retries sending a DHCPDISCOVER/DHCPREQUEST message to the server.
The allocation engine provides a cache-like feature: when a suitable lease already exists for a client if its age is small enough compared to the valid lifetime (threshold parameter) and below a configured maximum (max age parameter) the lease can be reused. A reusable lease is marked by a not zero reuseable_valid_lft_ value.
The isc::dhcp::TimerMgr
is a singleton class used throughout the server process to register and unregister timers triggering periodic tasks such as lease file cleanup, reclamation of expired leases etc.
The Timer Manger is using ASIO deadline timers (wrapped in isc::asiolink::IntervalTimer
class) to execute tasks according to the configured periods. Therefore, the server process must provide the Timer Manager with the pointer to the isc::asiolink::IOService
which the server is using to run asynchronous tasks.
Current implementation of the DHCP servers uses synchronous calls to select()
function to check if any transmission has been received on any socket. This poses a problem with running asynchronous calls via IOService
in the main server loop because the select()
blocks for a specified amount of time while asynchronous calls are not triggered. In the future we should migrate from the synchronous select()
calls into asynchronous calls using ASIO. Currently, we mitigate the problem by lowering the select()
timeout to 1s, and polling IOService
for "ready" timers (handlers) after select()
returns. This may cause delays of "ready" handlers execution by around 1s. However, this is acceptable for the current applications of the periodic timers.
Lease reclamation is the process in which the expired lease becomes available for re-assignment to the same or another client. When the server reclaims the lease it executes the callouts registered for the "lease4_expire" and "lease6_expire" hook points, performs the DNS update to remove any DNS records associated with the expired lease, and finally marks a lease as reclaimed in the lease database. The lease may be marked as reclaimed by setting its state to Lease::STATE_EXPIRED_RECLAIMED
or by being removed from the database.
Reclamation is performed periodically for a bulk of expired leases in the lease reclamation routine. The lease reclamation routines for both DHCP servers are implemented in the isc::dhcp::AllocEngine
:
isc::dhcp::AllocEngine::reclaimExpiredLeases4
(DHCPv4)isc::dhcp::AllocEngine::reclaimExpiredLeases6
(DHCPv6)Note that besides the reclamation of the leases, these methods also update the relevant statistics, i.e. decrease the number of assigned leases and increase the number of reclaimed leases.
The reclamation routines are executed periodically according to the server configuration (see the documentation for the "expired-leases-processing" configuration map). Internally, they are registered as callback functions in the isc::dhcp::TimerMgr
(see Timer Manager for the details), during the servers' startup or reconfiguration.
Execution of the reclamation routine may take a relatively long period of time. It depends on the complexity of the callouts, whether the DNS update is required for leases, and the type of the lease database used. While the reclamation routine is executed, the server will not process any DHCP messages to avoid race conditions being a result of concurrent access to the lease database to allocate and reclaim leases. To make sure that the server remains responsive, it is possible to limit the number of leases being processed by the leases reclamation routine and/or limit the time for the reclamation routine to process leases. Both limits are specified in the respective arguments passed to the lease reclamation routines.
As mentioned above, reclaimed leases may be marked as such, by updating their state to Lease::STATE_EXPIRED_RECLAIMED
or by being removed. This behavior is controlled by the boolean parameter passed to the reclamation routine. The first approach is desired when the server should provide "lease affinity", i.e. ability to re-assign the same lease to the returning client. By only updating the lease state, the server preserves association of the lease with a particular client. When that client returns the server may assign the same lease to the client, assuming that this lease is still available. The lease is removed during the reclamation when the lease affinity is not required and it is preferred to not keep redundant information (about expired leases) in the lease database.
If the reclaimed leases are not removed, they are held in the database for a specified amount of time after their expiration. Each reclaimed lease is removed when this time elapses for it. The isc::dhcp::LeaseMgr::deleteExpiredReclaimedLeases4
and isc::dhcp::LeaseMgr::deleteExpiredReclaimedLeases6
are used to remove those leases for which the specified amount of time since expiration elapsed. These methods are executed periodically by the DHCP servers using the dedicated timers registered in the isc::dhcp::TimerMgr
.
An important service offered by this library is the subnet selection from a query packet.
Selectors (i.e., members of SubnetSelector
class) are:
First use the option select, next if the message was relayed (not undefined gateway address) use the gateway address as a subnet relay address.
If a subnet was not already selected choose an address between:
Match the chosen address in a subnet address range.
Selectors (i.e., members of SubnetSelector
class) are:
Check if DHCPv4-over-DHCPv6 is enabled for this particular subnet and continue with the next subnet is if it is not.
First the remote address is matched in a subnet IPv6 range, second the interface ID if it is set is matched, third the interface name.
These rules are applied for each subnet configuration so if two subnets match the first one is returned (vs. the first matching rule).
Selectors (i.e., members of SubnetSelector
class) are:
If the first relay link address is undefined the client is directly connected: the interface name is matched and if it does not select a subnet the remote address is matched in a subnet address range.
If the first relay link address is not undefined the query was relayed: the interface ID is tried and if it does not select a subnet the first relay address is matched as a subnet relay address.
DHCPv4-over-DHCPv6 support is implemented using cooperating DHCPv6 and DHCPv6 servers. Servers communicate over a pair of local UDP sockets using consecutive ports. The common part of the Inter Process Communication (IPC) is provided by the base class isc::dhcp::Dhcp4o6IpcBase
.
The receiving interface name and remote IPv6 address meta information are conveyed within a Vendor Specific Information option with the ISC enterprise ID carrying interface and remote address suboptions. isc::dhcp::Dhcp4o6IpcBase::send adds them, isc::dhcp::Dhcp4o6IpcBase::receive decodes and removes them.
Also see DHCPv4-over-DHCPv6 DHCPv4 Server Side and DHCPv4-over-DHCPv6 DHCPv6 Server Side for details on how IPC is used by DHCPv4 and DHCPv6 components.
DHCPv4-over-DHCPv6 which are relayed by a DHCPv6 relay are not yet supported.
Note that for backends specific consideration is in Multi-Threading Consideration for DHCP Database Backends.
Below Kea thread safe means thread safe when the multi-threading mode is true (when it is false packets are processed by the main thread).
By default this library is not thread safe, in particular all classes used for configuration are not thread safe. Exceptions are: