1#![allow(unsafe_code)]
6
7use anyhow::{bail, Context, Result};
8use std::ffi::CString;
9use std::os::fd::{AsRawFd, FromRawFd, OwnedFd};
10use std::time::Duration;
11
12const RECV_BUF_LEN: usize = 65536;
14
15#[derive(Debug)]
16pub struct Link {
17 fd: OwnedFd,
18 buf: Vec<u8>,
20}
21
22impl Link {
23 pub fn open(iface: &str, read_timeout: Duration) -> Result<Self> {
29 let proto = (libc::ETH_P_ALL as u16).to_be();
30 let raw =
31 unsafe { libc::socket(libc::AF_PACKET, libc::SOCK_RAW, libc::c_int::from(proto)) };
32 if raw < 0 {
33 let e = std::io::Error::last_os_error();
34 if e.raw_os_error() == Some(libc::EPERM) || e.raw_os_error() == Some(libc::EACCES) {
35 bail!(
36 "socket(AF_PACKET): {e} (try: sudo setcap cap_net_raw,cap_net_admin+ep \
37 $(command -v rxp), or run as root)"
38 );
39 }
40 return Err(e).context("socket(AF_PACKET)");
41 }
42 let fd = unsafe { OwnedFd::from_raw_fd(raw) };
43
44 let name = CString::new(iface).with_context(|| format!("interface name: {iface}"))?;
45 let ifindex = unsafe { libc::if_nametoindex(name.as_ptr()) };
46 if ifindex == 0 {
47 bail!("no such interface: {iface}");
48 }
49
50 let mut sa: libc::sockaddr_ll = unsafe { std::mem::zeroed() };
51 sa.sll_family = libc::AF_PACKET as u16;
52 sa.sll_protocol = proto;
53 sa.sll_ifindex = ifindex as libc::c_int;
54 let rc = unsafe {
55 libc::bind(
56 fd.as_raw_fd(),
57 std::ptr::from_ref(&sa).cast(),
58 std::mem::size_of::<libc::sockaddr_ll>() as libc::socklen_t,
59 )
60 };
61 if rc < 0 {
62 return Err(std::io::Error::last_os_error()).with_context(|| format!("bind {iface}"));
63 }
64
65 let mut mr: libc::packet_mreq = unsafe { std::mem::zeroed() };
66 mr.mr_ifindex = ifindex as libc::c_int;
67 mr.mr_type = libc::PACKET_MR_PROMISC as u16;
68 setsockopt(&fd, libc::SOL_PACKET, libc::PACKET_ADD_MEMBERSHIP, &mr)
69 .context("PACKET_ADD_MEMBERSHIP promisc")?;
70
71 #[allow(clippy::cast_lossless)]
73 let tv = libc::timeval {
74 tv_sec: read_timeout.as_secs() as libc::time_t,
75 tv_usec: read_timeout.subsec_micros() as libc::suseconds_t,
76 };
77 setsockopt(&fd, libc::SOL_SOCKET, libc::SO_RCVTIMEO, &tv).context("SO_RCVTIMEO")?;
78
79 Ok(Self {
80 fd,
81 buf: vec![0u8; RECV_BUF_LEN],
82 })
83 }
84
85 pub fn send(&mut self, frame: &[u8]) -> Result<()> {
90 let n = unsafe { libc::send(self.fd.as_raw_fd(), frame.as_ptr().cast(), frame.len(), 0) };
91 if n < 0 {
92 return Err(std::io::Error::last_os_error()).context("send on packet socket");
93 }
94 if n as usize != frame.len() {
95 bail!("short write: {n} of {} bytes", frame.len());
96 }
97 Ok(())
98 }
99
100 pub fn recv(&mut self) -> Result<Frames<'_>> {
106 let n = unsafe {
107 libc::recv(
108 self.fd.as_raw_fd(),
109 self.buf.as_mut_ptr().cast(),
110 self.buf.len(),
111 0,
112 )
113 };
114 if n < 0 {
115 let e = std::io::Error::last_os_error();
116 return match e.raw_os_error() {
117 Some(libc::EAGAIN | libc::EINTR) => Ok(Frames(None)),
118 _ => Err(e).context("recv on packet socket"),
119 };
120 }
121 Ok(Frames(Some(&self.buf[..n as usize])))
122 }
123}
124
125#[derive(Clone, Debug)]
127pub struct Frames<'a>(pub Option<&'a [u8]>);
128
129impl<'a> Iterator for Frames<'a> {
130 type Item = &'a [u8];
131
132 fn next(&mut self) -> Option<&'a [u8]> {
133 self.0.take()
134 }
135}
136
137fn setsockopt<T>(fd: &OwnedFd, level: libc::c_int, name: libc::c_int, value: &T) -> Result<()> {
138 let rc = unsafe {
139 libc::setsockopt(
140 fd.as_raw_fd(),
141 level,
142 name,
143 std::ptr::from_ref(value).cast(),
144 std::mem::size_of::<T>() as libc::socklen_t,
145 )
146 };
147 if rc < 0 {
148 return Err(std::io::Error::last_os_error().into());
149 }
150 Ok(())
151}
152
153#[cfg(test)]
154mod tests {
155 use super::*;
156
157 #[test]
158 fn frames_yield_at_most_one_frame() {
159 assert!(Frames(None).next().is_none());
160 let mut f = Frames(Some(&[1, 2, 3][..]));
161 assert_eq!(f.next(), Some(&[1, 2, 3][..]));
162 assert!(f.next().is_none());
163 }
164}