1use std::os::fd::AsRawFd;
4use std::time::Duration;
5
6use socket2::{Domain, Protocol, Socket, Type};
7use wireforge_core::ether::EthernetPacket;
8
9use crate::common;
10use crate::error::IoError;
11use crate::traits::{L2Reader, L2Writer};
12
13pub struct L2Receiver {
15 sock: Socket,
16 buf: Vec<u8>,
17 ifname: String,
18}
19
20impl L2Receiver {
21 pub fn new(ifname: &str) -> Result<Self, IoError> {
23 let sock = Socket::new(
24 Domain::PACKET,
25 Type::RAW,
26 Some(Protocol::from(libc::ETH_P_ALL)),
27 )?;
28
29 sock.bind_device(Some(ifname.as_bytes()))?;
31
32 Ok(Self {
33 sock,
34 buf: vec![0u8; 65536],
35 ifname: ifname.to_string(),
36 })
37 }
38
39 pub fn recv(&mut self) -> Result<EthernetPacket<'_>, IoError> {
41 let n = unsafe {
42 libc::recv(
43 self.sock.as_raw_fd(),
44 self.buf.as_mut_ptr() as *mut libc::c_void,
45 self.buf.len(),
46 0,
47 )
48 };
49 if n < 0 {
50 return Err(IoError::Socket(std::io::Error::last_os_error()));
51 }
52 let n = n as usize;
53 EthernetPacket::new(&self.buf[..n]).ok_or(IoError::Parse("invalid Ethernet frame"))
54 }
55
56 pub fn set_promiscuous(&mut self, on: bool) -> Result<(), IoError> {
58 let fd = self.sock.as_raw_fd();
59 unsafe {
60 let mut ifreq: libc::ifreq = std::mem::zeroed();
61 copy_ifname(&mut ifreq, &self.ifname);
62 libc::ioctl(fd, libc::SIOCGIFFLAGS, &mut ifreq);
64 let mut flags = ifreq.ifr_ifru.ifru_flags;
65 if on {
66 flags |= libc::IFF_PROMISC as i16;
67 } else {
68 flags &= !(libc::IFF_PROMISC as i16);
69 }
70 ifreq.ifr_ifru.ifru_flags = flags;
71 if libc::ioctl(fd, libc::SIOCSIFFLAGS, &ifreq) < 0 {
72 return Err(IoError::Socket(std::io::Error::last_os_error()));
73 }
74 }
75 Ok(())
76 }
77
78 pub fn set_timeout(&mut self, dur: Option<Duration>) -> Result<(), IoError> {
80 common::set_socket_timeout(self.sock.as_raw_fd(), dur)
81 }
82}
83
84pub struct L2Sender {
86 sock: Socket,
87 sockaddr: libc::sockaddr_ll,
88}
89
90impl L2Sender {
91 pub fn new(ifname: &str) -> Result<Self, IoError> {
93 let sock = Socket::new(
94 Domain::PACKET,
95 Type::RAW,
96 Some(Protocol::from(libc::ETH_P_ALL)),
97 )?;
98
99 let fd = sock.as_raw_fd();
100 let ifindex = unsafe {
101 let mut ifreq: libc::ifreq = std::mem::zeroed();
102 let name_bytes = ifname.as_bytes();
103 let copy_len = name_bytes.len().min(ifreq.ifr_name.len() - 1);
104 let name_i8 = std::slice::from_raw_parts(name_bytes.as_ptr() as *const i8, copy_len);
106 ifreq.ifr_name[..copy_len].copy_from_slice(name_i8);
107 libc::ioctl(fd, libc::SIOCGIFINDEX, &mut ifreq);
108 ifreq.ifr_ifru.ifru_ifindex
109 };
110
111 let sockaddr = libc::sockaddr_ll {
112 sll_family: libc::AF_PACKET as u16,
113 sll_protocol: (libc::ETH_P_ALL as u16).to_be(),
114 sll_ifindex: ifindex,
115 sll_hatype: 0,
116 sll_pkttype: 0,
117 sll_halen: 0,
118 sll_addr: [0u8; 8],
119 };
120
121 Ok(Self { sock, sockaddr })
122 }
123
124 pub fn send(&self, packet: &[u8]) -> Result<usize, IoError> {
126 let n = unsafe {
127 let addr: &libc::sockaddr = &*(&self.sockaddr as *const libc::sockaddr_ll as *const libc::sockaddr);
129 libc::sendto(
130 self.sock.as_raw_fd(),
131 packet.as_ptr() as *const libc::c_void,
132 packet.len(),
133 0,
134 addr,
135 std::mem::size_of::<libc::sockaddr_ll>() as libc::socklen_t,
136 )
137 };
138 if n < 0 {
139 return Err(IoError::Socket(std::io::Error::last_os_error()));
140 }
141 Ok(n as usize)
142 }
143}
144
145impl L2Reader for L2Receiver {
147 fn recv(&mut self) -> Result<EthernetPacket<'_>, IoError> {
148 self.recv()
149 }
150
151 fn set_timeout(&mut self, dur: Option<Duration>) -> Result<(), IoError> {
152 self.set_timeout(dur)
153 }
154
155 fn set_promiscuous(&mut self, on: bool) -> Result<(), IoError> {
156 self.set_promiscuous(on)
157 }
158}
159
160impl L2Writer for L2Sender {
161 fn send(&self, packet: &[u8]) -> Result<usize, IoError> {
162 self.send(packet)
163 }
164}
165
166#[cfg(target_os = "linux")]
168fn copy_ifname(ifr: &mut libc::ifreq, name: &str) {
169 let bytes = name.as_bytes();
170 let len = bytes.len().min(ifr.ifr_name.len() - 1);
171 let name_i8 = unsafe { std::slice::from_raw_parts(bytes.as_ptr() as *const i8, len) };
173 ifr.ifr_name[..len].copy_from_slice(name_i8);
174}