aboutsummaryrefslogtreecommitdiff
path: root/embassy-net
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2023-08-25 19:48:45 +0200
committerDario Nieuwenhuis <[email protected]>2023-08-25 20:26:46 +0200
commitcc8961034e53d4fc2ac4539096c2a67059eb60b7 (patch)
tree78c99dafc7a4b4d6ac3c6271df82799daa15dc77 /embassy-net
parent2a6b743b9e8dbeef6b02320b2485f3f1efe4a337 (diff)
net: allow changing IP config at runtime.
Diffstat (limited to 'embassy-net')
-rw-r--r--embassy-net/src/lib.rs320
1 files changed, 148 insertions, 172 deletions
diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs
index 2fb34f43a..9f8812894 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")]
@@ -181,23 +186,27 @@ impl Config {
181 186
182/// Network stack IPv4 configuration. 187/// Network stack IPv4 configuration.
183#[cfg(feature = "proto-ipv4")] 188#[cfg(feature = "proto-ipv4")]
189#[derive(Debug, Clone, Default)]
184pub enum ConfigV4 { 190pub enum ConfigV4 {
191 /// Do not configure IPv4.
192 #[default]
193 None,
185 /// Use a static IPv4 address configuration. 194 /// Use a static IPv4 address configuration.
186 Static(StaticConfigV4), 195 Static(StaticConfigV4),
187 /// Use DHCP to obtain an IP address configuration. 196 /// Use DHCP to obtain an IP address configuration.
188 #[cfg(feature = "dhcpv4")] 197 #[cfg(feature = "dhcpv4")]
189 Dhcp(DhcpConfig), 198 Dhcp(DhcpConfig),
190 /// Do not configure IPv6.
191 None,
192} 199}
193 200
194/// Network stack IPv6 configuration. 201/// Network stack IPv6 configuration.
195#[cfg(feature = "proto-ipv6")] 202#[cfg(feature = "proto-ipv6")]
203#[derive(Debug, Clone, Default)]
196pub enum ConfigV6 { 204pub enum ConfigV6 {
197 /// Use a static IPv6 address configuration.
198 Static(StaticConfigV6),
199 /// Do not configure IPv6. 205 /// Do not configure IPv6.
206 #[default]
200 None, 207 None,
208 /// Use a static IPv6 address configuration.
209 Static(StaticConfigV6),
201} 210}
202 211
203/// A network stack. 212/// A network stack.
@@ -276,7 +285,6 @@ impl<D: Driver + 'static> Stack<D> {
276 next_local_port, 285 next_local_port,
277 }; 286 };
278 287
279 #[cfg_attr(feature = "medium-ieee802154", allow(unused_mut))]
280 let mut inner = Inner { 288 let mut inner = Inner {
281 device, 289 device,
282 link_up: false, 290 link_up: false,
@@ -295,30 +303,11 @@ impl<D: Driver + 'static> Stack<D> {
295 dns_waker: WakerRegistration::new(), 303 dns_waker: WakerRegistration::new(),
296 }; 304 };
297 305
298 #[cfg(feature = "medium-ieee802154")]
299 let _ = config;
300
301 #[cfg(feature = "proto-ipv4")] 306 #[cfg(feature = "proto-ipv4")]
302 match config.ipv4 { 307 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")] 308 #[cfg(feature = "proto-ipv6")]
316 match config.ipv6 { 309 inner.set_config_v6(&mut socket, config.ipv6);
317 ConfigV6::Static(config) => { 310 inner.apply_static_config(&mut socket);
318 inner.apply_config_v6(&mut socket, config);
319 }
320 ConfigV6::None => {}
321 }
322 311
323 Self { 312 Self {
324 socket: RefCell::new(socket), 313 socket: RefCell::new(socket),
@@ -372,15 +361,36 @@ impl<D: Driver + 'static> Stack<D> {
372 } 361 }
373 362
374 /// Get the current IPv4 configuration. 363 /// Get the current IPv4 configuration.
364 ///
365 /// If using DHCP, this will be None if DHCP hasn't been able to
366 /// acquire an IP address, or Some if it has.
375 #[cfg(feature = "proto-ipv4")] 367 #[cfg(feature = "proto-ipv4")]
376 pub fn config_v4(&self) -> Option<StaticConfigV4> { 368 pub fn config_v4(&self) -> Option<StaticConfigV4> {
377 self.with(|_s, i| i.static_v4.clone()) 369 self.with(|_, i| i.static_v4.clone())
378 } 370 }
379 371
380 /// Get the current IPv6 configuration. 372 /// Get the current IPv6 configuration.
381 #[cfg(feature = "proto-ipv6")] 373 #[cfg(feature = "proto-ipv6")]
382 pub fn config_v6(&self) -> Option<StaticConfigV6> { 374 pub fn config_v6(&self) -> Option<StaticConfigV6> {
383 self.with(|_s, i| i.static_v6.clone()) 375 self.with(|_, i| i.static_v6.clone())
376 }
377
378 /// Set the IPv4 configuration.
379 #[cfg(feature = "proto-ipv4")]
380 pub fn set_config_v4(&self, config: ConfigV4) {
381 self.with_mut(|s, i| {
382 i.set_config_v4(s, config);
383 i.apply_static_config(s);
384 })
385 }
386
387 /// Set the IPv6 configuration.
388 #[cfg(feature = "proto-ipv6")]
389 pub fn set_config_v6(&self, config: ConfigV6) {
390 self.with_mut(|s, i| {
391 i.set_config_v6(s, config);
392 i.apply_static_config(s);
393 })
384 } 394 }
385 395
386 /// Run the network stack. 396 /// Run the network stack.
@@ -582,166 +592,125 @@ impl SocketStack {
582 592
583impl<D: Driver + 'static> Inner<D> { 593impl<D: Driver + 'static> Inner<D> {
584 #[cfg(feature = "proto-ipv4")] 594 #[cfg(feature = "proto-ipv4")]
585 fn apply_config_v4(&mut self, s: &mut SocketStack, config: StaticConfigV4) { 595 pub fn set_config_v4(&mut self, _s: &mut SocketStack, config: ConfigV4) {
586 debug!("Acquired IP configuration:"); 596 // Handle static config.
587 597 self.static_v4 = match config.clone() {
588 debug!(" IP address: {}", config.address); 598 ConfigV4::None => None,
589 s.iface.update_ip_addrs(|addrs| { 599 #[cfg(feature = "dhcpv4")]
590 if let Some((index, _)) = addrs 600 ConfigV4::Dhcp(_) => None,
591 .iter() 601 ConfigV4::Static(c) => Some(c),
592 .enumerate() 602 };
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 603
618 self.static_v4 = Some(config); 604 // Handle DHCP config.
605 #[cfg(feature = "dhcpv4")]
606 match config {
607 ConfigV4::Dhcp(c) => {
608 // Create the socket if it doesn't exist.
609 if self.dhcp_socket.is_none() {
610 let socket = smoltcp::socket::dhcpv4::Socket::new();
611 let handle = _s.sockets.add(socket);
612 self.dhcp_socket = Some(handle);
613 }
619 614
620 #[cfg(feature = "dns")] 615 // Configure it
621 { 616 let socket = _s.sockets.get_mut::<dhcpv4::Socket>(self.dhcp_socket.unwrap());
622 self.update_dns_servers(s) 617 socket.set_ignore_naks(c.ignore_naks);
618 socket.set_max_lease_duration(c.max_lease_duration.map(crate::time::duration_to_smoltcp));
619 socket.set_ports(c.server_port, c.client_port);
620 socket.set_retry_config(c.retry_config);
621 socket.reset();
622 }
623 _ => {
624 // Remove DHCP socket if any.
625 if let Some(socket) = self.dhcp_socket {
626 _s.sockets.remove(socket);
627 self.dhcp_socket = None;
628 }
629 }
623 } 630 }
624 } 631 }
625 632
626 /// Replaces the current IPv6 static configuration with a newly supplied config.
627 #[cfg(feature = "proto-ipv6")] 633 #[cfg(feature = "proto-ipv6")]
628 fn apply_config_v6(&mut self, s: &mut SocketStack, config: StaticConfigV6) { 634 pub fn set_config_v6(&mut self, _s: &mut SocketStack, config: ConfigV6) {
629 #[cfg(feature = "medium-ethernet")] 635 self.static_v6 = match config {
630 let medium = self.device.capabilities().medium; 636 ConfigV6::None => None,
637 ConfigV6::Static(c) => Some(c),
638 };
639 }
631 640
632 debug!("Acquired IPv6 configuration:"); 641 fn apply_static_config(&mut self, s: &mut SocketStack) {
642 let mut addrs = Vec::new();
643 #[cfg(feature = "dns")]
644 let mut dns_servers: Vec<_, 6> = Vec::new();
645 #[cfg(feature = "proto-ipv4")]
646 let mut gateway_v4 = None;
647 #[cfg(feature = "proto-ipv6")]
648 let mut gateway_v6 = None;
633 649
634 debug!(" IP address: {}", config.address); 650 #[cfg(feature = "proto-ipv4")]
635 s.iface.update_ip_addrs(|addrs| { 651 if let Some(config) = &self.static_v4 {
636 if let Some((index, _)) = addrs 652 debug!("IPv4: UP");
637 .iter() 653 debug!(" IP address: {:?}", config.address);
638 .enumerate() 654 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 655
646 #[cfg(feature = "medium-ethernet")] 656 addrs.push(IpCidr::Ipv4(config.address)).unwrap();
647 if Medium::Ethernet == medium { 657 gateway_v4 = config.gateway.into();
648 if let Some(gateway) = config.gateway { 658 #[cfg(feature = "dns")]
649 debug!(" Default gateway: {}", gateway); 659 for s in &config.dns_servers {
650 s.iface.routes_mut().add_default_ipv6_route(gateway).unwrap(); 660 debug!(" DNS server: {:?}", s);
651 } else { 661 dns_servers.push(s.clone().into()).unwrap();
652 debug!(" Default gateway: None");
653 s.iface.routes_mut().remove_default_ipv6_route();
654 } 662 }
655 } 663 } else {
656 for (i, s) in config.dns_servers.iter().enumerate() { 664 info!("IPv4: DOWN");
657 debug!(" DNS server {}: {}", i, s);
658 } 665 }
659 666
660 self.static_v6 = Some(config); 667 #[cfg(feature = "proto-ipv6")]
668 if let Some(config) = &self.static_v6 {
669 debug!("IPv6: UP");
670 debug!(" IP address: {:?}", config.address);
671 debug!(" Default gateway: {:?}", config.gateway);
661 672
662 #[cfg(feature = "dns")] 673 addrs.push(IpCidr::Ipv6(config.address)).unwrap();
663 { 674 gateway_v6 = config.gateway.into();
664 self.update_dns_servers(s) 675 #[cfg(feature = "dns")]
676 for s in &config.dns_servers {
677 debug!(" DNS server: {:?}", s);
678 dns_servers.push(s.clone().into()).unwrap();
679 }
680 } else {
681 info!("IPv6: DOWN");
665 } 682 }
666 }
667 683
668 #[cfg(feature = "dns")] 684 // Apply addresses
669 fn update_dns_servers(&mut self, s: &mut SocketStack) { 685 s.iface.update_ip_addrs(|a| *a = addrs);
670 let socket = s.sockets.get_mut::<smoltcp::socket::dns::Socket>(self.dns_socket);
671 686
672 let servers_v4; 687 // Apply gateways
673 #[cfg(feature = "proto-ipv4")] 688 #[cfg(feature = "proto-ipv4")]
674 { 689 if let Some(gateway) = gateway_v4 {
675 servers_v4 = self 690 s.iface.routes_mut().add_default_ipv4_route(gateway).unwrap();
676 .static_v4 691 } else {
677 .iter() 692 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 } 693 }
684
685 let servers_v6;
686 #[cfg(feature = "proto-ipv6")] 694 #[cfg(feature = "proto-ipv6")]
687 { 695 if let Some(gateway) = gateway_v6 {
688 servers_v6 = self 696 s.iface.routes_mut().add_default_ipv6_route(gateway).unwrap();
689 .static_v6 697 } else {
690 .iter() 698 s.iface.routes_mut().remove_default_ipv6_route();
691 .flat_map(|cfg| cfg.dns_servers.iter().map(|c| IpAddress::Ipv6(*c)));
692 } 699 }
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 700
703 #[cfg(feature = "dhcpv4")] 701 // Apply DNS servers
704 fn apply_dhcp_config(&self, socket: &mut smoltcp::socket::dhcpv4::Socket, config: DhcpConfig) { 702 #[cfg(feature = "dns")]
705 socket.set_ignore_naks(config.ignore_naks); 703 s.sockets
706 socket.set_max_lease_duration(config.max_lease_duration.map(crate::time::duration_to_smoltcp)); 704 .get_mut::<smoltcp::socket::dns::Socket>(self.dns_socket)
707 socket.set_ports(config.server_port, config.client_port); 705 .update_servers(&dns_servers[..]);
708 socket.set_retry_config(config.retry_config);
709 }
710
711 #[cfg(feature = "dhcpv4")]
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 } 706 }
738 707
739 fn poll(&mut self, cx: &mut Context<'_>, s: &mut SocketStack) { 708 fn poll(&mut self, cx: &mut Context<'_>, s: &mut SocketStack) {
740 s.waker.register(cx.waker()); 709 s.waker.register(cx.waker());
741 710
742 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] 711 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
743 if self.device.capabilities().medium == Medium::Ethernet 712 if self.device.capabilities().medium == embassy_net_driver::Medium::Ethernet
744 || self.device.capabilities().medium == Medium::Ieee802154 713 || self.device.capabilities().medium == embassy_net_driver::Medium::Ieee802154
745 { 714 {
746 s.iface 715 s.iface
747 .set_hardware_addr(to_smoltcp_hardware_address(self.device.hardware_address())); 716 .set_hardware_addr(to_smoltcp_hardware_address(self.device.hardware_address()));
@@ -763,6 +732,9 @@ impl<D: Driver + 'static> Inner<D> {
763 info!("link_up = {:?}", self.link_up); 732 info!("link_up = {:?}", self.link_up);
764 } 733 }
765 734
735 #[allow(unused_mut)]
736 let mut apply_config = false;
737
766 #[cfg(feature = "dhcpv4")] 738 #[cfg(feature = "dhcpv4")]
767 if let Some(dhcp_handle) = self.dhcp_socket { 739 if let Some(dhcp_handle) = self.dhcp_socket {
768 let socket = s.sockets.get_mut::<dhcpv4::Socket>(dhcp_handle); 740 let socket = s.sockets.get_mut::<dhcpv4::Socket>(dhcp_handle);
@@ -770,25 +742,29 @@ impl<D: Driver + 'static> Inner<D> {
770 if self.link_up { 742 if self.link_up {
771 match socket.poll() { 743 match socket.poll() {
772 None => {} 744 None => {}
773 Some(dhcpv4::Event::Deconfigured) => self.unapply_config_v4(s), 745 Some(dhcpv4::Event::Deconfigured) => {
746 self.static_v4 = None;
747 apply_config = true;
748 }
774 Some(dhcpv4::Event::Configured(config)) => { 749 Some(dhcpv4::Event::Configured(config)) => {
775 let config = StaticConfigV4 { 750 self.static_v4 = Some(StaticConfigV4 {
776 address: config.address, 751 address: config.address,
777 gateway: config.router, 752 gateway: config.router,
778 dns_servers: config.dns_servers, 753 dns_servers: config.dns_servers,
779 }; 754 });
780 self.apply_config_v4(s, config) 755 apply_config = true;
781 } 756 }
782 } 757 }
783 } else if old_link_up { 758 } else if old_link_up {
784 socket.reset(); 759 socket.reset();
785 self.unapply_config_v4(s); 760 self.static_v4 = None;
761 apply_config = true;
786 } 762 }
787 } 763 }
788 //if old_link_up || self.link_up { 764
789 // self.poll_configurator(timestamp) 765 if apply_config {
790 //} 766 self.apply_static_config(s);
791 // 767 }
792 768
793 if let Some(poll_at) = s.iface.poll_at(timestamp, &mut s.sockets) { 769 if let Some(poll_at) = s.iface.poll_at(timestamp, &mut s.sockets) {
794 let t = Timer::at(instant_from_smoltcp(poll_at)); 770 let t = Timer::at(instant_from_smoltcp(poll_at));