1use {
2 crate::{
3 netlink::MacAddress,
4 route::Router,
5 umem::{Frame, FrameOffset},
6 },
7 libc::{
8 AF_INET, IF_NAMESIZE, SIOCETHTOOL, SIOCGIFADDR, SIOCGIFHWADDR, SOCK_DGRAM, SYS_ioctl,
9 ifreq, mmap, munmap, socket, syscall, xdp_ring_offset,
10 },
11 std::{
12 ffi::{CStr, CString, c_char},
13 fs,
14 io::{self, ErrorKind},
15 marker::PhantomData,
16 mem,
17 net::Ipv4Addr,
18 os::fd::{AsRawFd as _, FromRawFd as _, OwnedFd, RawFd},
19 ptr, slice,
20 sync::atomic::{AtomicU32, Ordering},
21 },
22};
23
24#[derive(Copy, Clone, Debug)]
25pub struct QueueId(pub u64);
26
27pub struct NetworkDevice {
28 if_index: u32,
29 if_name: String,
30}
31
32impl NetworkDevice {
33 pub fn new(name: impl Into<String>) -> Result<Self, io::Error> {
34 let if_name = name.into();
35 let if_name_c = CString::new(if_name.as_bytes())
36 .map_err(|_| io::Error::new(ErrorKind::InvalidInput, "Invalid interface name"))?;
37
38 let if_index = unsafe { libc::if_nametoindex(if_name_c.as_ptr()) };
39
40 if if_index == 0 {
41 return Err(io::Error::last_os_error());
42 }
43
44 Ok(Self { if_index, if_name })
45 }
46
47 pub fn new_from_index(if_index: u32) -> Result<Self, io::Error> {
48 let mut buf = [0u8; 1024];
49 let ret = unsafe { libc::if_indextoname(if_index, buf.as_mut_ptr() as *mut c_char) };
50 if ret.is_null() {
51 return Err(io::Error::last_os_error());
52 }
53
54 let cstr = unsafe { CStr::from_ptr(ret) };
55 let if_name = String::from_utf8_lossy(cstr.to_bytes()).to_string();
56
57 Ok(Self { if_index, if_name })
58 }
59
60 pub fn new_from_default_route() -> Result<Self, io::Error> {
61 let router = Router::new()?;
62 let default_route = router.default().unwrap();
63 NetworkDevice::new_from_index(default_route.if_index)
64 }
65
66 pub fn name(&self) -> &str {
67 &self.if_name
68 }
69
70 pub fn if_index(&self) -> u32 {
71 self.if_index
72 }
73
74 pub fn mac_addr(&self) -> Result<MacAddress, io::Error> {
75 let fd = unsafe { libc::socket(libc::AF_INET, libc::SOCK_DGRAM, 0) };
76 if fd < 0 {
77 return Err(io::Error::last_os_error());
78 }
79 let fd = unsafe { OwnedFd::from_raw_fd(fd) };
80
81 let mut req: ifreq = unsafe { mem::zeroed() };
82 let if_name = CString::new(self.if_name.as_bytes()).unwrap();
83
84 let if_name_bytes = if_name.as_bytes_with_nul();
85 let len = std::cmp::min(if_name_bytes.len(), IF_NAMESIZE);
86 unsafe {
87 std::ptr::copy_nonoverlapping(
88 if_name_bytes.as_ptr() as *const c_char,
89 req.ifr_name.as_mut_ptr(),
90 len,
91 );
92 }
93
94 let result = unsafe { syscall(SYS_ioctl, fd.as_raw_fd(), SIOCGIFHWADDR, &mut req) };
95 if result < 0 {
96 return Err(io::Error::last_os_error());
97 }
98
99 Ok(MacAddress(
100 unsafe {
101 slice::from_raw_parts(req.ifr_ifru.ifru_hwaddr.sa_data.as_ptr() as *const u8, 6)
102 }
103 .try_into()
104 .unwrap(),
105 ))
106 }
107
108 pub fn ipv4_addr(&self) -> Result<Ipv4Addr, io::Error> {
109 let fd = unsafe { libc::socket(libc::AF_INET, libc::SOCK_DGRAM, 0) };
110 if fd < 0 {
111 return Err(io::Error::last_os_error());
112 }
113 let fd = unsafe { OwnedFd::from_raw_fd(fd) };
114
115 let mut req: ifreq = unsafe { mem::zeroed() };
116 let if_name = CString::new(self.if_name.as_bytes()).unwrap();
117
118 let if_name_bytes = if_name.as_bytes_with_nul();
119 let len = std::cmp::min(if_name_bytes.len(), IF_NAMESIZE);
120 unsafe {
121 std::ptr::copy_nonoverlapping(
122 if_name_bytes.as_ptr() as *const c_char,
123 req.ifr_name.as_mut_ptr(),
124 len,
125 );
126 }
127
128 let result = unsafe { syscall(SYS_ioctl, fd.as_raw_fd(), SIOCGIFADDR, &mut req) };
129 if result < 0 {
130 return Err(io::Error::last_os_error());
131 }
132
133 let addr = unsafe {
134 let addr_ptr = &req.ifr_ifru.ifru_addr as *const libc::sockaddr;
135 let sin_addr = (*(addr_ptr as *const libc::sockaddr_in)).sin_addr;
136 Ipv4Addr::from(sin_addr.s_addr.to_ne_bytes())
137 };
138 Ok(addr)
139 }
140
141 pub fn driver(&self) -> io::Result<String> {
142 let path = format!("/sys/class/net/{}/device/driver", self.if_name);
143
144 let path = fs::read_link(path).map_err(|e| {
145 io::Error::new(
146 e.kind(),
147 format!(
148 "Failed to read driver link for interface {}: {}",
149 self.if_name, e
150 ),
151 )
152 })?;
153
154 Ok(path.file_name().unwrap().to_str().unwrap().into())
155 }
156
157 pub fn open_queue(&self, queue_id: QueueId) -> Result<DeviceQueue, io::Error> {
158 let ring_sizes = Self::ring_sizes(&self.if_name).ok();
159 Ok(DeviceQueue::new(self.if_index, queue_id, ring_sizes))
160 }
161
162 pub fn ring_sizes(if_name: &str) -> Result<RingSizes, io::Error> {
163 const ETHTOOL_GRINGPARAM: u32 = 0x00000010;
164
165 #[repr(C)]
166 struct EthtoolRingParam {
167 cmd: u32,
168 rx_max_pending: u32,
169 rx_mini_max_pending: u32,
170 rx_jumbo_max_pending: u32,
171 tx_max_pending: u32,
172 rx_pending: u32,
173 rx_mini_pending: u32,
174 rx_jumbo_pending: u32,
175 tx_pending: u32,
176 }
177
178 let fd = unsafe { socket(AF_INET, SOCK_DGRAM, 0) };
179 if fd < 0 {
180 return Err(io::Error::last_os_error());
181 }
182 let fd = unsafe { OwnedFd::from_raw_fd(fd) };
183
184 let mut rp: EthtoolRingParam = unsafe { mem::zeroed() };
185 rp.cmd = ETHTOOL_GRINGPARAM;
186
187 let mut ifr: ifreq = unsafe { mem::zeroed() };
188 unsafe {
189 ptr::copy_nonoverlapping(
190 if_name.as_ptr() as *const c_char,
191 ifr.ifr_name.as_mut_ptr(),
192 if_name.len().min(IF_NAMESIZE),
193 );
194 }
195 ifr.ifr_name[IF_NAMESIZE - 1] = 0;
196 ifr.ifr_ifru.ifru_data = &mut rp as *mut _ as *mut c_char;
197
198 let res = unsafe { syscall(SYS_ioctl, fd.as_raw_fd(), SIOCETHTOOL, &ifr) };
199 if res < 0 {
200 return Err(io::Error::last_os_error());
201 }
202
203 Ok(RingSizes {
204 rx: rp.rx_pending as usize,
205 tx: rp.tx_pending as usize,
206 })
207 }
208}
209
210#[derive(Debug, Clone, Copy, PartialEq, Eq)]
211pub struct RingSizes {
212 pub rx: usize,
213 pub tx: usize,
214}
215
216impl Default for RingSizes {
217 fn default() -> Self {
218 Self { rx: 1024, tx: 1024 }
221 }
222}
223
224pub struct DeviceQueue {
225 if_index: u32,
226 queue_id: QueueId,
227 ring_sizes: Option<RingSizes>,
228 completion: Option<TxCompletionRing>,
229}
230
231impl DeviceQueue {
232 pub fn new(if_index: u32, queue_id: QueueId, ring_sizes: Option<RingSizes>) -> Self {
233 Self {
234 if_index,
235 queue_id,
236 ring_sizes,
237 completion: None,
238 }
239 }
240
241 pub fn if_index(&self) -> u32 {
242 self.if_index
243 }
244
245 pub fn id(&self) -> QueueId {
246 self.queue_id
247 }
248
249 pub fn tx_completion(&mut self) -> Option<&TxCompletionRing> {
250 self.completion.as_ref()
251 }
252
253 pub fn ring_sizes(&self) -> Option<RingSizes> {
254 self.ring_sizes
255 }
256}
257
258pub(crate) struct RingConsumer {
259 producer: *mut AtomicU32,
260 cached_producer: u32,
261 consumer: *mut AtomicU32,
262 cached_consumer: u32,
263}
264
265unsafe impl Send for RingConsumer {}
267
268impl RingConsumer {
269 pub fn new(producer: *mut AtomicU32, consumer: *mut AtomicU32) -> Self {
270 Self {
271 producer,
272 cached_producer: unsafe { (*producer).load(Ordering::Acquire) },
273 consumer,
274 cached_consumer: unsafe { (*consumer).load(Ordering::Relaxed) },
275 }
276 }
277
278 pub fn available(&self) -> u32 {
279 self.cached_producer.wrapping_sub(self.cached_consumer)
280 }
281
282 pub fn consume(&mut self) -> Option<u32> {
283 if self.cached_consumer == self.cached_producer {
284 return None;
285 }
286
287 let index = self.cached_consumer;
288 self.cached_consumer = self.cached_consumer.wrapping_add(1);
289 Some(index)
290 }
291
292 pub fn commit(&mut self) {
293 unsafe { (*self.consumer).store(self.cached_consumer, Ordering::Release) };
294 }
295
296 pub fn sync(&mut self, commit: bool) {
297 if commit {
298 self.commit();
299 }
300 self.cached_producer = unsafe { (*self.producer).load(Ordering::Acquire) };
301 }
302}
303
304pub(crate) struct RingProducer {
305 producer: *mut AtomicU32,
306 cached_producer: u32,
307 consumer: *mut AtomicU32,
308 cached_consumer: u32,
309 size: u32,
310}
311
312unsafe impl Send for RingProducer {}
314
315impl RingProducer {
316 pub fn new(producer: *mut AtomicU32, consumer: *mut AtomicU32, size: u32) -> Self {
317 Self {
318 producer,
319 cached_producer: unsafe { (*producer).load(Ordering::Relaxed) },
320 consumer,
321 cached_consumer: unsafe { (*consumer).load(Ordering::Acquire) },
322 size,
323 }
324 }
325
326 pub fn available(&self) -> u32 {
327 self.size
328 .saturating_sub(self.cached_producer.wrapping_sub(self.cached_consumer))
329 }
330
331 pub fn produce(&mut self) -> Option<u32> {
332 if self.available() == 0 {
333 return None;
334 }
335
336 let index = self.cached_producer;
337 self.cached_producer = self.cached_producer.wrapping_add(1);
338 Some(index)
339 }
340
341 pub fn commit(&mut self) {
342 unsafe { (*self.producer).store(self.cached_producer, Ordering::Release) };
343 }
344
345 pub fn sync(&mut self, commit: bool) {
346 if commit {
347 self.commit();
348 }
349 self.cached_consumer = unsafe { (*self.consumer).load(Ordering::Acquire) };
350 }
351}
352
353#[repr(C)]
354#[derive(Debug, Clone)]
355pub(crate) struct XdpDesc {
356 pub(crate) addr: u64,
357 pub(crate) len: u32,
358 pub(crate) options: u32,
359}
360
361pub struct TxCompletionRing {
362 mmap: RingMmap<u64>,
363 consumer: RingConsumer,
364 size: u32,
365}
366
367impl TxCompletionRing {
368 pub(crate) fn new(mmap: RingMmap<u64>, size: u32) -> Self {
369 debug_assert!(size.is_power_of_two());
370 Self {
371 consumer: RingConsumer::new(mmap.producer, mmap.consumer),
372 mmap,
373 size,
374 }
375 }
376
377 pub fn read(&mut self) -> Option<FrameOffset> {
378 let index = self.consumer.consume()? & self.size.saturating_sub(1);
379 let index = unsafe { *self.mmap.desc.add(index as usize) } as usize;
380 Some(FrameOffset(index))
381 }
382
383 pub fn commit(&mut self) {
384 self.consumer.commit();
385 }
386
387 pub fn sync(&mut self, commit: bool) {
388 self.consumer.sync(commit);
389 }
390}
391
392pub struct RxFillRing<F: Frame> {
393 mmap: RingMmap<u64>,
394 producer: RingProducer,
395 size: u32,
396 _fd: RawFd,
397 _frame: PhantomData<F>,
398}
399
400impl<F: Frame> RxFillRing<F> {
401 pub(crate) fn new(mmap: RingMmap<u64>, size: u32, fd: RawFd) -> Self {
402 debug_assert!(size.is_power_of_two());
403 Self {
404 producer: RingProducer::new(mmap.producer, mmap.consumer, size),
405 mmap,
406 size,
407 _fd: fd,
408 _frame: PhantomData,
409 }
410 }
411
412 pub fn write(&mut self, frame: F) -> Result<(), io::Error> {
413 let Some(index) = self.producer.produce() else {
414 return Err(ErrorKind::StorageFull.into());
415 };
416 let index = index & self.size.saturating_sub(1);
417 let desc = unsafe { self.mmap.desc.add(index as usize) };
418 unsafe {
420 desc.write(frame.offset().0 as u64);
421 }
422
423 Ok(())
424 }
425
426 pub fn commit(&mut self) {
427 self.producer.commit();
428 }
429
430 pub fn sync(&mut self, commit: bool) {
431 self.producer.sync(commit);
432 }
433}
434
435pub struct RingMmap<T> {
436 pub mmap: *const u8,
437 pub mmap_len: usize,
438 pub producer: *mut AtomicU32,
439 pub consumer: *mut AtomicU32,
440 pub desc: *mut T,
441 pub flags: *mut AtomicU32,
442}
443
444unsafe impl<T> Send for RingMmap<T> {}
446
447impl<T> Drop for RingMmap<T> {
448 fn drop(&mut self) {
449 unsafe {
450 munmap(self.mmap as *mut _, self.mmap_len);
451 }
452 }
453}
454
455pub(crate) unsafe fn mmap_ring<T>(
456 fd: i32,
457 size: usize,
458 offsets: &xdp_ring_offset,
459 ring_type: u64,
460) -> Result<RingMmap<T>, io::Error> {
461 let map_size = (offsets.desc as usize).saturating_add(size);
462 let map_addr = unsafe {
464 mmap(
465 ptr::null_mut(),
466 map_size,
467 libc::PROT_READ | libc::PROT_WRITE,
468 libc::MAP_SHARED | libc::MAP_POPULATE,
469 fd,
470 ring_type as i64,
471 )
472 };
473 if ptr::eq(map_addr, libc::MAP_FAILED) {
474 return Err(io::Error::last_os_error());
475 }
476 unsafe {
479 let producer = map_addr.add(offsets.producer as usize) as *mut AtomicU32;
480 let consumer = map_addr.add(offsets.consumer as usize) as *mut AtomicU32;
481 let desc = map_addr.add(offsets.desc as usize) as *mut T;
482 let flags = map_addr.add(offsets.flags as usize) as *mut AtomicU32;
483 Ok(RingMmap {
486 mmap: map_addr as *const u8,
487 mmap_len: map_size,
488 producer,
489 consumer,
490 desc,
491 flags,
492 })
493 }
494}
495
496#[cfg(test)]
497mod test {
498 use super::*;
499
500 #[test]
501 fn test_ring_producer() {
502 let mut producer = AtomicU32::new(0);
503 let mut consumer = AtomicU32::new(0);
504 let size = 16;
505 let mut ring = RingProducer::new(&mut producer as *mut _, &mut consumer as *mut _, size);
506 assert_eq!(ring.available(), size);
507
508 for i in 0..size {
509 assert_eq!(ring.produce(), Some(i));
510 assert_eq!(ring.available(), size - i - 1);
511 }
512 assert_eq!(ring.produce(), None);
513
514 consumer.store(1, Ordering::Release);
515 assert_eq!(ring.produce(), None);
516 ring.commit();
517 assert_eq!(ring.produce(), None);
518 ring.sync(true);
519 assert_eq!(ring.produce(), Some(16));
520 assert_eq!(ring.produce(), None);
521
522 consumer.store(2, Ordering::Release);
523 ring.sync(true);
524 assert_eq!(ring.produce(), Some(17));
525 }
526
527 #[test]
528 fn test_ring_producer_wrap_around() {
529 let size = 16;
530 let mut producer = AtomicU32::new(u32::MAX - 1);
531 let mut consumer = AtomicU32::new(u32::MAX - size - 1);
532 let mut ring = RingProducer::new(&mut producer as *mut _, &mut consumer as *mut _, size);
533 assert_eq!(ring.available(), 0);
534
535 consumer.fetch_add(1, Ordering::Release);
536 ring.sync(true);
537 assert_eq!(ring.produce(), Some(u32::MAX - 1));
538 consumer.fetch_add(1, Ordering::Release);
539 ring.sync(true);
540 assert_eq!(ring.produce(), Some(u32::MAX));
541 consumer.fetch_add(1, Ordering::Release);
542 ring.sync(true);
543 assert_eq!(ring.produce(), Some(0));
544 consumer.fetch_add(1, Ordering::Release);
545 ring.sync(true);
546 assert_eq!(ring.produce(), Some(1));
547 }
548
549 #[test]
550 fn test_ring_consumer() {
551 let mut producer = AtomicU32::new(0);
552 let mut consumer = AtomicU32::new(0);
553 let size = 16;
554 let mut ring = RingConsumer::new(&mut producer as *mut _, &mut consumer as *mut _);
555 assert_eq!(ring.available(), 0);
556
557 producer.store(1, Ordering::Release);
558 assert_eq!(ring.available(), 0);
559 ring.sync(true);
560 assert_eq!(ring.available(), 1);
561
562 producer.store(size, Ordering::Release);
563 ring.sync(true);
564
565 for i in 0..size {
566 assert_eq!(ring.consume(), Some(i));
567 assert_eq!(ring.available(), size - i - 1);
568 }
569 assert_eq!(ring.consume(), None);
570 }
571
572 #[test]
573 fn test_ring_consumer_wrap_around() {
574 let mut producer = AtomicU32::new(u32::MAX - 1);
575 let mut consumer = AtomicU32::new(u32::MAX - 1);
576 let mut ring = RingConsumer::new(&mut producer as *mut _, &mut consumer as *mut _);
577 assert_eq!(ring.available(), 0);
578 assert_eq!(ring.consume(), None);
579
580 producer.fetch_add(1, Ordering::Release);
581 ring.sync(true);
582 assert_eq!(ring.consume(), Some(u32::MAX - 1));
583
584 producer.store(0, Ordering::Release);
585 ring.sync(true);
586 assert_eq!(ring.available(), 1);
587 assert_eq!(ring.consume(), Some(u32::MAX));
588
589 producer.fetch_add(1, Ordering::Release);
590 ring.sync(true);
591 assert_eq!(ring.consume(), Some(0));
592
593 producer.fetch_add(1, Ordering::Release);
594 ring.sync(true);
595 assert_eq!(ring.consume(), Some(1));
596 }
597}