Skip to content

Commit 1611ece

Browse files
committed
Add AF_XDP syscalls, constants and types
1 parent dbd3521 commit 1611ece

File tree

18 files changed

+1126
-10
lines changed

18 files changed

+1126
-10
lines changed

Cargo.toml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ once_cell = { version = "1.5.2", optional = true }
3636
# libc backend can be selected via adding `--cfg=rustix_use_libc` to
3737
# `RUSTFLAGS` or enabling the `use-libc` cargo feature.
3838
[target.'cfg(all(not(rustix_use_libc), not(miri), target_os = "linux", target_endian = "little", any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "riscv64", all(rustix_use_experimental_asm, target_arch = "powerpc64"), all(rustix_use_experimental_asm, target_arch = "mips"), all(rustix_use_experimental_asm, target_arch = "mips32r6"), all(rustix_use_experimental_asm, target_arch = "mips64"), all(rustix_use_experimental_asm, target_arch = "mips64r6"), target_arch = "x86", all(target_arch = "x86_64", target_pointer_width = "64"))))'.dependencies]
39-
linux-raw-sys = { version = "0.4.11", default-features = false, features = ["general", "errno", "ioctl", "no_std", "elf"] }
39+
#linux-raw-sys = { version = "0.4.11", default-features = false, features = ["general", "errno", "ioctl", "no_std", "elf"] }
40+
linux-raw-sys = { git = "https://github.com/arctic-alpaca/linux-raw-sys", branch = "v0.4.11", version = "0.4.11", default-features = false, features = ["general", "errno", "ioctl", "no_std", "elf"] }
4041
libc_errno = { package = "errno", version = "0.3.8", default-features = false, optional = true }
4142
libc = { version = "0.2.150", default-features = false, features = ["extra_traits"], optional = true }
4243

@@ -53,7 +54,8 @@ libc = { version = "0.2.150", default-features = false, features = ["extra_trait
5354
# Some syscalls do not have libc wrappers, such as in `io_uring`. For these,
5455
# the libc backend uses the linux-raw-sys ABI and `libc::syscall`.
5556
[target.'cfg(all(any(target_os = "android", target_os = "linux"), any(rustix_use_libc, miri, not(all(target_os = "linux", target_endian = "little", any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "riscv64", all(rustix_use_experimental_asm, target_arch = "powerpc64"), all(rustix_use_experimental_asm, target_arch = "mips"), all(rustix_use_experimental_asm, target_arch = "mips32r6"), all(rustix_use_experimental_asm, target_arch = "mips64"), all(rustix_use_experimental_asm, target_arch = "mips64r6"), target_arch = "x86", all(target_arch = "x86_64", target_pointer_width = "64")))))))'.dependencies]
56-
linux-raw-sys = { version = "0.4.11", default-features = false, features = ["general", "ioctl", "no_std"] }
57+
#linux-raw-sys = { version = "0.4.11", default-features = false, features = ["general", "ioctl", "no_std"] }
58+
linux-raw-sys = { git = "https://github.com/arctic-alpaca/linux-raw-sys", branch = "v0.4.11", version = "0.4.11", default-features = false, features = ["general", "ioctl", "no_std"] }
5759

5860
# For the libc backend on Windows, use the Winsock2 API in windows-sys.
5961
[target.'cfg(windows)'.dependencies.windows-sys]
@@ -141,7 +143,7 @@ io_uring = ["event", "fs", "net", "linux-raw-sys/io_uring"]
141143
mount = []
142144

143145
# Enable `rustix::net::*`.
144-
net = ["linux-raw-sys/net", "linux-raw-sys/netlink", "linux-raw-sys/if_ether"]
146+
net = ["linux-raw-sys/net", "linux-raw-sys/netlink", "linux-raw-sys/if_ether", "linux-raw-sys/xdp"]
145147

146148
# Enable `rustix::thread::*`.
147149
thread = ["linux-raw-sys/prctl"]

src/backend/libc/net/msghdr.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,13 @@
55
66
use crate::backend::c;
77
use crate::backend::conv::{msg_control_len, msg_iov_len};
8+
#[cfg(target_os = "linux")]
9+
use crate::backend::net::write_sockaddr::encode_sockaddr_xdp;
810
use crate::backend::net::write_sockaddr::{encode_sockaddr_v4, encode_sockaddr_v6};
911

1012
use crate::io::{self, IoSlice, IoSliceMut};
13+
#[cfg(target_os = "linux")]
14+
use crate::net::xdp::SocketAddrXdp;
1115
use crate::net::{RecvAncillaryBuffer, SendAncillaryBuffer, SocketAddrV4, SocketAddrV6};
1216
use crate::utils::as_ptr;
1317

@@ -124,6 +128,27 @@ pub(crate) fn with_unix_msghdr<R>(
124128
})
125129
}
126130

