Implement a Device that emits a stream in libpcap format.
parent
e381f6ec3f
commit
250a84e513
|
@ -1,2 +1,3 @@
|
||||||
target
|
/target
|
||||||
Cargo.lock
|
Cargo.lock
|
||||||
|
*.pcap
|
||||||
|
|
|
@ -14,14 +14,14 @@ use collections::{Vec, VecDeque};
|
||||||
use Error;
|
use Error;
|
||||||
use super::{Device, DeviceLimits};
|
use super::{Device, DeviceLimits};
|
||||||
|
|
||||||
/// A loopback interface.
|
/// A loopback device.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Loopback(Rc<RefCell<VecDeque<Vec<u8>>>>);
|
pub struct Loopback(Rc<RefCell<VecDeque<Vec<u8>>>>);
|
||||||
|
|
||||||
impl Loopback {
|
impl Loopback {
|
||||||
/// Creates a loopback interface.
|
/// Creates a loopback device.
|
||||||
///
|
///
|
||||||
/// Every packet transmitted through this interface will be received through it
|
/// Every packet transmitted through this device will be received through it
|
||||||
/// in FIFO order.
|
/// in FIFO order.
|
||||||
pub fn new() -> Loopback {
|
pub fn new() -> Loopback {
|
||||||
Loopback(Rc::new(RefCell::new(VecDeque::new())))
|
Loopback(Rc::new(RefCell::new(VecDeque::new())))
|
||||||
|
|
|
@ -36,7 +36,7 @@ fn rx_setup(_buf: *mut u8, _length: &mut usize) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tx_empty() -> bool {
|
fn tx_empty() -> bool {
|
||||||
/* platform-specific code to check if the outgoing packet was sent */
|
/* platform-specific code to check if an outgoing packet can be sent */
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,6 +111,7 @@ mod sys;
|
||||||
|
|
||||||
mod tracer;
|
mod tracer;
|
||||||
mod fault_injector;
|
mod fault_injector;
|
||||||
|
mod pcap_writer;
|
||||||
#[cfg(any(feature = "std", feature = "collections"))]
|
#[cfg(any(feature = "std", feature = "collections"))]
|
||||||
mod loopback;
|
mod loopback;
|
||||||
#[cfg(feature = "raw_socket")]
|
#[cfg(feature = "raw_socket")]
|
||||||
|
@ -120,6 +121,7 @@ mod tap_interface;
|
||||||
|
|
||||||
pub use self::tracer::Tracer;
|
pub use self::tracer::Tracer;
|
||||||
pub use self::fault_injector::FaultInjector;
|
pub use self::fault_injector::FaultInjector;
|
||||||
|
pub use self::pcap_writer::{PcapLinkType, PcapMode, PcapSink, PcapWriter};
|
||||||
#[cfg(any(feature = "std", feature = "collections"))]
|
#[cfg(any(feature = "std", feature = "collections"))]
|
||||||
pub use self::loopback::Loopback;
|
pub use self::loopback::Loopback;
|
||||||
#[cfg(any(feature = "raw_socket"))]
|
#[cfg(any(feature = "raw_socket"))]
|
||||||
|
|
|
@ -0,0 +1,182 @@
|
||||||
|
use core::cell::RefCell;
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use std::io::Write;
|
||||||
|
use byteorder::{ByteOrder, NativeEndian};
|
||||||
|
|
||||||
|
use Error;
|
||||||
|
use super::{DeviceLimits, Device};
|
||||||
|
|
||||||
|
enum_with_unknown! {
|
||||||
|
/// Captured packet header type.
|
||||||
|
pub doc enum PcapLinkType(u32) {
|
||||||
|
/// Ethernet frames
|
||||||
|
Ethernet = 1,
|
||||||
|
/// IPv4 or IPv6 packets (depending on the version field)
|
||||||
|
Ip = 101
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Packet capture mode.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum PcapMode {
|
||||||
|
/// Capture both received and transmitted packets.
|
||||||
|
Both,
|
||||||
|
/// Capture only received packets.
|
||||||
|
RxOnly,
|
||||||
|
/// Capture only transmitted packets.
|
||||||
|
TxOnly
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A packet capture sink.
|
||||||
|
///
|
||||||
|
/// A sink is an interface to the platform functions, providing timestamping
|
||||||
|
/// and streaming data output.
|
||||||
|
pub trait PcapSink {
|
||||||
|
/// Write data into the sink.
|
||||||
|
fn write(&self, data: &[u8]);
|
||||||
|
|
||||||
|
/// Write an `u16` into the sink, in native byte order.
|
||||||
|
fn write_u16(&self, value: u16) {
|
||||||
|
let mut bytes = [0u8; 2];
|
||||||
|
NativeEndian::write_u16(&mut bytes, value);
|
||||||
|
self.write(&bytes[..])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write an `u32` into the sink, in native byte order.
|
||||||
|
fn write_u32(&self, value: u32) {
|
||||||
|
let mut bytes = [0u8; 4];
|
||||||
|
NativeEndian::write_u32(&mut bytes, value);
|
||||||
|
self.write(&bytes[..])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write the libpcap global header into the sink.
|
||||||
|
///
|
||||||
|
/// This method may be overridden e.g. if special synchronization is necessary.
|
||||||
|
fn global_header(&self, link_type: PcapLinkType) {
|
||||||
|
self.write_u32(0xa1b2c3d4); // magic number
|
||||||
|
self.write_u16(2); // major version
|
||||||
|
self.write_u16(4); // minor version
|
||||||
|
self.write_u32(0); // timezone (= UTC)
|
||||||
|
self.write_u32(0); // accuracy (not used)
|
||||||
|
self.write_u32(65535); // maximum packet length
|
||||||
|
self.write_u32(link_type.into()); // link-layer header type
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write the libpcap packet header into the sink.
|
||||||
|
///
|
||||||
|
/// See also the note for [global_header](#method.global_header).
|
||||||
|
fn packet_header(&self, timestamp: u64, length: usize) {
|
||||||
|
assert!(length <= 65535);
|
||||||
|
|
||||||
|
let (seconds, micros) = (timestamp / 1000, timestamp % 1000 * 1000);
|
||||||
|
self.write_u32(seconds as u32); // timestamp seconds
|
||||||
|
self.write_u32(micros as u32); // timestamp microseconds
|
||||||
|
self.write_u32(length as u32); // captured length
|
||||||
|
self.write_u32(length as u32); // original length
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write the libpcap packet header followed by packet data into the sink.
|
||||||
|
///
|
||||||
|
/// See also the note for [global_header](#method.global_header).
|
||||||
|
fn packet(&self, timestamp: u64, packet: &[u8]) {
|
||||||
|
self.packet_header(timestamp, packet.len());
|
||||||
|
self.write(packet)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: AsRef<PcapSink>> PcapSink for T {
|
||||||
|
fn write(&self, data: &[u8]) {
|
||||||
|
self.as_ref().write(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
impl<T: AsMut<Write>> PcapSink for RefCell<T> {
|
||||||
|
fn write(&self, data: &[u8]) {
|
||||||
|
self.borrow_mut().as_mut().write_all(data).expect("cannot write")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn packet(&self, timestamp: u64, packet: &[u8]) {
|
||||||
|
self.packet_header(timestamp, packet.len());
|
||||||
|
PcapSink::write(self, packet);
|
||||||
|
self.borrow_mut().as_mut().flush().expect("cannot flush")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A packet capture writer device.
|
||||||
|
///
|
||||||
|
/// Every packet transmitted or received through this device is timestamped
|
||||||
|
/// and written (in the [libpcap] format) using the provided [sink].
|
||||||
|
/// Note that writes are fine-grained, and buffering is recommended.
|
||||||
|
///
|
||||||
|
/// The packet sink should be cheaply cloneable, as it is cloned on every
|
||||||
|
/// transmitted packet. For example, `&'a mut Vec<u8>` is cheaply cloneable
|
||||||
|
/// but `&std::io::File`
|
||||||
|
///
|
||||||
|
/// [libpcap]: https://wiki.wireshark.org/Development/LibpcapFileFormat
|
||||||
|
/// [sink]: trait.PcapSink.html
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PcapWriter<D: Device, S: PcapSink + Clone> {
|
||||||
|
lower: D,
|
||||||
|
sink: S,
|
||||||
|
mode: PcapMode
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D: Device, 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> {
|
||||||
|
sink.global_header(link_type);
|
||||||
|
PcapWriter { lower, sink, mode }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D: Device, S: PcapSink + Clone> Device for PcapWriter<D, S> {
|
||||||
|
type RxBuffer = D::RxBuffer;
|
||||||
|
type TxBuffer = TxBuffer<D::TxBuffer, S>;
|
||||||
|
|
||||||
|
fn limits(&self) -> DeviceLimits { self.lower.limits() }
|
||||||
|
|
||||||
|
fn receive(&mut self, timestamp: u64) -> Result<Self::RxBuffer, Error> {
|
||||||
|
let buffer = self.lower.receive(timestamp)?;
|
||||||
|
match self.mode {
|
||||||
|
PcapMode::Both | PcapMode::RxOnly =>
|
||||||
|
self.sink.packet(timestamp, buffer.as_ref()),
|
||||||
|
PcapMode::TxOnly => ()
|
||||||
|
}
|
||||||
|
Ok(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn transmit(&mut self, timestamp: u64, length: usize) -> Result<Self::TxBuffer, Error> {
|
||||||
|
let buffer = self.lower.transmit(timestamp, length)?;
|
||||||
|
Ok(TxBuffer { buffer, timestamp, sink: self.sink.clone(), mode: self.mode })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub struct TxBuffer<B: AsRef<[u8]> + AsMut<[u8]>, S: PcapSink> {
|
||||||
|
buffer: B,
|
||||||
|
timestamp: u64,
|
||||||
|
sink: S,
|
||||||
|
mode: PcapMode
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B, S> AsRef<[u8]> for TxBuffer<B, S>
|
||||||
|
where B: AsRef<[u8]> + AsMut<[u8]>, S: PcapSink {
|
||||||
|
fn as_ref(&self) -> &[u8] { self.buffer.as_ref() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B, S> AsMut<[u8]> for TxBuffer<B, S>
|
||||||
|
where B: AsRef<[u8]> + AsMut<[u8]>, S: PcapSink {
|
||||||
|
fn as_mut(&mut self) -> &mut [u8] { self.buffer.as_mut() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B, S> Drop for TxBuffer<B, S>
|
||||||
|
where B: AsRef<[u8]> + AsMut<[u8]>, S: PcapSink {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
match self.mode {
|
||||||
|
PcapMode::Both | PcapMode::TxOnly =>
|
||||||
|
self.sink.packet(self.timestamp, self.as_ref()),
|
||||||
|
PcapMode::RxOnly => ()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue