Address order: sequential, reverse or random, per subnet and pool

- Which never-used pool address a new client gets: sequential (the
  lowest, as before), reverse or random, in the settings, a subnet or
  a single pool (FIRST-LAST:ORDER). When no never-used address is left,
  the one free the longest, whatever the order; returning clients keep
  theirs.
- A subnet's pools are used in the order listed, or one at random
  weighted by its never-used addresses. Pools now keep the order they
  are given in (no more sorting by address).
- CLI --address-order and --pool-order; --add-pool of an existing range
  changes its order in place. Web UI: per-pool order and a move-up
  button, pool and address order on the subnet and settings pages.
- Kea's allocator: random is imported. Schema migration 5.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
abrown38andClaude Opus 5.5 committed 2026-10-18 18:13:25 +01:00
1 parent 29fc991ce1
commit 8ee430276c
29 files changed
+653 -79

No files matched your search

+13 -4
View File
@@ -71,9 +71,13 @@ Creating answers 201, other changes 200.
```json
{"lease_time": 43200, "ping_check": true, "ping_timeout_ms": 500, "decline_hold": 86400,
"not_authoritative": false, "options": [{"option": "domain-name-servers", "value": "10.0.10.2"}]}
"not_authoritative": false, "address_order": "sequential",
"options": [{"option": "domain-name-servers", "value": "10.0.10.2"}]}
```
`address_order` is `sequential` (left out when it is), `reverse` or
`random` ([subnets.md](subnets.md#address-order)).
`options` replaces the list. Each option is `{"option": NAME or NUMBER,
"value": TEXT}`, with `"type"` for numbers the catalog doesn't know
([options.md](options.md)).
@@ -90,12 +94,17 @@ Creating answers 201, other changes 200.
```json
{"name": "office", "interface": "eth0", "prefix": "10.0.10.0/24",
"pools": ["10.0.10.100-10.0.10.199", "10.0.10.224/28"], "lease_time": 0,
"pools": ["10.0.10.100-10.0.10.199", "10.0.10.224/28:random"], "pool_order": "listed",
"address_order": "default", "lease_time": 0,
"options": [{"option": "routers", "value": "10.0.10.1"}], "disabled": false, "comment": ""}
```
`pools` and `options` replace the lists; `lease_time` 0 means the
settings'.
`pools` and `options` replace the lists; the pools keep their order. A
pool's `:ORDER` is its own address order. `pool_order` is `listed` or
`random`; `address_order` is `sequential`, `reverse`, `random` or
`default` (the settings'). Subnets come back with pools as
`{"start", "end", "order"}`, and the defaults (`listed`, `default`) left
out. `lease_time` 0 means the settings'.
### Reservations
+3 -3
View File
@@ -62,8 +62,8 @@ since the start, and problems.
|---|---|
| `subnet list` | subnets, with interface, pools, use, reservations, lease time |
| `subnet show SUBNET` | one subnet; SUBNET is its ID, name or prefix |
| `subnet add PREFIX --interface IFACE [--name N] [--pool A-B]... [--lease-time 12h] [--option NAME=VALUE]... [--comment C]` | add one |
| `subnet update SUBNET [--name] [--interface] [--prefix] [--pool]... [--add-pool] [--remove-pool] [--lease-time 1h\|default] [--option NAME=VALUE]... [--unset-option NAME]... [--comment]` | change what is given |
| `subnet add PREFIX --interface IFACE [--name N] [--pool A-B[:ORDER]]... [--pool-order listed\|random] [--address-order sequential\|reverse\|random] [--lease-time 12h] [--option NAME=VALUE]... [--comment C]` | add one |
| `subnet update SUBNET [--name] [--interface] [--prefix] [--pool]... [--add-pool] [--remove-pool] [--pool-order] [--address-order ...\|default] [--lease-time 1h\|default] [--option NAME=VALUE]... [--unset-option NAME]... [--comment]` | change what is given; `--add-pool` of a pool it has changes that pool's order ([subnets.md](subnets.md#address-order)) |
| `subnet enable\|disable SUBNET` | answer its clients, or not |
| `subnet delete SUBNET [-y]` | delete it and its reservations |
@@ -115,7 +115,7 @@ sent.
| Command | |
|---|---|
| `settings show` | |
| `settings set [--lease-time 1d] [--ping-check on\|off] [--ping-timeout 500ms] [--decline-hold 24h] [--authoritative on\|off] [--option NAME=VALUE]... [--unset-option NAME]...` | |
| `settings set [--lease-time 1d] [--ping-check on\|off] [--ping-timeout 500ms] [--decline-hold 24h] [--authoritative on\|off] [--address-order sequential\|reverse\|random] [--option NAME=VALUE]... [--unset-option NAME]...` | |
| `option list` | the options Steward knows ([options.md](options.md)) |
| `interface list` | the server's interfaces, their addresses and subnets (not those in `interfaces.exclude`) |
+1
View File
@@ -51,6 +51,7 @@ pings before it offers an address, and clients renew or rebind.
| `shared-networks` | their subnets, all on the shared network's interface |
| `reservations` with `hw-address` or `client-id` and `ip-address`, `hostname` | reservations |
| `decline-probation-period` | the decline hold |
| `allocator` (global, shared network, subnet) | `random`: the subnet's pool and address order random. `iterative` and `flq` become the default order, the lowest never-used address (with a note for `flq`) |
| `user-context.comment` or `comment` of a subnet | the subnet's comment |
| the lease file: active and declined leases, and released ones | leases (the last line of each address wins) |
+37 -2
View File
@@ -121,8 +121,9 @@ For a DISCOVER, Steward offers, in this order:
ended, as long as it is still free and in a pool;
3. the address the client asks for (option 50), if it is free and in a
pool;
4. a free pool address: one never handed out before, else the one that has
been free the longest.
4. a free pool address: one never handed out before, in the subnet's
[address order](#address-order), else the one that has been free the
longest.
An address is free when no other client has an active lease on it, it
isn't held back, and it isn't offered to someone else in the last minute.
@@ -133,6 +134,40 @@ tried.
When the client confirms with a REQUEST, the lease is saved, and only
then is the ACK sent.
## Address order
Which never-used address a new client gets is set at three levels, each
overriding the one above:
| Level | Setting | Values |
|---|---|---|
| settings | `steward settings set --address-order` | `sequential` (the lowest free address, the default), `reverse` (the highest), `random` |
| subnet | `--address-order` | the same, or `default` (the settings') |
| pool | `:ORDER` after the pool: `--pool 10.0.10.200-10.0.10.250:random` | the same, or `default` (the subnet's) |
A subnet with several pools also has a **pool order**, `--pool-order`:
* `listed` (the default): the pools are used in the order they are
listed; the next one only when the one before has no never-used address
left. `--pool` gives the order; in the web UI, ↑ moves a pool up.
* `random`: a pool at random, weighted by how many never-used addresses
each has, so addresses spread over all of them. With `random` addresses
too, every never-used address in the subnet is as likely as any other.
```
steward subnet update office --pool-order random --address-order reverse
steward subnet update office --add-pool 10.0.10.200-10.0.10.250:random # one pool's own order
steward subnet update office --add-pool 10.0.10.200-10.0.10.250:default # back to the subnet's
```
The order only picks among addresses **never handed out** (or whose lease
was deleted, or ended over 30 days ago). Once every free address has been
used before, the one free the longest is given, whatever the order: an
address that just came free isn't handed to the next device, so stale ARP
caches and DNS records don't point at the wrong one. Returning clients
keep their address (steps 2 and 3), so changing the order doesn't move
anyone.
## Leases
| State | Meaning |
+2
View File
@@ -17,6 +17,7 @@ interfaces. They come from stewardd, not from a configuration on its own.
| `lease_time_range` | error | a lease time (settings or subnet) under 1 minute or over 365 days |
| `ping_timeout_range` | error | the ping timeout isn't 50 to 5000 ms |
| `decline_hold_range` | error | the decline hold isn't 1 minute to 30 days |
| `address_order` | error | an address order (settings, subnet or pool) that isn't `sequential`, `reverse` or `random` |
## Options
@@ -45,6 +46,7 @@ These apply to options anywhere (settings, subnets, reservations).
| `pool_outside_subnet` | error | a pool not inside the subnet |
| `pool_network_address` | error | a pool with the network or broadcast address |
| `pool_overlap` | error | two pools overlap |
| `pool_order` | error | a pool order that isn't `listed` or `random` |
| `router_outside_subnet` | warning | a `routers` address outside the subnet: clients can't reach it |
| `router_in_pool` | warning | a `routers` address inside a pool. The ping check keeps it from being handed out while it answers, but take it out of the pool |
| `interface_unknown` | warning, host | the interface doesn't exist on this machine. It is served once it appears |