Browse Source

Implement network part of communication through UDP sockets.

whitequark 8 years ago
parent
commit
3377b911d8
5 changed files with 277 additions and 99 deletions
  1. 14 9
      examples/smoltcpserver.rs
  2. 38 17
      src/iface/arp_cache.rs
  3. 130 44
      src/iface/ethernet.rs
  4. 52 2
      src/socket/mod.rs
  5. 43 27
      src/socket/udp.rs

+ 14 - 9
examples/smoltcpserver.rs

@@ -3,25 +3,30 @@ extern crate smoltcp;
 
 use std::env;
 use smoltcp::phy::{Tracer, TapInterface};
-use smoltcp::wire::{EthernetFrame, EthernetAddress, InternetAddress};
+use smoltcp::wire::{EthernetFrame, EthernetAddress, InternetAddress, InternetEndpoint};
 use smoltcp::iface::{SliceArpCache, EthernetInterface};
+use smoltcp::socket::{Socket, UdpSocket, UdpUnitaryBuffer};
 
 fn main() {
     let ifname = env::args().nth(1).unwrap();
 
-    let hardware_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]);
-    let protocol_addrs = [InternetAddress::ipv4([192, 168, 69, 1])];
-
     let device = TapInterface::new(ifname.as_ref()).unwrap();
     let device = Tracer::<_, EthernetFrame<&[u8]>>::new(device);
+    let arp_cache = SliceArpCache::new(vec![Default::default(); 8]);
+
+    let hardware_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]);
+    let mut protocol_addrs = [InternetAddress::ipv4([192, 168, 69, 1])];
 
-    let mut arp_cache_data = [Default::default(); 8];
-    let arp_cache = SliceArpCache::new(&mut arp_cache_data);
-    let mut iface = EthernetInterface::new(device, arp_cache);
+    let listen_address = InternetAddress::ipv4([0, 0, 0, 0]);
+    let endpoint = InternetEndpoint::new(listen_address, 6969);
 
-    iface.set_hardware_addr(hardware_addr);
-    iface.set_protocol_addrs(&protocol_addrs);
+    let udp_rx_buffer = UdpUnitaryBuffer::new(vec![0; 2048]);
+    let udp_tx_buffer = UdpUnitaryBuffer::new(vec![0; 2048]);
+    let mut udp_socket = UdpSocket::new(endpoint, udp_rx_buffer, udp_tx_buffer);
+    let mut sockets: [&mut Socket; 1] = [&mut udp_socket];
 
