Skip to main content

smoltcp/phy/
raw_socket.rs

1use std::cell::RefCell;
2use std::io;
3use std::os::unix::io::{AsRawFd, RawFd};
4use std::rc::Rc;
5use std::vec::Vec;
6
7use crate::phy::{self, Device, DeviceCapabilities, Medium, sys};
8use crate::time::Instant;
9
10/// A socket that captures or transmits the complete frame.
11#[derive(Debug)]
12pub struct RawSocket {
13    medium: Medium,
14    lower: Rc<RefCell<sys::RawSocketDesc>>,
15    mtu: usize,
16}
17
18impl AsRawFd for RawSocket {
19    fn as_raw_fd(&self) -> RawFd {
20        self.lower.borrow().as_raw_fd()
21    }
22}
23
24impl RawSocket {
25    /// Creates a raw socket, bound to the interface called `name`.
26    ///
27    /// This requires superuser privileges or a corresponding capability bit
28    /// set on the executable.
29    pub fn new(name: &str, medium: Medium) -> io::Result<RawSocket> {
30        let mut lower = sys::RawSocketDesc::new(name, medium)?;
31        lower.bind_interface()?;
32
33        let mut mtu = lower.interface_mtu()?;
34
35        #[cfg(feature = "medium-ieee802154")]
36        if medium == Medium::Ieee802154 {
37            // SIOCGIFMTU returns 127 - (ACK_PSDU - FCS - 1) - FCS.
38            //                    127 - (5 - 2 - 1) - 2 = 123
39            // For IEEE802154, we want to add (ACK_PSDU - FCS - 1), since that is what SIOCGIFMTU
40            // uses as the size of the link layer header.
41            //
42            // https://github.com/torvalds/linux/blob/7475e51b87969e01a6812eac713a1c8310372e8a/net/mac802154/iface.c#L541
43            mtu += 2;
44        }
45
46        #[cfg(feature = "medium-ethernet")]
47        if medium == Medium::Ethernet {
48            // SIOCGIFMTU returns the IP MTU (typically 1500 bytes.)
49            // smoltcp counts the entire Ethernet packet in the MTU, so add the Ethernet header size to it.
50            mtu += crate::wire::EthernetFrame::<&[u8]>::header_len()
51        }
52
53        Ok(RawSocket {
54            medium,
55            lower: Rc::new(RefCell::new(lower)),
56            mtu,
57        })
58    }
59}
60
61impl Device for RawSocket {
62    type RxToken<'a>
63        = RxToken
64    where
65        Self: 'a;
66    type TxToken<'a>
67        = TxToken
68    where
69        Self: 'a;
70
71    fn capabilities(&self) -> DeviceCapabilities {
72        DeviceCapabilities {
73            max_transmission_unit: self.mtu,
74            medium: self.medium,
75            ..DeviceCapabilities::default()
76        }
77    }
78
79    fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
80        let mut lower = self.lower.borrow_mut();
81        let mut buffer = vec![0; self.mtu];
82        match lower.recv(&mut buffer[..]) {
83            Ok(size) => {
84                buffer.resize(size, 0);
85                let rx = RxToken { buffer };
86                let tx = TxToken {
87                    lower: self.lower.clone(),
88                };
89                Some((rx, tx))
90            }
91            Err(err) if err.kind() == io::ErrorKind::WouldBlock => None,
92            Err(err) => panic!("{}", err),
93        }
94    }
95
96    fn transmit(&mut self, _timestamp: Instant) -> Option<Self::TxToken<'_>> {
97        Some(TxToken {
98            lower: self.lower.clone(),
99        })
100    }
101}
102
103#[doc(hidden)]
104pub struct RxToken {
105    buffer: Vec<u8>,
106}
107
108impl phy::RxToken for RxToken {
109    fn consume<R, F>(self, f: F) -> R
110    where
111        F: FnOnce(&[u8]) -> R,
112    {
113        f(&self.buffer[..])
114    }
115}
116
117#[doc(hidden)]
118pub struct TxToken {
119    lower: Rc<RefCell<sys::RawSocketDesc>>,
120}
121
122impl phy::TxToken for TxToken {
123    fn consume<R, F>(self, len: usize, f: F) -> R
124    where
125        F: FnOnce(&mut [u8]) -> R,
126    {
127        let mut lower = self.lower.borrow_mut();
128        let mut buffer = vec![0; len];
129        let result = f(&mut buffer);
130        match lower.send(&buffer[..]) {
131            Ok(_) => {}
132            Err(err) if err.kind() == io::ErrorKind::WouldBlock => {
133                net_debug!("phy: tx failed due to WouldBlock")
134            }
135            Err(err) => panic!("{}", err),
136        }
137        result
138    }
139}