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