소스 검색

Implement UDP sockets.

whitequark 8 년 전
부모
커밋
659f6ed6f6
10개의 변경된 파일278개의 추가작업 그리고 4개의 파일을 삭제
  1. 6 2
      README.md
  2. 10 1
      src/iface/ethernet.rs
  3. 1 0
      src/iface/mod.rs
  4. 7 0
      src/lib.rs
  5. 1 0
      src/phy/mod.rs
  6. 24 0
      src/socket/mod.rs
  7. 198 0
      src/socket/udp.rs
  8. 28 0
      src/wire/ip.rs
  9. 2 1
      src/wire/ipv4.rs
  10. 1 0
      src/wire/mod.rs

+ 6 - 2
README.md

@@ -19,7 +19,7 @@ The only supported medium is Ethernet.
 
   * Regular Ethernet II frames are supported.
   * ARP packets (including gratuitous requests and replies) are supported.
-  * 802.3 and 802.1Q is **not** supported.
+  * 802.3 and 802.1Q are **not** supported.
   * Jumbo frames are **not** supported.
   * Frame check sequence calculation is **not** supported.
 
@@ -30,13 +30,17 @@ The only supported internetworking protocol is IPv4.
   * IPv4 header checksum is supported.
   * IPv4 fragmentation is **not** supported.
   * IPv4 options are **not** supported.
+  * ICMPv4 header checksum is supported.
   * ICMPv4 echo requests and replies are supported.
   * ICMPv4 destination unreachable message is **not** supported.
   * ICMPv4 parameter problem message is **not** supported.
 
 ### UDP layer
 
-UDP is **not** supported yet.
+The UDP protocol is supported over IPv4.
+
+  * UDP header checksum is supported.
+  * UDP sockets are supported.
 
 ### TCP layer
 

+ 10 - 1
src/iface/ethernet.rs

@@ -5,6 +5,7 @@ use wire::{ArpPacket, ArpRepr, ArpOperation};
 use wire::{InternetAddress, InternetProtocolType};
 use wire::{Ipv4Packet, Ipv4Repr};
 use wire::{Icmpv4Packet, Icmpv4Repr};
+use wire::{UdpPacket, UdpRepr};
 use super::{ArpCache};
 
 /// An Ethernet network interface.
@@ -121,7 +122,7 @@ impl<'a, DeviceT: Device, ArpCacheT: ArpCache> Interface<'a, DeviceT, ArpCacheT>
                 }
             },
 
-            // Respond to IP packets directed at us.
+            // Handle IP packets directed at us.
             EthernetProtocolType::Ipv4 => {
                 let ip_packet = try!(Ipv4Packet::new(eth_frame.payload()));
                 match try!(Ipv4Repr::parse(&ip_packet)) {
@@ -158,6 +159,14 @@ 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")
+                    }
+
                     // FIXME: respond with ICMP unknown protocol here?
                     _ => return Err(Error::Unrecognized)
                 }

+ 1 - 0
src/iface/mod.rs

@@ -2,6 +2,7 @@
 //!
 //! The `iface` module deals with the *network interfaces*. It filters incoming frames,
 //! provides lookup and caching of hardware addresses, and handles management packets.
+
 mod arp_cache;
 mod ethernet;
 

+ 7 - 0
src/lib.rs

@@ -14,6 +14,7 @@ use core::fmt;
 pub mod phy;
 pub mod wire;
 pub mod iface;
+pub mod socket;
 
 /// The error type for the networking stack.
 #[derive(Debug)]
@@ -36,6 +37,10 @@ pub enum Error {
     /// to hardware address. E.g. an IPv4 packet did not have an Ethernet address
     /// corresponding to its IPv4 destination address.
     Unaddressable,
+    /// A buffer for incoming packets is empty, or a buffer for outgoing packets is full.
+    Exhausted,
+    /// An incoming packet does not match the socket endpoint.
+    Rejected,
 
     #[doc(hidden)]
     __Nonexhaustive
@@ -50,6 +55,8 @@ impl fmt::Display for Error {
             &Error::Checksum      => write!(f, "checksum error"),
             &Error::Fragmented    => write!(f, "fragmented packet"),
             &Error::Unaddressable => write!(f, "unaddressable destination"),
+            &Error::Exhausted     => write!(f, "buffer space exhausted"),
+            &Error::Rejected      => write!(f, "rejected by socket"),
             &Error::__Nonexhaustive => unreachable!()
         }
     }

+ 1 - 0
src/phy/mod.rs

@@ -4,6 +4,7 @@
 //! for transmitting and receiving frames, [Device](trait.Device.html),
 //! as well as an implementations of that trait that uses the host OS,
 //! [RawSocket](struct.RawSocket.html) and [TapInterface](struct.TapInterface.html).
+
 use Error;
 
 #[cfg(feature = "std")]

+ 24 - 0
src/socket/mod.rs

