netd
The network manager as built: how it reads the interface layer and profiles, reconciles links, addresses and routes, runs its DHCPv4, router-discovery and DHCPv6 clients, identifies networks, and publishes readiness and DNS facts.
Single-page view · as markdown
1.1 Overview
Peios / Peios technical reference / netd / Introduction
netd is the Peios network manager. It is the executor of the
interface layer of PNP, the Peios Network Policy: it reads which
interfaces join which networks, and how they stand there, from
Machine\System\Network, and makes the kernel match. It runs as a
SYSTEM service started at boot, and it is the only program on a Peios
machine that configures links, addresses and routes.
Three decisions shape everything else in this manual.
It is a reconciler, not a command interpreter. netd keeps no list of things it has done. On every input — a kernel link or address event, a registry change, a DHCP or router-discovery packet, a timer, a control request — it re-derives what each interface should look like and applies the difference from what the kernel reports. Nothing happens imperatively in a handler. Two consequences follow directly: a manual change to an interface netd manages lasts until the next pass, and a restarted netd derives its desired state afresh. While its first INIT-REBOOT is pending, it preserves the requested IPv4 address already installed and applicable netd-owned routes (§2.1). Static configuration is desired from the first pass and stays.
It owns addresses wholly and routes only by mark. On an interface netd has joined, every IPv4 address and every non-link-local IPv6 address is netd's: anything else is removed. Routes are netd's only when they carry routing protocol 200, the number netd stamps on every route it adds, so a route another program adds for itself survives. The kernel's own IPv6 link-local address is never touched.
It is the only router-advertisement listener. netd switches the kernel's own RA processing off on every interface before it does anything else, and acts on advertisements itself, so exactly one place decides what an advertisement means. The kernel still makes link-local addresses and answers neighbour solicitations; everything an advertisement could configure — addresses, the default route, DNS, the MTU — is netd's decision, filtered through the interface's profile.
netd builds and judges the interface layer with pnp-core, the same
engine the kernel's NTFE uses for the packet layers, so a rule means the
same thing in every layer. The kernel never reads Rules\Interface;
netd never reads the packet layers. Each executor validates and refuses
its own layers on its own.
1.1.1 Where netd sits #
registry (Machine\System\Network) peinit
│ Rules\Interface, Profiles, ▲ READY=1, LEVEL=<level>
│ Hostname, Duid, ControlSecurity │
▼ │
┌─────────────────────── netd ──────────────────────┐
│ interface layer (pnp-core) → profile per interface │
│ DHCPv4 · router discovery/SLAAC · DHCPv6 │──▶ rtnetlink (links,
│ reconciler · network identification │ addresses, routes)
└────────────────────────────────────────────────────┘
│ Interfaces\<id>\Status, Networks\<id>, │ /run/netd/control.sock
│ Readiness ▼
▼ net, resolvd (subscribe)
registry (written back)
What netd writes back to the registry is read by others: the kernel
reads each interface's Status Network to give the packet layers their
Network.* facts (PKM §6.5), services and scripts watch Readiness,
and resolvd subscribes to netd's DNS snapshots over the control socket
(PSPU §6.9).
1.1.2 What netd does not do #
- Answer names. netd reports what each interface contributes to name resolution; resolvd routes and answers queries.
- Filter packets. The packet layers are NTFE's. netd's own DHCP and router-discovery traffic passes them only because the shipped baseline policy permits it.
- Decide who can bind a port. That is the kernel's port reservation table.
- Associate with wireless networks. A supplicant brings a wireless link up; netd then treats it as any other interface.
- Hold leases across a reboot. Nothing about a lease is persisted; a rebooted machine asks for the address its network last gave it (§5.7).
1.2 What this manual covers
Peios / Peios technical reference / netd / Introduction
This manual describes netd as built: the netd workspace's netd
daemon, its dhcp4, dhcp6 and ndp state-machine crates, the
libnetd wire crate, and the net command. It describes version 0.1.7.
1.2.1 What is covered elsewhere #
| Subject | Where |
|---|---|
| The interface layer's vocabulary — facts, verdicts, the collation laws — and every profile value, as an operator writes them | The network policy reference (~peios/networking/network-policy-reference) |
| The rule engine itself: ingestion, conditions, collation, refusal | Kernel TRM chapter 6 (PKM §6.4, §6.5); netd links the same pnp-core crate |
The kernel's side of the network context — how Status Network becomes Network.* facts in the packet layers | PKM §6.5 |
| The netd → resolver channel's contract | PSPU §6.9 |
| What a resolver does with the snapshots | PSPU book 6 and the resolvd manual |
| The query-channel framing the control socket uses | PSPU book 3 |
| How peinit holds a service until a readiness level is published | The peinit manual |
| Every registry value netd reads or writes, one entry each | regman 'Machine\System\Network' on a Peios machine |
Where this manual and the network policy reference both mention a profile value, the reference owns the meaning and this manual owns what netd does with it: how the value is parsed, when it takes effect, and how it combines with what a network offers.
1.2.2 Conventions #
Section numbers in this manual are written §n.m. A reference into
another book is qualified with the book's short name: PKM §6.5 is the
Kernel TRM, PSPU §6.9 the userspace protocols book.
Times are given as netd's monotonic clock sees them. Every timer netd
runs — DHCP retransmission, lease boundaries, advertisement lifetimes,
solicitation backoff — is measured on CLOCK_MONOTONIC, so setting the
wall clock moves none of them.
1.3 Terminology
Peios / Peios technical reference / netd / Introduction
Terms from the policy language — rule, fact, verdict, backstop, priority, exception — mean what the network policy reference says. The terms below are netd's own.
| Term | Meaning |
|---|---|
| interface | A kernel network link netd has seen, keyed by its kernel index while it exists and named in the registry by its interface id. |
| interface id | A UUID-shaped digest of an interface's bus path and MAC (§4.1). Stable across boots and kernel renames. |
| verdict | What the interface layer said about an interface: JOIN(profile), IGNORE or DOWN. |
| joined (managed) | An interface whose verdict is JOIN. Loopback is never joined. Only joined interfaces get clients, contribute readiness, and appear in DNS snapshots. |
| profile | A resolved key under Profiles\, inheritance applied: how a joined interface stands on its network. |
| generation | One build of the interface layer and the profiles from one reading of the registry. netd holds the last good generation; a refused one changes nothing. |
| pass | One run of netd's converge sequence (§2.2): identify networks, judge links, start or stop clients, reconcile, publish. |
| desired state | What one interface should look like: up or down, its MTU, its addresses and its routes (§4.2). |
| reconcile | Diffing desired state against the kernel's and applying the difference (§4.3). |
| network | What is on the other side of a joined interface's carrier, identified from what it has offered (§7.1) and recorded under Networks\<id>. |
| offer | Everything a network has said: a DHCPv4 lease, router advertisements, a stateless DHCPv6 reply. |
| level | How far an interface has got towards being usable: absent, link, addressed or routed (§8.2). |
| scope | What one interface contributes to name resolution, as sent to resolvd (§8.3). |
2.1 Startup
Peios / Peios technical reference / netd / The Daemon
2.1.1 The service definition #
The netd package ships its service definition as a registry seed,
netd-service.reg, applied by an image that names it. The definition:
| Value | Setting | Why |
|---|---|---|
ImagePath | /usr/sbin/netd | |
Triggers | boot | |
Identity | SYSTEM | rtnetlink changes, the packet socket and a bind to udp/68 each need a privilege only SYSTEM holds |
Provides | network | the role a dependent names: Requires = ["network:routed"] |
Readiness | 0 (Notify) | readiness is READY=1 on the notify socket |
RuntimeDirectories | netd | peinit creates /run/netd for the control socket |
RestartPolicy | 2 (Always) | a stopped network manager is a machine whose leases silently expire |
ErrorControl | 0 |
A second seed, netd-default-profile.reg, ships the baseline policy: the
profile default and the rule wired (§3.1). It is separate so an image
can run netd without agreeing that every wired interface joins whatever
it is plugged into.
2.1.2 The startup sequence #
netd does these steps in this order:
- Opens rtnetlink: one socket for requests and dumps, and a second subscribed to the link, IPv4 and IPv6 address, and IPv4 and IPv6 route multicast groups. Failure is fatal.
- Opens the control socket at
/run/netd/control.sock, removing a stale one first (§9.1). Failure is fatal. - Opens the udp/68 absorber (§5.6). Failure is a warning: netd runs without it, and a DHCP server's unicast reply can draw an ICMP port-unreachable from the kernel.
- Switches the kernel's RA processing off on every interface that
exists, and in
allanddefault(§6.1). This happens before any link is brought up. - Reads the configuration (§2.3) and builds a generation from it (§3.2). A generation that fails to build at startup is logged as refused, and netd runs with an empty policy, so the backstop ignores every interface until a good generation is written.
- Arms the registry watch on
Machine\System\Network. Failure is a warning, and the configuration is then read once, never again. - Dumps the kernel's state (links, addresses and routes, both families). Failure is fatal.
- Runs the first pass (§2.2): every interface is judged, every static address applied, every client that is due started.
- Sends
READY=1on the notify socket.
Readiness is reported after the first pass, not after any lease arrives. A lease is a network event, not a startup one. A service that needs an address waits on a readiness level (§8.2), not on netd being ready.
A tie between two rules is not checked at startup: the startup build refuses a generation only for the build errors of §3.2. A tie present at startup is judged per interface as a conflict (§3.3), so the interfaces it covers are ignored with a warning rather than the generation being refused.
If Machine\System\Network does not exist when netd starts, the
configuration is empty and the watch cannot be armed, so a key created
later is never read; a restart of netd picks it up.
2.1.3 Restart #
netd re-derives everything from the registry and the kernel. Its DHCP clients start again, and a client remembers nothing across a restart: each one begins with an INIT-REBOOT request for the address its network last leased (§5.7), and holds no lease until that request is answered.
During an interface's first DHCP start in this process, netd preserves
its requested IPv4 address at the prefix already installed in the kernel.
It also preserves its netd-marked IPv4 routes while Route.Offered allows
them; an explicit Route.Gateway takes precedence and the current profile's
metric applies. This provisional state lasts only while INIT-REBOOT is
pending. An unchanged ACK replaces it with the confirmed lease without
deleting the address or route, so IPv4 readiness stays in place.
A NAK or two unanswered requests end revalidation and discard the provisional state as the client enters fresh discovery (§5.2). Carrier loss or a changed profile clears it too. Later client restarts in the same process do not adopt kernel state again. A requested address that is not already installed is never added merely because it was remembered.
This is not a restored lease: no lease duration, expiry, DNS servers or other offered information is inferred from the installed address. Status still reports no lease until the ACK. DHCP-provided DNS information and IPv6 router-discovery state must be reacquired; this does not promise that every aspect of a daemon restart is invisible. IPv6 addresses and routes from advertisements remain desired only after discovery hears the routers again (§6.1). Static addresses are desired from the first pass and stay.
2.2 The loop
Peios / Peios technical reference / netd / The Daemon
netd is one thread around one poll(2).
Every input is a descriptor in the poll set or a timer deadline, and every
handler records what it learnt and lets the end of the iteration act on
it. Because there is one thread, a handler that blocks holds everything
up: no DHCP timer fires and no kernel event is read until it returns.
2.2.1 Inputs #
| Input | Descriptor | What it leads to |
|---|---|---|
| Kernel events | the rtnetlink multicast socket | a fresh dump, then a full pass |
| Registry changes | the watch on Machine\System\Network | a reload (§2.3), then a full pass |
| udp/68 absorber | the absorber socket | drained and discarded |
| DHCPv4 replies | one packet socket per running client | the client's actions; a reconcile if the network changed |
| Router advertisements | one ICMPv6 socket per interface doing router discovery | the engine's actions; a full pass if anything changed |
| DHCPv6 replies | one UDP socket per running DHCPv6 client | the client's actions; a reconcile if the DNS facts changed |
| Control requests | the listener and accepted client sockets | the request (§9.2); reconcile runs a full pass |
| Time | the earliest deadline of any client | every client's timers, each acted on as above |
The poll timeout is the earliest deadline any DHCPv4, router-discovery or DHCPv6 client holds, or a pending control request or reply holds (§9.1); with none, netd waits indefinitely. A deadline already acted on is not held again: once an address is deprecated, its preferred lifetime is behind it, and its next deadline is the end of its valid lifetime. So netd sleeps while addresses sit deprecated, which on an IPv6 network with temporary addresses is most of the time.
After a kernel event netd does not fold the event into its model. It dumps the whole state again and always runs a full pass, because a dump taken after an apply can already show a state whose consequences have not been decided yet.
2.2.2 A full pass #
A full pass runs these steps, in this order:
- identify networks on joined interfaces with carrier (§7.1);
- bring the interface table in line with the kernel's links and judge every non-loopback interface (§3.3), stopping the clients of an interface that lost carrier or whose outcome changed;
- start DHCPv4 clients where due (§5.1);
- start or stop router discovery and DHCPv6 (§6.1, §6.3);
- reconcile every interface (§4.3) and dump the kernel's state again.
It then repeats steps 1 to 5 while the dump differs from the one before the reconcile. The steps run at most four times in one pass, the first run included, even if the dump is still changing after the fourth.
Applying changes alters what the next decisions see: bringing a link up gives it carrier, carrier starts a DHCP client, and a lease adds an address. One pass would leave that chain waiting for a later event that, on a virtual NIC, has already arrived.
After the repeats, the pass applies the hostname (§8.4) and publishes:
the machine level to peinit and the registry, every joined interface's
Status, and a DNS snapshot to subscribers if it changed (§8.2, §8.3).
2.2.3 Reconcile without a full pass #
A DHCPv4 lease arriving, a lease being lost, link-local fallback, and a DHCPv6 answer each change only what an interface should carry, so they lead to a reconcile of every interface, the hostname, and a publish, without re-judging anything. If the same iteration also needs a full pass, only the full pass runs.
A DHCPv4 lease also marks its interface for re-judgement, because the network on the other side might now be identifiable (§7.1). Any interface so marked makes the iteration run a full pass.
2.3 Reading the registry
Peios / Peios technical reference / netd / The Daemon
netd reads its configuration from Machine\System\Network, whole, every
time it reads it.
2.3.1 What is read #
| Value or key | Used for |
|---|---|
Hostname (REG_SZ) | the machine's name (§8.4). An empty string counts as unset. |
ControlSecurity (REG_BINARY) | the control object's descriptor (§9.1). An empty value counts as unset. |
Duid (REG_SZ) | the machine's DHCP unique identifier, as hex (§5.7). A value that is not hex is ignored with a warning, and replaced when netd next decides the DUID (§5.7). |
Rules\Interface\ and everything under it | the interface layer (§3.2) |
Profiles\ and everything under it | the profiles (§3.1) |
Nothing else under the key is configuration to netd. Interfaces\ and
Networks\ are read only for the values netd shares with the operator
(§5.7, §7.2), at the moment it needs them.
A missing Machine\System\Network means no configuration: no hostname,
no rules (so the backstop ignores every interface), no profiles.
2.3.2 Lowering #
Every value in the two trees is lowered to one of four shapes before anything interprets it:
| Registry type | Lowered to |
|---|---|
REG_DWORD (4 bytes) | an integer |
REG_QWORD (8 bytes) | an integer |
REG_SZ, REG_EXPAND_SZ | a string, cut at the first NUL; other if not UTF-8 |
REG_MULTI_SZ | a list of strings, empty items dropped, invalid UTF-8 items dropped |
| anything else, or a DWORD or QWORD of the wrong length | other |
The rule builder refuses an other by name (§3.2), and the profile parser refuses it as a value of the wrong shape (§3.1), so an unusable value is never silently dropped. The default (unnamed) value of a key is skipped in both trees.
Values and subkeys are sorted by name, so two readings of an unchanged tree compare equal.
A tree is read at most 16 levels deep. Below that the read stops with a warning, and whatever lies there is not part of the configuration. Nothing else bounds the depth of a rule tree or a profile tree (§3.2).
2.3.3 A registry change #
The watch is armed on the whole Machine\System\Network subtree. On any
event, netd reads the whole configuration again and compares it with
what it holds:
- If anything differs, the control object is rebuilt from
ControlSecurity. If the rules or the profiles differ, a new generation is built (§3.2), and taken or refused. - Whatever differs, a full pass follows.
netd's own writes to Interfaces\ and Networks\ come back through the
same watch. They cause a reload that finds nothing changed, and a pass
that plans nothing.
A failed read of the watch's events re-arms the watch.
There is no reload command: a write is noticed within the time it takes the watch to deliver and a pass to run.
3.1 Profiles
Peios / Peios technical reference / netd / The Interface Layer
The profile vocabulary — what each value means to an operator — is the
network policy reference's (~peios/networking/network-policy-reference#profiles).
This article is what netd does with it.
3.1.1 Resolution #
Every key under Profiles\ is a profile, at every depth, named by its
path below Profiles with / between levels. The Profiles key's own
values are ignored.
Profiles are looked up case-insensitively: netd keys them by the
lower-cased path and keeps the path as written for display and for
Status Profile.
A key whose name is empty or contains / or \ refuses the generation.
3.1.1.1 Inheritance #
A profile's effective values are its ancestors' values with its own laid
over them, per value name. A value a profile names replaces the
inherited one whole — a list replaces a list, never appends to it — and
a value it does not name is inherited. Value names compare
case-insensitively, so address.offered overrides Address.Offered.
Each profile is parsed from its own effective values. A malformed value in a parent therefore refuses the generation at the parent, before any child is reached.
3.1.1.2 Enabled #
Enabled is a profile's switch, not a value: it is never inherited as
one. A profile is enabled when its own Enabled and every ancestor's say
so; absent means enabled. A child cannot re-enable itself under a
disabled parent. A disabled profile is still resolved and parsed — a
malformed value in it still refuses the generation — but a rule that
names it abstains (§3.2).
Enabled accepts the boolean shapes below; anything else refuses the
generation.
3.1.2 Accepted shapes #
Every value is parsed from its lowered form (§2.3):
| Shape | Accepted |
|---|---|
| boolean | an integer (non-zero is true), or a string on, yes, true, 1, off, no, false, 0 in any case |
| number | a non-negative integer that fits 32 bits, or a string of one, surrounding space trimmed |
| list | a multi-string (empty items dropped), or a single string as a one-item list; an empty string is the empty list |
| string | a string, or an integer written as its decimal text |
A value of any other shape — a list where a boolean belongs, a binary
value — refuses the generation with the message profile <path>: <name> has the wrong shape, where <name> is the value's name in lower case.
3.1.3 Values #
| Value | Shape | What netd does with it |
|---|---|---|
Address.Offered | boolean | runs a DHCPv4 client while IPv4 is in the families, and router discovery while IPv6 is |
Address.Families | list | items ipv4 and ipv6, in any case. The list replaces the default of both, so an empty list means neither family. Any other item refuses the generation. |
Address.Static | list | each item address/prefix, either family, prefix at most 32 or 128. An item that does not parse refuses the generation. |
Address.LinkLocal | boolean | link-local fallback (§5.5) |
Address.Temporary | boolean | temporary IPv6 addresses (§6.2) |
Address.OnExpiry | string | Drop or Keep, in any case. Anything else refuses the generation. |
Route.Offered | boolean | takes the lease's gateway and classless routes and the routers' default route (§4.2) |
Route.Gateway | list | each item an address of either family. The last IPv4 item is the IPv4 gateway, the last IPv6 item the IPv6 gateway. An item that does not parse refuses the generation. |
Route.Metric | number | the metric of every route netd adds for the interface, and its place in DNS scope order |
Mtu.Offered | boolean | takes the lease's MTU, else the routers' |
Mtu.Value | number | the MTU to set. Below 68 refuses the generation. |
Hostname.Announce | boolean | sends the machine's Hostname in DHCP option 12 |
Hostname.Offered | boolean | adopts a lease's hostname when Hostname is unset (§8.4) |
Dns.Offered | boolean | adds the network's DNS servers and search domains after the profile's own (§8.3) |
Dns.Servers | list | each item an address of either family. An item that does not parse refuses the generation. |
Dns.Domains | list | taken as written; nothing validates a domain |
Dns.Default | boolean | present sets the scope's default-route flag; absent follows the interface's level (§8.3) |
Dns.Exclusive | boolean | sets the scope's exclusive flag |
A value name not in this table (case-insensitively) refuses the
generation with profile <path>: unknown value <name>, the name as
written.
After parsing, any Address.Static entry outside the profile's families
is dropped without an error: the family switch wins.
3.1.4 A bare profile #
Every compiled default is false, empty or absent, apart from the
families (both) and OnExpiry (Drop). A profile that sets nothing
therefore brings its interface up and does nothing else: no clients, no
addresses, no routes, no DNS. The friendly behaviour of the shipped
default profile is four values in its seed: Address.Offered,
Address.LinkLocal, Route.Offered and Dns.Offered, each 1.
3.2 Building a generation
Peios / Peios technical reference / netd / The Interface Layer
A generation is built whenever the rules or the profiles have changed (§2.3), and once at startup.
3.2.1 The order of the build #
- Profiles are resolved (§3.1). Any error refuses the generation.
- If
Rules\Interfacedoes not exist, the generation has no forest. Every interface meets the backstop,IGNORE, and the profiles are kept for nothing to name. - Each subkey of
Rules\Interfaceis a root rule. Its tree is lowered topnp-core's input and built as theInterfacelayer withbuild_forest— the same ingestion, condition parsing and lints the kernel applies to the packet layers (PKM §6.5). Every build error refuses the generation. - Every profile a
JOINnames is looked up. AJOINnaming no profile refuses the generation withJOIN(<path>) names no profile.
3.2.2 Lowering a rule #
Integers and strings pass through. A value of any other lowered shape
(§2.3) refuses the generation with rule <path>: value <name> has an unsupported type.
A list is passed through item by item, except in Actions. There, an
item that is a JOIN naming a profile that exists and is disabled is
replaced by NULL before the build, so the rule abstains and the
forest's own collation answers without it: its parent, another tree, or
the backstop. A JOIN of a disabled profile is not a fault.
A JOIN target is recognised loosely. Whitespace anywhere is ignored,
JOIN( matches in any case, \ in the path is read as /, and the
path is compared case-insensitively. So join ( Office\London ) names
the profile office/london.
3.2.3 What refuses a generation #
Everything pnp-core refuses at the interface layer refuses it here, with
netd's wording:
| Cause | Message |
|---|---|
| An unknown fact in a condition | rule <path>: unknown fact <key> |
| A malformed operator | rule <path>: bad operator in <key> |
| A malformed pattern | rule <path>: bad pattern in <key> |
Actions not a list | rule <path>: Actions is not a list |
| A malformed action | rule <path>: bad action (…) |
An action the interface layer does not speak (PASS, DROP, TAG, …) | rule <path>: an action the interface layer does not speak |
| A key that does not exist at the interface layer | rule <path>: <key> does not exist at the interface layer |
Present on a fact never present at this layer | rule <path>: <key> on a fact that never exists at this layer |
Priority not an integer | rule <path>: Priority is not an integer |
Enabled not 0 or 1 | rule <path>: Enabled is not 0 or 1 |
| A malformed rule name | rule <path>: bad name |
A JOIN naming no profile | JOIN(<path>) names no profile |
| A malformed profile | the profile parser's message (§3.1) |
The depth of a rule tree refuses nothing. It is bounded only by netd's
registry read, which takes rules to 16 levels below Rules\Interface and
ignores anything deeper (§2.3).
A condition on a fact that exists in the vocabulary but can never be
present at the interface layer, such as DstPort, is not refused. It is
a lint: logged as a warning, rule <path>: <key> can never hold at the interface layer, and the generation is taken.
3.2.4 A tie refuses at runtime #
A generation built while netd is running is judged against every
non-loopback interface before it is taken. If any interface meets a
conflict (two rules tied on priority naming different profiles,
§3.3), the generation is refused with rules <a> vs <b> tie on interface <name>.
3.2.5 Taken or refused #
A taken generation replaces the old one whole, clears any recorded refusal, and is logged with its counts of rule trees and profiles. The pass that follows judges every interface against it.
A refused generation changes nothing. The last good generation stays in
force, netd logs interface layer refused: <why>; the last good generation stands at error level, and records the reason, which
status reports as refusal and net status prints as a policy REFUSED line.
The refusal stays recorded until a generation builds.
The interface layer's refusal is netd's alone. The kernel reads the
packet layers from the same registry state and refuses or takes them on
its own (PKM §6.5): a dangling JOIN never stalls the firewall, and a
bad Flow rule never stops an interface joining.
3.3 Judging an interface
Peios / Peios technical reference / netd / The Interface Layer
3.3.1 The facts #
An interface is judged on a snapshot carrying only interface and network facts:
| Fact | Value |
|---|---|
Interface | the kernel's name for the link |
Interface.Kind | wired, wireless, loopback or other (§4.1) |
Interface.Id | the interface id (§4.1) |
Interface.Mac | the hardware address, when the link has a 6-byte one |
Interface.Path | the bus path; absent when sysfs gives none |
Interface.Driver | the driver; absent when sysfs gives none |
Network.Id | the id of the network identified on the link (§7.1); absent until one is |
Network.Name | the record's Name; absent when unset or empty |
Network.Trust | the record's Trust; absent when unset or empty |
Network.Kind | the interface's kind, once a network is identified; absent before |
A fact the interface lacks is absent, which makes a condition on it false, never an error.
3.3.2 When an interface is judged #
Every non-loopback interface is judged on every full pass (§2.2).
Loopback is never judged and never managed: it has no verdict, no record
under Interfaces\, and netd never changes it.
3.3.3 The verdict #
pnp-core evaluates the forest and netd reads the result:
JOIN(i): the profile the forest'sithJOINnames, looked up case-insensitively. netd stands the interface in that profile.DOWN: the interface is kept administratively down.- anything else, including the backstop:
IGNORE.
The attribution is the evaluation's: the path of the rule that spoke, or
backstop when none did.
3.3.4 Conflicts #
When the evaluation reports a conflict — the highest-priority candidates
tie and name different profiles — netd does not choose. The interface's
outcome is IGNORE, its attribution is the tied rules' paths, sorted and
joined with vs, and its warning is rules <a> vs <b> tie; the interface is ignored. The first time an interface meets a conflict, netd
logs a warning.
A conflict reaches an interface in two ways. A generation taken at startup is not checked for ties (§2.1), and an interface that appears after a generation was taken is judged against it with no check. A generation built at runtime that ties on an interface already present is refused instead (§3.2).
3.3.5 A change of verdict #
The outcome compares the verdict and the profile's resolved values. When either differs from the interface's previous outcome, netd logs the new verdict and its rule, and stops the interface's clients:
- the DHCPv4 client sends a RELEASE if it holds a lease (§5.4) and is discarded, along with its lease and any link-local address;
- router discovery and DHCPv6 are discarded, and their addresses go at the next reconcile.
The pass then starts whatever clients the new outcome wants. Editing any value of the profile an interface stands in is a change of outcome, so it restarts the interface's clients against the new profile, rather than trusting a lease obtained under the old one.
3.3.6 What each verdict does #
| Verdict | Clients | Desired state (§4.2) |
|---|---|---|
JOIN(profile) | started as the profile asks | up, with the profile's addresses and routes |
DOWN | none | down, carrying no addresses and no netd routes |
IGNORE | none | none: netd plans nothing for the interface |
An IGNOREd interface is left exactly as it is. If it was joined before,
the addresses and routes netd gave it stay where they are, because
nothing desires their removal.
4.1 Identity and carrier
Peios / Peios technical reference / netd / Interfaces
4.1.1 Kind #
netd classifies each kernel link once per dump:
| Link | Interface.Kind |
|---|---|
| the loopback flag set | loopback |
link type Ethernet, with /sys/class/net/<name>/wireless or /sys/class/net/<name>/phy80211 present | wireless |
| link type Ethernet otherwise | wired |
| any other link type | other |
A bridge, a veth or a TAP device has link type Ethernet and is wired.
4.1.2 Bus path and driver #
The bus path comes from sysfs. netd resolves
/sys/class/net/<name>/device and walks up from it to the nearest
ancestor whose name is a PCI address (dddd:bb:dd.f), and names it
pci-<address>. A device with no PCI ancestor is named by the device's
own directory name. A link with no device (a virtual device) has no bus
path. A virtio NIC sits one level below its PCI function, so it is named
by the function.
The driver is the name the device/driver link points at
(virtio_net, for example), and empty when there is none.
Both are read once, when netd first sees the link.
4.1.3 The interface id #
The interface id is a SHA-1 digest of the bytes peios-netd-ifid|, then
the bus path if there is one, then |, then the six MAC bytes — or, for
a link with no 6-byte hardware address, its name. The first 16 bytes of
the digest are stamped as a version 5, RFC 4122 variant UUID and written
in lower-case hyphenated form.
So the same card in the same slot has the same id on every boot, however the kernel names it, and a renamed link keeps its id. A virtual device has no bus path, so its id follows its MAC alone.
The id is the key of the interface's record under Interfaces\ (§8.1),
the Interface.Id fact, the key of the RFC 7217 address derivation
(§6.2), and the basis of its DHCP IAID (§5.7).
4.1.4 Appearing and disappearing #
When netd first sees a link, it reads the link's identity, logs
interface <name> (<path>) is <id>, and, unless it is the loopback,
writes accept_ra = 0 for it (§6.1).
When a link disappears from the kernel's dump, netd drops it and
everything it held: its clients, lease, network and judgement. Nothing is
sent on the wire. Its record under Interfaces\ stays.
4.1.5 Carrier #
netd counts an interface as having carrier when it is administratively up
and the kernel reports IFF_LOWER_UP. When an interface that had
carrier loses it, netd logs interface <name>: carrier lost and:
- stops its DHCPv4 client: a RELEASE is sent if a lease was held, and the lease and any link-local address are forgotten (§5.4);
- stops router discovery and DHCPv6;
- forgets the network identified on it (§7.1), so
Status Networkis cleared and theNetwork.*facts go absent, for netd's own judgement and the kernel's packet layers alike.
The next reconcile then removes the leased and autoconfigured addresses and the routes that depended on them, because nothing desires them any more. Static addresses stay.
When carrier returns, the pass starts the clients again from scratch (§5.1, §6.1), and the DHCPv4 client asks first for the address the network last gave (§5.7).
4.2 Desired state
Peios / Peios technical reference / netd / Interfaces
On every reconcile netd computes, for each interface, what it should look like. That desired state is a link state, an MTU, a set of addresses and a set of routes. It is derived from the interface's verdict, its profile, and its clients' current state. During the first INIT-REBOOT it also includes the provisional IPv4 kernel state described in §2.1; netd keeps no general record of what it applied.
4.2.1 By verdict #
- Loopback, or an
IGNOREd interface: there is no desired state, and netd plans nothing for the interface. DOWN: the link down, with no addresses and no routes.JOIN: the link up, and everything below.
4.2.2 The metric #
Every route netd adds for an interface carries the interface's metric:
the profile's Route.Metric when it is set, otherwise 600 for a
wireless interface and 100 for anything else.
4.2.3 The MTU #
The profile's Mtu.Value when set. Otherwise, with Mtu.Offered, the
lease's MTU (DHCP option 26), else the routers' advertised MTU (§6.1).
Otherwise no MTU is desired and the link's MTU is left as it is.
4.2.4 IPv4 addresses #
- every IPv4 entry of
Address.Static, at its prefix; - the lease's address at the lease's prefix (§5.3), when a lease is held;
- while the first INIT-REBOOT is pending, the requested address at its already-installed prefix (§2.1);
- the link-local address (§5.5) at prefix 16, only when the client has fallen back to one and no lease is held and the profile has no static address of either family.
The order in which they are listed, or written in Address.Static, has
no effect. netd adds the addresses an interface lacks in ascending
address order (§4.3), and the kernel makes an address primary only when
the interface holds no other address in the same subnet, so the first
one added in a subnet is its primary. Of two statics in one subnet written
10.0.0.9/24, 10.0.0.3/24, both added in one pass, 10.0.0.3 is the
primary.
While a lease that carried a broadcast address (option 28) is held, that broadcast is the one netd sets on every IPv4 address it adds to the interface: the lease's own address, and a static added in that time, whatever its subnet (§4.3).
4.2.5 IPv4 routes #
- The default route goes via
Route.Gateway's IPv4 entry when it is set. Otherwise, withRoute.Offered, it goes via the lease's gateway: the first router of option 3, or, when the lease carried classless routes, the gateway of the classless0.0.0.0/0route. With neither, there is no IPv4 default route. - Classless static routes (option 121) with a prefix above zero are
added, each via its gateway, only with
Route.Offered.
During the first INIT-REBOOT, existing netd-marked IPv4 routes accompany
the provisional address when Route.Offered is enabled. The profile's
explicit gateway and current metric still take precedence (§2.1).
Route.Gateway is used as written; netd does not check that the gateway
is on a subnet the interface has. A gateway the kernel cannot reach makes
the route's add fail on every reconcile, logged each time (§11).
4.2.6 IPv6 addresses #
- every IPv6 entry of
Address.Static, at its prefix, preferred, with a prefix route; - every address router discovery currently wants (§6.2): stable-privacy and temporary addresses, each deprecated or not, each with or without a prefix route according to the prefix's on-link flag.
4.2.7 IPv6 default route #
Via Route.Gateway's IPv6 entry when it is set. Otherwise, with
Route.Offered, via the default router router discovery has chosen
(§6.1). With neither, there is no IPv6 default route.
4.2.8 Not desired #
netd never desires the kernel's own IPv6 link-local address, the kernel's prefix routes, or any route without netd's protocol mark. Those are outside what reconciliation compares (§4.3).
4.3 Reconciliation
Peios / Peios technical reference / netd / Interfaces
4.3.1 What netd owns #
On an interface with a desired state:
- Every IPv4 address, including a 169.254 address, and every IPv6 address except a link-local one is netd's. An address netd does not desire is removed, whoever added it.
- A route is netd's only if it carries routing protocol 200
(
RTPROT_NETD). netd stamps it on every route it adds, and removes no route without it. - The kernel's IPv6 link-local addresses (
fe80::/10) are never compared, added or removed. Every up interface has one, and neighbour discovery needs it.
Only routes in the main table, of unicast type and with an output interface, are read from the kernel at all.
4.3.2 The plan #
For each interface with a desired state, netd plans these operations, in this order:
- Link up, if it is desired up and is down.
- MTU, if one is desired, differs from the link's, and is at least 68.
- Address deletions: every address netd owns whose address and prefix length together match no desired address.
- Address additions: every desired address the kernel does not hold with the same flags, in ascending address order, IPv4 before IPv6.
- Route deletions: every protocol-200 route on the interface that is not desired.
- Route additions: every desired route the kernel does not hold.
- Link down, if it is desired down and is up — last, after its addresses and routes have gone.
Addresses compare by address and prefix length, plus the two flags netd sets: deprecated and no-prefix-route. A tentative flag (duplicate address detection still running) is erased before comparing. A change of flags alone is therefore an addition of the same address, which replaces it (below), never a delete and re-add that would reset standing connections.
An IPv4 address's broadcast address is not compared. An address keeps the broadcast it was added with: a lease whose broadcast changes, or a lease that arrives after a static was added, changes nothing on an address the interface already holds.
Routes compare on everything: destination, prefix length, gateway, metric
and protocol. A change of metric or gateway is a deletion and an
addition. For IPv6 routes, configured metric zero uses the kernel default
of 1024: netd sends and compares that effective value, so an unchanged
route converges. IPv4 metric zero and all nonzero metrics are unchanged.
The configured Route.Metric still controls DNS scope ordering.
An interface whose desired state the kernel already matches gets an empty plan, and nothing is sent.
4.3.3 Applying #
Operations are applied one at a time, each as an rtnetlink request with an acknowledgement:
| Operation | Request |
|---|---|
| link up or down | RTM_SETLINK changing only IFF_UP |
| MTU | RTM_SETLINK with IFLA_MTU |
| address add | RTM_NEWADDR with NLM_F_CREATE and NLM_F_REPLACE |
| address delete | RTM_DELADDR |
| IPv4 route add | RTM_NEWROUTE with NLM_F_CREATE and NLM_F_APPEND, main table, protocol 200 |
| IPv6 route add | RTM_NEWROUTE with NLM_F_CREATE and NLM_F_EXCL, main table, protocol 200 |
| route delete | RTM_DELROUTE |
IPv4 route additions append a distinct gateway/interface at the configured
metric. Two joined interfaces may therefore both hold default routes at the
same metric; adding one preserves the other, and withdrawing one removes
only its own route. Exact duplicates remain EEXIST. Equal-metric route
selection follows the kernel's routing behavior; set distinct metrics when
one interface should have a defined preference.
How addresses are phrased:
- An IPv4 address carries
IFA_LOCALandIFA_ADDRESSset to itself, link scope for 169.254/16 and universe scope otherwise. Below a /31 it also carries a broadcast address: the broadcast of the lease the interface holds, if that lease gave one, otherwise the address's own subnet's all-ones address. The lease's broadcast goes on every IPv4 address added while the lease is held, statics included. - An IPv6 address carries universe scope and, when its prefix is not
on-link,
IFA_F_NOPREFIXROUTE. Its lifetimes are netd's to manage, so the kernel is told a valid lifetime of forever and a preferred lifetime of forever, or of zero when the address is deprecated. Source address selection then stops choosing a deprecated address, and connections already using it keep it.
A route with a gateway has universe scope, and one without has link scope. A route carries a metric only when it is above zero.
A failed operation is logged as reconcile: <operation> failed: <error>
and the rest of the plan carries on, so one bad route never stops an
address landing. EEXIST on an add and ENOENT or ESRCH on a delete
mean the kernel got there first, and are not logged.
After every interface's plan is applied, netd dumps the kernel's state again, so readiness and the status reply describe what actually landed.
5.1 The client
Peios / Peios technical reference / netd / Dhcpv4
netd's DHCPv4 client is the dhcp4 crate: the RFC 2131 state machine as a
pure, clock-injected type, driven by netd's loop and sockets.
5.1.1 Starting and stopping #
On every full pass, for each joined interface:
- the interface wants a client when its profile has
Address.Offeredand IPv4 in its families; - it can run one when it is up with carrier.
A running client is stopped (§5.4) in the step of the pass that brings the interface table in line with the kernel's links, before clients are started (§2.2):
- when the link loses carrier or goes down, logged as
interface <name>: carrier lost; - when the interface's outcome changes, logged as the new verdict (§3.3). Any edit to the profile the interface stands in is a change of outcome, so it stops the client even when the edited profile still wants one, and the same pass starts a new one.
Only a change of outcome changes what an interface wants, so a client its interface no longer wants is always stopped the second way. A link that leaves the kernel takes its client with it (§5.4).
An interface that wants and can run a client but has none starts one,
logging interface <name>: dhcp starting, provided:
- the link has a 6-byte hardware address — otherwise no client is started, silently;
- its packet socket opens (§5.6) — otherwise netd logs
no packet socketand tries again on the next pass.
A new client starts with no lease. It is configured with:
| Setting | Value |
|---|---|
chaddr | the interface's MAC |
| client identifier (option 61) | the interface's ClientId (§5.7) |
| hostname (option 12) | the machine's Hostname, only when the profile has Hostname.Announce and Hostname is set |
| randomness | 8 bytes of /dev/urandom XORed with the interface index, seeding transaction ids and jitter |
| a previous address | the RequestedAddress of the interface's network, if one is known; otherwise that of the network named by its Status LastNetwork (§5.7) |
With a previous address the client begins in Rebooting (RFC 2131 §3.2, INIT-REBOOT). Without one it begins in Selecting.
5.1.2 States #
| State | Entered | Leaves on |
|---|---|---|
| Selecting | start without a previous address; a NAK while requesting or rebooting; a request that went stale; a reboot nobody confirmed; a lost lease | an acceptable OFFER |
| Requesting | an OFFER in Selecting | an ACK (Bound), a NAK (Selecting), four unanswered transmissions (Selecting) |
| Rebooting | start with a previous address | an ACK (Bound), a NAK (Selecting), two unanswered transmissions (Selecting) |
| Bound | an ACK | T1 (Renewing) |
| Renewing | T1; or an operator renew while bound, renewing or rebinding | an ACK (Bound), a NAK (lease lost, Selecting), T2 (Rebinding) |
| Rebinding | T2 | an ACK (Bound), a NAK or the lease's end (lease lost, Selecting) |
The status reply shows a client's state only inside its lease, which is
present only while the client holds a lease (§9.2), and net status
prints it on the lease line. So the state shown is always bound,
renewing or rebinding. A client in Selecting, Requesting or Rebooting
holds no lease, and neither shows a state for it. The init state exists
only between a stop and the client being discarded.
A new transaction id is drawn on entering Selecting, Rebooting, Renewing and Rebinding. Requesting keeps the id of the Selecting exchange it came from.
5.1.3 What every message carries #
Every message is a BOOTREQUEST with hardware type 1 and length 6, the
current transaction id, chaddr, and secs set to the whole seconds
since the client started, capped at 65535. Every message except a RELEASE
carries, in this order:
| Option | Content |
|---|---|
| 53 | the message type |
| 61 | the client identifier |
| 57 | maximum message size 1500 |
| 55 | the parameter request list: 1, 3, 6, 12, 15, 26, 28, 42, 51, 58, 59, 119, 121 |
| 12 | the hostname, when configured |
The encoded message ends with option 255 and is padded with zeros to 300 bytes. An option longer than 255 bytes would be cut to 255.
5.1.4 The messages #
| Message | When | Broadcast flag | ciaddr | Option 50 | Option 54 | Sent |
|---|---|---|---|---|---|---|
| DISCOVER | Selecting | set | 0 | the address of a lease that expired, until a NAK or a reboot timeout forgets it | — | broadcast |
| REQUEST | Requesting | set | 0 | the offered address | the offer's | broadcast |
| REQUEST | Rebooting | set | 0 | the previous address | — | broadcast |
| REQUEST | Renewing | clear | the lease address | — | — | unicast to the server |
| REQUEST | Rebinding | clear | the lease address | — | — | broadcast |
| RELEASE | a stop while holding a lease | clear | the lease address | — | the server | unicast to the server |
A RELEASE carries only options 53, 54 and 61.
"Broadcast" and "unicast" are the paths of §5.6: a broadcast leaves the
packet socket from 0.0.0.0 to 255.255.255.255, rebinding included; a
unicast leaves an ordinary UDP socket bound to the lease address.
5.2 Timers
Peios / Peios technical reference / netd / Dhcpv4
Every timer is on netd's monotonic clock.
5.2.1 Retransmission while acquiring #
In Selecting, Requesting and Rebooting each transmission schedules the next one after the current backoff plus a jitter drawn uniformly from −1 s to +1 s, then doubles the backoff, up to 64 s. The backoff starts at 4 s on entering each of the three states. So transmissions follow at roughly 4, 8, 16, 32, 64, 64, … seconds.
| State | Transmissions before it gives up | Then |
|---|---|---|
| Selecting | never gives up | — |
| Requesting | four REQUESTs (about 0, 4, 12 and 28 s) | at the next timer, about 60 s, Selecting with a new DISCOVER |
| Rebooting | two REQUESTs (about 0 and 4 s) | at the next timer, about 12 s, Selecting with a new DISCOVER and no option 50 |
5.2.2 "Nobody answered" #
On the third retransmission of a DISCOVER — the fourth DISCOVER, about
28 s after the first — the client reports that no offer came. netd then
records the interface warning asked for an address; nobody answered and,
when the profile wants it, falls back to a link-local address (§5.5). The
client keeps discovering. The warning survives full reconciliation passes,
including the pass triggered by adding the link-local fallback. It clears
when a lease binds or that DHCP client stops (for example, on carrier loss
or a profile change). A rule-conflict warning takes display precedence;
rejudging an unchanged client does not erase its discovery warning.
In source 23af1f6,
the report is made at most once between client starts: the flag is reset
only when a client is started. A client that reported it, then got a lease,
then lost it, does not report it again, so it does not fall back to a
link-local address a second time.
The proposed source correction resets the flag after a valid ACK produces a lease. If that lease is later lost, unanswered discovery can report again at the same fourth-DISCOVER threshold, allowing the profile's link-local fallback to be requested again. An uninterrupted unanswered acquisition still reports only once: a malformed ACK, a NAK while Requesting, or a Requesting timeout does not rearm it. This describes the proposed source behavior, not a released package or an installed image; check the installed build before relying on repeat fallback.
5.2.3 Lease boundaries #
A lease's T1, T2 and length are measured from the moment its ACK arrived, and are fixed when the lease is read (§5.3).
- At T1 a Bound client enters Renewing and sends a unicast REQUEST.
- At T2 a Renewing client enters Rebinding and sends a broadcast REQUEST.
- At the lease's end a Rebinding client loses the lease (§5.4) and enters Selecting.
A boundary is acted on before any retransmission due at the same moment.
5.2.4 Retransmission while renewing and rebinding #
Each REQUEST in Renewing or Rebinding schedules the next for half the time remaining to the state's next boundary (T2 while renewing, the lease's end while rebinding), but never sooner than 60 s from now. With a short lease the 60 s floor means a state's boundary usually arrives before its second REQUEST: a 20 s lease renews once at 10 s and rebinds once at 17 s, then lapses at 20 s.
An ACK in either state starts the lease's clock again from that moment.
5.3 Replies and leases
Peios / Peios technical reference / netd / Dhcpv4
5.3.1 Which replies are looked at #
A received message is ignored unless all of these hold:
- it is a BOOTREPLY;
- its transaction id is the client's current one;
- its
chaddris the interface's MAC; - it carries a message type (option 53) the client knows.
5.3.2 The server lock #
Once a server has been chosen — the server of the lease held, or else the server identifier of the offer being requested — an ACK or NAK is accepted only if its option 54 names that server. A stranger who guesses the transaction id cannot take a lease away or substitute one.
In Rebooting, with no lease and no offer, no server has been chosen, so an ACK or NAK from any server is accepted.
5.3.3 What each state accepts #
| State | Message | Result |
|---|---|---|
| Selecting | OFFER with a non-zero yiaddr and a server identifier | taken: Requesting. The first acceptable OFFER wins; there is no comparison between offers. |
| Selecting | OFFER without either | ignored |
| Requesting, Rebooting, Renewing, Rebinding | ACK that reads as a lease (below) | Bound, with the lease |
| Requesting, Rebooting, Renewing, Rebinding | ACK that does not read as a lease | ignored; retransmission continues |
| Requesting, Rebooting | NAK | Selecting; the previous address and the offer are forgotten |
| Renewing, Rebinding | NAK | the lease is lost (§5.4); Selecting |
| any other combination | ignored |
An ACK in Renewing or Rebinding that grants a different address replaces the lease. The next reconcile removes the old address and adds the new.
5.3.4 Reading an ACK #
An ACK is a lease only when it has:
- a non-zero
yiaddr; - a server identifier (option 54);
- a lease time (option 51) of exactly 4 bytes, and at least 4 seconds.
A lease time under 4 s is refused: it cannot hold T1 < T2 < lease, and a server asking for it wants the client to spin.
5.3.4.1 The prefix #
From the subnet mask (option 1) when it is a non-zero run of contiguous ones. Otherwise, absent or malformed, from the address's class: /8 for a first octet up to 127, /16 up to 191, /24 above.
5.3.4.2 Routes #
Classless static routes (option 121, RFC 3442) are read as a sequence of
prefix length, the significant destination octets, and a 4-byte gateway.
In source 23af1f6,
a prefix length above 32, or an entry running past the end of the
option, makes the whole ACK fail to read as a lease: it is ignored like
any other unusable ACK.
The proposed source correction instead discards the entire malformed optional route list, including any valid entries before the malformed one, and keeps an otherwise-valid lease. The existing router-option (3) fallback then applies; without that option, the lease has no routes or gateway. This recovery does not relax whole-message or option framing, the mandatory lease requirements above, or the client and server-identity checks. It describes the proposed source policy, not a released package or installed image, and does not assert an RFC requirement to recover malformed route lists.
When the decoded classless route list is nonempty, the router option (3) is ignored. The lease's gateway is the first router of option 3, or, when classless routes are present, the gateway of the classless route with prefix 0.
5.3.4.3 T1 and T2 #
- T1 is option 58 when it is above 0 and below the lease time minus 2. Otherwise it is half the lease time.
- T2 is option 59 when it is above T1 and below the lease time. Otherwise it is seven-eighths of the lease time, rounded down, raised to at least T1 + 1 and capped at the lease time minus 1.
So T1 < T2 < the lease time always holds, whatever the server sent.
5.3.4.4 Other options kept #
| Option | Kept as |
|---|---|
| 6 | DNS servers, every 4-byte entry |
| 15 | the domain name: the bytes up to the first NUL; dropped if not UTF-8 |
| 119 | the domain search list, RFC 1035 names with compression pointers followed, at most 16 jumps per name |
| 12 | the hostname offered: the bytes up to the first NUL; dropped if not UTF-8 |
| 26 | the MTU, when it is at least 68 |
| 28 | the broadcast address |
| 42 | NTP servers, every 4-byte entry |
Where an option appears twice, its first occurrence is used. Nothing else in the ACK is kept.
How each of these is used — and whether, by the profile — is in §4.2 (addresses, routes, MTU), §8.3 (DNS and NTP) and §8.4 (the hostname).
5.3.5 The record of a lease #
A lease is logged as interface <name>: lease <address>/<prefix> from <server> for <seconds>s. Binding clears the interface's link-local
address and its warning, and marks the interface for re-judgement,
because the network might now be identifiable (§7.1).
5.4 Losing a lease
Peios / Peios technical reference / netd / Dhcpv4
5.4.1 Lost by the network #
A lease is lost when a Rebinding client reaches the lease's end
(§5.2), or when a NAK arrives while renewing or rebinding (§5.3). The
client forgets the lease and enters Selecting, and netd acts on the
interface's Address.OnExpiry:
Drop(the default): netd logsinterface <name>: lease lostand forgets the lease. The next reconcile removes the address and the routes that came with it.Keep: netd logsinterface <name>: lease lost; keeping the address (Address.OnExpiry = Keep)at warning level and goes on desiring the lost lease — its address, gateway and routes — while the client looks for a new one. The kept lease is replaced the moment a new one binds.
With Keep, the status reply's lease describes the client, not the
kept lease: it disappears with the client's lease, while the address
stays on the interface.
Keep lasts while the client lasts. A stop (below) forgets the kept lease
as it would any other.
5.4.2 Stopped by netd #
netd stops a client when:
- the interface's outcome changes: another verdict, another profile, or any value of its profile edited (§3.3);
- the interface loses carrier (§4.1);
- the profile no longer wants a client (§5.1).
A client in Bound, Renewing or Rebinding sends a RELEASE to its server first: unicast from the lease address (§5.1, §5.6). After a carrier loss that RELEASE goes out on a link with no carrier, so the server never sees it. The client is then discarded, and the interface's lease and link-local address are forgotten, so the next reconcile removes both addresses.
A link that disappears from the kernel takes its client with it and sends nothing.
5.4.3 The operator's renew #
The control request renew (§9.2), and so net renew <interface>, moves
a client in Bound, Renewing or Rebinding into Renewing at once: a new
transaction id and a unicast REQUEST to the lease's server. The lease is
not released first, and its address stays on the interface throughout.
A client that holds no lease is left as it is, and the request still
answers ok.
5.5 Link-local fallback
Peios / Peios technical reference / netd / Dhcpv4
With Address.LinkLocal in the interface's profile, an interface whose
DHCPv4 discovery goes unanswered gets an IPv4 link-local address
(RFC 3927), so two machines on a cable with no server can still reach
each other.
5.5.1 When #
At the client's "nobody answered" report (§5.2), about 28 s after
discovery began, netd logs `interface
The address is desired at prefix 16 only while no lease is held and the
profile has no Address.Static entry of either family (§4.2). A profile
with a static address therefore never shows one, even after the report.
5.5.2 Which address #
A pure function of the interface, so it is the same every time:
- the seed is the last four bytes of the MAC, read as a big-endian 32-bit number, or, for a link with no MAC, the interface index times 2654435761, wrapping at 32 bits;
- the host part is 256 plus the seed modulo 64768;
- the address is
169.254.<host / 256>.<host % 256>.
That lands in 169.254.1.0 to 169.254.254.255, the range RFC 3927 allows for self-assignment.
netd does not probe for a conflicting holder or announce the address (RFC 3927 §2.2 and §2.4). Two machines whose MACs share their last four bytes choose the same address.
5.5.3 When it goes #
The link-local address is forgotten when a lease binds, and when the client is stopped (§5.4). The next reconcile removes it, because netd owns every IPv4 address on a joined interface (§4.3).
5.6 Sockets
Peios / Peios technical reference / netd / Dhcpv4
Before an interface has an address, nothing but a packet socket can send
from 0.0.0.0. So every DHCPv4 broadcast leaves through AF_PACKET, with
an IPv4 and UDP header netd builds itself, and every reply is read from
the same socket.
5.6.1 The packet socket #
One per running client: AF_PACKET, SOCK_DGRAM, protocol ETH_P_IP,
bound to the interface. In the proposed corrected source, a classic BPF filter
passes only an IPv4 UDP datagram that is not a fragment and is addressed
to port 68. It refuses both a non-zero fragment offset and the
more-fragments flag; the don't-fragment flag is allowed. Everything read
is checked again for an IPv4 header and UDP port 68, then decoded as a
DHCP message.
In versions 0.1.7 (described by this manual) and 0.1.8, the filter tests only the fragment offset: a later fragment is refused but a first fragment passes and is decoded as if it were the whole datagram. The proposed source correction above is not present in those releases.
The socket sees every frame on the interface that passes the filter, so it receives both broadcast replies and replies unicast to the lease address.
5.6.2 The broadcast path #
DISCOVER, REQUEST in Requesting and Rebooting, and REQUEST in Rebinding go
out on the packet socket to Ethernet ff:ff:ff:ff:ff:ff, as one IPv4
datagram:
| Field | Value |
|---|---|
| source | 0.0.0.0 |
| destination | 255.255.255.255 |
| TOS | 0x10 |
| identification | 0 |
| flags | don't-fragment |
| TTL | 64 |
| UDP ports | 68 → 67 |
| UDP checksum | 0 (none, which IPv4 permits) |
5.6.3 The unicast path #
REQUEST in Renewing, and RELEASE, go out on a transient AF_INET UDP
socket with SO_REUSEADDR and SO_BINDTODEVICE set to the interface,
bound to the lease address on port 68, sent to the server on port 67, and
closed. The kernel resolves the server's link-layer address. The reply
is read from the packet socket.
Binding port 68 meets the kernel's port reservation for ports 1 to 1023, which the shipped seed grants to SYSTEM, netd's identity.
5.6.4 The absorber #
At startup netd binds one more UDP socket, to 0.0.0.0:68 with
SO_REUSEADDR, and discards whatever arrives on it. Its only purpose is
that a server's unicast reply finds a bound port, so the kernel does not
answer it with ICMP port-unreachable. The packet sockets see the same
datagrams. If it cannot be bound, netd runs without it (§2.1).
5.7 Identity and memory
Peios / Peios technical reference / netd / Dhcpv4
A DHCP server recognises a client by its client identifier, and a reservation on the server matches on it. netd keeps the identifier stable across boots and NIC swaps in the same slot, and remembers per network what it was given.
5.7.1 The DUID #
The machine's DHCP unique identifier, shared by DHCPv4 and DHCPv6 (RFC 4361), is decided once per netd process, the first time a client needs it:
- the registry's
Machine\System\Network Duid, as hex, when it is set and parses; - otherwise the file
/var/state/netd/duid, when it holds at least 4 bytes; - otherwise a new DUID-LL:
00 03 00 01followed by the MAC of the interface whose client is starting. It is written to/var/state/netd/duid; a failure to write is logged and changes nothing else.
When the registry's Duid is absent, empty or not hex, the result of 2
or 3 is written to it, as colon-separated lower-case hex, replacing a
value that was not hex.
Hex in Duid (and ClientId) can be written with :, - or spaces
between digits, or none. A value with any other character, an odd number
of digits, or no digits is not hex.
5.7.2 The client identifier #
Per interface, Interfaces\<id> ClientId:
- when the value is set and is non-empty hex, it is sent as written;
- when it is absent, the identifier is generated and written back:
ff, then a 4-byte IAID, then the DUID. The IAID is the interface id's text folded into 4 bytes by XOR, byte i of the text into byte i mod 4; - when it is set but not hex, netd logs
<id>: ClientId "<value>" is not hex; regenerating, where<id>is the interface id, generates one, and overwrites the value.
ClientId is a value both netd and the operator write. Writing it is
how an operator makes netd present the identifier a server reservation
expects. It is read when a client starts, so it takes effect at the next
start (a carrier loss, a profile edit, a restart of netd).
5.7.3 The address a network gave #
While an interface holds a lease on an identified network (§7.1), netd
writes the lease address to that network's Networks\<id> RequestedAddress whenever it differs from what is there.
When a client starts, it takes as its previous address (§5.1):
- the
RequestedAddressof the network currently identified on the interface, when there is one; otherwise - the
RequestedAddressof the network the interface'sStatus LastNetworknames (§8.1).
With one, the client opens with an INIT-REBOOT REQUEST for it. A server that agrees answers with an ACK and the machine keeps the address in one round trip. One that refuses answers with a NAK and the client discovers afresh. Silence for two transmissions does the same (§5.2).
RequestedAddress is also the operator's: writing it is a soft
reservation, an address the client asks for first and the server can
refuse.
Nothing about a lease itself — its timers, its server, its options — is persisted. A machine that reboots asks for its address again.
6.1 Router discovery
Peios / Peios technical reference / netd / Ipv6
6.1.1 The kernel's RA handling is off #
netd writes 0 to /proc/sys/net/ipv6/conf/<name>/accept_ra for all,
for default, and for every interface listed there, at startup before
any link is brought up (§2.1). It writes it again for each non-loopback
interface the first time it sees it. A missing file — a kernel without
IPv6 — is a warning only for all.
The kernel still creates link-local addresses and answers neighbour solicitations. That is the protocol, not policy. Everything an advertisement could configure is netd's to decide.
6.1.2 When router discovery runs #
On every full pass, for each joined interface:
- it wants router discovery when its profile has
Address.Offeredand IPv6 in its families; - it can run it when it is up with carrier and the kernel's link-local address on it has finished duplicate address detection. A tentative address cannot source a solicitation.
Running discovery that is no longer wanted or possible is stopped (ipv6 stopping): the engine and any DHCPv6 client are discarded, and the next
reconcile removes their addresses and route. Nothing is sent on the
wire, because neither protocol has a goodbye. An interface that wants and
can run it starts it (soliciting routers), provided its ICMPv6 socket
opens — otherwise netd logs no icmpv6 socket and tries again on the
next pass.
6.1.3 The socket #
One raw ICMPv6 socket per interface, bound to it with SO_BINDTODEVICE,
filtered to receive only type 134 (router advertisement), sending with a
multicast hop limit of 255, and asking for each received packet's hop
limit. The kernel computes and checks ICMPv6 checksums.
6.1.4 Solicitation #
A router solicitation is type 133, code 0, with a source link-layer
address option carrying the interface's MAC, sent to ff02::2 with hop
limit 255.
The first is sent as soon as discovery starts. The interval is 4 s for the next two, then doubles before each later one, up to 3600 s. Each interval carries a jitter of ±10%. So solicitations go at about 0, 4, 8, 16, 32, 64, … s.
Soliciting stops when an advertisement arrives with a router lifetime above zero. It begins again, from the first interval, as soon as every default router has expired.
6.1.5 Which advertisements count #
An advertisement is dropped unless it arrived with hop limit 255 and from a link-local source (RFC 4861 §6.1.2). A lower hop limit has crossed a router, and a non-link-local source is not a router on this link.
It is then decoded. The whole advertisement is refused when:
- it is shorter than 16 bytes, or its type is not 134 or its code not 0;
- an option has length zero, or runs past the end of the message.
Within an accepted advertisement, an option that does not parse is skipped and the rest stand:
| Option | Accepted when | Limits |
|---|---|---|
| Prefix information (3) | 32 bytes long; prefix length ≤ 128; the prefix neither multicast nor link-local | the first 16 per advertisement |
| MTU (5) | 8 bytes long | |
| RDNSS (25) | at least 24 bytes, and a whole number of addresses | 16 servers per option; unspecified, multicast and loopback addresses dropped |
| DNSSL (31) | at least 16 bytes | 16 domains per option; names uncompressed, lower-cased, labels of printable ASCII, 253 characters at most; a malformed name ends the list |
Any other option is ignored. So are the advertisement's hop limit, reachable time and retransmission timer.
6.1.6 What an advertisement does #
- Router lifetime above zero: the source becomes, or stays, a default router until that many seconds from now. Zero: the source is removed as a default router. Its prefixes, servers and domains stay.
- M or O flag: stateless DHCPv6 is wanted (§6.3). Once set it stays set until discovery restarts; a later advertisement without the flags does not withdraw it.
- MTU option: remembered as the link's advertised MTU. It is used only
with
Mtu.Offered, only when no lease gave one, and only when it is at least 68 (§4.2, §4.3). - Prefixes: §6.2. RDNSS and DNSSL: §6.3.
Whenever what the interface should carry changes — an address, its deprecation, the default router, the servers, the domains, the MTU, the DHCPv6 flag — netd runs a full pass. That includes a change caused by time alone: a lifetime running out is a change.
6.1.7 The default router #
Of the routers whose lifetime has not run out, netd uses the one with the numerically lowest link-local address, so the choice is stable while the set is. Router preference (RFC 4191) is not read.
6.2 Addresses
Peios / Peios technical reference / netd / Ipv6
6.2.1 Which prefixes form addresses #
A prefix information option forms addresses only when its A flag is set, its prefix length is exactly 64, and its preferred lifetime does not exceed its valid lifetime. Any other prefix option is ignored whole. A prefix with only the L flag gets no address, and no on-link route, because the kernel's RA handling is off.
A prefix seen for the first time is taken only with a valid lifetime above zero.
6.2.2 The stable address #
Each accepted prefix gets one stable address (RFC 7217): the prefix's first 64 bits, followed by the first 8 bytes of a SHA-1 digest of:
- the bytes
peios-ndp-stable-iid|; - the machine's 32-byte secret;
- the prefix's first 8 bytes;
- the interface id's text;
- a one-byte counter, starting at 0.
When the resulting interface identifier is reserved (RFC 5453: all zeros,
0200:5eff:fe00::/40, or fdff:ffff:ffff:ff80 to
fdff:ffff:ffff:ffff), the counter is incremented and the digest taken
again, up to 8 times.
So the same machine on the same network has the same address on every boot, a different network sees an unrelated address, and no MAC address ever appears in it. The address is a function of the interface id, so a kernel rename does not change it either.
6.2.2.1 The secret #
The 32-byte secret is read from /var/state/netd/secret the first time an
interface starts router discovery. When the file does not hold exactly
32 bytes, a new secret is drawn from /dev/urandom and written there; a
failure to write is logged and the new secret used for the rest of the
process's life. Losing the secret renumbers the machine, and does
nothing else.
6.2.3 Lifetimes #
A lifetime of 0xffffffff is forever. Others count from the moment the
advertisement arrived.
When an advertisement updates a prefix already held:
- the on-link flag and the preferred lifetime are taken as advertised;
- the valid lifetime uses the two-hour rule from RFC 4862 §5.5.3(e), with the infinite-lifetime exception described below:
| Advertised valid lifetime | Remaining valid lifetime | Result |
|---|---|---|
| above two hours, or above what remains | any finite | the advertised lifetime |
| two hours or less, not above what remains | two hours or less | unchanged |
| two hours or less, not above what remains | above two hours | two hours from now |
| above two hours | forever | the advertised lifetime |
| two hours or less | forever | forever |
The table records the 0.1.7 behaviour this manual describes; it is also present in netd 0.1.8 source. The final row is a known deviation: infinity is above two hours, so RFC 4862 §5.5.3(e)(3) requires two hours from the new advertisement, including when the advertised lifetime is zero. A source build containing the proposed source correction uses that deadline; it still accepts advertised lifetimes above two hours, including infinity, and takes the preferred lifetime as advertised. This source correction does not establish that an installed package or image has changed.
When a prefix's preferred lifetime runs out, its addresses are deprecated: kept on the interface for standing connections, with a preferred lifetime of zero so that nothing new chooses them (§4.3). When its valid lifetime runs out, the prefix and its addresses are removed.
6.2.4 Flags on the address #
An address in a prefix advertised on-link (L set) is added normally,
so the kernel adds the prefix route. One advertised off-link is added
with IFA_F_NOPREFIXROUTE, so the kernel adds no prefix route for it.
6.2.5 Temporary addresses #
With Address.Temporary in the profile, each prefix also carries
temporary addresses (RFC 8981):
- one is formed with the prefix when the prefix arrives with a preferred lifetime above zero;
- its interface identifier is random, avoiding the reserved identifiers, the stable address and the prefix's other temporaries;
- its preferred lifetime is one day minus a random 0 to 599 s, so a rack of machines does not regenerate together, and its valid lifetime is two days. Each is cut to the prefix's own lifetimes.
When the newest temporary is no longer preferred while the prefix still is, a new one is formed. The old one stays, deprecated, until its valid lifetime ends. A prefix keeps at most four temporaries, dropping the oldest beyond that.
A temporary is deprecated when its own preferred lifetime or its prefix's has run out, and removed when its own valid lifetime or its prefix's has.
Address.Temporary is read when router discovery starts. Changing it is a
profile edit, which restarts discovery (§3.3).
6.3 DNS and DHCPv6
Peios / Peios technical reference / netd / Ipv6
6.3.1 RDNSS and DNSSL #
Each server of an RDNSS option, and each domain of a DNSSL option, is held
for the option's lifetime from the moment the advertisement arrived.
0xffffffff is forever, and a lifetime of zero removes that server or
domain at once. A server or domain advertised again has its lifetime
replaced.
6.3.2 Stateless DHCPv6 #
Peios gets IPv6 addresses by SLAAC alone. DHCPv6 is used for configuration only: an INFORMATION-REQUEST (RFC 8415 §18.2.6) for DNS servers and search domains. The stateful address exchange is not implemented, and an advertisement's M flag is treated as O.
6.3.2.1 When it runs #
A DHCPv6 client starts on an interface once its router-discovery engine
has seen an advertisement with M or O (§6.1), provided the interface has
a non-tentative link-local address and a MAC, and its socket opens. netd
logs interface <name>: dhcpv6 information request. It stops with router
discovery.
6.3.2.2 The socket #
UDP, bound with SO_BINDTODEVICE to the interface and to its link-local
address on port 546, so two interfaces' clients never share a port.
Messages go to ff02::1:2 port 547.
6.3.2.3 The request #
An INFORMATION-REQUEST (type 11) with a random 3-byte transaction id and these options:
| Option | Content |
|---|---|
| 1, client identifier | the machine's DUID (§5.7) |
| 8, elapsed time | hundredths of a second since this exchange began, capped at 65535 |
| 6, option request | 23 (DNS servers), 24 (domain list), 32 (information refresh time) |
The first is sent when the exchange begins. Retransmissions follow after 1 s, doubling up to 3600 s, each with a jitter of ±10%, and never stop until a reply is accepted.
6.3.2.4 The reply #
A datagram is accepted only while an exchange is running, and only when:
- it is a REPLY (type 7) with the exchange's transaction id;
- it carries a client identifier equal to the machine's DUID;
- it carries a non-empty server identifier.
Anything else is ignored, and retransmission continues. From an accepted reply, option 23 gives the DNS servers — when its length is a whole number of addresses, up to 16, with unspecified, multicast and loopback addresses dropped — and option 24 the search domains, read as a DNSSL's are (§6.1).
6.3.2.5 Refresh #
After a reply, the client asks again after the reply's information refresh time (option 32), or one day when there is none, but never sooner than 600 s. Each refresh is a new exchange with a new transaction id.
An answer that differs from the last logs dhcpv6 answered and leads to a
reconcile and a publish. An identical one changes nothing.
6.3.3 Where these go #
Advertised and DHCPv6 servers and domains reach name resolution only
through the profile's Dns.Offered, including link-local servers with
the interface supplied by their DNS scope (§8.3). They are always part of what a network has shown
for its identity and its record (§7).
7.1 Identification
Peios / Peios technical reference / netd / Networks
netd has to say "this is the same network as last time" with nothing on the wire that says so. The identity is a guess from what the network has offered, recorded with every signal it was made from, so that a later proof can replace the guess without changing the record's key.
7.1.1 Signals #
What a joined interface's network has shown, at the moment of the pass:
| Signal | From |
|---|---|
| kind | the interface's Interface.Kind |
| server | the DHCPv4 lease's server identifier |
| subnet | the lease address masked to the lease prefix |
| gateway | the lease's gateway (§5.3) |
| router | the default router router discovery chose (§6.1) |
| prefixes | the subnet, then every non-deprecated autoconfigured address's prefix, each once |
| DNS servers | the lease's, then the routers' RDNSS servers, link-local ones included |
7.1.2 The identity #
- With a lease (a server and a subnet), the basis is
dhcp:<server>|<subnet>/<prefix>. - Otherwise, with a default router and at least one autoconfigured
prefix, the basis is
ra:<router>|<prefix>/<length>, using the first prefix. - Otherwise there is no identity yet: nothing has been offered, or the interface is static only.
The network id is a SHA-1 digest of the bytes peios-netd-network|, the
kind, |, and the basis, stamped and written as a version 5 UUID exactly
as an interface id is (§4.1).
So two leases from the same server in the same subnet are the same network, whatever the address. A different server, a different subnet, or a different kind of interface is a different network. An IPv4 lease always wins over the IPv6 basis, so a dual-stack network is identified by its DHCPv4 server.
7.1.3 When #
Identification is the first step of every full pass (§2.2). It runs only on a joined interface that is up with carrier, and only when the signals yield an identity.
For each identity it creates or refreshes the network's record (§7.2) and
reads back what the operator wrote on it. While a lease is held, it
writes the lease address to the record's RequestedAddress (§5.7).
7.1.4 A newly identified network #
When the identity differs from the network the interface already had —
including when it had none — netd logs interface <name>: network <name or id>, with (trust <trust>) when the record has one. It then
marks the interface for re-judgement, so the same pass judges it with the
Network.* facts present (§3.3). A rule conditioned on Network.Name
or Network.Trust takes effect then. If it moves the interface to
another profile, the interface's clients restart under that profile
(§3.3).
The network an interface stands on is kept while it has carrier, even when a profile switch restarts the clients that identified it. A switch that its own identification triggered would otherwise forget the network and switch back. Losing carrier forgets it (§4.1).
The interface's Status Network names the identified network (§8.1). The
kernel reads it to give the packet layers their Network.* facts
(PKM §6.5), so it never names a network the link is not on.
7.2 Records
Peios / Peios technical reference / netd / Networks
Every network a joined interface has been identified on gets a record,
Machine\System\Network\Networks\<id>. Records are never deleted by
netd.
7.2.1 What netd writes #
On every identification (§7.1), netd creates the record if it is missing
and writes these values under its Status subkey, each only when it
differs from what is there:
| Value | Type | Content |
|---|---|---|
Kind | REG_SZ | the interface kind it was seen on |
Server | REG_SZ | the DHCPv4 server; removed when there is none |
Gateway | REG_SZ | the lease's gateway; removed when there is none |
Router | REG_SZ | the IPv6 default router; removed when there is none |
Prefixes | REG_MULTI_SZ | the subnet and the autoconfigured prefixes, CIDR form |
DnsServers | REG_MULTI_SZ | the DNS servers offered, either family |
LastSeen | REG_SZ | the wall-clock time of the identification, in Unix seconds |
LastInterface | REG_SZ | the kernel name of the interface it was seen on |
LastSeen is rewritten on every pass that identifies the network, so a
record's Status changes whenever netd runs a pass on a link that is on
it.
Status is created with the descriptor O:SYG:SYD:P(A;;KA;;;SY)(A;;KR;;;WD):
SYSTEM has full control and Everyone has read access. It is protected, so the
hive root's inheritable grants do not reach it, and a hand edit is
refused rather than silently reverted at the next pass. The descriptor is
set only when netd creates the key; a Status key that already exists
keeps whatever descriptor it has.
7.2.2 What the operator writes #
On the record itself, not under Status:
| Value | Read as | Meaning |
|---|---|---|
Name | REG_SZ, empty is unset | the Network.Name fact |
Trust | REG_SZ, empty is unset | the Network.Trust fact |
RequestedAddress | REG_SZ, an IPv4 address | the address the next DHCPv4 client asks for first (§5.7); netd writes it too |
Name and Trust are read at every identification, so an edit reaches
the interface layer on the pass that follows the registry write (§2.3).
They are opaque: netd compares nothing about them and gives them no
vocabulary.
The kernel reads Name and Trust itself, from the record that an
interface's Status Network names (PKM §6.5).
8.1 The inventory
Peios / Peios technical reference / netd / What Netd Publishes
8.1.1 The record #
Every non-loopback interface netd has seen has a key
Machine\System\Network\Interfaces\<id>, named by its interface id
(§4.1). netd creates it on the first pass that sees the interface. The
key is the operator's, apart from ClientId, a value both write (§5.7).
netd's findings are under its Status subkey.
Records are never deleted. The record of a card that has been removed stays, with what netd last wrote.
8.1.2 Status #
| Value | Content | Present |
|---|---|---|
Name | the kernel's name for the link | always |
Kind | wired, wireless or other | always |
Mac | the hardware address, lower-case, colon-separated | when the link has one |
Path | the bus path | when there is one |
Driver | the driver | when there is one |
Verdict | JOIN, IGNORE or DOWN | when a rule spoke; removed when the backstop answered or rules tied |
Rule | the path of the rule that spoke, or the tied rules as <a> vs <b> | when a rule spoke or rules tied; removed when the backstop answered |
Profile | the profile path as written | JOIN only |
Readiness | the interface's level (§8.2) | JOIN only |
Network | the id of the network identified on the link now | while one is identified; removed when the carrier goes |
LastNetwork | the id of the network the link last stood on | from the first identification; never removed |
Every value is REG_SZ. A value is written only when it differs from
what is there, and removed only when it is there, so a pass that changes
nothing writes nothing to the registry.
Status is refreshed on every full pass for every non-loopback interface,
and again at each publish (§2.2) for joined ones, so Readiness follows
a reconcile that moved it without any link event.
Status Network is read by the kernel to give the packet layers their
Network.* facts (PKM §6.5). It names the network only while netd has
identified one on the link (§7.1).
8.1.3 The descriptor #
Status is created with the descriptor
O:SYG:SYD:P(A;;KA;;;SY)(A;;KR;;;WD): SYSTEM has full control, Everyone
has read access, and the DACL is protected, so the hive root's inheritable grant
to Administrators does not reach it. Only netd, as SYSTEM, can write
there. An administrator's hand edit is refused at the write, not
reverted at the next pass. The descriptor is set only when netd creates
the key.
8.1.4 The machine's values #
On Machine\System\Network itself netd writes:
Readiness, the machine level (§8.2);Duid, when it was absent and netd generated or read one (§5.7).
8.2 Readiness
Peios / Peios technical reference / netd / What Netd Publishes
"The network is up" means different things to different services, so
netd reports a level, not a flag. Levels are ordered:
absent < link < addressed < routed.
8.2.1 An interface's level #
From the kernel's state after the last reconcile:
| Level | When |
|---|---|
absent | the interface is not up, or has no carrier |
link | up with carrier, and no usable address |
addressed | a usable address, and no default route |
routed | a usable address, and a default route on the interface |
A usable address is any IPv4 address other than a loopback one —
a 169.254 link-local address counts, because it is the outcome of address
acquisition — and any IPv6 address that is neither loopback nor
link-local. Every up interface has an IPv6 link-local address, so
counting one would make addressed mean nothing. Deprecated and
tentative addresses count.
A default route is any route with prefix length 0, of either family, in the main table, out of the interface. It need not be one of netd's.
8.2.2 The machine's level #
The highest level among joined interfaces, or absent when none is
joined. An IGNOREd or DOWN interface contributes nothing, whatever
state it is in.
8.2.3 Publishing it #
At every publish (§2.2) netd computes the machine level. When it differs
from the last one published — and at the first publish after startup,
which precedes READY=1 — netd:
- logs
machine readiness is <level>; - sends the complete readiness set below in one datagram on the notify
socket named by
NOTIFY_SOCKET; - writes
ReadinessonMachine\System\Network.
The notify set is determined by the highest machine level:
| Machine level | LEVEL values, in wire order |
|---|---|
absent | absent |
link | link |
addressed | link, addressed |
routed | link, addressed, routed |
Each value has its own LEVEL= line in the same datagram. The highest
level comes last, so an older manager that retains only the last value
continues to see the highest level. Upgrade peinit before or alongside
netd to make every member available to dependents.
The set is sent only on a change. The notify channel is lossy by design and unacknowledged (PSPU §4.16), and a level is a statement of a current condition, so a lost one is corrected by the next change.
Each joined interface's own level is written as Status Readiness
(§8.1).
8.2.4 What peinit makes of it #
peinit records the set netd last sent against the sending service, and
resolves the network role to that service. A dependency on
network:<level> waits until the service is active and its published set
contains the requested level (peinit TRM §7.5).
A DHCPv4 lease that installs an address and default route in the same
pass takes the highest machine level directly from link to routed.
The notify datagram contains link, addressed and routed, so both
network:addressed and network:routed dependents can start. peinit needs
no knowledge of network level ordering; netd publishes every condition
that holds. Losing the link replaces this set with absent, preventing
new starts on any of the three previous conditions. Dependents already
running keep running.
The registry, log and control status continue to report the highest
machine level. net wait <level> polls over the control socket and
succeeds when that level is at least the one named (§10.1).
8.3 The resolver channel
Peios / Peios technical reference / netd / What Netd Publishes
The channel's contract — that a resolver subscribes, and receives whole snapshots, never deltas — is PSPU §6.9. This article is netd's half of it.
8.3.1 Subscribing #
A subscribe request on the control socket (§9.2), which needs the query
right, is answered with a snapshot at once, and the connection is kept:
switched to non-blocking and added to the subscriber list. It is the one
request that does not end its connection.
The first snapshot is the last one published, or, before any has been, one computed on the spot.
8.3.2 When snapshots are sent #
At every publish (§2.2) netd computes the snapshot and compares it with the last one sent. If it differs, it is sent to every subscriber. If it is equal, nothing is sent.
A subscriber that cannot take the whole frame at once — closed, or so far behind that its socket buffer is full — is dropped. A resolver reconnects and receives a fresh snapshot.
8.3.3 The snapshot #
A reply map with ok = true, kind = snapshot, hostname — the name
netd last set (§8.4), empty if none — and scopes: one scope per joined
interface whose level is above absent, ordered by the interface's
metric, then its kernel index.
Each scope:
| Field | Content |
|---|---|
ifid | the interface id |
name | the kernel name |
servers | DNS servers, as text, in the order to try them (below) |
domains | search domains, likewise |
ntp | the lease's NTP servers (option 42) |
addresses | every address the kernel holds on the interface, address/prefix, link-local ones included |
default_route | the profile's Dns.Default when set; otherwise whether the interface's level is routed |
exclusive | the profile's Dns.Exclusive |
metric | the interface's metric (§4.2) |
level | the interface's level (§8.2) |
8.3.4 Merging DNS facts #
Servers, in this order:
- the profile's
Dns.Servers, always; - with
Dns.Offered: the lease's servers (option 6); - with
Dns.Offered: the routers' RDNSS servers, including link-local IPv6; - with
Dns.Offered: the DHCPv6 reply's servers, including link-local IPv6.
Domains, in this order:
- the profile's
Dns.Domains, always; - with
Dns.Offered: the lease's domain search list (option 119) if it is not empty, otherwise its domain name (option 15); - with
Dns.Offered: the routers' DNSSL domains; - with
Dns.Offered: the DHCPv6 reply's domains.
Each list removes duplicates across the entire list, retaining the first occurrence and the source order above. This includes repeated values with other values between them.
The containing scope supplies the interface for link-local IPv6 servers. resolvd uses its index on UDP and TCP upstream sockets; the address text does not need a zone suffix. Deploy this publication change with a resolver that supports interface-scoped upstreams (resolvd TRM §4.7).
NTP servers are reported whatever Dns.Offered says. Whether to believe
a lease's time servers is timed's decision, made against its own
registry value. A snapshot describes what the network said.
The same merge feeds the dns and search fields of the status reply
(§9.2), for every non-loopback interface, joined or not.
8.4 The hostname
Peios / Peios technical reference / netd / What Netd Publishes
8.4.1 Which name #
Machine\System\Network Hostname, when it is set and not empty;- otherwise the hostname option (12) of a lease. netd takes the first
joined interface, in kernel index order, whose profile has
Hostname.Offeredand whose lease carries a hostname; - otherwise none.
The selected name is checked by libnetd::hostname, the same rule used
by System Settings and first-boot setup: after trimming outer whitespace,
it must be one nonempty label of at most 63 ASCII letters, digits or
hyphens, with no leading or trailing hyphen, and must not be localhost
(case-insensitive). This applies to registry and lease names alike.
An invalid selected name leaves the kernel's name and netd's last applied
name unchanged. It is logged as hostname <quoted name> refused: <quoted reason>, with control characters escaped, once while the same rejected
candidate remains selected. A different candidate, a valid name, or no
name clears that suppression. A nonempty but invalid registry name still
takes precedence: it does not silently fall through to a lease. Likewise,
the first selected lease name is checked without searching later leases
for a replacement.
A valid name that the kernel nevertheless refuses is logged as
sethostname(<name>): <error> and tried again at the next pass.
8.4.2 When #
After every full pass and every reconcile-only iteration (§2.2), netd
calls sethostname(2) if the name differs from the last one it set,
and logs hostname is <name>. It does not compare against the kernel's
current hostname. A change made by anything else is not reverted until
netd's own name changes.
When there is no name — the registry value removed and no lease offering one — netd does nothing: the kernel keeps whatever hostname it had.
The status reply and the resolver snapshot report the name netd last set, empty if it has set none.
8.4.3 What is recorded #
Each sethostname(2) that changes the kernel's name writes a
netd.hostname.changed event, which is essential: the emission policy
cannot switch it off. It carries config.name (always Hostname, whether
the name came from the registry or from a lease), config.text (the name
now in force) and config.text-previous (the kernel's name, read just
before the call; the kernel's own (none) on the first change after boot).
A call that leaves the kernel's name as it was — netd setting a name the
kernel already holds, as after a restart of netd or a change made behind
its back — records nothing, and neither does a name the kernel refuses.
The record's subject.token.sid is the principal that acted (PGSS §6.4).
When the change is made during the pass a reconcile request (§9.2) asked
for, that is the requester's user SID; netd refuses a request whose user
it cannot read. A change netd makes on its own authority — after a
registry change, a lease, or at startup — names netd's own user, SYSTEM;
who wrote the registry value is LCS's to record.
8.4.4 Telling the network #
With Hostname.Announce in an interface's profile, a newly started DHCPv4
client sends the validated, trimmed registry Hostname in option 12
(§5.1). An invalid registry name is omitted. An existing client's
announcement configuration is unchanged until it restarts. A name
adopted from a lease is never announced.
9.1 The socket and its object
Peios / Peios technical reference / netd / The Control Socket
9.1.1 The socket #
netd listens on /run/netd/control.sock, a SOCK_STREAM Unix socket. At
startup it:
- creates
/run/netdif it is missing, sets its mode to0755, and gives it the descriptor below; - removes a socket file left by a previous run, logging
removed a stale /run/netd/control.sock; - binds, sets the socket file's mode to
0666, and gives it the same descriptor.
The descriptor on both: owner and group SYSTEM, and a DACL allowing SYSTEM
GENERIC_ALL and Everyone GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE. peinit creates /run SYSTEM-only and inheritable, so
without it nobody else could reach the socket. Everyone can connect; the
control object decides what each connection can do. A failure to set it is logged
as an error and netd carries on.
9.1.2 The control object #
Every request is checked against the control object, a security
descriptor netd holds in memory. It is
Machine\System\Network ControlSecurity, a self-relative descriptor in a
REG_BINARY value, when that is set and valid. When it is not valid,
netd logs ControlSecurity is not a valid descriptor (…); using the default.
The compiled default: owner and group SYSTEM, and a DACL of:
| Trustee | Rights |
|---|---|
| SYSTEM | NETWORK_ALL_ACCESS |
| Administrators | NETWORK_ALL_ACCESS |
| Everyone | NETWORK_QUERY, READ_CONTROL |
The object is rebuilt whenever the configuration changes (§2.3), so a
written ControlSecurity applies to the next connection.
9.1.3 Rights #
| Right | Value |
|---|---|
NETWORK_QUERY | 0x00000001 |
NETWORK_CONTROL | 0x00000002 |
NETWORK_ALL_ACCESS | 0x000F0003: both, plus the standard rights |
The generic mapping:
| Generic right | Maps to |
|---|---|
| read | NETWORK_QUERY, READ_CONTROL |
| write | NETWORK_CONTROL, READ_CONTROL |
| execute | NETWORK_QUERY |
| all | NETWORK_ALL_ACCESS |
9.1.4 The check #
For each request, netd opens the connecting peer's token through KACS and
runs a real access check of the request's right (§9.2) against the
control object. It never looks at SO_PEERCRED, so a deny-only group or
a filtered token is judged as the kernel would judge it anywhere else. A
peer whose token cannot be opened is denied, and so is one whose token's
user cannot be read, since a change made for it is recorded under that
user (§8.4).
A denied request is answered {ok: false, error: "access denied"}, and
nothing else happens.
9.1.5 Nonblocking client I/O #
The control socket is served on netd's one thread (§2.2). Accepted connections are nonblocking and polled alongside network and registry inputs. An idle client, partial request, or client that does not read its reply does not hold the loop in a socket read or write.
At most 64 requests or replies are pending at once. While full, netd stops polling the listener until a slot becomes free; connections still waiting in the kernel backlog have not been accepted. Each iteration accepts at most 16 connections and reads or writes at most 4 096 bytes per ready pending connection. The 65 536-byte message ceiling is checked from the header before reading a request payload (§9.2).
A request must complete within two seconds of acceptance. A normal reply must be written within two seconds of being queued. Partial progress does not extend either deadline. Expiry closes the connection without a timeout reply. These deadlines participate in the same poll timeout as network-client timers. Completed subscriptions leave this pending-client set and use the existing nonblocking snapshot publisher (§8.3).
9.2 Requests
Peios / Peios technical reference / netd / The Control Socket
9.2.1 Framing #
The control socket is a PSPU observability query channel: one
length-prefixed MessagePack map in each direction, one request and one
reply per connection, except subscribe.
- A frame is a 4-byte little-endian payload length, then the payload.
- A length above 65536 is refused before any of the payload is read, with
{ok: false, error: "message of <n> bytes exceeds the ceiling"}. - A request is a map with a string
query. Unknown keys are ignored, and a key that appears twice refuses the request. - A reply is a map with a boolean
ok, and either the result's fields or a stringerror.
An oversized, incomplete-at-EOF, or undecodable request is answered with an error reply and the connection ends:
| Problem | error |
|---|---|
no query | missing field query |
an unknown query | unknown query "<query>" |
| a duplicate key | duplicate field <key> |
renew without interface | missing field interface |
| malformed MessagePack | malformed message: … |
Expired client deadlines and failed socket I/O close the connection without a reply (§9.1). A normal reply that exceeds the message ceiling is replaced with an error reporting its size.
9.2.2 The requests #
query | Right | Does | Reply |
|---|---|---|---|
status | NETWORK_QUERY | nothing | the status (below) |
subscribe | NETWORK_QUERY | keeps the connection as a subscriber (§8.3) | a snapshot now, and another on each change |
reconcile | NETWORK_CONTROL | a full pass (§2.2) | {ok: true} |
renew, with interface | NETWORK_CONTROL | the client renews (§5.4) | {ok: true}, or an error |
renew finds its interface by kernel name or by interface id. It answers
no interface <name> when there is none, and <name> is not using DHCP
when the interface has no DHCPv4 client.
9.2.3 The status reply #
| Field | Content |
|---|---|
hostname | the name netd last set, empty if none (§8.4) |
level | the machine level (§8.2) |
refusal | why the newest generation was refused, or nil (§3.2) |
interfaces | one map per non-loopback interface netd has seen, joined or not, in kernel index order |
Each interface:
| Field | Content |
|---|---|
ifid, name, index | the interface id, kernel name and index |
mac, path, driver | identity (§4.1); empty strings when absent |
verdict | JOIN, IGNORE or DOWN; nil when the backstop answered or rules tied |
rule | the rule that spoke, backstop, or the tied rules <a> vs <b> |
profile | the profile path, JOIN only |
network, network_name, network_trust | the network identified now (§7) |
warning | discovery unanswered (§5.2), or a tie (§3.3); nil otherwise |
up, carrier | the link's flags |
level | the interface's level (§8.2), computed for every interface |
addresses | every address the kernel holds, address/prefix, link-local ones included |
gateway, gateway6 | the gateway of the interface's first IPv4 and IPv6 default route, any owner's |
dns, search | the merged DNS facts (§8.3); empty for an interface not joined |
lease | while the client holds a lease: server, expires_in (whole seconds), state (§5.1), duration (the lease's length), and renew_at and rebind_at (T1 and T2), the last three in whole seconds after the lease was bound; nil otherwise. A client reading a netd that predates the last three finds them absent. |
10.1 Commands
Peios / Peios technical reference / netd / The Net Command
net is netd's operator command. It talks to netd over the control
socket, and reads the registry directly for rules and profiles,
because those are configuration that netd merely executes. Changing the
network is a registry write; net shows state and pokes the daemon. The
task-oriented guide is ~peios/networking/the-net-command.
10.1.1 Subcommands #
| Command | Does |
|---|---|
net status | prints the status reply (§9.2) |
net renew <interface> | sends renew |
net reconcile | sends reconcile |
net rules | prints Rules\Interface from the registry: one line per rule, indented by depth, with its priority, (disabled) for Enabled = 0, its conditions, and its actions (NULL when it has none) |
net profiles, net profile list | prints Profiles\ from the registry: one line per profile, indented by depth, with the values it sets itself ((inherits only) when none) and (disabled) |
net wait <link|addressed|routed> [seconds] | waits for the machine level |
net policy | prints the kernel engine's status: enforcing or not, the generation, unwalked changes, a refused last walk, the context count |
net policy wait [seconds] | waits for the packet policy to be in force |
net version, net --version | prints net <version> |
rules and profiles fail when their key does not exist. Neither
needs netd running.
10.1.2 net status output #
A hostname line ((unset) when empty) and a readiness line, then
policy REFUSED: <why> (the last good generation stands) when there
is a refusal. Then a block per interface: <name> [<id>], then verdict
(<VERDICT>(<profile>) by <rule>, or (none) by <rule>), state, and
readiness for a joined interface. Then, where present, hardware,
network (<name> [<id>] trust <trust>), each address, gateway,
gateway6, dns, search, lease (<state> from <server>, <n>s left) and warning.
10.1.3 net wait #
It asks for status every 500 ms and succeeds as soon as the machine
level is at least the one named. An error reaching netd is printed and
the wait goes on. The timeout is the second argument, or 60 s when absent
or not a number. When it lapses, net prints net: timed out waiting for <level> and fails.
absent is accepted as a level, and is met at once.
10.1.4 net policy wait #
It reads /dev/peios-ntfe's status (ABI 5) once and takes the number of
policy changes the engine has noted as its target. It then polls every
5 ms until the engine's count of changes walked reaches the target. If
the last walk was refused, it fails, naming the errno and the generation
that stands; otherwise it succeeds. The timeout is the argument, or 10 s.
The counters are the kernel's (PKM §6.5). A device that answers with
another ABI fails at once.
10.1.5 Exit status #
| Code | Meaning |
|---|---|
| 0 | success |
| 1 | netd unreachable, an error reply, a timeout, a refused policy, or a registry key that cannot be opened |
| 2 | usage: an unknown subcommand, a wrong argument count, an unknown level, or a non-numeric policy wait timeout |
11.1 Failure modes
Peios / Peios technical reference / netd / Failure Modes
11.1.1 Where the evidence is #
net status: the policy refusal, each interface's verdict and the rule that gave it, its warning, its addresses and gateways, and its lease's state.- netd's log. Informational lines go to standard output; warnings and
errors go to standard error. peinit forwards both to eventd
(
evctl 'LOGS FROM netd SINCE 1h ago TAKE 40'). Each line is also mirrored to/dev/kmsgatKERN_INFO,KERN_WARNINGorKERN_ERR, so it reaches the kernel log and, at a permissive console log level, the console. If the mirror cannot be opened, netd says so once on standard error and tries again on the next line. Each mirrored line opens and closes its own/dev/kmsgdescriptor, so boot and reconciliation bursts do not lose lines to the kernel's per-open rate limit (normally ten lines in five seconds). All three severities are retained. The mirror remains best-effort if opening or writing the device fails; stdout/stderr delivery is independent. - The registry:
Interfaces\<id>\Status,Networks\<id>\Status,Readiness.
11.1.2 No address on a joined interface #
What net status shows | Likely cause |
|---|---|
warning asked for an address; nobody answered, a 169.254 address or none | no DHCPv4 server answered. Either there is none, or NTFE refused the exchange: the shipped baseline's DHCP rules were deleted or overridden. netd cannot see the verdict stream. |
no lease line, no warning | the profile does not offer the address (Address.Offered, Address.Families), the interface has no MAC, or the packet socket would not open (logged). Or a server is answering but its ACK does not read as a lease: no server identifier, a lease time under 4 s, or a malformed classless route option (§5.3). The client then cycles through Selecting and Requesting, neither of which is shown (§5.1). |
| no IPv6 address | no advertisement arrived, or none was accepted: hop limit under 255, a non-link-local source, a prefix that is not an autonomous /64 (§6.1, §6.2). Also check Address.Families. |
11.1.3 The wrong interfaces are configured #
What net status shows | Meaning |
|---|---|
policy REFUSED: … | the newest generation was refused (§3.2). Everything shown is still the last good generation. Fix the named rule or profile; the next write is read at once. |
verdict (none) by backstop | no rule speaks for the interface. It is left as the kernel left it. |
verdict (none) by <a> vs <b>, warning rules … tie | two rules tied on priority and named different profiles (§3.3). The interface is ignored until one outranks the other. |
an interface that was joined is now IGNOREd, with its old addresses still on it | by design: IGNORE touches nothing, removal included (§3.3) |
11.1.4 Changes that do not stick #
- An address added by hand disappears. netd owns every address on a
joined interface, and the next pass removes it (§4.3). Add it to
Address.Staticinstead. - A route added by hand stays, unless it carries protocol 200.
- A pinned gateway never appears.
Route.Gatewayis not checked against the interface's subnets. When the kernel cannot reach it, every reconcile logsreconcile: AddRoute(…) failed, and the interface staysaddressed. - The hostname does not change back when
Hostnameis removed. netd never unsets a name (§8.4).
11.1.5 Losing what was working #
- A cable pull drops everything offered — the lease, autoconfigured addresses, routes, the network identity — and stops the clients (§4.1). When the carrier returns, the DHCPv4 client asks for the old address first, so a cooperative server restores it in one exchange.
- A lease that cannot be renewed drops its address at the lease's
end, unless the profile says
Address.OnExpiry = Keep(§5.4). After that, the link-local fallback does not happen again in the same client's life (§5.2). - Editing a profile restarts its interfaces' clients (§3.3), so a DHCPv4 lease is released and asked for again.
11.1.6 netd is slow or unresponsive #
- Everything pauses for seconds at a time. A program that connects to
the control socket and does not send its request holds netd's single
loop for up to 2 s per connection (§9.1). Look for a misbehaving client
of
/run/netd/control.sock. net: cannot reach netd at /run/netd/control.sock. netd is not running, or/run/netdlost its descriptor. The service restarts netd always;evctlshows why it stopped.
11.1.7 Configuration not read #
- netd ignores every registry change. The watch could not be armed at
startup (
registry watch unavailable … configuration is read once), orMachine\System\Networkdid not exist then (§2.1). Restart netd.
11.1.8 Getting back to a known state #
- Undo the last registry write. A refused generation names what to undo, and the next good write is taken at once.
net reconcileruns a full pass without waiting for an event.net renew <interface>renews a lease without releasing it.- Restarting netd re-derives everything from the registry and the kernel, and asks for the remembered IPv4 address with INIT-REBOOT (§2.1). The requested address already installed and applicable netd-owned routes stay while that request is pending. A NAK or unanswered revalidation discards this provisional state and starts fresh discovery; an unchanged ACK keeps IPv4 connectivity in place. Offered DNS and IPv6 information still have to be reacquired.
Appendix A Constants
Peios / Peios technical reference / netd
A.1 Paths #
| Path | Use |
|---|---|
Machine\System\Network | configuration root, readiness, DUID (§2.3, §8) |
/run/netd/control.sock | control socket (§9.1) |
/run/netd | runtime directory, created by peinit (§2.1) |
/var/state/netd/duid | the DUID, when the registry has none (§5.7) |
/var/state/netd/secret | the RFC 7217 secret (§6.2) |
/usr/sbin/netd | the daemon |
/proc/sys/net/ipv6/conf/<name>/accept_ra | set to 0 (§6.1) |
A.2 Numbers #
| Constant | Value | § |
|---|---|---|
| Routing protocol stamped on netd's routes | 200 | §4.3 |
| Default route metric | 100; 600 for wireless | §4.2 |
| Minimum MTU applied | 68 | §4.3 |
| Converge repetitions per pass | at most 4 | §2.2 |
| Registry tree depth read | 16 | §2.3 |
| Control message ceiling | 65536 bytes | §9.2 |
| Control read and write timeout | 2 s | §9.1 |
| DHCPv4 first backoff, ceiling | 4 s, 64 s, ±1 s jitter | §5.2 |
| DISCOVER retransmissions before "nobody answered" | 3 | §5.2 |
| REQUESTs in Requesting before rediscovery | 4 | §5.2 |
| REQUESTs in Rebooting before rediscovery | 2 | §5.2 |
| Renewing and rebinding retransmission floor | 60 s | §5.2 |
| Shortest lease accepted | 4 s | §5.3 |
| DHCPv4 maximum message size advertised | 1500 | §5.1 |
| DHCPv4 message padding | to 300 bytes | §5.1 |
| Domain-search compression jumps per name | 16 | §5.3 |
| Link-local range | 169.254.1.0–169.254.254.255 | §5.5 |
| Router solicitation interval, ceiling | 4 s, 3600 s, ±10% jitter | §6.1 |
| Solicitations before backoff | 3 | §6.1 |
| Prefixes, RDNSS servers, DNSSL domains per advertisement option | 16 | §6.1 |
| SLAAC prefix length | 64 | §6.2 |
| Two-hour rule | 7200 s | §6.2 |
| Temporary address preferred lifetime | 86400 s minus 0–599 s | §6.2 |
| Temporary address valid lifetime | 172800 s | §6.2 |
| Temporary addresses per prefix | 4 | §6.2 |
| Stable address counter attempts | 8 | §6.2 |
| DHCPv6 first retransmission, ceiling | 1 s, 3600 s, ±10% jitter | §6.3 |
| DHCPv6 refresh default, floor | 86400 s, 600 s | §6.3 |
| DHCPv6 servers and domains kept | 16 each | §6.3 |
A.3 Identifiers #
| Identifier | Value | § |
|---|---|---|
| Interface id digest prefix | peios-netd-ifid| | §4.1 |
| Network id digest prefix | peios-netd-network| | §7.1 |
| Stable address digest prefix | peios-ndp-stable-iid| | §6.2 |
| DUID-LL header | 00 03 00 01 | §5.7 |
Status descriptor | O:SYG:SYD:P(A;;KA;;;SY)(A;;KR;;;WD) | §8.1 |
NETWORK_QUERY, NETWORK_CONTROL, NETWORK_ALL_ACCESS | 0x1, 0x2, 0x000F0003 | §9.1 |