ya_smoltcp/phy/sys/
tuntap_interface.rs

1use super::*;
2use crate::{phy::Medium, wire::EthernetFrame};
3use std::io;
4use std::os::unix::io::{AsRawFd, RawFd};
5
6#[derive(Debug)]
7pub struct TunTapInterfaceDesc {
8    lower: libc::c_int,
9    ifreq: ifreq,
10    medium: Medium,
11}
12
13impl AsRawFd for TunTapInterfaceDesc {
14    fn as_raw_fd(&self) -> RawFd {
15        self.lower
16    }
17}
18
19impl TunTapInterfaceDesc {
20    pub fn new(name: &str, medium: Medium) -> io::Result<TunTapInterfaceDesc> {
21        let lower = unsafe {
22            let lower = libc::open(
23                "/dev/net/tun\0".as_ptr() as *const libc::c_char,
24                libc::O_RDWR | libc::O_NONBLOCK,
25            );
26            if lower == -1 {
27                return Err(io::Error::last_os_error());
28            }
29            lower
30        };
31
32        Ok(TunTapInterfaceDesc {
33            lower,
34            ifreq: ifreq_for(name),
35            medium,
36        })
37    }
38
39    pub fn attach_interface(&mut self) -> io::Result<()> {
40        let mode = match self.medium {
41            #[cfg(feature = "medium-ip")]
42            Medium::Ip => imp::IFF_TUN,
43            #[cfg(feature = "medium-ethernet")]
44            Medium::Ethernet => imp::IFF_TAP,
45            #[cfg(feature = "medium-ieee802154")]
46            Medium::Ieee802154 => todo!(),
47        };
48        self.ifreq.ifr_data = mode | imp::IFF_NO_PI;
49        ifreq_ioctl(self.lower, &mut self.ifreq, imp::TUNSETIFF).map(|_| ())
50    }
51
52    pub fn interface_mtu(&mut self) -> io::Result<usize> {
53        let lower = unsafe {
54            let lower = libc::socket(libc::AF_INET, libc::SOCK_DGRAM, libc::IPPROTO_IP);
55            if lower == -1 {
56                return Err(io::Error::last_os_error());
57            }
58            lower
59        };
60
61        let ip_mtu = ifreq_ioctl(lower, &mut self.ifreq, imp::SIOCGIFMTU).map(|mtu| mtu as usize);
62
63        unsafe {
64            libc::close(lower);
65        }
66
67        // Propagate error after close, to ensure we always close.
68        let ip_mtu = ip_mtu?;
69
70        // SIOCGIFMTU returns the IP MTU (typically 1500 bytes.)
71        // smoltcp counts the entire Ethernet packet in the MTU, so add the Ethernet header size to it.
72        let mtu = match self.medium {
73            #[cfg(feature = "medium-ip")]
74            Medium::Ip => ip_mtu,
75            #[cfg(feature = "medium-ethernet")]
76            Medium::Ethernet => ip_mtu + EthernetFrame::<&[u8]>::header_len(),
77            #[cfg(feature = "medium-ieee802154")]
78            Medium::Ieee802154 => todo!(),
79        };
80
81        Ok(mtu)
82    }
83
84    pub fn recv(&mut self, buffer: &mut [u8]) -> io::Result<usize> {
85        unsafe {
86            let len = libc::read(
87                self.lower,
88                buffer.as_mut_ptr() as *mut libc::c_void,
89                buffer.len(),
90            );
91            if len == -1 {
92                return Err(io::Error::last_os_error());
93            }
94            Ok(len as usize)
95        }
96    }
97
98    pub fn send(&mut self, buffer: &[u8]) -> io::Result<usize> {
99        unsafe {
100            let len = libc::write(
101                self.lower,
102                buffer.as_ptr() as *const libc::c_void,
103                buffer.len(),
104            );
105            if len == -1 {
106                Err(io::Error::last_os_error()).unwrap()
107            }
108            Ok(len as usize)
109        }
110    }
111}
112
113impl Drop for TunTapInterfaceDesc {
114    fn drop(&mut self) {
115        unsafe {
116            libc::close(self.lower);
117        }
118    }
119}