ADR-079 — air-netlink (couche 1) : transport netlink générique, crate indépendante + descellement additif couche-0-v1.11 (AF_NETLINK + sockaddr_nl)
Statut : Accepté (décision BDFL, 2026-07-07 — option B, couche 1). RFC de
structure (ADR-015). Applique le modèle de
nouvelle crate couche 1 + descellement additif (ADR-062),
précédents ADR-069 (crate air-handle)
et ADR-051/ADR-070/ADR-071
(descellements additifs couche 0).
Catégorie : Nouvelle crate couche 1 (air-netlink) + descellement additif
couche 0 (AF_NETLINK/sockaddr_nl). Aucune rupture d’ABI ni de signature ; jalons
antérieurs conservés.
Contexte
La spec air-network (substrat de connexion L2) a
besoin, pour sa conscience du chemin (AirNetworkPathMonitor ≈ NWPathMonitor),
d’observer l’état réseau du noyau : interfaces, adresses, routes, liens up/down.
C’est du plan de contrôle réseau — le domaine de rtnetlink (NETLINK_ROUTE).
La couche 0 n’expose qu’un socket NETLINK_KOBJECT_UEVENT spécifique (hotplug,
consommé par air-device), pas de netlink générique ; air-socket a
délibérément retiré netlink (décision 2026-06-15 : « netlink relève de la
configuration kernel, concern distinct → futur crate air-netlink »).
Deux options ont été analysées :
- (A) Étendre
air-device— réutilise l’infra netlink existante, mais conflate deux protocoles très différents (uevent = broadcast texte read-only vs rtnetlink = requête/réponse + dump + attributs TLV, surface hostile), exige un descellement lourd d’une crate scellée dédiée aux devices, et disperse un transport que d’autres consommateurs veulent (WireGuard, DHCP, routesair-config, wifi nl80211). - (B) Crate indépendante
air-netlink— responsabilité unique, transport réutilisable par tous les consommateurs, fuzzable en isolation (le TLV kernel est la surface hostile), cohérent avec la décisionair-socketdéjà écrite ; coût = une crate + un petit additif couche 0.
Décision
Option B, couche 1. On crée une crate
air-netlink(couche 1) : un transport netlink générique (message framing, codec d’attributs TLV, corrélation requête/réponse/dump), suivant le patron sans-IO 9-composants (note réseau §2.2). Elle est réutilisable parair-network(path monitor), les daemons WireGuard/DHCP (L5),air-config(projection de routes), et le futur wifi (nl80211) / firewall (nftables).
Placement couche 1 (pair d’air-socket), pas couche 2. Un transport kernel-facing
générique est au niveau d’air-socket (L1) ; ce sont ses consommateurs qui vivent
plus haut (air-network L2, daemons L5). Cela révise la mention prospective
« couche 2 » de la spec air-socket (référence approximative), à réconcilier par grep.
Descellement additif couche 0 requis (couche-0-v1.11). Les syscalls génériques
socket/bind/sendmsg/recvmsg existent déjà (air-sys-syscall::net). Il manque
seulement :
SocketDomain::Netlink = 16(AF_NETLINK) dans l’enum typé (air-sys-types::net, aujourd’huiUnix=1/Ipv4=2/Ipv6=10) ;- un type/codec
SocketAddrNetlink(sockaddr_nl:nl_family,nl_pad,nl_pid,nl_groups— 12 octets) exposé pourbind.
Aucun nouveau syscall. Additif modéré (modèle ADR-070 sockaddr codec).
air-device inchangé. Son socket uevent NETLINK_KOBJECT_UEVENT reste tel quel
(pas de descellement de air-device). Une migration future de ce socket sur
air-netlink est possible mais non requise (non bloquante) — notée en suite.
Gouvernance
- Décision structurante (nouvelle crate + touche couche 0 scellée) → ratification BDFL (présent ADR), pas la délégation additive neutre.
- Tags posés après implémentation + barrière 2 arches (x86_64 + aarch64,
ADR-072) :
couche-0-v1.11(additifAF_NETLINK) puiscouche-1-v1.x(crateair-netlink). - Zéro-C, zéro
unsafeexposé ; parsing TLV borné + fuzzé (surface hostile kernel), couverture 100 % (couche 1).
Conséquences
- Nouvelle crate
crates/air-netlink(couche 1) ; specdocs/specs/layer-1/air-netlink.md. - Descellement couche 0 :
SocketDomain::Netlink+SocketAddrNetlink→couche-0-v1.11. air-network::AirNetworkPathMonitordébloqué (rtnetlink lien/adresse/route + abonnement multicastRTNLGRP_*).- Réconciliation doc : mention « couche 2 » d’
air-socketcorrigée en « couche 1 ». - Précédent : les autres familles netlink (nl80211, nftables, genetlink) se poseront au-dessus de ce transport, sans le réécrire.
Alternatives rejetées
- (A) Étendre
air-device— conflate uevent (broadcast texte) et rtnetlink (plan de contrôle TLV) ; descellement lourd d’une crate scellée hors périmètre ; disperse un transport multi-consommateurs. Rejeté. air-netlinken couche 2 — un transport kernel générique appartient à L1 (pair d’air-socket) ; le placer en L2 créerait des dépendances descendantes tordues pourair-config(L1) et compliquerait la réutilisation. Rejeté.- Réécrire un parseur netlink par consommateur — duplication d’une surface de parsing hostile ; contraire à la règle des 80 % et au patron sans-IO. Rejeté.
Suite
- Spec
air-netlink(couche 1) — faite (docs/specs/layer-1/air-netlink.md). - Implémentation : additif couche 0 (
AF_NETLINK/sockaddr_nl, RFC de descellement) puis crateair-netlink; tags après barrière 2 arches. - Migration éventuelle du socket uevent d’
air-devicesurair-netlink(non bloquante). - Câblage : registre ADRs,
SUMMARY.md,INDEX.md; note « à résoudre » d’air-networkmise à jour.