1#[cfg(test)]
6mod tests;
7
8#[cfg(feature = "medium-ethernet")]
9mod ethernet;
10#[cfg(feature = "medium-ieee802154")]
11mod ieee802154;
12
13#[cfg(feature = "proto-ipv4")]
14mod ipv4;
15#[cfg(feature = "proto-ipv6")]
16mod ipv6;
17#[cfg(feature = "proto-sixlowpan")]
18mod sixlowpan;
19
20#[cfg(feature = "multicast")]
21pub(crate) mod multicast;
22#[cfg(feature = "socket-tcp")]
23mod tcp;
24#[cfg(any(feature = "socket-udp", feature = "socket-dns"))]
25mod udp;
26
27use super::packet::*;
28
29use core::result::Result;
30use heapless::Vec;
31
32#[cfg(feature = "_proto-fragmentation")]
33use super::fragmentation::FragKey;
34#[cfg(any(feature = "proto-ipv4", feature = "proto-sixlowpan"))]
35use super::fragmentation::PacketAssemblerSet;
36use super::fragmentation::{Fragmenter, FragmentsBuffer};
37
38#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
39use super::neighbor::{Answer as NeighborAnswer, Cache as NeighborCache};
40use super::socket_set::SocketSet;
41use crate::config::{
42 IFACE_MAX_ADDR_COUNT, IFACE_MAX_PREFIX_COUNT, IFACE_MAX_SIXLOWPAN_ADDRESS_CONTEXT_COUNT,
43};
44use crate::iface::Routes;
45#[cfg(feature = "proto-ipv6-slaac")]
46use crate::iface::Slaac;
47use crate::phy::PacketMeta;
48use crate::phy::{ChecksumCapabilities, Device, DeviceCapabilities, Medium, RxToken, TxToken};
49use crate::rand::Rand;
50use crate::socket::*;
51use crate::time::{Duration, Instant};
52
53use crate::wire::*;
54
55macro_rules! check {
56 ($e:expr) => {
57 match $e {
58 Ok(x) => x,
59 Err(_) => {
60 #[cfg(not(feature = "defmt"))]
62 net_trace!(concat!("iface: malformed ", stringify!($e)));
63 #[cfg(feature = "defmt")]
64 net_trace!("iface: malformed");
65 return Default::default();
66 }
67 }
68 };
69}
70use check;
71
72#[derive(Copy, Clone, PartialEq, Eq, Debug)]
76#[cfg_attr(feature = "defmt", derive(defmt::Format))]
77pub enum PollResult {
78 None,
80 SocketStateChanged,
82}
83
84#[derive(Copy, Clone, PartialEq, Eq, Debug)]
89#[cfg_attr(feature = "defmt", derive(defmt::Format))]
90pub enum PollIngressSingleResult {
91 None,
96 PacketProcessed,
102 SocketStateChanged,
108}
109
110pub struct Interface {
116 pub(crate) inner: InterfaceInner,
117 fragments: FragmentsBuffer,
118 fragmenter: Fragmenter,
119}
120
121pub struct InterfaceInner {
129 caps: DeviceCapabilities,
130 now: Instant,
131 rand: Rand,
132
133 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
134 neighbor_cache: NeighborCache,
135 hardware_addr: HardwareAddress,
136 #[cfg(feature = "medium-ieee802154")]
137 sequence_no: u8,
138 #[cfg(feature = "medium-ieee802154")]
139 pan_id: Option<Ieee802154Pan>,
140 #[cfg(feature = "proto-ipv4-fragmentation")]
141 ipv4_id: u16,
142 #[cfg(feature = "proto-sixlowpan")]
143 sixlowpan_address_context:
144 Vec<SixlowpanAddressContext, IFACE_MAX_SIXLOWPAN_ADDRESS_CONTEXT_COUNT>,
145 #[cfg(feature = "proto-sixlowpan-fragmentation")]
146 tag: u16,
147 ip_addrs: Vec<IpCidr, IFACE_MAX_ADDR_COUNT>,
148 any_ip: bool,
149 #[cfg(feature = "proto-ipv6-slaac")]
150 slaac_enabled: bool,
151 #[cfg(feature = "proto-ipv6-slaac")]
152 slaac: Slaac,
153 #[cfg(feature = "proto-ipv6-slaac")]
154 slaac_updated: Instant,
155 routes: Routes,
156 #[cfg(feature = "multicast")]
157 multicast: multicast::State,
158}
159
160#[non_exhaustive]
162pub struct Config {
163 pub random_seed: u64,
170
171 pub hardware_addr: HardwareAddress,
176
177 #[cfg(feature = "medium-ieee802154")]
181 pub pan_id: Option<Ieee802154Pan>,
182
183 #[cfg(feature = "proto-ipv6")]
185 pub slaac: bool,
186}
187
188impl Config {
189 pub fn new(hardware_addr: HardwareAddress) -> Self {
190 Config {
191 random_seed: 0,
192 hardware_addr,
193 #[cfg(feature = "medium-ieee802154")]
194 pan_id: None,
195 #[cfg(feature = "proto-ipv6")]
196 slaac: false,
197 }
198 }
199}
200
201impl Interface {
202 pub fn new(config: Config, device: &mut (impl Device + ?Sized), now: Instant) -> Self {
208 let caps = device.capabilities();
209 assert_eq!(
210 config.hardware_addr.medium(),
211 caps.medium,
212 "The hardware address does not match the medium of the interface."
213 );
214
215 let mut rand = Rand::new(config.random_seed);
216
217 #[cfg(feature = "medium-ieee802154")]
218 let mut sequence_no;
219 #[cfg(feature = "medium-ieee802154")]
220 loop {
221 sequence_no = (rand.rand_u32() & 0xff) as u8;
222 if sequence_no != 0 {
223 break;
224 }
225 }
226
227 #[cfg(feature = "proto-sixlowpan")]
228 let mut tag;
229
230 #[cfg(feature = "proto-sixlowpan")]
231 loop {
232 tag = rand.rand_u16();
233 if tag != 0 {
234 break;
235 }
236 }
237
238 #[cfg(feature = "proto-ipv4")]
239 let mut ipv4_id;
240
241 #[cfg(feature = "proto-ipv4")]
242 loop {
243 ipv4_id = rand.rand_u16();
244 if ipv4_id != 0 {
245 break;
246 }
247 }
248
249 Interface {
250 fragments: FragmentsBuffer {
251 #[cfg(feature = "proto-sixlowpan")]
252 decompress_buf: [0u8; sixlowpan::MAX_DECOMPRESSED_LEN],
253
254 #[cfg(feature = "_proto-fragmentation")]
255 assembler: PacketAssemblerSet::new(),
256 #[cfg(feature = "_proto-fragmentation")]
257 reassembly_timeout: Duration::from_secs(60),
258 },
259 fragmenter: Fragmenter::new(),
260 inner: InterfaceInner {
261 now,
262 caps,
263 hardware_addr: config.hardware_addr,
264 ip_addrs: Vec::new(),
265 any_ip: false,
266 routes: Routes::new(),
267 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
268 neighbor_cache: NeighborCache::new(),
269 #[cfg(feature = "multicast")]
270 multicast: multicast::State::new(),
271 #[cfg(feature = "medium-ieee802154")]
272 sequence_no,
273 #[cfg(feature = "medium-ieee802154")]
274 pan_id: config.pan_id,
275 #[cfg(feature = "proto-sixlowpan-fragmentation")]
276 tag,
277 #[cfg(feature = "proto-ipv4-fragmentation")]
278 ipv4_id,
279 #[cfg(feature = "proto-sixlowpan")]
280 sixlowpan_address_context: Vec::new(),
281 #[cfg(feature = "proto-ipv6-slaac")]
282 slaac_enabled: config.slaac,
283 #[cfg(feature = "proto-ipv6-slaac")]
284 slaac: Slaac::new(),
285 #[cfg(feature = "proto-ipv6-slaac")]
286 slaac_updated: Instant::from_millis(0),
287 rand,
288 },
289 }
290 }
291
292 pub fn context(&mut self) -> &mut InterfaceInner {
296 &mut self.inner
297 }
298
299 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
304 pub fn hardware_addr(&self) -> HardwareAddress {
305 #[cfg(all(feature = "medium-ethernet", not(feature = "medium-ieee802154")))]
306 assert!(self.inner.caps.medium == Medium::Ethernet);
307 #[cfg(all(feature = "medium-ieee802154", not(feature = "medium-ethernet")))]
308 assert!(self.inner.caps.medium == Medium::Ieee802154);
309
310 #[cfg(all(feature = "medium-ieee802154", feature = "medium-ethernet"))]
311 assert!(
312 self.inner.caps.medium == Medium::Ethernet
313 || self.inner.caps.medium == Medium::Ieee802154
314 );
315
316 self.inner.hardware_addr
317 }
318
319 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
325 pub fn set_hardware_addr(&mut self, addr: HardwareAddress) {
326 #[cfg(all(feature = "medium-ethernet", not(feature = "medium-ieee802154")))]
327 assert!(self.inner.caps.medium == Medium::Ethernet);
328 #[cfg(all(feature = "medium-ieee802154", not(feature = "medium-ethernet")))]
329 assert!(self.inner.caps.medium == Medium::Ieee802154);
330
331 #[cfg(all(feature = "medium-ieee802154", feature = "medium-ethernet"))]
332 assert!(
333 self.inner.caps.medium == Medium::Ethernet
334 || self.inner.caps.medium == Medium::Ieee802154
335 );
336
337 InterfaceInner::check_hardware_addr(&addr);
338 self.inner.hardware_addr = addr;
339 }
340
341 pub fn ip_addrs(&self) -> &[IpCidr] {
343 self.inner.ip_addrs.as_ref()
344 }
345
346 #[cfg(feature = "proto-ipv4")]
348 pub fn ipv4_addr(&self) -> Option<Ipv4Address> {
349 self.inner.ipv4_addr()
350 }
351
352 #[cfg(feature = "proto-ipv6")]
354 pub fn ipv6_addr(&self) -> Option<Ipv6Address> {
355 self.inner.ipv6_addr()
356 }
357
358 pub fn get_source_address(&self, dst_addr: &IpAddress) -> Option<IpAddress> {
363 self.inner.get_source_address(dst_addr)
364 }
365
366 #[cfg(feature = "proto-ipv4")]
371 pub fn get_source_address_ipv4(&self, dst_addr: &Ipv4Address) -> Option<Ipv4Address> {
372 self.inner.get_source_address_ipv4(dst_addr)
373 }
374
375 #[cfg(feature = "proto-ipv6")]
378 pub fn get_source_address_ipv6(&self, dst_addr: &Ipv6Address) -> Ipv6Address {
379 self.inner.get_source_address_ipv6(dst_addr)
380 }
381
382 pub fn update_ip_addrs<F: FnOnce(&mut Vec<IpCidr, IFACE_MAX_ADDR_COUNT>)>(&mut self, f: F) {
387 f(&mut self.inner.ip_addrs);
388 InterfaceInner::flush_neighbor_cache(&mut self.inner);
389 InterfaceInner::check_ip_addrs(&self.inner.ip_addrs);
390
391 #[cfg(all(
392 feature = "proto-ipv6",
393 feature = "multicast",
394 feature = "medium-ethernet"
395 ))]
396 if self.inner.caps.medium == Medium::Ethernet {
397 self.update_solicited_node_groups();
398 }
399 }
400
401 pub fn has_ip_addr<T: Into<IpAddress>>(&self, addr: T) -> bool {
403 self.inner.has_ip_addr(addr)
404 }
405
406 pub fn routes(&self) -> &Routes {
407 &self.inner.routes
408 }
409
410 pub fn routes_mut(&mut self) -> &mut Routes {
411 &mut self.inner.routes
412 }
413
414 pub fn set_any_ip(&mut self, any_ip: bool) {
422 self.inner.any_ip = any_ip;
423 }
424
425 pub fn any_ip(&self) -> bool {
429 self.inner.any_ip
430 }
431
432 #[cfg(feature = "_proto-fragmentation")]
434 pub fn reassembly_timeout(&self) -> Duration {
435 self.fragments.reassembly_timeout
436 }
437
438 #[cfg(feature = "_proto-fragmentation")]
440 pub fn set_reassembly_timeout(&mut self, timeout: Duration) {
441 if timeout > Duration::from_secs(60) {
442 net_debug!(
443 "RFC 4944 specifies that the reassembly timeout MUST be set to a maximum of 60 seconds"
444 );
445 }
446 self.fragments.reassembly_timeout = timeout;
447 }
448
449 pub fn poll(
469 &mut self,
470 timestamp: Instant,
471 device: &mut (impl Device + ?Sized),
472 sockets: &mut SocketSet<'_>,
473 ) -> PollResult {
474 self.inner.now = timestamp;
475
476 let mut res = PollResult::None;
477
478 self.poll_maintenance(timestamp);
479
480 loop {
482 match self.socket_ingress(device, sockets) {
483 PollIngressSingleResult::None => break,
484 PollIngressSingleResult::PacketProcessed => {}
485 PollIngressSingleResult::SocketStateChanged => res = PollResult::SocketStateChanged,
486 }
487 }
488
489 loop {
491 match self.poll_egress(timestamp, device, sockets) {
492 PollResult::None => break,
493 PollResult::SocketStateChanged => res = PollResult::SocketStateChanged,
494 }
495 }
496
497 res
498 }
499
500 pub fn poll_egress(
507 &mut self,
508 timestamp: Instant,
509 device: &mut (impl Device + ?Sized),
510 sockets: &mut SocketSet<'_>,
511 ) -> PollResult {
512 self.inner.now = timestamp;
513
514 match self.inner.caps.medium {
515 #[cfg(feature = "medium-ieee802154")]
516 Medium::Ieee802154 => {
517 #[cfg(feature = "proto-sixlowpan-fragmentation")]
518 self.sixlowpan_egress(device);
519 }
520 #[cfg(any(feature = "medium-ethernet", feature = "medium-ip"))]
521 _ => {
522 #[cfg(feature = "proto-ipv4-fragmentation")]
523 self.ipv4_egress(device);
524 }
525 }
526
527 #[cfg(feature = "proto-ipv6-slaac")]
528 if self.inner.slaac_enabled {
529 self.ndisc_rs_egress(device);
530 }
531
532 #[cfg(feature = "multicast")]
533 self.multicast_egress(device);
534
535 self.socket_egress(device, sockets)
536 }
537
538 pub fn poll_ingress_single(
546 &mut self,
547 timestamp: Instant,
548 device: &mut (impl Device + ?Sized),
549 sockets: &mut SocketSet<'_>,
550 ) -> PollIngressSingleResult {
551 self.inner.now = timestamp;
552
553 #[cfg(feature = "_proto-fragmentation")]
554 self.fragments.assembler.remove_expired(timestamp);
555
556 self.socket_ingress(device, sockets)
557 }
558
559 pub fn poll_maintenance(&mut self, timestamp: Instant) {
563 self.inner.now = timestamp;
564
565 #[cfg(feature = "_proto-fragmentation")]
566 self.fragments.assembler.remove_expired(timestamp);
567
568 #[cfg(feature = "proto-ipv6-slaac")]
569 if self.inner.slaac.sync_required(timestamp) {
570 self.sync_slaac_state(timestamp)
571 }
572 }
573
574 pub fn poll_at(&mut self, timestamp: Instant, sockets: &SocketSet<'_>) -> Option<Instant> {
583 self.inner.now = timestamp;
584
585 #[cfg(feature = "_proto-fragmentation")]
586 if !self.fragmenter.is_empty() {
587 return Some(Instant::from_millis(0));
588 }
589
590 #[allow(unused_mut)]
591 let mut res = sockets
592 .items()
593 .filter_map(|item| {
594 let socket_poll_at = item.socket.poll_at(&mut self.inner);
595 match item.meta.poll_at(
596 socket_poll_at,
597 |ip_addr| self.inner.has_neighbor(&ip_addr),
598 timestamp,
599 ) {
600 PollAt::Ingress => None,
601 PollAt::Time(instant) => Some(instant),
602 PollAt::Now => Some(Instant::from_millis(0)),
603 }
604 })
605 .min();
606
607 #[cfg(feature = "proto-ipv6-slaac")]
608 if self.inner.slaac_enabled {
609 res = res.min(self.inner.slaac.poll_at(timestamp));
610 }
611
612 res
613 }
614
615 pub fn poll_delay(&mut self, timestamp: Instant, sockets: &SocketSet<'_>) -> Option<Duration> {
624 match self.poll_at(timestamp, sockets) {
625 Some(poll_at) if timestamp < poll_at => Some(poll_at - timestamp),
626 Some(_) => Some(Duration::from_millis(0)),
627 _ => None,
628 }
629 }
630
631 fn socket_ingress(
632 &mut self,
633 device: &mut (impl Device + ?Sized),
634 sockets: &mut SocketSet<'_>,
635 ) -> PollIngressSingleResult {
636 let Some((rx_token, tx_token)) = device.receive(self.inner.now) else {
637 return PollIngressSingleResult::None;
638 };
639
640 let rx_meta = rx_token.meta();
641 rx_token.consume(|frame| {
642 if frame.is_empty() {
643 return PollIngressSingleResult::PacketProcessed;
644 }
645
646 match self.inner.caps.medium {
647 #[cfg(feature = "medium-ethernet")]
648 Medium::Ethernet => {
649 if let Some(packet) =
650 self.inner
651 .process_ethernet(sockets, rx_meta, frame, &mut self.fragments)
652 && let Err(err) =
653 self.inner.dispatch(tx_token, packet, &mut self.fragmenter)
654 {
655 net_debug!("Failed to send response: {:?}", err);
656 }
657 }
658 #[cfg(feature = "medium-ip")]
659 Medium::Ip => {
660 if let Some(packet) =
661 self.inner
662 .process_ip(sockets, rx_meta, frame, &mut self.fragments)
663 && let Err(err) = self.inner.dispatch_ip(
664 tx_token,
665 PacketMeta::default(),
666 packet,
667 &mut self.fragmenter,
668 )
669 {
670 net_debug!("Failed to send response: {:?}", err);
671 }
672 }
673 #[cfg(feature = "medium-ieee802154")]
674 Medium::Ieee802154 => {
675 if let Some(packet) =
676 self.inner
677 .process_ieee802154(sockets, rx_meta, frame, &mut self.fragments)
678 && let Err(err) = self.inner.dispatch_ip(
679 tx_token,
680 PacketMeta::default(),
681 packet,
682 &mut self.fragmenter,
683 )
684 {
685 net_debug!("Failed to send response: {:?}", err);
686 }
687 }
688 }
689
690 PollIngressSingleResult::SocketStateChanged
697 })
698 }
699
700 fn socket_egress(
701 &mut self,
702 device: &mut (impl Device + ?Sized),
703 sockets: &mut SocketSet<'_>,
704 ) -> PollResult {
705 let _caps = device.capabilities();
706
707 enum EgressError {
708 Exhausted,
709 Dispatch,
710 }
711
712 let mut result = PollResult::None;
713 for item in sockets.items_mut() {
714 if !item
715 .meta
716 .egress_permitted(self.inner.now, |ip_addr| self.inner.has_neighbor(&ip_addr))
717 {
718 continue;
719 }
720
721 let mut neighbor_addr = None;
722 let mut respond = |inner: &mut InterfaceInner, meta: PacketMeta, response: Packet| {
723 neighbor_addr = Some(response.ip_repr().dst_addr());
724 let t = device.transmit(inner.now).ok_or_else(|| {
725 net_debug!("failed to transmit IP: device exhausted");
726 EgressError::Exhausted
727 })?;
728
729 inner
730 .dispatch_ip(t, meta, response, &mut self.fragmenter)
731 .map_err(|_| EgressError::Dispatch)?;
732
733 result = PollResult::SocketStateChanged;
734
735 Ok(())
736 };
737
738 let result = match &mut item.socket {
739 #[cfg(feature = "socket-raw")]
740 Socket::Raw(socket) => socket.dispatch(&mut self.inner, |inner, (ip, raw)| {
741 respond(
742 inner,
743 PacketMeta::default(),
744 Packet::new(ip, IpPayload::Raw(raw)),
745 )
746 }),
747 #[cfg(feature = "socket-icmp")]
748 Socket::Icmp(socket) => {
749 socket.dispatch(&mut self.inner, |inner, response| match response {
750 #[cfg(feature = "proto-ipv4")]
751 (IpRepr::Ipv4(ipv4_repr), IcmpRepr::Ipv4(icmpv4_repr)) => respond(
752 inner,
753 PacketMeta::default(),
754 Packet::new_ipv4(ipv4_repr, IpPayload::Icmpv4(icmpv4_repr)),
755 ),
756 #[cfg(feature = "proto-ipv6")]
757 (IpRepr::Ipv6(ipv6_repr), IcmpRepr::Ipv6(icmpv6_repr)) => respond(
758 inner,
759 PacketMeta::default(),
760 Packet::new_ipv6(ipv6_repr, IpPayload::Icmpv6(icmpv6_repr)),
761 ),
762 #[allow(unreachable_patterns)]
763 _ => unreachable!(),
764 })
765 }
766 #[cfg(feature = "socket-udp")]
767 Socket::Udp(socket) => {
768 socket.dispatch(&mut self.inner, |inner, meta, (ip, udp, payload)| {
769 respond(inner, meta, Packet::new(ip, IpPayload::Udp(udp, payload)))
770 })
771 }
772 #[cfg(feature = "socket-tcp")]
773 Socket::Tcp(socket) => socket.dispatch(&mut self.inner, |inner, (ip, tcp)| {
774 respond(
775 inner,
776 PacketMeta::default(),
777 Packet::new(ip, IpPayload::Tcp(tcp)),
778 )
779 }),
780 #[cfg(feature = "socket-dhcpv4")]
781 Socket::Dhcpv4(socket) => {
782 socket.dispatch(&mut self.inner, |inner, (ip, udp, dhcp)| {
783 respond(
784 inner,
785 PacketMeta::default(),
786 Packet::new_ipv4(ip, IpPayload::Dhcpv4(udp, dhcp)),
787 )
788 })
789 }
790 #[cfg(feature = "socket-dns")]
791 Socket::Dns(socket) => socket.dispatch(&mut self.inner, |inner, (ip, udp, dns)| {
792 respond(
793 inner,
794 PacketMeta::default(),
795 Packet::new(ip, IpPayload::Udp(udp, dns)),
796 )
797 }),
798 };
799
800 match result {
801 Err(EgressError::Exhausted) => break, Err(EgressError::Dispatch) => {
803 item.meta.neighbor_missing(
808 self.inner.now,
809 neighbor_addr.expect("non-IP response packet"),
810 );
811 }
812 Ok(()) => {}
813 }
814 }
815 result
816 }
817}
818
819impl InterfaceInner {
820 #[allow(unused)] pub(crate) fn now(&self) -> Instant {
822 self.now
823 }
824
825 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
826 #[allow(unused)] pub(crate) fn hardware_addr(&self) -> HardwareAddress {
828 self.hardware_addr
829 }
830
831 #[allow(unused)] pub(crate) fn checksum_caps(&self) -> ChecksumCapabilities {
833 self.caps.checksum.clone()
834 }
835
836 #[allow(unused)] pub(crate) fn ip_mtu(&self) -> usize {
838 self.caps.ip_mtu()
839 }
840
841 #[allow(unused)] pub(crate) fn rand(&mut self) -> &mut Rand {
843 &mut self.rand
844 }
845
846 #[allow(unused)] pub(crate) fn get_source_address(&self, dst_addr: &IpAddress) -> Option<IpAddress> {
848 match dst_addr {
849 #[cfg(feature = "proto-ipv4")]
850 IpAddress::Ipv4(addr) => self.get_source_address_ipv4(addr).map(|a| a.into()),
851 #[cfg(feature = "proto-ipv6")]
852 IpAddress::Ipv6(addr) => Some(self.get_source_address_ipv6(addr).into()),
853 }
854 }
855
856 #[cfg(test)]
857 #[allow(unused)] pub(crate) fn set_now(&mut self, now: Instant) {
859 self.now = now
860 }
861
862 #[cfg(test)]
863 #[allow(unused)] pub(crate) fn set_ip_addrs(&mut self, addrs: Vec<IpCidr, IFACE_MAX_ADDR_COUNT>) {
865 self.ip_addrs = addrs;
866 }
867
868 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
869 fn check_hardware_addr(addr: &HardwareAddress) {
870 if !addr.is_unicast() {
871 panic!("Hardware address {addr} is not unicast")
872 }
873 }
874
875 fn check_ip_addrs(addrs: &[IpCidr]) {
876 for cidr in addrs {
877 if !cidr.address().is_unicast() && !cidr.address().is_unspecified() {
878 panic!("IP address {} is not unicast", cidr.address())
879 }
880 }
881 }
882
883 pub(crate) fn has_ip_addr<T: Into<IpAddress>>(&self, addr: T) -> bool {
887 if self.any_ip {
889 return true;
890 }
891
892 let addr = addr.into();
893 self.ip_addrs.iter().any(|probe| probe.address() == addr)
894 }
895
896 fn has_multicast_group<T: Into<IpAddress>>(&self, addr: T) -> bool {
898 let addr = addr.into();
899
900 #[cfg(feature = "multicast")]
901 if self.multicast.has_multicast_group(addr) {
902 return true;
903 }
904
905 match addr {
906 #[cfg(feature = "proto-ipv4")]
907 IpAddress::Ipv4(key) => key == IPV4_MULTICAST_ALL_SYSTEMS,
908 #[cfg(feature = "proto-rpl")]
909 IpAddress::Ipv6(IPV6_LINK_LOCAL_ALL_RPL_NODES) => true,
910 #[cfg(feature = "proto-ipv6")]
911 IpAddress::Ipv6(key) => {
912 key == IPV6_LINK_LOCAL_ALL_NODES || self.has_solicited_node(key)
913 }
914 #[allow(unreachable_patterns)]
915 _ => false,
916 }
917 }
918
919 #[cfg(feature = "medium-ip")]
920 fn process_ip<'frame>(
921 &mut self,
922 sockets: &mut SocketSet,
923 meta: PacketMeta,
924 ip_payload: &'frame [u8],
925 frag: &'frame mut FragmentsBuffer,
926 ) -> Option<Packet<'frame>> {
927 match IpVersion::of_packet(ip_payload) {
928 #[cfg(feature = "proto-ipv4")]
929 Ok(IpVersion::Ipv4) => {
930 let ipv4_packet = check!(Ipv4Packet::new_checked(ip_payload));
931 self.process_ipv4(sockets, meta, HardwareAddress::Ip, &ipv4_packet, frag)
932 }
933 #[cfg(feature = "proto-ipv6")]
934 Ok(IpVersion::Ipv6) => {
935 let ipv6_packet = check!(Ipv6Packet::new_checked(ip_payload));
936 self.process_ipv6(sockets, meta, HardwareAddress::Ip, &ipv6_packet)
937 }
938 _ => None,
940 }
941 }
942
943 #[cfg(feature = "socket-raw")]
944 fn raw_socket_filter(
945 &mut self,
946 sockets: &mut SocketSet,
947 ip_repr: &IpRepr,
948 ip_payload: &[u8],
949 ) -> bool {
950 let mut handled_by_raw_socket = false;
951
952 for raw_socket in sockets
954 .items_mut()
955 .filter_map(|i| raw::Socket::downcast_mut(&mut i.socket))
956 {
957 if raw_socket.accepts(ip_repr) {
958 raw_socket.process(self, ip_repr, ip_payload);
959 handled_by_raw_socket = true;
960 }
961 }
962 handled_by_raw_socket
963 }
964
965 pub(crate) fn is_broadcast(&self, address: &IpAddress) -> bool {
968 match address {
969 #[cfg(feature = "proto-ipv4")]
970 IpAddress::Ipv4(address) => self.is_broadcast_v4(*address),
971 #[cfg(feature = "proto-ipv6")]
972 IpAddress::Ipv6(_) => false,
973 }
974 }
975
976 #[cfg(feature = "medium-ethernet")]
977 fn dispatch<Tx>(
978 &mut self,
979 tx_token: Tx,
980 packet: EthernetPacket,
981 frag: &mut Fragmenter,
982 ) -> Result<(), DispatchError>
983 where
984 Tx: TxToken,
985 {
986 match packet {
987 #[cfg(feature = "proto-ipv4")]
988 EthernetPacket::Arp(arp_repr) => {
989 let dst_hardware_addr = match arp_repr {
990 ArpRepr::EthernetIpv4 {
991 target_hardware_addr,
992 ..
993 } => target_hardware_addr,
994 };
995
996 self.dispatch_ethernet(tx_token, arp_repr.buffer_len(), |mut frame| {
997 frame.set_dst_addr(dst_hardware_addr);
998 frame.set_ethertype(EthernetProtocol::Arp);
999
1000 let mut packet = ArpPacket::new_unchecked(frame.payload_mut());
1001 arp_repr.emit(&mut packet);
1002 })
1003 }
1004 EthernetPacket::Ip(packet) => {
1005 self.dispatch_ip(tx_token, PacketMeta::default(), packet, frag)
1006 }
1007 }
1008 }
1009
1010 fn in_same_network(&self, addr: &IpAddress) -> bool {
1011 self.ip_addrs.iter().any(|cidr| cidr.contains_addr(addr))
1012 }
1013
1014 fn route(&self, addr: &IpAddress, timestamp: Instant) -> Option<IpAddress> {
1015 if self.in_same_network(addr) || addr.is_broadcast() {
1019 return Some(*addr);
1020 }
1021
1022 self.routes.lookup(addr, timestamp)
1024 }
1025
1026 fn has_neighbor(&self, addr: &IpAddress) -> bool {
1027 match self.route(addr, self.now) {
1028 Some(_routed_addr) => match self.caps.medium {
1029 #[cfg(feature = "medium-ethernet")]
1030 Medium::Ethernet => self.neighbor_cache.lookup(&_routed_addr, self.now).found(),
1031 #[cfg(feature = "medium-ieee802154")]
1032 Medium::Ieee802154 => self.neighbor_cache.lookup(&_routed_addr, self.now).found(),
1033 #[cfg(feature = "medium-ip")]
1034 Medium::Ip => true,
1035 },
1036 None => false,
1037 }
1038 }
1039
1040 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
1041 fn lookup_hardware_addr<Tx>(
1042 &mut self,
1043 tx_token: Tx,
1044 dst_addr: &IpAddress,
1045 fragmenter: &mut Fragmenter,
1046 ) -> Result<(HardwareAddress, Tx), DispatchError>
1047 where
1048 Tx: TxToken,
1049 {
1050 if self.is_broadcast(dst_addr) {
1051 let hardware_addr = match self.caps.medium {
1052 #[cfg(feature = "medium-ethernet")]
1053 Medium::Ethernet => HardwareAddress::Ethernet(EthernetAddress::BROADCAST),
1054 #[cfg(feature = "medium-ieee802154")]
1055 Medium::Ieee802154 => HardwareAddress::Ieee802154(Ieee802154Address::BROADCAST),
1056 #[cfg(feature = "medium-ip")]
1057 Medium::Ip => unreachable!(),
1058 };
1059
1060 return Ok((hardware_addr, tx_token));
1061 }
1062
1063 if dst_addr.is_multicast() {
1064 let hardware_addr = match *dst_addr {
1065 #[cfg(feature = "proto-ipv4")]
1066 IpAddress::Ipv4(addr) => match self.caps.medium {
1067 #[cfg(feature = "medium-ethernet")]
1068 Medium::Ethernet => {
1069 let b = addr.octets();
1070 HardwareAddress::Ethernet(EthernetAddress::from_bytes(&[
1071 0x01,
1072 0x00,
1073 0x5e,
1074 b[1] & 0x7F,
1075 b[2],
1076 b[3],
1077 ]))
1078 }
1079 #[cfg(feature = "medium-ieee802154")]
1080 Medium::Ieee802154 => unreachable!(),
1081 #[cfg(feature = "medium-ip")]
1082 Medium::Ip => unreachable!(),
1083 },
1084 #[cfg(feature = "proto-ipv6")]
1085 IpAddress::Ipv6(addr) => match self.caps.medium {
1086 #[cfg(feature = "medium-ethernet")]
1087 Medium::Ethernet => {
1088 let b = addr.octets();
1089 HardwareAddress::Ethernet(EthernetAddress::from_bytes(&[
1090 0x33, 0x33, b[12], b[13], b[14], b[15],
1091 ]))
1092 }
1093 #[cfg(feature = "medium-ieee802154")]
1094 Medium::Ieee802154 => {
1095 HardwareAddress::Ieee802154(Ieee802154Address::BROADCAST)
1097 }
1098 #[cfg(feature = "medium-ip")]
1099 Medium::Ip => unreachable!(),
1100 },
1101 };
1102
1103 return Ok((hardware_addr, tx_token));
1104 }
1105
1106 let dst_addr = self
1107 .route(dst_addr, self.now)
1108 .ok_or(DispatchError::NoRoute)?;
1109
1110 match self.neighbor_cache.lookup(&dst_addr, self.now) {
1111 NeighborAnswer::Found(hardware_addr) => return Ok((hardware_addr, tx_token)),
1112 NeighborAnswer::RateLimited => return Err(DispatchError::NeighborPending),
1113 _ => (), }
1115
1116 match dst_addr {
1117 #[cfg(all(feature = "medium-ethernet", feature = "proto-ipv4"))]
1118 IpAddress::Ipv4(dst_addr) if matches!(self.caps.medium, Medium::Ethernet) => {
1119 net_debug!(
1120 "address {} not in neighbor cache, sending ARP request",
1121 dst_addr
1122 );
1123 let src_hardware_addr = self.hardware_addr.ethernet_or_panic();
1124
1125 let arp_repr = ArpRepr::EthernetIpv4 {
1126 operation: ArpOperation::Request,
1127 source_hardware_addr: src_hardware_addr,
1128 source_protocol_addr: self
1129 .get_source_address_ipv4(&dst_addr)
1130 .ok_or(DispatchError::NoRoute)?,
1131 target_hardware_addr: EthernetAddress::BROADCAST,
1132 target_protocol_addr: dst_addr,
1133 };
1134
1135 if let Err(e) =
1136 self.dispatch_ethernet(tx_token, arp_repr.buffer_len(), |mut frame| {
1137 frame.set_dst_addr(EthernetAddress::BROADCAST);
1138 frame.set_ethertype(EthernetProtocol::Arp);
1139
1140 arp_repr.emit(&mut ArpPacket::new_unchecked(frame.payload_mut()))
1141 })
1142 {
1143 net_debug!("Failed to dispatch ARP request: {:?}", e);
1144 return Err(DispatchError::NeighborPending);
1145 }
1146 }
1147
1148 #[cfg(feature = "proto-ipv6")]
1149 IpAddress::Ipv6(dst_addr) => {
1150 net_debug!(
1151 "address {} not in neighbor cache, sending Neighbor Solicitation",
1152 dst_addr
1153 );
1154
1155 let solicit = Icmpv6Repr::Ndisc(NdiscRepr::NeighborSolicit {
1156 target_addr: dst_addr,
1157 lladdr: Some(self.hardware_addr.into()),
1158 });
1159
1160 let packet = Packet::new_ipv6(
1161 Ipv6Repr {
1162 src_addr: self.get_source_address_ipv6(&dst_addr),
1163 dst_addr: dst_addr.solicited_node(),
1164 next_header: IpProtocol::Icmpv6,
1165 payload_len: solicit.buffer_len(),
1166 hop_limit: 0xff,
1167 },
1168 IpPayload::Icmpv6(solicit),
1169 );
1170
1171 if let Err(e) =
1172 self.dispatch_ip(tx_token, PacketMeta::default(), packet, fragmenter)
1173 {
1174 net_debug!("Failed to dispatch NDISC solicit: {:?}", e);
1175 return Err(DispatchError::NeighborPending);
1176 }
1177 }
1178
1179 #[allow(unreachable_patterns)]
1180 _ => (),
1181 }
1182
1183 self.neighbor_cache.limit_rate(self.now);
1185 Err(DispatchError::NeighborPending)
1186 }
1187
1188 fn flush_neighbor_cache(&mut self) {
1189 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
1190 self.neighbor_cache.flush()
1191 }
1192
1193 fn dispatch_ip<Tx: TxToken>(
1194 &mut self,
1195 #[allow(unused_mut)] mut tx_token: Tx,
1198 meta: PacketMeta,
1199 packet: Packet,
1200 frag: &mut Fragmenter,
1201 ) -> Result<(), DispatchError> {
1202 let mut ip_repr = packet.ip_repr();
1203 assert!(!ip_repr.dst_addr().is_unspecified());
1204
1205 #[cfg(feature = "medium-ieee802154")]
1208 if matches!(self.caps.medium, Medium::Ieee802154) {
1209 let (addr, tx_token) =
1210 self.lookup_hardware_addr(tx_token, &ip_repr.dst_addr(), frag)?;
1211 let addr = addr.ieee802154_or_panic();
1212
1213 self.dispatch_ieee802154(addr, tx_token, meta, packet, frag);
1214 return Ok(());
1215 }
1216
1217 let caps = self.caps.clone();
1220
1221 #[cfg(feature = "proto-ipv4-fragmentation")]
1222 let ipv4_id = self.next_ipv4_frag_ident();
1223
1224 let mut total_len = ip_repr.buffer_len();
1226
1227 #[cfg(feature = "medium-ethernet")]
1229 if matches!(self.caps.medium, Medium::Ethernet) {
1230 total_len = EthernetFrame::<&[u8]>::buffer_len(total_len);
1231 }
1232
1233 #[cfg(feature = "medium-ethernet")]
1235 let (dst_hardware_addr, mut tx_token) = match self.caps.medium {
1236 Medium::Ethernet => {
1237 match self.lookup_hardware_addr(tx_token, &ip_repr.dst_addr(), frag)? {
1238 (HardwareAddress::Ethernet(addr), tx_token) => (addr, tx_token),
1239 (_, _) => unreachable!(),
1240 }
1241 }
1242 _ => (EthernetAddress([0; 6]), tx_token),
1243 };
1244
1245 #[cfg(feature = "medium-ethernet")]
1247 let emit_ethernet = |repr: &IpRepr, tx_buffer: &mut [u8]| {
1248 let mut frame = EthernetFrame::new_unchecked(tx_buffer);
1249
1250 let src_addr = self.hardware_addr.ethernet_or_panic();
1251 frame.set_src_addr(src_addr);
1252 frame.set_dst_addr(dst_hardware_addr);
1253
1254 match repr.version() {
1255 #[cfg(feature = "proto-ipv4")]
1256 IpVersion::Ipv4 => frame.set_ethertype(EthernetProtocol::Ipv4),
1257 #[cfg(feature = "proto-ipv6")]
1258 IpVersion::Ipv6 => frame.set_ethertype(EthernetProtocol::Ipv6),
1259 }
1260 };
1261
1262 let emit_ip = |repr: &IpRepr, tx_buffer: &mut [u8]| {
1264 repr.emit(&mut *tx_buffer, &self.caps.checksum);
1265
1266 let payload = &mut tx_buffer[repr.header_len()..];
1267 packet.emit_payload(repr, payload, &caps)
1268 };
1269
1270 let total_ip_len = ip_repr.buffer_len();
1271
1272 match &mut ip_repr {
1273 #[cfg(feature = "proto-ipv4")]
1274 IpRepr::Ipv4(repr) => {
1275 if total_ip_len > self.caps.ip_mtu() {
1277 #[cfg(feature = "proto-ipv4-fragmentation")]
1278 {
1279 net_debug!("start fragmentation");
1280
1281 let ip_header_len = repr.buffer_len();
1284 let first_frag_data_len =
1285 self.caps.max_ipv4_fragment_size(repr.buffer_len());
1286 let first_frag_ip_len = first_frag_data_len + ip_header_len;
1287 let mut tx_len = first_frag_ip_len;
1288 #[cfg(feature = "medium-ethernet")]
1289 if matches!(caps.medium, Medium::Ethernet) {
1290 tx_len += EthernetFrame::<&[u8]>::header_len();
1291 }
1292
1293 if frag.buffer.len() < total_ip_len {
1294 net_debug!(
1295 "Fragmentation buffer is too small, at least {} needed. Dropping",
1296 total_ip_len
1297 );
1298 return Ok(());
1299 }
1300
1301 #[cfg(feature = "medium-ethernet")]
1302 {
1303 frag.ipv4.dst_hardware_addr = dst_hardware_addr;
1304 }
1305
1306 frag.packet_len = total_ip_len;
1309
1310 frag.ipv4.repr = *repr;
1312
1313 repr.payload_len = first_frag_data_len;
1315
1316 frag.sent_bytes = first_frag_ip_len;
1318
1319 emit_ip(&ip_repr, &mut frag.buffer);
1321
1322 let mut ipv4_packet = Ipv4Packet::new_unchecked(&mut frag.buffer[..]);
1323 frag.ipv4.ident = ipv4_id;
1324 ipv4_packet.set_ident(ipv4_id);
1325 ipv4_packet.set_more_frags(true);
1326 ipv4_packet.set_dont_frag(false);
1327 ipv4_packet.set_frag_offset(0);
1328
1329 if caps.checksum.ipv4.tx() {
1330 ipv4_packet.fill_checksum();
1331 }
1332
1333 tx_token.consume(tx_len, |mut tx_buffer| {
1335 #[cfg(feature = "medium-ethernet")]
1336 if matches!(self.caps.medium, Medium::Ethernet) {
1337 emit_ethernet(&ip_repr, tx_buffer);
1338 tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..];
1339 }
1340
1341 frag.ipv4.frag_offset = (first_frag_ip_len - ip_header_len) as u16;
1343
1344 tx_buffer[..first_frag_ip_len]
1346 .copy_from_slice(&frag.buffer[..first_frag_ip_len]);
1347 });
1348
1349 Ok(())
1350 }
1351
1352 #[cfg(not(feature = "proto-ipv4-fragmentation"))]
1353 {
1354 net_debug!(
1355 "Enable the `proto-ipv4-fragmentation` feature for fragmentation support."
1356 );
1357 Ok(())
1358 }
1359 } else {
1360 tx_token.set_meta(meta);
1361
1362 tx_token.consume(total_len, |mut tx_buffer| {
1364 #[cfg(feature = "medium-ethernet")]
1365 if matches!(self.caps.medium, Medium::Ethernet) {
1366 emit_ethernet(&ip_repr, tx_buffer);
1367 tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..];
1368 }
1369
1370 emit_ip(&ip_repr, tx_buffer);
1371 });
1372
1373 Ok(())
1374 }
1375 }
1376 #[cfg(feature = "proto-ipv6")]
1378 IpRepr::Ipv6(_) => {
1379 if total_ip_len > self.caps.ip_mtu() {
1381 net_debug!("IPv6 fragmentation support is unimplemented. Dropping.");
1382 Ok(())
1383 } else {
1384 tx_token.consume(total_len, |mut tx_buffer| {
1385 #[cfg(feature = "medium-ethernet")]
1386 if matches!(self.caps.medium, Medium::Ethernet) {
1387 emit_ethernet(&ip_repr, tx_buffer);
1388 tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..];
1389 }
1390
1391 emit_ip(&ip_repr, tx_buffer);
1392 });
1393 Ok(())
1394 }
1395 }
1396 }
1397 }
1398}
1399
1400#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1401#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1402enum DispatchError {
1403 NoRoute,
1406 NeighborPending,
1410}