netmap_min_sys/lib.rs
1//! Raw FFI bindings and helper shims for the [Netmap](https://netmap.org/)
2//! kernel-bypass networking framework.
3//!
4//! The bulk of the API is generated with `bindgen` from the Netmap C headers
5//! (`<net/netmap_user.h>` and `<net/netmap.h>`) and re-exported here. On top of
6//! the raw bindings this crate also provides:
7//!
8//! * [`nm_open`] / [`nm_close`] — the standard Netmap descriptor helpers. These
9//! are `static` functions in the C headers (so no shared/static library
10//! exports them); this crate compiles them in via a small C shim.
11//! * [`NETMAP_TXRING`] / [`NETMAP_RXRING`] / [`NETMAP_IF`] / [`NETMAP_BUF`] —
12//! Rust implementations of the function-like offset macros from
13//! `netmap_user.h`.
14//! * The Netmap ioctl numbers ([`NIOCRXSYNC`], [`NIOCTXSYNC`], [`NIOCREGIF`],
15//! [`NIOCCTRL`]) which bindgen cannot evaluate from the `_IO`/`_IOWR` macros.
16//!
17//! # Build configuration
18//!
19//! The netmap headers are discovered through the `NETMAP_LOCATION` environment
20//! variable (defaulting to `/usr/local`), looking for headers in
21//! `$NETMAP_LOCATION/include`. When Netmap is not needed, enable the
22//! `disable-netmap-kernel` feature (or set `DISABLE_NETMAP_KERNEL`) to build an
23//! empty crate. If the headers cannot be found at all (for example when the
24//! crate is built by crates.io or docs.rs, which never have Netmap installed),
25//! the build degrades to the same empty state automatically instead of failing.
26
27#![allow(non_upper_case_globals)]
28#![allow(non_camel_case_types)]
29#![allow(non_snake_case)]
30#![allow(dead_code)]
31#![allow(clippy::missing_safety_doc)]
32
33#[cfg(not(netmap_disabled))]
34mod bindings {
35 include!(concat!(env!("OUT_DIR"), "/binding.rs"));
36}
37
38#[cfg(not(netmap_disabled))]
39pub use bindings::*;
40
41/// Export the helper types only when the netmap bindings are available.
42#[cfg(not(netmap_disabled))]
43pub mod exports {
44 pub use super::bindings::NR_REG_ALL_NIC;
45 pub use super::bindings::NR_REG_NIC_SW;
46 pub use super::bindings::NR_REG_ONE_NIC;
47 pub use super::bindings::NR_REG_PIPE_MASTER;
48 pub use super::bindings::NR_REG_PIPE_SLAVE;
49 pub use super::bindings::NR_REG_SW;
50}
51
52// ---------------------------------------------------------------------------
53// Hand-written core structs.
54//
55// bindgen emits nm_desc (and its nm_pkthdr/nm_stat constituents) with an
56// incorrect opaque layout because of the mutually-recursive nm_desc <->
57// nm_pkthdr type cycle in netmap_user.h. Their layout is defined below to
58// match the C headers exactly (verified with `offsetof`/`sizeof` on x86-64).
59// ---------------------------------------------------------------------------
60
61#[cfg(not(netmap_disabled))]
62/// A Netmap descriptor (`struct nm_desc` in `netmap_user.h`).
63///
64/// This is the main handle returned by [`nm_open`] and used to reach the
65/// `netmap_if` (via the [`nifp`](Self::nifp) field) and the underlying file
66/// descriptor (via the [`fd`](Self::fd) field).
67#[repr(C)]
68#[derive(Debug)]
69pub struct nm_desc {
70 /// Points to itself when the descriptor is open; used for sanity checks.
71 pub self_: *mut nm_desc,
72 /// File descriptor of the `/dev/netmap` device.
73 pub fd: i32,
74 /// Base address of the mapped Netmap shared-memory region.
75 pub mem: *mut core::ffi::c_void,
76 /// Size of the mapped region.
77 pub memsize: usize,
78 /// Non-zero if `mem` is the result of `mmap`.
79 pub done_mmap: i32,
80 /// Immutable pointer to the `netmap_if` at the start of the region.
81 pub nifp: *const netmap_if,
82 pub first_tx_ring: u16,
83 pub last_tx_ring: u16,
84 pub cur_tx_ring: u16,
85 pub first_rx_ring: u16,
86 pub last_rx_ring: u16,
87 pub cur_rx_ring: u16,
88 /// The registration request used to open the port.
89 pub req: nmreq,
90 /// Header of the last packet read via `nm_nextpkt`.
91 pub hdr: nm_pkthdr,
92 /// A pointer to one of the rings; used to translate buffer indices.
93 pub some_ring: *const netmap_ring,
94 /// Start of the buffer area within the mapped region.
95 pub buf_start: *const core::ffi::c_void,
96 /// End of the buffer area within the mapped region.
97 pub buf_end: *const core::ffi::c_void,
98 pub snaplen: i32,
99 pub promisc: i32,
100 pub to_ms: i32,
101 /// Buffer used by the `nm_*` helpers to report the last error.
102 pub errbuf: *mut core::ffi::c_char,
103 pub if_flags: u32,
104 pub if_reqcap: u32,
105 pub if_curcap: u32,
106 pub st: nm_stat,
107 /// Last error message written by the `nm_*` helpers.
108 pub msg: [core::ffi::c_char; NM_ERRBUF_SIZE as usize],
109}
110
111#[cfg(not(netmap_disabled))]
112/// Netmap packet header returned by the `nm_*` convenience helpers
113/// (`struct nm_pkthdr` in `netmap_user.h`).
114#[repr(C)]
115#[derive(Debug, Copy, Clone)]
116pub struct nm_pkthdr {
117 pub ts: timeval,
118 pub caplen: core::ffi::c_uint,
119 pub len: core::ffi::c_uint,
120 pub flags: u64,
121 pub d: *mut nm_desc,
122 pub slot: *mut netmap_slot,
123 pub buf: *mut core::ffi::c_uchar,
124}
125
126#[cfg(not(netmap_disabled))]
127/// Netmap statistics (`struct nm_stat` in `netmap_user.h`).
128#[repr(C)]
129#[derive(Debug, Copy, Clone)]
130pub struct nm_stat {
131 pub ps_recv: core::ffi::c_uint,
132 pub ps_drop: core::ffi::c_uint,
133 pub ps_ifdrop: core::ffi::c_uint,
134}
135
136// ---------------------------------------------------------------------------
137// C shim linkage
138// ---------------------------------------------------------------------------
139
140#[cfg(not(netmap_disabled))]
141extern "C" {
142 /// Open a Netmap port (see `netmap_user.h` `nm_open(4)`).
143 ///
144 /// Returns a pointer to a `nm_desc` on success, or a null pointer on
145 /// failure (with `errno` set).
146 #[link_name = "ffi_nm_open"]
147 pub fn nm_open(
148 ifname: *const core::ffi::c_char,
149 req: *const nmreq,
150 flags: u64,
151 arg: *const nm_desc,
152 ) -> *mut nm_desc;
153
154 /// Close a Netmap descriptor opened with [`nm_open`].
155 #[link_name = "ffi_nm_close"]
156 pub fn nm_close(d: *mut nm_desc) -> core::ffi::c_int;
157
158 /// Map the Netmap memory region for a descriptor (see `nm_mmap` in
159 /// `netmap_user.h`).
160 #[link_name = "ffi_nm_mmap"]
161 pub fn nm_mmap(d: *mut nm_desc, parent: *const nm_desc) -> core::ffi::c_int;
162
163 /// Inject a single packet into the TX ring of a descriptor (see `nm_inject`
164 /// in `netmap_user.h`).
165 #[link_name = "ffi_nm_inject"]
166 pub fn nm_inject(
167 d: *mut nm_desc,
168 buf: *const core::ffi::c_void,
169 size: usize,
170 ) -> core::ffi::c_int;
171}
172
173// ---------------------------------------------------------------------------
174// ioctl numbers (bindgen cannot expand the _IO/_IOWR macros)
175// ---------------------------------------------------------------------------
176
177/// Synchronize the TX rings (`_IO('i', 148)` on Linux).
178pub const NIOCTXSYNC: std::os::raw::c_ulong = 0x6994;
179/// Synchronize the RX rings (`_IO('i', 149)` on Linux).
180pub const NIOCRXSYNC: std::os::raw::c_ulong = 0x6995;
181/// Register a Netmap interface with the kernel (`_IOWR('i', 146, struct nmreq)`).
182pub const NIOCREGIF: std::os::raw::c_ulong = 0xc03c_6992;
183/// Control-device request header (`_IOWR('i', 151, struct nmreq_header)`).
184pub const NIOCCTRL: std::os::raw::c_ulong = 0xc058_6997;
185
186// ---------------------------------------------------------------------------
187// Registration-mode constants. The C header exposes the NR_REG_* values as an
188// anonymous enum, which bindgen emits under a generated type name. The aliases
189// below keep the older netmap-rs public names working and map onto the current
190// API using `u32` values.
191// ---------------------------------------------------------------------------
192
193#[cfg(not(netmap_disabled))]
194/// Attach only the host (SW) rings. Legacy name for `NR_REG_SW`.
195pub const NR_REG_SW_ONLY: u32 = NR_REG_SW as u32;
196#[cfg(not(netmap_disabled))]
197/// Attach all NIC rings. Legacy name for `NR_REG_ALL_NIC`.
198pub const NR_REG_NIC_ONLY: u32 = NR_REG_ALL_NIC as u32;
199#[cfg(not(netmap_disabled))]
200/// Attach both NIC and host rings. Legacy name for `NR_REG_NIC_SW`.
201pub const NR_REG_NIC_AND_SW: u32 = NR_REG_NIC_SW as u32;
202
203// ---------------------------------------------------------------------------
204// Function-like macros from netmap_user.h (bindgen does not emit macros).
205// ---------------------------------------------------------------------------
206
207#[cfg(not(netmap_disabled))]
208/// `NETMAP_TXRING(nifp, index)` — the TX ring at `index`, offset from the
209/// `netmap_if` pointer.
210///
211/// # Safety
212/// `nifp` must point to a valid `netmap_if`; `index` must be lower than
213/// `nifp.ni_tx_rings`.
214#[inline]
215pub unsafe fn NETMAP_TXRING(nifp: *const netmap_if, index: u32) -> *mut netmap_ring {
216 let offset = (*nifp).ring_ofs.as_ptr().add(index as usize).read();
217 (nifp as *const u8).add(offset as usize) as *mut netmap_ring
218}
219
220#[cfg(not(netmap_disabled))]
221/// `NETMAP_RXRING(nifp, index)` — the RX ring at `index`, offset from the
222/// `netmap_if` pointer. RX rings start after all TX rings (HW + host).
223///
224/// # Safety
225/// `nifp` must point to a valid `netmap_if`; `index` must be lower than
226/// `nifp.ni_rx_rings`.
227#[inline]
228pub unsafe fn NETMAP_RXRING(nifp: *const netmap_if, index: u32) -> *mut netmap_ring {
229 let base = (*nifp).ni_tx_rings + (*nifp).ni_host_tx_rings;
230 let offset = (*nifp)
231 .ring_ofs
232 .as_ptr()
233 .add((base + index) as usize)
234 .read();
235 (nifp as *const u8).add(offset as usize) as *mut netmap_ring
236}
237
238#[cfg(not(netmap_disabled))]
239/// `NETMAP_IF(base, ofs)` — the `netmap_if` at `ofs` bytes into the mapped
240/// memory region `base`.
241///
242/// # Safety
243/// `base` must point into a valid Netmap shared-memory region.
244#[inline]
245pub unsafe fn NETMAP_IF(base: *const core::ffi::c_void, ofs: isize) -> *mut netmap_if {
246 (base as *const u8).offset(ofs) as *mut netmap_if
247}
248
249#[cfg(not(netmap_disabled))]
250/// `NETMAP_BUF(ring, index)` — pointer to buffer `index` inside `ring`'s buffer
251/// pool.
252///
253/// # Safety
254/// `ring` must point to a valid `netmap_ring`; `index` must be a valid buffer
255/// index for that ring.
256#[inline]
257pub unsafe fn NETMAP_BUF(ring: *const netmap_ring, index: u32) -> *mut core::ffi::c_void {
258 (ring as *const u8)
259 .offset((*ring).buf_ofs as isize)
260 .add(index as usize * (*ring).nr_buf_size as usize) as *mut core::ffi::c_void
261}
262
263#[cfg(netmap_disabled)]
264mod disabled {
265 /// Dummy stamp emitted only when the crate is built without Netmap (via
266 /// `disable-netmap-kernel`, `DISABLE_NETMAP_KERNEL`, or missing headers) so
267 /// the crate is not completely empty; use nothing from this module.
268 pub const NETMAP_DISABLED: bool = true;
269}
270
271#[cfg(all(test, not(netmap_disabled)))]
272mod tests {
273 use super::*;
274 use core::mem;
275
276 #[test]
277 fn struct_sizes() {
278 // Values verified against `gcc -I <netmap include>` on x86-64 Linux.
279 assert_eq!(mem::size_of::<netmap_slot>(), 16);
280 assert_eq!(mem::align_of::<netmap_slot>(), 8);
281 // netmap_ring has a flexible array member; its fixed part is 256 bytes.
282 assert_eq!(mem::size_of::<netmap_ring>(), 256);
283 assert_eq!(mem::size_of::<nmreq>(), 60);
284 // A netmap_if has a fixed header of 56 bytes before the flexible
285 // ring offset array.
286 assert_eq!(mem::size_of::<netmap_if>(), 56);
287 assert_eq!(mem::size_of::<nm_desc>(), 760);
288 assert_eq!(mem::size_of::<nm_pkthdr>(), 56);
289 assert_eq!(mem::size_of::<nm_stat>(), 12);
290 }
291
292 #[test]
293 fn ioctl_constants() {
294 assert_eq!(NIOCTXSYNC, 0x6994);
295 assert_eq!(NIOCRXSYNC, 0x6995);
296 assert_eq!(NETMAP_API, 14);
297 assert_eq!(NR_REG_ALL_NIC as u32, 1);
298 assert_eq!(NR_REG_SW as u32, 2);
299 }
300
301 #[test]
302 fn reg_mode_constants_make_sense() {
303 let all_nic = NR_REG_ALL_NIC as u32;
304 let sw = NR_REG_SW as u32;
305 let nic_sw = NR_REG_NIC_SW as u32;
306 assert!(all_nic < sw);
307 assert!(sw < nic_sw);
308 assert_eq!(NR_REG_SW_ONLY, NR_REG_SW as u32);
309 assert_eq!(NR_REG_NIC_ONLY, NR_REG_ALL_NIC as u32);
310 }
311
312 #[test]
313 fn ring_macro_shims_are_unsafe_fn() {
314 // Compile-time check that the macros are exposed as unsafe functions.
315 fn _assert_fn_ptr(_f: unsafe fn(*const netmap_if, u32) -> *mut netmap_ring) {}
316 _assert_fn_ptr(NETMAP_TXRING);
317 _assert_fn_ptr(NETMAP_RXRING);
318 }
319}