This document presents a design for static configuration of resources allocated to the DHCP clients. This mechanism is commonly referred to as "Host Reservation", or "HR". The statically assigned resources, such as specific IP address or hostname, may only be allocated by the server to the specific clients contacting the server as desired by the administrator. Clients are identified using unique properties which can be extracted from the DHCP messages, e.g. hardware address, client identifier, circuit identifier etc. Mechanisms designed in this document are meant to be extensible to support other identifiers specified in the future.
The design described in this document is a result of multiple discussions carried on mailing lists. At the time of creation of this, the following mail threads (along with followups) about the host reservation design were known:
The host reservations for the legacy ISC DHCP server are created in a server configuration file. However, this does not scale well for the large number of reservations and it becomes troublesome for the administrator to manage the reservations as it requires editing and parsing of the configuration file in the proprietary format, and reconfiguration of the server to commit the changes. Hence, in many cases the server operators want to store the information about the subscribers (clients) in the database. Although, this approach brings significant benefits for the large installations it also brings additional dependencies (e.g. database, management tools) and complexity. So, Kea should still support host reservations via the server configuration file, which smaller installations would benefit from. It should also be possible to use both configuration methods concurrently, i.e. store information about some reservations in a configuration file, some in a database. At this time, there is no requirement for Kea to support concurrent use of multiple databases at the same time, e.g. store some reservations in a MySQL database and other reservations in PostgreSQL.
Independent sources of HR configuration
Kea supports switchable lease database backends. Two of them support storing and retrieving lease information from the SQL database. Since lease information held in the database is similar to the information about host reservation, the team discussed whether it is feasible to use the tables holding lease information to create reservations in a form of static (non-expiring) leases. This approach has a number issues, e.g.:
Host reservation requires additional information which doesn't "fit well" into the purpose of the lease table, e.g. options to be handed out to the client
Each extension to the information defining host reservation (adding new data) requires modification of the lease table which requires upgrade of the lease database
A user desiring to store host reservations in the database would be enforced to use the same database for leases, whereas a user may want to not use database for leases at all.
For these reasons, it has been decided that it should be possible to store the HR information in a separate data storage. However, it should also be possible to configure the server to store both types of information in the same database (e.g. MySQL), but both types of information must be stored in separate SQL tables.
Multiple reservations for the same host
With the mobile devices being very popular, there is a need to support clients which move between networks. Such clients often need multiple static reservations, depending on the network they are connected to. This implies that the reservations must be possible on per-subnet basis, rather than being created "globally".
In-pool vs Out-of-pool Reservations
The Kea configuration structure defines pools of dynamically allocated addresses for each subnet. When a new address is allocated an allocator picks an address from a pool within a subnet to which the client is connected, and verifies if the lease for the particular address exists in a lease database. If the lease doesn't exist (address hasn't been taken by the other client) the lease is created and the address is handed out to the client. The host reservations will also be defined on per-subnet basis which may potentially be a source of conflicts between the dynamically allocated addresses and static reservations. There are two possible approaches:
It is disallowed to add host reservation for an address which belongs to the pool from which dynamic allocations are also made. With this approach, when the server is allocating a new address it does not have to check in the HR database if the reservation for the particular address exists. This avoids performance penalty for performing additional queries to the HR database. Note, that if the HRs are stored in the database the penalty may be significant because it may requires additional SQL queries to the database. This problem may be partially mitigated by caching the queries to the database but caching will be mostly efficient in cases where there are many entries in the HR database. If there are just a couple of entries or none, the server would need to cache negative responses (no reservation for the host) which hasn't been yet discussed between team members as a possible caching strategy. The details of caching mechanism is out of scope for this document.
It is allowed to add host reservations for the addresses belonging to the pool from which the addresses are dynamically allocated. This facilitates the case when the client obtains an address from the server and the reservation is later added for this address to grant this address to the client. This is quite common scenario for home routers. The down side of this approach is that it requires additional query to the HR database for each packet for which the address is dynamically allocated, to check if there is no collision with the HR. If the HR exists for this address a different address needs to be allocated.
The solution proposed in this design is to support both use cases for networks in which the performance is critical and for the other (possibly small) deployments for which the administration aspect wins. To facilitate this, there is a need for the optional parameter for each configured address pool which would indicate whether the pool supports a "relaxed" or "strict" allocation. The allocation engine would check this parameter for the pool during allocation of a new lease. If the pool is in the "relaxed" allocation mode the allocation engine would have to use HR database to check for the conflicts. If the pool is in the "strict" mode, the server would assume that no reservations are made for the addresses in this pool and would skip the query do the HR database. In the latter case, the server must check the host reservations for conflicts with the pools during the configuration, and reject the configuration if conflicts exist. This check is not needed for pools using a "relaxed" mode as appropriate checks are performed for individual addresses during lease allocations.
Implementation Note (September 30th, 2016): "relaxed" approach is the only one supported by Kea 1.1.0.
Compatibility with future configuration extensions
The design described in this document proposes that the HR information can be stored in the database. In the future it is possible that other types of configuration information may need to be stored in the database for similar reasons. In particular, some installations use large number of subnets and the information about them may well fit in a database, rather than the configuration file. Some type of information required for subnet definition is similar to the definition of the HR, e.g. DHCP options to be sent to a client. So, any data layout defined for HR should take into account that future extensions may need the same or similar type of information and that this information should be easily accessible.
The HR is not meant for temporary addresses
Because of the nature of temporary addresses (carried in IA_TA) assignment, there is no requirement for Kea to use the HR mechanisms described here for any kind of reservation of temporary addresses. Also note that Kea doesn't support processing of IA_TAs at the moment.
Integration of HR into DHCP Message Processing
The HR mechanism provides new sources of information about the addresses/prefixes to be allocated for the clients. It brings additional overhead for the server to assess if a client should be allocated a specific address/prefix or that the dynamic allocation should be in place. The overhead is unavoidable, but the HR implementation is expected to minimize this overhead. Most of the concerns about the performance degradation are related to the use of SQL database which is often a bottleneck for the DHCP message processing by the server. Hence, it is generally expected that the number of queries to the database will be minimal.
Dealing with Conflicts
When the server assigns a reserved resource for the client it is possible that this resource is in use by another client. Typically, it is a result of server's misconfiguration. For example, an administrator might have had a pool of dynamically allocated addresses from which one was assigned to client A. Later on, the administrator decided to take out a subset of addresses from the dynamic pool and statically assign them to selected clients (make reservations). The administrator might have configured an address in use by the client A to client B. When client B requests address allocation, the server can't assign the reserved address to this client because it is in use by client A. There are two possible approaches that the server may follow in this case:
Reject the allocation of the address to client B as long as client A holds an address reserved for client B (e.g. send !NoAddrsAvail status code in DHCPv6 or DHCPNAK in DHCPv4).
Allocate different address than reserved for client B for a period of time when client A is using reserved address.
In both cases, the server recovers from the conflict when client A renews a lease. The DHCPv6 server may allocate a different address for client A and return the address reserved for client B with lifetimes of 0, to indicate to the client A that it should stop using the address. In case of DHCPv4, the server may return DHCPNAK to force the client to request a new address allocation and assign a different address. The reserved address becomes available for client B.
Both approaches are applicable in different situations. The former is applicable in cases when the administrator is under a strict constraint that the client must not use address other than reserved, e.g. the administrator uses the HR mechanism for confining or segregating clients. The latter is applicable when the administrator desires that clients are provisioned (with temporarily assigned addresses) during reconfiguration of the pools, HRs etc. and when there is a risk of conflicts between statically and dynamically assigned addresses.
As it is impossible to know which of the two approaches the administrators would prefer in their specific deployments, the design assumes that Kea should provide a configuration parameter to choose one or another behavior. However, the flow charts and their descriptions use the latter case as this case seems to be more complex. In addition, it is easier and more natural to implement the latter approach and fall back to the former approach when the configuration switch is created; not the other way around.
The default server's behavior should be to reject the allocation of the address for the client if the address being reserved for this client is in use. A clear warning message should be displayed when the configuration is changed to act otherwise.
Implementation Note (Feb 9th, 2016): As of Kea 1.1.0 the servers will allocate a different address for a client having a reservation for an address currently assigned to a different client. When this different client renews it is NAKed and obtains a different address. The client for which reservation exists will be allocated reserved address when it renews. The "default" is not implemented in Kea 1.1.0.
Multiple IPv6 reservations
A DHCPv6 client (which can also be a requesting router per RFC3633) uses IA_NA and IA_PD options to request creation of the bindings by the server. A binding is indexed by a group of DUID, IA-type, IAID values. A binding may include a single or multiple addresses/prefixes. A client may request a single binding or multiple bindings in the same DHCP message. The server crates bindings and assigns addresses or prefixes to the bindings according to the server's configuration.
Host reservation mechanism designed in this document allows for creating reservations for multiple IPv6 addresses and/or prefixes (leases) for the same client. These leases are assigned to the bindings and returned to the client. An interesting question is how the server should distribute assigned leases across the bindings it creates for the client. Some interesting questions we should ask are:
how does the server distribute leases across IAs when the number of IAs sent by the client is lower than the number of reservations?
how does the server distribute leases across IAs when the number of IAs sent by the client is greater than the number of reservations?
There are multiple solutions for this and it often depends on the requirements of the particular deployment. In this section we're focusing on describing a "default" behavior for the server having to deal with multiple reservations to be assigned to a variable length of IAs sent by the clients. Non-default behaviors may be determined in the future and the appropriate configuration parameters will have to be introduced accordingly. However, these are out of scope for Kea 1.1.0.
The following table presents examples of the server behavior when it deals with different number of reservations and IA_NAs.
||= Number of reservations =||= Number of IAs =||= Action =||
|| 2 || 1 || Assign first reservation to the IA. If that fails, try to assign second reservation to the IA. ||
|| 6 || 3 || Assign three reservations, each to a different IA. ||
|| 1 || 2 || Allocate reserved address to the first IA, allocate address dynamically to the second IA ||
|| 2 || 8 || Allocate first reserved address to the first IA, second reserved address to the second IA, assign 6 addresses dynamically to the remaining 6 IAs ||
|| 2 || 2 || Allocate each reserved address to a different IA ||
|| 3 || 2 || Allocate first reserved address to the first IA and second reserved address to the second IA. ||
|| 5 || 2 || Allocate first reserved address to the first IA and second reserved address to the second IA. ||
The IA_PD processing is independent from IA_NA processing but it follows the same pattern.
In some cases an allocation of a reserved lease may fail, because the reserved lease may be currently assigned to another client. The server is prepared to recover from this situation but it requires that this client tries to renew the lease and the server can assign a different (unreserved) lease to this client, thus releasing the lease for the client which has a reservation for it.
If the allocation of the reserved lease fails and the client has multiple reservations, the server will try to allocate a next reserved lease to the currently processed IA. If that fails, it will try to allocate next reserved lease and so on. In this situation, if the server runs out of reserved leases, it will try to allocate an unreserved lease (dynamic allocation) to the IA.
The allocation process of reserved leases (addresses or prefixes) for the Request and Solicit messages is depicted on the picture below.
IA options of a specific type are processed sequentially by the server. They are typically processed in the order in which they are transmitted in a DHCP message sent by a client. If the client has reservations, the server will try to allocate reserved leases in the first place, and falls back to dynamic allocation if there are no more reservations available for an IA. When the server is processing a particular IA it iterates over all reservations for this client in the order in which they are stored in the ''Host'' object. For each reservation it checks if a valid, non expired lease exists in the lease database. If there is no lease or the lease is expired it indicates that neither our client nor any other client is using this reservation. The server would simply allocate this lease for our client.
If the server finds a non expired lease in the database for a given reservation there are several cases we to take into consideration:
the lease belongs to another client and can't be allocated to our client, in which case the server should try another reservation,
the lease belongs to our client but is assigned to another IA (identified by IAID), in which case the server should try another reservation assuming that the lifetime of the lease found will be extended during processing of another IA,
the lease belongs to our client and the IAID matches, in which case the lease lifetime should be extended.
Note that the server has to iterate over client's reservations for each processed IAs until it finds the reservation that can be allocated. However, this should not cause significant performance degradation because reservations for a host are retrieved once and they are held in memory. The more problematic seem to be a need to perform repeated queries to the lease database for each reservation for which the server is trying to allocate a lease. This problem can be mitigated by the introduction of a lease caching mechanism, which would be used to hold returned leases in memory throughout processing of entire DHCP message. This mechanism is out of scope for this design. The team should have a discussion whether it is worth to provide some generic caching mechanism to all lease database backends that potentially require it.
When the server is processing an IA and determines that none of the reservations could be allocated to this IA (perhaps they have been assigned to previously processed IAs), the server falls back to the regular lease allocation, as if no reservations were specified for the client. In this case, the server will first try to allocate a lease using a client's hint. If this fails, the server will try to allocate a lease from the dynamic address/prefix pool.
DHCPv6 Renew / Rebind Processing
When the server tries to allocate reservations to the respective IAs, the server will generally pick the reservations in the order in which they have been returned by the host data source, i.e. the order in which they have been specified in the configuration file or in which the SQL database queries ordered those addresses. As a result, if the client keeps sending the same IAs to the server in the same order, the reservations will be allocated to the same IAs during first allocation and subsequent renewals. However, there are cases when this order is not preserved, e.g. client added new IAs, removed some IAs and/or changed their order. Another case is that the server has been reconfigured and the order of the reservations has also changed. When the server receives a message from a renewing client, the server should first check if any of the allocated leases remain reserved for this client and can be renewed for the particular IA. This avoids the situation that the server reallocates all reserved addresses to different IAs simply because the order of the IAs have changed in the client's message.
DHCPDISCOVER and DHCPREQUEST Processing with HR
The client uses DHCPDISCOVER message to locate the DHCP server(s). The server responds with DHCPOFFER and offers an address for the client. If there is a static address reservation for this client, the server will offer the reserved address. In case of no reservations, it will dynamically pick the address to offer. The server may use a hint sent by the client to allocate a specific address. The reserved address always takes precedence over a hint. If the client is interested to obtain the offered address from the server it will send a DHCPREQUEST message and fill in "requested IP address" option with the offered address. The DHCPREQUEST message is also used in cases when the client has obtained an IPv4 address and wants to extend its lifetime, i.e. client in a renewing or rebinding state. Such client will set ciaddr field of the DHCPREQUEST message to indicate the address it is trying to renew.
The diagram below shows a very simplified processing model of DHCPREQUEST messages when HR is in use.
The server receiving a DHCPREQUEST message expects that the "requested IP address option" or ciaddr is set (depending on client's state). The server needs to obtain host reservation (if one exists) for the client sending a DHCPREQUEST message to validate that the client's notion of the address is correct. This is done in the '''Query for Host Reservation for the Client'''. The server will use the HR data source (e.g. MySQL) to retrieve this information. However, if the client returns after sending a DHCPDISCOVER or it is a renewing client, the server may already have this information cached during previous message processing.
If a reservation for the client exists, the server expects that the client has sent reserved address in "requested IP address option" or "ciaddr" because the server likely sent reserved address in the previous DHCPOFFER message or already allocated this address for the client and the client is renewing the lease. The comparison of the address supplied by the client and the reserved address is performed in the '''Compare supplied and reserved address'''. If it turns out that the client has sent a different address (e.g. as a result of race condition when the reservation was made after the DHCPOFFER) the server will respond with a DHCPNAK. This forces the client to start the new exchange with a server to obtain an address. As a result, the client should be able to obtain a correct (reserved) address.
If the address supplied by the client equals the reserved address, the server will use this address for lease allocation or to extend an existing lease (depending on the client's state). The server must check if the address to be allocated is not in use by other client as a result of misconfiguration. If the client is renewing an existing lease and the reserved address is in use, the server will send a DHCPNAK as it is unable to fulfill the client's request to extend the lease lifetime. However, if the client is requesting a new allocation and the reserved address is in use, the server will try to allocate a different address (either the one given as a hint or other) using a normal allocation process. The server should issue a warning at this point that it was unable to allocate reserved address as it is in use by other client (misconfiguration).
Options assignment in DHCPv4 and DHCPv6
In Kea 1.1.0 it will be possible to associate specific DHCP option values with a reservation. It is then possible to provide specific option values exclusively for the client. These values take precedence over the values specified on the subnet or global level, i.e. if the same option is specified in the reservation and on a higher level, the value from the reservation will be sent to the client. Other clients connected to the same subnet will receive options specific to the subnet. If option specified on the subnet or global level is not specified in the reservation, the value specified on the subnet or global level is sent. In other words, the client will receive options specified on all configuration levels, but those specified on the reservation level will supersede respective options specified on higher levels.
The servers' code will have to be modified to retrieve a pointer to the ''!CfgOption'' object associated with a reservation for a given client (assuming that such reservation exists). Options stored in this object will have to be added to the DHCP response first. Secondly, the server will have to retrieve options held in the ''!CfgOption'' object associated with a subnet the client belongs. The server will iterate over subnet specific options and add only those that are not already present in the response message. Then the server will process class specific options and global options.
Information stored for HR
The following type of information needs to be stored for each host reservation, either defined in the database or configuration file. However, some information is optional when defining a new HR. If it is not specified, the NULL value (or any other special value meaning "unspecified") is stored.
||= Type =||= Optional =||= Description =||
|| unique identifier || NO || A binary value of a unique identifier of a particular host for which reservation is made. This may be one of the supported identifiers (see "unique identifier type"). ||
|| unique identifier type || NO || An enumeration specifying a type of the identifier being used to identify a client. Currently supported identifier types are: HW addresss, DUID. In the Kea 1.1.0 release we are planning to introduce: Circuit ID, Remote ID, Subscriber ID ||.
|| subnet || NO || A subnet to which the client is connected. Subnet can be identified by a unique number across all subnets defined for a server (subnet identifier). This identifier is used in the database as well as in the data containers holding information about HR in memory. Note that the subnet identifier number space overlap for IPv4 and IPv6 subnets, so it is possible that there are two subnets for a given host with the same subnet id, one for IPv4 and one for IPv6. ||
|| IP address(es) || YES || One or more IP addresses reserved for an exclusive use of the host (client) identified by a particular hardware address, DUID or other unique identifier. Note that for a particular host there may be only one IPv4 address reserved and multiple IPv6 addresses/prefixes. ||
|| Prefix(es) || YES || One or more IPv6 prefixes reserved for an exclusive use of the host and handed out to the client using the prefix delegation mechanism. ||
|| IAID(s) || YES || One or more IAIDs (carried in IA options and generated by a client) for which the addresses and prefixes are reserved. If IAID is unspecified for the reservation, any IAID is allowed. If IAID is specified for the reservation, the value of IAID sent by the client must match the IAID for which the particular reservation is made. If there are no addresses assigned to the IAID in the host reservation entry, the addresses for the IA carrying the IAID are not assigned. ||
|| Hostname || YES || A name reserved for the particular host. Hosts may send names in the Client FQDN option to the server but server may pick different name on its discretion. This parameter holds the arbitrary name selected for the client. ||
|| Client classes || YES || Class identifies a group of hosts for which DHCP messages should be processed in some special manner. For example, a set of hosts belonging to a class may receive some additional DHCP options which hosts that do not belong to this class will not receive. A client may belong to multiple classes. ||
|| Options || YES || A collection of DHCP options to be sent to a client having one or more host reservations. The information for each option should be sufficient to create and send the option to the client, so it should include: option code, option data, encapsulated options, a flag indicating whether the option should be sent when requested or regardless if requested. ||
Configuration file format
The host reservations are specified on per-subnet basis, i.e. the reservation is created for a client which belongs to a particular subnet. If the client is connected to a different subnet the server will not allocate the reserved address to this client. Instead, the server may choose to dynamically allocate the address to the client. When the client moves to the subnet for which the client has a reservation, the reserved address(es) will be handed out. The following configuration fragment shows an example IPv6 subnet configuration, including a couple of reservations for different clients.
In the example above, there are three sample reservations specified. The first of them demonstrates the reservation of multiple IPv6 addresses for a host identified by its MAC address. When the server receives a request from the client having this MAC address and connected to the subnet "2001:db8::/48", the server will allocate given two addresses for this client. The server will include the "domain-name-servers" option (among other options, e.g. global or subnet specific) in its response to the client. If the client is requesting an update to DDNS, the server will send back the name "super-host.isc.org" in the Client FQDN option.
The second reservation specifies that two prefixes and one address will be allocated to the client identified by the given MAC address. Note that special notation is used for the second prefix: "2001:db8:2000:0102::/64@123" in which the value of "@123" restricts that this prefix can only be handed out in the IA_PD having IAID=123. If such IA_PD is not present in the message from the client, this prefix will not be allocated. The allocation of the other prefix and IP address is not restricted to any specific IAID so it will be handed out in any IA_PD and/or IA_NA sent by a client. Obviously, no prefix/address will be assigned to a client which didn't send any IA_PD/IA_NA option. The "client-classes" parameter specifies a list of classes that the client belongs to. This client will receive options defined for clients belonging to these classes.
Implementation Note (Sep 30th, 2016): grouping reservations to specific IAIDs is not implemented in Kea 1.1.0.
The third reservation demonstrates that resources other than IP address or prefix may be assigned to the client and that the assignment of IP address or prefix is not mandatory. The third reservation specifies that the server will use the "another-host.isc.org" name for the host having a specified DUID. This client will also receive options reserved for a group of hosts belonging to class "class-foo" (assuming that the options for this class have been specified elsewhere in the configuration). Note that since the last example doesn't specify reservations for addresses or prefixes, if the client has requested assignment of any of them (by sending appropriate IA options), the server will dynamically allocate these resources using a normal allocation process.
Note that if the reservation specifies that the client belongs to multiple classes it is possible that some of the classes may specify overlapping options (the same option codes). This case is handled by the section 22. of RFC3315 which explicitly allows for occurrence of multiple instances of the same option. However, it is a responsibility of the server administrator to ensure that overlapping options should/can be sent to the client. If not, the classes and corresponding options need to be redefined.
One final note about the use of MAC addresses for reservations in DHCPv6. The base DHCPv6 specification doesn't support sending the MAC (or any other link layer) address in the DHCP messages. If the server receives a message from a directly connected client, the server could extract the MAC address from the received packet. However, OS kernel's API doesn't provide means to retrieve this kind of information from the packet. If the client is behind the relay, the relay can retrieve this information and send to the server in the DHCP option. However, the issue with lacking API also applies to a relay. There is an ongoing effort to implement various mechanisms to retrieve the MAC address from the DHCP messages received from the clients, but none of these solutions is fully reliable. Hence, the administrator of the server must take into account what capabilities are currently supported by the server and relays that he is uses, and not use MAC address-based reservations if these capabilities are insufficient.
This section presents the configuration format for defining reservations in IPv4 domain. This is similar to the IPv6 reservations demonstrated above, but with the following differences:
Only one IPv4 address can be reserved for a client in a particular subnet. Although, it is possible that the administrator specifies two reservations: one for the DUID, another one for the MAC address which happen to belong to the same client. This is considered misconfiguration but the server has no means to verify this until it actually receives a DHCP message which holds both a DUID and MAC address for which reservations have been made. This should result in a warning. Similar issue applies to IPv6 reservations.
Prefix delegation doesn't apply to IPv4, so the "prefixes" parameter is not supported.
An example host reservation configuration looks like this:
Note that reservations by DUID are also supported as specified in RFC4361. The DUID value is specified in the same way as for IPv6 reservation.
In addition to ''hw-address'' and ''duid'' new identifiers to be supported in Kea 1.1.0 release are:
''client-id'' (option 61) - specifies client identifier as defined in Section 9.14 of RFC2132 without option code and length. Note that DUID is a special type of client identifier (for Type = 255). Also, Type field value is not included in the "duid" parameter value.
''circuit-id'' (DHCP Relay Agent Sub-Option 1) - a value carried in Circuit-ID option inserted by a relay, specified in a textual or hexadecimal format.
''subscriber-id'' (DHCP Relay Agent Sub-Option 6) - a value carried in Subscriber-ID option inserted by a relay, specified in a textual format.
''remote-id'' (DHCP Relay Agent Sub-Option 2) - a value carried in the Remote-ID option (excluding option code and length) inserted by a relay agent, specified in a textual or hexadecimal format.
Identifier value format
Identifiers supported in Kea 1.0.0 are specified in hexadecimal format (as a JSON string of hexadecimal digits). Hardware addresses also include colon signs separating each octet of an address, following the standards of MAC address notation.
In Kea 1.1.0 release we're going to extend the set of supported identifiers and some of the new identifiers may be coded both in hexadecimal format or as an ASCII string (textual format). Use of textual format is convenient for the administrators, because it allows for using human friendly identification of devices, links etc. For example, a circuit may be named "circuit-no-1234", which can be represented as the following string of hexadecimal digits "636972637569742d6e6f2d31323334". Use of hexadecimal notation in host reservation is not only inconvenient but also error prone. Thus, textual notation should also be possible when creating reservation using specific identifiers. The design doesn't restrict identifiers for which textual notation is allowed, i.e. it should be even possible to specify "hw-address" using textual notation. In this case, however, colons should not be used between characters representing respective octets of the MAC address.
In order for the server to know whether the value specified is a textual or hexadecimal representation the textual values must be enclosed in single quotes (in addition to double quotes required by JSON format):
Note there are no single quotes in the latter case!
Order of identifiers
Server receiving a packet from a client needs to check if the client has any reservation. Typically, a server administrator would prefer one selected identifier to associate a client with its static reservations. MAC address is one of those commonly used identifiers. However, in the fixed infrastructures, e.g. cable networks, it is often better idea to use circuit-id (or other similar identifier), as it identifies the link to which the particular device is connected and it doesn't change when the device is replaced.
In general, a server administrator can use different identifier types for different reservations and the server has no means to know which identifier was used for the client which packet is being currently processed. By default, the server will iteratively search for the client's reservation using every supported identifier in some specific order. This may be expensive operation resulting in multiple queries to host database for each packet and causing server performance degradation.
To facilitate the case when an administrator uses limited number of identifiers (typically one) to create reservations, it must be possible for the server to learn which identifier types are in use and only use those identifiers to search for the reservations. Two approaches were taken into account:
automatic discovery: when the server starts up (or is configured) it queries hosts database to obtain a collection of identifiers in use,
explicit configuration: administrator specifies a list of identifiers in the specific order of preference in the configuration file.
The former is convenient for the administrator as he/she doesn't need to specify any extra configuration to optimize reservation search efficiency. However, this approach adds complexity to the host data sources (need for new queries), needs to make queries to gather list of identifier types at the cost of slower server startup and reconfiguration and doesn't guarantee that the identifier types are returned in the order of preference.
The "explicit configuration" approach requires an administrator to manually configure the list of supported identifiers in order of preference. The administrator has full control which identifiers are used by the server and in what order. This includes ability to explicitly disable searches using some identifier(s), even if reservations for this identifier exist.
The "explicit configuration" has been selected for implementation in Kea 1.1 because of lower complexity and more flexibility. Nevertheless, the "automatic discovery" is still perceived as a useful extension, which can be added in a future release, as it may work pretty well in situations when there is a limited number of identifiers in use (e.g. one), in which case the order in which they are used doesn't matter.
The ordered list of identifiers to be used by the server will be specified with a new global configuration parameter "host-reservation-identifiers". For example:
In this example the server processing a packet will first use circuit identifier inserted by a relay agent to find a static reservation for a client. If no reservation is found, the server will search for a reservation using client's MAC address. If no reservation is found, the server will process the packet as if the client has no reservation.
Values specified in the list match the names of respective parameters in the "reservations" list.
The same parameter and syntax is used in the DHCPv6 server configuration. However, a set of allowed values differs from DHCPv4 as it may contain some DHCPv6 specific identifier types.
The "automatic discovery" will be enabled using the same parameter:
Until "automated discovery" is implemented, the use of "auto" keyword is not allowed.
MySQL hosts database
MySQL database schema
Kea should support any custom storage for host reservations, but typically host reservations will be stored in an SQL database. The first implemented lease database backend used MySQL to store leases, so it seems natural that the first backend for storing host reservations should also use MySQL. This way we create no new dependencies for users already using MySQL for leases. Although, the schema presented here is created specifically for implementation of the MySQL backend, the schemas for other types of SQL databases will be very similar or the same.
The Kea 1.1.0 MySQL schema is depicted on the picture below.
The ''hosts'' table is a "central point" of information about reservations. It combines the information about both IPv4 and IPv6 reservations as in dual stack networks it is often important to be able to correlate information about the DHCPv4 and DHCPv6 clients (e.g. using MAC address, DUID). For example, an administrator may want to view full information about the host connected to his network, including all type of resources allocated to this host, e.g. IPv4 address, IPv6 addresses and prefixes. Another use case is that an administrator may want to assign the same hostname when responding to the DHCPv4 and DHCPv6 queries from the same host and send DNS updates accordingly.
The following naming convention is proposed for the table columns:
A name prefixed with ''dhcp_'' indicates that the value held in this field is specific to the DHCP protocol or its implementation and may refer to both DHCPv4 and DHCPv6. For example, the ''dhcp_identifier'' is one of the following: MAC address or DUID, and may apply to DHCPv4 and DHCPv6 clients. The prefix also indicates that the identifier is NOT an identifier created solely for the purpose of creating relations between tables. The identifier used for creating relations is ''host_id'' which doesn't have any meaning for the DHCP protocol's operation. Moreover, it is not even returned by the SELECT queries to the server.
A name prefixed with ''dhcp4_'' or ''dhcp6_'' refers to the value which is specific to the DHCPv4 or DHCPv6 protocol or its implementation respectively. For example: the ''dhcp4_subnet_id'' is a unique identifier identifying a subnet and is generated by the DHCPv4 server code, not by the database.
A name which has a postfix ''_id'' holds an identifier of row in the database which is used to create relations between tables.
DHCPv6 protocol allows for allocating multiple IPv6 addresses or/and prefixes in a single session. Moreover, these resources may be sent in the same or distinct IA options. Although, in most common scenario a client would only get one IPv6 address at the time, it is very common that the client gets an address and prefix in a single Reply message from a server. This is reflected in the database schema as a 1:n relation between the ''hosts'' and ''ipv6_reservations'' tables and the ''host_id'' is a primary and foreign key for this relation. Note, that this is an ''Indentifying Relation'' which means that the entry in the ''ipv6_reservations'' doesn't make sense without a corresponding entry in the ''hosts'' table because this table contains a unique identifier of the client (MAC address or DUID). This in turn implies that removal of the host from the ''hosts'' table should trigger a removal of all corresponding entries in the ''ipv6_reservations'' table. Kea 1.1.0 uses the following trigger to remove IPv6 reservations belonging to a removed host.
CREATE TRIGGER `host_BDEL` BEFORE DELETE ON hosts FOR EACH RAWBEGINDELETE FROM `ipv6_reservations` WHERE `ipv6_reservations`.host_id = OLD.host_id;END
However, the ticket #4521 proposes a more generic approach using CASCADE DELETE, which will replace this trigger.
The detailed description of each column of the ''hosts'' and ''ipv6_reservations'' tables is presented below.
||= Column name =||= Description =||
||||= hosts =||
|| host_id || Unique identifier of the host entry in the database; an auto-incremented primary key for this table. ||
|| dhcp_identifier || Holds the value of the identifier used to identify the host. At present it may be one of the following: hardware address or DUID. ||
|| dhcp_identifier_type || Indicates a type of the identifier: 0 (hardware address), 1 (DUID). This value must not be NULL. ||
|| dhcp4_subnet_id || IPv4 subnet identifier. Note, that each server (DHCPv4 and DHCPv6) generates subnet identifiers on their own and the generated identifiers may overlap. This column holds identifier of the IPv4 subnet to which the client is connected. This value may be NULL if the host is not connected to any IPv4 subnet or if there is no IPv4 reservations for this host, in which case there should be some IPv6 reservations. and the ''dhcp6_subnet_id'' should be NOT-NULL. ||
|| dhcp6_subnet_id || IPv6 subnet identifier. This is similar as ''dhcp4_subnet_id'' above, but for IPv6 subnets. ||
|| ipv4_address || IPv4 address reserved for this host. This address is handed out in response to the DHCPv4 client contacting a server from the subnet identified by ''dhcp4_subnet_id''. This value must be NULL if ''dhcp4_subnet_id'' is not specified. A NULL value indicates that there is no IPv4 address reservation for the host. The host may still have IPv6 reservations. ||
|| hostname || Hostname assigned to the client. It may contain a single label, e.g. "host-foo" or an FQDN, e.g. "host-foo.example.org". If only a single label is specified, it will be appended with a domain name specified for a given subnet. This value is host specific and is handed out to both DHCPv4 and DHCPv6 clients contacting the server. A NULL value indicates no reservation for the hostname. ||
|| dhcp4_client_classes || A comma separated list of classes to which the DHCPv4 client is assigned. In the future, it will be possible to specify a collection of options that the group of clients of a particular class will be given. A NULL or empty value indicates that the client doesn't belong to any class. ||
|| dhcp6_client_classes || See above for ''dhcp4_client_classes''. ||
|| dhcp4_next_server || An IPv4 address to be included in the siaddr field of the DHCPv4 message sent to a client. ||
|| dhcp4_server_hostname || Server hostname to be included in the sname field of the DHCPv4 message sent to a client. ||
|| dhcp4_boot_file || Boot file name to be included in the file field of the DHCPv4 message sent to a client. ||
||||= ipv6_reservations =||
|| reservation_id || Unique identifier of the reservation in the database; an auto-incremented primary key for this table. The values stored in this column are not used by the DHCP server but may be used by the third party updating the reservations for a host to refer to a specific reservation. ||
|| address || Address or prefix being reserved. This is represented as a string value in the canonical address format. ||
|| prefix_len || Length of the prefix. The default value is 128 which is used for addresses. The allowed range for this value is 0..128. ||
|| type || Type of the reservation. A value of 0 is IPv6 address reservation, a value of 2 is IPv6 prefix reservation. ||
|| dhcp6_iaid || This value is only used for reservations for the addresses or prefixes to be handed out to the client in the IA options with specific IAIDs. The IAID field in the IA_NA or IA_PD option is generated by the client. If known in advance, it is possible to specify that the particular address or prefix is only assigned if the client sends the IA option with this IAID. Otherwise, the reserved address or prefix is not sent to a client and client may obtain a dynamically allocated address or prefix. The NULL value of this field indicates that the reservation is not bound to any IAID and will be handed out to the client regardless of the value of the IAID sent in IA option. ||
|| host_id || Foreign key which links the reservation with the particular host in the database. It is also used by the trigger which deletes reservations for the hosts being deleted from the ''hosts'' table. ||
The schema contains two additional tables for storing DHCPv4 and DHCPv6 options respectively. The options are spread in two different tables because the subnet identifiers, being the keys for matching the options with an appropriate host, are specific to the protocols in use (DHCPv4 or DHCPv6). Previously considered solution was that DHCPv4 and DHCPv6 options are combined within the same table. But, this would require specifying both subnet_id and the option space value (e.g. "dhcp6 or "dhcp4") in SELECT queries to the database. This solution has this downside, that apart from the top level option spaces: "dhcp4" or "dhcp6", Kea defines option spaces for sub-options which names are not specific to any of those two protocols and it would be impossible to differentiate v4 and v6 options belonging to these option spaces. Hence, having two tables seems to be a reasonable approach. Also, note that most of the queries will be gathering only v4 options or only v6 options, not both.
The table below presents the descriptions of the columns in the ''dhcp4_options'' and ''dhcp6_options'' tables.
||= Column name =||= Description =||
|| option_id || Unique identifier for the entry in the table; an auto-incremented primary key. ||
|| code || Option code. Depending whether it is a DHCPv4 or DHCPv6 option, this value is 1 or 2 bytes long. ||
|| value || Binary data holding an option payload. This data is not formatted according to the structure of the option. It is just raw data which can be sent directly over the wire. This value must not be specified if the ''formatted_value'' is specified. ||
|| formatted_value || A string containing comma separated values which should be assigned to corresponding fields in the DHCP option. For example: if an option comprises a 1-byte long value followed by the IPv4 address, the formatted option data may look similar to this "10, 192.168.0.1" where the number of 10 will be stored in the 1-byte long option field, and the ip address will be stored in the second field. Note that the server should validate whether the option values specified as a formatted string adhere to the option definitions defined in the Kea configuration file or predefined (hardcoded) option definitions for standard DHCP options. This value must not be specified if a binary value for the option is specified in the ''value'' column for the option. Also, it is important to note that usually it will be better (from the performance standpoint) to specify a binary value for the option because it can be directly used to "put an option on the wire". However, if the caching mechanism is implemented for options stored in the MySQL, each instance of the option will be created once and stored in the server process's memory, so the performance impact may be acceptable. ||
|| space || Option space name. For the top level options it will be "dhcp4" or "dhcp6". For encapsulated options it may be any other name. Please see [http://kea.isc.org/docs/kea-guide.html#dhcp4-option-spaces Nested DHCPv4 Options] and [http://kea.isc.org/docs/kea-guide.html#dhcp6-option-spaces Nested DHCPv6 Options] in [http://kea.isc.org/docs/kea-guide.html Kea ARM] for the detailed description of option space use in Kea. ||
|| persistent || A boolean value which indicates if the particular option should always be returned to the client or only if the client requested the option using ORO or PRL option. ||
|| host_id || A host identifier referring to the ''host_id'' field of the ''hosts'' table. This field matches the option instance with a host reservation. ||
|| scope_id || An identifier of the scope for the given option: global, subnet specific, class specific or host specific option. The value of '3' is used for host reservations. ||
The ''dhcp_option_scope'' table associate option scope identifiers with their names. This table is not used in queries issued by the host data source. It is meant to be used in queries issued by management tools when displaying associations of options with scopes.
The MySQL script for creating a database schema proposed above can be found in Kea sources, under src/share/databases/scripts/mysql/dhcpdb_create.mysql.
Queries to MySQL hosts database
Performance measurements of Kea DHCP server using MySQL lease database indicate that the number of queries sent to the database for a processed message is one of the most important factors impacting server's performance. To minimize the impact of host reservation mechanism on the servers' performance, the implementation must gather all required data for a processed packet in a single query (rather than multiple queries). Experiments conducted as part of this design show that even a complex query would typically be more efficient than multiple simple queries gathering the same data. In this section we present examples how to retrieve full information about host, reserved options and IPv6 reservations for using a single query to the MySQL database.
The following is the simplified query which retrieves information about multiple hosts, their IPv6 address/prefix reservations and assigned options from three tables using "LEFT JOIN" condition:
SELECT DISTINCT h.host_id, HEX(h.dhcp_identifier) AS dhcp_identifier, h.dhcp_identifier_type, h.dhcp4_subnet_id, h.dhcp6_subnet_id, INET_NTOA(h.ipv4_address) AS ipv4_address, h.hostname, o.code, o.formatted_value, o.space, r.host_id, r.address, r.prefix_len, r.type, r.dhcp6_iaidFROM hosts AS h LEFT JOIN dhcp6_options AS o ON h.host_id = o.host_id LEFT JOIN ipv6_reservations AS r ON h.host_id = r.host_idWHERE h.dhcp6_subnet_id = ?ORDER BY h.host_id, o.space, o.code, r.address, r.prefix_len;
which could return a result similar to the following:
''Note that some columns were removed from the results for clarity! ''
The typical query used by the server would limit the results returned by additionally filter them using suitable identifier and identifier type. In such case the host_id field would contain the same value for all returned rows. However, it is useful to not limit the results here to show how the returned data is organized. All queries used by the server would derive from this example query.
For any single combination of ''dhcp_identifier'', ''dhcp-identifier-type'' and ''dhcp6_subnet_id'' there should be exactly one host in the database. The number of rows returned from this query for a particular host is thus a multiplication of number of IPv6 reservations and options assigned to this host instance because LEFT JOIN condition returns all possible combinations of host, reservations and options. If there are no options assigned, the number of rows returned for the host is equal to the number of reservations for the host in the ''ipv6_reservations'' table. Similarly, if there are no IPv6 reservations, the number of rows returned is equal to the number of assigned options. If there are neither options nor reservations for a host, a single row is returned for a host. A total number of rows returned is a sum of rows returned for all hosts.
The rows returned are ordered by unique host_id. In this case all rows containing host_id value of 1 pertain to a single host.
MySQL host data source has to parse results returned and create a ''Host'' object holding pointers to the collection of associated IPv6 reservations and options. Let's follow the example above to determine how host datasource would process results returned.
Host datasource iterates over rows using the same order in which they are returned. When the data source determines that the host_id of a currently processed row differs from the host_id of a previous row (or it is a first row in results) it creates a new Host object for currently processed host_id. If there is no change in host_id, an already created Host object is retrieved and updated accordingly.
For any currently processed host the returned rows are ordered by option space and option code. For any given host there might be multiple rows containing the same option data. The number of rows containing the repeated option information is equal to the number of reservations for a host. Host data source creates a new option instance only when it finds that the current option space/code tuple is different than the option space/code for a previous row processed. The host data source will further assume that only a couple of rows which contain definition of a first option contain all IPv6 reservations. When processing further options (e.g. option code 11 for host_id=1) the host data source can skip processing reservation data.
Note that, if there are no options specified for a host a NULL value is returned for option code. In this case, all returned rows contain respective IPv6 reservations. Similarly, if no reservations exist but there are options, all processed rows contain respective options.
The !HostMgr API facilitates retrieving a Host for which a specific reservation exist, i.e. ''Find host and its options, which has a reservation for a particular IPv6 address.'' The appropriate query requires a nested SELECT to first find a host_id for a particular reservation and then use this host_id as an input to the query above:
SELECT DISTINCT h.host_id, HEX(h.dhcp_identifier) AS dhcp_identifier, h.dhcp_identifier_type, h.dhcp4_subnet_id, h.dhcp6_subnet_id, INET_NTOA(h.ipv4_address) AS ipv4_address, h.hostname, o.code, o.formatted_value, o.space, r.host_id, r.address, r.prefix_len, r.type, r.dhcp6_iaidFROM hosts AS h LEFT JOIN dhcp6_options AS o ON h.host_id = o.host_id LEFT JOIN ipv6_reservations AS r ON h.host_id = r.host_idWHERE h.host_id = (SELECT host_id FROM ipv6_reservations WHERE address = ? AND prefix_len = ?)ORDER BY h.host_id, o.space, o.code, r.address, r.prefix_len;
PostgreSQL host datasource
The PostgreSQL database will use the same schema and queries to manage the host reservation information.
The ''!BaseHostDataSource'' defines an interface to be implemented by derivations providing access to sources of host reservation information. Typically, these sources are used to store and retrieve reservations from the server configuration or a database. The brief description of each method of the ''!BaseHostDataSource'' class is provided in the table below.
||= Method =||= Description =||
|| getAll || Returns a collection of hosts using specified identifier for all subnets. This method should not be called by the server processing a packet from a client because in such case the server would rather be interested in reservations for a particular subnet the client is connected to. The server should instead use get4 and get6 methods. This method should be used for the management of host reservations. ||
|| getAll4 || Returns a collection of hosts for which a specified IPv4 address is reserved. This should currently always return at most one host because Kea doesn't support the case when a given address can be assigned to different hosts connected to different networks. ||
|| get4(subnet_id, identifier_type, identifier_value) || Returns a host with an associated IPv4 reservation. It doesn't return IPv6 reservations associated with a host. ||
|| get4(subnet_id, address) || Returns a host and associated IPv4 reservation for a given subnet and address reserved. ||
|| get6(subnet_id, identifier_types, identifier_values) || Returns a host and associated IPv6 reservations for a specified IPv6 subnet and identifier type. ||
|| get6(prefix, prefix_len) || Returns a host and associated IPv6 reservation for a specified IPv6 prefix and prefix length. ||
|| add || Adds a new host. If this host already exists, it will not be added. ||
|| getType || Returns a type of the database. ||
|| commit || Commits the changes in the host reservation configuration to the storage. This method may be no-op for some backends. ||
|| rollback || Rollback changes to the host reservation configuration. This may throw !NotImplemented exception for some sources. ||
A DHCP server processing packet received uses ''get4'' and ''get6'' methods to retrieve reservations for a host in a particular subnet the client is connected to. Those methods allow for specifying one of the supported identifiers such as HW address, DUID, Circuit-ID etc. The server may be configured to use specific identifier or multiple identifiers extracted from the client's message to find a reservation for this client (see "Order of identifiers" above for details). If multiple identifiers are specified they are ordered by preference and the server should make multiple calls to ''get4'' or ''get6'' using this order. When the reservation is found, the server should not make any further calls for remaining identifiers and should simply use the one it has found. If a reservation does not exist for the client the client will make a call to ''get4'' or ''get6'' for each specified identifier type and will assume that there is no reservation when the last of these calls returns no result.
'''Important Note''': The current design is focused on retrieving existing reservations from the database. It does not describe the management system to be used to update reservations, add new reservations etc. It is assumed that the interface exposed by the ''!BaseHostDataSource'' may need to be extended in the (near) future to address the problem of updating partial information, e.g.:
Only add new option to an existing reservation
Remove specific options for the specified reservation
Remove selected reservation, but leave other reservations
Remove or update IPv4 reservation, but do not update the IPv6 reservations etc.
The ''!IdType'' enumerates supported types of identifiers which can be used to create reservations. Examples of such identifiers are: HW address, DUID, Circuit-ID. Note that some of the identifiers are applicable both for DHCPv4 and DHCPv6 and some identifiers are applicable only for one server type, i.e. Circuit-ID is a DHCPv4 specific option.
A ''Host'' class represents a host for which multiple reservations (IPv4 and multiple IPv6) can be made. It mirrors the information held in the SQL database for each host, in the ''hosts'' and ''ipv6_reservations'' tables. The ''Host'' class is also used to hold information about host reservation in the configuration file. ''Host'' instance holds a ''CfgOptionPtr'' pointer to a collection of options reserved for a client. These options take precedence over the options specified on a subnet or global level, i.e. if option value is specified in the reservation for the client and the same option with a different value is specified on any higher level, the value specified on the reservation level is sent to the client. The ''!HostContainer'' class holds a collection of ''Host'' objects, either gathered from the configuration file or cached from the queries made to the SQL data base.
The ''!CachingHostDataSource'' extends the abstract interface and implements mechanisms required to cache results of queries to the database. Any backend deriving from this class will be capable of caching queries. The maximum number of the cached reservations will be configurable. If this number is set to 0, no queries will be cached and the behavior of the backend will be the same as in case of non-cached queries. Any backend using SQL database as a storage should derive from the ''!CachingHostDataSource'' to inherit the mechanism of caching host reservations.
'''Note:''' The caching mechanism will be implemented later in the schedule, and the ''!CachingHostDataSource'' will initially provide a stub implementation only.
The ''!CfgHosts'' class derives from the ''!BaseHostDataSource'' directly and it doesn't use caching, because host reservations obtained from the configuration file are held in memory. The ''!CfgMgr'' holds a pointer to the ''!CfgHosts'' object. This pointer is used by the ''!HostMgr'' to retrieve reservations specified in the server configuration file.
''!HostMgr'' provides the API to retrieve the host reservation from multiple data sources. It derives from ''!CachingHostDataSource'' so it provides the same methods to modify and gather the host reservations as any data source. When accessor functions are called, i.e. ''getAll'', ''get4'', ''get6'', the manager will first try to search for the information in the in-memory data sources: ''!CfgHosts'' and then cached data. If the host reservations are not found, the MySQL data source will be used to obtain the information from the database.
When modifier functions are called on ''!HostMgr'', the updates are populated to the SQL data source.
'''Note:''' At later time we may need to implement additional parameter to select to which data source the updates are populated. Alternatively, the ''!HostMgr'' could implement a logic to query which data source has relevant data and use this source for the update.
Finally, the !HostMgr searches for the reservations specified in the configuration file and falls back to a SQL host data source if the reservation is not specified in the configuration file. We think that some administrators may prefer the opposite order, i.e. the reservation in the database overrides the reservation in the configuration file. However, using the opposite order yields performance penalty, because access to an SQL database is slower. Thus, we decided to wait if users ask for the possibility to reverse search order and implement suitable configuration parameters to allow that. Currently it remains out of scope for this document.
Host Reservations for DSL network
Note: This section is temporary. We recently decided to start supporting DSL networks. This section discusses work necessary to be conducted for to support the most urgent scenario - to make host reservations based on circuit-id.
Reservations by circuit-id in config file.
Extend Host class to store circuit-id (and possibly remote-id) values.
Extend HostReservationParser to be able to parse circuit-id (and possibly remote-id) values.
Extend HostMgr to be able to get hosts by identifier_type, identifier_value, e.g. get(subnet_id, identifier_type, identifier_value);
Extend AllocEngine::findReservation() with finding reservations of other types, in particular by circuit-id (and possibly by remote-id).
need to develop tests to test reservations by circuit-id (needs to simulate relays)
The following tasks are not urgent, but need to be completed in 1.1 timeframe:
Each additional reservation type supported has negative impact on performance, even if there are no reservations defined. The findReservation() method needs to search for reservations by hwaddr, by duid, by remote-id, by circuit-id etc. One way to mitigate this impact is to introduce a parameter that configures types of supported reservation. By default it would allow all reservations. This parameter could be tweaked by sysadmin if needed. It could be implemented similar to mac-sources parameter in DHCPv6: as an ordered list of preferences, in which reservation types should be checked.
MySQL backend is prepared for getting reservations by (identifier-type, identifier-value). Circuit-id is just another identifier type. It should be easy to add support for it in MySQL. this is the same for PostgreSQL.
Performance measurements how long it takes to start up with 1k, 2k and 20k leases.