Redo networking (#22)
* Rewriting network functions * Add buffer to dup Fix non-blocking handling by triggering once on enabling events to read to EOF * Modifications for UDP API * Implement TCP client side * Add active close * Add DMAR parser * Implement basic TCP listening. Need to improve the state machine * Reduce debugging * Fixes for close procedure * Updates to fix path processing in libstd
This commit is contained in:
parent
268c859fd6
commit
2491e4771e
54 changed files with 1884 additions and 1476 deletions
|
@ -3,6 +3,6 @@ name = "ipd"
|
|||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
event = { path = "../../crates/event/" }
|
||||
netutils = { path = "../../programs/netutils/" }
|
||||
resource_scheme = { path = "../../crates/resource_scheme/" }
|
||||
syscall = { path = "../../syscall/" }
|
||||
|
|
|
@ -1,120 +1,371 @@
|
|||
#![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. 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
|
||||
//! (see http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml).
|
||||
|
||||
extern crate event;
|
||||
extern crate netutils;
|
||||
extern crate resource_scheme;
|
||||
extern crate syscall;
|
||||
|
||||
use event::EventQueue;
|
||||
use netutils::{getcfg, n16, Ipv4Addr, MacAddr, Ipv4, EthernetII, EthernetIIHeader, Arp, Tcp};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::{BTreeMap, VecDeque};
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::thread;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::os::unix::io::FromRawFd;
|
||||
use std::rc::Rc;
|
||||
use std::{slice, str, thread};
|
||||
use syscall::data::Packet;
|
||||
use syscall::error::{Error, Result, EACCES, EADDRNOTAVAIL, EBADF, EINVAL, ENOENT, EWOULDBLOCK};
|
||||
use syscall::flag::{EVENT_READ, O_NONBLOCK};
|
||||
use syscall::scheme::SchemeMut;
|
||||
|
||||
use resource_scheme::ResourceScheme;
|
||||
use syscall::Packet;
|
||||
struct Interface {
|
||||
mac: MacAddr,
|
||||
ip: Ipv4Addr,
|
||||
router: Ipv4Addr,
|
||||
subnet: Ipv4Addr,
|
||||
arp_file: File,
|
||||
ip_file: File,
|
||||
arp: BTreeMap<Ipv4Addr, MacAddr>,
|
||||
rarp: BTreeMap<MacAddr, Ipv4Addr>,
|
||||
}
|
||||
|
||||
use scheme::IpScheme;
|
||||
impl Interface {
|
||||
fn new(arp_fd: usize, ip_fd: usize) -> Self {
|
||||
Interface {
|
||||
mac: MacAddr::from_str(&getcfg("mac").unwrap()),
|
||||
ip: Ipv4Addr::from_str(&getcfg("ip").unwrap()),
|
||||
router: Ipv4Addr::from_str(&getcfg("ip_router").unwrap()),
|
||||
subnet: Ipv4Addr::from_str(&getcfg("ip_subnet").unwrap()),
|
||||
arp_file: unsafe { File::from_raw_fd(arp_fd) },
|
||||
ip_file: unsafe { File::from_raw_fd(ip_fd) },
|
||||
arp: BTreeMap::new(),
|
||||
rarp: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod resource;
|
||||
mod scheme;
|
||||
struct Handle {
|
||||
proto: u8,
|
||||
flags: usize,
|
||||
events: usize,
|
||||
data: VecDeque<Vec<u8>>,
|
||||
todo: VecDeque<Packet>,
|
||||
}
|
||||
|
||||
struct Ipd {
|
||||
scheme_file: File,
|
||||
interfaces: Vec<Interface>,
|
||||
next_id: usize,
|
||||
handles: BTreeMap<usize, Handle>,
|
||||
}
|
||||
|
||||
impl Ipd {
|
||||
fn new(scheme_file: File) -> Self {
|
||||
Ipd {
|
||||
scheme_file: scheme_file,
|
||||
interfaces: Vec::new(),
|
||||
next_id: 1,
|
||||
handles: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn scheme_event(&mut self) -> io::Result<()> {
|
||||
loop {
|
||||
let mut packet = Packet::default();
|
||||
if self.scheme_file.read(&mut packet)? == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
let a = packet.a;
|
||||
self.handle(&mut packet);
|
||||
if packet.a == (-EWOULDBLOCK) as usize {
|
||||
packet.a = a;
|
||||
if let Some(mut handle) = self.handles.get_mut(&packet.b) {
|
||||
handle.todo.push_back(packet);
|
||||
}
|
||||
} else {
|
||||
self.scheme_file.write(&packet)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn arp_event(&mut self, if_id: usize) -> io::Result<()> {
|
||||
if let Some(mut interface) = self.interfaces.get_mut(if_id) {
|
||||
loop {
|
||||
let mut bytes = [0; 65536];
|
||||
let count = interface.arp_file.read(&mut bytes)?;
|
||||
if count == 0 {
|
||||
break;
|
||||
}
|
||||
if let Some(frame) = EthernetII::from_bytes(&bytes[.. count]) {
|
||||
if let Some(packet) = Arp::from_bytes(&frame.data) {
|
||||
if packet.header.oper.get() == 1 {
|
||||
if packet.header.dst_ip == interface.ip {
|
||||
let mut response = Arp {
|
||||
header: packet.header,
|
||||
data: packet.data.clone(),
|
||||
};
|
||||
response.header.oper.set(2);
|
||||
response.header.dst_mac = packet.header.src_mac;
|
||||
response.header.dst_ip = packet.header.src_ip;
|
||||
response.header.src_mac = interface.mac;
|
||||
response.header.src_ip = interface.ip;
|
||||
|
||||
let mut response_frame = EthernetII {
|
||||
header: frame.header,
|
||||
data: response.to_bytes()
|
||||
};
|
||||
|
||||
response_frame.header.dst = response_frame.header.src;
|
||||
response_frame.header.src = interface.mac;
|
||||
|
||||
interface.arp_file.write(&response_frame.to_bytes())?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn ip_event(&mut self, if_id: usize) -> io::Result<()> {
|
||||
if let Some(mut interface) = self.interfaces.get_mut(if_id) {
|
||||
loop {
|
||||
let mut bytes = [0; 65536];
|
||||
let count = interface.ip_file.read(&mut bytes)?;
|
||||
if count == 0 {
|
||||
break;
|
||||
}
|
||||
if let Some(frame) = EthernetII::from_bytes(&bytes[.. count]) {
|
||||
if let Some(ip) = Ipv4::from_bytes(&frame.data) {
|
||||
if ip.header.dst == interface.ip || ip.header.dst == Ipv4Addr::BROADCAST {
|
||||
//TODO: Handle ping here
|
||||
for (id, handle) in self.handles.iter_mut() {
|
||||
if ip.header.proto == handle.proto {
|
||||
handle.data.push_back(frame.data.clone());
|
||||
|
||||
while ! handle.todo.is_empty() && ! handle.data.is_empty() {
|
||||
let mut packet = handle.todo.pop_front().unwrap();
|
||||
let buf = unsafe { slice::from_raw_parts_mut(packet.c as *mut u8, packet.d) };
|
||||
let data = handle.data.pop_front().unwrap();
|
||||
|
||||
let mut i = 0;
|
||||
while i < buf.len() && i < data.len() {
|
||||
buf[i] = data[i];
|
||||
i += 1;
|
||||
}
|
||||
packet.a = i;
|
||||
|
||||
self.scheme_file.write(&packet)?;
|
||||
}
|
||||
|
||||
if handle.events & EVENT_READ == EVENT_READ {
|
||||
if let Some(data) = handle.data.get(0) {
|
||||
self.scheme_file.write(&Packet {
|
||||
id: 0,
|
||||
pid: 0,
|
||||
uid: 0,
|
||||
gid: 0,
|
||||
a: syscall::number::SYS_FEVENT,
|
||||
b: *id,
|
||||
c: EVENT_READ,
|
||||
d: data.len()
|
||||
})?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl SchemeMut for Ipd {
|
||||
fn open(&mut self, url: &[u8], flags: usize, uid: u32, _gid: u32) -> Result<usize> {
|
||||
if uid == 0 {
|
||||
let path = str::from_utf8(url).or(Err(Error::new(EINVAL)))?;
|
||||
|
||||
let proto = u8::from_str_radix(path, 16).or(Err(Error::new(ENOENT)))?;
|
||||
|
||||
let id = self.next_id;
|
||||
self.next_id += 1;
|
||||
|
||||
self.handles.insert(id, Handle {
|
||||
proto: proto,
|
||||
flags: flags,
|
||||
events: 0,
|
||||
data: VecDeque::new(),
|
||||
todo: VecDeque::new(),
|
||||
});
|
||||
|
||||
Ok(id)
|
||||
} else {
|
||||
Err(Error::new(EACCES))
|
||||
}
|
||||
}
|
||||
|
||||
fn dup(&mut self, file: usize, _buf: &[u8]) -> Result<usize> {
|
||||
let handle = {
|
||||
let handle = self.handles.get(&file).ok_or(Error::new(EBADF))?;
|
||||
Handle {
|
||||
proto: handle.proto,
|
||||
flags: handle.flags,
|
||||
events: 0,
|
||||
data: handle.data.clone(),
|
||||
todo: VecDeque::new(),
|
||||
}
|
||||
};
|
||||
|
||||
let id = self.next_id;
|
||||
self.next_id += 1;
|
||||
|
||||
self.handles.insert(id, handle);
|
||||
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
fn read(&mut self, file: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
let mut handle = self.handles.get_mut(&file).ok_or(Error::new(EBADF))?;
|
||||
|
||||
if let Some(data) = handle.data.pop_front() {
|
||||
let mut i = 0;
|
||||
while i < buf.len() && i < data.len() {
|
||||
buf[i] = data[i];
|
||||
i += 1;
|
||||
}
|
||||
|
||||
Ok(i)
|
||||
} else if handle.flags & O_NONBLOCK == O_NONBLOCK {
|
||||
Ok(0)
|
||||
} else {
|
||||
Err(Error::new(EWOULDBLOCK))
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, file: usize, buf: &[u8]) -> Result<usize> {
|
||||
let handle = self.handles.get(&file).ok_or(Error::new(EBADF))?;
|
||||
|
||||
if let Some(mut ip) = Ipv4::from_bytes(buf) {
|
||||
for mut interface in self.interfaces.iter_mut() {
|
||||
if ip.header.src == interface.ip || ip.header.src == Ipv4Addr::NULL {
|
||||
ip.header.src = interface.ip;
|
||||
ip.header.proto = handle.proto;
|
||||
|
||||
if let Some(mut tcp) = Tcp::from_bytes(&ip.data) {
|
||||
tcp.checksum(&ip.header.src, &ip.header.dst);
|
||||
ip.data = tcp.to_bytes();
|
||||
}
|
||||
|
||||
ip.checksum();
|
||||
|
||||
let frame = EthernetII {
|
||||
header: EthernetIIHeader {
|
||||
//TODO: Get real dst
|
||||
dst: MacAddr::BROADCAST,
|
||||
src: interface.mac,
|
||||
ethertype: n16::new(0x800),
|
||||
},
|
||||
data: ip.to_bytes()
|
||||
};
|
||||
|
||||
interface.ip_file.write(&frame.to_bytes()).map_err(|err| err.into_sys())?;
|
||||
|
||||
return Ok(buf.len());
|
||||
}
|
||||
}
|
||||
|
||||
Err(Error::new(EADDRNOTAVAIL))
|
||||
} else {
|
||||
Err(Error::new(EINVAL))
|
||||
}
|
||||
}
|
||||
|
||||
fn fevent(&mut self, file: usize, flags: usize) -> Result<usize> {
|
||||
let mut handle = self.handles.get_mut(&file).ok_or(Error::new(EBADF))?;
|
||||
|
||||
handle.events = flags;
|
||||
|
||||
Ok(file)
|
||||
}
|
||||
|
||||
fn fpath(&mut self, id: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
let path_string = format!("ip:{:X}", handle.proto);
|
||||
let path = path_string.as_bytes();
|
||||
|
||||
let mut i = 0;
|
||||
while i < buf.len() && i < path.len() {
|
||||
buf[i] = path[i];
|
||||
i += 1;
|
||||
}
|
||||
|
||||
Ok(i)
|
||||
}
|
||||
|
||||
fn fsync(&mut self, file: usize) -> Result<usize> {
|
||||
let _handle = self.handles.get(&file).ok_or(Error::new(EBADF))?;
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn close(&mut self, file: usize) -> Result<usize> {
|
||||
let handle = self.handles.remove(&file).ok_or(Error::new(EBADF))?;
|
||||
|
||||
drop(handle);
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
thread::spawn(move || {
|
||||
let mut socket = File::create(":ip").expect("ipd: failed to create ip scheme");
|
||||
let scheme = IpScheme::new();
|
||||
loop {
|
||||
let mut packet = Packet::default();
|
||||
socket.read(&mut packet).expect("ipd: failed to read events from ip scheme");
|
||||
scheme.handle(&mut packet);
|
||||
socket.write(&packet).expect("ipd: failed to write responses to ip scheme");
|
||||
let scheme_fd = syscall::open(":ip", syscall::O_RDWR | syscall::O_CREAT | syscall::O_NONBLOCK).expect("ipd: failed to create :ip");
|
||||
let scheme_file = unsafe { File::from_raw_fd(scheme_fd) };
|
||||
|
||||
let ipd = Rc::new(RefCell::new(Ipd::new(scheme_file)));
|
||||
|
||||
let mut event_queue = EventQueue::<()>::new().expect("ipd: failed to create event queue");
|
||||
|
||||
//TODO: Multiple interfaces
|
||||
{
|
||||
let arp_fd = syscall::open("ethernet:806", syscall::O_RDWR | syscall::O_NONBLOCK).expect("ipd: failed to open ethernet:806");
|
||||
let ip_fd = syscall::open("ethernet:800", syscall::O_RDWR | syscall::O_NONBLOCK).expect("ipd: failed to open ethernet:800");
|
||||
let if_id = {
|
||||
let mut ipd = ipd.borrow_mut();
|
||||
let if_id = ipd.interfaces.len();
|
||||
ipd.interfaces.push(Interface::new(arp_fd, ip_fd));
|
||||
if_id
|
||||
};
|
||||
|
||||
let arp_ipd = ipd.clone();
|
||||
event_queue.add(arp_fd, move |_count: usize| -> io::Result<Option<()>> {
|
||||
arp_ipd.borrow_mut().arp_event(if_id)?;
|
||||
Ok(None)
|
||||
}).expect("ipd: failed to listen to events on ethernet:806");
|
||||
|
||||
let ip_ipd = ipd.clone();
|
||||
event_queue.add(ip_fd, move |_count: usize| -> io::Result<Option<()>> {
|
||||
ip_ipd.borrow_mut().ip_event(if_id)?;
|
||||
Ok(None)
|
||||
}).expect("ipd: failed to listen to events on ethernet:800");
|
||||
}
|
||||
|
||||
event_queue.add(scheme_fd, move |_count: usize| -> io::Result<Option<()>> {
|
||||
ipd.borrow_mut().scheme_event()?;
|
||||
Ok(None)
|
||||
}).expect("ipd: failed to listen to events on :ip");
|
||||
|
||||
// Make sure that all descriptors are at EOF
|
||||
event_queue.trigger_all(0).expect("ipd: failed to trigger event queue");
|
||||
|
||||
event_queue.run().expect("ipd: failed to run event queue");
|
||||
});
|
||||
}
|
||||
|
||||
#[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".as_bytes(), 0, 0, 0).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 the loopback is now empty.");
|
||||
let num_bytes_read = scheme.read(a, &mut buf).unwrap();
|
||||
assert_eq!(num_bytes_read, 0);
|
||||
|
||||
|
||||
|
||||
println!("* Test that we can read the same packet from a different connection.");
|
||||
let num_bytes_written = scheme.write(a, bytes).unwrap();
|
||||
assert_eq!(num_bytes_written, bytes.len());
|
||||
|
||||
let b = scheme.open("ip:127.0.0.1/11".as_bytes(), 0, 0, 0).unwrap();
|
||||
|
||||
let num_bytes_read = scheme.read(b, &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 the loopback is now empty for both connections.");
|
||||
|
||||
let num_bytes_read = scheme.read(a, &mut buf).unwrap();
|
||||
assert_eq!(num_bytes_read, 0);
|
||||
|
||||
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<String> = (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);
|
||||
}
|
||||
}
|
|
@ -1,242 +0,0 @@
|
|||
use std::cell::RefCell;
|
||||
use std::{cmp, mem};
|
||||
use std::collections::VecDeque;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Mutex;
|
||||
|
||||
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 {
|
||||
/// 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,
|
||||
|
||||
/// 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,
|
||||
}
|
||||
|
||||
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<u8>,
|
||||
},
|
||||
Loopback {
|
||||
/// FIFO queue of packets written to the loopback 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.
|
||||
///
|
||||
/// This buffer is shared between all loopback connections.
|
||||
packets: Rc<RefCell<VecDeque<Vec<u8>>>>
|
||||
}
|
||||
}
|
||||
|
||||
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<Box<Self>> {
|
||||
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 {
|
||||
host_addr: self.host_addr,
|
||||
peer_addr: self.peer_addr,
|
||||
proto: self.proto,
|
||||
id: self.id,
|
||||
connection: connection,
|
||||
}))
|
||||
}
|
||||
|
||||
/// 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<usize> {
|
||||
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()))
|
||||
}
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// 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<usize> {
|
||||
use self::Connection::*;
|
||||
match self.connection {
|
||||
Loopback { ref packets }=> {
|
||||
match packets.borrow_mut().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<u8> = Vec::new();
|
||||
mem::swap(init_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; 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Send data to the peer.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Fails if the call to `syscall::write()` fails for this device.
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize> {
|
||||
use self::Connection::*;
|
||||
|
||||
let ip_data = Vec::from(buf);
|
||||
match self.connection {
|
||||
Loopback { ref 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.borrow_mut().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::<Ipv4Header>() / 4 & 0xF) as u8, // No Options
|
||||
services: 0,
|
||||
len: n16::new((mem::size_of::<Ipv4Header>() + 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::<Ipv4Header>()) +
|
||||
Checksum::sum(ip.options.as_ptr() as usize, ip.options.len()));
|
||||
}
|
||||
|
||||
match syscall::write(link, &ip.to_bytes()) {
|
||||
Ok(_) => Ok(buf.len()),
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn sync(&mut self) -> Result<usize> {
|
||||
if let Connection::Device { link, .. } = self.connection {
|
||||
syscall::fsync(link)
|
||||
} else {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for IpResource {
|
||||
fn drop(&mut self) {
|
||||
if let Connection::Device { link, .. } = self.connection {
|
||||
let _ = syscall::close(link);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,193 +0,0 @@
|
|||
use std::cell::RefCell;
|
||||
use std::collections::VecDeque;
|
||||
use std::rand;
|
||||
use std::rc::Rc;
|
||||
use std::{str, u16};
|
||||
|
||||
use netutils::{getcfg, n16, MacAddr, Ipv4Addr, ArpHeader, Arp, Ipv4};
|
||||
use resource_scheme::ResourceScheme;
|
||||
use syscall;
|
||||
use syscall::error::{Error, Result, EACCES, ENOENT, EINVAL};
|
||||
use syscall::flag::O_RDWR;
|
||||
|
||||
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 {
|
||||
ip: Ipv4Addr,
|
||||
mac: MacAddr,
|
||||
}
|
||||
|
||||
/// A IP scheme
|
||||
pub struct IpScheme {
|
||||
pub arp: RefCell<Vec<ArpEntry>>,
|
||||
|
||||
/// FIFO queue of packets written to the loopback 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.
|
||||
///
|
||||
/// This buffer is shared between all loopback connections.
|
||||
pub loopback_fifo: Rc<RefCell<VecDeque<Vec<u8>>>>,
|
||||
}
|
||||
|
||||
impl IpScheme {
|
||||
pub fn new() -> IpScheme {
|
||||
IpScheme {
|
||||
arp: RefCell::new(Vec::new()),
|
||||
loopback_fifo: Rc::new(RefCell::new(VecDeque::new())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ResourceScheme<IpResource> for IpScheme {
|
||||
fn open_resource(&self, url: &[u8], _flags: usize, uid: u32, _gid: u32) -> Result<Box<IpResource>> {
|
||||
if uid == 0 {
|
||||
let mac_addr = MacAddr::from_str(&getcfg("mac").map_err(|err| err.into_sys())?);
|
||||
let ip_addr = Ipv4Addr::from_str(&getcfg("ip").map_err(|err| err.into_sys())?);
|
||||
let ip_subnet = Ipv4Addr::from_str(&getcfg("ip_subnet").map_err(|err| err.into_sys())?);
|
||||
let ip_router = Ipv4Addr::from_str(&getcfg("ip_router").map_err(|err| err.into_sys())?);
|
||||
|
||||
let path = try!(str::from_utf8(url).or(Err(Error::new(EINVAL))));
|
||||
let mut parts = path.split('/');
|
||||
if let Some(host_string) = parts.next() {
|
||||
if let Some(proto_string) = parts.next() {
|
||||
let proto = u8::from_str_radix(proto_string, 16).unwrap_or(0);
|
||||
|
||||
if ! host_string.is_empty() {
|
||||
let peer_addr = Ipv4Addr::from_str(host_string);
|
||||
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: self.loopback_fifo.clone()
|
||||
},
|
||||
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 {
|
||||
let me = ip_addr.bytes[octet];
|
||||
let mask = ip_subnet.bytes[octet];
|
||||
let them = peer_addr.bytes[octet];
|
||||
if me & mask != them & mask {
|
||||
needs_routing = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let route_addr = if needs_routing {
|
||||
ip_router
|
||||
} else {
|
||||
peer_addr
|
||||
};
|
||||
|
||||
for entry in self.arp.borrow().iter() {
|
||||
if entry.ip.equals(route_addr) {
|
||||
route_mac = entry.mac;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if route_mac.equals(MacAddr::BROADCAST) {
|
||||
if let Ok(link) = syscall::open(&format!("ethernet:{}/806", &route_mac.to_string()), O_RDWR) {
|
||||
let arp = Arp {
|
||||
header: ArpHeader {
|
||||
htype: n16::new(1),
|
||||
ptype: n16::new(0x800),
|
||||
hlen: 6,
|
||||
plen: 4,
|
||||
oper: n16::new(1),
|
||||
src_mac: mac_addr,
|
||||
src_ip: ip_addr,
|
||||
dst_mac: route_mac,
|
||||
dst_ip: route_addr,
|
||||
},
|
||||
data: Vec::new(),
|
||||
};
|
||||
|
||||
match syscall::write(link, &arp.to_bytes()) {
|
||||
Ok(_) => loop {
|
||||
let mut bytes = [0; 65536];
|
||||
match syscall::read(link, &mut bytes) {
|
||||
Ok(count) => if let Some(packet) = Arp::from_bytes(&bytes[..count]) {
|
||||
if packet.header.oper.get() == 2 &&
|
||||
packet.header.src_ip.equals(route_addr) {
|
||||
route_mac = packet.header.src_mac;
|
||||
self.arp.borrow_mut().push(ArpEntry {
|
||||
ip: route_addr,
|
||||
mac: route_mac,
|
||||
});
|
||||
break;
|
||||
}
|
||||
},
|
||||
Err(_) => (),
|
||||
}
|
||||
},
|
||||
Err(err) => println!("IP: ARP Write Failed: {}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(link) = syscall::open(&format!("ethernet:{}/800", &route_mac.to_string()), O_RDWR) {
|
||||
return Ok(Box::new(IpResource {
|
||||
connection: Connection::Device {
|
||||
link: link,
|
||||
init_data: Vec::new(),
|
||||
},
|
||||
host_addr: ip_addr,
|
||||
peer_addr: peer_addr,
|
||||
proto: proto,
|
||||
id: (rand() % 65536) as u16,
|
||||
}));
|
||||
}
|
||||
} 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]) {
|
||||
if packet.header.proto == proto &&
|
||||
(packet.header.dst.equals(ip_addr) || packet.header.dst.equals(Ipv4Addr::BROADCAST)) {
|
||||
return Ok(Box::new(IpResource {
|
||||
connection: Connection::Device {
|
||||
link: link,
|
||||
init_data: packet.data,
|
||||
},
|
||||
host_addr: ip_addr,
|
||||
peer_addr: packet.header.src,
|
||||
proto: proto,
|
||||
id: (rand() % 65536) as u16,
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println!("IP: No protocol provided");
|
||||
}
|
||||
} else {
|
||||
println!("IP: No host provided");
|
||||
}
|
||||
|
||||
Err(Error::new(ENOENT))
|
||||
} else {
|
||||
Err(Error::new(EACCES))
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue