Skip to main content

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;