====== Hsqldb Back-end ======
This is the default back-end for DHCP Cluster. It is based on "Hypersonic DB" which is a simple and very fast database in pure Java, suitable for small to medium size deployments.
This back-end allows for fast deployment of end-to-end DHCP solutions without installing or managing a separate database (MySQL, Oracle...).
This back-end offers high brute performance, but is not suitable for high-availability cluster solutions or highly scalable services.
==== Pros ====
* Pure Java module running in the same process as the DHCP server - simple to install and to manage - equivalent to many other free solutions
* Very fast database engine capable of managing requests in-memory; it can be typically 20x faster than classical databases
==== Cons ====
* database is not multi-threaded (yet multi-thread safe); this can become a bottleneck issue for very high performance servers
* there is no support for active high-availability modes allowing automatic failover from one server to another in case of hardware failure
===== Design decisions =====
The Hsqldb back-end is kept as small and simple as possible.
It is currently only used to keep track of client leases. All other configuration parameters are stored in a single XML file.
There is basically only one persistant table: T_LEASE, which stores usage of IP addresses.
Other non-persistent table are created in-memory for lease calculation and OFFER tracking:
* minimal topology description: T_POOL lists all dynamic address pools available for address distribution, all pools in the same network are linked through the T_POOL_SET table
* to accelerate calculation of free addresses in pools, a T_BUBBLE keeps track of blocks (bubbles) of free addresses in each pool. These bubbles listed through a linked list.
* offers are temporarily kept in the table T_OFFER which is memory-only (not persistent), since offers have only a 60 seconds life-time. A timed-out offer becomes a free bubble again.
===== Running modes and performance =====
During debug and development, it is interesting to run the db in "Server" mode, which opens a server socket. The front-end connects via this socket despite the fact that the db runs in the same VM as the front-end. This allows for external client connection for manual data inspection. For maximum performance, you can swith to in-process mode where communication goes directly without going through network layers; this however prevents external client connection.
TODO : howto and performance gain
The table T_LEASE is kept entirely in-memory by default for simpliest and maximum performance. Data commited is however immediately written to disk into a SQL script. This SQL script is completely reloaded at next startup.
If the lease volume increases widely, it can be interesting to switch this table to cached mode, where the data is kept on disk and only partially cached in memory. Performance will be slightly reduced but server restart will be faster.
TODO
===== Leases state transitions =====
{{statemachine.png?nolink|}}
===== Data model =====
{{ datamodel.png?nolink|Data Model}}
^ Table T_LEASE ^^^
^IP|BIGINT PK|binary (long) representation of the IPv4 address of the lease|
^CREATE_DATE|TIMESTAMP|time when the lease was initially created for this client (mac)|
^UPDATE_DATE|TIMESTAMP|time when the lease was last updated|
^EXPIRE_DATE|TIMESTAMP|end of lease time|
^RECYCLE_DATE|TIMESTAMP |time when the IP can be reallocated (including a secure margin to take into account clock drifts)|
^MAC|VARCHAR|MAC address of the client|
^STATUS|INTEGER|current status of the lease\\ -1: ABANDONED (address not usable)\\ 0:FREE\\ 1:OFFERED\\ 2:USED|
^ Table T_POOL_SET ^^^
^SET_ID|BIGINT PK|id (long) of the pool set|
^ Table T_POOL ^^^
^START_IP|BIGINT PK|start address of the pool as binary (long) IPv4 address|
^END_IP|BIGINT|end address of the pool as binary (long) IPv4 address|
^CLASSES|INTEGER|//[reserved for future use]//|
^SET_ID|BIGINT|foreign key to T_POOL_SET entity|
^ T_BUBBLE ^^^
^BUBBLE_ID|INTEGER PK|auto-increment identifier|
^POOL_ID|BIGINT|foreign key to T_POOL entity|
^START_IP|BIGINT|start address of the free address bubble|
^END_IP|BIGINT|end address of the free address bubble|
===== Serving DHCPDiscover =====
The first sequence in IP address allocation is the client sending a DHCPDiscover request and the server sending back a DHCPOffer with a (hopefully) proposed IP address.
The internal sequence when receiving a DHCPDiscover is as follows:
- Applying Front-End rules to filter out requests (cf. Front End TODO)
- Find out in which subnet the client is, hence which address pools are eligible for address allocation
- Check if there rules allocating a static address to this client, if so directly send back the response
- If there are dynamic addresses pools, call the DB stored procedure "DHCP_DISCOVER" to ask for an IP address
==== DHCP_DISCOVER stored procedure ====
This stored procedure is responsible for finding a suitable IP address for the client.
=== Pre-conditions ===
* The temporary tables ''T_POOL_SET'', ''T_POOL'' and ''T_BUBBLE'' are populated and consistent
* There is no overlap between ''T_POOL'' ranges
* There is no overlap between ''T_BUBBLE'' ranges
* There is no ''OFFERED'' or ''USED'' address in any ''T_BUBBLE'' range
* There is no empty ''T_BUBBLE'' range (start address > end address)
=== Parameters ===
* ''poolId'' id of the ''POOL_SET'', hence the address ranges
* ''macHex'' hex representation of the client's MAC address (must not be null)
* ''iccQuota'' maximum number of addresses allowed of this icc, or 0 to ignore icc
* ''icc'' icc identifier of the client, must not be null if iccQuota is > 0
* ''offerTime'' number of seconds to reserve OFFER before reallocating address
=== Internal process ===
- Check if the client already has an active lease in the specified address pool\\ //Select ''T_LEASE'' table with client's Mac address and Status is not ''FREE'' or ''ABANDONED'',\\ then filtering the result set with the specified address pool.//
- If no active lease is found, then look for the first free available address
- First check if we have not reached the ICC quota\\ //Select ''T_LEASE'' table with the client's ICC, then count active leases//\\ If the limit is reached, return an error (-4)
- Find a the first free address in the address pool\\ //Select ''T_BUBBLE'' to find the first free bubble, retrieve the first address then update the bubble: increase its start address or delete it if it becomes empty//\\ If there is no more available address return an empty code (0)
- If a lease for this client (same MAC address) is already in DB
- If RECYCLE_DATE is over, change lease into FREE status
- If status is OFFERED, extend lease time
- If status is USED, extend lease time if it needs to be
- If status is ABANDONED, return error (-5) (pre-condition violation)
- If status is FREE, set up lease
- If unknown status, return error (-6)
- If a lease existed for another client (and has expired, or pre-condition violation)\\ Reserve lease for this client (reset all fields including CREATE_DATE)
- If lease does not exist in db\\ Create new lease for this client
=== Post-conditions ===
* //same as pre-conditions//
* if not successfull (returned value <= 0), the database is unchanged (rollback)
* if successful, for the IP address returned, a ''T_LEASE'' row is created with
* ''IP'' = offered address
* ''UPDATE_DATE'' = now
* ''CREATION_DATE'' <= ''UPDATE_DATE''
* ''EXPIRATION_DATE'' >= now+offerTime
* ''RECYCLE_DATE'' >= ''EXPIRATION_DATE''
* ''MAC'' = client's mac address
* ''ICC'' = client's ICC identifier (if present)
* ''STATUS'' = ''OFFERED'' or ''USED''
===== DHCP_REQUEST stored procedure =====
This stored procedure is responsible for allocating the final IP address for the client.
=== Pre-conditions ===
* to be completed
=== Parameters ===
* ''poolId'' id of the ''POOL_SET'', hence the address ranges
* ''requestedIp'' IP address requested by client
* ''leaseTime'' number of seconds to reserve the lease in DB
* ''margin'' percentage of margin for the lease time before reallocated, this is necessary for potential clock drifts between client and server (e.g. 30 means allocated 30% more time)
* ''macHex'' hex representation of the client's MAC address (must not be null)
* ''optimisticAllocation'' whether we allow a client to request a free address even if there was no previous OFFER\\ this can be necessary to rebuild the database and allowing client renewals
=== Finding out what actions to do ===
The first step is to analyze both the client request and the database state. This lets us determine what actions should be done on the db, and which kind of response to send back to the client.
^updateLease|boolean|true= lease already exists in db, we should update it\\ false=we need to create a fresh new lease in db|
^updateBubble|boolean|true= we need to adjust bubbles because the address lies within a free bubble (only in optimistic mode)\\ false=bubbles are untouched|
^renewal|boolean|true= this is a lease renewal (we don't touch the CREATE_DATE)\\ false= fresh new lease, this address was not owned by this client|
- Retrieve the lease in db for the requested ip
- if the lease already exists in db:
- if lease status is ABANDONED, return error (-5)
- if the lease's status is FREE, the address needs to be taken of free bubbles //updateBubble=true//
- is the lease's RECYCLE_DATE is past, the lease is temporarily considered as FREE before being reallocated (but bubbles are not updated)
- if the lease's status is OFFERED or USED, and the mac address is different, the then lease is owned by another client, return an error (-2)
- else mark lease to be updated //updateLease=true//
- if the lease does not exist:
- mark lease to be created //updateLease=false// and mark bubble to be updated //updateBubble=true//
=== Now apply changes ===
- If existing lease object in db needs update:
- if previous lease owner is the same client, set //renewal=true//
- if this is not a //renewal// and we are not in optimistic mode, return an error (-1)
- update the lease in db
- Else if we need to create a new lease object in db
- if not in optimistic mode, return an error (-1)
- insert new lease object in db
- If updateBubble is true (we need to remove IP address from a free bubble):
- if IP address is not in a bubble, this is a pre-conditions violation, return an error (-6)
- if IP address is stricly inside a bubble (not the lower or upper limit), split the bubble into 2 bubbles excluding the allocated IP address
- if IP address is the lower or upper limit of the bubble, reduce the bubble's limit. If the bubble becomes empty, remove the bubble
=== Return codes ===
Return code if successful >= 0 (bit array)
- 1: (bit 0) lease is a renewal (was already in OFFERED or USED mode)
- 2: (bit 1) bubble has been updated (possible only in optimistic mode)
- 4: (bit 2) lease has been updated in database (a lease already existed potentially outdated)
=== Post-conditions ===
* to be completed
===== Batch clean-up =====
When leases RECYCLE_DATE is past, they are eligible for garbage collection. The garbage collector is called every 5 minutes to reclaim free leases.
//Design issue//: there can be a significant delay between the time a lease has expired and the time when the address is free to be allocated to another client. The trade-off is in favor of runtime performance causing potentially some address starving in extreme conditions. However, a specific "urgent" clean-up may be introduced in case of an free address starvation.