Factor out pretty-printing of packets.

This commit is contained in:
whitequark 2016-12-10 23:15:56 +00:00
parent e7d6237279
commit 2a438b6e8d
5 changed files with 135 additions and 38 deletions

View File

@ -2,32 +2,14 @@ extern crate smoltcp;
use std::env;
use smoltcp::phy::{Device, RawSocket};
use smoltcp::wire::{EthernetFrame, EthernetProtocolType, ArpPacket};
fn print_frame(buffer: &[u8]) -> Result<(), ()> {
let frame = try!(EthernetFrame::new(&buffer[..]));
println!("{}", frame);
match frame.ethertype() {
EthernetProtocolType::Arp => {
let packet = try!(ArpPacket::new(frame.payload()));
println!("| {}", packet);
},
_ => ()
}
Ok(())
}
use smoltcp::wire::{PrettyPrinter, EthernetFrame};
fn main() {
let ifname = env::args().nth(1).unwrap();
let mut socket = RawSocket::new(ifname.as_ref()).unwrap();
loop {
socket.recv(|buffer| {
match print_frame(buffer) {
Ok(()) => (),
Err(()) => println!("buffer too small")
}
print!("{}", PrettyPrinter::<EthernetFrame<_>>::new("", &buffer))
})
}
}

View File

@ -210,24 +210,6 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
}
}
impl<T: AsRef<[u8]>> fmt::Display for Packet<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match Repr::parse(self) {
Ok(repr) => write!(f, "{}", repr),
_ => {
try!(write!(f, "ARP htype={:?} ptype={:?} hlen={:?} plen={:?} op={:?}",
self.hardware_type(), self.protocol_type(),
self.hardware_length(), self.protocol_length(),
self.operation()));
try!(write!(f, " sha={:?} spa={:?} tha={:?} tpa={:?}",
self.source_hardware_addr(), self.source_protocol_addr(),
self.target_hardware_addr(), self.target_protocol_addr()));
Ok(())
}
}
}
}
use super::{EthernetAddress, Ipv4Address};
/// A high-level representation of an Address Resolution Protocol packet.
@ -291,6 +273,24 @@ impl Repr {
}
}
impl<T: AsRef<[u8]>> fmt::Display for Packet<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match Repr::parse(self) {
Ok(repr) => write!(f, "{}", repr),
_ => {
try!(write!(f, "ARP htype={:?} ptype={:?} hlen={:?} plen={:?} op={:?}",
self.hardware_type(), self.protocol_type(),
self.hardware_length(), self.protocol_length(),
self.operation()));
try!(write!(f, " sha={:?} spa={:?} tha={:?} tpa={:?}",
self.source_hardware_addr(), self.source_protocol_addr(),
self.target_hardware_addr(), self.target_protocol_addr()));
Ok(())
}
}
}
}
impl fmt::Display for Repr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
@ -309,6 +309,18 @@ impl fmt::Display for Repr {
}
}
use super::pretty_print::{PrettyPrint, PrettyIndent};
impl<T: AsRef<[u8]>> PrettyPrint<T> for Packet<T> {
fn pretty_print(buffer: T, f: &mut fmt::Formatter,
indent: &mut PrettyIndent) -> fmt::Result {
match Packet::new(buffer) {
Err(()) => write!(f, "{}(truncated)\n", indent),
Ok(frame) => write!(f, "{}{}\n", indent, frame)
}
}
}
#[cfg(test)]
mod test {
use super::*;

View File

@ -147,6 +147,26 @@ impl<T: AsRef<[u8]>> fmt::Display for Frame<T> {
}
}
use super::pretty_print::{PrettyPrint, PrettyIndent};
impl<T: AsRef<[u8]>> PrettyPrint<T> for Frame<T> {
fn pretty_print(buffer: T, f: &mut fmt::Formatter,
indent: &mut PrettyIndent) -> fmt::Result {
let frame = match Frame::new(buffer) {
Err(()) => return write!(f, "{}(truncated)\n", indent),
Ok(frame) => frame
};
try!(write!(f, "{}{}\n", indent, frame));
indent.increase();
match frame.ethertype() {
EtherType::Arp =>
super::ArpPacket::pretty_print(frame.payload(), f, indent),
_ => Ok(())
}
}
}
#[cfg(test)]
mod test {
use super::*;

View File

@ -55,10 +55,14 @@ mod field {
pub type FieldFrom = ::core::ops::RangeFrom<usize>;
}
pub mod pretty_print;
mod ethernet;
mod arp;
mod ipv4;
pub use self::pretty_print::PrettyPrinter;
pub use self::ethernet::EtherType as EthernetProtocolType;
pub use self::ethernet::Address as EthernetAddress;
pub use self::ethernet::Frame as EthernetFrame;

79
src/wire/pretty_print.rs Normal file
View File

@ -0,0 +1,79 @@
//! Pretty-printing of packet representation.
//!
//! The `pretty_print` module provides bits and pieces for printing concise,
//! easily human readable packet listings.
//!
//! # Example
//!
//! A packet can be formatted using the `PrettyPrinter` wrapper:
//!
//! ```rust,ignore
//! print!("{}", PrettyPrinter::<EthernetFrame<_>>::new("", &buffer))
//! ```
use core::fmt;
use core::marker::PhantomData;
/// Indentation state.
#[derive(Debug)]
pub struct PrettyIndent {
prefix: &'static str,
level: usize
}
impl PrettyIndent {
/// Create an indentation state. The entire listing will be indented by the width
/// of `prefix`, and `prefix` will appear at the start of the first line.
pub fn new(prefix: &'static str) -> PrettyIndent {
PrettyIndent { prefix: prefix, level: 0 }
}
/// Increase indentation level.
pub fn increase(&mut self) {
self.level += 1
}
}
impl fmt::Display for PrettyIndent {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.level == 0 {
write!(f, "{}", self.prefix)
} else {
write!(f, "{0:1$}{0:2$}| ", "", self.prefix.len(), (self.level - 1) * 2)
}
}
}
/// Interface for printing listings.
pub trait PrettyPrint<T: AsRef<[u8]>> {
/// Write a concise, formatted representation of a packet contained in the provided
/// buffer, and any nested packets it may contain.
///
/// `pretty_print` accepts a buffer and not a packet wrapper because the packet might
/// be truncated, and so it might not be possible to create the packet wrapper.
fn pretty_print(buffer: T, fmt: &mut fmt::Formatter, indent: &mut PrettyIndent) -> fmt::Result;
}
/// Wrapper for using a `PrettyPrint` where a `Display` is expected.
pub struct PrettyPrinter<'a, T: PrettyPrint<&'a AsRef<[u8]>>> {
prefix: &'static str,
buffer: &'a AsRef<[u8]>,
phantom: PhantomData<T>
}
impl<'a, T: PrettyPrint<&'a AsRef<[u8]>>> PrettyPrinter<'a, T> {
/// Format the listing with the recorded parameters when Display::fmt is called.
pub fn new(prefix: &'static str, buffer: &'a AsRef<[u8]>) -> PrettyPrinter<'a, T> {
PrettyPrinter {
prefix: prefix,
buffer: buffer,
phantom: PhantomData
}
}
}
impl<'a, T: PrettyPrint<&'a AsRef<[u8]>>> fmt::Display for PrettyPrinter<'a, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
T::pretty_print(self.buffer, f, &mut PrettyIndent::new(self.prefix))
}
}