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 = "proto-igmp")]
21mod igmp;
22
23#[cfg(feature = "proto-igmp")]
24pub use igmp::MulticastError;
25
26use super::packet::*;
27
28use core::result::Result;
29use heapless::{LinearMap, Vec};
30
31#[cfg(feature = "_proto-fragmentation")]
32use super::fragmentation::FragKey;
33#[cfg(any(feature = "proto-ipv4", feature = "proto-sixlowpan"))]
34use super::fragmentation::PacketAssemblerSet;
35use super::fragmentation::{Fragmenter, FragmentsBuffer};
36
37#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
38use super::neighbor::{Answer as NeighborAnswer, Cache as NeighborCache};
39use super::socket_set::SocketSet;
40use crate::config::{
41 IFACE_MAX_ADDR_COUNT, IFACE_MAX_MULTICAST_GROUP_COUNT,
42 IFACE_MAX_SIXLOWPAN_ADDRESS_CONTEXT_COUNT,
43};
44use crate::iface::Routes;
45use crate::phy::PacketMeta;
46use crate::phy::{ChecksumCapabilities, Device, DeviceCapabilities, Medium, RxToken, TxToken};
47use crate::rand::Rand;
48#[cfg(feature = "socket-dns")]
49use crate::socket::dns;
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
72pub struct Interface {
78 pub(crate) inner: InterfaceInner,
79 fragments: FragmentsBuffer,
80 fragmenter: Fragmenter,
81}
82
83pub struct InterfaceInner {
91 caps: DeviceCapabilities,
92 now: Instant,
93 rand: Rand,
94
95 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
96 neighbor_cache: NeighborCache,
97 hardware_addr: HardwareAddress,
98 #[cfg(feature = "medium-ieee802154")]
99 sequence_no: u8,
100 #[cfg(feature = "medium-ieee802154")]
101 pan_id: Option<Ieee802154Pan>,
102 #[cfg(feature = "proto-ipv4-fragmentation")]
103 ipv4_id: u16,
104 #[cfg(feature = "proto-sixlowpan")]
105 sixlowpan_address_context:
106 Vec<SixlowpanAddressContext, IFACE_MAX_SIXLOWPAN_ADDRESS_CONTEXT_COUNT>,
107 #[cfg(feature = "proto-sixlowpan-fragmentation")]
108 tag: u16,
109 ip_addrs: Vec<IpCidr, IFACE_MAX_ADDR_COUNT>,
110 #[cfg(feature = "proto-ipv4")]
111 any_ip: bool,
112 routes: Routes,
113 #[cfg(feature = "proto-igmp")]
114 ipv4_multicast_groups: LinearMap<Ipv4Address, (), IFACE_MAX_MULTICAST_GROUP_COUNT>,
115 #[cfg(feature = "proto-igmp")]
117 igmp_report_state: IgmpReportState,
118}
119
120#[non_exhaustive]
122pub struct Config {
123 pub random_seed: u64,
130
131 pub hardware_addr: HardwareAddress,
136
137 #[cfg(feature = "medium-ieee802154")]
141 pub pan_id: Option<Ieee802154Pan>,
142}
143
144impl Config {
145 pub fn new(hardware_addr: HardwareAddress) -> Self {
146 Config {
147 random_seed: 0,
148 hardware_addr,
149 #[cfg(feature = "medium-ieee802154")]
150 pan_id: None,
151 }
152 }
153}
154
155impl Interface {
156 pub fn new<D>(config: Config, device: &mut D, now: Instant) -> Self
162 where
163 D: Device + ?Sized,
164 {
165 let caps = device.capabilities();
166 assert_eq!(
167 config.hardware_addr.medium(),
168 caps.medium,
169 "The hardware address does not match the medium of the interface."
170 );
171
172 let mut rand = Rand::new(config.random_seed);
173
174 #[cfg(feature = "medium-ieee802154")]
175 let mut sequence_no;
176 #[cfg(feature = "medium-ieee802154")]
177 loop {
178 sequence_no = (rand.rand_u32() & 0xff) as u8;
179 if sequence_no != 0 {
180 break;
181 }
182 }
183
184 #[cfg(feature = "proto-sixlowpan")]
185 let mut tag;
186
187 #[cfg(feature = "proto-sixlowpan")]
188 loop {
189 tag = rand.rand_u16();
190 if tag != 0 {
191 break;
192 }
193 }
194
195 #[cfg(feature = "proto-ipv4")]
196 let mut ipv4_id;
197
198 #[cfg(feature = "proto-ipv4")]
199 loop {
200 ipv4_id = rand.rand_u16();
201 if ipv4_id != 0 {
202 break;
203 }
204 }
205
206 Interface {
207 fragments: FragmentsBuffer {
208 #[cfg(feature = "proto-sixlowpan")]
209 decompress_buf: [0u8; sixlowpan::MAX_DECOMPRESSED_LEN],
210
211 #[cfg(feature = "_proto-fragmentation")]
212 assembler: PacketAssemblerSet::new(),
213 #[cfg(feature = "_proto-fragmentation")]
214 reassembly_timeout: Duration::from_secs(60),
215 },
216 fragmenter: Fragmenter::new(),
217 inner: InterfaceInner {
218 now,
219 caps,
220 hardware_addr: config.hardware_addr,
221 ip_addrs: Vec::new(),
222 #[cfg(feature = "proto-ipv4")]
223 any_ip: false,
224 routes: Routes::new(),
225 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
226 neighbor_cache: NeighborCache::new(),
227 #[cfg(feature = "proto-igmp")]
228 ipv4_multicast_groups: LinearMap::new(),
229 #[cfg(feature = "proto-igmp")]
230 igmp_report_state: IgmpReportState::Inactive,
231 #[cfg(feature = "medium-ieee802154")]
232 sequence_no,
233 #[cfg(feature = "medium-ieee802154")]
234 pan_id: config.pan_id,
235 #[cfg(feature = "proto-sixlowpan-fragmentation")]
236 tag,
237 #[cfg(feature = "proto-ipv4-fragmentation")]
238 ipv4_id,
239 #[cfg(feature = "proto-sixlowpan")]
240 sixlowpan_address_context: Vec::new(),
241 rand,
242 },
243 }
244 }
245
246 pub fn context(&mut self) -> &mut InterfaceInner {
250 &mut self.inner
251 }
252
253 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
258 pub fn hardware_addr(&self) -> HardwareAddress {
259 #[cfg(all(feature = "medium-ethernet", not(feature = "medium-ieee802154")))]
260 assert!(self.inner.caps.medium == Medium::Ethernet);
261 #[cfg(all(feature = "medium-ieee802154", not(feature = "medium-ethernet")))]
262 assert!(self.inner.caps.medium == Medium::Ieee802154);
263
264 #[cfg(all(feature = "medium-ieee802154", feature = "medium-ethernet"))]
265 assert!(
266 self.inner.caps.medium == Medium::Ethernet
267 || self.inner.caps.medium == Medium::Ieee802154
268 );
269
270 self.inner.hardware_addr
271 }
272
273 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
279 pub fn set_hardware_addr(&mut self, addr: HardwareAddress) {
280 #[cfg(all(feature = "medium-ethernet", not(feature = "medium-ieee802154")))]
281 assert!(self.inner.caps.medium == Medium::Ethernet);
282 #[cfg(all(feature = "medium-ieee802154", not(feature = "medium-ethernet")))]
283 assert!(self.inner.caps.medium == Medium::Ieee802154);
284
285 #[cfg(all(feature = "medium-ieee802154", feature = "medium-ethernet"))]
286 assert!(
287 self.inner.caps.medium == Medium::Ethernet
288 || self.inner.caps.medium == Medium::Ieee802154
289 );
290
291 InterfaceInner::check_hardware_addr(&addr);
292 self.inner.hardware_addr = addr;
293 }
294
295 pub fn ip_addrs(&self) -> &[IpCidr] {
297 self.inner.ip_addrs.as_ref()
298 }
299
300 #[cfg(feature = "proto-ipv4")]
302 pub fn ipv4_addr(&self) -> Option<Ipv4Address> {
303 self.inner.ipv4_addr()
304 }
305
306 #[cfg(feature = "proto-ipv6")]
308 pub fn ipv6_addr(&self) -> Option<Ipv6Address> {
309 self.inner.ipv6_addr()
310 }
311
312 pub fn get_source_address(&self, dst_addr: &IpAddress) -> Option<IpAddress> {
316 self.inner.get_source_address(dst_addr)
317 }
318
319 #[cfg(feature = "proto-ipv4")]
322 pub fn get_source_address_ipv4(&self, dst_addr: &Ipv4Address) -> Option<Ipv4Address> {
323 self.inner.get_source_address_ipv4(dst_addr)
324 }
325
326 #[cfg(feature = "proto-ipv6")]
329 pub fn get_source_address_ipv6(&self, dst_addr: &Ipv6Address) -> Option<Ipv6Address> {
330 self.inner.get_source_address_ipv6(dst_addr)
331 }
332
333 pub fn update_ip_addrs<F: FnOnce(&mut Vec<IpCidr, IFACE_MAX_ADDR_COUNT>)>(&mut self, f: F) {
338 f(&mut self.inner.ip_addrs);
339 InterfaceInner::flush_cache(&mut self.inner);
340 InterfaceInner::check_ip_addrs(&self.inner.ip_addrs)
341 }
342
343 pub fn has_ip_addr<T: Into<IpAddress>>(&self, addr: T) -> bool {
345 self.inner.has_ip_addr(addr)
346 }
347
348 pub fn routes(&self) -> &Routes {
349 &self.inner.routes
350 }
351
352 pub fn routes_mut(&mut self) -> &mut Routes {
353 &mut self.inner.routes
354 }
355
356 #[cfg(feature = "proto-ipv4")]
369 pub fn set_any_ip(&mut self, any_ip: bool) {
370 self.inner.any_ip = any_ip;
371 }
372
373 #[cfg(feature = "proto-ipv4")]
377 pub fn any_ip(&self) -> bool {
378 self.inner.any_ip
379 }
380
381 #[cfg(feature = "proto-sixlowpan")]
383 pub fn sixlowpan_address_context(
384 &self,
385 ) -> &Vec<SixlowpanAddressContext, IFACE_MAX_SIXLOWPAN_ADDRESS_CONTEXT_COUNT> {
386 &self.inner.sixlowpan_address_context
387 }
388
389 #[cfg(feature = "proto-sixlowpan")]
391 pub fn sixlowpan_address_context_mut(
392 &mut self,
393 ) -> &mut Vec<SixlowpanAddressContext, IFACE_MAX_SIXLOWPAN_ADDRESS_CONTEXT_COUNT> {
394 &mut self.inner.sixlowpan_address_context
395 }
396
397 #[cfg(feature = "_proto-fragmentation")]
399 pub fn reassembly_timeout(&self) -> Duration {
400 self.fragments.reassembly_timeout
401 }
402
403 #[cfg(feature = "_proto-fragmentation")]
405 pub fn set_reassembly_timeout(&mut self, timeout: Duration) {
406 if timeout > Duration::from_secs(60) {
407 net_debug!("RFC 4944 specifies that the reassembly timeout MUST be set to a maximum of 60 seconds");
408 }
409 self.fragments.reassembly_timeout = timeout;
410 }
411
412 pub fn poll<D>(
419 &mut self,
420 timestamp: Instant,
421 device: &mut D,
422 sockets: &mut SocketSet<'_>,
423 ) -> bool
424 where
425 D: Device + ?Sized,
426 {
427 self.inner.now = timestamp;
428
429 #[cfg(feature = "_proto-fragmentation")]
430 self.fragments.assembler.remove_expired(timestamp);
431
432 match self.inner.caps.medium {
433 #[cfg(feature = "medium-ieee802154")]
434 Medium::Ieee802154 =>
435 {
436 #[cfg(feature = "proto-sixlowpan-fragmentation")]
437 if self.sixlowpan_egress(device) {
438 return true;
439 }
440 }
441 #[cfg(any(feature = "medium-ethernet", feature = "medium-ip"))]
442 _ =>
443 {
444 #[cfg(feature = "proto-ipv4-fragmentation")]
445 if self.ipv4_egress(device) {
446 return true;
447 }
448 }
449 }
450
451 let mut readiness_may_have_changed = false;
452
453 loop {
454 let mut did_something = false;
455 did_something |= self.socket_ingress(device, sockets);
456 did_something |= self.socket_egress(device, sockets);
457
458 #[cfg(feature = "proto-igmp")]
459 {
460 did_something |= self.igmp_egress(device);
461 }
462
463 if did_something {
464 readiness_may_have_changed = true;
465 } else {
466 break;
467 }
468 }
469
470 readiness_may_have_changed
471 }
472
473 pub fn poll_at(&mut self, timestamp: Instant, sockets: &SocketSet<'_>) -> Option<Instant> {
482 self.inner.now = timestamp;
483
484 #[cfg(feature = "_proto-fragmentation")]
485 if !self.fragmenter.is_empty() {
486 return Some(Instant::from_millis(0));
487 }
488
489 let inner = &mut self.inner;
490
491 sockets
492 .items()
493 .filter_map(move |item| {
494 let socket_poll_at = item.socket.poll_at(inner);
495 match item
496 .meta
497 .poll_at(socket_poll_at, |ip_addr| inner.has_neighbor(&ip_addr))
498 {
499 PollAt::Ingress => None,
500 PollAt::Time(instant) => Some(instant),
501 PollAt::Now => Some(Instant::from_millis(0)),
502 }
503 })
504 .min()
505 }
506
507 pub fn poll_delay(&mut self, timestamp: Instant, sockets: &SocketSet<'_>) -> Option<Duration> {
516 match self.poll_at(timestamp, sockets) {
517 Some(poll_at) if timestamp < poll_at => Some(poll_at - timestamp),
518 Some(_) => Some(Duration::from_millis(0)),
519 _ => None,
520 }
521 }
522
523 fn socket_ingress<D>(&mut self, device: &mut D, sockets: &mut SocketSet<'_>) -> bool
524 where
525 D: Device + ?Sized,
526 {
527 let mut processed_any = false;
528
529 while let Some((rx_token, tx_token)) = device.receive(self.inner.now) {
530 let rx_meta = rx_token.meta();
531 rx_token.consume(|frame| {
532 if frame.is_empty() {
533 return;
534 }
535
536 match self.inner.caps.medium {
537 #[cfg(feature = "medium-ethernet")]
538 Medium::Ethernet => {
539 if let Some(packet) = self.inner.process_ethernet(
540 sockets,
541 rx_meta,
542 frame,
543 &mut self.fragments,
544 ) {
545 if let Err(err) =
546 self.inner.dispatch(tx_token, packet, &mut self.fragmenter)
547 {
548 net_debug!("Failed to send response: {:?}", err);
549 }
550 }
551 }
552 #[cfg(feature = "medium-ip")]
553 Medium::Ip => {
554 if let Some(packet) =
555 self.inner
556 .process_ip(sockets, rx_meta, frame, &mut self.fragments)
557 {
558 if let Err(err) = self.inner.dispatch_ip(
559 tx_token,
560 PacketMeta::default(),
561 packet,
562 &mut self.fragmenter,
563 ) {
564 net_debug!("Failed to send response: {:?}", err);
565 }
566 }
567 }
568 #[cfg(feature = "medium-ieee802154")]
569 Medium::Ieee802154 => {
570 if let Some(packet) = self.inner.process_ieee802154(
571 sockets,
572 rx_meta,
573 frame,
574 &mut self.fragments,
575 ) {
576 if let Err(err) = self.inner.dispatch_ip(
577 tx_token,
578 PacketMeta::default(),
579 packet,
580 &mut self.fragmenter,
581 ) {
582 net_debug!("Failed to send response: {:?}", err);
583 }
584 }
585 }
586 }
587 processed_any = true;
588 });
589 }
590
591 processed_any
592 }
593
594 fn socket_egress<D>(&mut self, device: &mut D, sockets: &mut SocketSet<'_>) -> bool
595 where
596 D: Device + ?Sized,
597 {
598 let _caps = device.capabilities();
599
600 enum EgressError {
601 Exhausted,
602 Dispatch(DispatchError),
603 }
604
605 let mut emitted_any = false;
606 for item in sockets.items_mut() {
607 if !item
608 .meta
609 .egress_permitted(self.inner.now, |ip_addr| self.inner.has_neighbor(&ip_addr))
610 {
611 continue;
612 }
613
614 let mut neighbor_addr = None;
615 let mut respond = |inner: &mut InterfaceInner, meta: PacketMeta, response: Packet| {
616 neighbor_addr = Some(response.ip_repr().dst_addr());
617 let t = device.transmit(inner.now).ok_or_else(|| {
618 net_debug!("failed to transmit IP: device exhausted");
619 EgressError::Exhausted
620 })?;
621
622 inner
623 .dispatch_ip(t, meta, response, &mut self.fragmenter)
624 .map_err(EgressError::Dispatch)?;
625
626 emitted_any = true;
627
628 Ok(())
629 };
630
631 let result = match &mut item.socket {
632 #[cfg(feature = "socket-raw")]
633 Socket::Raw(socket) => socket.dispatch(&mut self.inner, |inner, (ip, raw)| {
634 respond(
635 inner,
636 PacketMeta::default(),
637 Packet::new(ip, IpPayload::Raw(raw)),
638 )
639 }),
640 #[cfg(feature = "socket-icmp")]
641 Socket::Icmp(socket) => {
642 socket.dispatch(&mut self.inner, |inner, response| match response {
643 #[cfg(feature = "proto-ipv4")]
644 (IpRepr::Ipv4(ipv4_repr), IcmpRepr::Ipv4(icmpv4_repr)) => respond(
645 inner,
646 PacketMeta::default(),
647 Packet::new_ipv4(ipv4_repr, IpPayload::Icmpv4(icmpv4_repr)),
648 ),
649 #[cfg(feature = "proto-ipv6")]
650 (IpRepr::Ipv6(ipv6_repr), IcmpRepr::Ipv6(icmpv6_repr)) => respond(
651 inner,
652 PacketMeta::default(),
653 Packet::new_ipv6(ipv6_repr, IpPayload::Icmpv6(icmpv6_repr)),
654 ),
655 #[allow(unreachable_patterns)]
656 _ => unreachable!(),
657 })
658 }
659 #[cfg(feature = "socket-udp")]
660 Socket::Udp(socket) => {
661 socket.dispatch(&mut self.inner, |inner, meta, (ip, udp, payload)| {
662 respond(inner, meta, Packet::new(ip, IpPayload::Udp(udp, payload)))
663 })
664 }
665 #[cfg(feature = "socket-tcp")]
666 Socket::Tcp(socket) => socket.dispatch(&mut self.inner, |inner, (ip, tcp)| {
667 respond(
668 inner,
669 PacketMeta::default(),
670 Packet::new(ip, IpPayload::Tcp(tcp)),
671 )
672 }),
673 #[cfg(feature = "socket-dhcpv4")]
674 Socket::Dhcpv4(socket) => {
675 socket.dispatch(&mut self.inner, |inner, (ip, udp, dhcp)| {
676 respond(
677 inner,
678 PacketMeta::default(),
679 Packet::new_ipv4(ip, IpPayload::Dhcpv4(udp, dhcp)),
680 )
681 })
682 }
683 #[cfg(feature = "socket-dns")]
684 Socket::Dns(socket) => socket.dispatch(&mut self.inner, |inner, (ip, udp, dns)| {
685 respond(
686 inner,
687 PacketMeta::default(),
688 Packet::new(ip, IpPayload::Udp(udp, dns)),
689 )
690 }),
691 };
692
693 match result {
694 Err(EgressError::Exhausted) => break, Err(EgressError::Dispatch(_)) => {
696 item.meta.neighbor_missing(
701 self.inner.now,
702 neighbor_addr.expect("non-IP response packet"),
703 );
704 }
705 Ok(()) => {}
706 }
707 }
708 emitted_any
709 }
710
711 #[cfg(feature = "proto-ipv4-fragmentation")]
717 fn ipv4_egress<D>(&mut self, device: &mut D) -> bool
718 where
719 D: Device + ?Sized,
720 {
721 if self.fragmenter.finished() {
723 self.fragmenter.reset();
724 }
725
726 if self.fragmenter.is_empty() {
727 return false;
728 }
729
730 let pkt = &self.fragmenter;
731 if pkt.packet_len > pkt.sent_bytes {
732 if let Some(tx_token) = device.transmit(self.inner.now) {
733 self.inner
734 .dispatch_ipv4_frag(tx_token, &mut self.fragmenter);
735 return true;
736 }
737 }
738 false
739 }
740
741 #[cfg(feature = "proto-sixlowpan-fragmentation")]
747 fn sixlowpan_egress<D>(&mut self, device: &mut D) -> bool
748 where
749 D: Device + ?Sized,
750 {
751 if self.fragmenter.finished() {
753 self.fragmenter.reset();
754 }
755
756 if self.fragmenter.is_empty() {
757 return false;
758 }
759
760 let pkt = &self.fragmenter;
761 if pkt.packet_len > pkt.sent_bytes {
762 if let Some(tx_token) = device.transmit(self.inner.now) {
763 self.inner
764 .dispatch_ieee802154_frag(tx_token, &mut self.fragmenter);
765 return true;
766 }
767 }
768 false
769 }
770}
771
772impl InterfaceInner {
773 #[allow(unused)] pub(crate) fn now(&self) -> Instant {
775 self.now
776 }
777
778 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
779 #[allow(unused)] pub(crate) fn hardware_addr(&self) -> HardwareAddress {
781 self.hardware_addr
782 }
783
784 #[allow(unused)] pub(crate) fn checksum_caps(&self) -> ChecksumCapabilities {
786 self.caps.checksum.clone()
787 }
788
789 #[allow(unused)] pub(crate) fn ip_mtu(&self) -> usize {
791 self.caps.ip_mtu()
792 }
793
794 #[allow(unused)] pub(crate) fn rand(&mut self) -> &mut Rand {
796 &mut self.rand
797 }
798
799 #[allow(unused)] pub(crate) fn get_source_address(&self, dst_addr: &IpAddress) -> Option<IpAddress> {
801 match dst_addr {
802 #[cfg(feature = "proto-ipv4")]
803 IpAddress::Ipv4(addr) => self.get_source_address_ipv4(addr).map(|a| a.into()),
804 #[cfg(feature = "proto-ipv6")]
805 IpAddress::Ipv6(addr) => self.get_source_address_ipv6(addr).map(|a| a.into()),
806 }
807 }
808
809 #[cfg(feature = "proto-ipv4")]
810 #[allow(unused)]
811 pub(crate) fn get_source_address_ipv4(&self, _dst_addr: &Ipv4Address) -> Option<Ipv4Address> {
812 for cidr in self.ip_addrs.iter() {
813 #[allow(irrefutable_let_patterns)] if let IpCidr::Ipv4(cidr) = cidr {
815 return Some(cidr.address());
816 }
817 }
818 None
819 }
820
821 #[cfg(feature = "proto-ipv6")]
822 #[allow(unused)]
823 pub(crate) fn get_source_address_ipv6(&self, dst_addr: &Ipv6Address) -> Option<Ipv6Address> {
824 fn is_candidate_source_address(dst_addr: &Ipv6Address, src_addr: &Ipv6Address) -> bool {
829 if dst_addr.is_link_local() && !src_addr.is_link_local() {
832 return false;
833 }
834
835 if dst_addr.is_multicast()
836 && matches!(dst_addr.scope(), Ipv6AddressScope::LinkLocal)
837 && src_addr.is_multicast()
838 && !matches!(src_addr.scope(), Ipv6AddressScope::LinkLocal)
839 {
840 return false;
841 }
842
843 if src_addr.is_loopback() || src_addr.is_multicast() {
847 return false;
848 }
849
850 true
851 }
852
853 fn common_prefix_length(dst_addr: &Ipv6Cidr, src_addr: &Ipv6Address) -> usize {
855 let addr = dst_addr.address();
856 let mut bits = 0;
857 for (l, r) in addr.as_bytes().iter().zip(src_addr.as_bytes().iter()) {
858 if l == r {
859 bits += 8;
860 } else {
861 bits += (l ^ r).leading_zeros();
862 break;
863 }
864 }
865
866 bits = bits.min(dst_addr.prefix_len() as u32);
867
868 bits as usize
869 }
870
871 let mut candidate = self
873 .ip_addrs
874 .iter()
875 .filter_map(|a| match a {
876 #[cfg(feature = "proto-ipv4")]
877 IpCidr::Ipv4(_) => None,
878 #[cfg(feature = "proto-ipv6")]
879 IpCidr::Ipv6(a) => Some(a),
880 })
881 .find(|a| is_candidate_source_address(dst_addr, &a.address()))
882 .unwrap();
883
884 for addr in self.ip_addrs.iter().filter_map(|a| match a {
885 #[cfg(feature = "proto-ipv4")]
886 IpCidr::Ipv4(_) => None,
887 #[cfg(feature = "proto-ipv6")]
888 IpCidr::Ipv6(a) => Some(a),
889 }) {
890 if !is_candidate_source_address(dst_addr, &addr.address()) {
891 continue;
892 }
893
894 if candidate.address() != *dst_addr && addr.address() == *dst_addr {
896 candidate = addr;
897 }
898
899 if (candidate.address().scope() as u8) < (addr.address().scope() as u8) {
901 if (candidate.address().scope() as u8) < (dst_addr.scope() as u8) {
902 candidate = addr;
903 }
904 } else if (addr.address().scope() as u8) > (dst_addr.scope() as u8) {
905 candidate = addr;
906 }
907
908 if common_prefix_length(candidate, dst_addr) < common_prefix_length(addr, dst_addr) {
916 candidate = addr;
917 }
918 }
919
920 Some(candidate.address())
921 }
922
923 #[cfg(test)]
924 #[allow(unused)] pub(crate) fn set_now(&mut self, now: Instant) {
926 self.now = now
927 }
928
929 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
930 fn check_hardware_addr(addr: &HardwareAddress) {
931 if !addr.is_unicast() {
932 panic!("Hardware address {addr} is not unicast")
933 }
934 }
935
936 fn check_ip_addrs(addrs: &[IpCidr]) {
937 for cidr in addrs {
938 if !cidr.address().is_unicast() && !cidr.address().is_unspecified() {
939 panic!("IP address {} is not unicast", cidr.address())
940 }
941 }
942 }
943
944 #[cfg(feature = "medium-ieee802154")]
945 fn get_sequence_number(&mut self) -> u8 {
946 let no = self.sequence_no;
947 self.sequence_no = self.sequence_no.wrapping_add(1);
948 no
949 }
950
951 #[cfg(feature = "proto-ipv4-fragmentation")]
952 fn get_ipv4_ident(&mut self) -> u16 {
953 let ipv4_id = self.ipv4_id;
954 self.ipv4_id = self.ipv4_id.wrapping_add(1);
955 ipv4_id
956 }
957
958 #[cfg(feature = "proto-sixlowpan-fragmentation")]
959 fn get_sixlowpan_fragment_tag(&mut self) -> u16 {
960 let tag = self.tag;
961 self.tag = self.tag.wrapping_add(1);
962 tag
963 }
964
965 #[cfg(feature = "proto-ipv6")]
971 pub fn has_solicited_node(&self, addr: Ipv6Address) -> bool {
972 self.ip_addrs.iter().any(|cidr| {
973 match *cidr {
974 IpCidr::Ipv6(cidr) if cidr.address() != Ipv6Address::LOOPBACK => {
975 addr.as_bytes()[14..] == cidr.address().as_bytes()[14..]
978 }
979 _ => false,
980 }
981 })
982 }
983
984 fn has_ip_addr<T: Into<IpAddress>>(&self, addr: T) -> bool {
986 let addr = addr.into();
987 self.ip_addrs.iter().any(|probe| probe.address() == addr)
988 }
989
990 #[cfg(feature = "proto-ipv4")]
992 pub fn ipv4_addr(&self) -> Option<Ipv4Address> {
993 self.ip_addrs.iter().find_map(|addr| match *addr {
994 IpCidr::Ipv4(cidr) => Some(cidr.address()),
995 #[allow(unreachable_patterns)]
996 _ => None,
997 })
998 }
999
1000 #[cfg(feature = "proto-ipv6")]
1002 pub fn ipv6_addr(&self) -> Option<Ipv6Address> {
1003 self.ip_addrs.iter().find_map(|addr| match *addr {
1004 IpCidr::Ipv6(cidr) => Some(cidr.address()),
1005 #[allow(unreachable_patterns)]
1006 _ => None,
1007 })
1008 }
1009
1010 fn has_multicast_group<T: Into<IpAddress>>(&self, addr: T) -> bool {
1015 match addr.into() {
1016 #[cfg(feature = "proto-igmp")]
1017 IpAddress::Ipv4(key) => {
1018 key == Ipv4Address::MULTICAST_ALL_SYSTEMS
1019 || self.ipv4_multicast_groups.get(&key).is_some()
1020 }
1021 #[cfg(feature = "proto-ipv6")]
1022 IpAddress::Ipv6(Ipv6Address::LINK_LOCAL_ALL_NODES) => true,
1023 #[cfg(feature = "proto-rpl")]
1024 IpAddress::Ipv6(Ipv6Address::LINK_LOCAL_ALL_RPL_NODES) => true,
1025 #[cfg(feature = "proto-ipv6")]
1026 IpAddress::Ipv6(addr) => self.has_solicited_node(addr),
1027 #[allow(unreachable_patterns)]
1028 _ => false,
1029 }
1030 }
1031
1032 #[cfg(feature = "medium-ip")]
1033 fn process_ip<'frame>(
1034 &mut self,
1035 sockets: &mut SocketSet,
1036 meta: PacketMeta,
1037 ip_payload: &'frame [u8],
1038 frag: &'frame mut FragmentsBuffer,
1039 ) -> Option<Packet<'frame>> {
1040 match IpVersion::of_packet(ip_payload) {
1041 #[cfg(feature = "proto-ipv4")]
1042 Ok(IpVersion::Ipv4) => {
1043 let ipv4_packet = check!(Ipv4Packet::new_checked(ip_payload));
1044
1045 self.process_ipv4(sockets, meta, &ipv4_packet, frag)
1046 }
1047 #[cfg(feature = "proto-ipv6")]
1048 Ok(IpVersion::Ipv6) => {
1049 let ipv6_packet = check!(Ipv6Packet::new_checked(ip_payload));
1050 self.process_ipv6(sockets, meta, &ipv6_packet)
1051 }
1052 _ => None,
1054 }
1055 }
1056
1057 #[cfg(feature = "socket-raw")]
1058 fn raw_socket_filter(
1059 &mut self,
1060 sockets: &mut SocketSet,
1061 ip_repr: &IpRepr,
1062 ip_payload: &[u8],
1063 ) -> bool {
1064 let mut handled_by_raw_socket = false;
1065
1066 for raw_socket in sockets
1068 .items_mut()
1069 .filter_map(|i| raw::Socket::downcast_mut(&mut i.socket))
1070 {
1071 if raw_socket.accepts(ip_repr) {
1072 raw_socket.process(self, ip_repr, ip_payload);
1073 handled_by_raw_socket = true;
1074 }
1075 }
1076 handled_by_raw_socket
1077 }
1078
1079 pub(crate) fn is_broadcast(&self, address: &IpAddress) -> bool {
1082 match address {
1083 #[cfg(feature = "proto-ipv4")]
1084 IpAddress::Ipv4(address) => self.is_broadcast_v4(*address),
1085 #[cfg(feature = "proto-ipv6")]
1086 IpAddress::Ipv6(_) => false,
1087 }
1088 }
1089
1090 #[cfg(feature = "proto-ipv4")]
1093 pub(crate) fn is_broadcast_v4(&self, address: Ipv4Address) -> bool {
1094 if address.is_broadcast() {
1095 return true;
1096 }
1097
1098 self.ip_addrs
1099 .iter()
1100 .filter_map(|own_cidr| match own_cidr {
1101 IpCidr::Ipv4(own_ip) => Some(own_ip.broadcast()?),
1102 #[cfg(feature = "proto-ipv6")]
1103 IpCidr::Ipv6(_) => None,
1104 })
1105 .any(|broadcast_address| address == broadcast_address)
1106 }
1107
1108 #[cfg(feature = "proto-ipv4")]
1110 fn is_unicast_v4(&self, address: Ipv4Address) -> bool {
1111 address.is_unicast() && !self.is_broadcast_v4(address)
1112 }
1113
1114 #[cfg(any(feature = "socket-udp", feature = "socket-dns"))]
1115 #[allow(clippy::too_many_arguments)]
1116 fn process_udp<'frame>(
1117 &mut self,
1118 sockets: &mut SocketSet,
1119 meta: PacketMeta,
1120 ip_repr: IpRepr,
1121 udp_repr: UdpRepr,
1122 handled_by_raw_socket: bool,
1123 udp_payload: &'frame [u8],
1124 ip_payload: &'frame [u8],
1125 ) -> Option<Packet<'frame>> {
1126 #[cfg(feature = "socket-udp")]
1127 for udp_socket in sockets
1128 .items_mut()
1129 .filter_map(|i| udp::Socket::downcast_mut(&mut i.socket))
1130 {
1131 if udp_socket.accepts(self, &ip_repr, &udp_repr) {
1132 udp_socket.process(self, meta, &ip_repr, &udp_repr, udp_payload);
1133 return None;
1134 }
1135 }
1136
1137 #[cfg(feature = "socket-dns")]
1138 for dns_socket in sockets
1139 .items_mut()
1140 .filter_map(|i| dns::Socket::downcast_mut(&mut i.socket))
1141 {
1142 if dns_socket.accepts(&ip_repr, &udp_repr) {
1143 dns_socket.process(self, &ip_repr, &udp_repr, udp_payload);
1144 return None;
1145 }
1146 }
1147
1148 match ip_repr {
1150 #[cfg(feature = "proto-ipv4")]
1151 IpRepr::Ipv4(_) if handled_by_raw_socket => None,
1152 #[cfg(feature = "proto-ipv6")]
1153 IpRepr::Ipv6(_) if handled_by_raw_socket => None,
1154 #[cfg(feature = "proto-ipv4")]
1155 IpRepr::Ipv4(ipv4_repr) => {
1156 let payload_len =
1157 icmp_reply_payload_len(ip_payload.len(), IPV4_MIN_MTU, ipv4_repr.buffer_len());
1158 let icmpv4_reply_repr = Icmpv4Repr::DstUnreachable {
1159 reason: Icmpv4DstUnreachable::PortUnreachable,
1160 header: ipv4_repr,
1161 data: &ip_payload[0..payload_len],
1162 };
1163 self.icmpv4_reply(ipv4_repr, icmpv4_reply_repr)
1164 }
1165 #[cfg(feature = "proto-ipv6")]
1166 IpRepr::Ipv6(ipv6_repr) => {
1167 let payload_len =
1168 icmp_reply_payload_len(ip_payload.len(), IPV6_MIN_MTU, ipv6_repr.buffer_len());
1169 let icmpv6_reply_repr = Icmpv6Repr::DstUnreachable {
1170 reason: Icmpv6DstUnreachable::PortUnreachable,
1171 header: ipv6_repr,
1172 data: &ip_payload[0..payload_len],
1173 };
1174 self.icmpv6_reply(ipv6_repr, icmpv6_reply_repr)
1175 }
1176 }
1177 }
1178
1179 #[cfg(feature = "socket-tcp")]
1180 pub(crate) fn process_tcp<'frame>(
1181 &mut self,
1182 sockets: &mut SocketSet,
1183 ip_repr: IpRepr,
1184 ip_payload: &'frame [u8],
1185 ) -> Option<Packet<'frame>> {
1186 let (src_addr, dst_addr) = (ip_repr.src_addr(), ip_repr.dst_addr());
1187 let tcp_packet = check!(TcpPacket::new_checked(ip_payload));
1188 let tcp_repr = check!(TcpRepr::parse(
1189 &tcp_packet,
1190 &src_addr,
1191 &dst_addr,
1192 &self.caps.checksum
1193 ));
1194
1195 for tcp_socket in sockets
1196 .items_mut()
1197 .filter_map(|i| tcp::Socket::downcast_mut(&mut i.socket))
1198 {
1199 if tcp_socket.accepts(self, &ip_repr, &tcp_repr) {
1200 return tcp_socket
1201 .process(self, &ip_repr, &tcp_repr)
1202 .map(|(ip, tcp)| Packet::new(ip, IpPayload::Tcp(tcp)));
1203 }
1204 }
1205
1206 if tcp_repr.control == TcpControl::Rst
1207 || ip_repr.dst_addr().is_unspecified()
1208 || ip_repr.src_addr().is_unspecified()
1209 {
1210 None
1213 } else {
1214 let (ip, tcp) = tcp::Socket::rst_reply(&ip_repr, &tcp_repr);
1216 Some(Packet::new(ip, IpPayload::Tcp(tcp)))
1217 }
1218 }
1219
1220 #[cfg(feature = "medium-ethernet")]
1221 fn dispatch<Tx>(
1222 &mut self,
1223 tx_token: Tx,
1224 packet: EthernetPacket,
1225 frag: &mut Fragmenter,
1226 ) -> Result<(), DispatchError>
1227 where
1228 Tx: TxToken,
1229 {
1230 match packet {
1231 #[cfg(feature = "proto-ipv4")]
1232 EthernetPacket::Arp(arp_repr) => {
1233 let dst_hardware_addr = match arp_repr {
1234 ArpRepr::EthernetIpv4 {
1235 target_hardware_addr,
1236 ..
1237 } => target_hardware_addr,
1238 };
1239
1240 self.dispatch_ethernet(tx_token, arp_repr.buffer_len(), |mut frame| {
1241 frame.set_dst_addr(dst_hardware_addr);
1242 frame.set_ethertype(EthernetProtocol::Arp);
1243
1244 let mut packet = ArpPacket::new_unchecked(frame.payload_mut());
1245 arp_repr.emit(&mut packet);
1246 })
1247 }
1248 EthernetPacket::Ip(packet) => {
1249 self.dispatch_ip(tx_token, PacketMeta::default(), packet, frag)
1250 }
1251 }
1252 }
1253
1254 fn in_same_network(&self, addr: &IpAddress) -> bool {
1255 self.ip_addrs.iter().any(|cidr| cidr.contains_addr(addr))
1256 }
1257
1258 fn route(&self, addr: &IpAddress, timestamp: Instant) -> Option<IpAddress> {
1259 if self.in_same_network(addr) || addr.is_broadcast() {
1263 return Some(*addr);
1264 }
1265
1266 self.routes.lookup(addr, timestamp)
1268 }
1269
1270 fn has_neighbor(&self, addr: &IpAddress) -> bool {
1271 match self.route(addr, self.now) {
1272 Some(_routed_addr) => match self.caps.medium {
1273 #[cfg(feature = "medium-ethernet")]
1274 Medium::Ethernet => self.neighbor_cache.lookup(&_routed_addr, self.now).found(),
1275 #[cfg(feature = "medium-ieee802154")]
1276 Medium::Ieee802154 => self.neighbor_cache.lookup(&_routed_addr, self.now).found(),
1277 #[cfg(feature = "medium-ip")]
1278 Medium::Ip => true,
1279 },
1280 None => false,
1281 }
1282 }
1283
1284 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
1285 fn lookup_hardware_addr<Tx>(
1286 &mut self,
1287 tx_token: Tx,
1288 src_addr: &IpAddress,
1289 dst_addr: &IpAddress,
1290 fragmenter: &mut Fragmenter,
1291 ) -> Result<(HardwareAddress, Tx), DispatchError>
1292 where
1293 Tx: TxToken,
1294 {
1295 if self.is_broadcast(dst_addr) {
1296 let hardware_addr = match self.caps.medium {
1297 #[cfg(feature = "medium-ethernet")]
1298 Medium::Ethernet => HardwareAddress::Ethernet(EthernetAddress::BROADCAST),
1299 #[cfg(feature = "medium-ieee802154")]
1300 Medium::Ieee802154 => HardwareAddress::Ieee802154(Ieee802154Address::BROADCAST),
1301 #[cfg(feature = "medium-ip")]
1302 Medium::Ip => unreachable!(),
1303 };
1304
1305 return Ok((hardware_addr, tx_token));
1306 }
1307
1308 if dst_addr.is_multicast() {
1309 let b = dst_addr.as_bytes();
1310 let hardware_addr = match *dst_addr {
1311 #[cfg(feature = "proto-ipv4")]
1312 IpAddress::Ipv4(_addr) => match self.caps.medium {
1313 #[cfg(feature = "medium-ethernet")]
1314 Medium::Ethernet => HardwareAddress::Ethernet(EthernetAddress::from_bytes(&[
1315 0x01,
1316 0x00,
1317 0x5e,
1318 b[1] & 0x7F,
1319 b[2],
1320 b[3],
1321 ])),
1322 #[cfg(feature = "medium-ieee802154")]
1323 Medium::Ieee802154 => unreachable!(),
1324 #[cfg(feature = "medium-ip")]
1325 Medium::Ip => unreachable!(),
1326 },
1327 #[cfg(feature = "proto-ipv6")]
1328 IpAddress::Ipv6(_addr) => match self.caps.medium {
1329 #[cfg(feature = "medium-ethernet")]
1330 Medium::Ethernet => HardwareAddress::Ethernet(EthernetAddress::from_bytes(&[
1331 0x33, 0x33, b[12], b[13], b[14], b[15],
1332 ])),
1333 #[cfg(feature = "medium-ieee802154")]
1334 Medium::Ieee802154 => {
1335 HardwareAddress::Ieee802154(Ieee802154Address::BROADCAST)
1337 }
1338 #[cfg(feature = "medium-ip")]
1339 Medium::Ip => unreachable!(),
1340 },
1341 };
1342
1343 return Ok((hardware_addr, tx_token));
1344 }
1345
1346 let dst_addr = self
1347 .route(dst_addr, self.now)
1348 .ok_or(DispatchError::NoRoute)?;
1349
1350 match self.neighbor_cache.lookup(&dst_addr, self.now) {
1351 NeighborAnswer::Found(hardware_addr) => return Ok((hardware_addr, tx_token)),
1352 NeighborAnswer::RateLimited => return Err(DispatchError::NeighborPending),
1353 _ => (), }
1355
1356 match (src_addr, dst_addr) {
1357 #[cfg(all(feature = "medium-ethernet", feature = "proto-ipv4"))]
1358 (&IpAddress::Ipv4(src_addr), IpAddress::Ipv4(dst_addr))
1359 if matches!(self.caps.medium, Medium::Ethernet) =>
1360 {
1361 net_debug!(
1362 "address {} not in neighbor cache, sending ARP request",
1363 dst_addr
1364 );
1365 let src_hardware_addr = self.hardware_addr.ethernet_or_panic();
1366
1367 let arp_repr = ArpRepr::EthernetIpv4 {
1368 operation: ArpOperation::Request,
1369 source_hardware_addr: src_hardware_addr,
1370 source_protocol_addr: src_addr,
1371 target_hardware_addr: EthernetAddress::BROADCAST,
1372 target_protocol_addr: dst_addr,
1373 };
1374
1375 if let Err(e) =
1376 self.dispatch_ethernet(tx_token, arp_repr.buffer_len(), |mut frame| {
1377 frame.set_dst_addr(EthernetAddress::BROADCAST);
1378 frame.set_ethertype(EthernetProtocol::Arp);
1379
1380 arp_repr.emit(&mut ArpPacket::new_unchecked(frame.payload_mut()))
1381 })
1382 {
1383 net_debug!("Failed to dispatch ARP request: {:?}", e);
1384 return Err(DispatchError::NeighborPending);
1385 }
1386 }
1387
1388 #[cfg(feature = "proto-ipv6")]
1389 (&IpAddress::Ipv6(src_addr), IpAddress::Ipv6(dst_addr)) => {
1390 net_debug!(
1391 "address {} not in neighbor cache, sending Neighbor Solicitation",
1392 dst_addr
1393 );
1394
1395 let solicit = Icmpv6Repr::Ndisc(NdiscRepr::NeighborSolicit {
1396 target_addr: dst_addr,
1397 lladdr: Some(self.hardware_addr.into()),
1398 });
1399
1400 let packet = Packet::new_ipv6(
1401 Ipv6Repr {
1402 src_addr,
1403 dst_addr: dst_addr.solicited_node(),
1404 next_header: IpProtocol::Icmpv6,
1405 payload_len: solicit.buffer_len(),
1406 hop_limit: 0xff,
1407 },
1408 IpPayload::Icmpv6(solicit),
1409 );
1410
1411 if let Err(e) =
1412 self.dispatch_ip(tx_token, PacketMeta::default(), packet, fragmenter)
1413 {
1414 net_debug!("Failed to dispatch NDISC solicit: {:?}", e);
1415 return Err(DispatchError::NeighborPending);
1416 }
1417 }
1418
1419 #[allow(unreachable_patterns)]
1420 _ => (),
1421 }
1422
1423 self.neighbor_cache.limit_rate(self.now);
1425 Err(DispatchError::NeighborPending)
1426 }
1427
1428 fn flush_cache(&mut self) {
1429 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
1430 self.neighbor_cache.flush()
1431 }
1432
1433 fn dispatch_ip<Tx: TxToken>(
1434 &mut self,
1435 #[allow(unused_mut)] mut tx_token: Tx,
1438 meta: PacketMeta,
1439 packet: Packet,
1440 frag: &mut Fragmenter,
1441 ) -> Result<(), DispatchError> {
1442 let mut ip_repr = packet.ip_repr();
1443 assert!(!ip_repr.dst_addr().is_unspecified());
1444
1445 #[cfg(feature = "medium-ieee802154")]
1448 if matches!(self.caps.medium, Medium::Ieee802154) {
1449 let (addr, tx_token) = self.lookup_hardware_addr(
1450 tx_token,
1451 &ip_repr.src_addr(),
1452 &ip_repr.dst_addr(),
1453 frag,
1454 )?;
1455 let addr = addr.ieee802154_or_panic();
1456
1457 self.dispatch_ieee802154(addr, tx_token, meta, packet, frag);
1458 return Ok(());
1459 }
1460
1461 let caps = self.caps.clone();
1464
1465 #[cfg(feature = "proto-ipv4-fragmentation")]
1466 let ipv4_id = self.get_ipv4_ident();
1467
1468 let mut total_len = ip_repr.buffer_len();
1470
1471 #[cfg(feature = "medium-ethernet")]
1473 if matches!(self.caps.medium, Medium::Ethernet) {
1474 total_len = EthernetFrame::<&[u8]>::buffer_len(total_len);
1475 }
1476
1477 #[cfg(feature = "medium-ethernet")]
1479 let (dst_hardware_addr, mut tx_token) = match self.caps.medium {
1480 Medium::Ethernet => {
1481 match self.lookup_hardware_addr(
1482 tx_token,
1483 &ip_repr.src_addr(),
1484 &ip_repr.dst_addr(),
1485 frag,
1486 )? {
1487 (HardwareAddress::Ethernet(addr), tx_token) => (addr, tx_token),
1488 (_, _) => unreachable!(),
1489 }
1490 }
1491 _ => (EthernetAddress([0; 6]), tx_token),
1492 };
1493
1494 #[cfg(feature = "medium-ethernet")]
1496 let emit_ethernet = |repr: &IpRepr, tx_buffer: &mut [u8]| {
1497 let mut frame = EthernetFrame::new_unchecked(tx_buffer);
1498
1499 let src_addr = self.hardware_addr.ethernet_or_panic();
1500 frame.set_src_addr(src_addr);
1501 frame.set_dst_addr(dst_hardware_addr);
1502
1503 match repr.version() {
1504 #[cfg(feature = "proto-ipv4")]
1505 IpVersion::Ipv4 => frame.set_ethertype(EthernetProtocol::Ipv4),
1506 #[cfg(feature = "proto-ipv6")]
1507 IpVersion::Ipv6 => frame.set_ethertype(EthernetProtocol::Ipv6),
1508 }
1509
1510 Ok(())
1511 };
1512
1513 let emit_ip = |repr: &IpRepr, mut tx_buffer: &mut [u8]| {
1515 repr.emit(&mut tx_buffer, &self.caps.checksum);
1516
1517 let payload = &mut tx_buffer[repr.header_len()..];
1518 packet.emit_payload(repr, payload, &caps)
1519 };
1520
1521 let total_ip_len = ip_repr.buffer_len();
1522
1523 match &mut ip_repr {
1524 #[cfg(feature = "proto-ipv4")]
1525 IpRepr::Ipv4(repr) => {
1526 if total_ip_len > self.caps.max_transmission_unit {
1528 #[cfg(feature = "proto-ipv4-fragmentation")]
1529 {
1530 net_debug!("start fragmentation");
1531
1532 let tx_len = self.caps.max_transmission_unit;
1534
1535 let ip_header_len = repr.buffer_len();
1536 let first_frag_ip_len = self.caps.ip_mtu();
1537
1538 if frag.buffer.len() < total_ip_len {
1539 net_debug!(
1540 "Fragmentation buffer is too small, at least {} needed. Dropping",
1541 total_ip_len
1542 );
1543 return Ok(());
1544 }
1545
1546 #[cfg(feature = "medium-ethernet")]
1547 {
1548 frag.ipv4.dst_hardware_addr = dst_hardware_addr;
1549 }
1550
1551 frag.packet_len = total_ip_len;
1554
1555 frag.ipv4.repr = *repr;
1557
1558 frag.sent_bytes = first_frag_ip_len;
1560
1561 repr.payload_len = first_frag_ip_len - repr.buffer_len();
1563
1564 emit_ip(&ip_repr, &mut frag.buffer);
1566
1567 let mut ipv4_packet = Ipv4Packet::new_unchecked(&mut frag.buffer[..]);
1568 frag.ipv4.ident = ipv4_id;
1569 ipv4_packet.set_ident(ipv4_id);
1570 ipv4_packet.set_more_frags(true);
1571 ipv4_packet.set_dont_frag(false);
1572 ipv4_packet.set_frag_offset(0);
1573
1574 if caps.checksum.ipv4.tx() {
1575 ipv4_packet.fill_checksum();
1576 }
1577
1578 tx_token.consume(tx_len, |mut tx_buffer| {
1580 #[cfg(feature = "medium-ethernet")]
1581 if matches!(self.caps.medium, Medium::Ethernet) {
1582 emit_ethernet(&ip_repr, tx_buffer)?;
1583 tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..];
1584 }
1585
1586 frag.ipv4.frag_offset = (first_frag_ip_len - ip_header_len) as u16;
1588
1589 tx_buffer[..first_frag_ip_len]
1591 .copy_from_slice(&frag.buffer[..first_frag_ip_len]);
1592
1593 Ok(())
1594 })
1595 }
1596
1597 #[cfg(not(feature = "proto-ipv4-fragmentation"))]
1598 {
1599 net_debug!("Enable the `proto-ipv4-fragmentation` feature for fragmentation support.");
1600 Ok(())
1601 }
1602 } else {
1603 tx_token.set_meta(meta);
1604
1605 tx_token.consume(total_len, |mut tx_buffer| {
1607 #[cfg(feature = "medium-ethernet")]
1608 if matches!(self.caps.medium, Medium::Ethernet) {
1609 emit_ethernet(&ip_repr, tx_buffer)?;
1610 tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..];
1611 }
1612
1613 emit_ip(&ip_repr, tx_buffer);
1614 Ok(())
1615 })
1616 }
1617 }
1618 #[cfg(feature = "proto-ipv6")]
1620 IpRepr::Ipv6(_) => tx_token.consume(total_len, |mut tx_buffer| {
1621 #[cfg(feature = "medium-ethernet")]
1622 if matches!(self.caps.medium, Medium::Ethernet) {
1623 emit_ethernet(&ip_repr, tx_buffer)?;
1624 tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..];
1625 }
1626
1627 emit_ip(&ip_repr, tx_buffer);
1628 Ok(())
1629 }),
1630 }
1631 }
1632}
1633
1634#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1635#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1636enum DispatchError {
1637 NoRoute,
1640 NeighborPending,
1644}