+18
-1
@@ -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
|
||||
|
||||
+39
-13
@@ -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<Rc<str>, Domain>,
|
||||
requests: BTreeMap<RawFd, Rc<str>>,
|
||||
resolved_timeouts: VecDeque<(TimeSpec, Rc<str>)>,
|
||||
@@ -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<Option<()>> {
|
||||
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<usize>) {
|
||||
pub fn on_nameserver_event(&mut self) -> Result<Option<()>> {
|
||||
self.domains.update_nameserver();
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn wakeup_fds(&mut self, fds_to_wakeup: &BTreeSet<usize>) {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ impl Log for Logger {
|
||||
}
|
||||
|
||||
fn log(&self, record: &LogRecord) {
|
||||
println!("{}: {}", record.level(), record.args());
|
||||
println!("{}: {}", record.target(), record.args());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+17
-2
@@ -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()
|
||||
|
||||
@@ -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::<IcmpSocket>(handle);
|
||||
let ident = ident_set
|
||||
|
||||
+46
-33
@@ -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<RefCell<EthernetInterface<'static, 'static, NetworkDevice>>>;
|
||||
|
||||
pub struct Smolnetd {
|
||||
network_file: Rc<RefCell<File>>,
|
||||
time_file: File,
|
||||
|
||||
iface: EthernetInterface<'static, 'static, NetworkDevice>,
|
||||
iface: Interface,
|
||||
socket_set: Rc<RefCell<SocketSet>>,
|
||||
|
||||
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<RefCell<VecDeque<Buffer>>>,
|
||||
buffer_pool: Rc<RefCell<BufferPool>>,
|
||||
@@ -61,12 +65,13 @@ 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");
|
||||
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),
|
||||
];
|
||||
@@ -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<Option<()>> {
|
||||
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::<TimeSpec>() {
|
||||
@@ -160,30 +172,33 @@ impl Smolnetd {
|
||||
}
|
||||
|
||||
fn poll(&mut self) -> Result<i64> {
|
||||
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 +212,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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,346 @@
|
||||
#[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::rc::Rc;
|
||||
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 self::nodes::*;
|
||||
use self::notifier::*;
|
||||
use redox_netstack::error::Result;
|
||||
use super::{post_fevent, Interface};
|
||||
|
||||
const WRITE_BUFFER_MAX_SIZE: usize = 0xffff;
|
||||
|
||||
fn parse_default_gw(value: &str) -> SyscallResult<Ipv4Address> {
|
||||
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))
|
||||
}
|
||||
|
||||
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] || {
|
||||
if let Some(ip) = iface.borrow().ipv4_gateway() {
|
||||
format!("default via {}\n", ip)
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
},
|
||||
"add" => {
|
||||
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, 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)
|
||||
}
|
||||
}
|
||||
},
|
||||
"ifaces" => {
|
||||
"eth0" => {
|
||||
"mac" => {
|
||||
rw [iface, notifier]
|
||||
|| {
|
||||
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);
|
||||
notifier.borrow_mut().schedule_notify("ifaces/eth0/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, notifier] |input| {
|
||||
let mut iface = iface.borrow_mut();
|
||||
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))?;
|
||||
if !cidr.address().is_unicast() {
|
||||
return Err(SyscallError::new(syscall::EINVAL));
|
||||
}
|
||||
cidrs.insert(0, cidr);
|
||||
}
|
||||
iface.update_ip_addrs(|s| {
|
||||
*s = From::from(cidrs);
|
||||
});
|
||||
notifier.borrow_mut().schedule_notify("ifaces/eth0/addr/list");
|
||||
Ok(0)
|
||||
}
|
||||
},
|
||||
"rm" => {
|
||||
wo [iface, notifier] |input| {
|
||||
let mut iface = iface.borrow_mut();
|
||||
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))?;
|
||||
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);
|
||||
});
|
||||
notifier.borrow_mut().schedule_notify("ifaces/eth0/addr/list");
|
||||
Ok(0)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct DNSConfig {
|
||||
name_server: Ipv4Address,
|
||||
}
|
||||
|
||||
type DNSConfigRef = Rc<RefCell<DNSConfig>>;
|
||||
|
||||
struct NetCfgFile {
|
||||
path: String,
|
||||
cfg_node: CfgNodeRef,
|
||||
read_buf: Vec<u8>,
|
||||
write_buf: Vec<u8>,
|
||||
pos: usize,
|
||||
uid: u32,
|
||||
}
|
||||
|
||||
pub struct NetCfgScheme {
|
||||
scheme_file: File,
|
||||
next_fd: usize,
|
||||
files: BTreeMap<usize, NetCfgFile>,
|
||||
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, Rc::clone(¬ifier), dns_config),
|
||||
notifier,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_scheme_event(&mut self) -> Result<Option<()>> {
|
||||
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)?;
|
||||
}
|
||||
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 {
|
||||
fn open(&mut self, url: &[u8], _flags: usize, uid: u32, _gid: u32) -> SyscallResult<usize> {
|
||||
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 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,
|
||||
read_buf,
|
||||
write_buf: vec![],
|
||||
},
|
||||
);
|
||||
Ok(fd)
|
||||
}
|
||||
|
||||
fn close(&mut self, fd: usize) -> SyscallResult<usize> {
|
||||
trace!("close {}", fd);
|
||||
if let Some(file) = self.files.remove(&fd) {
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, fd: usize, buf: &[u8]) -> SyscallResult<usize> {
|
||||
let file = self.files
|
||||
.get_mut(&fd)
|
||||
.ok_or_else(|| SyscallError::new(syscall::EBADF))?;
|
||||
|
||||
if file.uid != 0 {
|
||||
return Err(SyscallError::new(syscall::EACCES));
|
||||
}
|
||||
|
||||
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<usize> {
|
||||
let file = self.files
|
||||
.get_mut(&fd)
|
||||
.ok_or_else(|| SyscallError::new(syscall::EBADF))?;
|
||||
|
||||
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;
|
||||
}
|
||||
Ok(i)
|
||||
}
|
||||
|
||||
fn fstat(&mut self, fd: usize, stat: &mut Stat) -> SyscallResult<usize> {
|
||||
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;
|
||||
stat.st_size = file.read_buf.len() as u64;
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn fevent(&mut self, fd: usize, events: usize) -> SyscallResult<usize> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -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<RefCell<CfgNode>>;
|
||||
|
||||
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<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn open(&self, _file: &str) -> Option<CfgNodeRef> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RONode<F>
|
||||
where
|
||||
F: Fn() -> String,
|
||||
{
|
||||
read_fun: F,
|
||||
}
|
||||
|
||||
impl<F> CfgNode for RONode<F>
|
||||
where
|
||||
F: Fn() -> String,
|
||||
{
|
||||
fn read(&self) -> String {
|
||||
(self.read_fun)()
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> RONode<F>
|
||||
where
|
||||
F: 'static + Fn() -> String,
|
||||
{
|
||||
pub fn new_ref(read_fun: F) -> CfgNodeRef {
|
||||
Rc::new(RefCell::new(RONode { read_fun }))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WONode<F>
|
||||
where
|
||||
F: Fn(&str) -> SyscallResult<usize>,
|
||||
{
|
||||
write_fun: F,
|
||||
}
|
||||
|
||||
impl<F> CfgNode for WONode<F>
|
||||
where
|
||||
F: Fn(&str) -> SyscallResult<usize>,
|
||||
{
|
||||
fn write(&self, buf: &str) -> SyscallResult<usize> {
|
||||
(self.write_fun)(buf)
|
||||
}
|
||||
|
||||
fn is_readable(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn is_writable(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> WONode<F>
|
||||
where
|
||||
F: 'static + Fn(&str) -> SyscallResult<usize>,
|
||||
{
|
||||
pub fn new_ref(write_fun: F) -> CfgNodeRef {
|
||||
Rc::new(RefCell::new(WONode { write_fun }))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RWNode<F, G>
|
||||
where
|
||||
F: Fn() -> String,
|
||||
G: Fn(&str) -> SyscallResult<usize>,
|
||||
{
|
||||
read_fun: F,
|
||||
write_fun: G,
|
||||
}
|
||||
|
||||
impl<F, G> CfgNode for RWNode<F, G>
|
||||
where
|
||||
F: Fn() -> String,
|
||||
G: Fn(&str) -> SyscallResult<usize>,
|
||||
{
|
||||
fn read(&self) -> String {
|
||||
(self.read_fun)()
|
||||
}
|
||||
|
||||
fn write(&self, buf: &str) -> SyscallResult<usize> {
|
||||
(self.write_fun)(buf)
|
||||
}
|
||||
|
||||
fn is_writable(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, G> RWNode<F, G>
|
||||
where
|
||||
F: 'static + Fn() -> String,
|
||||
G: 'static + Fn(&str) -> SyscallResult<usize>,
|
||||
{
|
||||
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<String, CfgNodeRef>,
|
||||
}
|
||||
|
||||
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<CfgNodeRef> {
|
||||
self.child_nodes.get(file).map(|node| Rc::clone(node))
|
||||
}
|
||||
}
|
||||
|
||||
impl StaticDirNode {
|
||||
pub fn new_ref(child_nodes: BTreeMap<String, CfgNodeRef>) -> 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)
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -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<String, BTreeSet<usize>>,
|
||||
notified: BTreeSet<usize>,
|
||||
}
|
||||
|
||||
pub type NotifierRef = Rc<RefCell<Notifier>>;
|
||||
|
||||
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<usize> {
|
||||
use std::mem::swap;
|
||||
let mut notified = BTreeSet::new();
|
||||
swap(&mut self.notified, &mut notified);
|
||||
notified
|
||||
}
|
||||
}
|
||||
@@ -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<usize> {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user