ax_net/options.rs
1//! Socket option data structures and dispatch traits.
2//!
3//! This module defines Linux-compatible option payloads plus the `Configurable`
4//! trait used by each socket implementation to handle getsockopt/setsockopt.
5//! The goal is to keep the syscall layer protocol-neutral: it builds one option
6//! request enum, then the concrete socket decides which values it supports.
7//!
8//! # Compatibility Boundary
9//!
10//! Option structs model Linux-visible ABI state, but not every field maps to a
11//! smoltcp feature. Unsupported or synthetic fields should be filled
12//! conservatively in the socket implementation, with defaults documented near
13//! the protocol that reports them.
14
15use alloc::{boxed::Box, sync::Arc};
16use core::{any::Any, fmt, time::Duration};
17
18use enum_dispatch::enum_dispatch;
19
20use crate::{InterfaceId, NetError, NetResult};
21
22/// Linux-like TCP connection state reported by TCP_INFO.
23#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
24pub enum TcpState {
25 /// No active TCP connection.
26 #[default]
27 Closed,
28 /// Passive listener.
29 Listen,
30 /// SYN sent, waiting for a matching SYN+ACK.
31 SynSent,
32 /// SYN received, waiting for the final ACK.
33 SynReceived,
34 /// Fully established connection.
35 Established,
36 /// Local endpoint has closed and sent FIN.
37 FinWait1,
38 /// Local FIN has been acknowledged.
39 FinWait2,
40 /// Remote endpoint has closed first.
41 CloseWait,
42 /// Both endpoints have closed simultaneously.
43 Closing,
44 /// Waiting for ACK of the local FIN after remote close.
45 LastAck,
46 /// Waiting for delayed packets to expire.
47 TimeWait,
48}
49
50bitflags::bitflags! {
51 /// Negotiated TCP options reported by TCP_INFO.
52 #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
53 pub struct TcpInfoOptions: u8 {
54 /// TCP timestamps are enabled.
55 const TIMESTAMPS = 1 << 0;
56 /// Selective ACK is enabled.
57 const SACK = 1 << 1;
58 /// Window scaling is enabled.
59 const WSCALE = 1 << 2;
60 /// Explicit congestion notification is enabled.
61 const ECN = 1 << 3;
62 /// ECN has been seen on the connection.
63 const ECN_SEEN = 1 << 4;
64 /// SYN data was used by the connection.
65 const SYN_DATA = 1 << 5;
66 }
67}
68
69/// Transport-independent TCP_INFO snapshot.
70#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
71pub struct TcpInfo {
72 /// Current TCP state.
73 pub state: TcpState,
74 /// Congestion-avoidance state. Zero means open.
75 pub ca_state: u8,
76 /// Number of unacknowledged retransmits.
77 pub retransmits: u8,
78 /// Number of pending keepalive or zero-window probes.
79 pub probes: u8,
80 /// Exponential backoff counter.
81 pub backoff: u8,
82 /// Negotiated TCP options.
83 pub options: TcpInfoOptions,
84 /// Send window scale.
85 pub snd_wscale: u8,
86 /// Receive window scale.
87 pub rcv_wscale: u8,
88 /// Retransmission timeout in microseconds.
89 pub rto_micros: u32,
90 /// Delayed ACK timeout in microseconds.
91 pub ato_micros: u32,
92 /// Send maximum segment size.
93 pub snd_mss: u32,
94 /// Receive maximum segment size.
95 pub rcv_mss: u32,
96 /// Bytes currently queued for transmit.
97 pub notsent_bytes: u32,
98 /// Advertised path MTU.
99 pub pmtu: u32,
100 /// Advertised maximum segment size.
101 pub advmss: u32,
102 /// Current send congestion window, in segments.
103 pub snd_cwnd: u32,
104 /// Packet reordering tolerance.
105 pub reordering: u32,
106 /// Available receive buffer space.
107 pub rcv_space: u32,
108 /// Current send window estimate.
109 pub snd_wnd: u32,
110 /// Current receive window estimate.
111 pub rcv_wnd: u32,
112}
113
114/// TCP congestion-control algorithms exposed by the transport backend.
115#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
116pub enum TcpCongestionControl {
117 /// The transport does not apply congestion-window control.
118 #[default]
119 None,
120}
121
122macro_rules! define_options {
123 ($($name:ident($value:ty),)*) => {
124 /// Operation to get a socket option.
125 ///
126 /// See [`Configurable::get_option`].
127 #[allow(missing_docs)]
128 pub enum GetSocketOption<'a> {
129 $(
130 $name(&'a mut $value),
131 )*
132 }
133
134 /// Operation to set a socket option.
135 ///
136 /// See [`Configurable::set_option`].
137 #[allow(missing_docs)]
138 #[derive(Clone, Copy)]
139 pub enum SetSocketOption<'a> {
140 $(
141 $name(&'a $value),
142 )*
143 }
144 };
145}
146
147/// Transport-owned Unix credentials plus an optional OS identity generation.
148///
149/// `pid`, `uid`, and `gid` are ABI-compatible fallback values. OS layers with
150/// PID namespaces can attach their generation object opaquely so queued
151/// messages and peer sockets do not depend on a reusable numeric PID.
152#[derive(Default, Clone)]
153pub struct UnixCredentials {
154 /// Process ID.
155 pub pid: u32,
156 /// User ID.
157 pub uid: u32,
158 /// Group ID.
159 pub gid: u32,
160 identity: Option<Arc<dyn Any + Send + Sync>>,
161}
162impl UnixCredentials {
163 /// Create a new `UnixCredentials` with the given PID and default UID/GID.
164 pub fn new(pid: u32) -> Self {
165 UnixCredentials {
166 pid,
167 uid: 0,
168 gid: 0,
169 identity: None,
170 }
171 }
172
173 /// Creates credentials with explicit numeric fallback values.
174 pub fn from_parts(pid: u32, uid: u32, gid: u32) -> Self {
175 Self {
176 pid,
177 uid,
178 gid,
179 identity: None,
180 }
181 }
182
183 /// Attaches an OS-specific stable process generation.
184 pub fn with_identity<T>(mut self, identity: Arc<T>) -> Self
185 where
186 T: Any + Send + Sync,
187 {
188 self.identity = Some(identity);
189 self
190 }
191
192 /// Borrows the attached process generation when it has the requested type.
193 pub fn identity<T: Any + Send + Sync>(&self) -> Option<&T> {
194 self.identity.as_deref()?.downcast_ref()
195 }
196}
197
198impl fmt::Debug for UnixCredentials {
199 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
200 formatter
201 .debug_struct("UnixCredentials")
202 .field("pid", &self.pid)
203 .field("uid", &self.uid)
204 .field("gid", &self.gid)
205 .field("has_identity", &self.identity.is_some())
206 .finish()
207 }
208}
209
210impl PartialEq for UnixCredentials {
211 fn eq(&self, other: &Self) -> bool {
212 self.pid == other.pid && self.uid == other.uid && self.gid == other.gid
213 }
214}
215
216impl Eq for UnixCredentials {}
217
218impl From<u32> for UnixCredentials {
219 fn from(pid: u32) -> Self {
220 Self::new(pid)
221 }
222}
223
224define_options! {
225 // ---- Socket level options (SO_*) ----
226 ReuseAddress(bool),
227 ReusePort(bool),
228 Error(i32),
229 DontRoute(bool),
230 SendBuffer(usize),
231 ReceiveBuffer(usize),
232 KeepAlive(bool),
233 SendTimeout(Duration),
234 ReceiveTimeout(Duration),
235 SendBufferForce(usize),
236 PassCredentials(bool),
237 ReceiveTimestamp(bool),
238 PeerCredentials(UnixCredentials),
239 SocketType(i32),
240 SocketProtocol(i32),
241 SocketDomain(i32),
242 BindToDevice(Option<InterfaceId>),
243 Priority(i32),
244
245 // --- TCP level options (TCP_*) ----
246 NoDelay(bool),
247 MaxSegment(usize),
248 TcpKeepIdle(u32),
249 TcpKeepInterval(u32),
250 TcpKeepCount(u32),
251 TcpUserTimeout(u32),
252 TcpInfo(TcpInfo),
253 TcpCongestionControl(TcpCongestionControl),
254
255 // ---- IP level options (IP_*) ----
256 Ttl(u8),
257 IpTos(u8),
258 RecvTtl(bool),
259 RecvTos(bool),
260 RecvTrafficClass(bool),
261 RecvErr(bool),
262 IpMtuDiscover(u8),
263
264 // ---- Extra options ----
265 NonBlocking(bool),
266}
267
268/// Trait for configurable socket-like objects.
269#[enum_dispatch]
270pub trait Configurable {
271 /// Get a socket option, returns `true` if the socket supports the option.
272 fn get_option_inner(&self, opt: &mut GetSocketOption) -> NetResult<bool>;
273 /// Set a socket option, returns `true` if the socket supports the option.
274 fn set_option_inner(&self, opt: SetSocketOption) -> NetResult<bool>;
275
276 /// Get a socket option. Dispatches to [`Configurable::get_option_inner`].
277 fn get_option(&self, mut opt: GetSocketOption) -> NetResult {
278 self.get_option_inner(&mut opt).and_then(|supported| {
279 if !supported {
280 Err(NetError::ProtocolOptionUnsupported)
281 } else {
282 Ok(())
283 }
284 })
285 }
286 /// Set a socket option. Dispatches to [`Configurable::set_option_inner`].
287 fn set_option(&self, opt: SetSocketOption) -> NetResult {
288 self.set_option_inner(opt).and_then(|supported| {
289 if !supported {
290 Err(NetError::ProtocolOptionUnsupported)
291 } else {
292 Ok(())
293 }
294 })
295 }
296}
297
298impl<T: Configurable + ?Sized> Configurable for Box<T> {
299 fn get_option_inner(&self, opt: &mut GetSocketOption) -> NetResult<bool> {
300 self.as_ref().get_option_inner(opt)
301 }
302
303 fn set_option_inner(&self, opt: SetSocketOption) -> NetResult<bool> {
304 self.as_ref().set_option_inner(opt)
305 }
306}