2.2 The loop

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 #

InputDescriptorWhat it leads to
Kernel eventsthe rtnetlink multicast socketa fresh dump, then a full pass
Registry changesthe watch on Machine\System\Networka reload (§2.3), then a full pass
udp/68 absorberthe absorber socketdrained and discarded
DHCPv4 repliesone packet socket per running clientthe client's actions; a reconcile if the network changed
Router advertisementsone ICMPv6 socket per interface doing router discoverythe engine's actions; a full pass if anything changed
DHCPv6 repliesone UDP socket per running DHCPv6 clientthe client's actions; a reconcile if the DNS facts changed
Control requeststhe listener and accepted client socketsthe request (§9.2); reconcile runs a full pass
Timethe earliest deadline of any clientevery 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:

  1. identify networks on joined interfaces with carrier (§7.1);
  2. 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;
  3. start DHCPv4 clients where due (§5.1);
  4. start or stop router discovery and DHCPv6 (§6.1, §6.3);
  5. 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.

Edit this page