Skip to content

Commit d5f52af

Browse files
committed
Add support for Packet MMAP
1 parent 4ed2ea2 commit d5f52af

File tree

14 files changed

+853
-14
lines changed

14 files changed

+853
-14
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.12", default-features = false, features = ["general", "errno", "ioctl", "no_std", "elf"] }
39+
linux-raw-sys = { version = "0.6.4", 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.12", default-features = false, features = ["general", "ioctl", "no_std"] }
56+
linux-raw-sys = { version = "0.6.4", 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", "linux-raw-sys/xdp"]
144+
net = ["linux-raw-sys/net", "linux-raw-sys/netlink", "linux-raw-sys/if_ether", "linux-raw-sys/if_packet", "linux-raw-sys/xdp"]
145145

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

examples/packet.rs

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
//! Packet MMAP.
2+
3+
#[cfg(all(
4+
feature = "mm",
5+
feature = "net",
6+
feature = "event",
7+
feature = "std",
8+
target_os = "linux"
9+
))]
10+
fn main() -> std::io::Result<()> {
11+
use rustix::event::{poll, PollFd, PollFlags};
12+
use rustix::mm::{mmap, munmap, MapFlags, ProtFlags};
13+
use rustix::net::{
14+
bind_link, eth,
15+
netdevice::name_to_index,
16+
packet::{PacketHeader, PacketReq, PacketReqAny, PacketStatus, SocketAddrLink},
17+
socket_with,
18+
sockopt::{
19+
get_packet_stats, set_packet_rx_ring, set_packet_tx_ring, set_packet_version,
20+
PacketVersion,
21+
},
22+
AddressFamily, SocketFlags, SocketType,
23+
};
24+
use std::{env::args, ptr};
25+
26+
let name = args().nth(1).unwrap();
27+
println!("Name: {}", name);
28+
29+
let family = AddressFamily::PACKET;
30+
let type_ = SocketType::RAW;
31+
let flags = SocketFlags::empty();
32+
let fd = socket_with(family, type_, flags, None)?;
33+
34+
let index = name_to_index(&fd, &name)?;
35+
println!("Index: {}", index);
36+
37+
set_packet_version(&fd, PacketVersion::V1)?;
38+
39+
let req = PacketReq {
40+
block_size: 4096,
41+
block_nr: 4,
42+
frame_size: 2048,
43+
frame_nr: 8,
44+
};
45+
println!("{:?}", req);
46+
let size = req.block_size as usize * req.block_nr as usize;
47+
48+
let req = PacketReqAny::V1(req);
49+
set_packet_rx_ring(&fd, &req)?;
50+
println!("Set RX ring");
51+
set_packet_tx_ring(&fd, &req)?;
52+
println!("Set TX ring");
53+
54+
let addr = SocketAddrLink::new(eth::ALL, index);
55+
bind_link(&fd, &addr)?;
56+
57+
let rx = unsafe {
58+
mmap(
59+
ptr::null_mut(),
60+
size * 2,
61+
ProtFlags::READ | ProtFlags::WRITE,
62+
MapFlags::SHARED,
63+
&fd,
64+
0,
65+
)
66+
}?;
67+
let tx = rx.wrapping_add(size);
68+
69+
println!("RX: {:p}, TX: {:p}", rx, tx);
70+
71+
let pfd = PollFd::new(&fd, PollFlags::IN | PollFlags::RDNORM | PollFlags::ERR);
72+
let pfd = &mut [pfd];
73+
74+
loop {
75+
let n = match poll(pfd, -1) {
76+
Ok(n) => n,
77+
Err(e) => {
78+
println!("Poll error: {}", e);
79+
break;
80+
}
81+
};
82+
assert_eq!(n, 1);
83+
if pfd[0].revents().is_empty() {
84+
println!("No events");
85+
continue;
86+
}
87+
println!("Events: {:?}", pfd[0].revents());
88+
for i in 0..8 {
89+
let (frame, addr) = unsafe { PacketHeader::from_ptr(rx.wrapping_add(i * 2048)) };
90+
// TODO why fields in frame looks like garbage?
91+
println!("{:?}", frame);
92+
println!("{}", addr);
93+
if frame.status == PacketStatus::USER.into_raw() as _ {
94+
frame.status = PacketStatus::AVAILABLE.into_raw() as _;
95+
}
96+
}
97+
break;
98+
}
99+
100+
let stats = get_packet_stats(&fd, PacketVersion::V2)?;
101+
println!("{:?}", stats);
102+
103+
unsafe { munmap(rx, size * 2) }?;
104+
105+
Ok(())
106+
}
107+
108+
#[cfg(any(
109+
not(feature = "mm"),
110+
not(feature = "net"),
111+
not(feature = "event"),
112+
not(feature = "std"),
113+
not(target_os = "linux")
114+
))]
115+
fn main() -> Result<(), &'static str> {
116+
Err("This example requires --features=mm,net,std and is only supported on Linux.")
117+
}

