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}