diff --git a/schemes/ipd/src/main.rs b/schemes/ipd/src/main.rs index 0bf8777..7c66cff 100644 --- a/schemes/ipd/src/main.rs +++ b/schemes/ipd/src/main.rs @@ -13,7 +13,10 @@ //! 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. +//! and the connection may be used immediately to send/receive data. Ip v4 number +//! `127.0.0.1` is hardwired to the loopback device (i.e. localhost), which doesn't +//! access any physical device and in which data can only be read by the same +//! connection that has written it. //! * 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 @@ -47,3 +50,43 @@ fn main() { } }); } + +#[cfg(test)] +fn test() { + use scheme::IpScheme; + + println!("* Test that we can read a simple packet from the same connection."); + let bytes = "TEST".as_bytes(); + let mut scheme = IpScheme::new(); + let a = scheme.open("ip:127.0.0.1/11").unwrap(); + let b = scheme.open("ip:127.0.0.1/11").unwrap(); + let num_bytes_written = scheme.write(a, bytes).unwrap(); + assert_eq!(num_bytes_written, bytes.len()); + + let mut buf = [0;65536]; + let num_bytes_read = scheme.read(a, &mut buf).unwrap(); + assert_eq!(num_bytes_read, num_bytes_written); + + let bytes_read = &buf[0..num_bytes_read]; + assert_eq!(bytes, bytes_read); + + println!("* Test that we cannot read the same packet from a different connection."); + let num_bytes_read = scheme.read(b, &mut buf).unwrap(); + assert_eq!(num_bytes_read, 0); + + println!("* Push a number of packets, check that we get them in the right order."); + let mut payloads : Vec = 0..100.map(|i| format!("TEST {}", i)).collect(); + for payload in &payloads { + let bytes = payload.into_bytes(); + let num_bytes_written = scheme.write(a, &bytes).unwrap(); + assert_eq!(bytes.len(), num_bytes_written); + } + for payload in &payloads { + let bytes = payload.into_bytes(); + let mut buf = [0;65536]; + let num_bytes_read = scheme.read(a, &mut buf).unwrap(); + assert_eq!(bytes.len(), num_bytes_read); + let bytes_read = &buf[0..num_bytes_read]; + assert_eq!(bytes, bytes_read); + } +} \ No newline at end of file diff --git a/schemes/ipd/src/resource.rs b/schemes/ipd/src/resource.rs index 273b05b..aad343a 100644 --- a/schemes/ipd/src/resource.rs +++ b/schemes/ipd/src/resource.rs @@ -1,21 +1,20 @@ use std::{cmp, mem}; +use std::collections::VecDeque; use netutils::{n16, Ipv4Addr, Checksum, Ipv4Header, Ipv4}; use resource_scheme::Resource; use syscall; use syscall::error::*; +/// Max number of bytes in a packet. +const MAX_PACKET_LENGTH : usize = 65536; + /// 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, - - /// 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 underlying mechanism ensured to connect to the peer. + pub connection: Connection, /// The IP address of the host (i.e. this machine). pub host_addr: Ipv4Addr, @@ -33,6 +32,25 @@ pub struct IpResource { pub id: u16, } +pub enum Connection { + Device { + /// Link to the underlying device (typically, an Ethernet card). + link: usize, + + /// 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()`. + init_data: Vec, + }, + Loopback { + /// FIFO queue of packets written to the connection and waiting to be read. + /// + /// The data stored contains the exact data that has been added by the client + /// calling `write()`, without adding any headers. + packets: VecDeque> + } +} + impl Resource for IpResource { /// Duplicate the connection. /// @@ -43,14 +61,26 @@ impl Resource for IpResource { /// Fails if the `link` to the underlying device cannot be /// duplicated. fn dup(&self) -> Result> { - let link = try!(syscall::dup(self.link)); + use self::Connection::*; + let connection = match self.connection { + Loopback { ref packets } => Loopback { + packets: packets.clone() + }, + Device { link, ref init_data } => { + let link = try!(syscall::dup(link)); + let init_data = init_data.clone(); + Device { + link: link, + init_data: init_data + } + } + }; Ok(Box::new(IpResource { - link: link, - init_data: self.init_data.clone(), host_addr: self.host_addr, peer_addr: self.peer_addr, proto: self.proto, id: self.id, + connection: connection, })) } @@ -79,37 +109,61 @@ impl Resource for IpResource { /// can happen only if the connection was waiting for a remote peer to connect, i.e. /// with a url `ip:/protocol`, without host. /// + /// If this connection is a loopback, oldest unread packet written is read. + /// /// # Errors /// /// Fails if the call to `syscall::read()` fails for this device. + /// + /// # Data loss + /// + /// If `buf` is too small, *exceeding data is discarded*. To be sure that you read + /// all data, you should provide a 64kb `buf`. fn read(&mut self, buf: &mut [u8]) -> Result { - if !self.init_data.is_empty() { - let mut data: Vec = Vec::new(); - mem::swap(&mut self.init_data, &mut data); - - for (b, d) in buf.iter_mut().zip(data.iter()) { - *b = *d; + use self::Connection::*; + match self.connection { + Loopback { ref mut packets } => { + match packets.pop_front() { + None => Ok(0), + Some(data) => { + for (b, d) in buf.iter_mut().zip(data.iter()) { + *b = *d; + } + // Note: We're discarding excess `data`. + Ok(cmp::min(buf.len(), data.len())) + } + } } + Device { ref mut init_data, link } => { + if !init_data.is_empty() { + let mut data: Vec = Vec::new(); + mem::swap(init_data, &mut data); - return Ok(cmp::min(buf.len(), data.len())); - } + for (b, d) in buf.iter_mut().zip(data.iter()) { + *b = *d; + } - let mut bytes = [0; 65536]; - let count = try!(syscall::read(self.link, &mut bytes)); - - if let Some(packet) = Ipv4::from_bytes(&bytes[..count]) { - if packet.header.proto == self.proto && - (packet.header.dst.equals(self.host_addr) || packet.header.dst.equals(Ipv4Addr::BROADCAST)) && - (packet.header.src.equals(self.peer_addr) || self.peer_addr.equals(Ipv4Addr::BROADCAST)) { - for (b, d) in buf.iter_mut().zip(packet.data.iter()) { - *b = *d; + return Ok(cmp::min(buf.len(), data.len())); } - return Ok(cmp::min(buf.len(), packet.data.len())); + let mut bytes = [0; MAX_PACKET_LENGTH]; + let count = try!(syscall::read(link, &mut bytes)); + + if let Some(packet) = Ipv4::from_bytes(&bytes[..count]) { + if packet.header.proto == self.proto && + (packet.header.dst.equals(self.host_addr) || packet.header.dst.equals(Ipv4Addr::BROADCAST)) && + (packet.header.src.equals(self.peer_addr) || self.peer_addr.equals(Ipv4Addr::BROADCAST)) { + for (b, d) in buf.iter_mut().zip(packet.data.iter()) { + *b = *d; + } + + return Ok(cmp::min(buf.len(), packet.data.len())); + } + } + + Ok(0) } } - - Ok(0) } /// Send data to the peer. @@ -118,46 +172,68 @@ impl Resource for IpResource { /// /// Fails if the call to `syscall::write()` fails for this device. fn write(&mut self, buf: &[u8]) -> Result { + use self::Connection::*; + let ip_data = Vec::from(buf); + match self.connection { + Loopback { ref mut packets } => { + // Make sure that we're not going to store data that can't be read. + let buf = + if buf.len() > MAX_PACKET_LENGTH { + &buf[0..MAX_PACKET_LENGTH] + } else { + buf + }; + packets.push_back(buf.to_vec()); + return Ok(buf.len()) + } + Device { link, .. } => { + self.id += 1; + let mut ip = Ipv4 { + header: Ipv4Header { + ver_hlen: 0x40 | (mem::size_of::() / 4 & 0xF) as u8, // No Options + services: 0, + len: n16::new((mem::size_of::() + ip_data.len()) as u16), // No Options + id: n16::new(self.id), + flags_fragment: n16::new(0), + ttl: 128, + proto: self.proto, + checksum: Checksum { data: 0 }, + src: self.host_addr, + dst: self.peer_addr, + }, + options: Vec::new(), + data: ip_data, + }; - self.id += 1; - let mut ip = Ipv4 { - header: Ipv4Header { - ver_hlen: 0x40 | (mem::size_of::() / 4 & 0xF) as u8, // No Options - services: 0, - len: n16::new((mem::size_of::() + ip_data.len()) as u16), // No Options - id: n16::new(self.id), - flags_fragment: n16::new(0), - ttl: 128, - proto: self.proto, - checksum: Checksum { data: 0 }, - src: self.host_addr, - dst: self.peer_addr, - }, - options: Vec::new(), - data: ip_data, - }; + unsafe { + let header_ptr: *const Ipv4Header = &ip.header; + ip.header.checksum.data = + Checksum::compile(Checksum::sum(header_ptr as usize, mem::size_of::()) + + Checksum::sum(ip.options.as_ptr() as usize, ip.options.len())); + } - unsafe { - let header_ptr: *const Ipv4Header = &ip.header; - ip.header.checksum.data = - Checksum::compile(Checksum::sum(header_ptr as usize, mem::size_of::()) + - Checksum::sum(ip.options.as_ptr() as usize, ip.options.len())); - } - - match syscall::write(self.link, &ip.to_bytes()) { - Ok(_) => Ok(buf.len()), - Err(err) => Err(err), + match syscall::write(link, &ip.to_bytes()) { + Ok(_) => Ok(buf.len()), + Err(err) => Err(err), + } + } } } fn sync(&mut self) -> Result { - syscall::fsync(self.link) + if let Connection::Device { link, .. } = self.connection { + syscall::fsync(link) + } else { + Ok(0) + } } } impl Drop for IpResource { fn drop(&mut self) { - let _ = syscall::close(self.link); + if let Connection::Device { link, .. } = self.connection { + let _ = syscall::close(link); + } } } diff --git a/schemes/ipd/src/scheme.rs b/schemes/ipd/src/scheme.rs index 471b2c6..5eda001 100644 --- a/schemes/ipd/src/scheme.rs +++ b/schemes/ipd/src/scheme.rs @@ -1,4 +1,5 @@ use std::cell::RefCell; +use std::collections::VecDeque; use std::rand; use std::{str, u16}; @@ -8,7 +9,10 @@ use syscall; use syscall::error::{Error, Result, EACCES, ENOENT, EINVAL}; use syscall::flag::O_RDWR; -use resource::IpResource; +use resource::*; + +/// The IP address of the localhost. +const LOCALHOST: Ipv4Addr = Ipv4Addr { bytes: [127, 0, 0, 1] }; /// A ARP entry (MAC + IP) pub struct ArpEntry { @@ -48,6 +52,18 @@ impl ResourceScheme for IpScheme { let mut route_mac = MacAddr::BROADCAST; if ! peer_addr.equals(Ipv4Addr::BROADCAST) { + if peer_addr.equals(LOCALHOST) { + return Ok(Box::new(IpResource { + connection: Connection::Loopback { + packets: VecDeque::new() + }, + host_addr: ip_addr, + peer_addr: peer_addr, + proto: proto, + id: (rand() % 65536) as u16, + })); + } + let mut needs_routing = false; for octet in 0..4 { @@ -116,8 +132,10 @@ 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, - init_data: Vec::new(), + connection: Connection::Device { + link: link, + init_data: Vec::new(), + }, host_addr: ip_addr, peer_addr: peer_addr, proto: proto, @@ -134,8 +152,10 @@ impl ResourceScheme for IpScheme { if packet.header.proto == proto && (packet.header.dst.equals(ip_addr) || packet.header.dst.equals(Ipv4Addr::BROADCAST)) { return Ok(Box::new(IpResource { - link: link, - init_data: packet.data, + connection: Connection::Device { + link: link, + init_data: packet.data, + }, host_addr: ip_addr, peer_addr: packet.header.src, proto: proto,