Browse Source

Implement ICMPv4 echo request/reply representation parsing and emission.

whitequark 8 năm trước cách đây
mục cha
commit
8204502725
4 tập tin đã thay đổi với 192 bổ sung4 xóa
  1. 179 0
      src/wire/icmpv4.rs
  2. 11 3
      src/wire/ipv4.rs
  3. 1 0
      src/wire/mod.rs
  4. 1 1
      src/wire/pretty_print.rs

+ 179 - 0
src/wire/icmpv4.rs

@@ -30,6 +30,24 @@ enum_with_unknown! {
     }
     }
 }
 }
 
 
+impl fmt::Display for Type {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            &Type::EchoReply      => write!(f, "echo reply"),
+            &Type::DstUnreachable => write!(f, "destination unreachable"),
+            &Type::Redirect       => write!(f, "message redirect"),
+            &Type::EchoRequest    => write!(f, "echo request"),
+            &Type::RouterAdvert   => write!(f, "router advertisement"),
+            &Type::RouterSolicit  => write!(f, "router solicitation"),
+            &Type::TimeExceeded   => write!(f, "time exceeded"),
+            &Type::ParamProblem   => write!(f, "parameter problem"),
+            &Type::Timestamp      => write!(f, "timestamp"),
+            &Type::TimestampReply => write!(f, "timestamp reply"),
+            &Type::Unknown(id) => write!(f, "{}", id)
+        }
+    }
+}
+
 enum_with_unknown! {
 enum_with_unknown! {
     /// Internet protocol control message subtype for type "Destination Unreachable".
     /// Internet protocol control message subtype for type "Destination Unreachable".
     pub doc enum DstUnreachable(u8) {
     pub doc enum DstUnreachable(u8) {
@@ -272,3 +290,164 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
         self.set_checksum(checksum)
         self.set_checksum(checksum)
     }
     }
 }
 }
+
+/// A high-level representation of an Internet Control Message Protocol version 4 packet header.
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub enum Repr<'a> {
+    EchoRequest {
+        ident:  u16,
+        seq_no: u16,
+        data:   &'a [u8]
+    },
+    EchoReply {
+        ident:  u16,
+        seq_no: u16,
+        data:   &'a [u8]
+    },
+    #[doc(hidden)]
+    __Nonexhaustive
+}
+
+impl<'a> Repr<'a> {
+    /// Parse an Internet Control Message Protocol version 4 packet and return
+    /// a high-level representation, or return `Err(())` if the packet is not recognized
+    /// or is malformed.
+    pub fn parse<T: AsRef<[u8]>>(packet: &'a Packet<T>) -> Result<Repr<'a>, Error> {
+        match (packet.msg_type(), packet.msg_code()) {
+            (Type::EchoRequest, 0) => {
+                Ok(Repr::EchoRequest {
+                    ident:  packet.echo_ident(),
+                    seq_no: packet.echo_seq_no(),
+                    data:   packet.data()
+                })
+            },
+            (Type::EchoReply, 0) => {
+                Ok(Repr::EchoReply {
+                    ident:  packet.echo_ident(),
+                    seq_no: packet.echo_seq_no(),
+                    data:   packet.data()
+                })
+            },
+            _ => Err(Error::Unrecognized)
+        }
+    }
+
+    /// Emit a high-level representation into an Internet Protocol version 4 packet.
+    pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(&self, packet: &mut Packet<T>) {
+        packet.set_msg_code(0);
+        match self {
+            &Repr::EchoRequest { ident, seq_no, data } => {
+                packet.set_msg_type(Type::EchoRequest);
+                packet.set_echo_ident(ident);
+                packet.set_echo_seq_no(seq_no);
+                packet.data_mut().copy_from_slice(data)
+            },
+            &Repr::EchoReply { ident, seq_no, data } => {
+                packet.set_msg_type(Type::EchoReply);
+                packet.set_echo_ident(ident);
+                packet.set_echo_seq_no(seq_no);
+                packet.data_mut().copy_from_slice(data)
+            },
+            &Repr::__Nonexhaustive => unreachable!()
+        }
+        packet.fill_checksum()
+    }
+}
+
+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),
+            _ => {
+                write!(f, "ICMPv4 type={} code={}",
+                       self.msg_type(), self.msg_code())
+            }
+        }
+    }
+}
+
+impl<'a> fmt::Display for Repr<'a> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            &Repr::EchoRequest { ident, seq_no, .. } =>
+                write!(f, "ICMPv4 Echo Request id={} seq={}", ident, seq_no),
+            &Repr::EchoReply { ident, seq_no, .. } =>
+                write!(f, "ICMPv4 Echo Reply id={} seq={}", ident, seq_no),
+            &Repr::__Nonexhaustive => unreachable!()
+        }
+    }
+}
+
+use super::pretty_print::{PrettyPrint, PrettyIndent};
+
+impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
+    fn pretty_print(buffer: &AsRef<[u8]>, f: &mut fmt::Formatter,
+                    indent: &mut PrettyIndent) -> fmt::Result {
+        match Packet::new(buffer) {
+            Err(err)  => write!(f, "{}({})\n", indent, err),
+            Ok(frame) => write!(f, "{}{}\n", indent, frame)
+        }
+    }
+}
+
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    static ECHO_PACKET_BYTES: [u8; 12] =
+        [0x08, 0x00, 0x39, 0xfe,
+         0x12, 0x34, 0xab, 0xcd,
+         0xaa, 0x00, 0x00, 0xff];
+
+    static ECHO_DATA_BYTES: [u8; 4] =
+        [0xaa, 0x00, 0x00, 0xff];
+
+    #[test]
+    fn test_echo_deconstruct() {
+        let packet = Packet::new(&ECHO_PACKET_BYTES[..]).unwrap();
+        assert_eq!(packet.msg_type(), Type::EchoRequest);
+        assert_eq!(packet.msg_code(), 0);
+        assert_eq!(packet.checksum(), 0x39fe);
+        assert_eq!(packet.echo_ident(), 0x1234);
+        assert_eq!(packet.echo_seq_no(), 0xabcd);
+        assert_eq!(packet.verify_checksum(), true);
+        assert_eq!(packet.data(), &ECHO_DATA_BYTES[..]);
+    }
+
+    #[test]
+    fn test_echo_construct() {
+        let mut bytes = vec![0; 12];
+        let mut packet = Packet::new(&mut bytes).unwrap();
+        packet.set_msg_type(Type::EchoRequest);
+        packet.set_msg_code(0);
+        packet.set_echo_ident(0x1234);
+        packet.set_echo_seq_no(0xabcd);
+        packet.fill_checksum();
+        packet.data_mut().copy_from_slice(&ECHO_DATA_BYTES[..]);
+        assert_eq!(&packet.into_inner()[..], &ECHO_PACKET_BYTES[..]);
+    }
+
+    fn echo_packet_repr() -> Repr<'static> {
+        Repr::EchoRequest {
+            ident: 0x1234,
+            seq_no: 0xabcd,
+            data: &ECHO_DATA_BYTES
+        }
+    }
+
+    #[test]
+    fn test_echo_parse() {
+        let packet = Packet::new(&ECHO_PACKET_BYTES[..]).unwrap();
+        let repr = Repr::parse(&packet).unwrap();
+        assert_eq!(repr, echo_packet_repr());
+    }
+
+    #[test]
+    fn test_echo_emit() {
+        let mut bytes = vec![0; 12];
+        let mut packet = Packet::new(&mut bytes).unwrap();
+        echo_packet_repr().emit(&mut packet);
+        assert_eq!(&packet.into_inner()[..], &ECHO_PACKET_BYTES[..]);
+    }
+}

