From 9cfea693edec5af17ba698f64b3f0a168ad92944 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 31 Jan 2023 22:06:41 +0100 Subject: [PATCH 01/14] Add DNS socket to embassy-net --- embassy-net/Cargo.toml | 4 +- embassy-net/src/dns.rs | 114 ++++++++++++++++++++++++++++++++ embassy-net/src/lib.rs | 2 + examples/std/Cargo.toml | 2 +- examples/std/src/bin/net_dns.rs | 102 ++++++++++++++++++++++++++++ 5 files changed, 221 insertions(+), 3 deletions(-) create mode 100644 embassy-net/src/dns.rs create mode 100644 examples/std/src/bin/net_dns.rs diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 4ec340b7..6b346828 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -13,7 +13,7 @@ target = "thumbv7em-none-eabi" [features] default = [] -std = [] +std = ["smoltcp/alloc", "managed/std"] defmt = ["dep:defmt", "smoltcp/defmt", "embassy-net-driver/defmt"] @@ -22,7 +22,7 @@ unstable-traits = [] udp = ["smoltcp/socket-udp"] tcp = ["smoltcp/socket-tcp"] -dns = ["smoltcp/socket-dns"] +dns = ["smoltcp/socket-dns", "smoltcp/proto-dns"] dhcpv4 = ["medium-ethernet", "smoltcp/socket-dhcpv4"] proto-ipv6 = ["smoltcp/proto-ipv6"] medium-ethernet = ["smoltcp/medium-ethernet"] diff --git a/embassy-net/src/dns.rs b/embassy-net/src/dns.rs new file mode 100644 index 00000000..f18750cc --- /dev/null +++ b/embassy-net/src/dns.rs @@ -0,0 +1,114 @@ +//! DNS socket with async support. +use core::cell::RefCell; +use core::future::poll_fn; +use core::mem; +use core::task::Poll; + +use embassy_net_driver::Driver; +use heapless::Vec; +use managed::ManagedSlice; +use smoltcp::iface::{Interface, SocketHandle}; +pub use smoltcp::socket::dns::DnsQuery; +use smoltcp::socket::dns::{self, GetQueryResultError, StartQueryError, MAX_ADDRESS_COUNT}; +pub use smoltcp::wire::{DnsQueryType, IpAddress}; + +use crate::{SocketStack, Stack}; + +/// Errors returned by DnsSocket. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Error { + /// No available query slot + NoFreeSlot, + /// Invalid name + InvalidName, + /// Name too long + NameTooLong, + /// Name lookup failed + Failed, +} + +impl From for Error { + fn from(_: GetQueryResultError) -> Self { + Self::Failed + } +} + +impl From for Error { + fn from(e: StartQueryError) -> Self { + match e { + StartQueryError::NoFreeSlot => Self::NoFreeSlot, + StartQueryError::InvalidName => Self::InvalidName, + StartQueryError::NameTooLong => Self::NameTooLong, + } + } +} + +/// Async socket for making DNS queries. +pub struct DnsSocket<'a> { + stack: &'a RefCell, + handle: SocketHandle, +} + +impl<'a> DnsSocket<'a> { + /// Create a new DNS socket using the provided stack and query storage. + /// + /// DNS servers are derived from the stack configuration. + /// + /// NOTE: If using DHCP, make sure it has reconfigured the stack to ensure the DNS servers are updated. + pub fn new(stack: &'a Stack, queries: Q) -> Self + where + D: Driver + 'static, + Q: Into>>, + { + let servers = stack + .config() + .map(|c| { + let v: Vec = c.dns_servers.iter().map(|c| IpAddress::Ipv4(*c)).collect(); + v + }) + .unwrap_or(Vec::new()); + let s = &mut *stack.socket.borrow_mut(); + let queries: ManagedSlice<'static, Option> = unsafe { mem::transmute(queries.into()) }; + + let handle = s.sockets.add(dns::Socket::new(&servers[..], queries)); + Self { + stack: &stack.socket, + handle, + } + } + + fn with_mut(&mut self, f: impl FnOnce(&mut dns::Socket, &mut Interface) -> R) -> R { + let s = &mut *self.stack.borrow_mut(); + let socket = s.sockets.get_mut::(self.handle); + let res = f(socket, &mut s.iface); + s.waker.wake(); + res + } + + /// Make a query for a given name and return the corresponding IP addresses. + pub async fn query(&mut self, name: &str, qtype: DnsQueryType) -> Result, Error> { + let query = match { self.with_mut(|s, i| s.start_query(i.context(), name, qtype)) } { + Ok(handle) => handle, + Err(e) => return Err(e.into()), + }; + + poll_fn(|cx| { + self.with_mut(|s, _| match s.get_query_result(query) { + Ok(addrs) => Poll::Ready(Ok(addrs)), + Err(GetQueryResultError::Pending) => { + s.register_query_waker(query, cx.waker()); + Poll::Pending + } + Err(e) => Poll::Ready(Err(e.into())), + }) + }) + .await + } +} + +impl<'a> Drop for DnsSocket<'a> { + fn drop(&mut self) { + self.stack.borrow_mut().sockets.remove(self.handle); + } +} diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 0f694ee7..ae447d06 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -11,6 +11,8 @@ pub(crate) mod fmt; pub use embassy_net_driver as driver; mod device; +#[cfg(feature = "dns")] +pub mod dns; #[cfg(feature = "tcp")] pub mod tcp; #[cfg(feature = "udp")] diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index af1481e0..8087df09 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["log"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["log", "std", "nightly", "integrated-timers"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["log", "std", "nightly"] } -embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "nightly", "log", "medium-ethernet", "tcp", "udp", "dhcpv4"] } +embassy-net = { version = "0.1.0", path = "../../embassy-net", features=[ "std", "nightly", "log", "medium-ethernet", "tcp", "udp", "dns", "dhcpv4", "unstable-traits", "proto-ipv6"] } embassy-net-driver = { version = "0.1.0", path = "../../embassy-net-driver" } embedded-io = { version = "0.4.0", features = ["async", "std", "futures"] } critical-section = { version = "1.1", features = ["std"] } diff --git a/examples/std/src/bin/net_dns.rs b/examples/std/src/bin/net_dns.rs new file mode 100644 index 00000000..6203f837 --- /dev/null +++ b/examples/std/src/bin/net_dns.rs @@ -0,0 +1,102 @@ +#![feature(type_alias_impl_trait)] + +use std::default::Default; + +use clap::Parser; +use embassy_executor::{Executor, Spawner}; +use embassy_net::dns::{DnsQueryType, DnsSocket}; +use embassy_net::{Config, Ipv4Address, Ipv4Cidr, Stack, StackResources}; +use heapless::Vec; +use log::*; +use rand_core::{OsRng, RngCore}; +use static_cell::StaticCell; + +#[path = "../tuntap.rs"] +mod tuntap; + +use crate::tuntap::TunTapDevice; + +macro_rules! singleton { + ($val:expr) => {{ + type T = impl Sized; + static STATIC_CELL: StaticCell = StaticCell::new(); + STATIC_CELL.init_with(move || $val) + }}; +} + +#[derive(Parser)] +#[clap(version = "1.0")] +struct Opts { + /// TAP device name + #[clap(long, default_value = "tap0")] + tap: String, + /// use a static IP instead of DHCP + #[clap(long)] + static_ip: bool, +} + +#[embassy_executor::task] +async fn net_task(stack: &'static Stack) -> ! { + stack.run().await +} + +#[embassy_executor::task] +async fn main_task(spawner: Spawner) { + let opts: Opts = Opts::parse(); + + // Init network device + let device = TunTapDevice::new(&opts.tap).unwrap(); + + // Choose between dhcp or static ip + let config = if opts.static_ip { + Config::Static(embassy_net::StaticConfig { + address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 1), 24), + dns_servers: Vec::from_slice(&[Ipv4Address::new(8, 8, 4, 4).into(), Ipv4Address::new(8, 8, 8, 8).into()]) + .unwrap(), + gateway: Some(Ipv4Address::new(192, 168, 69, 100)), + }) + } else { + Config::Dhcp(Default::default()) + }; + + // Generate random seed + let mut seed = [0; 8]; + OsRng.fill_bytes(&mut seed); + let seed = u64::from_le_bytes(seed); + + // Init network stack + let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); + + // Launch network task + spawner.spawn(net_task(stack)).unwrap(); + + // Then we can use it! + + let mut socket = DnsSocket::new(stack, vec![]); + + let host = "example.com"; + info!("querying host {:?}...", host); + match socket.query(host, DnsQueryType::A).await { + Ok(r) => { + info!("query response: {:?}", r); + } + Err(e) => { + warn!("query error: {:?}", e); + } + }; +} + +static EXECUTOR: StaticCell = StaticCell::new(); + +fn main() { + env_logger::builder() + .filter_level(log::LevelFilter::Debug) + .filter_module("async_io", log::LevelFilter::Info) + .format_timestamp_nanos() + .init(); + + let executor = EXECUTOR.init(Executor::new()); + executor.run(|spawner| { + spawner.spawn(main_task(spawner)).unwrap(); + }); +} From c203cefe0103ae150be514875b6b110b1f9a3a90 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 6 Feb 2023 20:18:12 +0100 Subject: [PATCH 02/14] Handle cancellation --- embassy-net/Cargo.toml | 1 + embassy-net/src/dns.rs | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 6b346828..6eea8c30 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -40,6 +40,7 @@ smoltcp = { version = "0.9.0", default-features = false, features = [ ]} embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } +embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common" } embassy-time = { version = "0.1.0", path = "../embassy-time" } embassy-sync = { version = "0.1.0", path = "../embassy-sync" } embedded-io = { version = "0.4.0", optional = true } diff --git a/embassy-net/src/dns.rs b/embassy-net/src/dns.rs index f18750cc..2d2e7b79 100644 --- a/embassy-net/src/dns.rs +++ b/embassy-net/src/dns.rs @@ -11,6 +11,7 @@ use smoltcp::iface::{Interface, SocketHandle}; pub use smoltcp::socket::dns::DnsQuery; use smoltcp::socket::dns::{self, GetQueryResultError, StartQueryError, MAX_ADDRESS_COUNT}; pub use smoltcp::wire::{DnsQueryType, IpAddress}; +use embassy_hal_common::drop::OnDrop; use crate::{SocketStack, Stack}; @@ -93,7 +94,15 @@ impl<'a> DnsSocket<'a> { Err(e) => return Err(e.into()), }; - poll_fn(|cx| { + let handle = self.handle; + let drop = OnDrop::new(|| { + let s = &mut *self.stack.borrow_mut(); + let socket = s.sockets.get_mut::(handle); + socket.cancel_query(query); + s.waker.wake(); + }); + + let res = poll_fn(|cx| { self.with_mut(|s, _| match s.get_query_result(query) { Ok(addrs) => Poll::Ready(Ok(addrs)), Err(GetQueryResultError::Pending) => { @@ -103,7 +112,10 @@ impl<'a> DnsSocket<'a> { Err(e) => Poll::Ready(Err(e.into())), }) }) - .await + .await; + + drop.defuse(); + res } } From 614740a1b2ea1cada16e3391cb86d0394f64edfc Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 6 Feb 2023 20:36:08 +0100 Subject: [PATCH 03/14] cargo fmt --- embassy-net/src/dns.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-net/src/dns.rs b/embassy-net/src/dns.rs index 2d2e7b79..e98247bf 100644 --- a/embassy-net/src/dns.rs +++ b/embassy-net/src/dns.rs @@ -4,6 +4,7 @@ use core::future::poll_fn; use core::mem; use core::task::Poll; +use embassy_hal_common::drop::OnDrop; use embassy_net_driver::Driver; use heapless::Vec; use managed::ManagedSlice; @@ -11,7 +12,6 @@ use smoltcp::iface::{Interface, SocketHandle}; pub use smoltcp::socket::dns::DnsQuery; use smoltcp::socket::dns::{self, GetQueryResultError, StartQueryError, MAX_ADDRESS_COUNT}; pub use smoltcp::wire::{DnsQueryType, IpAddress}; -use embassy_hal_common::drop::OnDrop; use crate::{SocketStack, Stack}; From cd440a49d677f7dfc09e405d99b87a49fba9ba31 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 10 Feb 2023 17:43:23 +0100 Subject: [PATCH 04/14] Rewrite to use a single socket --- embassy-net/Cargo.toml | 2 +- embassy-net/src/dns.rs | 124 +++++++++++++------------------- embassy-net/src/lib.rs | 83 ++++++++++++++++++++- examples/std/src/bin/net_dns.rs | 3 +- 4 files changed, 133 insertions(+), 79 deletions(-) diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 6eea8c30..53778899 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -52,5 +52,5 @@ generic-array = { version = "0.14.4", default-features = false } stable_deref_trait = { version = "1.2.0", default-features = false } futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] } atomic-pool = "1.0" -embedded-nal-async = { version = "0.3.0", optional = true } +embedded-nal-async = { version = "0.4.0", optional = true } atomic-polyfill = { version = "1.0" } diff --git a/embassy-net/src/dns.rs b/embassy-net/src/dns.rs index e98247bf..1815d258 100644 --- a/embassy-net/src/dns.rs +++ b/embassy-net/src/dns.rs @@ -1,19 +1,10 @@ //! DNS socket with async support. -use core::cell::RefCell; -use core::future::poll_fn; -use core::mem; -use core::task::Poll; - -use embassy_hal_common::drop::OnDrop; -use embassy_net_driver::Driver; use heapless::Vec; -use managed::ManagedSlice; -use smoltcp::iface::{Interface, SocketHandle}; -pub use smoltcp::socket::dns::DnsQuery; -use smoltcp::socket::dns::{self, GetQueryResultError, StartQueryError, MAX_ADDRESS_COUNT}; +pub use smoltcp::socket::dns::{DnsQuery, Socket, MAX_ADDRESS_COUNT}; +pub(crate) use smoltcp::socket::dns::{GetQueryResultError, StartQueryError}; pub use smoltcp::wire::{DnsQueryType, IpAddress}; -use crate::{SocketStack, Stack}; +use crate::{Driver, Stack}; /// Errors returned by DnsSocket. #[derive(Debug, PartialEq, Eq, Clone, Copy)] @@ -46,81 +37,64 @@ impl From for Error { } /// Async socket for making DNS queries. -pub struct DnsSocket<'a> { - stack: &'a RefCell, - handle: SocketHandle, +pub struct DnsSocket<'a, D> +where + D: Driver + 'static, +{ + stack: &'a Stack, } -impl<'a> DnsSocket<'a> { +impl<'a, D> DnsSocket<'a, D> +where + D: Driver + 'static, +{ /// Create a new DNS socket using the provided stack and query storage. /// /// DNS servers are derived from the stack configuration. /// /// NOTE: If using DHCP, make sure it has reconfigured the stack to ensure the DNS servers are updated. - pub fn new(stack: &'a Stack, queries: Q) -> Self - where - D: Driver + 'static, - Q: Into>>, - { - let servers = stack - .config() - .map(|c| { - let v: Vec = c.dns_servers.iter().map(|c| IpAddress::Ipv4(*c)).collect(); - v - }) - .unwrap_or(Vec::new()); - let s = &mut *stack.socket.borrow_mut(); - let queries: ManagedSlice<'static, Option> = unsafe { mem::transmute(queries.into()) }; - - let handle = s.sockets.add(dns::Socket::new(&servers[..], queries)); - Self { - stack: &stack.socket, - handle, - } - } - - fn with_mut(&mut self, f: impl FnOnce(&mut dns::Socket, &mut Interface) -> R) -> R { - let s = &mut *self.stack.borrow_mut(); - let socket = s.sockets.get_mut::(self.handle); - let res = f(socket, &mut s.iface); - s.waker.wake(); - res + pub fn new(stack: &'a Stack) -> Self { + Self { stack } } /// Make a query for a given name and return the corresponding IP addresses. - pub async fn query(&mut self, name: &str, qtype: DnsQueryType) -> Result, Error> { - let query = match { self.with_mut(|s, i| s.start_query(i.context(), name, qtype)) } { - Ok(handle) => handle, - Err(e) => return Err(e.into()), + pub async fn query(&self, name: &str, qtype: DnsQueryType) -> Result, Error> { + self.stack.dns_query(name, qtype).await + } +} + +#[cfg(all(feature = "unstable-traits", feature = "nightly"))] +impl<'a, D> embedded_nal_async::Dns for DnsSocket<'a, D> +where + D: Driver + 'static, +{ + type Error = Error; + + async fn get_host_by_name( + &self, + host: &str, + addr_type: embedded_nal_async::AddrType, + ) -> Result { + use embedded_nal_async::{AddrType, IpAddr}; + let qtype = match addr_type { + AddrType::IPv6 => DnsQueryType::Aaaa, + _ => DnsQueryType::A, }; - - let handle = self.handle; - let drop = OnDrop::new(|| { - let s = &mut *self.stack.borrow_mut(); - let socket = s.sockets.get_mut::(handle); - socket.cancel_query(query); - s.waker.wake(); - }); - - let res = poll_fn(|cx| { - self.with_mut(|s, _| match s.get_query_result(query) { - Ok(addrs) => Poll::Ready(Ok(addrs)), - Err(GetQueryResultError::Pending) => { - s.register_query_waker(query, cx.waker()); - Poll::Pending - } - Err(e) => Poll::Ready(Err(e.into())), + let addrs = self.query(host, qtype).await?; + if let Some(first) = addrs.get(0) { + Ok(match first { + IpAddress::Ipv4(addr) => IpAddr::V4(addr.0.into()), + IpAddress::Ipv6(addr) => IpAddr::V6(addr.0.into()), }) - }) - .await; + } else { + Err(Error::Failed) + } + } - drop.defuse(); - res - } -} - -impl<'a> Drop for DnsSocket<'a> { - fn drop(&mut self) { - self.stack.borrow_mut().sockets.remove(self.handle); + async fn get_host_by_address( + &self, + _addr: embedded_nal_async::IpAddr, + ) -> Result, Self::Error> { + todo!() } } diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index ae447d06..b63aa83d 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -48,15 +48,22 @@ use crate::device::DriverAdapter; const LOCAL_PORT_MIN: u16 = 1025; const LOCAL_PORT_MAX: u16 = 65535; +const MAX_QUERIES: usize = 2; pub struct StackResources { sockets: [SocketStorage<'static>; SOCK], + #[cfg(feature = "dns")] + queries: Option<[Option; MAX_QUERIES]>, } impl StackResources { pub fn new() -> Self { + #[cfg(feature = "dns")] + const INIT: Option = None; Self { sockets: [SocketStorage::EMPTY; SOCK], + #[cfg(feature = "dns")] + queries: Some([INIT; MAX_QUERIES]), } } } @@ -109,6 +116,8 @@ struct Inner { config: Option, #[cfg(feature = "dhcpv4")] dhcp_socket: Option, + #[cfg(feature = "dns")] + dns_socket: Option, } pub(crate) struct SocketStack { @@ -153,6 +162,8 @@ impl Stack { config: None, #[cfg(feature = "dhcpv4")] dhcp_socket: None, + #[cfg(feature = "dns")] + dns_socket: None, }; let mut socket = SocketStack { sockets, @@ -161,8 +172,17 @@ impl Stack { next_local_port, }; + #[cfg(feature = "dns")] + { + if let Some(queries) = resources.queries.take() { + inner.dns_socket = Some(socket.sockets.add(dns::Socket::new(&[], queries))); + } + } + match config { - Config::Static(config) => inner.apply_config(&mut socket, config), + Config::Static(config) => { + inner.apply_config(&mut socket, config); + } #[cfg(feature = "dhcpv4")] Config::Dhcp(config) => { let mut dhcp_socket = smoltcp::socket::dhcpv4::Socket::new(); @@ -210,6 +230,59 @@ impl Stack { .await; unreachable!() } + + #[cfg(feature = "dns")] + async fn dns_query( + &self, + name: &str, + qtype: dns::DnsQueryType, + ) -> Result, dns::Error> { + let query = self.with_mut(|s, i| { + if let Some(dns_handle) = i.dns_socket { + let socket = s.sockets.get_mut::(dns_handle); + match socket.start_query(s.iface.context(), name, qtype) { + Ok(handle) => Ok(handle), + Err(e) => Err(e.into()), + } + } else { + Err(dns::Error::Failed) + } + })?; + + use embassy_hal_common::drop::OnDrop; + let drop = OnDrop::new(|| { + self.with_mut(|s, i| { + if let Some(dns_handle) = i.dns_socket { + let socket = s.sockets.get_mut::(dns_handle); + socket.cancel_query(query); + s.waker.wake(); + } + }) + }); + + let res = poll_fn(|cx| { + self.with_mut(|s, i| { + if let Some(dns_handle) = i.dns_socket { + let socket = s.sockets.get_mut::(dns_handle); + match socket.get_query_result(query) { + Ok(addrs) => Poll::Ready(Ok(addrs)), + Err(dns::GetQueryResultError::Pending) => { + socket.register_query_waker(query, cx.waker()); + Poll::Pending + } + Err(e) => Poll::Ready(Err(e.into())), + } + } else { + Poll::Ready(Err(dns::Error::Failed)) + } + }) + }) + .await; + + drop.defuse(); + + res + } } impl SocketStack { @@ -251,6 +324,13 @@ impl Inner { debug!(" DNS server {}: {}", i, s); } + #[cfg(feature = "dns")] + if let Some(dns_socket) = self.dns_socket { + let socket = s.sockets.get_mut::(dns_socket); + let servers: Vec = config.dns_servers.iter().map(|c| IpAddress::Ipv4(*c)).collect(); + socket.update_servers(&servers[..]); + } + self.config = Some(config) } @@ -326,6 +406,7 @@ impl Inner { //if old_link_up || self.link_up { // self.poll_configurator(timestamp) //} + // if let Some(poll_at) = s.iface.poll_at(timestamp, &mut s.sockets) { let t = Timer::at(instant_from_smoltcp(poll_at)); diff --git a/examples/std/src/bin/net_dns.rs b/examples/std/src/bin/net_dns.rs index 6203f837..e787cb82 100644 --- a/examples/std/src/bin/net_dns.rs +++ b/examples/std/src/bin/net_dns.rs @@ -71,8 +71,7 @@ async fn main_task(spawner: Spawner) { spawner.spawn(net_task(stack)).unwrap(); // Then we can use it! - - let mut socket = DnsSocket::new(stack, vec![]); + let socket = DnsSocket::new(stack); let host = "example.com"; info!("querying host {:?}...", host); From 7ae47cb1d81d5112fdc42ceaec470350aab5c372 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 10 Feb 2023 18:20:50 +0100 Subject: [PATCH 05/14] Expose api on Stack and add doc Make it work with smoltcp 0.9 --- embassy-net/src/dns.rs | 8 +++----- embassy-net/src/lib.rs | 7 ++----- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/embassy-net/src/dns.rs b/embassy-net/src/dns.rs index 1815d258..9b1b936c 100644 --- a/embassy-net/src/dns.rs +++ b/embassy-net/src/dns.rs @@ -1,6 +1,6 @@ //! DNS socket with async support. use heapless::Vec; -pub use smoltcp::socket::dns::{DnsQuery, Socket, MAX_ADDRESS_COUNT}; +pub use smoltcp::socket::dns::{DnsQuery, Socket}; pub(crate) use smoltcp::socket::dns::{GetQueryResultError, StartQueryError}; pub use smoltcp::wire::{DnsQueryType, IpAddress}; @@ -48,9 +48,7 @@ impl<'a, D> DnsSocket<'a, D> where D: Driver + 'static, { - /// Create a new DNS socket using the provided stack and query storage. - /// - /// DNS servers are derived from the stack configuration. + /// Create a new DNS socket using the provided stack. /// /// NOTE: If using DHCP, make sure it has reconfigured the stack to ensure the DNS servers are updated. pub fn new(stack: &'a Stack) -> Self { @@ -58,7 +56,7 @@ where } /// Make a query for a given name and return the corresponding IP addresses. - pub async fn query(&self, name: &str, qtype: DnsQueryType) -> Result, Error> { + pub async fn query(&self, name: &str, qtype: DnsQueryType) -> Result, Error> { self.stack.dns_query(name, qtype).await } } diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index b63aa83d..5b6ab0e3 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -231,12 +231,9 @@ impl Stack { unreachable!() } + /// Make a query for a given name and return the corresponding IP addresses. #[cfg(feature = "dns")] - async fn dns_query( - &self, - name: &str, - qtype: dns::DnsQueryType, - ) -> Result, dns::Error> { + pub async fn dns_query(&self, name: &str, qtype: dns::DnsQueryType) -> Result, dns::Error> { let query = self.with_mut(|s, i| { if let Some(dns_handle) = i.dns_socket { let socket = s.sockets.get_mut::(dns_handle); From 6e68353a931cc268f61b4606c3a99bf8f5e96f6e Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 10 Feb 2023 18:30:17 +0100 Subject: [PATCH 06/14] attempt removing option --- embassy-net/src/lib.rs | 76 ++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 48 deletions(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 5b6ab0e3..627c0a0f 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -53,7 +53,7 @@ const MAX_QUERIES: usize = 2; pub struct StackResources { sockets: [SocketStorage<'static>; SOCK], #[cfg(feature = "dns")] - queries: Option<[Option; MAX_QUERIES]>, + queries: [Option; MAX_QUERIES], } impl StackResources { @@ -63,7 +63,7 @@ impl StackResources { Self { sockets: [SocketStorage::EMPTY; SOCK], #[cfg(feature = "dns")] - queries: Some([INIT; MAX_QUERIES]), + queries: [INIT; MAX_QUERIES], } } } @@ -117,7 +117,7 @@ struct Inner { #[cfg(feature = "dhcpv4")] dhcp_socket: Option, #[cfg(feature = "dns")] - dns_socket: Option, + dns_socket: SocketHandle, } pub(crate) struct SocketStack { @@ -156,15 +156,7 @@ impl Stack { let next_local_port = (random_seed % (LOCAL_PORT_MAX - LOCAL_PORT_MIN) as u64) as u16 + LOCAL_PORT_MIN; - let mut inner = Inner { - device, - link_up: false, - config: None, - #[cfg(feature = "dhcpv4")] - dhcp_socket: None, - #[cfg(feature = "dns")] - dns_socket: None, - }; + let mut socket = SocketStack { sockets, iface, @@ -172,12 +164,15 @@ impl Stack { next_local_port, }; - #[cfg(feature = "dns")] - { - if let Some(queries) = resources.queries.take() { - inner.dns_socket = Some(socket.sockets.add(dns::Socket::new(&[], queries))); - } - } + let mut inner = Inner { + device, + link_up: false, + config: None, + #[cfg(feature = "dhcpv4")] + dhcp_socket: None, + #[cfg(feature = "dns")] + dns_socket: socket.sockets.add(dns::Socket::new(&[], &mut resources.queries)), + }; match config { Config::Static(config) => { @@ -235,42 +230,29 @@ impl Stack { #[cfg(feature = "dns")] pub async fn dns_query(&self, name: &str, qtype: dns::DnsQueryType) -> Result, dns::Error> { let query = self.with_mut(|s, i| { - if let Some(dns_handle) = i.dns_socket { - let socket = s.sockets.get_mut::(dns_handle); - match socket.start_query(s.iface.context(), name, qtype) { - Ok(handle) => Ok(handle), - Err(e) => Err(e.into()), - } - } else { - Err(dns::Error::Failed) - } + let socket = s.sockets.get_mut::(i.dns_socket); + socket.start_query(s.iface.context(), name, qtype) })?; use embassy_hal_common::drop::OnDrop; let drop = OnDrop::new(|| { self.with_mut(|s, i| { - if let Some(dns_handle) = i.dns_socket { - let socket = s.sockets.get_mut::(dns_handle); - socket.cancel_query(query); - s.waker.wake(); - } + let socket = s.sockets.get_mut::(i.dns_socket); + socket.cancel_query(query); + s.waker.wake(); }) }); let res = poll_fn(|cx| { self.with_mut(|s, i| { - if let Some(dns_handle) = i.dns_socket { - let socket = s.sockets.get_mut::(dns_handle); - match socket.get_query_result(query) { - Ok(addrs) => Poll::Ready(Ok(addrs)), - Err(dns::GetQueryResultError::Pending) => { - socket.register_query_waker(query, cx.waker()); - Poll::Pending - } - Err(e) => Poll::Ready(Err(e.into())), + let socket = s.sockets.get_mut::(i.dns_socket); + match socket.get_query_result(query) { + Ok(addrs) => Poll::Ready(Ok(addrs)), + Err(dns::GetQueryResultError::Pending) => { + socket.register_query_waker(query, cx.waker()); + Poll::Pending } - } else { - Poll::Ready(Err(dns::Error::Failed)) + Err(e) => Poll::Ready(Err(e.into())), } }) }) @@ -322,11 +304,9 @@ impl Inner { } #[cfg(feature = "dns")] - if let Some(dns_socket) = self.dns_socket { - let socket = s.sockets.get_mut::(dns_socket); - let servers: Vec = config.dns_servers.iter().map(|c| IpAddress::Ipv4(*c)).collect(); - socket.update_servers(&servers[..]); - } + let socket = s.sockets.get_mut::(self.dns_socket); + let servers: Vec = config.dns_servers.iter().map(|c| IpAddress::Ipv4(*c)).collect(); + socket.update_servers(&servers[..]); self.config = Some(config) } From 472473d8c1cfc5985ee81fa8dac88c46b8fa64a9 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 10 Feb 2023 18:32:35 +0100 Subject: [PATCH 07/14] Create slice using ::Owned --- embassy-net/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 627c0a0f..25a7891c 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -171,7 +171,7 @@ impl Stack { #[cfg(feature = "dhcpv4")] dhcp_socket: None, #[cfg(feature = "dns")] - dns_socket: socket.sockets.add(dns::Socket::new(&[], &mut resources.queries)), + dns_socket: socket.sockets.add(dns::Socket::new(&[], managed::ManagedSlice::Borrowed(&mut resources.queries))), }; match config { From 48dff04d647a5303b9ed0a85ecce2f8a8f17ba0f Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 10 Feb 2023 18:34:21 +0100 Subject: [PATCH 08/14] Bump max queries --- embassy-net/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 25a7891c..e5f7479c 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -48,7 +48,8 @@ use crate::device::DriverAdapter; const LOCAL_PORT_MIN: u16 = 1025; const LOCAL_PORT_MAX: u16 = 65535; -const MAX_QUERIES: usize = 2; +#[cfg(feature = "dns")] +const MAX_QUERIES: usize = 4; pub struct StackResources { sockets: [SocketStorage<'static>; SOCK], From 32c3725631b72807b926187d29916468fed68c81 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 10 Feb 2023 18:44:51 +0100 Subject: [PATCH 09/14] add waker for DNS slots --- embassy-net/src/dns.rs | 4 +--- embassy-net/src/lib.rs | 39 +++++++++++++++++++++++++++++++-------- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/embassy-net/src/dns.rs b/embassy-net/src/dns.rs index 9b1b936c..2dd44a4e 100644 --- a/embassy-net/src/dns.rs +++ b/embassy-net/src/dns.rs @@ -10,8 +10,6 @@ use crate::{Driver, Stack}; #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { - /// No available query slot - NoFreeSlot, /// Invalid name InvalidName, /// Name too long @@ -29,7 +27,7 @@ impl From for Error { impl From for Error { fn from(e: StartQueryError) -> Self { match e { - StartQueryError::NoFreeSlot => Self::NoFreeSlot, + StartQueryError::NoFreeSlot => Self::Failed, StartQueryError::InvalidName => Self::InvalidName, StartQueryError::NameTooLong => Self::NameTooLong, } diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index e5f7479c..9d9de913 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -119,6 +119,8 @@ struct Inner { dhcp_socket: Option, #[cfg(feature = "dns")] dns_socket: SocketHandle, + #[cfg(feature = "dns")] + dns_waker: WakerRegistration, } pub(crate) struct SocketStack { @@ -157,7 +159,6 @@ impl Stack { let next_local_port = (random_seed % (LOCAL_PORT_MAX - LOCAL_PORT_MIN) as u64) as u16 + LOCAL_PORT_MIN; - let mut socket = SocketStack { sockets, iface, @@ -172,7 +173,12 @@ impl Stack { #[cfg(feature = "dhcpv4")] dhcp_socket: None, #[cfg(feature = "dns")] - dns_socket: socket.sockets.add(dns::Socket::new(&[], managed::ManagedSlice::Borrowed(&mut resources.queries))), + dns_socket: socket.sockets.add(dns::Socket::new( + &[], + managed::ManagedSlice::Borrowed(&mut resources.queries), + )), + #[cfg(feature = "dns")] + dns_waker: WakerRegistration::new(), }; match config { @@ -230,10 +236,20 @@ impl Stack { /// Make a query for a given name and return the corresponding IP addresses. #[cfg(feature = "dns")] pub async fn dns_query(&self, name: &str, qtype: dns::DnsQueryType) -> Result, dns::Error> { - let query = self.with_mut(|s, i| { - let socket = s.sockets.get_mut::(i.dns_socket); - socket.start_query(s.iface.context(), name, qtype) - })?; + let query = poll_fn(|cx| { + self.with_mut(|s, i| { + let socket = s.sockets.get_mut::(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?; use embassy_hal_common::drop::OnDrop; let drop = OnDrop::new(|| { @@ -241,6 +257,7 @@ impl Stack { let socket = s.sockets.get_mut::(i.dns_socket); socket.cancel_query(query); s.waker.wake(); + i.dns_waker.wake(); }) }); @@ -248,12 +265,18 @@ impl Stack { self.with_mut(|s, i| { let socket = s.sockets.get_mut::(i.dns_socket); match socket.get_query_result(query) { - Ok(addrs) => Poll::Ready(Ok(addrs)), + 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) => Poll::Ready(Err(e.into())), + Err(e) => { + i.dns_waker.wake(); + Poll::Ready(Err(e.into())) + } } }) }) From a7d3ef9122e45a808e1c401b33ee380a0c7c867d Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 10 Feb 2023 19:00:00 +0100 Subject: [PATCH 10/14] scope dns operations within a cfged block --- embassy-net/src/lib.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 9d9de913..bda5f9e1 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -328,9 +328,11 @@ impl Inner { } #[cfg(feature = "dns")] - let socket = s.sockets.get_mut::(self.dns_socket); - let servers: Vec = config.dns_servers.iter().map(|c| IpAddress::Ipv4(*c)).collect(); - socket.update_servers(&servers[..]); + { + let socket = s.sockets.get_mut::(self.dns_socket); + let servers: Vec = config.dns_servers.iter().map(|c| IpAddress::Ipv4(*c)).collect(); + socket.update_servers(&servers[..]); + } self.config = Some(config) } From 128a453163acbdd2f3e3e320bbd06eb38f196601 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 10 Feb 2023 19:04:54 +0100 Subject: [PATCH 11/14] remove unneeded features --- embassy-net/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 53778899..2d8c5c7b 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -13,7 +13,7 @@ target = "thumbv7em-none-eabi" [features] default = [] -std = ["smoltcp/alloc", "managed/std"] +std = [] defmt = ["dep:defmt", "smoltcp/defmt", "embassy-net-driver/defmt"] From a2b8921ff3e0b50da824c2242f0b90f9f8f9a0e2 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 10 Feb 2023 19:38:17 +0100 Subject: [PATCH 12/14] fix: cfg guard for ipv6 --- embassy-net/src/dns.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-net/src/dns.rs b/embassy-net/src/dns.rs index 2dd44a4e..97320e44 100644 --- a/embassy-net/src/dns.rs +++ b/embassy-net/src/dns.rs @@ -80,6 +80,7 @@ where if let Some(first) = addrs.get(0) { Ok(match first { IpAddress::Ipv4(addr) => IpAddr::V4(addr.0.into()), + #[cfg(feature = "proto-ipv6")] IpAddress::Ipv6(addr) => IpAddr::V6(addr.0.into()), }) } else { From a509af4bc00ae6945e568b268731e854e8ae3994 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 10 Feb 2023 23:00:16 +0100 Subject: [PATCH 13/14] exmaples/dns: don't use the socket. --- examples/std/src/bin/net_dns.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/examples/std/src/bin/net_dns.rs b/examples/std/src/bin/net_dns.rs index e787cb82..e1cc45a3 100644 --- a/examples/std/src/bin/net_dns.rs +++ b/examples/std/src/bin/net_dns.rs @@ -4,7 +4,7 @@ use std::default::Default; use clap::Parser; use embassy_executor::{Executor, Spawner}; -use embassy_net::dns::{DnsQueryType, DnsSocket}; +use embassy_net::dns::DnsQueryType; use embassy_net::{Config, Ipv4Address, Ipv4Cidr, Stack, StackResources}; use heapless::Vec; use log::*; @@ -65,17 +65,14 @@ async fn main_task(spawner: Spawner) { let seed = u64::from_le_bytes(seed); // Init network stack - let stack = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); + let stack: &Stack<_> = &*singleton!(Stack::new(device, config, singleton!(StackResources::<2>::new()), seed)); // Launch network task spawner.spawn(net_task(stack)).unwrap(); - // Then we can use it! - let socket = DnsSocket::new(stack); - let host = "example.com"; info!("querying host {:?}...", host); - match socket.query(host, DnsQueryType::A).await { + match stack.dns_query(host, DnsQueryType::A).await { Ok(r) => { info!("query response: {:?}", r); } From 76642b3a3cddaa226dcd741d5f9f8e9d01c2f3ac Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 10 Feb 2023 23:35:44 +0100 Subject: [PATCH 14/14] fix h7 examples --- examples/nrf52840/src/bin/usb_ethernet.rs | 23 +++++++++++++++++++++++ examples/stm32h7/Cargo.toml | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/examples/nrf52840/src/bin/usb_ethernet.rs b/examples/nrf52840/src/bin/usb_ethernet.rs index 97978089..430468ad 100644 --- a/examples/nrf52840/src/bin/usb_ethernet.rs +++ b/examples/nrf52840/src/bin/usb_ethernet.rs @@ -46,8 +46,31 @@ async fn net_task(stack: &'static Stack>) -> ! { stack.run().await } +#[inline(never)] +pub fn test_function() -> (usize, u32, [u32; 2]) { + let mut array = [3; 2]; + + let mut index = 0; + let mut result = 0; + + for x in [1, 2] { + if x == 1 { + array[1] = 99; + } else { + index = if x == 2 { 1 } else { 0 }; + + // grabs value from array[0], not array[1] + result = array[index]; + } + } + + (index, result, array) +} + #[embassy_executor::main] async fn main(spawner: Spawner) { + info!("{:?}", test_function()); + let p = embassy_nrf::init(Default::default()); let clock: pac::CLOCK = unsafe { mem::transmute(()) }; diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index bcf97641..a0413478 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -21,7 +21,7 @@ cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9" } embedded-hal-async = { version = "=0.2.0-alpha.0" } -embedded-nal-async = "0.3.0" +embedded-nal-async = "0.4.0" panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } heapless = { version = "0.7.5", default-features = false }