| /*! Access to networking hardware. |
| |
| The `phy` module deals with the *network devices*. It provides a trait |
| for transmitting and receiving frames, [Device](trait.Device.html) |
| and implementations of it: |
| |
| * the [_loopback_](struct.Loopback.html), for zero dependency testing; |
| * _middleware_ [Tracer](struct.Tracer.html) and |
| [FaultInjector](struct.FaultInjector.html), to facilitate debugging; |
| * _adapters_ [RawSocket](struct.RawSocket.html) and |
| [TunTapInterface](struct.TunTapInterface.html), to transmit and receive frames |
| on the host OS. |
| */ |
| # trait for a simple hardware |
| Ethernet controller could look as follows: |
| |
| ```rust |
| use smoltcp::phy::{self, DeviceCapabilities, Device, Medium}; |
| use smoltcp::time::Instant; |
| |
| struct StmPhy { |
| rx_buffer: [u8; 1536], |
| tx_buffer: [u8; 1536], |
| } |
| |
| impl<'a> StmPhy { |
| fn new() -> StmPhy { |
| StmPhy { |
| rx_buffer: [0; 1536], |
| tx_buffer: [0; 1536], |
| } |
| } |
| } |
| |
| impl phy::Device for StmPhy { |
| type RxToken<'a> = StmPhyRxToken<'a> where Self: 'a; |
| type TxToken<'a> = StmPhyTxToken<'a> where Self: 'a; |
| |
| fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { |
| Some((StmPhyRxToken(&mut self.rx_buffer[..]), |
| StmPhyTxToken(&mut self.tx_buffer[..]))) |
| } |
| |
| fn transmit(&mut self, _timestamp: Instant) -> Option<Self::TxToken<'_>> { |
| Some(StmPhyTxToken(&mut self.tx_buffer[..])) |
| } |
| |
| fn capabilities(&self) -> DeviceCapabilities { |
| let mut caps = DeviceCapabilities::default(); |
| caps.max_transmission_unit = 1536; |
| caps.max_burst_size = Some(1); |
| caps.medium = Medium::Ethernet; |
| caps |
| } |
| } |
| |
| struct StmPhyRxToken<'a>(&'a mut [u8]); |
| |
| impl<'a> phy::RxToken for StmPhyRxToken<'a> { |
| fn consume<R, F>(mut self, f: F) -> R |
| where F: FnOnce(&mut [u8]) -> R |
| { |
| // TODO: receive packet into buffer |
| let result = f(&mut self.0); |
| println!("rx called"); |
| result |
| } |
| } |
| |
| struct StmPhyTxToken<'a>(&'a mut [u8]); |
| |
| impl<'a> phy::TxToken for StmPhyTxToken<'a> { |
| fn consume<R, F>(self, len: usize, f: F) -> R |
| where F: FnOnce(&mut [u8]) -> R |
| { |
| let result = f(&mut self.0[..len]); |
| println!("tx called {}", len); |
| // TODO: send packet out |
| result |
| } |
| } |
| ``` |
| "## |
| )] |
| |
| use crate::time::Instant; |
| |
| #[cfg(all( |
| any(feature = "phy-raw_socket", feature = "phy-tuntap_interface"), |
| unix |
| ))] |
| mod sys; |
| |
| mod fault_injector; |
| mod fuzz_injector; |
| #[cfg(feature = "alloc")] |
| mod loopback; |
| mod pcap_writer; |
| #[cfg(all(feature = "phy-raw_socket", unix))] |
| mod raw_socket; |
| mod tracer; |
| #[cfg(all( |
| feature = "phy-tuntap_interface", |
| any(target_os = "linux", target_os = "android") |
| ))] |
| mod tuntap_interface; |
| |
| #[cfg(all( |
| any(feature = "phy-raw_socket", feature = "phy-tuntap_interface"), |
| unix |
| ))] |
| pub use self::sys::wait; |
| |
| pub use self::fault_injector::FaultInjector; |
| pub use self::fuzz_injector::{FuzzInjector, Fuzzer}; |
| #[cfg(feature = "alloc")] |
| pub use self::loopback::Loopback; |
| pub use self::pcap_writer::{PcapLinkType, PcapMode, PcapSink, PcapWriter}; |
| #[cfg(all(feature = "phy-raw_socket", unix))] |
| pub use self::raw_socket::RawSocket; |
| pub use self::tracer::Tracer; |
| #[cfg(all( |
| feature = "phy-tuntap_interface", |
| any(target_os = "linux", target_os = "android") |
| ))] |
| pub use self::tuntap_interface::TunTapInterface; |
| |
| /// Metadata associated to a packet. |
| /// |
| /// The packet metadata is a set of attributes associated to network packets |
| /// as they travel up or down the stack. The metadata is get/set by the |
| /// [`Device`] implementations or by the user when sending/receiving packets from a |
| /// socket. |
| /// |
| /// Metadata fields are enabled via Cargo features. If no field is enabled, this |
| /// struct becomes zero-sized, which allows the compiler to optimize it out as if |
| /// the packet metadata mechanism didn't exist at all. |
| /// |
| /// Currently only UDP sockets allow setting/retrieving packet metadata. The metadata |
| /// for packets emitted with other sockets will be all default values. |
| /// |
| /// This struct is marked as `#[non_exhaustive]`. This means it is not possible to |
| /// create it directly by specifying all fields. You have to instead create it with |
| /// default values and then set the fields you want. This makes adding metadata |
| /// fields a non-breaking change. |
| /// |
| /// ```rust |
| /// let mut meta = smoltcp::phy::PacketMeta::default(); |
| /// #[cfg(feature = "packetmeta-id")] |
| /// { |
| /// meta.id = 15; |
| /// } |
| /// ``` |
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Default)] |
| #[non_exhaustive] |
| pub struct PacketMeta { |
| #[cfg(feature = "packetmeta-id")] |
| pub id: u32, |
| } |
| |
| /// A description of checksum behavior for a particular protocol. |
| #[derive(Debug, Clone, Copy, Default)] |
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| pub enum Checksum { |
| /// Verify checksum when receiving and compute checksum when sending. |
| #[default] |
| Both, |
| /// Verify checksum when receiving. |
| Rx, |
| /// Compute checksum before sending. |
| Tx, |
| /// Ignore checksum completely. |
| None, |
| } |
| |
| impl Checksum { |
| /// Returns whether checksum should be verified when receiving. |
| pub fn rx(&self) -> bool { |
| match *self { |
| Checksum::Both | Checksum::Rx => true, |
| _ => false, |
| } |
| } |
| |
| /// Returns whether checksum should be verified when sending. |
| pub fn tx(&self) -> bool { |
| match *self { |
| Checksum::Both | Checksum::Tx => true, |
| _ => false, |
| } |
| } |
| } |
| |
| /// A description of checksum behavior for every supported protocol. |
| #[derive(Debug, Clone, Default)] |
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| #[non_exhaustive] |
| pub struct ChecksumCapabilities { |
| pub ipv4: Checksum, |
| pub udp: Checksum, |
| pub tcp: Checksum, |
| #[cfg(feature = "proto-ipv4")] |
| pub icmpv4: Checksum, |
| #[cfg(feature = "proto-ipv6")] |
| pub icmpv6: Checksum, |
| } |
| |
| impl ChecksumCapabilities { |
| /// Checksum behavior that results in not computing or verifying checksums |
| /// for any of the supported protocols. |
| pub fn ignored() -> Self { |
| ChecksumCapabilities { |
| ipv4: Checksum::None, |
| udp: Checksum::None, |
| tcp: Checksum::None, |
| #[cfg(feature = "proto-ipv4")] |
| icmpv4: Checksum::None, |
| #[cfg(feature = "proto-ipv6")] |
| icmpv6: Checksum::None, |
| } |
| } |
| } |
| |
| /// A description of device capabilities. |
| /// |
| /// Higher-level protocols may achieve higher throughput or lower latency if they consider |
| /// the bandwidth or packet size limitations. |
| #[derive(Debug, Clone, Default)] |
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| #[non_exhaustive] |
| pub struct DeviceCapabilities { |
| /// Medium of the device. |
| /// |
| /// This indicates what kind of packet the sent/received bytes are, and determines |
| /// some behaviors of Interface. For example, ARP/NDISC address resolution is only done |
| /// for Ethernet mediums. |
| pub medium: Medium, |
| |
| /// Maximum transmission unit. |
| /// |
| /// The network device is unable to send or receive frames larger than the value returned |
| /// by this function. |
| /// |
| /// For Ethernet devices, this is the maximum Ethernet frame size, including the Ethernet header (14 octets), but |
| /// *not* including the Ethernet FCS (4 octets). Therefore, Ethernet MTU = IP MTU + 14. |
| /// |
| /// Note that in Linux and other OSes, "MTU" is the IP MTU, not the Ethernet MTU, even for Ethernet |
| /// devices. This is a common source of confusion. |
| /// |
| /// Most common IP MTU is 1500. Minimum is 576 (for IPv4) or 1280 (for IPv6). Maximum is 9216 octets. |
| pub max_transmission_unit: usize, |
| |
| /// Maximum burst size, in terms of MTU. |
| /// |
| /// The network device is unable to send or receive bursts large than the value returned |
| /// by this function. |
| /// |
| /// If `None`, there is no fixed limit on burst size, e.g. if network buffers are |
| /// dynamically allocated. |
| pub max_burst_size: Option<usize>, |
| |
| /// Checksum behavior. |
| /// |
| /// If the network device is capable of verifying or computing checksums for some protocols, |
| /// it can request that the stack not do so in software to improve performance. |
| pub checksum: ChecksumCapabilities, |
| } |
| |
| impl DeviceCapabilities { |
| pub fn ip_mtu(&self) -> usize { |
| match self.medium { |
| #[cfg(feature = "medium-ethernet")] |
| Medium::Ethernet => { |
| self.max_transmission_unit - crate::wire::EthernetFrame::<&[u8]>::header_len() |
| } |
| #[cfg(feature = "medium-ip")] |
| Medium::Ip => self.max_transmission_unit, |
| #[cfg(feature = "medium-ieee802154")] |
| Medium::Ieee802154 => self.max_transmission_unit, // TODO(thvdveld): what is the MTU for Medium::IEEE802 |
| } |
| } |
| } |
| |
| /// Type of medium of a device. |
| #[derive(Debug, Eq, PartialEq, Copy, Clone)] |
| #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| pub enum Medium { |
| /// Ethernet medium. Devices of this type send and receive Ethernet frames, |
| /// and interfaces using it must do neighbor discovery via ARP or NDISC. |
| /// |
| /// Examples of devices of this type are Ethernet, WiFi (802.11), Linux `tap`, and VPNs in tap (layer 2) mode. |
| #[cfg(feature = "medium-ethernet")] |
| Ethernet, |
| |
| /// IP medium. Devices of this type send and receive IP frames, without an |
| /// Ethernet header. MAC addresses are not used, and no neighbor discovery (ARP, NDISC) is done. |
| /// |
| /// Examples of devices of this type are the Linux `tun`, PPP interfaces, VPNs in tun (layer 3) mode. |
| #[cfg(feature = "medium-ip")] |
| Ip, |
| |
| #[cfg(feature = "medium-ieee802154")] |
| Ieee802154, |
| } |
| |
| impl Default for Medium { |
| fn default() -> Medium { |
| #[cfg(feature = "medium-ethernet")] |
| return Medium::Ethernet; |
| #[cfg(all(feature = "medium-ip", not(feature = "medium-ethernet")))] |
| return Medium::Ip; |
| #[cfg(all( |
| feature = "medium-ieee802154", |
| not(feature = "medium-ip"), |
| not(feature = "medium-ethernet") |
| ))] |
| return Medium::Ieee802154; |
| #[cfg(all( |
| not(feature = "medium-ip"), |
| not(feature = "medium-ethernet"), |
| not(feature = "medium-ieee802154") |
| ))] |
| return panic!("No medium enabled"); |
| } |
| } |
| |
| /// An interface for sending and receiving raw network frames. |
| /// |
| /// The interface is based on _tokens_, which are types that allow to receive/transmit a |
| /// single packet. The `receive` and `transmit` functions only construct such tokens, the |
| /// real sending/receiving operation are performed when the tokens are consumed. |
| pub trait Device { |
| type RxToken<'a>: RxToken |
| where |
| Self: 'a; |
| type TxToken<'a>: TxToken |
| where |
| Self: 'a; |
| |
| /// Construct a token pair consisting of one receive token and one transmit token. |
| /// |
| /// The additional transmit token makes it possible to generate a reply packet based |
| /// on the contents of the received packet. For example, this makes it possible to |
| /// handle arbitrarily large ICMP echo ("ping") requests, where the all received bytes |
| /// need to be sent back, without heap allocation. |
| /// |
| /// The timestamp must be a number of milliseconds, monotonically increasing since an |
| /// arbitrary moment in time, such as system startup. |
| fn receive(&mut self, timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)>; |
| |
| /// Construct a transmit token. |
| /// |
| /// The timestamp must be a number of milliseconds, monotonically increasing since an |
| /// arbitrary moment in time, such as system startup. |
| fn transmit(&mut self, timestamp: Instant) -> Option<Self::TxToken<'_>>; |
| |
| /// Get a description of device capabilities. |
| fn capabilities(&self) -> DeviceCapabilities; |
| } |
| |
| /// A token to receive a single network packet. |
| pub trait RxToken { |
| /// Consumes the token to receive a single network packet. |
| /// |
| /// This method receives a packet and then calls the given closure `f` with the raw |
| /// packet bytes as argument. |
| fn consume<R, F>(self, f: F) -> R |
| where |
| F: FnOnce(&mut [u8]) -> R; |
| |
| /// The Packet ID associated with the frame received by this [`RxToken`] |
| fn meta(&self) -> PacketMeta { |
| PacketMeta::default() |
| } |
| } |
| |
| /// A token to transmit a single network packet. |
| pub trait TxToken { |
| /// Consumes the token to send a single network packet. |
| /// |
| /// This method constructs a transmit buffer of size `len` and calls the passed |
| /// closure `f` with a mutable reference to that buffer. The closure should construct |
| /// a valid network packet (e.g. an ethernet packet) in the buffer. When the closure |
| /// returns, the transmit buffer is sent out. |
| fn consume<R, F>(self, len: usize, f: F) -> R |
| where |
| F: FnOnce(&mut [u8]) -> R; |
| |
| /// The Packet ID to be associated with the frame to be transmitted by this [`TxToken`]. |
| #[allow(unused_variables)] |
| fn set_meta(&mut self, meta: PacketMeta) {} |
| } |