Kea 2.7.4
|
Congestion occurs when servers are subjected to client queries faster than they can be fulfilled. Subsequently, the servers begin accumulating a backlog of pending queries. The longer the high rate of traffic continues the farther behind the servers fall. Depending on the client implementations, those that fail to get leases either give up or simply continue to retry forever. In the former case, the server may eventually recover. The latter case is vicious cycle from which the server is unable to escape.
In a well-planned deployment, the number and capacity of servers is matched to the maximum client loads expected. As long as capacity is matched to load, congestion does not occur. If the load is routinely too heavy, then the deployment needs to be re-evaluated. Congestion typically occurs when there is a network event that causes overly large numbers of clients to simultaneously need leases such as recovery after a network outage.
Kea 1.5.0 introduces a new feature referred to as Congestion Handling. The goal of Congestion Handling is to help the servers mitigate the peak in traffic by fulfilling as many of the most relevant requests as possible until it subsides.
Prior to Kea 1.5.0, Kea DHCP servers read inbound packets directly from the interface sockets in the main application thread. This meant that packets waiting to be processed were held in socket buffers themselves. Once these buffers fill any new packets are discarded. Under swamped conditions the servers can end up processing client packets that may no longer be relevant, or worse are redundant. In other words, the packets waiting in the FIFO socket buffers become increasingly stale.
Congestion Handling offers the ability to configure the server to use a separate thread to read packets from the interface socket buffers. As the thread reads packets from the buffers they are added to an internal "packet queue". The server's main application thread processes packets from this queue rather than the socket buffers. By structuring it this way, we've introduced a configurable layer which can make decisions on which packets to process, how to store them, and the order in which they are processed by the server.
The default packet queue implementation for both Kea DHCPv4 and DHCPv6 servers is a simple ring buffer. Once it reaches capacity, new packets get added to the back of queue by discarding packets from the front of queue. Rather than always discarding the newest packets, we now always discard the oldest packets. The capacity of the buffer (i.e. the maximum number of packets the buffer can contain) is configurable.
It is possible to replace the default packet queue implementation with a custom implementation by registering it with your Kea server via a hook library. The steps for doing this are listed below:
(If you are not familiar with writing Kea hook libraries, you may wish to read Hooks Developer's Guide before continuing).
Your custom packet queue must derive from the class template, isc::dhcp::PacketQueue. The class is almost entirely abstract and deliberately brief to provide developers wide latitude in the internals of their solutions.
The template argument, PacketTypePtr
, is expected to be either isc::dhcp::Pkt4Ptr or isc::dhcp::Pkt6Ptr, depending upon which protocol the implementation will handle. Please note that the while following text and examples largely focus on DHCPv4 out of convenience as the concepts are identical for DHCPv6. For completeness there are code snippets at the end of this chapter for DHCPv6.
The two primary functions of interest are:
The remaining functions that you'll need to implement are self-explanatory.
How your actual "queue" is implemented is entirely up to you. Kea's default implementation using a ring buffer based on Boost's boost::circular_buffer (please refer to isc::dhcp::PacketQueueRing, isc::dhcp::PacketQueueRing4 and isc::dhcp::PacketQueueRing6). The most critical aspects to remember when developing your implementation are:
isc::dhcp::IfaceMgr using two derivations of isc::dhcp::PacketQueueMgr (one for DHCPv4 and one for DHCPv6), to register queue implementations and instantiate the appropriate queue type based the current configuration. In order to register your queue implementation your hook library must provide a factory function that will be used to create packet queues. This function will be invoked by the server during the configuration process to instantiate the appropriate queue type. For DHCPv4, the factory should be as follows:
and for DHCPv6:
The factory's only argument is an isc::data::ConstElementPtr. This is will be an isc::data::MapElement instance containing the contents of the configuration element "dhcp-queue-control" from the Kea server's configuration. It will always have the following two values:
Beyond that you may add whatever additional values you may require. In other words, the content is arbitrary so long as it is valid JSON. It is up to your factory implementation to examine the contents and use them to construct a queue instance.
@subsection packet-queue-derivation-example An Example
Let's suppose you wish to develop a queue for DHCPv4 and your implementation requires two configurable parameters: capacity and threshold. Your class declaration might look something like this:
Your factory implementation would then look something like this:
Kea's configuration parser cannot know your parameter requirements and thus can only flag JSON syntax errors. Thus it is important for your factory to validate your parameters according to your requirements and throw meaningful exceptions when they are not met. This allows users to know what to correct.
All hook libraries must provide a load() and unload() function. Your hook library should register you queue factory during load() and un-register it during unload(). Picking up with the our example, those functions might look something like this:
You're almost there. You developed your implementation, you've unit tested it (You did unit test it right?). Now you just have to tell Kea to load it and use it. Continuing with the example, your kea-dhcp4 configuration would need to look something like this:
For completeness, this section includes the example from above implemented for DHCPv6.
DHCPv6 Class declaration:
DHCPv6 Factory implementation:
DHCPv6 Hook load/unload functions
Server configuration for kea-dhcp6: