Add IP medium support to PcapWriter and Tracer.
parent
9e3b373e36
commit
6e8c2a8455
20
README.md
20
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):
|
||||
|
|
|
@ -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<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())
|
||||
|
@ -136,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);
|
||||
|
|
|
@ -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<T> = Tracer<T, super::wire::EthernetFrame<&'static [u8]>>;
|
||||
|
||||
/// A description of checksum behavior for a particular protocol.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum Checksum {
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue