From 5b89874deb6e6b4e8feb6f198fafa9bdec7a0b64 Mon Sep 17 00:00:00 2001 From: Egor Karavaev Date: Sat, 9 Sep 2017 23:17:19 +0300 Subject: [PATCH] Wip. --- Cargo.lock | 2 + Cargo.toml | 7 +- src/icmpd/main.rs | 7 +- src/smolnetd/device.rs | 74 +++++++++ src/smolnetd/main.rs | 73 +++++++-- src/smolnetd/scheme.rs | 332 ++++++++++++++++++++++++++++++----------- 6 files changed, 390 insertions(+), 105 deletions(-) create mode 100644 src/smolnetd/device.rs diff --git a/Cargo.lock b/Cargo.lock index 9d529e7365..7b55db6412 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,7 @@ name = "redox_netstack" version = "0.1.0" dependencies = [ + "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "netutils 0.1.0 (git+https://github.com/redox-os/netutils.git)", "rand 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", "redox_event 0.1.0 (git+https://github.com/redox-os/event.git)", @@ -338,6 +339,7 @@ version = "0.4.0-pre" source = "git+https://github.com/m-labs/smoltcp.git#9cb813134c88dd41904d409de45818829bd94889" dependencies = [ "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "managed 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/Cargo.toml b/Cargo.toml index a8a6f44305..938eeb57f7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,12 @@ rand = "0.3" redox_event = { git = "https://github.com/redox-os/event.git" } redox_syscall = "0.1" +[dependencies.log] +version = "0.3" +default-features = false +features = ["release_max_level_off"] + [dependencies.smoltcp] git = "https://github.com/m-labs/smoltcp.git" default-features = false -features = ["std"] +features = ["std", "log"] diff --git a/src/icmpd/main.rs b/src/icmpd/main.rs index 25026b656c..4d5cb96728 100644 --- a/src/icmpd/main.rs +++ b/src/icmpd/main.rs @@ -18,9 +18,9 @@ mod scheme; fn run() -> Result<()> { use syscall::flag::*; - if unsafe { syscall::clone(0).unwrap() } != 0 { - return Ok(()); - } + // if unsafe { syscall::clone(0).unwrap() } != 0 { + // return Ok(()); + // } println!("icmpd: opening ip:1:"); let icmp_fd = syscall::open("ip:1", O_RDWR | O_NONBLOCK) @@ -32,7 +32,6 @@ fn run() -> Result<()> { .map_err(|e| Error::from_syscall_error(e, "failed to open :icmp"))? as RawFd; - let icmpd = Rc::new(RefCell::new(Icmpd::new(unsafe { File::from_raw_fd(icmp_fd) }, unsafe { File::from_raw_fd(scheme_fd) }))); diff --git a/src/smolnetd/device.rs b/src/smolnetd/device.rs new file mode 100644 index 0000000000..b96e8fe185 --- /dev/null +++ b/src/smolnetd/device.rs @@ -0,0 +1,74 @@ +use std::rc::Rc; +use std::fs::File; +use std::cell::RefCell; +use std::io::Write; +use std::collections::VecDeque; +use smoltcp; + +pub struct NetworkDevice { + network_file: Rc>, + input_queue: Rc>>>, +} + +impl NetworkDevice { + pub const MTU: usize = 120; + + pub fn new( + network_file: Rc>, + input_queue: Rc>>>, + ) -> NetworkDevice { + NetworkDevice { + network_file, + input_queue, + } + } +} + +pub struct TxBuffer { + buffer: Vec, + network_file: Rc>, +} + +impl AsRef<[u8]> for TxBuffer { + fn as_ref(&self) -> &[u8] { + self.buffer.as_ref() + } +} + +impl AsMut<[u8]> for TxBuffer { + fn as_mut(&mut self) -> &mut [u8] { + self.buffer.as_mut() + } +} + +impl Drop for TxBuffer { + fn drop(&mut self) { + let _ = self.network_file.borrow_mut().write(&self.buffer); + } +} + +impl smoltcp::phy::Device for NetworkDevice { + type RxBuffer = Vec; + type TxBuffer = TxBuffer; + + fn limits(&self) -> smoltcp::phy::DeviceLimits { + let mut limits = smoltcp::phy::DeviceLimits::default(); + limits.max_transmission_unit = Self::MTU; + limits.max_burst_size = Some(1); + limits + } + + fn receive(&mut self, _timestamp: u64) -> smoltcp::Result { + self.input_queue + .borrow_mut() + .pop_front() + .ok_or(smoltcp::Error::Exhausted) + } + + fn transmit(&mut self, _timestamp: u64, length: usize) -> smoltcp::Result { + Ok(TxBuffer { + network_file: self.network_file.clone(), + buffer: vec![0; length], + }) + } +} diff --git a/src/smolnetd/main.rs b/src/smolnetd/main.rs index 4be9d06beb..d783e52c97 100644 --- a/src/smolnetd/main.rs +++ b/src/smolnetd/main.rs @@ -1,38 +1,75 @@ extern crate event; -extern crate syscall; +#[macro_use] +extern crate log; +extern crate netutils; extern crate smoltcp; +extern crate syscall; use error::{Error, Result}; use event::EventQueue; use scheme::Smolnetd; +use std::cell::RefCell; +use std::fs::File; use std::os::unix::io::{FromRawFd, RawFd}; use std::process; use std::rc::Rc; -use std::fs::File; -use std::cell::RefCell; mod error; +mod device; mod scheme; +struct SimpleLogger; + +impl log::Log for SimpleLogger { + fn enabled(&self, _metadata: &log::LogMetadata) -> bool { + true + } + + fn log(&self, record: &log::LogRecord) { + if self.enabled(record.metadata()) { + println!("{} : {}", record.level(), record.args()); + } + } +} + fn run() -> Result<()> { use syscall::flag::*; - if unsafe { syscall::clone(0).unwrap() } != 0 { - return Ok(()); + unsafe { + log::set_logger_raw(|max_log_level| { + max_log_level.set(log::LogLevelFilter::Trace); + &SimpleLogger + }).expect("Can't initialize logger"); } - println!("icmpd: opening network:"); + // if unsafe { syscall::clone(0).unwrap() } != 0 { + // return Ok(()); + // } + + trace!("opening network:"); let network_fd = syscall::open("network:", O_RDWR | O_NONBLOCK) .map_err(|e| Error::from_syscall_error(e, "failed to open network:"))? as RawFd; - println!("icmpd: opening :ip"); - let ip_fd = syscall::open(":ip", O_RDWR | O_NONBLOCK) + trace!("opening :ip"); + let ip_fd = syscall::open(":ip", O_RDWR | O_CREAT | O_NONBLOCK) .map_err(|e| Error::from_syscall_error(e, "failed to open :ip"))? as RawFd; - let (network_file, ip_file) = - unsafe { (File::from_raw_fd(network_fd), File::from_raw_fd(ip_fd)) }; - let smolnetd = Rc::new(RefCell::new(Smolnetd::new(network_file, ip_file))); + let time_path = format!("time:{}", syscall::CLOCK_MONOTONIC); + let time_fd = syscall::open(&time_path, syscall::O_RDWR) + .map_err(|e| Error::from_syscall_error(e, "failed to open time:"))? as + RawFd; + + let (network_file, ip_file, time_file) = unsafe { + ( + File::from_raw_fd(network_fd), + File::from_raw_fd(ip_fd), + File::from_raw_fd(time_fd), + ) + }; + let smolnetd = Rc::new(RefCell::new( + Smolnetd::new(network_file, ip_file, time_file), + )); let mut event_queue = EventQueue::<(), Error>::new() .map_err(|e| Error::from_io_error(e, "failed to create event queue"))?; @@ -47,16 +84,26 @@ fn run() -> Result<()> { Error::from_io_error(e, "failed to listen to network events") })?; + let smolnetd_ = smolnetd.clone(); + event_queue - .add(ip_fd, move |_| smolnetd.borrow_mut().on_ip_scheme_event()) + .add(ip_fd, move |_| smolnetd_.borrow_mut().on_ip_scheme_event()) .map_err(|e| Error::from_io_error(e, "failed to listen to ip events"))?; + event_queue + .add(time_fd, move |_| smolnetd.borrow_mut().on_time_event()) + .map_err(|e| { + Error::from_io_error(e, "failed to listen to time events") + })?; + + event_queue.trigger_all(0)?; + event_queue.run() } fn main() { if let Err(err) = run() { - println!("smoltcpd: {}", err); + error!("smoltcpd: {}", err); process::exit(1); } process::exit(0); diff --git a/src/smolnetd/scheme.rs b/src/smolnetd/scheme.rs index 70071f55ae..34a0b2912e 100644 --- a/src/smolnetd/scheme.rs +++ b/src/smolnetd/scheme.rs @@ -1,97 +1,59 @@ -use error::Result; -use std::rc::Rc; -use std::fs::File; -use std::cell::RefCell; -use std::time::Instant; -use std::io::{Read, Write}; -use syscall::SchemeMut; -use syscall; +use device::NetworkDevice; +use error::{Error, Result}; +use netutils::{getcfg, MacAddr}; use smoltcp; +use std::collections::{BTreeMap, VecDeque}; +use std::fs::File; +use std::io::{Read, Write}; +use std::mem; +use std::net::Ipv4Addr; +use std::str::FromStr; +use std::time::Instant; +use std::rc::Rc; +use std::cell::RefCell; +use smoltcp::socket::AsSocket; +use syscall; -struct NetworkDevice { - network_file: Rc>, -} - -impl NetworkDevice { - const MTU: usize = 1520; - - pub fn new(network_file: File) -> NetworkDevice { - NetworkDevice { - network_file: Rc::new(RefCell::new(network_file)), - } - } -} - -struct TxBuffer { - buffer: Vec, - network_file: Rc>, -} - -impl AsRef<[u8]> for TxBuffer { - fn as_ref(&self) -> &[u8] { - self.buffer.as_ref() - } -} - -impl AsMut<[u8]> for TxBuffer { - fn as_mut(&mut self) -> &mut [u8] { - self.buffer.as_mut() - } -} - -impl Drop for TxBuffer { - fn drop(&mut self) { - let _ = self.network_file.borrow_mut().write(&self.buffer); - } -} - -impl smoltcp::phy::Device for NetworkDevice { - type RxBuffer = Vec; - type TxBuffer = TxBuffer; - - fn limits(&self) -> smoltcp::phy::DeviceLimits { - let mut limits = smoltcp::phy::DeviceLimits::default(); - limits.max_transmission_unit = Self::MTU; - limits.max_burst_size = Some(1); - limits - } - - fn receive(&mut self, _timestamp: u64) -> smoltcp::Result { - let mut buffer = Vec::with_capacity(65536); - if let Ok(count) = self.network_file.borrow_mut().read(&mut buffer) { - if count == 0 { - return Err(smoltcp::Error::Exhausted); - } - buffer.resize(count, 0); - Ok(buffer) - } else { - Err(smoltcp::Error::Exhausted) - } - } - - fn transmit(&mut self, _timestamp: u64, length: usize) -> smoltcp::Result { - Ok(TxBuffer { - network_file: self.network_file.clone(), - buffer: vec![0; length], - }) - } +struct IpHandle { + flags: usize, + events: usize, + socket_handle: smoltcp::socket::SocketHandle, } pub struct Smolnetd { iface: smoltcp::iface::EthernetInterface<'static, 'static, 'static, NetworkDevice>, - sockets: smoltcp::socket::SocketSet<'static, 'static, 'static>, + socket_set: smoltcp::socket::SocketSet<'static, 'static, 'static>, ip_file: File, + time_file: File, + network_file: Rc>, startup_time: Instant, + ip_sockets: BTreeMap, + input_queue: Rc>>>, + next_fd: usize, } -struct IpHandler<'a>(&'a mut Smolnetd); +struct IpScheme<'a>(&'a mut Smolnetd); impl Smolnetd { - pub fn new(network_file: File, ip_file: File) -> Smolnetd { + const IP_BUFFER_SIZE: usize = 128; + const CHECK_TIMEOUT_MS: i64 = 1000; + + pub fn new(network_file: File, ip_file: File, time_file: File) -> Smolnetd { let arp_cache = smoltcp::iface::SliceArpCache::new(vec![Default::default(); 8]); - let hardware_addr = smoltcp::wire::EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]); - let protocol_addrs = [smoltcp::wire::IpAddress::v4(192, 168, 69, 1)]; - let network_device = NetworkDevice::new(network_file); + //TODO Use smoltcp::wire::EthernetAddress::from_str + let mac_addr = MacAddr::from_str(&getcfg("mac").unwrap().trim()); + let hardware_addr = smoltcp::wire::EthernetAddress(mac_addr.bytes); + //TODO Use smoltcp::wire::Ipv4Addr::from_str + let ip_bytes = Ipv4Addr::from_str(&getcfg("ip").unwrap().trim()) + .unwrap() + .octets(); + let protocol_addrs = [ + smoltcp::wire::IpAddress::Ipv4(smoltcp::wire::Ipv4Address::from_bytes(&ip_bytes)), + ]; + trace!("mac {:?} ip {:?}", hardware_addr, protocol_addrs); + let input_queue = Rc::new(RefCell::new(VecDeque::new())); + let network_file = Rc::new(RefCell::new(network_file)); + let network_device = NetworkDevice::new(network_file.clone(), input_queue.clone()); Smolnetd { iface: smoltcp::iface::EthernetInterface::new( Box::new(network_device), @@ -99,37 +61,233 @@ impl Smolnetd { hardware_addr, protocol_addrs, ), - sockets: smoltcp::socket::SocketSet::new(vec![]), + socket_set: smoltcp::socket::SocketSet::new(vec![]), startup_time: Instant::now(), ip_file, + time_file, + ip_sockets: BTreeMap::new(), + next_fd: 0, + input_queue, + network_file, } } pub fn on_network_scheme_event(&mut self) -> Result> { - let timestamp = self.get_timestamp(); - let _ = self.iface.poll(&mut self.sockets, timestamp); + if self.read_frames()? > 0 { + self.poll().map(Some)?; + } Ok(None) } pub fn on_ip_scheme_event(&mut self) -> Result> { + use syscall::SchemeMut; + loop { let mut packet = syscall::Packet::default(); if self.ip_file.read(&mut packet)? == 0 { break; } - IpHandler(self).handle(&mut packet); + IpScheme(self).handle(&mut packet); self.ip_file.write_all(&packet)?; } Ok(None) } + pub fn on_time_event(&mut self) -> Result> { + let mut time = syscall::data::TimeSpec::default(); + if self.time_file.read(&mut time)? < mem::size_of::() { + panic!(); + } + let mut time_ms = time.tv_sec * 1000i64 + (time.tv_nsec as i64) / 1_000_000i64; + time_ms += Smolnetd::CHECK_TIMEOUT_MS; + time.tv_sec = time_ms / 1000; + time.tv_nsec = ((time_ms % 1000) * 1_000_00) as i32; + self.time_file + .write_all(&time) + .map_err(|e| Error::from_io_error(e, "Failed to write to time file"))?; + + self.poll().map(Some)?; + Ok(None) + } + + fn poll(&mut self) -> Result<()> { + let timestamp = self.get_timestamp(); + self.iface + .poll(&mut self.socket_set, timestamp) + .expect("poll error"); + self.notify_ip_sockets() + } + + fn read_frames(&mut self) -> Result { + let mut total_frames = 0; + loop { + let mut buffer = vec![0; 65536]; + let count = self.network_file + .borrow_mut() + .read(&mut buffer) + .map_err(|e| { + Error::from_io_error(e, "Failed to read from network file") + })?; + if count == 0 { + break; + } + trace!("got frame {}", count); + buffer.resize(count, 0); + self.input_queue.borrow_mut().push_back(buffer); + total_frames += 1; + } + Ok(total_frames) + } + fn get_timestamp(&self) -> u64 { let duration = Instant::now().duration_since(self.startup_time); let duration_ms = (duration.as_secs() * 1000) + (duration.subsec_nanos() / 1000000) as u64; duration_ms } + + fn network_fsync(&mut self) -> syscall::Result { + use std::os::unix::io::AsRawFd; + syscall::fsync(self.network_file.borrow_mut().as_raw_fd() as usize) + } + + fn notify_ip_sockets(&mut self) -> Result<()> { + for (&fd, ref handle) in &self.ip_sockets { + let socket: &mut smoltcp::socket::RawSocket = + self.socket_set.get_mut(handle.socket_handle).as_socket(); + if socket.can_send() { + post_fevent(&mut self.ip_file, fd, syscall::EVENT_READ, 1)?; + } + } + Ok(()) + } } -impl<'a> SchemeMut for IpHandler<'a> { - +impl<'a> syscall::SchemeMut for IpScheme<'a> { + fn open(&mut self, url: &[u8], flags: usize, uid: u32, _gid: u32) -> syscall::Result { + use std::str; + + if uid != 0 { + return Err(syscall::Error::new(syscall::EACCES)); + } + let path = str::from_utf8(url).or_else(|_| Err(syscall::Error::new(syscall::EINVAL)))?; + let proto = u8::from_str_radix(path, 16).or(Err(syscall::Error::new(syscall::ENOENT)))?; + + let mut rx_packets = Vec::with_capacity(Smolnetd::IP_BUFFER_SIZE); + let mut tx_packets = Vec::with_capacity(Smolnetd::IP_BUFFER_SIZE); + for _ in 0..Smolnetd::IP_BUFFER_SIZE { + rx_packets.push(smoltcp::socket::RawPacketBuffer::new( + vec![0; NetworkDevice::MTU], + )); + tx_packets.push(smoltcp::socket::RawPacketBuffer::new( + vec![0; NetworkDevice::MTU], + )); + } + let rx_buffer = smoltcp::socket::RawSocketBuffer::new(rx_packets); + let tx_buffer = smoltcp::socket::RawSocketBuffer::new(tx_packets); + let raw_socket = smoltcp::socket::RawSocket::new( + smoltcp::wire::IpVersion::Ipv4, + smoltcp::wire::IpProtocol::from(proto), + rx_buffer, + tx_buffer, + ); + + let socket_handle = self.0.socket_set.add(raw_socket); + let id = self.0.next_fd; + trace!("Open {} -> {}", path, id); + + self.0.ip_sockets.insert( + id, + IpHandle { + flags, + events: 0, + socket_handle, + }, + ); + self.0.next_fd += 1; + Ok(id) + } + + fn close(&mut self, fd: usize) -> syscall::Result { + trace!("Close {}", fd); + let socket_handle = { + let handle = self.0 + .ip_sockets + .get(&fd) + .ok_or_else(|| syscall::Error::new(syscall::EBADF))?; + handle.socket_handle + }; + self.0.ip_sockets.remove(&fd); + self.0.socket_set.remove(socket_handle); + Ok(0) + } + + fn write(&mut self, fd: usize, buf: &[u8]) -> syscall::Result { + trace!("Write {} len {}", fd, buf.len()); + + let handle = self.0 + .ip_sockets + .get(&fd) + .ok_or_else(|| syscall::Error::new(syscall::EBADF))?; + let socket: &mut smoltcp::socket::RawSocket = + self.0.socket_set.get_mut(handle.socket_handle).as_socket(); + socket.send_slice(buf).expect("Can't send slice"); + Ok(buf.len()) + } + + fn read(&mut self, fd: usize, buf: &mut [u8]) -> syscall::Result { + trace!("Read {}", fd); + use smoltcp::socket::AsSocket; + + let handle = self.0 + .ip_sockets + .get(&fd) + .ok_or_else(|| syscall::Error::new(syscall::EBADF))?; + let socket: &mut smoltcp::socket::RawSocket = + self.0.socket_set.get_mut(handle.socket_handle).as_socket(); + if socket.can_recv() { + let length = socket.recv_slice(buf).expect("Can't receive slice"); + Ok(length) + } else if handle.flags & syscall::O_NONBLOCK == syscall::O_NONBLOCK { + Ok(0) + } else { + Err(syscall::Error::new(syscall::EWOULDBLOCK)) + } + } + + fn fevent(&mut self, fd: usize, events: usize) -> syscall::Result { + trace!("fevent {}", fd); + let handle = self.0 + .ip_sockets + .get_mut(&fd) + .ok_or_else(|| syscall::Error::new(syscall::EBADF))?; + handle.events = events; + Ok(fd) + } + + fn fsync(&mut self, fd: usize) -> syscall::Result { + trace!("fsync {}", fd); + { + let _handle = self.0 + .ip_sockets + .get_mut(&fd) + .ok_or_else(|| syscall::Error::new(syscall::EBADF))?; + } + self.0.network_fsync() + } +} + +fn post_fevent(scheme_file: &mut File, fd: usize, event: usize, data_len: usize) -> Result<()> { + scheme_file + .write(&syscall::Packet { + id: 0, + pid: 0, + uid: 0, + gid: 0, + a: syscall::number::SYS_FEVENT, + b: fd, + c: event, + d: data_len, + }) + .map(|_| ()) + .map_err(|e| Error::from_io_error(e, "failed to post fevent")) }