瀏覽代碼

Merge pull request #401 from smoltcp-rs/medium-ip-part2

Add IP medium support, part 2
Dario Nieuwenhuis 4 年之前
父節點
當前提交
7f24e0e548

+ 12 - 12
.github/workflows/test.yml

@@ -26,23 +26,23 @@ jobs:
           - std proto-ipv4
 
           # Test features chosen to be as orthogonal as possible.
-          - std ethernet phy-raw_socket proto-ipv6 socket-udp
-          - std ethernet phy-tap_interface proto-ipv6 socket-udp
-          - std ethernet proto-ipv4 proto-igmp socket-raw
-          - std ethernet proto-ipv4 socket-udp socket-tcp
-          - std ethernet proto-ipv4 proto-dhcpv4 socket-udp
-          - std ethernet proto-ipv6 socket-udp
-          - std ethernet proto-ipv6 socket-tcp
-          - std ethernet proto-ipv4 socket-icmp socket-tcp
-          - std ethernet proto-ipv6 socket-icmp socket-tcp
+          - std medium-ethernet phy-raw_socket proto-ipv6 socket-udp
+          - std medium-ethernet phy-tuntap_interface proto-ipv6 socket-udp
+          - std medium-ethernet proto-ipv4 proto-igmp socket-raw
+          - std medium-ethernet proto-ipv4 socket-udp socket-tcp
+          - std medium-ethernet proto-ipv4 proto-dhcpv4 socket-udp
+          - std medium-ethernet proto-ipv6 socket-udp
+          - std medium-ethernet proto-ipv6 socket-tcp
+          - std medium-ethernet proto-ipv4 socket-icmp socket-tcp
+          - std medium-ethernet proto-ipv6 socket-icmp socket-tcp
 
           # Test features chosen to be as aggressive as possible.
-          - std ethernet proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-icmp async
+          - std medium-ethernet proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-icmp async
 
         include:
           # Test alloc feature which requires nightly.
           - rust: nightly
-            features: alloc ethernet proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-icmp
+            features: alloc medium-ethernet proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-icmp
           - rust: nightly
             features: alloc proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-icmp
     steps:
@@ -69,7 +69,7 @@ jobs:
 
         features:
           # These feature sets cannot run tests, so we only check they build.
-          - ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async
+          - medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async
 
     steps:
       - uses: actions/checkout@v2

+ 15 - 14
Cargo.toml

@@ -32,12 +32,13 @@ url = "1.0"
 std = ["managed/std"]
 alloc = ["managed/alloc"]
 verbose = []
-ethernet = ["socket"]
-"phy-raw_socket" = ["std", "libc", "ethernet"]
-"phy-tap_interface" = ["std", "libc", "ethernet"]
+"medium-ethernet" = ["socket"]
+"medium-ip" = ["socket"]
+"phy-raw_socket" = ["std", "libc", "medium-ethernet"]
+"phy-tuntap_interface" = ["std", "libc", "medium-ethernet"]
 "proto-ipv4" = []
 "proto-igmp" = ["proto-ipv4"]
-"proto-dhcpv4" = ["proto-ipv4", "socket-raw", "ethernet"]
+"proto-dhcpv4" = ["proto-ipv4", "socket-raw", "medium-ethernet"]
 "proto-ipv6" = []
 "socket" = []
 "socket-raw" = ["socket"]