131+
/// Create a message header intended to send with an IPv6 address.
132+
pub(crate) fn with_xdp_msghdr<R>(
133+
addr: &SocketAddrXdp,
134+
iov: &[IoSlice<'_>],
135+
control: &mut SendAncillaryBuffer<'_, '_, '_>,
136+
f: impl FnOnce(c::msghdr) -> R,
137+
) -> R {
138+
let encoded = encode_sockaddr_xdp(addr);
139+
140+
f({
141+
let mut h = zero_msghdr();
142+
h.msg_name = as_ptr(&encoded) as _;
143+
h.msg_namelen = size_of::<SocketAddrXdp>() as _;
144+
h.msg_iov = iov.as_ptr() as _;
145+
h.msg_iovlen = msg_iov_len(iov.len());
146+
h.msg_control = control.as_control_ptr().cast();
147+
h.msg_controllen = msg_control_len(control.control_len());
148+
h
149+
})
150+
}
151+
127152
/// Create a zero-initialized message header struct value.
128153
#[cfg(all(unix, not(target_os = "redox")))]
129154
pub(crate) fn zero_msghdr() -> c::msghdr {

src/backend/libc/net/read_sockaddr.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ use crate::backend::c;
88
#[cfg(not(windows))]
99
use crate::ffi::CStr;
1010
use crate::io;
11+
#[cfg(target_os = "linux")]
12+
use crate::net::xdp::{SockaddrXdpFlags, SocketAddrXdp};
1113
use crate::net::{Ipv4Addr, Ipv6Addr, SocketAddrAny, SocketAddrV4, SocketAddrV6};
1214
use core::mem::size_of;
1315

@@ -193,6 +195,19 @@ pub(crate) unsafe fn read_sockaddr(
193195
.map(SocketAddrAny::Unix)
194196
}
195197
}
198+
#[cfg(target_os = "linux")]
199+
c::AF_XDP => {
200+
if len < size_of::<c::sockaddr_xdp>() {
201+
return Err(io::Errno::INVAL);
202+
}
203+
let decode = &*storage.cast::<c::sockaddr_xdp>();
204+
SocketAddrAny::Xdp(SocketAddrXdp::new(
205+
SockaddrXdpFlags::from_bits_retain(decode.sxdp_flags),
206+
u32::from_be(decode.sxdp_ifindex),
207+
u32::from_be(decode.sxdp_queue_id),
208+
u32::from_be(decode.sxdp_shared_umem_fd),
209+
))
210+
}
196211
_ => Err(io::Errno::INVAL),
197212
}
198213
}
@@ -301,6 +316,17 @@ unsafe fn inner_read_sockaddr_os(
301316
)
302317
}
303318
}
319+
#[cfg(target_os = "linux")]
320+
c::AF_XDP => {
321+
assert!(len >= size_of::<c::sockaddr_xdp>());
322+
let decode = &*storage.cast::<c::sockaddr_xdp>();
323+
SocketAddrAny::Xdp(SocketAddrXdp::new(
324+
SockaddrXdpFlags::from_bits_retain(decode.sxdp_flags),
325+
u32::from_be(decode.sxdp_ifindex),
326+
u32::from_be(decode.sxdp_queue_id),
327+
u32::from_be(decode.sxdp_shared_umem_fd),
328+
))
329+
}
304330
other => unimplemented!("{:?}", other),
305331
}
306332
}

src/backend/libc/net/sockopt.rs

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ use crate::fd::BorrowedFd;
1414
use crate::ffi::CStr;
1515
use crate::io;
1616
use crate::net::sockopt::Timeout;
17+
#[cfg(target_os = "linux")]
18+
use crate::net::xdp::{
19+
XdpMmapOffsets, XdpOptionsFlags, XdpRingFlags, XdpRingOffset, XdpStatistics, XdpUmemReg,
20+
};
1721
#[cfg(not(any(
1822
apple,
1923
windows,
@@ -67,12 +71,15 @@ use alloc::borrow::ToOwned;
6771
target_os = "illumos"
6872
))]
6973
use alloc::string::String;
74+
use bitflags::Flags;
7075
#[cfg(apple)]
7176
use c::TCP_KEEPALIVE as TCP_KEEPIDLE;
7277
#[cfg(not(any(apple, target_os = "openbsd", target_os = "haiku", target_os = "nto")))]
7378
use c::TCP_KEEPIDLE;
7479
use core::mem::{size_of, MaybeUninit};
7580
use core::time::Duration;
81+
#[cfg(target_os = "linux")]
82+
use linux_raw_sys::xdp::{xdp_mmap_offsets, xdp_statistics, xdp_statistics_v1};
7683
#[cfg(windows)]
7784
use windows_sys::Win32::Foundation::BOOL;
7885

