use std::{cmp, mem}; use netutils::{n16, Ipv4Addr, Checksum, Ipv4Header, Ipv4}; use resource_scheme::Resource; use syscall; use syscall::error::*; /// A IP (internet protocole) resource pub struct IpResource { pub link: usize, pub data: Vec, pub host_addr: Ipv4Addr, pub peer_addr: Ipv4Addr, pub proto: u8, pub id: u16, } impl Resource for IpResource { fn dup(&self) -> Result> { let link = try!(syscall::dup(self.link)); Ok(Box::new(IpResource { link: link, data: self.data.clone(), host_addr: self.host_addr, peer_addr: self.peer_addr, proto: self.proto, id: self.id, })) } 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(); for (b, p) in buf.iter_mut().zip(path.iter()) { *b = *p; } Ok(cmp::min(buf.len(), path.len())) } fn read(&mut self, buf: &mut [u8]) -> Result { if !self.data.is_empty() { let mut data: Vec = Vec::new(); mem::swap(&mut self.data, &mut data); for (b, d) in buf.iter_mut().zip(data.iter()) { *b = *d; } return Ok(cmp::min(buf.len(), data.len())); } 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(), packet.data.len())); } } Ok(0) } fn write(&mut self, buf: &[u8]) -> Result { let ip_data = Vec::from(buf); 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())); } match syscall::write(self.link, &ip.to_bytes()) { Ok(_) => Ok(buf.len()), Err(err) => Err(err), } } fn sync(&mut self) -> Result { syscall::fsync(self.link) } } impl Drop for IpResource { fn drop(&mut self) { let _ = syscall::close(self.link); } }