@@ -47,8 +48,8 @@ ethernet = ["socket"]
 "async" = []
 default = [
   "std", "log", # needed for `cargo test --no-default-features --features default` :/
-  "ethernet",
-  "phy-raw_socket", "phy-tap_interface",
+  "medium-ethernet", "medium-ip",
+  "phy-raw_socket", "phy-tuntap_interface",
   "proto-ipv4", "proto-igmp", "proto-dhcpv4", "proto-ipv6",
   "socket-raw", "socket-icmp", "socket-udp", "socket-tcp",
   "async"
@@ -68,35 +69,35 @@ required-features = ["std", "phy-raw_socket", "proto-ipv4"]
 
 [[example]]
 name = "httpclient"
-required-features = ["std", "phy-tap_interface", "proto-ipv4", "proto-ipv6", "socket-tcp"]
+required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interface", "proto-ipv4", "proto-ipv6", "socket-tcp"]
 
 [[example]]
 name = "ping"
-required-features = ["std", "phy-tap_interface", "proto-ipv4", "proto-ipv6", "socket-icmp"]
+required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interface", "proto-ipv4", "proto-ipv6", "socket-icmp"]
 
 [[example]]
 name = "server"
-required-features = ["std", "phy-tap_interface", "proto-ipv4", "socket-tcp", "socket-udp"]
+required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interface", "proto-ipv4", "socket-tcp", "socket-udp"]
 
 [[example]]
 name = "client"
-required-features = ["std", "phy-tap_interface", "proto-ipv4", "socket-tcp", "socket-udp"]
+required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interface", "proto-ipv4", "socket-tcp", "socket-udp"]
 
 [[example]]
 name = "loopback"
-required-features = ["log", "proto-ipv4", "socket-tcp"]
+required-features = ["log", "medium-ethernet", "proto-ipv4", "socket-tcp"]
 
 [[example]]
 name = "multicast"
-required-features = ["std", "phy-tap_interface", "proto-ipv4", "proto-igmp", "socket-udp"]
+required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interface", "proto-ipv4", "proto-igmp", "socket-udp"]
 
 [[example]]
 name = "benchmark"
-required-features = ["std", "phy-tap_interface", "proto-ipv4", "socket-raw", "socket-udp"]
+required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interface", "proto-ipv4", "socket-raw", "socket-udp"]
 
 [[example]]
 name = "dhcp_client"
-required-features = ["std", "phy-tap_interface", "proto-ipv4", "proto-dhcpv4", "socket-raw"]
+required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interface", "proto-ipv4", "proto-dhcpv4", "socket-raw"]
 
 [profile.release]
 debug = 2

+ 12 - 12
README.md

@@ -170,9 +170,9 @@ or `BufWriter` is used, which are of course not available on heap-less systems.
 
 This feature is disabled by default.
 
-### Features `phy-raw_socket` and `phy-tap_interface`
+### Features `phy-raw_socket` and `phy-tuntap_interface`
 
-Enable `smoltcp::phy::RawSocket` and `smoltcp::phy::TapInterface`, respectively.
+Enable `smoltcp::phy::RawSocket` and `smoltcp::phy::TunTapInterface`, respectively.
 
 These features are enabled by default.
 
@@ -270,19 +270,19 @@ The host is assigned the hardware address `02-00-00-00-00-02`, IPv4 address `192
 Read its [source code](/examples/httpclient.rs), then run it as:
 
 ```sh
-cargo run --example httpclient -- tap0 ADDRESS URL
+cargo run --example httpclient -- --tap tap0 ADDRESS URL
 ```
 
 For example:
 
 ```sh
-cargo run --example httpclient -- tap0 93.184.216.34 http://example.org/
+cargo run --example httpclient -- --tap tap0 93.184.216.34 http://example.org/
 ```
 
 or:
 
 ```sh
-cargo run --example httpclient -- tap0 2606:2800:220:1:248:1893:25c8:1946 http://example.org/
+cargo run --example httpclient -- --tap tap0 2606:2800:220:1:248:1893:25c8:1946 http://example.org/
 ```
 
 It connects to the given address (not a hostname) and URL, and prints any returned response data.
@@ -297,7 +297,7 @@ The host is assigned the hardware address `02-00-00-00-00-02` and IPv4 address `
 Read its [source code](/examples/ping.rs), then run it as:
 
 ```sh
-cargo run --example ping -- tap0 ADDRESS
+cargo run --example ping -- --tap tap0 ADDRESS
 ```
 
 It sends a series of 4 ICMP ECHO\_REQUEST packets to the given address at one second intervals and
@@ -319,7 +319,7 @@ The host is assigned the hardware address `02-00-00-00-00-01` and IPv4 address `
 Read its [source code](/examples/server.rs), then run it as:
 
 ```sh
-cargo run --example server -- tap0
+cargo run --example server -- --tap tap0
 ```
 
 It responds to:
@@ -349,7 +349,7 @@ The host is assigned the hardware address `02-00-00-00-00-02` and IPv4 address `
 Read its [source code](/examples/client.rs), then run it as:
 
 ```sh
-cargo run --example client -- tap0 ADDRESS PORT
+cargo run --example client -- --tap tap0 ADDRESS PORT
 ```
 
 It connects to the given address (not a hostname) and port (e.g. `socat stdio tcp4-listen:1234`),
@@ -362,7 +362,7 @@ _examples/benchmark.rs_ implements a simple throughput benchmark.
 Read its [source code](/examples/benchmark.rs), then run it as:
 
 ```sh
-cargo run --release --example benchmark -- tap0 [reader|writer]
+cargo run --release --example benchmark -- --tap tap0 [reader|writer]
 ```
 
 It establishes a connection to itself from a different thread and reads or writes a large amount
@@ -372,9 +372,9 @@ A typical result (achieved on a Intel Core i7-7500U CPU and a Linux 4.9.65 x86_6
 on a Dell XPS 13 9360 laptop) is as follows:
 
 ```
-$ cargo run -q --release --example benchmark tap0 reader
+$ cargo run -q --release --example benchmark -- --tap tap0 reader
 throughput: 2.556 Gbps
-$ cargo run -q --release --example benchmark tap0 writer
+$ cargo run -q --release --example benchmark -- --tap tap0 writer
 throughput: 5.301 Gbps
 ```
 
@@ -391,7 +391,7 @@ Although it does not require `std`, this example still requires the `alloc` feat
 Read its [source code](/examples/loopback.rs), then run it without `std`:
 
 ```sh
-cargo run --example loopback --no-default-features --features="log proto-ipv4  socket-tcp alloc"
+cargo run --example loopback --no-default-features --features="log proto-ipv4 socket-tcp alloc"
 ```
 
 ... or with `std` (in this case the features don't have to be explicitly listed):

+ 12 - 8
examples/benchmark.rs

@@ -11,9 +11,9 @@ use std::net::TcpStream;
 use std::os::unix::io::AsRawFd;
 use log::debug;
 
-use smoltcp::phy::wait as phy_wait;
+use smoltcp::phy::{Device, Medium, 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};
@@ -62,12 +62,12 @@ fn main() {
     utils::setup_logging("info");
 
     let (mut opts, mut free) = utils::create_options();
-    utils::add_tap_options(&mut opts, &mut free);
+    utils::add_tuntap_options(&mut opts, &mut free);
     utils::add_middleware_options(&mut opts, &mut free);
     free.push("MODE");
 
     let mut matches = utils::parse_options(&opts, free);
-    let device = utils::parse_tap_options(&mut matches);
+    let device = utils::parse_tuntap_options(&mut matches);
     let fd = device.as_raw_fd();
     let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false);
     let mode = match matches.free[0].as_ref() {
@@ -90,11 +90,15 @@ 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 medium = device.capabilities().medium;
+    let mut builder = InterfaceBuilder::new(device)
+            .ip_addrs(ip_addrs);
+    if medium == Medium::Ethernet {
+        builder = builder
             .ethernet_addr(ethernet_addr)
-            .neighbor_cache(neighbor_cache)
-            .ip_addrs(ip_addrs)
-            .finalize();
+            .neighbor_cache(neighbor_cache);
+    }
+    let mut iface = builder.finalize();
 
     let mut sockets = SocketSet::new(vec![]);
     let tcp1_handle = sockets.add(tcp1_socket);

+ 15 - 9
examples/client.rs

@@ -5,9 +5,9 @@ use std::collections::BTreeMap;
 use std::os::unix::io::AsRawFd;
 use log::debug;
 
-use smoltcp::phy::wait as phy_wait;
+use smoltcp::phy::{Device, Medium, 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;
 
@@ -15,13 +15,14 @@ fn main() {
     utils::setup_logging("");
 
     let (mut opts, mut free) = utils::create_options();
-    utils::add_tap_options(&mut opts, &mut free);
+    utils::add_tuntap_options(&mut opts, &mut free);
     utils::add_middleware_options(&mut opts, &mut free);
     free.push("ADDRESS");
     free.push("PORT");
 
     let mut matches = utils::parse_options(&opts, free);
-    let device = utils::parse_tap_options(&mut matches);
+    let device = utils::parse_tuntap_options(&mut matches);
+
     let fd = device.as_raw_fd();
     let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false);
     let address = IpAddress::from_str(&matches.free[0]).expect("invalid address format");
@@ -39,12 +40,17 @@ 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)
-            .ethernet_addr(ethernet_addr)
-            .neighbor_cache(neighbor_cache)
+    
+    let medium = device.capabilities().medium;
+    let mut builder = InterfaceBuilder::new(device)
             .ip_addrs(ip_addrs)
-            .routes(routes)
-            .finalize();
+            .routes(routes);
+    if medium == Medium::Ethernet {
+        builder = builder
+            .ethernet_addr(ethernet_addr)
+            .neighbor_cache(neighbor_cache);
+    }
+    let mut iface = builder.finalize();
 
     let mut sockets = SocketSet::new(vec![]);
     let tcp_handle = sockets.add(tcp_socket);

+ 14 - 9
examples/dhcp_client.rs

@@ -3,9 +3,9 @@ mod utils;
 
 use std::collections::BTreeMap;
 use std::os::unix::io::AsRawFd;
-use smoltcp::phy::wait as phy_wait;
+use smoltcp::phy::{Device, Medium, 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;
@@ -15,11 +15,11 @@ fn main() {
     utils::setup_logging("");
 
     let (mut opts, mut free) = utils::create_options();
-    utils::add_tap_options(&mut opts, &mut free);
+    utils::add_tuntap_options(&mut opts, &mut free);
     utils::add_middleware_options(&mut opts, &mut free);
 
     let mut matches = utils::parse_options(&opts, free);
-    let device = utils::parse_tap_options(&mut matches);
+    let device = utils::parse_tuntap_options(&mut matches);
     let fd = device.as_raw_fd();
     let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false);
 
@@ -28,12 +28,17 @@ 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)
-            .ethernet_addr(ethernet_addr)
-            .neighbor_cache(neighbor_cache)
+
+    let medium = device.capabilities().medium;
+    let mut builder = InterfaceBuilder::new(device)
             .ip_addrs(ip_addrs)
-            .routes(routes)
-            .finalize();
+            .routes(routes);
+    if medium == Medium::Ethernet {
+        builder = builder
+            .ethernet_addr(ethernet_addr)
+            .neighbor_cache(neighbor_cache);
+    }
+    let mut iface = builder.finalize();
 
     let mut sockets = SocketSet::new(vec![]);
     let dhcp_rx_buffer = RawSocketBuffer::new(

+ 14 - 9
examples/httpclient.rs

@@ -6,9 +6,9 @@ use std::os::unix::io::AsRawFd;
 use url::Url;
 use log::debug;
 
-use smoltcp::phy::wait as phy_wait;
+use smoltcp::phy::{Device, Medium, 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;
 
@@ -16,13 +16,13 @@ fn main() {
     utils::setup_logging("");
 
     let (mut opts, mut free) = utils::create_options();
-    utils::add_tap_options(&mut opts, &mut free);
+    utils::add_tuntap_options(&mut opts, &mut free);
     utils::add_middleware_options(&mut opts, &mut free);
     free.push("ADDRESS");
     free.push("URL");
 
     let mut matches = utils::parse_options(&opts, free);
-    let device = utils::parse_tap_options(&mut matches);
+    let device = utils::parse_tuntap_options(&mut matches);
     let fd = device.as_raw_fd();
     let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false);
     let address = IpAddress::from_str(&matches.free[0]).expect("invalid address format");
@@ -45,12 +45,17 @@ 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)
-            .ethernet_addr(ethernet_addr)
-            .neighbor_cache(neighbor_cache)
+
+    let medium = device.capabilities().medium;
+    let mut builder = InterfaceBuilder::new(device)
             .ip_addrs(ip_addrs)
-            .routes(routes)
-            .finalize();
+            .routes(routes);
+    if medium == Medium::Ethernet {
+        builder = builder
+            .ethernet_addr(ethernet_addr)
+            .neighbor_cache(neighbor_cache);
+    }
+    let mut iface = builder.finalize();
 
     let mut sockets = SocketSet::new(vec![]);
     let tcp_handle = sockets.add(tcp_socket);

+ 4 - 4
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)

+ 4 - 4
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};
@@ -20,11 +20,11 @@ fn main() {
     utils::setup_logging("warn");
 
     let (mut opts, mut free) = utils::create_options();
-    utils::add_tap_options(&mut opts, &mut free);
+    utils::add_tuntap_options(&mut opts, &mut free);
     utils::add_middleware_options(&mut opts, &mut free);
 
     let mut matches = utils::parse_options(&opts, free);
-    let device = utils::parse_tap_options(&mut matches);
+    let device = utils::parse_tuntap_options(&mut matches);
     let fd = device.as_raw_fd();
     let device = utils::parse_middleware_options(&mut matches,
                                                  device,
@@ -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])

+ 14 - 9
examples/ping.rs

@@ -8,13 +8,13 @@ use std::collections::HashMap;
 use log::debug;
 use byteorder::{ByteOrder, NetworkEndian};
 
-use smoltcp::time::{Duration, Instant};
+use smoltcp::{phy::Medium, time::{Duration, Instant}};
 use smoltcp::phy::Device;
 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 {
@@ -55,7 +55,7 @@ fn main() {
     utils::setup_logging("warn");
 
     let (mut opts, mut free) = utils::create_options();
-    utils::add_tap_options(&mut opts, &mut free);
+    utils::add_tuntap_options(&mut opts, &mut free);
     utils::add_middleware_options(&mut opts, &mut free);
     opts.optopt("c", "count", "Amount of echo request packets to send (default: 4)", "COUNT");
     opts.optopt("i", "interval",
@@ -66,7 +66,7 @@ fn main() {
     free.push("ADDRESS");
 
     let mut matches = utils::parse_options(&opts, free);
-    let device = utils::parse_tap_options(&mut matches);
+    let device = utils::parse_tuntap_options(&mut matches);
     let fd = device.as_raw_fd();
     let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false);
     let device_caps = device.capabilities();
@@ -98,12 +98,17 @@ 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)
-            .ethernet_addr(ethernet_addr)
+
+    let medium = device.capabilities().medium;
+    let mut builder = InterfaceBuilder::new(device)
             .ip_addrs(ip_addrs)
-            .routes(routes)
-            .neighbor_cache(neighbor_cache)
-            .finalize();
+            .routes(routes);
+    if medium == Medium::Ethernet {
+        builder = builder
+            .ethernet_addr(ethernet_addr)
+            .neighbor_cache(neighbor_cache);
+    }
+    let mut iface = builder.finalize();
 
     let mut sockets = SocketSet::new(vec![]);
     let icmp_handle = sockets.add(icmp_socket);

+ 13 - 8
examples/server.rs

@@ -6,9 +6,9 @@ use std::fmt::Write;
 use std::os::unix::io::AsRawFd;
 use log::debug;
 
-use smoltcp::phy::wait as phy_wait;
+use smoltcp::phy::{Device, Medium, 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};
@@ -18,11 +18,11 @@ fn main() {
     utils::setup_logging("");
 
     let (mut opts, mut free) = utils::create_options();
-    utils::add_tap_options(&mut opts, &mut free);
+    utils::add_tuntap_options(&mut opts, &mut free);
     utils::add_middleware_options(&mut opts, &mut free);
 
     let mut matches = utils::parse_options(&opts, free);
-    let device = utils::parse_tap_options(&mut matches);
+    let device = utils::parse_tuntap_options(&mut matches);
     let fd = device.as_raw_fd();
     let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false);
 
@@ -54,11 +54,16 @@ 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 medium = device.capabilities().medium;
+    let mut builder = InterfaceBuilder::new(device)
+            .ip_addrs(ip_addrs);
+    if medium == Medium::Ethernet {
+        builder = builder
             .ethernet_addr(ethernet_addr)
-            .neighbor_cache(neighbor_cache)
-            .ip_addrs(ip_addrs)
-            .finalize();
+            .neighbor_cache(neighbor_cache);
+    }
+    let mut iface = builder.finalize();
 
     let mut sockets = SocketSet::new(vec![]);
     let udp_handle  = sockets.add(udp_socket);

+ 25 - 15
examples/utils.rs

@@ -14,10 +14,10 @@ use log::{Level, LevelFilter, trace};
 use env_logger::Builder;
 use getopts::{Options, Matches};
 
-use smoltcp::phy::{Device, EthernetTracer, FaultInjector};
-#[cfg(feature = "phy-tap_interface")]
-use smoltcp::phy::TapInterface;
-use smoltcp::phy::{PcapWriter, PcapSink, PcapMode, PcapLinkType};
+use smoltcp::phy::{Device, Tracer, FaultInjector, Medium};
+#[cfg(feature = "phy-tuntap_interface")]
+use smoltcp::phy::TunTapInterface;
+use smoltcp::phy::{PcapWriter, PcapSink, PcapMode};
 use smoltcp::phy::RawSocket;
 use smoltcp::time::{Duration, Instant};
 
@@ -77,14 +77,20 @@ pub fn parse_options(options: &Options, free: Vec<&str>) -> Matches {
     }
 }
 
-pub fn add_tap_options(_opts: &mut Options, free: &mut Vec<&str>) {
-    free.push("INTERFACE");
+pub fn add_tuntap_options(opts: &mut Options, _free: &mut Vec<&str>) {
+    opts.optopt("", "tun", "TUN interface to use", "tun0");
+    opts.optopt("", "tap", "TAP interface to use", "tap0");
 }
 
-#[cfg(feature = "phy-tap_interface")]
-pub fn parse_tap_options(matches: &mut Matches) -> TapInterface {
-    let interface = matches.free.remove(0);
-    TapInterface::new(&interface).unwrap()
+#[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");
+    match(tun,tap) {
+        (Some(tun), None) => TunTapInterface::new(&tun, Medium::Ip).unwrap(),
+        (None, Some(tap)) => TunTapInterface::new(&tap, Medium::Ethernet).unwrap(),
+        _ => panic!("You must specify exactly one of --tun or --tap"),
+    }
 }
 
 pub fn parse_raw_socket_options(matches: &mut Matches) -> RawSocket {
@@ -105,7 +111,7 @@ pub fn add_middleware_options(opts: &mut Options, _free: &mut Vec<&str>) {
 }
 
 pub fn parse_middleware_options<D>(matches: &mut Matches, device: D, loopback: bool)
-        -> FaultInjector<EthernetTracer<PcapWriter<D, Rc<dyn PcapSink>>>>
+        -> FaultInjector<Tracer<PcapWriter<D, Rc<dyn PcapSink>>>>
     where D: for<'a> Device<'a>
 {
     let drop_chance      = matches.opt_str("drop-chance").map(|s| u8::from_str(&s).unwrap())
@@ -130,13 +136,17 @@ pub fn parse_middleware_options<D>(matches: &mut Matches, device: D, loopback: b
 
     let seed = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().subsec_nanos();
 
-    let device = PcapWriter::new(device, Rc::new(RefCell::new(pcap_writer)) as Rc<dyn PcapSink>,
-                                 if loopback { PcapMode::TxOnly } else { PcapMode::Both },
-                                 PcapLinkType::Ethernet);
-    let device = EthernetTracer::new(device, |_timestamp, _printer| {
+    let device = PcapWriter::new(
+        device,
+        Rc::new(RefCell::new(pcap_writer)) as Rc<dyn PcapSink>,
+        if loopback { PcapMode::TxOnly } else { PcapMode::Both },
+    );
+
+    let device = Tracer::new(device, |_timestamp, _printer| {
         #[cfg(feature = "log")]
         trace!("{}", _printer);
     });
+
     let mut device = FaultInjector::new(device, seed);
     device.set_drop_chance(drop_chance);
     device.set_corrupt_chance(corrupt_chance);

+ 4 - 4
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)

+ 1 - 1
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};
 

文件差異過大導致無法顯示
+ 339 - 242
src/iface/interface.rs


+ 9 - 9
src/iface/mod.rs

@@ -4,19 +4,19 @@ The `iface` module deals with the *network interfaces*. It filters incoming fram
 provides lookup and caching of hardware addresses, and handles management packets.
 */
 
-#[cfg(feature = "ethernet")]
+#[cfg(feature = "medium-ethernet")]
 mod neighbor;
 mod route;
-#[cfg(feature = "ethernet")]
-mod ethernet;
+#[cfg(any(feature = "medium-ethernet", feature = "medium-ip"))]
+mod interface;
 
-#[cfg(feature = "ethernet")]
+#[cfg(feature = "medium-ethernet")]
 pub use self::neighbor::Neighbor as Neighbor;
-#[cfg(feature = "ethernet")]
+#[cfg(feature = "medium-ethernet")]
 pub(crate) use self::neighbor::Answer as NeighborAnswer;
-#[cfg(feature = "ethernet")]
+#[cfg(feature = "medium-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};
+
+#[cfg(any(feature = "medium-ethernet", feature = "medium-ip"))]
+pub use self::interface::{Interface, InterfaceBuilder};

+ 1 - 1
src/lib.rs

@@ -1,6 +1,6 @@
 #![cfg_attr(not(any(test, feature = "std")), no_std)]
 #![deny(unsafe_code)]
-#![cfg_attr(all(any(feature = "proto-ipv4", feature = "proto-ipv6"), feature = "ethernet"), deny(unused))]
+#![cfg_attr(all(any(feature = "proto-ipv4", feature = "proto-ipv6"), feature = "medium-ethernet"), deny(unused))]
 
 //! The _smoltcp_ library is built in a layered structure, with the layers corresponding
 //! to the levels of API abstraction. Only the highest layers would be used by a typical

+ 5 - 5
src/parsers.rs

@@ -3,7 +3,7 @@
 use core::str::FromStr;
 use core::result;
 
-#[cfg(feature = "ethernet")]
+#[cfg(feature = "medium-ethernet")]
 use crate::wire::EthernetAddress;
 use crate::wire::{IpAddress, IpCidr, IpEndpoint};
 #[cfg(feature = "proto-ipv4")]
@@ -118,7 +118,7 @@ impl<'a> Parser<'a> {
         }
     }
 
-    #[cfg(feature = "ethernet")]
+    #[cfg(feature = "medium-ethernet")]
     fn accept_mac_joined_with(&mut self, separator: u8) -> Result<EthernetAddress> {
         let mut octets = [0u8; 6];
         for (n, octet) in octets.iter_mut().enumerate() {
@@ -130,7 +130,7 @@ impl<'a> Parser<'a> {
         Ok(EthernetAddress(octets))
     }
 
-    #[cfg(feature = "ethernet")]
+    #[cfg(feature = "medium-ethernet")]
     fn accept_mac(&mut self) -> Result<EthernetAddress> {
         if let Some(mac) = self.try_do(|p| p.accept_mac_joined_with(b'-')) {
             return Ok(mac)
@@ -352,7 +352,7 @@ impl<'a> Parser<'a> {
     }
 }
 
-#[cfg(feature = "ethernet")]
+#[cfg(feature = "medium-ethernet")]
 impl FromStr for EthernetAddress {
     type Err = ();
 
@@ -473,7 +473,7 @@ mod test {
     }
 
     #[test]
-    #[cfg(all(feature = "proto-ipv4", feature = "ethernet"))]
+    #[cfg(all(feature = "proto-ipv4", feature = "medium-ethernet"))]
     fn test_mac() {
         assert_eq!(EthernetAddress::from_str(""), Err(()));
         assert_eq!(EthernetAddress::from_str("02:00:00:00:00:00"),

+ 5 - 2
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<Vec<u8>>,
+    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()
         }
     }

+ 49 - 14
src/phy/mod.rs

@@ -8,9 +8,10 @@ and implementations of it:
   * _middleware_ [Tracer](struct.Tracer.html) and
     [FaultInjector](struct.FaultInjector.html), to facilitate debugging;
   * _adapters_ [RawSocket](struct.RawSocket.html) and
-    [TapInterface](struct.TapInterface.html), to transmit and receive frames
+    [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
@@ -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,12 +84,12 @@ impl<'a> phy::TxToken for StmPhyTxToken<'a> {
     }
 }
 ```
-*/
+"##)]
 
 use crate::Result;
 use crate::time::Instant;
 
-#[cfg(all(any(feature = "phy-raw_socket", feature = "phy-tap_interface"), unix))]
+#[cfg(all(any(feature = "phy-raw_socket", feature = "phy-tuntap_interface"), unix))]
 mod sys;
 
 mod tracer;
@@ -98,10 +100,10 @@ mod pcap_writer;
 mod loopback;
 #[cfg(all(feature = "phy-raw_socket", unix))]
 mod raw_socket;
-#[cfg(all(feature = "phy-tap_interface", any(target_os = "linux", target_os = "android")))]
-mod tap_interface;
+#[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-tap_interface"), unix))]
+#[cfg(all(any(feature = "phy-raw_socket", feature = "phy-tuntap_interface"), unix))]
 pub use self::sys::wait;
 
 pub use self::tracer::Tracer;
@@ -112,12 +114,8 @@ pub use self::pcap_writer::{PcapLinkType, PcapMode, PcapSink, PcapWriter};
 pub use self::loopback::Loopback;
 #[cfg(all(feature = "phy-raw_socket", unix))]
 pub use self::raw_socket::RawSocket;
-#[cfg(all(feature = "phy-tap_interface", any(target_os = "linux", target_os = "android")))]
-pub use self::tap_interface::TapInterface;
-
-#[cfg(feature = "ethernet")]
-/// A tracer device for Ethernet frames.
-pub type EthernetTracer<T> = Tracer<T, super::wire::EthernetFrame<&'static [u8]>>;
+#[cfg(all(feature = "phy-tuntap_interface", any(target_os = "linux", target_os = "android")))]
+pub use self::tuntap_interface::TunTapInterface;
 
 /// A description of checksum behavior for a particular protocol.
 #[derive(Debug, Clone, Copy)]
@@ -192,6 +190,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 +227,36 @@ 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 = "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,
+}
+
+
+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(not(feature = "medium-ip"), not(feature = "medium-ethernet")))]
+        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

+ 10 - 2
src/phy/pcap_writer.rs

@@ -3,6 +3,7 @@ use std::cell::RefCell;
 #[cfg(feature = "std")]
 use std::io::Write;
 use byteorder::{ByteOrder, NativeEndian};
+use phy::Medium;
 
 use crate::Result;
 use crate::phy::{self, DeviceCapabilities, Device};
@@ -14,7 +15,7 @@ enum_with_unknown! {
         /// Ethernet frames
         Ethernet =   1,
         /// IPv4 or IPv6 packets (depending on the version field)
-        Ip       = 101
+        Ip       = 101,
     }
 }
 
@@ -128,7 +129,14 @@ pub struct PcapWriter<D, S>
 
 impl<D: for<'a> Device<'a>, S: PcapSink + Clone> PcapWriter<D, S> {
     /// Creates a packet capture writer.
-    pub fn new(lower: D, sink: S, mode: PcapMode, link_type: PcapLinkType) -> PcapWriter<D, S> {
+    pub fn new(lower: D, sink: S, mode: PcapMode) -> PcapWriter<D, S> {
+        let medium = lower.capabilities().medium;
+        let link_type = match medium {
+            #[cfg(feature = "medium-ip")]
+            Medium::Ip => PcapLinkType::Ip,
+            #[cfg(feature = "medium-ethernet")]
+            Medium::Ethernet => PcapLinkType::Ethernet,
+        };
         sink.global_header(link_type);
         PcapWriter { lower, sink, mode }
     }

+ 2 - 1
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()
         }
     }

+ 3 - 7
src/phy/sys/linux.rs

@@ -1,14 +1,10 @@
-#[cfg(any(feature = "phy-raw_socket",
-          feature = "phy-tap_interface"))]
+#![allow(unused)]
+
 pub const SIOCGIFMTU:   libc::c_ulong = 0x8921;
-#[cfg(any(feature = "phy-raw_socket"))]
 pub const SIOCGIFINDEX: libc::c_ulong = 0x8933;
-#[cfg(any(feature = "phy-raw_socket"))]
 pub const ETH_P_ALL:    libc::c_short = 0x0003;
 
-#[cfg(feature = "phy-tap_interface")]
 pub const TUNSETIFF:    libc::c_ulong = 0x400454CA;
-#[cfg(feature = "phy-tap_interface")]
+pub const IFF_TUN:      libc::c_int   = 0x0001;
 pub const IFF_TAP:      libc::c_int   = 0x0002;
-#[cfg(feature = "phy-tap_interface")]
 pub const IFF_NO_PI:    libc::c_int   = 0x1000;

+ 7 - 7
src/phy/sys/mod.rs

@@ -12,15 +12,15 @@ mod imp;
 pub mod raw_socket;
 #[cfg(all(feature = "phy-raw_socket", not(any(target_os = "linux", target_os = "android")), unix))]
 pub mod bpf;
-#[cfg(all(feature = "phy-tap_interface", any(target_os = "linux", target_os = "android")))]
-pub mod tap_interface;
+#[cfg(all(feature = "phy-tuntap_interface", any(target_os = "linux", target_os = "android")))]
+pub mod tuntap_interface;
 
 #[cfg(all(feature = "phy-raw_socket", any(target_os = "linux", target_os = "android")))]
 pub use self::raw_socket::RawSocketDesc;
 #[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-tap_interface", any(target_os = "linux", target_os = "android")))]
-pub use self::tap_interface::TapInterfaceDesc;
+#[cfg(all(feature = "phy-tuntap_interface", any(target_os = "linux", target_os = "android")))]
+pub use self::tuntap_interface::TunTapInterfaceDesc;
 
 /// Wait until given file descriptor becomes readable, but no longer than given timeout.
 pub fn wait(fd: RawFd, duration: Option<Duration>) -> io::Result<()> {
@@ -60,7 +60,7 @@ pub fn wait(fd: RawFd, duration: Option<Duration>) -> io::Result<()> {
     }
 }
 
-#[cfg(all(any(feature = "phy-tap_interface", feature = "phy-raw_socket"), unix))]
+#[cfg(all(any(feature = "phy-tuntap_interface", feature = "phy-raw_socket"), unix))]
 #[repr(C)]
 #[derive(Debug)]
 struct ifreq {
@@ -68,7 +68,7 @@ struct ifreq {
     ifr_data: libc::c_int /* ifr_ifindex or ifr_mtu */
 }
 
-#[cfg(all(any(feature = "phy-tap_interface", feature = "phy-raw_socket"), unix))]
+#[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],
@@ -81,7 +81,7 @@ fn ifreq_for(name: &str) -> ifreq {
 }
 
 #[cfg(all(any(target_os = "linux", target_os = "android"),
-          any(feature = "phy-tap_interface", feature = "phy-raw_socket")))]
+          any(feature = "phy-tuntap_interface", feature = "phy-raw_socket")))]
 fn ifreq_ioctl(lower: libc::c_int, ifreq: &mut ifreq,
                cmd: libc::c_ulong) -> io::Result<libc::c_int> {
     unsafe {

+ 30 - 12
src/phy/sys/tap_interface.rs → src/phy/sys/tuntap_interface.rs

@@ -1,22 +1,23 @@
 use std::io;
 use std::os::unix::io::{RawFd, AsRawFd};
 use super::*;
-use crate::wire::EthernetFrame;
+use crate::{phy::Medium, wire::EthernetFrame};
 
 #[derive(Debug)]
-pub struct TapInterfaceDesc {
+pub struct TunTapInterfaceDesc {
     lower: libc::c_int,
-    ifreq: ifreq
+    ifreq: ifreq,
+    medium: Medium,
 }
 
-impl AsRawFd for TapInterfaceDesc {
+impl AsRawFd for TunTapInterfaceDesc {
     fn as_raw_fd(&self) -> RawFd {
         self.lower
     }
 }
 
-impl TapInterfaceDesc {
-    pub fn new(name: &str) -> io::Result<TapInterfaceDesc> {
+impl TunTapInterfaceDesc {
+    pub fn new(name: &str, medium: Medium) -> io::Result<TunTapInterfaceDesc> {
         let lower = unsafe {
             let lower = libc::open("/dev/net/tun\0".as_ptr() as *const libc::c_char,
                                    libc::O_RDWR | libc::O_NONBLOCK);
@@ -24,14 +25,21 @@ impl TapInterfaceDesc {
             lower
         };
 
-        Ok(TapInterfaceDesc {
-            lower: lower,
-            ifreq: ifreq_for(name)
+        Ok(TunTapInterfaceDesc {
+            lower,
+            ifreq: ifreq_for(name),
+            medium,
         })
     }
 
     pub fn attach_interface(&mut self) -> io::Result<()> {
-        self.ifreq.ifr_data = imp::IFF_TAP | imp::IFF_NO_PI;
+        let mode = match self.medium {
+            #[cfg(feature = "medium-ip")]
+            Medium::Ip => imp::IFF_TUN,
+            #[cfg(feature = "medium-ethernet")]
+            Medium::Ethernet => imp::IFF_TAP,
+        };
+        self.ifreq.ifr_data = mode | imp::IFF_NO_PI;
         ifreq_ioctl(self.lower, &mut self.ifreq, imp::TUNSETIFF).map(|_| ())
     }
 
@@ -46,9 +54,19 @@ impl TapInterfaceDesc {
 
         unsafe { libc::close(lower); }
 
+        // Propagate error after close, to ensure we always close.
+        let ip_mtu = ip_mtu?;
+
         // 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.
-        Ok(ip_mtu? + EthernetFrame::<&[u8]>::header_len())
+        let mtu = match self.medium {
+            #[cfg(feature = "medium-ip")]
+            Medium::Ip => ip_mtu,
+            #[cfg(feature = "medium-ethernet")]
+            Medium::Ethernet => ip_mtu + EthernetFrame::<&[u8]>::header_len(),
+        };
+
+        Ok(mtu)
     }
 
     pub fn recv(&mut self, buffer: &mut [u8]) -> io::Result<usize> {
@@ -70,7 +88,7 @@ impl TapInterfaceDesc {
     }
 }
 
-impl Drop for TapInterfaceDesc {
+impl Drop for TunTapInterfaceDesc {
     fn drop(&mut self) {
         unsafe { libc::close(self.lower); }
     }

+ 60 - 24
src/phy/tracer.rs

@@ -1,6 +1,7 @@
-use crate::Result;
-use crate::wire::pretty_print::{PrettyPrint, PrettyPrinter};
-use crate::phy::{self, DeviceCapabilities, Device};
+use core::fmt;
+
+use crate::{Result, wire::pretty_print::{PrettyIndent, PrettyPrint}};
+use crate::phy::{self, DeviceCapabilities, Device, Medium};
 use crate::time::Instant;
 
 /// A tracer device.
@@ -8,14 +9,14 @@ use crate::time::Instant;
 /// A tracer is a device that pretty prints all packets traversing it
 /// using the provided writer function, and then passes them to another
 /// device.
-pub struct Tracer<D: for<'a> Device<'a>, P: PrettyPrint> {
+pub struct Tracer<D: for<'a> Device<'a>> {
     inner:  D,
-    writer: fn(Instant, PrettyPrinter<P>),
+    writer: fn(Instant, Packet),
 }
 
-impl<D: for<'a> Device<'a>, P: PrettyPrint> Tracer<D, P> {
+impl<D: for<'a> Device<'a>> Tracer<D> {
     /// Create a tracer device.
-    pub fn new(inner: D, writer: fn(timestamp: Instant, printer: PrettyPrinter<P>)) -> Tracer<D, P> {
+    pub fn new(inner: D, writer: fn(timestamp: Instant, packet: Packet)) -> Tracer<D> {
         Tracer { inner, writer }
     }
 
@@ -40,65 +41,100 @@ impl<D: for<'a> Device<'a>, P: PrettyPrint> Tracer<D, P> {
     }
 }
 
-impl<'a, D, P> Device<'a> for Tracer<D, P>
+impl<'a, D> Device<'a> for Tracer<D>
     where D: for<'b> Device<'b>,
-          P: PrettyPrint + 'a,
 {
-    type RxToken = RxToken<<D as Device<'a>>::RxToken, P>;
-    type TxToken = TxToken<<D as Device<'a>>::TxToken, P>;
+    type RxToken = RxToken<<D as Device<'a>>::RxToken>;
+    type TxToken = TxToken<<D as Device<'a>>::TxToken>;
 
     fn capabilities(&self) -> DeviceCapabilities { self.inner.capabilities() }
 
     fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> {
         let &mut Self { ref mut inner, writer, .. } = self;
+        let medium = inner.capabilities().medium;
         inner.receive().map(|(rx_token, tx_token)| {
-            let rx = RxToken { token: rx_token, writer };
-            let tx = TxToken { token: tx_token, writer };
+            let rx = RxToken { token: rx_token, writer, medium };
+            let tx = TxToken { token: tx_token, writer, medium };
             (rx, tx)
         })
     }
 
     fn transmit(&'a mut self) -> Option<Self::TxToken> {
         let &mut Self { ref mut inner, writer } = self;
+        let medium = inner.capabilities().medium;
         inner.transmit().map(|tx_token| {
-            TxToken { token: tx_token, writer }
+            TxToken { token: tx_token, medium, writer }
         })
     }
 }
 
 #[doc(hidden)]
-pub struct RxToken<Rx: phy::RxToken, P: PrettyPrint> {
+pub struct RxToken<Rx: phy::RxToken> {
     token:     Rx,
-    writer:    fn(Instant, PrettyPrinter<P>)
+    writer:    fn(Instant, Packet),
+    medium:    Medium,
 }
 
-impl<Rx: phy::RxToken, P: PrettyPrint> phy::RxToken for RxToken<Rx, P> {
+impl<Rx: phy::RxToken> phy::RxToken for RxToken<Rx> {
     fn consume<R, F>(self, timestamp: Instant, f: F) -> Result<R>
         where F: FnOnce(&mut [u8]) -> Result<R>
     {
-        let Self { token, writer } = self;
+        let Self { token, writer, medium } = self;
         token.consume(timestamp, |buffer| {
-            writer(timestamp, PrettyPrinter::<P>::new("<- ", &buffer));
+            writer(timestamp, Packet{
+                buffer,
+                medium,
+                prefix: "<- ",
+            });
             f(buffer)
         })
     }
 }
 
 #[doc(hidden)]
-pub struct TxToken<Tx: phy::TxToken, P: PrettyPrint> {
+pub struct TxToken<Tx: phy::TxToken> {
     token:     Tx,
-    writer:    fn(Instant, PrettyPrinter<P>)
+    writer:    fn(Instant, Packet),
+    medium:    Medium,
 }
 
-impl<Tx: phy::TxToken, P: PrettyPrint> phy::TxToken for TxToken<Tx, P> {
+impl<Tx: phy::TxToken> phy::TxToken for TxToken<Tx> {
     fn consume<R, F>(self, timestamp: Instant, len: usize, f: F) -> Result<R>
         where F: FnOnce(&mut [u8]) -> Result<R>
     {
-        let Self { token, writer } = self;
+        let Self { token, writer, medium } = self;
         token.consume(timestamp, len, |buffer| {
             let result = f(buffer);
-            writer(timestamp, PrettyPrinter::<P>::new("-> ", &buffer));
+            writer(timestamp, Packet{
+                buffer,
+                medium,
+                prefix: "-> ",
+            });
             result
         })
     }
 }
+
+pub struct Packet<'a> {
+    buffer: &'a [u8],
+    medium: Medium,
+    prefix: &'static str,
+}
+
+impl<'a> fmt::Display for Packet<'a> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        let mut indent = PrettyIndent::new(self.prefix);
+        match self.medium {
+            #[cfg(feature = "medium-ethernet")]
+            Medium::Ethernet => crate::wire::EthernetFrame::<&'static [u8]>::pretty_print(&self.buffer, f, &mut indent),
+            #[cfg(feature = "medium-ip")]
+            Medium::Ip => match crate::wire::IpVersion::of_packet(&self.buffer) {
+                #[cfg(feature = "proto-ipv4")]
+                Ok(crate::wire::IpVersion::Ipv4) => crate::wire::Ipv4Packet::<&'static [u8]>::pretty_print(&self.buffer, f, &mut indent),
+                #[cfg(feature = "proto-ipv6")]
+                Ok(crate::wire::IpVersion::Ipv6) => crate::wire::Ipv6Packet::<&'static [u8]>::pretty_print(&self.buffer, f, &mut indent),
+                _ => f.write_str("unrecognized IP version")
+            }
+        }
+    }
+}

+ 17 - 14
src/phy/tap_interface.rs → src/phy/tuntap_interface.rs

@@ -5,46 +5,49 @@ 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.
+/// A virtual TUN (IP) or TAP (Ethernet) interface.
 #[derive(Debug)]
-pub struct TapInterface {
-    lower:  Rc<RefCell<sys::TapInterfaceDesc>>,
-    mtu:    usize
+pub struct TunTapInterface {
+    lower:  Rc<RefCell<sys::TunTapInterfaceDesc>>,
+    mtu:    usize,
+    medium: Medium,
 }
 
-impl AsRawFd for TapInterface {
+impl AsRawFd for TunTapInterface {
     fn as_raw_fd(&self) -> RawFd {
         self.lower.borrow().as_raw_fd()
     }
 }
 
-impl TapInterface {
-    /// Attaches to a TAP interface called `name`, or creates it if it does not exist.
+impl TunTapInterface {
+    /// Attaches to a TUN/TAP interface called `name`, or creates it if it does not exist.
     ///
     /// If `name` is a persistent interface configured with UID of the current user,
     /// no special privileges are needed. Otherwise, this requires superuser privileges
     /// or a corresponding capability set on the executable.
-    pub fn new(name: &str) -> io::Result<TapInterface> {
-        let mut lower = sys::TapInterfaceDesc::new(name)?;
+    pub fn new(name: &str, medium: Medium) -> io::Result<TunTapInterface> {
+        let mut lower = sys::TunTapInterfaceDesc::new(name, medium)?;
         lower.attach_interface()?;
         let mtu = lower.interface_mtu()?;
-        Ok(TapInterface {
+        Ok(TunTapInterface {
             lower: Rc::new(RefCell::new(lower)),
-            mtu:   mtu
+            mtu,
+            medium,
         })
     }
 }
 
-impl<'a> Device<'a> for TapInterface {
+impl<'a> Device<'a> for TunTapInterface {
     type RxToken = RxToken;
     type TxToken = TxToken;
 
     fn capabilities(&self) -> DeviceCapabilities {
         DeviceCapabilities {
             max_transmission_unit: self.mtu,
+            medium: self.medium,
             ..DeviceCapabilities::default()
         }
     }
@@ -88,7 +91,7 @@ impl phy::RxToken for RxToken {
 
 #[doc(hidden)]
 pub struct TxToken {
-    lower: Rc<RefCell<sys::TapInterfaceDesc>>,
+    lower: Rc<RefCell<sys::TunTapInterfaceDesc>>,
 }
 
 impl phy::TxToken for TxToken {

+ 46 - 46
src/socket/icmp.rs

@@ -3,7 +3,7 @@ use core::cmp;
 use core::task::Waker;
 
 use crate::{Error, Result};
-use crate::phy::{ChecksumCapabilities, DeviceCapabilities};
+use crate::phy::ChecksumCapabilities;
 use crate::socket::{Socket, SocketMeta, SocketHandle, PollAt};
 use crate::storage::{PacketBuffer, PacketMetadata};
 #[cfg(feature = "async")]
@@ -400,7 +400,7 @@ impl<'a> IcmpSocket<'a> {
         Ok(())
     }
 
-    pub(crate) fn dispatch<F>(&mut self, _caps: &DeviceCapabilities, emit: F) -> Result<()>
+    pub(crate) fn dispatch<F>(&mut self, emit: F) -> Result<()>
         where F: FnOnce((IpRepr, IcmpRepr)) -> Result<()>
     {
         let handle    = self.meta.handle;
@@ -528,9 +528,9 @@ mod test_ipv4 {
     #[test]
     fn test_send_dispatch() {
         let mut socket = socket(buffer(0), buffer(1));
-        let caps = DeviceCapabilities::default();
+        let checksum = ChecksumCapabilities::default();
 
-        assert_eq!(socket.dispatch(&caps, |_| unreachable!()),
+        assert_eq!(socket.dispatch(|_| unreachable!()),
                    Err(Error::Exhausted));
 
         // This buffer is too long
@@ -539,13 +539,13 @@ mod test_ipv4 {
 
         let mut bytes = [0xff; 24];
         let mut packet = Icmpv4Packet::new_unchecked(&mut bytes);
-        ECHOV4_REPR.emit(&mut packet, &caps.checksum);
+        ECHOV4_REPR.emit(&mut packet, &checksum);
 
         assert_eq!(socket.send_slice(&packet.into_inner()[..], REMOTE_IPV4.into()), Ok(()));
         assert_eq!(socket.send_slice(b"123456", REMOTE_IPV4.into()), Err(Error::Exhausted));
         assert!(!socket.can_send());
 
-        assert_eq!(socket.dispatch(&caps, |(ip_repr, icmp_repr)| {
+        assert_eq!(socket.dispatch(|(ip_repr, icmp_repr)| {
             assert_eq!(ip_repr, LOCAL_IPV4_REPR);
             assert_eq!(icmp_repr, ECHOV4_REPR.into());
             Err(Error::Unaddressable)
@@ -553,7 +553,7 @@ mod test_ipv4 {
         // buffer is not taken off of the tx queue due to the error
         assert!(!socket.can_send());
 
-        assert_eq!(socket.dispatch(&caps, |(ip_repr, icmp_repr)| {
+        assert_eq!(socket.dispatch(|(ip_repr, icmp_repr)| {
             assert_eq!(ip_repr, LOCAL_IPV4_REPR);
             assert_eq!(icmp_repr, ECHOV4_REPR.into());
             Ok(())
@@ -565,16 +565,16 @@ mod test_ipv4 {
     #[test]
     fn test_set_hop_limit_v4() {
         let mut s = socket(buffer(0), buffer(1));
-        let caps = DeviceCapabilities::default();
+        let checksum = ChecksumCapabilities::default();
 
         let mut bytes = [0xff; 24];
         let mut packet = Icmpv4Packet::new_unchecked(&mut bytes);
-        ECHOV4_REPR.emit(&mut packet, &caps.checksum);
+        ECHOV4_REPR.emit(&mut packet, &checksum);
 
         s.set_hop_limit(Some(0x2a));
 
         assert_eq!(s.send_slice(&packet.into_inner()[..], REMOTE_IPV4.into()), Ok(()));
-        assert_eq!(s.dispatch(&caps, |(ip_repr, _)| {
+        assert_eq!(s.dispatch(|(ip_repr, _)| {
             assert_eq!(ip_repr, IpRepr::Ipv4(Ipv4Repr {
                 src_addr: Ipv4Address::UNSPECIFIED,
                 dst_addr: REMOTE_IPV4,
@@ -594,20 +594,20 @@ mod test_ipv4 {
         assert!(!socket.can_recv());
         assert_eq!(socket.recv(), Err(Error::Exhausted));
 
-        let caps = DeviceCapabilities::default();
+        let checksum = ChecksumCapabilities::default();
 
         let mut bytes = [0xff; 24];
         let mut packet = Icmpv4Packet::new_unchecked(&mut bytes);
-        ECHOV4_REPR.emit(&mut packet, &caps.checksum);
+        ECHOV4_REPR.emit(&mut packet, &checksum);
         let data = &packet.into_inner()[..];
 
-        assert!(socket.accepts(&REMOTE_IPV4_REPR, &ECHOV4_REPR.into(), &caps.checksum));
-        assert_eq!(socket.process(&REMOTE_IPV4_REPR, &ECHOV4_REPR.into(), &caps.checksum),
+        assert!(socket.accepts(&REMOTE_IPV4_REPR, &ECHOV4_REPR.into(), &checksum));
+        assert_eq!(socket.process(&REMOTE_IPV4_REPR, &ECHOV4_REPR.into(), &checksum),
                    Ok(()));
         assert!(socket.can_recv());
 
-        assert!(socket.accepts(&REMOTE_IPV4_REPR, &ECHOV4_REPR.into(), &caps.checksum));
-        assert_eq!(socket.process(&REMOTE_IPV4_REPR, &ECHOV4_REPR.into(), &caps.checksum),
+        assert!(socket.accepts(&REMOTE_IPV4_REPR, &ECHOV4_REPR.into(), &checksum));
+        assert_eq!(socket.process(&REMOTE_IPV4_REPR, &ECHOV4_REPR.into(), &checksum),
                    Err(Error::Exhausted));
 
         assert_eq!(socket.recv(), Ok((&data[..], REMOTE_IPV4.into())));
@@ -619,7 +619,7 @@ mod test_ipv4 {
         let mut socket = socket(buffer(1), buffer(1));
         assert_eq!(socket.bind(Endpoint::Ident(0x1234)), Ok(()));
 
-        let caps = DeviceCapabilities::default();
+        let checksum = ChecksumCapabilities::default();
         let mut bytes = [0xff; 20];
         let mut packet = Icmpv4Packet::new_unchecked(&mut bytes);
         let icmp_repr = Icmpv4Repr::EchoRequest {
@@ -627,11 +627,11 @@ mod test_ipv4 {
             seq_no: 0x5678,
             data:   &[0xff; 16]
         };
-        icmp_repr.emit(&mut packet, &caps.checksum);
+        icmp_repr.emit(&mut packet, &checksum);
 
         // Ensure that a packet with an identifier that isn't the bound
         // ID is not accepted
-        assert!(!socket.accepts(&REMOTE_IPV4_REPR, &icmp_repr.into(), &caps.checksum));
+        assert!(!socket.accepts(&REMOTE_IPV4_REPR, &icmp_repr.into(), &checksum));
     }
 
     #[test]
@@ -639,11 +639,11 @@ mod test_ipv4 {
         let mut socket = socket(buffer(1), buffer(1));
         assert_eq!(socket.bind(Endpoint::Udp(LOCAL_END_V4)), Ok(()));
 
-        let caps = DeviceCapabilities::default();
+        let checksum = ChecksumCapabilities::default();
 
         let mut bytes = [0xff; 18];
         let mut packet = UdpPacket::new_unchecked(&mut bytes);
-        UDP_REPR.emit(&mut packet, &REMOTE_IPV4.into(), &LOCAL_IPV4.into(), &caps.checksum);
+        UDP_REPR.emit(&mut packet, &REMOTE_IPV4.into(), &LOCAL_IPV4.into(), &checksum);
 
         let data = &packet.into_inner()[..];
 
@@ -670,14 +670,14 @@ mod test_ipv4 {
 
         // Ensure we can accept ICMP error response to the bound
         // UDP port
-        assert!(socket.accepts(&ip_repr, &icmp_repr.into(), &caps.checksum));
-        assert_eq!(socket.process(&ip_repr, &icmp_repr.into(), &caps.checksum),
+        assert!(socket.accepts(&ip_repr, &icmp_repr.into(), &checksum));
+        assert_eq!(socket.process(&ip_repr, &icmp_repr.into(), &checksum),
                    Ok(()));
         assert!(socket.can_recv());
 
         let mut bytes = [0x00; 46];
         let mut packet = Icmpv4Packet::new_unchecked(&mut bytes[..]);
-        icmp_repr.emit(&mut packet, &caps.checksum);
+        icmp_repr.emit(&mut packet, &checksum);
         assert_eq!(socket.recv(), Ok((&packet.into_inner()[..], REMOTE_IPV4.into())));
         assert!(!socket.can_recv());
     }
@@ -727,9 +727,9 @@ mod test_ipv6 {
     #[test]
     fn test_send_dispatch() {
         let mut socket = socket(buffer(0), buffer(1));
-        let caps = DeviceCapabilities::default();
+        let checksum = ChecksumCapabilities::default();
 
-        assert_eq!(socket.dispatch(&caps, |_| unreachable!()),
+        assert_eq!(socket.dispatch(|_| unreachable!()),
                    Err(Error::Exhausted));
 
         // This buffer is too long
@@ -738,13 +738,13 @@ mod test_ipv6 {
 
         let mut bytes = vec![0xff; 24];
         let mut packet = Icmpv6Packet::new_unchecked(&mut bytes);
-        ECHOV6_REPR.emit(&LOCAL_IPV6.into(), &REMOTE_IPV6.into(), &mut packet, &caps.checksum);
+        ECHOV6_REPR.emit(&LOCAL_IPV6.into(), &REMOTE_IPV6.into(), &mut packet, &checksum);
 
         assert_eq!(socket.send_slice(&packet.into_inner()[..], REMOTE_IPV6.into()), Ok(()));
         assert_eq!(socket.send_slice(b"123456", REMOTE_IPV6.into()), Err(Error::Exhausted));
         assert!(!socket.can_send());
 
-        assert_eq!(socket.dispatch(&caps, |(ip_repr, icmp_repr)| {
+        assert_eq!(socket.dispatch(|(ip_repr, icmp_repr)| {
             assert_eq!(ip_repr, LOCAL_IPV6_REPR);
             assert_eq!(icmp_repr, ECHOV6_REPR.into());
             Err(Error::Unaddressable)
@@ -752,7 +752,7 @@ mod test_ipv6 {
         // buffer is not taken off of the tx queue due to the error
         assert!(!socket.can_send());
 
-        assert_eq!(socket.dispatch(&caps, |(ip_repr, icmp_repr)| {
+        assert_eq!(socket.dispatch(|(ip_repr, icmp_repr)| {
             assert_eq!(ip_repr, LOCAL_IPV6_REPR);
             assert_eq!(icmp_repr, ECHOV6_REPR.into());
             Ok(())
@@ -764,16 +764,16 @@ mod test_ipv6 {
     #[test]
     fn test_set_hop_limit() {
         let mut s = socket(buffer(0), buffer(1));
-        let caps = DeviceCapabilities::default();
+        let checksum = ChecksumCapabilities::default();
 
         let mut bytes = vec![0xff; 24];
         let mut packet = Icmpv6Packet::new_unchecked(&mut bytes);
-        ECHOV6_REPR.emit(&LOCAL_IPV6.into(), &REMOTE_IPV6.into(), &mut packet, &caps.checksum);
+        ECHOV6_REPR.emit(&LOCAL_IPV6.into(), &REMOTE_IPV6.into(), &mut packet, &checksum);
 
         s.set_hop_limit(Some(0x2a));
 
         assert_eq!(s.send_slice(&packet.into_inner()[..], REMOTE_IPV6.into()), Ok(()));
-        assert_eq!(s.dispatch(&caps, |(ip_repr, _)| {
+        assert_eq!(s.dispatch(|(ip_repr, _)| {
             assert_eq!(ip_repr, IpRepr::Ipv6(Ipv6Repr {
                 src_addr: Ipv6Address::UNSPECIFIED,
                 dst_addr: REMOTE_IPV6,
@@ -793,20 +793,20 @@ mod test_ipv6 {
         assert!(!socket.can_recv());
         assert_eq!(socket.recv(), Err(Error::Exhausted));
 
-        let caps = DeviceCapabilities::default();
+        let checksum = ChecksumCapabilities::default();
 
         let mut bytes = [0xff; 24];
         let mut packet = Icmpv6Packet::new_unchecked(&mut bytes);
-        ECHOV6_REPR.emit(&LOCAL_IPV6.into(), &REMOTE_IPV6.into(), &mut packet, &caps.checksum);
+        ECHOV6_REPR.emit(&LOCAL_IPV6.into(), &REMOTE_IPV6.into(), &mut packet, &checksum);
         let data = &packet.into_inner()[..];
 
-        assert!(socket.accepts(&REMOTE_IPV6_REPR, &ECHOV6_REPR.into(), &caps.checksum));
-        assert_eq!(socket.process(&REMOTE_IPV6_REPR, &ECHOV6_REPR.into(), &caps.checksum),
+        assert!(socket.accepts(&REMOTE_IPV6_REPR, &ECHOV6_REPR.into(), &checksum));
+        assert_eq!(socket.process(&REMOTE_IPV6_REPR, &ECHOV6_REPR.into(), &checksum),
                    Ok(()));
         assert!(socket.can_recv());
 
-        assert!(socket.accepts(&REMOTE_IPV6_REPR, &ECHOV6_REPR.into(), &caps.checksum));
-        assert_eq!(socket.process(&REMOTE_IPV6_REPR, &ECHOV6_REPR.into(), &caps.checksum),
+        assert!(socket.accepts(&REMOTE_IPV6_REPR, &ECHOV6_REPR.into(), &checksum));
+        assert_eq!(socket.process(&REMOTE_IPV6_REPR, &ECHOV6_REPR.into(), &checksum),
                    Err(Error::Exhausted));
 
         assert_eq!(socket.recv(), Ok((&data[..], REMOTE_IPV6.into())));
@@ -818,7 +818,7 @@ mod test_ipv6 {
         let mut socket = socket(buffer(1), buffer(1));
         assert_eq!(socket.bind(Endpoint::Ident(0x1234)), Ok(()));
 
-        let caps = DeviceCapabilities::default();
+        let checksum = ChecksumCapabilities::default();
         let mut bytes = [0xff; 20];
         let mut packet = Icmpv6Packet::new_unchecked(&mut bytes);
         let icmp_repr = Icmpv6Repr::EchoRequest {
@@ -826,11 +826,11 @@ mod test_ipv6 {
             seq_no: 0x5678,
             data:   &[0xff; 16]
         };
-        icmp_repr.emit(&LOCAL_IPV6.into(), &REMOTE_IPV6.into(), &mut packet, &caps.checksum);
+        icmp_repr.emit(&LOCAL_IPV6.into(), &REMOTE_IPV6.into(), &mut packet, &checksum);
 
         // Ensure that a packet with an identifier that isn't the bound
         // ID is not accepted
-        assert!(!socket.accepts(&REMOTE_IPV6_REPR, &icmp_repr.into(), &caps.checksum));
+        assert!(!socket.accepts(&REMOTE_IPV6_REPR, &icmp_repr.into(), &checksum));
     }
 
     #[test]
@@ -838,11 +838,11 @@ mod test_ipv6 {
         let mut socket = socket(buffer(1), buffer(1));
         assert_eq!(socket.bind(Endpoint::Udp(LOCAL_END_V6)), Ok(()));
 
-        let caps = DeviceCapabilities::default();
+        let checksum = ChecksumCapabilities::default();
 
         let mut bytes = [0xff; 18];
         let mut packet = UdpPacket::new_unchecked(&mut bytes);
-        UDP_REPR.emit(&mut packet, &REMOTE_IPV6.into(), &LOCAL_IPV6.into(), &caps.checksum);
+        UDP_REPR.emit(&mut packet, &REMOTE_IPV6.into(), &LOCAL_IPV6.into(), &checksum);
 
         let data = &packet.into_inner()[..];
 
@@ -869,14 +869,14 @@ mod test_ipv6 {
 
         // Ensure we can accept ICMP error response to the bound
         // UDP port
-        assert!(socket.accepts(&ip_repr, &icmp_repr.into(), &caps.checksum));
-        assert_eq!(socket.process(&ip_repr, &icmp_repr.into(), &caps.checksum),
+        assert!(socket.accepts(&ip_repr, &icmp_repr.into(), &checksum));
+        assert_eq!(socket.process(&ip_repr, &icmp_repr.into(), &checksum),
                    Ok(()));
         assert!(socket.can_recv());
 
         let mut bytes = [0x00; 66];
         let mut packet = Icmpv6Packet::new_unchecked(&mut bytes[..]);
-        icmp_repr.emit(&LOCAL_IPV6.into(), &REMOTE_IPV6.into(), &mut packet, &caps.checksum);
+        icmp_repr.emit(&LOCAL_IPV6.into(), &REMOTE_IPV6.into(), &mut packet, &checksum);
         assert_eq!(socket.recv(), Ok((&packet.into_inner()[..], REMOTE_IPV6.into())));
         assert!(!socket.can_recv());
     }

+ 7 - 14
src/socket/tcp.rs

@@ -7,7 +7,6 @@ use core::{cmp, fmt, mem};
 use core::task::Waker;
 
 use crate::{Error, Result};
-use crate::phy::DeviceCapabilities;
 use crate::time::{Duration, Instant};
 use crate::socket::{Socket, SocketMeta, SocketHandle, PollAt};
 use crate::storage::{Assembler, RingBuffer};
@@ -1662,7 +1661,7 @@ impl<'a> TcpSocket<'a> {
         }
     }
 
-    pub(crate) fn dispatch<F>(&mut self, timestamp: Instant, caps: &DeviceCapabilities,
+    pub(crate) fn dispatch<F>(&mut self, timestamp: Instant, ip_mtu: usize,
                               emit: F) -> Result<()>
             where F: FnOnce((IpRepr, TcpRepr)) -> Result<()> {
         if !self.remote_endpoint.is_specified() { return Err(Error::Exhausted) }
@@ -1789,7 +1788,7 @@ impl<'a> TcpSocket<'a> {
                 let offset = self.remote_last_seq - self.local_seq_no;
                 let win_limit = self.local_seq_no + self.remote_win_len - self.remote_last_seq;
                 let size = cmp::min(cmp::min(win_limit, self.remote_mss),
-                     caps.max_transmission_unit - ip_repr.buffer_len() - repr.mss_header_len());
+                     ip_mtu - ip_repr.buffer_len() - repr.mss_header_len());
                 repr.payload = self.tx_buffer.get_allocated(offset, size);
                 // If we've sent everything we had in the buffer, follow it with the PSH or FIN
                 // flags, depending on whether the transmit half of the connection is open.
@@ -1848,7 +1847,7 @@ impl<'a> TcpSocket<'a> {
 
         if repr.control == TcpControl::Syn {
             // Fill the MSS option. See RFC 6691 for an explanation of this calculation.
-            let mut max_segment_size = caps.max_transmission_unit;
+            let mut max_segment_size = ip_mtu;
             max_segment_size -= ip_repr.buffer_len();
             max_segment_size -= repr.mss_header_len();
             repr.max_seg_size = Some(max_segment_size as u16);
@@ -2044,11 +2043,8 @@ mod test {
 
     fn recv<F>(socket: &mut TcpSocket, timestamp: Instant, mut f: F)
             where F: FnMut(Result<TcpRepr>) {
-        let caps = DeviceCapabilities {
-            max_transmission_unit: 1520,
-            ..Default::default()
-        };
-        let result = socket.dispatch(timestamp, &caps, |(ip_repr, tcp_repr)| {
+        let mtu = 1520;
+        let result = socket.dispatch(timestamp, mtu, |(ip_repr, tcp_repr)| {
             let ip_repr = ip_repr.lower(&[IpCidr::new(LOCAL_END.addr, 24)]).unwrap();
 
             assert_eq!(ip_repr.protocol(), IpProtocol::Tcp);
@@ -4897,13 +4893,10 @@ mod test {
     #[test]
     fn test_set_hop_limit() {
         let mut s = socket_syn_received();
-        let caps = DeviceCapabilities {
-            max_transmission_unit: 1520,
-            ..Default::default()
-        };
+        let mtu = 1520;
 
         s.set_hop_limit(Some(0x2a));
-        assert_eq!(s.dispatch(Instant::from_millis(0), &caps, |(ip_repr, _)| {
+        assert_eq!(s.dispatch(Instant::from_millis(0), mtu, |(ip_repr, _)| {
             assert_eq!(ip_repr.hop_limit(), 0x2a);
             Ok(())
         }), Ok(()));

+ 5 - 5
src/wire/icmpv6.rs

@@ -6,7 +6,7 @@ use crate::phy::ChecksumCapabilities;
 use crate::wire::ip::checksum;
 use crate::wire::{IpAddress, IpProtocol, Ipv6Packet, Ipv6Repr};
 use crate::wire::MldRepr;
-#[cfg(feature = "ethernet")]
+#[cfg(feature = "medium-ethernet")]
 use crate::wire::NdiscRepr;
 
 enum_with_unknown! {
@@ -536,7 +536,7 @@ pub enum Repr<'a> {
         seq_no: u16,
         data:   &'a [u8]
     },
-    #[cfg(feature = "ethernet")]
+    #[cfg(feature = "medium-ethernet")]
     Ndisc(NdiscRepr<'a>),
     Mld(MldRepr<'a>),
 }
@@ -617,7 +617,7 @@ impl<'a> Repr<'a> {
                     data:   packet.payload()
                 })
             },
-            #[cfg(feature = "ethernet")]
+            #[cfg(feature = "medium-ethernet")]
             (msg_type, 0) if msg_type.is_ndisc() => {
                 NdiscRepr::parse(packet).map(Repr::Ndisc)
             },
@@ -639,7 +639,7 @@ impl<'a> Repr<'a> {
             &Repr::EchoReply { data, .. } => {
                 field::ECHO_SEQNO.end + data.len()
             },
-            #[cfg(feature = "ethernet")]
+            #[cfg(feature = "medium-ethernet")]
             &Repr::Ndisc(ndisc) => {
                 ndisc.buffer_len()
             },
@@ -710,7 +710,7 @@ impl<'a> Repr<'a> {
                 packet.payload_mut()[..data_len].copy_from_slice(&data[..data_len])
             },
 
-            #[cfg(feature = "ethernet")]
+            #[cfg(feature = "medium-ethernet")]
             Repr::Ndisc(ndisc) => {
                 ndisc.emit(packet)
             },

+ 8 - 8
src/wire/mod.rs

@@ -77,9 +77,9 @@ mod field {
 
 pub mod pretty_print;
 
-#[cfg(feature = "ethernet")]
+#[cfg(feature = "medium-ethernet")]
 mod ethernet;
-#[cfg(all(feature = "proto-ipv4", feature = "ethernet"))]
+#[cfg(all(feature = "proto-ipv4", feature = "medium-ethernet"))]
 mod arp;
 pub(crate) mod ip;
 #[cfg(feature = "proto-ipv4")]
@@ -102,9 +102,9 @@ mod icmpv6;
 mod icmp;
 #[cfg(feature = "proto-igmp")]
 mod igmp;
-#[cfg(all(feature = "proto-ipv6", feature = "ethernet"))]
+#[cfg(all(feature = "proto-ipv6", feature = "medium-ethernet"))]
 mod ndisc;
-#[cfg(all(feature = "proto-ipv6", feature = "ethernet"))]
+#[cfg(all(feature = "proto-ipv6", feature = "medium-ethernet"))]
 mod ndiscoption;
 #[cfg(feature = "proto-ipv6")]
 mod mld;
@@ -115,14 +115,14 @@ pub(crate) mod dhcpv4;
 
 pub use self::pretty_print::PrettyPrinter;
 
-#[cfg(feature = "ethernet")]
+#[cfg(feature = "medium-ethernet")]
 pub use self::ethernet::{EtherType as EthernetProtocol,
                          Address as EthernetAddress,
                          Frame as EthernetFrame,
                          HEADER_LEN as ETHERNET_HEADER_LEN,
                          Repr as EthernetRepr};
 
-#[cfg(all(feature = "proto-ipv4", feature = "ethernet"))]
+#[cfg(all(feature = "proto-ipv4", feature = "medium-ethernet"))]
 pub use self::arp::{Hardware as ArpHardware,
                     Operation as ArpOperation,
                     Packet as ArpPacket,
@@ -193,12 +193,12 @@ pub use self::icmpv6::{Message as Icmpv6Message,
 pub use self::icmp::Repr as IcmpRepr;
 
 
-#[cfg(all(feature = "proto-ipv6", feature = "ethernet"))]
+#[cfg(all(feature = "proto-ipv6", feature = "medium-ethernet"))]
 pub use self::ndisc::{Repr as NdiscRepr,
                       RouterFlags as NdiscRouterFlags,
                       NeighborFlags as NdiscNeighborFlags};
 
-#[cfg(all(feature = "proto-ipv6", feature = "ethernet"))]
+#[cfg(all(feature = "proto-ipv6", feature = "medium-ethernet"))]
 pub use self::ndiscoption::{NdiscOption,
                             Repr as NdiscOptionRepr,
                             Type as NdiscOptionType,

部分文件因文件數量過多而無法顯示