1#![no_std]
2#![allow(async_fn_in_trait)]
3#![allow(unsafe_op_in_unsafe_fn)]
4#![warn(missing_docs)]
5#![doc = include_str!("../README.md")]
6
7#![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)]
9
10#[cfg(not(any(feature = "proto-ipv4", feature = "proto-ipv6")))]
11compile_error!("You must enable at least one of the following features: proto-ipv4, proto-ipv6");
12
13pub(crate) mod fmt;
15
16#[cfg(feature = "dns")]
17pub mod dns;
18mod driver_util;
19#[cfg(feature = "icmp")]
20pub mod icmp;
21#[cfg(feature = "raw")]
22pub mod raw;
23#[cfg(feature = "tcp")]
24pub mod tcp;
25mod time;
26#[cfg(feature = "udp")]
27pub mod udp;
28
29use core::cell::RefCell;
30use core::future::{Future, poll_fn};
31use core::mem::MaybeUninit;
32use core::pin::pin;
33use core::task::{Context, Poll};
34
35pub use embassy_net_driver as driver;
36use embassy_net_driver::{Driver, LinkState};
37use embassy_sync::waitqueue::WakerRegistration;
38use embassy_time::{Instant, Timer};
39use heapless::Vec;
40#[cfg(feature = "dns")]
41pub use smoltcp::config::DNS_MAX_SERVER_COUNT;
42#[cfg(feature = "multicast")]
43pub use smoltcp::iface::MulticastError;
44#[cfg(any(feature = "dns", feature = "dhcpv4"))]
45use smoltcp::iface::SocketHandle;
46use smoltcp::iface::{Interface, SocketSet, SocketStorage};
47use smoltcp::phy::Medium;
48#[cfg(feature = "dhcpv4")]
49use smoltcp::socket::dhcpv4::{self, RetryConfig};
50#[cfg(feature = "medium-ethernet")]
51pub use smoltcp::wire::EthernetAddress;
52#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154", feature = "medium-ip"))]
53pub use smoltcp::wire::HardwareAddress;
54#[cfg(any(feature = "udp", feature = "tcp"))]
55pub use smoltcp::wire::IpListenEndpoint;
56#[cfg(feature = "medium-ieee802154")]
57pub use smoltcp::wire::{Ieee802154Address, Ieee802154Frame};
58pub use smoltcp::wire::{IpAddress, IpCidr, IpEndpoint};
59#[cfg(feature = "proto-ipv4")]
60pub use smoltcp::wire::{Ipv4Address, Ipv4Cidr};
61#[cfg(feature = "proto-ipv6")]
62pub use smoltcp::wire::{Ipv6Address, Ipv6Cidr};
63
64use crate::driver_util::DriverAdapter;
65use crate::time::{instant_from_smoltcp, instant_to_smoltcp};
66
67const LOCAL_PORT_MIN: u16 = 1025;
68const LOCAL_PORT_MAX: u16 = 65535;
69#[cfg(feature = "dns")]
70const MAX_QUERIES: usize = 4;
71#[cfg(feature = "dhcpv4-hostname")]
72const MAX_HOSTNAME_LEN: usize = 32;
73
74pub struct StackResources<const SOCK: usize> {
76 sockets: MaybeUninit<[SocketStorage<'static>; SOCK]>,
77 inner: MaybeUninit<RefCell<Inner>>,
78 #[cfg(feature = "dns")]
79 queries: MaybeUninit<[Option<dns::DnsQuery>; MAX_QUERIES]>,
80 #[cfg(feature = "dhcpv4-hostname")]
81 hostname: HostnameResources,
82}
83
84#[cfg(feature = "dhcpv4-hostname")]
85struct HostnameResources {
86 option: MaybeUninit<smoltcp::wire::DhcpOption<'static>>,
87 data: MaybeUninit<[u8; MAX_HOSTNAME_LEN]>,
88}
89
90impl<const SOCK: usize> StackResources<SOCK> {
91 pub const fn new() -> Self {
93 Self {
94 sockets: MaybeUninit::uninit(),
95 inner: MaybeUninit::uninit(),
96 #[cfg(feature = "dns")]
97 queries: MaybeUninit::uninit(),
98 #[cfg(feature = "dhcpv4-hostname")]
99 hostname: HostnameResources {
100 option: MaybeUninit::uninit(),
101 data: MaybeUninit::uninit(),
102 },
103 }
104 }
105}
106
107#[cfg(feature = "proto-ipv4")]
109#[derive(Debug, Clone, PartialEq, Eq)]
110#[cfg_attr(feature = "defmt", derive(defmt::Format))]
111pub struct StaticConfigV4 {
112 pub address: Ipv4Cidr,
114 pub gateway: Option<Ipv4Address>,
116 pub dns_servers: Vec<Ipv4Address, 3>,
118}
119
120#[cfg(feature = "proto-ipv6")]
122#[derive(Debug, Clone, PartialEq, Eq)]
123#[cfg_attr(feature = "defmt", derive(defmt::Format))]
124pub struct StaticConfigV6 {
125 pub address: Ipv6Cidr,
127 pub gateway: Option<Ipv6Address>,
129 pub dns_servers: Vec<Ipv6Address, 3>,
131}
132
133#[cfg(feature = "dhcpv4")]
135#[derive(Debug, Clone, PartialEq, Eq)]
136#[cfg_attr(feature = "defmt", derive(defmt::Format))]
137#[non_exhaustive]
138pub struct DhcpConfig {
139 pub max_lease_duration: Option<embassy_time::Duration>,
144 pub retry_config: RetryConfig,
146 pub ignore_naks: bool,
150 pub server_port: u16,
152 pub client_port: u16,
154 #[cfg(feature = "dhcpv4-hostname")]
156 pub hostname: Option<heapless::String<MAX_HOSTNAME_LEN>>,
157}
158
159#[cfg(feature = "dhcpv4")]
160impl Default for DhcpConfig {
161 fn default() -> Self {
162 Self {
163 max_lease_duration: Default::default(),
164 retry_config: Default::default(),
165 ignore_naks: Default::default(),
166 server_port: smoltcp::wire::DHCP_SERVER_PORT,
167 client_port: smoltcp::wire::DHCP_CLIENT_PORT,
168 #[cfg(feature = "dhcpv4-hostname")]
169 hostname: None,
170 }
171 }
172}
173
174#[derive(Debug, Clone, Default, PartialEq, Eq)]
176#[cfg_attr(feature = "defmt", derive(defmt::Format))]
177#[non_exhaustive]
178pub struct Config {
179 #[cfg(feature = "proto-ipv4")]
181 pub ipv4: ConfigV4,
182 #[cfg(feature = "proto-ipv6")]
184 pub ipv6: ConfigV6,
185}
186
187impl Config {
188 #[cfg(feature = "proto-ipv4")]
190 pub const fn ipv4_static(config: StaticConfigV4) -> Self {
191 Self {
192 ipv4: ConfigV4::Static(config),
193 #[cfg(feature = "proto-ipv6")]
194 ipv6: ConfigV6::None,
195 }
196 }
197
198 #[cfg(feature = "proto-ipv6")]
200 pub const fn ipv6_static(config: StaticConfigV6) -> Self {
201 Self {
202 #[cfg(feature = "proto-ipv4")]
203 ipv4: ConfigV4::None,
204 ipv6: ConfigV6::Static(config),
205 }
206 }
207
208 #[cfg(feature = "dhcpv4")]
216 pub const fn dhcpv4(config: DhcpConfig) -> Self {
217 Self {
218 ipv4: ConfigV4::Dhcp(config),
219 #[cfg(feature = "proto-ipv6")]
220 ipv6: ConfigV6::None,
221 }
222 }
223
224 #[cfg(feature = "slaac")]
226 pub const fn slaac() -> Self {
227 Self {
228 #[cfg(feature = "proto-ipv4")]
229 ipv4: ConfigV4::None,
230 ipv6: ConfigV6::Slaac,
231 }
232 }
233}
234
235#[cfg(feature = "proto-ipv4")]
237#[derive(Debug, Clone, Default, PartialEq, Eq)]
238#[cfg_attr(feature = "defmt", derive(defmt::Format))]
239pub enum ConfigV4 {
240 #[default]
242 None,
243 Static(StaticConfigV4),
245 #[cfg(feature = "dhcpv4")]
247 Dhcp(DhcpConfig),
248}
249
250#[cfg(feature = "proto-ipv6")]
252#[derive(Debug, Clone, Default, PartialEq, Eq)]
253#[cfg_attr(feature = "defmt", derive(defmt::Format))]
254pub enum ConfigV6 {
255 #[default]
257 None,
258 Static(StaticConfigV6),
260 #[cfg(feature = "slaac")]
262 Slaac,
263}
264
265pub struct Runner<'d, D: Driver> {
269 driver: D,
270 stack: Stack<'d>,
271}
272
273#[derive(Copy, Clone)]
278pub struct Stack<'d> {
279 inner: &'d RefCell<Inner>,
280}
281
282pub(crate) struct Inner {
283 pub(crate) sockets: SocketSet<'static>, pub(crate) iface: Interface,
285 pub(crate) waker: WakerRegistration,
287 state_waker: WakerRegistration,
289 hardware_address: HardwareAddress,
290 next_local_port: u16,
291 link_up: bool,
292 #[cfg(feature = "proto-ipv4")]
293 static_v4: Option<StaticConfigV4>,
294 #[cfg(feature = "proto-ipv6")]
295 static_v6: Option<StaticConfigV6>,
296 #[cfg(feature = "slaac")]
297 slaac: bool,
298 #[cfg(feature = "dhcpv4")]
299 dhcp_socket: Option<SocketHandle>,
300 #[cfg(feature = "dns")]
301 dns_socket: SocketHandle,
302 #[cfg(feature = "dns")]
303 dns_waker: WakerRegistration,
304 #[cfg(feature = "dhcpv4-hostname")]
305 hostname: *mut HostnameResources,
306}
307
308fn _assert_covariant<'a, 'b: 'a>(x: Stack<'b>) -> Stack<'a> {
309 x
310}
311
312pub fn new<'d, D: Driver, const SOCK: usize>(
314 mut driver: D,
315 config: Config,
316 resources: &'d mut StackResources<SOCK>,
317 random_seed: u64,
318) -> (Stack<'d>, Runner<'d, D>) {
319 let (hardware_address, medium) = to_smoltcp_hardware_address(driver.hardware_address());
320 let mut iface_cfg = smoltcp::iface::Config::new(hardware_address);
321 iface_cfg.random_seed = random_seed;
322 #[cfg(feature = "slaac")]
323 {
324 iface_cfg.slaac = matches!(config.ipv6, ConfigV6::Slaac);
325 }
326
327 #[allow(unused_mut)]
328 let mut iface = Interface::new(
329 iface_cfg,
330 &mut DriverAdapter {
331 inner: &mut driver,
332 cx: None,
333 medium,
334 tx_exhausted: false,
335 },
336 instant_to_smoltcp(Instant::now()),
337 );
338
339 unsafe fn transmute_slice<T>(x: &mut [T]) -> &'static mut [T] {
340 core::mem::transmute(x)
341 }
342
343 let sockets = resources.sockets.write([SocketStorage::EMPTY; SOCK]);
344 #[allow(unused_mut)]
345 let mut sockets: SocketSet<'static> = SocketSet::new(unsafe { transmute_slice(sockets) });
346
347 let next_local_port = (random_seed % (LOCAL_PORT_MAX - LOCAL_PORT_MIN) as u64) as u16 + LOCAL_PORT_MIN;
348
349 #[cfg(feature = "dns")]
350 let dns_socket = sockets.add(dns::Socket::new(
351 &[],
352 managed::ManagedSlice::Borrowed(unsafe {
353 transmute_slice(resources.queries.write([const { None }; MAX_QUERIES]))
354 }),
355 ));
356
357 let mut inner = Inner {
358 sockets,
359 iface,
360 waker: WakerRegistration::new(),
361 state_waker: WakerRegistration::new(),
362 next_local_port,
363 hardware_address,
364 link_up: false,
365 #[cfg(feature = "proto-ipv4")]
366 static_v4: None,
367 #[cfg(feature = "proto-ipv6")]
368 static_v6: None,
369 #[cfg(feature = "slaac")]
370 slaac: false,
371 #[cfg(feature = "dhcpv4")]
372 dhcp_socket: None,
373 #[cfg(feature = "dns")]
374 dns_socket,
375 #[cfg(feature = "dns")]
376 dns_waker: WakerRegistration::new(),
377 #[cfg(feature = "dhcpv4-hostname")]
378 hostname: &mut resources.hostname,
379 };
380
381 #[cfg(feature = "proto-ipv4")]
382 inner.set_config_v4(config.ipv4);
383 #[cfg(feature = "proto-ipv6")]
384 inner.set_config_v6(config.ipv6);
385 inner.apply_static_config();
386
387 let inner = &*resources.inner.write(RefCell::new(inner));
388 let stack = Stack { inner };
389 (stack, Runner { driver, stack })
390}
391
392fn to_smoltcp_hardware_address(addr: driver::HardwareAddress) -> (HardwareAddress, Medium) {
393 match addr {
394 #[cfg(feature = "medium-ethernet")]
395 driver::HardwareAddress::Ethernet(eth) => (HardwareAddress::Ethernet(EthernetAddress(eth)), Medium::Ethernet),
396 #[cfg(feature = "medium-ieee802154")]
397 driver::HardwareAddress::Ieee802154(ieee) => (
398 HardwareAddress::Ieee802154(Ieee802154Address::Extended(ieee)),
399 Medium::Ieee802154,
400 ),
401 #[cfg(feature = "medium-ip")]
402 driver::HardwareAddress::Ip => (HardwareAddress::Ip, Medium::Ip),
403
404 #[allow(unreachable_patterns)]
405 _ => panic!(
406 "Unsupported medium {:?}. Make sure to enable the right medium feature in embassy-net's Cargo features.",
407 addr
408 ),
409 }
410}
411
412impl<'d> Stack<'d> {
413 fn with<R>(&self, f: impl FnOnce(&Inner) -> R) -> R {
414 f(&self.inner.borrow())
415 }
416
417 fn with_mut<R>(&self, f: impl FnOnce(&mut Inner) -> R) -> R {
418 f(&mut self.inner.borrow_mut())
419 }
420
421 pub fn hardware_address(&self) -> HardwareAddress {
423 self.with(|i| i.hardware_address)
424 }
425
426 pub fn is_link_up(&self) -> bool {
428 self.with(|i| i.link_up)
429 }
430
431 pub fn is_config_up(&self) -> bool {
434 let v4_up;
435 let v6_up;
436
437 #[cfg(feature = "proto-ipv4")]
438 {
439 v4_up = self.config_v4().is_some();
440 }
441 #[cfg(not(feature = "proto-ipv4"))]
442 {
443 v4_up = false;
444 }
445
446 #[cfg(feature = "proto-ipv6")]
447 {
448 v6_up = self.config_v6().is_some();
449 }
450 #[cfg(not(feature = "proto-ipv6"))]
451 {
452 v6_up = false;
453 }
454
455 v4_up || v6_up
456 }
457
458 pub async fn wait_link_up(&self) {
460 self.wait(|| self.is_link_up()).await
461 }
462
463 pub async fn wait_link_down(&self) {
465 self.wait(|| !self.is_link_up()).await
466 }
467
468 pub async fn wait_config_up(&self) {
498 self.wait(|| self.is_config_up()).await
499 }
500
501 pub async fn wait_config_down(&self) {
503 self.wait(|| !self.is_config_up()).await
504 }
505
506 fn wait<'a>(&'a self, mut predicate: impl FnMut() -> bool + 'a) -> impl Future<Output = ()> + 'a {
507 poll_fn(move |cx| {
508 if predicate() {
509 Poll::Ready(())
510 } else {
511 trace!("Waiting for config up");
514
515 self.with_mut(|i| {
516 i.state_waker.register(cx.waker());
517 });
518
519 Poll::Pending
520 }
521 })
522 }
523
524 #[cfg(feature = "proto-ipv4")]
529 pub fn config_v4(&self) -> Option<StaticConfigV4> {
530 self.with(|i| i.static_v4.clone())
531 }
532
533 #[cfg(feature = "proto-ipv6")]
535 pub fn config_v6(&self) -> Option<StaticConfigV6> {
536 self.with(|i| i.static_v6.clone())
537 }
538
539 #[cfg(feature = "proto-ipv4")]
541 pub fn set_config_v4(&self, config: ConfigV4) {
542 self.with_mut(|i| {
543 i.set_config_v4(config);
544 i.apply_static_config();
545 })
546 }
547
548 #[cfg(feature = "proto-ipv6")]
550 pub fn set_config_v6(&self, config: ConfigV6) {
551 self.with_mut(|i| {
552 i.set_config_v6(config);
553 i.apply_static_config();
554 })
555 }
556
557 #[cfg(feature = "dns")]
559 pub async fn dns_query(
560 &self,
561 name: &str,
562 qtype: dns::DnsQueryType,
563 ) -> Result<Vec<IpAddress, { smoltcp::config::DNS_MAX_RESULT_COUNT }>, dns::Error> {
564 match qtype {
566 #[cfg(feature = "proto-ipv4")]
567 dns::DnsQueryType::A => {
568 if let Ok(ip) = name.parse().map(IpAddress::Ipv4) {
569 return Ok([ip].into_iter().collect());
570 }
571 }
572 #[cfg(feature = "proto-ipv6")]
573 dns::DnsQueryType::Aaaa => {
574 if let Ok(ip) = name.parse().map(IpAddress::Ipv6) {
575 return Ok([ip].into_iter().collect());
576 }
577 }
578 _ => {}
579 }
580
581 let query = poll_fn(|cx| {
582 self.with_mut(|i| {
583 let socket = i.sockets.get_mut::<dns::Socket>(i.dns_socket);
584 match socket.start_query(i.iface.context(), name, qtype) {
585 Ok(handle) => {
586 i.waker.wake();
587 Poll::Ready(Ok(handle))
588 }
589 Err(dns::StartQueryError::NoFreeSlot) => {
590 i.dns_waker.register(cx.waker());
591 Poll::Pending
592 }
593 Err(e) => Poll::Ready(Err(e)),
594 }
595 })
596 })
597 .await?;
598
599 #[must_use = "to delay the drop handler invocation to the end of the scope"]
600 struct OnDrop<F: FnOnce()> {
601 f: core::mem::MaybeUninit<F>,
602 }
603
604 impl<F: FnOnce()> OnDrop<F> {
605 fn new(f: F) -> Self {
606 Self {
607 f: core::mem::MaybeUninit::new(f),
608 }
609 }
610
611 fn defuse(self) {
612 core::mem::forget(self)
613 }
614 }
615
616 impl<F: FnOnce()> Drop for OnDrop<F> {
617 fn drop(&mut self) {
618 unsafe { self.f.as_ptr().read()() }
619 }
620 }
621
622 let drop = OnDrop::new(|| {
623 self.with_mut(|i| {
624 let socket = i.sockets.get_mut::<dns::Socket>(i.dns_socket);
625 socket.cancel_query(query);
626 i.waker.wake();
627 i.dns_waker.wake();
628 })
629 });
630
631 let res = poll_fn(|cx| {
632 self.with_mut(|i| {
633 let socket = i.sockets.get_mut::<dns::Socket>(i.dns_socket);
634 match socket.get_query_result(query) {
635 Ok(addrs) => {
636 i.dns_waker.wake();
637 Poll::Ready(Ok(addrs))
638 }
639 Err(dns::GetQueryResultError::Pending) => {
640 socket.register_query_waker(query, cx.waker());
641 Poll::Pending
642 }
643 Err(e) => {
644 i.dns_waker.wake();
645 Poll::Ready(Err(e.into()))
646 }
647 }
648 })
649 })
650 .await;
651
652 drop.defuse();
653
654 res
655 }
656}
657
658#[cfg(feature = "multicast")]
659impl<'d> Stack<'d> {
660 pub fn join_multicast_group(&self, addr: impl Into<IpAddress>) -> Result<(), MulticastError> {
662 self.with_mut(|i| i.iface.join_multicast_group(addr))
663 }
664
665 pub fn leave_multicast_group(&self, addr: impl Into<IpAddress>) -> Result<(), MulticastError> {
667 self.with_mut(|i| i.iface.leave_multicast_group(addr))
668 }
669
670 pub fn has_multicast_group(&self, addr: impl Into<IpAddress>) -> bool {
672 self.with(|i| i.iface.has_multicast_group(addr))
673 }
674}
675
676impl Inner {
677 #[cfg(feature = "slaac")]
678 fn get_link_local_address(&self) -> IpCidr {
679 let ll_prefix = Ipv6Cidr::new(Ipv6Cidr::LINK_LOCAL_PREFIX.address(), 64);
680 Ipv6Cidr::from_link_prefix(&ll_prefix, self.hardware_address)
681 .unwrap()
682 .into()
683 }
684
685 #[allow(clippy::absurd_extreme_comparisons)]
686 pub fn get_local_port(&mut self) -> u16 {
687 let res = self.next_local_port;
688 self.next_local_port = if res >= LOCAL_PORT_MAX { LOCAL_PORT_MIN } else { res + 1 };
689 res
690 }
691
692 #[cfg(feature = "proto-ipv4")]
693 pub fn set_config_v4(&mut self, config: ConfigV4) {
694 self.static_v4 = match config.clone() {
696 ConfigV4::None => None,
697 #[cfg(feature = "dhcpv4")]
698 ConfigV4::Dhcp(_) => None,
699 ConfigV4::Static(c) => Some(c),
700 };
701
702 #[cfg(feature = "dhcpv4")]
704 match config {
705 ConfigV4::Dhcp(c) => {
706 if self.dhcp_socket.is_none() {
708 let socket = smoltcp::socket::dhcpv4::Socket::new();
709 let handle = self.sockets.add(socket);
710 self.dhcp_socket = Some(handle);
711 }
712
713 let socket = self.sockets.get_mut::<dhcpv4::Socket>(unwrap!(self.dhcp_socket));
715 socket.set_ignore_naks(c.ignore_naks);
716 socket.set_max_lease_duration(c.max_lease_duration.map(crate::time::duration_to_smoltcp));
717 socket.set_ports(c.server_port, c.client_port);
718 socket.set_retry_config(c.retry_config);
719
720 socket.set_outgoing_options(&[]);
721 #[cfg(feature = "dhcpv4-hostname")]
722 if let Some(h) = c.hostname {
723 let hostname = unsafe { &mut *self.hostname };
728
729 let data = hostname.data.write([0; MAX_HOSTNAME_LEN]);
731 data[..h.len()].copy_from_slice(h.as_bytes());
732 let data: &[u8] = &data[..h.len()];
733
734 let option = hostname.option.write(smoltcp::wire::DhcpOption { data, kind: 12 });
736 socket.set_outgoing_options(core::slice::from_ref(option));
737 }
738
739 socket.reset();
740 }
741 _ => {
742 if let Some(socket) = self.dhcp_socket {
744 self.sockets.remove(socket);
745 self.dhcp_socket = None;
746 }
747 }
748 }
749 }
750
751 #[cfg(feature = "proto-ipv6")]
752 pub fn set_config_v6(&mut self, config: ConfigV6) {
753 #[cfg(feature = "slaac")]
754 {
755 self.slaac = matches!(config, ConfigV6::Slaac);
756 }
757 self.static_v6 = match config {
758 ConfigV6::None => None,
759 ConfigV6::Static(c) => Some(c),
760 #[cfg(feature = "slaac")]
761 ConfigV6::Slaac => None,
762 };
763 }
764
765 fn apply_static_config(&mut self) {
766 let mut addrs = Vec::new();
767 #[cfg(feature = "dns")]
768 let mut dns_servers: Vec<_, 6> = Vec::new();
769 #[cfg(feature = "proto-ipv4")]
770 let mut gateway_v4 = None;
771 #[cfg(feature = "proto-ipv6")]
772 let mut gateway_v6 = None;
773
774 #[cfg(feature = "proto-ipv4")]
775 if let Some(config) = &self.static_v4 {
776 debug!("IPv4: UP");
777 debug!(" IP address: {:?}", config.address);
778 debug!(" Default gateway: {:?}", config.gateway);
779
780 unwrap!(addrs.push(IpCidr::Ipv4(config.address)).ok());
781 gateway_v4 = config.gateway;
782 #[cfg(feature = "dns")]
783 for s in &config.dns_servers {
784 debug!(" DNS server: {:?}", s);
785 unwrap!(dns_servers.push(s.clone().into()).ok());
786 }
787 } else {
788 info!("IPv4: DOWN");
789 }
790
791 #[cfg(feature = "proto-ipv6")]
792 if let Some(config) = &self.static_v6 {
793 debug!("IPv6: UP");
794 debug!(" IP address: {:?}", config.address);
795 debug!(" Default gateway: {:?}", config.gateway);
796
797 unwrap!(addrs.push(IpCidr::Ipv6(config.address)).ok());
798 gateway_v6 = config.gateway;
799 #[cfg(feature = "dns")]
800 for s in &config.dns_servers {
801 debug!(" DNS server: {:?}", s);
802 unwrap!(dns_servers.push(s.clone().into()).ok());
803 }
804 } else {
805 info!("IPv6: DOWN");
806 }
807
808 self.iface.update_ip_addrs(|a| {
810 *a = addrs;
811 });
812
813 #[cfg(feature = "slaac")]
815 {
816 let ll_address = self.get_link_local_address();
817 self.iface.update_ip_addrs(|a| {
818 let _ = a.push(ll_address);
819 })
820 }
821
822 #[cfg(feature = "proto-ipv4")]
824 if let Some(gateway) = gateway_v4 {
825 unwrap!(self.iface.routes_mut().add_default_ipv4_route(gateway));
826 } else {
827 self.iface.routes_mut().remove_default_ipv4_route();
828 }
829 #[cfg(feature = "proto-ipv6")]
830 if let Some(gateway) = gateway_v6 {
831 unwrap!(self.iface.routes_mut().add_default_ipv6_route(gateway));
832 } else {
833 self.iface.routes_mut().remove_default_ipv6_route();
834 }
835
836 #[cfg(feature = "dns")]
838 if !dns_servers.is_empty() {
839 let count = if dns_servers.len() > DNS_MAX_SERVER_COUNT {
840 warn!("Number of DNS servers exceeds DNS_MAX_SERVER_COUNT, truncating list.");
841 DNS_MAX_SERVER_COUNT
842 } else {
843 dns_servers.len()
844 };
845 self.sockets
846 .get_mut::<smoltcp::socket::dns::Socket>(self.dns_socket)
847 .update_servers(&dns_servers[..count]);
848 }
849
850 self.state_waker.wake();
851 }
852
853 fn poll<D: Driver>(&mut self, cx: &mut Context<'_>, driver: &mut D) {
854 self.waker.register(cx.waker());
855
856 let (_hardware_addr, medium) = to_smoltcp_hardware_address(driver.hardware_address());
857
858 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
859 {
860 let do_set = match medium {
861 #[cfg(feature = "medium-ethernet")]
862 Medium::Ethernet => true,
863 #[cfg(feature = "medium-ieee802154")]
864 Medium::Ieee802154 => true,
865 #[allow(unreachable_patterns)]
866 _ => false,
867 };
868 if do_set {
869 self.iface.set_hardware_addr(_hardware_addr);
870 }
871 }
872
873 let timestamp = instant_to_smoltcp(Instant::now());
874 let mut smoldev = DriverAdapter {
875 cx: Some(cx),
876 inner: driver,
877 medium,
878 tx_exhausted: false,
879 };
880 self.iface.poll(timestamp, &mut smoldev, &mut self.sockets);
881 let tx_exhausted = smoldev.tx_exhausted;
882
883 let old_link_up = self.link_up;
885 self.link_up = driver.link_state(cx) == LinkState::Up;
886
887 if old_link_up != self.link_up {
889 info!("link_up = {:?}", self.link_up);
890 self.state_waker.wake();
891 }
892
893 #[allow(unused_mut)]
894 let mut configure = false;
895
896 #[cfg(feature = "dhcpv4")]
897 {
898 configure |= if let Some(dhcp_handle) = self.dhcp_socket {
899 let socket = self.sockets.get_mut::<dhcpv4::Socket>(dhcp_handle);
900
901 if self.link_up {
902 if old_link_up != self.link_up {
903 socket.reset();
904 }
905 match socket.poll() {
906 None => false,
907 Some(dhcpv4::Event::Deconfigured) => {
908 self.static_v4 = None;
909 true
910 }
911 Some(dhcpv4::Event::Configured(config)) => {
912 self.static_v4 = Some(StaticConfigV4 {
913 address: config.address,
914 gateway: config.router,
915 dns_servers: config.dns_servers,
916 });
917 true
918 }
919 }
920 } else if old_link_up {
921 socket.reset();
922 self.static_v4 = None;
923 true
924 } else {
925 false
926 }
927 } else {
928 false
929 }
930 }
931
932 #[cfg(feature = "slaac")]
933 if self.slaac && self.iface.slaac_updated_at() == timestamp {
934 let ipv6_address = self.iface.ip_addrs().iter().find_map(|addr| match addr {
935 IpCidr::Ipv6(ip6_address) if !Ipv6Cidr::LINK_LOCAL_PREFIX.contains_addr(&ip6_address.address()) => {
936 Some(ip6_address)
937 }
938 _ => None,
939 });
940 self.static_v6 = if let Some(address) = ipv6_address {
941 let gateway = self
942 .iface
943 .routes()
944 .get_default_ipv6_route()
945 .map(|r| match r.via_router {
946 IpAddress::Ipv6(gateway) => Some(gateway),
947 #[cfg(feature = "proto-ipv4")]
948 _ => None,
949 })
950 .flatten();
951 let config = StaticConfigV6 {
952 address: *address,
953 gateway,
954 dns_servers: Vec::new(), };
956 Some(config)
957 } else {
958 None
959 };
960 configure = true;
961 }
962
963 if configure {
964 self.apply_static_config()
965 }
966
967 if let Some(poll_at) = self.iface.poll_at(timestamp, &mut self.sockets)
968 && !tx_exhausted
969 {
970 let t = pin!(Timer::at(instant_from_smoltcp(poll_at)));
971 if t.poll(cx).is_ready() {
972 cx.waker().wake_by_ref();
973 }
974 }
975 }
976}
977
978impl<'d, D: Driver> Runner<'d, D> {
979 pub async fn run(&mut self) -> ! {
983 poll_fn(|cx| {
984 self.stack.with_mut(|i| i.poll(cx, &mut self.driver));
985 Poll::<()>::Pending
986 })
987 .await;
988 unreachable!()
989 }
990}