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
93#[cfg(all(
94    any(feature = "phy-raw_socket", feature = "phy-tuntap_interface"),
95    unix
96))]
97mod sys;
98
99mod fault_injector;
100#[cfg(feature = "alloc")]
101mod fuzz_injector;
102#[cfg(feature = "alloc")]
103mod loopback;
104mod pcap_writer;
105#[cfg(all(feature = "phy-raw_socket", unix))]
106mod raw_socket;
107mod tracer;
108#[cfg(all(
109    feature = "phy-tuntap_interface",
110    any(target_os = "linux", target_os = "android")
111))]
112mod tuntap_interface;
113
114#[cfg(all(
115    any(feature = "phy-raw_socket", feature = "phy-tuntap_interface"),
116    unix
117))]
118pub use self::sys::wait;
119
120pub use self::fault_injector::FaultInjector;
121#[cfg(feature = "alloc")]
122pub use self::fuzz_injector::{FuzzInjector, Fuzzer};
123#[cfg(feature = "alloc")]
124pub use self::loopback::Loopback;
125pub use self::pcap_writer::{PcapLinkType, PcapMode, PcapSink, PcapWriter};
126#[cfg(all(feature = "phy-raw_socket", unix))]
127pub use self::raw_socket::RawSocket;
128pub use self::tracer::{Tracer, TracerDirection, TracerPacket};
129#[cfg(all(
130    feature = "phy-tuntap_interface",
131    any(target_os = "linux", target_os = "android")
132))]
133pub use self::tuntap_interface::TunTapInterface;
134
135/// The IPV4 payload fragment size must be an increment of this value.
136#[cfg(feature = "proto-ipv4-fragmentation")]
137pub const IPV4_FRAGMENT_PAYLOAD_ALIGNMENT: usize = 8;
138
139/// Metadata associated to a packet.
140///
141/// The packet metadata is a set of attributes associated to network packets
142/// as they travel up or down the stack. The metadata is get/set by the
143/// [`Device`] implementations or by the user when sending/receiving packets from a
144/// socket.
145///
146/// Metadata fields are enabled via Cargo features. If no field is enabled, this
147/// struct becomes zero-sized, which allows the compiler to optimize it out as if
148/// the packet metadata mechanism didn't exist at all.
149///
150/// Currently only UDP sockets allow setting/retrieving packet metadata. The metadata
151/// for packets emitted with other sockets will be all default values.
152///
153/// This struct is marked as `#[non_exhaustive]`. This means it is not possible to
154/// create it directly by specifying all fields. You have to instead create it with
155/// default values and then set the fields you want. This makes adding metadata
156/// fields a non-breaking change.
157///
158/// ```rust
159/// let mut meta = smoltcp::phy::PacketMeta::default();
160/// #[cfg(feature = "packetmeta-id")]
161/// {
162///     meta.id = 15;
163/// }
164/// ```
165#[cfg_attr(feature = "defmt", derive(defmt::Format))]
166#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Default)]
167#[non_exhaustive]
168pub struct PacketMeta {
169    #[cfg(feature = "packetmeta-id")]
170    pub id: u32,
171}
172
173/// A description of checksum behavior for a particular protocol.
174#[derive(Debug, Clone, Copy, Default)]
175#[cfg_attr(feature = "defmt", derive(defmt::Format))]
176pub enum Checksum {
177    /// Verify checksum when receiving and compute checksum when sending.
178    #[default]
179    Both,
180    /// Verify checksum when receiving.
181    Rx,
182    /// Compute checksum before sending.
183    Tx,
184    /// Ignore checksum completely.
185    None,
186}
187
188impl Checksum {
189    /// Returns whether checksum should be verified when receiving.
190    pub fn rx(&self) -> bool {
191        match *self {
192            Checksum::Both | Checksum::Rx => true,
193            _ => false,
194        }
195    }
196
197    /// Returns whether checksum should be verified when sending.
198    pub fn tx(&self) -> bool {
199        match *self {
200            Checksum::Both | Checksum::Tx => true,
201            _ => false,
202        }
203    }
204}
205
206/// A description of checksum behavior for every supported protocol.
207#[derive(Debug, Clone, Default)]
208#[cfg_attr(feature = "defmt", derive(defmt::Format))]
209#[non_exhaustive]
210pub struct ChecksumCapabilities {
211    pub ipv4: Checksum,
212    pub udp: Checksum,
213    pub tcp: Checksum,
214    #[cfg(feature = "proto-ipv4")]
215    pub icmpv4: Checksum,
216    #[cfg(feature = "proto-ipv6")]
217    pub icmpv6: Checksum,
218}
219
220impl ChecksumCapabilities {
221    /// Checksum behavior that results in not computing or verifying checksums
222    /// for any of the supported protocols.
223    pub fn ignored() -> Self {
224        ChecksumCapabilities {
225            ipv4: Checksum::None,
226            udp: Checksum::None,
227            tcp: Checksum::None,
228            #[cfg(feature = "proto-ipv4")]
229            icmpv4: Checksum::None,
230            #[cfg(feature = "proto-ipv6")]
231            icmpv6: Checksum::None,
232        }
233    }
234}
235
236/// A description of device capabilities.
237///
238/// Higher-level protocols may achieve higher throughput or lower latency if they consider
239/// the bandwidth or packet size limitations.
240#[derive(Debug, Clone, Default)]
241#[cfg_attr(feature = "defmt", derive(defmt::Format))]
242#[non_exhaustive]
243pub struct DeviceCapabilities {
244    /// Medium of the device.
245    ///
246    /// This indicates what kind of packet the sent/received bytes are, and determines
247    /// some behaviors of Interface. For example, ARP/NDISC address resolution is only done
248    /// for Ethernet mediums.
249    pub medium: Medium,
250
251    /// Maximum transmission unit.
252    ///
253    /// The network device is unable to send or receive frames larger than the value returned
254    /// by this function.
255    ///
256    /// For Ethernet devices, this is the maximum Ethernet frame size, including the Ethernet header (14 octets), but
257    /// *not* including the Ethernet FCS (4 octets). Therefore, Ethernet MTU = IP MTU + 14.
258    ///
259    /// Note that in Linux and other OSes, "MTU" is the IP MTU, not the Ethernet MTU, even for Ethernet
260    /// devices. This is a common source of confusion.
261    ///
262    /// Most common IP MTU is 1500. Minimum is 576 (for IPv4) or 1280 (for IPv6). Maximum is 9216 octets.
263    pub max_transmission_unit: usize,
264
265    /// Maximum burst size, in terms of MTU.
266    ///
267    /// The network device is unable to send or receive bursts large than the value returned
268    /// by this function.
269    ///
270    /// If `None`, there is no fixed limit on burst size, e.g. if network buffers are
271    /// dynamically allocated.
272    pub max_burst_size: Option<usize>,
273
274    /// Checksum behavior.
275    ///
276    /// If the network device is capable of verifying or computing checksums for some protocols,
277    /// it can request that the stack not do so in software to improve performance.
278    pub checksum: ChecksumCapabilities,
279}
280
281impl DeviceCapabilities {
282    pub fn ip_mtu(&self) -> usize {
283        match self.medium {
284            #[cfg(feature = "medium-ethernet")]
285            Medium::Ethernet => {
286                self.max_transmission_unit - crate::wire::EthernetFrame::<&[u8]>::header_len()
287            }
288            #[cfg(feature = "medium-ip")]
289            Medium::Ip => self.max_transmission_unit,
290            #[cfg(feature = "medium-ieee802154")]
291            Medium::Ieee802154 => self.max_transmission_unit, // TODO(thvdveld): what is the MTU for Medium::IEEE802
292        }
293    }
294
295    /// Special case method to determine the maximum payload size that is based on the MTU and also aligned per spec.
296    #[cfg(feature = "proto-ipv4-fragmentation")]
297    pub fn max_ipv4_fragment_size(&self, ip_header_len: usize) -> usize {
298        let payload_mtu = self.ip_mtu() - ip_header_len;
299        payload_mtu - (payload_mtu % IPV4_FRAGMENT_PAYLOAD_ALIGNMENT)
300    }
301}
302
303/// Type of medium of a device.
304#[derive(Debug, Eq, PartialEq, Copy, Clone)]
305#[cfg_attr(feature = "defmt", derive(defmt::Format))]
306pub enum Medium {
307    /// Ethernet medium. Devices of this type send and receive Ethernet frames,
308    /// and interfaces using it must do neighbor discovery via ARP or NDISC.
309    ///
310    /// Examples of devices of this type are Ethernet, WiFi (802.11), Linux `tap`, and VPNs in tap (layer 2) mode.
311    #[cfg(feature = "medium-ethernet")]
312    Ethernet,
313
314    /// IP medium. Devices of this type send and receive IP frames, without an
315    /// Ethernet header. MAC addresses are not used, and no neighbor discovery (ARP, NDISC) is done.
316    ///
317    /// Examples of devices of this type are the Linux `tun`, PPP interfaces, VPNs in tun (layer 3) mode.
318    #[cfg(feature = "medium-ip")]
319    Ip,
320
321    #[cfg(feature = "medium-ieee802154")]
322    Ieee802154,
323}
324
325impl Default for Medium {
326    fn default() -> Medium {
327        #[cfg(feature = "medium-ethernet")]
328        return Medium::Ethernet;
329        #[cfg(all(feature = "medium-ip", not(feature = "medium-ethernet")))]
330        return Medium::Ip;
331        #[cfg(all(
332            feature = "medium-ieee802154",
333            not(feature = "medium-ip"),
334            not(feature = "medium-ethernet")
335        ))]
336        return Medium::Ieee802154;
337        #[cfg(all(
338            not(feature = "medium-ip"),
339            not(feature = "medium-ethernet"),
340            not(feature = "medium-ieee802154")
341        ))]
342        return panic!("No medium enabled");
343    }
344}
345
346/// An interface for sending and receiving raw network frames.
347///
348/// The interface is based on _tokens_, which are types that allow to receive/transmit a
349/// single packet. The `receive` and `transmit` functions only construct such tokens, the
350/// real sending/receiving operation are performed when the tokens are consumed.
351pub trait Device {
352    type RxToken<'a>: RxToken
353    where
354        Self: 'a;
355    type TxToken<'a>: TxToken
356    where
357        Self: 'a;
358
359    /// Construct a token pair consisting of one receive token and one transmit token.
360    ///
361    /// The additional transmit token makes it possible to generate a reply packet based
362    /// on the contents of the received packet. For example, this makes it possible to
363    /// handle arbitrarily large ICMP echo ("ping") requests, where the all received bytes
364    /// need to be sent back, without heap allocation.
365    ///
366    /// The timestamp must be a number of milliseconds, monotonically increasing since an
367    /// arbitrary moment in time, such as system startup.
368    fn receive(&mut self, timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)>;
369
370    /// Construct a transmit token.
371    ///
372    /// The timestamp must be a number of milliseconds, monotonically increasing since an
373    /// arbitrary moment in time, such as system startup.
374    fn transmit(&mut self, timestamp: Instant) -> Option<Self::TxToken<'_>>;
375
376    /// Get a description of device capabilities.
377    fn capabilities(&self) -> DeviceCapabilities;
378}
379
380/// A token to receive a single network packet.
381pub trait RxToken {
382    /// Consumes the token to receive a single network packet.
383    ///
384    /// This method receives a packet and then calls the given closure `f` with the raw
385    /// packet bytes as argument.
386    fn consume<R, F>(self, f: F) -> R
387    where
388        F: FnOnce(&[u8]) -> R;
389
390    /// The Packet ID associated with the frame received by this [`RxToken`]
391    fn meta(&self) -> PacketMeta {
392        PacketMeta::default()
393    }
394}
395
396/// A token to transmit a single network packet.
397pub trait TxToken {
398    /// Consumes the token to send a single network packet.
399    ///
400    /// This method constructs a transmit buffer of size `len` and calls the passed
401    /// closure `f` with a mutable reference to that buffer. The closure should construct
402    /// a valid network packet (e.g. an ethernet packet) in the buffer. When the closure
403    /// returns, the transmit buffer is sent out.
404    fn consume<R, F>(self, len: usize, f: F) -> R
405    where
406        F: FnOnce(&mut [u8]) -> R;
407
408    /// The Packet ID to be associated with the frame to be transmitted by this [`TxToken`].
409    #[allow(unused_variables)]
410    fn set_meta(&mut self, meta: PacketMeta) {}
411}