A phased plan to replace dhcpcd in this project with a port of Apple's IPConfiguration daemon. Targets freebsd-launchd-mach (v2) — the Mach IPC + libxpc + libdispatch track. Shipped through iter 8 as a standalone Mach daemon at src/IPConfiguration/, NOT as a configd plugin (this repo's configd has no plugin loader). DHCPv4 INIT→BOUND, RFC 5227 ARP probe, IPv6 RA/SLAAC, SCDynamicStore publish, MIG service worker, and the Apple-shape ipconfig(8) CLI are live.
freebsd-launchd (AF_UNIX/DO) repo as a netconfigd plugin. Refactored 2026-05-23 to target freebsd-launchd-mach (v2): standalone Mach daemon (this repo's configd has no plugin loader), Mach service com.apple.IPConfiguration, ipconfig.defs MIG IDL retained. SHIPPED through iter 8 — PRs #42–#51 merged covering daemon skeleton, full DHCPv4 INIT→BOUND, ARP probe (RFC 5227), IPv6 RA/SLAAC, SCDynamicStore publish, MIG service worker thread, IPCONFIGD_FAST_LEASE renewal gate, and the Apple-shape ipconfig(8) CLI at /usr/sbin/ipconfig. iter 7b (DHCPv6 stateful) deferred indefinitely — SLAAC covers the common case; Apple's DHCPv6Client.c is 3717 LOC with no CI validation path (QEMU SLIRP doesn't run a DHCPv6 server). Future iters grow the MIG surface as real consumers need it (Apple's ipconfig.defs has 21+ routines; this repo has 2).
/usr/sbin/ipconfigd, NOT a configd plugin — this repo's configd has no plugin loader. Source upstream lives in the apple-oss-distributions/bootp repo (currently bootp-534.100.6) under IPConfiguration.bproj/.State:/Network/Service/<UUID>/IPv4 + /IPv6 dynamic-store keys (via Mach RPC to configd at com.apple.SystemConfiguration) and an Apple-shape ipconfig(8) CLI. Removes the third-party dhcpcd port dependency. Pairs cleanly with the SCDynamicStore Mach-IPC track.freebsd-launchd-mach. DHCPv4 INIT→BOUND, RFC 2131 RENEWING/REBINDING, RFC 5227 ARP probe + announce, IPv6 RA listener + EUI-64 SLAAC + ::/0 route, State:/Network/Service/<UUID>/IPv4 + /IPv6 publish, MIG service worker thread (2-routine surface so far), IPCONFIGD_FAST_LEASE CI gate, and an Apple-shape ipconfig(8) CLI with command-table dispatch.com.apple.IPConfiguration (advertised via libxpc / launchd). IPC IDL is ipconfig.defs (this repo's slim subset — 2 routines today: ipconfig_if_count + ipconfig_if_addr; Apple's is 21+). Talks to configd over Mach RPC for SCDynamicStore publish. Subscribes to nothing yet — DHCP fires once at boot on the first non-loopback Ethernet. Hot-plug / multi-interface deferred.DHCPv6Client.c is 3717 LOC with no CI validation path — QEMU SLIRP doesn't run a DHCPv6 server. Build when a real consumer needs it.Replace dhcpcd in the freebsd-launchd-mach (v2) project with a port of Apple's IPConfiguration. End state (now reached through iter 8): a standalone Mach daemon /usr/sbin/ipconfigd owns DHCPv4 / RA-SLAAC / ARP processing for the first non-loopback Ethernet; state lands in this repo's SCDynamicStore (via Mach RPC to configd at com.apple.SystemConfiguration) under the canonical Apple key namespace (State:/Network/Service/<UUID>/IPv4 + /IPv6); client tools (scutil, ipconfig(8)) reach the daemon via MIG over mach_msg on service name com.apple.IPConfiguration.
freebsd-launchd repo uses that pattern; this is the Mach-IPC track. ipconfig.defs MIG IDL is retained (this repo's slim 2-routine subset).IORegisterForSystemPower + IOPMConnectionCreate + scheduled wake events have no FreeBSD equivalent. Live without sleep-coordinated lease renewal.Apple80211. WLAN association state arrives as the State:/Network/Interface/<iface>/AirPort key published by wland — ipconfigd does not read wpa_supplicant's control socket itself. Current iters target Ethernet only; the DHCP gate that consumes AirPort{Authenticated} is specced in the WLAN plan §1.4.SymptomReporter / IPv6AWDReport. Apple wireless diagnostics telemetry — deleted on import.MobileGestalt. One call site in dhcp.c; stub returns "FreeBSD".EAPOLController. 802.1X auth coordination skipped; if needed later, route through wpa_supplicant.#ifdef'd out.For context: Apple's bootp repo total scope. This repo's port is much smaller — a fresh-written thin daemon at src/IPConfiguration/ that draws on Apple's protocol cores rather than re-shaping Apple's plugin tree wholesale.
| Component | Apple LOC | This repo's approach |
|---|---|---|
IPConfiguration.bproj/ (Apple's daemon-as-plugin) | 39,722 | Fresh thin standalone daemon; protocol cores referenced as Apple-shape oracles. |
bootplib/ (shared protocol library) | 26,997 | Reference for option tables + RA parser + DHCP packet structs. |
IPConfiguration_framework/ (Mach client API library) | 5,228 | Not built. CLI talks MIG directly via stubs from ipconfig.defs. |
IPConfigurationHelper/ (XPC helper for PvD HTTPS fetch) | 1,250 | Not built. PvD deferred indefinitely. |
ipconfig.tproj/ (CLI tool) | 1,630 | Fresh Apple-shape ipconfig(8) in iter 8 (ipconfig.c). |
| Total Apple scope | ~74,827 | This repo: a few thousand LOC of fresh-written daemon code. |
Out-of-scope siblings (bootpd, bsdpd, dhcp6d, rtadvd, BSDPClient, netbootdisk, tests) | ~24,000 | Not imported. |
Largest single Apple files (for orientation):
IPConfiguration.bproj/ipconfigd.c — 10,312 LOC. Plugin shell + IFState + SCDynamicStore + power events + service lifecycle. Not ported wholesale; this repo's ipconfigd.c is fresh-written and much smaller.IPConfiguration.bproj/dhcp.c — 4,660 LOC. DHCPv4 state machine; protocol-core reference for our dhcp_discover.c.IPConfiguration.bproj/DHCPv6Client.c — 3,717 LOC. Not built — iter 7b deferred indefinitely.bootplib/RouterAdvertisement.c — 1,788 LOC. RA parser/builder; reference for our ra_listen.c.bootplib/dhcp_options.c — 1,716 LOC. 113 known DHCPv4 options; option-table reference.bootplib/DHCPv6Options.c — 1,637 LOC. DHCPv6 options — not built (iter 7b deferred).bootplib/interfaces.c — 1,589 LOC. Uses getifaddrs(3) + SIOCGIFMEDIA; FreeBSD-portable.Standalone Mach daemon (NOT a configd plugin — this repo's configd has no plugin loader). The two daemons live in separate address spaces and communicate exclusively over Mach RPC. ipconfigd registers Mach service com.apple.IPConfiguration; it publishes to com.apple.SystemConfiguration via sc_publish.c. Subscribes to nothing yet — DHCP fires once at boot on the first non-loopback Ethernet. Hot-plug / multi-interface deferred.
Source tree lives at src/IPConfiguration/ in the freebsd-launchd-mach repo. Translation units below are what's on disk through iter 8 (PRs #42–#51).
| File | Role | Iter |
|---|---|---|
src/IPConfiguration/ipconfigd.c | Main daemon loop. Wires DHCPv4 + RA + lease loop. Boot-time first-Ethernet selection. | 1 → 7a |
src/IPConfiguration/dhcp_discover.c | DHCPv4 state machine (DISCOVER / OFFER / REQUEST / ACK) over BPF. | 2 → 3 |
src/IPConfiguration/apply_lease.c | SIOCAIFADDR (assign IPv4 address) + RTM_ADD (default route). | 3 |
src/IPConfiguration/apply_lease_v6.c | EUI-64 SLAAC + IFDISABLED clear + SIOCAIFADDR_IN6 + RTM_ADD ::/0. | 7a |
src/IPConfiguration/arp_probe.c | RFC 5227 ARP probe + announce over BPF. BIOCGBLEN = 4096 rxbuf. | 6 |
src/IPConfiguration/ra_listen.c | ICMPv6 Router Advertisement listener (RFC 4861). | 7a |
src/IPConfiguration/sc_publish.c | SCDynamicStore publish over Mach RPC (CF translation unit) — State:/Network/Service/<UUID>/IPv4 + /IPv6. | 4, 7a |
src/IPConfiguration/mach_service.c | MIG service worker thread; bootstrap_check_in + mach_msg_server loop on com.apple.IPConfiguration. | 5a |
src/IPConfiguration/lease_loop.c | RFC 2131 RENEWING/REBINDING timers; IPCONFIGD_FAST_LEASE env var collapses the T1/T2 wait for CI. | 4, 5b |
src/IPConfiguration/bound_state.c | Shared lease state readable by the MIG worker (ifname / IP / lease metadata). | 5a |
src/IPConfiguration/ipconfig.c | iter 8 CLI at /usr/sbin/ipconfig — Apple-shape command-table dispatch with getifaddr + ifcount subcommands. | 8 |
src/IPConfiguration/ipconfig.defs | MIG IDL — this repo's slim subset. 2 routines today: ipconfig_if_count + ipconfig_if_addr. (Apple's has 21+.) | 5a |
src/IPConfiguration/ipconfigtest.c | Test binary exercising the lease pipeline directly. | 3+ |
src/IPConfiguration/ipconfigrpctest.c | Test binary exercising the MIG client-server path end-to-end. | 5a+ |
bootpd.tproj, bootpdutil.tproj, bsdpc.tproj, BSDPClient/, dhcp6d.tproj/, DHCPServer/, netbootdisk/, rtadvd/ — all sibling daemons not in scope.IPConfiguration.bproj/IPv6AWDReport.{h,m} — Apple Wireless Diagnostics telemetry.IPConfiguration.bproj/report_symptoms.{c,h} — same.IPConfiguration.bproj/Embedded-Info.plist — iOS variant manifest.IPConfiguration.bproj/wireless-entitlements.plist — code-signing entitlements.bootplib/ioregpath.{c,h} — IORegistry walker, only used for NetBoot.bootplib/{nbimages,nbsp,bsdplib,macnc_options,netinfo,subnets}.{c,h} — NetBoot internals.SCTest-Swift/, tests/IPConfigurationTest/ Xcode test schemes — replace with our own.*.xcodeproj, xcconfigs/).Rather than line-for-line reshape Apple's ipconfigd.c (10k LOC of plugin shell + power events + service lifecycle + Mach RPC server), iter 1–8 wrote a thin daemon from scratch that reuses Apple's protocol cores (DHCPv4 state machine, DHCP option tables, RA parser, ARP packet build) and replaces everything Darwin-specific with native FreeBSD code.
| File | Apple LOC (origin) | What changed in our port |
|---|---|---|
mach_service.c | server.c (654) | Fresh-written Mach service: bootstrap_check_in + worker thread running mach_msg_server on com.apple.IPConfiguration. No BSM audit-token entitlement check (no Apple entitlement system in this repo); identity gating deferred. |
ipconfigd.c | ipconfigd.c (10,312) | Fresh thin daemon main loop. First-Ethernet selection + DHCPv4 launch + RA listener thread + lease loop. Power-event handlers (IORegisterForSystemPower) dropped entirely. No plugin shell — runs as a regular launchd-managed daemon at /usr/sbin/ipconfigd. |
sc_publish.c | (scattered in ipconfigd.c) | Fresh CF translation unit. Builds the IPv4 / IPv6 dictionaries and publishes via Mach RPC to com.apple.SystemConfiguration. Avoids the plugin model entirely. |
ipconfig.c | ipconfig.tproj/client.c (1,630) | Fresh Apple-shape CLI. Command-table dispatch (getifaddr, ifcount) calling the MIG client stubs generated from ipconfig.defs. Same Mach-IPC shape as Apple — different (smaller) routine set. |
ipconfig.defs | bootplib/ipconfig.defs (196, 21+ routines) | Slim 2-routine subset: ipconfig_if_count + ipconfig_if_addr. Grows as real consumers arrive. |
ifutil.c (Darwin-specific ioctls) | ifutil.c (2,211) | Replaced by direct ioctl calls in apply_lease.c + apply_lease_v6.c. SIOCAUTOADDR / SIOCSIFL4S / SIOCCLAT46_* / SIOCPROTOATTACH not used. CLAT46 + L4S features unavailable by design. |
HostUUID | HostUUID.c (56) | Use sysctlbyname("kern.hostuuid", ...). ISO first-boot seeds kern.hostuuid if blank. |
IPHXPCServer.m + IPHPvDInfoRequestServer.m (PvD helper) | 1,167 | Not built. PvD support deferred indefinitely; RFC 8801 deployment is rare. |
os_log wiring | throughout | Mapped to syslog(3), same shape as the launchd-mach mylog.h. |
Files in Apple's tree that would be portable verbatim (or near) on a future expansion. Today this repo's fresh-written daemon uses these as reference oracles rather than importing them directly — the iter 1–8 codebase is much smaller. Listed here for orientation when growing the surface in future iters.
| File | Apple LOC | Notes |
|---|---|---|
IPConfiguration.bproj/dhcp.c | 4,660 | DHCPv4 state machine. One MobileGestalt call at line 447 to stub. |
IPConfiguration.bproj/DHCPv6Client.c | 3,717 | DHCPv6 state machine. RFC 8415 retry constants in bootplib/DHCPv6.h:79-94 — no source changes. |
IPConfiguration.bproj/arp_session.c | 2,505 | ARP probe/announce/conflict via BPF. bpflib is BSD-portable. |
IPConfiguration.bproj/rtadv.c | 2,133 | RA consumer; PIO/RDNSS/DNSSL/PvD state. |
IPConfiguration.bproj/bootp_session.c | 827 | Raw IP/UDP DHCPv4 socket on port 68. |
IPConfiguration.bproj/RTADVSocket.c | 801 | ICMPv6 RS sender / RA receiver via <netinet6/nd6.h>. |
IPConfiguration.bproj/CGA.c | 711 | RFC 3972 Cryptographically Generated Addresses. |
IPConfiguration.bproj/DHCPv6Socket.c | 685 | UDP socket on port 546. |
IPConfiguration.bproj/linklocal.c | 577 | IPv4LL (RFC 3927). |
IPConfiguration.bproj/manual.c + manual_v6.c | 742 | Static IP config method. |
IPConfiguration.bproj/DHCPDUIDIAID.c | 371 | DUID/IAID generation, persisted to DUID_IA.plist. |
IPConfiguration.bproj/timer.c + FDSet.c | 494 | libdispatch-source timers + FD wrappers. |
bootplib/RouterAdvertisement.c | 1,788 | RA parser/builder. |
bootplib/dhcp_options.c | 1,716 | DHCPv4 option parse/build. |
bootplib/DHCPv6Options.c | 1,637 | DHCPv6 option parse/build. |
bootplib/DNSEncryptedServers.c + DNSNameList.c | 2,868 | RFC 1035 DNS-name compression + DDR. |
bootplib/interfaces.c | 1,589 | getifaddrs + SIOCGIFMEDIA; few Darwin-only IFEF_/IFXNAMSIZ usages. |
bootplib/cfutil.c | 1,068 | CFPropertyList helpers — corebase already provides. |
bootplib/IPv4ClasslessRoute.c | 651 | Option 121 parser. |
bootplib/NICache.c | 646 | DHCPv4 binding cache. |
bootplib/arp.c | 633 | ARP packet build/parse. |
bootplib/{ptrlist,dynarray,util}.c | ~700 | Utility types. |
bootplib/DHCPDUID.c | 283 | DUID factory. |
bootplib/inetroute.c | 263 | Route table reader. |
bootplib/udp_transmit.c | 245 | Raw UDP send. |
bootplib/bpflib.c | 204 | BPF socket open/filter. |
Apple's bootplib/ipconfig.defs is 196 LOC, 21+ routines. This repo retains the same MIG-over-mach_msg shape but ships a slim 2-routine subset as src/IPConfiguration/ipconfig.defs. The MIG-generated stubs link into both the daemon (mach_service.c) and the CLI (ipconfig.c).
subsystem ipconfig 17000;
#include <mach/std_types.defs>
#include <mach/mach_types.defs>
/* Iter 5a — 2 routines. */
routine ipconfig_if_count(
server : mach_port_t;
out count : int);
routine ipconfig_if_addr(
server : mach_port_t;
in ifname : (string_t);
out ipv4 : (string_t);
out result : int);
Future routines (Apple's full surface) grow the IDL as real consumers arrive — see §12 Future iters. Caller-identity gating on these MIG entry points is currently TODO; no BSM audit-token / Apple entitlement system exists in this repo. Production hardening would gate on the Mach audit-token PID + uid via set_security_token already used elsewhere in the launchd-mach kernel module (cf. fork_bsport).
Apple's ipconfigd subscribes to seven SCDynamicStore key suffixes (kSCEntNetLink, kSCEntNetAirPort, kSCEntNetRefreshConfiguration, kSCEntNetIPv6, kSCEntNetIPv4ARPCollision, kSCEntNetIPv6RouterExpired, kSCEntNetNAT64) translated from kernel events by the sibling KernelEventMonitor.bundle configd plugin. This repo's ipconfigd currently subscribes to none of them — it fires DHCPv4 once at boot on the first non-loopback Ethernet interface, runs the ARP probe, then publishes the resulting lease.
Concretely, today (iter 8):
State:/Network/Service/<UUID>/IPv4 + /IPv6 over Mach RPC to com.apple.SystemConfiguration via sc_publish.c.lease_loop.c (RFC 2131 T1/T2), not by external events. IPCONFIGD_FAST_LEASE=1 collapses the wait for CI.Hot-plug, multi-interface, link-flap handling, and ARP-collision recovery all wait on a KEM-equivalent. The future-iters plan calls for hwregd-driven subscription rather than a configd KEM plugin (this repo has no plugin loader). PF_ROUTE → publish mapping is the same on FreeBSD vs. Darwin:
| FreeBSD PF_ROUTE message | SC key suffix (when wired) |
|---|---|
RTM_IFINFO (link state from if_data.ifi_link_state) | State:/Network/Interface/<bsd>/Link |
RTM_NEWADDR (AF_INET) | State:/Network/Interface/<bsd>/IPv4 |
RTM_NEWADDR (AF_INET6) | State:/Network/Interface/<bsd>/IPv6 |
RTM_DELADDR | same keys, recomputed |
RTM_IFANNOUNCE (IFAN_ARRIVAL/IFAN_DEPARTURE) | recompute interface list |
| Dependency | Where | FreeBSD plan |
|---|---|---|
IORegisterForSystemPower + IOPMConnectionCreate | ipconfigd.c:8469, 8530 | Drop in v1; FreeBSD has no pmset-equivalent. Sleep-coordinated lease renewal not supported. Future: bridge from devd ACPI events. |
IOPMCopyScheduledPowerEvents / IOPMCancelScheduledPowerEvent | ipconfigd.c:1515, 1531 | Drop. No scheduled-wake support. |
gethostuuid(3) | HostUUID.c:52 | Replace with sysctlbyname("kern.hostuuid", ...). Caveat: ISO first-boot needs to seed kern.hostuuid if blank. |
MGCopyAnswer(kMGQProductType, ...) | dhcp.c:447 | One-line stub: return "FreeBSD". |
IO80211/Apple80211API.h | wireless.c:178 | Replace with wpa_supplicant control-socket consumer (/var/run/wpa_supplicant/<ifname>, STATUS command). |
SymptomReporter.framework | report_symptoms.c, DHCPv6Client.c:2692 | Delete entirely. Apple wireless-diag telemetry. |
os_log family | throughout | Map to syslog(3) via opaque handle wrapper. Keep call-sites unchanged. |
bsm/libbsm.h audit_token_to_au32() | server.c:79-115 | Not used today; MIG entry points are ungated. Future: gate via Mach audit token set_security_token path already used in fork_bsport. |
IODeviceTree:/chosen NetBoot blob | ipconfigd.c:2933 via ioregpath.c | Deleted (NetBoot out of scope). |
Darwin-only ioctls (SIOCAUTOADDR, SIOCSIFL4S, SIOCCLAT46_*, SIOCPROTOATTACH) | ifutil.c (~12 ioctls) | Not used in fresh-written apply_lease.c / apply_lease_v6.c. CLAT46 (464XLAT) and L4S features unavailable by design. |
SCPreferences watcher | throughout for tunables file | Not used. IPCONFIGD_FAST_LEASE env var is the only tunable today; future tunables can ride configd SCPreferences once the watcher is exposed over Mach RPC. |
arp_probe.c initially undersized the BPF rxbuf; BIOCGBLEN returns 4096 by default. Fixed in commit e790468 — match the kernel's reported buffer size, not a guessed-smaller value. The exact failure mode was probe responses getting truncated silently.sc_publish.c instead bundles up the CF dictionary and ships it as a Mach RPC to configd's com.apple.SystemConfiguration port. CF lifetime + key-shape symmetry both had to be right or scutil would not see the value.mach_msg_server blocking on the registered port while the main thread runs select() over BPF / PF_ROUTE / RA sockets. Shared lease state lives in bound_state.c behind locks. Bootstrap_check_in must happen before the MIG service can be reached.IPCONFIGD_FAST_LEASE=1 env var collapses T1/T2 to seconds; the IPCFG-RENEW-OK boot marker gates CI on observing one full renew cycle.net.inet6.ip6.accept_rtadv=0 default plus per-iface IFDISABLED meant SLAAC silently did nothing until apply_lease_v6.c explicitly cleared IFDISABLED and called SIOCAIFADDR_IN6 with the EUI-64 we derived ourselves. RTM_ADD for ::/0 goes through the standard route socket.ipconfig(8) command-table dispatch (iter 8). The CLI is structurally identical to Apple's — a static table of {name, handler, min_args, help} rows that the main dispatcher matches against argv[1]. Built fresh against our MIG client stubs.DHCPv6Client.c is 3717 LOC. QEMU SLIRP doesn't run a DHCPv6 server, so there's no CI validation path. SLAAC covers home / SOHO / most enterprise. Build when a real consumer needs it.set_security_token path already in mach.ko (cf. fork_bsport).Confirmed: IPConfiguration has the same problem as dhcpcd. Both implement RFC 8415 verbatim. RFC 8415 says DHCPv6 Solicit has no max retransmission count (SOL_MAX_RC = 0 = infinite). Both clients retry Solicit forever on networks where the router advertises the M-flag but the DHCPv6 server returns "No Addresses Available." Both cap at one Solicit per hour with random jitter (SOL_MAX_RT = 3600).
Source citations:
bootplib/DHCPv6.h:79-94 defines constants. DHCPv6Client.c:2641-2691 sends Solicit with DHCPv6ClientNextRetransmit(client, DHCPv6_SOL_TIMEOUT, DHCPv6_SOL_MAX_RT). No max-retry check.src/dhcp6.h:120-121 defines same constants. src/dhcp6.c:1723 sets state->MRC = SOL_MAX_RC (= 0).The Apple-vs-FreeBSD difference is purely logging policy:
os_log infrastructure suppresses LOG_INFO "Solicit Transmit (try=N)" messages from default user-visible streams. Packets still go out the wire; users don't see them.syslog(3) shows everything at default priority. dhcpcd at LOG_NOTICE for "DHCPv6 REPLY: No Addresses Available" → spammy.Knob to disable in IPConfiguration: DHCPv6Enabled boolean in Embedded-Info.plist (default true upstream). Per-service: set kSCNetworkProtocolTypeIPv6 ConfigMethod to Manual or LinkLocal.
Conclusion (iter 8 resolution): this repo's ipconfigd doesn't build the DHCPv6 stateful path at all (iter 7b deferred indefinitely). The spam problem is sidestepped entirely. SLAAC handles the IPv6 case. If someone later builds DHCPv6 here, they'll need to default it to off and require per-service opt-in.
| Feature | IPConfiguration | dhcpcd |
|---|---|---|
| DHCPv4 (RFC 2131) | ✓ | ✓ |
| DHCPv4 rapid commit (RFC 4039) | ✗ | ✗ (yes for v6) |
| BOOTP (RFC 951) | ✓ (separate ConfigMethod) | partial (frame layout shared) |
| IPv4LL (RFC 3927) | ✓ (linklocal.c) | ✓ (src/ipv4ll.c) |
| DHCPv6 stateful (RFC 8415) | ✓ | ✓ |
| DHCPv6 IA_PD prefix delegation | ✓ | ✓ |
| DHCPv6 Information-Request (RFC 3736) | ✓ | ✓ |
| DHCPv6 Reconfigure (RFC 8415 §18) | partial (option codes, no full handler) | ✓ |
| RS/RA (RFC 4861) | ✓ | ✓ |
| RDNSS / DNSSL (RFC 8106) | ✓ | ✓ |
| DHCP authentication (RFC 3118) | ✗ | ✓ |
| Dynamic SOL_MAX_RT learning | ✗ (fixed 3600) | ✓ |
| Bonjour Sleep Proxy coordination | ✓ (Apple-only) | ✗ |
| IOKit power-management awareness | ✓ (Apple-only) | ✗ |
| CGA (RFC 3972) | ✓ | ✗ |
| RFC 7217 stable IIDs | ✗ | ✓ (slaac private) |
| Captive portal URL option (RFC 8910) | ✓ (DHCPv6 code 103) | partial |
| Hook-script framework | ✗ (publishes to SCDynamicStore instead) | ✓ (/lib/dhcpcd/dhcpcd-hooks/) |
resolvconf(8) integration | writes /etc/resolv.conf directly via configd plugin chain | ✓ via hook |
| WLAN association coordination | via Apple80211 (wireless.c) | via the AirPort key wland publishes (not a wpa_supplicant hook — NextBSD has no dhcpcd, so no such hook exists) |
| Bridge / lagg / vlan layering | partial (Apple's interface model) | ✓ |
| Per-iface profiles | ✓ (each Service has its own UUID) | ✓ |
| Privsep | ✗ (runs in configd as root) | ✓ (src/privsep*.c) |
| Cross-platform | macOS only | NetBSD, FreeBSD, OpenBSD, DragonFly, Linux, Solaris, Haiku |
| SCDynamicStore publication | ✓ (canonical Apple keys) | ✗ (lease file + hooks) |
Native ObjC client API (IPConfigurationServiceCreate) | ✓ | ✗ (control socket protocol) |
What IPConfiguration gave us (iter 1–8): SCDynamicStore-keyed publication (cleaner integration with the rest of the Mach-IPC track), Apple-shape ipconfig(8) CLI with MIG-stub dispatch, RFC 5227 ARP probe + announce, IPv6 RA listener + SLAAC, RFC 2131 lease loop. CGA + Bonjour Sleep Proxy + IOKit power coordination remain Apple-only (no FreeBSD equivalents — out of scope by design).
What dhcpcd had that we gave up: RFC 3118 authentication (rare in real deployments), DHCPv6 Reconfigure handler (deferred — iter 7b), dynamic SOL_MAX_RT learning (DHCPv6 not built), hook-script ecosystem (replaced by SCDynamicStore publish), bridge/lagg/vlan layering recognition (deferred — multi-interface future iter), privilege separation (daemon runs as root under launchd), RFC 7217 stable IIDs (deferred), cross-platform maintenance burden shared with NetBSD/Linux/etc. (intentional — this is an Apple-track port).
| Decision | Choice |
|---|---|
| Repo / track | freebsd-launchd-mach (v2) — the Mach IPC + libxpc + libdispatch track. The sibling freebsd-launchd (AF_UNIX/DO) track is a separate effort. |
| Deployment shape | Standalone Mach daemon at /usr/sbin/ipconfigd — NOT a configd plugin. This repo's configd has no plugin loader; the two daemons live in separate address spaces. |
| Source repo (Apple) | apple-oss-distributions/bootp at the latest tag (currently bootp-534.100.6). Used as reference + protocol-core source; the daemon shell is fresh-written. |
| IPC | MIG over mach_msg. Mach service name com.apple.IPConfiguration. IDL src/IPConfiguration/ipconfig.defs — slim 2-routine subset (ipconfig_if_count, ipconfig_if_addr); grows as real consumers arrive. |
| SCDynamicStore | Publish-only via Mach RPC to configd at com.apple.SystemConfiguration. Subscribes to nothing yet. |
| Power management | Dropped. No FreeBSD equivalent for IOPMConnection. Sleep-coordinated renew unsupported. |
| WLAN coordination | Not built in iter 1–8 (Ethernet only). Next: the ~70-LOC sc_link_watch.c gate requiring Link{Active} and AirPort{Authenticated}, key-absent meaning ready so wired NICs are untouched. WLAN plan §1.4. |
| EAPOL / 802.1X | Skipped. Route through wpa_supplicant if needed later. |
| NetBoot / BSDP | Out of scope. |
| Logging | syslog(3) wrapper around the os_log macros, matching the launchd-mach mylog.h. |
| Privsep | Runs as root under launchd. No separate sandboxing. |
| License | BSD-2-Clause for new code; preserve Apple OSReference 2.0 (Apache) headers on imported files. |
| DHCPv6 (iter 7b) | Deferred indefinitely. SLAAC covers the common case; Apple's DHCPv6Client.c is 3717 LOC with no CI validation path (QEMU SLIRP doesn't run a DHCPv6 server). Build when a real consumer needs it. |
| CLI shape | Apple-shape ipconfig(8) at /usr/sbin/ipconfig. Command-table dispatch with getifaddr + ifcount so far (iter 8). |
src/IPConfiguration/ tree, ipconfigd binary, launchd plist.com.apple.IPConfiguration registered via bootstrap_check_in.dhcp_discover.c opens BPF on the first non-loopback Ethernet, builds the DHCPv4 packet, sends DISCOVER, parses OFFER.apply_lease.c calls SIOCAIFADDR and RTM_ADD for the default route.sc_publish.c ships the CF dictionary to configd over Mach RPC; scutil sees State:/Network/Service/<UUID>/IPv4.lease_loop.c implements RFC 2131 T1/T2 timers, sends RENEW unicast, falls back to REBIND broadcast.ipconfig.defs MIG IDL with ipconfig_if_count + ipconfig_if_addr.mach_service.c runs a worker thread blocked in mach_msg_server; bound_state.c holds shared lease state behind locks.ipconfigrpctest.c verifies end-to-end MIG client-server path.IPCFG-RENEW-OK added to the QEMU CI lane.arp_probe.c sends 3 ARP probes pre-claim, listens for conflicts, then 2 ARP announces post-claim.ra_listen.c opens an ICMPv6 socket, parses Router Advertisements (RFC 4861), extracts the PIO prefix.apply_lease_v6.c derives EUI-64 from the interface MAC, clears IFDISABLED, calls SIOCAIFADDR_IN6, adds ::/0 via the announced router.sc_publish.c also publishes State:/Network/Service/<UUID>/IPv6.DHCPv6Client.c is 3717 LOC with no CI validation path — QEMU SLIRP doesn't run a DHCPv6 server./usr/sbin/ipconfig with Apple-shape command-table dispatch.getifaddr, ifcount. Each calls a MIG client stub generated from ipconfig.defs.ipconfig.defs routines as real consumers arrive: getsummary, getpacket, getra, getoption, set.ipconfig.defs has 21+ routines; this repo's is 2 today. Don't gold-plate — grow when needed.ipconfig(8) subcommands track the IDL routines 1:1.Deferred-by-design (per the iter plan):
resolv.conf integration deferred until a consumer needs it.::/0 default route.ConfigMethod=Manual path yet.IPConfigurationHelper XPC helper not ported.ipconfigd gates DHCP on wland's AirPort key rather than talking 802.11 itself./var/db/dhcpclient/ equivalent — lease lives in bound_state.c RAM only.set_security_token (already used in mach.ko's fork_bsport path). No urgency — the daemon runs as root and the queries are read-only.
IORegisterForSystemPower + scheduled wake events let IPConfiguration coordinate lease renewal across sleep. FreeBSD has no pmset-equivalent. Build a bridge from devd ACPI events later, or live without sleep coordination on laptops.
ipconfigd coordinates with mDNSResponder so a sleep proxy answers ARP/ND while the host sleeps. Useful on a Mac; less useful on a desktop. Requires freebsd-mdnsresponder shipped first.
bootp-534.100.6). The IPConfiguration.bproj/ directory is the upstream we reference.
Revision 2026-05-23. Refactored from a deferred netconfigd-plugin plan (sibling AF_UNIX/DO track) to a shipped freebsd-launchd-mach (v2) standalone Mach daemon. Iter 1–8 (PRs #42–#51) merged: daemon skeleton, DHCPv4 INIT→BOUND, SCDynamicStore publish, RFC 2131 RENEW/REBIND with IPCONFIGD_FAST_LEASE CI gate, MIG service worker (2-routine surface), RFC 5227 ARP probe + announce, IPv6 RA listener + EUI-64 SLAAC + ::/0 route + State:/.../IPv6 publish, Apple-shape ipconfig(8) CLI. iter 7b (DHCPv6 stateful) deferred indefinitely. Future iters grow the MIG surface, add multi-interface workers, swap select() for kqueue, and subscribe to hwregd for hot-plug.