Skip to main content

smoltcp/iface/interface/
mod.rs

1// Heads up! Before working on this file you should read the parts
2// of RFC 1122 that discuss Ethernet, ARP and IP for any IPv4 work
3// and RFCs 8200 and 4861 for any IPv6 and NDISC work.
4
5#[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                // concat!/stringify! doesn't work with defmt macros
61                #[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/// Result returned by [`Interface::poll`].
73///
74/// This contains information on whether socket states might have changed.
75#[derive(Copy, Clone, PartialEq, Eq, Debug)]
76#[cfg_attr(feature = "defmt", derive(defmt::Format))]
77pub enum PollResult {
78    /// Socket state is guaranteed to not have changed.
79    None,
80    /// You should check the state of sockets again for received data or completion of operations.
81    SocketStateChanged,
82}
83
84/// Result returned by [`Interface::poll_ingress_single`].
85///
86/// This contains information on whether a packet was processed or not,
87/// and whether it might've affected socket states.
88#[derive(Copy, Clone, PartialEq, Eq, Debug)]
89#[cfg_attr(feature = "defmt", derive(defmt::Format))]
90pub enum PollIngressSingleResult {
91    /// No packet was processed. You don't need to call [`Interface::poll_ingress_single`]
92    /// again, until more packets arrive.
93    ///
94    /// Socket state is guaranteed to not have changed.
95    None,
96    /// A packet was processed.
97    ///
98    /// There may be more packets in the device's RX queue, so you should call [`Interface::poll_ingress_single`] again.
99    ///
100    /// Socket state is guaranteed to not have changed.
101    PacketProcessed,
102    /// A packet was processed, which might have caused socket state to change.
103    ///
104    /// There may be more packets in the device's RX queue, so you should call [`Interface::poll_ingress_single`] again.
105    ///
106    /// You should check the state of sockets again for received data or completion of operations.
107    SocketStateChanged,
108}
109
110/// A  network interface.
111///
112/// The network interface logically owns a number of other data structures; to avoid
113/// a dependency on heap allocation, it instead owns a `BorrowMut<[T]>`, which can be
114/// a `&mut [T]`, or `Vec<T>` if a heap is available.
115pub struct Interface {
116    pub(crate) inner: InterfaceInner,
117    fragments: FragmentsBuffer,
118    fragmenter: Fragmenter,
119}
120
121/// The device independent part of an Ethernet network interface.
122///
123/// Separating the device from the data required for processing and dispatching makes
124/// it possible to borrow them independently. For example, the tx and rx tokens borrow
125/// the `device` mutably until they're used, which makes it impossible to call other
126/// methods on the `Interface` in this time (since its `device` field is borrowed
127/// exclusively). However, it is still possible to call methods on its `inner` field.
128pub 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/// Configuration structure used for creating a network interface.
161#[non_exhaustive]
162pub struct Config {
163    /// Random seed.
164    ///
165    /// It is strongly recommended that the random seed is different on each boot,
166    /// to avoid problems with TCP port/sequence collisions.
167    ///
168    /// The seed doesn't have to be cryptographically secure.
169    pub random_seed: u64,
170
171    /// Set the Hardware address the interface will use.
172    ///
173    /// # Panics
174    /// Creating the interface panics if the address is not unicast.
175    pub hardware_addr: HardwareAddress,
176
177    /// Set the IEEE802.15.4 PAN ID the interface will use.
178    ///
179    /// **NOTE**: we use the same PAN ID for destination and source.
180    #[cfg(feature = "medium-ieee802154")]
181    pub pan_id: Option<Ieee802154Pan>,
182
183    /// Enable stateless address autoconfiguration on the interface.
184    #[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    /// Create a network interface using the previously provided configuration.
203    ///
204    /// # Panics
205    /// This function panics if the [`Config::hardware_address`] does not match
206    /// the medium of the device.
207    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    /// Get the socket context.
293    ///
294    /// The context is needed for some socket methods.
295    pub fn context(&mut self) -> &mut InterfaceInner {
296        &mut self.inner
297    }
298
299    /// Get the HardwareAddress address of the interface.
300    ///
301    /// # Panics
302    /// This function panics if the medium is not Ethernet or Ieee802154.
303    #[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    /// Set the HardwareAddress address of the interface.
320    ///
321    /// # Panics
322    /// This function panics if the address is not unicast, and if the medium is not Ethernet or
323    /// Ieee802154.
324    #[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    /// Get the IP addresses of the interface.
342    pub fn ip_addrs(&self) -> &[IpCidr] {
343        self.inner.ip_addrs.as_ref()
344    }
345
346    /// Get the first IPv4 address if present.
347    #[cfg(feature = "proto-ipv4")]
348    pub fn ipv4_addr(&self) -> Option<Ipv4Address> {
349        self.inner.ipv4_addr()
350    }
351
352    /// Get the first IPv6 address if present.
353    #[cfg(feature = "proto-ipv6")]
354    pub fn ipv6_addr(&self) -> Option<Ipv6Address> {
355        self.inner.ipv6_addr()
356    }
357
358    /// Get an address from the interface that could be used as source address.
359    /// For IPv4, this function tries to find a registered IPv4 address in the same
360    /// subnet as the destination, falling back to the first IPv4 address if none is
361    /// found. For IPv6, the selection is based on RFC6724.
362    pub fn get_source_address(&self, dst_addr: &IpAddress) -> Option<IpAddress> {
363        self.inner.get_source_address(dst_addr)
364    }
365
366    /// Get an IPv4 source address based on a destination address. This function tries
367    /// to find the first IPv4 address from the interface that is in the same subnet as
368    /// the destination address. If no such address is found, the first IPv4 address
369    /// from the interface is returned.
370    #[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    /// Get an address from the interface that could be used as source address. The selection is
376    /// based on RFC6724.
377    #[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    /// Update the IP addresses of the interface.
383    ///
384    /// # Panics
385    /// This function panics if any of the addresses are not unicast.
386    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    /// Check whether the interface has the given IP address assigned.
402    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    /// Enable or disable the AnyIP capability.
415    ///
416    /// AnyIP allowins packets to be received
417    /// locally on IP addresses other than the interface's configured [ip_addrs].
418    /// When AnyIP is enabled and a route prefix in [`routes`](Self::routes) specifies one of
419    /// the interface's [`ip_addrs`](Self::ip_addrs) as its gateway, the interface will accept
420    /// packets addressed to that prefix.
421    pub fn set_any_ip(&mut self, any_ip: bool) {
422        self.inner.any_ip = any_ip;
423    }
424
425    /// Get whether AnyIP is enabled.
426    ///
427    /// See [`set_any_ip`](Self::set_any_ip) for details on AnyIP
428    pub fn any_ip(&self) -> bool {
429        self.inner.any_ip
430    }
431
432    /// Get the packet reassembly timeout.
433    #[cfg(feature = "_proto-fragmentation")]
434    pub fn reassembly_timeout(&self) -> Duration {
435        self.fragments.reassembly_timeout
436    }
437
438    /// Set the packet reassembly timeout.
439    #[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    /// Transmit packets queued in the sockets, and receive packets queued
450    /// in the device.
451    ///
452    /// This function returns a value indicating whether the state of any socket
453    /// might have changed.
454    ///
455    /// ## DoS warning
456    ///
457    /// This function processes all packets in the device's queue. This can
458    /// be an unbounded amount of work if packets arrive faster than they're
459    /// processed.
460    ///
461    /// If this is a concern for your application (i.e. your environment doesn't
462    /// have preemptive scheduling, or `poll()` is called from a main loop where
463    /// other important things are processed), you may use the lower-level methods
464    /// [`poll_egress()`](Self::poll_egress), [`poll_maintenance()`](Self::poll_maintenance)
465    /// and [`poll_ingress_single()`](Self::poll_ingress_single).
466    /// This allows you to insert yields or process other events between processing
467    /// individual ingress packets.
468    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        // Process ingress while there's packets available.
481        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        // Process egress.
490        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    /// Transmit packets queued in the sockets.
501    ///
502    /// This function returns a value indicating whether the state of any socket
503    /// might have changed.
504    ///
505    /// This is guaranteed to always perform a bounded amount of work.
506    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    /// Process one incoming packet queued in the device.
539    ///
540    /// Returns a value indicating:
541    /// - whether a packet was processed, in which case you have to call this method again in case there's more packets queued.
542    /// - whether the state of any socket might have changed.
543    ///
544    /// Since it processes at most one packet, this is guaranteed to always perform a bounded amount of work.
545    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    /// Maintain stateful processing on the device.
560    ///
561    /// This is guaranteed to always perform a bounded amount of work.
562    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    /// Return a _soft deadline_ for calling [poll] the next time.
575    /// The [Instant] returned is the time at which you should call [poll] next.
576    /// It is harmless (but wastes energy) to call it before the [Instant], and
577    /// potentially harmful (impacting quality of service) to call it after the
578    /// [Instant]
579    ///
580    /// [poll]: #method.poll
581    /// [Instant]: struct.Instant.html
582    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    /// Return an _advisory wait time_ for calling [poll] the next time.
616    /// The [Duration] returned is the time left to wait before calling [poll] next.
617    /// It is harmless (but wastes energy) to call it before the [Duration] has passed,
618    /// and potentially harmful (impacting quality of service) to call it after the
619    /// [Duration] has passed.
620    ///
621    /// [poll]: #method.poll
622    /// [Duration]: struct.Duration.html
623    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            // TODO: Propagate the PollIngressSingleResult from deeper.
691            // There's many received packets that we process but can't cause sockets
692            // to change state. For example IP fragments, multicast stuff, ICMP pings
693            // if they dont't match any raw socket...
694            // We should return `PacketProcessed` for these to save the user from
695            // doing useless socket polls.
696            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, // Device buffer full.
802                Err(EgressError::Dispatch) => {
803                    // `NeighborCache` already takes care of rate limiting the neighbor discovery
804                    // requests from the socket. However, without an additional rate limiting
805                    // mechanism, we would spin on every socket that has yet to discover its
806                    // neighbor.
807                    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)] // unused depending on which sockets are enabled
821    pub(crate) fn now(&self) -> Instant {
822        self.now
823    }
824
825    #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
826    #[allow(unused)] // unused depending on which sockets are enabled
827    pub(crate) fn hardware_addr(&self) -> HardwareAddress {
828        self.hardware_addr
829    }
830
831    #[allow(unused)] // unused depending on which sockets are enabled
832    pub(crate) fn checksum_caps(&self) -> ChecksumCapabilities {
833        self.caps.checksum.clone()
834    }
835
836    #[allow(unused)] // unused depending on which sockets are enabled
837    pub(crate) fn ip_mtu(&self) -> usize {
838        self.caps.ip_mtu()
839    }
840
841    #[allow(unused)] // unused depending on which sockets are enabled, and in tests
842    pub(crate) fn rand(&mut self) -> &mut Rand {
843        &mut self.rand
844    }
845
846    #[allow(unused)] // unused depending on which sockets are enabled
847    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)] // unused depending on which sockets are enabled
858    pub(crate) fn set_now(&mut self, now: Instant) {
859        self.now = now
860    }
861
862    #[cfg(test)]
863    #[allow(unused)] // unused depending on which sockets are enabled
864    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    /// Check whether the interface has the given IP address assigned.
884    ///
885    /// Always returns true if [`InterfaceInner::any_ip`].
886    pub(crate) fn has_ip_addr<T: Into<IpAddress>>(&self, addr: T) -> bool {
887        // If any IP is set to true, we don't bother about checking the IP.
888        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    /// Check whether the interface listens to given destination multicast IP address.
897    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            // Drop all other traffic.
939            _ => 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        // Pass every IP packet to all raw sockets we have registered.
953        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    /// Checks if an address is broadcast, taking into account ipv4 subnet-local
966    /// broadcast addresses.
967    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        // Send directly.
1016        // note: no need to use `self.is_broadcast()` to check for subnet-local broadcast addrs
1017        //       here because `in_same_network` will already return true.
1018        if self.in_same_network(addr) || addr.is_broadcast() {
1019            return Some(*addr);
1020        }
1021
1022        // Route via a router.
1023        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                        // Not sure if this is correct
1096                        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            _ => (), // XXX
1114        }
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        // The request got dispatched, limit the rate on the cache.
1184        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        // NOTE(unused_mut): tx_token isn't always mutated, depending on
1196        // the feature set that is used.
1197        #[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        // Dispatch IEEE802.15.4:
1206
1207        #[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        // Dispatch IP/Ethernet:
1218
1219        let caps = self.caps.clone();
1220
1221        #[cfg(feature = "proto-ipv4-fragmentation")]
1222        let ipv4_id = self.next_ipv4_frag_ident();
1223
1224        // First we calculate the total length that we will have to emit.
1225        let mut total_len = ip_repr.buffer_len();
1226
1227        // Add the size of the Ethernet header if the medium is Ethernet.
1228        #[cfg(feature = "medium-ethernet")]
1229        if matches!(self.caps.medium, Medium::Ethernet) {
1230            total_len = EthernetFrame::<&[u8]>::buffer_len(total_len);
1231        }
1232
1233        // If the medium is Ethernet, then we need to retrieve the destination hardware address.
1234        #[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        // Emit function for the Ethernet header.
1246        #[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        // Emit function for the IP header and payload.
1263        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 we have an IPv4 packet, then we need to check if we need to fragment it.
1276                if total_ip_len > self.caps.ip_mtu() {
1277                    #[cfg(feature = "proto-ipv4-fragmentation")]
1278                    {
1279                        net_debug!("start fragmentation");
1280
1281                        // Calculate how much we will send now (including the Ethernet header).
1282
1283                        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                        // Save the total packet len (without the Ethernet header, but with the first
1307                        // IP header).
1308                        frag.packet_len = total_ip_len;
1309
1310                        // Save the IP header for other fragments.
1311                        frag.ipv4.repr = *repr;
1312
1313                        // Modify the IP header
1314                        repr.payload_len = first_frag_data_len;
1315
1316                        // Save the number of bytes we will send now.
1317                        frag.sent_bytes = first_frag_ip_len;
1318
1319                        // Emit the IP header to the buffer.
1320                        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                        // Transmit the first packet.
1334                        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                            // Change the offset for the next packet.
1342                            frag.ipv4.frag_offset = (first_frag_ip_len - ip_header_len) as u16;
1343
1344                            // Copy the IP header and the payload.
1345                            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                    // No fragmentation is required.
1363                    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            // We don't support IPv6 fragmentation yet.
1377            #[cfg(feature = "proto-ipv6")]
1378            IpRepr::Ipv6(_) => {
1379                // Check if we need to fragment it.
1380                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    /// No route to dispatch this packet. Retrying won't help unless
1404    /// configuration is changed.
1405    NoRoute,
1406    /// We do have a route to dispatch this packet, but we haven't discovered
1407    /// the neighbor for it yet. Discovery has been initiated, dispatch
1408    /// should be retried later.
1409    NeighborPending,
1410}