Skip to content

Commit 5648e33

Browse files
Add AF_XDP syscalls, constants and types (#946)
1 parent e460984 commit 5648e33

18 files changed

+1125
-10
lines changed

Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ 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.12", default-features = false, features = ["general", "errno", "ioctl", "no_std", "elf"] }
4040
libc_errno = { package = "errno", version = "0.3.8", default-features = false, optional = true }
4141
libc = { version = "0.2.152", default-features = false, features = ["extra_traits"], optional = true }
4242

@@ -53,7 +53,7 @@ libc = { version = "0.2.152", default-features = false, features = ["extra_trait
5353
# Some syscalls do not have libc wrappers, such as in `io_uring`. For these,
5454
# the libc backend uses the linux-raw-sys ABI and `libc::syscall`.
5555
[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"] }
56+
linux-raw-sys = { version = "0.4.12", default-features = false, features = ["general", "ioctl", "no_std"] }
5757

5858
# For the libc backend on Windows, use the Winsock API in windows-sys.
5959
[target.'cfg(windows)'.dependencies.windows-sys]
@@ -141,7 +141,7 @@ io_uring = ["event", "fs", "net", "linux-raw-sys/io_uring"]
141141
mount = []
142142

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

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

src/backend/libc/net/msghdr.rs

Lines changed: 26 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,28 @@ pub(crate) fn with_unix_msghdr<R>(
124128
})
125129
}
126130

131+
/// Create a message header intended to send with an IPv6 address.
132+
#[cfg(target_os = "linux")]
133+
pub(crate) fn with_xdp_msghdr<R>(
134+
addr: &SocketAddrXdp,
135+
iov: &[IoSlice<'_>],
136+
control: &mut SendAncillaryBuffer<'_, '_, '_>,
137+
f: impl FnOnce(c::msghdr) -> R,
138+
) -> R {
139+
let encoded = encode_sockaddr_xdp(addr);
140+
141+
f({
142+
let mut h = zero_msghdr();
143+
h.msg_name = as_ptr(&encoded) as _;
144+
h.msg_namelen = size_of::<SocketAddrXdp>() as _;
145+
h.msg_iov = iov.as_ptr() as _;
146+
h.msg_iovlen = msg_iov_len(iov.len());
147+
h.msg_control = control.as_control_ptr().cast();
148+
h.msg_controllen = msg_control_len(control.control_len());
149+
h
150+
})
151+
}
152+
127153
/// Create a zero-initialized message header struct value.
128154
#[cfg(all(unix, not(target_os = "redox")))]
129155
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+
Ok(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: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ 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::{XdpMmapOffsets, XdpOptionsFlags, XdpRingOffset, XdpStatistics, XdpUmemReg};
1719
#[cfg(not(any(
1820
apple,
1921
windows,
@@ -73,6 +75,8 @@ use c::TCP_KEEPALIVE as TCP_KEEPIDLE;
7375
use c::TCP_KEEPIDLE;
7476
use core::mem::{size_of, MaybeUninit};
7577
use core::time::Duration;
78+
#[cfg(target_os = "linux")]
79+
use linux_raw_sys::xdp::{xdp_mmap_offsets, xdp_statistics, xdp_statistics_v1};
7680
#[cfg(windows)]
7781
use windows_sys::Win32::Foundation::BOOL;
7882

@@ -963,6 +967,170 @@ pub(crate) fn get_socket_peercred(fd: BorrowedFd<'_>) -> io::Result<UCred> {
963967
getsockopt(fd, c::SOL_SOCKET, c::SO_PEERCRED)
964968
}
965969

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

src/backend/libc/net/syscalls.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,16 @@
22
33
#[cfg(unix)]
44
use super::addr::SocketAddrUnix;
5+
#[cfg(target_os = "linux")]
6+
use super::msghdr::with_xdp_msghdr;
7+
#[cfg(target_os = "linux")]
8+
use super::write_sockaddr::encode_sockaddr_xdp;
59
use crate::backend::c;
610
use crate::backend::conv::{borrowed_fd, ret, ret_owned_fd, ret_send_recv, send_recv_len};
711
use crate::fd::{BorrowedFd, OwnedFd};
812
use crate::io;
13+
#[cfg(target_os = "linux")]
14+
use crate::net::xdp::SocketAddrXdp;
915
use crate::net::{SocketAddrAny, SocketAddrV4, SocketAddrV6};
1016
use crate::utils::as_ptr;
1117
use core::mem::{size_of, MaybeUninit};
@@ -145,6 +151,25 @@ pub(crate) fn sendto_unix(
145151
}
146152
}
147153

154+
#[cfg(target_os = "linux")]
155+
pub(crate) fn sendto_xdp(
156+
fd: BorrowedFd<'_>,
157+
buf: &[u8],
158+
flags: SendFlags,
159+
addr: &SocketAddrXdp,
160+
) -> io::Result<usize> {
161+
unsafe {
162+
ret_send_recv(c::sendto(
163+
borrowed_fd(fd),
164+
buf.as_ptr().cast(),
165+
send_recv_len(buf.len()),
166+
bitflags_bits!(flags),
167+
as_ptr(&encode_sockaddr_xdp(addr)).cast::<c::sockaddr>(),
168+
size_of::<c::sockaddr_xdp>() as _,
169+
))
170+
}
171+
}
172+
148173
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
149174
pub(crate) fn socket(
150175
domain: AddressFamily,
@@ -217,6 +242,17 @@ pub(crate) fn bind_unix(sockfd: BorrowedFd<'_>, addr: &SocketAddrUnix) -> io::Re
217242
}
218243
}
219244

245+
#[cfg(target_os = "linux")]
246+
pub(crate) fn bind_xdp(sockfd: BorrowedFd<'_>, addr: &SocketAddrXdp) -> io::Result<()> {
247+
unsafe {
248+
ret(c::bind(
249+
borrowed_fd(sockfd),
250+
as_ptr(&encode_sockaddr_xdp(addr)).cast(),
251+
size_of::<c::sockaddr_xdp>() as c::socklen_t,
252+
))
253+
}
254+
}
255+
220256
#[cfg(not(any(target_os = "redox", target_os = "wasi")))]
221257
pub(crate) fn connect_v4(sockfd: BorrowedFd<'_>, addr: &SocketAddrV4) -> io::Result<()> {
222258
unsafe {
@@ -402,6 +438,23 @@ pub(crate) fn sendmsg_unix(
402438
})
403439
}
404440

441+
#[cfg(target_os = "linux")]
442+
pub(crate) fn sendmsg_xdp(
443+
sockfd: BorrowedFd<'_>,
444+
addr: &SocketAddrXdp,
445+
iov: &[IoSlice<'_>],
446+
control: &mut SendAncillaryBuffer<'_, '_, '_>,
447+
msg_flags: SendFlags,
448+
) -> io::Result<usize> {
449+
with_xdp_msghdr(addr, iov, control, |msghdr| unsafe {
450+
ret_send_recv(c::sendmsg(
451+
borrowed_fd(sockfd),
452+
&msghdr,
453+
bitflags_bits!(msg_flags),
454+
))
455+
})
456+
}
457+
405458
#[cfg(not(any(
406459
apple,
407460
windows,

0 commit comments

Comments
 (0)