#![no_std]
#![allow(async_fn_in_trait)]
#![allow(unsafe_op_in_unsafe_fn)]
#![warn(missing_docs)]
#![doc = include_str!("../README.md")]
#![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)]
#[cfg(not(any(feature = "proto-ipv4", feature = "proto-ipv6")))]
compile_error!("You must enable at least one of the following features: proto-ipv4, proto-ipv6");
pub(crate) mod fmt;
#[cfg(feature = "dns")]
pub mod dns;
mod driver_util;
#[cfg(feature = "icmp")]
pub mod icmp;
#[cfg(feature = "raw")]
pub mod raw;
#[cfg(feature = "tcp")]
pub mod tcp;
mod time;
#[cfg(feature = "udp")]
pub mod udp;
use core::cell::RefCell;
use core::future::{Future, poll_fn};
use core::mem::MaybeUninit;
use core::pin::pin;
use core::task::{Context, Poll};
pub use embassy_net_driver as driver;
use embassy_net_driver::{Driver, LinkState};
use embassy_sync::waitqueue::WakerRegistration;
use embassy_time::{Instant, Timer};
use heapless::Vec;
#[cfg(feature = "dns")]
pub use smoltcp::config::DNS_MAX_SERVER_COUNT;
#[cfg(feature = "multicast")]
pub use smoltcp::iface::MulticastError;
#[cfg(any(feature = "dns", feature = "dhcpv4"))]
use smoltcp::iface::SocketHandle;
use smoltcp::iface::{Interface, SocketSet, SocketStorage};
use smoltcp::phy::Medium;
#[cfg(feature = "dhcpv4")]
use smoltcp::socket::dhcpv4::{self, RetryConfig};
#[cfg(feature = "medium-ethernet")]
pub use smoltcp::wire::EthernetAddress;
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154", feature = "medium-ip"))]
pub use smoltcp::wire::HardwareAddress;
#[cfg(any(feature = "udp", feature = "tcp"))]
pub use smoltcp::wire::IpListenEndpoint;
#[cfg(feature = "medium-ieee802154")]
pub use smoltcp::wire::{Ieee802154Address, Ieee802154Frame};
pub use smoltcp::wire::{IpAddress, IpCidr, IpEndpoint};
#[cfg(feature = "proto-ipv4")]
pub use smoltcp::wire::{Ipv4Address, Ipv4Cidr};
#[cfg(feature = "proto-ipv6")]
pub use smoltcp::wire::{Ipv6Address, Ipv6Cidr};
use crate::driver_util::DriverAdapter;
use crate::time::{instant_from_smoltcp, instant_to_smoltcp};
const LOCAL_PORT_MIN: u16 = 1025;
const LOCAL_PORT_MAX: u16 = 65535;
#[cfg(feature = "dns")]
const MAX_QUERIES: usize = 4;
#[cfg(feature = "dhcpv4-hostname")]
const MAX_HOSTNAME_LEN: usize = 32;
pub struct StackResources<const SOCK: usize> {
sockets: MaybeUninit<[SocketStorage<'static>; SOCK]>,
inner: MaybeUninit<RefCell<Inner>>,
#[cfg(feature = "dns")]
queries: MaybeUninit<[Option<dns::DnsQuery>; MAX_QUERIES]>,
#[cfg(feature = "dhcpv4-hostname")]
hostname: HostnameResources,
}
#[cfg(feature = "dhcpv4-hostname")]
struct HostnameResources {
option: MaybeUninit<smoltcp::wire::DhcpOption<'static>>,
data: MaybeUninit<[u8; MAX_HOSTNAME_LEN]>,
}
impl<const SOCK: usize> StackResources<SOCK> {
pub const fn new() -> Self {
Self {
sockets: MaybeUninit::uninit(),
inner: MaybeUninit::uninit(),
#[cfg(feature = "dns")]
queries: MaybeUninit::uninit(),
#[cfg(feature = "dhcpv4-hostname")]
hostname: HostnameResources {
option: MaybeUninit::uninit(),
data: MaybeUninit::uninit(),
},
}
}
}
#[cfg(feature = "proto-ipv4")]
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct StaticConfigV4 {
pub address: Ipv4Cidr,
pub gateway: Option<Ipv4Address>,
pub dns_servers: Vec<Ipv4Address, 3>,
}
#[cfg(feature = "proto-ipv6")]
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct StaticConfigV6 {
pub address: Ipv6Cidr,
pub gateway: Option<Ipv6Address>,
pub dns_servers: Vec<Ipv6Address, 3>,
}
#[cfg(feature = "dhcpv4")]
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub struct DhcpConfig {
pub max_lease_duration: Option<embassy_time::Duration>,
pub retry_config: RetryConfig,
pub ignore_naks: bool,
pub server_port: u16,
pub client_port: u16,
#[cfg(feature = "dhcpv4-hostname")]
pub hostname: Option<heapless::String<MAX_HOSTNAME_LEN>>,
}
#[cfg(feature = "dhcpv4")]
impl Default for DhcpConfig {
fn default() -> Self {
Self {
max_lease_duration: Default::default(),
retry_config: Default::default(),
ignore_naks: Default::default(),
server_port: smoltcp::wire::DHCP_SERVER_PORT,
client_port: smoltcp::wire::DHCP_CLIENT_PORT,
#[cfg(feature = "dhcpv4-hostname")]
hostname: None,
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub struct Config {
#[cfg(feature = "proto-ipv4")]
pub ipv4: ConfigV4,
#[cfg(feature = "proto-ipv6")]
pub ipv6: ConfigV6,
}
impl Config {
#[cfg(feature = "proto-ipv4")]
pub const fn ipv4_static(config: StaticConfigV4) -> Self {
Self {
ipv4: ConfigV4::Static(config),
#[cfg(feature = "proto-ipv6")]
ipv6: ConfigV6::None,
}
}
#[cfg(feature = "proto-ipv6")]
pub const fn ipv6_static(config: StaticConfigV6) -> Self {
Self {
#[cfg(feature = "proto-ipv4")]
ipv4: ConfigV4::None,
ipv6: ConfigV6::Static(config),
}
}
#[cfg(feature = "dhcpv4")]
pub const fn dhcpv4(config: DhcpConfig) -> Self {
Self {
ipv4: ConfigV4::Dhcp(config),
#[cfg(feature = "proto-ipv6")]
ipv6: ConfigV6::None,
}
}
#[cfg(feature = "slaac")]
pub const fn slaac() -> Self {
Self {
#[cfg(feature = "proto-ipv4")]
ipv4: ConfigV4::None,
ipv6: ConfigV6::Slaac,
}
}
}
#[cfg(feature = "proto-ipv4")]
#[derive(Debug, Clone, Default, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ConfigV4 {
#[default]
None,
Static(StaticConfigV4),
#[cfg(feature = "dhcpv4")]
Dhcp(DhcpConfig),
}
#[cfg(feature = "proto-ipv6")]
#[derive(Debug, Clone, Default, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ConfigV6 {
#[default]
None,
Static(StaticConfigV6),
#[cfg(feature = "slaac")]
Slaac,
}
pub struct Runner<'d, D: Driver> {
driver: D,
stack: Stack<'d>,
}
#[derive(Copy, Clone)]
pub struct Stack<'d> {
inner: &'d RefCell<Inner>,
}
pub(crate) struct Inner {
pub(crate) sockets: SocketSet<'static>, pub(crate) iface: Interface,
pub(crate) waker: WakerRegistration,
state_waker: WakerRegistration,
hardware_address: HardwareAddress,
next_local_port: u16,
link_up: bool,
#[cfg(feature = "proto-ipv4")]
static_v4: Option<StaticConfigV4>,
#[cfg(feature = "proto-ipv6")]
static_v6: Option<StaticConfigV6>,
#[cfg(feature = "slaac")]
slaac: bool,
#[cfg(feature = "dhcpv4")]
dhcp_socket: Option<SocketHandle>,
#[cfg(feature = "dns")]
dns_socket: SocketHandle,
#[cfg(feature = "dns")]
dns_waker: WakerRegistration,
#[cfg(feature = "dhcpv4-hostname")]
hostname: *mut HostnameResources,
}
fn _assert_covariant<'a, 'b: 'a>(x: Stack<'b>) -> Stack<'a> {
x
}
pub fn new<'d, D: Driver, const SOCK: usize>(
mut driver: D,
config: Config,
resources: &'d mut StackResources<SOCK>,
random_seed: u64,
) -> (Stack<'d>, Runner<'d, D>) {
let (hardware_address, medium) = to_smoltcp_hardware_address(driver.hardware_address());
let mut iface_cfg = smoltcp::iface::Config::new(hardware_address);
iface_cfg.random_seed = random_seed;
#[cfg(feature = "slaac")]
{
iface_cfg.slaac = matches!(config.ipv6, ConfigV6::Slaac);
}
#[allow(unused_mut)]
let mut iface = Interface::new(
iface_cfg,
&mut DriverAdapter {
inner: &mut driver,
cx: None,
medium,
tx_exhausted: false,
},
instant_to_smoltcp(Instant::now()),
);
unsafe fn transmute_slice<T>(x: &mut [T]) -> &'static mut [T] {
core::mem::transmute(x)
}
let sockets = resources.sockets.write([SocketStorage::EMPTY; SOCK]);
#[allow(unused_mut)]
let mut sockets: SocketSet<'static> = SocketSet::new(unsafe { transmute_slice(sockets) });
let next_local_port = (random_seed % (LOCAL_PORT_MAX - LOCAL_PORT_MIN) as u64) as u16 + LOCAL_PORT_MIN;
#[cfg(feature = "dns")]
let dns_socket = sockets.add(dns::Socket::new(
&[],
managed::ManagedSlice::Borrowed(unsafe {
transmute_slice(resources.queries.write([const { None }; MAX_QUERIES]))
}),
));
let mut inner = Inner {
sockets,
iface,
waker: WakerRegistration::new(),
state_waker: WakerRegistration::new(),
next_local_port,
hardware_address,
link_up: false,
#[cfg(feature = "proto-ipv4")]
static_v4: None,
#[cfg(feature = "proto-ipv6")]
static_v6: None,
#[cfg(feature = "slaac")]
slaac: false,
#[cfg(feature = "dhcpv4")]
dhcp_socket: None,
#[cfg(feature = "dns")]
dns_socket,
#[cfg(feature = "dns")]
dns_waker: WakerRegistration::new(),
#[cfg(feature = "dhcpv4-hostname")]
hostname: &mut resources.hostname,
};
#[cfg(feature = "proto-ipv4")]
inner.set_config_v4(config.ipv4);
#[cfg(feature = "proto-ipv6")]
inner.set_config_v6(config.ipv6);
inner.apply_static_config();
let inner = &*resources.inner.write(RefCell::new(inner));
let stack = Stack { inner };
(stack, Runner { driver, stack })
}
fn to_smoltcp_hardware_address(addr: driver::HardwareAddress) -> (HardwareAddress, Medium) {
match addr {
#[cfg(feature = "medium-ethernet")]
driver::HardwareAddress::Ethernet(eth) => (HardwareAddress::Ethernet(EthernetAddress(eth)), Medium::Ethernet),
#[cfg(feature = "medium-ieee802154")]
driver::HardwareAddress::Ieee802154(ieee) => (
HardwareAddress::Ieee802154(Ieee802154Address::Extended(ieee)),
Medium::Ieee802154,
),
#[cfg(feature = "medium-ip")]
driver::HardwareAddress::Ip => (HardwareAddress::Ip, Medium::Ip),
#[allow(unreachable_patterns)]
_ => panic!(
"Unsupported medium {:?}. Make sure to enable the right medium feature in embassy-net's Cargo features.",
addr
),
}
}
impl<'d> Stack<'d> {
fn with<R>(&self, f: impl FnOnce(&Inner) -> R) -> R {
f(&self.inner.borrow())
}
fn with_mut<R>(&self, f: impl FnOnce(&mut Inner) -> R) -> R {
f(&mut self.inner.borrow_mut())
}
pub fn hardware_address(&self) -> HardwareAddress {
self.with(|i| i.hardware_address)
}
pub fn is_link_up(&self) -> bool {
self.with(|i| i.link_up)
}
pub fn is_config_up(&self) -> bool {
let v4_up;
let v6_up;
#[cfg(feature = "proto-ipv4")]
{
v4_up = self.config_v4().is_some();
}
#[cfg(not(feature = "proto-ipv4"))]
{
v4_up = false;
}
#[cfg(feature = "proto-ipv6")]
{
v6_up = self.config_v6().is_some();
}
#[cfg(not(feature = "proto-ipv6"))]
{
v6_up = false;
}
v4_up || v6_up
}
pub async fn wait_link_up(&self) {
self.wait(|| self.is_link_up()).await
}
pub async fn wait_link_down(&self) {
self.wait(|| !self.is_link_up()).await
}
pub async fn wait_config_up(&self) {
self.wait(|| self.is_config_up()).await
}
pub async fn wait_config_down(&self) {
self.wait(|| !self.is_config_up()).await
}
fn wait<'a>(&'a self, mut predicate: impl FnMut() -> bool + 'a) -> impl Future<Output = ()> + 'a {
poll_fn(move |cx| {
if predicate() {
Poll::Ready(())
} else {
trace!("Waiting for config up");
self.with_mut(|i| {
i.state_waker.register(cx.waker());
});
Poll::Pending
}
})
}
#[cfg(feature = "proto-ipv4")]
pub fn config_v4(&self) -> Option<StaticConfigV4> {
self.with(|i| i.static_v4.clone())
}
#[cfg(feature = "proto-ipv6")]
pub fn config_v6(&self) -> Option<StaticConfigV6> {
self.with(|i| i.static_v6.clone())
}
#[cfg(feature = "proto-ipv4")]
pub fn set_config_v4(&self, config: ConfigV4) {
self.with_mut(|i| {
i.set_config_v4(config);
i.apply_static_config();
})
}
#[cfg(feature = "proto-ipv6")]
pub fn set_config_v6(&self, config: ConfigV6) {
self.with_mut(|i| {
i.set_config_v6(config);
i.apply_static_config();
})
}
#[cfg(feature = "dns")]
pub async fn dns_query(
&self,
name: &str,
qtype: dns::DnsQueryType,
) -> Result<Vec<IpAddress, { smoltcp::config::DNS_MAX_RESULT_COUNT }>, dns::Error> {
match qtype {
#[cfg(feature = "proto-ipv4")]
dns::DnsQueryType::A => {
if let Ok(ip) = name.parse().map(IpAddress::Ipv4) {
return Ok([ip].into_iter().collect());
}
}
#[cfg(feature = "proto-ipv6")]
dns::DnsQueryType::Aaaa => {
if let Ok(ip) = name.parse().map(IpAddress::Ipv6) {
return Ok([ip].into_iter().collect());
}
}
_ => {}
}
let query = poll_fn(|cx| {
self.with_mut(|i| {
let socket = i.sockets.get_mut::<dns::Socket>(i.dns_socket);
match socket.start_query(i.iface.context(), name, qtype) {
Ok(handle) => {
i.waker.wake();
Poll::Ready(Ok(handle))
}
Err(dns::StartQueryError::NoFreeSlot) => {
i.dns_waker.register(cx.waker());
Poll::Pending
}
Err(e) => Poll::Ready(Err(e)),
}
})
})
.await?;
#[must_use = "to delay the drop handler invocation to the end of the scope"]
struct OnDrop<F: FnOnce()> {
f: core::mem::MaybeUninit<F>,
}
impl<F: FnOnce()> OnDrop<F> {
fn new(f: F) -> Self {
Self {
f: core::mem::MaybeUninit::new(f),
}
}
fn defuse(self) {
core::mem::forget(self)
}
}
impl<F: FnOnce()> Drop for OnDrop<F> {
fn drop(&mut self) {
unsafe { self.f.as_ptr().read()() }
}
}
let drop = OnDrop::new(|| {
self.with_mut(|i| {
let socket = i.sockets.get_mut::<dns::Socket>(i.dns_socket);
socket.cancel_query(query);
i.waker.wake();
i.dns_waker.wake();
})
});
let res = poll_fn(|cx| {
self.with_mut(|i| {
let socket = i.sockets.get_mut::<dns::Socket>(i.dns_socket);
match socket.get_query_result(query) {
Ok(addrs) => {
i.dns_waker.wake();
Poll::Ready(Ok(addrs))
}
Err(dns::GetQueryResultError::Pending) => {
socket.register_query_waker(query, cx.waker());
Poll::Pending
}
Err(e) => {
i.dns_waker.wake();
Poll::Ready(Err(e.into()))
}
}
})
})
.await;
drop.defuse();
res
}
}
#[cfg(feature = "multicast")]
impl<'d> Stack<'d> {
pub fn join_multicast_group(&self, addr: impl Into<IpAddress>) -> Result<(), MulticastError> {
self.with_mut(|i| i.iface.join_multicast_group(addr))
}
pub fn leave_multicast_group(&self, addr: impl Into<IpAddress>) -> Result<(), MulticastError> {
self.with_mut(|i| i.iface.leave_multicast_group(addr))
}
pub fn has_multicast_group(&self, addr: impl Into<IpAddress>) -> bool {
self.with(|i| i.iface.has_multicast_group(addr))
}
}
impl Inner {
#[cfg(feature = "slaac")]
fn get_link_local_address(&self) -> IpCidr {
let ll_prefix = Ipv6Cidr::new(Ipv6Cidr::LINK_LOCAL_PREFIX.address(), 64);
Ipv6Cidr::from_link_prefix(&ll_prefix, self.hardware_address)
.unwrap()
.into()
}
#[allow(clippy::absurd_extreme_comparisons)]
pub fn get_local_port(&mut self) -> u16 {
let res = self.next_local_port;
self.next_local_port = if res >= LOCAL_PORT_MAX { LOCAL_PORT_MIN } else { res + 1 };
res
}
#[cfg(feature = "proto-ipv4")]
pub fn set_config_v4(&mut self, config: ConfigV4) {
self.static_v4 = match config.clone() {
ConfigV4::None => None,
#[cfg(feature = "dhcpv4")]
ConfigV4::Dhcp(_) => None,
ConfigV4::Static(c) => Some(c),
};
#[cfg(feature = "dhcpv4")]
match config {
ConfigV4::Dhcp(c) => {
if self.dhcp_socket.is_none() {
let socket = smoltcp::socket::dhcpv4::Socket::new();
let handle = self.sockets.add(socket);
self.dhcp_socket = Some(handle);
}
let socket = self.sockets.get_mut::<dhcpv4::Socket>(unwrap!(self.dhcp_socket));
socket.set_ignore_naks(c.ignore_naks);
socket.set_max_lease_duration(c.max_lease_duration.map(crate::time::duration_to_smoltcp));
socket.set_ports(c.server_port, c.client_port);
socket.set_retry_config(c.retry_config);
socket.set_outgoing_options(&[]);
#[cfg(feature = "dhcpv4-hostname")]
if let Some(h) = c.hostname {
let hostname = unsafe { &mut *self.hostname };
let data = hostname.data.write([0; MAX_HOSTNAME_LEN]);
data[..h.len()].copy_from_slice(h.as_bytes());
let data: &[u8] = &data[..h.len()];
let option = hostname.option.write(smoltcp::wire::DhcpOption { data, kind: 12 });
socket.set_outgoing_options(core::slice::from_ref(option));
}
socket.reset();
}
_ => {
if let Some(socket) = self.dhcp_socket {
self.sockets.remove(socket);
self.dhcp_socket = None;
}
}
}
}
#[cfg(feature = "proto-ipv6")]
pub fn set_config_v6(&mut self, config: ConfigV6) {
#[cfg(feature = "slaac")]
{
self.slaac = matches!(config, ConfigV6::Slaac);
}
self.static_v6 = match config {
ConfigV6::None => None,
ConfigV6::Static(c) => Some(c),
#[cfg(feature = "slaac")]
ConfigV6::Slaac => None,
};
}
fn apply_static_config(&mut self) {
let mut addrs = Vec::new();
#[cfg(feature = "dns")]
let mut dns_servers: Vec<_, 6> = Vec::new();
#[cfg(feature = "proto-ipv4")]
let mut gateway_v4 = None;
#[cfg(feature = "proto-ipv6")]
let mut gateway_v6 = None;
#[cfg(feature = "proto-ipv4")]
if let Some(config) = &self.static_v4 {
debug!("IPv4: UP");
debug!(" IP address: {:?}", config.address);
debug!(" Default gateway: {:?}", config.gateway);
unwrap!(addrs.push(IpCidr::Ipv4(config.address)).ok());
gateway_v4 = config.gateway;
#[cfg(feature = "dns")]
for s in &config.dns_servers {
debug!(" DNS server: {:?}", s);
unwrap!(dns_servers.push(s.clone().into()).ok());
}
} else {
info!("IPv4: DOWN");
}
#[cfg(feature = "proto-ipv6")]
if let Some(config) = &self.static_v6 {
debug!("IPv6: UP");
debug!(" IP address: {:?}", config.address);
debug!(" Default gateway: {:?}", config.gateway);
unwrap!(addrs.push(IpCidr::Ipv6(config.address)).ok());
gateway_v6 = config.gateway;
#[cfg(feature = "dns")]
for s in &config.dns_servers {
debug!(" DNS server: {:?}", s);
unwrap!(dns_servers.push(s.clone().into()).ok());
}
} else {
info!("IPv6: DOWN");
}
self.iface.update_ip_addrs(|a| {
*a = addrs;
});
#[cfg(feature = "slaac")]
{
let ll_address = self.get_link_local_address();
self.iface.update_ip_addrs(|a| {
let _ = a.push(ll_address);
})
}
#[cfg(feature = "proto-ipv4")]
if let Some(gateway) = gateway_v4 {
unwrap!(self.iface.routes_mut().add_default_ipv4_route(gateway));
} else {
self.iface.routes_mut().remove_default_ipv4_route();
}
#[cfg(feature = "proto-ipv6")]
if let Some(gateway) = gateway_v6 {
unwrap!(self.iface.routes_mut().add_default_ipv6_route(gateway));
} else {
self.iface.routes_mut().remove_default_ipv6_route();
}
#[cfg(feature = "dns")]
if !dns_servers.is_empty() {
let count = if dns_servers.len() > DNS_MAX_SERVER_COUNT {
warn!("Number of DNS servers exceeds DNS_MAX_SERVER_COUNT, truncating list.");
DNS_MAX_SERVER_COUNT
} else {
dns_servers.len()
};
self.sockets
.get_mut::<smoltcp::socket::dns::Socket>(self.dns_socket)
.update_servers(&dns_servers[..count]);
}
self.state_waker.wake();
}
fn poll<D: Driver>(&mut self, cx: &mut Context<'_>, driver: &mut D) {
self.waker.register(cx.waker());
let (_hardware_addr, medium) = to_smoltcp_hardware_address(driver.hardware_address());
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
{
let do_set = match medium {
#[cfg(feature = "medium-ethernet")]
Medium::Ethernet => true,
#[cfg(feature = "medium-ieee802154")]
Medium::Ieee802154 => true,
#[allow(unreachable_patterns)]
_ => false,
};
if do_set {
self.iface.set_hardware_addr(_hardware_addr);
}
}
let timestamp = instant_to_smoltcp(Instant::now());
let mut smoldev = DriverAdapter {
cx: Some(cx),
inner: driver,
medium,
tx_exhausted: false,
};
self.iface.poll(timestamp, &mut smoldev, &mut self.sockets);
let tx_exhausted = smoldev.tx_exhausted;
let old_link_up = self.link_up;
self.link_up = driver.link_state(cx) == LinkState::Up;
if old_link_up != self.link_up {
info!("link_up = {:?}", self.link_up);
self.state_waker.wake();
}
#[allow(unused_mut)]
let mut configure = false;
#[cfg(feature = "dhcpv4")]
{
configure |= if let Some(dhcp_handle) = self.dhcp_socket {
let socket = self.sockets.get_mut::<dhcpv4::Socket>(dhcp_handle);
if self.link_up {
if old_link_up != self.link_up {
socket.reset();
}
match socket.poll() {
None => false,
Some(dhcpv4::Event::Deconfigured) => {
self.static_v4 = None;
true
}
Some(dhcpv4::Event::Configured(config)) => {
self.static_v4 = Some(StaticConfigV4 {
address: config.address,
gateway: config.router,
dns_servers: config.dns_servers,
});
true
}
}
} else if old_link_up {
socket.reset();
self.static_v4 = None;
true
} else {
false
}
} else {
false
}
}
#[cfg(feature = "slaac")]
if self.slaac && self.iface.slaac_updated_at() == timestamp {
let ipv6_address = self.iface.ip_addrs().iter().find_map(|addr| match addr {
IpCidr::Ipv6(ip6_address) if !Ipv6Cidr::LINK_LOCAL_PREFIX.contains_addr(&ip6_address.address()) => {
Some(ip6_address)
}
_ => None,
});
self.static_v6 = if let Some(address) = ipv6_address {
let gateway = self
.iface
.routes()
.get_default_ipv6_route()
.map(|r| match r.via_router {
IpAddress::Ipv6(gateway) => Some(gateway),
#[cfg(feature = "proto-ipv4")]
_ => None,
})
.flatten();
let config = StaticConfigV6 {
address: *address,
gateway,
dns_servers: Vec::new(), };
Some(config)
} else {
None
};
configure = true;
}
if configure {
self.apply_static_config()
}
if let Some(poll_at) = self.iface.poll_at(timestamp, &mut self.sockets)
&& !tx_exhausted
{
let t = pin!(Timer::at(instant_from_smoltcp(poll_at)));
if t.poll(cx).is_ready() {
cx.waker().wake_by_ref();
}
}
}
}
impl<'d, D: Driver> Runner<'d, D> {
pub async fn run(&mut self) -> ! {
poll_fn(|cx| {
self.stack.with_mut(|i| i.poll(cx, &mut self.driver));
Poll::<()>::Pending
})
.await;
unreachable!()
}
}