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