From dff03ecfc74d6af716637888338ebfa99ab7a027 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 2 Jun 2021 01:30:07 +0200 Subject: Move examples to a subdirectory --- examples/std/src/tuntap.rs | 225 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 225 insertions(+) create mode 100644 examples/std/src/tuntap.rs (limited to 'examples/std/src/tuntap.rs') diff --git a/examples/std/src/tuntap.rs b/examples/std/src/tuntap.rs new file mode 100644 index 000000000..dd453deb3 --- /dev/null +++ b/examples/std/src/tuntap.rs @@ -0,0 +1,225 @@ +use async_io::Async; +use libc; +use log::*; +use smoltcp::wire::EthernetFrame; +use std::io; +use std::io::{Read, Write}; +use std::os::unix::io::{AsRawFd, RawFd}; + +pub const SIOCGIFMTU: libc::c_ulong = 0x8921; +pub const SIOCGIFINDEX: libc::c_ulong = 0x8933; +pub const ETH_P_ALL: libc::c_short = 0x0003; +pub const TUNSETIFF: libc::c_ulong = 0x400454CA; +pub const IFF_TUN: libc::c_int = 0x0001; +pub const IFF_TAP: libc::c_int = 0x0002; +pub const IFF_NO_PI: libc::c_int = 0x1000; + +#[repr(C)] +#[derive(Debug)] +struct ifreq { + ifr_name: [libc::c_char; libc::IF_NAMESIZE], + ifr_data: libc::c_int, /* ifr_ifindex or ifr_mtu */ +} + +fn ifreq_for(name: &str) -> ifreq { + let mut ifreq = ifreq { + ifr_name: [0; libc::IF_NAMESIZE], + ifr_data: 0, + }; + for (i, byte) in name.as_bytes().iter().enumerate() { + ifreq.ifr_name[i] = *byte as libc::c_char + } + ifreq +} + +fn ifreq_ioctl( + lower: libc::c_int, + ifreq: &mut ifreq, + cmd: libc::c_ulong, +) -> io::Result { + unsafe { + let res = libc::ioctl(lower, cmd as _, ifreq as *mut ifreq); + if res == -1 { + return Err(io::Error::last_os_error()); + } + } + + Ok(ifreq.ifr_data) +} + +#[derive(Debug)] +pub struct TunTap { + fd: libc::c_int, + ifreq: ifreq, + mtu: usize, +} + +impl AsRawFd for TunTap { + fn as_raw_fd(&self) -> RawFd { + self.fd + } +} + +impl TunTap { + pub fn new(name: &str) -> io::Result { + unsafe { + let fd = libc::open( + "/dev/net/tun\0".as_ptr() as *const libc::c_char, + libc::O_RDWR | libc::O_NONBLOCK, + ); + if fd == -1 { + return Err(io::Error::last_os_error()); + } + + let mut ifreq = ifreq_for(name); + ifreq.ifr_data = IFF_TAP | IFF_NO_PI; + ifreq_ioctl(fd, &mut ifreq, TUNSETIFF)?; + + let socket = libc::socket(libc::AF_INET, libc::SOCK_DGRAM, libc::IPPROTO_IP); + if socket == -1 { + return Err(io::Error::last_os_error()); + } + + let ip_mtu = ifreq_ioctl(socket, &mut ifreq, SIOCGIFMTU); + libc::close(socket); + let ip_mtu = ip_mtu? as usize; + + // SIOCGIFMTU returns the IP MTU (typically 1500 bytes.) + // smoltcp counts the entire Ethernet packet in the MTU, so add the Ethernet header size to it. + let mtu = ip_mtu + EthernetFrame::<&[u8]>::header_len(); + + Ok(TunTap { fd, mtu, ifreq }) + } + } +} + +impl Drop for TunTap { + fn drop(&mut self) { + unsafe { + libc::close(self.fd); + } + } +} + +impl io::Read for TunTap { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let len = unsafe { libc::read(self.fd, buf.as_mut_ptr() as *mut libc::c_void, buf.len()) }; + if len == -1 { + Err(io::Error::last_os_error()) + } else { + Ok(len as usize) + } + } +} + +impl io::Write for TunTap { + fn write(&mut self, buf: &[u8]) -> io::Result { + let len = unsafe { libc::write(self.fd, buf.as_ptr() as *mut libc::c_void, buf.len()) }; + if len == -1 { + Err(io::Error::last_os_error()) + } else { + Ok(len as usize) + } + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +pub struct TunTapDevice { + device: Async, + waker: Option, +} + +impl TunTapDevice { + pub fn new(name: &str) -> io::Result { + Ok(Self { + device: Async::new(TunTap::new(name)?)?, + waker: None, + }) + } +} + +use core::task::Waker; +use embassy_net::{DeviceCapabilities, LinkState, Packet, PacketBox, PacketBoxExt, PacketBuf}; +use std::task::Context; + +impl crate::Device for TunTapDevice { + fn is_transmit_ready(&mut self) -> bool { + true + } + + fn transmit(&mut self, pkt: PacketBuf) { + // todo handle WouldBlock + match self.device.get_mut().write(&pkt) { + Ok(_) => {} + Err(e) if e.kind() == io::ErrorKind::WouldBlock => { + info!("transmit WouldBlock"); + } + Err(e) => panic!("transmit error: {:?}", e), + } + } + + fn receive(&mut self) -> Option { + let mut pkt = PacketBox::new(Packet::new()).unwrap(); + loop { + match self.device.get_mut().read(&mut pkt[..]) { + Ok(n) => { + return Some(pkt.slice(0..n)); + } + Err(e) if e.kind() == io::ErrorKind::WouldBlock => { + let ready = if let Some(w) = self.waker.as_ref() { + let mut cx = Context::from_waker(w); + let ready = self.device.poll_readable(&mut cx).is_ready(); + ready + } else { + false + }; + if !ready { + return None; + } + } + Err(e) => panic!("read error: {:?}", e), + } + } + } + + fn register_waker(&mut self, w: &Waker) { + match self.waker { + // Optimization: If both the old and new Wakers wake the same task, we can simply + // keep the old waker, skipping the clone. (In most executor implementations, + // cloning a waker is somewhat expensive, comparable to cloning an Arc). + Some(ref w2) if (w2.will_wake(w)) => {} + _ => { + // clone the new waker and store it + if let Some(old_waker) = core::mem::replace(&mut self.waker, Some(w.clone())) { + // We had a waker registered for another task. Wake it, so the other task can + // reregister itself if it's still interested. + // + // If two tasks are waiting on the same thing concurrently, this will cause them + // to wake each other in a loop fighting over this WakerRegistration. This wastes + // CPU but things will still work. + // + // If the user wants to have two tasks waiting on the same thing they should use + // a more appropriate primitive that can store multiple wakers. + old_waker.wake() + } + } + } + } + + fn capabilities(&mut self) -> DeviceCapabilities { + let mut caps = DeviceCapabilities::default(); + caps.max_transmission_unit = self.device.get_ref().mtu; + caps + } + + fn link_state(&mut self) -> LinkState { + LinkState::Up + } + + fn ethernet_address(&mut self) -> [u8; 6] { + [0x02, 0x03, 0x04, 0x05, 0x06, 0x07] + } +} -- cgit