Browse Source

Factor out pretty-printing of packets.

whitequark 8 years ago
parent
commit
5eff8040e4
5 changed files with 135 additions and 38 deletions
  1. 2 20
      examples/smoltcpdump.rs
  2. 30 18
      src/wire/arp.rs
  3. 20 0
      src/wire/ethernet.rs
  4. 4 0
      src/wire/mod.rs
  5. 79 0
      src/wire/pretty_print.rs

+ 2 - 20
examples/smoltcpdump.rs

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

+ 30 - 18
src/wire/arp.rs

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

+ 20 - 0
src/wire/ethernet.rs

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

+ 4 - 0
src/wire/mod.rs

@@ -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 - 0
src/wire/pretty_print.rs

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