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