air_sys_types/net.rs
1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5//! Types de la famille `net` — sockets, adresses, options.
6//!
7//! Cf. `docs/specs/layer-0/family-net.md`.
8
9use alloc::ffi::CString;
10use alloc::vec::Vec;
11use core::time::Duration;
12
13use bitflags::bitflags;
14
15/// Tranche de mémoire en lecture seule pour I/O vectorielle.
16///
17/// Layout C-compatible avec `struct iovec` du kernel Linux (`iov_base` + `iov_len`).
18/// Garantit que le buffer référencé reste valide pour la durée de l'appel syscall.
19///
20/// # Safety
21///
22/// Le buffer pointé doit rester valide et immuable pendant tout appel syscall
23/// qui consomme cette tranche.
24#[repr(C)]
25pub struct IoSlice<'a> {
26 ptr: *const u8,
27 len: usize,
28 _marker: core::marker::PhantomData<&'a [u8]>,
29}
30
31impl<'a> IoSlice<'a> {
32 /// Construit une tranche depuis une référence slice.
33 #[must_use]
34 pub fn new(buffer: &'a [u8]) -> Self {
35 Self {
36 ptr: buffer.as_ptr(),
37 len: buffer.len(),
38 _marker: core::marker::PhantomData,
39 }
40 }
41}
42
43impl core::fmt::Debug for IoSlice<'_> {
44 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
45 f.debug_struct("IoSlice")
46 .field("ptr", &self.ptr)
47 .field("length", &self.len)
48 .finish()
49 }
50}
51
52// SAFETY: IoSlice contient un pointeur vers des données immuables partagées
53// de façon read-only ; Send+Sync sont corrects si le buffer référencé est Send+Sync.
54unsafe impl Send for IoSlice<'_> {}
55// SAFETY: comme `&[u8]`, qu'il représente : des octets en lecture seule, partageables
56// entre fils.
57unsafe impl Sync for IoSlice<'_> {}
58
59/// Tranche de mémoire en lecture/écriture pour I/O vectorielle.
60///
61/// Layout C-compatible avec `struct iovec` du kernel Linux.
62///
63/// # Safety
64///
65/// Le buffer pointé doit rester valide et accessible en écriture pendant tout appel
66/// syscall qui consomme cette tranche.
67#[repr(C)]
68pub struct IoSliceMut<'a> {
69 ptr: *mut u8,
70 len: usize,
71 _marker: core::marker::PhantomData<&'a mut [u8]>,
72}
73
74impl<'a> IoSliceMut<'a> {
75 /// Construit une tranche mutable depuis une référence slice mutable.
76 #[must_use]
77 pub fn new(buffer: &'a mut [u8]) -> Self {
78 Self {
79 ptr: buffer.as_mut_ptr(),
80 len: buffer.len(),
81 _marker: core::marker::PhantomData,
82 }
83 }
84}
85
86impl core::fmt::Debug for IoSliceMut<'_> {
87 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
88 f.debug_struct("IoSliceMut")
89 .field("ptr", &self.ptr)
90 .field("length", &self.len)
91 .finish()
92 }
93}
94
95// SAFETY: IoSliceMut contient un pointeur exclusif vers des données mutables ;
96// Send est correct si le buffer référencé est Send (accès exclusif garanti par &mut).
97unsafe impl Send for IoSliceMut<'_> {}
98// SAFETY: comme `&mut [u8]`, qu'il représente : un `&IoSliceMut` partagé ne permet que
99// la lecture, la mutation exigeant l'accès exclusif `&mut`.
100unsafe impl Sync for IoSliceMut<'_> {}
101
102use crate::fd::{BorrowedFd, OwnedFd};
103use crate::process::Pid;
104
105/// Famille de protocole socket (premier argument de `socket(2)`).
106///
107/// Correspond aux constantes `AF_*` de `<sys/socket.h>`.
108#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
109#[repr(i32)]
110pub enum SocketDomain {
111 /// Sockets Unix locaux (communication intra-machine). IPC principal d'Air.
112 Unix = 1,
113 /// Sockets IPv4.
114 Ipv4 = 2,
115 /// Sockets IPv6.
116 Ipv6 = 10,
117}
118
119/// Type de socket (deuxième argument de `socket(2)`).
120///
121/// Le wrapper Air ajoute `SOCK_CLOEXEC` en interne. L'appelant ne doit
122/// pas le spécifier manuellement.
123#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
124#[repr(i32)]
125pub enum SocketType {
126 /// Flux d'octets ordonné et fiable (TCP, Unix stream).
127 Stream = 1,
128 /// Datagrammes sans connexion (UDP, Unix datagram).
129 Datagram = 2,
130 /// Protocole brut (pas de couche transport kernel).
131 Raw = 3,
132 /// Flux de messages ordonnés et fiables (SCTP, Unix seqpacket).
133 SeqPacket = 5,
134}
135
136/// Niveau de protocole d'une option socket (`level` de `getsockopt`/`setsockopt`).
137///
138/// Correspond aux constantes `SOL_SOCKET` / `IPPROTO_*` de `<sys/socket.h>` et
139/// `<netinet/in.h>`. Newtype typé (ADR-029 : jamais d'`i32` brut pour un niveau) ;
140/// partagé entre `family-net` (synchrone) et les façades `io_uring::cmd`
141/// `getsockopt`/`setsockopt`.
142#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
143#[repr(i32)]
144pub enum SocketOptionLevel {
145 /// `SOL_SOCKET` (1) : options génériques du socket (`SO_*`).
146 Socket = 1,
147 /// `IPPROTO_IP` (0) : options IPv4 (`IP_*`).
148 Ip = 0,
149 /// `IPPROTO_TCP` (6) : options TCP (`TCP_*`).
150 Tcp = 6,
151 /// `IPPROTO_UDP` (17) : options UDP (`UDP_*`).
152 Udp = 17,
153 /// `IPPROTO_IPV6` (41) : options IPv6 (`IPV6_*`).
154 Ipv6 = 41,
155}
156
157impl SocketOptionLevel {
158 /// Valeur brute (`level`) attendue par le kernel.
159 #[must_use]
160 #[inline]
161 pub const fn as_raw(self) -> i32 {
162 self as i32
163 }
164}
165
166/// Adresse d'un socket Unix (local).
167///
168/// Linux distingue trois sous-types d'adresses Unix :
169/// - **path** : lié à un chemin filesystem (supprimé par `unlinkat` après usage).
170/// - **abstract** : dans le namespace abstrait kernel (pas de fichier).
171/// - **unnamed** : créé avec `socketpair` sans bind ; pas d'adresse.
172#[derive(Debug, Clone, PartialEq, Eq)]
173pub enum UnixSocketAddr {
174 /// Socket lié à un chemin filesystem.
175 Path(CString),
176 /// Socket dans le namespace abstrait (commence par `\0` côté kernel).
177 Abstract(Vec<u8>),
178 /// Socket non lié (sans adresse).
179 Unnamed,
180}
181
182/// Adresse IPv4 (host + port).
183#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
184pub struct Ipv4SocketAddr {
185 /// Adresse en réseau byte-order (big-endian).
186 pub address: [u8; 4],
187 /// Port en host byte-order.
188 pub port: u16,
189}
190
191/// Adresse IPv6 (host + port + flowinfo + scope).
192#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
193pub struct Ipv6SocketAddr {
194 /// Adresse IPv6 (16 octets, big-endian).
195 pub address: [u8; 16],
196 /// Port en host byte-order.
197 pub port: u16,
198 /// Etiquette de flux (RFC 2460).
199 pub flowinfo: u32,
200 /// Identifiant de scope de lien (utile pour les adresses link-local).
201 pub scope_id: u32,
202}
203
204/// Adhésion à un groupe multicast **IPv4** — `struct ip_mreq` du kernel.
205///
206/// Argument de `setsockopt(IP_ADD_MEMBERSHIP / IP_DROP_MEMBERSHIP)`. Layout
207/// `#[repr(C)]` **fidèle à l'ABI** : deux `struct in_addr` accolées (chacune un
208/// `__be32`, **ordre réseau**), soit **8 octets sans rembourrage** (assertion de
209/// taille en test). Type dédié (ADR-021) — jamais d'octets bruts exposés.
210#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
211#[repr(C)]
212pub struct IpMembershipV4 {
213 /// Adresse du groupe multicast (`imr_multiaddr`, ordre réseau big-endian).
214 pub multiaddr: [u8; 4],
215 /// Adresse de l'interface locale (`imr_interface`, ordre réseau) ;
216 /// `[0, 0, 0, 0]` = `INADDR_ANY` (le kernel choisit via la table de routage).
217 pub interface: [u8; 4],
218}
219
220/// Adhésion à un groupe multicast **IPv6** — `struct ipv6_mreq` du kernel.
221///
222/// Argument de `setsockopt(IPV6_ADD_MEMBERSHIP / IPV6_DROP_MEMBERSHIP)`. Layout
223/// `#[repr(C)]` **fidèle à l'ABI** : `struct in6_addr` (16 octets, ordre réseau)
224/// suivie de l'index d'interface (`int ipv6mr_ifindex`, **ordre hôte**), soit
225/// **20 octets** (`interface` à l'offset 16 — assertions en test). Type dédié
226/// (ADR-021).
227#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
228#[repr(C)]
229pub struct IpMembershipV6 {
230 /// Adresse du groupe multicast (`ipv6mr_multiaddr`, 16 octets big-endian).
231 pub multiaddr: [u8; 16],
232 /// Index de l'interface locale (`ipv6mr_ifindex`, ordre hôte) ; `0` laisse
233 /// le kernel choisir. L'index de `lo` vaut toujours `1` sur Linux.
234 pub interface: u32,
235}
236
237/// Adresse socket (toutes familles).
238///
239/// Utilisée par `bind`, `connect`, `accept4`, `getsockname`,
240/// `getpeername`, `sendmsg`, `recvmsg`.
241#[derive(Debug, Clone, PartialEq, Eq)]
242pub enum SocketAddr {
243 /// Adresse Unix locale.
244 Unix(UnixSocketAddr),
245 /// Adresse IPv4.
246 Ipv4(Ipv4SocketAddr),
247 /// Adresse IPv6.
248 Ipv6(Ipv6SocketAddr),
249}
250
251bitflags! {
252 /// Drapeaux de message pour `send`, `recv`, `sendmsg`, `recvmsg`.
253 ///
254 /// Correspond aux constantes `MSG_*` de `<sys/socket.h>`.
255 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
256 pub struct MessageFlags: i32 {
257 /// Données urgentes out-of-band.
258 const OOB = 0x0001;
259 /// Lit sans consommer (les données restent disponibles).
260 const PEEK = 0x0002;
261 /// Ne passe pas par la table de routage (sockets raw).
262 const DONTROUTE = 0x0004;
263 /// Données de contrôle tronquées.
264 const CTRUNC = 0x0008;
265 /// Message tronqué (taille buffer insuffisante).
266 const TRUNC = 0x0020;
267 /// Opération non-bloquante (équivalent à `O_NONBLOCK` pour ce msg).
268 const DONTWAIT = 0x0040;
269 /// Fin de l'enregistrement (sockets SEQPACKET).
270 const EOR = 0x0080;
271 /// Attend tous les octets demandés (bloquant total ou erreur).
272 const WAITALL = 0x0100;
273 /// Ne génère pas de `SIGPIPE` si l'autre extrémité est fermée.
274 /// **Activé par défaut** par le wrapper `send` synchrone.
275 const NOSIGNAL = 0x4000;
276 /// Les FDs reçus via SCM_RIGHTS sont marqués `CLOEXEC`.
277 const CMSG_CLOEXEC = 0x4000_0000;
278 }
279}
280
281bitflags! {
282 /// Drapeaux pour `accept4(2)`.
283 ///
284 /// `CLOEXEC` est activé par défaut par le wrapper.
285 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
286 pub struct AcceptFlags: i32 {
287 /// Marque le nouveau socket `CLOEXEC` atomiquement.
288 const CLOEXEC = 0x0008_0000;
289 /// Nouveau socket non-bloquant.
290 const NONBLOCK = 0x0000_0800;
291 }
292}
293
294/// Résultat d'un `accept4`.
295#[derive(Debug)]
296pub struct AcceptResult {
297 /// FD du nouveau socket connecté (possédé, CLOEXEC par défaut).
298 pub fd: OwnedFd,
299 /// Adresse du pair, si disponible.
300 pub address: Option<SocketAddr>,
301}
302
303/// Mode d'arrêt pour `shutdown(2)`.
304///
305/// Correspond aux constantes `SHUT_*` de `<sys/socket.h>`.
306#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
307#[repr(i32)]
308pub enum ShutdownMode {
309 /// Arrête les réceptions futures (`SHUT_RD`).
310 Read = 0,
311 /// Arrête les envois futurs (`SHUT_WR`).
312 Write = 1,
313 /// Arrête les deux (`SHUT_RDWR`).
314 Both = 2,
315}
316
317/// Identité Unix reçue via `SO_PEERCRED`.
318///
319/// Authentifie l'identité du processus pair sur un socket Unix
320/// sans nécessiter de protocole applicatif d'authentification.
321#[derive(Debug, Clone, Copy, PartialEq, Eq)]
322pub struct UnixCredentials {
323 /// PID du processus pair.
324 pub pid: Pid,
325 /// UID effectif du processus pair.
326 pub uid: u32,
327 /// GID effectif du processus pair.
328 pub gid: u32,
329}
330
331/// Configuration du linger pour `SO_LINGER`.
332#[derive(Debug, Clone, Copy, PartialEq, Eq)]
333pub enum LingerOption {
334 /// Linger désactivé : `close` retourne immédiatement.
335 Disabled,
336 /// Linger activé : `close` bloque jusqu'à l'envoi des données ou
337 /// l'expiration du délai.
338 Enabled(Duration),
339}
340
341/// Requête pour `sendmsg(2)`.
342///
343/// Supporte l'envoi d'un vecteur de buffers et le passage de FDs
344/// via le mécanisme `SCM_RIGHTS` des ancillaires Unix.
345///
346/// **Allocation heap :** `fds` est un slice — pas d'allocation ici.
347/// La construction des ancillaires en passe 2 se fait sur la pile.
348#[derive(Debug)]
349pub struct SendMessageRequest<'a> {
350 /// Vecteur de buffers à envoyer.
351 pub iov: &'a [IoSlice<'a>],
352 /// Adresse de destination (pour les sockets non connectés).
353 pub address: Option<&'a SocketAddr>,
354 /// FDs à passer au pair via `SCM_RIGHTS`.
355 pub fds: &'a [BorrowedFd<'a>],
356 /// Drapeaux de message.
357 pub flags: MessageFlags,
358}
359
360/// Requête pour `recvmsg(2)`.
361#[derive(Debug)]
362pub struct ReceiveMessageRequest<'a> {
363 /// Vecteur de buffers de destination.
364 pub iov: &'a mut [IoSliceMut<'a>],
365 /// Drapeaux de message.
366 pub flags: MessageFlags,
367}
368
369/// Résultat de `sendmsg(2)`.
370#[derive(Debug, Clone, Copy)]
371pub struct SendMessageResult {
372 /// Nombre d'octets effectivement envoyés.
373 pub bytes_sent: usize,
374}
375
376/// Résultat de `recvmsg(2)`.
377///
378/// **Allocation heap :** `fds` contient les [`OwnedFd`] reçus via
379/// `SCM_RIGHTS`. Le nombre de FDs est dynamique ; l'allocation est
380/// intrinsèque à l'opération (nécessité documentée, ADR-021 convention 4).
381#[derive(Debug)]
382pub struct ReceiveMessageResult {
383 /// Nombre d'octets reçus.
384 pub bytes_received: usize,
385 /// Adresse du pair (si disponible).
386 pub address: Option<SocketAddr>,
387 /// FDs reçus via `SCM_RIGHTS` (possédés, CLOEXEC selon `CMSG_CLOEXEC`).
388 pub fds: Vec<OwnedFd>,
389 /// Drapeaux retournés par le kernel.
390 pub flags: MessageFlags,
391}
392
393// ─────────────────────────────────────────────────────────────────────────
394// Types ajoutés pour io_uring Temps 2b (réseau). Cf.
395// docs/specs/layer-0/io-uring-2b-network.md.
396//
397// NOTE DE DIVERGENCE (signalée) : la spec 2b nomme `SendMessageRequest`/
398// `ReceiveMessageRequest` comme types partagés, mais ceux de `family-net`
399// ci-dessus sont **basés sur des emprunts** (`&'a [...]`), adaptés au chemin
400// synchrone (les buffers vivent sur la pile de l'appelant pendant le syscall).
401// io_uring lit les buffers **de façon asynchrone** : ils doivent être
402// **possédés** et survivre jusqu'à la complétion (transfert d'ownership S1). On
403// introduit donc des **siblings possédés** (`OwnedSendMessage`/
404// `OwnedReceiveMessage`) plutôt que de redéfinir les types empruntés. Idem
405// `ControlMessages` : `family-net` modélise le passage de FD via le champ `fds`
406// directement (pas de type dédié) — repris ici en `OwnedSendMessage::fds`.
407// ─────────────────────────────────────────────────────────────────────────
408
409bitflags! {
410 /// Drapeaux zero-copy d'io_uring (`IORING_SEND_ZC_*`) pour `send_zc` /
411 /// `sendmsg_zc`.
412 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
413 pub struct ZeroCopyFlags: u32 {
414 /// `IORING_SEND_ZC_REPORT_USAGE` : la complétion NOTIF rapporte si le
415 /// kernel a vraiment fait du zero-copy ou s'il a dû copier
416 /// ([`crate::net`] ; lisible via `Completion::zero_copy_copied`).
417 const REPORT_USAGE = 1 << 0;
418 }
419}
420
421/// Métadonnées d'une réception `recvmsg` via io_uring, adossées à
422/// `struct io_uring_recvmsg_out` (`namelen`/`controllen`/`payloadlen`/`flags`),
423/// **étendues** des FD matérialisés et de l'adresse pair décodée.
424///
425/// Pour le `recvmsg` one-shot du Temps 2b, ces scalaires proviennent du
426/// `msghdr` réécrit par le kernel ; le format `io_uring_recvmsg_out` préfixé au
427/// buffer est la variante **multishot** (Temps 3d).
428#[derive(Debug)]
429pub struct ReceiveMessageMeta {
430 /// Longueur de l'adresse pair écrite (`msg_namelen`).
431 pub namelen: u32,
432 /// Longueur des données de contrôle écrites (`msg_controllen`).
433 pub controllen: u32,
434 /// Longueur de la charge utile reçue (octets de données).
435 pub payloadlen: u32,
436 /// Drapeaux retournés par le kernel (`msg_flags`).
437 pub flags: MessageFlags,
438 /// Adresse du pair décodée (si `namelen > 0`).
439 pub address: Option<SocketAddr>,
440 /// FD reçus via `SCM_RIGHTS`, matérialisés en [`OwnedFd`] **CLOEXEC**.
441 pub fds: Vec<OwnedFd>,
442}
443
444impl ReceiveMessageMeta {
445 /// `MSG_TRUNC` : la charge utile a été tronquée (buffers de données trop
446 /// petits) — des octets ont été perdus.
447 #[must_use]
448 pub fn payload_truncated(&self) -> bool {
449 self.flags.contains(MessageFlags::TRUNC)
450 }
451
452 /// `MSG_CTRUNC` : les données de contrôle ont été tronquées (buffer de
453 /// contrôle trop petit) — des FD ont pu être perdus (fermés proprement par
454 /// la façade, aucune fuite).
455 #[must_use]
456 pub fn control_truncated(&self) -> bool {
457 self.flags.contains(MessageFlags::CTRUNC)
458 }
459}
460
461/// Requête `sendmsg` **possédée** pour io_uring (sibling async de
462/// [`SendMessageRequest`]). Tout est déplacé dans le slot S1 et restitué à la
463/// complétion.
464#[derive(Debug, Default)]
465pub struct OwnedSendMessage {
466 /// Buffers de données à envoyer (vectorisé ; possédés).
467 pub buffers: Vec<Vec<u8>>,
468 /// Adresse de destination (sockets non connectés ; possédée).
469 pub address: Option<SocketAddr>,
470 /// FD à passer au pair via `SCM_RIGHTS` (gardés vivants jusqu'à la
471 /// complétion, puis restitués — le kernel en duplique des copies).
472 pub fds: Vec<OwnedFd>,
473 /// Drapeaux de message (`MSG_NOSIGNAL` ajouté par défaut par la façade).
474 pub flags: MessageFlags,
475}
476
477/// Requête `recvmsg` **possédée** pour io_uring (sibling async de
478/// [`ReceiveMessageRequest`]).
479#[derive(Debug, Default)]
480pub struct OwnedReceiveMessage {
481 /// Buffers de réception (vectorisé ; possédés, remplis par le kernel).
482 pub buffers: Vec<Vec<u8>>,
483 /// Capacité du buffer de contrôle pré-dimensionné pour les cmsg entrants
484 /// (FD reçus). 0 = aucune réception de FD attendue.
485 pub control_capacity: usize,
486 /// Drapeaux de message (`MSG_CMSG_CLOEXEC` ajouté par défaut par la façade).
487 pub flags: MessageFlags,
488}
489
490#[cfg(test)]
491mod tests;