aboutsummaryrefslogtreecommitdiff
path: root/embassy-net-tuntap/src
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2023-08-03 14:23:11 +0200
committerGitHub <[email protected]>2023-08-03 14:23:11 +0200
commit4d60c715e683aaadf25d9f066bde805c725fefb4 (patch)
treed3601429cef8850fba4a99df0e148da19fd96efc /embassy-net-tuntap/src
parent2c96fe917de6e0120053e80d8da5a98d0d0f35d0 (diff)
net: move tuntap from std example to separate crate. (#1737)
Diffstat (limited to 'embassy-net-tuntap/src')
-rw-r--r--embassy-net-tuntap/src/lib.rs225
1 files changed, 225 insertions, 0 deletions
diff --git a/embassy-net-tuntap/src/lib.rs b/embassy-net-tuntap/src/lib.rs
new file mode 100644
index 000000000..75c54c487
--- /dev/null
+++ b/embassy-net-tuntap/src/lib.rs
@@ -0,0 +1,225 @@
1use std::io;
2use std::io::{Read, Write};
3use std::os::unix::io::{AsRawFd, RawFd};
4use std::task::Context;
5
6use async_io::Async;
7use embassy_net_driver::{self, Capabilities, Driver, HardwareAddress, LinkState};
8use log::*;
9
10pub const SIOCGIFMTU: libc::c_ulong = 0x8921;
11pub const _SIOCGIFINDEX: libc::c_ulong = 0x8933;
12pub const _ETH_P_ALL: libc::c_short = 0x0003;
13pub const TUNSETIFF: libc::c_ulong = 0x400454CA;
14pub const _IFF_TUN: libc::c_int = 0x0001;
15pub const IFF_TAP: libc::c_int = 0x0002;
16pub const IFF_NO_PI: libc::c_int = 0x1000;
17
18const ETHERNET_HEADER_LEN: usize = 14;
19
20#[repr(C)]
21#[derive(Debug)]
22#[allow(non_camel_case_types)]
23struct ifreq {
24 ifr_name: [libc::c_char; libc::IF_NAMESIZE],
25 ifr_data: libc::c_int, /* ifr_ifindex or ifr_mtu */
26}
27
28fn ifreq_for(name: &str) -> ifreq {
29 let mut ifreq = ifreq {
30 ifr_name: [0; libc::IF_NAMESIZE],
31 ifr_data: 0,
32 };
33 for (i, byte) in name.as_bytes().iter().enumerate() {
34 ifreq.ifr_name[i] = *byte as libc::c_char
35 }
36 ifreq
37}
38
39fn ifreq_ioctl(lower: libc::c_int, ifreq: &mut ifreq, cmd: libc::c_ulong) -> io::Result<libc::c_int> {
40 unsafe {
41 let res = libc::ioctl(lower, cmd as _, ifreq as *mut ifreq);
42 if res == -1 {
43 return Err(io::Error::last_os_error());
44 }
45 }
46
47 Ok(ifreq.ifr_data)
48}
49
50#[derive(Debug)]
51pub struct TunTap {
52 fd: libc::c_int,
53 mtu: usize,
54}
55
56impl AsRawFd for TunTap {
57 fn as_raw_fd(&self) -> RawFd {
58 self.fd
59 }
60}
61
62impl TunTap {
63 pub fn new(name: &str) -> io::Result<TunTap> {
64 unsafe {
65 let fd = libc::open(
66 "/dev/net/tun\0".as_ptr() as *const libc::c_char,
67 libc::O_RDWR | libc::O_NONBLOCK,
68 );
69 if fd == -1 {
70 return Err(io::Error::last_os_error());
71 }
72
73 let mut ifreq = ifreq_for(name);
74 ifreq.ifr_data = IFF_TAP | IFF_NO_PI;
75 ifreq_ioctl(fd, &mut ifreq, TUNSETIFF)?;
76
77 let socket = libc::socket(libc::AF_INET, libc::SOCK_DGRAM, libc::IPPROTO_IP);
78 if socket == -1 {
79 return Err(io::Error::last_os_error());
80 }
81
82 let ip_mtu = ifreq_ioctl(socket, &mut ifreq, SIOCGIFMTU);
83 libc::close(socket);
84 let ip_mtu = ip_mtu? as usize;
85
86 // SIOCGIFMTU returns the IP MTU (typically 1500 bytes.)
87 // smoltcp counts the entire Ethernet packet in the MTU, so add the Ethernet header size to it.
88 let mtu = ip_mtu + ETHERNET_HEADER_LEN;
89
90 Ok(TunTap { fd, mtu })
91 }
92 }
93}
94
95impl Drop for TunTap {
96 fn drop(&mut self) {
97 unsafe {
98 libc::close(self.fd);
99 }
100 }
101}
102
103impl io::Read for TunTap {
104 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
105 let len = unsafe { libc::read(self.fd, buf.as_mut_ptr() as *mut libc::c_void, buf.len()) };
106 if len == -1 {
107 Err(io::Error::last_os_error())
108 } else {
109 Ok(len as usize)
110 }
111 }
112}
113
114impl io::Write for TunTap {
115 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
116 let len = unsafe { libc::write(self.fd, buf.as_ptr() as *mut libc::c_void, buf.len()) };
117 if len == -1 {
118 Err(io::Error::last_os_error())
119 } else {
120 Ok(len as usize)
121 }
122 }
123
124 fn flush(&mut self) -> io::Result<()> {
125 Ok(())
126 }
127}
128
129pub struct TunTapDevice {
130 device: Async<TunTap>,
131}
132
133impl TunTapDevice {
134 pub fn new(name: &str) -> io::Result<TunTapDevice> {
135 Ok(Self {
136 device: Async::new(TunTap::new(name)?)?,
137 })
138 }
139}
140
141impl Driver for TunTapDevice {
142 type RxToken<'a> = RxToken where Self: 'a;
143 type TxToken<'a> = TxToken<'a> where Self: 'a;
144
145 fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
146 let mut buf = vec![0; self.device.get_ref().mtu];
147 loop {
148 match self.device.get_mut().read(&mut buf) {
149 Ok(n) => {
150 buf.truncate(n);
151 return Some((
152 RxToken { buffer: buf },
153 TxToken {
154 device: &mut self.device,
155 },
156 ));
157 }
158 Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
159 if !self.device.poll_readable(cx).is_ready() {
160 return None;
161 }
162 }
163 Err(e) => panic!("read error: {:?}", e),
164 }
165 }
166 }
167
168 fn transmit(&mut self, _cx: &mut Context) -> Option<Self::TxToken<'_>> {
169 Some(TxToken {
170 device: &mut self.device,
171 })
172 }
173
174 fn capabilities(&self) -> Capabilities {
175 let mut caps = Capabilities::default();
176 caps.max_transmission_unit = self.device.get_ref().mtu;
177 caps
178 }
179
180 fn link_state(&mut self, _cx: &mut Context) -> LinkState {
181 LinkState::Up
182 }
183
184 fn hardware_address(&self) -> HardwareAddress {
185 HardwareAddress::Ethernet([0x02, 0x03, 0x04, 0x05, 0x06, 0x07])
186 }
187}
188
189#[doc(hidden)]
190pub struct RxToken {
191 buffer: Vec<u8>,
192}
193
194impl embassy_net_driver::RxToken for RxToken {
195 fn consume<R, F>(mut self, f: F) -> R
196 where
197 F: FnOnce(&mut [u8]) -> R,
198 {
199 f(&mut self.buffer)
200 }
201}
202
203#[doc(hidden)]
204pub struct TxToken<'a> {
205 device: &'a mut Async<TunTap>,
206}
207
208impl<'a> embassy_net_driver::TxToken for TxToken<'a> {
209 fn consume<R, F>(self, len: usize, f: F) -> R
210 where
211 F: FnOnce(&mut [u8]) -> R,
212 {
213 let mut buffer = vec![0; len];
214 let result = f(&mut buffer);
215
216 // todo handle WouldBlock with async
217 match self.device.get_mut().write(&buffer) {
218 Ok(_) => {}
219 Err(e) if e.kind() == io::ErrorKind::WouldBlock => info!("transmit WouldBlock"),
220 Err(e) => panic!("transmit error: {:?}", e),
221 }
222
223 result
224 }
225}