Skip to main content

teto_dpdk/
config.rs

1/// Builder for F-Stack / DPDK initialisation arguments.
2///
3/// Separates the config-file arguments (understood by F-Stack's own parser)
4/// from extra EAL arguments (passed directly to DPDK's `rte_eal_init`).
5///
6/// # Examples
7///
8/// Docker / TAP development:
9/// ```rust
10/// let cfg = FStackConfig::for_docker();
11/// ```
12///
13/// Bare metal with a real NIC bound via VFIO:
14/// ```rust
15/// let cfg = FStackConfig::for_bare_metal();
16/// ```
17///
18/// Custom build:
19/// ```rust
20/// let cfg = FStackConfig::new("config.ini")
21///     .with_eal_arg("--vdev=net_tap0,iface=dtap0,mac=fixed")
22///     .with_eal_arg("--no-pci");
23/// ```
24pub struct FStackConfig {
25    config_file: String,
26    eal_args:    Vec<String>,
27}
28
29impl FStackConfig {
30    /// Create a config pointing at `config_file` with no extra EAL arguments.
31    pub fn new(config_file: impl Into<String>) -> Self {
32        Self {
33            config_file: config_file.into(),
34            eal_args:    Vec::new(),
35        }
36    }
37
38    /// Append a single extra EAL argument (e.g. `"--no-pci"`).
39    pub fn with_eal_arg(mut self, arg: impl Into<String>) -> Self {
40        self.eal_args.push(arg.into());
41        self
42    }
43
44    // ------------------------------------------------------------------
45    // Pre-built profiles
46    // ------------------------------------------------------------------
47
48    /// Docker / TAP device profile.
49    ///
50    /// Injects the three EAL arguments that are required when running DPDK
51    /// inside a container with a TAP virtual interface instead of a real NIC:
52    ///
53    /// - `--vdev=net_tap0,iface=dtap0,mac=fixed`  — create a TAP-backed DPDK
54    ///   port tied to the kernel interface `dtap0`; `mac=fixed` makes the MAC
55    ///   deterministic so that it is stable across restarts. The kernel side of
56    ///   the TAP is automatically assigned a *different* MAC by `entrypoint.sh`
57    ///   (derived from the DPDK MAC by incrementing the last octet). The two
58    ///   MACs must differ: FreeBSD's `ether_input` drops frames whose source
59    ///   MAC matches the interface MAC (anti-loop protection).
60    /// - `--no-pci`  — skip PCI bus scan; without this DPDK could claim a PCI
61    ///   device and push the TAP device to port 1, breaking `port_list=0`.
62    /// - `--iova-mode=va`  — force Virtual Address IOVA mode, required in
63    ///   containers / WSL2 where physical address access is unavailable.
64    pub fn for_docker() -> Self {
65        Self::new("config.ini")
66            .with_eal_arg("--vdev=net_tap0,iface=dtap0,mac=fixed")
67            .with_eal_arg("--no-pci")
68            .with_eal_arg("--iova-mode=va")
69    }
70
71    /// Bare-metal / AWS profile.
72    ///
73    /// No extra EAL arguments are needed: DPDK discovers the NIC via the
74    /// `allow=` key in `config.ini`, PCI scanning is required, and IOVA mode
75    /// is auto-detected based on whether IOMMU is present.
76    pub fn for_bare_metal() -> Self {
77        Self::new("config.ini")
78    }
79
80    // ------------------------------------------------------------------
81    // Accessors used by the cxx bridge
82    // ------------------------------------------------------------------
83
84    /// The arguments passed to `ff_load_config` (F-Stack's config parser).
85    pub fn config_args(&self) -> Vec<String> {
86        vec![
87            "teto".to_string(),
88            "--conf".to_string(),
89            self.config_file.clone(),
90        ]
91    }
92
93    /// Extra EAL arguments injected into `dpdk_argv` after `ff_load_config`.
94    pub fn eal_args(&self) -> Vec<String> {
95        self.eal_args.clone()
96    }
97}
98
99// ---------------------------------------------------------------------------
100// Per-connection TCP socket options
101// ---------------------------------------------------------------------------
102
103/// Per-connection TCP tuning applied to every accepted socket.
104///
105/// All fields are optional. When `None`, the FreeBSD default is used.
106/// Construct with `Default::default()` for all defaults, or use the builder
107/// methods to override specific values.
108///
109/// ```rust
110/// let opts = TcpSocketOptions::default()
111///     .nodelay(true)
112///     .keepalive(true)
113///     .keepalive_idle_secs(10)
114///     .keepalive_interval_secs(5)
115///     .keepalive_count(3);
116/// ```
117#[derive(Clone, Debug, Default)]
118pub struct TcpSocketOptions {
119    /// Disable Nagle's algorithm — send data immediately without coalescing
120    /// small writes. Essential for latency-sensitive protocols.
121    pub nodelay: Option<bool>,
122
123    /// Enable TCP keepalive probes on idle connections. When the remote peer
124    /// disappears without sending a FIN (crash, network partition), keepalive
125    /// detects it and tears down the connection.
126    pub keepalive: Option<bool>,
127
128    /// Seconds of idle time before the first keepalive probe is sent.
129    /// FreeBSD default: 7200 (2 hours).
130    pub keepalive_idle_secs: Option<u32>,
131
132    /// Seconds between consecutive keepalive probes after the first.
133    /// FreeBSD default: 75.
134    pub keepalive_interval_secs: Option<u32>,
135
136    /// Number of unacknowledged keepalive probes before the connection is
137    /// dropped. FreeBSD default: 8.
138    pub keepalive_count: Option<u32>,
139
140    /// Receive buffer size in bytes. Larger buffers allow higher throughput on
141    /// high-latency links. FreeBSD default: ~64 KB.
142    pub recv_buf: Option<u32>,
143
144    /// Send buffer size in bytes. FreeBSD default: ~64 KB.
145    pub send_buf: Option<u32>,
146
147    /// Linger timeout in seconds. When set, `ff_close` blocks (up to this
148    /// many seconds) until buffered data is sent, then sends RST if it
149    /// couldn't drain in time. When `None`, close returns immediately and
150    /// the stack drains in the background.
151    pub linger_secs: Option<u32>,
152
153    /// Disable delayed ACKs — acknowledge segments immediately instead of
154    /// waiting up to 40ms to piggyback the ACK on outgoing data.
155    /// Complementary to `nodelay` for lowest latency.
156    pub quickack: Option<bool>,
157
158    /// Allow multiple sockets to bind the same address:port combination.
159    /// Useful for multi-process F-Stack setups.
160    pub reuse_port: Option<bool>,
161}
162
163impl TcpSocketOptions {
164    pub fn nodelay(mut self, v: bool) -> Self { self.nodelay = Some(v); self }
165    pub fn keepalive(mut self, v: bool) -> Self { self.keepalive = Some(v); self }
166    pub fn keepalive_idle_secs(mut self, v: u32) -> Self { self.keepalive_idle_secs = Some(v); self }
167    pub fn keepalive_interval_secs(mut self, v: u32) -> Self { self.keepalive_interval_secs = Some(v); self }
168    pub fn keepalive_count(mut self, v: u32) -> Self { self.keepalive_count = Some(v); self }
169    pub fn recv_buf(mut self, v: u32) -> Self { self.recv_buf = Some(v); self }
170    pub fn send_buf(mut self, v: u32) -> Self { self.send_buf = Some(v); self }
171    pub fn linger_secs(mut self, v: u32) -> Self { self.linger_secs = Some(v); self }
172    pub fn quickack(mut self, v: bool) -> Self { self.quickack = Some(v); self }
173    pub fn reuse_port(mut self, v: bool) -> Self { self.reuse_port = Some(v); self }
174
175    /// Convert to the flat cxx-bridge struct. `-1` encodes "not set / use FreeBSD default".
176    pub fn to_ffi(&self) -> crate::fstack::ffi::TcpSocketOptionsFfi {
177        crate::fstack::ffi::TcpSocketOptionsFfi {
178            nodelay:                 self.nodelay.map_or(-1, |v| v as i32),
179            keepalive:               self.keepalive.map_or(-1, |v| v as i32),
180            keepalive_idle_secs:     self.keepalive_idle_secs.map_or(-1, |v| v as i32),
181            keepalive_interval_secs: self.keepalive_interval_secs.map_or(-1, |v| v as i32),
182            keepalive_count:         self.keepalive_count.map_or(-1, |v| v as i32),
183            recv_buf:                self.recv_buf.map_or(-1, |v| v as i32),
184            send_buf:                self.send_buf.map_or(-1, |v| v as i32),
185            linger_secs:             self.linger_secs.map_or(-1, |v| v as i32),
186            quickack:                self.quickack.map_or(-1, |v| v as i32),
187            reuse_port:              self.reuse_port.map_or(-1, |v| v as i32),
188        }
189    }
190}