Skip to main content

wireforge_io/linux/
l3.rs

1//! L3 (raw socket) I/O — IPv4 packet receive and send.
2
3use std::net::SocketAddrV4;
4use std::os::fd::AsRawFd;
5use std::time::Duration;
6
7use socket2::{Domain, Protocol, Socket, Type};
8use wireforge_core::ipv4::Ipv4Packet;
9use wireforge_core::types::IpProtocol;
10
11use crate::common;
12use crate::error::IoError;
13use crate::traits::{L3Reader, L3Writer};
14
15/// Receiver for raw IPv4 packets via SOCK_RAW socket.
16pub struct L3Receiver {
17    sock: Socket,
18    buf: Vec<u8>,
19}
20
21impl L3Receiver {
22    /// Open a SOCK_RAW socket for the given IP protocol.
23    pub fn new(protocol: IpProtocol) -> Result<Self, IoError> {
24        let proto = Protocol::from(u8::from(protocol) as i32);
25        let sock = Socket::new(Domain::IPV4, Type::RAW, Some(proto))?;
26        Ok(Self {
27            sock,
28            buf: vec![0u8; 65536],
29        })
30    }
31
32    /// Receive a single IPv4 packet and parse it.
33    pub fn recv(&mut self) -> Result<Ipv4Packet<'_>, IoError> {
34        let n = unsafe {
35            libc::recv(
36                self.sock.as_raw_fd(),
37                self.buf.as_mut_ptr() as *mut libc::c_void,
38                self.buf.len(),
39                0,
40            )
41        };
42        if n < 0 {
43            return Err(IoError::Socket(std::io::Error::last_os_error()));
44        }
45        let n = n as usize;
46        Ipv4Packet::new(&self.buf[..n]).ok_or(IoError::Parse("invalid IPv4 packet"))
47    }
48
49    /// Enable or disable IP_HDRINCL (header included in send).
50    pub fn set_header_included(&mut self, on: bool) -> Result<(), IoError> {
51        let fd = self.sock.as_raw_fd();
52        let val: libc::c_int = if on { 1 } else { 0 };
53        unsafe {
54            if libc::setsockopt(
55                fd,
56                libc::IPPROTO_IP,
57                libc::IP_HDRINCL,
58                &val as *const _ as *const libc::c_void,
59                std::mem::size_of::<libc::c_int>() as libc::socklen_t,
60            ) < 0
61            {
62                return Err(IoError::Socket(std::io::Error::last_os_error()));
63            }
64        }
65        Ok(())
66    }
67
68    /// Set receive timeout.
69    pub fn set_timeout(&mut self, dur: Option<Duration>) -> Result<(), IoError> {
70        common::set_socket_timeout(self.sock.as_raw_fd(), dur)
71    }
72}
73
74/// Sender for raw IPv4 packets via SOCK_RAW socket.
75pub struct L3Sender {
76    sock: Socket,
77}
78
79impl L3Sender {
80    /// Open a SOCK_RAW socket for sending IPv4 packets.
81    pub fn new() -> Result<Self, IoError> {
82        let sock = Socket::new(
83            Domain::IPV4,
84            Type::RAW,
85            Some(Protocol::from(libc::IPPROTO_RAW)),
86        )?;
87        Ok(Self { sock })
88    }
89
90    /// Send an IPv4 packet to the given destination.
91    pub fn send_to(&self, packet: &[u8], dst: SocketAddrV4) -> Result<usize, IoError> {
92        let addr = socket2::SockAddr::from(std::net::SocketAddr::V4(dst));
93        let n = self.sock.send_to(packet, &addr)?;
94        Ok(n)
95    }
96}
97
98// Trait implementations
99impl L3Reader for L3Receiver {
100    fn recv(&mut self) -> Result<Ipv4Packet<'_>, IoError> {
101        self.recv()
102    }
103
104    fn set_timeout(&mut self, dur: Option<Duration>) -> Result<(), IoError> {
105        self.set_timeout(dur)
106    }
107
108    fn set_header_included(&mut self, on: bool) -> Result<(), IoError> {
109        self.set_header_included(on)
110    }
111}
112
113impl L3Writer for L3Sender {
114    fn send_to(&self, packet: &[u8], dst: SocketAddrV4) -> Result<usize, IoError> {
115        self.send_to(packet, dst)
116    }
117}