Browse Source

Implement IPv4 packet support.

whitequark 8 years ago
parent
commit
f2104eb969
7 changed files with 415 additions and 18 deletions
  1. 2 2
      src/iface/ethernet.rs
  2. 5 2
      src/lib.rs
  3. 12 2
      src/wire/arp.rs
  4. 12 12
      src/wire/ethernet.rs
  5. 21 0
      src/wire/ip.rs
  6. 359 0
      src/wire/ipv4.rs
  7. 4 0
      src/wire/mod.rs

+ 2 - 2
src/iface/ethernet.rs

@@ -111,8 +111,8 @@ impl<'a, DeviceT: Device, ArpCacheT: ArpCache> Interface<'a, DeviceT, ArpCacheT>
                 let tx_size = self.device.mtu();
                 let tx_buffer = try!(self.device.transmit(tx_size));
                 let mut frame = try!(EthernetFrame::new(tx_buffer));
-                frame.set_source(self.hardware_addr);
-                frame.set_destination(match repr {
+                frame.set_src_addr(self.hardware_addr);
+                frame.set_dst_addr(match repr {
                     ArpRepr::EthernetIpv4 { target_hardware_addr, .. } => target_hardware_addr,
                     _ => unreachable!()
                 });

+ 5 - 2
src/lib.rs

@@ -1,4 +1,4 @@
-#![feature(associated_consts, const_fn)]
+#![feature(associated_consts, const_fn, step_by)]
 #![no_std]
 
 extern crate byteorder;
@@ -21,8 +21,10 @@ pub enum Error {
     /// A packet could not be parsed or emitted because a field was out of bounds
     /// for the underlying buffer.
     Truncated,
-    /// A packet could not be recognized and was dropped.
+    /// An incoming packet could not be recognized and was dropped.
     Unrecognized,
+    /// An incoming packet had an incorrect checksum and was dropped.
+    Checksum,
 
     #[doc(hidden)]
     __Nonexhaustive
@@ -33,6 +35,7 @@ impl fmt::Display for Error {
         match self {
             &Error::Truncated    => write!(f, "truncated packet"),
             &Error::Unrecognized => write!(f, "unrecognized packet"),
+            &Error::Checksum     => write!(f, "checksum error"),
             &Error::__Nonexhaustive => unreachable!()
         }
     }

+ 12 - 2
src/wire/arp.rs

@@ -19,7 +19,7 @@ enum_with_unknown! {
     }
 }
 
-/// A read/write wrapper around an Address Resolution Protocol packet.
+/// A read/write wrapper around an Address Resolution Protocol packet buffer.
 #[derive(Debug)]
 pub struct Packet<T: AsRef<[u8]>> {
     buffer: T
@@ -28,7 +28,7 @@ pub struct Packet<T: AsRef<[u8]>> {
 mod field {
     #![allow(non_snake_case)]
 
-    use ::wire::field::*;
+    use wire::field::*;
 
     pub const HTYPE: Field = 0..2;
     pub const PTYPE: Field = 2..4;
@@ -84,6 +84,7 @@ impl<T: AsRef<[u8]>> Packet<T> {
     }
 
     /// Return the hardware type field.
+    #[inline(always)]
     pub fn hardware_type(&self) -> HardwareType {
         let data = self.buffer.as_ref();
         let raw = NetworkEndian::read_u16(&data[field::HTYPE]);
@@ -91,6 +92,7 @@ impl<T: AsRef<[u8]>> Packet<T> {
     }
 
     /// Return the protocol type field.
+    #[inline(always)]
     pub fn protocol_type(&self) -> ProtocolType {
         let data = self.buffer.as_ref();
         let raw = NetworkEndian::read_u16(&data[field::PTYPE]);
@@ -98,18 +100,21 @@ impl<T: AsRef<[u8]>> Packet<T> {
     }
 
     /// Return the hardware length field.
+    #[inline(always)]
     pub fn hardware_len(&self) -> u8 {
         let data = self.buffer.as_ref();
         data[field::HLEN]
     }
 
     /// Return the protocol length field.
+    #[inline(always)]
     pub fn protocol_len(&self) -> u8 {
         let data = self.buffer.as_ref();
         data[field::PLEN]
     }
 
     /// Return the operation field.
+    #[inline(always)]
     pub fn operation(&self) -> Operation {
         let data = self.buffer.as_ref();
         let raw = NetworkEndian::read_u16(&data[field::OPER]);
@@ -143,30 +148,35 @@ impl<T: AsRef<[u8]>> Packet<T> {
 
 impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
     /// Set the hardware type field.
+    #[inline(always)]
     pub fn set_hardware_type(&mut self, value: HardwareType) {
         let data = self.buffer.as_mut();
         NetworkEndian::write_u16(&mut data[field::HTYPE], value.into())
     }
 
     /// Set the protocol type field.
+    #[inline(always)]
     pub fn set_protocol_type(&mut self, value: ProtocolType) {
         let data = self.buffer.as_mut();
         NetworkEndian::write_u16(&mut data[field::PTYPE], value.into())
     }
 
     /// Set the hardware length field.
+    #[inline(always)]
     pub fn set_hardware_len(&mut self, value: u8) {
         let data = self.buffer.as_mut();
         data[field::HLEN] = value
     }
 
     /// Set the protocol length field.
+    #[inline(always)]
     pub fn set_protocol_len(&mut self, value: u8) {
         let data = self.buffer.as_mut();
         data[field::PLEN] = value
     }
 
     /// Set the operation field.
+    #[inline(always)]
     pub fn set_operation(&mut self, value: Operation) {
         let data = self.buffer.as_mut();
         NetworkEndian::write_u16(&mut data[field::OPER], value.into())

+ 12 - 12
src/wire/ethernet.rs

@@ -17,7 +17,7 @@ impl fmt::Display for EtherType {
             &EtherType::Ipv4 => write!(f, "IPv4"),
             &EtherType::Ipv6 => write!(f, "IPv6"),
             &EtherType::Arp  => write!(f, "ARP"),
-            &EtherType::Unknown(ty) => write!(f, "0x{:04x}", ty)
+            &EtherType::Unknown(id) => write!(f, "0x{:04x}", id)
         }
     }
 }
@@ -63,14 +63,14 @@ impl fmt::Display for Address {
     }
 }
 
-/// A read/write wrapper around an Ethernet II frame.
+/// A read/write wrapper around an Ethernet II frame buffer.
 #[derive(Debug)]
 pub struct Frame<T: AsRef<[u8]>> {
     buffer: T
 }
 
 mod field {
-    use ::wire::field::*;
+    use wire::field::*;
 
     pub const DESTINATION: Field     =  0..6;
     pub const SOURCE:      Field     =  6..12;
@@ -97,14 +97,14 @@ impl<T: AsRef<[u8]>> Frame<T> {
 
     /// Return the destination address field.
     #[inline(always)]
-    pub fn destination(&self) -> Address {
+    pub fn dst_addr(&self) -> Address {
         let data = self.buffer.as_ref();
         Address::from_bytes(&data[field::DESTINATION])
     }
 
     /// Return the source address field.
     #[inline(always)]
-    pub fn source(&self) -> Address {
+    pub fn src_addr(&self) -> Address {
         let data = self.buffer.as_ref();
         Address::from_bytes(&data[field::SOURCE])
     }
@@ -128,14 +128,14 @@ impl<T: AsRef<[u8]>> Frame<T> {
 impl<T: AsRef<[u8]> + AsMut<[u8]>> Frame<T> {
     /// Set the destination address field.
     #[inline(always)]
-    pub fn set_destination(&mut self, value: Address) {
+    pub fn set_dst_addr(&mut self, value: Address) {
         let data = self.buffer.as_mut();
         data[field::DESTINATION].copy_from_slice(value.as_bytes())
     }
 
     /// Set the source address field.
     #[inline(always)]
-    pub fn set_source(&mut self, value: Address) {
+    pub fn set_src_addr(&mut self, value: Address) {
         let data = self.buffer.as_mut();
         data[field::SOURCE].copy_from_slice(value.as_bytes())
     }
@@ -158,7 +158,7 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Frame<T> {
 impl<T: AsRef<[u8]>> fmt::Display for Frame<T> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         write!(f, "EthernetII src={} dst={} type={}",
-               self.source(), self.destination(), self.ethertype())
+               self.src_addr(), self.dst_addr(), self.ethertype())
     }
 }
 
@@ -210,8 +210,8 @@ mod test {
     #[test]
     fn test_deconstruct() {
         let frame = Frame::new(&FRAME_BYTES[..]).unwrap();
-        assert_eq!(frame.destination(), Address([0x01, 0x02, 0x03, 0x04, 0x05, 0x06]));
-        assert_eq!(frame.source(), Address([0x11, 0x12, 0x13, 0x14, 0x15, 0x16]));
+        assert_eq!(frame.dst_addr(), Address([0x01, 0x02, 0x03, 0x04, 0x05, 0x06]));
+        assert_eq!(frame.src_addr(), Address([0x11, 0x12, 0x13, 0x14, 0x15, 0x16]));
         assert_eq!(frame.ethertype(), EtherType::Ipv4);
         assert_eq!(frame.payload(), &PAYLOAD_BYTES[..]);
     }
@@ -220,8 +220,8 @@ mod test {
     fn test_construct() {
         let mut bytes = vec![0; 64];
         let mut frame = Frame::new(&mut bytes).unwrap();
-        frame.set_destination(Address([0x01, 0x02, 0x03, 0x04, 0x05, 0x06]));
-        frame.set_source(Address([0x11, 0x12, 0x13, 0x14, 0x15, 0x16]));
+        frame.set_dst_addr(Address([0x01, 0x02, 0x03, 0x04, 0x05, 0x06]));
+        frame.set_src_addr(Address([0x11, 0x12, 0x13, 0x14, 0x15, 0x16]));
         frame.set_ethertype(EtherType::Ipv4);
         frame.payload_mut().copy_from_slice(&PAYLOAD_BYTES[..]);
         assert_eq!(&frame.into_inner()[..], &FRAME_BYTES[..]);

+ 21 - 0
src/wire/ip.rs

@@ -0,0 +1,21 @@
+use core::fmt;
+
+enum_with_unknown! {
+    /// Internet protocol type.
+    pub enum ProtocolType(u8) {
+        Icmp = 0x01,
+        Tcp  = 0x06,
+        Udp  = 0x11
+    }
+}
+
+impl fmt::Display for ProtocolType {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            &ProtocolType::Icmp => write!(f, "ICMP"),
+            &ProtocolType::Tcp  => write!(f, "TCP"),
+            &ProtocolType::Udp  => write!(f, "UDP"),
+            &ProtocolType::Unknown(id) => write!(f, "0x{:02x}", id)
+        }
+    }
+}

+ 359 - 0
src/wire/ipv4.rs

@@ -1,4 +1,8 @@
 use core::fmt;
+use byteorder::{ByteOrder, NetworkEndian};
+use Error;
+
+pub use super::InternetProtocolType as ProtocolType;
 
 /// A four-octet IPv4 address.
 #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
@@ -61,3 +65,358 @@ impl fmt::Display for Address {
         write!(f, "{}.{}.{}.{}", bytes[0], bytes[1], bytes[2], bytes[3])
     }
 }
+
+/// A read/write wrapper around an Internet Protocol version 4 packet buffer.
+#[derive(Debug)]
+pub struct Packet<T: AsRef<[u8]>> {
+    buffer: T
+}
+
+mod field {
+    #![allow(non_snake_case)]
+
+    use wire::field::*;
+
+    pub const VER_IHL:  usize = 0;
+    pub const DSCP_ECN: usize = 1;
+    pub const LENGTH:   Field = 2..4;
+    pub const IDENT:    Field = 4..6;
+    pub const FLG_OFF1: usize = 6;
+    pub const FLG_OFF0: usize = 7;
+    pub const TTL:      usize = 8;
+    pub const PROTOCOL: usize = 9;
+    pub const CHECKSUM: Field = 10..12;
+    pub const SRC_ADDR: Field = 12..16;
+    pub const DST_ADDR: Field = 16..20;
+}
+
+fn checksum(data: &[u8]) -> u16 {
+    let mut accum: u32 = 0;
+    for i in (0..data.len()).step_by(2) {
+        if i == field::CHECKSUM.start { continue }
+        let word = NetworkEndian::read_u16(&data[i..i + 2]) as u32;
+        accum += word;
+    }
+    !(((accum >> 16) as u16) + (accum as u16))
+}
+
+impl<T: AsRef<[u8]>> Packet<T> {
+    /// Wrap a buffer with an IPv4 packet. Returns an error if the buffer
+    /// is too small to contain one.
+    pub fn new(buffer: T) -> Result<Packet<T>, Error> {
+        let len = buffer.as_ref().len();
+        if len < field::VER_IHL {
+            Err(Error::Truncated)
+        } else {
+            let packet = Packet { buffer: buffer };
+            if len < packet.header_len() as usize {
+                Err(Error::Truncated)
+            } else {
+                Ok(packet)
+            }
+        }
+    }
+
+    /// Consumes the packet, returning the underlying buffer.
+    pub fn into_inner(self) -> T {
+        self.buffer
+    }
+
+    /// Return the version field.
+    #[inline(always)]
+    pub fn version(&self) -> u8 {
+        let data = self.buffer.as_ref();
+        data[field::VER_IHL] & 0x04
+    }
+
+    /// Return the header length, in octets.
+    #[inline(always)]
+    pub fn header_len(&self) -> u8 {
+        let data = self.buffer.as_ref();
+        (data[field::VER_IHL] >> 4) * 4
+    }
+
+    /// Return the Differential Services Code Point field.
+    pub fn dscp(&self) -> u8 {
+        let data = self.buffer.as_ref();
+        data[field::DSCP_ECN] & 0x3f
+    }
+
+    /// Return the Explicit Congestion Notification field.
+    pub fn ecn(&self) -> u8 {
+        let data = self.buffer.as_ref();
+        data[field::DSCP_ECN] >> 6
+    }
+
+    /// Return the total length field.
+    #[inline(always)]
+    pub fn total_len(&self) -> u16 {
+        let data = self.buffer.as_ref();
+        NetworkEndian::read_u16(&data[field::LENGTH])
+    }
+
+    /// Return the fragment identification field.
+    #[inline(always)]
+    pub fn ident(&self) -> u16 {
+        let data = self.buffer.as_ref();
+        NetworkEndian::read_u16(&data[field::IDENT])
+    }
+
+    /// Return the "don't fragment" flag.
+    #[inline(always)]
+    pub fn dont_frag(&self) -> bool {
+        let data = self.buffer.as_ref();
+        data[field::FLG_OFF1] & 0x02 != 0
+    }
+
+    /// Return the "more fragments" flag.
+    #[inline(always)]
+    pub fn more_frags(&self) -> bool {
+        let data = self.buffer.as_ref();
+        data[field::FLG_OFF1] & 0x04 != 0
+    }
+
+    /// Return the fragment offset, in octets.
+    #[inline(always)]
+    pub fn frag_offset(&self) -> u16 {
+        let data = self.buffer.as_ref();
+        let chunks = (((data[field::FLG_OFF1] >> 3) as u16) << 8) |
+                        data[field::FLG_OFF0] as u16;
+        chunks * 8
+    }
+
+    /// Return the time to live field.
+    #[inline(always)]
+    pub fn ttl(&self) -> u8 {
+        let data = self.buffer.as_ref();
+        data[field::TTL]
+    }
+
+    /// Return the protocol field.
+    #[inline(always)]
+    pub fn protocol(&self) -> ProtocolType {
+        let data = self.buffer.as_ref();
+        ProtocolType::from(data[field::PROTOCOL])
+    }
+
+    /// Return the header checksum field.
+    #[inline(always)]
+    pub fn checksum(&self) -> u16 {
+        let data = self.buffer.as_ref();
+        NetworkEndian::read_u16(&data[field::CHECKSUM])
+    }
+
+    /// Return the source address field.
+    #[inline(always)]
+    pub fn src_addr(&self) -> Address {
+        let data = self.buffer.as_ref();
+        Address::from_bytes(&data[field::SRC_ADDR])
+    }
+
+    /// Return the destination address field.
+    #[inline(always)]
+    pub fn dst_addr(&self) -> Address {
+        let data = self.buffer.as_ref();
+        Address::from_bytes(&data[field::DST_ADDR])
+    }
+
+    /// Return a pointer to the payload.
+    #[inline(always)]
+    pub fn payload(&self) -> &[u8] {
+        let range = self.header_len() as usize;
+        let data = self.buffer.as_ref();
+        &data[range..]
+    }
+
+    /// Validate the header checksum.
+    pub fn verify_checksum(&self) -> bool {
+        let checksum = {
+            let data = self.buffer.as_ref();
+            checksum(&data[..self.header_len() as usize])
+        };
+        self.checksum() == checksum
+    }
+}
+
+impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
+    /// Set the version field.
+    #[inline(always)]
+    pub fn set_version(&mut self, value: u8) {
+        let data = self.buffer.as_mut();
+        data[field::VER_IHL] = (data[field::VER_IHL] & !0x04) | (value & 0x04);
+    }
+
+    /// Set the header length, in octets.
+    #[inline(always)]
+    pub fn set_header_len(&mut self, value: u8) {
+        let data = self.buffer.as_mut();
+        data[field::VER_IHL] = (data[field::VER_IHL] & !0x40) | ((value / 4) << 4);
+    }
+
+    /// Set the Differential Services Code Point field.
+    pub fn set_dscp(&mut self, value: u8) {
+        let data = self.buffer.as_mut();
+        data[field::DSCP_ECN] = (data[field::DSCP_ECN] & !0x3f) | (value & 0x3f)
+    }
+
+    /// Set the Explicit Congestion Notification field.
+    pub fn set_ecn(&mut self, value: u8) {
+        let data = self.buffer.as_mut();
+        data[field::DSCP_ECN] = (data[field::DSCP_ECN] & 0x3f) | (value << 6)
+    }
+
+    /// Set the total length field.
+    #[inline(always)]
+    pub fn set_total_len(&mut self, value: u16) {
+        let data = self.buffer.as_mut();
+        NetworkEndian::write_u16(&mut data[field::LENGTH], value)
+    }
+
+    /// Set the fragment identification field.
+    #[inline(always)]
+    pub fn set_ident(&mut self, value: u16) {
+        let data = self.buffer.as_mut();
+        NetworkEndian::write_u16(&mut data[field::IDENT], value)
+    }
+
+    /// Set the "don't fragment" flag.
+    #[inline(always)]
+    pub fn set_dont_frag(&mut self, value: bool) {
+        let data = self.buffer.as_mut();
+        let raw = data[field::FLG_OFF1];
+        data[field::FLG_OFF1] = if value { raw | 0x02 } else { raw & !0x02 };
+    }
+
+    /// Set the "more fragments" flag.
+    #[inline(always)]
+    pub fn set_more_frags(&mut self, value: bool) {
+        let data = self.buffer.as_mut();
+        let raw = data[field::FLG_OFF1];
+        data[field::FLG_OFF1] = if value { raw | 0x04 } else { raw & !0x04 };
+    }
+
+    /// Set the fragment offset, in octets.
+    #[inline(always)]
+    pub fn set_frag_offset(&mut self, value: u16) {
+        let data = self.buffer.as_mut();
+        let chunks = value / 8;
+        let raw = data[field::FLG_OFF1] & 0x7;
+        data[field::FLG_OFF1] = raw | (((chunks >> 8) << 3) as u8);
+        data[field::FLG_OFF0] = chunks as u8;
+    }
+
+    /// Set the time to live field.
+    #[inline(always)]
+    pub fn set_ttl(&mut self, value: u8) {
+        let data = self.buffer.as_mut();
+        data[field::TTL] = value
+    }
+
+    /// Set the protocol field.
+    #[inline(always)]
+    pub fn set_protocol(&mut self, value: ProtocolType) {
+        let data = self.buffer.as_mut();
+        data[field::PROTOCOL] = value.into()
+    }
+
+    /// Set the header checksum field.
+    #[inline(always)]
+    pub fn set_checksum(&mut self, value: u16) {
+        let data = self.buffer.as_mut();
+        NetworkEndian::write_u16(&mut data[field::CHECKSUM], value)
+    }
+
+    /// Set the source address field.
+    #[inline(always)]
+    pub fn set_src_addr(&mut self, value: Address) {
+        let data = self.buffer.as_mut();
+        data[field::SRC_ADDR].copy_from_slice(value.as_bytes())
+    }
+
+    /// Set the destination address field.
+    #[inline(always)]
+    pub fn set_dst_addr(&mut self, value: Address) {
+        let data = self.buffer.as_mut();
+        data[field::DST_ADDR].copy_from_slice(value.as_bytes())
+    }
+
+    /// Return a mutable pointer to the payload.
+    #[inline(always)]
+    pub fn payload_mut(&mut self) -> &mut [u8] {
+        let range = self.header_len() as usize..;
+        let data = self.buffer.as_mut();
+        &mut data[range]
+    }
+
+    /// Compute and fill in the header checksum.
+    pub fn fill_checksum(&mut self) {
+        let checksum = {
+            let data = self.buffer.as_ref();
+            checksum(&data[..self.header_len() as usize])
+        };
+        self.set_checksum(checksum)
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    static PACKET_BYTES: [u8; 30] =
+        [0x54, 0x00, 0x00, 0x1e,
+         0x01, 0x02, 0x16, 0x03,
+         0x1a, 0x01, 0x12, 0x6f,
+         0x11, 0x12, 0x13, 0x14,
+         0x21, 0x22, 0x23, 0x24,
+         0xaa, 0x00, 0x00, 0x00,
+         0x00, 0x00, 0x00, 0x00,
+         0x00, 0xff];
+
+    static PAYLOAD_BYTES: [u8; 10] =
+        [0xaa, 0x00, 0x00, 0x00,
+         0x00, 0x00, 0x00, 0x00,
+         0x00, 0xff];
+
+    #[test]
+    fn test_deconstruct() {
+        let packet = Packet::new(&PACKET_BYTES[..]).unwrap();
+        assert_eq!(packet.version(), 4);
+        assert_eq!(packet.header_len(), 20);
+        assert_eq!(packet.dscp(), 0);
+        assert_eq!(packet.ecn(), 0);
+        assert_eq!(packet.total_len(), 30);
+        assert_eq!(packet.ident(), 0x102);
+        assert_eq!(packet.more_frags(), true);
+        assert_eq!(packet.dont_frag(), true);
+        assert_eq!(packet.frag_offset(), 0x203 * 8);
+        assert_eq!(packet.ttl(), 0x1a);
+        assert_eq!(packet.protocol(), ProtocolType::Icmp);
+        assert_eq!(packet.checksum(), 0x126f);
+        assert_eq!(packet.src_addr(), Address([0x11, 0x12, 0x13, 0x14]));
+        assert_eq!(packet.dst_addr(), Address([0x21, 0x22, 0x23, 0x24]));
+        assert_eq!(packet.verify_checksum(), true);
+        assert_eq!(packet.payload(), &PAYLOAD_BYTES[..]);
+    }
+
+    #[test]
+    fn test_construct() {
+        let mut bytes = vec![0; 30];
+        let mut packet = Packet::new(&mut bytes).unwrap();
+        packet.set_version(4);
+        packet.set_header_len(20);
+        packet.set_dscp(0);
+        packet.set_ecn(0);
+        packet.set_total_len(30);
+        packet.set_ident(0x102);
+        packet.set_more_frags(true);
+        packet.set_dont_frag(true);
+        packet.set_frag_offset(0x203 * 8);
+        packet.set_ttl(0x1a);
+        packet.set_protocol(ProtocolType::Icmp);
+        packet.set_src_addr(Address([0x11, 0x12, 0x13, 0x14]));
+        packet.set_dst_addr(Address([0x21, 0x22, 0x23, 0x24]));
+        packet.fill_checksum();
+        packet.payload_mut().copy_from_slice(&PAYLOAD_BYTES[..]);
+        assert_eq!(&packet.into_inner()[..], &PACKET_BYTES[..]);
+    }
+}

+ 4 - 0
src/wire/mod.rs

@@ -59,6 +59,7 @@ pub mod pretty_print;
 
 mod ethernet;
 mod arp;
+mod ip;
 mod ipv4;
 
 pub use self::pretty_print::PrettyPrinter;
@@ -73,4 +74,7 @@ pub use self::arp::Operation as ArpOperation;
 pub use self::arp::Packet as ArpPacket;
 pub use self::arp::Repr as ArpRepr;
 
+pub use self::ip::ProtocolType as InternetProtocolType;
+
 pub use self::ipv4::Address as Ipv4Address;
+pub use self::ipv4::Packet as Ipv4Packet;