embassy/embassy-net/src/lib.rs

802 lines
25 KiB
Rust
Raw Normal View History

2021-02-03 05:09:37 +01:00
#![cfg_attr(not(feature = "std"), no_std)]
#![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;
#[cfg(feature = "tcp")]
2022-05-04 20:48:37 +02:00
pub mod tcp;
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;
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)]
use smoltcp::iface::{Interface, SocketHandle, SocketSet, SocketStorage};
2022-12-07 16:02:28 +01:00
#[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"))]
pub use smoltcp::wire::HardwareAddress;
#[cfg(feature = "udp")]
pub use smoltcp::wire::IpListenEndpoint;
2023-07-16 02:02:04 +02:00
#[cfg(feature = "medium-ieee802154")]
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};
#[cfg(feature = "proto-ipv6")]
pub use smoltcp::wire::{Ipv6Address, Ipv6Cidr};
2022-12-07 16:02:28 +01:00
use crate::device::DriverAdapter;
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>,
}
/// 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.
#[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.
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,
}
#[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 {
/// 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,
/// IPv6 configuration
2023-06-07 12:04:15 +02:00
#[cfg(feature = "proto-ipv6")]
pub ipv6: ConfigV6,
}
impl Config {
/// 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,
}
}
/// IPv6 configuration with static addressing.
#[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),
}
}
/// 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.
pub struct Stack<D: Driver> {
2022-12-07 16:02:28 +01:00
pub(crate) socket: RefCell<SocketStack>,
inner: RefCell<Inner<D>>,
}
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>,
#[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,
}
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")]
Medium::Ethernet => HardwareAddress::Ethernet(EthernetAddress(device.ethernet_address())),
#[cfg(feature = "medium-ip")]
Medium::Ip => HardwareAddress::Ip,
2023-07-16 02:02:04 +02:00
#[cfg(feature = "medium-ieee802154")]
Medium::Ieee802154 => HardwareAddress::Ieee802154(Ieee802154Address::Absent),
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,
#[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) => {
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-05-15 00:39:57 +02:00
/// Get the MAC address of the network interface.
2022-12-07 16:02:28 +01:00
pub fn ethernet_address(&self) -> [u8; 6] {
self.with(|_s, i| i.device.ethernet_address())
}
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
/// Make a query for a given name and return the corresponding IP addresses.
2023-02-10 17:43:23 +01:00
#[cfg(feature = "dns")]
pub async fn dns_query(&self, name: &str, qtype: dns::DnsQueryType) -> Result<Vec<IpAddress, 1>, dns::Error> {
// 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")]
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.
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
}
}
impl<D: Driver + 'static> Inner<D> {
2023-06-05 16:00:53 +02:00
#[cfg(feature = "proto-ipv4")]
fn apply_config_v4(&mut self, s: &mut SocketStack, config: StaticConfigV4) {
#[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);
}
self.static_v4 = Some(config);
2023-02-10 17:43:23 +01:00
#[cfg(feature = "dns")]
{
self.update_dns_servers(s)
}
2022-12-07 16:02:28 +01: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-07-18 10:39:29 +02:00
addrs.push(IpCidr::Ipv6(config.address)).unwrap();
});
#[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);
}
self.static_v6 = Some(config);
#[cfg(feature = "dns")]
{
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();
}
// Prefer the v6 DNS servers over the v4 servers
let servers: Vec<IpAddress, 6> = servers_v6.chain(servers_v4).collect();
socket.update_servers(&servers[..]);
}
#[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);
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());
#[cfg(feature = "medium-ethernet")]
if self.device.capabilities().medium == Medium::Ethernet {
s.iface.set_hardware_addr(HardwareAddress::Ethernet(EthernetAddress(
self.device.ethernet_address(),
)));
}
2022-12-07 16:02:28 +01:00
let timestamp = instant_to_smoltcp(Instant::now());
let mut smoldev = DriverAdapter {
2022-12-07 16:02:28 +01:00
cx: Some(cx),
inner: &mut self.device,
};
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,
};
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();
}
}
}
}