Skip to main content

smoltcp/phy/
mod.rs

1/*! Access to networking hardware.
2
3The `phy` module deals with the *network devices*. It provides a trait
4for transmitting and receiving frames, [Device](trait.Device.html)
5and implementations of it:
6
7  * the [_loopback_](struct.Loopback.html), for zero dependency testing;
8  * _middleware_ [Tracer](struct.Tracer.html) and
9    [FaultInjector](struct.FaultInjector.html), to facilitate debugging;
10  * _adapters_ [RawSocket](struct.RawSocket.html) and
11    [TunTapInterface](struct.TunTapInterface.html), to transmit and receive frames
12    on the host OS.
13*/
14#![cfg_attr(
15    feature = "medium-ethernet",
16    doc = r##"
17# Examples
18
19An implementation of the [Device](trait.Device.html) trait for a simple hardware
20Ethernet controller could look as follows:
21
22```rust
23use smoltcp::phy::{self, DeviceCapabilities, Device, Medium};
24use smoltcp::time::Instant;
25
26struct StmPhy {
27    rx_buffer: [u8; 1536],
28    tx_buffer: [u8; 1536],
29}
30
31impl<'a> StmPhy {
32    fn new() -> StmPhy {
33        StmPhy {
34            rx_buffer: [0; 1536],
35            tx_buffer: [0; 1536],
36        }
37    }
38}
39
40impl phy::Device for StmPhy {
41    type RxToken<'a> = StmPhyRxToken<'a> where Self: 'a;
42    type TxToken<'a> = StmPhyTxToken<'a> where Self: 'a;
43
44    fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
45        Some((StmPhyRxToken(&mut self.rx_buffer[..]),
46              StmPhyTxToken(&mut self.tx_buffer[..])))
47    }
48
49    fn transmit(&mut self, _timestamp: Instant) -> Option<Self::TxToken<'_>> {
50        Some(StmPhyTxToken(&mut self.tx_buffer[..]))
51    }
52
53    fn capabilities(&self) -> DeviceCapabilities {
54        let mut caps = DeviceCapabilities::default();
55        caps.max_transmission_unit = 1536;
56        caps.max_burst_size = Some(1);
57        caps.medium = Medium::Ethernet;
58        caps
59    }
60}
61
62struct StmPhyRxToken<'a>(&'a mut [u8]);
63
64impl<'a> phy::RxToken for StmPhyRxToken<'a> {
65    fn consume<R, F>(self, f: F) -> R
66        where F: FnOnce(& [u8]) -> R
67    {
68        // TODO: receive packet into buffer
69        let result = f(&self.0);
70        println!("rx called");
71        result
72    }
73}
74
75struct StmPhyTxToken<'a>(&'a mut [u8]);
76
77impl<'a> phy::TxToken for StmPhyTxToken<'a> {
78    fn consume<R, F>(self, len: usize, f: F) -> R
79        where F: FnOnce(&mut [u8]) -> R
80    {
81        let result = f(&mut self.0[..len]);
82        println!("tx called {}", len);
83        // TODO: send packet out
84        result
85    }
86}
87```
88"##
89)]
90
91use crate::time::Instant;
92#[cfg(feature = "segmentation-offload")]
93use core::num::{NonZeroU16, NonZeroUsize};
94
95#[cfg(all(
96    any(feature = "phy-raw_socket", feature = "phy-tuntap_interface"),
97    unix
98))]
99mod sys;
100
101mod fault_injector;
102#[cfg(feature = "alloc")]
103mod fuzz_injector;
104#[cfg(feature = "alloc")]
105mod loopback;
106mod pcap_writer;
107#[cfg(all(feature = "phy-raw_socket", unix))]
108mod raw_socket;
109mod tracer;
110#[cfg(all(
111    feature = "phy-tuntap_interface",
112    any(target_os = "linux", target_os = "android")
113))]
114mod tuntap_interface;
115
116#[cfg(all(
117    any(feature = "phy-raw_socket", feature = "phy-tuntap_interface"),
118    unix
119))]
120pub use self::sys::wait;
121
122pub use self::fault_injector::FaultInjector;
123#[cfg(feature = "alloc")]
124pub use self::fuzz_injector::{FuzzInjector, Fuzzer};
125#[cfg(feature = "alloc")]
126pub use self::loopback::Loopback;
127pub use self::pcap_writer::{PcapLinkType, PcapMode, PcapSink, PcapWriter};
128#[cfg(all(feature = "phy-raw_socket", unix))]
129pub use self::raw_socket::RawSocket;
130pub use self::tracer::{Tracer, TracerDirection, TracerPacket};
131#[cfg(all(
132    feature = "phy-tuntap_interface",
133    any(target_os = "linux", target_os = "android")
134))]
135pub use self::tuntap_interface::TunTapInterface;
136
137/// The IPV4 payload fragment size must be an increment of this value.
138#[cfg(feature = "proto-ipv4-fragmentation")]
139pub const IPV4_FRAGMENT_PAYLOAD_ALIGNMENT: usize = 8;
140
141/// Metadata associated to a packet.
142///
143/// The packet metadata is a set of attributes associated to network packets
144/// as they travel up or down the stack. The metadata is get/set by the
145/// [`Device`] implementations or by the user when sending/receiving packets from a
146/// socket.
147///
148/// Metadata fields are enabled via Cargo features. If no field is enabled, this
149/// struct becomes zero-sized, which allows the compiler to optimize it out as if
150/// the packet metadata mechanism didn't exist at all.
151///
152/// Currently only TCP and UDP sockets allow setting/retrieving packet metadata. The metadata
153/// for packets emitted with other sockets will be all default values.
154///
155/// This struct is marked as `#[non_exhaustive]`. This means it is not possible to
156/// create it directly by specifying all fields. You have to instead create it with
157/// default values and then set the fields you want. This makes adding metadata
158/// fields a non-breaking change.
159///
160/// ```rust
161/// let mut meta = smoltcp::phy::PacketMeta::default();
162/// #[cfg(feature = "packetmeta-id")]
163/// {
164///     meta.id = 15;
165/// }
166/// ```
167#[cfg_attr(feature = "defmt", derive(defmt::Format))]
168#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Default)]
169#[non_exhaustive]
170pub struct PacketMeta {
171    #[cfg(feature = "packetmeta-id")]
172    pub id: u32,
173
174    /// Segmentation offload size.
175    ///
176    /// If the network device advertised support for segmentation offload, the
177    /// stack can request that the device segments the provided packet into
178    /// segments of this size. The size does not include the headers that will
179    /// be replicated across the segments (e.g. TCP, IP, Ethernet headers).
180    ///
181    /// If `None`, no segmentation will be performed by the device.
182    #[cfg(feature = "segmentation-offload")]
183    pub segmentation_offload_size: Option<NonZeroU16>,
184}
185
186/// A description of checksum behavior for a particular protocol.
187#[derive(Debug, Clone, Copy, Default)]
188#[cfg_attr(feature = "defmt", derive(defmt::Format))]
189pub enum Checksum {
190    /// Verify checksum when receiving and compute checksum when sending.
191    #[default]
192    Both,
193    /// Verify checksum when receiving.
194    Rx,
195    /// Compute checksum before sending.
196    Tx,
197    /// Ignore checksum completely.
198    None,
199}
200
201impl Checksum {
202    /// Returns whether checksum should be verified when receiving.
203    pub fn rx(&self) -> bool {
204        match *self {
205            Checksum::Both | Checksum::Rx => true,
206            _ => false,
207        }
208    }
209
210    /// Returns whether checksum should be verified when sending.
211    pub fn tx(&self) -> bool {
212        match *self {
213            Checksum::Both | Checksum::Tx => true,
214            _ => false,
215        }
216    }
217}
218
219/// A description of checksum behavior for every supported protocol.
220#[derive(Debug, Clone, Default)]
221#[cfg_attr(feature = "defmt", derive(defmt::Format))]
222#[non_exhaustive]
223pub struct ChecksumCapabilities {
224    pub ipv4: Checksum,
225    pub udp: Checksum,
226    pub tcp: Checksum,
227    #[cfg(feature = "proto-ipv4")]
228    pub icmpv4: Checksum,
229    #[cfg(feature = "proto-ipv6")]
230    pub icmpv6: Checksum,
231}
232
233impl ChecksumCapabilities {
234    /// Checksum behavior that results in not computing or verifying checksums
235    /// for any of the supported protocols.
236    pub fn ignored() -> Self {
237        ChecksumCapabilities {
238            ipv4: Checksum::None,
239            udp: Checksum::None,
240            tcp: Checksum::None,
241            #[cfg(feature = "proto-ipv4")]
242            icmpv4: Checksum::None,
243            #[cfg(feature = "proto-ipv6")]
244            icmpv6: Checksum::None,
245        }
246    }
247}
248
249/// The maximum buffer size for a particular protocol or protocol pair that
250/// can be offloaded to the device for segmentation, or [None] if segmentation
251/// offload is not supported.
252///
253/// For Ethernet devices, this includes the Ethernet header (14 octets), but
254/// *not* the Ethernet FCS (4 octets).
255///
256/// If the device supports unsegmented IP packets with (depending on the IP
257/// version, total or payload) lengths greater than [u16::MAX], it should not
258/// rely on the length field in the IP header, as the actual length cannot be
259/// represented there. The value will be 0 instead.
260#[derive(Debug, Clone, Default)]
261#[cfg_attr(feature = "defmt", derive(defmt::Format))]
262#[non_exhaustive]
263#[cfg(feature = "segmentation-offload")]
264pub struct SegmentationCapabilities {
265    #[cfg(all(feature = "socket-tcp", feature = "proto-ipv4"))]
266    pub tcpv4: Option<NonZeroUsize>,
267    #[cfg(all(feature = "socket-tcp", feature = "proto-ipv6"))]
268    pub tcpv6: Option<NonZeroUsize>,
269}
270
271/// A description of device capabilities.
272///
273/// Higher-level protocols may achieve higher throughput or lower latency if they consider
274/// the bandwidth or packet size limitations.
275#[derive(Debug, Clone, Default)]
276#[cfg_attr(feature = "defmt", derive(defmt::Format))]
277#[non_exhaustive]
278pub struct DeviceCapabilities {
279    /// Medium of the device.
280    ///
281    /// This indicates what kind of packet the sent/received bytes are, and determines
282    /// some behaviors of Interface. For example, ARP/NDISC address resolution is only done
283    /// for Ethernet mediums.
284    pub medium: Medium,
285
286    /// Maximum transmission unit.
287    ///
288    /// The network device is unable to send or receive frames larger than the value returned
289    /// by this function.
290    ///
291    /// For Ethernet devices, this is the maximum Ethernet frame size, including the Ethernet header (14 octets), but
292    /// *not* including the Ethernet FCS (4 octets). Therefore, Ethernet MTU = IP MTU + 14.
293    ///
294    /// Note that in Linux and other OSes, "MTU" is the IP MTU, not the Ethernet MTU, even for Ethernet
295    /// devices. This is a common source of confusion.
296    ///
297    /// Most common IP MTU is 1500. Minimum is 576 (for IPv4) or 1280 (for IPv6). Maximum is 9216 octets.
298    pub max_transmission_unit: usize,
299
300    /// Maximum burst size, in terms of MTU.
301    ///
302    /// The network device is unable to send or receive bursts large than the value returned
303    /// by this function.
304    ///
305    /// If `None`, there is no fixed limit on burst size, e.g. if network buffers are
306    /// dynamically allocated.
307    pub max_burst_size: Option<usize>,
308
309    /// Checksum behavior.
310    ///
311    /// If the network device is capable of verifying or computing checksums for some protocols,
312    /// it can request that the stack not do so in software to improve performance.
313    pub checksum: ChecksumCapabilities,
314
315    #[cfg(feature = "segmentation-offload")]
316    /// Segmentation offload capabilities.
317    ///
318    /// If the network device is capable of segmenting packets for some protocols,
319    /// it can request that the stack not do so in software to improve performance.
320    ///
321    /// The device needs to support checksum offload in the send direction for
322    /// the corresponding protocol.
323    pub segmentation: SegmentationCapabilities,
324}
325
326impl DeviceCapabilities {
327    pub fn ip_mtu(&self) -> usize {
328        match self.medium {
329            #[cfg(feature = "medium-ethernet")]
330            Medium::Ethernet => {
331                self.max_transmission_unit - crate::wire::EthernetFrame::<&[u8]>::header_len()
332            }
333            #[cfg(feature = "medium-ip")]
334            Medium::Ip => self.max_transmission_unit,
335            #[cfg(feature = "medium-ieee802154")]
336            Medium::Ieee802154 => self.max_transmission_unit, // TODO(thvdveld): what is the MTU for Medium::IEEE802
337        }
338    }
339
340    /// Special case method to determine the maximum payload size that is based on the MTU and also aligned per spec.
341    #[cfg(feature = "proto-ipv4-fragmentation")]
342    pub fn max_ipv4_fragment_size(&self, ip_header_len: usize) -> usize {
343        let payload_mtu = self.ip_mtu() - ip_header_len;
344        payload_mtu - (payload_mtu % IPV4_FRAGMENT_PAYLOAD_ALIGNMENT)
345    }
346}
347
348/// Type of medium of a device.
349#[derive(Debug, Eq, PartialEq, Copy, Clone)]
350#[cfg_attr(feature = "defmt", derive(defmt::Format))]
351pub enum Medium {
352    /// Ethernet medium. Devices of this type send and receive Ethernet frames,
353    /// and interfaces using it must do neighbor discovery via ARP or NDISC.
354    ///
355    /// Examples of devices of this type are Ethernet, WiFi (802.11), Linux `tap`, and VPNs in tap (layer 2) mode.
356    #[cfg(feature = "medium-ethernet")]
357    Ethernet,
358
359    /// IP medium. Devices of this type send and receive IP frames, without an
360    /// Ethernet header. MAC addresses are not used, and no neighbor discovery (ARP, NDISC) is done.
361    ///
362    /// Examples of devices of this type are the Linux `tun`, PPP interfaces, VPNs in tun (layer 3) mode.
363    #[cfg(feature = "medium-ip")]
364    Ip,
365
366    #[cfg(feature = "medium-ieee802154")]
367    Ieee802154,
368}
369
370impl Default for Medium {
371    fn default() -> Medium {
372        #[cfg(feature = "medium-ethernet")]
373        return Medium::Ethernet;
374        #[cfg(all(feature = "medium-ip", not(feature = "medium-ethernet")))]
375        return Medium::Ip;
376        #[cfg(all(
377            feature = "medium-ieee802154",
378            not(feature = "medium-ip"),
379            not(feature = "medium-ethernet")
380        ))]
381        return Medium::Ieee802154;
382        #[cfg(all(
383            not(feature = "medium-ip"),
384            not(feature = "medium-ethernet"),
385            not(feature = "medium-ieee802154")
386        ))]
387        return panic!("No medium enabled");
388    }
389}
390
391/// An interface for sending and receiving raw network frames.
392///
393/// The interface is based on _tokens_, which are types that allow to receive/transmit a
394/// single packet. The `receive` and `transmit` functions only construct such tokens, the
395/// real sending/receiving operation are performed when the tokens are consumed.
396pub trait Device {
397    type RxToken<'a>: RxToken
398    where
399        Self: 'a;
400    type TxToken<'a>: TxToken
401    where
402        Self: 'a;
403
404    /// Construct a token pair consisting of one receive token and one transmit token.
405    ///
406    /// The additional transmit token makes it possible to generate a reply packet based
407    /// on the contents of the received packet. For example, this makes it possible to
408    /// handle arbitrarily large ICMP echo ("ping") requests, where the all received bytes
409    /// need to be sent back, without heap allocation.
410    ///
411    /// The timestamp must be a number of milliseconds, monotonically increasing since an
412    /// arbitrary moment in time, such as system startup.
413    fn receive(&mut self, timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)>;
414
415    /// Construct a transmit token.
416    ///
417    /// The timestamp must be a number of milliseconds, monotonically increasing since an
418    /// arbitrary moment in time, such as system startup.
419    fn transmit(&mut self, timestamp: Instant) -> Option<Self::TxToken<'_>>;
420
421    /// Get a description of device capabilities.
422    fn capabilities(&self) -> DeviceCapabilities;
423}
424
425/// A token to receive a single network packet.
426pub trait RxToken {
427    /// Consumes the token to receive a single network packet.
428    ///
429    /// This method receives a packet and then calls the given closure `f` with the raw
430    /// packet bytes as argument.
431    fn consume<R, F>(self, f: F) -> R
432    where
433        F: FnOnce(&[u8]) -> R;
434
435    /// The Packet ID associated with the frame received by this [`RxToken`]
436    fn meta(&self) -> PacketMeta {
437        PacketMeta::default()
438    }
439}
440
441/// A token to transmit a single network packet.
442pub trait TxToken {
443    /// Consumes the token to send a single network packet.
444    ///
445    /// This method constructs a transmit buffer of size `len` and calls the passed
446    /// closure `f` with a mutable reference to that buffer. The closure should construct
447    /// a valid network packet (e.g. an ethernet packet) in the buffer. When the closure
448    /// returns, the transmit buffer is sent out.
449    fn consume<R, F>(self, len: usize, f: F) -> R
450    where
451        F: FnOnce(&mut [u8]) -> R;
452
453    /// The Packet ID to be associated with the frame to be transmitted by this [`TxToken`].
454    #[allow(unused_variables)]
455    fn set_meta(&mut self, meta: PacketMeta) {}
456}