2021-02-03 05:09:37 +01:00
#![ cfg_attr(not(feature = " std " ), no_std) ]
2023-05-14 23:44:53 +02:00
#![ cfg_attr(feature = " nightly " , feature(async_fn_in_trait, impl_trait_projections)) ]
2023-05-15 00:39:57 +02:00
#![ warn(missing_docs) ]
#![ doc = include_str!( " ../README.md " ) ]
2021-02-03 05:09:37 +01:00
// This mod MUST go first, so that the others see its macros.
pub ( crate ) mod fmt ;
2023-02-08 17:52:02 +01:00
mod device ;
2023-01-31 22:06:41 +01:00
#[ cfg(feature = " dns " ) ]
pub mod dns ;
2021-04-07 19:06:45 +02:00
#[ cfg(feature = " tcp " ) ]
2022-05-04 20:48:37 +02:00
pub mod tcp ;
2023-05-15 00:38:58 +02:00
mod time ;
2022-07-28 10:25:47 +02:00
#[ cfg(feature = " udp " ) ]
pub mod udp ;
2022-12-07 16:02:28 +01:00
use core ::cell ::RefCell ;
use core ::future ::{ poll_fn , Future } ;
use core ::task ::{ Context , Poll } ;
2023-05-15 00:39:57 +02:00
pub use embassy_net_driver as driver ;
2022-12-26 03:33:49 +01:00
use embassy_net_driver ::{ Driver , LinkState , Medium } ;
2022-12-07 16:02:28 +01:00
use embassy_sync ::waitqueue ::WakerRegistration ;
use embassy_time ::{ Instant , Timer } ;
use futures ::pin_mut ;
2023-07-16 02:02:04 +02:00
#[ allow(unused_imports) ]
2022-12-07 16:02:28 +01:00
use heapless ::Vec ;
2023-05-15 00:39:57 +02:00
#[ cfg(feature = " igmp " ) ]
pub use smoltcp ::iface ::MulticastError ;
2023-07-16 02:02:04 +02:00
#[ allow(unused_imports) ]
2023-04-18 22:11:15 +02:00
use smoltcp ::iface ::{ Interface , SocketHandle , SocketSet , SocketStorage } ;
2022-12-07 16:02:28 +01:00
#[ cfg(feature = " dhcpv4 " ) ]
2023-04-18 22:11:15 +02:00
use smoltcp ::socket ::dhcpv4 ::{ self , RetryConfig } ;
2023-07-28 14:52:03 +02:00
#[ cfg(feature = " medium-ethernet " ) ]
pub use smoltcp ::wire ::EthernetAddress ;
#[ cfg(any(feature = " medium-ethernet " , feature = " medium-ieee802154 " )) ]
pub use smoltcp ::wire ::HardwareAddress ;
2023-05-15 00:55:34 +02:00
#[ cfg(feature = " udp " ) ]
pub use smoltcp ::wire ::IpListenEndpoint ;
2023-07-16 02:02:04 +02:00
#[ cfg(feature = " medium-ieee802154 " ) ]
2023-07-28 14:52:03 +02:00
pub use smoltcp ::wire ::{ Ieee802154Address , Ieee802154Frame } ;
2023-07-07 16:38:56 +02:00
pub use smoltcp ::wire ::{ IpAddress , IpCidr , IpEndpoint } ;
2023-06-05 16:00:53 +02:00
#[ cfg(feature = " proto-ipv4 " ) ]
pub use smoltcp ::wire ::{ Ipv4Address , Ipv4Cidr } ;
2022-06-01 13:48:09 +02:00
#[ cfg(feature = " proto-ipv6 " ) ]
pub use smoltcp ::wire ::{ Ipv6Address , Ipv6Cidr } ;
2022-12-07 16:02:28 +01:00
2022-12-26 03:33:49 +01:00
use crate ::device ::DriverAdapter ;
2023-05-15 00:38:58 +02:00
use crate ::time ::{ instant_from_smoltcp , instant_to_smoltcp } ;
2022-12-07 16:02:28 +01:00
const LOCAL_PORT_MIN : u16 = 1025 ;
const LOCAL_PORT_MAX : u16 = 65535 ;
2023-02-10 18:34:21 +01:00
#[ cfg(feature = " dns " ) ]
const MAX_QUERIES : usize = 4 ;
2022-12-07 16:02:28 +01:00
2023-05-15 00:39:57 +02:00
/// Memory resources needed for a network stack.
2023-01-18 10:10:33 +01:00
pub struct StackResources < const SOCK : usize > {
2022-12-07 16:02:28 +01:00
sockets : [ SocketStorage < 'static > ; SOCK ] ,
2023-02-10 17:43:23 +01:00
#[ cfg(feature = " dns " ) ]
2023-02-10 18:30:17 +01:00
queries : [ Option < dns ::DnsQuery > ; MAX_QUERIES ] ,
2022-12-07 16:02:28 +01:00
}
2023-01-18 10:10:33 +01:00
impl < const SOCK : usize > StackResources < SOCK > {
2023-05-15 00:39:57 +02:00
/// Create a new set of stack resources.
2023-06-28 15:03:57 +02:00
pub const fn new ( ) -> Self {
2023-02-10 17:43:23 +01:00
#[ cfg(feature = " dns " ) ]
const INIT : Option < dns ::DnsQuery > = None ;
2022-12-07 16:02:28 +01:00
Self {
sockets : [ SocketStorage ::EMPTY ; SOCK ] ,
2023-02-10 17:43:23 +01:00
#[ cfg(feature = " dns " ) ]
2023-02-10 18:30:17 +01:00
queries : [ INIT ; MAX_QUERIES ] ,
2022-12-07 16:02:28 +01:00
}
}
}
2023-05-15 00:39:57 +02:00
/// Static IP address configuration.
2023-06-05 16:00:53 +02:00
#[ cfg(feature = " proto-ipv4 " ) ]
2022-12-07 16:02:28 +01:00
#[ derive(Debug, Clone, PartialEq, Eq) ]
2023-06-05 14:57:17 +02:00
pub struct StaticConfigV4 {
2023-05-15 00:39:57 +02:00
/// IP address and subnet mask.
2022-12-07 16:02:28 +01:00
pub address : Ipv4Cidr ,
2023-05-15 00:39:57 +02:00
/// Default gateway.
2022-12-07 16:02:28 +01:00
pub gateway : Option < Ipv4Address > ,
2023-05-15 00:39:57 +02:00
/// DNS servers.
2022-12-07 16:02:28 +01:00
pub dns_servers : Vec < Ipv4Address , 3 > ,
}
2023-06-05 16:12:24 +02:00
/// Static IPv6 address configuration
#[ cfg(feature = " proto-ipv6 " ) ]
#[ derive(Debug, Clone, PartialEq, Eq) ]
pub struct StaticConfigV6 {
/// IP address and subnet mask.
pub address : Ipv6Cidr ,
/// Default gateway.
pub gateway : Option < Ipv6Address > ,
/// DNS servers.
pub dns_servers : Vec < Ipv6Address , 3 > ,
}
2023-05-15 00:39:57 +02:00
/// DHCP configuration.
2023-04-18 22:11:15 +02:00
#[ cfg(feature = " dhcpv4 " ) ]
2023-01-18 10:10:33 +01:00
#[ derive(Debug, Clone, PartialEq, Eq) ]
pub struct DhcpConfig {
2023-05-15 00:39:57 +02:00
/// Maximum lease duration.
///
/// If not set, the lease duration specified by the server will be used.
/// If set, the lease duration will be capped at this value.
2023-05-15 00:38:58 +02:00
pub max_lease_duration : Option < embassy_time ::Duration > ,
2023-05-15 00:39:57 +02:00
/// Retry configuration.
2023-01-18 10:10:33 +01:00
pub retry_config : RetryConfig ,
2023-05-15 00:39:57 +02:00
/// Ignore NAKs from DHCP servers.
///
/// This is not compliant with the DHCP RFCs, since theoretically we must stop using the assigned IP when receiving a NAK. This can increase reliability on broken networks with buggy routers or rogue DHCP servers, however.
2023-01-18 10:10:33 +01:00
pub ignore_naks : bool ,
2023-05-15 00:39:57 +02:00
/// Server port. This is almost always 67. Do not change unless you know what you're doing.
2023-01-18 10:10:33 +01:00
pub server_port : u16 ,
2023-05-15 00:39:57 +02:00
/// Client port. This is almost always 68. Do not change unless you know what you're doing.
2023-01-18 10:10:33 +01:00
pub client_port : u16 ,
}
2023-04-18 22:11:15 +02:00
#[ cfg(feature = " dhcpv4 " ) ]
2023-01-18 10:10:33 +01:00
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 ,
}
}
}
2023-05-15 00:39:57 +02:00
/// Network stack configuration.
2023-06-07 12:04:15 +02:00
pub struct Config {
2023-06-07 12:08:53 +02:00
/// IPv4 configuration
2023-06-05 16:00:53 +02:00
#[ cfg(feature = " proto-ipv4 " ) ]
2023-06-07 12:04:15 +02:00
pub ipv4 : ConfigV4 ,
2023-06-07 12:08:53 +02:00
/// IPv6 configuration
2023-06-07 12:04:15 +02:00
#[ cfg(feature = " proto-ipv6 " ) ]
pub ipv6 : ConfigV6 ,
}
impl Config {
2023-06-07 12:08:53 +02:00
/// IPv4 configuration with static addressing.
2023-06-07 12:04:15 +02:00
#[ cfg(feature = " proto-ipv4 " ) ]
pub fn ipv4_static ( config : StaticConfigV4 ) -> Self {
Self {
ipv4 : ConfigV4 ::Static ( config ) ,
#[ cfg(feature = " proto-ipv6 " ) ]
ipv6 : ConfigV6 ::None ,
}
}
2023-06-07 12:08:53 +02:00
/// IPv6 configuration with static addressing.
2023-06-05 16:12:24 +02:00
#[ cfg(feature = " proto-ipv6 " ) ]
2023-06-07 12:04:15 +02:00
pub fn ipv6_static ( config : StaticConfigV6 ) -> Self {
Self {
#[ cfg(feature = " proto-ipv4 " ) ]
ipv4 : ConfigV4 ::None ,
ipv6 : ConfigV6 ::Static ( config ) ,
}
}
2023-06-07 12:08:53 +02:00
/// IPv6 configuration with dynamic addressing.
///
/// # Example
/// ```rust
/// let _cfg = Config::dhcpv4(Default::default());
/// ```
2023-06-07 12:04:15 +02:00
#[ cfg(feature = " dhcpv4 " ) ]
pub fn dhcpv4 ( config : DhcpConfig ) -> Self {
Self {
ipv4 : ConfigV4 ::Dhcp ( config ) ,
#[ cfg(feature = " proto-ipv6 " ) ]
ipv6 : ConfigV6 ::None ,
}
}
}
/// Network stack IPv4 configuration.
#[ cfg(feature = " proto-ipv4 " ) ]
pub enum ConfigV4 {
/// Use a static IPv4 address configuration.
Static ( StaticConfigV4 ) ,
2023-05-15 00:39:57 +02:00
/// Use DHCP to obtain an IP address configuration.
2022-12-07 16:02:28 +01:00
#[ cfg(feature = " dhcpv4 " ) ]
2023-01-18 10:10:33 +01:00
Dhcp ( DhcpConfig ) ,
2023-06-07 12:04:15 +02:00
/// Do not configure IPv6.
None ,
}
/// Network stack IPv6 configuration.
#[ cfg(feature = " proto-ipv6 " ) ]
pub enum ConfigV6 {
/// Use a static IPv6 address configuration.
Static ( StaticConfigV6 ) ,
/// Do not configure IPv6.
None ,
2022-12-07 16:02:28 +01:00
}
2023-05-15 00:39:57 +02:00
/// A network stack.
///
/// This is the main entry point for the network stack.
2022-12-26 03:33:49 +01:00
pub struct Stack < D : Driver > {
2022-12-07 16:02:28 +01:00
pub ( crate ) socket : RefCell < SocketStack > ,
inner : RefCell < Inner < D > > ,
}
2022-12-26 03:33:49 +01:00
struct Inner < D : Driver > {
2022-12-07 16:02:28 +01:00
device : D ,
link_up : bool ,
2023-06-05 16:00:53 +02:00
#[ cfg(feature = " proto-ipv4 " ) ]
static_v4 : Option < StaticConfigV4 > ,
2023-06-05 16:12:24 +02:00
#[ cfg(feature = " proto-ipv6 " ) ]
static_v6 : Option < StaticConfigV6 > ,
2022-12-07 16:02:28 +01:00
#[ cfg(feature = " dhcpv4 " ) ]
dhcp_socket : Option < SocketHandle > ,
2023-02-10 17:43:23 +01:00
#[ cfg(feature = " dns " ) ]
2023-02-10 18:30:17 +01:00
dns_socket : SocketHandle ,
2023-02-10 18:44:51 +01:00
#[ cfg(feature = " dns " ) ]
dns_waker : WakerRegistration ,
2022-12-07 16:02:28 +01:00
}
pub ( crate ) struct SocketStack {
pub ( crate ) sockets : SocketSet < 'static > ,
2023-01-19 14:41:39 +01:00
pub ( crate ) iface : Interface ,
2022-12-07 16:02:28 +01:00
pub ( crate ) waker : WakerRegistration ,
next_local_port : u16 ,
}
2023-07-31 10:40:48 +02:00
fn to_smoltcp_hardware_address ( addr : driver ::HardwareAddress ) -> HardwareAddress {
match addr {
#[ cfg(feature = " medium-ethernet " ) ]
driver ::HardwareAddress ::Ethernet ( eth ) = > HardwareAddress ::Ethernet ( EthernetAddress ( eth ) ) ,
#[ cfg(feature = " medium-ieee802154 " ) ]
driver ::HardwareAddress ::Ieee802154 ( ieee ) = > HardwareAddress ::Ieee802154 ( Ieee802154Address ::Extended ( ieee ) ) ,
#[ allow(unreachable_patterns) ]
_ = > panic! ( " Unsupported address {:?} . Make sure to enable medium-ethernet or medium-ieee802154 in embassy-net's Cargo features. " , addr ) ,
}
}
2022-12-26 03:33:49 +01:00
impl < D : Driver + 'static > Stack < D > {
2023-05-15 00:39:57 +02:00
/// Create a new network stack.
2023-01-18 10:10:33 +01:00
pub fn new < const SOCK : usize > (
2022-12-07 16:02:28 +01:00
mut device : D ,
2023-01-18 10:10:33 +01:00
config : Config ,
resources : & 'static mut StackResources < SOCK > ,
2022-12-07 16:02:28 +01:00
random_seed : u64 ,
) -> Self {
2023-07-16 02:02:04 +02:00
#[ cfg(any(feature = " medium-ethernet " , feature = " medium-ieee802154 " )) ]
2022-12-07 16:02:28 +01:00
let medium = device . capabilities ( ) . medium ;
2023-06-26 01:59:25 +02:00
let hardware_addr = match medium {
#[ cfg(feature = " medium-ethernet " ) ]
2023-07-31 10:40:48 +02:00
Medium ::Ethernet = > to_smoltcp_hardware_address ( device . hardware_address ( ) ) ,
2023-06-26 01:59:25 +02:00
#[ cfg(feature = " medium-ip " ) ]
Medium ::Ip = > HardwareAddress ::Ip ,
2023-07-16 02:02:04 +02:00
#[ cfg(feature = " medium-ieee802154 " ) ]
2023-07-31 10:40:48 +02:00
Medium ::Ieee802154 = > to_smoltcp_hardware_address ( device . hardware_address ( ) ) ,
2023-06-26 01:59:25 +02:00
#[ allow(unreachable_patterns) ]
_ = > panic! (
" Unsupported medium {:?}. Make sure to enable it in embassy-net's Cargo features. " ,
medium
) ,
} ;
let mut iface_cfg = smoltcp ::iface ::Config ::new ( hardware_addr ) ;
2023-01-19 14:41:39 +01:00
iface_cfg . random_seed = random_seed ;
2022-12-07 16:02:28 +01:00
2023-01-19 14:41:39 +01:00
let iface = Interface ::new (
iface_cfg ,
& mut DriverAdapter {
inner : & mut device ,
cx : None ,
} ,
2023-06-26 01:59:25 +02:00
instant_to_smoltcp ( Instant ::now ( ) ) ,
2023-01-19 14:41:39 +01:00
) ;
2022-12-07 16:02:28 +01:00
let sockets = SocketSet ::new ( & mut resources . sockets [ .. ] ) ;
let next_local_port = ( random_seed % ( LOCAL_PORT_MAX - LOCAL_PORT_MIN ) as u64 ) as u16 + LOCAL_PORT_MIN ;
2023-07-16 02:02:04 +02:00
#[ cfg_attr(feature = " medium-ieee802154 " , allow(unused_mut)) ]
2023-02-10 18:30:17 +01:00
let mut socket = SocketStack {
sockets ,
iface ,
waker : WakerRegistration ::new ( ) ,
next_local_port ,
} ;
2023-07-16 02:02:04 +02:00
#[ cfg_attr(feature = " medium-ieee802154 " , allow(unused_mut)) ]
2022-12-07 16:02:28 +01:00
let mut inner = Inner {
device ,
link_up : false ,
2023-06-05 16:00:53 +02:00
#[ cfg(feature = " proto-ipv4 " ) ]
static_v4 : None ,
2023-06-05 16:12:24 +02:00
#[ cfg(feature = " proto-ipv6 " ) ]
static_v6 : None ,
2022-12-07 16:02:28 +01:00
#[ cfg(feature = " dhcpv4 " ) ]
dhcp_socket : None ,
2023-02-10 17:43:23 +01:00
#[ cfg(feature = " dns " ) ]
2023-02-10 18:44:51 +01:00
dns_socket : socket . sockets . add ( dns ::Socket ::new (
& [ ] ,
managed ::ManagedSlice ::Borrowed ( & mut resources . queries ) ,
) ) ,
#[ cfg(feature = " dns " ) ]
dns_waker : WakerRegistration ::new ( ) ,
2022-12-07 16:02:28 +01:00
} ;
2023-02-10 17:43:23 +01:00
2023-07-16 02:02:04 +02:00
#[ cfg(feature = " medium-ieee802154 " ) ]
let _ = config ;
2023-06-07 12:04:15 +02:00
#[ cfg(feature = " proto-ipv4 " ) ]
match config . ipv4 {
ConfigV4 ::Static ( config ) = > {
2023-06-05 16:12:24 +02:00
inner . apply_config_v4 ( & mut socket , config ) ;
}
2022-12-07 16:02:28 +01:00
#[ cfg(feature = " dhcpv4 " ) ]
2023-06-07 12:04:15 +02:00
ConfigV4 ::Dhcp ( config ) = > {
2023-01-18 10:10:33 +01:00
let mut dhcp_socket = smoltcp ::socket ::dhcpv4 ::Socket ::new ( ) ;
inner . apply_dhcp_config ( & mut dhcp_socket , config ) ;
let handle = socket . sockets . add ( dhcp_socket ) ;
2022-12-07 16:02:28 +01:00
inner . dhcp_socket = Some ( handle ) ;
}
2023-06-07 12:04:15 +02:00
ConfigV4 ::None = > { }
}
#[ cfg(feature = " proto-ipv6 " ) ]
match config . ipv6 {
ConfigV6 ::Static ( config ) = > {
inner . apply_config_v6 ( & mut socket , config ) ;
}
ConfigV6 ::None = > { }
2022-12-07 16:02:28 +01:00
}
Self {
socket : RefCell ::new ( socket ) ,
inner : RefCell ::new ( inner ) ,
}
}
fn with < R > ( & self , f : impl FnOnce ( & SocketStack , & Inner < D > ) -> R ) -> R {
f ( & * self . socket . borrow ( ) , & * self . inner . borrow ( ) )
}
fn with_mut < R > ( & self , f : impl FnOnce ( & mut SocketStack , & mut Inner < D > ) -> R ) -> R {
f ( & mut * self . socket . borrow_mut ( ) , & mut * self . inner . borrow_mut ( ) )
}
2023-07-28 16:19:24 +02:00
/// Get the hardware address of the network interface.
pub fn hardware_address ( & self ) -> HardwareAddress {
2023-07-31 10:40:48 +02:00
self . with ( | _s , i | to_smoltcp_hardware_address ( i . device . hardware_address ( ) ) )
2022-12-07 16:02:28 +01:00
}
2023-05-15 00:39:57 +02:00
/// Get whether the link is up.
2022-12-07 16:02:28 +01:00
pub fn is_link_up ( & self ) -> bool {
self . with ( | _s , i | i . link_up )
}
2023-05-15 00:39:57 +02:00
/// Get whether the network stack has a valid IP configuration.
/// This is true if the network stack has a static IP configuration or if DHCP has completed
2022-12-07 16:02:28 +01:00
pub fn is_config_up ( & self ) -> bool {
2023-06-06 11:17:02 +02:00
let v4_up ;
let v6_up ;
2023-06-05 16:00:53 +02:00
#[ cfg(feature = " proto-ipv4 " ) ]
{
2023-06-06 11:17:02 +02:00
v4_up = self . config_v4 ( ) . is_some ( ) ;
}
#[ cfg(not(feature = " proto-ipv4 " )) ]
{
v4_up = false ;
2023-06-05 16:00:53 +02:00
}
2023-06-06 11:17:02 +02:00
#[ cfg(feature = " proto-ipv6 " ) ]
{
v6_up = self . config_v6 ( ) . is_some ( ) ;
}
#[ cfg(not(feature = " proto-ipv6 " )) ]
2023-06-05 16:00:53 +02:00
{
2023-06-06 11:17:02 +02:00
v6_up = false ;
2023-06-05 16:00:53 +02:00
}
2023-06-06 11:17:02 +02:00
v4_up | | v6_up
2022-12-07 16:02:28 +01:00
}
2023-06-06 11:17:02 +02:00
/// Get the current IPv4 configuration.
2023-06-05 16:00:53 +02:00
#[ cfg(feature = " proto-ipv4 " ) ]
pub fn config_v4 ( & self ) -> Option < StaticConfigV4 > {
self . with ( | _s , i | i . static_v4 . clone ( ) )
2022-12-07 16:02:28 +01:00
}
2023-06-06 11:17:02 +02:00
/// Get the current IPv6 configuration.
#[ cfg(feature = " proto-ipv6 " ) ]
pub fn config_v6 ( & self ) -> Option < StaticConfigV6 > {
self . with ( | _s , i | i . static_v6 . clone ( ) )
}
2023-05-15 00:39:57 +02:00
/// Run the network stack.
///
/// You must call this in a background task, to process network events.
2022-12-07 16:02:28 +01:00
pub async fn run ( & self ) -> ! {
poll_fn ( | cx | {
self . with_mut ( | s , i | i . poll ( cx , s ) ) ;
Poll ::< ( ) > ::Pending
} )
. await ;
unreachable! ( )
}
2023-02-10 17:43:23 +01:00
2023-02-10 18:20:50 +01:00
/// Make a query for a given name and return the corresponding IP addresses.
2023-02-10 17:43:23 +01:00
#[ cfg(feature = " dns " ) ]
2023-02-10 18:20:50 +01:00
pub async fn dns_query ( & self , name : & str , qtype : dns ::DnsQueryType ) -> Result < Vec < IpAddress , 1 > , dns ::Error > {
2023-02-25 20:58:28 +01:00
// For A and AAAA queries we try detect whether `name` is just an IP address
match qtype {
2023-06-05 16:00:53 +02:00
#[ cfg(feature = " proto-ipv4 " ) ]
2023-02-25 20:58:28 +01:00
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 ( ) ) ;
}
}
_ = > { }
}
2023-02-10 18:44:51 +01:00
let query = poll_fn ( | cx | {
self . with_mut ( | s , i | {
let socket = s . sockets . get_mut ::< dns ::Socket > ( i . dns_socket ) ;
match socket . start_query ( s . iface . context ( ) , name , qtype ) {
Ok ( handle ) = > Poll ::Ready ( Ok ( handle ) ) ,
Err ( dns ::StartQueryError ::NoFreeSlot ) = > {
i . dns_waker . register ( cx . waker ( ) ) ;
Poll ::Pending
}
Err ( e ) = > Poll ::Ready ( Err ( e ) ) ,
}
} )
} )
. await ? ;
2023-02-10 17:43:23 +01:00
2023-06-29 19:51:16 +02:00
#[ 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 ( ) ( ) }
}
}
2023-02-10 17:43:23 +01:00
let drop = OnDrop ::new ( | | {
self . with_mut ( | s , i | {
2023-02-10 18:30:17 +01:00
let socket = s . sockets . get_mut ::< dns ::Socket > ( i . dns_socket ) ;
socket . cancel_query ( query ) ;
s . waker . wake ( ) ;
2023-02-10 18:44:51 +01:00
i . dns_waker . wake ( ) ;
2023-02-10 17:43:23 +01:00
} )
} ) ;
let res = poll_fn ( | cx | {
self . with_mut ( | s , i | {
2023-02-10 18:30:17 +01:00
let socket = s . sockets . get_mut ::< dns ::Socket > ( i . dns_socket ) ;
match socket . get_query_result ( query ) {
2023-02-10 18:44:51 +01:00
Ok ( addrs ) = > {
i . dns_waker . wake ( ) ;
Poll ::Ready ( Ok ( addrs ) )
}
2023-02-10 18:30:17 +01:00
Err ( dns ::GetQueryResultError ::Pending ) = > {
socket . register_query_waker ( query , cx . waker ( ) ) ;
Poll ::Pending
2023-02-10 17:43:23 +01:00
}
2023-02-10 18:44:51 +01:00
Err ( e ) = > {
i . dns_waker . wake ( ) ;
Poll ::Ready ( Err ( e . into ( ) ) )
}
2023-02-10 17:43:23 +01:00
}
} )
} )
. await ;
drop . defuse ( ) ;
res
}
2022-12-07 16:02:28 +01:00
}
2023-03-06 17:50:57 +01:00
#[ cfg(feature = " igmp " ) ]
2023-07-18 17:35:20 +02:00
impl < D : Driver + 'static > Stack < D > {
2023-05-15 00:39:57 +02:00
/// Join a multicast group.
2023-07-18 17:35:20 +02:00
pub async fn join_multicast_group < T > ( & self , addr : T ) -> Result < bool , MulticastError >
where
T : Into < IpAddress > ,
{
let addr = addr . into ( ) ;
poll_fn ( move | cx | self . poll_join_multicast_group ( addr , cx ) ) . await
}
2023-05-15 00:39:57 +02:00
/// Join a multicast group.
2023-07-18 17:35:20 +02:00
///
/// When the send queue is full, this method will return `Poll::Pending`
/// and register the current task to be notified when the queue has space available.
pub fn poll_join_multicast_group < T > ( & self , addr : T , cx : & mut Context < '_ > ) -> Poll < Result < bool , MulticastError > >
2023-03-07 23:40:20 +01:00
where
T : Into < IpAddress > ,
2023-03-06 17:50:57 +01:00
{
let addr = addr . into ( ) ;
self . with_mut ( | s , i | {
2023-07-18 17:35:20 +02:00
let mut smoldev = DriverAdapter {
cx : Some ( cx ) ,
inner : & mut i . device ,
} ;
match s
. iface
. join_multicast_group ( & mut smoldev , addr , instant_to_smoltcp ( Instant ::now ( ) ) )
{
Ok ( announce_sent ) = > Poll ::Ready ( Ok ( announce_sent ) ) ,
Err ( MulticastError ::Exhausted ) = > Poll ::Pending ,
Err ( other ) = > Poll ::Ready ( Err ( other ) ) ,
}
2023-03-06 17:50:57 +01:00
} )
}
2023-05-15 00:39:57 +02:00
/// Leave a multicast group.
2023-07-18 17:35:20 +02:00
pub async fn leave_multicast_group < T > ( & self , addr : T ) -> Result < bool , MulticastError >
where
T : Into < IpAddress > ,
{
let addr = addr . into ( ) ;
poll_fn ( move | cx | self . poll_leave_multicast_group ( addr , cx ) ) . await
}
/// Leave a multicast group.
///
/// When the send queue is full, this method will return `Poll::Pending`
/// and register the current task to be notified when the queue has space available.
pub fn poll_leave_multicast_group < T > ( & self , addr : T , cx : & mut Context < '_ > ) -> Poll < Result < bool , MulticastError > >
2023-03-07 23:40:20 +01:00
where
T : Into < IpAddress > ,
2023-03-06 17:50:57 +01:00
{
let addr = addr . into ( ) ;
self . with_mut ( | s , i | {
2023-07-18 17:35:20 +02:00
let mut smoldev = DriverAdapter {
cx : Some ( cx ) ,
inner : & mut i . device ,
} ;
match s
. iface
. leave_multicast_group ( & mut smoldev , addr , instant_to_smoltcp ( Instant ::now ( ) ) )
{
Ok ( leave_sent ) = > Poll ::Ready ( Ok ( leave_sent ) ) ,
Err ( MulticastError ::Exhausted ) = > Poll ::Pending ,
Err ( other ) = > Poll ::Ready ( Err ( other ) ) ,
}
2023-03-06 17:50:57 +01:00
} )
}
2023-05-15 00:39:57 +02:00
/// Get whether the network stack has joined the given multicast group.
2023-03-08 12:37:00 +01:00
pub fn has_multicast_group < T : Into < IpAddress > > ( & self , addr : T ) -> bool {
2023-03-06 17:50:57 +01:00
self . socket . borrow ( ) . iface . has_multicast_group ( addr )
}
}
2022-12-07 16:02:28 +01:00
impl SocketStack {
2023-01-18 10:10:33 +01:00
#[ allow(clippy::absurd_extreme_comparisons, dead_code) ]
2022-12-07 16:02:28 +01:00
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
}
}
2022-12-26 03:33:49 +01:00
impl < D : Driver + 'static > Inner < D > {
2023-06-05 16:00:53 +02:00
#[ cfg(feature = " proto-ipv4 " ) ]
2023-06-05 16:12:24 +02:00
fn apply_config_v4 ( & mut self , s : & mut SocketStack , config : StaticConfigV4 ) {
2023-07-28 14:52:03 +02:00
#[ cfg(any(feature = " medium-ethernet " , feature = " medium-ieee802154 " )) ]
2022-12-07 16:02:28 +01:00
let medium = self . device . capabilities ( ) . medium ;
debug! ( " Acquired IP configuration: " ) ;
debug! ( " IP address: {} " , config . address ) ;
2023-01-19 14:41:39 +01:00
s . iface . update_ip_addrs ( | addrs | {
2023-07-18 10:39:29 +02:00
if let Some ( ( index , _ ) ) = addrs
. iter ( )
. enumerate ( )
. find ( | ( _ , & addr ) | matches! ( addr , IpCidr ::Ipv4 ( _ ) ) )
{
addrs . remove ( index ) ;
2023-01-19 14:41:39 +01:00
}
2023-07-18 10:39:29 +02:00
addrs . push ( IpCidr ::Ipv4 ( config . address ) ) . unwrap ( ) ;
2023-01-19 14:41:39 +01:00
} ) ;
2022-12-07 16:02:28 +01:00
#[ cfg(feature = " medium-ethernet " ) ]
if medium = = Medium ::Ethernet {
if let Some ( gateway ) = config . gateway {
debug! ( " Default gateway: {} " , gateway ) ;
s . iface . routes_mut ( ) . add_default_ipv4_route ( gateway ) . unwrap ( ) ;
} else {
debug! ( " Default gateway: None " ) ;
s . iface . routes_mut ( ) . remove_default_ipv4_route ( ) ;
}
}
for ( i , s ) in config . dns_servers . iter ( ) . enumerate ( ) {
debug! ( " DNS server {}: {} " , i , s ) ;
}
2023-06-05 16:35:31 +02:00
self . static_v4 = Some ( config ) ;
2023-02-10 17:43:23 +01:00
#[ cfg(feature = " dns " ) ]
2023-02-10 19:00:00 +01:00
{
2023-06-05 16:35:31 +02:00
self . update_dns_servers ( s )
2023-02-10 19:00:00 +01:00
}
2022-12-07 16:02:28 +01:00
}
2023-06-05 16:12:24 +02:00
/// Replaces the current IPv6 static configuration with a newly supplied config.
#[ cfg(feature = " proto-ipv6 " ) ]
fn apply_config_v6 ( & mut self , s : & mut SocketStack , config : StaticConfigV6 ) {
#[ cfg(feature = " medium-ethernet " ) ]
let medium = self . device . capabilities ( ) . medium ;
debug! ( " Acquired IPv6 configuration: " ) ;
debug! ( " IP address: {} " , config . address ) ;
s . iface . update_ip_addrs ( | addrs | {
2023-07-18 10:39:29 +02:00
if let Some ( ( index , _ ) ) = addrs
. iter ( )
. enumerate ( )
. find ( | ( _ , & addr ) | matches! ( addr , IpCidr ::Ipv6 ( _ ) ) )
{
addrs . remove ( index ) ;
2023-06-05 16:12:24 +02:00
}
2023-07-18 10:39:29 +02:00
addrs . push ( IpCidr ::Ipv6 ( config . address ) ) . unwrap ( ) ;
2023-06-05 16:12:24 +02:00
} ) ;
#[ cfg(feature = " medium-ethernet " ) ]
if Medium ::Ethernet = = medium {
if let Some ( gateway ) = config . gateway {
debug! ( " Default gateway: {} " , gateway ) ;
s . iface . routes_mut ( ) . add_default_ipv6_route ( gateway ) . unwrap ( ) ;
} else {
debug! ( " Default gateway: None " ) ;
s . iface . routes_mut ( ) . remove_default_ipv6_route ( ) ;
}
}
for ( i , s ) in config . dns_servers . iter ( ) . enumerate ( ) {
debug! ( " DNS server {}: {} " , i , s ) ;
}
2023-06-05 16:35:31 +02:00
self . static_v6 = Some ( config ) ;
2023-06-05 16:12:24 +02:00
#[ cfg(feature = " dns " ) ]
{
2023-06-05 16:35:31 +02:00
self . update_dns_servers ( s )
}
}
#[ cfg(feature = " dns " ) ]
fn update_dns_servers ( & mut self , s : & mut SocketStack ) {
let socket = s . sockets . get_mut ::< smoltcp ::socket ::dns ::Socket > ( self . dns_socket ) ;
let servers_v4 ;
#[ cfg(feature = " proto-ipv4 " ) ]
{
servers_v4 = self
. static_v4
. iter ( )
. flat_map ( | cfg | cfg . dns_servers . iter ( ) . map ( | c | IpAddress ::Ipv4 ( * c ) ) ) ;
} ;
#[ cfg(not(feature = " proto-ipv4 " )) ]
{
servers_v4 = core ::iter ::empty ( ) ;
}
let servers_v6 ;
#[ cfg(feature = " proto-ipv6 " ) ]
{
servers_v6 = self
. static_v6
. iter ( )
. flat_map ( | cfg | cfg . dns_servers . iter ( ) . map ( | c | IpAddress ::Ipv6 ( * c ) ) ) ;
}
#[ cfg(not(feature = " proto-ipv6 " )) ]
{
servers_v6 = core ::iter ::empty ( ) ;
2023-06-05 16:12:24 +02:00
}
2023-06-05 16:35:31 +02:00
// Prefer the v6 DNS servers over the v4 servers
let servers : Vec < IpAddress , 6 > = servers_v6 . chain ( servers_v4 ) . collect ( ) ;
socket . update_servers ( & servers [ .. ] ) ;
2023-06-05 16:12:24 +02:00
}
2023-04-18 22:11:15 +02:00
#[ cfg(feature = " dhcpv4 " ) ]
2023-01-18 10:10:33 +01:00
fn apply_dhcp_config ( & self , socket : & mut smoltcp ::socket ::dhcpv4 ::Socket , config : DhcpConfig ) {
socket . set_ignore_naks ( config . ignore_naks ) ;
2023-05-15 00:38:58 +02:00
socket . set_max_lease_duration ( config . max_lease_duration . map ( crate ::time ::duration_to_smoltcp ) ) ;
2023-01-18 10:10:33 +01:00
socket . set_ports ( config . server_port , config . client_port ) ;
socket . set_retry_config ( config . retry_config ) ;
}
2023-07-18 10:57:05 +02:00
#[ cfg(feature = " dhcpv4 " ) ]
2023-07-18 10:39:29 +02:00
fn unapply_config_v4 ( & mut self , s : & mut SocketStack ) {
2022-12-07 16:02:28 +01:00
#[ cfg(feature = " medium-ethernet " ) ]
let medium = self . device . capabilities ( ) . medium ;
debug! ( " Lost IP configuration " ) ;
2023-07-18 10:39:29 +02:00
s . iface . update_ip_addrs ( | ip_addrs | {
2023-07-18 10:57:05 +02:00
#[ cfg(feature = " proto-ipv4 " ) ]
2023-07-18 10:39:29 +02:00
if let Some ( ( index , _ ) ) = ip_addrs
. iter ( )
. enumerate ( )
2023-07-18 10:57:05 +02:00
. find ( | ( _ , & addr ) | matches! ( addr , IpCidr ::Ipv4 ( _ ) ) )
2023-07-18 10:39:29 +02:00
{
ip_addrs . remove ( index ) ;
}
} ) ;
2022-12-07 16:02:28 +01:00
#[ cfg(feature = " medium-ethernet " ) ]
if medium = = Medium ::Ethernet {
2023-06-05 16:00:53 +02:00
#[ cfg(feature = " proto-ipv4 " ) ]
{
s . iface . routes_mut ( ) . remove_default_ipv4_route ( ) ;
}
}
#[ cfg(feature = " proto-ipv4 " ) ]
{
self . static_v4 = None
2022-12-07 16:02:28 +01:00
}
}
fn poll ( & mut self , cx : & mut Context < '_ > , s : & mut SocketStack ) {
s . waker . register ( cx . waker ( ) ) ;
2023-07-28 16:19:24 +02:00
#[ cfg(any(feature = " medium-ethernet " , feature = " medium-ieee802154 " )) ]
if self . device . capabilities ( ) . medium = = Medium ::Ethernet
| | self . device . capabilities ( ) . medium = = Medium ::Ieee802154
{
2023-07-31 10:40:48 +02:00
s . iface
. set_hardware_addr ( to_smoltcp_hardware_address ( self . device . hardware_address ( ) ) ) ;
2023-07-28 16:22:03 +02:00
}
2022-12-07 16:02:28 +01:00
let timestamp = instant_to_smoltcp ( Instant ::now ( ) ) ;
2022-12-26 03:33:49 +01:00
let mut smoldev = DriverAdapter {
2022-12-07 16:02:28 +01:00
cx : Some ( cx ) ,
inner : & mut self . device ,
} ;
2023-01-19 13:30:51 +01:00
s . iface . poll ( timestamp , & mut smoldev , & mut s . sockets ) ;
2022-12-07 16:02:28 +01:00
// Update link up
let old_link_up = self . link_up ;
self . link_up = self . device . link_state ( cx ) = = LinkState ::Up ;
// Print when changed
if old_link_up ! = self . link_up {
info! ( " link_up = {:?} " , self . link_up ) ;
}
#[ cfg(feature = " dhcpv4 " ) ]
if let Some ( dhcp_handle ) = self . dhcp_socket {
let socket = s . sockets . get_mut ::< dhcpv4 ::Socket > ( dhcp_handle ) ;
if self . link_up {
match socket . poll ( ) {
None = > { }
2023-07-18 10:39:29 +02:00
Some ( dhcpv4 ::Event ::Deconfigured ) = > self . unapply_config_v4 ( s ) ,
2022-12-07 16:02:28 +01:00
Some ( dhcpv4 ::Event ::Configured ( config ) ) = > {
2023-06-05 14:57:17 +02:00
let config = StaticConfigV4 {
2022-12-07 16:02:28 +01:00
address : config . address ,
gateway : config . router ,
dns_servers : config . dns_servers ,
} ;
2023-06-05 16:12:24 +02:00
self . apply_config_v4 ( s , config )
2022-12-07 16:02:28 +01:00
}
}
} else if old_link_up {
socket . reset ( ) ;
2023-07-18 10:39:29 +02:00
self . unapply_config_v4 ( s ) ;
2022-12-07 16:02:28 +01:00
}
}
//if old_link_up || self.link_up {
// self.poll_configurator(timestamp)
//}
2023-02-10 17:43:23 +01:00
//
2022-12-07 16:02:28 +01:00
if let Some ( poll_at ) = s . iface . poll_at ( timestamp , & mut s . sockets ) {
let t = Timer ::at ( instant_from_smoltcp ( poll_at ) ) ;
pin_mut! ( t ) ;
if t . poll ( cx ) . is_ready ( ) {
cx . waker ( ) . wake_by_ref ( ) ;
}
}
}
}