From cb5931d583d283dda3a1b5ed2014c086bb8f98ae Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 3 Feb 2021 05:09:37 +0100 Subject: :rainbow: --- embassy-net-examples/src/main.rs | 79 +++++++++++++++ embassy-net-examples/src/tuntap.rs | 200 +++++++++++++++++++++++++++++++++++++ 2 files changed, 279 insertions(+) create mode 100644 embassy-net-examples/src/main.rs create mode 100644 embassy-net-examples/src/tuntap.rs (limited to 'embassy-net-examples/src') diff --git a/embassy-net-examples/src/main.rs b/embassy-net-examples/src/main.rs new file mode 100644 index 000000000..bc413f1a2 --- /dev/null +++ b/embassy-net-examples/src/main.rs @@ -0,0 +1,79 @@ +#![feature(type_alias_impl_trait)] + +use embassy::executor::{Spawner, task}; +use embassy::io::{AsyncBufReadExt, AsyncWriteExt}; +use embassy::time::{Duration, Timer}; +use embassy::util::Forever; +use embassy_net::*; +use embassy_std::Executor; +use heapless::Vec; +use log::*; + +mod tuntap; + +use crate::tuntap::TunTapDevice; + +static DEVICE: Forever = Forever::new(); +static CONFIG: Forever = Forever::new(); + +#[task] +async fn net_task() { + embassy_net::run().await +} + +#[task] +async fn main_task(spawner: Spawner) { + // Init network device + let device = TunTapDevice::new("tap0").unwrap(); + + // Static IP configuration + let config = StaticConfigurator::new(UpConfig { + address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 1), 24), + dns_servers: Vec::new(), + gateway: Ipv4Address::new(192, 168, 69, 100), + }); + + // Init network stack + embassy_net::init(DEVICE.put(device), CONFIG.put(config)); + + // Launch network task + spawner.spawn(net_task()).unwrap(); + + // Then we can use it! + let mut rx_buffer = [0; 4096]; + let mut tx_buffer = [0; 4096]; + let mut socket = TcpSocket::new(&mut rx_buffer, &mut tx_buffer); + + socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); + + let remote_endpoint = (Ipv4Address::new(192, 168, 69, 100), 8000); + info!("connecting to {:?}...", remote_endpoint); + let r = socket.connect(remote_endpoint).await; + if let Err(e) = r { + warn!("connect error: {:?}", e); + return; + } + info!("connected!"); + loop { + let r = socket.write_all(b"Hello!\n").await; + if let Err(e) = r { + warn!("write error: {:?}", e); + return; + } + } +} + +static EXECUTOR: Forever = Forever::new(); + +fn main() { + env_logger::builder() + .filter_level(log::LevelFilter::Debug) + .filter_module("async_io", log::LevelFilter::Info) + .format_timestamp_nanos() + .init(); + + let executor = EXECUTOR.put(Executor::new()); + executor.run(|spawner| { + spawner.spawn(main_task(spawner)).unwrap(); + }); +} diff --git a/embassy-net-examples/src/tuntap.rs b/embassy-net-examples/src/tuntap.rs new file mode 100644 index 000000000..5c138c069 --- /dev/null +++ b/embassy-net-examples/src/tuntap.rs @@ -0,0 +1,200 @@ +use async_io::Async; +use embassy::util::WakerRegistration; +use libc; +use smoltcp::wire::EthernetFrame; +use std::io; +use std::io::{Read, Write}; +use std::os::unix::io::{AsRawFd, RawFd}; +use log::*; + +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: WakerRegistration, +} + +impl TunTapDevice { + pub fn new(name: &str) -> io::Result { + Ok(Self { + device: Async::new(TunTap::new(name)?)?, + waker: WakerRegistration::new(), + }) + } +} + +use embassy_net::{LinkState, DeviceCapabilities, Packet, PacketBox, PacketBuf}; +use core::task::Waker; + +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(mut cx) = self.waker.context() { + 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, waker: &Waker) { + self.waker.register(waker) + } + + 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 + } +} -- cgit From 9bee576fd241f019c363919b0c29551c6b8ee4b2 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 2 Mar 2021 21:20:00 +0100 Subject: Update embassy --- embassy-net-examples/src/main.rs | 5 ++--- embassy-net-examples/src/tuntap.rs | 8 ++++++-- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'embassy-net-examples/src') diff --git a/embassy-net-examples/src/main.rs b/embassy-net-examples/src/main.rs index bc413f1a2..dba1415b7 100644 --- a/embassy-net-examples/src/main.rs +++ b/embassy-net-examples/src/main.rs @@ -1,8 +1,7 @@ #![feature(type_alias_impl_trait)] -use embassy::executor::{Spawner, task}; -use embassy::io::{AsyncBufReadExt, AsyncWriteExt}; -use embassy::time::{Duration, Timer}; +use embassy::executor::{task, Spawner}; +use embassy::io::AsyncWriteExt; use embassy::util::Forever; use embassy_net::*; use embassy_std::Executor; diff --git a/embassy-net-examples/src/tuntap.rs b/embassy-net-examples/src/tuntap.rs index 5c138c069..b2117e81b 100644 --- a/embassy-net-examples/src/tuntap.rs +++ b/embassy-net-examples/src/tuntap.rs @@ -1,11 +1,11 @@ use async_io::Async; use embassy::util::WakerRegistration; use libc; +use log::*; use smoltcp::wire::EthernetFrame; use std::io; use std::io::{Read, Write}; use std::os::unix::io::{AsRawFd, RawFd}; -use log::*; pub const SIOCGIFMTU: libc::c_ulong = 0x8921; pub const SIOCGIFINDEX: libc::c_ulong = 0x8933; @@ -142,8 +142,8 @@ impl TunTapDevice { } } -use embassy_net::{LinkState, DeviceCapabilities, Packet, PacketBox, PacketBuf}; use core::task::Waker; +use embassy_net::{DeviceCapabilities, LinkState, Packet, PacketBox, PacketBuf}; impl crate::Device for TunTapDevice { fn is_transmit_ready(&mut self) -> bool { @@ -197,4 +197,8 @@ impl crate::Device for TunTapDevice { fn link_state(&mut self) -> LinkState { LinkState::Up } + + fn ethernet_address(&mut self) -> [u8; 6] { + [0x02, 0x03, 0x04, 0x05, 0x06, 0x07] + } } -- cgit From 9c5a8b945a743e75d586fdc2ef857d7c2a038e7d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 7 Apr 2021 19:06:45 +0200 Subject: Update to latest embassy, atomic-pool, smoltcp --- embassy-net-examples/src/main.rs | 36 +++++++++++++++++++++++++++--------- embassy-net-examples/src/tuntap.rs | 35 ++++++++++++++++++++++++++++------- 2 files changed, 55 insertions(+), 16 deletions(-) (limited to 'embassy-net-examples/src') diff --git a/embassy-net-examples/src/main.rs b/embassy-net-examples/src/main.rs index dba1415b7..c157ea34e 100644 --- a/embassy-net-examples/src/main.rs +++ b/embassy-net-examples/src/main.rs @@ -1,6 +1,10 @@ #![feature(type_alias_impl_trait)] +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] +#![allow(incomplete_features)] -use embassy::executor::{task, Spawner}; +use clap::{AppSettings, Clap}; +use embassy::executor::Spawner; use embassy::io::AsyncWriteExt; use embassy::util::Forever; use embassy_net::*; @@ -13,25 +17,39 @@ mod tuntap; use crate::tuntap::TunTapDevice; static DEVICE: Forever = Forever::new(); -static CONFIG: Forever = Forever::new(); +static CONFIG: Forever = Forever::new(); -#[task] +#[derive(Clap)] +#[clap(version = "1.0")] +#[clap(setting = AppSettings::ColoredHelp)] +struct Opts { + /// TAP device name + #[clap(long, default_value = "tap0")] + tap: String, +} + +#[embassy::task] async fn net_task() { embassy_net::run().await } -#[task] +#[embassy::task] async fn main_task(spawner: Spawner) { + let opts: Opts = Opts::parse(); + // Init network device - let device = TunTapDevice::new("tap0").unwrap(); + let device = TunTapDevice::new(&opts.tap).unwrap(); // Static IP configuration - let config = StaticConfigurator::new(UpConfig { - address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 1), 24), + let config = StaticConfigurator::new(Config { + address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24), dns_servers: Vec::new(), - gateway: Ipv4Address::new(192, 168, 69, 100), + gateway: Some(Ipv4Address::new(192, 168, 69, 1)), }); + // DHCP configruation + let config = DhcpConfigurator::new(); + // Init network stack embassy_net::init(DEVICE.put(device), CONFIG.put(config)); @@ -45,7 +63,7 @@ async fn main_task(spawner: Spawner) { socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); - let remote_endpoint = (Ipv4Address::new(192, 168, 69, 100), 8000); + let remote_endpoint = (Ipv4Address::new(192, 168, 69, 74), 8000); info!("connecting to {:?}...", remote_endpoint); let r = socket.connect(remote_endpoint).await; if let Err(e) = r { diff --git a/embassy-net-examples/src/tuntap.rs b/embassy-net-examples/src/tuntap.rs index b2117e81b..dd453deb3 100644 --- a/embassy-net-examples/src/tuntap.rs +++ b/embassy-net-examples/src/tuntap.rs @@ -1,5 +1,4 @@ use async_io::Async; -use embassy::util::WakerRegistration; use libc; use log::*; use smoltcp::wire::EthernetFrame; @@ -130,20 +129,21 @@ impl io::Write for TunTap { pub struct TunTapDevice { device: Async, - waker: WakerRegistration, + waker: Option, } impl TunTapDevice { pub fn new(name: &str) -> io::Result { Ok(Self { device: Async::new(TunTap::new(name)?)?, - waker: WakerRegistration::new(), + waker: None, }) } } use core::task::Waker; -use embassy_net::{DeviceCapabilities, LinkState, Packet, PacketBox, PacketBuf}; +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 { @@ -169,7 +169,8 @@ impl crate::Device for TunTapDevice { return Some(pkt.slice(0..n)); } Err(e) if e.kind() == io::ErrorKind::WouldBlock => { - let ready = if let Some(mut cx) = self.waker.context() { + 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 { @@ -184,8 +185,28 @@ impl crate::Device for TunTapDevice { } } - fn register_waker(&mut self, waker: &Waker) { - self.waker.register(waker) + 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 { -- cgit From 28c235d7866f2a0defaf235270ade6dbee10fc9e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 12 Apr 2021 21:00:12 +0200 Subject: Update example for rand --- embassy-net-examples/src/main.rs | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'embassy-net-examples/src') diff --git a/embassy-net-examples/src/main.rs b/embassy-net-examples/src/main.rs index c157ea34e..d1c2658dd 100644 --- a/embassy-net-examples/src/main.rs +++ b/embassy-net-examples/src/main.rs @@ -80,6 +80,12 @@ async fn main_task(spawner: Spawner) { } } +#[no_mangle] +fn _embassy_rand(buf: &mut [u8]) { + use rand_core::{OsRng, RngCore}; + OsRng.fill_bytes(buf); +} + static EXECUTOR: Forever = Forever::new(); fn main() { -- cgit