5.6 Sockets

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:

FieldValue
source0.0.0.0
destination255.255.255.255
TOS0x10
identification0
flagsdon't-fragment
TTL64
UDP ports68 → 67
UDP checksum0 (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).

Edit this page