diff --git a/schemes/ipd/src/main.rs b/schemes/ipd/src/main.rs index f3bb7d3..0bf8777 100644 --- a/schemes/ipd/src/main.rs +++ b/schemes/ipd/src/main.rs @@ -1,5 +1,24 @@ #![feature(rand)] +//! Implementation of the IP Scheme as a userland driver. +//! +//! # Role +//! +//! See https://en.wikipedia.org/wiki/Internet_Protocol for more details about the +//! IP protocol. Clients will often prefer using either higher-level protocols TCP +//! or UDP, both of which are built upon IP. +//! +//! # URL Syntax +//! +//! To open a IP connection, use `ip:[host]/protocol`. +//! +//! * If `host` is specified, it must be an ipv4 number (e.g. `192.168.0.1`) +//! and the connection may be used immediately to send/receive data. +//! * If `host` is omitted, this connectino will wait for a distant peer to +//! connect. +//! * The `protocol` is the hex-based number of the ip protocol +//! (see http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml). + extern crate netutils; extern crate resource_scheme; extern crate syscall; diff --git a/schemes/ipd/src/resource.rs b/schemes/ipd/src/resource.rs index 2e0b1ac..273b05b 100644 --- a/schemes/ipd/src/resource.rs +++ b/schemes/ipd/src/resource.rs @@ -5,22 +5,48 @@ use resource_scheme::Resource; use syscall; use syscall::error::*; -/// A IP (internet protocole) resource +/// A IP (internet protocol) resource. +/// +/// Each instance represents a connection (~ a IP socket). pub struct IpResource { + /// Link to the underlying device (typically, an Ethernet card). pub link: usize, - pub data: Vec, + + /// If this connection was opened waiting for a peer (i.e. `ip:/protocol`), + /// the data received when the peer actually connected. Otherwise, empty. + /// Emptied during the first call to `read()`. + pub init_data: Vec, + + /// The IP address of the host (i.e. this machine). pub host_addr: Ipv4Addr, + + /// The IP address of the peer (i.e. the other machine). pub peer_addr: Ipv4Addr, + + /// The IP protocol used by this connection. See + /// http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml + /// for the list of valid protocols. pub proto: u8, + + /// The id of the next packet being sent. + /// See https://en.wikipedia.org/wiki/IPv4#Identification . pub id: u16, } impl Resource for IpResource { + /// Duplicate the connection. + /// + /// This duplicates both `self.link` and `self.init_data`. + /// + /// # Errors + /// + /// Fails if the `link` to the underlying device cannot be + /// duplicated. fn dup(&self) -> Result> { let link = try!(syscall::dup(self.link)); Ok(Box::new(IpResource { link: link, - data: self.data.clone(), + init_data: self.init_data.clone(), host_addr: self.host_addr, peer_addr: self.peer_addr, proto: self.proto, @@ -28,6 +54,12 @@ impl Resource for IpResource { })) } + /// Get the current path, as `ip:peer/protocol`, where `peer` + /// is the IPv4 address of the peer and `protocol` is the hex-based + /// number of the IP protocol used. + /// + /// Note that the `peer` is specified even if the connection was initially + /// created as `ip:/protocol`. fn path(&self, buf: &mut [u8]) -> Result { let path_string = format!("ip:{}/{:X}", self.peer_addr.to_string(), self.proto); let path = path_string.as_bytes(); @@ -39,10 +71,21 @@ impl Resource for IpResource { Ok(cmp::min(buf.len(), path.len())) } + /// Read data from the device. + /// + /// If some data has already been made available during the establishment + /// of the connection, this data is (entirely) read during the first call + /// to `read()`, without attempting to actually read from the device. This + /// can happen only if the connection was waiting for a remote peer to connect, i.e. + /// with a url `ip:/protocol`, without host. + /// + /// # Errors + /// + /// Fails if the call to `syscall::read()` fails for this device. fn read(&mut self, buf: &mut [u8]) -> Result { - if !self.data.is_empty() { + if !self.init_data.is_empty() { let mut data: Vec = Vec::new(); - mem::swap(&mut self.data, &mut data); + mem::swap(&mut self.init_data, &mut data); for (b, d) in buf.iter_mut().zip(data.iter()) { *b = *d; @@ -69,6 +112,11 @@ impl Resource for IpResource { Ok(0) } + /// Send data to the peer. + /// + /// # Errors + /// + /// Fails if the call to `syscall::write()` fails for this device. fn write(&mut self, buf: &[u8]) -> Result { let ip_data = Vec::from(buf); diff --git a/schemes/ipd/src/scheme.rs b/schemes/ipd/src/scheme.rs index 47808dc..471b2c6 100644 --- a/schemes/ipd/src/scheme.rs +++ b/schemes/ipd/src/scheme.rs @@ -117,7 +117,7 @@ impl ResourceScheme for IpScheme { if let Ok(link) = syscall::open(&format!("ethernet:{}/800", &route_mac.to_string()), O_RDWR) { return Ok(Box::new(IpResource { link: link, - data: Vec::new(), + init_data: Vec::new(), host_addr: ip_addr, peer_addr: peer_addr, proto: proto, @@ -127,6 +127,7 @@ impl ResourceScheme for IpScheme { } else { while let Ok(link) = syscall::open("ethernet:/800", O_RDWR) { let mut bytes = [0; 65536]; + // FIXME: Blocking call? match syscall::read(link, &mut bytes) { Ok(count) => { if let Some(packet) = Ipv4::from_bytes(&bytes[..count]) { @@ -134,7 +135,7 @@ impl ResourceScheme for IpScheme { (packet.header.dst.equals(ip_addr) || packet.header.dst.equals(Ipv4Addr::BROADCAST)) { return Ok(Box::new(IpResource { link: link, - data: packet.data, + init_data: packet.data, host_addr: ip_addr, peer_addr: packet.header.src, proto: proto,