From 9ac2cac07514818e40e8e9d6abbd641bcf23d905 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 4 Jun 2020 03:00:49 +0200 Subject: [PATCH] Add support for IP mediums. - Add `medium` in `DeviceCapabilities`. - Rename EthernetInterface to Interface. - Add support to Interface for both Ethernet and IP mediums. The medium to use is detected from `device.capabilities().medium`. - Ethernet-only features are gated behind the "ethernet" feature, as before. - IP features are always enabled for now. --- examples/benchmark.rs | 4 +- examples/client.rs | 4 +- examples/dhcp_client.rs | 4 +- examples/httpclient.rs | 4 +- examples/loopback.rs | 8 +- examples/multicast.rs | 4 +- examples/ping.rs | 4 +- examples/server.rs | 4 +- fuzz/fuzz_targets/tcp_headers.rs | 8 +- src/dhcp/clientv4.rs | 2 +- src/iface/{ethernet.rs => interface.rs} | 673 +++++++++++++----------- src/iface/mod.rs | 8 +- src/phy/loopback.rs | 7 +- src/phy/mod.rs | 42 +- src/phy/raw_socket.rs | 3 +- src/phy/tap_interface.rs | 3 +- 16 files changed, 439 insertions(+), 343 deletions(-) rename src/iface/{ethernet.rs => interface.rs} (85%) diff --git a/examples/benchmark.rs b/examples/benchmark.rs index ce307db..9ce2f57 100644 --- a/examples/benchmark.rs +++ b/examples/benchmark.rs @@ -13,7 +13,7 @@ use log::debug; use smoltcp::phy::wait as phy_wait; use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr}; -use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder}; +use smoltcp::iface::{NeighborCache, InterfaceBuilder}; use smoltcp::socket::SocketSet; use smoltcp::socket::{TcpSocket, TcpSocketBuffer}; use smoltcp::time::{Duration, Instant}; @@ -90,7 +90,7 @@ fn main() { let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]); let ip_addrs = [IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)]; - let mut iface = EthernetInterfaceBuilder::new(device) + let mut iface = InterfaceBuilder::new(device) .ethernet_addr(ethernet_addr) .neighbor_cache(neighbor_cache) .ip_addrs(ip_addrs) diff --git a/examples/client.rs b/examples/client.rs index 66f5fb7..ecce1c7 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -7,7 +7,7 @@ use log::debug; use smoltcp::phy::wait as phy_wait; use smoltcp::wire::{EthernetAddress, Ipv4Address, IpAddress, IpCidr}; -use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder, Routes}; +use smoltcp::iface::{NeighborCache, InterfaceBuilder, Routes}; use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer}; use smoltcp::time::Instant; @@ -39,7 +39,7 @@ fn main() { let mut routes_storage = [None; 1]; let mut routes = Routes::new(&mut routes_storage[..]); routes.add_default_ipv4_route(default_v4_gw).unwrap(); - let mut iface = EthernetInterfaceBuilder::new(device) + let mut iface = InterfaceBuilder::new(device) .ethernet_addr(ethernet_addr) .neighbor_cache(neighbor_cache) .ip_addrs(ip_addrs) diff --git a/examples/dhcp_client.rs b/examples/dhcp_client.rs index 7fa009c..6e9a670 100644 --- a/examples/dhcp_client.rs +++ b/examples/dhcp_client.rs @@ -5,7 +5,7 @@ use std::collections::BTreeMap; use std::os::unix::io::AsRawFd; use smoltcp::phy::wait as phy_wait; use smoltcp::wire::{EthernetAddress, Ipv4Address, IpCidr, Ipv4Cidr}; -use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder, Routes}; +use smoltcp::iface::{NeighborCache, InterfaceBuilder, Routes}; use smoltcp::socket::{SocketSet, RawSocketBuffer, RawPacketMetadata}; use smoltcp::time::Instant; use smoltcp::dhcp::Dhcpv4Client; @@ -28,7 +28,7 @@ fn main() { let ip_addrs = [IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 0)]; let mut routes_storage = [None; 1]; let routes = Routes::new(&mut routes_storage[..]); - let mut iface = EthernetInterfaceBuilder::new(device) + let mut iface = InterfaceBuilder::new(device) .ethernet_addr(ethernet_addr) .neighbor_cache(neighbor_cache) .ip_addrs(ip_addrs) diff --git a/examples/httpclient.rs b/examples/httpclient.rs index 2ca8844..be52306 100644 --- a/examples/httpclient.rs +++ b/examples/httpclient.rs @@ -8,7 +8,7 @@ use log::debug; use smoltcp::phy::wait as phy_wait; use smoltcp::wire::{EthernetAddress, Ipv4Address, Ipv6Address, IpAddress, IpCidr}; -use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder, Routes}; +use smoltcp::iface::{NeighborCache, InterfaceBuilder, Routes}; use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer}; use smoltcp::time::Instant; @@ -45,7 +45,7 @@ fn main() { let mut routes = Routes::new(&mut routes_storage[..]); routes.add_default_ipv4_route(default_v4_gw).unwrap(); routes.add_default_ipv6_route(default_v6_gw).unwrap(); - let mut iface = EthernetInterfaceBuilder::new(device) + let mut iface = InterfaceBuilder::new(device) .ethernet_addr(ethernet_addr) .neighbor_cache(neighbor_cache) .ip_addrs(ip_addrs) diff --git a/examples/loopback.rs b/examples/loopback.rs index c84f286..13d0479 100644 --- a/examples/loopback.rs +++ b/examples/loopback.rs @@ -9,9 +9,9 @@ mod utils; use core::str; use log::{info, debug, error}; -use smoltcp::phy::Loopback; +use smoltcp::phy::{Loopback, Medium}; use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr}; -use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder}; +use smoltcp::iface::{NeighborCache, InterfaceBuilder}; use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer}; use smoltcp::time::{Duration, Instant}; @@ -65,7 +65,7 @@ mod mock { fn main() { let clock = mock::Clock::new(); - let device = Loopback::new(); + let device = Loopback::new(Medium::Ethernet); #[cfg(feature = "std")] let device = { @@ -83,7 +83,7 @@ fn main() { let mut neighbor_cache = NeighborCache::new(&mut neighbor_cache_entries[..]); let mut ip_addrs = [IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)]; - let mut iface = EthernetInterfaceBuilder::new(device) + let mut iface = InterfaceBuilder::new(device) .ethernet_addr(EthernetAddress::default()) .neighbor_cache(neighbor_cache) .ip_addrs(ip_addrs) diff --git a/examples/multicast.rs b/examples/multicast.rs index e637467..cffb73a 100644 --- a/examples/multicast.rs +++ b/examples/multicast.rs @@ -7,7 +7,7 @@ use log::debug; use smoltcp::phy::wait as phy_wait; use smoltcp::wire::{EthernetAddress, IpVersion, IpProtocol, IpAddress, IpCidr, Ipv4Address, Ipv4Packet, IgmpPacket, IgmpRepr}; -use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder}; +use smoltcp::iface::{NeighborCache, InterfaceBuilder}; use smoltcp::socket::{SocketSet, RawSocket, RawSocketBuffer, RawPacketMetadata, UdpSocket, UdpSocketBuffer, UdpPacketMetadata}; @@ -37,7 +37,7 @@ fn main() { let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]); let ip_addr = IpCidr::new(IpAddress::from(local_addr), 24); let mut ipv4_multicast_storage = [None; 1]; - let mut iface = EthernetInterfaceBuilder::new(device) + let mut iface = InterfaceBuilder::new(device) .ethernet_addr(ethernet_addr) .neighbor_cache(neighbor_cache) .ip_addrs([ip_addr]) diff --git a/examples/ping.rs b/examples/ping.rs index 1d290d4..69f3de4 100644 --- a/examples/ping.rs +++ b/examples/ping.rs @@ -14,7 +14,7 @@ use smoltcp::phy::wait as phy_wait; use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv6Address, Icmpv6Repr, Icmpv6Packet, Ipv4Address, Icmpv4Repr, Icmpv4Packet}; -use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder, Routes}; +use smoltcp::iface::{NeighborCache, InterfaceBuilder, Routes}; use smoltcp::socket::{SocketSet, IcmpSocket, IcmpSocketBuffer, IcmpPacketMetadata, IcmpEndpoint}; macro_rules! send_icmp_ping { @@ -98,7 +98,7 @@ fn main() { let mut routes = Routes::new(&mut routes_storage[..]); routes.add_default_ipv4_route(default_v4_gw).unwrap(); routes.add_default_ipv6_route(default_v6_gw).unwrap(); - let mut iface = EthernetInterfaceBuilder::new(device) + let mut iface = InterfaceBuilder::new(device) .ethernet_addr(ethernet_addr) .ip_addrs(ip_addrs) .routes(routes) diff --git a/examples/server.rs b/examples/server.rs index 95675c7..832f975 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -8,7 +8,7 @@ use log::debug; use smoltcp::phy::wait as phy_wait; use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr}; -use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder}; +use smoltcp::iface::{NeighborCache, InterfaceBuilder}; use smoltcp::socket::SocketSet; use smoltcp::socket::{UdpSocket, UdpSocketBuffer, UdpPacketMetadata}; use smoltcp::socket::{TcpSocket, TcpSocketBuffer}; @@ -54,7 +54,7 @@ fn main() { IpCidr::new(IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1), 64), IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64) ]; - let mut iface = EthernetInterfaceBuilder::new(device) + let mut iface = InterfaceBuilder::new(device) .ethernet_addr(ethernet_addr) .neighbor_cache(neighbor_cache) .ip_addrs(ip_addrs) diff --git a/fuzz/fuzz_targets/tcp_headers.rs b/fuzz/fuzz_targets/tcp_headers.rs index fec2772..aec7a25 100644 --- a/fuzz/fuzz_targets/tcp_headers.rs +++ b/fuzz/fuzz_targets/tcp_headers.rs @@ -6,10 +6,10 @@ use std as core; extern crate getopts; use core::cmp; -use smoltcp::phy::Loopback; +use smoltcp::phy::{Loopback, Medium}; use smoltcp::wire::{EthernetAddress, EthernetFrame, EthernetProtocol}; use smoltcp::wire::{IpAddress, IpCidr, Ipv4Packet, Ipv6Packet, TcpPacket}; -use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder}; +use smoltcp::iface::{NeighborCache, InterfaceBuilder}; use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer}; use smoltcp::time::{Duration, Instant}; @@ -118,7 +118,7 @@ fuzz_target!(|data: &[u8]| { utils::add_middleware_options(&mut opts, &mut free); let mut matches = utils::parse_options(&opts, free); - let device = utils::parse_middleware_options(&mut matches, Loopback::new(), + let device = utils::parse_middleware_options(&mut matches, Loopback::new(Medium::Ethernet), /*loopback=*/true); smoltcp::phy::FuzzInjector::new(device, @@ -130,7 +130,7 @@ fuzz_target!(|data: &[u8]| { let neighbor_cache = NeighborCache::new(&mut neighbor_cache_entries[..]); let ip_addrs = [IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)]; - let mut iface = EthernetInterfaceBuilder::new(device) + let mut iface = InterfaceBuilder::new(device) .ethernet_addr(EthernetAddress::default()) .neighbor_cache(neighbor_cache) .ip_addrs(ip_addrs) diff --git a/src/dhcp/clientv4.rs b/src/dhcp/clientv4.rs index 30efa8e..e5029df 100644 --- a/src/dhcp/clientv4.rs +++ b/src/dhcp/clientv4.rs @@ -6,7 +6,7 @@ use crate::wire::{IpVersion, IpProtocol, IpEndpoint, IpAddress, use crate::wire::dhcpv4::field as dhcpv4_field; use crate::socket::{SocketSet, SocketHandle, RawSocket, RawSocketBuffer}; use crate::phy::{Device, ChecksumCapabilities}; -use crate::iface::EthernetInterface as Interface; +use crate::iface::Interface; use crate::time::{Instant, Duration}; use super::{UDP_SERVER_PORT, UDP_CLIENT_PORT}; diff --git a/src/iface/ethernet.rs b/src/iface/interface.rs similarity index 85% rename from src/iface/ethernet.rs rename to src/iface/interface.rs index bb60bfb..5fd518f 100644 --- a/src/iface/ethernet.rs +++ b/src/iface/interface.rs @@ -6,51 +6,15 @@ use core::cmp; use managed::{ManagedSlice, ManagedMap}; use crate::{Error, Result}; -use crate::phy::{Device, DeviceCapabilities, RxToken, TxToken}; +use crate::phy::{Device, DeviceCapabilities, RxToken, TxToken, Medium}; use crate::time::{Duration, Instant}; -use crate::wire::pretty_print::PrettyPrinter; -use crate::wire::{EthernetAddress, EthernetProtocol, EthernetFrame}; -use crate::wire::{IpAddress, IpProtocol, IpRepr, IpCidr}; -#[cfg(feature = "proto-ipv6")] -use crate::wire::{Ipv6Address, Ipv6Packet, Ipv6Repr, IPV6_MIN_MTU}; -#[cfg(feature = "proto-ipv4")] -use crate::wire::{Ipv4Address, Ipv4Packet, Ipv4Repr, IPV4_MIN_MTU}; -#[cfg(feature = "proto-ipv4")] -use crate::wire::{ArpPacket, ArpRepr, ArpOperation}; -#[cfg(feature = "proto-ipv4")] -use crate::wire::{Icmpv4Packet, Icmpv4Repr, Icmpv4DstUnreachable}; -#[cfg(feature = "proto-igmp")] -use crate::wire::{IgmpPacket, IgmpRepr, IgmpVersion}; -#[cfg(feature = "proto-ipv6")] -use crate::wire::{Icmpv6Packet, Icmpv6Repr, Icmpv6ParamProblem}; -#[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))] -use crate::wire::IcmpRepr; -#[cfg(feature = "proto-ipv6")] -use crate::wire::{Ipv6HopByHopHeader, Ipv6HopByHopRepr}; -#[cfg(feature = "proto-ipv6")] -use crate::wire::{Ipv6OptionRepr, Ipv6OptionFailureType}; -#[cfg(feature = "proto-ipv6")] -use crate::wire::{NdiscNeighborFlags, NdiscRepr}; -#[cfg(all(feature = "proto-ipv6", feature = "socket-udp"))] -use crate::wire::Icmpv6DstUnreachable; -#[cfg(feature = "socket-udp")] -use crate::wire::{UdpPacket, UdpRepr}; -#[cfg(feature = "socket-tcp")] -use crate::wire::{TcpPacket, TcpRepr, TcpControl}; - -use crate::socket::{Socket, SocketSet, AnySocket, PollAt}; -#[cfg(feature = "socket-raw")] -use crate::socket::RawSocket; -#[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))] -use crate::socket::IcmpSocket; -#[cfg(feature = "socket-udp")] -use crate::socket::UdpSocket; -#[cfg(feature = "socket-tcp")] -use crate::socket::TcpSocket; +use crate::wire::*; +use crate::socket::*; +#[cfg(feature = "ethernet")] use crate::iface::{NeighborCache, NeighborAnswer}; use crate::iface::Routes; -/// An Ethernet network interface. +/// A network interface. /// /// The network interface logically owns a number of other data structures; to avoid /// a dependency on heap allocation, it instead owns a `BorrowMut<[T]>`, which can be @@ -68,8 +32,10 @@ pub struct Interface<'a, DeviceT: for<'d> Device<'d>> { /// methods on the `Interface` in this time (since its `device` field is borrowed /// exclusively). However, it is still possible to call methods on its `inner` field. struct InterfaceInner<'a> { - neighbor_cache: NeighborCache<'a>, - ethernet_addr: EthernetAddress, + #[cfg(feature = "ethernet")] + neighbor_cache: Option>, + #[cfg(feature = "ethernet")] + ethernet_addr: Option, ip_addrs: ManagedSlice<'a, IpCidr>, #[cfg(feature = "proto-ipv4")] any_ip: bool, @@ -82,11 +48,12 @@ struct InterfaceInner<'a> { device_capabilities: DeviceCapabilities, } -/// A builder structure used for creating a Ethernet network -/// interface. +/// A builder structure used for creating a network interface. pub struct InterfaceBuilder <'a, DeviceT: for<'d> Device<'d>> { device: DeviceT, + #[cfg(feature = "ethernet")] ethernet_addr: Option, + #[cfg(feature = "ethernet")] neighbor_cache: Option>, ip_addrs: ManagedSlice<'a, IpCidr>, #[cfg(feature = "proto-ipv4")] @@ -101,33 +68,36 @@ impl<'a, DeviceT> InterfaceBuilder<'a, DeviceT> where DeviceT: for<'d> Device<'d> { /// Create a builder used for creating a network interface using the /// given device and address. - /// - /// # Examples - /// - /// ``` - /// # use std::collections::BTreeMap; - /// use smoltcp::iface::{EthernetInterfaceBuilder, NeighborCache}; - /// # use smoltcp::phy::Loopback; - /// use smoltcp::wire::{EthernetAddress, IpCidr, IpAddress}; - /// - /// let device = // ... - /// # Loopback::new(); - /// let hw_addr = // ... - /// # EthernetAddress::default(); - /// let neighbor_cache = // ... - /// # NeighborCache::new(BTreeMap::new()); - /// let ip_addrs = // ... - /// # []; - /// let iface = EthernetInterfaceBuilder::new(device) - /// .ethernet_addr(hw_addr) - /// .neighbor_cache(neighbor_cache) - /// .ip_addrs(ip_addrs) - /// .finalize(); - /// ``` + #[cfg_attr(feature = "ethernet", doc = r##" +# Examples + +``` +# use std::collections::BTreeMap; +use smoltcp::iface::{InterfaceBuilder, NeighborCache}; +# use smoltcp::phy::{Loopback, Medium}; +use smoltcp::wire::{EthernetAddress, IpCidr, IpAddress}; + +let device = // ... +# Loopback::new(Medium::Ethernet); +let hw_addr = // ... +# EthernetAddress::default(); +let neighbor_cache = // ... +# NeighborCache::new(BTreeMap::new()); +let ip_addrs = // ... +# []; +let iface = InterfaceBuilder::new(device) + .ethernet_addr(hw_addr) + .neighbor_cache(neighbor_cache) + .ip_addrs(ip_addrs) + .finalize(); +``` + "##)] pub fn new(device: DeviceT) -> Self { InterfaceBuilder { device: device, + #[cfg(feature = "ethernet")] ethernet_addr: None, + #[cfg(feature = "ethernet")] neighbor_cache: None, ip_addrs: ManagedSlice::Borrowed(&mut []), #[cfg(feature = "proto-ipv4")] @@ -144,7 +114,8 @@ impl<'a, DeviceT> InterfaceBuilder<'a, DeviceT> /// # Panics /// This function panics if the address is not unicast. /// - /// [ethernet_addr]: struct.EthernetInterface.html#method.ethernet_addr + /// [ethernet_addr]: struct.Interface.html#method.ethernet_addr + #[cfg(feature = "ethernet")] pub fn ethernet_addr(mut self, addr: EthernetAddress) -> Self { InterfaceInner::check_ethernet_addr(&addr); self.ethernet_addr = Some(addr); @@ -157,7 +128,7 @@ impl<'a, DeviceT> InterfaceBuilder<'a, DeviceT> /// # Panics /// This function panics if any of the addresses are not unicast. /// - /// [ip_addrs]: struct.EthernetInterface.html#method.ip_addrs + /// [ip_addrs]: struct.Interface.html#method.ip_addrs pub fn ip_addrs(mut self, ip_addrs: T) -> Self where T: Into> { @@ -178,8 +149,8 @@ impl<'a, DeviceT> InterfaceBuilder<'a, DeviceT> /// This option is not available or required for IPv6 as packets sent to /// the interface are not filtered by IPv6 address. /// - /// [routes]: struct.EthernetInterface.html#method.routes - /// [ip_addrs]: struct.EthernetInterface.html#method.ip_addrs + /// [routes]: struct.Interface.html#method.routes + /// [ip_addrs]: struct.Interface.html#method.ip_addrs #[cfg(feature = "proto-ipv4")] pub fn any_ip(mut self, enabled: bool) -> Self { self.any_ip = enabled; @@ -189,7 +160,7 @@ impl<'a, DeviceT> InterfaceBuilder<'a, DeviceT> /// Set the IP routes the interface will use. See also /// [routes]. /// - /// [routes]: struct.EthernetInterface.html#method.routes + /// [routes]: struct.Interface.html#method.routes pub fn routes(mut self, routes: T) -> InterfaceBuilder<'a, DeviceT> where T: Into> { @@ -206,7 +177,7 @@ impl<'a, DeviceT> InterfaceBuilder<'a, DeviceT> /// storage, i.e. providing a non-empty storage to `ipv4_multicast_groups()`. /// Note that this way initial membership reports are **not** sent. /// - /// [`join_multicast_group()`]: struct.EthernetInterface.html#method.join_multicast_group + /// [`join_multicast_group()`]: struct.Interface.html#method.join_multicast_group #[cfg(feature = "proto-igmp")] pub fn ipv4_multicast_groups(mut self, ipv4_multicast_groups: T) -> Self where T: Into> @@ -216,6 +187,7 @@ impl<'a, DeviceT> InterfaceBuilder<'a, DeviceT> } /// Set the Neighbor Cache the interface will use. + #[cfg(feature = "ethernet")] pub fn neighbor_cache(mut self, neighbor_cache: NeighborCache<'a>) -> Self { self.neighbor_cache = Some(neighbor_cache); self @@ -233,38 +205,55 @@ impl<'a, DeviceT> InterfaceBuilder<'a, DeviceT> /// [ethernet_addr]: #method.ethernet_addr /// [neighbor_cache]: #method.neighbor_cache pub fn finalize(self) -> Interface<'a, DeviceT> { - match (self.ethernet_addr, self.neighbor_cache) { - (Some(ethernet_addr), Some(neighbor_cache)) => { - let device_capabilities = self.device.capabilities(); + let device_capabilities = self.device.capabilities(); - Interface { - device: self.device, - inner: InterfaceInner { - ethernet_addr, device_capabilities, neighbor_cache, - ip_addrs: self.ip_addrs, - #[cfg(feature = "proto-ipv4")] - any_ip: self.any_ip, - routes: self.routes, - #[cfg(feature = "proto-igmp")] - ipv4_multicast_groups: self.ipv4_multicast_groups, - #[cfg(feature = "proto-igmp")] - igmp_report_state: IgmpReportState::Inactive, - } - } - }, - _ => panic!("a required option was not set"), + #[cfg(feature = "ethernet")] + let mut ethernet_addr = None; + #[cfg(feature = "ethernet")] + let mut neighbor_cache = None; + match device_capabilities.medium { + #[cfg(feature = "ethernet")] + Medium::Ethernet => { + ethernet_addr = Some(self.ethernet_addr.expect("ethernet_addr required option was not set")); + neighbor_cache = Some(self.neighbor_cache.expect("neighbor_cache required option was not set")); + } + Medium::Ip => { + #[cfg(feature = "ethernet")] + assert!(self.ethernet_addr.is_none(), "ethernet_addr is set, but device medium is IP"); + #[cfg(feature = "ethernet")] + assert!(self.neighbor_cache.is_none(), "neighbor_cache is set, but device medium is IP"); + } + } + + Interface { + device: self.device, + inner: InterfaceInner { + #[cfg(feature = "ethernet")] + ethernet_addr, + ip_addrs: self.ip_addrs, + #[cfg(feature = "proto-ipv4")] + any_ip: self.any_ip, + routes: self.routes, + device_capabilities, + #[cfg(feature = "ethernet")] + neighbor_cache, + #[cfg(feature = "proto-igmp")] + ipv4_multicast_groups: self.ipv4_multicast_groups, + #[cfg(feature = "proto-igmp")] + igmp_report_state: IgmpReportState::Inactive, + } } } } #[derive(Debug, PartialEq)] +#[cfg(feature = "ethernet")] enum EthernetPacket<'a> { #[cfg(feature = "proto-ipv4")] Arp(ArpRepr), Ip(IpPacket<'a>), } - #[derive(Debug, PartialEq)] pub(crate) enum IpPacket<'a> { #[cfg(feature = "proto-ipv4")] @@ -378,17 +367,25 @@ enum IgmpReportState { impl<'a, DeviceT> Interface<'a, DeviceT> where DeviceT: for<'d> Device<'d> { /// Get the Ethernet address of the interface. + /// + /// # Panics + /// This function panics if if the interface's medium is not Ethernet. + + #[cfg(feature = "ethernet")] pub fn ethernet_addr(&self) -> EthernetAddress { - self.inner.ethernet_addr + self.inner.ethernet_addr.unwrap() } /// Set the Ethernet address of the interface. /// /// # Panics - /// This function panics if the address is not unicast. + /// This function panics if the address is not unicast, or if the + /// interface's medium is not Ethernet. + #[cfg(feature = "ethernet")] pub fn set_ethernet_addr(&mut self, addr: EthernetAddress) { - self.inner.ethernet_addr = addr; - InterfaceInner::check_ethernet_addr(&self.inner.ethernet_addr); + assert!(self.device.capabilities().medium == Medium::Ethernet); + InterfaceInner::check_ethernet_addr(&addr); + self.inner.ethernet_addr = Some(addr); } /// Get a reference to the inner device. @@ -424,7 +421,7 @@ impl<'a, DeviceT> Interface<'a, DeviceT> self.inner.igmp_report_packet(IgmpVersion::Version2, addr) { // Send initial membership report let tx_token = self.device.transmit().ok_or(Error::Exhausted)?; - self.inner.dispatch(tx_token, _timestamp, EthernetPacket::Ip(pkt))?; + self.inner.dispatch_ip(tx_token, _timestamp, pkt)?; Ok(true) } else { Ok(false) @@ -450,7 +447,7 @@ impl<'a, DeviceT> Interface<'a, DeviceT> } else if let Some(pkt) = self.inner.igmp_leave_packet(addr) { // Send group leave packet let tx_token = self.device.transmit().ok_or(Error::Exhausted)?; - self.inner.dispatch(tx_token, _timestamp, EthernetPacket::Ip(pkt))?; + self.inner.dispatch_ip(tx_token, _timestamp, pkt)?; Ok(true) } else { Ok(false) @@ -593,23 +590,47 @@ impl<'a, DeviceT> Interface<'a, DeviceT> Some(tokens) => tokens, }; rx_token.consume(timestamp, |frame| { - inner.process_ethernet(sockets, timestamp, &frame).map_err(|err| { - net_debug!("cannot process ingress packet: {}", err); - net_debug!("packet dump follows:\n{}", - PrettyPrinter::>::new("", &frame)); - err - }).and_then(|response| { - processed_any = true; - match response { - Some(packet) => { - inner.dispatch(tx_token, timestamp, packet).map_err(|err| { - net_debug!("cannot dispatch response packet: {}", err); - err - }) - } - None => Ok(()) + match inner.device_capabilities.medium { + #[cfg(feature = "ethernet")] + Medium::Ethernet => { + inner.process_ethernet(sockets, timestamp, &frame).map_err(|err| { + net_debug!("cannot process ingress packet: {}", err); + net_debug!("packet dump follows:\n{}", + PrettyPrinter::>::new("", &frame)); + err + }).and_then(|response| { + processed_any = true; + match response { + Some(packet) => { + inner.dispatch(tx_token, timestamp, packet).map_err(|err| { + net_debug!("cannot dispatch response packet: {}", err); + err + }) + } + None => Ok(()) + } + }) } - }) + Medium::Ip => { + inner.process_ip(sockets, timestamp, &frame).map_err(|err| { + net_debug!("cannot process ingress packet: {}", err); + //net_debug!("packet dump follows:\n{}", + // PrettyPrinter::>::new("", &frame)); + err + }).and_then(|response| { + processed_any = true; + match response { + Some(packet) => { + inner.dispatch_ip(tx_token, timestamp, packet).map_err(|err| { + net_debug!("cannot dispatch response packet: {}", err); + err + }) + } + None => Ok(()) + } + }) + } + } })?; } Ok(processed_any) @@ -617,7 +638,12 @@ impl<'a, DeviceT> Interface<'a, DeviceT> fn socket_egress(&mut self, sockets: &mut SocketSet, timestamp: Instant) -> Result { let mut caps = self.device.capabilities(); - caps.max_transmission_unit -= EthernetFrame::<&[u8]>::header_len(); + match caps.medium { + #[cfg(feature = "ethernet")] + Medium::Ethernet => + caps.max_transmission_unit -= EthernetFrame::<&[u8]>::header_len(), + _ => {} + } let mut emitted_any = false; for mut socket in sockets.iter_mut() { @@ -634,9 +660,8 @@ impl<'a, DeviceT> Interface<'a, DeviceT> ($response:expr) => ({ let response = $response; neighbor_addr = Some(response.ip_repr().dst_addr()); - let response = EthernetPacket::Ip(response); let tx_token = device.transmit().ok_or(Error::Exhausted)?; - device_result = inner.dispatch(tx_token, timestamp, response); + device_result = inner.dispatch_ip(tx_token, timestamp, response); device_result }) } @@ -703,7 +728,7 @@ impl<'a, DeviceT> Interface<'a, DeviceT> if let Some(pkt) = self.inner.igmp_report_packet(version, group) { // Send initial membership report let tx_token = self.device.transmit().ok_or(Error::Exhausted)?; - self.inner.dispatch(tx_token, timestamp, EthernetPacket::Ip(pkt))?; + self.inner.dispatch_ip(tx_token, timestamp, pkt)?; } self.inner.igmp_report_state = IgmpReportState::Inactive; @@ -721,7 +746,7 @@ impl<'a, DeviceT> Interface<'a, DeviceT> if let Some(pkt) = self.inner.igmp_report_packet(version, addr) { // Send initial membership report let tx_token = self.device.transmit().ok_or(Error::Exhausted)?; - self.inner.dispatch(tx_token, timestamp, EthernetPacket::Ip(pkt))?; + self.inner.dispatch_ip(tx_token, timestamp, pkt)?; } let next_timeout = (timeout + interval).max(timestamp); @@ -743,6 +768,7 @@ impl<'a, DeviceT> Interface<'a, DeviceT> } impl<'a> InterfaceInner<'a> { + #[cfg(feature = "ethernet")] fn check_ethernet_addr(addr: &EthernetAddress) { if addr.is_multicast() { panic!("Ethernet address {} is not unicast", addr) @@ -810,6 +836,7 @@ impl<'a> InterfaceInner<'a> { } } + #[cfg(feature = "ethernet")] fn process_ethernet<'frame, T: AsRef<[u8]>> (&mut self, sockets: &mut SocketSet, timestamp: Instant, frame: &'frame T) -> Result>> @@ -819,7 +846,7 @@ impl<'a> InterfaceInner<'a> { // Ignore any packets not directed to our hardware address or any of the multicast groups. if !eth_frame.dst_addr().is_broadcast() && !eth_frame.dst_addr().is_multicast() && - eth_frame.dst_addr() != self.ethernet_addr + eth_frame.dst_addr() != self.ethernet_addr.unwrap() { return Ok(None) } @@ -829,17 +856,58 @@ impl<'a> InterfaceInner<'a> { EthernetProtocol::Arp => self.process_arp(timestamp, ð_frame), #[cfg(feature = "proto-ipv4")] - EthernetProtocol::Ipv4 => - self.process_ipv4(sockets, timestamp, ð_frame).map(|o| o.map(EthernetPacket::Ip)), + EthernetProtocol::Ipv4 => { + let ipv4_packet = Ipv4Packet::new_checked(eth_frame.payload())?; + if eth_frame.src_addr().is_unicast() && ipv4_packet.src_addr().is_unicast() { + // Fill the neighbor cache from IP header of unicast frames. + let ip_addr = IpAddress::Ipv4(ipv4_packet.src_addr()); + if self.in_same_network(&ip_addr) { + self.neighbor_cache.as_mut().unwrap().fill(ip_addr, eth_frame.src_addr(), timestamp); + } + } + + self.process_ipv4(sockets, timestamp, &ipv4_packet).map(|o| o.map(EthernetPacket::Ip)) + } #[cfg(feature = "proto-ipv6")] - EthernetProtocol::Ipv6 => - self.process_ipv6(sockets, timestamp, ð_frame).map(|o| o.map(EthernetPacket::Ip)), + EthernetProtocol::Ipv6 => { + let ipv6_packet = Ipv6Packet::new_checked(eth_frame.payload())?; + if eth_frame.src_addr().is_unicast() && ipv6_packet.src_addr().is_unicast() { + // Fill the neighbor cache from IP header of unicast frames. + let ip_addr = IpAddress::Ipv6(ipv6_packet.src_addr()); + if self.in_same_network(&ip_addr) && + self.neighbor_cache.as_mut().unwrap().lookup(&ip_addr, timestamp).found() { + self.neighbor_cache.as_mut().unwrap().fill(ip_addr, eth_frame.src_addr(), timestamp); + } + } + + self.process_ipv6(sockets, timestamp, &ipv6_packet).map(|o| o.map(EthernetPacket::Ip)) + } // Drop all other traffic. _ => Err(Error::Unrecognized), } } - #[cfg(feature = "proto-ipv4")] + fn process_ip<'frame, T: AsRef<[u8]>> + (&mut self, sockets: &mut SocketSet, timestamp: Instant, ip_payload: &'frame T) -> + Result>> + { + match IpVersion::of_packet(ip_payload.as_ref()) { + #[cfg(feature = "proto-ipv4")] + Ok(IpVersion::Ipv4) => { + let ipv4_packet = Ipv4Packet::new_checked(ip_payload)?; + self.process_ipv4(sockets, timestamp, &ipv4_packet) + } + #[cfg(feature = "proto-ipv6")] + Ok(IpVersion::Ipv6) => { + let ipv6_packet = Ipv6Packet::new_checked(ip_payload)?; + self.process_ipv6(sockets, timestamp, &ipv6_packet) + } + // Drop all other traffic. + _ => Err(Error::Unrecognized), + } + } + + #[cfg(all(feature = "ethernet", feature = "proto-ipv4"))] fn process_arp<'frame, T: AsRef<[u8]>> (&mut self, timestamp: Instant, eth_frame: &EthernetFrame<&'frame T>) -> Result>> @@ -855,7 +923,7 @@ impl<'a> InterfaceInner<'a> { operation, source_hardware_addr, source_protocol_addr, target_protocol_addr, .. } => { if source_protocol_addr.is_unicast() && source_hardware_addr.is_unicast() { - self.neighbor_cache.fill(source_protocol_addr.into(), + self.neighbor_cache.as_mut().unwrap().fill(source_protocol_addr.into(), source_hardware_addr, timestamp); } else { @@ -867,7 +935,7 @@ impl<'a> InterfaceInner<'a> { if operation == ArpOperation::Request && self.has_ip_addr(target_protocol_addr) { Ok(Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 { operation: ArpOperation::Reply, - source_hardware_addr: self.ethernet_addr, + source_hardware_addr: self.ethernet_addr.unwrap(), source_protocol_addr: target_protocol_addr, target_hardware_addr: source_hardware_addr, target_protocol_addr: source_protocol_addr @@ -902,12 +970,10 @@ impl<'a> InterfaceInner<'a> { } #[cfg(feature = "proto-ipv6")] - fn process_ipv6<'frame, T: AsRef<[u8]>> + fn process_ipv6<'frame, T: AsRef<[u8]> + ?Sized> (&mut self, sockets: &mut SocketSet, timestamp: Instant, - eth_frame: &EthernetFrame<&'frame T>) -> - Result>> - { - let ipv6_packet = Ipv6Packet::new_checked(eth_frame.payload())?; + ipv6_packet: &Ipv6Packet<&'frame T>) -> + Result>> { let ipv6_repr = Ipv6Repr::parse(&ipv6_packet)?; if !ipv6_repr.src_addr.is_unicast() { @@ -916,15 +982,6 @@ impl<'a> InterfaceInner<'a> { return Err(Error::Malformed) } - if eth_frame.src_addr().is_unicast() { - // Fill the neighbor cache from IP header of unicast frames. - let ip_addr = IpAddress::Ipv6(ipv6_repr.src_addr); - if self.in_same_network(&ip_addr) && - !self.neighbor_cache.lookup(&ip_addr, timestamp).found() { - self.neighbor_cache.fill(ip_addr, eth_frame.src_addr(), timestamp); - } - } - let ip_payload = ipv6_packet.payload(); #[cfg(feature = "socket-raw")] @@ -980,12 +1037,11 @@ impl<'a> InterfaceInner<'a> { } #[cfg(feature = "proto-ipv4")] - fn process_ipv4<'frame, T: AsRef<[u8]>> + fn process_ipv4<'frame, T: AsRef<[u8]> + ?Sized> (&mut self, sockets: &mut SocketSet, timestamp: Instant, - eth_frame: &EthernetFrame<&'frame T>) -> + ipv4_packet: &Ipv4Packet<&'frame T>) -> Result>> { - let ipv4_packet = Ipv4Packet::new_checked(eth_frame.payload())?; let checksum_caps = self.device_capabilities.checksum.clone(); let ipv4_repr = Ipv4Repr::parse(&ipv4_packet, &checksum_caps)?; @@ -995,14 +1051,6 @@ impl<'a> InterfaceInner<'a> { return Err(Error::Malformed) } - if eth_frame.src_addr().is_unicast() { - // Fill the neighbor cache from IP header of unicast frames. - let ip_addr = IpAddress::Ipv4(ipv4_repr.src_addr); - if self.in_same_network(&ip_addr) { - self.neighbor_cache.fill(ip_addr, eth_frame.src_addr(), timestamp); - } - } - let ip_repr = IpRepr::Ipv4(ipv4_repr); let ip_payload = ipv4_packet.payload(); @@ -1126,7 +1174,7 @@ impl<'a> InterfaceInner<'a> { } #[cfg(feature = "proto-ipv6")] - fn process_icmpv6<'frame>(&mut self, _sockets: &mut SocketSet, timestamp: Instant, + fn process_icmpv6<'frame>(&mut self, _sockets: &mut SocketSet, _timestamp: Instant, ip_repr: IpRepr, ip_payload: &'frame [u8]) -> Result>> { let icmp_packet = Icmpv6Packet::new_checked(ip_payload)?; @@ -1167,8 +1215,9 @@ impl<'a> InterfaceInner<'a> { Icmpv6Repr::EchoReply { .. } => Ok(None), // Forward any NDISC packets to the ndisc packet handler + #[cfg(feature = "ethernet")] Icmpv6Repr::Ndisc(repr) if ip_repr.hop_limit() == 0xff => match ip_repr { - IpRepr::Ipv6(ipv6_repr) => self.process_ndisc(timestamp, ipv6_repr, repr), + IpRepr::Ipv6(ipv6_repr) => self.process_ndisc(_timestamp, ipv6_repr, repr), _ => Ok(None) }, @@ -1182,7 +1231,7 @@ impl<'a> InterfaceInner<'a> { } } - #[cfg(feature = "proto-ipv6")] + #[cfg(all(feature = "ethernet", feature = "proto-ipv6"))] fn process_ndisc<'frame>(&mut self, timestamp: Instant, ip_repr: Ipv6Repr, repr: NdiscRepr<'frame>) -> Result>> { match repr { @@ -1191,8 +1240,8 @@ impl<'a> InterfaceInner<'a> { match lladdr { Some(lladdr) if lladdr.is_unicast() && target_addr.is_unicast() => { if flags.contains(NdiscNeighborFlags::OVERRIDE) || - !self.neighbor_cache.lookup(&ip_addr, timestamp).found() { - self.neighbor_cache.fill(ip_addr, lladdr, timestamp) + !self.neighbor_cache.as_mut().unwrap().lookup(&ip_addr, timestamp).found() { + self.neighbor_cache.as_mut().unwrap().fill(ip_addr, lladdr, timestamp) } }, _ => (), @@ -1202,7 +1251,7 @@ impl<'a> InterfaceInner<'a> { NdiscRepr::NeighborSolicit { target_addr, lladdr, .. } => { match lladdr { Some(lladdr) if lladdr.is_unicast() && target_addr.is_unicast() => { - self.neighbor_cache.fill(ip_repr.src_addr.into(), lladdr, timestamp) + self.neighbor_cache.as_mut().unwrap().fill(ip_repr.src_addr.into(), lladdr, timestamp) }, _ => (), } @@ -1210,7 +1259,7 @@ impl<'a> InterfaceInner<'a> { let advert = Icmpv6Repr::Ndisc(NdiscRepr::NeighborAdvert { flags: NdiscNeighborFlags::SOLICITED, target_addr: target_addr, - lladdr: Some(self.ethernet_addr) + lladdr: Some(self.ethernet_addr.unwrap()) }); let ip_repr = Ipv6Repr { src_addr: target_addr, @@ -1454,6 +1503,7 @@ impl<'a> InterfaceInner<'a> { } } + #[cfg(feature = "ethernet")] fn dispatch(&mut self, tx_token: Tx, timestamp: Instant, packet: EthernetPacket) -> Result<()> where Tx: TxToken @@ -1480,6 +1530,7 @@ impl<'a> InterfaceInner<'a> { } } + #[cfg(feature = "ethernet")] fn dispatch_ethernet(&mut self, tx_token: Tx, timestamp: Instant, buffer_len: usize, f: F) -> Result<()> where Tx: TxToken, F: FnOnce(EthernetFrame<&mut [u8]>) @@ -1488,7 +1539,7 @@ impl<'a> InterfaceInner<'a> { tx_token.consume(timestamp, tx_len, |tx_buffer| { debug_assert!(tx_buffer.as_ref().len() == tx_len); let mut frame = EthernetFrame::new_unchecked(tx_buffer); - frame.set_src_addr(self.ethernet_addr); + frame.set_src_addr(self.ethernet_addr.unwrap()); f(frame); @@ -1517,15 +1568,20 @@ impl<'a> InterfaceInner<'a> { fn has_neighbor(&self, addr: &IpAddress, timestamp: Instant) -> bool { match self.route(addr, timestamp) { - Ok(routed_addr) => { - self.neighbor_cache - .lookup(&routed_addr, timestamp) - .found() + Ok(_routed_addr) => { + match self.device_capabilities.medium { + #[cfg(feature = "ethernet")] + Medium::Ethernet => self.neighbor_cache.as_ref().unwrap() + .lookup(&_routed_addr, timestamp) + .found(), + Medium::Ip => true, + } } Err(_) => false } } + #[cfg(feature = "ethernet")] fn lookup_hardware_addr(&mut self, tx_token: Tx, timestamp: Instant, src_addr: &IpAddress, dst_addr: &IpAddress) -> Result<(EthernetAddress, Tx)> @@ -1559,7 +1615,7 @@ impl<'a> InterfaceInner<'a> { let dst_addr = self.route(dst_addr, timestamp)?; - match self.neighbor_cache.lookup(&dst_addr, timestamp) { + match self.neighbor_cache.as_mut().unwrap().lookup(&dst_addr, timestamp) { NeighborAnswer::Found(hardware_addr) => return Ok((hardware_addr, tx_token)), NeighborAnswer::RateLimited => @@ -1575,7 +1631,7 @@ impl<'a> InterfaceInner<'a> { let arp_repr = ArpRepr::EthernetIpv4 { operation: ArpOperation::Request, - source_hardware_addr: self.ethernet_addr, + source_hardware_addr: self.ethernet_addr.unwrap(), source_protocol_addr: src_addr, target_hardware_addr: EthernetAddress::BROADCAST, target_protocol_addr: dst_addr, @@ -1596,7 +1652,7 @@ impl<'a> InterfaceInner<'a> { let solicit = Icmpv6Repr::Ndisc(NdiscRepr::NeighborSolicit { target_addr: dst_addr, - lladdr: Some(self.ethernet_addr), + lladdr: Some(self.ethernet_addr.unwrap()), }); let packet = IpPacket::Icmpv6(( @@ -1616,7 +1672,7 @@ impl<'a> InterfaceInner<'a> { _ => () } // The request got dispatched, limit the rate on the cache. - self.neighbor_cache.limit_rate(timestamp); + self.neighbor_cache.as_mut().unwrap().limit_rate(timestamp); Err(Error::Unaddressable) } @@ -1625,25 +1681,43 @@ impl<'a> InterfaceInner<'a> { let ip_repr = packet.ip_repr().lower(&self.ip_addrs)?; let caps = self.device_capabilities.clone(); - let (dst_hardware_addr, tx_token) = - self.lookup_hardware_addr(tx_token, timestamp, - &ip_repr.src_addr(), &ip_repr.dst_addr())?; + match self.device_capabilities.medium { + #[cfg(feature = "ethernet")] + Medium::Ethernet => { + let (dst_hardware_addr, tx_token) = + self.lookup_hardware_addr(tx_token, timestamp, + &ip_repr.src_addr(), &ip_repr.dst_addr())?; - self.dispatch_ethernet(tx_token, timestamp, ip_repr.total_len(), |mut frame| { - frame.set_dst_addr(dst_hardware_addr); - match ip_repr { - #[cfg(feature = "proto-ipv4")] - IpRepr::Ipv4(_) => frame.set_ethertype(EthernetProtocol::Ipv4), - #[cfg(feature = "proto-ipv6")] - IpRepr::Ipv6(_) => frame.set_ethertype(EthernetProtocol::Ipv6), - _ => return + self.dispatch_ethernet(tx_token, timestamp, ip_repr.total_len(), |mut frame| { + frame.set_dst_addr(dst_hardware_addr); + match ip_repr { + #[cfg(feature = "proto-ipv4")] + IpRepr::Ipv4(_) => frame.set_ethertype(EthernetProtocol::Ipv4), + #[cfg(feature = "proto-ipv6")] + IpRepr::Ipv6(_) => frame.set_ethertype(EthernetProtocol::Ipv6), + _ => return + } + + ip_repr.emit(frame.payload_mut(), &caps.checksum); + + let payload = &mut frame.payload_mut()[ip_repr.buffer_len()..]; + packet.emit_payload(ip_repr, payload, &caps); + }) } + Medium::Ip => { + let tx_len = ip_repr.total_len(); + tx_token.consume(timestamp, tx_len, |mut tx_buffer| { + debug_assert!(tx_buffer.as_ref().len() == tx_len); - ip_repr.emit(frame.payload_mut(), &caps.checksum); + ip_repr.emit(&mut tx_buffer, &caps.checksum); - let payload = &mut frame.payload_mut()[ip_repr.buffer_len()..]; - packet.emit_payload(ip_repr, payload, &caps); - }) + let payload = &mut tx_buffer[ip_repr.buffer_len()..]; + packet.emit_payload(ip_repr, payload, &caps); + + Ok(()) + }) + } + } } #[cfg(feature = "proto-igmp")] @@ -1687,42 +1761,44 @@ mod test { use std::vec::Vec; use std::collections::BTreeMap; + use super::*; + use crate::{Result, Error}; - use super::InterfaceBuilder; - use crate::iface::{NeighborCache, EthernetInterface}; - use crate::phy::{self, Loopback, ChecksumCapabilities}; + #[cfg(feature = "medium-ethernet")] + use crate::iface::NeighborCache; + use crate::iface::Interface; #[cfg(feature = "proto-igmp")] - use crate::phy::{Device, RxToken, TxToken}; use crate::time::Instant; use crate::socket::SocketSet; - #[cfg(feature = "proto-ipv4")] - use crate::wire::{ArpOperation, ArpPacket, ArpRepr}; - use crate::wire::{EthernetAddress, EthernetFrame, EthernetProtocol}; - use crate::wire::{IpAddress, IpCidr, IpProtocol, IpRepr}; - #[cfg(feature = "proto-ipv4")] - use crate::wire::{Ipv4Address, Ipv4Repr, Ipv4Cidr}; - #[cfg(feature = "proto-igmp")] - use crate::wire::Ipv4Packet; - #[cfg(feature = "proto-ipv4")] - use crate::wire::{Icmpv4Repr, Icmpv4DstUnreachable}; - #[cfg(feature = "proto-igmp")] - use crate::wire::{IgmpPacket, IgmpRepr, IgmpVersion}; - #[cfg(all(feature = "socket-udp", any(feature = "proto-ipv4", feature = "proto-ipv6")))] - use crate::wire::{UdpPacket, UdpRepr}; - #[cfg(feature = "proto-ipv6")] - use crate::wire::{Ipv6Address, Ipv6Repr}; - #[cfg(feature = "proto-ipv6")] - use crate::wire::{Icmpv6Packet, Icmpv6Repr, Icmpv6ParamProblem}; - #[cfg(feature = "proto-ipv6")] - use crate::wire::{NdiscNeighborFlags, NdiscRepr}; - #[cfg(feature = "proto-ipv6")] - use crate::wire::{Ipv6HopByHopHeader, Ipv6Option, Ipv6OptionRepr}; + use crate::phy::{Loopback, ChecksumCapabilities}; - use super::{EthernetPacket, IpPacket}; - - fn create_loopback<'a>() -> (EthernetInterface<'a, Loopback>, SocketSet<'a>) { + fn create_loopback<'a>() -> (Interface<'a, Loopback>, SocketSet<'a>) { // Create a basic device - let device = Loopback::new(); + let device = Loopback::new(Medium::Ip); + let ip_addrs = [ + #[cfg(feature = "proto-ipv4")] + IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8), + #[cfg(feature = "proto-ipv6")] + IpCidr::new(IpAddress::v6(0, 0, 0, 0, 0, 0, 0, 1), 128), + #[cfg(feature = "proto-ipv6")] + IpCidr::new(IpAddress::v6(0xfdbe, 0, 0, 0, 0, 0, 0, 1), 64), + ]; + + let iface_builder = InterfaceBuilder::new(device) + .ip_addrs(ip_addrs); + #[cfg(feature = "proto-igmp")] + let iface_builder = iface_builder + .ipv4_multicast_groups(BTreeMap::new()); + let iface = iface_builder + .finalize(); + + (iface, SocketSet::new(vec![])) + } + + #[cfg(all(feature = "ethernet"))] + fn create_loopback_ethernet<'a>() -> (Interface<'a, Loopback>, SocketSet<'a>) { + // Create a basic device + let device = Loopback::new(Medium::Ethernet); let ip_addrs = [ #[cfg(feature = "proto-ipv4")] IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8), @@ -1746,7 +1822,7 @@ mod test { } #[cfg(feature = "proto-igmp")] - fn recv_all(iface: &mut EthernetInterface<'_, Loopback>, timestamp: Instant) -> Vec> { + fn recv_all(iface: &mut Interface<'_, Loopback>, timestamp: Instant) -> Vec> { let mut pkts = Vec::new(); while let Some((rx, _tx)) = iface.device.receive() { rx.consume(timestamp, |pkt| { @@ -1760,7 +1836,7 @@ mod test { #[derive(Debug, PartialEq)] struct MockTxToken; - impl phy::TxToken for MockTxToken { + impl TxToken for MockTxToken { fn consume(self, _: Instant, _: usize, _: F) -> Result where F: FnOnce(&mut [u8]) -> Result { Err(Error::Unaddressable) @@ -1768,23 +1844,22 @@ mod test { } #[test] - #[should_panic(expected = "a required option was not set")] + #[should_panic(expected = "ethernet_addr required option was not set")] + #[cfg(all(feature = "ethernet"))] fn test_builder_initialization_panic() { - InterfaceBuilder::new(Loopback::new()).finalize(); + InterfaceBuilder::new(Loopback::new(Medium::Ethernet)).finalize(); } #[test] - fn test_no_icmp_no_unicast() { + #[cfg(feature = "proto-ipv4")] + fn test_no_icmp_no_unicast_ipv4() { let (mut iface, mut socket_set) = create_loopback(); - let mut eth_bytes = vec![0u8; 54]; - // Unknown Ipv4 Protocol // // Because the destination is the broadcast address // this should not trigger and Destination Unreachable // response. See RFC 1122 § 3.2.2. - #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] let repr = IpRepr::Ipv4(Ipv4Repr { src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]), dst_addr: Ipv4Address::BROADCAST, @@ -1792,7 +1867,28 @@ mod test { payload_len: 0, hop_limit: 0x40 }); - #[cfg(feature = "proto-ipv6")] + + let mut bytes = vec![0u8; 54]; + repr.emit(&mut bytes, &ChecksumCapabilities::default()); + let frame = Ipv4Packet::new_unchecked(&bytes); + + // Ensure that the unknown protocol frame does not trigger an + // ICMP error response when the destination address is a + // broadcast address + assert_eq!(iface.inner.process_ipv4(&mut socket_set, Instant::from_millis(0), &frame), + Ok(None)); + } + + #[test] + #[cfg(feature = "proto-ipv6")] + fn test_no_icmp_no_unicast_ipv6() { + let (mut iface, mut socket_set) = create_loopback(); + + // Unknown Ipv6 Protocol + // + // Because the destination is the broadcast address + // this should not trigger and Destination Unreachable + // response. See RFC 1122 § 3.2.2. let repr = IpRepr::Ipv6(Ipv6Repr { src_addr: Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1), dst_addr: Ipv6Address::LINK_LOCAL_ALL_NODES, @@ -1801,22 +1897,13 @@ mod test { hop_limit: 0x40 }); - let frame = { - let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes); - frame.set_dst_addr(EthernetAddress::BROADCAST); - frame.set_src_addr(EthernetAddress([0x52, 0x54, 0x00, 0x00, 0x00, 0x00])); - frame.set_ethertype(EthernetProtocol::Ipv4); - repr.emit(frame.payload_mut(), &ChecksumCapabilities::default()); - EthernetFrame::new_unchecked(&*frame.into_inner()) - }; + let mut bytes = vec![0u8; 54]; + repr.emit(&mut bytes, &ChecksumCapabilities::default()); + let frame = Ipv6Packet::new_unchecked(&bytes); // Ensure that the unknown protocol frame does not trigger an // ICMP error response when the destination address is a // broadcast address - #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))] - assert_eq!(iface.inner.process_ipv4(&mut socket_set, Instant::from_millis(0), &frame), - Ok(None)); - #[cfg(feature = "proto-ipv6")] assert_eq!(iface.inner.process_ipv6(&mut socket_set, Instant::from_millis(0), &frame), Ok(None)); } @@ -1827,8 +1914,6 @@ mod test { static NO_BYTES: [u8; 0] = []; let (mut iface, mut socket_set) = create_loopback(); - let mut eth_bytes = vec![0u8; 34]; - // Unknown Ipv4 Protocol with no payload let repr = IpRepr::Ipv4(Ipv4Repr { src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]), @@ -1838,15 +1923,9 @@ mod test { hop_limit: 0x40 }); - // emit the above repr to a frame - let frame = { - let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes); - frame.set_dst_addr(EthernetAddress([0x00, 0x00, 0x00, 0x00, 0x00, 0x00])); - frame.set_src_addr(EthernetAddress([0x52, 0x54, 0x00, 0x00, 0x00, 0x00])); - frame.set_ethertype(EthernetProtocol::Ipv4); - repr.emit(frame.payload_mut(), &ChecksumCapabilities::default()); - EthernetFrame::new_unchecked(&*frame.into_inner()) - }; + let mut bytes = vec![0u8; 34]; + repr.emit(&mut bytes, &ChecksumCapabilities::default()); + let frame = Ipv4Packet::new_unchecked(&bytes); // The expected Destination Unreachable response due to the // unknown protocol @@ -2094,21 +2173,19 @@ mod test { payload_len: icmpv4_repr.buffer_len(), }; - // Emit to ethernet frame - let mut eth_bytes = vec![0u8; - EthernetFrame::<&[u8]>::header_len() + + // Emit to ip frame + let mut bytes = vec![0u8; ipv4_repr.buffer_len() + icmpv4_repr.buffer_len() ]; let frame = { - let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes); ipv4_repr.emit( - &mut Ipv4Packet::new_unchecked(frame.payload_mut()), + &mut Ipv4Packet::new_unchecked(&mut bytes), &ChecksumCapabilities::default()); icmpv4_repr.emit( &mut Icmpv4Packet::new_unchecked( - &mut frame.payload_mut()[ipv4_repr.buffer_len()..]), + &mut bytes[ipv4_repr.buffer_len()..]), &ChecksumCapabilities::default()); - EthernetFrame::new_unchecked(&*frame.into_inner()) + Ipv4Packet::new_unchecked(&bytes) }; // Expected ICMPv4 echo reply @@ -2223,9 +2300,9 @@ mod test { } #[test] - #[cfg(feature = "proto-ipv4")] + #[cfg(all(feature = "ethernet", feature = "proto-ipv4"))] fn test_handle_valid_arp_request() { - let (mut iface, mut socket_set) = create_loopback(); + let (mut iface, mut socket_set) = create_loopback_ethernet(); let mut eth_bytes = vec![0u8; 42]; @@ -2268,9 +2345,9 @@ mod test { } #[test] - #[cfg(feature = "proto-ipv6")] + #[cfg(all(feature = "ethernet", feature = "proto-ipv6"))] fn test_handle_valid_ndisc_request() { - let (mut iface, mut socket_set) = create_loopback(); + let (mut iface, mut socket_set) = create_loopback_ethernet(); let mut eth_bytes = vec![0u8; 86]; @@ -2328,9 +2405,9 @@ mod test { } #[test] - #[cfg(feature = "proto-ipv4")] + #[cfg(all(feature = "ethernet", feature = "proto-ipv4"))] fn test_handle_other_arp_request() { - let (mut iface, mut socket_set) = create_loopback(); + let (mut iface, mut socket_set) = create_loopback_ethernet(); let mut eth_bytes = vec![0u8; 42]; @@ -2452,9 +2529,7 @@ mod test { let (mut iface, mut socket_set) = create_loopback(); let remote_ip_addr = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1); - let remote_hw_addr = EthernetAddress([0x52, 0x54, 0x00, 0x00, 0x00, 0x01]); - let mut eth_bytes = vec![0; 66]; let payload = [0x12, 0x34, 0x56, 0x78]; let ipv6_repr = Ipv6Repr { @@ -2465,17 +2540,14 @@ mod test { hop_limit: 0x40, }; + let mut bytes = vec![0; 52]; let frame = { - let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes); let ip_repr = IpRepr::Ipv6(ipv6_repr); - frame.set_dst_addr(EthernetAddress([0x52, 0x54, 0x00, 0x00, 0x00, 0x00])); - frame.set_src_addr(remote_hw_addr); - frame.set_ethertype(EthernetProtocol::Ipv6); - ip_repr.emit(frame.payload_mut(), &ChecksumCapabilities::default()); + ip_repr.emit(&mut bytes, &ChecksumCapabilities::default()); let mut offset = ipv6_repr.buffer_len(); { let mut hbh_pkt = - Ipv6HopByHopHeader::new_unchecked(&mut frame.payload_mut()[offset..]); + Ipv6HopByHopHeader::new_unchecked(&mut bytes[offset..]); hbh_pkt.set_next_header(IpProtocol::Unknown(0x0c)); hbh_pkt.set_header_len(0); offset += 8; @@ -2488,8 +2560,8 @@ mod test { Ipv6OptionRepr::Pad1.emit(&mut pad_pkt); } } - frame.payload_mut()[offset..].copy_from_slice(&payload); - EthernetFrame::new_unchecked(&*frame.into_inner()) + bytes[offset..].copy_from_slice(&payload); + Ipv6Packet::new_unchecked(&bytes) }; let reply_icmp_repr = Icmpv6Repr::ParamProblem { @@ -2511,24 +2583,17 @@ mod test { // error message to be sent to the sender. assert_eq!(iface.inner.process_ipv6(&mut socket_set, Instant::from_millis(0), &frame), Ok(Some(IpPacket::Icmpv6((reply_ipv6_repr, reply_icmp_repr))))); - - // Ensure the address of the requestor was entered in the cache - assert_eq!(iface.inner.lookup_hardware_addr(MockTxToken, Instant::from_secs(0), - &IpAddress::Ipv6(Ipv6Address::LOOPBACK), - &IpAddress::Ipv6(remote_ip_addr)), - Ok((remote_hw_addr, MockTxToken))); } #[test] #[cfg(feature = "proto-igmp")] fn test_handle_igmp() { - fn recv_igmp(mut iface: &mut EthernetInterface<'_, Loopback>, timestamp: Instant) -> Vec<(Ipv4Repr, IgmpRepr)> { + fn recv_igmp(mut iface: &mut Interface<'_, Loopback>, timestamp: Instant) -> Vec<(Ipv4Repr, IgmpRepr)> { let checksum_caps = &iface.device.capabilities().checksum; recv_all(&mut iface, timestamp) .iter() .filter_map(|frame| { - let eth_frame = EthernetFrame::new_checked(frame).ok()?; - let ipv4_packet = Ipv4Packet::new_checked(eth_frame.payload()).ok()?; + let ipv4_packet = Ipv4Packet::new_checked(frame).ok()?; let ipv4_repr = Ipv4Repr::parse(&ipv4_packet, &checksum_caps).ok()?; let ip_payload = ipv4_packet.payload(); let igmp_packet = IgmpPacket::new_checked(ip_payload).ok()?; @@ -2566,14 +2631,12 @@ mod test { // General query let timestamp = Instant::now(); const GENERAL_QUERY_BYTES: &[u8] = &[ - 0x01, 0x00, 0x5e, 0x00, 0x00, 0x01, 0x0a, 0x14, - 0x48, 0x01, 0x21, 0x01, 0x08, 0x00, 0x46, 0xc0, - 0x00, 0x24, 0xed, 0xb4, 0x00, 0x00, 0x01, 0x02, - 0x47, 0x43, 0xac, 0x16, 0x63, 0x04, 0xe0, 0x00, - 0x00, 0x01, 0x94, 0x04, 0x00, 0x00, 0x11, 0x64, - 0xec, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0c, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 + 0x46, 0xc0, 0x00, 0x24, 0xed, 0xb4, 0x00, 0x00, + 0x01, 0x02, 0x47, 0x43, 0xac, 0x16, 0x63, 0x04, + 0xe0, 0x00, 0x00, 0x01, 0x94, 0x04, 0x00, 0x00, + 0x11, 0x64, 0xec, 0x8f, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]; { // Transmit GENERAL_QUERY_BYTES into loopback @@ -2640,23 +2703,21 @@ mod test { payload_len: udp_repr.buffer_len() }; - // Emit to ethernet frame - let mut eth_bytes = vec![0u8; - EthernetFrame::<&[u8]>::header_len() + + // Emit to frame + let mut bytes = vec![0u8; ipv4_repr.buffer_len() + udp_repr.buffer_len() ]; let frame = { - let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes); ipv4_repr.emit( - &mut Ipv4Packet::new_unchecked(frame.payload_mut()), + &mut Ipv4Packet::new_unchecked(&mut bytes), &ChecksumCapabilities::default()); udp_repr.emit( &mut UdpPacket::new_unchecked( - &mut frame.payload_mut()[ipv4_repr.buffer_len()..]), + &mut bytes[ipv4_repr.buffer_len()..]), &src_addr.into(), &dst_addr.into(), &ChecksumCapabilities::default()); - EthernetFrame::new_unchecked(&*frame.into_inner()) + Ipv4Packet::new_unchecked(&bytes) }; assert_eq!(iface.inner.process_ipv4(&mut socket_set, Instant::from_millis(0), &frame), @@ -2696,23 +2757,21 @@ mod test { payload_len: udp_repr.buffer_len() }; - // Emit to ethernet frame - let mut eth_bytes = vec![0u8; - EthernetFrame::<&[u8]>::header_len() + + // Emit to frame + let mut bytes = vec![0u8; ipv4_repr.buffer_len() + udp_repr.buffer_len() ]; let frame = { - let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes); ipv4_repr.emit( - &mut Ipv4Packet::new_unchecked(frame.payload_mut()), + &mut Ipv4Packet::new_unchecked(&mut bytes), &ChecksumCapabilities::default()); udp_repr.emit( &mut UdpPacket::new_unchecked( - &mut frame.payload_mut()[ipv4_repr.buffer_len()..]), + &mut bytes[ipv4_repr.buffer_len()..]), &src_addr.into(), &dst_addr.into(), &ChecksumCapabilities::default()); - EthernetFrame::new_unchecked(&*frame.into_inner()) + Ipv4Packet::new_unchecked(&bytes) }; let frame = iface.inner.process_ipv4(&mut socket_set, Instant::from_millis(0), &frame); @@ -2772,23 +2831,21 @@ mod test { payload_len: udp_repr.buffer_len() }; - // Emit to ethernet frame - let mut eth_bytes = vec![0u8; - EthernetFrame::<&[u8]>::header_len() + + // Emit to frame + let mut bytes = vec![0u8; ipv4_repr.buffer_len() + udp_repr.buffer_len() ]; let frame = { - let mut frame = EthernetFrame::new_unchecked(&mut eth_bytes); ipv4_repr.emit( - &mut Ipv4Packet::new_unchecked(frame.payload_mut()), + &mut Ipv4Packet::new_unchecked(&mut bytes), &ChecksumCapabilities::default()); udp_repr.emit( &mut UdpPacket::new_unchecked( - &mut frame.payload_mut()[ipv4_repr.buffer_len()..]), + &mut bytes[ipv4_repr.buffer_len()..]), &src_addr.into(), &dst_addr.into(), &ChecksumCapabilities::default()); - EthernetFrame::new_unchecked(&*frame.into_inner()) + Ipv4Packet::new_unchecked(&bytes) }; assert_eq!(iface.inner.process_ipv4(&mut socket_set, Instant::from_millis(0), &frame), diff --git a/src/iface/mod.rs b/src/iface/mod.rs index d38593f..a21bc25 100644 --- a/src/iface/mod.rs +++ b/src/iface/mod.rs @@ -7,8 +7,7 @@ provides lookup and caching of hardware addresses, and handles management packet #[cfg(feature = "ethernet")] mod neighbor; mod route; -#[cfg(feature = "ethernet")] -mod ethernet; +mod interface; #[cfg(feature = "ethernet")] pub use self::neighbor::Neighbor as Neighbor; @@ -17,6 +16,5 @@ pub(crate) use self::neighbor::Answer as NeighborAnswer; #[cfg(feature = "ethernet")] pub use self::neighbor::Cache as NeighborCache; pub use self::route::{Route, Routes}; -#[cfg(feature = "ethernet")] -pub use self::ethernet::{Interface as EthernetInterface, - InterfaceBuilder as EthernetInterfaceBuilder}; + +pub use self::interface::{Interface, InterfaceBuilder}; diff --git a/src/phy/loopback.rs b/src/phy/loopback.rs index b41535a..beeba9f 100644 --- a/src/phy/loopback.rs +++ b/src/phy/loopback.rs @@ -10,13 +10,14 @@ use alloc::collections::VecDeque; use alloc::VecDeque; use crate::Result; -use crate::phy::{self, Device, DeviceCapabilities}; +use crate::phy::{self, Device, DeviceCapabilities, Medium}; use crate::time::Instant; /// A loopback device. #[derive(Debug)] pub struct Loopback { queue: VecDeque>, + medium: Medium, } #[allow(clippy::new_without_default)] @@ -25,9 +26,10 @@ impl Loopback { /// /// Every packet transmitted through this device will be received through it /// in FIFO order. - pub fn new() -> Loopback { + pub fn new(medium: Medium) -> Loopback { Loopback { queue: VecDeque::new(), + medium, } } } @@ -39,6 +41,7 @@ impl<'a> Device<'a> for Loopback { fn capabilities(&self) -> DeviceCapabilities { DeviceCapabilities { max_transmission_unit: 65535, + medium: self.medium, ..DeviceCapabilities::default() } } diff --git a/src/phy/mod.rs b/src/phy/mod.rs index e2962fd..99947af 100644 --- a/src/phy/mod.rs +++ b/src/phy/mod.rs @@ -10,7 +10,8 @@ and implementations of it: * _adapters_ [RawSocket](struct.RawSocket.html) and [TapInterface](struct.TapInterface.html), to transmit and receive frames on the host OS. - +*/ +#![cfg_attr(feature = "ethernet", doc = r##" # Examples An implementation of the [Device](trait.Device.html) trait for a simple hardware @@ -18,7 +19,7 @@ Ethernet controller could look as follows: ```rust use smoltcp::Result; -use smoltcp::phy::{self, DeviceCapabilities, Device}; +use smoltcp::phy::{self, DeviceCapabilities, Device, Medium}; use smoltcp::time::Instant; struct StmPhy { @@ -52,6 +53,7 @@ impl<'a> phy::Device<'a> for StmPhy { let mut caps = DeviceCapabilities::default(); caps.max_transmission_unit = 1536; caps.max_burst_size = Some(1); + caps.medium = Medium::Ethernet; caps } } @@ -82,7 +84,7 @@ impl<'a> phy::TxToken for StmPhyTxToken<'a> { } } ``` -*/ +"##)] use crate::Result; use crate::time::Instant; @@ -192,6 +194,13 @@ impl ChecksumCapabilities { #[derive(Debug, Clone, Default)] #[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 @@ -222,6 +231,33 @@ pub struct DeviceCapabilities { pub checksum: ChecksumCapabilities, } +/// Type of medium of a device. +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +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 = "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. + Ip, +} + + +impl Default for Medium { + fn default() -> Medium { + #[cfg(feature = "ethernet")] + return Medium::Ethernet; + #[cfg(not(feature = "ethernet"))] + return Medium::Ip; + } +} + /// An interface for sending and receiving raw network frames. /// /// The interface is based on _tokens_, which are types that allow to receive/transmit a diff --git a/src/phy/raw_socket.rs b/src/phy/raw_socket.rs index 3764551..b4d4e68 100644 --- a/src/phy/raw_socket.rs +++ b/src/phy/raw_socket.rs @@ -5,7 +5,7 @@ use std::io; use std::os::unix::io::{RawFd, AsRawFd}; use crate::Result; -use crate::phy::{self, sys, DeviceCapabilities, Device}; +use crate::phy::{self, sys, DeviceCapabilities, Device, Medium}; use crate::time::Instant; /// A socket that captures or transmits the complete frame. @@ -44,6 +44,7 @@ impl<'a> Device<'a> for RawSocket { fn capabilities(&self) -> DeviceCapabilities { DeviceCapabilities { max_transmission_unit: self.mtu, + medium: Medium::Ethernet, ..DeviceCapabilities::default() } } diff --git a/src/phy/tap_interface.rs b/src/phy/tap_interface.rs index 95202ab..018c2c0 100644 --- a/src/phy/tap_interface.rs +++ b/src/phy/tap_interface.rs @@ -5,7 +5,7 @@ use std::io; use std::os::unix::io::{RawFd, AsRawFd}; use crate::Result; -use crate::phy::{self, sys, DeviceCapabilities, Device}; +use crate::phy::{self, sys, DeviceCapabilities, Device, Medium}; use crate::time::Instant; /// A virtual Ethernet interface. @@ -45,6 +45,7 @@ impl<'a> Device<'a> for TapInterface { fn capabilities(&self) -> DeviceCapabilities { DeviceCapabilities { max_transmission_unit: self.mtu, + medium: Medium::Ethernet, ..DeviceCapabilities::default() } }