diff --git a/Cargo.toml b/Cargo.toml index 7ab99c2e..8ed84db9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,8 +42,8 @@ opt-level = 0 overflow-checks = false [patch.crates-io] -embassy = { git = "https://github.com/akiles/embassy" } -embassy-std = { git = "https://github.com/akiles/embassy" } -embassy-macros = { git = "https://github.com/akiles/embassy" } -embassy-traits = { git = "https://github.com/akiles/embassy" } -smoltcp = { git = "https://github.com/akiles/smoltcp" } \ No newline at end of file +embassy = { git = "https://github.com/embassy-rs/embassy" } +embassy-std = { git = "https://github.com/embassy-rs/embassy" } +embassy-macros = { git = "https://github.com/embassy-rs/embassy" } +embassy-traits = { git = "https://github.com/embassy-rs/embassy" } +smoltcp = { git = "https://github.com/smoltcp-rs/smoltcp", rev="9ce3d9505ef5455bb049713b9262561d78ebf330" } \ No newline at end of file diff --git a/embassy-net-examples/Cargo.toml b/embassy-net-examples/Cargo.toml index 944e9191..796c4474 100644 --- a/embassy-net-examples/Cargo.toml +++ b/embassy-net-examples/Cargo.toml @@ -8,10 +8,11 @@ edition = "2018" heapless = { version = "0.5.6", default-features = false } embassy = { version = "0.1.0", features=["std", "log"] } embassy-std = { version = "0.1.0" } -embassy-net = { version = "0.1.0", path = "../embassy-net", features=["std", "log"] } +embassy-net = { version = "0.1.0", path = "../embassy-net", features=["std", "log", "tcp", "dhcpv4"] } env_logger = "0.8.2" log = "0.4.11" futures = "0.3.8" libc = "0.2.81" async-io = "1.3.1" -smoltcp = { version = "0.6.0", default-features = false } +smoltcp = { version = "0.7.0", default-features = false } +clap = { version = "3.0.0-beta.2", features = ["derive"] } \ No newline at end of file diff --git a/embassy-net-examples/src/main.rs b/embassy-net-examples/src/main.rs index dba1415b..c157ea34 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 b2117e81..dd453deb 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 { diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index de625018..dccd7984 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -12,6 +12,9 @@ defmt-info = [] defmt-warn = [] defmt-error = [] +tcp = ["smoltcp/socket-tcp"] +dhcpv4 = ["smoltcp/socket-dhcpv4"] + [dependencies] defmt = { version = "0.2.0", optional = true } @@ -25,9 +28,10 @@ as-slice = { version = "0.1.4" } generic-array = { version = "0.14.4", default-features = false } stable_deref_trait = { version = "1.2.0", default-features = false } futures = { version = "0.3.5", default-features = false, features = [ "async-await" ]} +atomic-pool = "0.1.0" [dependencies.smoltcp] -version = "0.6.0" +version = "0.7.0" #git = "https://github.com/akiles/smoltcp" #rev = "00952e2c5cdf5667a1dfb6142258055f58d3851c" default-features = false @@ -35,12 +39,12 @@ features = [ "medium-ethernet", "medium-ip", "proto-ipv4", - "proto-dhcpv4", + #"proto-dhcpv4", #"proto-igmp", #"proto-ipv6", #"socket-raw", #"socket-icmp", #"socket-udp", - "socket-tcp", + #"socket-tcp", "async", ] diff --git a/embassy-net/src/config/dhcp.rs b/embassy-net/src/config/dhcp.rs index 0df67baf..007c398b 100644 --- a/embassy-net/src/config/dhcp.rs +++ b/embassy-net/src/config/dhcp.rs @@ -1,80 +1,59 @@ -use embassy::util::Forever; use heapless::Vec; -use smoltcp::dhcp::Dhcpv4Client; -use smoltcp::socket::{RawPacketMetadata, RawSocketBuffer}; +use smoltcp::socket::{Dhcpv4Event, Dhcpv4Socket, SocketHandle}; use smoltcp::time::Instant; -use smoltcp::wire::Ipv4Address; use super::*; use crate::device::LinkState; use crate::fmt::*; use crate::{Interface, SocketSet}; -pub struct DhcpResources { - rx_buffer: [u8; 900], - tx_buffer: [u8; 600], - rx_meta: [RawPacketMetadata; 1], - tx_meta: [RawPacketMetadata; 1], -} - pub struct DhcpConfigurator { - client: Option, + handle: Option, } impl DhcpConfigurator { pub fn new() -> Self { - Self { client: None } + Self { handle: None } } } -static DHCP_RESOURCES: Forever = Forever::new(); - impl Configurator for DhcpConfigurator { fn poll( &mut self, iface: &mut Interface, sockets: &mut SocketSet, - timestamp: Instant, - ) -> Option { - if self.client.is_none() { - let res = DHCP_RESOURCES.put(DhcpResources { - rx_buffer: [0; 900], - tx_buffer: [0; 600], - rx_meta: [RawPacketMetadata::EMPTY; 1], - tx_meta: [RawPacketMetadata::EMPTY; 1], - }); - let rx_buffer = RawSocketBuffer::new(&mut res.rx_meta[..], &mut res.rx_buffer[..]); - let tx_buffer = RawSocketBuffer::new(&mut res.tx_meta[..], &mut res.tx_buffer[..]); - let dhcp = Dhcpv4Client::new(sockets, rx_buffer, tx_buffer, timestamp); - info!("created dhcp"); - self.client = Some(dhcp) + _timestamp: Instant, + ) -> Event { + if self.handle.is_none() { + let handle = sockets.add(Dhcpv4Socket::new()); + self.handle = Some(handle) } - let client = self.client.as_mut().unwrap(); + let mut socket = sockets.get::(self.handle.unwrap()); let link_up = iface.device_mut().device.link_state() == LinkState::Up; if !link_up { - client.reset(timestamp); - return Some(Config::Down); + socket.reset(); + return Event::Deconfigured; } - let config = client.poll(iface, sockets, timestamp).unwrap_or(None)?; + match socket.poll() { + Dhcpv4Event::NoChange => Event::NoChange, + Dhcpv4Event::Deconfigured => Event::Deconfigured, + Dhcpv4Event::Configured(config) => { + let mut dns_servers = Vec::new(); + for s in &config.dns_servers { + if let Some(addr) = s { + dns_servers.push(addr.clone()).unwrap(); + } + } - if config.address.is_none() { - return Some(Config::Down); - } - - let mut dns_servers = Vec::new(); - for s in &config.dns_servers { - if let Some(addr) = s { - dns_servers.push(addr.clone()).unwrap(); + Event::Configured(Config { + address: config.address, + gateway: config.router, + dns_servers, + }) } } - - return Some(Config::Up(UpConfig { - address: config.address.unwrap(), - gateway: config.router.unwrap_or(Ipv4Address::UNSPECIFIED), - dns_servers, - })); } } diff --git a/embassy-net/src/config/mod.rs b/embassy-net/src/config/mod.rs index 596374f9..a090aaac 100644 --- a/embassy-net/src/config/mod.rs +++ b/embassy-net/src/config/mod.rs @@ -6,29 +6,33 @@ use smoltcp::wire::{Ipv4Address, Ipv4Cidr}; use crate::fmt::*; use crate::{Interface, SocketSet}; -mod dhcp; mod statik; -pub use dhcp::DhcpConfigurator; pub use statik::StaticConfigurator; +#[cfg(feature = "dhcpv4")] +mod dhcp; +#[cfg(feature = "dhcpv4")] +pub use dhcp::DhcpConfigurator; + +/// Return value for the `Configurator::poll` function #[derive(Debug, Clone)] -pub enum Config { - Down, - Up(UpConfig), +pub enum Event { + /// No change has occured to the configuration. + NoChange, + /// Configuration has been lost (for example, DHCP lease has expired) + Deconfigured, + /// Configuration has been newly acquired, or modified. + Configured(Config), } #[derive(Debug, Clone)] -pub struct UpConfig { +pub struct Config { pub address: Ipv4Cidr, - pub gateway: Ipv4Address, + pub gateway: Option, pub dns_servers: Vec, } pub trait Configurator { - fn poll( - &mut self, - iface: &mut Interface, - sockets: &mut SocketSet, - timestamp: Instant, - ) -> Option; + fn poll(&mut self, iface: &mut Interface, sockets: &mut SocketSet, timestamp: Instant) + -> Event; } diff --git a/embassy-net/src/config/statik.rs b/embassy-net/src/config/statik.rs index 52196f48..912143bf 100644 --- a/embassy-net/src/config/statik.rs +++ b/embassy-net/src/config/statik.rs @@ -5,12 +5,16 @@ use crate::fmt::*; use crate::{Interface, SocketSet}; pub struct StaticConfigurator { - config: UpConfig, + config: Config, + returned: bool, } impl StaticConfigurator { - pub fn new(config: UpConfig) -> Self { - Self { config } + pub fn new(config: Config) -> Self { + Self { + config, + returned: false, + } } } @@ -20,7 +24,12 @@ impl Configurator for StaticConfigurator { _iface: &mut Interface, _sockets: &mut SocketSet, _timestamp: Instant, - ) -> Option { - Some(Config::Up(self.config.clone())) + ) -> Event { + if self.returned { + Event::NoChange + } else { + self.returned = true; + Event::Configured(self.config.clone()) + } } } diff --git a/embassy-net/src/device.rs b/embassy-net/src/device.rs index 32b56e5b..6c06b060 100644 --- a/embassy-net/src/device.rs +++ b/embassy-net/src/device.rs @@ -4,6 +4,7 @@ use smoltcp::phy::DeviceCapabilities; use smoltcp::time::Instant as SmolInstant; use crate::fmt::*; +use crate::packet_pool::PacketBoxExt; use crate::Result; use crate::{Packet, PacketBox, PacketBuf}; diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index de2f2ea7..c79c38c0 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -7,18 +7,19 @@ // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; -mod pool; // TODO extract to embassy, or to own crate - mod config; mod device; mod packet_pool; mod stack; -mod tcp_socket; -pub use config::{Config, Configurator, DhcpConfigurator, StaticConfigurator, UpConfig}; +pub use config::{Config, Configurator, DhcpConfigurator, StaticConfigurator}; pub use device::{Device, LinkState}; -pub use packet_pool::{Packet, PacketBox, PacketBuf}; +pub use packet_pool::{Packet, PacketBox, PacketBoxExt, PacketBuf}; pub use stack::{init, is_init, run}; + +#[cfg(feature = "tcp")] +mod tcp_socket; +#[cfg(feature = "tcp")] pub use tcp_socket::TcpSocket; // smoltcp reexports @@ -28,4 +29,4 @@ pub use smoltcp::time::Instant as SmolInstant; pub use smoltcp::wire::{IpAddress, IpCidr, Ipv4Address, Ipv4Cidr}; pub type Interface = smoltcp::iface::Interface<'static, device::DeviceAdapter>; pub type SocketSet = smoltcp::socket::SocketSet<'static>; -pub use smoltcp::{Error, Result}; \ No newline at end of file +pub use smoltcp::{Error, Result}; diff --git a/embassy-net/src/packet_pool.rs b/embassy-net/src/packet_pool.rs index 24635643..2c27d401 100644 --- a/embassy-net/src/packet_pool.rs +++ b/embassy-net/src/packet_pool.rs @@ -1,7 +1,7 @@ use as_slice::{AsMutSlice, AsSlice}; use core::ops::{Deref, DerefMut, Range}; -use super::pool::{BitPool, Box, StaticPool}; +use atomic_pool::{pool, Box}; pub const MTU: usize = 1514; pub const PACKET_POOL_SIZE: usize = 4; @@ -17,8 +17,12 @@ impl Packet { } } -impl Box { - pub fn slice(self, range: Range) -> PacketBuf { +pub trait PacketBoxExt { + fn slice(self, range: Range) -> PacketBuf; +} + +impl PacketBoxExt for PacketBox { + fn slice(self, range: Range) -> PacketBuf { PacketBuf { packet: self, range, diff --git a/embassy-net/src/pool.rs b/embassy-net/src/pool.rs deleted file mode 100644 index 3ab36e4c..00000000 --- a/embassy-net/src/pool.rs +++ /dev/null @@ -1,245 +0,0 @@ -#![macro_use] - -use as_slice::{AsMutSlice, AsSlice}; -use core::cmp; -use core::fmt; -use core::hash::{Hash, Hasher}; -use core::mem::MaybeUninit; -use core::ops::{Deref, DerefMut}; -use core::sync::atomic::{AtomicU32, Ordering}; - -use crate::fmt::{assert, *}; - -struct AtomicBitset -where - [AtomicU32; (N + 31) / 32]: Sized, -{ - used: [AtomicU32; (N + 31) / 32], -} - -impl AtomicBitset -where - [AtomicU32; (N + 31) / 32]: Sized, -{ - const fn new() -> Self { - const Z: AtomicU32 = AtomicU32::new(0); - Self { - used: [Z; (N + 31) / 32], - } - } - - fn alloc(&self) -> Option { - for (i, val) in self.used.iter().enumerate() { - let res = val.fetch_update(Ordering::AcqRel, Ordering::Acquire, |val| { - let n = val.trailing_ones() as usize + i * 32; - if n >= N { - None - } else { - Some(val | (1 << n)) - } - }); - if let Ok(val) = res { - let n = val.trailing_ones() as usize + i * 32; - return Some(n); - } - } - None - } - fn free(&self, i: usize) { - assert!(i < N); - self.used[i / 32].fetch_and(!(1 << ((i % 32) as u32)), Ordering::AcqRel); - } -} - -pub trait Pool { - fn alloc(&self) -> Option<*mut T>; - unsafe fn free(&self, p: *mut T); -} - -pub struct BitPool -where - [AtomicU32; (N + 31) / 32]: Sized, -{ - used: AtomicBitset, - data: MaybeUninit<[T; N]>, -} - -impl BitPool -where - [AtomicU32; (N + 31) / 32]: Sized, -{ - pub const fn new() -> Self { - Self { - used: AtomicBitset::new(), - data: MaybeUninit::uninit(), - } - } -} - -impl Pool for BitPool -where - [AtomicU32; (N + 31) / 32]: Sized, -{ - fn alloc(&self) -> Option<*mut T> { - let n = self.used.alloc()?; - let origin = self.data.as_ptr() as *mut T; - Some(unsafe { origin.add(n) }) - } - - /// safety: p must be a pointer obtained from self.alloc that hasn't been freed yet. - unsafe fn free(&self, p: *mut T) { - let origin = self.data.as_ptr() as *mut T; - let n = p.offset_from(origin); - assert!(n >= 0); - assert!((n as usize) < N); - self.used.free(n as usize); - } -} - -pub trait StaticPool: 'static { - type Item: 'static; - type Pool: Pool; - fn get() -> &'static Self::Pool; -} - -pub struct Box { - ptr: *mut P::Item, -} - -impl Box

{ - pub fn new(item: P::Item) -> Option { - let p = match P::get().alloc() { - Some(p) => p, - None => { - warn!("alloc failed!"); - return None; - } - }; - //trace!("allocated {:u32}", p as u32); - unsafe { p.write(item) }; - Some(Self { ptr: p }) - } -} - -impl Drop for Box

{ - fn drop(&mut self) { - unsafe { - //trace!("dropping {:u32}", self.ptr as u32); - self.ptr.drop_in_place(); - P::get().free(self.ptr); - }; - } -} - -unsafe impl Send for Box

where P::Item: Send {} - -unsafe impl Sync for Box

where P::Item: Sync {} - -unsafe impl stable_deref_trait::StableDeref for Box

{} - -impl AsSlice for Box

-where - P::Item: AsSlice, -{ - type Element = ::Element; - - fn as_slice(&self) -> &[Self::Element] { - self.deref().as_slice() - } -} - -impl AsMutSlice for Box

-where - P::Item: AsMutSlice, -{ - fn as_mut_slice(&mut self) -> &mut [Self::Element] { - self.deref_mut().as_mut_slice() - } -} - -impl Deref for Box

{ - type Target = P::Item; - - fn deref(&self) -> &P::Item { - unsafe { &*self.ptr } - } -} - -impl DerefMut for Box

{ - fn deref_mut(&mut self) -> &mut P::Item { - unsafe { &mut *self.ptr } - } -} - -impl fmt::Debug for Box

-where - P::Item: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - ::fmt(self, f) - } -} - -impl fmt::Display for Box

-where - P::Item: fmt::Display, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - ::fmt(self, f) - } -} - -impl PartialEq for Box

-where - P::Item: PartialEq, -{ - fn eq(&self, rhs: &Box

) -> bool { - ::eq(self, rhs) - } -} - -impl Eq for Box

where P::Item: Eq {} - -impl PartialOrd for Box

-where - P::Item: PartialOrd, -{ - fn partial_cmp(&self, rhs: &Box

) -> Option { - ::partial_cmp(self, rhs) - } -} - -impl Ord for Box

-where - P::Item: Ord, -{ - fn cmp(&self, rhs: &Box

) -> cmp::Ordering { - ::cmp(self, rhs) - } -} - -impl Hash for Box

-where - P::Item: Hash, -{ - fn hash(&self, state: &mut H) - where - H: Hasher, - { - ::hash(self, state) - } -} - -macro_rules! pool { - ($vis:vis $name:ident: [$ty:ty; $size:expr]) => { - $vis struct $name; - impl StaticPool for $name { - type Item = $ty; - type Pool = BitPool<$ty, $size>; - fn get() -> &'static Self::Pool { - static POOL: BitPool<$ty, $size> = BitPool::new(); - &POOL - } - } - }; -} diff --git a/embassy-net/src/stack.rs b/embassy-net/src/stack.rs index f8a945a5..dc8043e6 100644 --- a/embassy-net/src/stack.rs +++ b/embassy-net/src/stack.rs @@ -11,14 +11,12 @@ use smoltcp::phy::Device as _; use smoltcp::phy::Medium; use smoltcp::socket::SocketSetItem; use smoltcp::time::Instant as SmolInstant; -use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address}; +use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address, Ipv4Cidr}; -use crate::device::{Device, DeviceAdapter}; +use crate::config::Configurator; +use crate::config::Event; +use crate::device::{Device, DeviceAdapter, LinkState}; use crate::fmt::*; -use crate::{ - config::{Config, Configurator}, - device::LinkState, -}; use crate::{Interface, SocketSet}; const ADDRESSES_LEN: usize = 1; @@ -68,39 +66,41 @@ impl Stack { } fn poll_configurator(&mut self, timestamp: SmolInstant) { - if let Some(config) = self + let medium = self.iface.device().capabilities().medium; + + match self .configurator .poll(&mut self.iface, &mut self.sockets, timestamp) { - let medium = self.iface.device().capabilities().medium; + Event::NoChange => {} + Event::Configured(config) => { + debug!("Acquired IP configuration:"); - let (addr, gateway) = match config { - Config::Up(config) => (config.address.into(), Some(config.gateway)), - Config::Down => (IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 32), None), - }; + debug!(" IP address: {}", config.address); + set_ipv4_addr(&mut self.iface, config.address); - self.iface.update_ip_addrs(|addrs| { - let curr_addr = &mut addrs[0]; - if *curr_addr != addr { - info!("IPv4 address: {:?} -> {:?}", *curr_addr, addr); - *curr_addr = addr; - } - }); - - if medium == Medium::Ethernet { - self.iface.routes_mut().update(|r| { - let cidr = IpCidr::new(IpAddress::v4(0, 0, 0, 0), 0); - let curr_gateway = r.get(&cidr).map(|r| r.via_router); - - if curr_gateway != gateway.map(|a| a.into()) { - info!("IPv4 gateway: {:?} -> {:?}", curr_gateway, gateway); - if let Some(gateway) = gateway { - r.insert(cidr, Route::new_ipv4_gateway(gateway)).unwrap(); - } else { - r.remove(&cidr); - } + if medium == Medium::Ethernet { + if let Some(gateway) = config.gateway { + debug!(" Default gateway: {}", gateway); + self.iface + .routes_mut() + .add_default_ipv4_route(gateway) + .unwrap(); + } else { + debug!(" Default gateway: None"); + self.iface.routes_mut().remove_default_ipv4_route(); } - }); + } + for (i, s) in config.dns_servers.iter().enumerate() { + debug!(" DNS server {}: {}", i, s); + } + } + Event::Deconfigured => { + debug!("Lost IP configuration"); + set_ipv4_addr(&mut self.iface, Ipv4Cidr::new(Ipv4Address::UNSPECIFIED, 0)); + if medium == Medium::Ethernet { + self.iface.routes_mut().remove_default_ipv4_route(); + } } } } @@ -143,6 +143,13 @@ impl Stack { } } +fn set_ipv4_addr(iface: &mut Interface, cidr: Ipv4Cidr) { + iface.update_ip_addrs(|addrs| { + let dest = addrs.iter_mut().next().unwrap(); + *dest = IpCidr::Ipv4(cidr); + }); +} + /// Initialize embassy_net. /// This function must be called from thread mode. pub fn init(device: &'static mut dyn Device, configurator: &'static mut dyn Configurator) {