Files
steward/docs/subnets.md
T
abrown38andClaude Opus 5.5 1bc9f9f63a Let the Reserve button pick another address of the lease's subnet
The Reserve dialog fills in the lease's address but lets it be changed;
POST /leases/{ip}/reserve takes an optional ip, steward lease reserve
takes --ip. The address must be in the lease's subnet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 00:29:07 +01:00

260 lines
12 KiB
Markdown

# Subnets, pools, reservations and leases
## Subnets and interfaces
A subnet is an IPv4 network Steward hands addresses out in, served on
one network interface:
```sh
steward subnet add 10.0.10.0/24 --interface eth0 --name office \
--pool 10.0.10.100-10.0.10.199 --option routers=10.0.10.1
```
**The interface decides.** stewardd opens one socket on port 67 per
interface that has a subnet, bound to that interface. A request is
answered only from the subnets of the interface it came in on. A client on
`eth0.20` never gets an `eth0` address, even if it asks for one. It gets a
NAK, then an address of its own VLAN.
For VLANs, give the server an interface on each, such as `eth0.20`, or a
bridge such as `br-iot`, each with an address in its subnet. That address
is the server identifier clients see, and the one they renew their lease
with. `steward interface list` shows the server's interfaces with their
addresses and subnets. To keep interfaces such as `lo`, `docker0` or
`veth*` out of that list and the web UI's picker, name them in
`interfaces.exclude` in steward.yaml ([configuration.md](configuration.md)).
An interface that doesn't exist yet is fine. The subnet gets a warning
(`interface_unknown`), and stewardd starts serving it within 30 seconds of
the interface appearing. `steward status` and the dashboard show each
interface's state; when one isn't served, they say why.
**Several subnets on one interface** form a shared network: two networks
on the same wire, the server with an address in each. Addresses come from
the first subnet's pools, then the next. Clients of every subnet are
answered from the interface's address in their own subnet, or its first
address.
A subnet can be **disabled**: its configuration and leases stay, but its
clients get no answers (`steward subnet disable NAME`, or Disable in the
web UI).
## Pools
Pools are the ranges handed out to any client: `FIRST-LAST`, or a prefix,
`10.0.10.128/25`. A subnet may have several pools, or none if only clients
with a reservation should get addresses. Pools must be inside the subnet,
must not overlap, and can't include the network or broadcast address.
Pool sizes are shown as the addresses that can really be handed out.
Reserved addresses inside a pool don't count, since nobody else gets them.
## Reservations
A reservation fixes a client's address:
```sh
steward reservation add 10.0.10.20 aa:bb:cc:dd:ee:01 --hostname nas
steward lease reserve 10.0.10.57 # the address a client has now
steward lease reserve 10.0.10.57 --ip 10.0.10.21 # its client, another address
```
* **The client** is known by its MAC address, its client identifier
(option 61, `--client-id`; give `-` as the MAC for an identifier alone),
or both. With both, **either one** gets the client its address: a
machine that boots two systems sends different identifiers from one MAC
address, and a device with a randomised MAC may keep its identifier.
The Leases page and `steward lease show` show the identifier a client
sent; `steward lease reserve IP` uses both when it sent one (`--by mac`
or `--by client-id` for just one).
* **The address picks the subnet**: the subnet whose network holds it. It
may be inside a pool or outside one; either way, nobody else gets it.
* **The host name** is sent to the client (option 12).
* **Options** set on the reservation are for that client only, and win
over the subnet's and the settings'.
* **A lease time** of its own (`--lease-time 7d`) wins over the subnet's.
* **A device on two VLANs** can have a reservation in each subnet. Within
one subnet, a client has at most one.
A reservation can be **disabled** (`steward reservation disable RES`, or
Disable in the web UI). It is kept but ignored: the client gets a pool
address like any other, and the reserved address, if it is inside a pool,
may go to someone else. The client keeps the address it has until its
lease runs out. While it is disabled, another reservation may use its
address or client; enabling it again is then refused until one of them
moves.
A client with a reservation always gets that address, not a pool address.
If it asks for another one, for example after the reservation was added,
it gets a NAK that names its reserved address, and then the reservation.
## Options
Options are set at three levels. A reservation's win over its subnet's,
and a subnet's over the settings' (the options every subnet gets):
```sh
steward settings set --option domain-name-servers=10.0.10.2 --option domain-name=lan
steward subnet update office --option routers=10.0.10.1 --option ntp-servers=10.0.10.2
steward reservation update nas --option domain-name-servers=1.1.1.1
```
`--option NAME=VALUE` sets one and keeps the others; `--unset-option NAME`
removes one. [options.md](options.md) lists the names, and how to write
each kind of value. Any other option can be set by number, with its type:
`--option 224:string=hello`.
Every reply carries the subnet mask, the server identifier and, for a
lease, the lease time and the times to renew (half of it) and rebind (7/8
of it). With a parameter request list (most clients send one), the client
gets the options it asked for, in its order. The subnet mask, routers, DNS
servers, domain name and host name are sent even if it didn't ask. Without
a list, it gets every option set for it.
## Lease times
The settings have the default (12 hours, `steward settings set
--lease-time 1d`); a subnet can have its own (`--lease-time 1h`), and a
reservation its own over that (`steward reservation update nas
--lease-time 7d`). Clients
renew halfway through. Short times let changes reach clients sooner;
long ones keep clients working longer if Steward is down.
## How an address is chosen
For a DISCOVER, Steward offers, in this order:
1. the client's reservation in a subnet of the interface;
2. the address the client had before, from its last lease, even if that
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, 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.
The server's own addresses are never offered. A fresh pool address is
pinged first: if something answers, it is held back and the next one is
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 |
|---|---|
| active | the client holds the address until the lease ends |
| expired | the lease ran out; the client gets the address back if it returns while it is free |
| released | the client gave the address back |
| held back | a client declined the address because another device answered for it, or it answered Steward's ping. It isn't handed out until the hold ends (a day; `--decline-hold`) |
`steward lease list` shows active and held-back leases; `--all` shows
the others too. Leases that ended more than 30 days ago are deleted.
### Online and last seen
A client is **online** while its lease is active. With the ping check on
(the default), Steward also pings every active lease's address once a
minute, and a client that answers gets a **last seen** time: on the
Leases page, next to a reservation's online badge, and in `steward lease
list`. Many devices block pings (Windows' firewall does by default), so a
client that doesn't answer stays online and simply has no last-seen time,
or keeps the one it had. The time stays with the lease when it is
renewed, and is kept across restarts.
`steward lease delete IP` frees an address at once. A client still using
it gets a NAK when it next renews, then asks for a new address.
## Access: allow and deny lists
A subnet decides who it answers:
* **deny-list** (the default): every client except those on its deny
list;
* **allow-list**: only clients on its allow list or with a reservation in
it, and not on its deny list.
The deny list counts in both modes, and wins over a reservation; so an
allow-list subnet can allow a maker's devices by prefix and still deny one
of them. Switching the mode keeps both lists.
Entries are a MAC address (`aa:bb:cc:dd:ee:ff`), a prefix for one maker's
devices (`aa:bb:cc:*`, 1 to 5 bytes) or a client identifier
(`id 01aabbccddeeff`), each with an optional comment:
```sh
steward access mode iot allow-list
steward access allow iot aa:bb:cc:* --comment "Sonos speakers"
steward access deny office 3c:22:fb:11:22:33 --comment "old laptop"
steward access remove office 3c:22:fb:11:22:33
steward access show iot
```
In the web UI it is a subnet's **Access** tab; a DENIED line in Activity
has an **Allow** button that puts that client on the subnet's allow list.
A refused client gets **no answer**: its DISCOVER shows as `DENIED` in the
activity log, with the reason. A client refused after it got its lease
gets a **NAK** when it next renews, so it stops using the address then
(within half its lease time).
This is a control, not security: anyone can copy an allowed MAC address.
Phones and laptops also use a **private MAC address** per network by
default (iOS, Android, Windows), different from the one on their label;
on an allow-list subnet, turn that off for the network on the device, or
allow the private address it shows (Activity lists it).
## Authority
Steward is **authoritative** for its interfaces. A client that asks to
keep an address that doesn't belong on its interface, or that someone
else holds, gets a NAK and starts over at once, rather than keeping a
wrong address until its lease runs out. Turn this off (`steward settings
set --authoritative off`) only when another DHCP server serves the same
network. Requests for addresses Steward knows nothing about then get no
answer.
## Not in 0.1
Relayed requests (with `giaddr`) are ignored and logged once an hour: a
subnet must be on one of the server's own interfaces. DHCPv6, failover,
client classes and DNS names through Jarl are planned
([design.md](design.md)).