@@ -963,6 +970,170 @@ pub(crate) fn get_socket_peercred(fd: BorrowedFd<'_>) -> io::Result<UCred> {
963970
getsockopt(fd, c::SOL_SOCKET, c::SO_PEERCRED)
964971
}
965972

973+
#[cfg(target_os = "linux")]
974+
#[inline]
975+
pub(crate) fn set_xdp_umem_reg(fd: BorrowedFd<'_>, value: XdpUmemReg) -> io::Result<()> {
976+
setsockopt(fd, c::SOL_XDP, c::XDP_UMEM_REG, value)
977+
}
978+
979+
#[cfg(target_os = "linux")]
980+
#[inline]
981+
pub(crate) fn set_xdp_umem_fill_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> {
982+
setsockopt(fd, c::SOL_XDP, c::XDP_UMEM_FILL_RING, value)
983+
}
984+
985+
#[cfg(target_os = "linux")]
986+
#[inline]
987+
pub(crate) fn set_xdp_umem_completion_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> {
988+
setsockopt(fd, c::SOL_XDP, c::XDP_UMEM_COMPLETION_RING, value)
989+
}
990+
991+
#[cfg(target_os = "linux")]
992+
#[inline]
993+
pub(crate) fn set_xdp_tx_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> {
994+
setsockopt(fd, c::SOL_XDP, c::XDP_TX_RING, value)
995+
}
996+
997+
#[cfg(target_os = "linux")]
998+
#[inline]
999+
pub(crate) fn set_xdp_rx_ring_size(fd: BorrowedFd<'_>, value: u32) -> io::Result<()> {
1000+
setsockopt(fd, c::SOL_XDP, c::XDP_RX_RING, value)
1001+
}
1002+
1003+
#[cfg(target_os = "linux")]
1004+
#[inline]
1005+
pub(crate) fn get_xdp_mmap_offsets(fd: BorrowedFd<'_>) -> io::Result<XdpMmapOffsets> {
1006+
// The kernel will write `xdp_mmap_offsets` or `xdp_mmap_offsets_v1` to the supplied pointer,
1007+
// depending on the kernel version. Both structs only contain u64 values.
1008+
// By using the larger of both as the parameter, we can shuffle the values to the non-v1 version
1009+
// returned by `get_xdp_mmap_offsets` while keeping the return type unaffected by the kernel
1010+
// version. This works because C will layout all struct members one after the other.
1011+
1012+
let mut optlen = core::mem::size_of::<xdp_mmap_offsets>().try_into().unwrap();
1013+
debug_assert!(
1014+
optlen as usize >= core::mem::size_of::<c::c_int>(),
1015+
"Socket APIs don't ever use `bool` directly"
1016+
);
1017+
let mut value = MaybeUninit::<xdp_mmap_offsets>::zeroed();
1018+
getsockopt_raw(fd, c::SOL_XDP, c::XDP_MMAP_OFFSETS, &mut value, &mut optlen)?;
1019+
1020+
if optlen as usize == core::mem::size_of::<c::xdp_mmap_offsets_v1>() {
1021+
// Safety: All members of xdp_mmap_offsets are u64 and thus are correctly initialized
1022+
// by `MaybeUninit::<xdp_statistics>::zeroed()`
1023+
let xpd_mmap_offsets = unsafe { value.assume_init() };
1024+
Ok(XdpMmapOffsets {
1025+
rx: XdpRingOffset {
1026+
producer: xpd_mmap_offsets.rx.producer,
1027+
consumer: xpd_mmap_offsets.rx.consumer,
1028+
desc: xpd_mmap_offsets.rx.desc,
1029+
flags: None,
1030+
},
1031+
tx: XdpRingOffset {
1032+
producer: xpd_mmap_offsets.rx.flags,
1033+
consumer: xpd_mmap_offsets.tx.producer,
1034+
desc: xpd_mmap_offsets.tx.consumer,
1035+
flags: None,
1036+
},
1037+
fr: XdpRingOffset {
1038+
producer: xpd_mmap_offsets.tx.desc,
1039+
consumer: xpd_mmap_offsets.tx.flags,
1040+
desc: xpd_mmap_offsets.fr.producer,
1041+
flags: None,
1042+
},
1043+
cr: XdpRingOffset {
1044+
producer: xpd_mmap_offsets.fr.consumer,
1045+
consumer: xpd_mmap_offsets.fr.desc,
1046+
desc: xpd_mmap_offsets.fr.flags,
1047+
flags: None,
1048+
},
1049+
})
1050+
} else {
1051+
assert_eq!(
1052+
optlen as usize,
1053+
core::mem::size_of::<xdp_mmap_offsets>(),
1054+
"unexpected getsockopt size"
1055+
);
1056+
// Safety: All members of xdp_mmap_offsets are u64 and thus are correctly initialized
1057+
// by `MaybeUninit::<xdp_statistics>::zeroed()`
1058+
let xpd_mmap_offsets = unsafe { value.assume_init() };
1059+
Ok(XdpMmapOffsets {
1060+
rx: XdpRingOffset {
1061+
producer: xpd_mmap_offsets.rx.producer,
1062+
consumer: xpd_mmap_offsets.rx.consumer,
1063+
desc: xpd_mmap_offsets.rx.desc,
1064+
flags: Some(XdpRingFlags::from_bits_retain(xpd_mmap_offsets.rx.flags)),
1065+
},
1066+
tx: XdpRingOffset {
1067+
producer: xpd_mmap_offsets.tx.producer,
1068+
consumer: xpd_mmap_offsets.tx.consumer,
1069+
desc: xpd_mmap_offsets.tx.desc,
1070+
flags: Some(XdpRingFlags::from_bits_retain(xpd_mmap_offsets.tx.flags)),
1071+
},
1072+
fr: XdpRingOffset {
1073+
producer: xpd_mmap_offsets.fr.producer,
1074+
consumer: xpd_mmap_offsets.fr.consumer,
1075+
desc: xpd_mmap_offsets.fr.desc,
1076+
flags: Some(XdpRingFlags::from_bits_retain(xpd_mmap_offsets.fr.flags)),
1077+
},
1078+
cr: XdpRingOffset {
1079+
producer: xpd_mmap_offsets.cr.producer,
1080+
consumer: xpd_mmap_offsets.cr.consumer,
1081+
desc: xpd_mmap_offsets.cr.desc,
1082+
flags: Some(XdpRingFlags::from_bits_retain(xpd_mmap_offsets.cr.flags)),
1083+
},
1084+
})
1085+
}
1086+
}
1087+
1088+
#[cfg(target_os = "linux")]
1089+
#[inline]
1090+
pub(crate) fn get_xdp_statistics(fd: BorrowedFd<'_>) -> io::Result<XdpStatistics> {
1091+
let mut optlen = core::mem::size_of::<xdp_statistics>().try_into().unwrap();
1092+
debug_assert!(
1093+
optlen as usize >= core::mem::size_of::<c::c_int>(),
1094+
"Socket APIs don't ever use `bool` directly"
1095+
);
1096+
let mut value = MaybeUninit::<xdp_statistics>::zeroed();
1097+
getsockopt_raw(fd, c::SOL_XDP, c::XDP_STATISTICS, &mut value, &mut optlen)?;
1098+
1099+
if optlen as usize == core::mem::size_of::<xdp_statistics_v1>() {
1100+
// Safety: All members of xdp_statistics are u64 and thus are correctly initialized
1101+
// by `MaybeUninit::<xdp_statistics>::zeroed()`
1102+
let xdp_statistics = unsafe { value.assume_init() };
1103+
Ok(XdpStatistics {
1104+
rx_dropped: xdp_statistics.rx_dropped,
1105+
rx_invalid_descs: xdp_statistics.rx_dropped,
1106+
tx_invalid_descs: xdp_statistics.rx_dropped,
1107+
rx_ring_full: None,
1108+
rx_fill_ring_empty_descs: None,
1109+
tx_ring_empty_descs: None,
1110+
})
1111+
} else {
1112+
assert_eq!(
1113+
optlen as usize,
1114+
core::mem::size_of::<xdp_statistics>(),
1115+
"unexpected getsockopt size"
1116+
);
1117+
// Safety: All members of xdp_statistics are u64 and thus are correctly initialized
1118+
// by `MaybeUninit::<xdp_statistics>::zeroed()`
1119+
let xdp_statistics = unsafe { value.assume_init() };
1120+
Ok(XdpStatistics {
1121+
rx_dropped: xdp_statistics.rx_dropped,
1122+
rx_invalid_descs: xdp_statistics.rx_invalid_descs,
1123+
tx_invalid_descs: xdp_statistics.tx_invalid_descs,
1124+
rx_ring_full: Some(xdp_statistics.rx_ring_full),
1125+
rx_fill_ring_empty_descs: Some(xdp_statistics.rx_fill_ring_empty_descs),
1126+
tx_ring_empty_descs: Some(xdp_statistics.tx_ring_empty_descs),
1127+
})
1128+
}
1129+
}
1130+
1131+
#[cfg(target_os = "linux")]
1132+
#[inline]
1133+
pub(crate) fn get_xdp_options(fd: BorrowedFd<'_>) -> io::Result<XdpOptionsFlags> {
1134+
getsockopt(fd, c::SOL_XDP, c::XDP_OPTIONS)
1135+
}
1136+
9661137
#[inline]
9671138
fn to_ip_mreq(multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> c::ip_mreq {
9681139
c::ip_mreq {

0 commit comments

Comments
 (0)