diff --git a/Cargo.toml b/Cargo.toml index 0d84c0bd7..3b75cf77d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,6 @@ +[workspace] +members = ["smoltcp-device", "smoltcp-device-mock", "smoltcp-device-unix"] + [package] name = "smoltcp" version = "0.12.0" @@ -28,6 +31,7 @@ bitflags = { version = "1.0", default-features = false } defmt = { version = "0.3.8", optional = true, features = ["ip_in_core"] } cfg-if = "1.0.0" heapless = "0.8" +smoltcp-device = { version = "0.1.0", path = "smoltcp-device" } [dev-dependencies] env_logger = "0.10" @@ -38,18 +42,19 @@ rstest = "0.17" insta = "1.41.1" rand_chacha = "0.3.1" idna = { version = "=1.0.1" } +smoltcp-device-mock = { path = "smoltcp-device-mock" } + +[target.'cfg(unix)'.dev-dependencies] +smoltcp-device-unix = { path = "smoltcp-device-unix" } [features] -std = ["managed/std", "alloc"] +std = ["managed/std", "alloc", "smoltcp-device/std"] alloc = ["managed/alloc", "defmt?/alloc"] verbose = [] -defmt = ["dep:defmt", "heapless/defmt-03"] -"medium-ethernet" = ["socket"] -"medium-ip" = ["socket"] -"medium-ieee802154" = ["socket", "proto-sixlowpan"] - -"phy-raw_socket" = ["std", "libc"] -"phy-tuntap_interface" = ["std", "libc", "medium-ethernet"] +defmt = ["dep:defmt", "heapless/defmt-03", "smoltcp-device/defmt"] +"medium-ethernet" = ["socket", "smoltcp-device/requires-medium-ethernet", "smoltcp-device-mock/medium-ethernet", "smoltcp-device-unix/medium-ethernet"] +"medium-ip" = ["socket", "smoltcp-device/requires-medium-ip", "smoltcp-device-mock/medium-ip", "smoltcp-device-unix/medium-ip"] +"medium-ieee802154" = ["socket", "proto-sixlowpan", "smoltcp-device/requires-medium-ieee802154", "smoltcp-device-mock/medium-ieee802154", "smoltcp-device-unix/medium-ieee802154"] "proto-ipv4" = [] "proto-ipv4-fragmentation" = ["proto-ipv4", "_proto-fragmentation"] @@ -93,14 +98,13 @@ defmt = ["dep:defmt", "heapless/defmt-03"] # Enable Reno TCP congestion control algorithm, and it is used as a default congestion controller. "socket-tcp-reno" = [] -"packetmeta-id" = [] +"packetmeta-id" = ["smoltcp-device/packetmeta-id"] "async" = [] default = [ "std", "log", # needed for `cargo test --no-default-features --features default` :/ "medium-ethernet", "medium-ip", "medium-ieee802154", - "phy-raw_socket", "phy-tuntap_interface", "proto-ipv4", "proto-dhcpv4", "proto-ipv6", "proto-dns", "proto-ipv4-fragmentation", "proto-sixlowpan-fragmentation", "socket-raw", "socket-icmp", "socket-udp", "socket-tcp", "socket-dhcpv4", "socket-dns", "socket-mdns", @@ -113,6 +117,7 @@ default = [ "_proto-fragmentation" = [] +# Prevents running netsim (slow) with the other tests. "_netsim" = [] # BEGIN AUTOGENERATED CONFIG FEATURES @@ -285,59 +290,59 @@ required-features = ["std"] [[example]] name = "tcpdump" -required-features = ["std", "phy-raw_socket", "proto-ipv4"] +required-features = ["std", "log", "medium-ethernet", "proto-ipv4"] [[example]] name = "httpclient" -required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interface", "proto-ipv4", "proto-ipv6", "socket-tcp"] +required-features = ["std", "log", "medium-ethernet", "medium-ip", "proto-ipv4", "proto-ipv6", "socket-tcp"] [[example]] name = "ping" -required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interface", "proto-ipv4", "proto-ipv6", "socket-icmp"] +required-features = ["std", "log", "medium-ethernet", "medium-ip", "proto-ipv4", "proto-ipv6", "socket-icmp"] [[example]] name = "server" -required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interface", "proto-ipv4", "socket-tcp", "socket-udp"] +required-features = ["std", "log", "medium-ethernet", "medium-ip", "proto-ipv4", "socket-tcp", "socket-udp"] [[example]] name = "client" -required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interface", "proto-ipv4", "socket-tcp", "socket-udp"] +required-features = ["std", "log", "medium-ethernet", "medium-ip", "proto-ipv4", "socket-tcp", "socket-udp"] [[example]] name = "loopback" -required-features = ["log", "medium-ethernet", "proto-ipv4", "socket-tcp"] +required-features = ["log", "medium-ethernet", "medium-ip", "medium-ieee802154", "proto-ipv4", "socket-tcp"] [[example]] name = "loopback_benchmark" -required-features = ["std", "log", "medium-ethernet", "proto-ipv4", "socket-tcp"] +required-features = ["std", "log", "medium-ethernet", "medium-ip", "medium-ieee802154", "proto-ipv4", "socket-tcp"] [[example]] name = "multicast" -required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interface", "proto-ipv4", "multicast", "socket-udp"] +required-features = ["std", "log", "medium-ethernet", "medium-ip", "proto-ipv4", "multicast", "socket-udp"] [[example]] name = "multicast6" -required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interface", "proto-ipv6", "socket-udp"] +required-features = ["std", "log", "medium-ethernet", "medium-ip", "proto-ipv6", "socket-udp"] [[example]] name = "benchmark" -required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interface", "proto-ipv4", "socket-raw", "socket-udp"] +required-features = ["std", "log", "medium-ethernet", "medium-ip", "proto-ipv4", "socket-raw", "socket-udp"] [[example]] name = "dhcp_client" -required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interface", "proto-ipv4", "proto-dhcpv4", "socket-raw"] +required-features = ["std", "log", "medium-ethernet", "medium-ip", "proto-ipv4", "proto-dhcpv4", "socket-raw"] [[example]] name = "sixlowpan" -required-features = ["std", "medium-ieee802154", "phy-raw_socket", "proto-sixlowpan", "proto-sixlowpan-fragmentation", "socket-udp"] +required-features = ["std", "log", "medium-ethernet", "medium-ieee802154", "medium-ip", "proto-sixlowpan", "proto-sixlowpan-fragmentation", "socket-udp"] [[example]] name = "sixlowpan_benchmark" -required-features = ["std", "medium-ieee802154", "phy-raw_socket", "proto-sixlowpan", "proto-sixlowpan-fragmentation", "socket-udp"] +required-features = ["std", "log", "medium-ethernet", "medium-ieee802154", "medium-ip", "proto-sixlowpan", "proto-sixlowpan-fragmentation", "socket-udp"] [[example]] name = "dns" -required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interface", "proto-ipv4", "socket-dns"] +required-features = ["std", "log", "medium-ethernet", "medium-ip", "proto-ipv4", "socket-dns"] [profile.release] debug = 2 diff --git a/ci.sh b/ci.sh index 3b8f75fe9..f506d9d40 100755 --- a/ci.sh +++ b/ci.sh @@ -15,8 +15,8 @@ RUSTC_VERSIONS=( FEATURES_TEST=( "default" "std,proto-ipv4" - "std,medium-ethernet,phy-raw_socket,proto-ipv6,socket-udp,socket-dns" - "std,medium-ethernet,phy-tuntap_interface,proto-ipv6,socket-udp" + "std,medium-ethernet,proto-ipv6,socket-udp,socket-dns" + "std,medium-ethernet,proto-ipv6,socket-udp" "std,medium-ethernet,proto-ipv4,proto-ipv4-fragmentation,socket-raw,socket-dns" "std,medium-ethernet,proto-ipv4,multicast,socket-raw,socket-dns" "std,medium-ethernet,proto-ipv4,socket-udp,socket-tcp,socket-dns" @@ -40,16 +40,19 @@ FEATURES_TEST_NIGHTLY=( ) FEATURES_CHECK=( - "medium-ip,medium-ethernet,medium-ieee802154,proto-ipv6,proto-ipv6,multicast,proto-dhcpv4,proto-ipsec,socket-raw,socket-udp,socket-tcp,socket-icmp,socket-dns,async" - "defmt,medium-ip,medium-ethernet,proto-ipv6,proto-ipv6,multicast,proto-dhcpv4,socket-raw,socket-udp,socket-tcp,socket-icmp,socket-dns,async" - "defmt,alloc,medium-ip,medium-ethernet,proto-ipv6,proto-ipv6,multicast,proto-dhcpv4,socket-raw,socket-udp,socket-tcp,socket-icmp,socket-dns,async" - "medium-ieee802154,proto-sixlowpan,socket-dns" + "medium-ip,medium-ethernet,medium-ieee802154,proto-ipv6,proto-ipv6,multicast,proto-dhcpv4,proto-ipsec,socket-raw,socket-udp,socket-tcp,socket-icmp,socket-dns,async,smoltcp-device/provides-medium-ethernet,smoltcp-device/provides-medium-ip,smoltcp-device/provides-medium-ieee802154" + "defmt,medium-ip,medium-ethernet,proto-ipv6,proto-ipv6,multicast,proto-dhcpv4,socket-raw,socket-udp,socket-tcp,socket-icmp,socket-dns,async,smoltcp-device/provides-medium-ethernet,smoltcp-device/provides-medium-ip" + "log,medium-ip,medium-ethernet,proto-ipv6,proto-ipv6,multicast,proto-dhcpv4,socket-raw,socket-udp,socket-tcp,socket-icmp,socket-dns,async,smoltcp-device/provides-medium-ethernet,smoltcp-device/provides-medium-ip" + "defmt,alloc,medium-ip,medium-ethernet,proto-ipv6,proto-ipv6,multicast,proto-dhcpv4,socket-raw,socket-udp,socket-tcp,socket-icmp,socket-dns,async,smoltcp-device/provides-medium-ethernet,smoltcp-device/provides-medium-ip" + "medium-ieee802154,proto-sixlowpan,socket-dns,smoltcp-device/provides-medium-ieee802154" ) test() { local version=$1 rustup toolchain install $version + cargo +$version test -p smoltcp-device + for features in ${FEATURES_TEST[@]}; do cargo +$version test --no-default-features --features "$features" done @@ -99,6 +102,8 @@ build_16bit() { } coverage() { + cargo llvm-cov --no-report -p smoltcp-device + cargo llvm-cov --no-report -p smoltcp-device --features std for features in ${FEATURES_TEST[@]}; do cargo llvm-cov --no-report --no-default-features --features "$features" done diff --git a/examples/benchmark.rs b/examples/benchmark.rs index be1959f6d..0322d012d 100644 --- a/examples/benchmark.rs +++ b/examples/benchmark.rs @@ -8,10 +8,11 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::thread; use smoltcp::iface::{Config, Interface, SocketSet}; -use smoltcp::phy::{wait as phy_wait, Device, Medium}; use smoltcp::socket::tcp; use smoltcp::time::{Duration, Instant}; use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr}; +use smoltcp_device::{Device, Medium}; +use smoltcp_device_unix::wait as phy_wait; const AMOUNT: usize = 1_000_000_000; diff --git a/examples/client.rs b/examples/client.rs index c18c08ff7..976331daa 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -5,10 +5,11 @@ use std::os::unix::io::AsRawFd; use std::str::{self, FromStr}; use smoltcp::iface::{Config, Interface, SocketSet}; -use smoltcp::phy::{wait as phy_wait, Device, Medium}; use smoltcp::socket::tcp; use smoltcp::time::Instant; use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address, Ipv6Address}; +use smoltcp_device::{Device, Medium}; +use smoltcp_device_unix::wait as phy_wait; fn main() { utils::setup_logging(""); diff --git a/examples/dhcp_client.rs b/examples/dhcp_client.rs index 348e9676c..c317b4980 100644 --- a/examples/dhcp_client.rs +++ b/examples/dhcp_client.rs @@ -6,12 +6,11 @@ use std::os::unix::io::AsRawFd; use smoltcp::iface::{Config, Interface, SocketSet}; use smoltcp::socket::dhcpv4; +use smoltcp::time::Duration; use smoltcp::time::Instant; use smoltcp::wire::{EthernetAddress, IpCidr, Ipv4Cidr}; -use smoltcp::{ - phy::{wait as phy_wait, Device, Medium}, - time::Duration, -}; +use smoltcp_device::{Device, Medium}; +use smoltcp_device_unix::wait as phy_wait; fn main() { #[cfg(feature = "log")] diff --git a/examples/dns.rs b/examples/dns.rs index 977f40546..8d56c9cfb 100644 --- a/examples/dns.rs +++ b/examples/dns.rs @@ -1,11 +1,11 @@ mod utils; use smoltcp::iface::{Config, Interface, SocketSet}; -use smoltcp::phy::Device; -use smoltcp::phy::{wait as phy_wait, Medium}; use smoltcp::socket::dns::{self, GetQueryResultError}; use smoltcp::time::Instant; use smoltcp::wire::{DnsQueryType, EthernetAddress, IpAddress, IpCidr, Ipv4Address, Ipv6Address}; +use smoltcp_device::{Device, Medium}; +use smoltcp_device_unix::wait as phy_wait; use std::os::unix::io::AsRawFd; fn main() { diff --git a/examples/httpclient.rs b/examples/httpclient.rs index 8f3a53aa7..a6f21cd49 100644 --- a/examples/httpclient.rs +++ b/examples/httpclient.rs @@ -6,10 +6,11 @@ use std::str::{self, FromStr}; use url::Url; use smoltcp::iface::{Config, Interface, SocketSet}; -use smoltcp::phy::{wait as phy_wait, Device, Medium}; use smoltcp::socket::tcp; use smoltcp::time::Instant; use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address, Ipv6Address}; +use smoltcp_device::{Device, Medium}; +use smoltcp_device_unix::wait as phy_wait; fn main() { utils::setup_logging(""); diff --git a/examples/multicast.rs b/examples/multicast.rs index 024c034df..f514f8220 100644 --- a/examples/multicast.rs +++ b/examples/multicast.rs @@ -3,13 +3,14 @@ mod utils; use std::os::unix::io::AsRawFd; use smoltcp::iface::{Config, Interface, SocketSet}; -use smoltcp::phy::{wait as phy_wait, Device, Medium}; +use smoltcp::phy::{Device, Medium}; use smoltcp::socket::{raw, udp}; use smoltcp::time::Instant; use smoltcp::wire::{ EthernetAddress, IgmpPacket, IgmpRepr, IpAddress, IpCidr, IpProtocol, IpVersion, Ipv4Address, Ipv4Packet, Ipv6Address, }; +use smoltcp_device_unix::wait as phy_wait; const MDNS_PORT: u16 = 5353; const MDNS_GROUP: Ipv4Address = Ipv4Address::new(224, 0, 0, 251); diff --git a/examples/multicast6.rs b/examples/multicast6.rs index e9a2712b2..d49d33070 100644 --- a/examples/multicast6.rs +++ b/examples/multicast6.rs @@ -3,11 +3,11 @@ mod utils; use std::os::unix::io::AsRawFd; use smoltcp::iface::{Config, Interface, SocketSet}; -use smoltcp::phy::wait as phy_wait; use smoltcp::phy::{Device, Medium}; use smoltcp::socket::udp; use smoltcp::time::Instant; use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv6Address}; +use smoltcp_device_unix::wait as phy_wait; // Note: If testing with a tap interface in linux, you may need to specify the // interface index when addressing. E.g., diff --git a/examples/ping.rs b/examples/ping.rs index 29c6bcd4c..0da9e6e64 100644 --- a/examples/ping.rs +++ b/examples/ping.rs @@ -8,7 +8,6 @@ use std::os::unix::io::AsRawFd; use std::str::FromStr; use smoltcp::iface::Config; -use smoltcp::phy::wait as phy_wait; use smoltcp::phy::Device; use smoltcp::socket::icmp; use smoltcp::wire::{ @@ -19,6 +18,7 @@ use smoltcp::{ phy::Medium, time::{Duration, Instant}, }; +use smoltcp_device_unix::wait as phy_wait; macro_rules! send_icmp_ping { ( $repr_type:ident, $packet_type:ident, $ident:expr, $seq_no:expr, diff --git a/examples/server.rs b/examples/server.rs index 33d95c5d5..4bde8e26b 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -5,10 +5,11 @@ use std::fmt::Write; use std::os::unix::io::AsRawFd; use smoltcp::iface::{Config, Interface, SocketSet}; -use smoltcp::phy::{wait as phy_wait, Device, Medium}; +use smoltcp::phy::{Device, Medium}; use smoltcp::socket::{tcp, udp}; use smoltcp::time::{Duration, Instant}; use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address, Ipv6Address}; +use smoltcp_device_unix::wait as phy_wait; fn main() { utils::setup_logging(""); diff --git a/examples/sixlowpan.rs b/examples/sixlowpan.rs index 0d9ec21d8..f8aa96eae 100644 --- a/examples/sixlowpan.rs +++ b/examples/sixlowpan.rs @@ -47,11 +47,12 @@ use std::os::unix::io::AsRawFd; use std::str; use smoltcp::iface::{Config, Interface, SocketSet}; -use smoltcp::phy::{wait as phy_wait, Device, Medium, RawSocket}; +use smoltcp::phy::{Device, Medium}; use smoltcp::socket::tcp; use smoltcp::socket::udp; use smoltcp::time::Instant; use smoltcp::wire::{EthernetAddress, Ieee802154Address, Ieee802154Pan, IpAddress, IpCidr}; +use smoltcp_device_unix::{wait as phy_wait, RawSocket}; fn main() { utils::setup_logging(""); diff --git a/examples/sixlowpan_benchmark.rs b/examples/sixlowpan_benchmark.rs index 4e61491fe..7c161f770 100644 --- a/examples/sixlowpan_benchmark.rs +++ b/examples/sixlowpan_benchmark.rs @@ -18,7 +18,7 @@ //! //! //! # Setup -//! +//! //! modprobe mac802154_hwsim //! //! ip link set wpan0 down @@ -47,9 +47,10 @@ use std::os::unix::io::AsRawFd; use std::str; use smoltcp::iface::{Config, Interface, SocketSet}; -use smoltcp::phy::{wait as phy_wait, Device, Medium, RawSocket}; +use smoltcp::phy::{Device, Medium}; use smoltcp::socket::tcp; use smoltcp::wire::{EthernetAddress, Ieee802154Address, Ieee802154Pan, IpAddress, IpCidr}; +use smoltcp_device_unix::{wait as phy_wait, RawSocket}; //For benchmark use smoltcp::time::{Duration, Instant}; diff --git a/examples/tcpdump.rs b/examples/tcpdump.rs index 2baf376e1..7e4ac3598 100644 --- a/examples/tcpdump.rs +++ b/examples/tcpdump.rs @@ -1,7 +1,7 @@ -use smoltcp::phy::wait as phy_wait; -use smoltcp::phy::{Device, RawSocket, RxToken}; +use smoltcp::phy::{Device, RxToken}; use smoltcp::time::Instant; use smoltcp::wire::{EthernetFrame, PrettyPrinter}; +use smoltcp_device_unix::{wait as phy_wait, RawSocket}; use std::env; use std::os::unix::io::AsRawFd; diff --git a/examples/utils.rs b/examples/utils.rs index dbe907615..4f829278e 100644 --- a/examples/utils.rs +++ b/examples/utils.rs @@ -12,11 +12,10 @@ use std::process; use std::str::{self, FromStr}; use std::time::{SystemTime, UNIX_EPOCH}; -#[cfg(feature = "phy-tuntap_interface")] -use smoltcp::phy::TunTapInterface; use smoltcp::phy::{Device, FaultInjector, Medium, Tracer}; use smoltcp::phy::{PcapMode, PcapWriter}; use smoltcp::time::{Duration, Instant}; +use smoltcp_device_unix::TunTapInterface; #[cfg(feature = "log")] pub fn setup_logging_with_clock(filter: &str, since_startup: F) @@ -96,7 +95,6 @@ pub fn add_tuntap_options(opts: &mut Options, _free: &mut [&str]) { opts.optopt("", "tap", "TAP interface to use", "tap0"); } -#[cfg(feature = "phy-tuntap_interface")] pub fn parse_tuntap_options(matches: &mut Matches) -> TunTapInterface { let tun = matches.opt_str("tun"); let tap = matches.opt_str("tap"); diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index f526d7551..6e1a8228f 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -13,6 +13,7 @@ libfuzzer-sys = "0.4" arbitrary = { version = "1", features = ["derive"] } getopts = "0.2" smoltcp = { path = ".." } +smoltcp-device = { path = "../smoltcp-device", features = ["provides-medium-ethernet", "provides-medium-ip", "provides-medium-ieee802154"] } # Prevent this from interfering with workspaces [workspace] diff --git a/smoltcp-device-mock/Cargo.toml b/smoltcp-device-mock/Cargo.toml new file mode 100644 index 000000000..e185db33c --- /dev/null +++ b/smoltcp-device-mock/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "smoltcp-device-mock" +version = "0.1.0" +edition = "2021" +rust-version = "1.81" + +[dependencies] +smoltcp-device = { version = "0.1.0", path = "../smoltcp-device" } + +[features] +medium-ethernet = ["smoltcp-device/provides-medium-ethernet"] +medium-ieee802154 = ["smoltcp-device/provides-medium-ieee802154"] +medium-ip = ["smoltcp-device/provides-medium-ip"] diff --git a/smoltcp-device-mock/src/lib.rs b/smoltcp-device-mock/src/lib.rs new file mode 100644 index 000000000..a709cb53c --- /dev/null +++ b/smoltcp-device-mock/src/lib.rs @@ -0,0 +1,99 @@ +use std::collections::VecDeque; + +use smoltcp_device::{time::Instant, Device, DeviceCapabilities, Medium}; + +/// A testing device. +#[derive(Debug)] +pub struct TestingDevice { + pub tx_queue: VecDeque>, + pub rx_queue: VecDeque>, + max_transmission_unit: usize, + medium: Medium, +} + +#[allow(clippy::new_without_default)] +impl TestingDevice { + /// Creates a testing device. + /// + /// Every packet transmitted through this device will be received through it + /// in FIFO order. + pub fn new(medium: Medium) -> Self { + #[allow(unreachable_patterns)] + TestingDevice { + tx_queue: VecDeque::new(), + rx_queue: VecDeque::new(), + max_transmission_unit: match medium { + #[cfg(feature = "medium-ethernet")] + Medium::Ethernet => 1514, + #[cfg(feature = "medium-ip")] + Medium::Ip => 1500, + #[cfg(feature = "medium-ieee802154")] + Medium::Ieee802154 => 1500, + // Just in case another crate provides a medium that we don't: + _ => unreachable!("Medium {medium:?} is not provided."), + }, + medium, + } + } +} + +impl Device for TestingDevice { + type RxToken<'a> = RxToken; + type TxToken<'a> = TxToken<'a>; + + fn capabilities(&self) -> DeviceCapabilities { + let mut capabilities = DeviceCapabilities::new(self.medium); + + capabilities.max_transmission_unit = self.max_transmission_unit; + + capabilities + } + + fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + self.rx_queue.pop_front().map(move |buffer| { + let rx = RxToken { buffer }; + let tx = TxToken { + queue: &mut self.tx_queue, + }; + (rx, tx) + }) + } + + fn transmit(&mut self, _timestamp: Instant) -> Option> { + Some(TxToken { + queue: &mut self.tx_queue, + }) + } +} + +#[doc(hidden)] +pub struct RxToken { + buffer: Vec, +} + +impl smoltcp_device::RxToken for RxToken { + fn consume(self, f: F) -> R + where + F: FnOnce(&[u8]) -> R, + { + f(&self.buffer) + } +} + +#[doc(hidden)] +#[derive(Debug)] +pub struct TxToken<'a> { + queue: &'a mut VecDeque>, +} + +impl<'a> smoltcp_device::TxToken for TxToken<'a> { + fn consume(self, len: usize, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + let mut buffer = vec![0; len]; + let result = f(&mut buffer); + self.queue.push_back(buffer); + result + } +} diff --git a/smoltcp-device-unix/Cargo.toml b/smoltcp-device-unix/Cargo.toml new file mode 100644 index 000000000..c62aca024 --- /dev/null +++ b/smoltcp-device-unix/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "smoltcp-device-unix" +version = "0.1.0" +edition = "2021" +rust-version = "1.81" + +[dependencies] +smoltcp-device = { version = "0.1.0", path = "../smoltcp-device" } +libc = "0.2.18" +log = { version = "0.4.4", default-features = false, optional = true } +defmt = { version = "0.3.8", optional = true, features = ["ip_in_core"] } + +[features] +medium-ethernet = ["smoltcp-device/provides-medium-ethernet"] +medium-ieee802154 = ["smoltcp-device/provides-medium-ieee802154"] +medium-ip = ["smoltcp-device/provides-medium-ip"] + +defmt = ["dep:defmt", "smoltcp-device/defmt"] + +default = [ + "log", # needed for `cargo test --no-default-features --features default` :/ +] diff --git a/smoltcp-device-unix/src/lib.rs b/smoltcp-device-unix/src/lib.rs new file mode 100644 index 000000000..b331757c3 --- /dev/null +++ b/smoltcp-device-unix/src/lib.rs @@ -0,0 +1,15 @@ +#[cfg(all(feature = "defmt", feature = "log"))] +compile_error!("You must enable at most one of the following features: defmt, log"); + +#[macro_use] +mod macros; +mod sys; + +pub mod raw_socket; +pub mod tuntap_interface; + +pub use sys::wait; + +pub use self::raw_socket::RawSocket; +#[cfg(any(target_os = "linux", target_os = "android"))] +pub use self::tuntap_interface::TunTapInterface; diff --git a/smoltcp-device-unix/src/macros.rs b/smoltcp-device-unix/src/macros.rs new file mode 100644 index 000000000..7ce432784 --- /dev/null +++ b/smoltcp-device-unix/src/macros.rs @@ -0,0 +1,34 @@ +#![allow(unused_macros)] + +#[cfg(not(test))] +#[cfg(feature = "log")] +macro_rules! net_log { + (trace, $($arg:expr),*) => { log::trace!($($arg),*) }; + (debug, $($arg:expr),*) => { log::debug!($($arg),*) }; +} + +#[cfg(test)] +#[cfg(feature = "log")] +macro_rules! net_log { + (trace, $($arg:expr),*) => { println!($($arg),*) }; + (debug, $($arg:expr),*) => { println!($($arg),*) }; +} + +#[cfg(feature = "defmt")] +macro_rules! net_log { + (trace, $($arg:expr),*) => { defmt::trace!($($arg),*) }; + (debug, $($arg:expr),*) => { defmt::debug!($($arg),*) }; +} + +#[cfg(not(any(feature = "log", feature = "defmt")))] +macro_rules! net_log { + ($level:ident, $($arg:expr),*) => {{ $( let _ = $arg; )* }} +} + +macro_rules! net_trace { + ($($arg:expr),*) => (net_log!(trace, $($arg),*)); +} + +macro_rules! net_debug { + ($($arg:expr),*) => (net_log!(debug, $($arg),*)); +} diff --git a/src/phy/raw_socket.rs b/smoltcp-device-unix/src/raw_socket.rs similarity index 90% rename from src/phy/raw_socket.rs rename to smoltcp-device-unix/src/raw_socket.rs index 3c1ad1262..753782aed 100644 --- a/src/phy/raw_socket.rs +++ b/smoltcp-device-unix/src/raw_socket.rs @@ -4,8 +4,8 @@ use std::os::unix::io::{AsRawFd, RawFd}; use std::rc::Rc; use std::vec::Vec; -use crate::phy::{self, sys, Device, DeviceCapabilities, Medium}; -use crate::time::Instant; +use crate::sys; +use smoltcp_device::{time::Instant, Device, DeviceCapabilities, Medium}; /// A socket that captures or transmits the complete frame. #[derive(Debug)] @@ -47,7 +47,7 @@ impl RawSocket { if medium == Medium::Ethernet { // SIOCGIFMTU returns the IP MTU (typically 1500 bytes.) // smoltcp counts the entire Ethernet packet in the MTU, so add the Ethernet header size to it. - mtu += crate::wire::EthernetFrame::<&[u8]>::header_len() + mtu += sys::ETHERNET_HEADER_LEN; } Ok(RawSocket { @@ -69,11 +69,11 @@ impl Device for RawSocket { Self: 'a; fn capabilities(&self) -> DeviceCapabilities { - DeviceCapabilities { - max_transmission_unit: self.mtu, - medium: self.medium, - ..DeviceCapabilities::default() - } + let mut capabilities = DeviceCapabilities::new(self.medium); + + capabilities.max_transmission_unit = self.mtu; + + capabilities } fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { @@ -105,7 +105,7 @@ pub struct RxToken { buffer: Vec, } -impl phy::RxToken for RxToken { +impl smoltcp_device::RxToken for RxToken { fn consume(self, f: F) -> R where F: FnOnce(&[u8]) -> R, @@ -119,7 +119,7 @@ pub struct TxToken { lower: Rc>, } -impl phy::TxToken for TxToken { +impl smoltcp_device::TxToken for TxToken { fn consume(self, len: usize, f: F) -> R where F: FnOnce(&mut [u8]) -> R, diff --git a/src/phy/sys/bpf.rs b/smoltcp-device-unix/src/sys/bpf.rs similarity index 97% rename from src/phy/sys/bpf.rs rename to smoltcp-device-unix/src/sys/bpf.rs index 60f96213b..2a01a6374 100644 --- a/src/phy/sys/bpf.rs +++ b/smoltcp-device-unix/src/sys/bpf.rs @@ -4,9 +4,8 @@ use std::os::unix::io::{AsRawFd, RawFd}; use libc; -use super::{ifreq, ifreq_for}; -use crate::phy::Medium; -use crate::wire::ETHERNET_HEADER_LEN; +use super::{ifreq, ifreq_for, ETHERNET_HEADER_LEN}; +use smoltcp_device::Medium; /// set interface #[cfg(any( @@ -190,17 +189,15 @@ impl Drop for BpfDevice { #[cfg(test)] mod test { - use super::*; - #[test] #[cfg(any(target_os = "macos", target_os = "netbsd"))] fn test_aligned_bpf_hdr_len() { - assert_eq!(18, BPF_HDRLEN); + assert_eq!(18, super::BPF_HDRLEN); } #[test] #[cfg(target_os = "openbsd")] fn test_aligned_bpf_hdr_len() { - assert_eq!(26, BPF_HDRLEN); + assert_eq!(26, super::BPF_HDRLEN); } } diff --git a/src/phy/sys/linux.rs b/smoltcp-device-unix/src/sys/linux.rs similarity index 100% rename from src/phy/sys/linux.rs rename to smoltcp-device-unix/src/sys/linux.rs diff --git a/src/phy/sys/mod.rs b/smoltcp-device-unix/src/sys/mod.rs similarity index 72% rename from src/phy/sys/mod.rs rename to smoltcp-device-unix/src/sys/mod.rs index 3f42301c5..0250050be 100644 --- a/src/phy/sys/mod.rs +++ b/smoltcp-device-unix/src/sys/mod.rs @@ -1,6 +1,6 @@ #![allow(unsafe_code)] -use crate::time::Duration; +use smoltcp_device::time::Duration; use std::os::unix::io::RawFd; use std::{io, mem, ptr}; @@ -8,40 +8,25 @@ use std::{io, mem, ptr}; #[path = "linux.rs"] mod imp; -#[cfg(all( - feature = "phy-raw_socket", - not(any(target_os = "linux", target_os = "android")), - unix -))] -pub mod bpf; -#[cfg(all( - feature = "phy-raw_socket", - any(target_os = "linux", target_os = "android") -))] +#[cfg(not(any(target_os = "linux", target_os = "android")))] +mod bpf; + +#[cfg(any(target_os = "linux", target_os = "android"))] +pub use self::raw_socket::RawSocketDesc; + +#[cfg(not(any(target_os = "linux", target_os = "android")))] +pub use self::bpf::BpfDevice as RawSocketDesc; + +#[cfg(any(target_os = "linux", target_os = "android"))] pub mod raw_socket; -#[cfg(all( - feature = "phy-tuntap_interface", - any(target_os = "linux", target_os = "android") -))] +#[cfg(any(target_os = "linux", target_os = "android"))] pub mod tuntap_interface; -#[cfg(all( - feature = "phy-raw_socket", - not(any(target_os = "linux", target_os = "android")), - unix -))] -pub use self::bpf::BpfDevice as RawSocketDesc; -#[cfg(all( - feature = "phy-raw_socket", - any(target_os = "linux", target_os = "android") -))] -pub use self::raw_socket::RawSocketDesc; -#[cfg(all( - feature = "phy-tuntap_interface", - any(target_os = "linux", target_os = "android") -))] +#[cfg(any(target_os = "linux", target_os = "android"))] pub use self::tuntap_interface::TunTapInterfaceDesc; +pub(crate) const ETHERNET_HEADER_LEN: usize = 14; + /// Wait until given file descriptor becomes readable, but no longer than given timeout. pub fn wait(fd: RawFd, duration: Option) -> io::Result<()> { unsafe { @@ -90,21 +75,14 @@ pub fn wait(fd: RawFd, duration: Option) -> io::Result<()> { } } -#[cfg(all( - any(feature = "phy-tuntap_interface", feature = "phy-raw_socket"), - unix -))] #[repr(C)] #[derive(Debug)] +#[allow(non_camel_case_types)] struct ifreq { ifr_name: [libc::c_char; libc::IF_NAMESIZE], ifr_data: libc::c_int, /* ifr_ifindex or ifr_mtu */ } -#[cfg(all( - any(feature = "phy-tuntap_interface", feature = "phy-raw_socket"), - unix -))] fn ifreq_for(name: &str) -> ifreq { let mut ifreq = ifreq { ifr_name: [0; libc::IF_NAMESIZE], @@ -116,10 +94,6 @@ fn ifreq_for(name: &str) -> ifreq { ifreq } -#[cfg(all( - any(target_os = "linux", target_os = "android"), - any(feature = "phy-tuntap_interface", feature = "phy-raw_socket") -))] fn ifreq_ioctl( lower: libc::c_int, ifreq: &mut ifreq, diff --git a/src/phy/sys/raw_socket.rs b/smoltcp-device-unix/src/sys/raw_socket.rs similarity index 92% rename from src/phy/sys/raw_socket.rs rename to smoltcp-device-unix/src/sys/raw_socket.rs index f37fe960f..d82593519 100644 --- a/src/phy/sys/raw_socket.rs +++ b/smoltcp-device-unix/src/sys/raw_socket.rs @@ -1,5 +1,5 @@ use super::*; -use crate::phy::Medium; +use smoltcp_device::Medium; use std::os::unix::io::{AsRawFd, RawFd}; use std::{io, mem}; @@ -18,13 +18,16 @@ impl AsRawFd for RawSocketDesc { impl RawSocketDesc { pub fn new(name: &str, medium: Medium) -> io::Result { - let protocol = match medium { + #[allow(unreachable_patterns)] + let protocol: libc::c_short = match medium { #[cfg(feature = "medium-ethernet")] Medium::Ethernet => imp::ETH_P_ALL, #[cfg(feature = "medium-ip")] Medium::Ip => imp::ETH_P_ALL, #[cfg(feature = "medium-ieee802154")] Medium::Ieee802154 => imp::ETH_P_IEEE802154, + // Just in case another crate provides a medium that we don't: + _ => unreachable!("Medium {medium:?} is not provided."), }; let lower = unsafe { diff --git a/src/phy/sys/tuntap_interface.rs b/smoltcp-device-unix/src/sys/tuntap_interface.rs similarity index 87% rename from src/phy/sys/tuntap_interface.rs rename to smoltcp-device-unix/src/sys/tuntap_interface.rs index 3019cadea..33fff8e28 100644 --- a/src/phy/sys/tuntap_interface.rs +++ b/smoltcp-device-unix/src/sys/tuntap_interface.rs @@ -1,5 +1,5 @@ use super::*; -use crate::{phy::Medium, wire::EthernetFrame}; +use smoltcp_device::Medium; use std::io; use std::os::unix::io::{AsRawFd, RawFd}; @@ -44,14 +44,18 @@ impl TunTapInterfaceDesc { medium: Medium, ifr: &mut ifreq, ) -> io::Result<()> { - let mode = match medium { + #[allow(unreachable_patterns)] + let mode: libc::c_int = match medium { #[cfg(feature = "medium-ip")] Medium::Ip => imp::IFF_TUN, #[cfg(feature = "medium-ethernet")] Medium::Ethernet => imp::IFF_TAP, #[cfg(feature = "medium-ieee802154")] Medium::Ieee802154 => todo!(), + // Just in case another crate provides a medium that we don't: + _ => unreachable!("Medium {medium:?} is not provided."), }; + ifr.ifr_data = mode | imp::IFF_NO_PI; ifreq_ioctl(lower, ifr, imp::TUNSETIFF).map(|_| ()) } @@ -76,13 +80,16 @@ impl TunTapInterfaceDesc { // SIOCGIFMTU returns the IP MTU (typically 1500 bytes.) // smoltcp counts the entire Ethernet packet in the MTU, so add the Ethernet header size to it. + #[allow(unreachable_patterns)] let mtu = match medium { #[cfg(feature = "medium-ip")] Medium::Ip => ip_mtu, #[cfg(feature = "medium-ethernet")] - Medium::Ethernet => ip_mtu + EthernetFrame::<&[u8]>::header_len(), + Medium::Ethernet => ip_mtu + ETHERNET_HEADER_LEN, #[cfg(feature = "medium-ieee802154")] Medium::Ieee802154 => todo!(), + // Just in case another crate provides a medium that we don't: + _ => unreachable!("Medium {medium:?} is not provided."), }; Ok(mtu) diff --git a/src/phy/tuntap_interface.rs b/smoltcp-device-unix/src/tuntap_interface.rs similarity index 90% rename from src/phy/tuntap_interface.rs rename to smoltcp-device-unix/src/tuntap_interface.rs index 45c96f508..fafdcb177 100644 --- a/src/phy/tuntap_interface.rs +++ b/smoltcp-device-unix/src/tuntap_interface.rs @@ -4,8 +4,10 @@ use std::os::unix::io::{AsRawFd, RawFd}; use std::rc::Rc; use std::vec::Vec; -use crate::phy::{self, sys, Device, DeviceCapabilities, Medium}; -use crate::time::Instant; +use super::sys; + +use smoltcp_device::time::Instant; +use smoltcp_device::{Device, DeviceCapabilities, Medium}; /// A virtual TUN (IP) or TAP (Ethernet) interface. #[derive(Debug)] @@ -56,11 +58,11 @@ impl Device for TunTapInterface { type TxToken<'a> = TxToken; fn capabilities(&self) -> DeviceCapabilities { - DeviceCapabilities { - max_transmission_unit: self.mtu, - medium: self.medium, - ..DeviceCapabilities::default() - } + let mut capabilities = DeviceCapabilities::new(self.medium); + + capabilities.max_transmission_unit = self.mtu; + + capabilities } fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { @@ -92,7 +94,7 @@ pub struct RxToken { buffer: Vec, } -impl phy::RxToken for RxToken { +impl smoltcp_device::RxToken for RxToken { fn consume(self, f: F) -> R where F: FnOnce(&[u8]) -> R, @@ -106,7 +108,7 @@ pub struct TxToken { lower: Rc>, } -impl phy::TxToken for TxToken { +impl smoltcp_device::TxToken for TxToken { fn consume(self, len: usize, f: F) -> R where F: FnOnce(&mut [u8]) -> R, diff --git a/smoltcp-device/Cargo.toml b/smoltcp-device/Cargo.toml new file mode 100644 index 000000000..eb54ac638 --- /dev/null +++ b/smoltcp-device/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "smoltcp-device" +version = "0.1.0" +edition = "2021" +rust-version = "1.81" + +[dependencies] +defmt = { version = "0.3.8", optional = true, features = ["ip_in_core"] } + +[features] +defmt = ["dep:defmt"] + +std = [] + +# Set by device driver crates +"provides-medium-ethernet" = [] +"provides-medium-ip" = [] +"provides-medium-ieee802154" = [] + +# Set by smoltcp +"requires-medium-ethernet" = [] +"requires-medium-ip" = [] +"requires-medium-ieee802154" = [] + +"packetmeta-id" = [] diff --git a/smoltcp-device/src/lib.rs b/smoltcp-device/src/lib.rs new file mode 100644 index 000000000..94dc21127 --- /dev/null +++ b/smoltcp-device/src/lib.rs @@ -0,0 +1,391 @@ +#![cfg_attr(not(any(test, feature = "std")), no_std)] +#![deny(unsafe_code)] + +/*! Networking hardware abstraction. + +This crate deals with the *network devices*. It provides a trait +for transmitting and receiving frames, [Device](trait.Device.html). +*/ +#![cfg_attr( + feature = "provides-medium-ethernet", + doc = r##" +# Examples + +An implementation of the [Device](trait.Device.html) trait for a simple hardware +Ethernet controller could look as follows: + +```rust +use smoltcp_device::{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 smoltcp_device::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> { + Some(StmPhyTxToken(&mut self.tx_buffer[..])) + } + + fn capabilities(&self) -> DeviceCapabilities { + let mut caps = DeviceCapabilities::new(Medium::Ethernet); + caps.max_transmission_unit = 1536; + caps.max_burst_size = Some(1); + caps + } +} + +struct StmPhyRxToken<'a>(&'a mut [u8]); + +impl<'a> smoltcp_device::RxToken for StmPhyRxToken<'a> { + fn consume(self, f: F) -> R + where F: FnOnce(& [u8]) -> R + { + // TODO: receive packet into buffer + let result = f(&self.0); + println!("rx called"); + result + } +} + +struct StmPhyTxToken<'a>(&'a mut [u8]); + +impl<'a> smoltcp_device::TxToken for StmPhyTxToken<'a> { + fn consume(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 + } +} +``` +"## +)] +#![allow(clippy::match_like_matches_macro)] +#![allow(clippy::redundant_field_names)] +#![allow(clippy::identity_op)] +#![allow(clippy::option_map_unit_fn)] +#![allow(clippy::unit_arg)] +#![allow(clippy::new_without_default)] + +// Check that required media are provided +#[cfg(all(feature = "requires-medium-ip", not(feature = "provides-medium-ip")))] +compile_error!("smoltcp requires the IP medium, but no network device implementation in the project supports it. Either disable the medium in smoltcp, or add a device implementation that provides it."); + +#[cfg(all( + feature = "requires-medium-ethernet", + not(feature = "provides-medium-ethernet") +))] +compile_error!("smoltcp requires the Ethernet medium, but no network device implementation in the project supports it. Either disable the medium in smoltcp, or add a device implementation that provides it."); + +#[cfg(all( + feature = "requires-medium-ieee802154", + not(feature = "provides-medium-ieee802154") +))] +compile_error!( + "smoltcp requires the IEEE 802.15.4 medium, but no network device implementation in the project supports it. Either disable the medium in smoltcp, or add a device implementation that provides it." +); + +// Check that provided media are required. This ensures that smoltcp includes all code necessary to handle the media. + +#[cfg(all(feature = "provides-medium-ip", not(feature = "requires-medium-ip")))] +compile_error!("A smoltcp network device provides the IP medium, but smoltcp is not configured to support it. Either disable the medium in your network devices, or enable it in smoltcp."); + +#[cfg(all( + feature = "provides-medium-ethernet", + not(feature = "requires-medium-ethernet"), +))] +compile_error!("A smoltcp network device provides the Ethernet medium, but smoltcp is not configured to support it. Either disable the medium in your network devices, or enable it in smoltcp."); + +#[cfg(all( + feature = "provides-medium-ieee802154", + not(feature = "requires-medium-ieee802154"), +))] +compile_error!("A smoltcp network device provides the IEEE 802.15.4 medium, but smoltcp is not configured to support it. Either disable the medium in your network devices, or enable it in smoltcp."); + +pub mod time; + +use time::Instant; + +/// 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, + pub icmpv4: Checksum, + 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, + icmpv4: Checksum::None, + 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)] +#[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, + + /// 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 { + /// Creates a new `DeviceCapabilities` struct using the given `medium`. + pub fn new(medium: Medium) -> Self { + Self { + medium, + max_transmission_unit: Default::default(), + max_burst_size: Default::default(), + checksum: Default::default(), + } + } +} + +/// 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 = "provides-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 = "provides-medium-ip")] + Ip, + + #[cfg(feature = "provides-medium-ieee802154")] + Ieee802154, +} + +/// 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>; + + /// 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(self, f: F) -> R + where + F: FnOnce(&[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(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) {} +} + +/// 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 using setters. This makes adding +/// metadata fields, as well as mixing multiple device implementations with different +/// metadata fields, a non-breaking change. +/// +/// ```rust +/// let mut meta = smoltcp_device::PacketMeta::default(); +/// #[cfg(feature = "packetmeta-id")] +/// { +/// meta.set_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")] + id: Option, +} + +impl PacketMeta { + #[inline] + pub fn set_id(&mut self, _id: u32) { + #[cfg(feature = "packetmeta-id")] + { + self.id = Some(_id); + } + } + + #[inline] + pub fn id(&self) -> Option { + #[cfg(feature = "packetmeta-id")] + { + self.id + } + + #[cfg(not(feature = "packetmeta-id"))] + { + None + } + } +} diff --git a/src/time.rs b/smoltcp-device/src/time.rs similarity index 100% rename from src/time.rs rename to smoltcp-device/src/time.rs diff --git a/src/iface/interface/mod.rs b/src/iface/interface/mod.rs index 8b6fce4a7..a6627f91e 100644 --- a/src/iface/interface/mod.rs +++ b/src/iface/interface/mod.rs @@ -30,9 +30,7 @@ use core::result::Result; use heapless::Vec; #[cfg(feature = "_proto-fragmentation")] -use super::fragmentation::FragKey; -#[cfg(any(feature = "proto-ipv4", feature = "proto-sixlowpan"))] -use super::fragmentation::PacketAssemblerSet; +use super::fragmentation::{FragKey, PacketAssemblerSet}; use super::fragmentation::{Fragmenter, FragmentsBuffer}; #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] @@ -783,7 +781,15 @@ impl InterfaceInner { #[allow(unused)] // unused depending on which sockets are enabled pub(crate) fn ip_mtu(&self) -> usize { - self.caps.ip_mtu() + match self.caps.medium { + #[cfg(feature = "medium-ethernet")] + Medium::Ethernet => self.caps.max_transmission_unit - 14, + #[cfg(feature = "medium-ip")] + Medium::Ip => self.caps.max_transmission_unit, + #[cfg(feature = "medium-ieee802154")] + Medium::Ieee802154 => self.caps.max_transmission_unit, // TODO(thvdveld): what is the MTU for Medium::IEEE802 + _ => unimplemented!(), + } } #[allow(unused)] // unused depending on which sockets are enabled, and in tests @@ -1210,7 +1216,7 @@ impl InterfaceInner { #[cfg(feature = "proto-ipv4")] IpRepr::Ipv4(repr) => { // If we have an IPv4 packet, then we need to check if we need to fragment it. - if total_ip_len > self.caps.ip_mtu() { + if total_ip_len > self.ip_mtu() { #[cfg(feature = "proto-ipv4-fragmentation")] { net_debug!("start fragmentation"); @@ -1219,7 +1225,7 @@ impl InterfaceInner { let tx_len = self.caps.max_transmission_unit; let ip_header_len = repr.buffer_len(); - let first_frag_ip_len = self.caps.ip_mtu(); + let first_frag_ip_len = self.ip_mtu(); if frag.buffer.len() < total_ip_len { net_debug!( @@ -1305,7 +1311,7 @@ impl InterfaceInner { #[cfg(feature = "proto-ipv6")] IpRepr::Ipv6(_) => { // Check if we need to fragment it. - if total_ip_len > self.caps.ip_mtu() { + if total_ip_len > self.ip_mtu() { net_debug!("IPv6 fragmentation support is unimplemented. Dropping."); Ok(()) } else { diff --git a/src/lib.rs b/src/lib.rs index 0f81a7474..3a1453602 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -166,9 +166,10 @@ pub mod phy; #[cfg(feature = "socket")] pub mod socket; pub mod storage; -pub mod time; pub mod wire; +pub use smoltcp_device::time; + #[cfg(all( test, any( diff --git a/src/phy/fault_injector.rs b/src/phy/fault_injector.rs index 1f30db549..66ff751fa 100644 --- a/src/phy/fault_injector.rs +++ b/src/phy/fault_injector.rs @@ -217,7 +217,7 @@ impl Device for FaultInjector { let (rx_token, tx_token) = self.inner.receive(timestamp)?; let rx_meta = as phy::RxToken>::meta(&rx_token); - let len = super::RxToken::consume(rx_token, |buffer| { + let len = phy::RxToken::consume(rx_token, |buffer| { if (self.config.max_size > 0 && buffer.len() > self.config.max_size) || buffer.len() > self.rx_buf.len() { diff --git a/src/phy/loopback.rs b/src/phy/loopback.rs index fd3aea7d1..f535fa2a8 100644 --- a/src/phy/loopback.rs +++ b/src/phy/loopback.rs @@ -31,12 +31,12 @@ impl Device for Loopback { type TxToken<'a> = TxToken<'a>; fn capabilities(&self) -> DeviceCapabilities { - DeviceCapabilities { - max_transmission_unit: 65535, - medium: self.medium, - checksum: ChecksumCapabilities::ignored(), - ..DeviceCapabilities::default() - } + let mut capabilities = DeviceCapabilities::new(self.medium); + + capabilities.max_transmission_unit = 65535; + capabilities.checksum = ChecksumCapabilities::ignored(); + + capabilities } fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { diff --git a/src/phy/mod.rs b/src/phy/mod.rs index 79bacce9d..6fcca4ebc 100644 --- a/src/phy/mod.rs +++ b/src/phy/mod.rs @@ -1,7 +1,7 @@ /*! 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) +for transmitting and receiving frames, [Device][smoltcp_device::Device] and implementations of it: * the [_loopback_](struct.Loopback.html), for zero dependency testing; @@ -10,91 +10,9 @@ and implementations of it: * _adapters_ [RawSocket](struct.RawSocket.html) and [TunTapInterface](struct.TunTapInterface.html), to transmit and receive frames on the host OS. -*/ -#![cfg_attr( - feature = "medium-ethernet", - doc = r##" -# Examples - -An implementation of the [Device](trait.Device.html) 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> { - 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(self, f: F) -> R - where F: FnOnce(& [u8]) -> R - { - // TODO: receive packet into buffer - let result = f(&self.0); - println!("rx called"); - result - } -} - -struct StmPhyTxToken<'a>(&'a mut [u8]); - -impl<'a> phy::TxToken for StmPhyTxToken<'a> { - fn consume(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; +For information about implementing [Device], refer to the [`smoltcp_device`] crate. +*/ mod fault_injector; #[cfg(feature = "alloc")] @@ -102,20 +20,7 @@ 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; #[cfg(feature = "alloc")] @@ -123,278 +28,6 @@ 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, TracerDirection, TracerPacket}; -#[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, - - /// 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>; - - /// 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(self, f: F) -> R - where - F: FnOnce(&[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(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) {} -} +pub use smoltcp_device::*; diff --git a/src/phy/tracer.rs b/src/phy/tracer.rs index 4fa9417ae..f24eeaa04 100644 --- a/src/phy/tracer.rs +++ b/src/phy/tracer.rs @@ -216,7 +216,6 @@ mod tests { use super::*; - use crate::phy::ChecksumCapabilities; use crate::{ phy::{Device, Loopback, RxToken, TxToken}, time::Instant, @@ -235,7 +234,22 @@ mod tests { } TRACE_EVENTS.replace(VecDeque::new()); - let medium = Medium::default(); + fn default_medium() -> 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; + } + + let medium = default_medium(); let loopback_device = Loopback::new(medium); let mut tracer_device = Tracer::new(loopback_device, |instant, packet| { @@ -325,6 +339,7 @@ mod tests { #[cfg(all(feature = "medium-ip", feature = "proto-ipv4"))] #[test] fn test_tracer_packet_display_ip() { + use crate::phy::ChecksumCapabilities; use crate::wire::{IpProtocol, Ipv4Address, Ipv4Repr}; let repr = Ipv4Repr { diff --git a/src/socket/dhcpv4.rs b/src/socket/dhcpv4.rs index 7d8c920ad..c204d73c5 100644 --- a/src/socket/dhcpv4.rs +++ b/src/socket/dhcpv4.rs @@ -742,7 +742,7 @@ impl<'a> Socket<'a> { /// /// The socket has an internal "configuration changed" flag. If /// set, this function returns the configuration and resets the flag. - pub fn poll(&mut self) -> Option { + pub fn poll(&mut self) -> Option> { if !self.config_changed { None } else if let ClientState::Renewing(state) = &self.state { diff --git a/src/storage/assembler.rs b/src/storage/assembler.rs index 365a1e03a..26f38da20 100644 --- a/src/storage/assembler.rs +++ b/src/storage/assembler.rs @@ -327,7 +327,7 @@ impl Assembler { /// |--- 100 ---|--- 200 ---|--- 100 ---| /// /// An offset of 1500 would return the ranges: ``(1500, 1600), (1800, 1900)`` - pub fn iter_data(&self, first_offset: usize) -> AssemblerIter { + pub fn iter_data(&self, first_offset: usize) -> AssemblerIter<'_> { AssemblerIter::new(self, first_offset) } } diff --git a/src/tests.rs b/src/tests.rs index 165bd52f7..77f23f255 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -1,7 +1,7 @@ -use std::collections::VecDeque; +pub(crate) use smoltcp_device_mock::TestingDevice; use crate::iface::*; -use crate::phy::{self, Device, DeviceCapabilities, Medium}; +use crate::phy::Medium; use crate::time::Instant; use crate::wire::*; @@ -52,96 +52,3 @@ pub(crate) fn setup<'a>(medium: Medium) -> (Interface, SocketSet<'a>, TestingDev (iface, SocketSet::new(vec![]), device) } - -/// A testing device. -#[derive(Debug)] -pub struct TestingDevice { - pub(crate) tx_queue: VecDeque>, - pub(crate) rx_queue: VecDeque>, - max_transmission_unit: usize, - medium: Medium, -} - -#[allow(clippy::new_without_default)] -impl TestingDevice { - /// Creates a testing device. - /// - /// Every packet transmitted through this device will be received through it - /// in FIFO order. - pub fn new(medium: Medium) -> Self { - TestingDevice { - tx_queue: VecDeque::new(), - rx_queue: VecDeque::new(), - max_transmission_unit: match medium { - #[cfg(feature = "medium-ethernet")] - Medium::Ethernet => 1514, - #[cfg(feature = "medium-ip")] - Medium::Ip => 1500, - #[cfg(feature = "medium-ieee802154")] - Medium::Ieee802154 => 1500, - }, - medium, - } - } -} - -impl Device for TestingDevice { - type RxToken<'a> = RxToken; - type TxToken<'a> = TxToken<'a>; - - fn capabilities(&self) -> DeviceCapabilities { - DeviceCapabilities { - medium: self.medium, - max_transmission_unit: self.max_transmission_unit, - ..DeviceCapabilities::default() - } - } - - fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { - self.rx_queue.pop_front().map(move |buffer| { - let rx = RxToken { buffer }; - let tx = TxToken { - queue: &mut self.tx_queue, - }; - (rx, tx) - }) - } - - fn transmit(&mut self, _timestamp: Instant) -> Option> { - Some(TxToken { - queue: &mut self.tx_queue, - }) - } -} - -#[doc(hidden)] -pub struct RxToken { - buffer: Vec, -} - -impl phy::RxToken for RxToken { - fn consume(self, f: F) -> R - where - F: FnOnce(&[u8]) -> R, - { - f(&self.buffer) - } -} - -#[doc(hidden)] -#[derive(Debug)] -pub struct TxToken<'a> { - queue: &'a mut VecDeque>, -} - -impl<'a> phy::TxToken for TxToken<'a> { - fn consume(self, len: usize, f: F) -> R - where - F: FnOnce(&mut [u8]) -> R, - { - let mut buffer = vec![0; len]; - let result = f(&mut buffer); - self.queue.push_back(buffer); - result - } -} diff --git a/src/wire/sixlowpan/iphc.rs b/src/wire/sixlowpan/iphc.rs index 429114128..a8374e14e 100644 --- a/src/wire/sixlowpan/iphc.rs +++ b/src/wire/sixlowpan/iphc.rs @@ -218,7 +218,7 @@ impl> Packet { } /// Return the Source Address. - pub fn src_addr(&self) -> Result { + pub fn src_addr(&self) -> Result> { let start = (self.ip_fields_start() + self.traffic_class_size() + self.next_header_size() @@ -275,7 +275,7 @@ impl> Packet { } /// Return the Destination Address. - pub fn dst_addr(&self) -> Result { + pub fn dst_addr(&self) -> Result> { let start = (self.ip_fields_start() + self.traffic_class_size() + self.next_header_size() diff --git a/tests/netsim.rs b/tests/netsim.rs index e7e5c47ef..4a0d29030 100644 --- a/tests/netsim.rs +++ b/tests/netsim.rs @@ -268,10 +268,11 @@ impl Device for QueueDevice<'_> { Self: 'a; fn capabilities(&self) -> DeviceCapabilities { - let mut caps = DeviceCapabilities::default(); + let mut caps = DeviceCapabilities::new(self.medium); + caps.max_transmission_unit = 1514; - caps.medium = self.medium; caps.checksum = ChecksumCapabilities::ignored(); + caps }