Quellcode durchsuchen

Implement UDP representation parsing and emission.

whitequark vor 8 Jahren
Ursprung
Commit
f6f7dc8cb6
10 geänderte Dateien mit 274 neuen und 110 gelöschten Zeilen
  1. 3 3
      examples/smoltcpserver.rs
  2. 14 15
      src/iface/arp_cache.rs
  3. 6 6
      src/iface/ethernet.rs
  4. 0 49
      src/iface/mod.rs
  5. 2 1
      src/wire/arp.rs
  6. 11 9
      src/wire/icmpv4.rs
  7. 91 9
      src/wire/ip.rs
  8. 7 5
      src/wire/ipv4.rs
  9. 5 4
      src/wire/mod.rs
  10. 135 9
      src/wire/udp.rs

+ 3 - 3
examples/smoltcpserver.rs

@@ -3,14 +3,14 @@ extern crate smoltcp;
 
 use std::env;
 use smoltcp::phy::{Tracer, TapInterface};
-use smoltcp::wire::{EthernetFrame, EthernetAddress};
-use smoltcp::iface::{ProtocolAddress, SliceArpCache, EthernetInterface};
+use smoltcp::wire::{EthernetFrame, EthernetAddress, InternetAddress};
+use smoltcp::iface::{SliceArpCache, EthernetInterface};
 
 fn main() {
     let ifname = env::args().nth(1).unwrap();
 
     let hardware_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]);
-    let protocol_addrs = [ProtocolAddress::ipv4([192, 168, 69, 1])];
+    let protocol_addrs = [InternetAddress::ipv4([192, 168, 69, 1])];
 
     let device = TapInterface::new(ifname.as_ref()).unwrap();
     let device = Tracer::<_, EthernetFrame<&[u8]>>::new(device);

+ 14 - 15
src/iface/arp_cache.rs

@@ -1,15 +1,14 @@
-use wire::EthernetAddress;
-use super::ProtocolAddress;
+use wire::{EthernetAddress, InternetAddress};
 
 /// An Address Resolution Protocol cache.
 ///
 /// This cache maps protocol addresses to hardware addresses.
 pub trait Cache {
     /// Update the cache to map given protocol address to given hardware address.
-    fn fill(&mut self, protocol_addr: ProtocolAddress, hardware_addr: EthernetAddress);
+    fn fill(&mut self, protocol_addr: InternetAddress, hardware_addr: EthernetAddress);
 
     /// Look up the hardware address corresponding for the given protocol address.
-    fn lookup(&mut self, protocol_addr: ProtocolAddress) -> Option<EthernetAddress>;
+    fn lookup(&mut self, protocol_addr: InternetAddress) -> Option<EthernetAddress>;
 }
 
 /// An Address Resolution Protocol cache backed by a slice.
@@ -26,7 +25,7 @@ pub trait Cache {
 /// let mut arp_cache = SliceArpCache::new(&mut arp_cache_storage);
 /// ```
 pub struct SliceCache<'a> {
-    storage: &'a mut [(ProtocolAddress, EthernetAddress, usize)],
+    storage: &'a mut [(InternetAddress, EthernetAddress, usize)],
     counter: usize
 }
 