+ 11 - 3
src/wire/ipv4.rs

@@ -453,9 +453,17 @@ use super::pretty_print::{PrettyPrint, PrettyIndent};
 impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
 impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
     fn pretty_print(buffer: &AsRef<[u8]>, f: &mut fmt::Formatter,
     fn pretty_print(buffer: &AsRef<[u8]>, f: &mut fmt::Formatter,
                     indent: &mut PrettyIndent) -> fmt::Result {
                     indent: &mut PrettyIndent) -> fmt::Result {
-        match Packet::new(buffer) {
-            Err(err)  => write!(f, "{}({})\n", indent, err),
-            Ok(frame) => write!(f, "{}{}\n", indent, frame)
+        let packet = match Packet::new(buffer) {
+            Err(err)  => return write!(f, "{}({})\n", indent, err),
+            Ok(packet) => packet
+        };
+        try!(write!(f, "{}{}\n", indent, packet));
+        indent.increase();
+
+        match packet.protocol() {
+            ProtocolType::Icmp =>
+                super::Icmpv4Packet::<&[u8]>::pretty_print(&packet.payload(), f, indent),
+            _ => Ok(())
         }
         }
     }
     }
 }
 }

+ 1 - 0
src/wire/mod.rs

@@ -110,3 +110,4 @@ pub use self::icmpv4::Redirect as Icmpv4Redirect;
 pub use self::icmpv4::TimeExceeded as Icmpv4TimeExceeded;
 pub use self::icmpv4::TimeExceeded as Icmpv4TimeExceeded;
 pub use self::icmpv4::ParamProblem as Icmpv4ParamProblem;
 pub use self::icmpv4::ParamProblem as Icmpv4ParamProblem;
 pub use self::icmpv4::Packet as Icmpv4Packet;
 pub use self::icmpv4::Packet as Icmpv4Packet;
+pub use self::icmpv4::Repr as Icmpv4Repr;

+ 1 - 1
src/wire/pretty_print.rs

@@ -39,7 +39,7 @@ impl fmt::Display for PrettyIndent {
         if self.level == 0 {
         if self.level == 0 {
             write!(f, "{}", self.prefix)
             write!(f, "{}", self.prefix)
         } else {
         } else {
-            write!(f, "{0:1$}{0:2$}| ", "", self.prefix.len(), (self.level - 1) * 2)
+            write!(f, "{0:1$}{0:2$}\\ ", "", self.prefix.len(), self.level - 1)
         }
         }
     }
     }
 }
 }