aboutsummaryrefslogtreecommitdiff
path: root/embassy-net
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-net')
-rw-r--r--embassy-net/Cargo.toml5
-rw-r--r--embassy-net/src/device.rs4
-rw-r--r--embassy-net/src/fmt.rs45
-rw-r--r--embassy-net/src/lib.rs385
-rw-r--r--embassy-net/src/tcp.rs4
-rw-r--r--embassy-net/src/udp.rs11
6 files changed, 265 insertions, 189 deletions
diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml
index 0c551f204..8aca92a68 100644
--- a/embassy-net/Cargo.toml
+++ b/embassy-net/Cargo.toml
@@ -9,6 +9,7 @@ categories = [
9 "embedded", 9 "embedded",
10 "no-std", 10 "no-std",
11 "asynchronous", 11 "asynchronous",
12 "network-programming",
12] 13]
13 14
14[package.metadata.embassy_docs] 15[package.metadata.embassy_docs]
@@ -50,8 +51,8 @@ smoltcp = { version = "0.10.0", default-features = false, features = [
50] } 51] }
51 52
52embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } 53embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
53embassy-time = { version = "0.1.2", path = "../embassy-time" } 54embassy-time = { version = "0.1.3", path = "../embassy-time" }
54embassy-sync = { version = "0.2.0", path = "../embassy-sync" } 55embassy-sync = { version = "0.3.0", path = "../embassy-sync" }
55embedded-io-async = { version = "0.5.0", optional = true } 56embedded-io-async = { version = "0.5.0", optional = true }
56 57
57managed = { version = "0.8.0", default-features = false, features = [ "map" ] } 58managed = { version = "0.8.0", default-features = false, features = [ "map" ] }
diff --git a/embassy-net/src/device.rs b/embassy-net/src/device.rs
index d29ab8970..8c2b7d31a 100644
--- a/embassy-net/src/device.rs
+++ b/embassy-net/src/device.rs
@@ -22,13 +22,13 @@ where
22 22
23 fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { 23 fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
24 self.inner 24 self.inner
25 .receive(self.cx.as_deref_mut().unwrap()) 25 .receive(unwrap!(self.cx.as_deref_mut()))
26 .map(|(rx, tx)| (RxTokenAdapter(rx), TxTokenAdapter(tx))) 26 .map(|(rx, tx)| (RxTokenAdapter(rx), TxTokenAdapter(tx)))
27 } 27 }
28 28
29 /// Construct a transmit token. 29 /// Construct a transmit token.
30 fn transmit(&mut self, _timestamp: Instant) -> Option<Self::TxToken<'_>> { 30 fn transmit(&mut self, _timestamp: Instant) -> Option<Self::TxToken<'_>> {
31 self.inner.transmit(self.cx.as_deref_mut().unwrap()).map(TxTokenAdapter) 31 self.inner.transmit(unwrap!(self.cx.as_deref_mut())).map(TxTokenAdapter)
32 } 32 }
33 33
34 /// Get a description of device capabilities. 34 /// Get a description of device capabilities.
diff --git a/embassy-net/src/fmt.rs b/embassy-net/src/fmt.rs
index 066970813..78e583c1c 100644
--- a/embassy-net/src/fmt.rs
+++ b/embassy-net/src/fmt.rs
@@ -1,6 +1,8 @@
1#![macro_use] 1#![macro_use]
2#![allow(unused_macros)] 2#![allow(unused_macros)]
3 3
4use core::fmt::{Debug, Display, LowerHex};
5
4#[cfg(all(feature = "defmt", feature = "log"))] 6#[cfg(all(feature = "defmt", feature = "log"))]
5compile_error!("You may not enable both `defmt` and `log` features."); 7compile_error!("You may not enable both `defmt` and `log` features.");
6 8
@@ -81,14 +83,17 @@ macro_rules! todo {
81 }; 83 };
82} 84}
83 85
86#[cfg(not(feature = "defmt"))]
84macro_rules! unreachable { 87macro_rules! unreachable {
85 ($($x:tt)*) => { 88 ($($x:tt)*) => {
86 { 89 ::core::unreachable!($($x)*)
87 #[cfg(not(feature = "defmt"))] 90 };
88 ::core::unreachable!($($x)*); 91}
89 #[cfg(feature = "defmt")] 92
90 ::defmt::unreachable!($($x)*); 93#[cfg(feature = "defmt")]
91 } 94macro_rules! unreachable {
95 ($($x:tt)*) => {
96 ::defmt::unreachable!($($x)*)
92 }; 97 };
93} 98}
94 99
@@ -223,3 +228,31 @@ impl<T, E> Try for Result<T, E> {
223 self 228 self
224 } 229 }
225} 230}
231
232#[allow(unused)]
233pub(crate) struct Bytes<'a>(pub &'a [u8]);
234
235impl<'a> Debug for Bytes<'a> {
236 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
237 write!(f, "{:#02x?}", self.0)
238 }
239}
240
241impl<'a> Display for Bytes<'a> {
242 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
243 write!(f, "{:#02x?}", self.0)
244 }
245}
246
247impl<'a> LowerHex for Bytes<'a> {
248 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
249 write!(f, "{:#02x?}", self.0)
250 }
251}
252
253#[cfg(feature = "defmt")]
254impl<'a> defmt::Format for Bytes<'a> {
255 fn format(&self, fmt: defmt::Formatter) {
256 defmt::write!(fmt, "{:02x}", self.0)
257 }
258}
diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs
index 2fb34f43a..0d7ac47a2 100644
--- a/embassy-net/src/lib.rs
+++ b/embassy-net/src/lib.rs
@@ -3,6 +3,9 @@
3#![warn(missing_docs)] 3#![warn(missing_docs)]
4#![doc = include_str!("../README.md")] 4#![doc = include_str!("../README.md")]
5 5
6#[cfg(not(any(feature = "proto-ipv4", feature = "proto-ipv6")))]
7compile_error!("You must enable at least one of the following features: proto-ipv4, proto-ipv6");
8
6// This mod MUST go first, so that the others see its macros. 9// This mod MUST go first, so that the others see its macros.
7pub(crate) mod fmt; 10pub(crate) mod fmt;
8 11
@@ -20,7 +23,7 @@ use core::future::{poll_fn, Future};
20use core::task::{Context, Poll}; 23use core::task::{Context, Poll};
21 24
22pub use embassy_net_driver as driver; 25pub use embassy_net_driver as driver;
23use embassy_net_driver::{Driver, LinkState, Medium}; 26use embassy_net_driver::{Driver, LinkState};
24use embassy_sync::waitqueue::WakerRegistration; 27use embassy_sync::waitqueue::WakerRegistration;
25use embassy_time::{Instant, Timer}; 28use embassy_time::{Instant, Timer};
26use futures::pin_mut; 29use futures::pin_mut;
@@ -133,6 +136,8 @@ impl Default for DhcpConfig {
133} 136}
134 137
135/// Network stack configuration. 138/// Network stack configuration.
139#[derive(Debug, Clone, Default)]
140#[non_exhaustive]
136pub struct Config { 141pub struct Config {
137 /// IPv4 configuration 142 /// IPv4 configuration
138 #[cfg(feature = "proto-ipv4")] 143 #[cfg(feature = "proto-ipv4")]
@@ -163,10 +168,11 @@ impl Config {
163 } 168 }
164 } 169 }
165 170
166 /// IPv6 configuration with dynamic addressing. 171 /// IPv4 configuration with dynamic addressing.
167 /// 172 ///
168 /// # Example 173 /// # Example
169 /// ```rust 174 /// ```rust
175 /// # use embassy_net::Config;
170 /// let _cfg = Config::dhcpv4(Default::default()); 176 /// let _cfg = Config::dhcpv4(Default::default());
171 /// ``` 177 /// ```
172 #[cfg(feature = "dhcpv4")] 178 #[cfg(feature = "dhcpv4")]
@@ -181,23 +187,27 @@ impl Config {
181 187
182/// Network stack IPv4 configuration. 188/// Network stack IPv4 configuration.
183#[cfg(feature = "proto-ipv4")] 189#[cfg(feature = "proto-ipv4")]
190#[derive(Debug, Clone, Default)]
184pub enum ConfigV4 { 191pub enum ConfigV4 {
192 /// Do not configure IPv4.
193 #[default]
194 None,
185 /// Use a static IPv4 address configuration. 195 /// Use a static IPv4 address configuration.
186 Static(StaticConfigV4), 196 Static(StaticConfigV4),
187 /// Use DHCP to obtain an IP address configuration. 197 /// Use DHCP to obtain an IP address configuration.
188 #[cfg(feature = "dhcpv4")] 198 #[cfg(feature = "dhcpv4")]
189 Dhcp(DhcpConfig), 199 Dhcp(DhcpConfig),
190 /// Do not configure IPv6.
191 None,
192} 200}
193 201
194/// Network stack IPv6 configuration. 202/// Network stack IPv6 configuration.
195#[cfg(feature = "proto-ipv6")] 203#[cfg(feature = "proto-ipv6")]
204#[derive(Debug, Clone, Default)]
196pub enum ConfigV6 { 205pub enum ConfigV6 {
197 /// Use a static IPv6 address configuration.
198 Static(StaticConfigV6),
199 /// Do not configure IPv6. 206 /// Do not configure IPv6.
207 #[default]
200 None, 208 None,
209 /// Use a static IPv6 address configuration.
210 Static(StaticConfigV6),
201} 211}
202 212
203/// A network stack. 213/// A network stack.
@@ -217,6 +227,7 @@ struct Inner<D: Driver> {
217 static_v6: Option<StaticConfigV6>, 227 static_v6: Option<StaticConfigV6>,
218 #[cfg(feature = "dhcpv4")] 228 #[cfg(feature = "dhcpv4")]
219 dhcp_socket: Option<SocketHandle>, 229 dhcp_socket: Option<SocketHandle>,
230 config_waker: WakerRegistration,
220 #[cfg(feature = "dns")] 231 #[cfg(feature = "dns")]
221 dns_socket: SocketHandle, 232 dns_socket: SocketHandle,
222 #[cfg(feature = "dns")] 233 #[cfg(feature = "dns")]
@@ -240,11 +251,14 @@ fn to_smoltcp_hardware_address(addr: driver::HardwareAddress) -> HardwareAddress
240 driver::HardwareAddress::Ip => HardwareAddress::Ip, 251 driver::HardwareAddress::Ip => HardwareAddress::Ip,
241 252
242 #[allow(unreachable_patterns)] 253 #[allow(unreachable_patterns)]
243 _ => panic!("Unsupported address {:?}. Make sure to enable medium-ethernet or medium-ieee802154 in embassy-net's Cargo features.", addr), 254 _ => panic!(
255 "Unsupported medium {:?}. Make sure to enable the right medium feature in embassy-net's Cargo features.",
256 addr
257 ),
244 } 258 }
245} 259}
246 260
247impl<D: Driver + 'static> Stack<D> { 261impl<D: Driver> Stack<D> {
248 /// Create a new network stack. 262 /// Create a new network stack.
249 pub fn new<const SOCK: usize>( 263 pub fn new<const SOCK: usize>(
250 mut device: D, 264 mut device: D,
@@ -276,7 +290,6 @@ impl<D: Driver + 'static> Stack<D> {
276 next_local_port, 290 next_local_port,
277 }; 291 };
278 292
279 #[cfg_attr(feature = "medium-ieee802154", allow(unused_mut))]
280 let mut inner = Inner { 293 let mut inner = Inner {
281 device, 294 device,
282 link_up: false, 295 link_up: false,
@@ -286,6 +299,7 @@ impl<D: Driver + 'static> Stack<D> {
286 static_v6: None, 299 static_v6: None,
287 #[cfg(feature = "dhcpv4")] 300 #[cfg(feature = "dhcpv4")]
288 dhcp_socket: None, 301 dhcp_socket: None,
302 config_waker: WakerRegistration::new(),
289 #[cfg(feature = "dns")] 303 #[cfg(feature = "dns")]
290 dns_socket: socket.sockets.add(dns::Socket::new( 304 dns_socket: socket.sockets.add(dns::Socket::new(
291 &[], 305 &[],
@@ -295,30 +309,11 @@ impl<D: Driver + 'static> Stack<D> {
295 dns_waker: WakerRegistration::new(), 309 dns_waker: WakerRegistration::new(),
296 }; 310 };
297 311
298 #[cfg(feature = "medium-ieee802154")]
299 let _ = config;
300
301 #[cfg(feature = "proto-ipv4")] 312 #[cfg(feature = "proto-ipv4")]
302 match config.ipv4 { 313 inner.set_config_v4(&mut socket, config.ipv4);
303 ConfigV4::Static(config) => {
304 inner.apply_config_v4(&mut socket, config);
305 }
306 #[cfg(feature = "dhcpv4")]
307 ConfigV4::Dhcp(config) => {
308 let mut dhcp_socket = smoltcp::socket::dhcpv4::Socket::new();
309 inner.apply_dhcp_config(&mut dhcp_socket, config);
310 let handle = socket.sockets.add(dhcp_socket);
311 inner.dhcp_socket = Some(handle);
312 }
313 ConfigV4::None => {}
314 }
315 #[cfg(feature = "proto-ipv6")] 314 #[cfg(feature = "proto-ipv6")]
316 match config.ipv6 { 315 inner.set_config_v6(&mut socket, config.ipv6);
317 ConfigV6::Static(config) => { 316 inner.apply_static_config(&mut socket);
318 inner.apply_config_v6(&mut socket, config);
319 }
320 ConfigV6::None => {}
321 }
322 317
323 Self { 318 Self {
324 socket: RefCell::new(socket), 319 socket: RefCell::new(socket),
@@ -371,16 +366,86 @@ impl<D: Driver + 'static> Stack<D> {
371 v4_up || v6_up 366 v4_up || v6_up
372 } 367 }
373 368
369 /// Wait for the network stack to obtain a valid IP configuration.
370 ///
371 /// ## Notes:
372 /// - Ensure [`Stack::run`] has been called before using this function.
373 ///
374 /// - This function may never return (e.g. if no configuration is obtained through DHCP).
375 /// The caller is supposed to handle a timeout for this case.
376 ///
377 /// ## Example
378 /// ```ignore
379 /// let config = embassy_net::Config::dhcpv4(Default::default());
380 ///// Init network stack
381 /// let stack = &*make_static!(embassy_net::Stack::new(
382 /// device,
383 /// config,
384 /// make_static!(embassy_net::StackResources::<2>::new()),
385 /// seed
386 /// ));
387 /// // Launch network task that runs `stack.run().await`
388 /// spawner.spawn(net_task(stack)).unwrap();
389 /// // Wait for DHCP config
390 /// stack.wait_config_up().await;
391 /// // use the network stack
392 /// // ...
393 /// ```
394 pub async fn wait_config_up(&self) {
395 // If the config is up already, we can return immediately.
396 if self.is_config_up() {
397 return;
398 }
399
400 poll_fn(|cx| {
401 if self.is_config_up() {
402 Poll::Ready(())
403 } else {
404 // If the config is not up, we register a waker that is woken up
405 // when a config is applied (static or DHCP).
406 trace!("Waiting for config up");
407
408 self.with_mut(|_, i| {
409 i.config_waker.register(cx.waker());
410 });
411
412 Poll::Pending
413 }
414 })
415 .await;
416 }
417
374 /// Get the current IPv4 configuration. 418 /// Get the current IPv4 configuration.
419 ///
420 /// If using DHCP, this will be None if DHCP hasn't been able to
421 /// acquire an IP address, or Some if it has.
375 #[cfg(feature = "proto-ipv4")] 422 #[cfg(feature = "proto-ipv4")]
376 pub fn config_v4(&self) -> Option<StaticConfigV4> { 423 pub fn config_v4(&self) -> Option<StaticConfigV4> {
377 self.with(|_s, i| i.static_v4.clone()) 424 self.with(|_, i| i.static_v4.clone())
378 } 425 }
379 426
380 /// Get the current IPv6 configuration. 427 /// Get the current IPv6 configuration.
381 #[cfg(feature = "proto-ipv6")] 428 #[cfg(feature = "proto-ipv6")]
382 pub fn config_v6(&self) -> Option<StaticConfigV6> { 429 pub fn config_v6(&self) -> Option<StaticConfigV6> {
383 self.with(|_s, i| i.static_v6.clone()) 430 self.with(|_, i| i.static_v6.clone())
431 }
432
433 /// Set the IPv4 configuration.
434 #[cfg(feature = "proto-ipv4")]
435 pub fn set_config_v4(&self, config: ConfigV4) {
436 self.with_mut(|s, i| {
437 i.set_config_v4(s, config);
438 i.apply_static_config(s);
439 })
440 }
441
442 /// Set the IPv6 configuration.
443 #[cfg(feature = "proto-ipv6")]
444 pub fn set_config_v6(&self, config: ConfigV6) {
445 self.with_mut(|s, i| {
446 i.set_config_v6(s, config);
447 i.apply_static_config(s);
448 })
384 } 449 }
385 450
386 /// Run the network stack. 451 /// Run the network stack.
@@ -490,7 +555,7 @@ impl<D: Driver + 'static> Stack<D> {
490} 555}
491 556
492#[cfg(feature = "igmp")] 557#[cfg(feature = "igmp")]
493impl<D: Driver + 'static> Stack<D> { 558impl<D: Driver> Stack<D> {
494 /// Join a multicast group. 559 /// Join a multicast group.
495 pub async fn join_multicast_group<T>(&self, addr: T) -> Result<bool, MulticastError> 560 pub async fn join_multicast_group<T>(&self, addr: T) -> Result<bool, MulticastError>
496 where 561 where
@@ -580,168 +645,129 @@ impl SocketStack {
580 } 645 }
581} 646}
582 647
583impl<D: Driver + 'static> Inner<D> { 648impl<D: Driver> Inner<D> {
584 #[cfg(feature = "proto-ipv4")] 649 #[cfg(feature = "proto-ipv4")]
585 fn apply_config_v4(&mut self, s: &mut SocketStack, config: StaticConfigV4) { 650 pub fn set_config_v4(&mut self, _s: &mut SocketStack, config: ConfigV4) {
586 debug!("Acquired IP configuration:"); 651 // Handle static config.
587 652 self.static_v4 = match config.clone() {
588 debug!(" IP address: {}", config.address); 653 ConfigV4::None => None,
589 s.iface.update_ip_addrs(|addrs| { 654 #[cfg(feature = "dhcpv4")]
590 if let Some((index, _)) = addrs 655 ConfigV4::Dhcp(_) => None,
591 .iter() 656 ConfigV4::Static(c) => Some(c),
592 .enumerate() 657 };
593 .find(|(_, &addr)| matches!(addr, IpCidr::Ipv4(_)))
594 {
595 addrs.remove(index);
596 }
597 addrs.push(IpCidr::Ipv4(config.address)).unwrap();
598 });
599
600 #[cfg(feature = "medium-ip")]
601 let skip_gateway = self.device.capabilities().medium != Medium::Ip;
602 #[cfg(not(feature = "medium-ip"))]
603 let skip_gateway = false;
604
605 if !skip_gateway {
606 if let Some(gateway) = config.gateway {
607 debug!(" Default gateway: {}", gateway);
608 s.iface.routes_mut().add_default_ipv4_route(gateway).unwrap();
609 } else {
610 debug!(" Default gateway: None");
611 s.iface.routes_mut().remove_default_ipv4_route();
612 }
613 }
614 for (i, s) in config.dns_servers.iter().enumerate() {
615 debug!(" DNS server {}: {}", i, s);
616 }
617 658
618 self.static_v4 = Some(config); 659 // Handle DHCP config.
660 #[cfg(feature = "dhcpv4")]
661 match config {
662 ConfigV4::Dhcp(c) => {
663 // Create the socket if it doesn't exist.
664 if self.dhcp_socket.is_none() {
665 let socket = smoltcp::socket::dhcpv4::Socket::new();
666 let handle = _s.sockets.add(socket);
667 self.dhcp_socket = Some(handle);
668 }
619 669
620 #[cfg(feature = "dns")] 670 // Configure it
621 { 671 let socket = _s.sockets.get_mut::<dhcpv4::Socket>(unwrap!(self.dhcp_socket));
622 self.update_dns_servers(s) 672 socket.set_ignore_naks(c.ignore_naks);
673 socket.set_max_lease_duration(c.max_lease_duration.map(crate::time::duration_to_smoltcp));
674 socket.set_ports(c.server_port, c.client_port);
675 socket.set_retry_config(c.retry_config);
676 socket.reset();
677 }
678 _ => {
679 // Remove DHCP socket if any.
680 if let Some(socket) = self.dhcp_socket {
681 _s.sockets.remove(socket);
682 self.dhcp_socket = None;
683 }
684 }
623 } 685 }
624 } 686 }
625 687
626 /// Replaces the current IPv6 static configuration with a newly supplied config.
627 #[cfg(feature = "proto-ipv6")] 688 #[cfg(feature = "proto-ipv6")]
628 fn apply_config_v6(&mut self, s: &mut SocketStack, config: StaticConfigV6) { 689 pub fn set_config_v6(&mut self, _s: &mut SocketStack, config: ConfigV6) {
629 #[cfg(feature = "medium-ethernet")] 690 self.static_v6 = match config {
630 let medium = self.device.capabilities().medium; 691 ConfigV6::None => None,
692 ConfigV6::Static(c) => Some(c),
693 };
694 }
631 695
632 debug!("Acquired IPv6 configuration:"); 696 fn apply_static_config(&mut self, s: &mut SocketStack) {
697 let mut addrs = Vec::new();
698 #[cfg(feature = "dns")]
699 let mut dns_servers: Vec<_, 6> = Vec::new();
700 #[cfg(feature = "proto-ipv4")]
701 let mut gateway_v4 = None;
702 #[cfg(feature = "proto-ipv6")]
703 let mut gateway_v6 = None;
633 704
634 debug!(" IP address: {}", config.address); 705 #[cfg(feature = "proto-ipv4")]
635 s.iface.update_ip_addrs(|addrs| { 706 if let Some(config) = &self.static_v4 {
636 if let Some((index, _)) = addrs 707 debug!("IPv4: UP");
637 .iter() 708 debug!(" IP address: {:?}", config.address);
638 .enumerate() 709 debug!(" Default gateway: {:?}", config.gateway);
639 .find(|(_, &addr)| matches!(addr, IpCidr::Ipv6(_)))
640 {
641 addrs.remove(index);
642 }
643 addrs.push(IpCidr::Ipv6(config.address)).unwrap();
644 });
645 710
646 #[cfg(feature = "medium-ethernet")] 711 unwrap!(addrs.push(IpCidr::Ipv4(config.address)).ok());
647 if Medium::Ethernet == medium { 712 gateway_v4 = config.gateway.into();
648 if let Some(gateway) = config.gateway { 713 #[cfg(feature = "dns")]
649 debug!(" Default gateway: {}", gateway); 714 for s in &config.dns_servers {
650 s.iface.routes_mut().add_default_ipv6_route(gateway).unwrap(); 715 debug!(" DNS server: {:?}", s);
651 } else { 716 unwrap!(dns_servers.push(s.clone().into()).ok());
652 debug!(" Default gateway: None");
653 s.iface.routes_mut().remove_default_ipv6_route();
654 } 717 }
655 } 718 } else {
656 for (i, s) in config.dns_servers.iter().enumerate() { 719 info!("IPv4: DOWN");
657 debug!(" DNS server {}: {}", i, s);
658 } 720 }
659 721
660 self.static_v6 = Some(config); 722 #[cfg(feature = "proto-ipv6")]
723 if let Some(config) = &self.static_v6 {
724 debug!("IPv6: UP");
725 debug!(" IP address: {:?}", config.address);
726 debug!(" Default gateway: {:?}", config.gateway);
661 727
662 #[cfg(feature = "dns")] 728 unwrap!(addrs.push(IpCidr::Ipv6(config.address)).ok());
663 { 729 gateway_v6 = config.gateway.into();
664 self.update_dns_servers(s) 730 #[cfg(feature = "dns")]
731 for s in &config.dns_servers {
732 debug!(" DNS server: {:?}", s);
733 unwrap!(dns_servers.push(s.clone().into()).ok());
734 }
735 } else {
736 info!("IPv6: DOWN");
665 } 737 }
666 }
667 738
668 #[cfg(feature = "dns")] 739 // Apply addresses
669 fn update_dns_servers(&mut self, s: &mut SocketStack) { 740 s.iface.update_ip_addrs(|a| *a = addrs);
670 let socket = s.sockets.get_mut::<smoltcp::socket::dns::Socket>(self.dns_socket);
671 741
672 let servers_v4; 742 // Apply gateways
673 #[cfg(feature = "proto-ipv4")] 743 #[cfg(feature = "proto-ipv4")]
674 { 744 if let Some(gateway) = gateway_v4 {
675 servers_v4 = self 745 unwrap!(s.iface.routes_mut().add_default_ipv4_route(gateway));
676 .static_v4 746 } else {
677 .iter() 747 s.iface.routes_mut().remove_default_ipv4_route();
678 .flat_map(|cfg| cfg.dns_servers.iter().map(|c| IpAddress::Ipv4(*c)));
679 };
680 #[cfg(not(feature = "proto-ipv4"))]
681 {
682 servers_v4 = core::iter::empty();
683 } 748 }
684
685 let servers_v6;
686 #[cfg(feature = "proto-ipv6")] 749 #[cfg(feature = "proto-ipv6")]
687 { 750 if let Some(gateway) = gateway_v6 {
688 servers_v6 = self 751 unwrap!(s.iface.routes_mut().add_default_ipv6_route(gateway));
689 .static_v6 752 } else {
690 .iter() 753 s.iface.routes_mut().remove_default_ipv6_route();
691 .flat_map(|cfg| cfg.dns_servers.iter().map(|c| IpAddress::Ipv6(*c)));
692 } 754 }
693 #[cfg(not(feature = "proto-ipv6"))]
694 {
695 servers_v6 = core::iter::empty();
696 }
697
698 // Prefer the v6 DNS servers over the v4 servers
699 let servers: Vec<IpAddress, 6> = servers_v6.chain(servers_v4).collect();
700 socket.update_servers(&servers[..]);
701 }
702 755
703 #[cfg(feature = "dhcpv4")] 756 // Apply DNS servers
704 fn apply_dhcp_config(&self, socket: &mut smoltcp::socket::dhcpv4::Socket, config: DhcpConfig) { 757 #[cfg(feature = "dns")]
705 socket.set_ignore_naks(config.ignore_naks); 758 s.sockets
706 socket.set_max_lease_duration(config.max_lease_duration.map(crate::time::duration_to_smoltcp)); 759 .get_mut::<smoltcp::socket::dns::Socket>(self.dns_socket)
707 socket.set_ports(config.server_port, config.client_port); 760 .update_servers(&dns_servers[..]);
708 socket.set_retry_config(config.retry_config);
709 }
710 761
711 #[cfg(feature = "dhcpv4")] 762 self.config_waker.wake();
712 fn unapply_config_v4(&mut self, s: &mut SocketStack) {
713 #[cfg(feature = "medium-ethernet")]
714 let medium = self.device.capabilities().medium;
715 debug!("Lost IP configuration");
716 s.iface.update_ip_addrs(|ip_addrs| {
717 #[cfg(feature = "proto-ipv4")]
718 if let Some((index, _)) = ip_addrs
719 .iter()
720 .enumerate()
721 .find(|(_, &addr)| matches!(addr, IpCidr::Ipv4(_)))
722 {
723 ip_addrs.remove(index);
724 }
725 });
726 #[cfg(feature = "medium-ethernet")]
727 if medium == Medium::Ethernet {
728 #[cfg(feature = "proto-ipv4")]
729 {
730 s.iface.routes_mut().remove_default_ipv4_route();
731 }
732 }
733 #[cfg(feature = "proto-ipv4")]
734 {
735 self.static_v4 = None
736 }
737 } 763 }
738 764
739 fn poll(&mut self, cx: &mut Context<'_>, s: &mut SocketStack) { 765 fn poll(&mut self, cx: &mut Context<'_>, s: &mut SocketStack) {
740 s.waker.register(cx.waker()); 766 s.waker.register(cx.waker());
741 767
742 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] 768 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
743 if self.device.capabilities().medium == Medium::Ethernet 769 if self.device.capabilities().medium == embassy_net_driver::Medium::Ethernet
744 || self.device.capabilities().medium == Medium::Ieee802154 770 || self.device.capabilities().medium == embassy_net_driver::Medium::Ieee802154
745 { 771 {
746 s.iface 772 s.iface
747 .set_hardware_addr(to_smoltcp_hardware_address(self.device.hardware_address())); 773 .set_hardware_addr(to_smoltcp_hardware_address(self.device.hardware_address()));
@@ -763,6 +789,9 @@ impl<D: Driver + 'static> Inner<D> {
763 info!("link_up = {:?}", self.link_up); 789 info!("link_up = {:?}", self.link_up);
764 } 790 }
765 791
792 #[allow(unused_mut)]
793 let mut apply_config = false;
794
766 #[cfg(feature = "dhcpv4")] 795 #[cfg(feature = "dhcpv4")]
767 if let Some(dhcp_handle) = self.dhcp_socket { 796 if let Some(dhcp_handle) = self.dhcp_socket {
768 let socket = s.sockets.get_mut::<dhcpv4::Socket>(dhcp_handle); 797 let socket = s.sockets.get_mut::<dhcpv4::Socket>(dhcp_handle);
@@ -770,25 +799,29 @@ impl<D: Driver + 'static> Inner<D> {
770 if self.link_up { 799 if self.link_up {
771 match socket.poll() { 800 match socket.poll() {
772 None => {} 801 None => {}
773 Some(dhcpv4::Event::Deconfigured) => self.unapply_config_v4(s), 802 Some(dhcpv4::Event::Deconfigured) => {
803 self.static_v4 = None;
804 apply_config = true;
805 }
774 Some(dhcpv4::Event::Configured(config)) => { 806 Some(dhcpv4::Event::Configured(config)) => {
775 let config = StaticConfigV4 { 807 self.static_v4 = Some(StaticConfigV4 {
776 address: config.address, 808 address: config.address,
777 gateway: config.router, 809 gateway: config.router,
778 dns_servers: config.dns_servers, 810 dns_servers: config.dns_servers,
779 }; 811 });
780 self.apply_config_v4(s, config) 812 apply_config = true;
781 } 813 }
782 } 814 }
783 } else if old_link_up { 815 } else if old_link_up {
784 socket.reset(); 816 socket.reset();
785 self.unapply_config_v4(s); 817 self.static_v4 = None;
818 apply_config = true;
786 } 819 }
787 } 820 }
788 //if old_link_up || self.link_up { 821
789 // self.poll_configurator(timestamp) 822 if apply_config {
790 //} 823 self.apply_static_config(s);
791 // 824 }
792 825
793 if let Some(poll_at) = s.iface.poll_at(timestamp, &mut s.sockets) { 826 if let Some(poll_at) = s.iface.poll_at(timestamp, &mut s.sockets) {
794 let t = Timer::at(instant_from_smoltcp(poll_at)); 827 let t = Timer::at(instant_from_smoltcp(poll_at));
diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs
index c92ad2d2e..a12fd382a 100644
--- a/embassy-net/src/tcp.rs
+++ b/embassy-net/src/tcp.rs
@@ -440,7 +440,7 @@ impl<'d> TcpIo<'d> {
440 Poll::Ready(Err(Error::ConnectionReset)) 440 Poll::Ready(Err(Error::ConnectionReset))
441 } 441 }
442 } else { 442 } else {
443 Poll::Ready(match s.send(f.take().unwrap()) { 443 Poll::Ready(match s.send(unwrap!(f.take())) {
444 // Connection reset. TODO: this can also be timeouts etc, investigate. 444 // Connection reset. TODO: this can also be timeouts etc, investigate.
445 Err(tcp::SendError::InvalidState) => Err(Error::ConnectionReset), 445 Err(tcp::SendError::InvalidState) => Err(Error::ConnectionReset),
446 Ok(r) => Ok(r), 446 Ok(r) => Ok(r),
@@ -468,7 +468,7 @@ impl<'d> TcpIo<'d> {
468 Poll::Ready(Err(Error::ConnectionReset)) 468 Poll::Ready(Err(Error::ConnectionReset))
469 } 469 }
470 } else { 470 } else {
471 Poll::Ready(match s.recv(f.take().unwrap()) { 471 Poll::Ready(match s.recv(unwrap!(f.take())) {
472 // Connection reset. TODO: this can also be timeouts etc, investigate. 472 // Connection reset. TODO: this can also be timeouts etc, investigate.
473 Err(tcp::RecvError::Finished) | Err(tcp::RecvError::InvalidState) => { 473 Err(tcp::RecvError::Finished) | Err(tcp::RecvError::InvalidState) => {
474 Err(Error::ConnectionReset) 474 Err(Error::ConnectionReset)
diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs
index 0a5a7b8f6..61058c1ba 100644
--- a/embassy-net/src/udp.rs
+++ b/embassy-net/src/udp.rs
@@ -29,6 +29,8 @@ pub enum BindError {
29pub enum Error { 29pub enum Error {
30 /// No route to host. 30 /// No route to host.
31 NoRoute, 31 NoRoute,
32 /// Socket not bound to an outgoing port.
33 SocketNotBound,
32} 34}
33 35
34/// An UDP socket. 36/// An UDP socket.
@@ -155,7 +157,14 @@ impl<'a> UdpSocket<'a> {
155 s.register_send_waker(cx.waker()); 157 s.register_send_waker(cx.waker());
156 Poll::Pending 158 Poll::Pending
157 } 159 }
158 Err(udp::SendError::Unaddressable) => Poll::Ready(Err(Error::NoRoute)), 160 Err(udp::SendError::Unaddressable) => {
161 // If no sender/outgoing port is specified, there is not really "no route"
162 if s.endpoint().port == 0 {
163 Poll::Ready(Err(Error::SocketNotBound))
164 } else {
165 Poll::Ready(Err(Error::NoRoute))
166 }
167 }
159 }) 168 })
160 } 169 }
161 170