From 4058f5c4e8b5ed5eeb50dafc446d11afad207795 Mon Sep 17 00:00:00 2001 From: Egor Karavaev Date: Sun, 17 Sep 2017 00:03:29 +0300 Subject: [PATCH] Udp scheme support. --- Cargo.lock | 6 +- Cargo.toml | 3 +- src/smolnetd/device.rs | 2 +- src/smolnetd/main.rs | 21 +++- src/smolnetd/scheme.rs | 225 ++++++++++++++++++++++++++++++++++++----- 5 files changed, 223 insertions(+), 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7b55db6412..8203619c13 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7,7 +7,7 @@ dependencies = [ "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)", "redox_syscall 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)", - "smoltcp 0.4.0-pre (git+https://github.com/m-labs/smoltcp.git)", + "smoltcp 0.4.0-pre (git+https://github.com/batonius/smoltcp.git?branch=default_route)", ] [[package]] @@ -336,7 +336,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "smoltcp" version = "0.4.0-pre" -source = "git+https://github.com/m-labs/smoltcp.git#9cb813134c88dd41904d409de45818829bd94889" +source = "git+https://github.com/batonius/smoltcp.git?branch=default_route#b64a43d93f3829e0127664b946be116e1f1fc583" 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)", @@ -487,7 +487,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum rustls 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17727f4b991294da2c84d75a43c003151ff58072212768800f66c56ee46dca43" "checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f" "checksum scopeguard 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c79eb2c3ac4bc2507cda80e7f3ac5b88bd8eae4c0914d5663e6a8933994be918" -"checksum smoltcp 0.4.0-pre (git+https://github.com/m-labs/smoltcp.git)" = "" +"checksum smoltcp 0.4.0-pre (git+https://github.com/batonius/smoltcp.git?branch=default_route)" = "" "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" "checksum time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)" = "d5d788d3aa77bc0ef3e9621256885555368b47bd495c13dd2e7413c89f845520" "checksum traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" diff --git a/Cargo.toml b/Cargo.toml index 938eeb57f7..622615f407 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,6 +38,7 @@ default-features = false features = ["release_max_level_off"] [dependencies.smoltcp] -git = "https://github.com/m-labs/smoltcp.git" +git = "https://github.com/batonius/smoltcp.git" +branch = "default_route" default-features = false features = ["std", "log"] diff --git a/src/smolnetd/device.rs b/src/smolnetd/device.rs index b96e8fe185..9ac0705aef 100644 --- a/src/smolnetd/device.rs +++ b/src/smolnetd/device.rs @@ -11,7 +11,7 @@ pub struct NetworkDevice { } impl NetworkDevice { - pub const MTU: usize = 120; + pub const MTU: usize = 1520; pub fn new( network_file: Rc>, diff --git a/src/smolnetd/main.rs b/src/smolnetd/main.rs index d783e52c97..cdf6723b27 100644 --- a/src/smolnetd/main.rs +++ b/src/smolnetd/main.rs @@ -55,20 +55,26 @@ fn run() -> Result<()> { 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; + trace!("opening :udp"); + let udp_fd = syscall::open(":udp", O_RDWR | O_CREAT | O_NONBLOCK) + .map_err(|e| Error::from_syscall_error(e, "failed to open :udp"))? as + RawFd; + 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 { + let (network_file, ip_file, time_file, udp_file) = unsafe { ( File::from_raw_fd(network_fd), File::from_raw_fd(ip_fd), File::from_raw_fd(time_fd), + File::from_raw_fd(udp_fd), ) }; let smolnetd = Rc::new(RefCell::new( - Smolnetd::new(network_file, ip_file, time_file), + Smolnetd::new(network_file, ip_file, udp_file, time_file), )); let mut event_queue = EventQueue::<(), Error>::new() @@ -90,6 +96,17 @@ fn run() -> Result<()> { .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"))?; + let smolnetd_ = smolnetd.clone(); + + event_queue + .add( + udp_fd, + move |_| smolnetd_.borrow_mut().on_udp_scheme_event(), + ) + .map_err(|e| { + Error::from_io_error(e, "failed to listen to udp events") + })?; + event_queue .add(time_fd, move |_| smolnetd.borrow_mut().on_time_event()) .map_err(|e| { diff --git a/src/smolnetd/scheme.rs b/src/smolnetd/scheme.rs index 34a0b2912e..74cc29cdf3 100644 --- a/src/smolnetd/scheme.rs +++ b/src/smolnetd/scheme.rs @@ -12,33 +12,48 @@ use std::time::Instant; use std::rc::Rc; use std::cell::RefCell; use smoltcp::socket::AsSocket; +use syscall::SchemeMut; use syscall; -struct IpHandle { +struct RawHandle { flags: usize, events: usize, socket_handle: smoltcp::socket::SocketHandle, } +struct UdpHandle { + flags: usize, + events: usize, + remote_endpoint: smoltcp::wire::IpEndpoint, + socket_handle: smoltcp::socket::SocketHandle, +} + pub struct Smolnetd { + network_file: Rc>, + ip_file: File, + udp_file: File, + time_file: File, + iface: smoltcp::iface::EthernetInterface<'static, 'static, 'static, NetworkDevice>, 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, + + raw_sockets: BTreeMap, + udp_sockets: BTreeMap, + + input_queue: Rc>>>, } struct IpScheme<'a>(&'a mut Smolnetd); +struct UdpScheme<'a>(&'a mut Smolnetd); impl 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 { + pub fn new(network_file: File, ip_file: File, udp_file: File, time_file: File) -> Smolnetd { let arp_cache = smoltcp::iface::SliceArpCache::new(vec![Default::default(); 8]); //TODO Use smoltcp::wire::EthernetAddress::from_str let mac_addr = MacAddr::from_str(&getcfg("mac").unwrap().trim()); @@ -50,22 +65,31 @@ impl Smolnetd { let protocol_addrs = [ smoltcp::wire::IpAddress::Ipv4(smoltcp::wire::Ipv4Address::from_bytes(&ip_bytes)), ]; + let default_gw = smoltcp::wire::IpAddress::Ipv4(smoltcp::wire::Ipv4Address::from_bytes( + &Ipv4Addr::from_str(&getcfg("ip_router").unwrap().trim()) + .unwrap() + .octets(), + )); 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()); + let mut iface = smoltcp::iface::EthernetInterface::new( + Box::new(network_device), + Box::new(arp_cache) as Box, + hardware_addr, + protocol_addrs, + ); + iface.set_default_gateway(24, Some(default_gw)); Smolnetd { - iface: smoltcp::iface::EthernetInterface::new( - Box::new(network_device), - Box::new(arp_cache) as Box, - hardware_addr, - protocol_addrs, - ), + iface, socket_set: smoltcp::socket::SocketSet::new(vec![]), startup_time: Instant::now(), ip_file, + udp_file, time_file, - ip_sockets: BTreeMap::new(), + raw_sockets: BTreeMap::new(), + udp_sockets: BTreeMap::new(), next_fd: 0, input_queue, network_file, @@ -80,8 +104,6 @@ impl Smolnetd { } 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 { @@ -93,6 +115,18 @@ impl Smolnetd { Ok(None) } + pub fn on_udp_scheme_event(&mut self) -> Result> { + loop { + let mut packet = syscall::Packet::default(); + if self.udp_file.read(&mut packet)? == 0 { + break; + } + UdpScheme(self).handle(&mut packet); + self.udp_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::() { @@ -115,7 +149,7 @@ impl Smolnetd { self.iface .poll(&mut self.socket_set, timestamp) .expect("poll error"); - self.notify_ip_sockets() + self.notify_raw_sockets() } fn read_frames(&mut self) -> Result { @@ -150,8 +184,8 @@ impl Smolnetd { 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 { + fn notify_raw_sockets(&mut self) -> Result<()> { + for (&fd, ref handle) in &self.raw_sockets { let socket: &mut smoltcp::socket::RawSocket = self.socket_set.get_mut(handle.socket_handle).as_socket(); if socket.can_send() { @@ -195,9 +229,9 @@ impl<'a> syscall::SchemeMut for IpScheme<'a> { let id = self.0.next_fd; trace!("Open {} -> {}", path, id); - self.0.ip_sockets.insert( + self.0.raw_sockets.insert( id, - IpHandle { + RawHandle { flags, events: 0, socket_handle, @@ -211,12 +245,12 @@ impl<'a> syscall::SchemeMut for IpScheme<'a> { trace!("Close {}", fd); let socket_handle = { let handle = self.0 - .ip_sockets + .raw_sockets .get(&fd) .ok_or_else(|| syscall::Error::new(syscall::EBADF))?; handle.socket_handle }; - self.0.ip_sockets.remove(&fd); + self.0.raw_sockets.remove(&fd); self.0.socket_set.remove(socket_handle); Ok(0) } @@ -225,7 +259,7 @@ impl<'a> syscall::SchemeMut for IpScheme<'a> { trace!("Write {} len {}", fd, buf.len()); let handle = self.0 - .ip_sockets + .raw_sockets .get(&fd) .ok_or_else(|| syscall::Error::new(syscall::EBADF))?; let socket: &mut smoltcp::socket::RawSocket = @@ -239,7 +273,7 @@ impl<'a> syscall::SchemeMut for IpScheme<'a> { use smoltcp::socket::AsSocket; let handle = self.0 - .ip_sockets + .raw_sockets .get(&fd) .ok_or_else(|| syscall::Error::new(syscall::EBADF))?; let socket: &mut smoltcp::socket::RawSocket = @@ -257,7 +291,7 @@ impl<'a> syscall::SchemeMut for IpScheme<'a> { fn fevent(&mut self, fd: usize, events: usize) -> syscall::Result { trace!("fevent {}", fd); let handle = self.0 - .ip_sockets + .raw_sockets .get_mut(&fd) .ok_or_else(|| syscall::Error::new(syscall::EBADF))?; handle.events = events; @@ -268,7 +302,133 @@ impl<'a> syscall::SchemeMut for IpScheme<'a> { trace!("fsync {}", fd); { let _handle = self.0 - .ip_sockets + .raw_sockets + .get_mut(&fd) + .ok_or_else(|| syscall::Error::new(syscall::EBADF))?; + } + self.0.network_fsync() + } +} + +impl<'a> syscall::SchemeMut for UdpScheme<'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 mut parts = path.split("/"); + let (remote_ip, remote_port) = parse_socket(parts.next().unwrap_or("")); + let (local_ip, local_port) = parse_socket(parts.next().unwrap_or("")); + + 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::UdpPacketBuffer::new( + vec![0; NetworkDevice::MTU], + )); + tx_packets.push(smoltcp::socket::UdpPacketBuffer::new( + vec![0; NetworkDevice::MTU], + )); + } + let rx_buffer = smoltcp::socket::UdpSocketBuffer::new(rx_packets); + let tx_buffer = smoltcp::socket::UdpSocketBuffer::new(tx_packets); + let mut udp_socket = smoltcp::socket::UdpSocket::new(rx_buffer, tx_buffer); + + if !local_ip.is_unspecified() && local_port != 0 { + let udp_socket: &mut smoltcp::socket::UdpSocket = udp_socket.as_socket(); + udp_socket.bind(smoltcp::wire::IpEndpoint::new( + smoltcp::wire::IpAddress::Ipv4( + smoltcp::wire::Ipv4Address::from_bytes(&local_ip.octets()), + ), + local_port, + )).unwrap(); + } + + let socket_handle = self.0.socket_set.add(udp_socket); + let id = self.0.next_fd; + trace!("Open {} -> {}", path, id); + + self.0.udp_sockets.insert( + id, + UdpHandle { + flags, + events: 0, + socket_handle, + remote_endpoint: smoltcp::wire::IpEndpoint::new( + smoltcp::wire::IpAddress::Ipv4( + smoltcp::wire::Ipv4Address::from_bytes(&remote_ip.octets()), + ), + remote_port, + ), + }, + ); + 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 + .udp_sockets + .get(&fd) + .ok_or_else(|| syscall::Error::new(syscall::EBADF))?; + handle.socket_handle + }; + self.0.udp_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 + .udp_sockets + .get(&fd) + .ok_or_else(|| syscall::Error::new(syscall::EBADF))?; + let socket: &mut smoltcp::socket::UdpSocket = + self.0.socket_set.get_mut(handle.socket_handle).as_socket(); + socket.send_slice(buf, handle.remote_endpoint).expect("Can't send slice"); + Ok(buf.len()) + } + + fn read(&mut self, fd: usize, buf: &mut [u8]) -> syscall::Result { + trace!("Read {}", fd); + + let handle = self.0 + .udp_sockets + .get(&fd) + .ok_or_else(|| syscall::Error::new(syscall::EBADF))?; + let socket: &mut smoltcp::socket::UdpSocket = + 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 + .udp_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 + .udp_sockets .get_mut(&fd) .ok_or_else(|| syscall::Error::new(syscall::EBADF))?; } @@ -291,3 +451,14 @@ fn post_fevent(scheme_file: &mut File, fd: usize, event: usize, data_len: usize) .map(|_| ()) .map_err(|e| Error::from_io_error(e, "failed to post fevent")) } + +fn parse_socket(socket: &str) -> (Ipv4Addr, u16) { + let mut socket_parts = socket.split(":"); + let host = Ipv4Addr::from_str(socket_parts.next().unwrap_or("")).unwrap(); + let port = socket_parts + .next() + .unwrap_or("") + .parse::() + .unwrap_or(0); + (host, port) +}