From a7f365d96cb99a8c046c3d60c74a06a745c4b6e9 Mon Sep 17 00:00:00 2001 From: Egor Karavaev Date: Wed, 10 Jan 2018 01:38:23 +0300 Subject: [PATCH 1/5] Add the `netcfg` schema. --- src/smolnetd/main.rs | 19 ++- src/smolnetd/scheme/mod.rs | 78 +++++---- src/smolnetd/scheme/netcfg.rs | 292 ++++++++++++++++++++++++++++++++++ 3 files changed, 355 insertions(+), 34 deletions(-) create mode 100644 src/smolnetd/scheme/netcfg.rs diff --git a/src/smolnetd/main.rs b/src/smolnetd/main.rs index 673a1b02cc..62233cf90d 100644 --- a/src/smolnetd/main.rs +++ b/src/smolnetd/main.rs @@ -52,12 +52,17 @@ fn run() -> Result<()> { .map_err(|e| Error::from_syscall_error(e, "failed to open :icmp"))? as RawFd; + trace!("opening :netcfg"); + let netcfg_fd = syscall::open(":netcfg", O_RDWR | O_CREAT | O_NONBLOCK) + .map_err(|e| Error::from_syscall_error(e, "failed to open :netcfg"))? + 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, udp_file, tcp_file, icmp_file) = unsafe { + let (network_file, ip_file, time_file, udp_file, tcp_file, icmp_file, netcfg_file) = unsafe { ( File::from_raw_fd(network_fd), File::from_raw_fd(ip_fd), @@ -65,6 +70,7 @@ fn run() -> Result<()> { File::from_raw_fd(udp_fd), File::from_raw_fd(tcp_fd), File::from_raw_fd(icmp_fd), + File::from_raw_fd(netcfg_fd), ) }; @@ -75,6 +81,7 @@ fn run() -> Result<()> { tcp_file, icmp_file, time_file, + netcfg_file, ))); let mut event_queue = EventQueue::<(), Error>::new() @@ -120,10 +127,18 @@ fn run() -> Result<()> { }) .map_err(|e| Error::from_io_error(e, "failed to listen to icmp events"))?; + let smolnetd_ = Rc::clone(&smolnetd); + event_queue - .add(time_fd, move |_| smolnetd.borrow_mut().on_time_event()) + .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 + .add(netcfg_fd, move |_| { + smolnetd.borrow_mut().on_netcfg_scheme_event() + }) + .map_err(|e| Error::from_io_error(e, "failed to listen to netcfg events"))?; + event_queue.trigger_all(0)?; event_queue.run() diff --git a/src/smolnetd/scheme/mod.rs b/src/smolnetd/scheme/mod.rs index 6ba011acaf..611e41810d 100644 --- a/src/smolnetd/scheme/mod.rs +++ b/src/smolnetd/scheme/mod.rs @@ -1,10 +1,10 @@ use netutils::getcfg; use smoltcp; -use smoltcp::iface::{NeighborCache, EthernetInterface, EthernetInterfaceBuilder}; +use smoltcp::iface::{EthernetInterface, EthernetInterfaceBuilder, NeighborCache}; use smoltcp::socket::SocketSet as SmoltcpSocketSet; use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, IpEndpoint, Ipv4Address}; use std::cell::RefCell; -use std::collections::{VecDeque, BTreeMap}; +use std::collections::{BTreeMap, VecDeque}; use std::fs::File; use std::io::{Read, Write}; use std::mem::size_of; @@ -21,20 +21,23 @@ use self::ip::IpScheme; use self::tcp::TcpScheme; use self::udp::UdpScheme; use self::icmp::IcmpScheme; +use self::netcfg::NetCfgScheme; mod ip; mod socket; mod tcp; mod udp; mod icmp; +mod netcfg; type SocketSet = SmoltcpSocketSet<'static, 'static, 'static>; +type Interface = Rc>>; pub struct Smolnetd { network_file: Rc>, time_file: File, - iface: EthernetInterface<'static, 'static, NetworkDevice>, + iface: Interface, socket_set: Rc>, startup_time: Instant, @@ -43,6 +46,7 @@ pub struct Smolnetd { udp_scheme: UdpScheme, tcp_scheme: TcpScheme, icmp_scheme: IcmpScheme, + netcfg_scheme: NetCfgScheme, input_queue: Rc>>, buffer_pool: Rc>, @@ -61,6 +65,7 @@ impl Smolnetd { tcp_file: File, icmp_file: File, time_file: File, + netcfg_file: File, ) -> Smolnetd { let hardware_addr = EthernetAddress::from_str(getcfg("mac").unwrap().trim()) .expect("Can't parse the 'mac' cfg"); @@ -83,14 +88,15 @@ impl Smolnetd { Rc::clone(&buffer_pool), ); let iface = EthernetInterfaceBuilder::new(network_device) - .neighbor_cache(NeighborCache::new(BTreeMap::new())) - .ethernet_addr(hardware_addr) - .ip_addrs(protocol_addrs) - .ipv4_gateway(default_gw) - .finalize(); + .neighbor_cache(NeighborCache::new(BTreeMap::new())) + .ethernet_addr(hardware_addr) + .ip_addrs(protocol_addrs) + .ipv4_gateway(default_gw) + .finalize(); + let iface = Rc::new(RefCell::new(iface)); let socket_set = Rc::new(RefCell::new(SocketSet::new(vec![]))); Smolnetd { - iface, + iface: Rc::clone(&iface), socket_set: Rc::clone(&socket_set), startup_time: Instant::now(), time_file, @@ -98,6 +104,7 @@ impl Smolnetd { udp_scheme: UdpScheme::new(Rc::clone(&socket_set), udp_file), tcp_scheme: TcpScheme::new(Rc::clone(&socket_set), tcp_file), icmp_scheme: IcmpScheme::new(Rc::clone(&socket_set), icmp_file), + netcfg_scheme: NetCfgScheme::new(Rc::clone(&iface), netcfg_file), input_queue, network_file, buffer_pool, @@ -141,6 +148,11 @@ impl Smolnetd { Ok(None) } + pub fn on_netcfg_scheme_event(&mut self) -> Result> { + self.netcfg_scheme.on_scheme_event()?; + Ok(None) + } + fn schedule_time_event(&mut self, timeout: i64) -> Result<()> { let mut time = TimeSpec::default(); if self.time_file.read(&mut time)? < size_of::() { @@ -160,30 +172,34 @@ impl Smolnetd { } fn poll(&mut self) -> Result { - let mut iter_limit = 10usize; - let timeout = loop { - iter_limit -= 1; - if iter_limit == 0 { - break 0; - } + let timeout = { + let mut iter_limit = 10usize; + let mut iface = self.iface.borrow_mut(); + let mut socket_set = self.socket_set.borrow_mut(); let timestamp = self.get_timestamp(); - match self.iface.poll(&mut *self.socket_set.borrow_mut(), timestamp) { - Ok(_) => (), - Err(smoltcp::Error::Unrecognized) => (), - Err(e) => { - error!("poll error: {}", e); - break 0 + loop { + if iter_limit == 0 { + break 0; + } + iter_limit -= 1; + match iface.poll(&mut socket_set, timestamp) { + Ok(_) => (), + Err(smoltcp::Error::Unrecognized) => (), + Err(e) => { + error!("poll error: {}", e); + break 0 + } + } + match iface.poll_at(&socket_set, timestamp) { + Some(n) if n > timestamp => { + break ::std::cmp::min(::std::i64::MAX as u64, n - timestamp) as i64 + }, + Some(_) => {}, + None => break ::std::i64::MAX } } - self.notify_sockets()?; - match self.iface.poll_at(&*self.socket_set.borrow(), timestamp) { - Some(n) if n > timestamp => { - break ::std::cmp::min(::std::i64::MAX as u64, n - timestamp) as i64 - }, - Some(_) => {}, - None => break ::std::i64::MAX - } }; + self.notify_sockets()?; Ok(::std::cmp::min( ::std::cmp::max(Smolnetd::MIN_CHECK_TIMEOUT_MS, timeout), Smolnetd::MAX_CHECK_TIMEOUT_MS, @@ -197,9 +213,7 @@ impl Smolnetd { let count = self.network_file .borrow_mut() .read(&mut buffer) - .map_err(|e| { - Error::from_io_error(e, "Failed to read from network file") - })?; + .map_err(|e| Error::from_io_error(e, "Failed to read from network file"))?; if count == 0 { break; } diff --git a/src/smolnetd/scheme/netcfg.rs b/src/smolnetd/scheme/netcfg.rs new file mode 100644 index 0000000000..8e4833fec1 --- /dev/null +++ b/src/smolnetd/scheme/netcfg.rs @@ -0,0 +1,292 @@ +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::fs::File; +use std::io::{Read, Write}; +use std::str; +use std::rc::Rc; +use std::iter::FromIterator; +use syscall::data::Stat; +use syscall::flag::{MODE_DIR, MODE_FILE}; +use syscall::{Error as SyscallError, Packet as SyscallPacket, Result as SyscallResult, SchemeMut}; +use syscall; + +use error::Result; +use super::Interface; + +type CfgNodeRef = Rc>; + +trait CfgNode { + fn is_dir(&self) -> bool { + false + } + + fn is_writable(&self) -> bool { + false + } + + fn is_readable(&self) -> bool { + true + } + + fn read(&self) -> Vec { + vec![] + } + + fn write(&mut self, _buf: &[u8]) -> Option { + None + } + + fn open(&self, _file: &str) -> Option { + None + } + + fn close(&mut self) {} +} + +struct RONode +where + F: Fn() -> Vec, +{ + read_fun: F, +} + +impl CfgNode for RONode +where + F: Fn() -> Vec, +{ + fn read(&self) -> Vec { + (self.read_fun)() + } +} + +impl RONode +where + F: 'static + Fn() -> Vec, +{ + fn new(read_fun: F) -> CfgNodeRef { + Rc::new(RefCell::new(RONode { read_fun })) + } +} + +struct StaticDirNode { + child_nodes: BTreeMap, +} + +impl CfgNode for StaticDirNode { + fn is_dir(&self) -> bool { + true + } + + fn read(&self) -> Vec { + let mut files = vec![]; + for child in self.child_nodes.keys() { + if !files.is_empty() { + files.push(b'\n'); + } + files.extend(child.bytes()); + } + files + } + + fn open(&self, file: &str) -> Option { + self.child_nodes.get(file).map(|node| Rc::clone(node)) + } +} + +impl StaticDirNode { + pub fn new(child_nodes: BTreeMap) -> CfgNodeRef { + Rc::new(RefCell::new(StaticDirNode { child_nodes })) + } +} + +struct RootNode { + route_node: CfgNodeRef, + iface_nodes: BTreeMap, +} + +impl RootNode { + pub fn new(iface: Interface) -> RootNode { + let route_list_node = RONode::new(move || { + let default_route = if let Some(ip) = iface.borrow().ipv4_gateway() { + format!("default via {}\n", ip) + } else { + String::new() + }; + Vec::from_iter(default_route.bytes()) + }); + let mut route_child_nodes = BTreeMap::new(); + route_child_nodes.insert("list".to_owned(), route_list_node); + let route_node = StaticDirNode::new(route_child_nodes); + let iface_nodes = BTreeMap::new(); + // let eth0_node: CfgNodeRef = Rc::new(RefCell::new(IfaceNode::new(iface))); + // iface_nodes.insert("eth0".to_owned(), eth0_node); + RootNode { + route_node, + iface_nodes, + } + } +} + +impl CfgNode for RootNode { + fn is_dir(&self) -> bool { + true + } + + fn open(&self, file: &str) -> Option { + match file { + "route" => Some(Rc::clone(&self.route_node)), + _ => self.iface_nodes.get(file).map(|node| Rc::clone(node)), + } + } + + fn read(&self) -> Vec { + let mut files = vec![]; + files.extend_from_slice(b"route"); + for iface in self.iface_nodes.keys() { + files.push(b'\n'); + files.extend(iface.bytes()); + } + files + } +} + +struct NetCfgFile { + cfg_node: CfgNodeRef, + data: Option>, + pos: usize, + uid: u32, +} + +pub struct NetCfgScheme { + scheme_file: File, + next_fd: usize, + files: BTreeMap, + root_node: CfgNodeRef, +} + +impl NetCfgScheme { + pub fn new(iface: Interface, scheme_file: File) -> NetCfgScheme { + NetCfgScheme { + scheme_file, + next_fd: 1, + files: BTreeMap::new(), + root_node: Rc::new(RefCell::new(RootNode::new(iface))), + } + } + + pub fn on_scheme_event(&mut self) -> Result> { + loop { + let mut packet = SyscallPacket::default(); + if self.scheme_file.read(&mut packet)? == 0 { + break; + } + self.handle(&mut packet); + self.scheme_file.write_all(&packet)?; + } + Ok(None) + } +} + +impl SchemeMut for NetCfgScheme { + fn open(&mut self, url: &[u8], _flags: usize, uid: u32, _gid: u32) -> SyscallResult { + let path = str::from_utf8(url).or_else(|_| Err(SyscallError::new(syscall::EINVAL)))?; + let mut current_node = Rc::clone(&self.root_node); + for part in path.split('/') { + if part.is_empty() { + continue; + } + let next_node = current_node + .borrow_mut() + .open(part) + .ok_or_else(|| SyscallError::new(syscall::EINVAL))?; + current_node = next_node; + } + let fd = self.next_fd; + self.next_fd += 1; + self.files.insert( + fd, + NetCfgFile { + cfg_node: current_node, + uid, + pos: 0, + data: None, + }, + ); + Ok(fd) + } + + fn close(&mut self, fd: usize) -> SyscallResult { + self.files + .get(&fd) + .ok_or_else(|| SyscallError::new(syscall::EBADF))? + .cfg_node + .borrow_mut() + .close(); + self.files.remove(&fd); + Ok(0) + } + + fn write(&mut self, fd: usize, buf: &[u8]) -> SyscallResult { + let file = self.files + .get(&fd) + .ok_or_else(|| SyscallError::new(syscall::EBADF))?; + if file.uid != 0 { + return Err(SyscallError::new(syscall::EACCES)); + } + file.cfg_node + .borrow_mut() + .write(buf) + .ok_or_else(|| SyscallError::new(syscall::EINVAL)) + } + + fn read(&mut self, fd: usize, buf: &mut [u8]) -> SyscallResult { + let file = self.files + .get_mut(&fd) + .ok_or_else(|| SyscallError::new(syscall::EBADF))?; + if file.data.is_none() { + file.data = Some(file.cfg_node.borrow().read()) + } + if let Some(ref data) = file.data { + let mut i = 0; + while i < buf.len() && file.pos < data.len() { + buf[i] = data[file.pos]; + i += 1; + file.pos += 1; + } + return Ok(i); + } + Err(SyscallError::new(syscall::EINVAL)) + } + + fn fstat(&mut self, fd: usize, stat: &mut Stat) -> SyscallResult { + let file = self.files + .get_mut(&fd) + .ok_or_else(|| SyscallError::new(syscall::EBADF))?; + let cfg_node = file.cfg_node.borrow(); + + stat.st_mode = if cfg_node.is_dir() { + MODE_DIR + } else { + MODE_FILE + }; + if cfg_node.is_writable() { + stat.st_mode |= 0o222; + } + if cfg_node.is_readable() { + stat.st_mode |= 0o444; + } + stat.st_uid = 0; + stat.st_gid = 0; + + if file.data.is_none() { + file.data = Some(file.cfg_node.borrow().read()) + } + if let Some(ref data) = file.data { + stat.st_size = data.len() as u64; + } else { + stat.st_size = 0; + } + + Ok(0) + } +} From 1e9580824252aae98d1c09985cbadf6bba873b33 Mon Sep 17 00:00:00 2001 From: Egor Karavaev Date: Thu, 11 Jan 2018 00:57:18 +0300 Subject: [PATCH 2/5] netcfg write nodes --- src/smolnetd/scheme/icmp.rs | 6 +- src/smolnetd/scheme/mod.rs | 8 +- src/smolnetd/scheme/netcfg.rs | 262 +++++++++++++++++++++++----------- src/smolnetd/scheme/socket.rs | 11 +- 4 files changed, 193 insertions(+), 94 deletions(-) diff --git a/src/smolnetd/scheme/icmp.rs b/src/smolnetd/scheme/icmp.rs index bccb75a3b0..cacb9f6605 100644 --- a/src/smolnetd/scheme/icmp.rs +++ b/src/smolnetd/scheme/icmp.rs @@ -92,8 +92,10 @@ impl<'a, 'b> SchemeSocket for IcmpSocket<'a, 'b> { tx_packets.push(IcmpPacketBuffer::new(vec![0; NetworkDevice::MTU])); } - let socket = IcmpSocket::new(IcmpSocketBuffer::new(rx_packets), - IcmpSocketBuffer::new(tx_packets)); + let socket = IcmpSocket::new( + IcmpSocketBuffer::new(rx_packets), + IcmpSocketBuffer::new(tx_packets), + ); let handle = socket_set.add(socket); let mut icmp_socket = socket_set.get::(handle); let ident = ident_set diff --git a/src/smolnetd/scheme/mod.rs b/src/smolnetd/scheme/mod.rs index 611e41810d..9a0d4742ab 100644 --- a/src/smolnetd/scheme/mod.rs +++ b/src/smolnetd/scheme/mod.rs @@ -187,15 +187,15 @@ impl Smolnetd { Err(smoltcp::Error::Unrecognized) => (), Err(e) => { error!("poll error: {}", e); - break 0 + break 0; } } match iface.poll_at(&socket_set, timestamp) { Some(n) if n > timestamp => { break ::std::cmp::min(::std::i64::MAX as u64, n - timestamp) as i64 - }, - Some(_) => {}, - None => break ::std::i64::MAX + } + Some(_) => {} + None => break ::std::i64::MAX, } } }; diff --git a/src/smolnetd/scheme/netcfg.rs b/src/smolnetd/scheme/netcfg.rs index 8e4833fec1..7bf9049657 100644 --- a/src/smolnetd/scheme/netcfg.rs +++ b/src/smolnetd/scheme/netcfg.rs @@ -3,8 +3,10 @@ use std::collections::BTreeMap; use std::fs::File; use std::io::{Read, Write}; use std::str; +use std::str::FromStr; use std::rc::Rc; use std::iter::FromIterator; +use smoltcp::wire::{EthernetAddress, Ipv4Address}; use syscall::data::Stat; use syscall::flag::{MODE_DIR, MODE_FILE}; use syscall::{Error as SyscallError, Packet as SyscallPacket, Result as SyscallResult, SchemeMut}; @@ -13,6 +15,8 @@ use syscall; use error::Result; use super::Interface; +const WRITE_BUFFER_MAX_SIZE: usize = 0xffff; + type CfgNodeRef = Rc>; trait CfgNode { @@ -32,15 +36,13 @@ trait CfgNode { vec![] } - fn write(&mut self, _buf: &[u8]) -> Option { - None + fn write(&self, _buf: &[u8]) -> SyscallResult { + Ok(0) } fn open(&self, _file: &str) -> Option { None } - - fn close(&mut self) {} } struct RONode @@ -68,6 +70,75 @@ where } } +struct WONode +where + F: Fn(&[u8]) -> SyscallResult, +{ + write_fun: F, +} + +impl CfgNode for WONode +where + F: Fn(&[u8]) -> SyscallResult, +{ + fn write(&self, buf: &[u8]) -> SyscallResult { + (self.write_fun)(buf) + } + + fn is_writable(&self) -> bool { + true + } +} + +impl WONode +where + F: 'static + Fn(&[u8]) -> SyscallResult, +{ + fn new(write_fun: F) -> CfgNodeRef { + Rc::new(RefCell::new(WONode { write_fun })) + } +} + +struct RWNode +where + F: Fn() -> Vec, + G: Fn(&[u8]) -> SyscallResult, +{ + read_fun: F, + write_fun: G, +} + +impl CfgNode for RWNode +where + F: Fn() -> Vec, + G: Fn(&[u8]) -> SyscallResult, +{ + fn read(&self) -> Vec { + (self.read_fun)() + } + + fn write(&self, buf: &[u8]) -> SyscallResult { + (self.write_fun)(buf) + } + + fn is_writable(&self) -> bool { + true + } +} + +impl RWNode +where + F: 'static + Fn() -> Vec, + G: 'static + Fn(&[u8]) -> SyscallResult, +{ + fn new(read_fun: F, write_fun: G) -> CfgNodeRef { + Rc::new(RefCell::new(RWNode { + read_fun, + write_fun, + })) + } +} + struct StaticDirNode { child_nodes: BTreeMap, } @@ -99,60 +170,97 @@ impl StaticDirNode { } } -struct RootNode { - route_node: CfgNodeRef, - iface_nodes: BTreeMap, +fn parse_default_gw(buf: &[u8]) -> SyscallResult { + let value = str::from_utf8(buf).or_else(|_| Err(SyscallError::new(syscall::EINVAL)))?; + let mut routes = value.lines(); + if let Some(route) = routes.next() { + if !routes.next().is_none() { + return Err(SyscallError::new(syscall::EINVAL)); + } + let mut words = route.split_whitespace(); + if let Some("default") = words.next() { + if let Some("via") = words.next() { + if let Some(ip) = words.next() { + return Ipv4Address::from_str(ip) + .map_err(|_| SyscallError::new(syscall::EINVAL)); + } + } + } + } + Err(SyscallError::new(syscall::EINVAL)) } -impl RootNode { - pub fn new(iface: Interface) -> RootNode { - let route_list_node = RONode::new(move || { - let default_route = if let Some(ip) = iface.borrow().ipv4_gateway() { - format!("default via {}\n", ip) - } else { - String::new() - }; - Vec::from_iter(default_route.bytes()) - }); - let mut route_child_nodes = BTreeMap::new(); - route_child_nodes.insert("list".to_owned(), route_list_node); - let route_node = StaticDirNode::new(route_child_nodes); - let iface_nodes = BTreeMap::new(); - // let eth0_node: CfgNodeRef = Rc::new(RefCell::new(IfaceNode::new(iface))); - // iface_nodes.insert("eth0".to_owned(), eth0_node); - RootNode { - route_node, - iface_nodes, +fn mk_route_node(iface: &Interface) -> CfgNodeRef { + let iface_ = Rc::clone(iface); + let route_list_node = RONode::new(move || { + let default_route = if let Some(ip) = iface_.borrow().ipv4_gateway() { + format!("default via {}\n", ip) + } else { + String::new() + }; + Vec::from_iter(default_route.bytes()) + }); + let iface_ = Rc::clone(iface); + let route_add_node = WONode::new(move |buf: &[u8]| -> SyscallResult { + let default_gw = parse_default_gw(buf)?; + iface_.borrow_mut().set_ipv4_gateway(Some(default_gw)); + Ok(0) + }); + let iface_ = Rc::clone(iface); + let route_rm_node = WONode::new(move |buf: &[u8]| -> SyscallResult { + let default_gw = parse_default_gw(buf)?; + let mut iface = iface_.borrow_mut(); + if iface.ipv4_gateway() != Some(default_gw) { + return Err(SyscallError::new(syscall::EINVAL)); } - } + iface.set_ipv4_gateway(None); + Ok(0) + }); + let mut route_child_nodes = BTreeMap::new(); + route_child_nodes.insert("list".to_owned(), route_list_node); + route_child_nodes.insert("add".to_owned(), route_add_node); + route_child_nodes.insert("rm".to_owned(), route_rm_node); + StaticDirNode::new(route_child_nodes) } -impl CfgNode for RootNode { - fn is_dir(&self) -> bool { - true - } +fn mk_iface_node(iface: &Interface) -> CfgNodeRef { + let iface_ = Rc::clone(iface); + let iface__ = Rc::clone(iface); + let iface_mac_node = RWNode::new( + move || Vec::from_iter(format!("{}\n", iface_.borrow().ethernet_addr()).bytes()), + move |buf: &[u8]| -> SyscallResult { + let value = str::from_utf8(buf).or_else(|_| Err(SyscallError::new(syscall::EINVAL)))?; + let mac = + EthernetAddress::from_str(value).map_err(|_| SyscallError::new(syscall::EINVAL))?; + if !mac.is_unicast() { + return Err(SyscallError::new(syscall::EINVAL)); + } + iface__.borrow_mut().set_ethernet_addr(mac); + Ok(0) + }, + ); + let mut iface_child_nodes = BTreeMap::new(); + iface_child_nodes.insert("mac".to_owned(), iface_mac_node); + StaticDirNode::new(iface_child_nodes) +} - fn open(&self, file: &str) -> Option { - match file { - "route" => Some(Rc::clone(&self.route_node)), - _ => self.iface_nodes.get(file).map(|node| Rc::clone(node)), - } - } +fn mk_root_node(iface: Interface) -> CfgNodeRef { + let route_node = mk_route_node(&iface); + let mut ifaces_nodes = BTreeMap::new(); - fn read(&self) -> Vec { - let mut files = vec![]; - files.extend_from_slice(b"route"); - for iface in self.iface_nodes.keys() { - files.push(b'\n'); - files.extend(iface.bytes()); - } - files - } + ifaces_nodes.insert("eth0".to_owned(), mk_iface_node(&iface)); + let ifaces_node = StaticDirNode::new(ifaces_nodes); + + let mut root_child_nodes = BTreeMap::new(); + root_child_nodes.insert("route".to_owned(), route_node); + root_child_nodes.insert("ifaces".to_owned(), ifaces_node); + StaticDirNode::new(root_child_nodes) } struct NetCfgFile { cfg_node: CfgNodeRef, - data: Option>, + read_buf: Vec, + write_buf: Vec, pos: usize, uid: u32, } @@ -170,7 +278,7 @@ impl NetCfgScheme { scheme_file, next_fd: 1, files: BTreeMap::new(), - root_node: Rc::new(RefCell::new(RootNode::new(iface))), + root_node: mk_root_node(iface), } } @@ -201,6 +309,7 @@ impl SchemeMut for NetCfgScheme { .ok_or_else(|| SyscallError::new(syscall::EINVAL))?; current_node = next_node; } + let read_buf = current_node.borrow().read(); let fd = self.next_fd; self.next_fd += 1; self.files.insert( @@ -209,53 +318,48 @@ impl SchemeMut for NetCfgScheme { cfg_node: current_node, uid, pos: 0, - data: None, + read_buf, + write_buf: vec![], }, ); Ok(fd) } fn close(&mut self, fd: usize) -> SyscallResult { - self.files - .get(&fd) - .ok_or_else(|| SyscallError::new(syscall::EBADF))? - .cfg_node - .borrow_mut() - .close(); - self.files.remove(&fd); - Ok(0) + let file = self.files + .get_mut(&fd) + .ok_or_else(|| SyscallError::new(syscall::EBADF))?; + file.cfg_node.borrow().write(&file.write_buf) } fn write(&mut self, fd: usize, buf: &[u8]) -> SyscallResult { let file = self.files - .get(&fd) + .get_mut(&fd) .ok_or_else(|| SyscallError::new(syscall::EBADF))?; + if file.uid != 0 { return Err(SyscallError::new(syscall::EACCES)); } - file.cfg_node - .borrow_mut() - .write(buf) - .ok_or_else(|| SyscallError::new(syscall::EINVAL)) + + if (WRITE_BUFFER_MAX_SIZE - file.write_buf.len()) < buf.len() { + return Err(SyscallError::new(syscall::EMSGSIZE)); + } + file.write_buf.extend_from_slice(buf); + Ok(buf.len()) } fn read(&mut self, fd: usize, buf: &mut [u8]) -> SyscallResult { let file = self.files .get_mut(&fd) .ok_or_else(|| SyscallError::new(syscall::EBADF))?; - if file.data.is_none() { - file.data = Some(file.cfg_node.borrow().read()) + + let mut i = 0; + while i < buf.len() && file.pos < file.read_buf.len() { + buf[i] = file.read_buf[file.pos]; + i += 1; + file.pos += 1; } - if let Some(ref data) = file.data { - let mut i = 0; - while i < buf.len() && file.pos < data.len() { - buf[i] = data[file.pos]; - i += 1; - file.pos += 1; - } - return Ok(i); - } - Err(SyscallError::new(syscall::EINVAL)) + Ok(i) } fn fstat(&mut self, fd: usize, stat: &mut Stat) -> SyscallResult { @@ -277,15 +381,7 @@ impl SchemeMut for NetCfgScheme { } stat.st_uid = 0; stat.st_gid = 0; - - if file.data.is_none() { - file.data = Some(file.cfg_node.borrow().read()) - } - if let Some(ref data) = file.data { - stat.st_size = data.len() as u64; - } else { - stat.st_size = 0; - } + stat.st_size = file.read_buf.len() as u64; Ok(0) } diff --git a/src/smolnetd/scheme/socket.rs b/src/smolnetd/scheme/socket.rs index a94f16ac24..d6723964ab 100644 --- a/src/smolnetd/scheme/socket.rs +++ b/src/smolnetd/scheme/socket.rs @@ -84,8 +84,8 @@ where { pub fn socket_handle(&self) -> SocketHandle { match *self { - SchemeFile::Socket(SocketFile { socket_handle, .. }) | - SchemeFile::Setting(SettingFile { socket_handle, .. }) => socket_handle, + SchemeFile::Socket(SocketFile { socket_handle, .. }) + | SchemeFile::Setting(SettingFile { socket_handle, .. }) => socket_handle, } } } @@ -580,9 +580,10 @@ where } fn dup(&mut self, fd: usize, buf: &[u8]) -> SyscallResult { - if let Some((flags, uid, gid)) = self.nulls.get(&fd).map(|null| { - (null.flags, null.uid, null.gid) - }) { + if let Some((flags, uid, gid)) = self.nulls + .get(&fd) + .map(|null| (null.flags, null.uid, null.gid)) + { return self.open(buf, flags, uid, gid); } From ae7d9b40f8e3de3e8889c8a16d5fee4206b03697 Mon Sep 17 00:00:00 2001 From: Egor Karavaev Date: Fri, 12 Jan 2018 01:22:53 +0300 Subject: [PATCH 3/5] netcfg: interface addr node. --- src/smolnetd/scheme/mod.rs | 2 +- src/smolnetd/scheme/netcfg.rs | 264 ++++++++++++++++++++++------------ 2 files changed, 172 insertions(+), 94 deletions(-) diff --git a/src/smolnetd/scheme/mod.rs b/src/smolnetd/scheme/mod.rs index 9a0d4742ab..e03eed4b7b 100644 --- a/src/smolnetd/scheme/mod.rs +++ b/src/smolnetd/scheme/mod.rs @@ -71,7 +71,7 @@ impl Smolnetd { .expect("Can't parse the 'mac' cfg"); let local_ip = IpAddress::from_str(getcfg("ip").unwrap().trim()).expect("Can't parse the 'ip' cfg."); - let protocol_addrs = [ + let protocol_addrs = vec![ IpCidr::new(local_ip, 24), IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8), ]; diff --git a/src/smolnetd/scheme/netcfg.rs b/src/smolnetd/scheme/netcfg.rs index 7bf9049657..d629431fac 100644 --- a/src/smolnetd/scheme/netcfg.rs +++ b/src/smolnetd/scheme/netcfg.rs @@ -1,3 +1,4 @@ +// use managed::ManagedSlice; use std::cell::RefCell; use std::collections::BTreeMap; use std::fs::File; @@ -5,8 +6,7 @@ use std::io::{Read, Write}; use std::str; use std::str::FromStr; use std::rc::Rc; -use std::iter::FromIterator; -use smoltcp::wire::{EthernetAddress, Ipv4Address}; +use smoltcp::wire::{EthernetAddress, IpCidr, Ipv4Address}; use syscall::data::Stat; use syscall::flag::{MODE_DIR, MODE_FILE}; use syscall::{Error as SyscallError, Packet as SyscallPacket, Result as SyscallResult, SchemeMut}; @@ -32,11 +32,11 @@ trait CfgNode { true } - fn read(&self) -> Vec { - vec![] + fn read(&self) -> String { + String::new() } - fn write(&self, _buf: &[u8]) -> SyscallResult { + fn write(&self, _buf: &str) -> SyscallResult { Ok(0) } @@ -47,23 +47,23 @@ trait CfgNode { struct RONode where - F: Fn() -> Vec, + F: Fn() -> String, { read_fun: F, } impl CfgNode for RONode where - F: Fn() -> Vec, + F: Fn() -> String, { - fn read(&self) -> Vec { + fn read(&self) -> String { (self.read_fun)() } } impl RONode where - F: 'static + Fn() -> Vec, + F: 'static + Fn() -> String, { fn new(read_fun: F) -> CfgNodeRef { Rc::new(RefCell::new(RONode { read_fun })) @@ -72,16 +72,16 @@ where struct WONode where - F: Fn(&[u8]) -> SyscallResult, + F: Fn(&str) -> SyscallResult, { write_fun: F, } impl CfgNode for WONode where - F: Fn(&[u8]) -> SyscallResult, + F: Fn(&str) -> SyscallResult, { - fn write(&self, buf: &[u8]) -> SyscallResult { + fn write(&self, buf: &str) -> SyscallResult { (self.write_fun)(buf) } @@ -92,7 +92,7 @@ where impl WONode where - F: 'static + Fn(&[u8]) -> SyscallResult, + F: 'static + Fn(&str) -> SyscallResult, { fn new(write_fun: F) -> CfgNodeRef { Rc::new(RefCell::new(WONode { write_fun })) @@ -101,8 +101,8 @@ where struct RWNode where - F: Fn() -> Vec, - G: Fn(&[u8]) -> SyscallResult, + F: Fn() -> String, + G: Fn(&str) -> SyscallResult, { read_fun: F, write_fun: G, @@ -110,14 +110,14 @@ where impl CfgNode for RWNode where - F: Fn() -> Vec, - G: Fn(&[u8]) -> SyscallResult, + F: Fn() -> String, + G: Fn(&str) -> SyscallResult, { - fn read(&self) -> Vec { + fn read(&self) -> String { (self.read_fun)() } - fn write(&self, buf: &[u8]) -> SyscallResult { + fn write(&self, buf: &str) -> SyscallResult { (self.write_fun)(buf) } @@ -128,8 +128,8 @@ where impl RWNode where - F: 'static + Fn() -> Vec, - G: 'static + Fn(&[u8]) -> SyscallResult, + F: 'static + Fn() -> String, + G: 'static + Fn(&str) -> SyscallResult, { fn new(read_fun: F, write_fun: G) -> CfgNodeRef { Rc::new(RefCell::new(RWNode { @@ -148,13 +148,13 @@ impl CfgNode for StaticDirNode { true } - fn read(&self) -> Vec { - let mut files = vec![]; + fn read(&self) -> String { + let mut files = String::new(); for child in self.child_nodes.keys() { if !files.is_empty() { - files.push(b'\n'); + files.push('\n'); } - files.extend(child.bytes()); + files += child; } files } @@ -170,8 +170,45 @@ impl StaticDirNode { } } -fn parse_default_gw(buf: &[u8]) -> SyscallResult { - let value = str::from_utf8(buf).or_else(|_| Err(SyscallError::new(syscall::EINVAL)))?; +macro_rules! cfg_node { + (val $e:expr) => { + $e + }; + (ro [ $($c:ident)* ] || $b:block ) => { + { + $(let $c = $c.clone();)* + RONode::new(move|| $b) + } + }; + (wo [ $($c:ident)* ] |$i:ident| $b:block ) => { + { + $(let $c = $c.clone();)* + WONode::new(move |$i: &str| $b) + } + }; + (rw [ $($c:ident)* ] || $rb:block |$i:ident| $wb:block ) => { + { + let read_fun = { + $(let $c = $c.clone();)* + move || $rb + }; + let write_fun = { + $(let $c = $c.clone();)* + move |$i: &str| $wb + }; + RWNode::new(read_fun, write_fun) + } + }; + ($($e:expr => { $($t:tt)* }),* $(,)*) => { + { + let mut children = BTreeMap::new(); + $(children.insert($e.into(), cfg_node!($($t)*));)* + StaticDirNode::new(children) + } + }; +} + +fn parse_default_gw(value: &str) -> SyscallResult { let mut routes = value.lines(); if let Some(route) = routes.next() { if !routes.next().is_none() { @@ -190,71 +227,110 @@ fn parse_default_gw(buf: &[u8]) -> SyscallResult { Err(SyscallError::new(syscall::EINVAL)) } -fn mk_route_node(iface: &Interface) -> CfgNodeRef { - let iface_ = Rc::clone(iface); - let route_list_node = RONode::new(move || { - let default_route = if let Some(ip) = iface_.borrow().ipv4_gateway() { - format!("default via {}\n", ip) - } else { - String::new() - }; - Vec::from_iter(default_route.bytes()) - }); - let iface_ = Rc::clone(iface); - let route_add_node = WONode::new(move |buf: &[u8]| -> SyscallResult { - let default_gw = parse_default_gw(buf)?; - iface_.borrow_mut().set_ipv4_gateway(Some(default_gw)); - Ok(0) - }); - let iface_ = Rc::clone(iface); - let route_rm_node = WONode::new(move |buf: &[u8]| -> SyscallResult { - let default_gw = parse_default_gw(buf)?; - let mut iface = iface_.borrow_mut(); - if iface.ipv4_gateway() != Some(default_gw) { - return Err(SyscallError::new(syscall::EINVAL)); - } - iface.set_ipv4_gateway(None); - Ok(0) - }); - let mut route_child_nodes = BTreeMap::new(); - route_child_nodes.insert("list".to_owned(), route_list_node); - route_child_nodes.insert("add".to_owned(), route_add_node); - route_child_nodes.insert("rm".to_owned(), route_rm_node); - StaticDirNode::new(route_child_nodes) -} - -fn mk_iface_node(iface: &Interface) -> CfgNodeRef { - let iface_ = Rc::clone(iface); - let iface__ = Rc::clone(iface); - let iface_mac_node = RWNode::new( - move || Vec::from_iter(format!("{}\n", iface_.borrow().ethernet_addr()).bytes()), - move |buf: &[u8]| -> SyscallResult { - let value = str::from_utf8(buf).or_else(|_| Err(SyscallError::new(syscall::EINVAL)))?; - let mac = - EthernetAddress::from_str(value).map_err(|_| SyscallError::new(syscall::EINVAL))?; - if !mac.is_unicast() { - return Err(SyscallError::new(syscall::EINVAL)); - } - iface__.borrow_mut().set_ethernet_addr(mac); - Ok(0) - }, - ); - let mut iface_child_nodes = BTreeMap::new(); - iface_child_nodes.insert("mac".to_owned(), iface_mac_node); - StaticDirNode::new(iface_child_nodes) -} - fn mk_root_node(iface: Interface) -> CfgNodeRef { - let route_node = mk_route_node(&iface); - let mut ifaces_nodes = BTreeMap::new(); - - ifaces_nodes.insert("eth0".to_owned(), mk_iface_node(&iface)); - let ifaces_node = StaticDirNode::new(ifaces_nodes); - - let mut root_child_nodes = BTreeMap::new(); - root_child_nodes.insert("route".to_owned(), route_node); - root_child_nodes.insert("ifaces".to_owned(), ifaces_node); - StaticDirNode::new(root_child_nodes) + cfg_node!{ + "route" => { + "list" => { + ro [iface] || { + if let Some(ip) = iface.borrow().ipv4_gateway() { + format!("default via {}\n", ip) + } else { + String::new() + } + } + }, + "add" => { + wo [iface] |routes| { + let default_gw = parse_default_gw(routes)?; + iface.borrow_mut().set_ipv4_gateway(Some(default_gw)); + Ok(0) + } + }, + "rm" => { + wo [iface] |routes| { + let default_gw = parse_default_gw(routes)?; + let mut iface = iface.borrow_mut(); + if iface.ipv4_gateway() != Some(default_gw) { + return Err(SyscallError::new(syscall::EINVAL)); + } + iface.set_ipv4_gateway(None); + Ok(0) + } + } + }, + "ifaces" => { + "eth0" => { + "mac" => { + rw [iface] + || { + format!("{}\n", iface.borrow().ethernet_addr()) + } + |mac| { + let mac = mac.lines().next() + .ok_or_else(|| SyscallError::new(syscall::EINVAL))?; + let mac = EthernetAddress::from_str(mac). + map_err(|_| SyscallError::new(syscall::EINVAL))?; + if !mac.is_unicast() { + return Err(SyscallError::new(syscall::EINVAL)); + } + iface.borrow_mut().set_ethernet_addr(mac); + Ok(0) + } + }, + "addr" => { + "list" => { + ro [iface] || { + let mut ips = String::new(); + for cidr in iface.borrow().ip_addrs() { + ips += &format!("{}\n", cidr); + } + ips + } + }, + "add" => { + wo [iface] |input| { + let mut iface = iface.borrow_mut(); + let mut cidrs = iface.ip_addrs().iter().cloned().collect::>(); + for cidr in input.lines() { + let cidr = IpCidr::from_str(cidr) + .map_err(|_| SyscallError::new(syscall::EINVAL))?; + if !cidr.address().is_unicast() { + return Err(SyscallError::new(syscall::EINVAL)); + } + cidrs.insert(0, cidr); + } + iface.update_ip_addrs(|s| { + *s = From::from(cidrs); + }); + Ok(0) + } + }, + "rm" => { + wo [iface] |input| { + let mut iface = iface.borrow_mut(); + let mut cidrs = iface.ip_addrs().iter().cloned().collect::>(); + for cidr in input.lines() { + let cidr = IpCidr::from_str(cidr) + .map_err(|_| SyscallError::new(syscall::EINVAL))?; + if !cidr.address().is_unicast() { + return Err(SyscallError::new(syscall::EINVAL)); + } + let pre_retain_len = cidrs.len(); + cidrs.retain(|&c| c != cidr); + if pre_retain_len == cidrs.len() { + return Err(SyscallError::new(syscall::EINVAL)); + } + } + iface.update_ip_addrs(|s| { + *s = From::from(cidrs); + }); + Ok(0) + } + }, + } + } + } + } } struct NetCfgFile { @@ -309,7 +385,7 @@ impl SchemeMut for NetCfgScheme { .ok_or_else(|| SyscallError::new(syscall::EINVAL))?; current_node = next_node; } - let read_buf = current_node.borrow().read(); + let read_buf = Vec::from(current_node.borrow().read()); let fd = self.next_fd; self.next_fd += 1; self.files.insert( @@ -329,7 +405,9 @@ impl SchemeMut for NetCfgScheme { let file = self.files .get_mut(&fd) .ok_or_else(|| SyscallError::new(syscall::EBADF))?; - file.cfg_node.borrow().write(&file.write_buf) + let value = + str::from_utf8(&file.write_buf).or_else(|_| Err(SyscallError::new(syscall::EINVAL)))?; + file.cfg_node.borrow().write(&value) } fn write(&mut self, fd: usize, buf: &[u8]) -> SyscallResult { From eea72fcef3b616e9c1b3bd1cd18bc1ca0a991945 Mon Sep 17 00:00:00 2001 From: Egor Karavaev Date: Fri, 12 Jan 2018 22:10:46 +0300 Subject: [PATCH 4/5] Remove closed fd. --- src/smolnetd/scheme/netcfg.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/smolnetd/scheme/netcfg.rs b/src/smolnetd/scheme/netcfg.rs index d629431fac..22a7479a88 100644 --- a/src/smolnetd/scheme/netcfg.rs +++ b/src/smolnetd/scheme/netcfg.rs @@ -402,12 +402,13 @@ impl SchemeMut for NetCfgScheme { } fn close(&mut self, fd: usize) -> SyscallResult { - let file = self.files - .get_mut(&fd) - .ok_or_else(|| SyscallError::new(syscall::EBADF))?; - let value = - str::from_utf8(&file.write_buf).or_else(|_| Err(SyscallError::new(syscall::EINVAL)))?; - file.cfg_node.borrow().write(&value) + if let Some(file) = self.files.remove(&fd) { + let value = str::from_utf8(&file.write_buf) + .or_else(|_| Err(SyscallError::new(syscall::EINVAL)))?; + file.cfg_node.borrow().write(&value) + } else { + Err(SyscallError::new(syscall::EBADF)) + } } fn write(&mut self, fd: usize, buf: &[u8]) -> SyscallResult { From 1eeec8eae0e66400d92dd28f0a6101c77f025043 Mon Sep 17 00:00:00 2001 From: Egor Karavaev Date: Tue, 30 Jan 2018 00:47:49 +0300 Subject: [PATCH 5/5] Netcfg notifications. --- src/dnsd/main.rs | 19 +- src/dnsd/scheme.rs | 52 ++- src/lib/logger.rs | 2 +- src/smolnetd/scheme/mod.rs | 3 +- .../scheme/{netcfg.rs => netcfg/mod.rs} | 297 ++++++------------ src/smolnetd/scheme/netcfg/nodes.rs | 199 ++++++++++++ src/smolnetd/scheme/netcfg/notifier.rs | 62 ++++ src/smolnetd/scheme/tcp.rs | 8 +- 8 files changed, 412 insertions(+), 230 deletions(-) rename src/smolnetd/scheme/{netcfg.rs => netcfg/mod.rs} (66%) create mode 100644 src/smolnetd/scheme/netcfg/nodes.rs create mode 100644 src/smolnetd/scheme/netcfg/notifier.rs diff --git a/src/dnsd/main.rs b/src/dnsd/main.rs index 73babd05fd..6a1b67c9ef 100644 --- a/src/dnsd/main.rs +++ b/src/dnsd/main.rs @@ -31,7 +31,18 @@ fn run() -> Result<()> { .map_err(|e| Error::from_syscall_error(e, "failed to open time:"))? as RawFd; - let (dns_file, time_file) = unsafe { (File::from_raw_fd(dns_fd), File::from_raw_fd(time_fd)) }; + let nameserver_fd = syscall::open( + "netcfg:resolv/nameserver", + syscall::O_RDWR | syscall::O_CREAT | syscall::O_NONBLOCK, + ).map_err(|e| Error::from_syscall_error(e, "failed to open nameserver:"))? + as RawFd; + + let (dns_file, time_file) = unsafe { + ( + File::from_raw_fd(dns_fd), + File::from_raw_fd(time_fd), + ) + }; let dnsd = Rc::new(RefCell::new(Dnsd::new(dns_file, time_file))); @@ -46,6 +57,12 @@ fn run() -> Result<()> { let dnsd_ = Rc::clone(&dnsd); + event_queue + .add(nameserver_fd, move |_| dnsd_.borrow_mut().on_nameserver_event()) + .map_err(|e| Error::from_io_error(e, "failed to listen to nameserver"))?; + + let dnsd_ = Rc::clone(&dnsd); + event_queue.set_default_callback(move |fd, _| dnsd_.borrow_mut().on_unknown_fd_event(fd)); event_queue diff --git a/src/dnsd/scheme.rs b/src/dnsd/scheme.rs index 3be32c1d8d..d32e562736 100644 --- a/src/dnsd/scheme.rs +++ b/src/dnsd/scheme.rs @@ -9,7 +9,9 @@ use std::io::{Read, Write}; use std::mem; use std::os::unix::io::RawFd; use std::str; +use std::str::FromStr; use std::rc::Rc; +use std::net::Ipv4Addr; use syscall::data::TimeSpec; use syscall::{Error as SyscallError, Packet as SyscallPacket, Result as SyscallResult, SchemeMut}; use syscall; @@ -40,6 +42,7 @@ enum DnsParsingResult { } struct Domains { + nameserver: Ipv4Addr, domains: BTreeMap, Domain>, requests: BTreeMap>, resolved_timeouts: VecDeque<(TimeSpec, Rc)>, @@ -48,11 +51,28 @@ struct Domains { impl Domains { fn new() -> Domains { - Domains { + let mut domains = Domains { + nameserver: Ipv4Addr::new(8, 8, 8, 8), domains: BTreeMap::new(), requests: BTreeMap::new(), resolved_timeouts: VecDeque::new(), requested_timeouts: VecDeque::new(), + }; + domains.update_nameserver(); + domains + } + + pub fn update_nameserver(&mut self) { + if let Ok(mut file) = File::open("netcfg:resolv/nameserver") { + let mut nameserver = String::new(); + if let Ok(_) = file.read_to_string(&mut nameserver) { + if let Some(line) = nameserver.lines().next() { + if let Ok(ip) = Ipv4Addr::from_str(&line) { + trace!("Changing nameserver to {}", ip); + self.nameserver = ip; + } + } + } } } @@ -62,7 +82,7 @@ impl Domains { builder.add_question(domain, QueryType::A, QueryClass::IN); let packet = builder.build().ok()?; let udp_fd = syscall::open( - "udp:8.8.8.8:53", + &format!("udp:{}:53", self.nameserver), syscall::O_RDWR | syscall::O_CREAT | syscall::O_NONBLOCK, ).ok()? as RawFd; if syscall::write(udp_fd as usize, &packet) != Ok(packet.len()) { @@ -131,13 +151,13 @@ impl Domains { } Entry::Occupied(e) => e, }; - let mut buf = [0u8; 4096]; + let mut buf = [0u8; 0x1000]; let readed = syscall::read(fd as usize, &mut buf).ok()?; if readed == 0 { return None; } let pkt = DNSPacket::parse(&buf).ok()?; - if pkt.header.response_code != ResponseCode::NoError || pkt.answers.len() == 0 { + if pkt.header.response_code != ResponseCode::NoError || pkt.answers.is_empty() { if let Some(query) = pkt.questions.iter().next() { if query.qname.to_string().to_lowercase() == e.get().as_ref() { unsubscribe_from_fd(fd).ok()?; @@ -290,11 +310,11 @@ impl Dnsd { let fds_to_wakeup = self.domains.on_time_event(&time); if !fds_to_wakeup.is_empty() { for fd in &fds_to_wakeup { - if let Some(file) = self.files.get_mut(&fd) { + if let Some(file) = self.files.get_mut(fd) { *file = DnsFile::Timeout; } } - self.wakeup_fds(fds_to_wakeup); + self.wakeup_fds(&fds_to_wakeup); } time.tv_sec += Dnsd::TIME_EVENT_TIMEOUT_S; @@ -323,6 +343,7 @@ impl Dnsd { } pub fn on_unknown_fd_event(&mut self, fd: RawFd) -> Result> { + trace!("Unknown fd event {}", fd); let mut cur_time = TimeSpec::default(); syscall::clock_gettime(syscall::CLOCK_MONOTONIC, &mut cur_time) .map_err(|e| Error::from_syscall_error(e, "Can't get time"))?; @@ -330,24 +351,29 @@ impl Dnsd { match self.domains.on_fd_event(fd, &cur_time) { Some(DnsParsingResult::FailFiles(fds_to_fail)) => { for fd in &fds_to_fail { - if let Some(file) = self.files.get_mut(&fd) { + if let Some(file) = self.files.get_mut(fd) { *file = DnsFile::Failed; } } - self.wakeup_fds(fds_to_fail); + self.wakeup_fds(&fds_to_fail); } Some(DnsParsingResult::WakeUpFiles(fds_to_wakeup)) => { - self.wakeup_fds(fds_to_wakeup); + self.wakeup_fds(&fds_to_wakeup); } None => {} } Ok(None) } - fn wakeup_fds(&mut self, fds_to_wakeup: BTreeSet) { + pub fn on_nameserver_event(&mut self) -> Result> { + self.domains.update_nameserver(); + Ok(None) + } + + fn wakeup_fds(&mut self, fds_to_wakeup: &BTreeSet) { let mut syscall_packets = vec![]; - for fd in &fds_to_wakeup { - if let Some(packet) = self.wait_map.remove(&fd) { + for fd in fds_to_wakeup { + if let Some(packet) = self.wait_map.remove(fd) { syscall_packets.push(packet); } } @@ -375,7 +401,7 @@ impl Dnsd { } } - return true; + true } } diff --git a/src/lib/logger.rs b/src/lib/logger.rs index 0fd36d9b1b..90ed1f8306 100644 --- a/src/lib/logger.rs +++ b/src/lib/logger.rs @@ -8,7 +8,7 @@ impl Log for Logger { } fn log(&self, record: &LogRecord) { - println!("{}: {}", record.level(), record.args()); + println!("{}: {}", record.target(), record.args()); } } diff --git a/src/smolnetd/scheme/mod.rs b/src/smolnetd/scheme/mod.rs index e03eed4b7b..22ad873c48 100644 --- a/src/smolnetd/scheme/mod.rs +++ b/src/smolnetd/scheme/mod.rs @@ -183,8 +183,7 @@ impl Smolnetd { } iter_limit -= 1; match iface.poll(&mut socket_set, timestamp) { - Ok(_) => (), - Err(smoltcp::Error::Unrecognized) => (), + Ok(_) | Err(smoltcp::Error::Unrecognized) => (), Err(e) => { error!("poll error: {}", e); break 0; diff --git a/src/smolnetd/scheme/netcfg.rs b/src/smolnetd/scheme/netcfg/mod.rs similarity index 66% rename from src/smolnetd/scheme/netcfg.rs rename to src/smolnetd/scheme/netcfg/mod.rs index 22a7479a88..a77a1a5d20 100644 --- a/src/smolnetd/scheme/netcfg.rs +++ b/src/smolnetd/scheme/netcfg/mod.rs @@ -1,213 +1,27 @@ -// use managed::ManagedSlice; +#[macro_use] +mod nodes; +mod notifier; + +use smoltcp::wire::{EthernetAddress, IpCidr, Ipv4Address}; use std::cell::RefCell; use std::collections::BTreeMap; use std::fs::File; use std::io::{Read, Write}; -use std::str; -use std::str::FromStr; use std::rc::Rc; -use smoltcp::wire::{EthernetAddress, IpCidr, Ipv4Address}; +use std::str::FromStr; +use std::str; use syscall::data::Stat; use syscall::flag::{MODE_DIR, MODE_FILE}; use syscall::{Error as SyscallError, Packet as SyscallPacket, Result as SyscallResult, SchemeMut}; use syscall; -use error::Result; -use super::Interface; +use self::nodes::*; +use self::notifier::*; +use redox_netstack::error::Result; +use super::{post_fevent, Interface}; const WRITE_BUFFER_MAX_SIZE: usize = 0xffff; -type CfgNodeRef = Rc>; - -trait CfgNode { - fn is_dir(&self) -> bool { - false - } - - fn is_writable(&self) -> bool { - false - } - - fn is_readable(&self) -> bool { - true - } - - fn read(&self) -> String { - String::new() - } - - fn write(&self, _buf: &str) -> SyscallResult { - Ok(0) - } - - fn open(&self, _file: &str) -> Option { - None - } -} - -struct RONode -where - F: Fn() -> String, -{ - read_fun: F, -} - -impl CfgNode for RONode -where - F: Fn() -> String, -{ - fn read(&self) -> String { - (self.read_fun)() - } -} - -impl RONode -where - F: 'static + Fn() -> String, -{ - fn new(read_fun: F) -> CfgNodeRef { - Rc::new(RefCell::new(RONode { read_fun })) - } -} - -struct WONode -where - F: Fn(&str) -> SyscallResult, -{ - write_fun: F, -} - -impl CfgNode for WONode -where - F: Fn(&str) -> SyscallResult, -{ - fn write(&self, buf: &str) -> SyscallResult { - (self.write_fun)(buf) - } - - fn is_writable(&self) -> bool { - true - } -} - -impl WONode -where - F: 'static + Fn(&str) -> SyscallResult, -{ - fn new(write_fun: F) -> CfgNodeRef { - Rc::new(RefCell::new(WONode { write_fun })) - } -} - -struct RWNode -where - F: Fn() -> String, - G: Fn(&str) -> SyscallResult, -{ - read_fun: F, - write_fun: G, -} - -impl CfgNode for RWNode -where - F: Fn() -> String, - G: Fn(&str) -> SyscallResult, -{ - fn read(&self) -> String { - (self.read_fun)() - } - - fn write(&self, buf: &str) -> SyscallResult { - (self.write_fun)(buf) - } - - fn is_writable(&self) -> bool { - true - } -} - -impl RWNode -where - F: 'static + Fn() -> String, - G: 'static + Fn(&str) -> SyscallResult, -{ - fn new(read_fun: F, write_fun: G) -> CfgNodeRef { - Rc::new(RefCell::new(RWNode { - read_fun, - write_fun, - })) - } -} - -struct StaticDirNode { - child_nodes: BTreeMap, -} - -impl CfgNode for StaticDirNode { - fn is_dir(&self) -> bool { - true - } - - fn read(&self) -> String { - let mut files = String::new(); - for child in self.child_nodes.keys() { - if !files.is_empty() { - files.push('\n'); - } - files += child; - } - files - } - - fn open(&self, file: &str) -> Option { - self.child_nodes.get(file).map(|node| Rc::clone(node)) - } -} - -impl StaticDirNode { - pub fn new(child_nodes: BTreeMap) -> CfgNodeRef { - Rc::new(RefCell::new(StaticDirNode { child_nodes })) - } -} - -macro_rules! cfg_node { - (val $e:expr) => { - $e - }; - (ro [ $($c:ident)* ] || $b:block ) => { - { - $(let $c = $c.clone();)* - RONode::new(move|| $b) - } - }; - (wo [ $($c:ident)* ] |$i:ident| $b:block ) => { - { - $(let $c = $c.clone();)* - WONode::new(move |$i: &str| $b) - } - }; - (rw [ $($c:ident)* ] || $rb:block |$i:ident| $wb:block ) => { - { - let read_fun = { - $(let $c = $c.clone();)* - move || $rb - }; - let write_fun = { - $(let $c = $c.clone();)* - move |$i: &str| $wb - }; - RWNode::new(read_fun, write_fun) - } - }; - ($($e:expr => { $($t:tt)* }),* $(,)*) => { - { - let mut children = BTreeMap::new(); - $(children.insert($e.into(), cfg_node!($($t)*));)* - StaticDirNode::new(children) - } - }; -} - fn parse_default_gw(value: &str) -> SyscallResult { let mut routes = value.lines(); if let Some(route) = routes.next() { @@ -227,8 +41,26 @@ fn parse_default_gw(value: &str) -> SyscallResult { Err(SyscallError::new(syscall::EINVAL)) } -fn mk_root_node(iface: Interface) -> CfgNodeRef { +fn mk_root_node(iface: Interface, notifier: NotifierRef, dns_config: DNSConfigRef) -> CfgNodeRef { cfg_node!{ + "resolv" => { + "nameserver" => { + rw [dns_config, notifier] + || { + format!("{}\n", dns_config.borrow().name_server) + } + |name_server| { + let ip = Ipv4Address::from_str(name_server.trim()) + .map_err(|_| SyscallError::new(syscall::EINVAL))?; + if !ip.is_unicast() { + return Err(SyscallError::new(syscall::EINVAL)); + } + dns_config.borrow_mut().name_server = ip; + notifier.borrow_mut().schedule_notify("resolv/nameserver"); + Ok(0) + } + } + }, "route" => { "list" => { ro [iface] || { @@ -240,20 +72,22 @@ fn mk_root_node(iface: Interface) -> CfgNodeRef { } }, "add" => { - wo [iface] |routes| { + wo [iface, notifier] |routes| { let default_gw = parse_default_gw(routes)?; iface.borrow_mut().set_ipv4_gateway(Some(default_gw)); + notifier.borrow_mut().schedule_notify("route/list"); Ok(0) } }, "rm" => { - wo [iface] |routes| { + wo [iface, notifier] |routes| { let default_gw = parse_default_gw(routes)?; let mut iface = iface.borrow_mut(); if iface.ipv4_gateway() != Some(default_gw) { return Err(SyscallError::new(syscall::EINVAL)); } iface.set_ipv4_gateway(None); + notifier.borrow_mut().schedule_notify("route/list"); Ok(0) } } @@ -261,7 +95,7 @@ fn mk_root_node(iface: Interface) -> CfgNodeRef { "ifaces" => { "eth0" => { "mac" => { - rw [iface] + rw [iface, notifier] || { format!("{}\n", iface.borrow().ethernet_addr()) } @@ -274,6 +108,7 @@ fn mk_root_node(iface: Interface) -> CfgNodeRef { return Err(SyscallError::new(syscall::EINVAL)); } iface.borrow_mut().set_ethernet_addr(mac); + notifier.borrow_mut().schedule_notify("ifaces/eth0/mac"); Ok(0) } }, @@ -288,9 +123,9 @@ fn mk_root_node(iface: Interface) -> CfgNodeRef { } }, "add" => { - wo [iface] |input| { + wo [iface, notifier] |input| { let mut iface = iface.borrow_mut(); - let mut cidrs = iface.ip_addrs().iter().cloned().collect::>(); + let mut cidrs = iface.ip_addrs().to_vec(); for cidr in input.lines() { let cidr = IpCidr::from_str(cidr) .map_err(|_| SyscallError::new(syscall::EINVAL))?; @@ -302,13 +137,14 @@ fn mk_root_node(iface: Interface) -> CfgNodeRef { iface.update_ip_addrs(|s| { *s = From::from(cidrs); }); + notifier.borrow_mut().schedule_notify("ifaces/eth0/addr/list"); Ok(0) } }, "rm" => { - wo [iface] |input| { + wo [iface, notifier] |input| { let mut iface = iface.borrow_mut(); - let mut cidrs = iface.ip_addrs().iter().cloned().collect::>(); + let mut cidrs = iface.ip_addrs().to_vec(); for cidr in input.lines() { let cidr = IpCidr::from_str(cidr) .map_err(|_| SyscallError::new(syscall::EINVAL))?; @@ -324,6 +160,7 @@ fn mk_root_node(iface: Interface) -> CfgNodeRef { iface.update_ip_addrs(|s| { *s = From::from(cidrs); }); + notifier.borrow_mut().schedule_notify("ifaces/eth0/addr/list"); Ok(0) } }, @@ -333,7 +170,14 @@ fn mk_root_node(iface: Interface) -> CfgNodeRef { } } +struct DNSConfig { + name_server: Ipv4Address, +} + +type DNSConfigRef = Rc>; + struct NetCfgFile { + path: String, cfg_node: CfgNodeRef, read_buf: Vec, write_buf: Vec, @@ -346,15 +190,21 @@ pub struct NetCfgScheme { next_fd: usize, files: BTreeMap, root_node: CfgNodeRef, + notifier: NotifierRef, } impl NetCfgScheme { pub fn new(iface: Interface, scheme_file: File) -> NetCfgScheme { + let notifier = Notifier::new_ref(); + let dns_config = Rc::new(RefCell::new(DNSConfig { + name_server: Ipv4Address::new(8, 8, 8, 8), + })); NetCfgScheme { scheme_file, next_fd: 1, files: BTreeMap::new(), - root_node: mk_root_node(iface), + root_node: mk_root_node(iface, Rc::clone(¬ifier), dns_config), + notifier, } } @@ -367,8 +217,16 @@ impl NetCfgScheme { self.handle(&mut packet); self.scheme_file.write_all(&packet)?; } + self.notify_scheduled_fds(); Ok(None) } + + fn notify_scheduled_fds(&mut self) { + let fds_to_notify = self.notifier.borrow_mut().get_notified_fds(); + for fd in fds_to_notify { + let _ = post_fevent(&mut self.scheme_file, fd, syscall::EVENT_READ, 1); + } + } } impl SchemeMut for NetCfgScheme { @@ -387,10 +245,12 @@ impl SchemeMut for NetCfgScheme { } let read_buf = Vec::from(current_node.borrow().read()); let fd = self.next_fd; + trace!("open {} {}", fd, path); self.next_fd += 1; self.files.insert( fd, NetCfgFile { + path: path.to_owned(), cfg_node: current_node, uid, pos: 0, @@ -402,10 +262,17 @@ impl SchemeMut for NetCfgScheme { } fn close(&mut self, fd: usize) -> SyscallResult { + trace!("close {}", fd); if let Some(file) = self.files.remove(&fd) { - let value = str::from_utf8(&file.write_buf) - .or_else(|_| Err(SyscallError::new(syscall::EINVAL)))?; - file.cfg_node.borrow().write(&value) + self.notifier.borrow_mut().unsubscribe(&file.path, fd); + let node = file.cfg_node.borrow(); + if node.is_writable() { + let value = str::from_utf8(&file.write_buf) + .or_else(|_| Err(SyscallError::new(syscall::EINVAL)))?; + node.write(value) + } else { + Ok(0) + } } else { Err(SyscallError::new(syscall::EBADF)) } @@ -464,4 +331,16 @@ impl SchemeMut for NetCfgScheme { Ok(0) } + + fn fevent(&mut self, fd: usize, events: usize) -> SyscallResult { + let file = self.files + .get_mut(&fd) + .ok_or_else(|| SyscallError::new(syscall::EBADF))?; + if events & syscall::EVENT_READ == syscall::EVENT_READ { + self.notifier.borrow_mut().subscribe(&file.path, fd); + } else { + self.notifier.borrow_mut().unsubscribe(&file.path, fd); + } + Ok(fd) + } } diff --git a/src/smolnetd/scheme/netcfg/nodes.rs b/src/smolnetd/scheme/netcfg/nodes.rs new file mode 100644 index 0000000000..4b9fe17401 --- /dev/null +++ b/src/smolnetd/scheme/netcfg/nodes.rs @@ -0,0 +1,199 @@ +use std::cell::RefCell; +use std::rc::Rc; +use std::collections::BTreeMap; +use syscall::Result as SyscallResult; + +pub type CfgNodeRef = Rc>; + +pub trait CfgNode { + fn is_dir(&self) -> bool { + false + } + + fn is_writable(&self) -> bool { + false + } + + fn is_readable(&self) -> bool { + true + } + + fn read(&self) -> String { + String::new() + } + + fn write(&self, _buf: &str) -> SyscallResult { + Ok(0) + } + + fn open(&self, _file: &str) -> Option { + None + } +} + +pub struct RONode +where + F: Fn() -> String, +{ + read_fun: F, +} + +impl CfgNode for RONode +where + F: Fn() -> String, +{ + fn read(&self) -> String { + (self.read_fun)() + } +} + +impl RONode +where + F: 'static + Fn() -> String, +{ + pub fn new_ref(read_fun: F) -> CfgNodeRef { + Rc::new(RefCell::new(RONode { read_fun })) + } +} + +pub struct WONode +where + F: Fn(&str) -> SyscallResult, +{ + write_fun: F, +} + +impl CfgNode for WONode +where + F: Fn(&str) -> SyscallResult, +{ + fn write(&self, buf: &str) -> SyscallResult { + (self.write_fun)(buf) + } + + fn is_readable(&self) -> bool { + false + } + + fn is_writable(&self) -> bool { + true + } +} + +impl WONode +where + F: 'static + Fn(&str) -> SyscallResult, +{ + pub fn new_ref(write_fun: F) -> CfgNodeRef { + Rc::new(RefCell::new(WONode { write_fun })) + } +} + +pub struct RWNode +where + F: Fn() -> String, + G: Fn(&str) -> SyscallResult, +{ + read_fun: F, + write_fun: G, +} + +impl CfgNode for RWNode +where + F: Fn() -> String, + G: Fn(&str) -> SyscallResult, +{ + fn read(&self) -> String { + (self.read_fun)() + } + + fn write(&self, buf: &str) -> SyscallResult { + (self.write_fun)(buf) + } + + fn is_writable(&self) -> bool { + true + } +} + +impl RWNode +where + F: 'static + Fn() -> String, + G: 'static + Fn(&str) -> SyscallResult, +{ + pub fn new_ref(read_fun: F, write_fun: G) -> CfgNodeRef { + Rc::new(RefCell::new(RWNode { + read_fun, + write_fun, + })) + } +} + +pub struct StaticDirNode { + child_nodes: BTreeMap, +} + +impl CfgNode for StaticDirNode { + fn is_dir(&self) -> bool { + true + } + + fn read(&self) -> String { + let mut files = String::new(); + for child in self.child_nodes.keys() { + if !files.is_empty() { + files.push('\n'); + } + files += child; + } + files + } + + fn open(&self, file: &str) -> Option { + self.child_nodes.get(file).map(|node| Rc::clone(node)) + } +} + +impl StaticDirNode { + pub fn new_ref(child_nodes: BTreeMap) -> CfgNodeRef { + Rc::new(RefCell::new(StaticDirNode { child_nodes })) + } +} + +macro_rules! cfg_node { + (val $e:expr) => { + $e + }; + (ro [ $($c:ident),* ] || $b:block ) => { + { + $(let $c = $c.clone();)* + RONode::new_ref(move|| $b) + } + }; + (wo [ $($c:ident),* ] |$i:ident| $b:block ) => { + { + $(let $c = $c.clone();)* + WONode::new_ref(move |$i: &str| $b) + } + }; + (rw [ $($c:ident),* ] || $rb:block |$i:ident| $wb:block ) => { + { + let read_fun = { + $(#[allow(unused_variables)] let $c = $c.clone();)* + move || $rb + }; + let write_fun = { + $(#[allow(unused_variables)] let $c = $c.clone();)* + move |$i: &str| $wb + }; + RWNode::new_ref(read_fun, write_fun) + } + }; + ($($e:expr => { $($t:tt)* }),* $(,)*) => { + { + let mut children = BTreeMap::new(); + $(children.insert($e.into(), cfg_node!($($t)*));)* + StaticDirNode::new_ref(children) + } + }; +} diff --git a/src/smolnetd/scheme/netcfg/notifier.rs b/src/smolnetd/scheme/netcfg/notifier.rs new file mode 100644 index 0000000000..24b0a66fcb --- /dev/null +++ b/src/smolnetd/scheme/netcfg/notifier.rs @@ -0,0 +1,62 @@ +use std::rc::Rc; +use std::cell::RefCell; +use std::collections::{BTreeMap, BTreeSet}; +use std::collections::btree_map::Entry; + +pub struct Notifier { + listeners: BTreeMap>, + notified: BTreeSet, +} + +pub type NotifierRef = Rc>; + +impl Notifier { + pub fn new_ref() -> NotifierRef { + Rc::new(RefCell::new(Notifier { + listeners: BTreeMap::new(), + notified: BTreeSet::new(), + })) + } + + pub fn subscribe(&mut self, path: &str, fd: usize) { + trace!("Sub fd {} to {}", fd, path); + match self.listeners.entry(path.to_owned()) { + Entry::Occupied(mut e) => { + e.get_mut().insert(fd); + } + Entry::Vacant(e) => { + let mut fds = BTreeSet::new(); + fds.insert(fd); + e.insert(fds); + } + } + } + + pub fn unsubscribe(&mut self, path: &str, fd: usize) { + let empty = if let Some(fds) = self.listeners.get_mut(path) { + if fds.remove(&fd) { + trace!("Unsub fd {} from {}", fd, path); + } + fds.is_empty() + } else { + false + }; + if empty { + self.listeners.remove(path); + } + } + + pub fn schedule_notify(&mut self, path: &str) { + trace!("Notifying {}", path); + if let Some(fds) = self.listeners.get(path) { + self.notified.extend(fds); + } + } + + pub fn get_notified_fds(&mut self) -> BTreeSet { + use std::mem::swap; + let mut notified = BTreeSet::new(); + swap(&mut self.notified, &mut notified); + notified + } +} diff --git a/src/smolnetd/scheme/tcp.rs b/src/smolnetd/scheme/tcp.rs index 7896c22948..a7cbf99c35 100644 --- a/src/smolnetd/scheme/tcp.rs +++ b/src/smolnetd/scheme/tcp.rs @@ -65,8 +65,8 @@ impl<'a> SchemeSocket for TcpSocket<'a> { return Err(SyscallError::new(syscall::EACCES)); } - let rx_packets = vec![0; 65_535]; - let tx_packets = vec![0; 65_535]; + let rx_packets = vec![0; 0xffff]; + let tx_packets = vec![0; 0xffff]; let rx_buffer = TcpSocketBuffer::new(rx_packets); let tx_buffer = TcpSocketBuffer::new(tx_packets); let socket = TcpSocket::new(rx_buffer, tx_buffer); @@ -164,8 +164,8 @@ impl<'a> SchemeSocket for TcpSocket<'a> { trace!("TCP creating new listening socket"); let new_handle = SchemeFile::Socket(tcp_handle.clone_with_data(())); - let rx_packets = vec![0; 65_535]; - let tx_packets = vec![0; 65_535]; + let rx_packets = vec![0; 0xffff]; + let tx_packets = vec![0; 0xffff]; let rx_buffer = TcpSocketBuffer::new(rx_packets); let tx_buffer = TcpSocketBuffer::new(tx_packets); let socket = TcpSocket::new(rx_buffer, tx_buffer);