@@ -35,7 +34,7 @@ impl<'a> SliceCache<'a> {
     ///
     /// # Panics
     /// This function panics if `storage.len() == 0`.
-    pub fn new(storage: &'a mut [(ProtocolAddress, EthernetAddress, usize)]) -> SliceCache<'a> {
+    pub fn new(storage: &'a mut [(InternetAddress, EthernetAddress, usize)]) -> SliceCache<'a> {
         if storage.len() == 0 {
             panic!("ARP slice cache created with empty storage")
         }
@@ -50,9 +49,9 @@ impl<'a> SliceCache<'a> {
     }
 
     /// Find an entry for the given protocol address, if any.
-    fn find(&self, protocol_addr: ProtocolAddress) -> Option<usize> {
-        // The order of comparison is important: any valid ProtocolAddress should
-        // sort before ProtocolAddress::Invalid.
+    fn find(&self, protocol_addr: InternetAddress) -> Option<usize> {
+        // The order of comparison is important: any valid InternetAddress should
+        // sort before InternetAddress::Invalid.
         self.storage.binary_search_by_key(&protocol_addr, |&(key, _, _)| key).ok()
     }
 
@@ -68,14 +67,14 @@ impl<'a> SliceCache<'a> {
 }
 
 impl<'a> Cache for SliceCache<'a> {
-    fn fill(&mut self, protocol_addr: ProtocolAddress, hardware_addr: EthernetAddress) {
+    fn fill(&mut self, protocol_addr: InternetAddress, hardware_addr: EthernetAddress) {
         if let None = self.find(protocol_addr) {
             self.storage[self.lru()] = (protocol_addr, hardware_addr, self.counter);
             self.sort()
         }
     }
 
-    fn lookup(&mut self, protocol_addr: ProtocolAddress) -> Option<EthernetAddress> {
+    fn lookup(&mut self, protocol_addr: InternetAddress) -> Option<EthernetAddress> {
         if let Some(index) = self.find(protocol_addr) {
             let (_protocol_addr, hardware_addr, ref mut counter) = self.storage[index];
             self.counter += 1;
@@ -96,10 +95,10 @@ mod test {
     const HADDR_C: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 3]);
     const HADDR_D: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 4]);
 
-    const PADDR_A: ProtocolAddress = ProtocolAddress::ipv4([0, 0, 0, 0]);
-    const PADDR_B: ProtocolAddress = ProtocolAddress::ipv4([0, 0, 0, 1]);
-    const PADDR_C: ProtocolAddress = ProtocolAddress::ipv4([0, 0, 0, 2]);
-    const PADDR_D: ProtocolAddress = ProtocolAddress::ipv4([0, 0, 0, 3]);
+    const PADDR_A: InternetAddress = InternetAddress::ipv4([0, 0, 0, 0]);
+    const PADDR_B: InternetAddress = InternetAddress::ipv4([0, 0, 0, 1]);
+    const PADDR_C: InternetAddress = InternetAddress::ipv4([0, 0, 0, 2]);
+    const PADDR_D: InternetAddress = InternetAddress::ipv4([0, 0, 0, 3]);
 
     #[test]
     fn test_slice_cache() {

+ 6 - 6
src/iface/ethernet.rs

@@ -2,10 +2,10 @@ use Error;
 use phy::Device;
 use wire::{EthernetAddress, EthernetProtocolType, EthernetFrame};
 use wire::{ArpPacket, ArpRepr, ArpOperation};
-use wire::InternetProtocolType;
+use wire::{InternetAddress, InternetProtocolType};
 use wire::{Ipv4Packet, Ipv4Repr};
 use wire::{Icmpv4Packet, Icmpv4Repr};
-use super::{ProtocolAddress, ArpCache};
+use super::{ArpCache};
 
 /// An Ethernet network interface.
 #[derive(Debug)]
@@ -13,7 +13,7 @@ pub struct Interface<'a, DeviceT: Device, ArpCacheT: ArpCache> {
     device:         DeviceT,
     arp_cache:      ArpCacheT,
     hardware_addr:  EthernetAddress,
-    protocol_addrs: &'a [ProtocolAddress]
+    protocol_addrs: &'a [InternetAddress]
 }
 
 impl<'a, DeviceT: Device, ArpCacheT: ArpCache> Interface<'a, DeviceT, ArpCacheT> {
@@ -48,7 +48,7 @@ impl<'a, DeviceT: Device, ArpCacheT: ArpCache> Interface<'a, DeviceT, ArpCacheT>
     }
 
     /// Get the protocol addresses of the interface.
-    pub fn protocol_addrs(&self) -> &'a [ProtocolAddress] {
+    pub fn protocol_addrs(&self) -> &'a [InternetAddress] {
         self.protocol_addrs
     }
 
@@ -56,7 +56,7 @@ impl<'a, DeviceT: Device, ArpCacheT: ArpCache> Interface<'a, DeviceT, ArpCacheT>
     ///
     /// # Panics
     /// This function panics if any of the addresses is not unicast.
-    pub fn set_protocol_addrs(&mut self, addrs: &'a [ProtocolAddress]) {
+    pub fn set_protocol_addrs(&mut self, addrs: &'a [InternetAddress]) {
         for addr in addrs {
             if !addr.is_unicast() {
                 panic!("protocol address {} is not unicast", addr)
@@ -67,7 +67,7 @@ impl<'a, DeviceT: Device, ArpCacheT: ArpCache> Interface<'a, DeviceT, ArpCacheT>
     }
 
     /// Checks whether the interface has the given protocol address assigned.
-    pub fn has_protocol_addr<T: Into<ProtocolAddress>>(&self, addr: T) -> bool {
+    pub fn has_protocol_addr<T: Into<InternetAddress>>(&self, addr: T) -> bool {
         let addr = addr.into();
         self.protocol_addrs.iter().any(|&probe| probe == addr)
     }

+ 0 - 49
src/iface/mod.rs

@@ -2,58 +2,9 @@
 //!
 //! The `iface` module deals with the *network interfaces*. It filters incoming frames,
 //! provides lookup and caching of hardware addresses, and handles management packets.
-use core::fmt;
-use wire;
-
 mod arp_cache;
 mod ethernet;
 
 pub use self::arp_cache::Cache as ArpCache;
 pub use self::arp_cache::SliceCache as SliceArpCache;
 pub use self::ethernet::Interface as EthernetInterface;
-
-/// An internetworking protocol address.
-#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
-pub enum ProtocolAddress {
-    /// An invalid address.
-    /// May be used as a placeholder for storage where the address is not assigned yet.
-    Invalid,
-    /// An IPv4 address.
-    Ipv4(wire::Ipv4Address)
-}
-
-impl ProtocolAddress {
-    /// Create a protocol address wrapping an IPv4 address with the given octets.
-    pub const fn ipv4(octets: [u8; 4]) -> ProtocolAddress {
-        ProtocolAddress::Ipv4(wire::Ipv4Address(octets))
-    }
-
-    /// Query whether the address is a valid unicast address.
-    pub fn is_unicast(&self) -> bool {
-        match self {
-            &ProtocolAddress::Invalid    => false,
-            &ProtocolAddress::Ipv4(addr) => addr.is_unicast()
-        }
-    }
-}
-
-impl Default for ProtocolAddress {
-    fn default() -> ProtocolAddress {
-        ProtocolAddress::Invalid
-    }
-}
-
-impl From<wire::Ipv4Address> for ProtocolAddress {
-    fn from(addr: wire::Ipv4Address) -> Self {
-        ProtocolAddress::Ipv4(addr)
-    }
-}
-
-impl fmt::Display for ProtocolAddress {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        match self {
-            &ProtocolAddress::Invalid    => write!(f, "(invalid)"),
-            &ProtocolAddress::Ipv4(addr) => write!(f, "{}", addr)
-        }
-    }
-}

+ 2 - 1
src/wire/arp.rs

@@ -291,7 +291,8 @@ impl<T: AsRef<[u8]>> fmt::Display for Packet<T> {
         match Repr::parse(self) {
             Ok(repr) => write!(f, "{}", repr),
             _ => {
-                try!(write!(f, "ARP htype={:?} ptype={:?} hlen={:?} plen={:?} op={:?}",
+                try!(write!(f, "ARP (unrecognized)"));
+                try!(write!(f, " htype={:?} ptype={:?} hlen={:?} plen={:?} op={:?}",
                             self.hardware_type(), self.protocol_type(),
                             self.hardware_len(), self.protocol_len(),
                             self.operation()));

+ 11 - 9
src/wire/icmpv4.rs

@@ -215,7 +215,7 @@ impl<T: AsRef<[u8]>> Packet<T> {
     /// Validate the header checksum.
     pub fn verify_checksum(&self) -> bool {
         let data = self.buffer.as_ref();
-        checksum(data) == !0
+        checksum::data(data) == !0
     }
 }
 
@@ -275,7 +275,7 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
         self.set_checksum(0);
         let checksum = {
             let data = self.buffer.as_ref();
-            !checksum(data)
+            !checksum::data(data)
         };
         self.set_checksum(checksum)
     }
@@ -310,8 +310,7 @@ pub enum Repr<'a> {
 
 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.
+    /// a high-level representation.
     pub fn parse<T: AsRef<[u8]> + ?Sized>(packet: &Packet<&'a T>) -> Result<Repr<'a>, Error> {
         match (packet.msg_type(), packet.msg_code()) {
             (Type::EchoRequest, 0) => {
@@ -343,7 +342,8 @@ impl<'a> Repr<'a> {
         }
     }
 
-    /// Emit a high-level representation into an Internet Protocol version 4 packet.
+    /// Emit a high-level representation into an Internet Control Message Protocol version 4
+    /// packet.
     pub fn emit<T: AsRef<[u8]> + AsMut<[u8]> + ?Sized>(&self, packet: &mut Packet<&mut T>) {
         packet.set_msg_code(0);
         match self {
@@ -372,8 +372,10 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> {
         match Repr::parse(self) {
             Ok(repr) => write!(f, "{}", repr),
             _ => {
-                write!(f, "ICMPv4 type={} code={}",
-                       self.msg_type(), self.msg_code())
+                try!(write!(f, "ICMPv4 (unrecognized)"));
+                try!(write!(f, " type={} code={} cksum={:#04x}",
+                            self.msg_type(), self.msg_code(), self.checksum()));
+                Ok(())
             }
         }
     }
@@ -397,8 +399,8 @@ 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)
+            Err(err)   => write!(f, "{}({})\n", indent, err),
+            Ok(packet) => write!(f, "{}{}\n", indent, packet)
         }
     }
 }

+ 91 - 9
src/wire/ip.rs

@@ -1,8 +1,9 @@
 use core::fmt;
-use byteorder::{ByteOrder, NetworkEndian};
+
+use super::Ipv4Address;
 
 enum_with_unknown! {
-    /// Internet protocol type.
+    /// Internetworking protocol type.
     pub enum ProtocolType(u8) {
         Icmp = 0x01,
         Tcp  = 0x06,
@@ -21,12 +22,93 @@ impl fmt::Display for ProtocolType {
     }
 }
 
-/// Compute an RFC 1071 compliant checksum (without the final complement).
-pub fn checksum(data: &[u8]) -> u16 {
-    let mut accum: u32 = 0;
-    for i in (0..data.len()).step_by(2) {
-        let word = NetworkEndian::read_u16(&data[i..i + 2]) as u32;
-        accum += word;
+/// An internetworking address.
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
+pub enum Address {
+    /// An invalid address.
+    /// May be used as a placeholder for storage where the address is not assigned yet.
+    Invalid,
+    /// An IPv4 address.
+    Ipv4(Ipv4Address)
+}
+
+impl Address {
+    /// Create an address wrapping an IPv4 address with the given octets.
+    pub const fn ipv4(octets: [u8; 4]) -> Address {
+        Address::Ipv4(Ipv4Address(octets))
+    }
+
+    /// Query whether the address is a valid unicast address.
+    pub fn is_unicast(&self) -> bool {
+        match self {
+            &Address::Invalid    => false,
+            &Address::Ipv4(addr) => addr.is_unicast()
+        }
+    }
+}
+
+impl Default for Address {
+    fn default() -> Address {
+        Address::Invalid
+    }
+}
+
+impl From<Ipv4Address> for Address {
+    fn from(addr: Ipv4Address) -> Self {
+        Address::Ipv4(addr)
+    }
+}
+
+impl fmt::Display for Address {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            &Address::Invalid    => write!(f, "(invalid)"),
+            &Address::Ipv4(addr) => write!(f, "{}", addr)
+        }
+    }
+}
+
+pub mod checksum {
+    use byteorder::{ByteOrder, NetworkEndian};
+
+    use super::*;
+
+    /// Compute an RFC 1071 compliant checksum (without the final complement).
+    pub fn data(data: &[u8]) -> u16 {
+        let mut accum: u32 = 0;
+        for i in (0..data.len()).step_by(2) {
+            let word = NetworkEndian::read_u16(&data[i..i + 2]) as u32;
+            accum += word;
+        }
+        (((accum >> 16) as u16) + (accum as u16))
+    }
+
+    /// Combine several RFC 1071 compliant checksums.
+    pub fn combine(checksums: &[u16]) -> u16 {
+        let mut accum: u32 = 0;
+        for &word in checksums {
+            accum += word as u32;
+        }
+        (((accum >> 16) as u16) + (accum as u16))
+    }
+
+    /// Compute an IP pseudo header checksum.
+    pub fn pseudo_header(src_addr: &Address, dst_addr: &Address,
+                         protocol: ProtocolType, length: u32) -> u16 {
+        match (src_addr, dst_addr) {
+            (&Address::Ipv4(src_addr), &Address::Ipv4(dst_addr)) => {
+                let mut proto_len = [0u8; 4];
+                proto_len[1] = protocol.into();
+                NetworkEndian::write_u16(&mut proto_len[2..4], length as u16);
+
+                combine(&[
+                    data(src_addr.as_bytes()),
+                    data(dst_addr.as_bytes()),
+                    data(&proto_len[..])
+                ])
+            },
+
+            _ => panic!("Unexpected pseudo header ")
+        }
     }
-    (((accum >> 16) as u16) + (accum as u16))
 }

+ 7 - 5
src/wire/ipv4.rs

@@ -210,7 +210,7 @@ impl<T: AsRef<[u8]>> Packet<T> {
     /// Validate the header checksum.
     pub fn verify_checksum(&self) -> bool {
         let data = self.buffer.as_ref();
-        checksum(&data[..self.header_len() as usize]) == !0
+        checksum::data(&data[..self.header_len() as usize]) == !0
     }
 }
 
@@ -332,7 +332,7 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
         self.set_checksum(0);
         let checksum = {
             let data = self.buffer.as_ref();
-            !checksum(&data[..self.header_len() as usize])
+            !checksum::data(&data[..self.header_len() as usize])
         };
         self.set_checksum(checksum)
     }
@@ -357,8 +357,7 @@ pub struct Repr {
 }
 
 impl Repr {
-    /// Parse an Internet Protocol version 4 packet and return a high-level representation,
-    /// or return `Err(())` if the packet is not recognized or is malformed.
+    /// Parse an Internet Protocol version 4 packet and return a high-level representation.
     pub fn parse<T: AsRef<[u8]> + ?Sized>(packet: &Packet<&T>) -> Result<Repr, Error> {
         // Version 4 is expected.
         if packet.version() != 4 { return Err(Error::Malformed) }
@@ -408,7 +407,8 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> {
         match Repr::parse(self) {
             Ok(repr) => write!(f, "{}", repr),
             _ => {
-                try!(write!(f, "IPv4 src={} dst={} proto={} ttl={}",
+                try!(write!(f, "IPv4 (unrecognized)"));
+                try!(write!(f, " src={} dst={} proto={} ttl={}",
                             self.src_addr(), self.dst_addr(), self.protocol(), self.ttl()));
                 if self.version() != 4 {
                     try!(write!(f, " ver={}", self.version()))
@@ -463,6 +463,8 @@ impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
         match packet.protocol() {
             ProtocolType::Icmp =>
                 super::Icmpv4Packet::<&[u8]>::pretty_print(&packet.payload(), f, indent),
+            ProtocolType::Udp =>
+                super::UdpPacket::<&[u8]>::pretty_print(&packet.payload(), f, indent),
             _ => Ok(())
         }
     }

+ 5 - 4
src/wire/mod.rs

@@ -16,9 +16,9 @@
 //! `-Cpanic=abort`. The accessor and parsing functions never panic. The setter and emission
 //! functions only panic if the underlying buffer is too small.
 //!
-//! The data structures in the `wire` module do not perform validation of received data;
-//! that is the job of an upper layer. This includes the `Repr` family, which only validate
-//! as much as is necessary to build the representation.
+//! The `Frame` and `Packet` families of data structures in the `wire` module do not perform
+//! validation of received data except as needed to access the contents without panicking;
+//! the `Repr` family does.
 
 macro_rules! enum_with_unknown {
     (
@@ -94,12 +94,12 @@ pub use self::ethernet::Address as EthernetAddress;
 pub use self::ethernet::Frame as EthernetFrame;
 
 pub use self::arp::HardwareType as ArpHardwareType;
-pub use self::arp::ProtocolType as ArpProtocolType;
 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::ip::Address as InternetAddress;
 
 pub use self::ipv4::Address as Ipv4Address;
 pub use self::ipv4::Packet as Ipv4Packet;
@@ -114,3 +114,4 @@ pub use self::icmpv4::Packet as Icmpv4Packet;
 pub use self::icmpv4::Repr as Icmpv4Repr;
 
 pub use self::udp::Packet as UdpPacket;
+pub use self::udp::Repr as UdpRepr;

+ 135 - 9
src/wire/udp.rs

@@ -1,7 +1,8 @@
-use core::{cmp, fmt};
+use core::fmt;
 use byteorder::{ByteOrder, NetworkEndian};
 
 use Error;
+use super::{InternetProtocolType, InternetAddress};
 use super::ip::checksum;
 
 /// A read/write wrapper around an User Datagram Protocol packet buffer.
@@ -71,9 +72,17 @@ impl<T: AsRef<[u8]>> Packet<T> {
     }
 
     /// Validate the packet checksum.
-    pub fn verify_checksum(&self) -> bool {
+    ///
+    /// # Panics
+    /// This function panics unless `src_addr` and `dst_addr` belong to the same family,
+    /// and that family is IPv4 or IPv6.
+    pub fn verify_checksum(&self, src_addr: &InternetAddress, dst_addr: &InternetAddress) -> bool {
         let data = self.buffer.as_ref();
-        checksum(&data[..self.len() as usize]) == !0
+        checksum::combine(&[
+            checksum::pseudo_header(src_addr, dst_addr, InternetProtocolType::Udp,
+                                    self.len() as u32),
+            checksum::data(&data[..self.len() as usize])
+        ]) == !0
     }
 }
 
@@ -116,11 +125,19 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
     }
 
     /// Compute and fill in the header checksum.
-    pub fn fill_checksum(&mut self) {
+    ///
+    /// # Panics
+    /// This function panics unless `src_addr` and `dst_addr` belong to the same family,
+    /// and that family is IPv4 or IPv6.
+    pub fn fill_checksum(&mut self, src_addr: &InternetAddress, dst_addr: &InternetAddress) {
         self.set_checksum(0);
         let checksum = {
             let data = self.buffer.as_ref();
-            !checksum(&data[..self.len() as usize])
+            !checksum::combine(&[
+                checksum::pseudo_header(src_addr, dst_addr, InternetProtocolType::Udp,
+                                        self.len() as u32),
+                checksum::data(&data[..self.len() as usize])
+            ])
         };
         self.set_checksum(checksum)
     }
@@ -135,13 +152,99 @@ impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> Packet<&'a mut T> {
     }
 }
 
+/// A high-level representation of an User Datagram Protocol packet.
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub struct Repr<'a> {
+    pub src_port: u16,
+    pub dst_port: u16,
+    pub payload:  &'a [u8]
+}
+
+impl<'a> Repr<'a> {
+    /// Parse an User Datagram Protocol packet and return a high-level representation.
+    pub fn parse<T: ?Sized>(packet: &Packet<&'a T>,
+                            src_addr: &InternetAddress,
+                            dst_addr: &InternetAddress) -> Result<Repr<'a>, Error>
+            where T: AsRef<[u8]> {
+        // Destination port cannot be omitted (but source port can be).
+        if packet.dst_port() == 0 { return Err(Error::Malformed) }
+        // Valid checksum is expected...
+        if !packet.verify_checksum(src_addr, dst_addr) {
+            match (src_addr, dst_addr) {
+                (&InternetAddress::Ipv4(_), &InternetAddress::Ipv4(_))
+                        if packet.checksum() != 0 => {
+                    // ... except on UDP-over-IPv4, where it can be omitted.
+                    return Err(Error::Checksum)
+                },
+                _ => {
+                    return Err(Error::Checksum)
+                }
+            }
+        }
+
+        Ok(Repr {
+            src_port: packet.src_port(),
+            dst_port: packet.dst_port(),
+            payload:  packet.payload()
+        })
+    }
+
+    /// Return the length of a packet that will be emitted from this high-level representation.
+    pub fn len(&self) -> usize {
+        field::PAYLOAD.start + self.payload.len()
+    }
+
+    /// Emit a high-level representation into an User Datagram Protocol packet.
+    pub fn emit<T: ?Sized>(&self, packet: &mut Packet<&mut T>,
+                           src_addr: &InternetAddress,
+                           dst_addr: &InternetAddress)
+            where T: AsRef<[u8]> + AsMut<[u8]> {
+        packet.set_src_port(self.src_port);
+        packet.set_dst_port(self.dst_port);
+        packet.set_len((field::PAYLOAD.start + self.payload.len()) as u16);
+        packet.payload_mut().copy_from_slice(self.payload);
+        packet.fill_checksum(src_addr, dst_addr)
+    }
+}
+
+impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        // Cannot use Repr::parse because we don't have the IP addresses.
+        write!(f, "UDP src={} dst={} len={}",
+               self.src_port(), self.dst_port(), self.payload().len())
+    }
+}
+
+impl<'a> fmt::Display for Repr<'a> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "UDP src={} dst={} len={}",
+               self.src_port, self.dst_port, self.payload.len())
+    }
+}
+
+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(packet) => write!(f, "{}{}\n", indent, packet)
+        }
+    }
+}
+
 #[cfg(test)]
 mod test {
+    use wire::Ipv4Address;
     use super::*;
 
+    const SRC_ADDR: Ipv4Address = Ipv4Address([192, 168, 1, 1]);
+    const DST_ADDR: Ipv4Address = Ipv4Address([192, 168, 1, 2]);
+
     static PACKET_BYTES: [u8; 12] =
         [0xbf, 0x00, 0x00, 0x35,
-         0x00, 0x0c, 0x95, 0xbe,
+         0x00, 0x0c, 0x12, 0x4d,
          0xaa, 0x00, 0x00, 0xff];
 
     static PAYLOAD_BYTES: [u8; 4] =
@@ -153,9 +256,9 @@ mod test {
         assert_eq!(packet.src_port(), 48896);
         assert_eq!(packet.dst_port(), 53);
         assert_eq!(packet.len(), 12);
-        assert_eq!(packet.checksum(), 0x95be);
+        assert_eq!(packet.checksum(), 0x124d);
         assert_eq!(packet.payload(), &PAYLOAD_BYTES[..]);
-        assert_eq!(packet.verify_checksum(), true);
+        assert_eq!(packet.verify_checksum(&SRC_ADDR.into(), &DST_ADDR.into()), true);
     }
 
     #[test]
@@ -167,7 +270,30 @@ mod test {
         packet.set_len(12);
         packet.set_checksum(0xffff);
         packet.payload_mut().copy_from_slice(&PAYLOAD_BYTES[..]);
-        packet.fill_checksum();
+        packet.fill_checksum(&SRC_ADDR.into(), &DST_ADDR.into());
+        assert_eq!(&packet.into_inner()[..], &PACKET_BYTES[..]);
+    }
+
+    fn packet_repr() -> Repr<'static> {
+        Repr {
+            src_port: 48896,
+            dst_port: 53,
+            payload:  &PAYLOAD_BYTES
+        }
+    }
+
+    #[test]
+    fn test_parse() {
+        let packet = Packet::new(&PACKET_BYTES[..]).unwrap();
+        let repr = Repr::parse(&packet, &SRC_ADDR.into(), &DST_ADDR.into()).unwrap();
+        assert_eq!(repr, packet_repr());
+    }
+
+    #[test]
+    fn test_emit() {
+        let mut bytes = vec![0; 12];
+        let mut packet = Packet::new(&mut bytes).unwrap();
+        packet_repr().emit(&mut packet, &SRC_ADDR.into(), &DST_ADDR.into());
         assert_eq!(&packet.into_inner()[..], &PACKET_BYTES[..]);
     }
 }