@@ -0,0 +1,24 @@
+//! Communication between endpoints.
+//!
+//! The `socket` module deals with *network endpoints* and *buffering*.
+//! It provides interfaces for accessing buffers of data, and protocol state machines
+//! for filling and emptying these buffers.
+//!
+//! The programming interface implemented here differs greatly from the common Berkeley socket
+//! interface. Specifically, in the Berkeley interface the buffering is implicit:
+//! the operating system decides on the good size for a buffer and manages it.
+//! The interface implemented by this module uses explicit buffering: you decide on the good
+//! size for a buffer, allocate it, and let the networking stack use it.
+//!
+//! Every socket implementation allows selecting transmit and receive buffers separately;
+//! this means that, for example, a socket that never receives data does not have to allocate
+//! any storage to receive buffers.
+
+use core::fmt;
+
+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;

+ 198 - 0
src/socket/udp.rs

@@ -0,0 +1,198 @@
+use core::borrow::BorrowMut;
+
+use Error;
+use wire::{InternetAddress as Address, InternetEndpoint as Endpoint};
+use wire::UdpRepr;
+
+/// A packet buffer.
+///
+/// The packet buffer interface allows enqueueing and dequeueing separate packets.
+/// A packet is a sequence of octets and its associated endpoint.
+pub trait Buffer {
+    /// Enqueue a packet.
+    ///
+    /// This function allocates a sequence of octets the given size and associates
+    /// the given endpoint with it, then calls `f`; if the buffer is full, it
+    /// returns `Err(Error::Exhausted)` instead.
+    fn enqueue<R, F>(&mut self, endpoint: Endpoint, size: usize, f: F) -> Result<R, Error>
+        where F: FnOnce(&mut [u8]) -> Result<R, Error>;
+
+    /// Dequeue a packet.
+    ///
+    /// This function calls `f` with the oldest enqueued packet; if the buffer is empty,
+    /// it returns `Err(Error::Exhausted)` instead.
+    fn dequeue<R, F>(&mut self, f: F) -> Result<R, Error>
+        where F: FnOnce(Endpoint, &[u8]) -> Result<R, Error>;
+}
+
+/// A packet buffer that does not have any storage.
+///
+/// The null buffer rejects enqueue and dequeue operations with `Error::Exhausted`.
+pub struct NullBuffer(());
+
+impl NullBuffer {
+    /// Create a null packet buffer.
+    pub fn new() -> NullBuffer {
+        NullBuffer(())
+    }
+}
+
+impl Buffer for NullBuffer {
+    fn enqueue<R, F>(&mut self, _endpoint: Endpoint, _size: usize, _f: F) -> Result<R, Error>
+            where F: FnOnce(&mut [u8]) -> Result<R, Error> {
+        Err(Error::Exhausted)
+    }
+
+    fn dequeue<R, F>(&mut self, _f: F) -> Result<R, Error>
+            where F: FnOnce(Endpoint, &[u8]) -> Result<R, Error> {
+        Err(Error::Exhausted)
+    }
+}
+
+/// A packet buffer that only stores, at most, a single packet.
+///
+/// The unitary buffer uses a provided slice to store no more than one packet at any time.
+/// If there is an enqueued packet, or if the requested size is larger than the storage size,
+/// the unitary rejects the enqueue operation with `Error::Exhausted`.
+pub struct UnitaryBuffer<T: BorrowMut<[u8]>> {
+    endpoint: Endpoint,
+    storage:  T,
+    size:     usize
+}
+
+impl<T: BorrowMut<[u8]>> UnitaryBuffer<T> {
+    /// Create an unitary packet buffer, using the given storage.
+    pub fn new(storage: T) -> UnitaryBuffer<T> {
+        UnitaryBuffer {
+            endpoint: Default::default(),
+            storage:  storage,
+            size:     0
+        }
+    }
+}
+
+impl<T: BorrowMut<[u8]>> Buffer for UnitaryBuffer<T> {
+    fn enqueue<R, F>(&mut self, endpoint: Endpoint, size: usize, f: F) -> Result<R, Error>
+            where F: FnOnce(&mut [u8]) -> Result<R, Error> {
+        let mut storage = self.storage.borrow_mut();
+        match self.endpoint {
+            Endpoint { addr: Address::Invalid, .. }
+                    if size <= storage.len() => {
+                // If `f` fails, don't enqueue the packet.
+                let result = try!(f(&mut storage[..size]));
+                self.endpoint = endpoint;
+                Ok(result)
+            },
+            _ => {
+                Err(Error::Exhausted)
+            }
+        }
+    }
+
+    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();
+        match self.endpoint {
+            Endpoint { addr: Address::Invalid, .. } => {
+                Err(Error::Exhausted)
+            },
+            _ => {
+                // If `f` fails, still dequeue the packet.
+                let result = f(self.endpoint, &storage[..self.size]);
+                self.endpoint = Default::default();
+                result
+            }
+        }
+    }
+}
+
+/// An User Datagram Protocol socket.
+pub struct Socket<RxBufferT: Buffer, TxBufferT: Buffer> {
+    endpoint:  Endpoint,
+    rx_buffer: RxBufferT,
+    tx_buffer: TxBufferT
+}
+
+impl<RxBufferT: Buffer, TxBufferT: Buffer> Socket<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 {
+            endpoint:  endpoint,
+            rx_buffer: rx_buffer,
+            tx_buffer: tx_buffer
+        }
+    }
+
+    /// Send a packet to a remote endpoint, without copying.
+    pub fn send<R, F>(&mut self, endpoint: Endpoint, size: usize, f: F) -> Result<R, Error>
+            where F: FnOnce(&mut [u8]) -> Result<R, Error> {
+        self.tx_buffer.enqueue(endpoint, size, f)
+    }
+
+    /// Send a packet to remote endpoint, copying the given slice to the internal buffer.
+    ///
+    /// This function returns `Err(Error::Exhausted)` if the slice is larger than the internal
+    /// buffer can accomodate.
+    pub fn send_slice(&mut self, endpoint: Endpoint, data: &[u8]) -> Result<(), Error> {
+        self.tx_buffer.enqueue(endpoint, data.len(), |buffer| {
+            Ok(buffer.copy_from_slice(data))
+        })
+    }
+
+    /// Receive a packet from a remote endpoint, without copying.
+    pub fn recv<R, F>(&mut self, f: F) -> Result<R, Error>
+            where F: FnOnce(Endpoint, &[u8]) -> Result<R, Error> {
+        self.rx_buffer.dequeue(f)
+    }
+
+    /// Receive a packet from a remote endpoint, copying the given slice to the internal buffer.
+    ///
+    /// This function returns `Err(Error::Exhausted)` if the slice is smaller than the packet
+    /// queued in the internal buffer.
+    pub fn recv_slice(&mut self, data: &mut [u8]) -> Result<(usize, Endpoint), Error> {
+        self.rx_buffer.dequeue(|endpoint, buffer| {
+            if data.len() < buffer.len() { return Err(Error::Exhausted) }
+            data[..buffer.len()].copy_from_slice(buffer);
+            Ok((buffer.len(), endpoint))
+        })
+    }
+
+    /// 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) }
+        }
+        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> {
+        let src_endpoint = self.endpoint;
+        self.tx_buffer.dequeue(|dst_endpoint, buffer| {
+            f(src_endpoint.addr, dst_endpoint.addr, &UdpRepr {
+                src_port: src_endpoint.port,
+                dst_port: dst_endpoint.port,
+                payload:  buffer
+            })
+        })
+    }
+}