+    let mut iface = EthernetInterface::new(device, arp_cache,
+        hardware_addr, &mut protocol_addrs[..], &mut sockets[..]);
     loop {
         match iface.poll() {
             Ok(()) => (),

+ 38 - 17
src/iface/arp_cache.rs

@@ -1,8 +1,10 @@
+use core::borrow::BorrowMut;
+
 use wire::{EthernetAddress, InternetAddress};
 
 /// An Address Resolution Protocol cache.
 ///
-/// This cache maps protocol addresses to hardware addresses.
+/// This interface 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: InternetAddress, hardware_addr: EthernetAddress);
@@ -17,29 +19,40 @@ pub trait Cache {
 /// eviction strategy.
 ///
 /// # Examples
-/// This cache can be created as:
 ///
+/// On systems with heap, this cache can be created with:
+/// ```rust
+/// use smoltcp::iface::SliceArpCache;
+/// let mut arp_cache = SliceArpCache::new(vec![Default::default(); 8]);
+/// ```
+///
+/// On systems without heap, use:
 /// ```rust
 /// use smoltcp::iface::SliceArpCache;
-/// let mut arp_cache_storage = [Default::default(); 8];
-/// let mut arp_cache = SliceArpCache::new(&mut arp_cache_storage);
+/// let mut arp_cache_storage = [Default::default(); 8]
+/// let mut arp_cache = SliceArpCache::new(&mut arp_cache_storage[..]);
 /// ```
-pub struct SliceCache<'a> {
-    storage: &'a mut [(InternetAddress, EthernetAddress, usize)],
+
+pub struct SliceCache<
+    StorageT: BorrowMut<[(InternetAddress, EthernetAddress, usize)]>
+> {
+    storage: StorageT,
     counter: usize
 }
 
-impl<'a> SliceCache<'a> {
+impl<
+    StorageT: BorrowMut<[(InternetAddress, EthernetAddress, usize)]>
+> SliceCache<StorageT> {
     /// Create a cache. The backing storage is cleared upon creation.
     ///
     /// # Panics
     /// This function panics if `storage.len() == 0`.
-    pub fn new(storage: &'a mut [(InternetAddress, EthernetAddress, usize)]) -> SliceCache<'a> {
-        if storage.len() == 0 {
+    pub fn new(mut storage: StorageT) -> SliceCache<StorageT> {
+        if storage.borrow().len() == 0 {
             panic!("ARP slice cache created with empty storage")
         }
 
-        for elem in storage.iter_mut() {
+        for elem in storage.borrow_mut().iter_mut() {
             *elem = Default::default()
         }
         SliceCache {
@@ -52,31 +65,39 @@ impl<'a> SliceCache<'a> {
     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()
+        let storage = self.storage.borrow();
+        storage.binary_search_by_key(&protocol_addr, |&(key, _, _)| key).ok()
     }
 
     /// Sort entries in an order suitable for `find`.
     fn sort(&mut self) {
-        self.storage.sort_by_key(|&(key, _, _)| key)
+        let mut storage = self.storage.borrow_mut();
+        storage.sort_by_key(|&(key, _, _)| key)
     }
 
     /// Find the least recently used entry.
     fn lru(&self) -> usize {
-        self.storage.iter().enumerate().min_by_key(|&(_, &(_, _, counter))| counter).unwrap().0
+        let storage = self.storage.borrow();
+        storage.iter().enumerate().min_by_key(|&(_, &(_, _, counter))| counter).unwrap().0
     }
 }
 
-impl<'a> Cache for SliceCache<'a> {
+impl<
+    StorageT: BorrowMut<[(InternetAddress, EthernetAddress, usize)]>
+> Cache for SliceCache<StorageT> {
     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);
+            let lru_index = self.lru();
+            self.storage.borrow_mut()[lru_index] =
+                (protocol_addr, hardware_addr, self.counter);
             self.sort()
         }
     }
 
     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];
+            let (_protocol_addr, hardware_addr, ref mut counter) =
+                self.storage.borrow_mut()[index];
             self.counter += 1;
             *counter = self.counter;
             Some(hardware_addr)
@@ -103,7 +124,7 @@ mod test {
     #[test]
     fn test_slice_cache() {
         let mut cache_storage = [Default::default(); 3];
-        let mut cache = SliceCache::new(&mut cache_storage);
+        let mut cache = SliceCache::new(&mut cache_storage[..]);
 
         cache.fill(PADDR_A, HADDR_A);
         assert_eq!(cache.lookup(PADDR_A), Some(HADDR_A));

+ 130 - 44
src/iface/ethernet.rs

@@ -1,3 +1,6 @@
+use core::borrow::BorrowMut;
+use core::marker::PhantomData;
+
 use Error;
 use phy::Device;
 use wire::{EthernetAddress, EthernetProtocolType, EthernetFrame};
@@ -5,29 +8,58 @@ use wire::{ArpPacket, ArpRepr, ArpOperation};
 use wire::{InternetAddress, InternetProtocolType};
 use wire::{Ipv4Packet, Ipv4Repr};
 use wire::{Icmpv4Packet, Icmpv4Repr};
-use wire::{UdpPacket, UdpRepr};
+use socket::Socket;
 use super::{ArpCache};
 
 /// An Ethernet network interface.
+///
+/// The network interface logically owns a number of other data structures; to avoid
+/// a dependency on heap allocation, it instead owns a `BorrowMut<[T]>`, which can be
+/// a `&mut [T]`, or `Vec<T>` if a heap is available.
 #[derive(Debug)]
-pub struct Interface<'a, DeviceT: Device, ArpCacheT: ArpCache> {
+pub struct Interface<'a,
+    DeviceT:        Device,
+    ArpCacheT:      ArpCache,
+    ProtocolAddrsT: BorrowMut<[InternetAddress]>,
+    SocketsT:       BorrowMut<[&'a mut Socket]>
+> {
     device:         DeviceT,
     arp_cache:      ArpCacheT,
     hardware_addr:  EthernetAddress,
-    protocol_addrs: &'a [InternetAddress]
+    protocol_addrs: ProtocolAddrsT,
+    sockets:        SocketsT,
+    phantom:        PhantomData<&'a mut Socket>
 }
 
-impl<'a, DeviceT: Device, ArpCacheT: ArpCache> Interface<'a, DeviceT, ArpCacheT> {
+impl<'a,
+    DeviceT:        Device,
+    ArpCacheT:      ArpCache,
+    ProtocolAddrsT: BorrowMut<[InternetAddress]>,
+    SocketsT:       BorrowMut<[&'a mut Socket]>
+> Interface<'a, DeviceT, ArpCacheT, ProtocolAddrsT, SocketsT> {
     /// Create a network interface using the provided network device.
     ///
-    /// The newly created interface uses hardware address `00-00-00-00-00-00` and
-    /// has no assigned protocol addresses.
-    pub fn new(device: DeviceT, arp_cache: ArpCacheT) -> Interface<'a, DeviceT, ArpCacheT> {
+    /// # Panics
+    /// See the restrictions on [set_hardware_addr](#method.set_hardware_addr)
+    /// and [set_protocol_addrs](#method.set_protocol_addrs) functions.
+    pub fn new(device: DeviceT, arp_cache: ArpCacheT, hardware_addr: EthernetAddress,
+               protocol_addrs: ProtocolAddrsT, sockets: SocketsT) ->
+            Interface<'a, DeviceT, ArpCacheT, ProtocolAddrsT, SocketsT> {
+        Self::check_hardware_addr(&hardware_addr);
+        Self::check_protocol_addrs(protocol_addrs.borrow());
         Interface {
             device:         device,
             arp_cache:      arp_cache,
-            hardware_addr:  EthernetAddress([0x00; 6]),
-            protocol_addrs: &[]
+            hardware_addr:  hardware_addr,
+            protocol_addrs: protocol_addrs,
+            sockets:        sockets,
+            phantom:        PhantomData
+        }
+    }
+
+    fn check_hardware_addr(addr: &EthernetAddress) {
+        if addr.is_multicast() {
+            panic!("hardware address {} is not unicast", addr)
         }
     }
 
@@ -41,36 +73,36 @@ impl<'a, DeviceT: Device, ArpCacheT: ArpCache> Interface<'a, DeviceT, ArpCacheT>
     /// # Panics
     /// This function panics if the address is not unicast.
     pub fn set_hardware_addr(&mut self, addr: EthernetAddress) {
-        if addr.is_multicast() {
-            panic!("hardware address {} is not unicast", addr)
-        }
+        self.hardware_addr = addr;
+        Self::check_hardware_addr(&self.hardware_addr);
+    }
 
-        self.hardware_addr = addr
+    fn check_protocol_addrs(addrs: &[InternetAddress]) {
+        for addr in addrs {
+            if !addr.is_unicast() {
+                panic!("protocol address {} is not unicast", addr)
+            }
+        }
     }
 
     /// Get the protocol addresses of the interface.
-    pub fn protocol_addrs(&self) -> &'a [InternetAddress] {
-        self.protocol_addrs
+    pub fn protocol_addrs(&self) -> &[InternetAddress] {
+        self.protocol_addrs.borrow()
     }
 
-    /// Set the protocol addresses of the interface.
+    /// Update the protocol addresses of the interface.
     ///
     /// # Panics
     /// This function panics if any of the addresses is not unicast.
-    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)
-            }
-        }
-
-        self.protocol_addrs = addrs
+    pub fn update_protocol_addrs<F: FnOnce(&mut [InternetAddress])>(&mut self, f: F) {
+        f(self.protocol_addrs.borrow_mut());
+        Self::check_protocol_addrs(self.protocol_addrs.borrow())
     }
 
     /// Checks whether the interface has the given protocol address assigned.
     pub fn has_protocol_addr<T: Into<InternetAddress>>(&self, addr: T) -> bool {
         let addr = addr.into();
-        self.protocol_addrs.iter().any(|&probe| probe == addr)
+        self.protocol_addrs.borrow().iter().any(|&probe| probe == addr)
     }
 
     /// Receive and process a packet, if available.
@@ -159,31 +191,32 @@ impl<'a, DeviceT: Device, ArpCacheT: ArpCache> Interface<'a, DeviceT, ArpCacheT>
                         }
                     },
 
-                    // Queue UDP packets.
-                    Ipv4Repr { protocol: InternetProtocolType::Udp, src_addr, dst_addr } => {
-                        let udp_packet = try!(UdpPacket::new(ip_packet.payload()));
-                        let udp_repr = try!(UdpRepr::parse(&udp_packet,
-                                                           &src_addr.into(), &dst_addr.into()));
-                        println!("yes")
-                    }
+                    // Try dispatching a packet to a socket.
+                    Ipv4Repr { src_addr, dst_addr, protocol } => {
+                        for socket in self.sockets.borrow_mut() {
+                            match socket.collect(&src_addr.into(), &dst_addr.into(),
+                                                 protocol, ip_packet.payload()) {
+                                Ok(()) => break,
+                                Err(Error::Rejected) => continue,
+                                Err(e) => return Err(e)
+                            }
+                        }
 
-                    // FIXME: respond with ICMP unknown protocol here?
-                    _ => return Err(Error::Unrecognized)
+                        // FIXME: respond with ICMP destination unreachable here?
+                    },
                 }
             }
 
             // Drop all other traffic.
             _ => return Err(Error::Unrecognized)
         }
-        if let Response::Nop = response { return Ok(()) }
 
         let tx_size = self.device.mtu();
-        let mut tx_buffer = try!(self.device.transmit(tx_size));
-        let mut frame = try!(EthernetFrame::new(&mut tx_buffer));
-        frame.set_src_addr(self.hardware_addr);
-
         match response {
             Response::Arp(repr) => {
+                let mut tx_buffer = try!(self.device.transmit(tx_size));
+                let mut frame = try!(EthernetFrame::new(&mut tx_buffer));
+                frame.set_src_addr(self.hardware_addr);
                 frame.set_dst_addr(match repr {
                     ArpRepr::EthernetIpv4 { target_hardware_addr, .. } => target_hardware_addr,
                     _ => unreachable!()
@@ -195,10 +228,16 @@ impl<'a, DeviceT: Device, ArpCacheT: ArpCache> Interface<'a, DeviceT, ArpCacheT>
             },
 
             Response::Icmpv4(ip_repr, icmp_repr) => {
-                match self.arp_cache.lookup(ip_repr.dst_addr.into()) {
-                    None => return Err(Error::Unaddressable),
-                    Some(hardware_addr) => frame.set_dst_addr(hardware_addr)
-                }
+                let dst_hardware_addr =
+                    match self.arp_cache.lookup(ip_repr.dst_addr.into()) {
+                        None => return Err(Error::Unaddressable),
+                        Some(hardware_addr) => hardware_addr
+                    };
+
+                let mut tx_buffer = try!(self.device.transmit(tx_size));
+                let mut frame = try!(EthernetFrame::new(&mut tx_buffer));
+                frame.set_src_addr(self.hardware_addr);
+                frame.set_dst_addr(dst_hardware_addr);
                 frame.set_ethertype(EthernetProtocolType::Ipv4);
 
                 let mut ip_packet = try!(Ipv4Packet::new(frame.payload_mut()));
@@ -208,9 +247,56 @@ impl<'a, DeviceT: Device, ArpCacheT: ArpCache> Interface<'a, DeviceT, ArpCacheT>
                 icmp_repr.emit(&mut icmp_packet);
             }
 
-            Response::Nop => unreachable!()
+            Response::Nop => {
+                // Borrow checker is being overly careful around closures, so we have
+                // to hack around that.
+                let src_hardware_addr = self.hardware_addr;
+                let arp_cache = &mut self.arp_cache;
+                let device = &mut self.device;
+
+                for socket in self.sockets.borrow_mut() {
+                    let result = socket.dispatch(&mut |src_addr, dst_addr, protocol, payload| {
+                        let dst_hardware_addr =
+                            match arp_cache.lookup(*dst_addr) {
+                                None => return Err(Error::Unaddressable),
+                                Some(hardware_addr) => hardware_addr
+                            };
+
+                        let mut tx_buffer = try!(device.transmit(tx_size));
+                        let mut frame = try!(EthernetFrame::new(&mut tx_buffer));
+                        frame.set_src_addr(src_hardware_addr);
+                        frame.set_dst_addr(dst_hardware_addr);
+                        frame.set_ethertype(EthernetProtocolType::Ipv4);
+
+                        let mut ip_packet = try!(Ipv4Packet::new(frame.payload_mut()));
+                        let ip_repr =
+                            match (src_addr, dst_addr) {
+                                (&InternetAddress::Ipv4(src_addr),
+                                 &InternetAddress::Ipv4(dst_addr)) => {
+                                    Ipv4Repr {
+                                        src_addr: src_addr,
+                                        dst_addr: dst_addr,
+                                        protocol: protocol
+                                    }
+                                },
+                                _ => unreachable!()
+                            };
+                        ip_repr.emit(&mut ip_packet, payload.len());
+                        payload.emit(src_addr, dst_addr, ip_packet.payload_mut());
+
+                        Ok(())
+                    });
+
+                    match result {
+                        Ok(()) => break,
+                        Err(Error::Exhausted) => continue,
+                        Err(e) => return Err(e)
+                    }
+                }
+            }
         }
 
         Ok(())
     }
 }
+

+ 52 - 2
src/socket/mod.rs

@@ -14,11 +14,61 @@
 //! this means that, for example, a socket that never receives data does not have to allocate
 //! any storage to receive buffers.
 
-use core::fmt;
+use Error;
+use wire::{InternetAddress as Address, InternetProtocolType as ProtocolType};
 
 mod udp;
 
 pub use self::udp::Buffer as UdpBuffer;
 pub use self::udp::NullBuffer as UdpNullBuffer;
 pub use self::udp::UnitaryBuffer as UdpUnitaryBuffer;
-pub use self::udp::Socket as UdpSocket;
+pub use self::udp::UdpSocket as UdpSocket;
+
+/// A packet representation.
+///
+/// This interface abstracts the various types of packets layered under the IP protocol,
+/// and serves as an accessory to [trait Socket](trait.Socket.html).
+pub trait PacketRepr {
+    /// Return the length required to serialize this high-level representation.
+    fn len(&self) -> usize;
+
+    /// Emit this high-level representation into a sequence of octets.
+    fn emit(&self, src_addr: &Address, dst_addr: &Address, payload: &mut [u8]);
+}
+
+/// A network socket.
+///
+/// This interface abstracts the various types of sockets based on the IP protocol.
+/// It is necessarily implemented as a trait, and not as an enumeration, to allow using different
+/// buffering strategies in sockets assigned to the same interface.
+///
+/// The `collect` and `dispatch` functions are fundamentally asymmetric and thus differ in
+/// their use of the [trait PacketRepr](trait.PacketRepr.html). When `collect` is called,
+/// the packet length is already known and no allocation is required; on the other hand,
+/// `collect` would have to downcast a `&PacketRepr` to e.g. an `&UdpRepr` through `Any`,
+/// which is rather inelegant. Conversely, when `dispatch` is called, the packet length is
+/// not yet known and the packet storage has to be allocated; but the `&PacketRepr` is sufficient
+/// since the lower layers treat the packet as an opaque octet sequence.
+pub trait Socket {
+    /// Process a packet received from a network interface.
+    ///
+    /// This function checks if the packet contained in the payload matches the socket endpoint,
+    /// and if it does, copies it into the internal buffer, otherwise, `Err(Error::Rejected)`
+    /// is returned.
+    ///
+    /// This function is used internally by the networking stack.
+    fn collect(&mut self, src_addr: &Address, dst_addr: &Address,
+               protocol: ProtocolType, payload: &[u8])
+        -> Result<(), Error>;
+
+    /// Prepare a packet to be transmitted to a network interface.
+    ///
+    /// This function checks if the internal buffer is empty, and if it is not, calls `f` with
+    /// the representation of the packet to be transmitted, otherwise, `Err(Error::Exhausted)`
+    /// is returned.
+    ///
+    /// This function is used internally by the networking stack.
+    fn dispatch(&mut self, f: &mut FnMut(&Address, &Address,
+                                         ProtocolType, &PacketRepr) -> Result<(), Error>)
+        -> Result<(), Error>;
+}

+ 43 - 27
src/socket/udp.rs

@@ -1,8 +1,10 @@
 use core::borrow::BorrowMut;
 
 use Error;
-use wire::{InternetAddress as Address, InternetEndpoint as Endpoint};
-use wire::UdpRepr;
+use wire::{InternetAddress as Address, InternetProtocolType as ProtocolType};
+use wire::{InternetEndpoint as Endpoint};
+use wire::{UdpPacket, UdpRepr};
+use socket::{Socket, PacketRepr};
 
 /// A packet buffer.
 ///
@@ -91,7 +93,7 @@ impl<T: BorrowMut<[u8]>> Buffer for UnitaryBuffer<T> {
 
     fn dequeue<R, F>(&mut self, f: F) -> Result<R, Error>
             where F: FnOnce(Endpoint, &[u8]) -> Result<R, Error> {
-        let mut storage = self.storage.borrow_mut();
+        let storage = self.storage.borrow_mut();
         match self.endpoint {
             Endpoint { addr: Address::Invalid, .. } => {
                 Err(Error::Exhausted)
@@ -107,18 +109,21 @@ impl<T: BorrowMut<[u8]>> Buffer for UnitaryBuffer<T> {
 }
 
 /// An User Datagram Protocol socket.
-pub struct Socket<RxBufferT: Buffer, TxBufferT: Buffer> {
+///
+/// An UDP socket is bound to a specific endpoint, and owns transmit and receive
+/// packet buffers.
+pub struct UdpSocket<RxBufferT: Buffer, TxBufferT: Buffer> {
     endpoint:  Endpoint,
     rx_buffer: RxBufferT,
     tx_buffer: TxBufferT
 }
 
-impl<RxBufferT: Buffer, TxBufferT: Buffer> Socket<RxBufferT, TxBufferT> {
+impl<RxBufferT: Buffer, TxBufferT: Buffer> UdpSocket<RxBufferT, TxBufferT> {
     /// Create an UDP socket with the given buffers.
     pub fn new(endpoint: Endpoint,
                rx_buffer: RxBufferT,
-               tx_buffer: TxBufferT) -> Socket<RxBufferT, TxBufferT> {
-        Socket {
+               tx_buffer: TxBufferT) -> UdpSocket<RxBufferT, TxBufferT> {
+        UdpSocket {
             endpoint:  endpoint,
             rx_buffer: rx_buffer,
             tx_buffer: tx_buffer
@@ -158,37 +163,37 @@ impl<RxBufferT: Buffer, TxBufferT: Buffer> Socket<RxBufferT, TxBufferT> {
             Ok((buffer.len(), endpoint))
         })
     }
+}
+
+impl<RxBufferT: Buffer, TxBufferT: Buffer> Socket for UdpSocket<RxBufferT, TxBufferT> {
+    fn collect(&mut self, src_addr: &Address, dst_addr: &Address,
+               protocol: ProtocolType, payload: &[u8])
+            -> Result<(), Error> {
+        if protocol != ProtocolType::Udp { return Err(Error::Rejected) }
+
+        let packet = try!(UdpPacket::new(payload));
+        let repr = try!(UdpRepr::parse(&packet, src_addr, dst_addr));
 
-    /// Process a packet received from a network interface.
-    ///
-    /// This function checks if the packet matches the socket endpoint, and if it does,
-    /// copies it into the internal buffer, otherwise, `Err(Error::Rejected)` is returned.
-    ///
-    /// This function is used internally by the networking stack.
-    pub fn collect<'a>(&mut self, src_addr: Address, dst_addr: Address,
-                       repr: &UdpRepr<'a>) -> Result<(), Error> {
         if repr.dst_port != self.endpoint.port { return Err(Error::Rejected) }
         if !self.endpoint.addr.is_unspecified() {
-            if self.endpoint.addr != dst_addr { return Err(Error::Rejected) }
+            if self.endpoint.addr != *dst_addr { return Err(Error::Rejected) }
         }
-        let endpoint = Endpoint { addr: src_addr, port: repr.src_port };
+
+        let endpoint = Endpoint { addr: *src_addr, port: repr.src_port };
         self.rx_buffer.enqueue(endpoint, repr.payload.len(), |buffer| {
             Ok(buffer.copy_from_slice(repr.payload))
         })
     }
 
-    /// Prepare a packet to be transmitted to a network interface.
-    ///
-    /// This function checks if the internal buffer is empty, and if it is not,
-    /// calls `f` with the representation of the UDP packet to be transmitted, otherwise,
-    /// `Err(Error::Exhausted)` is returned.
-    ///
-    /// This function is used internally by the networking stack.
-    pub fn dispatch<R, F>(&mut self, f: F) -> Result<R, Error>
-            where F: for<'a> FnOnce(Address, Address, &UdpRepr<'a>) -> Result<R, Error> {
+    fn dispatch(&mut self, f: &mut FnMut(&Address, &Address,
+                                         ProtocolType, &PacketRepr) -> Result<(), Error>)
+            -> Result<(), Error> {
         let src_endpoint = self.endpoint;
         self.tx_buffer.dequeue(|dst_endpoint, buffer| {
-            f(src_endpoint.addr, dst_endpoint.addr, &UdpRepr {
+            f(&src_endpoint.addr,
+              &dst_endpoint.addr,
+              ProtocolType::Udp,
+              &UdpRepr {
                 src_port: src_endpoint.port,
                 dst_port: dst_endpoint.port,
                 payload:  buffer
@@ -196,3 +201,14 @@ impl<RxBufferT: Buffer, TxBufferT: Buffer> Socket<RxBufferT, TxBufferT> {
         })
     }
 }
+
+impl<'a> PacketRepr for UdpRepr<'a> {
+    fn len(&self) -> usize {
+        self.len()
+    }
+
+    fn emit(&self, src_addr: &Address, dst_addr: &Address, payload: &mut [u8]) {
+        let mut packet = UdpPacket::new(payload).expect("undersized payload slice");
+        self.emit(&mut packet, src_addr, dst_addr)
+    }
+}