diff --git a/README.md b/README.md index 6098989..dc616ec 100644 --- a/README.md +++ b/README.md @@ -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): diff --git a/examples/utils.rs b/examples/utils.rs index 497ad97..b4687a4 100644 --- a/examples/utils.rs +++ b/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, Medium}; +use smoltcp::phy::{Device, Tracer, FaultInjector, Medium}; #[cfg(feature = "phy-tuntap_interface")] use smoltcp::phy::TunTapInterface; -use smoltcp::phy::{PcapWriter, PcapSink, PcapMode, PcapLinkType}; +use smoltcp::phy::{PcapWriter, PcapSink, PcapMode}; use smoltcp::phy::RawSocket; use smoltcp::time::{Duration, Instant}; @@ -111,7 +111,7 @@ pub fn add_middleware_options(opts: &mut Options, _free: &mut Vec<&str>) { } pub fn parse_middleware_options(matches: &mut Matches, device: D, loopback: bool) - -> FaultInjector>>> + -> FaultInjector>>> where D: for<'a> Device<'a> { let drop_chance = matches.opt_str("drop-chance").map(|s| u8::from_str(&s).unwrap()) @@ -136,13 +136,17 @@ pub fn parse_middleware_options(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, - 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, + 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); diff --git a/src/phy/mod.rs b/src/phy/mod.rs index df4a2e8..b6ae5ff 100644 --- a/src/phy/mod.rs +++ b/src/phy/mod.rs @@ -117,11 +117,6 @@ pub use self::raw_socket::RawSocket; #[cfg(all(feature = "phy-tuntap_interface", any(target_os = "linux", target_os = "android")))] pub use self::tuntap_interface::TunTapInterface; - -#[cfg(feature = "medium-ethernet")] -/// A tracer device for Ethernet frames. -pub type EthernetTracer = Tracer>; - /// A description of checksum behavior for a particular protocol. #[derive(Debug, Clone, Copy)] pub enum Checksum { diff --git a/src/phy/pcap_writer.rs b/src/phy/pcap_writer.rs index afee866..c14118d 100644 --- a/src/phy/pcap_writer.rs +++ b/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 impl Device<'a>, S: PcapSink + Clone> PcapWriter { /// Creates a packet capture writer. - pub fn new(lower: D, sink: S, mode: PcapMode, link_type: PcapLinkType) -> PcapWriter { + pub fn new(lower: D, sink: S, mode: PcapMode) -> PcapWriter { + 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 } } diff --git a/src/phy/tracer.rs b/src/phy/tracer.rs index d9ecfd2..8a5e377 100644 --- a/src/phy/tracer.rs +++ b/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 Device<'a>, P: PrettyPrint> { +pub struct Tracer Device<'a>> { inner: D, - writer: fn(Instant, PrettyPrinter

), + writer: fn(Instant, Packet), } -impl Device<'a>, P: PrettyPrint> Tracer { +impl Device<'a>> Tracer { /// Create a tracer device. - pub fn new(inner: D, writer: fn(timestamp: Instant, printer: PrettyPrinter

)) -> Tracer { + pub fn new(inner: D, writer: fn(timestamp: Instant, packet: Packet)) -> Tracer { Tracer { inner, writer } } @@ -40,65 +41,100 @@ impl Device<'a>, P: PrettyPrint> Tracer { } } -impl<'a, D, P> Device<'a> for Tracer +impl<'a, D> Device<'a> for Tracer where D: for<'b> Device<'b>, - P: PrettyPrint + 'a, { - type RxToken = RxToken<>::RxToken, P>; - type TxToken = TxToken<>::TxToken, P>; + type RxToken = RxToken<>::RxToken>; + type TxToken = TxToken<>::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 { 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 { +pub struct RxToken { token: Rx, - writer: fn(Instant, PrettyPrinter

) + writer: fn(Instant, Packet), + medium: Medium, } -impl phy::RxToken for RxToken { +impl phy::RxToken for RxToken { fn consume(self, timestamp: Instant, f: F) -> Result where F: FnOnce(&mut [u8]) -> Result { - let Self { token, writer } = self; + let Self { token, writer, medium } = self; token.consume(timestamp, |buffer| { - writer(timestamp, PrettyPrinter::

::new("<- ", &buffer)); + writer(timestamp, Packet{ + buffer, + medium, + prefix: "<- ", + }); f(buffer) }) } } #[doc(hidden)] -pub struct TxToken { +pub struct TxToken { token: Tx, - writer: fn(Instant, PrettyPrinter

) + writer: fn(Instant, Packet), + medium: Medium, } -impl phy::TxToken for TxToken { +impl phy::TxToken for TxToken { fn consume(self, timestamp: Instant, len: usize, f: F) -> Result where F: FnOnce(&mut [u8]) -> Result { - let Self { token, writer } = self; + let Self { token, writer, medium } = self; token.consume(timestamp, len, |buffer| { let result = f(buffer); - writer(timestamp, PrettyPrinter::

::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") + } + } + } +} \ No newline at end of file