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

Add IP medium support, part 2
master
Dario Nieuwenhuis 2021-03-31 17:16:18 +02:00 committed by GitHub
commit 5947c5947e
31 changed files with 814 additions and 603 deletions

View File

@ -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

View File

@ -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

View File

@ -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):

View File

@ -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);

View File

@ -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);

View File

@ -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(

View File

@ -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);

View File

@ -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)

View File

@ -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])

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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)

View File

@ -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};

File diff suppressed because it is too large Load Diff

View File

@ -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};

View File

@ -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

View File

@ -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"),

View File

@ -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()
}
}

View File

@ -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

View File

@ -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 }
}

View File

@ -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()
}
}

View File

@ -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;

View File

@ -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 {

View File

@ -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); }
}

View File

@ -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")
}
}
}
}

View File

@ -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 {

View File

@ -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());
}

View File

@ -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(()));

View File

@ -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)
},

View File

@ -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,