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 #[cfg(feature = "segmentation-offload")]
216 if [caps.segmentation.tcpv4, caps.segmentation.tcpv6]
218 .iter()
219 .any(Option::is_some)
220 {
221 assert!(
222 !caps.checksum.tcp.tx(),
223 "Device capabilities are inconsistent."
224 )
225 }
226
227 let mut rand = Rand::new(config.random_seed);
228
229 #[cfg(feature = "medium-ieee802154")]
230 let mut sequence_no;
231 #[cfg(feature = "medium-ieee802154")]
232 loop {
233 sequence_no = (rand.rand_u32() & 0xff) as u8;
234 if sequence_no != 0 {
235 break;
236 }
237 }
238
239 #[cfg(feature = "proto-sixlowpan")]
240 let mut tag;
241
242 #[cfg(feature = "proto-sixlowpan")]
243 loop {
244 tag = rand.rand_u16();
245 if tag != 0 {
246 break;
247 }
248 }
249
250 #[cfg(feature = "proto-ipv4")]
251 let mut ipv4_id;
252
253 #[cfg(feature = "proto-ipv4")]
254 loop {
255 ipv4_id = rand.rand_u16();
256 if ipv4_id != 0 {
257 break;
258 }
259 }
260
261 Interface {
262 fragments: FragmentsBuffer {
263 #[cfg(feature = "proto-sixlowpan")]
264 decompress_buf: [0u8; sixlowpan::MAX_DECOMPRESSED_LEN],
265
266 #[cfg(feature = "_proto-fragmentation")]
267 assembler: PacketAssemblerSet::new(),
268 #[cfg(feature = "_proto-fragmentation")]
269 reassembly_timeout: Duration::from_secs(60),
270 },
271 fragmenter: Fragmenter::new(),
272 inner: InterfaceInner {
273 now,
274 caps,
275 hardware_addr: config.hardware_addr,
276 ip_addrs: Vec::new(),
277 any_ip: false,
278 routes: Routes::new(),
279 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
280 neighbor_cache: NeighborCache::new(),
281 #[cfg(feature = "multicast")]
282 multicast: multicast::State::new(),
283 #[cfg(feature = "medium-ieee802154")]
284 sequence_no,
285 #[cfg(feature = "medium-ieee802154")]
286 pan_id: config.pan_id,
287 #[cfg(feature = "proto-sixlowpan-fragmentation")]
288 tag,
289 #[cfg(feature = "proto-ipv4-fragmentation")]
290 ipv4_id,
291 #[cfg(feature = "proto-sixlowpan")]
292 sixlowpan_address_context: Vec::new(),
293 #[cfg(feature = "proto-ipv6-slaac")]
294 slaac_enabled: config.slaac,
295 #[cfg(feature = "proto-ipv6-slaac")]
296 slaac: Slaac::new(),
297 #[cfg(feature = "proto-ipv6-slaac")]
298 slaac_updated: Instant::from_millis(0),
299 rand,
300 },
301 }
302 }
303
304 pub fn context(&mut self) -> &mut InterfaceInner {
308 &mut self.inner
309 }
310
311 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
316 pub fn hardware_addr(&self) -> HardwareAddress {
317 #[cfg(all(feature = "medium-ethernet", not(feature = "medium-ieee802154")))]
318 assert!(self.inner.caps.medium == Medium::Ethernet);
319 #[cfg(all(feature = "medium-ieee802154", not(feature = "medium-ethernet")))]
320 assert!(self.inner.caps.medium == Medium::Ieee802154);
321
322 #[cfg(all(feature = "medium-ieee802154", feature = "medium-ethernet"))]
323 assert!(
324 self.inner.caps.medium == Medium::Ethernet
325 || self.inner.caps.medium == Medium::Ieee802154
326 );
327
328 self.inner.hardware_addr
329 }
330
331 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
337 pub fn set_hardware_addr(&mut self, addr: HardwareAddress) {
338 #[cfg(all(feature = "medium-ethernet", not(feature = "medium-ieee802154")))]
339 assert!(self.inner.caps.medium == Medium::Ethernet);
340 #[cfg(all(feature = "medium-ieee802154", not(feature = "medium-ethernet")))]
341 assert!(self.inner.caps.medium == Medium::Ieee802154);
342
343 #[cfg(all(feature = "medium-ieee802154", feature = "medium-ethernet"))]
344 assert!(
345 self.inner.caps.medium == Medium::Ethernet
346 || self.inner.caps.medium == Medium::Ieee802154
347 );
348
349 InterfaceInner::check_hardware_addr(&addr);
350 self.inner.hardware_addr = addr;
351 }
352
353 pub fn ip_addrs(&self) -> &[IpCidr] {
355 self.inner.ip_addrs.as_ref()
356 }
357
358 #[cfg(feature = "proto-ipv4")]
360 pub fn ipv4_addr(&self) -> Option<Ipv4Address> {
361 self.inner.ipv4_addr()
362 }
363
364 #[cfg(feature = "proto-ipv6")]
366 pub fn ipv6_addr(&self) -> Option<Ipv6Address> {
367 self.inner.ipv6_addr()
368 }
369
370 pub fn get_source_address(&self, dst_addr: &IpAddress) -> Option<IpAddress> {
375 self.inner.get_source_address(dst_addr)
376 }
377
378 #[cfg(feature = "proto-ipv4")]
383 pub fn get_source_address_ipv4(&self, dst_addr: &Ipv4Address) -> Option<Ipv4Address> {
384 self.inner.get_source_address_ipv4(dst_addr)
385 }
386
387 #[cfg(feature = "proto-ipv6")]
390 pub fn get_source_address_ipv6(&self, dst_addr: &Ipv6Address) -> Ipv6Address {
391 self.inner.get_source_address_ipv6(dst_addr)
392 }
393
394 pub fn update_ip_addrs<F: FnOnce(&mut Vec<IpCidr, IFACE_MAX_ADDR_COUNT>)>(&mut self, f: F) {
399 f(&mut self.inner.ip_addrs);
400 InterfaceInner::flush_neighbor_cache(&mut self.inner);
401 InterfaceInner::check_ip_addrs(&self.inner.ip_addrs);
402
403 #[cfg(all(
404 feature = "proto-ipv6",
405 feature = "multicast",
406 feature = "medium-ethernet"
407 ))]
408 if self.inner.caps.medium == Medium::Ethernet {
409 self.update_solicited_node_groups();
410 }
411 }
412
413 pub fn has_ip_addr<T: Into<IpAddress>>(&self, addr: T) -> bool {
415 self.inner.has_ip_addr(addr)
416 }
417
418 pub fn routes(&self) -> &Routes {
419 &self.inner.routes
420 }
421
422 pub fn routes_mut(&mut self) -> &mut Routes {
423 &mut self.inner.routes
424 }
425
426 pub fn set_any_ip(&mut self, any_ip: bool) {
434 self.inner.any_ip = any_ip;
435 }
436
437 pub fn any_ip(&self) -> bool {
441 self.inner.any_ip
442 }
443
444 #[cfg(feature = "_proto-fragmentation")]
446 pub fn reassembly_timeout(&self) -> Duration {
447 self.fragments.reassembly_timeout
448 }
449
450 #[cfg(feature = "_proto-fragmentation")]
452 pub fn set_reassembly_timeout(&mut self, timeout: Duration) {
453 if timeout > Duration::from_secs(60) {
454 net_debug!(
455 "RFC 4944 specifies that the reassembly timeout MUST be set to a maximum of 60 seconds"
456 );
457 }
458 self.fragments.reassembly_timeout = timeout;
459 }
460
461 pub fn poll(
481 &mut self,
482 timestamp: Instant,
483 device: &mut (impl Device + ?Sized),
484 sockets: &mut SocketSet<'_>,
485 ) -> PollResult {
486 self.inner.now = timestamp;
487
488 let mut res = PollResult::None;
489
490 self.poll_maintenance(timestamp);
491
492 loop {
494 match self.socket_ingress(device, sockets) {
495 PollIngressSingleResult::None => break,
496 PollIngressSingleResult::PacketProcessed => {}
497 PollIngressSingleResult::SocketStateChanged => res = PollResult::SocketStateChanged,
498 }
499 }
500
501 loop {
503 match self.poll_egress(timestamp, device, sockets) {
504 PollResult::None => break,
505 PollResult::SocketStateChanged => res = PollResult::SocketStateChanged,
506 }
507 }
508
509 res
510 }
511
512 pub fn poll_egress(
519 &mut self,
520 timestamp: Instant,
521 device: &mut (impl Device + ?Sized),
522 sockets: &mut SocketSet<'_>,
523 ) -> PollResult {
524 self.inner.now = timestamp;
525
526 match self.inner.caps.medium {
527 #[cfg(feature = "medium-ieee802154")]
528 Medium::Ieee802154 => {
529 #[cfg(feature = "proto-sixlowpan-fragmentation")]
530 self.sixlowpan_egress(device);
531 }
532 #[cfg(any(feature = "medium-ethernet", feature = "medium-ip"))]
533 _ => {
534 #[cfg(feature = "proto-ipv4-fragmentation")]
535 self.ipv4_egress(device);
536 }
537 }
538
539 #[cfg(feature = "proto-ipv6-slaac")]
540 if self.inner.slaac_enabled {
541 self.ndisc_rs_egress(device);
542 }
543
544 #[cfg(feature = "multicast")]
545 self.multicast_egress(device);
546
547 self.socket_egress(device, sockets)
548 }
549
550 pub fn poll_ingress_single(
558 &mut self,
559 timestamp: Instant,
560 device: &mut (impl Device + ?Sized),
561 sockets: &mut SocketSet<'_>,
562 ) -> PollIngressSingleResult {
563 self.inner.now = timestamp;
564
565 #[cfg(feature = "_proto-fragmentation")]
566 self.fragments.assembler.remove_expired(timestamp);
567
568 self.socket_ingress(device, sockets)
569 }
570
571 pub fn poll_maintenance(&mut self, timestamp: Instant) {
575 self.inner.now = timestamp;
576
577 #[cfg(feature = "_proto-fragmentation")]
578 self.fragments.assembler.remove_expired(timestamp);
579
580 #[cfg(feature = "proto-ipv6-slaac")]
581 if self.inner.slaac.sync_required(timestamp) {
582 self.sync_slaac_state(timestamp)
583 }
584 }
585
586 pub fn poll_at(&mut self, timestamp: Instant, sockets: &SocketSet<'_>) -> Option<Instant> {
595 self.inner.now = timestamp;
596
597 #[cfg(feature = "_proto-fragmentation")]
598 if !self.fragmenter.is_empty() {
599 return Some(Instant::from_millis(0));
600 }
601
602 #[allow(unused_mut)]
603 let mut res = sockets
604 .items()
605 .filter_map(|item| {
606 let socket_poll_at = item.socket.poll_at(&mut self.inner);
607 match item.meta.poll_at(
608 socket_poll_at,
609 |ip_addr| self.inner.has_neighbor(&ip_addr),
610 timestamp,
611 ) {
612 PollAt::Ingress => None,
613 PollAt::Time(instant) => Some(instant),
614 PollAt::Now => Some(Instant::from_millis(0)),
615 }
616 })
617 .min();
618
619 #[cfg(feature = "proto-ipv6-slaac")]
620 if self.inner.slaac_enabled {
621 res = res.min(self.inner.slaac.poll_at(timestamp));
622 }
623
624 res
625 }
626
627 pub fn poll_delay(&mut self, timestamp: Instant, sockets: &SocketSet<'_>) -> Option<Duration> {
636 match self.poll_at(timestamp, sockets) {
637 Some(poll_at) if timestamp < poll_at => Some(poll_at - timestamp),
638 Some(_) => Some(Duration::from_millis(0)),
639 _ => None,
640 }
641 }
642
643 fn socket_ingress(
644 &mut self,
645 device: &mut (impl Device + ?Sized),
646 sockets: &mut SocketSet<'_>,
647 ) -> PollIngressSingleResult {
648 let Some((rx_token, tx_token)) = device.receive(self.inner.now) else {
649 return PollIngressSingleResult::None;
650 };
651
652 let rx_meta = rx_token.meta();
653 rx_token.consume(|frame| {
654 if frame.is_empty() {
655 return PollIngressSingleResult::PacketProcessed;
656 }
657
658 match self.inner.caps.medium {
659 #[cfg(feature = "medium-ethernet")]
660 Medium::Ethernet => {
661 if let Some(packet) =
662 self.inner
663 .process_ethernet(sockets, rx_meta, frame, &mut self.fragments)
664 && let Err(err) =
665 self.inner.dispatch(tx_token, packet, &mut self.fragmenter)
666 {
667 net_debug!("Failed to send response: {:?}", err);
668 }
669 }
670 #[cfg(feature = "medium-ip")]
671 Medium::Ip => {
672 if let Some(packet) =
673 self.inner
674 .process_ip(sockets, rx_meta, frame, &mut self.fragments)
675 && let Err(err) = self.inner.dispatch_ip(
676 tx_token,
677 PacketMeta::default(),
678 packet,
679 &mut self.fragmenter,
680 )
681 {
682 net_debug!("Failed to send response: {:?}", err);
683 }
684 }
685 #[cfg(feature = "medium-ieee802154")]
686 Medium::Ieee802154 => {
687 if let Some(packet) =
688 self.inner
689 .process_ieee802154(sockets, rx_meta, frame, &mut self.fragments)
690 && let Err(err) = self.inner.dispatch_ip(
691 tx_token,
692 PacketMeta::default(),
693 packet,
694 &mut self.fragmenter,
695 )
696 {
697 net_debug!("Failed to send response: {:?}", err);
698 }
699 }
700 }
701
702 PollIngressSingleResult::SocketStateChanged
709 })
710 }
711
712 fn socket_egress(
713 &mut self,
714 device: &mut (impl Device + ?Sized),
715 sockets: &mut SocketSet<'_>,
716 ) -> PollResult {
717 let _caps = device.capabilities();
718
719 enum EgressError {
720 Exhausted,
721 Dispatch,
722 }
723
724 let mut result = PollResult::None;
725 for item in sockets.items_mut() {
726 if !item
727 .meta
728 .egress_permitted(self.inner.now, |ip_addr| self.inner.has_neighbor(&ip_addr))
729 {
730 continue;
731 }
732
733 let mut neighbor_addr = None;
734 let mut respond = |inner: &mut InterfaceInner, meta: PacketMeta, response: Packet| {
735 neighbor_addr = Some(response.ip_repr().dst_addr());
736 let t = device.transmit(inner.now).ok_or_else(|| {
737 net_debug!("failed to transmit IP: device exhausted");
738 EgressError::Exhausted
739 })?;
740
741 inner
742 .dispatch_ip(t, meta, response, &mut self.fragmenter)
743 .map_err(|_| EgressError::Dispatch)?;
744
745 result = PollResult::SocketStateChanged;
746
747 Ok(())
748 };
749
750 let result = match &mut item.socket {
751 #[cfg(feature = "socket-raw")]
752 Socket::Raw(socket) => socket.dispatch(&mut self.inner, |inner, (ip, raw)| {
753 respond(
754 inner,
755 PacketMeta::default(),
756 Packet::new(ip, IpPayload::Raw(raw)),
757 )
758 }),
759 #[cfg(feature = "socket-icmp")]
760 Socket::Icmp(socket) => {
761 socket.dispatch(&mut self.inner, |inner, response| match response {
762 #[cfg(feature = "proto-ipv4")]
763 (IpRepr::Ipv4(ipv4_repr), IcmpRepr::Ipv4(icmpv4_repr)) => respond(
764 inner,
765 PacketMeta::default(),
766 Packet::new_ipv4(ipv4_repr, IpPayload::Icmpv4(icmpv4_repr)),
767 ),
768 #[cfg(feature = "proto-ipv6")]
769 (IpRepr::Ipv6(ipv6_repr), IcmpRepr::Ipv6(icmpv6_repr)) => respond(
770 inner,
771 PacketMeta::default(),
772 Packet::new_ipv6(ipv6_repr, IpPayload::Icmpv6(icmpv6_repr)),
773 ),
774 #[allow(unreachable_patterns)]
775 _ => unreachable!(),
776 })
777 }
778 #[cfg(feature = "socket-udp")]
779 Socket::Udp(socket) => {
780 socket.dispatch(&mut self.inner, |inner, meta, (ip, udp, payload)| {
781 respond(inner, meta, Packet::new(ip, IpPayload::Udp(udp, payload)))
782 })
783 }
784 #[cfg(feature = "socket-tcp")]
785 Socket::Tcp(socket) => {
786 socket.dispatch(&mut self.inner, |inner, meta, (ip, tcp)| {
787 respond(inner, meta, Packet::new(ip, IpPayload::Tcp(tcp)))
788 })
789 }
790 #[cfg(feature = "socket-dhcpv4")]
791 Socket::Dhcpv4(socket) => {
792 socket.dispatch(&mut self.inner, |inner, (ip, udp, dhcp)| {
793 respond(
794 inner,
795 PacketMeta::default(),
796 Packet::new_ipv4(ip, IpPayload::Dhcpv4(udp, dhcp)),
797 )
798 })
799 }
800 #[cfg(feature = "socket-dns")]
801 Socket::Dns(socket) => socket.dispatch(&mut self.inner, |inner, (ip, udp, dns)| {
802 respond(
803 inner,
804 PacketMeta::default(),
805 Packet::new(ip, IpPayload::Udp(udp, dns)),
806 )
807 }),
808 };
809
810 match result {
811 Err(EgressError::Exhausted) => break, Err(EgressError::Dispatch) => {
813 item.meta.neighbor_missing(
818 self.inner.now,
819 neighbor_addr.expect("non-IP response packet"),
820 );
821 }
822 Ok(()) => {}
823 }
824 }
825 result
826 }
827}
828
829impl InterfaceInner {
830 #[allow(unused)] pub(crate) fn now(&self) -> Instant {
832 self.now
833 }
834
835 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
836 #[allow(unused)] pub(crate) fn hardware_addr(&self) -> HardwareAddress {
838 self.hardware_addr
839 }
840
841 #[allow(unused)] pub(crate) fn checksum_caps(&self) -> ChecksumCapabilities {
843 self.caps.checksum.clone()
844 }
845
846 #[cfg(feature = "segmentation-offload")]
847 #[allow(unused)] pub(crate) fn segmentation_caps(&self) -> crate::phy::SegmentationCapabilities {
849 self.caps.segmentation.clone()
850 }
851
852 #[allow(unused)] pub(crate) fn max_transmission_unit(&self) -> usize {
854 self.caps.max_transmission_unit
855 }
856
857 #[allow(unused)] pub(crate) fn ip_mtu(&self) -> usize {
859 self.caps.ip_mtu()
860 }
861
862 #[allow(unused)] pub(crate) fn rand(&mut self) -> &mut Rand {
864 &mut self.rand
865 }
866
867 #[allow(unused)] pub(crate) fn get_source_address(&self, dst_addr: &IpAddress) -> Option<IpAddress> {
869 match dst_addr {
870 #[cfg(feature = "proto-ipv4")]
871 IpAddress::Ipv4(addr) => self.get_source_address_ipv4(addr).map(|a| a.into()),
872 #[cfg(feature = "proto-ipv6")]
873 IpAddress::Ipv6(addr) => Some(self.get_source_address_ipv6(addr).into()),
874 }
875 }
876
877 #[cfg(test)]
878 #[allow(unused)] pub(crate) fn set_now(&mut self, now: Instant) {
880 self.now = now
881 }
882
883 #[cfg(test)]
884 #[allow(unused)] pub(crate) fn set_ip_addrs(&mut self, addrs: Vec<IpCidr, IFACE_MAX_ADDR_COUNT>) {
886 self.ip_addrs = addrs;
887 }
888
889 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
890 fn check_hardware_addr(addr: &HardwareAddress) {
891 if !addr.is_unicast() {
892 panic!("Hardware address {addr} is not unicast")
893 }
894 }
895
896 fn check_ip_addrs(addrs: &[IpCidr]) {
897 for cidr in addrs {
898 if !cidr.address().is_unicast() && !cidr.address().is_unspecified() {
899 panic!("IP address {} is not unicast", cidr.address())
900 }
901 }
902 }
903
904 pub(crate) fn has_ip_addr<T: Into<IpAddress>>(&self, addr: T) -> bool {
908 if self.any_ip {
910 return true;
911 }
912
913 let addr = addr.into();
914 self.ip_addrs.iter().any(|probe| probe.address() == addr)
915 }
916
917 fn has_multicast_group<T: Into<IpAddress>>(&self, addr: T) -> bool {
919 let addr = addr.into();
920
921 #[cfg(feature = "multicast")]
922 if self.multicast.has_multicast_group(addr) {
923 return true;
924 }
925
926 match addr {
927 #[cfg(feature = "proto-ipv4")]
928 IpAddress::Ipv4(key) => key == IPV4_MULTICAST_ALL_SYSTEMS,
929 #[cfg(feature = "proto-rpl")]
930 IpAddress::Ipv6(IPV6_LINK_LOCAL_ALL_RPL_NODES) => true,
931 #[cfg(feature = "proto-ipv6")]
932 IpAddress::Ipv6(key) => {
933 key == IPV6_LINK_LOCAL_ALL_NODES || self.has_solicited_node(key)
934 }
935 #[allow(unreachable_patterns)]
936 _ => false,
937 }
938 }
939
940 #[cfg(feature = "medium-ip")]
941 fn process_ip<'frame>(
942 &mut self,
943 sockets: &mut SocketSet,
944 meta: PacketMeta,
945 ip_payload: &'frame [u8],
946 frag: &'frame mut FragmentsBuffer,
947 ) -> Option<Packet<'frame>> {
948 match IpVersion::of_packet(ip_payload) {
949 #[cfg(feature = "proto-ipv4")]
950 Ok(IpVersion::Ipv4) => {
951 let ipv4_packet = check!(Ipv4Packet::new_checked(ip_payload));
952 self.process_ipv4(sockets, meta, HardwareAddress::Ip, &ipv4_packet, frag)
953 }
954 #[cfg(feature = "proto-ipv6")]
955 Ok(IpVersion::Ipv6) => {
956 let ipv6_packet = check!(Ipv6Packet::new_checked(ip_payload));
957 self.process_ipv6(sockets, meta, HardwareAddress::Ip, &ipv6_packet)
958 }
959 _ => None,
961 }
962 }
963
964 #[cfg(feature = "socket-raw")]
965 fn raw_socket_filter(
966 &mut self,
967 sockets: &mut SocketSet,
968 ip_repr: &IpRepr,
969 ip_payload: &[u8],
970 ) -> bool {
971 let mut handled_by_raw_socket = false;
972
973 for raw_socket in sockets
975 .items_mut()
976 .filter_map(|i| raw::Socket::downcast_mut(&mut i.socket))
977 {
978 if raw_socket.accepts(ip_repr) {
979 raw_socket.process(self, ip_repr, ip_payload);
980 handled_by_raw_socket = true;
981 }
982 }
983 handled_by_raw_socket
984 }
985
986 pub(crate) fn is_broadcast(&self, address: &IpAddress) -> bool {
989 match address {
990 #[cfg(feature = "proto-ipv4")]
991 IpAddress::Ipv4(address) => self.is_broadcast_v4(*address),
992 #[cfg(feature = "proto-ipv6")]
993 IpAddress::Ipv6(_) => false,
994 }
995 }
996
997 #[cfg(feature = "medium-ethernet")]
998 fn dispatch<Tx>(
999 &mut self,
1000 tx_token: Tx,
1001 packet: EthernetPacket,
1002 frag: &mut Fragmenter,
1003 ) -> Result<(), DispatchError>
1004 where
1005 Tx: TxToken,
1006 {
1007 match packet {
1008 #[cfg(feature = "proto-ipv4")]
1009 EthernetPacket::Arp(arp_repr) => {
1010 let dst_hardware_addr = match arp_repr {
1011 ArpRepr::EthernetIpv4 {
1012 target_hardware_addr,
1013 ..
1014 } => target_hardware_addr,
1015 };
1016
1017 self.dispatch_ethernet(tx_token, arp_repr.buffer_len(), |mut frame| {
1018 frame.set_dst_addr(dst_hardware_addr);
1019 frame.set_ethertype(EthernetProtocol::Arp);
1020
1021 let mut packet = ArpPacket::new_unchecked(frame.payload_mut());
1022 arp_repr.emit(&mut packet);
1023 })
1024 }
1025 EthernetPacket::Ip(packet) => {
1026 self.dispatch_ip(tx_token, PacketMeta::default(), packet, frag)
1027 }
1028 }
1029 }
1030
1031 fn in_same_network(&self, addr: &IpAddress) -> bool {
1032 self.ip_addrs.iter().any(|cidr| cidr.contains_addr(addr))
1033 }
1034
1035 fn route(&self, addr: &IpAddress, timestamp: Instant) -> Option<IpAddress> {
1036 if self.in_same_network(addr) || addr.is_broadcast() {
1040 return Some(*addr);
1041 }
1042
1043 self.routes.lookup(addr, timestamp)
1045 }
1046
1047 fn has_neighbor(&self, addr: &IpAddress) -> bool {
1048 match self.route(addr, self.now) {
1049 Some(_routed_addr) => match self.caps.medium {
1050 #[cfg(feature = "medium-ethernet")]
1051 Medium::Ethernet => self.neighbor_cache.lookup(&_routed_addr, self.now).found(),
1052 #[cfg(feature = "medium-ieee802154")]
1053 Medium::Ieee802154 => self.neighbor_cache.lookup(&_routed_addr, self.now).found(),
1054 #[cfg(feature = "medium-ip")]
1055 Medium::Ip => true,
1056 },
1057 None => false,
1058 }
1059 }
1060
1061 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
1062 fn lookup_hardware_addr<Tx>(
1063 &mut self,
1064 tx_token: Tx,
1065 dst_addr: &IpAddress,
1066 fragmenter: &mut Fragmenter,
1067 ) -> Result<(HardwareAddress, Tx), DispatchError>
1068 where
1069 Tx: TxToken,
1070 {
1071 if self.is_broadcast(dst_addr) {
1072 let hardware_addr = match self.caps.medium {
1073 #[cfg(feature = "medium-ethernet")]
1074 Medium::Ethernet => HardwareAddress::Ethernet(EthernetAddress::BROADCAST),
1075 #[cfg(feature = "medium-ieee802154")]
1076 Medium::Ieee802154 => HardwareAddress::Ieee802154(Ieee802154Address::BROADCAST),
1077 #[cfg(feature = "medium-ip")]
1078 Medium::Ip => unreachable!(),
1079 };
1080
1081 return Ok((hardware_addr, tx_token));
1082 }
1083
1084 if dst_addr.is_multicast() {
1085 let hardware_addr = match *dst_addr {
1086 #[cfg(feature = "proto-ipv4")]
1087 IpAddress::Ipv4(addr) => match self.caps.medium {
1088 #[cfg(feature = "medium-ethernet")]
1089 Medium::Ethernet => {
1090 let b = addr.octets();
1091 HardwareAddress::Ethernet(EthernetAddress::from_bytes(&[
1092 0x01,
1093 0x00,
1094 0x5e,
1095 b[1] & 0x7F,
1096 b[2],
1097 b[3],
1098 ]))
1099 }
1100 #[cfg(feature = "medium-ieee802154")]
1101 Medium::Ieee802154 => unreachable!(),
1102 #[cfg(feature = "medium-ip")]
1103 Medium::Ip => unreachable!(),
1104 },
1105 #[cfg(feature = "proto-ipv6")]
1106 IpAddress::Ipv6(addr) => match self.caps.medium {
1107 #[cfg(feature = "medium-ethernet")]
1108 Medium::Ethernet => {
1109 let b = addr.octets();
1110 HardwareAddress::Ethernet(EthernetAddress::from_bytes(&[
1111 0x33, 0x33, b[12], b[13], b[14], b[15],
1112 ]))
1113 }
1114 #[cfg(feature = "medium-ieee802154")]
1115 Medium::Ieee802154 => {
1116 HardwareAddress::Ieee802154(Ieee802154Address::BROADCAST)
1118 }
1119 #[cfg(feature = "medium-ip")]
1120 Medium::Ip => unreachable!(),
1121 },
1122 };
1123
1124 return Ok((hardware_addr, tx_token));
1125 }
1126
1127 let dst_addr = self
1128 .route(dst_addr, self.now)
1129 .ok_or(DispatchError::NoRoute)?;
1130
1131 match self.neighbor_cache.lookup(&dst_addr, self.now) {
1132 NeighborAnswer::Found(hardware_addr) => return Ok((hardware_addr, tx_token)),
1133 NeighborAnswer::RateLimited => return Err(DispatchError::NeighborPending),
1134 _ => (), }
1136
1137 match dst_addr {
1138 #[cfg(all(feature = "medium-ethernet", feature = "proto-ipv4"))]
1139 IpAddress::Ipv4(dst_addr) if matches!(self.caps.medium, Medium::Ethernet) => {
1140 net_debug!(
1141 "address {} not in neighbor cache, sending ARP request",
1142 dst_addr
1143 );
1144 let src_hardware_addr = self.hardware_addr.ethernet_or_panic();
1145
1146 let arp_repr = ArpRepr::EthernetIpv4 {
1147 operation: ArpOperation::Request,
1148 source_hardware_addr: src_hardware_addr,
1149 source_protocol_addr: self
1150 .get_source_address_ipv4(&dst_addr)
1151 .ok_or(DispatchError::NoRoute)?,
1152 target_hardware_addr: EthernetAddress::BROADCAST,
1153 target_protocol_addr: dst_addr,
1154 };
1155
1156 if let Err(e) =
1157 self.dispatch_ethernet(tx_token, arp_repr.buffer_len(), |mut frame| {
1158 frame.set_dst_addr(EthernetAddress::BROADCAST);
1159 frame.set_ethertype(EthernetProtocol::Arp);
1160
1161 arp_repr.emit(&mut ArpPacket::new_unchecked(frame.payload_mut()))
1162 })
1163 {
1164 net_debug!("Failed to dispatch ARP request: {:?}", e);
1165 return Err(DispatchError::NeighborPending);
1166 }
1167 }
1168
1169 #[cfg(feature = "proto-ipv6")]
1170 IpAddress::Ipv6(dst_addr) => {
1171 net_debug!(
1172 "address {} not in neighbor cache, sending Neighbor Solicitation",
1173 dst_addr
1174 );
1175
1176 let solicit = Icmpv6Repr::Ndisc(NdiscRepr::NeighborSolicit {
1177 target_addr: dst_addr,
1178 lladdr: Some(self.hardware_addr.into()),
1179 });
1180
1181 let packet = Packet::new_ipv6(
1182 Ipv6Repr {
1183 src_addr: self.get_source_address_ipv6(&dst_addr),
1184 dst_addr: dst_addr.solicited_node(),
1185 next_header: IpProtocol::Icmpv6,
1186 payload_len: solicit.buffer_len(),
1187 hop_limit: 0xff,
1188 },
1189 IpPayload::Icmpv6(solicit),
1190 );
1191
1192 if let Err(e) =
1193 self.dispatch_ip(tx_token, PacketMeta::default(), packet, fragmenter)
1194 {
1195 net_debug!("Failed to dispatch NDISC solicit: {:?}", e);
1196 return Err(DispatchError::NeighborPending);
1197 }
1198 }
1199
1200 #[allow(unreachable_patterns)]
1201 _ => (),
1202 }
1203
1204 self.neighbor_cache.limit_rate(self.now);
1206 Err(DispatchError::NeighborPending)
1207 }
1208
1209 fn flush_neighbor_cache(&mut self) {
1210 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
1211 self.neighbor_cache.flush()
1212 }
1213
1214 fn dispatch_ip<Tx: TxToken>(
1215 &mut self,
1216 #[allow(unused_mut)] mut tx_token: Tx,
1219 meta: PacketMeta,
1220 packet: Packet,
1221 frag: &mut Fragmenter,
1222 ) -> Result<(), DispatchError> {
1223 let mut ip_repr = packet.ip_repr();
1224 assert!(!ip_repr.dst_addr().is_unspecified());
1225
1226 #[cfg(feature = "medium-ieee802154")]
1229 if matches!(self.caps.medium, Medium::Ieee802154) {
1230 let (addr, tx_token) =
1231 self.lookup_hardware_addr(tx_token, &ip_repr.dst_addr(), frag)?;
1232 let addr = addr.ieee802154_or_panic();
1233
1234 self.dispatch_ieee802154(addr, tx_token, meta, packet, frag);
1235 return Ok(());
1236 }
1237
1238 let caps = self.caps.clone();
1241
1242 #[cfg(feature = "proto-ipv4-fragmentation")]
1243 let ipv4_id = self.next_ipv4_frag_ident();
1244
1245 let mut total_len = ip_repr.buffer_len();
1247
1248 #[cfg(feature = "medium-ethernet")]
1250 if matches!(self.caps.medium, Medium::Ethernet) {
1251 total_len = EthernetFrame::<&[u8]>::buffer_len(total_len);
1252 }
1253
1254 #[cfg(feature = "medium-ethernet")]
1256 let (dst_hardware_addr, mut tx_token) = match self.caps.medium {
1257 Medium::Ethernet => {
1258 match self.lookup_hardware_addr(tx_token, &ip_repr.dst_addr(), frag)? {
1259 (HardwareAddress::Ethernet(addr), tx_token) => (addr, tx_token),
1260 (_, _) => unreachable!(),
1261 }
1262 }
1263 _ => (EthernetAddress([0; 6]), tx_token),
1264 };
1265
1266 #[cfg(feature = "medium-ethernet")]
1268 let emit_ethernet = |repr: &IpRepr, tx_buffer: &mut [u8]| {
1269 let mut frame = EthernetFrame::new_unchecked(tx_buffer);
1270
1271 let src_addr = self.hardware_addr.ethernet_or_panic();
1272 frame.set_src_addr(src_addr);
1273 frame.set_dst_addr(dst_hardware_addr);
1274
1275 match repr.version() {
1276 #[cfg(feature = "proto-ipv4")]
1277 IpVersion::Ipv4 => frame.set_ethertype(EthernetProtocol::Ipv4),
1278 #[cfg(feature = "proto-ipv6")]
1279 IpVersion::Ipv6 => frame.set_ethertype(EthernetProtocol::Ipv6),
1280 }
1281 };
1282
1283 let emit_ip = |repr: &IpRepr, tx_buffer: &mut [u8]| {
1285 repr.emit(&mut *tx_buffer, &self.caps.checksum);
1286
1287 let payload = &mut tx_buffer[repr.header_len()..];
1288 packet.emit_payload(repr, payload, &caps)
1289 };
1290
1291 let total_ip_len = ip_repr.buffer_len();
1292
1293 match &mut ip_repr {
1294 #[cfg(feature = "proto-ipv4")]
1295 IpRepr::Ipv4(repr) => {
1296 let should_fragment = total_ip_len > self.caps.ip_mtu();
1298
1299 #[cfg(feature = "segmentation-offload")]
1303 let should_fragment = should_fragment && meta.segmentation_offload_size.is_none();
1304
1305 if should_fragment {
1306 #[cfg(feature = "proto-ipv4-fragmentation")]
1307 {
1308 net_debug!("start fragmentation");
1309
1310 let ip_header_len = repr.buffer_len();
1313 let first_frag_data_len =
1314 self.caps.max_ipv4_fragment_size(repr.buffer_len());
1315 let first_frag_ip_len = first_frag_data_len + ip_header_len;
1316 let mut tx_len = first_frag_ip_len;
1317 #[cfg(feature = "medium-ethernet")]
1318 if matches!(caps.medium, Medium::Ethernet) {
1319 tx_len += EthernetFrame::<&[u8]>::header_len();
1320 }
1321
1322 if frag.buffer.len() < total_ip_len {
1323 net_debug!(
1324 "Fragmentation buffer is too small, at least {} needed. Dropping",
1325 total_ip_len
1326 );
1327 return Ok(());
1328 }
1329
1330 #[cfg(feature = "medium-ethernet")]
1331 {
1332 frag.ipv4.dst_hardware_addr = dst_hardware_addr;
1333 }
1334
1335 frag.packet_len = total_ip_len;
1338
1339 frag.ipv4.repr = *repr;
1341
1342 repr.payload_len = first_frag_data_len;
1344
1345 frag.sent_bytes = first_frag_ip_len;
1347
1348 emit_ip(&ip_repr, &mut frag.buffer);
1350
1351 let mut ipv4_packet = Ipv4Packet::new_unchecked(&mut frag.buffer[..]);
1352 frag.ipv4.ident = ipv4_id;
1353 ipv4_packet.set_ident(ipv4_id);
1354 ipv4_packet.set_more_frags(true);
1355 ipv4_packet.set_dont_frag(false);
1356 ipv4_packet.set_frag_offset(0);
1357
1358 if caps.checksum.ipv4.tx() {
1359 ipv4_packet.fill_checksum();
1360 }
1361
1362 tx_token.consume(tx_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 frag.ipv4.frag_offset = (first_frag_ip_len - ip_header_len) as u16;
1372
1373 tx_buffer[..first_frag_ip_len]
1375 .copy_from_slice(&frag.buffer[..first_frag_ip_len]);
1376 });
1377
1378 Ok(())
1379 }
1380
1381 #[cfg(not(feature = "proto-ipv4-fragmentation"))]
1382 {
1383 net_debug!(
1384 "Enable the `proto-ipv4-fragmentation` feature for fragmentation support."
1385 );
1386 Ok(())
1387 }
1388 } else {
1389 tx_token.set_meta(meta);
1390
1391 tx_token.consume(total_len, |mut tx_buffer| {
1393 #[cfg(feature = "medium-ethernet")]
1394 if matches!(self.caps.medium, Medium::Ethernet) {
1395 emit_ethernet(&ip_repr, tx_buffer);
1396 tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..];
1397 }
1398
1399 emit_ip(&ip_repr, tx_buffer);
1400 });
1401
1402 Ok(())
1403 }
1404 }
1405 #[cfg(feature = "proto-ipv6")]
1407 IpRepr::Ipv6(_) => {
1408 if total_ip_len > self.caps.ip_mtu() {
1410 net_debug!("IPv6 fragmentation support is unimplemented. Dropping.");
1411 Ok(())
1412 } else {
1413 tx_token.consume(total_len, |mut tx_buffer| {
1414 #[cfg(feature = "medium-ethernet")]
1415 if matches!(self.caps.medium, Medium::Ethernet) {
1416 emit_ethernet(&ip_repr, tx_buffer);
1417 tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..];
1418 }
1419
1420 emit_ip(&ip_repr, tx_buffer);
1421 });
1422 Ok(())
1423 }
1424 }
1425 }
1426 }
1427}
1428
1429#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1430#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1431enum DispatchError {
1432 NoRoute,
1435 NeighborPending,
1439}