1use libc::{
17 AF_PACKET, ETH_P_ALL, SOCK_RAW, htons, if_nametoindex, setsockopt, sockaddr_ll, socket,
18};
19use std::ffi::c_void;
20use std::fs;
21use std::io::{self, BufRead};
22use std::mem;
23use std::net::Ipv4Addr;
24use std::os::fd::RawFd;
25
26const TPACKET_V3: libc::c_int = 2;
27
28fn open_af_packet() -> RawFd {
30 unsafe {
31 let fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL as u16) as i32);
32
33 if fd < 0 {
34 let err = *libc::__errno_location();
35 if err == libc::EPERM || err == libc::EACCES {
36 panic!(
37 "AF_PACKET sys call failed;\n Error number: {}; No permissions \n \n Error: CAP_NET_RAW or root privileges required to use raw AF_PACKET sockets.\n - Run with: sudo <cmd>\n - Or give the binary capability: sudo setcap cap_net_raw+ep <binary>\n
38 ",
39 err
40 );
41 } else {
42 panic!("AF_PACKET sys call failed; Error number: {}", err);
43 }
44 }
45
46 fd
47 }
48}
49
50fn lookup_arp_cache(ip: Ipv4Addr) -> io::Result<Option<[u8; 6]>> {
51 let file = fs::File::open("proc/net/arp")?;
52 let reader = io::BufReader::new(file);
53
54 for line in reader.lines().skip(1) {
55 let line = line?;
56 let cols: Vec<&str> = line.split_whitespace().collect();
57
58 if cols.len() < 6 {
59 continue;
60 }
61
62 let ip_addr = cols[0];
63 if ip_addr != ip.to_string() {
64 continue;
65 }
66
67 let hw_addr = cols[3];
68 if hw_addr == "00:00:00:00:00:00" {
69 return Ok(None);
70 }
71
72 let bytes: Vec<u8> = hw_addr
73 .split(':')
74 .filter_map(|s| u8::from_str_radix(s, 16).ok())
75 .collect();
76
77 if bytes.len() != 6 {
78 continue;
79 }
80
81 let mut mac = [0u8; 6];
82 mac.copy_from_slice(&bytes);
83 return Ok(Some(mac));
84 }
85 Ok(None)
86}
87
88pub fn resolve_mac(socket: &LinuxSocket, dst_ip: Ipv4Addr) -> io::Result<[u8; 6]> {
89 if let Some(mac) = lookup_arp_cache(dst_ip)? {
90 return Ok(mac);
91 }
92
93 if let Some(mac) = lookup_arp_cache(socket.default_gateway)? {
94 return Ok(mac);
95 }
96
97 Err(io::Error::new(
99 io::ErrorKind::NotFound,
100 "MAC address not found in ARP for dst or gateway",
101 ))
102}
103
104fn get_default_ifindex_and_default_gateway() -> io::Result<(i32, Ipv4Addr)> {
105 let file = fs::File::open("/proc/net/route")?;
106 let reader = io::BufReader::new(file);
107
108 for line in reader.lines().skip(1) {
109 let line = line?;
110 let cols: Vec<&str> = line.split_whitespace().collect();
111
112 if cols.len() < 2 {
113 continue;
114 }
115
116 let iface = cols[0];
117 let destination = cols[1];
118 let gateway_hex = cols[2];
119
120 if destination == "00000000" {
121 let c_name = std::ffi::CString::new(iface).unwrap();
122 let index = unsafe { if_nametoindex(c_name.as_ptr()) };
123
124 if index == 0 {
125 return Err(io::Error::new(
126 io::ErrorKind::Other,
127 format!("if_nametoindex failed for {}", iface),
128 ));
129 }
130
131 let g = u32::from_str_radix(gateway_hex, 16).unwrap();
132
133 return Ok((index as i32, Ipv4Addr::from(g.swap_bytes())));
134 }
135 }
136
137 let fallback = unsafe { if_nametoindex(std::ffi::CString::new("lo").unwrap().as_ptr()) };
138
139 if fallback != 0 {
140 return Ok((fallback as i32, Ipv4Addr::new(127, 0, 0, 1)));
141 }
142
143 Err(io::Error::new(
144 io::ErrorKind::NotFound,
145 "No usable route found",
146 ))
147}
148
149fn bind_interface(fd: RawFd, if_index: i32) {
150 unsafe {
151 let mut addr: sockaddr_ll = mem::zeroed();
152 addr.sll_family = AF_PACKET as u16;
153 addr.sll_protocol = htons(ETH_P_ALL as u16);
154 addr.sll_ifindex = if_index;
155
156 let ret = libc::bind(
157 fd,
158 &addr as *const sockaddr_ll as *const libc::sockaddr,
159 mem::size_of::<sockaddr_ll>() as u32,
160 );
161
162 if ret < 0 {
163 panic!("AF_PACKET sys call bind failed");
164 }
165 }
166}
167
168fn set_sock_opt(fd: RawFd) -> Result<(*mut c_void, usize), SetSockOpsErrors> {
169 let version = TPACKET_V3;
170
171 let r = unsafe {
172 setsockopt(
173 fd,
174 libc::SOL_PACKET,
175 libc::PACKET_VERSION,
176 &version as *const _ as *const libc::c_void,
177 std::mem::size_of::<libc::c_int>() as libc::socklen_t,
178 )
179 };
180
181 if r != 0 {
182 return Err(SetSockOpsErrors::PacketVersion(
183 "Failed to set PACKET_VERSION TPACKET_V3".to_string(),
184 ));
185 }
186
187 let req = libc::tpacket_req3 {
188 tp_block_size: 1 << 20, tp_block_nr: 64,
190 tp_frame_size: 2048,
191 tp_frame_nr: (64 * (1 << 20)) / 2048,
192 tp_retire_blk_tov: 60, tp_sizeof_priv: 0,
194 tp_feature_req_word: 0,
195 };
196
197 let ret = unsafe {
198 setsockopt(
199 fd,
200 libc::SOL_PACKET,
201 libc::PACKET_RX_RING,
202 &req as *const _ as *const libc::c_void,
203 std::mem::size_of::<libc::tpacket_req3>() as libc::socklen_t,
204 )
205 };
206
207 if ret != 0 {
208 return Err(SetSockOpsErrors::RingBuffer(
209 "Failed to set PACKET_RX_RING".to_string(),
210 ));
211 }
212
213 let mmap_len = (req.tp_block_size * req.tp_block_nr) as usize;
214
215 let mmap = unsafe {
216 libc::mmap(
217 std::ptr::null_mut(),
218 mmap_len,
219 libc::PROT_READ | libc::PROT_WRITE,
220 libc::MAP_SHARED,
221 fd,
222 0,
223 )
224 };
225
226 if mmap.is_null() {
227 return Err(SetSockOpsErrors::Mmap(
228 "Failed set mmap on ring buffer".to_string(),
229 ));
230 }
231
232 Ok((mmap, mmap_len))
233}
234
235#[derive(Debug)]
236pub enum SetSockOpsErrors {
237 PacketVersion(String),
238 RingBuffer(String),
239 Mmap(String),
240}
241
242#[derive(Debug)]
243pub struct LinuxSocket {
244 pub fd: RawFd,
245 pub ifindex: i32,
246 pub default_gateway: Ipv4Addr,
247 pub mmap: Option<(*mut c_void, usize)>,
248}
249
250impl LinuxSocket {
251 pub fn new() -> Result<Self, LinuxSocketErrors> {
252 let (ifindex, default_gateway) = get_default_ifindex_and_default_gateway()
253 .map_err(|e| LinuxSocketErrors::DefaultIfIndex(e))?;
254
255 let fd = open_af_packet();
256 bind_interface(fd, ifindex);
257
258 Ok(LinuxSocket {
259 fd,
260 default_gateway,
261 ifindex,
262 mmap: None,
263 })
264 }
265
266 pub fn set_ops(&mut self) -> Result<(), LinuxSocketErrors> {
267 let (mmap, ptr_len) =
268 set_sock_opt(self.fd).map_err(|e| LinuxSocketErrors::SetSockOps(e))?;
269
270 self.mmap.replace((mmap, ptr_len));
271 Ok(())
272 }
273
274 pub fn send_raw_packet(
275 &self,
276 dst_mac: [u8; 6],
277 ether_type: u16,
278 packet: &[u8],
279 ) -> Result<(), LinuxSocketErrors> {
280 unsafe {
281 let mut addr: sockaddr_ll = mem::zeroed();
282 addr.sll_family = AF_PACKET as u16;
283 addr.sll_ifindex = self.ifindex;
284 addr.sll_protocol = htons(ether_type);
285 addr.sll_halen = 6;
286 addr.sll_addr[..6].copy_from_slice(&dst_mac);
287
288 let sent = libc::sendto(
289 self.fd,
290 packet.as_ptr() as *const _ as *const c_void,
291 packet.len(),
292 0,
293 &addr as *const sockaddr_ll as *const libc::sockaddr,
294 mem::size_of::<sockaddr_ll>() as u32,
295 );
296
297 if sent == -1 {
298 return Err(LinuxSocketErrors::SendingPacket(io::Error::last_os_error()));
299 }
300 Ok(())
301 }
302 }
303}
304
305impl Drop for LinuxSocket {
306 fn drop(&mut self) {
307 if let Some((mmap, ptr_len)) = self.mmap.take() {
308 let res = unsafe { libc::munmap(mmap, ptr_len) };
309 if res != 0 {
310 eprintln!("Failed to remove any mappings from the adress space");
311 }
312 }
313
314 unsafe { libc::close(self.fd) };
315 }
316}
317
318#[derive(Debug)]
319pub enum LinuxSocketErrors {
320 DefaultIfIndex(std::io::Error),
321 SetSockOps(SetSockOpsErrors),
322 SendingPacket(std::io::Error),
323}
324
325#[cfg(test)]
326mod tests {
327
328 use super::*;
329
330 #[test]
331 fn linux_af_packet_test() {
332 let (if_index, _default_gateway) = get_default_ifindex_and_default_gateway().unwrap();
333
334 let fd = open_af_packet();
335
336 bind_interface(fd, if_index);
337
338 let mut buf = [0u8; u16::MAX as usize];
339 unsafe {
340 let n = libc::recv(fd, buf.as_mut_ptr() as *mut _, buf.len(), 0);
341 assert!(n > 0);
342 }
343 }
344
345 #[test]
346 fn linux_af_packet_test_with_ring_buffer() {
347 let (if_index, _default_gateway) = get_default_ifindex_and_default_gateway().unwrap();
348
349 let fd = open_af_packet();
350
351 bind_interface(fd, if_index);
352
353 let mmap_ptr = set_sock_opt(fd);
354
355 assert!(mmap_ptr.is_ok());
356
357 let (mmap, len) = mmap_ptr.unwrap();
358
359 let res = unsafe { libc::munmap(mmap, len) };
360
361 assert!(res == 0);
362 }
363
364 #[test]
365 fn linux_socket_test() {
366 let socket = LinuxSocket::new();
367
368 assert!(socket.is_ok());
369
370 let mut s = socket.unwrap();
371
372 let res = s.set_ops();
373
374 assert!(res.is_ok());
375 }
376}