+ 28 - 0
src/wire/ip.rs

@@ -45,6 +45,14 @@ impl Address {
             &Address::Ipv4(addr) => addr.is_unicast()
         }
     }
+
+    /// Query whether the address falls into the "unspecified" range.
+    pub fn is_unspecified(&self) -> bool {
+        match self {
+            &Address::Invalid    => false,
+            &Address::Ipv4(addr) => addr.is_unspecified()
+        }
+    }
 }
 
 impl Default for Address {
@@ -68,6 +76,26 @@ impl fmt::Display for Address {
     }
 }
 
+/// An internet endpoint address.
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
+pub struct Endpoint {
+    pub addr: Address,
+    pub port: u16
+}
+
+impl Endpoint {
+    /// Create an internet endpoint address.
+    pub fn new(addr: Address, port: u16) -> Endpoint {
+        Endpoint { addr: addr, port: port }
+    }
+}
+
+impl fmt::Display for Endpoint {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{}:{}", self.addr, self.port)
+    }
+}
+
 pub mod checksum {
     use byteorder::{ByteOrder, NetworkEndian};
 

+ 2 - 1
src/wire/ipv4.rs

@@ -11,7 +11,8 @@ pub use super::InternetProtocolType as ProtocolType;
 pub struct Address(pub [u8; 4]);
 
 impl Address {
-    pub const BROADCAST: Address = Address([255; 4]);
+    pub const UNSPECIFIED: Address = Address([0x00; 4]);
+    pub const BROADCAST:   Address = Address([0xff; 4]);
 
     /// Construct an IPv4 address from a sequence of octets, in big-endian.
     ///

+ 1 - 0
src/wire/mod.rs

@@ -100,6 +100,7 @@ pub use self::arp::Repr as ArpRepr;
 
 pub use self::ip::ProtocolType as InternetProtocolType;
 pub use self::ip::Address as InternetAddress;
+pub use self::ip::Endpoint as InternetEndpoint;
 
 pub use self::ipv4::Address as Ipv4Address;
 pub use self::ipv4::Packet as Ipv4Packet;