src/backend/linux_raw/c.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,14 @@ pub(crate) use linux_raw_sys::{
5555
cmsg_macros::*,
5656
general::{O_CLOEXEC as SOCK_CLOEXEC, O_NONBLOCK as SOCK_NONBLOCK},
5757
if_ether::*,
58+
if_packet::*,
5859
net::{
5960
linger, msghdr, sockaddr, sockaddr_in, sockaddr_in6, sockaddr_un, socklen_t, AF_DECnet,
6061
__kernel_sa_family_t as sa_family_t, __kernel_sockaddr_storage as sockaddr_storage,
61-
cmsghdr, in6_addr, in_addr, ip_mreq, ip_mreq_source, ip_mreqn, ipv6_mreq, AF_APPLETALK,
62-
AF_ASH, AF_ATMPVC, AF_ATMSVC, AF_AX25, AF_BLUETOOTH, AF_BRIDGE, AF_CAN, AF_ECONET,
63-
AF_IEEE802154, AF_INET, AF_INET6, AF_IPX, AF_IRDA, AF_ISDN, AF_IUCV, AF_KEY, AF_LLC,
64-
AF_NETBEUI, AF_NETLINK, AF_NETROM, AF_PACKET, AF_PHONET, AF_PPPOX, AF_RDS, AF_ROSE,
62+
cmsghdr, ifreq, in6_addr, in_addr, ip_mreq, ip_mreq_source, ip_mreqn, ipv6_mreq,
63+
AF_APPLETALK, AF_ASH, AF_ATMPVC, AF_ATMSVC, AF_AX25, AF_BLUETOOTH, AF_BRIDGE, AF_CAN,
64+
AF_ECONET, AF_IEEE802154, AF_INET, AF_INET6, AF_IPX, AF_IRDA, AF_ISDN, AF_IUCV, AF_KEY,
65+
AF_LLC, AF_NETBEUI, AF_NETLINK, AF_NETROM, AF_PACKET, AF_PHONET, AF_PPPOX, AF_RDS, AF_ROSE,
6566
AF_RXRPC, AF_SECURITY, AF_SNA, AF_TIPC, AF_UNIX, AF_UNSPEC, AF_WANPIPE, AF_X25, AF_XDP,
6667
IP6T_SO_ORIGINAL_DST, IPPROTO_FRAGMENT, IPPROTO_ICMPV6, IPPROTO_MH, IPPROTO_ROUTING,
6768
IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP, IPV6_FREEBIND, IPV6_MULTICAST_HOPS,
@@ -71,12 +72,12 @@ pub(crate) use linux_raw_sys::{
7172
MSG_CMSG_CLOEXEC, MSG_CONFIRM, MSG_DONTROUTE, MSG_DONTWAIT, MSG_EOR, MSG_ERRQUEUE,
7273
MSG_MORE, MSG_NOSIGNAL, MSG_OOB, MSG_PEEK, MSG_TRUNC, MSG_WAITALL, SCM_CREDENTIALS,
7374
SCM_RIGHTS, SHUT_RD, SHUT_RDWR, SHUT_WR, SOCK_DGRAM, SOCK_RAW, SOCK_RDM, SOCK_SEQPACKET,
74-
SOCK_STREAM, SOL_SOCKET, SOL_XDP, SO_ACCEPTCONN, SO_BROADCAST, SO_COOKIE, SO_DOMAIN,
75-
SO_ERROR, SO_INCOMING_CPU, SO_KEEPALIVE, SO_LINGER, SO_OOBINLINE, SO_ORIGINAL_DST,
76-
SO_PASSCRED, SO_PROTOCOL, SO_RCVBUF, SO_RCVTIMEO_NEW, SO_RCVTIMEO_NEW as SO_RCVTIMEO,
77-
SO_RCVTIMEO_OLD, SO_REUSEADDR, SO_REUSEPORT, SO_SNDBUF, SO_SNDTIMEO_NEW,
78-
SO_SNDTIMEO_NEW as SO_SNDTIMEO, SO_SNDTIMEO_OLD, SO_TYPE, TCP_CONGESTION, TCP_CORK,
79-
TCP_KEEPCNT, TCP_KEEPIDLE, TCP_KEEPINTVL, TCP_NODELAY, TCP_QUICKACK,
75+
SOCK_STREAM, SOL_PACKET, SOL_SOCKET, SOL_XDP, SO_ACCEPTCONN, SO_BROADCAST, SO_COOKIE,
76+
SO_DOMAIN, SO_ERROR, SO_INCOMING_CPU, SO_KEEPALIVE, SO_LINGER, SO_OOBINLINE,
77+
SO_ORIGINAL_DST, SO_PASSCRED, SO_PROTOCOL, SO_RCVBUF, SO_RCVTIMEO_NEW,
78+
SO_RCVTIMEO_NEW as SO_RCVTIMEO, SO_RCVTIMEO_OLD, SO_REUSEADDR, SO_REUSEPORT, SO_SNDBUF,
79+
SO_SNDTIMEO_NEW, SO_SNDTIMEO_NEW as SO_SNDTIMEO, SO_SNDTIMEO_OLD, SO_TYPE, TCP_CONGESTION,
80+
TCP_CORK, TCP_KEEPCNT, TCP_KEEPIDLE, TCP_KEEPINTVL, TCP_NODELAY, TCP_QUICKACK,
8081
TCP_THIN_LINEAR_TIMEOUTS, TCP_USER_TIMEOUT,
8182
},
8283
netlink::*,

src/backend/linux_raw/net/sockopt.rs

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ use crate::fd::BorrowedFd;
1111
#[cfg(feature = "alloc")]
1212
use crate::ffi::CStr;
1313
use crate::io;
14-
use crate::net::sockopt::Timeout;
14+
#[cfg(target_os = "linux")]
15+
use crate::net::packet::{PacketReqAny, PacketStats, PacketStats3, PacketStatsAny};
16+
use crate::net::sockopt::{PacketVersion, Timeout};
1517
#[cfg(target_os = "linux")]
1618
use crate::net::xdp::{XdpMmapOffsets, XdpOptionsFlags, XdpRingOffset, XdpStatistics, XdpUmemReg};
1719
use crate::net::{
@@ -967,6 +969,62 @@ pub(crate) fn get_xdp_options(fd: BorrowedFd<'_>) -> io::Result<XdpOptionsFlags>
967969
getsockopt(fd, c::SOL_XDP, c::XDP_OPTIONS)
968970
}
969971

972+
#[cfg(target_os = "linux")]
973+
#[inline]
974+
pub(crate) fn set_packet_rx_ring(fd: BorrowedFd<'_>, value: &PacketReqAny) -> io::Result<()> {
975+
match *value {
976+
PacketReqAny::V1(value) | PacketReqAny::V2(value) => {
977+
setsockopt(fd, c::SOL_PACKET, c::PACKET_RX_RING, value)
978+
}
979+
PacketReqAny::V3(value) => setsockopt(fd, c::SOL_PACKET, c::PACKET_RX_RING, value),
980+
}
981+
}
982+
983+
#[cfg(target_os = "linux")]
984+
#[inline]
985+
pub(crate) fn set_packet_tx_ring(fd: BorrowedFd<'_>, value: &PacketReqAny) -> io::Result<()> {
986+
match *value {
987+
PacketReqAny::V1(value) | PacketReqAny::V2(value) => {
988+
setsockopt(fd, c::SOL_PACKET, c::PACKET_TX_RING, value)
989+
}
990+
PacketReqAny::V3(value) => setsockopt(fd, c::SOL_PACKET, c::PACKET_TX_RING, value),
991+
}
992+
}
993+
994+
#[cfg(target_os = "linux")]
995+
#[inline]
996+
pub(crate) fn set_packet_version(fd: BorrowedFd<'_>, value: PacketVersion) -> io::Result<()> {
997+
setsockopt(fd, c::SOL_PACKET, c::PACKET_VERSION, value)
998+
}
999+
1000+
#[cfg(target_os = "linux")]
1001+
#[inline]
1002+
pub(crate) fn get_packet_version(fd: BorrowedFd<'_>) -> io::Result<PacketVersion> {
1003+
getsockopt(fd, c::SOL_PACKET, c::PACKET_VERSION)
1004+
}
1005+
1006+
#[cfg(target_os = "linux")]
1007+
#[inline]
1008+
pub(crate) fn get_packet_stats(
1009+
fd: BorrowedFd<'_>,
1010+
version: PacketVersion,
1011+
) -> io::Result<PacketStatsAny> {
1012+
match version {
1013+
PacketVersion::V1 => {
1014+
let stats: PacketStats = getsockopt(fd, c::SOL_PACKET, c::PACKET_STATISTICS)?;
1015+
Ok(PacketStatsAny::V1(stats))
1016+
}
1017+
PacketVersion::V2 => {
1018+
let stats: PacketStats = getsockopt(fd, c::SOL_PACKET, c::PACKET_STATISTICS)?;
1019+
Ok(PacketStatsAny::V2(stats))
1020+
}
1021+
PacketVersion::V3 => {
1022+
let stats: PacketStats3 = getsockopt(fd, c::SOL_PACKET, c::PACKET_STATISTICS)?;
1023+
Ok(PacketStatsAny::V3(stats))
1024+
}
1025+
}
1026+
}
1027+
9701028
#[inline]
9711029
fn to_ip_mreq(multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> c::ip_mreq {
9721030
c::ip_mreq {

src/backend/linux_raw/net/syscalls.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ use super::msghdr::{
1313
use super::read_sockaddr::{initialize_family_to_unspec, maybe_read_sockaddr_os, read_sockaddr_os};
1414
use super::send_recv::{RecvFlags, SendFlags};
1515
#[cfg(target_os = "linux")]
16+
use super::write_sockaddr::encode_sockaddr_link;
17+
#[cfg(target_os = "linux")]
1618
use super::write_sockaddr::encode_sockaddr_xdp;
1719
use super::write_sockaddr::{encode_sockaddr_v4, encode_sockaddr_v6};
1820
use crate::backend::c;
@@ -23,6 +25,8 @@ use crate::backend::conv::{
2325
use crate::fd::{BorrowedFd, OwnedFd};
2426
use crate::io::{self, IoSlice, IoSliceMut};
2527
#[cfg(target_os = "linux")]
28+
use crate::net::packet::SocketAddrLink;
29+
#[cfg(target_os = "linux")]
2630
use crate::net::xdp::SocketAddrXdp;
2731
use crate::net::{
2832
AddressFamily, Protocol, RecvAncillaryBuffer, RecvMsgReturn, SendAncillaryBuffer, Shutdown,
@@ -439,6 +443,18 @@ pub(crate) fn sendmsg_xdp(
439443
})
440444
}
441445

446+
#[cfg(target_os = "linux")]
447+
#[inline]
448+
pub(crate) fn sendmsg_link(
449+
_sockfd: BorrowedFd<'_>,
450+
_addr: &SocketAddrLink,
451+
_iov: &[IoSlice<'_>],
452+
_control: &mut SendAncillaryBuffer<'_, '_, '_>,
453+
_msg_flags: SendFlags,
454+
) -> io::Result<usize> {
455+
todo!()
456+
}
457+
442458
#[inline]
443459
pub(crate) fn shutdown(fd: BorrowedFd<'_>, how: Shutdown) -> io::Result<()> {
444460
#[cfg(not(target_arch = "x86"))]
@@ -660,6 +676,17 @@ pub(crate) fn sendto_xdp(
660676
}
661677
}
662678

679+
#[cfg(target_os = "linux")]
680+
#[inline]
681+
pub(crate) fn sendto_link(
682+
_fd: BorrowedFd<'_>,
683+
_buf: &[u8],
684+
_flags: SendFlags,
685+
_addr: &SocketAddrLink,
686+
) -> io::Result<usize> {
687+
todo!()
688+
}
689+
663690
#[inline]
664691
pub(crate) unsafe fn recv(
665692
fd: BorrowedFd<'_>,
@@ -931,6 +958,32 @@ pub(crate) fn bind_xdp(fd: BorrowedFd<'_>, addr: &SocketAddrXdp) -> io::Result<(
931958
}
932959
}
933960

961+
#[cfg(target_os = "linux")]
962+
#[inline]
963+
pub(crate) fn bind_link(fd: BorrowedFd<'_>, addr: &SocketAddrLink) -> io::Result<()> {
964+
#[cfg(not(target_arch = "x86"))]
965+
unsafe {
966+
ret(syscall_readonly!(
967+
__NR_bind,
968+
fd,
969+
by_ref(&encode_sockaddr_link(addr)),
970+
size_of::<c::sockaddr_ll, _>()
971+
))
972+
}
973+
#[cfg(target_arch = "x86")]
974+
unsafe {
975+
ret(syscall_readonly!(
976+
__NR_socketcall,
977+
x86_sys(SYS_BIND),
978+
slice_just_addr::<ArgReg<'_, SocketArg>, _>(&[
979+
fd.into(),
980+
by_ref(&encode_sockaddr_link(addr)),
981+
size_of::<c::sockaddr_ll, _>(),
982+
])
983+
))
984+
}
985+
}
986+
934987
#[inline]
935988
pub(crate) fn connect_v4(fd: BorrowedFd<'_>, addr: &SocketAddrV4) -> io::Result<()> {
936989
#[cfg(not(target_arch = "x86"))]

src/backend/linux_raw/net/write_sockaddr.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
use crate::backend::c;
66
#[cfg(target_os = "linux")]
7+
use crate::net::packet::SocketAddrLink;
8+
#[cfg(target_os = "linux")]
79
use crate::net::xdp::SocketAddrXdp;
810
use crate::net::{SocketAddrAny, SocketAddrStorage, SocketAddrUnix, SocketAddrV4, SocketAddrV6};
911
use core::mem::size_of;
@@ -18,6 +20,8 @@ pub(crate) unsafe fn write_sockaddr(
1820
SocketAddrAny::Unix(unix) => write_sockaddr_unix(unix, storage),
1921
#[cfg(target_os = "linux")]
2022
SocketAddrAny::Xdp(xdp) => write_sockaddr_xdp(xdp, storage),
23+
#[cfg(target_os = "linux")]
24+
SocketAddrAny::Link(link) => write_sockaddr_link(link, storage),
2125
}
2226
}
2327

@@ -80,3 +84,27 @@ unsafe fn write_sockaddr_xdp(xdp: &SocketAddrXdp, storage: *mut SocketAddrStorag
8084
core::ptr::write(storage.cast(), encoded);
8185
size_of::<c::sockaddr_xdp>()
8286
}
87+
88+
#[cfg(target_os = "linux")]
89+
pub(crate) fn encode_sockaddr_link(link: &SocketAddrLink) -> c::sockaddr_ll {
90+
c::sockaddr_ll {
91+
sll_family: c::AF_PACKET as _,
92+
sll_protocol: link.protocol,
93+
sll_ifindex: link.ifindex,
94+
sll_hatype: link.hatype,
95+
sll_pkttype: link.pkttype,
96+
sll_halen: link.halen,
97+
__bindgen_anon_1: c::sockaddr_ll__bindgen_ty_1 {
98+
sll_addr: c::__BindgenUnionField::new(),
99+
__bindgen_anon_1: c::__BindgenUnionField::new(),
100+
bindgen_union_field: link.addr,
101+
},
102+
}
103+
}
104+
105+
#[cfg(target_os = "linux")]
106+
unsafe fn write_sockaddr_link(link: &SocketAddrLink, storage: *mut SocketAddrStorage) -> usize {
107+
let encoded = encode_sockaddr_link(link);
108+
core::ptr::write(storage.cast(), encoded);
109+
size_of::<c::sockaddr_ll>()
110+
}

src/net/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ mod wsa;
1818

1919
#[cfg(linux_kernel)]
2020
pub mod netdevice;
21+
#[cfg(linux_kernel)]
22+
pub mod packet;
2123
pub mod sockopt;
2224

2325
pub use crate::maybe_polyfill::net::{

0 commit comments

Comments
 (0)