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::{IFACE_MAX_ADDR_COUNT, IFACE_MAX_SIXLOWPAN_ADDRESS_CONTEXT_COUNT};
42use crate::iface::Routes;
43use crate::phy::PacketMeta;
44use crate::phy::{ChecksumCapabilities, Device, DeviceCapabilities, Medium, RxToken, TxToken};
45use crate::rand::Rand;
46use crate::socket::*;
47use crate::time::{Duration, Instant};
48
49use crate::wire::*;
50
51macro_rules! check {
52    ($e:expr) => {
53        match $e {
54            Ok(x) => x,
55            Err(_) => {
56                // concat!/stringify! doesn't work with defmt macros
57                #[cfg(not(feature = "defmt"))]
58                net_trace!(concat!("iface: malformed ", stringify!($e)));
59                #[cfg(feature = "defmt")]
60                net_trace!("iface: malformed");
61                return Default::default();
62            }
63        }
64    };
65}
66use check;
67
68/// Result returned by [`Interface::poll`].
69///
70/// This contains information on whether socket states might have changed.
71#[derive(Copy, Clone, PartialEq, Eq, Debug)]
72#[cfg_attr(feature = "defmt", derive(defmt::Format))]
73pub enum PollResult {
74    /// Socket state is guaranteed to not have changed.
75    None,
76    /// You should check the state of sockets again for received data or completion of operations.
77    SocketStateChanged,
78}
79
80/// Result returned by [`Interface::poll_ingress_single`].
81///
82/// This contains information on whether a packet was processed or not,
83/// and whether it might've affected socket states.
84#[derive(Copy, Clone, PartialEq, Eq, Debug)]
85#[cfg_attr(feature = "defmt", derive(defmt::Format))]
86pub enum PollIngressSingleResult {
87    /// No packet was processed. You don't need to call [`Interface::poll_ingress_single`]
88    /// again, until more packets arrive.
89    ///
90    /// Socket state is guaranteed to not have changed.
91    None,
92    /// A packet was processed.
93    ///
94    /// There may be more packets in the device's RX queue, so you should call [`Interface::poll_ingress_single`] again.
95    ///
96    /// Socket state is guaranteed to not have changed.
97    PacketProcessed,
98    /// A packet was processed, which might have caused socket state to change.
99    ///
100    /// There may be more packets in the device's RX queue, so you should call [`Interface::poll_ingress_single`] again.
101    ///
102    /// You should check the state of sockets again for received data or completion of operations.
103    SocketStateChanged,
104}
105
106/// A  network interface.
107///
108/// The network interface logically owns a number of other data structures; to avoid
109/// a dependency on heap allocation, it instead owns a `BorrowMut<[T]>`, which can be
110/// a `&mut [T]`, or `Vec<T>` if a heap is available.
111pub struct Interface {
112    pub(crate) inner: InterfaceInner,
113    fragments: FragmentsBuffer,
114    fragmenter: Fragmenter,
115}
116
117/// The device independent part of an Ethernet network interface.
118///
119/// Separating the device from the data required for processing and dispatching makes
120/// it possible to borrow them independently. For example, the tx and rx tokens borrow
121/// the `device` mutably until they're used, which makes it impossible to call other
122/// methods on the `Interface` in this time (since its `device` field is borrowed
123/// exclusively). However, it is still possible to call methods on its `inner` field.
124pub struct InterfaceInner {
125    caps: DeviceCapabilities,
126    now: Instant,
127    rand: Rand,
128
129    #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
130    neighbor_cache: NeighborCache,
131    hardware_addr: HardwareAddress,
132    #[cfg(feature = "medium-ieee802154")]
133    sequence_no: u8,
134    #[cfg(feature = "medium-ieee802154")]
135    pan_id: Option<Ieee802154Pan>,
136    #[cfg(feature = "proto-ipv4-fragmentation")]
137    ipv4_id: u16,
138    #[cfg(feature = "proto-sixlowpan")]
139    sixlowpan_address_context:
140        Vec<SixlowpanAddressContext, IFACE_MAX_SIXLOWPAN_ADDRESS_CONTEXT_COUNT>,
141    #[cfg(feature = "proto-sixlowpan-fragmentation")]
142    tag: u16,
143    ip_addrs: Vec<IpCidr, IFACE_MAX_ADDR_COUNT>,
144    any_ip: bool,
145    routes: Routes,
146    #[cfg(feature = "multicast")]
147    multicast: multicast::State,
148}
149
150/// Configuration structure used for creating a network interface.
151#[non_exhaustive]
152pub struct Config {
153    /// Random seed.
154    ///
155    /// It is strongly recommended that the random seed is different on each boot,
156    /// to avoid problems with TCP port/sequence collisions.
157    ///
158    /// The seed doesn't have to be cryptographically secure.
159    pub random_seed: u64,
160
161    /// Set the Hardware address the interface will use.
162    ///
163    /// # Panics
164    /// Creating the interface panics if the address is not unicast.
165    pub hardware_addr: HardwareAddress,
166
167    /// Set the IEEE802.15.4 PAN ID the interface will use.
168    ///
169    /// **NOTE**: we use the same PAN ID for destination and source.
170    #[cfg(feature = "medium-ieee802154")]
171    pub pan_id: Option<Ieee802154Pan>,
172}
173
174impl Config {
175    pub fn new(hardware_addr: HardwareAddress) -> Self {
176        Config {
177            random_seed: 0,
178            hardware_addr,
179            #[cfg(feature = "medium-ieee802154")]
180            pan_id: None,
181        }
182    }
183}
184
185impl Interface {
186    /// Create a network interface using the previously provided configuration.
187    ///
188    /// # Panics
189    /// This function panics if the [`Config::hardware_address`] does not match
190    /// the medium of the device.
191    pub fn new(config: Config, device: &mut (impl Device + ?Sized), now: Instant) -> Self {
192        let caps = device.capabilities();
193        assert_eq!(
194            config.hardware_addr.medium(),
195            caps.medium,
196            "The hardware address does not match the medium of the interface."
197        );
198
199        let mut rand = Rand::new(config.random_seed);
200
201        #[cfg(feature = "medium-ieee802154")]
202        let mut sequence_no;
203        #[cfg(feature = "medium-ieee802154")]
204        loop {
205            sequence_no = (rand.rand_u32() & 0xff) as u8;
206            if sequence_no != 0 {
207                break;
208            }
209        }
210
211        #[cfg(feature = "proto-sixlowpan")]
212        let mut tag;
213
214        #[cfg(feature = "proto-sixlowpan")]
215        loop {
216            tag = rand.rand_u16();
217            if tag != 0 {
218                break;
219            }
220        }
221
222        #[cfg(feature = "proto-ipv4")]
223        let mut ipv4_id;
224
225        #[cfg(feature = "proto-ipv4")]
226        loop {
227            ipv4_id = rand.rand_u16();
228            if ipv4_id != 0 {
229                break;
230            }
231        }
232
233        Interface {
234            fragments: FragmentsBuffer {
235                #[cfg(feature = "proto-sixlowpan")]
236                decompress_buf: [0u8; sixlowpan::MAX_DECOMPRESSED_LEN],
237
238                #[cfg(feature = "_proto-fragmentation")]
239                assembler: PacketAssemblerSet::new(),
240                #[cfg(feature = "_proto-fragmentation")]
241                reassembly_timeout: Duration::from_secs(60),
242            },
243            fragmenter: Fragmenter::new(),
244            inner: InterfaceInner {
245                now,
246                caps,
247                hardware_addr: config.hardware_addr,
248                ip_addrs: Vec::new(),
249                any_ip: false,
250                routes: Routes::new(),
251                #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
252                neighbor_cache: NeighborCache::new(),
253                #[cfg(feature = "multicast")]
254                multicast: multicast::State::new(),
255                #[cfg(feature = "medium-ieee802154")]
256                sequence_no,
257                #[cfg(feature = "medium-ieee802154")]
258                pan_id: config.pan_id,
259                #[cfg(feature = "proto-sixlowpan-fragmentation")]
260                tag,
261                #[cfg(feature = "proto-ipv4-fragmentation")]
262                ipv4_id,
263                #[cfg(feature = "proto-sixlowpan")]
264                sixlowpan_address_context: Vec::new(),
265                rand,
266            },
267        }
268    }
269
270    /// Get the socket context.
271    ///
272    /// The context is needed for some socket methods.
273    pub fn context(&mut self) -> &mut InterfaceInner {
274        &mut self.inner
275    }
276
277    /// Get the HardwareAddress address of the interface.
278    ///
279    /// # Panics
280    /// This function panics if the medium is not Ethernet or Ieee802154.
281    #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
282    pub fn hardware_addr(&self) -> HardwareAddress {
283        #[cfg(all(feature = "medium-ethernet", not(feature = "medium-ieee802154")))]
284        assert!(self.inner.caps.medium == Medium::Ethernet);
285        #[cfg(all(feature = "medium-ieee802154", not(feature = "medium-ethernet")))]
286        assert!(self.inner.caps.medium == Medium::Ieee802154);
287
288        #[cfg(all(feature = "medium-ieee802154", feature = "medium-ethernet"))]
289        assert!(
290            self.inner.caps.medium == Medium::Ethernet
291                || self.inner.caps.medium == Medium::Ieee802154
292        );
293
294        self.inner.hardware_addr
295    }
296
297    /// Set the HardwareAddress address of the interface.
298    ///
299    /// # Panics
300    /// This function panics if the address is not unicast, and if the medium is not Ethernet or
301    /// Ieee802154.
302    #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
303    pub fn set_hardware_addr(&mut self, addr: HardwareAddress) {
304        #[cfg(all(feature = "medium-ethernet", not(feature = "medium-ieee802154")))]
305        assert!(self.inner.caps.medium == Medium::Ethernet);
306        #[cfg(all(feature = "medium-ieee802154", not(feature = "medium-ethernet")))]
307        assert!(self.inner.caps.medium == Medium::Ieee802154);
308
309        #[cfg(all(feature = "medium-ieee802154", feature = "medium-ethernet"))]
310        assert!(
311            self.inner.caps.medium == Medium::Ethernet
312                || self.inner.caps.medium == Medium::Ieee802154
313        );
314
315        InterfaceInner::check_hardware_addr(&addr);
316        self.inner.hardware_addr = addr;
317    }
318
319    /// Get the IP addresses of the interface.
320    pub fn ip_addrs(&self) -> &[IpCidr] {
321        self.inner.ip_addrs.as_ref()
322    }
323
324    /// Get the first IPv4 address if present.
325    #[cfg(feature = "proto-ipv4")]
326    pub fn ipv4_addr(&self) -> Option<Ipv4Address> {
327        self.inner.ipv4_addr()
328    }
329
330    /// Get the first IPv6 address if present.
331    #[cfg(feature = "proto-ipv6")]
332    pub fn ipv6_addr(&self) -> Option<Ipv6Address> {
333        self.inner.ipv6_addr()
334    }
335
336    /// Get an address from the interface that could be used as source address. For IPv4, this is
337    /// the first IPv4 address from the list of addresses. For IPv6, the address is based on the
338    /// destination address and uses RFC6724 for selecting the source address.
339    pub fn get_source_address(&self, dst_addr: &IpAddress) -> Option<IpAddress> {
340        self.inner.get_source_address(dst_addr)
341    }
342
343    /// Get an address from the interface that could be used as source address. This is the first
344    /// IPv4 address from the list of addresses in the interface.
345    #[cfg(feature = "proto-ipv4")]
346    pub fn get_source_address_ipv4(&self, dst_addr: &Ipv4Address) -> Option<Ipv4Address> {
347        self.inner.get_source_address_ipv4(dst_addr)
348    }
349
350    /// Get an address from the interface that could be used as source address. The selection is
351    /// based on RFC6724.
352    #[cfg(feature = "proto-ipv6")]
353    pub fn get_source_address_ipv6(&self, dst_addr: &Ipv6Address) -> Ipv6Address {
354        self.inner.get_source_address_ipv6(dst_addr)
355    }
356
357    /// Update the IP addresses of the interface.
358    ///
359    /// # Panics
360    /// This function panics if any of the addresses are not unicast.
361    pub fn update_ip_addrs<F: FnOnce(&mut Vec<IpCidr, IFACE_MAX_ADDR_COUNT>)>(&mut self, f: F) {
362        f(&mut self.inner.ip_addrs);
363        InterfaceInner::flush_neighbor_cache(&mut self.inner);
364        InterfaceInner::check_ip_addrs(&self.inner.ip_addrs);
365
366        #[cfg(all(feature = "proto-ipv6", feature = "multicast"))]
367        if self.inner.caps.medium == Medium::Ethernet {
368            self.update_solicited_node_groups();
369        }
370    }
371
372    /// Check whether the interface has the given IP address assigned.
373    pub fn has_ip_addr<T: Into<IpAddress>>(&self, addr: T) -> bool {
374        self.inner.has_ip_addr(addr)
375    }
376
377    pub fn routes(&self) -> &Routes {
378        &self.inner.routes
379    }
380
381    pub fn routes_mut(&mut self) -> &mut Routes {
382        &mut self.inner.routes
383    }
384
385    /// Enable or disable the AnyIP capability.
386    ///
387    /// AnyIP allowins packets to be received
388    /// locally on IP addresses other than the interface's configured [ip_addrs].
389    /// When AnyIP is enabled and a route prefix in [`routes`](Self::routes) specifies one of
390    /// the interface's [`ip_addrs`](Self::ip_addrs) as its gateway, the interface will accept
391    /// packets addressed to that prefix.
392    pub fn set_any_ip(&mut self, any_ip: bool) {
393        self.inner.any_ip = any_ip;
394    }
395
396    /// Get whether AnyIP is enabled.
397    ///
398    /// See [`set_any_ip`](Self::set_any_ip) for details on AnyIP
399    pub fn any_ip(&self) -> bool {
400        self.inner.any_ip
401    }
402
403    /// Get the packet reassembly timeout.
404    #[cfg(feature = "_proto-fragmentation")]
405    pub fn reassembly_timeout(&self) -> Duration {
406        self.fragments.reassembly_timeout
407    }
408
409    /// Set the packet reassembly timeout.
410    #[cfg(feature = "_proto-fragmentation")]
411    pub fn set_reassembly_timeout(&mut self, timeout: Duration) {
412        if timeout > Duration::from_secs(60) {
413            net_debug!("RFC 4944 specifies that the reassembly timeout MUST be set to a maximum of 60 seconds");
414        }
415        self.fragments.reassembly_timeout = timeout;
416    }
417
418    /// Transmit packets queued in the sockets, and receive packets queued
419    /// in the device.
420    ///
421    /// This function returns a value indicating whether the state of any socket
422    /// might have changed.
423    ///
424    /// ## DoS warning
425    ///
426    /// This function processes all packets in the device's queue. This can
427    /// be an unbounded amount of work if packets arrive faster than they're
428    /// processed.
429    ///
430    /// If this is a concern for your application (i.e. your environment doesn't
431    /// have preemptive scheduling, or `poll()` is called from a main loop where
432    /// other important things are processed), you may use the lower-level methods
433    /// [`poll_egress()`](Self::poll_egress) and [`poll_ingress_single()`](Self::poll_ingress_single).
434    /// This allows you to insert yields or process other events between processing
435    /// individual ingress packets.
436    pub fn poll(
437        &mut self,
438        timestamp: Instant,
439        device: &mut (impl Device + ?Sized),
440        sockets: &mut SocketSet<'_>,
441    ) -> PollResult {
442        self.inner.now = timestamp;
443
444        let mut res = PollResult::None;
445
446        #[cfg(feature = "_proto-fragmentation")]
447        self.fragments.assembler.remove_expired(timestamp);
448
449        // Process ingress while there's packets available.
450        loop {
451            match self.socket_ingress(device, sockets) {
452                PollIngressSingleResult::None => break,
453                PollIngressSingleResult::PacketProcessed => {}
454                PollIngressSingleResult::SocketStateChanged => res = PollResult::SocketStateChanged,
455            }
456        }
457
458        // Process egress.
459        match self.poll_egress(timestamp, device, sockets) {
460            PollResult::None => {}
461            PollResult::SocketStateChanged => res = PollResult::SocketStateChanged,
462        }
463
464        res
465    }
466
467    /// Transmit packets queued in the sockets.
468    ///
469    /// This function returns a value indicating whether the state of any socket
470    /// might have changed.
471    ///
472    /// This is guaranteed to always perform a bounded amount of work.
473    pub fn poll_egress(
474        &mut self,
475        timestamp: Instant,
476        device: &mut (impl Device + ?Sized),
477        sockets: &mut SocketSet<'_>,
478    ) -> PollResult {
479        self.inner.now = timestamp;
480
481        match self.inner.caps.medium {
482            #[cfg(feature = "medium-ieee802154")]
483            Medium::Ieee802154 => {
484                #[cfg(feature = "proto-sixlowpan-fragmentation")]
485                self.sixlowpan_egress(device);
486            }
487            #[cfg(any(feature = "medium-ethernet", feature = "medium-ip"))]
488            _ => {
489                #[cfg(feature = "proto-ipv4-fragmentation")]
490                self.ipv4_egress(device);
491            }
492        }
493
494        #[cfg(feature = "multicast")]
495        self.multicast_egress(device);
496
497        self.socket_egress(device, sockets)
498    }
499
500    /// Process one incoming packet queued in the device.
501    ///
502    /// Returns a value indicating:
503    /// - whether a packet was processed, in which case you have to call this method again in case there's more packets queued.
504    /// - whether the state of any socket might have changed.
505    ///
506    /// Since it processes at most one packet, this is guaranteed to always perform a bounded amount of work.
507    pub fn poll_ingress_single(
508        &mut self,
509        timestamp: Instant,
510        device: &mut (impl Device + ?Sized),
511        sockets: &mut SocketSet<'_>,
512    ) -> PollIngressSingleResult {
513        self.inner.now = timestamp;
514
515        #[cfg(feature = "_proto-fragmentation")]
516        self.fragments.assembler.remove_expired(timestamp);
517
518        self.socket_ingress(device, sockets)
519    }
520
521    /// Return a _soft deadline_ for calling [poll] the next time.
522    /// The [Instant] returned is the time at which you should call [poll] next.
523    /// It is harmless (but wastes energy) to call it before the [Instant], and
524    /// potentially harmful (impacting quality of service) to call it after the
525    /// [Instant]
526    ///
527    /// [poll]: #method.poll
528    /// [Instant]: struct.Instant.html
529    pub fn poll_at(&mut self, timestamp: Instant, sockets: &SocketSet<'_>) -> Option<Instant> {
530        self.inner.now = timestamp;
531
532        #[cfg(feature = "_proto-fragmentation")]
533        if !self.fragmenter.is_empty() {
534            return Some(Instant::from_millis(0));
535        }
536
537        let inner = &mut self.inner;
538
539        sockets
540            .items()
541            .filter_map(move |item| {
542                let socket_poll_at = item.socket.poll_at(inner);
543                match item
544                    .meta
545                    .poll_at(socket_poll_at, |ip_addr| inner.has_neighbor(&ip_addr))
546                {
547                    PollAt::Ingress => None,
548                    PollAt::Time(instant) => Some(instant),
549                    PollAt::Now => Some(Instant::from_millis(0)),
550                }
551            })
552            .min()
553    }
554
555    /// Return an _advisory wait time_ for calling [poll] the next time.
556    /// The [Duration] returned is the time left to wait before calling [poll] next.
557    /// It is harmless (but wastes energy) to call it before the [Duration] has passed,
558    /// and potentially harmful (impacting quality of service) to call it after the
559    /// [Duration] has passed.
560    ///
561    /// [poll]: #method.poll
562    /// [Duration]: struct.Duration.html
563    pub fn poll_delay(&mut self, timestamp: Instant, sockets: &SocketSet<'_>) -> Option<Duration> {
564        match self.poll_at(timestamp, sockets) {
565            Some(poll_at) if timestamp < poll_at => Some(poll_at - timestamp),
566            Some(_) => Some(Duration::from_millis(0)),
567            _ => None,
568        }
569    }
570
571    fn socket_ingress(
572        &mut self,
573        device: &mut (impl Device + ?Sized),
574        sockets: &mut SocketSet<'_>,
575    ) -> PollIngressSingleResult {
576        let Some((rx_token, tx_token)) = device.receive(self.inner.now) else {
577            return PollIngressSingleResult::None;
578        };
579
580        let rx_meta = rx_token.meta();
581        rx_token.consume(|frame| {
582            if frame.is_empty() {
583                return PollIngressSingleResult::PacketProcessed;
584            }
585
586            match self.inner.caps.medium {
587                #[cfg(feature = "medium-ethernet")]
588                Medium::Ethernet => {
589                    if let Some(packet) =
590                        self.inner
591                            .process_ethernet(sockets, rx_meta, frame, &mut self.fragments)
592                    {
593                        if let Err(err) =
594                            self.inner.dispatch(tx_token, packet, &mut self.fragmenter)
595                        {
596                            net_debug!("Failed to send response: {:?}", err);
597                        }
598                    }
599                }
600                #[cfg(feature = "medium-ip")]
601                Medium::Ip => {
602                    if let Some(packet) =
603                        self.inner
604                            .process_ip(sockets, rx_meta, frame, &mut self.fragments)
605                    {
606                        if let Err(err) = self.inner.dispatch_ip(
607                            tx_token,
608                            PacketMeta::default(),
609                            packet,
610                            &mut self.fragmenter,
611                        ) {
612                            net_debug!("Failed to send response: {:?}", err);
613                        }
614                    }
615                }
616                #[cfg(feature = "medium-ieee802154")]
617                Medium::Ieee802154 => {
618                    if let Some(packet) =
619                        self.inner
620                            .process_ieee802154(sockets, rx_meta, frame, &mut self.fragments)
621                    {
622                        if let Err(err) = self.inner.dispatch_ip(
623                            tx_token,
624                            PacketMeta::default(),
625                            packet,
626                            &mut self.fragmenter,
627                        ) {
628                            net_debug!("Failed to send response: {:?}", err);
629                        }
630                    }
631                }
632            }
633
634            // TODO: Propagate the PollIngressSingleResult from deeper.
635            // There's many received packets that we process but can't cause sockets
636            // to change state. For example IP fragments, multicast stuff, ICMP pings
637            // if they dont't match any raw socket...
638            // We should return `PacketProcessed` for these to save the user from
639            // doing useless socket polls.
640            PollIngressSingleResult::SocketStateChanged
641        })
642    }
643
644    fn socket_egress(
645        &mut self,
646        device: &mut (impl Device + ?Sized),
647        sockets: &mut SocketSet<'_>,
648    ) -> PollResult {
649        let _caps = device.capabilities();
650
651        enum EgressError {
652            Exhausted,
653            Dispatch,
654        }
655
656        let mut result = PollResult::None;
657        for item in sockets.items_mut() {
658            if !item
659                .meta
660                .egress_permitted(self.inner.now, |ip_addr| self.inner.has_neighbor(&ip_addr))
661            {
662                continue;
663            }
664
665            let mut neighbor_addr = None;
666            let mut respond = |inner: &mut InterfaceInner, meta: PacketMeta, response: Packet| {
667                neighbor_addr = Some(response.ip_repr().dst_addr());
668                let t = device.transmit(inner.now).ok_or_else(|| {
669                    net_debug!("failed to transmit IP: device exhausted");
670                    EgressError::Exhausted
671                })?;
672
673                inner
674                    .dispatch_ip(t, meta, response, &mut self.fragmenter)
675                    .map_err(|_| EgressError::Dispatch)?;
676
677                result = PollResult::SocketStateChanged;
678
679                Ok(())
680            };
681
682            let result = match &mut item.socket {
683                #[cfg(feature = "socket-raw")]
684                Socket::Raw(socket) => socket.dispatch(&mut self.inner, |inner, (ip, raw)| {
685                    respond(
686                        inner,
687                        PacketMeta::default(),
688                        Packet::new(ip, IpPayload::Raw(raw)),
689                    )
690                }),
691                #[cfg(feature = "socket-icmp")]
692                Socket::Icmp(socket) => {
693                    socket.dispatch(&mut self.inner, |inner, response| match response {
694                        #[cfg(feature = "proto-ipv4")]
695                        (IpRepr::Ipv4(ipv4_repr), IcmpRepr::Ipv4(icmpv4_repr)) => respond(
696                            inner,
697                            PacketMeta::default(),
698                            Packet::new_ipv4(ipv4_repr, IpPayload::Icmpv4(icmpv4_repr)),
699                        ),
700                        #[cfg(feature = "proto-ipv6")]
701                        (IpRepr::Ipv6(ipv6_repr), IcmpRepr::Ipv6(icmpv6_repr)) => respond(
702                            inner,
703                            PacketMeta::default(),
704                            Packet::new_ipv6(ipv6_repr, IpPayload::Icmpv6(icmpv6_repr)),
705                        ),
706                        #[allow(unreachable_patterns)]
707                        _ => unreachable!(),
708                    })
709                }
710                #[cfg(feature = "socket-udp")]
711                Socket::Udp(socket) => {
712                    socket.dispatch(&mut self.inner, |inner, meta, (ip, udp, payload)| {
713                        respond(inner, meta, Packet::new(ip, IpPayload::Udp(udp, payload)))
714                    })
715                }
716                #[cfg(feature = "socket-tcp")]
717                Socket::Tcp(socket) => socket.dispatch(&mut self.inner, |inner, (ip, tcp)| {
718                    respond(
719                        inner,
720                        PacketMeta::default(),
721                        Packet::new(ip, IpPayload::Tcp(tcp)),
722                    )
723                }),
724                #[cfg(feature = "socket-dhcpv4")]
725                Socket::Dhcpv4(socket) => {
726                    socket.dispatch(&mut self.inner, |inner, (ip, udp, dhcp)| {
727                        respond(
728                            inner,
729                            PacketMeta::default(),
730                            Packet::new_ipv4(ip, IpPayload::Dhcpv4(udp, dhcp)),
731                        )
732                    })
733                }
734                #[cfg(feature = "socket-dns")]
735                Socket::Dns(socket) => socket.dispatch(&mut self.inner, |inner, (ip, udp, dns)| {
736                    respond(
737                        inner,
738                        PacketMeta::default(),
739                        Packet::new(ip, IpPayload::Udp(udp, dns)),
740                    )
741                }),
742            };
743
744            match result {
745                Err(EgressError::Exhausted) => break, // Device buffer full.
746                Err(EgressError::Dispatch) => {
747                    // `NeighborCache` already takes care of rate limiting the neighbor discovery
748                    // requests from the socket. However, without an additional rate limiting
749                    // mechanism, we would spin on every socket that has yet to discover its
750                    // neighbor.
751                    item.meta.neighbor_missing(
752                        self.inner.now,
753                        neighbor_addr.expect("non-IP response packet"),
754                    );
755                }
756                Ok(()) => {}
757            }
758        }
759        result
760    }
761}
762
763impl InterfaceInner {
764    #[allow(unused)] // unused depending on which sockets are enabled
765    pub(crate) fn now(&self) -> Instant {
766        self.now
767    }
768
769    #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
770    #[allow(unused)] // unused depending on which sockets are enabled
771    pub(crate) fn hardware_addr(&self) -> HardwareAddress {
772        self.hardware_addr
773    }
774
775    #[allow(unused)] // unused depending on which sockets are enabled
776    pub(crate) fn checksum_caps(&self) -> ChecksumCapabilities {
777        self.caps.checksum.clone()
778    }
779
780    #[allow(unused)] // unused depending on which sockets are enabled
781    pub(crate) fn ip_mtu(&self) -> usize {
782        self.caps.ip_mtu()
783    }
784
785    #[allow(unused)] // unused depending on which sockets are enabled, and in tests
786    pub(crate) fn rand(&mut self) -> &mut Rand {
787        &mut self.rand
788    }
789
790    #[allow(unused)] // unused depending on which sockets are enabled
791    pub(crate) fn get_source_address(&self, dst_addr: &IpAddress) -> Option<IpAddress> {
792        match dst_addr {
793            #[cfg(feature = "proto-ipv4")]
794            IpAddress::Ipv4(addr) => self.get_source_address_ipv4(addr).map(|a| a.into()),
795            #[cfg(feature = "proto-ipv6")]
796            IpAddress::Ipv6(addr) => Some(self.get_source_address_ipv6(addr).into()),
797        }
798    }
799
800    #[cfg(test)]
801    #[allow(unused)] // unused depending on which sockets are enabled
802    pub(crate) fn set_now(&mut self, now: Instant) {
803        self.now = now
804    }
805
806    #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
807    fn check_hardware_addr(addr: &HardwareAddress) {
808        if !addr.is_unicast() {
809            panic!("Hardware address {addr} is not unicast")
810        }
811    }
812
813    fn check_ip_addrs(addrs: &[IpCidr]) {
814        for cidr in addrs {
815            if !cidr.address().is_unicast() && !cidr.address().is_unspecified() {
816                panic!("IP address {} is not unicast", cidr.address())
817            }
818        }
819    }
820
821    /// Check whether the interface has the given IP address assigned.
822    fn has_ip_addr<T: Into<IpAddress>>(&self, addr: T) -> bool {
823        let addr = addr.into();
824        self.ip_addrs.iter().any(|probe| probe.address() == addr)
825    }
826
827    /// Check whether the interface listens to given destination multicast IP address.
828    fn has_multicast_group<T: Into<IpAddress>>(&self, addr: T) -> bool {
829        let addr = addr.into();
830
831        #[cfg(feature = "multicast")]
832        if self.multicast.has_multicast_group(addr) {
833            return true;
834        }
835
836        match addr {
837            #[cfg(feature = "proto-ipv4")]
838            IpAddress::Ipv4(key) => key == IPV4_MULTICAST_ALL_SYSTEMS,
839            #[cfg(feature = "proto-rpl")]
840            IpAddress::Ipv6(IPV6_LINK_LOCAL_ALL_RPL_NODES) => true,
841            #[cfg(feature = "proto-ipv6")]
842            IpAddress::Ipv6(key) => {
843                key == IPV6_LINK_LOCAL_ALL_NODES || self.has_solicited_node(key)
844            }
845            #[allow(unreachable_patterns)]
846            _ => false,
847        }
848    }
849
850    #[cfg(feature = "medium-ip")]
851    fn process_ip<'frame>(
852        &mut self,
853        sockets: &mut SocketSet,
854        meta: PacketMeta,
855        ip_payload: &'frame [u8],
856        frag: &'frame mut FragmentsBuffer,
857    ) -> Option<Packet<'frame>> {
858        match IpVersion::of_packet(ip_payload) {
859            #[cfg(feature = "proto-ipv4")]
860            Ok(IpVersion::Ipv4) => {
861                let ipv4_packet = check!(Ipv4Packet::new_checked(ip_payload));
862                self.process_ipv4(sockets, meta, HardwareAddress::Ip, &ipv4_packet, frag)
863            }
864            #[cfg(feature = "proto-ipv6")]
865            Ok(IpVersion::Ipv6) => {
866                let ipv6_packet = check!(Ipv6Packet::new_checked(ip_payload));
867                self.process_ipv6(sockets, meta, HardwareAddress::Ip, &ipv6_packet)
868            }
869            // Drop all other traffic.
870            _ => None,
871        }
872    }
873
874    #[cfg(feature = "socket-raw")]
875    fn raw_socket_filter(
876        &mut self,
877        sockets: &mut SocketSet,
878        ip_repr: &IpRepr,
879        ip_payload: &[u8],
880    ) -> bool {
881        let mut handled_by_raw_socket = false;
882
883        // Pass every IP packet to all raw sockets we have registered.
884        for raw_socket in sockets
885            .items_mut()
886            .filter_map(|i| raw::Socket::downcast_mut(&mut i.socket))
887        {
888            if raw_socket.accepts(ip_repr) {
889                raw_socket.process(self, ip_repr, ip_payload);
890                handled_by_raw_socket = true;
891            }
892        }
893        handled_by_raw_socket
894    }
895
896    /// Checks if an address is broadcast, taking into account ipv4 subnet-local
897    /// broadcast addresses.
898    pub(crate) fn is_broadcast(&self, address: &IpAddress) -> bool {
899        match address {
900            #[cfg(feature = "proto-ipv4")]
901            IpAddress::Ipv4(address) => self.is_broadcast_v4(*address),
902            #[cfg(feature = "proto-ipv6")]
903            IpAddress::Ipv6(_) => false,
904        }
905    }
906
907    #[cfg(feature = "medium-ethernet")]
908    fn dispatch<Tx>(
909        &mut self,
910        tx_token: Tx,
911        packet: EthernetPacket,
912        frag: &mut Fragmenter,
913    ) -> Result<(), DispatchError>
914    where
915        Tx: TxToken,
916    {
917        match packet {
918            #[cfg(feature = "proto-ipv4")]
919            EthernetPacket::Arp(arp_repr) => {
920                let dst_hardware_addr = match arp_repr {
921                    ArpRepr::EthernetIpv4 {
922                        target_hardware_addr,
923                        ..
924                    } => target_hardware_addr,
925                };
926
927                self.dispatch_ethernet(tx_token, arp_repr.buffer_len(), |mut frame| {
928                    frame.set_dst_addr(dst_hardware_addr);
929                    frame.set_ethertype(EthernetProtocol::Arp);
930
931                    let mut packet = ArpPacket::new_unchecked(frame.payload_mut());
932                    arp_repr.emit(&mut packet);
933                })
934            }
935            EthernetPacket::Ip(packet) => {
936                self.dispatch_ip(tx_token, PacketMeta::default(), packet, frag)
937            }
938        }
939    }
940
941    fn in_same_network(&self, addr: &IpAddress) -> bool {
942        self.ip_addrs.iter().any(|cidr| cidr.contains_addr(addr))
943    }
944
945    fn route(&self, addr: &IpAddress, timestamp: Instant) -> Option<IpAddress> {
946        // Send directly.
947        // note: no need to use `self.is_broadcast()` to check for subnet-local broadcast addrs
948        //       here because `in_same_network` will already return true.
949        if self.in_same_network(addr) || addr.is_broadcast() {
950            return Some(*addr);
951        }
952
953        // Route via a router.
954        self.routes.lookup(addr, timestamp)
955    }
956
957    fn has_neighbor(&self, addr: &IpAddress) -> bool {
958        match self.route(addr, self.now) {
959            Some(_routed_addr) => match self.caps.medium {
960                #[cfg(feature = "medium-ethernet")]
961                Medium::Ethernet => self.neighbor_cache.lookup(&_routed_addr, self.now).found(),
962                #[cfg(feature = "medium-ieee802154")]
963                Medium::Ieee802154 => self.neighbor_cache.lookup(&_routed_addr, self.now).found(),
964                #[cfg(feature = "medium-ip")]
965                Medium::Ip => true,
966            },
967            None => false,
968        }
969    }
970
971    #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
972    fn lookup_hardware_addr<Tx>(
973        &mut self,
974        tx_token: Tx,
975        dst_addr: &IpAddress,
976        fragmenter: &mut Fragmenter,
977    ) -> Result<(HardwareAddress, Tx), DispatchError>
978    where
979        Tx: TxToken,
980    {
981        if self.is_broadcast(dst_addr) {
982            let hardware_addr = match self.caps.medium {
983                #[cfg(feature = "medium-ethernet")]
984                Medium::Ethernet => HardwareAddress::Ethernet(EthernetAddress::BROADCAST),
985                #[cfg(feature = "medium-ieee802154")]
986                Medium::Ieee802154 => HardwareAddress::Ieee802154(Ieee802154Address::BROADCAST),
987                #[cfg(feature = "medium-ip")]
988                Medium::Ip => unreachable!(),
989            };
990
991            return Ok((hardware_addr, tx_token));
992        }
993
994        if dst_addr.is_multicast() {
995            let hardware_addr = match *dst_addr {
996                #[cfg(feature = "proto-ipv4")]
997                IpAddress::Ipv4(addr) => match self.caps.medium {
998                    #[cfg(feature = "medium-ethernet")]
999                    Medium::Ethernet => {
1000                        let b = addr.octets();
1001                        HardwareAddress::Ethernet(EthernetAddress::from_bytes(&[
1002                            0x01,
1003                            0x00,
1004                            0x5e,
1005                            b[1] & 0x7F,
1006                            b[2],
1007                            b[3],
1008                        ]))
1009                    }
1010                    #[cfg(feature = "medium-ieee802154")]
1011                    Medium::Ieee802154 => unreachable!(),
1012                    #[cfg(feature = "medium-ip")]
1013                    Medium::Ip => unreachable!(),
1014                },
1015                #[cfg(feature = "proto-ipv6")]
1016                IpAddress::Ipv6(addr) => match self.caps.medium {
1017                    #[cfg(feature = "medium-ethernet")]
1018                    Medium::Ethernet => {
1019                        let b = addr.octets();
1020                        HardwareAddress::Ethernet(EthernetAddress::from_bytes(&[
1021                            0x33, 0x33, b[12], b[13], b[14], b[15],
1022                        ]))
1023                    }
1024                    #[cfg(feature = "medium-ieee802154")]
1025                    Medium::Ieee802154 => {
1026                        // Not sure if this is correct
1027                        HardwareAddress::Ieee802154(Ieee802154Address::BROADCAST)
1028                    }
1029                    #[cfg(feature = "medium-ip")]
1030                    Medium::Ip => unreachable!(),
1031                },
1032            };
1033
1034            return Ok((hardware_addr, tx_token));
1035        }
1036
1037        let dst_addr = self
1038            .route(dst_addr, self.now)
1039            .ok_or(DispatchError::NoRoute)?;
1040
1041        match self.neighbor_cache.lookup(&dst_addr, self.now) {
1042            NeighborAnswer::Found(hardware_addr) => return Ok((hardware_addr, tx_token)),
1043            NeighborAnswer::RateLimited => return Err(DispatchError::NeighborPending),
1044            _ => (), // XXX
1045        }
1046
1047        match dst_addr {
1048            #[cfg(all(feature = "medium-ethernet", feature = "proto-ipv4"))]
1049            IpAddress::Ipv4(dst_addr) if matches!(self.caps.medium, Medium::Ethernet) => {
1050                net_debug!(
1051                    "address {} not in neighbor cache, sending ARP request",
1052                    dst_addr
1053                );
1054                let src_hardware_addr = self.hardware_addr.ethernet_or_panic();
1055
1056                let arp_repr = ArpRepr::EthernetIpv4 {
1057                    operation: ArpOperation::Request,
1058                    source_hardware_addr: src_hardware_addr,
1059                    source_protocol_addr: self
1060                        .get_source_address_ipv4(&dst_addr)
1061                        .ok_or(DispatchError::NoRoute)?,
1062                    target_hardware_addr: EthernetAddress::BROADCAST,
1063                    target_protocol_addr: dst_addr,
1064                };
1065
1066                if let Err(e) =
1067                    self.dispatch_ethernet(tx_token, arp_repr.buffer_len(), |mut frame| {
1068                        frame.set_dst_addr(EthernetAddress::BROADCAST);
1069                        frame.set_ethertype(EthernetProtocol::Arp);
1070
1071                        arp_repr.emit(&mut ArpPacket::new_unchecked(frame.payload_mut()))
1072                    })
1073                {
1074                    net_debug!("Failed to dispatch ARP request: {:?}", e);
1075                    return Err(DispatchError::NeighborPending);
1076                }
1077            }
1078
1079            #[cfg(feature = "proto-ipv6")]
1080            IpAddress::Ipv6(dst_addr) => {
1081                net_debug!(
1082                    "address {} not in neighbor cache, sending Neighbor Solicitation",
1083                    dst_addr
1084                );
1085
1086                let solicit = Icmpv6Repr::Ndisc(NdiscRepr::NeighborSolicit {
1087                    target_addr: dst_addr,
1088                    lladdr: Some(self.hardware_addr.into()),
1089                });
1090
1091                let packet = Packet::new_ipv6(
1092                    Ipv6Repr {
1093                        src_addr: self.get_source_address_ipv6(&dst_addr),
1094                        dst_addr: dst_addr.solicited_node(),
1095                        next_header: IpProtocol::Icmpv6,
1096                        payload_len: solicit.buffer_len(),
1097                        hop_limit: 0xff,
1098                    },
1099                    IpPayload::Icmpv6(solicit),
1100                );
1101
1102                if let Err(e) =
1103                    self.dispatch_ip(tx_token, PacketMeta::default(), packet, fragmenter)
1104                {
1105                    net_debug!("Failed to dispatch NDISC solicit: {:?}", e);
1106                    return Err(DispatchError::NeighborPending);
1107                }
1108            }
1109
1110            #[allow(unreachable_patterns)]
1111            _ => (),
1112        }
1113
1114        // The request got dispatched, limit the rate on the cache.
1115        self.neighbor_cache.limit_rate(self.now);
1116        Err(DispatchError::NeighborPending)
1117    }
1118
1119    fn flush_neighbor_cache(&mut self) {
1120        #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
1121        self.neighbor_cache.flush()
1122    }
1123
1124    fn dispatch_ip<Tx: TxToken>(
1125        &mut self,
1126        // NOTE(unused_mut): tx_token isn't always mutated, depending on
1127        // the feature set that is used.
1128        #[allow(unused_mut)] mut tx_token: Tx,
1129        meta: PacketMeta,
1130        packet: Packet,
1131        frag: &mut Fragmenter,
1132    ) -> Result<(), DispatchError> {
1133        let mut ip_repr = packet.ip_repr();
1134        assert!(!ip_repr.dst_addr().is_unspecified());
1135
1136        // Dispatch IEEE802.15.4:
1137
1138        #[cfg(feature = "medium-ieee802154")]
1139        if matches!(self.caps.medium, Medium::Ieee802154) {
1140            let (addr, tx_token) =
1141                self.lookup_hardware_addr(tx_token, &ip_repr.dst_addr(), frag)?;
1142            let addr = addr.ieee802154_or_panic();
1143
1144            self.dispatch_ieee802154(addr, tx_token, meta, packet, frag);
1145            return Ok(());
1146        }
1147
1148        // Dispatch IP/Ethernet:
1149
1150        let caps = self.caps.clone();
1151
1152        #[cfg(feature = "proto-ipv4-fragmentation")]
1153        let ipv4_id = self.next_ipv4_frag_ident();
1154
1155        // First we calculate the total length that we will have to emit.
1156        let mut total_len = ip_repr.buffer_len();
1157
1158        // Add the size of the Ethernet header if the medium is Ethernet.
1159        #[cfg(feature = "medium-ethernet")]
1160        if matches!(self.caps.medium, Medium::Ethernet) {
1161            total_len = EthernetFrame::<&[u8]>::buffer_len(total_len);
1162        }
1163
1164        // If the medium is Ethernet, then we need to retrieve the destination hardware address.
1165        #[cfg(feature = "medium-ethernet")]
1166        let (dst_hardware_addr, mut tx_token) = match self.caps.medium {
1167            Medium::Ethernet => {
1168                match self.lookup_hardware_addr(tx_token, &ip_repr.dst_addr(), frag)? {
1169                    (HardwareAddress::Ethernet(addr), tx_token) => (addr, tx_token),
1170                    (_, _) => unreachable!(),
1171                }
1172            }
1173            _ => (EthernetAddress([0; 6]), tx_token),
1174        };
1175
1176        // Emit function for the Ethernet header.
1177        #[cfg(feature = "medium-ethernet")]
1178        let emit_ethernet = |repr: &IpRepr, tx_buffer: &mut [u8]| {
1179            let mut frame = EthernetFrame::new_unchecked(tx_buffer);
1180
1181            let src_addr = self.hardware_addr.ethernet_or_panic();
1182            frame.set_src_addr(src_addr);
1183            frame.set_dst_addr(dst_hardware_addr);
1184
1185            match repr.version() {
1186                #[cfg(feature = "proto-ipv4")]
1187                IpVersion::Ipv4 => frame.set_ethertype(EthernetProtocol::Ipv4),
1188                #[cfg(feature = "proto-ipv6")]
1189                IpVersion::Ipv6 => frame.set_ethertype(EthernetProtocol::Ipv6),
1190            }
1191
1192            Ok(())
1193        };
1194
1195        // Emit function for the IP header and payload.
1196        let emit_ip = |repr: &IpRepr, tx_buffer: &mut [u8]| {
1197            repr.emit(&mut *tx_buffer, &self.caps.checksum);
1198
1199            let payload = &mut tx_buffer[repr.header_len()..];
1200            packet.emit_payload(repr, payload, &caps)
1201        };
1202
1203        let total_ip_len = ip_repr.buffer_len();
1204
1205        match &mut ip_repr {
1206            #[cfg(feature = "proto-ipv4")]
1207            IpRepr::Ipv4(repr) => {
1208                // If we have an IPv4 packet, then we need to check if we need to fragment it.
1209                if total_ip_len > self.caps.ip_mtu() {
1210                    #[cfg(feature = "proto-ipv4-fragmentation")]
1211                    {
1212                        net_debug!("start fragmentation");
1213
1214                        // Calculate how much we will send now (including the Ethernet header).
1215                        let tx_len = self.caps.max_transmission_unit;
1216
1217                        let ip_header_len = repr.buffer_len();
1218                        let first_frag_ip_len = self.caps.ip_mtu();
1219
1220                        if frag.buffer.len() < total_ip_len {
1221                            net_debug!(
1222                                "Fragmentation buffer is too small, at least {} needed. Dropping",
1223                                total_ip_len
1224                            );
1225                            return Ok(());
1226                        }
1227
1228                        #[cfg(feature = "medium-ethernet")]
1229                        {
1230                            frag.ipv4.dst_hardware_addr = dst_hardware_addr;
1231                        }
1232
1233                        // Save the total packet len (without the Ethernet header, but with the first
1234                        // IP header).
1235                        frag.packet_len = total_ip_len;
1236
1237                        // Save the IP header for other fragments.
1238                        frag.ipv4.repr = *repr;
1239
1240                        // Save how much bytes we will send now.
1241                        frag.sent_bytes = first_frag_ip_len;
1242
1243                        // Modify the IP header
1244                        repr.payload_len = first_frag_ip_len - repr.buffer_len();
1245
1246                        // Emit the IP header to the buffer.
1247                        emit_ip(&ip_repr, &mut frag.buffer);
1248
1249                        let mut ipv4_packet = Ipv4Packet::new_unchecked(&mut frag.buffer[..]);
1250                        frag.ipv4.ident = ipv4_id;
1251                        ipv4_packet.set_ident(ipv4_id);
1252                        ipv4_packet.set_more_frags(true);
1253                        ipv4_packet.set_dont_frag(false);
1254                        ipv4_packet.set_frag_offset(0);
1255
1256                        if caps.checksum.ipv4.tx() {
1257                            ipv4_packet.fill_checksum();
1258                        }
1259
1260                        // Transmit the first packet.
1261                        tx_token.consume(tx_len, |mut tx_buffer| {
1262                            #[cfg(feature = "medium-ethernet")]
1263                            if matches!(self.caps.medium, Medium::Ethernet) {
1264                                emit_ethernet(&ip_repr, tx_buffer)?;
1265                                tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..];
1266                            }
1267
1268                            // Change the offset for the next packet.
1269                            frag.ipv4.frag_offset = (first_frag_ip_len - ip_header_len) as u16;
1270
1271                            // Copy the IP header and the payload.
1272                            tx_buffer[..first_frag_ip_len]
1273                                .copy_from_slice(&frag.buffer[..first_frag_ip_len]);
1274
1275                            Ok(())
1276                        })
1277                    }
1278
1279                    #[cfg(not(feature = "proto-ipv4-fragmentation"))]
1280                    {
1281                        net_debug!("Enable the `proto-ipv4-fragmentation` feature for fragmentation support.");
1282                        Ok(())
1283                    }
1284                } else {
1285                    tx_token.set_meta(meta);
1286
1287                    // No fragmentation is required.
1288                    tx_token.consume(total_len, |mut tx_buffer| {
1289                        #[cfg(feature = "medium-ethernet")]
1290                        if matches!(self.caps.medium, Medium::Ethernet) {
1291                            emit_ethernet(&ip_repr, tx_buffer)?;
1292                            tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..];
1293                        }
1294
1295                        emit_ip(&ip_repr, tx_buffer);
1296                        Ok(())
1297                    })
1298                }
1299            }
1300            // We don't support IPv6 fragmentation yet.
1301            #[cfg(feature = "proto-ipv6")]
1302            IpRepr::Ipv6(_) => tx_token.consume(total_len, |mut tx_buffer| {
1303                #[cfg(feature = "medium-ethernet")]
1304                if matches!(self.caps.medium, Medium::Ethernet) {
1305                    emit_ethernet(&ip_repr, tx_buffer)?;
1306                    tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..];
1307                }
1308
1309                emit_ip(&ip_repr, tx_buffer);
1310                Ok(())
1311            }),
1312        }
1313    }
1314}
1315
1316#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1317#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1318enum DispatchError {
1319    /// No route to dispatch this packet. Retrying won't help unless
1320    /// configuration is changed.
1321    NoRoute,
1322    /// We do have a route to dispatch this packet, but we haven't discovered
1323    /// the neighbor for it yet. Discovery has been initiated, dispatch
1324    /// should be retried later.
1325    NeighborPending,
1326}