milestone: desktop path Phases 1-5
Phase 1 (Runtime Substrate): 4 check binaries, --probe, POSIX tests Phase 2 (Wayland Compositor): bounded scaffold, zero warnings Phase 3 (KWin Session): preflight checker (KWin stub, gated on Qt6Quick) Phase 4 (KDE Plasma): 18 KF6 enabled, preflight checker Phase 5 (Hardware GPU): DRM/firmware/Mesa preflight checker Build: zero warnings, all scripts syntax-clean. Oracle-verified.
This commit is contained in:
@@ -0,0 +1,304 @@
|
||||
use scheme_utils::FpathWriter;
|
||||
use smoltcp::iface::SocketHandle;
|
||||
use smoltcp::socket::icmp::{
|
||||
Endpoint as IcmpEndpoint, PacketBuffer as IcmpSocketBuffer,
|
||||
PacketMetadata as IcmpPacketMetadata, Socket as IcmpSocket,
|
||||
};
|
||||
use smoltcp::wire::{Icmpv4Packet, Icmpv4Repr, IpAddress, IpListenEndpoint};
|
||||
use std::mem;
|
||||
use std::str;
|
||||
use syscall;
|
||||
use syscall::{Error as SyscallError, Result as SyscallResult};
|
||||
|
||||
use super::socket::{Context, DupResult, SchemeFile, SchemeSocket, SocketFile};
|
||||
use super::{SchemeWrapper, Smolnetd, SocketSet};
|
||||
use crate::port_set::PortSet;
|
||||
use crate::router::Router;
|
||||
|
||||
pub type IcmpScheme = SchemeWrapper<IcmpSocket<'static>>;
|
||||
|
||||
enum IcmpSocketType {
|
||||
Echo,
|
||||
Udp,
|
||||
}
|
||||
|
||||
pub struct IcmpData {
|
||||
socket_type: IcmpSocketType,
|
||||
ip: IpAddress,
|
||||
ident: u16,
|
||||
}
|
||||
|
||||
impl<'a> SchemeSocket for IcmpSocket<'a> {
|
||||
type SchemeDataT = PortSet;
|
||||
type DataT = IcmpData;
|
||||
type SettingT = ();
|
||||
|
||||
fn new_scheme_data() -> Self::SchemeDataT {
|
||||
PortSet::new(1u16, 0xffffu16).expect("Wrong ICMP ident values")
|
||||
}
|
||||
|
||||
fn can_send(&self) -> bool {
|
||||
self.can_send()
|
||||
}
|
||||
|
||||
fn can_recv(&mut self, _data: &Self::DataT) -> bool {
|
||||
smoltcp::socket::icmp::Socket::can_recv(self)
|
||||
}
|
||||
|
||||
fn may_recv(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn get_setting(
|
||||
_file: &SocketFile<Self::DataT>,
|
||||
_setting: Self::SettingT,
|
||||
_buf: &mut [u8],
|
||||
) -> SyscallResult<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn set_setting(
|
||||
_file: &mut SocketFile<Self::DataT>,
|
||||
_setting: Self::SettingT,
|
||||
_buf: &[u8],
|
||||
) -> SyscallResult<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn hop_limit(&self) -> u8 {
|
||||
self.hop_limit().unwrap_or(64)
|
||||
}
|
||||
|
||||
fn set_hop_limit(&mut self, hop_limit: u8) {
|
||||
self.set_hop_limit(Some(hop_limit));
|
||||
}
|
||||
|
||||
fn new_socket(
|
||||
socket_set: &mut SocketSet,
|
||||
path: &str,
|
||||
_uid: u32,
|
||||
ident_set: &mut Self::SchemeDataT,
|
||||
_context: &Context,
|
||||
) -> SyscallResult<(SocketHandle, Self::DataT)> {
|
||||
use std::str::FromStr;
|
||||
|
||||
let mut parts = path.split('/');
|
||||
let method = parts
|
||||
.next()
|
||||
.ok_or_else(|| syscall::Error::new(syscall::EINVAL))?;
|
||||
|
||||
match method {
|
||||
"echo" => {
|
||||
let addr = parts
|
||||
.next()
|
||||
.ok_or_else(|| syscall::Error::new(syscall::EINVAL))?;
|
||||
let ip =
|
||||
IpAddress::from_str(addr).map_err(|_| syscall::Error::new(syscall::EINVAL))?;
|
||||
|
||||
let socket = IcmpSocket::new(
|
||||
IcmpSocketBuffer::new(
|
||||
vec![IcmpPacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE],
|
||||
vec![0; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE],
|
||||
),
|
||||
IcmpSocketBuffer::new(
|
||||
vec![IcmpPacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE],
|
||||
vec![0; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE],
|
||||
),
|
||||
);
|
||||
let handle = socket_set.add(socket);
|
||||
let icmp_socket = socket_set.get_mut::<IcmpSocket>(handle);
|
||||
let ident = ident_set
|
||||
.get_port()
|
||||
.ok_or_else(|| SyscallError::new(syscall::EINVAL))?;
|
||||
icmp_socket
|
||||
.bind(IcmpEndpoint::Ident(ident))
|
||||
.map_err(|_| syscall::Error::new(syscall::EINVAL))?;
|
||||
let socket_data = IcmpData {
|
||||
socket_type: IcmpSocketType::Echo,
|
||||
ident,
|
||||
ip,
|
||||
};
|
||||
Ok((handle, socket_data))
|
||||
}
|
||||
"udp" => {
|
||||
let addr = parts
|
||||
.next()
|
||||
.ok_or_else(|| syscall::Error::new(syscall::EINVAL))?;
|
||||
let ip =
|
||||
IpAddress::from_str(addr).map_err(|_| syscall::Error::new(syscall::EINVAL))?;
|
||||
|
||||
let socket = IcmpSocket::new(
|
||||
IcmpSocketBuffer::new(
|
||||
vec![IcmpPacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE],
|
||||
vec![0; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE],
|
||||
),
|
||||
IcmpSocketBuffer::new(
|
||||
vec![IcmpPacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE],
|
||||
vec![0; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE],
|
||||
),
|
||||
);
|
||||
let handle = socket_set.add(socket);
|
||||
let icmp_socket = socket_set.get_mut::<IcmpSocket>(handle);
|
||||
let ident = ident_set
|
||||
.get_port()
|
||||
.ok_or_else(|| SyscallError::new(syscall::EINVAL))?;
|
||||
icmp_socket
|
||||
.bind(IcmpEndpoint::Udp(IpListenEndpoint::from(ident)))
|
||||
.map_err(|_| syscall::Error::new(syscall::EINVAL))?;
|
||||
let socket_data = IcmpData {
|
||||
socket_type: IcmpSocketType::Udp,
|
||||
ident,
|
||||
ip,
|
||||
};
|
||||
Ok((handle, socket_data))
|
||||
}
|
||||
_ => Err(syscall::Error::new(syscall::EINVAL)),
|
||||
}
|
||||
}
|
||||
|
||||
fn close_file(
|
||||
&self,
|
||||
file: &SchemeFile<Self>,
|
||||
ident_set: &mut Self::SchemeDataT,
|
||||
) -> SyscallResult<()> {
|
||||
if let SchemeFile::Socket(ref file) = *file {
|
||||
ident_set.release_port(file.data.ident);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_buf(
|
||||
&mut self,
|
||||
file: &mut SocketFile<Self::DataT>,
|
||||
buf: &[u8],
|
||||
) -> SyscallResult<usize> {
|
||||
if !file.write_enabled {
|
||||
return Err(SyscallError::new(syscall::EPIPE));
|
||||
} else if self.can_send() {
|
||||
match file.data.socket_type {
|
||||
IcmpSocketType::Echo => {
|
||||
if buf.len() < mem::size_of::<u16>() {
|
||||
return Err(SyscallError::new(syscall::EINVAL));
|
||||
}
|
||||
let (&seq_buf, payload) = buf.split_first_chunk::<2>().unwrap();
|
||||
let seq_no = u16::from_be_bytes(seq_buf);
|
||||
let icmp_repr = Icmpv4Repr::EchoRequest {
|
||||
ident: file.data.ident,
|
||||
seq_no,
|
||||
data: payload,
|
||||
};
|
||||
|
||||
let icmp_payload = self
|
||||
.send(icmp_repr.buffer_len(), file.data.ip)
|
||||
.map_err(|_| syscall::Error::new(syscall::EINVAL))?;
|
||||
let mut icmp_packet = Icmpv4Packet::new_unchecked(icmp_payload);
|
||||
//TODO: replace Default with actual caps
|
||||
icmp_repr.emit(&mut icmp_packet, &Default::default());
|
||||
Ok(buf.len())
|
||||
}
|
||||
IcmpSocketType::Udp => Err(SyscallError::new(syscall::EINVAL)),
|
||||
}
|
||||
} else if file.flags & syscall::O_NONBLOCK == syscall::O_NONBLOCK {
|
||||
Err(SyscallError::new(syscall::EAGAIN))
|
||||
} else {
|
||||
Err(SyscallError::new(syscall::EWOULDBLOCK)) // internally scheduled to re-read
|
||||
}
|
||||
}
|
||||
|
||||
fn read_buf(
|
||||
&mut self,
|
||||
file: &mut SocketFile<Self::DataT>,
|
||||
buf: &mut [u8],
|
||||
) -> SyscallResult<usize> {
|
||||
if !file.read_enabled {
|
||||
return Ok(0);
|
||||
}
|
||||
while self.can_recv(&file.data) {
|
||||
let (payload, _) = self.recv().expect("Can't recv icmp packet");
|
||||
let icmp_packet = Icmpv4Packet::new_unchecked(&payload);
|
||||
//TODO: replace default with actual caps
|
||||
let icmp_repr = Icmpv4Repr::parse(&icmp_packet, &Default::default()).unwrap();
|
||||
|
||||
if let Icmpv4Repr::EchoReply { seq_no, data, .. } = icmp_repr {
|
||||
if buf.len() < mem::size_of::<u16>() + data.len() {
|
||||
return Err(SyscallError::new(syscall::EINVAL));
|
||||
}
|
||||
buf[0..2].copy_from_slice(&seq_no.to_be_bytes());
|
||||
|
||||
for i in 0..data.len() {
|
||||
buf[mem::size_of::<u16>() + i] = data[i];
|
||||
}
|
||||
|
||||
return Ok(mem::size_of::<u16>() + data.len());
|
||||
}
|
||||
}
|
||||
|
||||
if file.flags & syscall::O_NONBLOCK == syscall::O_NONBLOCK {
|
||||
Err(SyscallError::new(syscall::EAGAIN))
|
||||
} else {
|
||||
Err(SyscallError::new(syscall::EWOULDBLOCK)) // internally scheduled to re-read
|
||||
}
|
||||
}
|
||||
|
||||
fn dup(
|
||||
_socket_set: &mut SocketSet,
|
||||
_file: &mut SchemeFile<Self>,
|
||||
_path: &str,
|
||||
_: &mut Self::SchemeDataT,
|
||||
) -> SyscallResult<DupResult<Self>> {
|
||||
Err(SyscallError::new(syscall::EBADF))
|
||||
}
|
||||
|
||||
fn fpath(&self, file: &SchemeFile<Self>, buf: &mut [u8]) -> SyscallResult<usize> {
|
||||
FpathWriter::with(buf, "icmp", |w| {
|
||||
if let SchemeFile::Socket(ref socket_file) = *file {
|
||||
match socket_file.data.socket_type {
|
||||
IcmpSocketType::Echo => {
|
||||
write!(w, "echo/{}", socket_file.data.ip).unwrap();
|
||||
}
|
||||
IcmpSocketType::Udp => {
|
||||
write!(w, "udp/{}", socket_file.data.ip).unwrap();
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
Err(SyscallError::new(syscall::EBADF))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn handle_recvmsg(
|
||||
&mut self,
|
||||
file: &mut SchemeFile<Self>,
|
||||
how: &mut [u8],
|
||||
flags: usize,
|
||||
) -> SyscallResult<usize> {
|
||||
return Err(SyscallError::new(syscall::EOPNOTSUPP));
|
||||
}
|
||||
|
||||
fn handle_get_peer_name(
|
||||
&self,
|
||||
file: &SchemeFile<Self>,
|
||||
buf: &mut [u8],
|
||||
) -> SyscallResult<usize> {
|
||||
self.fpath(file, buf)
|
||||
}
|
||||
|
||||
fn handle_shutdown(&mut self, file: &mut SchemeFile<Self>, how: usize) -> SyscallResult<usize> {
|
||||
let socket_file = match file {
|
||||
SchemeFile::Socket(ref mut file) => file,
|
||||
_ => return Err(SyscallError::new(syscall::EBADF)),
|
||||
};
|
||||
|
||||
match how {
|
||||
0 => socket_file.read_enabled = false, // SHUT_RD
|
||||
1 => socket_file.write_enabled = false, // SHUT_WR
|
||||
2 => {
|
||||
socket_file.read_enabled = false;
|
||||
socket_file.write_enabled = false;
|
||||
} // SHUT_RDWR
|
||||
_ => return Err(SyscallError::new(syscall::EINVAL)),
|
||||
}
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
use scheme_utils::FpathWriter;
|
||||
use smoltcp::iface::SocketHandle;
|
||||
use smoltcp::socket::raw::{
|
||||
PacketBuffer as RawSocketBuffer, PacketMetadata as RawPacketMetadata, Socket as RawSocket,
|
||||
};
|
||||
use smoltcp::wire::{IpProtocol, IpVersion};
|
||||
use std::str;
|
||||
use syscall;
|
||||
use syscall::{Error as SyscallError, Result as SyscallResult};
|
||||
|
||||
use crate::router::Router;
|
||||
|
||||
use super::socket::{Context, DupResult, SchemeFile, SchemeSocket, SocketFile};
|
||||
use super::{SchemeWrapper, Smolnetd, SocketSet};
|
||||
|
||||
pub type IpScheme = SchemeWrapper<RawSocket<'static>>;
|
||||
|
||||
impl<'a> SchemeSocket for RawSocket<'a> {
|
||||
type SchemeDataT = ();
|
||||
type DataT = ();
|
||||
type SettingT = ();
|
||||
|
||||
fn new_scheme_data() -> Self::SchemeDataT {
|
||||
()
|
||||
}
|
||||
|
||||
fn can_send(&self) -> bool {
|
||||
self.can_send()
|
||||
}
|
||||
|
||||
fn can_recv(&mut self, _data: &Self::DataT) -> bool {
|
||||
smoltcp::socket::raw::Socket::can_recv(self)
|
||||
}
|
||||
|
||||
fn may_recv(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn get_setting(
|
||||
_file: &SocketFile<Self::DataT>,
|
||||
_setting: Self::SettingT,
|
||||
_buf: &mut [u8],
|
||||
) -> SyscallResult<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn set_setting(
|
||||
_file: &mut SocketFile<Self::DataT>,
|
||||
_setting: Self::SettingT,
|
||||
_buf: &[u8],
|
||||
) -> SyscallResult<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn hop_limit(&self) -> u8 {
|
||||
0
|
||||
}
|
||||
|
||||
fn set_hop_limit(&mut self, _hop_limit: u8) {}
|
||||
|
||||
fn new_socket(
|
||||
socket_set: &mut SocketSet,
|
||||
path: &str,
|
||||
uid: u32,
|
||||
_: &mut Self::SchemeDataT,
|
||||
_context: &Context,
|
||||
) -> SyscallResult<(SocketHandle, Self::DataT)> {
|
||||
if uid != 0 {
|
||||
return Err(SyscallError::new(syscall::EACCES));
|
||||
}
|
||||
let proto =
|
||||
u8::from_str_radix(path, 16).or_else(|_| Err(SyscallError::new(syscall::ENOENT)))?;
|
||||
|
||||
let rx_buffer = RawSocketBuffer::new(
|
||||
vec![RawPacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE],
|
||||
vec![0; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE],
|
||||
);
|
||||
let tx_buffer = RawSocketBuffer::new(
|
||||
vec![RawPacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE],
|
||||
vec![0; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE],
|
||||
);
|
||||
let ip_socket = RawSocket::new(
|
||||
IpVersion::Ipv4,
|
||||
IpProtocol::from(proto),
|
||||
rx_buffer,
|
||||
tx_buffer,
|
||||
);
|
||||
|
||||
let socket_handle = socket_set.add(ip_socket);
|
||||
Ok((socket_handle, ()))
|
||||
}
|
||||
|
||||
fn close_file(&self, _: &SchemeFile<Self>, _: &mut Self::SchemeDataT) -> SyscallResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_buf(
|
||||
&mut self,
|
||||
file: &mut SocketFile<Self::DataT>,
|
||||
buf: &[u8],
|
||||
) -> SyscallResult<usize> {
|
||||
if !file.write_enabled {
|
||||
return Err(SyscallError::new(syscall::EPIPE));
|
||||
} else if self.can_send() {
|
||||
self.send_slice(buf).expect("Can't send slice");
|
||||
Ok(buf.len())
|
||||
} else if file.flags & syscall::O_NONBLOCK == syscall::O_NONBLOCK {
|
||||
Err(SyscallError::new(syscall::EAGAIN))
|
||||
} else {
|
||||
Err(SyscallError::new(syscall::EWOULDBLOCK)) // internally scheduled to re-read
|
||||
}
|
||||
}
|
||||
|
||||
fn read_buf(
|
||||
&mut self,
|
||||
file: &mut SocketFile<Self::DataT>,
|
||||
buf: &mut [u8],
|
||||
) -> SyscallResult<usize> {
|
||||
if !file.read_enabled {
|
||||
Ok(0)
|
||||
} else if self.can_recv(&file.data) {
|
||||
let length = self.recv_slice(buf).expect("Can't receive slice");
|
||||
Ok(length)
|
||||
} else if file.flags & syscall::O_NONBLOCK == syscall::O_NONBLOCK {
|
||||
Err(SyscallError::new(syscall::EAGAIN))
|
||||
} else {
|
||||
Err(SyscallError::new(syscall::EWOULDBLOCK)) // internally scheduled to re-read
|
||||
}
|
||||
}
|
||||
|
||||
fn dup(
|
||||
_socket_set: &mut SocketSet,
|
||||
_file: &mut SchemeFile<Self>,
|
||||
_path: &str,
|
||||
_: &mut Self::SchemeDataT,
|
||||
) -> SyscallResult<DupResult<Self>> {
|
||||
Err(SyscallError::new(syscall::EBADF))
|
||||
}
|
||||
|
||||
fn fpath(&self, _file: &SchemeFile<Self>, buf: &mut [u8]) -> SyscallResult<usize> {
|
||||
FpathWriter::with(buf, "ip", |w| {
|
||||
write!(w, "{}", self.ip_protocol()).unwrap();
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn handle_recvmsg(
|
||||
&mut self,
|
||||
file: &mut SchemeFile<Self>,
|
||||
how: &mut [u8],
|
||||
flags: usize,
|
||||
) -> SyscallResult<usize> {
|
||||
return Err(SyscallError::new(syscall::EOPNOTSUPP));
|
||||
}
|
||||
|
||||
fn handle_get_peer_name(
|
||||
&self,
|
||||
file: &SchemeFile<Self>,
|
||||
buf: &mut [u8],
|
||||
) -> SyscallResult<usize> {
|
||||
self.fpath(file, buf)
|
||||
}
|
||||
|
||||
fn handle_shutdown(&mut self, file: &mut SchemeFile<Self>, how: usize) -> SyscallResult<usize> {
|
||||
let socket_file = match file {
|
||||
SchemeFile::Socket(ref mut file) => file,
|
||||
_ => return Err(SyscallError::new(syscall::EBADF)),
|
||||
};
|
||||
|
||||
match how {
|
||||
0 => socket_file.read_enabled = false, // SHUT_RD
|
||||
1 => socket_file.write_enabled = false, // SHUT_WR
|
||||
2 => {
|
||||
socket_file.read_enabled = false;
|
||||
socket_file.write_enabled = false;
|
||||
} // SHUT_RDWR
|
||||
_ => return Err(SyscallError::new(syscall::EINVAL)),
|
||||
}
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,558 @@
|
||||
use crate::link::ethernet::EthernetLink;
|
||||
use crate::link::LinkDevice;
|
||||
use crate::link::{loopback::LoopbackDevice, DeviceList};
|
||||
use crate::router::route_table::{RouteTable, Rule};
|
||||
use crate::router::Router;
|
||||
use crate::scheme::smoltcp::iface::SocketSet as SmoltcpSocketSet;
|
||||
use crate::scheme::socket::{Handle, SchemeSocket, SocketScheme};
|
||||
use libredox::flag;
|
||||
use libredox::Fd;
|
||||
use redox_scheme::{
|
||||
scheme::{IntoTag, Op, SchemeResponse, SchemeState, SchemeSync},
|
||||
CallerCtx, RequestKind, Response, SignalBehavior, Socket,
|
||||
};
|
||||
use smoltcp;
|
||||
use smoltcp::iface::{Config, Interface as SmoltcpInterface};
|
||||
use smoltcp::phy::Tracer;
|
||||
use smoltcp::socket::AnySocket;
|
||||
use smoltcp::time::{Duration, Instant};
|
||||
use smoltcp::wire::{
|
||||
EthernetAddress, HardwareAddress, IpAddress, IpCidr, IpListenEndpoint, Ipv4Address,
|
||||
};
|
||||
use std::cell::RefCell;
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::mem::size_of;
|
||||
use std::os::fd::{FromRawFd, RawFd};
|
||||
use std::rc::Rc;
|
||||
use std::str::FromStr;
|
||||
use syscall;
|
||||
use syscall::data::TimeSpec;
|
||||
use syscall::Error as SyscallError;
|
||||
|
||||
use self::icmp::IcmpScheme;
|
||||
use self::ip::IpScheme;
|
||||
use self::netcfg::NetCfgScheme;
|
||||
use self::tcp::TcpScheme;
|
||||
use self::udp::UdpScheme;
|
||||
use crate::error::{Error, Result};
|
||||
|
||||
mod icmp;
|
||||
mod ip;
|
||||
mod netcfg;
|
||||
mod socket;
|
||||
mod tcp;
|
||||
mod udp;
|
||||
|
||||
type SocketSet = SmoltcpSocketSet<'static>;
|
||||
type Interface = Rc<RefCell<SmoltcpInterface>>;
|
||||
|
||||
const MAX_DURATION: Duration = Duration::from_micros(u64::MAX);
|
||||
const MIN_DURATION: Duration = Duration::from_micros(0);
|
||||
|
||||
fn getcfg(key: &str) -> Result<String> {
|
||||
let mut value = String::new();
|
||||
let mut file = File::open(format!("/etc/net/{key}"))?;
|
||||
file.read_to_string(&mut value)?;
|
||||
Ok(value.trim().to_string())
|
||||
}
|
||||
|
||||
pub struct Smolnetd {
|
||||
router_device: Tracer<Router>,
|
||||
iface: Interface,
|
||||
time_file: File,
|
||||
|
||||
socket_set: Rc<RefCell<SocketSet>>,
|
||||
timer: ::std::time::Instant,
|
||||
|
||||
ip_scheme: IpScheme,
|
||||
udp_scheme: UdpScheme,
|
||||
tcp_scheme: TcpScheme,
|
||||
icmp_scheme: IcmpScheme,
|
||||
netcfg_scheme: NetCfgScheme,
|
||||
}
|
||||
|
||||
impl Smolnetd {
|
||||
pub const MAX_PACKET_SIZE: usize = 2048;
|
||||
pub const SOCKET_BUFFER_SIZE: usize = 128; //packets
|
||||
pub const MIN_CHECK_TIMEOUT: Duration = Duration::from_millis(10);
|
||||
pub const MAX_CHECK_TIMEOUT: Duration = Duration::from_millis(500);
|
||||
|
||||
pub fn new(
|
||||
network_file: Fd,
|
||||
hardware_addr: EthernetAddress,
|
||||
ip_file: Socket,
|
||||
udp_file: Socket,
|
||||
tcp_file: Socket,
|
||||
icmp_file: Socket,
|
||||
time_file: Fd,
|
||||
netcfg_file: Socket,
|
||||
) -> Result<Smolnetd> {
|
||||
let protocol_addrs = vec![
|
||||
//This is a placeholder IP for DHCP
|
||||
IpCidr::new(IpAddress::v4(0, 0, 0, 0), 8),
|
||||
IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8),
|
||||
];
|
||||
|
||||
let default_gw = Ipv4Address::from_str(getcfg("ip_router").unwrap().trim())
|
||||
.expect("Can't parse the 'ip_router' cfg.");
|
||||
|
||||
let devices = Rc::new(RefCell::new(DeviceList::default()));
|
||||
let route_table = Rc::new(RefCell::new(RouteTable::default()));
|
||||
let mut network_device = Tracer::new(
|
||||
Router::new(Rc::clone(&devices), Rc::clone(&route_table)),
|
||||
|_timestamp, printer| trace!("{}", printer),
|
||||
);
|
||||
|
||||
let config = Config::new(HardwareAddress::Ip);
|
||||
let mut iface = SmoltcpInterface::new(config, &mut network_device, Instant::now());
|
||||
iface.update_ip_addrs(|ip_addrs| ip_addrs.extend(protocol_addrs));
|
||||
iface
|
||||
.routes_mut()
|
||||
.add_default_ipv4_route(default_gw)
|
||||
.expect("Failed to add default gateway");
|
||||
|
||||
let iface = Rc::new(RefCell::new(iface));
|
||||
let socket_set = Rc::new(RefCell::new(SocketSet::new(vec![])));
|
||||
|
||||
let loopback = LoopbackDevice::default();
|
||||
route_table.borrow_mut().insert_rule(Rule::new(
|
||||
"127.0.0.0/8".parse().unwrap(),
|
||||
None,
|
||||
Rc::clone(loopback.name()),
|
||||
"127.0.0.1".parse().unwrap(),
|
||||
));
|
||||
|
||||
let mut eth0 = EthernetLink::new("eth0", unsafe {
|
||||
File::from_raw_fd(network_file.into_raw() as RawFd)
|
||||
});
|
||||
eth0.set_mac_address(hardware_addr);
|
||||
|
||||
devices.borrow_mut().push(loopback);
|
||||
devices.borrow_mut().push(eth0);
|
||||
|
||||
Ok(Smolnetd {
|
||||
iface: Rc::clone(&iface),
|
||||
router_device: network_device,
|
||||
socket_set: Rc::clone(&socket_set),
|
||||
timer: ::std::time::Instant::now(),
|
||||
time_file: unsafe { File::from_raw_fd(time_file.into_raw() as RawFd) },
|
||||
ip_scheme: IpScheme::new(
|
||||
"ip",
|
||||
Rc::clone(&iface),
|
||||
Rc::clone(&route_table),
|
||||
Rc::clone(&socket_set),
|
||||
ip_file,
|
||||
)?,
|
||||
udp_scheme: UdpScheme::new(
|
||||
"udp",
|
||||
Rc::clone(&iface),
|
||||
Rc::clone(&route_table),
|
||||
Rc::clone(&socket_set),
|
||||
udp_file,
|
||||
)?,
|
||||
tcp_scheme: TcpScheme::new(
|
||||
"tcp",
|
||||
Rc::clone(&iface),
|
||||
Rc::clone(&route_table),
|
||||
Rc::clone(&socket_set),
|
||||
tcp_file,
|
||||
)?,
|
||||
icmp_scheme: IcmpScheme::new(
|
||||
"icmp",
|
||||
Rc::clone(&iface),
|
||||
Rc::clone(&route_table),
|
||||
Rc::clone(&socket_set),
|
||||
icmp_file,
|
||||
)?,
|
||||
netcfg_scheme: NetCfgScheme::new(
|
||||
Rc::clone(&iface),
|
||||
netcfg_file,
|
||||
Rc::clone(&route_table),
|
||||
Rc::clone(&devices),
|
||||
)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn on_network_scheme_event(&mut self) -> Result<()> {
|
||||
self.poll()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn on_ip_scheme_event(&mut self) -> Result<()> {
|
||||
self.ip_scheme.on_scheme_event()?;
|
||||
let _ = self.poll()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn on_udp_scheme_event(&mut self) -> Result<()> {
|
||||
self.udp_scheme.on_scheme_event()?;
|
||||
let _ = self.poll()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn on_tcp_scheme_event(&mut self) -> Result<()> {
|
||||
self.tcp_scheme.on_scheme_event()?;
|
||||
let _ = self.poll()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn on_icmp_scheme_event(&mut self) -> Result<()> {
|
||||
self.icmp_scheme.on_scheme_event()?;
|
||||
let _ = self.poll()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn on_time_event(&mut self) -> Result<()> {
|
||||
let timeout = self.poll()?;
|
||||
self.schedule_time_event(timeout)?;
|
||||
//TODO: Fix network scheme to ensure events are not missed
|
||||
self.on_network_scheme_event()
|
||||
}
|
||||
|
||||
pub fn on_netcfg_scheme_event(&mut self) -> Result<()> {
|
||||
self.netcfg_scheme.on_scheme_event()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn schedule_time_event(&mut self, timeout: Duration) -> Result<()> {
|
||||
let mut time = TimeSpec::default();
|
||||
if self.time_file.read(&mut time)? < size_of::<TimeSpec>() {
|
||||
return Err(Error::from_syscall_error(
|
||||
syscall::Error::new(syscall::EBADF),
|
||||
"Can't read current time",
|
||||
));
|
||||
}
|
||||
let mut time_ms = time.tv_sec * 1000i64 + i64::from(time.tv_nsec) / 1_000_000i64;
|
||||
time_ms += timeout.total_millis() as i64;
|
||||
time.tv_sec = time_ms / 1000;
|
||||
time.tv_nsec = ((time_ms % 1000) * 1_000_000) as i32;
|
||||
self.time_file
|
||||
.write_all(&time)
|
||||
.map_err(|e| Error::from_io_error(e, "Failed to write to time file"))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn poll(&mut self) -> Result<Duration> {
|
||||
let timeout = {
|
||||
let mut iter_limit = 10usize;
|
||||
let mut iface = self.iface.borrow_mut();
|
||||
let mut socket_set = self.socket_set.borrow_mut();
|
||||
|
||||
loop {
|
||||
let timestamp = Instant::from(self.timer);
|
||||
if iter_limit == 0 {
|
||||
break MIN_DURATION;
|
||||
}
|
||||
iter_limit -= 1;
|
||||
|
||||
self.router_device.get_mut().poll(timestamp);
|
||||
|
||||
// TODO: Check what if the bool returned by poll can be useful
|
||||
iface.poll(timestamp, &mut self.router_device, &mut socket_set);
|
||||
|
||||
self.router_device.get_mut().dispatch(timestamp);
|
||||
|
||||
if !self.router_device.get_ref().can_recv() {
|
||||
match iface.poll_delay(timestamp, &socket_set) {
|
||||
Some(delay) if delay == Duration::ZERO => {}
|
||||
Some(delay) => break ::std::cmp::min(MAX_DURATION, delay),
|
||||
None => break MAX_DURATION,
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
self.notify_sockets()?;
|
||||
|
||||
Ok(::std::cmp::min(
|
||||
::std::cmp::max(Smolnetd::MIN_CHECK_TIMEOUT, timeout),
|
||||
Smolnetd::MAX_CHECK_TIMEOUT,
|
||||
))
|
||||
}
|
||||
|
||||
fn notify_sockets(&mut self) -> Result<()> {
|
||||
self.ip_scheme.notify_sockets()?;
|
||||
self.udp_scheme.notify_sockets()?;
|
||||
self.tcp_scheme.notify_sockets()?;
|
||||
self.icmp_scheme.notify_sockets()
|
||||
}
|
||||
}
|
||||
|
||||
fn post_fevent(socket: &Socket, id: usize, flags: usize) -> syscall::error::Result<()> {
|
||||
let fevent_response = Response::post_fevent(id, flags);
|
||||
match socket.write_response(fevent_response, SignalBehavior::Restart) {
|
||||
Ok(true) => Ok(()), // Write response success
|
||||
Ok(false) => Err(syscall::error::Error::new(syscall::EAGAIN)), // Write response failed, retry.
|
||||
Err(err) => Err(err), // Error writing response
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_endpoint(socket: &str) -> IpListenEndpoint {
|
||||
let mut socket_parts = socket.split(':');
|
||||
let host = Ipv4Address::from_str(socket_parts.next().unwrap_or(""))
|
||||
.ok()
|
||||
.filter(|addr| !addr.is_unspecified())
|
||||
.map(IpAddress::Ipv4);
|
||||
|
||||
let port = socket_parts
|
||||
.next()
|
||||
.unwrap_or("")
|
||||
.parse::<u16>()
|
||||
.unwrap_or(0);
|
||||
IpListenEndpoint { addr: host, port }
|
||||
}
|
||||
|
||||
struct WaitHandle {
|
||||
until: Option<TimeSpec>,
|
||||
cancelling: bool,
|
||||
packet: (Op, CallerCtx),
|
||||
}
|
||||
|
||||
type WaitQueue = Vec<WaitHandle>;
|
||||
|
||||
pub struct SchemeWrapper<SocketT>
|
||||
where
|
||||
SocketT: SchemeSocket + AnySocket<'static>,
|
||||
{
|
||||
scheme: socket::SocketScheme<SocketT>,
|
||||
state: SchemeState,
|
||||
wait_queue: WaitQueue,
|
||||
}
|
||||
impl<SocketT> SchemeWrapper<SocketT>
|
||||
where
|
||||
SocketT: SchemeSocket + AnySocket<'static>,
|
||||
{
|
||||
pub fn new(
|
||||
name: &str,
|
||||
iface: Interface,
|
||||
route_table: Rc<RefCell<RouteTable>>,
|
||||
socket_set: Rc<RefCell<SocketSet>>,
|
||||
scheme_file: Socket,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
scheme: SocketScheme::<SocketT>::new(name, iface, route_table, socket_set, scheme_file)
|
||||
.map_err(|e| {
|
||||
Error::from_syscall_error(e, &format!("failed to initialize {} scheme", name))
|
||||
})?,
|
||||
state: SchemeState::new(),
|
||||
wait_queue: Vec::new(),
|
||||
})
|
||||
}
|
||||
pub fn on_scheme_event(&mut self) -> Result<Option<()>> {
|
||||
let result = loop {
|
||||
let request = match self
|
||||
.scheme
|
||||
.scheme_file
|
||||
.next_request(SignalBehavior::Restart)
|
||||
{
|
||||
Ok(Some(req)) => req,
|
||||
Ok(None) => {
|
||||
break Some(());
|
||||
}
|
||||
Err(error)
|
||||
if error.errno == syscall::EWOULDBLOCK || error.errno == syscall::EAGAIN =>
|
||||
{
|
||||
break None;
|
||||
}
|
||||
Err(other) => {
|
||||
return Err(Error::from_syscall_error(
|
||||
other,
|
||||
"failed to receive new request",
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
let req = match request.kind() {
|
||||
RequestKind::Call(c) => c,
|
||||
RequestKind::OnClose { id } => {
|
||||
self.scheme.on_close(id);
|
||||
continue;
|
||||
}
|
||||
RequestKind::Cancellation(req) => {
|
||||
if let Some(idx) = self
|
||||
.wait_queue
|
||||
.iter()
|
||||
.position(|q| q.packet.0.req_id() == req.id)
|
||||
{
|
||||
self.wait_queue[idx].cancelling = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
_ => {
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let caller = req.caller();
|
||||
let mut op = match req.op() {
|
||||
Ok(op) => op,
|
||||
Err(req) => {
|
||||
self.scheme
|
||||
.scheme_file
|
||||
.write_response(
|
||||
Response::err(syscall::EOPNOTSUPP, req),
|
||||
SignalBehavior::Restart,
|
||||
)
|
||||
.map_err(|e| {
|
||||
Error::from_syscall_error(e.into(), "failed to write response")
|
||||
})?;
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let resp = match op.handle_sync_dont_consume(&caller, &mut self.scheme, &mut self.state)
|
||||
{
|
||||
SchemeResponse::Opened(Err(SyscallError {
|
||||
errno: syscall::EWOULDBLOCK,
|
||||
}))
|
||||
| SchemeResponse::Regular(Err(SyscallError {
|
||||
errno: syscall::EWOULDBLOCK,
|
||||
})) if !op.is_explicitly_nonblock() => {
|
||||
match self.scheme.handle_block(&op) {
|
||||
Ok(timeout) => {
|
||||
self.wait_queue.push(WaitHandle {
|
||||
until: timeout,
|
||||
cancelling: false,
|
||||
packet: (op, caller),
|
||||
});
|
||||
}
|
||||
Err(err) => {
|
||||
let _ = self
|
||||
.scheme
|
||||
.scheme_file
|
||||
.write_response(
|
||||
Response::err(err.errno, op),
|
||||
SignalBehavior::Restart,
|
||||
)
|
||||
.map_err(|e| {
|
||||
Error::from_syscall_error(e.into(), "failed to write response")
|
||||
})?;
|
||||
return Err(Error::from_syscall_error(
|
||||
err,
|
||||
"Can't handle blocked socket",
|
||||
));
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
SchemeResponse::Regular(r) => Response::new(r, op),
|
||||
SchemeResponse::Opened(o) => Response::open_dup_like(o, op),
|
||||
SchemeResponse::RegularAndNotifyOnDetach(status) => {
|
||||
Response::new_notify_on_detach(status, op)
|
||||
}
|
||||
};
|
||||
let _ = self
|
||||
.scheme
|
||||
.scheme_file
|
||||
.write_response(resp, SignalBehavior::Restart)
|
||||
.map_err(|e| Error::from_syscall_error(e.into(), "failed to write response"))?;
|
||||
};
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn notify_sockets(&mut self) -> Result<()> {
|
||||
let cur_time = libredox::call::clock_gettime(flag::CLOCK_MONOTONIC)
|
||||
.map_err(|e| Error::from_syscall_error(e.into(), "Can't get time"))?;
|
||||
|
||||
// Notify non-blocking sockets
|
||||
let scheme = &mut self.scheme;
|
||||
let state = &mut self.state;
|
||||
|
||||
for (&fd, handle) in scheme.handles.iter_mut() {
|
||||
let Handle::File(file) = handle else {
|
||||
continue;
|
||||
};
|
||||
let events = {
|
||||
let mut socket_set = scheme.socket_set.borrow_mut();
|
||||
file.events(&mut socket_set)
|
||||
};
|
||||
if events > 0 {
|
||||
post_fevent(&scheme.scheme_file, fd, events)
|
||||
.map_err(|e| Error::from_syscall_error(e.into(), "failed to post fevent"))?;
|
||||
}
|
||||
}
|
||||
// Wake up blocking queue
|
||||
let queue = &mut self.wait_queue;
|
||||
let mut i = 0;
|
||||
while i < queue.len() {
|
||||
let handle = &mut queue[i];
|
||||
let (op, caller) = &mut handle.packet;
|
||||
let res = op.handle_sync_dont_consume(caller, scheme, state);
|
||||
|
||||
match res {
|
||||
SchemeResponse::Opened(Err(SyscallError {
|
||||
errno: syscall::EWOULDBLOCK,
|
||||
}))
|
||||
| SchemeResponse::Regular(Err(SyscallError {
|
||||
errno: syscall::EWOULDBLOCK,
|
||||
})) if !op.is_explicitly_nonblock() => {
|
||||
if handle.cancelling {
|
||||
let (op, _) = queue.swap_remove(i).packet;
|
||||
scheme
|
||||
.scheme_file
|
||||
.write_response(
|
||||
Response::err(syscall::ECANCELED, op),
|
||||
SignalBehavior::Restart,
|
||||
)
|
||||
.map_err(|e| {
|
||||
Error::from_syscall_error(e.into(), "failed to write response")
|
||||
})?;
|
||||
continue;
|
||||
}
|
||||
match handle.until {
|
||||
Some(until)
|
||||
if (until.tv_sec < cur_time.tv_sec
|
||||
|| (until.tv_sec == cur_time.tv_sec
|
||||
&& i64::from(until.tv_nsec) < i64::from(cur_time.tv_nsec))) =>
|
||||
{
|
||||
let (op, _) = queue.swap_remove(i).packet;
|
||||
let _ = scheme
|
||||
.scheme_file
|
||||
.write_response(
|
||||
Response::err(syscall::ETIMEDOUT, op),
|
||||
SignalBehavior::Restart,
|
||||
)
|
||||
.map_err(|e| {
|
||||
Error::from_syscall_error(e.into(), "failed to write response")
|
||||
})?;
|
||||
}
|
||||
_ => {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
SchemeResponse::Regular(r) => {
|
||||
let (op, _) = queue.swap_remove(i).packet;
|
||||
let _ = scheme
|
||||
.scheme_file
|
||||
.write_response(Response::new(r, op), SignalBehavior::Restart)
|
||||
.map_err(|e| {
|
||||
Error::from_syscall_error(e.into(), "failed to write response")
|
||||
})?;
|
||||
}
|
||||
SchemeResponse::Opened(o) => {
|
||||
let (op, _) = queue.swap_remove(i).packet;
|
||||
let _ = scheme
|
||||
.scheme_file
|
||||
.write_response(Response::open_dup_like(o, op), SignalBehavior::Restart)
|
||||
.map_err(|e| {
|
||||
Error::from_syscall_error(e.into(), "failed to write response")
|
||||
})?;
|
||||
}
|
||||
SchemeResponse::RegularAndNotifyOnDetach(status) => {
|
||||
let (op, _) = queue.swap_remove(i).packet;
|
||||
let _ = scheme
|
||||
.scheme_file
|
||||
.write_response(
|
||||
Response::new_notify_on_detach(status, op),
|
||||
SignalBehavior::Restart,
|
||||
)
|
||||
.map_err(|e| {
|
||||
Error::from_syscall_error(e.into(), "failed to write response")
|
||||
})?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,627 @@
|
||||
#[macro_use]
|
||||
mod nodes;
|
||||
mod notifier;
|
||||
|
||||
use redox_scheme::{
|
||||
scheme::{register_scheme_inner, SchemeState, SchemeSync},
|
||||
CallerCtx, OpenResult, RequestKind, SignalBehavior, Socket,
|
||||
};
|
||||
use scheme_utils::HandleMap;
|
||||
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::BTreeMap;
|
||||
use std::mem;
|
||||
use std::rc::Rc;
|
||||
use std::str;
|
||||
use std::str::FromStr;
|
||||
use syscall;
|
||||
use syscall::data::Stat;
|
||||
use syscall::flag::{MODE_DIR, MODE_FILE};
|
||||
use syscall::schemev2::NewFdFlags;
|
||||
use syscall::{Error as SyscallError, EventFlags as SyscallEventFlags, Result as SyscallResult};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::link::DeviceList;
|
||||
use crate::router::route_table::{RouteTable, Rule};
|
||||
|
||||
use self::nodes::*;
|
||||
use self::notifier::*;
|
||||
use super::{post_fevent, Interface};
|
||||
|
||||
const WRITE_BUFFER_MAX_SIZE: usize = 0xffff;
|
||||
|
||||
fn gateway_cidr() -> IpCidr {
|
||||
// TODO: const fn
|
||||
IpCidr::new(IpAddress::v4(0, 0, 0, 0), 0)
|
||||
}
|
||||
|
||||
fn parse_route(value: &str, route_table: &RouteTable) -> SyscallResult<Rule> {
|
||||
let mut parts = value.split_whitespace();
|
||||
let cidr_str = parts.next().ok_or(SyscallError::new(syscall::EINVAL))?;
|
||||
let cidr = match cidr_str {
|
||||
"default" => gateway_cidr(),
|
||||
cidr_str => cidr_str
|
||||
.parse()
|
||||
.map_err(|_| SyscallError::new(syscall::EINVAL))?,
|
||||
};
|
||||
|
||||
let via: IpAddress = match parts.next().ok_or(SyscallError::new(syscall::EINVAL))? {
|
||||
"via" => parts
|
||||
.next()
|
||||
.ok_or(SyscallError::new(syscall::EINVAL))?
|
||||
.parse()
|
||||
.map_err(|_| SyscallError::new(syscall::EINVAL))?,
|
||||
_ => return Err(SyscallError::new(syscall::EINVAL)),
|
||||
};
|
||||
|
||||
if !via.is_unicast() {
|
||||
return Err(SyscallError::new(syscall::EINVAL));
|
||||
}
|
||||
|
||||
let rule = route_table
|
||||
.lookup_rule(&via)
|
||||
.ok_or(SyscallError::new(syscall::EINVAL))?;
|
||||
|
||||
Ok(Rule::new(cidr, Some(via), rule.dev.clone(), rule.src))
|
||||
}
|
||||
|
||||
fn mk_root_node(
|
||||
iface: Interface,
|
||||
notifier: NotifierRef,
|
||||
dns_config: DNSConfigRef,
|
||||
route_table: Rc<RefCell<RouteTable>>,
|
||||
devices: Rc<RefCell<DeviceList>>,
|
||||
) -> CfgNodeRef {
|
||||
cfg_node! {
|
||||
"resolv" => {
|
||||
"nameserver" => {
|
||||
rw [dns_config, notifier] (Option<Ipv4Address>, None)
|
||||
|| {
|
||||
format!("{}\n", dns_config.borrow().name_server)
|
||||
}
|
||||
|cur_value, line| {
|
||||
if cur_value.is_none() {
|
||||
let ip = Ipv4Address::from_str(line.trim())
|
||||
.map_err(|_| SyscallError::new(syscall::EINVAL))?;
|
||||
if ip.is_broadcast() || ip.is_multicast() || ip.is_unspecified() {
|
||||
return Err(SyscallError::new(syscall::EINVAL));
|
||||
}
|
||||
*cur_value = Some(ip);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(SyscallError::new(syscall::EINVAL))
|
||||
}
|
||||
}
|
||||
|cur_value| {
|
||||
if let Some(ip) = *cur_value {
|
||||
dns_config.borrow_mut().name_server = ip;
|
||||
notifier.borrow_mut().schedule_notify("resolv/nameserver");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
},
|
||||
"route" => {
|
||||
"list" => {
|
||||
ro [route_table] || {
|
||||
format!("{}", route_table.borrow())
|
||||
}
|
||||
},
|
||||
"add" => {
|
||||
wo [iface, notifier, route_table] (Option<Rule>, None)
|
||||
|cur_value, line| {
|
||||
if cur_value.is_none() {
|
||||
let route = parse_route(line, &route_table.borrow())?;
|
||||
*cur_value = Some(route);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(SyscallError::new(syscall::EINVAL))
|
||||
}
|
||||
}
|
||||
|cur_value| {
|
||||
if let Some(route) = cur_value.take() {
|
||||
route_table.borrow_mut().insert_rule(route);
|
||||
notifier.borrow_mut().schedule_notify("route/list");
|
||||
Ok(())
|
||||
} else {
|
||||
Err(SyscallError::new(syscall::EINVAL))
|
||||
}
|
||||
}
|
||||
},
|
||||
"rm" => {
|
||||
wo [iface, notifier, route_table] (Option<IpCidr>, None)
|
||||
|cur_value, line| {
|
||||
if cur_value.is_none() {
|
||||
match line.parse() {
|
||||
Ok(cidr) => {
|
||||
*cur_value = Some(cidr);
|
||||
Ok(())
|
||||
}
|
||||
Err(_) => Err(SyscallError::new(syscall::EINVAL))
|
||||
}
|
||||
} else {
|
||||
Err(SyscallError::new(syscall::EINVAL))
|
||||
}
|
||||
}
|
||||
|cur_value| {
|
||||
if let Some(cidr) = *cur_value {
|
||||
route_table.borrow_mut().remove_rule(cidr);
|
||||
notifier.borrow_mut().schedule_notify("route/list");
|
||||
Ok(())
|
||||
} else {
|
||||
Err(SyscallError::new(syscall::EINVAL))
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
"ifaces" => {
|
||||
"eth0" => {
|
||||
"mac" => {
|
||||
rw [iface, notifier, devices] (Option<EthernetAddress>, None)
|
||||
|| {
|
||||
match devices.borrow().get("eth0") {
|
||||
Some(dev) => {
|
||||
match dev.mac_address() {
|
||||
Some(addr) => format!("{addr}\n"),
|
||||
None => "Not configured\n".into(),
|
||||
}
|
||||
}
|
||||
None => "Device not found\n".into(),
|
||||
}
|
||||
}
|
||||
|cur_value, line| {
|
||||
if cur_value.is_none() {
|
||||
let mac = EthernetAddress::from_str(line).
|
||||
map_err(|_| SyscallError::new(syscall::EINVAL))?;
|
||||
if !mac.is_unicast() {
|
||||
return Err(SyscallError::new(syscall::EINVAL));
|
||||
}
|
||||
*cur_value = Some(mac);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(SyscallError::new(syscall::EINVAL))
|
||||
}
|
||||
}
|
||||
|cur_value| {
|
||||
if let Some(mac) = *cur_value {
|
||||
if let Some(dev) = devices.borrow_mut().get_mut("eth0") {
|
||||
dev.set_mac_address(mac);
|
||||
notifier.borrow_mut().schedule_notify("ifaces/eth0/mac");
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
},
|
||||
"addr" => {
|
||||
"list" => {
|
||||
ro [devices]
|
||||
|| {
|
||||
let res = match devices.borrow().get("eth0") {
|
||||
Some(dev) => {
|
||||
match dev.ip_address() {
|
||||
Some(addr) => format!("{addr}\n"),
|
||||
None => "Not configured\n".into(),
|
||||
}
|
||||
}
|
||||
None => "Device not found\n".into(),
|
||||
};
|
||||
res
|
||||
}
|
||||
},
|
||||
"set" => {
|
||||
wo [iface, notifier, devices, route_table] (Option<IpCidr>, None)
|
||||
|cur_value, line| {
|
||||
if cur_value.is_none() {
|
||||
let cidr = IpCidr::from_str(line)
|
||||
.map_err(|_| SyscallError::new(syscall::EINVAL))?;
|
||||
if !cidr.address().is_unicast() {
|
||||
return Err(SyscallError::new(syscall::EINVAL));
|
||||
}
|
||||
*cur_value = Some(cidr);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(SyscallError::new(syscall::EINVAL))
|
||||
}
|
||||
}
|
||||
|cur_value| {
|
||||
// TODO: Multiple IPs
|
||||
if let Some(cidr) = cur_value.take() {
|
||||
if let Some(dev) = devices.borrow_mut().get_mut("eth0") {
|
||||
|
||||
let mut route_table = route_table.borrow_mut();
|
||||
if let Some(old_addr) = dev.ip_address() {
|
||||
let IpCidr::Ipv4(old_v4_cidr) = old_addr;
|
||||
let old_network = IpCidr::Ipv4(old_v4_cidr.network());
|
||||
|
||||
route_table.remove_rule(old_network);
|
||||
route_table.change_src(old_addr.address(), cidr.address());
|
||||
iface.borrow_mut().update_ip_addrs(|addrs| addrs.retain(|addr| *addr != old_addr))
|
||||
}
|
||||
|
||||
dev.set_ip_address(cidr);
|
||||
// FIXME: Here, the insert 0 is a workaround to let UDP sockets
|
||||
// work with this interface only.
|
||||
// Smoltcp takes the first ip address when looking for a source
|
||||
// ip address when sending UDP packets.
|
||||
// This behavior will have to be fixed as it's our route table
|
||||
// job to find give this source.
|
||||
iface.borrow_mut().update_ip_addrs(|addrs| addrs.insert(0, cidr).unwrap());
|
||||
|
||||
let IpCidr::Ipv4(v4_cidr) = cidr;
|
||||
let network_cidr = IpCidr::Ipv4(v4_cidr.network());
|
||||
route_table.insert_rule(Rule::new(network_cidr, None, dev.name().clone(), cidr.address()))
|
||||
}
|
||||
notifier.borrow_mut().schedule_notify("ifaces/eth0/addr/list");
|
||||
notifier.borrow_mut().schedule_notify("route/list");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct DNSConfig {
|
||||
name_server: Ipv4Address,
|
||||
}
|
||||
|
||||
type DNSConfigRef = Rc<RefCell<DNSConfig>>;
|
||||
|
||||
struct NetCfgFile {
|
||||
path: String,
|
||||
is_dir: bool,
|
||||
is_writable: bool,
|
||||
is_readable: bool,
|
||||
node_writer: Option<Box<dyn NodeWriter>>,
|
||||
read_buf: Vec<u8>,
|
||||
write_buf: Vec<u8>,
|
||||
pos: usize,
|
||||
uid: u32,
|
||||
done: bool,
|
||||
}
|
||||
|
||||
impl NetCfgFile {
|
||||
fn commit(&mut self) -> SyscallResult<()> {
|
||||
if let Some(ref mut node_writer) = self.node_writer {
|
||||
if !self.write_buf.is_empty() {
|
||||
let line = str::from_utf8(&self.write_buf)
|
||||
.or_else(|_| Err(SyscallError::new(syscall::EINVAL)))?;
|
||||
node_writer.write_line(line)?;
|
||||
}
|
||||
node_writer.commit()?;
|
||||
self.write_buf.clear();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn consume_lines(&mut self) -> SyscallResult<()> {
|
||||
if let Some(ref mut node_writer) = self.node_writer {
|
||||
let mut swap_with = None;
|
||||
{
|
||||
let mut lines = self.write_buf.split(|&c| c == b'\n');
|
||||
if let Some(mut cur_line) = lines.next() {
|
||||
let mut consumed = false;
|
||||
for next_line in lines {
|
||||
let line = str::from_utf8(cur_line)
|
||||
.or_else(|_| Err(SyscallError::new(syscall::EINVAL)))?;
|
||||
trace!("writing line {}", line);
|
||||
node_writer.write_line(line)?;
|
||||
cur_line = next_line;
|
||||
consumed = true;
|
||||
}
|
||||
if consumed {
|
||||
swap_with = Some(From::from(cur_line))
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(ref mut new_vec) = swap_with {
|
||||
mem::swap(&mut self.write_buf, new_vec);
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
Err(SyscallError::new(syscall::EBADF))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NetCfgScheme {
|
||||
inner: NetCfgSchemeInner,
|
||||
state: SchemeState,
|
||||
}
|
||||
|
||||
impl NetCfgScheme {
|
||||
pub fn new(
|
||||
iface: Interface,
|
||||
scheme_file: Socket,
|
||||
route_table: Rc<RefCell<RouteTable>>,
|
||||
devices: Rc<RefCell<DeviceList>>,
|
||||
) -> Result<NetCfgScheme> {
|
||||
let notifier = Notifier::new_ref();
|
||||
let dns_config = Rc::new(RefCell::new(DNSConfig {
|
||||
name_server: Ipv4Address::new(8, 8, 8, 8),
|
||||
}));
|
||||
let mut inner = NetCfgSchemeInner {
|
||||
scheme_file,
|
||||
handles: HandleMap::new(),
|
||||
root_node: mk_root_node(
|
||||
iface,
|
||||
Rc::clone(¬ifier),
|
||||
dns_config,
|
||||
route_table,
|
||||
devices,
|
||||
),
|
||||
notifier,
|
||||
};
|
||||
let cap_id = inner
|
||||
.scheme_root()
|
||||
.map_err(|e| Error::from_syscall_error(e, "failed to get scheme root id"))?;
|
||||
register_scheme_inner(&inner.scheme_file, "netcfg", cap_id).map_err(|e| {
|
||||
Error::from_syscall_error(e, "failed to register netcfg scheme to namespace")
|
||||
})?;
|
||||
Ok(Self {
|
||||
inner,
|
||||
state: SchemeState::new(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn on_scheme_event(&mut self) -> Result<Option<()>> {
|
||||
let result = loop {
|
||||
let request = match self.inner.scheme_file.next_request(SignalBehavior::Restart) {
|
||||
Ok(Some(req)) => req,
|
||||
Ok(None) => {
|
||||
break Some(());
|
||||
}
|
||||
Err(error)
|
||||
if error.errno == syscall::EWOULDBLOCK || error.errno == syscall::EAGAIN =>
|
||||
{
|
||||
break None;
|
||||
}
|
||||
Err(other) => {
|
||||
return Err(Error::from_syscall_error(
|
||||
other,
|
||||
"failed to receive new request",
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
match request.kind() {
|
||||
RequestKind::Call(c) => {
|
||||
let resp = c.handle_sync(&mut self.inner, &mut self.state);
|
||||
let _ = self
|
||||
.inner
|
||||
.scheme_file
|
||||
.write_response(resp, SignalBehavior::Restart)
|
||||
.map_err(|e| {
|
||||
Error::from_syscall_error(e.into(), "failed to write response")
|
||||
})?;
|
||||
}
|
||||
RequestKind::OnClose { id } => {
|
||||
self.inner.on_close(id);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
};
|
||||
self.inner.notify_scheduled_fds();
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
enum Handle {
|
||||
SchemeRoot,
|
||||
File(NetCfgFile),
|
||||
}
|
||||
|
||||
struct NetCfgSchemeInner {
|
||||
scheme_file: Socket,
|
||||
handles: HandleMap<Handle>,
|
||||
root_node: CfgNodeRef,
|
||||
notifier: NotifierRef,
|
||||
}
|
||||
|
||||
impl NetCfgSchemeInner {
|
||||
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(&self.scheme_file, fd, syscall::EVENT_READ.bits());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SchemeSync for NetCfgSchemeInner {
|
||||
fn scheme_root(&mut self) -> SyscallResult<usize> {
|
||||
Ok(self.handles.insert(Handle::SchemeRoot))
|
||||
}
|
||||
|
||||
fn openat(
|
||||
&mut self,
|
||||
dirfd: usize,
|
||||
path: &str,
|
||||
_flags: usize,
|
||||
_fcntl_flags: u32,
|
||||
ctx: &CallerCtx,
|
||||
) -> SyscallResult<OpenResult> {
|
||||
{
|
||||
let handle = self.handles.get(dirfd)?;
|
||||
|
||||
if !matches!(handle, Handle::SchemeRoot) {
|
||||
return Err(SyscallError::new(syscall::EACCES));
|
||||
}
|
||||
}
|
||||
|
||||
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 current_node = current_node.borrow();
|
||||
let read_buf = Vec::from(current_node.read());
|
||||
let fd = self.handles.insert(Handle::File(NetCfgFile {
|
||||
path: path.to_owned(),
|
||||
is_dir: current_node.is_dir(),
|
||||
is_writable: current_node.is_writable(),
|
||||
is_readable: current_node.is_readable(),
|
||||
node_writer: if current_node.is_writable() {
|
||||
current_node.new_writer()
|
||||
} else {
|
||||
None
|
||||
},
|
||||
uid: ctx.uid,
|
||||
pos: 0,
|
||||
read_buf,
|
||||
write_buf: vec![],
|
||||
done: false,
|
||||
}));
|
||||
trace!("open {} {}", fd, path);
|
||||
Ok(OpenResult::ThisScheme {
|
||||
number: fd,
|
||||
flags: NewFdFlags::empty(),
|
||||
})
|
||||
}
|
||||
|
||||
fn on_close(&mut self, fd: usize) {
|
||||
trace!("close {}", fd);
|
||||
if let Some(handle) = self.handles.remove(fd) {
|
||||
match handle {
|
||||
Handle::SchemeRoot => {
|
||||
// SchemeRoot closed, nothing specific to clean up
|
||||
}
|
||||
Handle::File(mut file) => {
|
||||
self.notifier.borrow_mut().unsubscribe(&file.path, fd);
|
||||
if !file.done {
|
||||
let _ = file.commit().map(|_| 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write(
|
||||
&mut self,
|
||||
fd: usize,
|
||||
buf: &[u8],
|
||||
_offset: u64,
|
||||
_fcntl_flags: u32,
|
||||
_ctx: &CallerCtx,
|
||||
) -> SyscallResult<usize> {
|
||||
let handle = self.handles.get_mut(fd)?;
|
||||
|
||||
let file = match handle {
|
||||
Handle::File(file) => file,
|
||||
Handle::SchemeRoot => return Err(SyscallError::new(syscall::EBADF)),
|
||||
};
|
||||
|
||||
if file.done {
|
||||
return Err(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);
|
||||
|
||||
if let Err(e) = file.consume_lines() {
|
||||
trace!("Failed write {} {}", fd, e);
|
||||
file.done = true;
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn read(
|
||||
&mut self,
|
||||
fd: usize,
|
||||
buf: &mut [u8],
|
||||
_offset: u64,
|
||||
_fcntl_flags: u32,
|
||||
_ctx: &CallerCtx,
|
||||
) -> SyscallResult<usize> {
|
||||
let handle = self.handles.get_mut(fd)?;
|
||||
|
||||
let file = match handle {
|
||||
Handle::File(file) => file,
|
||||
Handle::SchemeRoot => return Err(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, _ctx: &CallerCtx) -> SyscallResult<()> {
|
||||
let handle = self.handles.get_mut(fd)?;
|
||||
|
||||
match handle {
|
||||
Handle::SchemeRoot => return Err(SyscallError::new(syscall::EBADF)),
|
||||
Handle::File(file) => {
|
||||
stat.st_mode = if file.is_dir { MODE_DIR } else { MODE_FILE };
|
||||
if file.is_writable {
|
||||
stat.st_mode |= 0o222;
|
||||
}
|
||||
if file.is_readable {
|
||||
stat.st_mode |= 0o444;
|
||||
}
|
||||
stat.st_uid = 0;
|
||||
stat.st_gid = 0;
|
||||
stat.st_size = file.read_buf.len() as u64;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fevent(
|
||||
&mut self,
|
||||
fd: usize,
|
||||
events: SyscallEventFlags,
|
||||
_ctx: &CallerCtx,
|
||||
) -> SyscallResult<SyscallEventFlags> {
|
||||
let handle = self.handles.get_mut(fd)?;
|
||||
|
||||
match handle {
|
||||
Handle::SchemeRoot => return Err(SyscallError::new(syscall::EBADF)),
|
||||
Handle::File(file) => {
|
||||
if events.contains(syscall::EVENT_READ) {
|
||||
self.notifier.borrow_mut().subscribe(&file.path, fd);
|
||||
} else {
|
||||
self.notifier.borrow_mut().unsubscribe(&file.path, fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(SyscallEventFlags::empty())
|
||||
}
|
||||
|
||||
fn fsync(&mut self, fd: usize, _ctx: &CallerCtx) -> SyscallResult<()> {
|
||||
let handle = self.handles.get_mut(fd)?;
|
||||
|
||||
let file = match handle {
|
||||
Handle::File(file) => file,
|
||||
Handle::SchemeRoot => return Err(SyscallError::new(syscall::EBADF)),
|
||||
};
|
||||
|
||||
if !file.done {
|
||||
let res = file.commit();
|
||||
file.done = true;
|
||||
res
|
||||
} else {
|
||||
Err(SyscallError::new(syscall::EBADF))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
use std::cell::RefCell;
|
||||
use std::collections::BTreeMap;
|
||||
use std::rc::Rc;
|
||||
use syscall::Result as SyscallResult;
|
||||
|
||||
pub type CfgNodeRef = Rc<RefCell<dyn CfgNode>>;
|
||||
|
||||
pub trait NodeWriter {
|
||||
fn write_line(&mut self, _: &str) -> SyscallResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn commit(&mut self) -> SyscallResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SimpleWriter<T, WL, C>
|
||||
where
|
||||
WL: 'static + Fn(&mut T, &str) -> SyscallResult<()>,
|
||||
C: 'static + Fn(&mut T) -> SyscallResult<()>,
|
||||
{
|
||||
data: T,
|
||||
write_line: WL,
|
||||
commit: C,
|
||||
}
|
||||
|
||||
impl<T, WL, C> NodeWriter for SimpleWriter<T, WL, C>
|
||||
where
|
||||
WL: 'static + Fn(&mut T, &str) -> SyscallResult<()>,
|
||||
C: 'static + Fn(&mut T) -> SyscallResult<()>,
|
||||
{
|
||||
fn write_line(&mut self, line: &str) -> SyscallResult<()> {
|
||||
(self.write_line)(&mut self.data, line)
|
||||
}
|
||||
|
||||
fn commit(&mut self) -> SyscallResult<()> {
|
||||
(self.commit)(&mut self.data)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, WL, C> SimpleWriter<T, WL, C>
|
||||
where
|
||||
WL: 'static + Fn(&mut T, &str) -> SyscallResult<()>,
|
||||
C: 'static + Fn(&mut T) -> SyscallResult<()>,
|
||||
{
|
||||
pub fn new_boxed(data: T, write_line: WL, commit: C) -> Box<Self> {
|
||||
Box::new(SimpleWriter {
|
||||
data,
|
||||
write_line,
|
||||
commit,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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 open(&self, _file: &str) -> Option<CfgNodeRef> {
|
||||
None
|
||||
}
|
||||
|
||||
fn new_writer(&self) -> Option<Box<dyn NodeWriter>> {
|
||||
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<W>
|
||||
where
|
||||
W: 'static + Fn() -> Box<dyn NodeWriter>,
|
||||
{
|
||||
new_writer: W,
|
||||
}
|
||||
|
||||
impl<W> CfgNode for WONode<W>
|
||||
where
|
||||
W: 'static + Fn() -> Box<dyn NodeWriter>,
|
||||
{
|
||||
fn is_readable(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn is_writable(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn new_writer(&self) -> Option<Box<dyn NodeWriter>> {
|
||||
Some((self.new_writer)())
|
||||
}
|
||||
}
|
||||
|
||||
impl<W> WONode<W>
|
||||
where
|
||||
W: 'static + Fn() -> Box<dyn NodeWriter>,
|
||||
{
|
||||
pub fn new_ref(new_writer: W) -> CfgNodeRef {
|
||||
Rc::new(RefCell::new(WONode { new_writer }))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RWNode<F, W>
|
||||
where
|
||||
F: Fn() -> String,
|
||||
W: 'static + Fn() -> Box<dyn NodeWriter>,
|
||||
{
|
||||
read_fun: F,
|
||||
new_writer: W,
|
||||
}
|
||||
|
||||
impl<F, W> CfgNode for RWNode<F, W>
|
||||
where
|
||||
F: Fn() -> String,
|
||||
W: 'static + Fn() -> Box<dyn NodeWriter>,
|
||||
{
|
||||
fn read(&self) -> String {
|
||||
(self.read_fun)()
|
||||
}
|
||||
|
||||
fn is_writable(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn new_writer(&self) -> Option<Box<dyn NodeWriter>> {
|
||||
Some((self.new_writer)())
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, W> RWNode<F, W>
|
||||
where
|
||||
F: 'static + Fn() -> String,
|
||||
W: 'static + Fn() -> Box<dyn NodeWriter>,
|
||||
{
|
||||
pub fn new_ref(read_fun: F, new_writer: W) -> CfgNodeRef {
|
||||
Rc::new(RefCell::new(RWNode {
|
||||
read_fun,
|
||||
new_writer,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
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),* ] ( $et:ty , $e:expr ) |$data_i:ident, $line_i:ident|
|
||||
$write_line:block |$data_i2:ident| $commit:block) => {
|
||||
{
|
||||
$(#[allow(unused_variables)] let $c = $c.clone();)*
|
||||
let new_writer = move || -> Box<dyn NodeWriter> {
|
||||
let write_line = {
|
||||
$(#[allow(unused_variables)] let $c = $c.clone();)*
|
||||
move |$data_i: &mut $et, $line_i: &str| $write_line
|
||||
};
|
||||
let commit = {
|
||||
$(#[allow(unused_variables)] let $c = $c.clone();)*
|
||||
move |$data_i2: &mut $et| $commit
|
||||
};
|
||||
let data: $et = $e;
|
||||
SimpleWriter::new_boxed(data, write_line, commit)
|
||||
};
|
||||
WONode::new_ref(new_writer)
|
||||
}
|
||||
};
|
||||
(rw [ $($c:ident),* ] ( $et:ty , $e:expr ) || $read_fun:block |$data_i:ident, $line_i:ident|
|
||||
$write_line:block |$data_i2:ident| $commit:block) => {
|
||||
{
|
||||
let read_fun = {
|
||||
$(#[allow(unused_variables)] let $c = $c.clone();)*
|
||||
move || $read_fun
|
||||
};
|
||||
$(#[allow(unused_variables)] let $c = $c.clone();)*
|
||||
let new_writer = move || -> Box<dyn NodeWriter> {
|
||||
let write_line = {
|
||||
$(#[allow(unused_variables)] let $c = $c.clone();)*
|
||||
move |$data_i: &mut $et, $line_i: &str| $write_line
|
||||
};
|
||||
let commit = {
|
||||
$(#[allow(unused_variables)] let $c = $c.clone();)*
|
||||
move |$data_i2: &mut $et| $commit
|
||||
};
|
||||
let data: $et = $e;
|
||||
SimpleWriter::new_boxed(data, write_line, commit)
|
||||
};
|
||||
RWNode::new_ref(read_fun, new_writer)
|
||||
}
|
||||
};
|
||||
($($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::cell::RefCell;
|
||||
use std::collections::btree_map::Entry;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::rc::Rc;
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,942 @@
|
||||
use std::cell::RefCell;
|
||||
use std::collections::btree_map::Entry;
|
||||
use std::collections::BTreeMap;
|
||||
use std::io::{Read, Write};
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::ops::Deref;
|
||||
use std::ops::DerefMut;
|
||||
use std::rc::Rc;
|
||||
use std::str;
|
||||
|
||||
use libredox::flag::CLOCK_MONOTONIC;
|
||||
use libredox::protocol::SocketCall;
|
||||
use redox_scheme::{
|
||||
scheme::{register_scheme_inner, Op, SchemeSync},
|
||||
CallerCtx, OpenResult, Socket,
|
||||
};
|
||||
use scheme_utils::HandleMap;
|
||||
use syscall::data::TimeSpec;
|
||||
use syscall::flag::{EVENT_READ, EVENT_WRITE};
|
||||
use syscall::schemev2::NewFdFlags;
|
||||
use syscall::{
|
||||
Error as SyscallError, EventFlags as SyscallEventFlags, Result as SyscallResult, EINVAL,
|
||||
EOPNOTSUPP,
|
||||
};
|
||||
|
||||
use super::Interface;
|
||||
use crate::router::route_table::RouteTable;
|
||||
use crate::scheme::smoltcp::iface::SocketHandle;
|
||||
use crate::scheme::Router;
|
||||
use crate::Smolnetd;
|
||||
use smoltcp::socket::AnySocket;
|
||||
|
||||
use super::SocketSet;
|
||||
|
||||
const SO_RCVBUF: usize = 8;
|
||||
const SO_SNDBUF: usize = 7;
|
||||
|
||||
pub struct Context {
|
||||
pub iface: Interface,
|
||||
pub route_table: Rc<RefCell<RouteTable>>,
|
||||
}
|
||||
|
||||
pub struct NullFile {
|
||||
pub flags: usize,
|
||||
pub uid: u32,
|
||||
pub gid: u32,
|
||||
pub read_enabled: bool,
|
||||
pub write_enabled: bool,
|
||||
}
|
||||
|
||||
pub struct SocketFile<DataT> {
|
||||
pub flags: usize,
|
||||
pub data: DataT,
|
||||
|
||||
events: usize,
|
||||
socket_handle: SocketHandle,
|
||||
read_notified: bool,
|
||||
write_notified: bool,
|
||||
read_timeout: Option<TimeSpec>,
|
||||
write_timeout: Option<TimeSpec>,
|
||||
pub read_enabled: bool,
|
||||
pub write_enabled: bool,
|
||||
}
|
||||
|
||||
impl<DataT> SocketFile<DataT> {
|
||||
pub fn clone_with_data(&self, data: DataT) -> SocketFile<DataT> {
|
||||
SocketFile {
|
||||
flags: self.flags,
|
||||
events: self.events,
|
||||
read_notified: false, // we still want to notify about this new socket
|
||||
write_notified: false,
|
||||
read_timeout: self.read_timeout,
|
||||
write_timeout: self.write_timeout,
|
||||
socket_handle: self.socket_handle,
|
||||
read_enabled: self.read_enabled,
|
||||
write_enabled: self.write_enabled,
|
||||
data,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_data(socket_handle: SocketHandle, data: DataT) -> SocketFile<DataT> {
|
||||
SocketFile {
|
||||
flags: 0,
|
||||
events: 0,
|
||||
read_notified: false,
|
||||
write_notified: false,
|
||||
read_timeout: None,
|
||||
write_timeout: None,
|
||||
read_enabled: true,
|
||||
write_enabled: true,
|
||||
socket_handle,
|
||||
data,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum Setting<SettingT: Copy> {
|
||||
HopLimit,
|
||||
ReadTimeout,
|
||||
WriteTimeout,
|
||||
#[allow(dead_code)]
|
||||
Other(SettingT),
|
||||
}
|
||||
|
||||
pub struct SettingFile<SettingT: Copy> {
|
||||
fd: usize,
|
||||
socket_handle: SocketHandle,
|
||||
setting: Setting<SettingT>,
|
||||
}
|
||||
|
||||
pub enum SchemeFile<SocketT>
|
||||
where
|
||||
SocketT: SchemeSocket,
|
||||
{
|
||||
Setting(SettingFile<SocketT::SettingT>),
|
||||
Socket(SocketFile<SocketT::DataT>),
|
||||
}
|
||||
|
||||
impl<SocketT> SchemeFile<SocketT>
|
||||
where
|
||||
SocketT: SchemeSocket,
|
||||
{
|
||||
pub fn socket_handle(&self) -> SocketHandle {
|
||||
match *self {
|
||||
SchemeFile::Socket(SocketFile { socket_handle, .. })
|
||||
| SchemeFile::Setting(SettingFile { socket_handle, .. }) => socket_handle,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn events(&mut self, socket_set: &mut SocketSet) -> usize
|
||||
where
|
||||
SocketT: AnySocket<'static>,
|
||||
{
|
||||
let mut revents = 0;
|
||||
if let &mut SchemeFile::Socket(SocketFile {
|
||||
socket_handle,
|
||||
events,
|
||||
ref mut read_notified,
|
||||
ref mut write_notified,
|
||||
ref data,
|
||||
..
|
||||
}) = self
|
||||
{
|
||||
let socket = socket_set.get_mut::<SocketT>(socket_handle);
|
||||
|
||||
if events & syscall::EVENT_READ.bits() == syscall::EVENT_READ.bits()
|
||||
&& (socket.can_recv(data) || !socket.may_recv())
|
||||
{
|
||||
if !*read_notified {
|
||||
*read_notified = true;
|
||||
revents |= EVENT_READ.bits();
|
||||
}
|
||||
} else {
|
||||
*read_notified = false;
|
||||
}
|
||||
|
||||
if events & syscall::EVENT_WRITE.bits() == syscall::EVENT_WRITE.bits()
|
||||
&& socket.can_send()
|
||||
{
|
||||
if !*write_notified {
|
||||
*write_notified = true;
|
||||
revents |= EVENT_WRITE.bits();
|
||||
}
|
||||
} else {
|
||||
*write_notified = false;
|
||||
}
|
||||
}
|
||||
revents
|
||||
}
|
||||
}
|
||||
|
||||
pub type DupResult<T> = Option<(
|
||||
SchemeFile<T>,
|
||||
Option<(SocketHandle, <T as SchemeSocket>::DataT)>,
|
||||
)>;
|
||||
|
||||
pub trait SchemeSocket
|
||||
where
|
||||
Self: ::std::marker::Sized,
|
||||
{
|
||||
type SchemeDataT;
|
||||
type DataT;
|
||||
type SettingT: Copy;
|
||||
|
||||
fn new_scheme_data() -> Self::SchemeDataT;
|
||||
|
||||
fn can_send(&self) -> bool;
|
||||
fn can_recv(&mut self, data: &Self::DataT) -> bool;
|
||||
fn may_recv(&self) -> bool;
|
||||
|
||||
fn hop_limit(&self) -> u8;
|
||||
fn set_hop_limit(&mut self, hop_limit: u8);
|
||||
|
||||
fn get_setting(
|
||||
file: &SocketFile<Self::DataT>,
|
||||
setting: Self::SettingT,
|
||||
buf: &mut [u8],
|
||||
) -> SyscallResult<usize>;
|
||||
fn set_setting(
|
||||
file: &mut SocketFile<Self::DataT>,
|
||||
setting: Self::SettingT,
|
||||
buf: &[u8],
|
||||
) -> SyscallResult<usize>;
|
||||
|
||||
fn new_socket(
|
||||
sockets: &mut SocketSet,
|
||||
path: &str,
|
||||
uid: u32,
|
||||
data: &mut Self::SchemeDataT,
|
||||
context: &Context,
|
||||
) -> SyscallResult<(SocketHandle, Self::DataT)>;
|
||||
|
||||
fn close_file(
|
||||
&self,
|
||||
file: &SchemeFile<Self>,
|
||||
data: &mut Self::SchemeDataT,
|
||||
) -> SyscallResult<()>;
|
||||
|
||||
fn write_buf(&mut self, file: &mut SocketFile<Self::DataT>, buf: &[u8])
|
||||
-> SyscallResult<usize>;
|
||||
|
||||
fn read_buf(
|
||||
&mut self,
|
||||
file: &mut SocketFile<Self::DataT>,
|
||||
buf: &mut [u8],
|
||||
) -> SyscallResult<usize>;
|
||||
|
||||
fn fpath(&self, file: &SchemeFile<Self>, buf: &mut [u8]) -> SyscallResult<usize>;
|
||||
|
||||
fn dup(
|
||||
sockets: &mut SocketSet,
|
||||
file: &mut SchemeFile<Self>,
|
||||
path: &str,
|
||||
data: &mut Self::SchemeDataT,
|
||||
) -> SyscallResult<DupResult<Self>>;
|
||||
|
||||
fn handle_get_peer_name(
|
||||
&self,
|
||||
file: &SchemeFile<Self>,
|
||||
payload: &mut [u8],
|
||||
) -> SyscallResult<usize>;
|
||||
|
||||
fn handle_shutdown(&mut self, file: &mut SchemeFile<Self>, how: usize) -> SyscallResult<usize>;
|
||||
|
||||
fn handle_recvmsg(
|
||||
&mut self,
|
||||
file: &mut SchemeFile<Self>,
|
||||
payload: &mut [u8],
|
||||
flags: usize,
|
||||
) -> SyscallResult<usize>;
|
||||
|
||||
fn get_sock_opt(
|
||||
&self,
|
||||
file: &SchemeFile<Self>,
|
||||
name: usize,
|
||||
buf: &mut [u8],
|
||||
) -> SyscallResult<usize> {
|
||||
// Return Err for default implementation
|
||||
Err(SyscallError::new(syscall::ENOPROTOOPT))
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Handle<SocketT>
|
||||
where
|
||||
SocketT: SchemeSocket,
|
||||
{
|
||||
SchemeRoot,
|
||||
Null(NullFile),
|
||||
File(SchemeFile<SocketT>),
|
||||
}
|
||||
|
||||
pub struct SocketScheme<SocketT>
|
||||
where
|
||||
SocketT: SchemeSocket + AnySocket<'static>,
|
||||
{
|
||||
pub handles: HandleMap<Handle<SocketT>>,
|
||||
ref_counts: BTreeMap<SocketHandle, usize>,
|
||||
context: Context,
|
||||
pub socket_set: Rc<RefCell<SocketSet>>,
|
||||
pub scheme_file: Socket,
|
||||
scheme_data: SocketT::SchemeDataT,
|
||||
_phantom_socket: PhantomData<SocketT>,
|
||||
}
|
||||
|
||||
impl<SocketT> SocketScheme<SocketT>
|
||||
where
|
||||
SocketT: SchemeSocket + AnySocket<'static>,
|
||||
{
|
||||
pub fn new(
|
||||
name: &str,
|
||||
iface: Interface,
|
||||
route_table: Rc<RefCell<RouteTable>>,
|
||||
socket_set: Rc<RefCell<SocketSet>>,
|
||||
scheme_file: Socket,
|
||||
) -> SyscallResult<SocketScheme<SocketT>> {
|
||||
let mut scheme = SocketScheme {
|
||||
handles: HandleMap::new(),
|
||||
ref_counts: BTreeMap::new(),
|
||||
socket_set,
|
||||
scheme_data: SocketT::new_scheme_data(),
|
||||
scheme_file,
|
||||
_phantom_socket: PhantomData,
|
||||
context: Context { iface, route_table },
|
||||
};
|
||||
let cap_id = scheme.scheme_root()?;
|
||||
register_scheme_inner(&scheme.scheme_file, name, cap_id)?;
|
||||
Ok(scheme)
|
||||
}
|
||||
|
||||
pub fn handle_block(&mut self, op: &Op) -> SyscallResult<Option<TimeSpec>> {
|
||||
let fd = op.file_id().expect("op is not fd based request");
|
||||
let (read_timeout, write_timeout) = {
|
||||
let handle = self.handles.get(fd)?;
|
||||
|
||||
if let Handle::File(SchemeFile::Socket(ref scheme_file)) = *handle {
|
||||
Ok((scheme_file.read_timeout, scheme_file.write_timeout))
|
||||
} else {
|
||||
Err(SyscallError::new(syscall::EBADF))
|
||||
}
|
||||
}?;
|
||||
|
||||
let mut timeout = match op {
|
||||
Op::Read(_) => write_timeout,
|
||||
Op::Write(_) => read_timeout,
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some(ref mut timeout) = timeout {
|
||||
let cur_time = libredox::call::clock_gettime(CLOCK_MONOTONIC)?;
|
||||
*timeout = add_time(
|
||||
timeout,
|
||||
&TimeSpec {
|
||||
tv_sec: cur_time.tv_sec,
|
||||
tv_nsec: cur_time.tv_nsec as i32,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
Ok(timeout)
|
||||
}
|
||||
|
||||
fn get_setting(
|
||||
&mut self,
|
||||
fd: usize,
|
||||
setting: Setting<SocketT::SettingT>,
|
||||
buf: &mut [u8],
|
||||
) -> SyscallResult<usize> {
|
||||
let file = self.handles.get_mut(fd)?;
|
||||
|
||||
let file = match *file {
|
||||
Handle::File(SchemeFile::Socket(ref mut file)) => file,
|
||||
_ => return Err(SyscallError::new(syscall::EBADF)),
|
||||
};
|
||||
|
||||
match setting {
|
||||
Setting::Other(setting) => SocketT::get_setting(file, setting, buf),
|
||||
Setting::HopLimit => {
|
||||
if let Some(hop_limit) = buf.get_mut(0) {
|
||||
let socket_set = self.socket_set.borrow();
|
||||
let socket = socket_set.get::<SocketT>(file.socket_handle);
|
||||
*hop_limit = socket.hop_limit();
|
||||
Ok(1)
|
||||
} else {
|
||||
Err(SyscallError::new(syscall::EIO))
|
||||
}
|
||||
}
|
||||
Setting::ReadTimeout | Setting::WriteTimeout => {
|
||||
let timespec = match (setting, file.read_timeout, file.write_timeout) {
|
||||
(Setting::ReadTimeout, Some(read_timeout), _) => read_timeout,
|
||||
(Setting::WriteTimeout, _, Some(write_timeout)) => write_timeout,
|
||||
_ => {
|
||||
return Ok(0);
|
||||
}
|
||||
};
|
||||
|
||||
if buf.len() < mem::size_of::<TimeSpec>() {
|
||||
Ok(0)
|
||||
} else {
|
||||
let count = timespec.deref().read(buf).map_err(|err| {
|
||||
SyscallError::new(err.raw_os_error().unwrap_or(syscall::EIO))
|
||||
})?;
|
||||
Ok(count)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn update_setting(
|
||||
&mut self,
|
||||
fd: usize,
|
||||
setting: Setting<SocketT::SettingT>,
|
||||
buf: &[u8],
|
||||
) -> SyscallResult<usize> {
|
||||
let file = self.handles.get_mut(fd)?;
|
||||
let file = match *file {
|
||||
Handle::File(SchemeFile::Socket(ref mut file)) => file,
|
||||
_ => return Err(SyscallError::new(syscall::EBADF)),
|
||||
};
|
||||
match setting {
|
||||
Setting::ReadTimeout | Setting::WriteTimeout => {
|
||||
let (timeout, count) = {
|
||||
if buf.len() < mem::size_of::<TimeSpec>() {
|
||||
(None, 0)
|
||||
} else {
|
||||
let mut timespec = TimeSpec::default();
|
||||
let count = timespec.deref_mut().write(buf).map_err(|err| {
|
||||
SyscallError::new(err.raw_os_error().unwrap_or(syscall::EIO))
|
||||
})?;
|
||||
(Some(timespec), count)
|
||||
}
|
||||
};
|
||||
match setting {
|
||||
Setting::ReadTimeout => {
|
||||
file.read_timeout = timeout;
|
||||
}
|
||||
Setting::WriteTimeout => {
|
||||
file.write_timeout = timeout;
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
Ok(count)
|
||||
}
|
||||
Setting::HopLimit => {
|
||||
if let Some(hop_limit) = buf.get(0) {
|
||||
let mut socket_set = self.socket_set.borrow_mut();
|
||||
let socket = socket_set.get_mut::<SocketT>(file.socket_handle);
|
||||
socket.set_hop_limit(*hop_limit);
|
||||
Ok(1)
|
||||
} else {
|
||||
Err(SyscallError::new(syscall::EIO))
|
||||
}
|
||||
}
|
||||
Setting::Other(setting) => SocketT::set_setting(file, setting, buf),
|
||||
}
|
||||
}
|
||||
|
||||
fn call_inner(
|
||||
&mut self,
|
||||
fd: usize,
|
||||
payload: &mut [u8],
|
||||
metadata: &[u64],
|
||||
ctx: &CallerCtx,
|
||||
) -> SyscallResult<usize> {
|
||||
// metadata to Vec<u8>
|
||||
let Some(verb) = SocketCall::try_from_raw(metadata[0] as usize) else {
|
||||
warn!("Invalid verb in metadata: {:?}", metadata);
|
||||
return Err(SyscallError::new(EINVAL));
|
||||
};
|
||||
match verb {
|
||||
// TODO
|
||||
// SocketCall::Bind => self.handle_bind(id, &payload),
|
||||
// SocketCall::Connect => self.handle_connect(id, &payload),
|
||||
SocketCall::SetSockOpt => {
|
||||
// currently not used
|
||||
// self.handle_setsockopt(id, metadata[1] as i32, &payload)
|
||||
// TODO: SO_REUSEADDR from null socket
|
||||
Ok(0)
|
||||
}
|
||||
SocketCall::GetSockOpt => {
|
||||
let handle = self.handles.get_mut(fd)?;
|
||||
|
||||
match *handle {
|
||||
Handle::File(ref mut file) => {
|
||||
let mut socket_set = self.socket_set.borrow_mut();
|
||||
let socket = socket_set.get_mut::<SocketT>(file.socket_handle());
|
||||
SocketT::get_sock_opt(socket, file, metadata[1] as usize, payload)
|
||||
}
|
||||
Handle::Null(_) => {
|
||||
// TODO
|
||||
// The socket exists but hasn't been bound/connected yet.
|
||||
// We return default values for buffer sizes to satisfy apps like iperf3.
|
||||
// Figure out maybe a better way?
|
||||
let name = metadata[1] as usize;
|
||||
if name == SO_RCVBUF || name == SO_SNDBUF {
|
||||
let val: i32 = (Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE) as i32;
|
||||
let bytes = val.to_ne_bytes();
|
||||
|
||||
if payload.len() < bytes.len() {
|
||||
return Err(SyscallError::new(syscall::EINVAL));
|
||||
}
|
||||
payload[..bytes.len()].copy_from_slice(&bytes);
|
||||
Ok(bytes.len())
|
||||
} else {
|
||||
Err(SyscallError::new(syscall::EINVAL))
|
||||
}
|
||||
}
|
||||
Handle::SchemeRoot => Err(SyscallError::new(syscall::EBADF)),
|
||||
}
|
||||
}
|
||||
// SocketCall::SendMsg => self.handle_sendmsg(id, payload, ctx),
|
||||
// SocketCall::Unbind => self.handle_unbind(id),
|
||||
// SocketCall::GetToken => self.handle_get_token(id, payload),
|
||||
SocketCall::GetPeerName => {
|
||||
let file = self.handles.get_mut(fd)?;
|
||||
|
||||
let file = match *file {
|
||||
Handle::File(ref mut f) => f,
|
||||
_ => return Err(SyscallError::new(syscall::EBADF)),
|
||||
};
|
||||
let mut socket_set = self.socket_set.borrow_mut();
|
||||
let socket = socket_set.get_mut::<SocketT>(file.socket_handle());
|
||||
|
||||
SocketT::handle_get_peer_name(socket, file, payload)
|
||||
}
|
||||
SocketCall::RecvMsg => {
|
||||
let flags = metadata[1] as usize;
|
||||
let handle = self.handles.get_mut(fd)?;
|
||||
|
||||
match *handle {
|
||||
Handle::File(ref mut file) => {
|
||||
let mut socket_set = self.socket_set.borrow_mut();
|
||||
let socket = socket_set.get_mut::<SocketT>(file.socket_handle());
|
||||
|
||||
SocketT::handle_recvmsg(socket, file, payload, flags)
|
||||
}
|
||||
Handle::Null(_) => Err(SyscallError::new(syscall::EINVAL)),
|
||||
Handle::SchemeRoot => Err(SyscallError::new(syscall::EBADF)),
|
||||
}
|
||||
}
|
||||
|
||||
SocketCall::Shutdown => {
|
||||
let how = metadata[1] as usize;
|
||||
|
||||
match self.handles.get_mut(fd)? {
|
||||
Handle::File(file) => {
|
||||
let mut socket_set = self.socket_set.borrow_mut();
|
||||
let socket = socket_set.get_mut::<SocketT>(file.socket_handle());
|
||||
|
||||
SocketT::handle_shutdown(socket, file, how)
|
||||
}
|
||||
Handle::Null(null_file) => {
|
||||
match how {
|
||||
0 => null_file.read_enabled = false,
|
||||
1 => null_file.write_enabled = false,
|
||||
2 => {
|
||||
null_file.read_enabled = false;
|
||||
null_file.write_enabled = false;
|
||||
}
|
||||
_ => return Err(SyscallError::new(EINVAL)),
|
||||
}
|
||||
Ok(0)
|
||||
}
|
||||
Handle::SchemeRoot => Err(SyscallError::new(syscall::EBADF)),
|
||||
}
|
||||
}
|
||||
_ => Err(SyscallError::new(EOPNOTSUPP)),
|
||||
}
|
||||
}
|
||||
|
||||
fn open_inner(
|
||||
&mut self,
|
||||
path: &str,
|
||||
flags: usize,
|
||||
uid: u32,
|
||||
gid: u32,
|
||||
read_enabled: bool,
|
||||
write_enabled: bool,
|
||||
) -> SyscallResult<OpenResult> {
|
||||
if path.is_empty() {
|
||||
let null = NullFile {
|
||||
flags,
|
||||
uid,
|
||||
gid,
|
||||
read_enabled,
|
||||
write_enabled,
|
||||
};
|
||||
|
||||
let id = self.handles.insert(Handle::Null(null));
|
||||
|
||||
Ok(OpenResult::ThisScheme {
|
||||
number: id,
|
||||
flags: NewFdFlags::empty(),
|
||||
})
|
||||
} else {
|
||||
let (socket_handle, data) = SocketT::new_socket(
|
||||
&mut self.socket_set.borrow_mut(),
|
||||
path,
|
||||
uid,
|
||||
&mut self.scheme_data,
|
||||
&self.context,
|
||||
)?;
|
||||
|
||||
let file = SchemeFile::Socket(SocketFile {
|
||||
flags,
|
||||
events: 0,
|
||||
socket_handle,
|
||||
read_notified: false,
|
||||
write_notified: false,
|
||||
write_timeout: None,
|
||||
read_timeout: None,
|
||||
read_enabled: read_enabled,
|
||||
write_enabled: write_enabled,
|
||||
data,
|
||||
});
|
||||
|
||||
self.ref_counts.insert(socket_handle, 1);
|
||||
let id = self.handles.insert(Handle::File(file));
|
||||
|
||||
Ok(OpenResult::ThisScheme {
|
||||
number: id,
|
||||
flags: NewFdFlags::empty(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<SocketT> SchemeSync for SocketScheme<SocketT>
|
||||
where
|
||||
SocketT: SchemeSocket + AnySocket<'static>,
|
||||
{
|
||||
fn scheme_root(&mut self) -> SyscallResult<usize> {
|
||||
Ok(self.handles.insert(Handle::SchemeRoot))
|
||||
}
|
||||
|
||||
fn openat(
|
||||
&mut self,
|
||||
fd: usize,
|
||||
path: &str,
|
||||
flags: usize,
|
||||
_fcntl_flags: u32,
|
||||
ctx: &CallerCtx,
|
||||
) -> SyscallResult<OpenResult> {
|
||||
match self.handles.get(fd)? {
|
||||
Handle::SchemeRoot => self.open_inner(path, flags, ctx.uid, ctx.gid, true, true),
|
||||
_ => Err(SyscallError::new(syscall::EACCES)),
|
||||
}
|
||||
}
|
||||
|
||||
fn call(
|
||||
&mut self,
|
||||
id: usize,
|
||||
payload: &mut [u8],
|
||||
metadata: &[u64],
|
||||
ctx: &CallerCtx,
|
||||
) -> SyscallResult<usize> {
|
||||
self.call_inner(id, payload, metadata, ctx)
|
||||
}
|
||||
|
||||
fn on_close(&mut self, fd: usize) {
|
||||
let Some(handle) = self.handles.remove(fd) else {
|
||||
return;
|
||||
};
|
||||
|
||||
// incorrect, and kernel can't send close until all references are gone
|
||||
/*self.wait_queue.retain(
|
||||
|&WaitHandle {
|
||||
packet: SyscallPacket { a, .. },
|
||||
..
|
||||
}| a != fd,
|
||||
);*/
|
||||
|
||||
match handle {
|
||||
Handle::SchemeRoot => return,
|
||||
Handle::Null(_) => return,
|
||||
Handle::File(scheme_file) => {
|
||||
let socket_handle = scheme_file.socket_handle();
|
||||
let mut socket_set = self.socket_set.borrow_mut();
|
||||
|
||||
let socket = socket_set.get::<SocketT>(socket_handle);
|
||||
let _ = socket.close_file(&scheme_file, &mut self.scheme_data);
|
||||
|
||||
let remove = match self.ref_counts.entry(socket_handle) {
|
||||
Entry::Vacant(_) => {
|
||||
warn!("Closing a socket_handle with no ref");
|
||||
true
|
||||
}
|
||||
Entry::Occupied(mut e) => {
|
||||
if *e.get() == 0 {
|
||||
warn!("Closing a socket_handle with no ref");
|
||||
e.remove();
|
||||
true
|
||||
} else {
|
||||
*e.get_mut() -= 1;
|
||||
if *e.get() == 0 {
|
||||
e.remove();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if remove {
|
||||
socket_set.remove(socket_handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write(
|
||||
&mut self,
|
||||
fd: usize,
|
||||
buf: &[u8],
|
||||
_offset: u64,
|
||||
_fcntl_flags: u32,
|
||||
_ctx: &CallerCtx,
|
||||
) -> SyscallResult<usize> {
|
||||
let (fd, setting) = {
|
||||
let file = self.handles.get_mut(fd)?;
|
||||
|
||||
match *file {
|
||||
Handle::File(SchemeFile::Setting(ref setting_handle)) => {
|
||||
(setting_handle.fd, setting_handle.setting)
|
||||
}
|
||||
Handle::File(SchemeFile::Socket(ref mut file)) => {
|
||||
let mut socket_set = self.socket_set.borrow_mut();
|
||||
let socket = socket_set.get_mut::<SocketT>(file.socket_handle);
|
||||
let ret = SocketT::write_buf(socket, file, buf);
|
||||
match ret {
|
||||
Err(e) if e.errno == syscall::EWOULDBLOCK || e.errno == syscall::EAGAIN => {
|
||||
}
|
||||
_ => file.write_notified = false,
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
_ => return Err(SyscallError::new(syscall::EBADF)),
|
||||
}
|
||||
};
|
||||
self.update_setting(fd, setting, buf)
|
||||
}
|
||||
|
||||
fn read(
|
||||
&mut self,
|
||||
fd: usize,
|
||||
buf: &mut [u8],
|
||||
_offset: u64,
|
||||
_fcntl_flags: u32,
|
||||
_ctx: &CallerCtx,
|
||||
) -> SyscallResult<usize> {
|
||||
let (fd, setting) = {
|
||||
let file = self.handles.get_mut(fd)?;
|
||||
match *file {
|
||||
Handle::File(SchemeFile::Setting(ref setting_handle)) => {
|
||||
(setting_handle.fd, setting_handle.setting)
|
||||
}
|
||||
Handle::File(SchemeFile::Socket(ref mut file)) => {
|
||||
let mut socket_set = self.socket_set.borrow_mut();
|
||||
let socket = socket_set.get_mut::<SocketT>(file.socket_handle);
|
||||
|
||||
let ret = SocketT::read_buf(socket, file, buf);
|
||||
match ret {
|
||||
Err(e) if e.errno == syscall::EWOULDBLOCK || e.errno == syscall::EAGAIN => {
|
||||
}
|
||||
_ => file.read_notified = false,
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
_ => return Err(SyscallError::new(syscall::EBADF)),
|
||||
}
|
||||
};
|
||||
self.get_setting(fd, setting, buf)
|
||||
}
|
||||
|
||||
fn dup(&mut self, fd: usize, buf: &[u8], _ctx: &CallerCtx) -> SyscallResult<OpenResult> {
|
||||
let path = str::from_utf8(buf).or_else(|_| Err(SyscallError::new(syscall::EINVAL)))?;
|
||||
|
||||
let new_file = {
|
||||
let handle = self.handles.get_mut(fd)?;
|
||||
|
||||
let file = match *handle {
|
||||
Handle::SchemeRoot => return Err(SyscallError::new(syscall::EBADF)),
|
||||
Handle::Null(ref null) => {
|
||||
let (flags, uid, gid, read_enabled, write_enabled) = (
|
||||
null.flags,
|
||||
null.uid,
|
||||
null.gid,
|
||||
null.read_enabled,
|
||||
null.write_enabled,
|
||||
);
|
||||
// dup from empty path to a new path
|
||||
return self.open_inner(path, flags, uid, gid, read_enabled, write_enabled);
|
||||
}
|
||||
Handle::File(ref mut file) => file,
|
||||
};
|
||||
|
||||
let socket_handle = file.socket_handle();
|
||||
|
||||
let (new_handle, update_with) = match path {
|
||||
"hop_limit" => (
|
||||
SchemeFile::Setting(SettingFile {
|
||||
socket_handle,
|
||||
fd,
|
||||
setting: Setting::HopLimit,
|
||||
}),
|
||||
None,
|
||||
),
|
||||
"read_timeout" => (
|
||||
SchemeFile::Setting(SettingFile {
|
||||
socket_handle,
|
||||
fd,
|
||||
setting: Setting::ReadTimeout,
|
||||
}),
|
||||
None,
|
||||
),
|
||||
"write_timeout" => (
|
||||
SchemeFile::Setting(SettingFile {
|
||||
socket_handle,
|
||||
fd,
|
||||
setting: Setting::WriteTimeout,
|
||||
}),
|
||||
None,
|
||||
),
|
||||
_ => match SocketT::dup(
|
||||
&mut self.socket_set.borrow_mut(),
|
||||
file,
|
||||
path,
|
||||
&mut self.scheme_data,
|
||||
)? {
|
||||
Some(some) => some,
|
||||
None => return Err(SyscallError::new(syscall::EWOULDBLOCK)),
|
||||
},
|
||||
};
|
||||
|
||||
if let Some((socket_handle, data)) = update_with {
|
||||
if let SchemeFile::Socket(ref mut file) = *file {
|
||||
// We replace the socket_handle pointed by file so update the ref_counts
|
||||
// accordingly
|
||||
self.ref_counts
|
||||
.entry(file.socket_handle)
|
||||
.and_modify(|e| *e = e.saturating_sub(1))
|
||||
.or_insert(0);
|
||||
|
||||
*self
|
||||
.ref_counts
|
||||
.entry(new_handle.socket_handle())
|
||||
.or_insert(0) += 1;
|
||||
|
||||
file.socket_handle = socket_handle;
|
||||
file.data = data;
|
||||
}
|
||||
}
|
||||
*self
|
||||
.ref_counts
|
||||
.entry(new_handle.socket_handle())
|
||||
.or_insert(0) += 1;
|
||||
new_handle
|
||||
};
|
||||
|
||||
let id = self.handles.insert(Handle::File(new_file));
|
||||
|
||||
Ok(OpenResult::ThisScheme {
|
||||
number: id,
|
||||
flags: NewFdFlags::empty(),
|
||||
})
|
||||
}
|
||||
|
||||
fn fevent(
|
||||
&mut self,
|
||||
fd: usize,
|
||||
events: SyscallEventFlags,
|
||||
_ctx: &CallerCtx,
|
||||
) -> SyscallResult<SyscallEventFlags> {
|
||||
let file = self.handles.get_mut(fd)?;
|
||||
|
||||
let file = match *file {
|
||||
Handle::File(ref mut f) => f,
|
||||
_ => return Err(SyscallError::new(syscall::EBADF)),
|
||||
};
|
||||
|
||||
match *file {
|
||||
SchemeFile::Setting(_) => return Err(SyscallError::new(syscall::EBADF)),
|
||||
SchemeFile::Socket(ref mut file) => {
|
||||
file.events = events.bits();
|
||||
file.read_notified = false; // resend missed events
|
||||
file.write_notified = false;
|
||||
}
|
||||
}
|
||||
let mut socket_set = self.socket_set.borrow_mut();
|
||||
let revents = SyscallEventFlags::from_bits_truncate(file.events(&mut socket_set));
|
||||
Ok(revents)
|
||||
}
|
||||
|
||||
fn fsync(&mut self, fd: usize, _ctx: &CallerCtx) -> SyscallResult<()> {
|
||||
{
|
||||
let _file = self.handles.get_mut(fd)?;
|
||||
}
|
||||
Ok(())
|
||||
// TODO Implement fsyncing
|
||||
// self.0.network_fsync()
|
||||
}
|
||||
|
||||
fn fpath(&mut self, fd: usize, buf: &mut [u8], _ctx: &CallerCtx) -> SyscallResult<usize> {
|
||||
let file = self.handles.get_mut(fd)?;
|
||||
|
||||
let file = match *file {
|
||||
Handle::File(ref mut f) => f,
|
||||
_ => return Err(SyscallError::new(syscall::EBADF)),
|
||||
};
|
||||
|
||||
let socket_set = self.socket_set.borrow();
|
||||
let socket = socket_set.get::<SocketT>(file.socket_handle());
|
||||
|
||||
socket.fpath(file, buf)
|
||||
}
|
||||
|
||||
fn fcntl(
|
||||
&mut self,
|
||||
fd: usize,
|
||||
cmd: usize,
|
||||
arg: usize,
|
||||
_ctx: &CallerCtx,
|
||||
) -> SyscallResult<usize> {
|
||||
let handle = self.handles.get_mut(fd)?;
|
||||
|
||||
match *handle {
|
||||
Handle::File(SchemeFile::Socket(ref mut socket_file)) => match cmd {
|
||||
syscall::F_GETFL => Ok(socket_file.flags),
|
||||
syscall::F_SETFL => {
|
||||
socket_file.flags = arg & !syscall::O_ACCMODE;
|
||||
Ok(0)
|
||||
}
|
||||
_ => Err(SyscallError::new(syscall::EINVAL)),
|
||||
},
|
||||
Handle::Null(ref mut null) => match cmd {
|
||||
syscall::F_GETFL => Ok(null.flags),
|
||||
syscall::F_SETFL => {
|
||||
null.flags = arg & !syscall::O_ACCMODE;
|
||||
Ok(0)
|
||||
}
|
||||
_ => Err(SyscallError::new(syscall::EINVAL)),
|
||||
},
|
||||
_ => Err(SyscallError::new(syscall::EBADF)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add_time(a: &TimeSpec, b: &TimeSpec) -> TimeSpec {
|
||||
let mut secs = a.tv_sec + b.tv_sec;
|
||||
let mut nsecs = a.tv_nsec + b.tv_nsec;
|
||||
|
||||
secs += i64::from(nsecs) / 1_000_000_000;
|
||||
nsecs %= 1_000_000_000;
|
||||
|
||||
TimeSpec {
|
||||
tv_sec: secs,
|
||||
tv_nsec: nsecs,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,431 @@
|
||||
use scheme_utils::FpathWriter;
|
||||
use smoltcp::iface::SocketHandle;
|
||||
use smoltcp::socket::tcp::{Socket as TcpSocket, SocketBuffer as TcpSocketBuffer};
|
||||
use smoltcp::wire::{IpEndpoint, IpListenEndpoint};
|
||||
use std::str;
|
||||
use syscall;
|
||||
use syscall::{Error as SyscallError, Result as SyscallResult};
|
||||
|
||||
use super::socket::{Context, DupResult, SchemeFile, SchemeSocket, SocketFile};
|
||||
use super::{parse_endpoint, SchemeWrapper, SocketSet};
|
||||
use crate::port_set::PortSet;
|
||||
use libredox::flag;
|
||||
|
||||
const SO_SNDBUF: usize = 7;
|
||||
const SO_RCVBUF: usize = 8;
|
||||
|
||||
pub type TcpScheme = SchemeWrapper<TcpSocket<'static>>;
|
||||
|
||||
impl<'a> SchemeSocket for TcpSocket<'a> {
|
||||
type SchemeDataT = PortSet;
|
||||
type DataT = Option<IpListenEndpoint>;
|
||||
type SettingT = ();
|
||||
|
||||
fn new_scheme_data() -> Self::SchemeDataT {
|
||||
PortSet::new(49_152u16, 65_535u16).expect("Wrong TCP port numbers")
|
||||
}
|
||||
|
||||
fn can_send(&self) -> bool {
|
||||
self.can_send()
|
||||
}
|
||||
|
||||
fn can_recv(&mut self, _data: &Self::DataT) -> bool {
|
||||
smoltcp::socket::tcp::Socket::can_recv(self)
|
||||
}
|
||||
|
||||
fn may_recv(&self) -> bool {
|
||||
self.may_recv()
|
||||
}
|
||||
|
||||
fn hop_limit(&self) -> u8 {
|
||||
self.hop_limit().unwrap_or(64)
|
||||
}
|
||||
|
||||
fn set_hop_limit(&mut self, hop_limit: u8) {
|
||||
self.set_hop_limit(Some(hop_limit));
|
||||
}
|
||||
|
||||
fn get_setting(
|
||||
_file: &SocketFile<Self::DataT>,
|
||||
_setting: Self::SettingT,
|
||||
_buf: &mut [u8],
|
||||
) -> SyscallResult<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn set_setting(
|
||||
_file: &mut SocketFile<Self::DataT>,
|
||||
_setting: Self::SettingT,
|
||||
_buf: &[u8],
|
||||
) -> SyscallResult<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn new_socket(
|
||||
socket_set: &mut SocketSet,
|
||||
path: &str,
|
||||
uid: u32,
|
||||
port_set: &mut Self::SchemeDataT,
|
||||
context: &Context,
|
||||
) -> SyscallResult<(SocketHandle, Self::DataT)> {
|
||||
trace!("TCP open {}", path);
|
||||
let mut parts = path.split('/');
|
||||
let remote_endpoint = parse_endpoint(parts.next().unwrap_or(""));
|
||||
let mut local_endpoint = parse_endpoint(parts.next().unwrap_or(""));
|
||||
|
||||
if local_endpoint.port > 0 && local_endpoint.port <= 1024 && uid != 0 {
|
||||
return Err(SyscallError::new(syscall::EACCES));
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// TODO: claim port with ethernet ip address
|
||||
if local_endpoint.port == 0 {
|
||||
local_endpoint.port = port_set
|
||||
.get_port()
|
||||
.ok_or_else(|| SyscallError::new(syscall::EINVAL))?;
|
||||
} else if !port_set.claim_port(local_endpoint.port) {
|
||||
return Err(SyscallError::new(syscall::EADDRINUSE));
|
||||
}
|
||||
|
||||
let socket_handle = socket_set.add(socket);
|
||||
|
||||
let tcp_socket = socket_set.get_mut::<TcpSocket>(socket_handle);
|
||||
|
||||
let listen_enpoint = if remote_endpoint.is_specified() {
|
||||
let local_endpoint_addr = match local_endpoint.addr {
|
||||
Some(addr) if !addr.is_unspecified() => Some(addr),
|
||||
_ => {
|
||||
// local ip is 0.0.0.0, resolve it
|
||||
let route_table = context.route_table.borrow();
|
||||
let addr = route_table
|
||||
.lookup_src_addr(&remote_endpoint.addr.expect("Checked in is_specified"));
|
||||
if matches!(addr, None) {
|
||||
error!("Opening a TCP connection with a probably invalid source IP as no route have been found for destination: {}", remote_endpoint);
|
||||
}
|
||||
addr
|
||||
}
|
||||
};
|
||||
let local_endpoint = IpListenEndpoint {
|
||||
addr: local_endpoint_addr,
|
||||
port: local_endpoint.port,
|
||||
};
|
||||
|
||||
trace!("Connecting tcp {} {}", local_endpoint, remote_endpoint);
|
||||
tcp_socket
|
||||
.connect(
|
||||
context.iface.borrow_mut().context(),
|
||||
IpEndpoint::new(remote_endpoint.addr.unwrap(), remote_endpoint.port),
|
||||
local_endpoint,
|
||||
)
|
||||
.expect("Can't connect tcp socket ");
|
||||
None
|
||||
} else {
|
||||
trace!("Listening tcp {}", local_endpoint);
|
||||
tcp_socket
|
||||
.listen(local_endpoint)
|
||||
.expect("Can't listen on local endpoint");
|
||||
Some(local_endpoint)
|
||||
};
|
||||
|
||||
Ok((socket_handle, listen_enpoint))
|
||||
}
|
||||
|
||||
fn close_file(
|
||||
&self,
|
||||
file: &SchemeFile<Self>,
|
||||
port_set: &mut Self::SchemeDataT,
|
||||
) -> SyscallResult<()> {
|
||||
if let SchemeFile::Socket(SocketFile { data, .. }) = *file {
|
||||
if let Some(endpoint) = self.local_endpoint() {
|
||||
// Socket was connected on some port
|
||||
port_set.release_port(endpoint.port);
|
||||
} else if let Some(endpoint) = data {
|
||||
// Socket was listening on some port
|
||||
port_set.release_port(endpoint.port);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_buf(
|
||||
&mut self,
|
||||
file: &mut SocketFile<Self::DataT>,
|
||||
buf: &[u8],
|
||||
) -> SyscallResult<usize> {
|
||||
if !file.write_enabled {
|
||||
return Err(SyscallError::new(syscall::EPIPE));
|
||||
} else if !self.is_active() {
|
||||
Err(SyscallError::new(syscall::ENOTCONN))
|
||||
} else if self.can_send() {
|
||||
self.send_slice(buf).expect("Can't send slice");
|
||||
Ok(buf.len())
|
||||
} else if file.flags & syscall::O_NONBLOCK == syscall::O_NONBLOCK {
|
||||
Err(SyscallError::new(syscall::EAGAIN))
|
||||
} else {
|
||||
Err(SyscallError::new(syscall::EWOULDBLOCK)) // internally scheduled to re-write
|
||||
}
|
||||
}
|
||||
|
||||
fn read_buf(
|
||||
&mut self,
|
||||
file: &mut SocketFile<Self::DataT>,
|
||||
buf: &mut [u8],
|
||||
) -> SyscallResult<usize> {
|
||||
if !file.read_enabled {
|
||||
Ok(0)
|
||||
} else if !self.is_active() {
|
||||
Err(SyscallError::new(syscall::ENOTCONN))
|
||||
} else if self.can_recv(&file.data) {
|
||||
let length = self.recv_slice(buf).expect("Can't receive slice");
|
||||
Ok(length)
|
||||
} else if !self.may_recv() {
|
||||
Ok(0)
|
||||
} else if file.flags & syscall::O_NONBLOCK == syscall::O_NONBLOCK {
|
||||
Err(SyscallError::new(syscall::EAGAIN))
|
||||
} else {
|
||||
Err(SyscallError::new(syscall::EWOULDBLOCK)) // internally scheduled to re-read
|
||||
}
|
||||
}
|
||||
|
||||
fn dup(
|
||||
socket_set: &mut SocketSet,
|
||||
file: &mut SchemeFile<Self>,
|
||||
path: &str,
|
||||
port_set: &mut Self::SchemeDataT,
|
||||
) -> SyscallResult<DupResult<Self>> {
|
||||
let socket_handle = file.socket_handle();
|
||||
|
||||
let (is_active, local_endpoint) = {
|
||||
let socket = socket_set.get::<TcpSocket>(socket_handle);
|
||||
(socket.is_active(), socket.local_endpoint())
|
||||
};
|
||||
|
||||
let file = match path {
|
||||
"listen" => {
|
||||
if let SchemeFile::Socket(ref tcp_handle) = *file {
|
||||
let Some(listen_enpoint) = tcp_handle.data else {
|
||||
// This socket is not listening so we can't accept a connection
|
||||
return Err(SyscallError::new(syscall::EINVAL));
|
||||
};
|
||||
|
||||
if !is_active {
|
||||
// Socket listening but no connection received
|
||||
if tcp_handle.flags & syscall::O_NONBLOCK == syscall::O_NONBLOCK {
|
||||
return Err(SyscallError::new(syscall::EAGAIN));
|
||||
} else {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
trace!("TCP creating new listening socket");
|
||||
// We pass None as data because this new handle is to the active connection so
|
||||
// not a listening socket
|
||||
let new_handle = SchemeFile::Socket(tcp_handle.clone_with_data(None));
|
||||
|
||||
// Creating a socket to continue listening
|
||||
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);
|
||||
let new_socket_handle = socket_set.add(socket);
|
||||
{
|
||||
let tcp_socket = socket_set.get_mut::<TcpSocket>(new_socket_handle);
|
||||
tcp_socket
|
||||
.listen(listen_enpoint)
|
||||
.expect("Can't listen on local endpoint");
|
||||
}
|
||||
// We got a new connection to the socket so acquire the port
|
||||
port_set.acquire_port(
|
||||
local_endpoint
|
||||
.expect("Socket was active so local endpoint must be set")
|
||||
.port,
|
||||
);
|
||||
return Ok(Some((
|
||||
new_handle,
|
||||
Some((new_socket_handle, Some(listen_enpoint))),
|
||||
)));
|
||||
} else {
|
||||
return Err(SyscallError::new(syscall::EBADF));
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
trace!("TCP dup unknown {}", path);
|
||||
if let SchemeFile::Socket(ref tcp_handle) = *file {
|
||||
SchemeFile::Socket(tcp_handle.clone_with_data(tcp_handle.data))
|
||||
} else {
|
||||
SchemeFile::Socket(SocketFile::new_with_data(socket_handle, None))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if let SchemeFile::Socket(_) = file {
|
||||
if let Some(local_endpoint) = local_endpoint {
|
||||
port_set.acquire_port(local_endpoint.port);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Some((file, None)))
|
||||
}
|
||||
|
||||
fn fpath(&self, file: &SchemeFile<Self>, buf: &mut [u8]) -> SyscallResult<usize> {
|
||||
FpathWriter::with(buf, "tcp", |w| {
|
||||
let unspecified = "0.0.0.0:0";
|
||||
match self.remote_endpoint() {
|
||||
Some(endpoint) => write!(w, "{}", endpoint).unwrap(),
|
||||
None => w.push_str(unspecified),
|
||||
}
|
||||
w.push_str("/");
|
||||
match (self.local_endpoint(), file) {
|
||||
(Some(endpoint), _) => write!(w, "{}", endpoint).unwrap(),
|
||||
(
|
||||
None,
|
||||
SchemeFile::Socket(SocketFile {
|
||||
data: Some(endpoint),
|
||||
..
|
||||
}),
|
||||
) => {
|
||||
if endpoint.is_specified() {
|
||||
write!(w, "{}", endpoint).unwrap()
|
||||
} else {
|
||||
write!(w, "0.0.0.0:{}", endpoint.port).unwrap()
|
||||
}
|
||||
}
|
||||
_ => w.push_str(unspecified),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn handle_recvmsg(
|
||||
&mut self,
|
||||
file: &mut SchemeFile<Self>,
|
||||
how: &mut [u8],
|
||||
flags: usize,
|
||||
) -> SyscallResult<usize> {
|
||||
let socket_file = match file {
|
||||
SchemeFile::Socket(ref mut sock_f) => sock_f,
|
||||
_ => return Err(SyscallError::new(syscall::EBADF)),
|
||||
};
|
||||
if !socket_file.read_enabled {
|
||||
Ok(0)
|
||||
} else if self.can_recv(&socket_file.data) {
|
||||
let usize_length = core::mem::size_of::<usize>();
|
||||
let prepared_name_len = usize::from_le_bytes(
|
||||
how[0..usize_length]
|
||||
.try_into()
|
||||
.map_err(|_| SyscallError::new(syscall::EINVAL))?,
|
||||
);
|
||||
let prepared_whole_iov_size = usize::from_le_bytes(
|
||||
how[usize_length..2 * usize_length]
|
||||
.try_into()
|
||||
.map_err(|_| SyscallError::new(syscall::EINVAL))?,
|
||||
);
|
||||
let prepared_msg_controllen = usize::from_le_bytes(
|
||||
how[2 * usize_length..3 * usize_length]
|
||||
.try_into()
|
||||
.map_err(|_| SyscallError::new(syscall::EINVAL))?,
|
||||
);
|
||||
if 3 * usize_length
|
||||
+ prepared_name_len
|
||||
+ prepared_msg_controllen
|
||||
+ prepared_whole_iov_size
|
||||
> how.len()
|
||||
{
|
||||
//expected returned buffer size is larger than provided -> return invalid
|
||||
return Err(SyscallError::new(syscall::EINVAL));
|
||||
}
|
||||
let address = match self.remote_endpoint() {
|
||||
Some(endpoint) => format!("/scheme/tcp/{}:{}", endpoint.addr, endpoint.port),
|
||||
None => String::from("/scheme/tcp/0.0.0.0:0"),
|
||||
};
|
||||
|
||||
how[..usize_length].copy_from_slice(&address.len().to_le_bytes());
|
||||
how[usize_length..address.len() + usize_length].copy_from_slice(&address.as_bytes());
|
||||
let payload_length = self
|
||||
.recv_slice(
|
||||
&mut how[address.len() + 2 * usize_length
|
||||
..address.len() + 2 * usize_length + prepared_whole_iov_size],
|
||||
)
|
||||
.unwrap();
|
||||
how[address.len() + usize_length..address.len() + 2 * usize_length]
|
||||
.copy_from_slice(&payload_length.to_le_bytes());
|
||||
|
||||
Ok(address.len() + 2 * usize_length + prepared_whole_iov_size)
|
||||
} else if socket_file.flags & syscall::O_NONBLOCK == syscall::O_NONBLOCK
|
||||
|| flags & flag::MSG_DONTWAIT as usize != 0
|
||||
{
|
||||
Err(SyscallError::new(syscall::EAGAIN))
|
||||
} else {
|
||||
Err(SyscallError::new(syscall::EWOULDBLOCK)) // internally scheduled to re-read
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_get_peer_name(
|
||||
&self,
|
||||
file: &SchemeFile<Self>,
|
||||
buf: &mut [u8],
|
||||
) -> SyscallResult<usize> {
|
||||
self.fpath(file, buf)
|
||||
}
|
||||
|
||||
fn handle_shutdown(&mut self, file: &mut SchemeFile<Self>, how: usize) -> SyscallResult<usize> {
|
||||
let socket_file = match file {
|
||||
SchemeFile::Socket(ref mut file) => file,
|
||||
_ => return Err(SyscallError::new(syscall::EBADF)),
|
||||
};
|
||||
|
||||
match how {
|
||||
0 => socket_file.read_enabled = false, // SHUT_RD
|
||||
1 => {
|
||||
socket_file.write_enabled = false;
|
||||
self.close();
|
||||
} // SHUT_WR
|
||||
2 => {
|
||||
socket_file.read_enabled = false;
|
||||
socket_file.write_enabled = false;
|
||||
self.close();
|
||||
} // SHUT_RDWR
|
||||
_ => return Err(SyscallError::new(syscall::EINVAL)),
|
||||
}
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn get_sock_opt(
|
||||
&self,
|
||||
_file: &SchemeFile<Self>,
|
||||
name: usize,
|
||||
buf: &mut [u8],
|
||||
) -> SyscallResult<usize> {
|
||||
match name {
|
||||
SO_RCVBUF => {
|
||||
let val = self.recv_capacity() as i32;
|
||||
let bytes = val.to_ne_bytes();
|
||||
|
||||
if buf.len() < bytes.len() {
|
||||
return Err(SyscallError::new(syscall::EINVAL));
|
||||
}
|
||||
|
||||
buf[0..bytes.len()].copy_from_slice(&bytes);
|
||||
Ok(bytes.len())
|
||||
}
|
||||
SO_SNDBUF => {
|
||||
let val = self.send_capacity() as i32;
|
||||
let bytes = val.to_ne_bytes();
|
||||
|
||||
if buf.len() < bytes.len() {
|
||||
return Err(SyscallError::new(syscall::EINVAL));
|
||||
}
|
||||
|
||||
buf[0..bytes.len()].copy_from_slice(&bytes);
|
||||
Ok(bytes.len())
|
||||
}
|
||||
_ => Err(SyscallError::new(syscall::ENOPROTOOPT)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,437 @@
|
||||
use scheme_utils::FpathWriter;
|
||||
use smoltcp::iface::SocketHandle;
|
||||
use smoltcp::socket::udp::{
|
||||
PacketBuffer as UdpSocketBuffer, PacketMetadata as UdpPacketMetadata, Socket as UdpSocket,
|
||||
};
|
||||
use smoltcp::wire::{IpEndpoint, IpListenEndpoint};
|
||||
use std::str;
|
||||
use syscall;
|
||||
use syscall::{Error as SyscallError, Result as SyscallResult};
|
||||
|
||||
use super::socket::{Context, DupResult, SchemeFile, SchemeSocket, SocketFile};
|
||||
use super::{parse_endpoint, SchemeWrapper, Smolnetd, SocketSet};
|
||||
use crate::port_set::PortSet;
|
||||
use crate::router::Router;
|
||||
use libredox::flag;
|
||||
|
||||
const SO_SNDBUF: usize = 7;
|
||||
const SO_RCVBUF: usize = 8;
|
||||
|
||||
pub type UdpScheme = SchemeWrapper<UdpSocket<'static>>;
|
||||
|
||||
impl<'a> SchemeSocket for UdpSocket<'a> {
|
||||
type SchemeDataT = PortSet;
|
||||
type DataT = IpListenEndpoint;
|
||||
type SettingT = ();
|
||||
|
||||
fn new_scheme_data() -> Self::SchemeDataT {
|
||||
PortSet::new(49_152u16, 65_535u16).expect("Wrong UDP port numbers")
|
||||
}
|
||||
|
||||
fn can_send(&self) -> bool {
|
||||
self.can_send()
|
||||
}
|
||||
|
||||
fn can_recv(&mut self, data: &IpListenEndpoint) -> bool {
|
||||
loop {
|
||||
// If buffer is empty, we definitely can't recv
|
||||
if !UdpSocket::can_recv(self) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we are not connected to a specific remote, any packet is valid
|
||||
if !data.is_specified() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we are connected, peek at the packet.
|
||||
match self.peek() {
|
||||
Ok((_, meta)) => {
|
||||
let source = meta.endpoint;
|
||||
let connected_addr = data.addr.unwrap(); // Safe because is_specified() checked it
|
||||
|
||||
// Allow Broadcast special case (DHCP)
|
||||
let is_broadcast = match connected_addr {
|
||||
smoltcp::wire::IpAddress::Ipv4(ip) => {
|
||||
ip == smoltcp::wire::Ipv4Address::BROADCAST
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if !is_broadcast && !connected_addr.is_unspecified() {
|
||||
if source.addr != connected_addr || source.port != data.port {
|
||||
// Bad packet detected
|
||||
// Remove it from the buffer immediately so poll() doesn't trigger
|
||||
let _ = self.recv();
|
||||
continue; // Loop again to check the next packet
|
||||
}
|
||||
}
|
||||
// Packet is valid
|
||||
return true;
|
||||
}
|
||||
Err(_) => return false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn may_recv(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn hop_limit(&self) -> u8 {
|
||||
self.hop_limit().unwrap_or(64)
|
||||
}
|
||||
|
||||
fn set_hop_limit(&mut self, hop_limit: u8) {
|
||||
self.set_hop_limit(Some(hop_limit));
|
||||
}
|
||||
|
||||
fn get_setting(
|
||||
_file: &SocketFile<Self::DataT>,
|
||||
_setting: Self::SettingT,
|
||||
_buf: &mut [u8],
|
||||
) -> SyscallResult<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn set_setting(
|
||||
_file: &mut SocketFile<Self::DataT>,
|
||||
_setting: Self::SettingT,
|
||||
_buf: &[u8],
|
||||
) -> SyscallResult<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn new_socket(
|
||||
socket_set: &mut SocketSet,
|
||||
path: &str,
|
||||
uid: u32,
|
||||
port_set: &mut Self::SchemeDataT,
|
||||
context: &Context,
|
||||
) -> SyscallResult<(SocketHandle, Self::DataT)> {
|
||||
let mut parts = path.split('/');
|
||||
let remote_endpoint = parse_endpoint(parts.next().unwrap_or(""));
|
||||
let mut local_endpoint = parse_endpoint(parts.next().unwrap_or(""));
|
||||
|
||||
if local_endpoint.port > 0 && local_endpoint.port <= 1024 && uid != 0 {
|
||||
return Err(SyscallError::new(syscall::EACCES));
|
||||
}
|
||||
|
||||
let rx_buffer = UdpSocketBuffer::new(
|
||||
vec![UdpPacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE],
|
||||
vec![0; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE],
|
||||
);
|
||||
let tx_buffer = UdpSocketBuffer::new(
|
||||
vec![UdpPacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE],
|
||||
vec![0; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE],
|
||||
);
|
||||
let udp_socket = UdpSocket::new(rx_buffer, tx_buffer);
|
||||
|
||||
// TODO: claim port with ethernet ip address
|
||||
if local_endpoint.port == 0 {
|
||||
local_endpoint.port = port_set
|
||||
.get_port()
|
||||
.ok_or_else(|| SyscallError::new(syscall::EINVAL))?;
|
||||
} else if !port_set.claim_port(local_endpoint.port) {
|
||||
return Err(SyscallError::new(syscall::EADDRINUSE));
|
||||
}
|
||||
|
||||
let socket_handle = socket_set.add(udp_socket);
|
||||
|
||||
let udp_socket = socket_set.get_mut::<UdpSocket>(socket_handle);
|
||||
|
||||
if remote_endpoint.is_specified() {
|
||||
let local_endpoint_addr = match local_endpoint.addr {
|
||||
Some(addr) if addr.is_unspecified() => Some(addr),
|
||||
_ => {
|
||||
// local ip is 0.0.0.0, resolve it
|
||||
let route_table = context.route_table.borrow();
|
||||
let addr = route_table
|
||||
.lookup_src_addr(&remote_endpoint.addr.expect("Checked in is_specified"));
|
||||
if matches!(addr, None) {
|
||||
error!("Opening a TCP connection with a probably invalid source IP as no route have been found for destination: {}", remote_endpoint);
|
||||
}
|
||||
addr
|
||||
}
|
||||
};
|
||||
local_endpoint = IpListenEndpoint {
|
||||
addr: local_endpoint_addr,
|
||||
port: local_endpoint.port,
|
||||
};
|
||||
}
|
||||
|
||||
udp_socket
|
||||
.bind(local_endpoint)
|
||||
.expect("Can't bind udp socket to local endpoint");
|
||||
|
||||
Ok((socket_handle, remote_endpoint))
|
||||
}
|
||||
|
||||
fn close_file(
|
||||
&self,
|
||||
file: &SchemeFile<Self>,
|
||||
port_set: &mut Self::SchemeDataT,
|
||||
) -> SyscallResult<()> {
|
||||
if let SchemeFile::Socket(_) = *file {
|
||||
port_set.release_port(self.endpoint().port);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_buf(
|
||||
&mut self,
|
||||
file: &mut SocketFile<Self::DataT>,
|
||||
buf: &[u8],
|
||||
) -> SyscallResult<usize> {
|
||||
if !file.data.is_specified() {
|
||||
return Err(SyscallError::new(syscall::EADDRNOTAVAIL));
|
||||
}
|
||||
if !file.write_enabled {
|
||||
return Err(SyscallError::new(syscall::EPIPE));
|
||||
}
|
||||
if self.can_send() {
|
||||
let endpoint = file.data;
|
||||
let endpoint = IpEndpoint::new(
|
||||
endpoint
|
||||
.addr
|
||||
.expect("If we can send, this should be specified"),
|
||||
endpoint.port,
|
||||
);
|
||||
self.send_slice(buf, endpoint).expect("Can't send slice");
|
||||
Ok(buf.len())
|
||||
} else if file.flags & syscall::O_NONBLOCK == syscall::O_NONBLOCK {
|
||||
Err(SyscallError::new(syscall::EAGAIN))
|
||||
} else {
|
||||
Err(SyscallError::new(syscall::EWOULDBLOCK)) // internally scheduled to re-read
|
||||
}
|
||||
}
|
||||
|
||||
fn read_buf(
|
||||
&mut self,
|
||||
file: &mut SocketFile<Self::DataT>,
|
||||
buf: &mut [u8],
|
||||
) -> SyscallResult<usize> {
|
||||
if !file.read_enabled {
|
||||
Ok(0)
|
||||
} else if self.can_recv(&file.data) {
|
||||
let (length, _) = self.recv_slice(buf).expect("Can't receive slice");
|
||||
Ok(length)
|
||||
} else if file.flags & syscall::O_NONBLOCK == syscall::O_NONBLOCK {
|
||||
Err(SyscallError::new(syscall::EAGAIN))
|
||||
} else {
|
||||
Err(SyscallError::new(syscall::EWOULDBLOCK)) // internally scheduled to re-read
|
||||
}
|
||||
}
|
||||
|
||||
fn dup(
|
||||
socket_set: &mut SocketSet,
|
||||
file: &mut SchemeFile<Self>,
|
||||
path: &str,
|
||||
port_set: &mut Self::SchemeDataT,
|
||||
) -> SyscallResult<DupResult<Self>> {
|
||||
let socket_handle = file.socket_handle();
|
||||
let file = match path {
|
||||
"listen" => {
|
||||
// there's no accept() for UDP
|
||||
return Err(SyscallError::new(syscall::EAFNOSUPPORT));
|
||||
}
|
||||
"disconnect" => {
|
||||
let remote_endpoint = IpListenEndpoint {
|
||||
addr: None,
|
||||
port: 0,
|
||||
};
|
||||
if let SchemeFile::Socket(ref udp_handle) = *file {
|
||||
SchemeFile::Socket(udp_handle.clone_with_data(remote_endpoint))
|
||||
} else {
|
||||
SchemeFile::Socket(SocketFile::new_with_data(socket_handle, remote_endpoint))
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let remote_endpoint = parse_endpoint(path);
|
||||
if let SchemeFile::Socket(ref udp_handle) = *file {
|
||||
SchemeFile::Socket(udp_handle.clone_with_data(remote_endpoint))
|
||||
} else {
|
||||
SchemeFile::Socket(SocketFile::new_with_data(socket_handle, remote_endpoint))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let endpoint = {
|
||||
let socket = socket_set.get::<UdpSocket>(socket_handle);
|
||||
socket.endpoint()
|
||||
};
|
||||
|
||||
if let SchemeFile::Socket(_) = file {
|
||||
port_set.acquire_port(endpoint.port);
|
||||
}
|
||||
|
||||
Ok(Some((file, None)))
|
||||
}
|
||||
|
||||
fn fpath(&self, file: &SchemeFile<Self>, buf: &mut [u8]) -> SyscallResult<usize> {
|
||||
FpathWriter::with(buf, "udp", |w| {
|
||||
let unspecified = "0.0.0.0:0";
|
||||
|
||||
// remote
|
||||
match file {
|
||||
SchemeFile::Socket(SocketFile { data: endpoint, .. }) => {
|
||||
if endpoint.is_specified() {
|
||||
write!(w, "{}", endpoint).unwrap()
|
||||
} else {
|
||||
write!(w, "0.0.0.0:{}", endpoint.port).unwrap()
|
||||
}
|
||||
}
|
||||
_ => w.push_str(unspecified),
|
||||
}
|
||||
w.push_str("/");
|
||||
// local
|
||||
let endpoint = self.endpoint();
|
||||
if endpoint.is_specified() {
|
||||
write!(w, "{}", endpoint).unwrap()
|
||||
} else {
|
||||
write!(w, "0.0.0.0:{}", endpoint.port).unwrap()
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn handle_recvmsg(
|
||||
&mut self,
|
||||
file: &mut SchemeFile<Self>,
|
||||
how: &mut [u8],
|
||||
flags: usize,
|
||||
) -> SyscallResult<usize> {
|
||||
//there is a separate flags argument for MSG_DONTWAIT which is call specific not socket-wide like socket_file.flags
|
||||
let socket_file = match file {
|
||||
SchemeFile::Socket(ref mut sock_f) => sock_f,
|
||||
_ => return Err(SyscallError::new(syscall::EBADF)),
|
||||
};
|
||||
|
||||
if !socket_file.read_enabled {
|
||||
Ok(0)
|
||||
} else if self.can_recv(&socket_file.data) {
|
||||
let usize_length = core::mem::size_of::<usize>();
|
||||
let prepared_name_len = usize::from_le_bytes(
|
||||
how[0..usize_length]
|
||||
.try_into()
|
||||
.map_err(|_| SyscallError::new(syscall::EINVAL))?,
|
||||
);
|
||||
let prepared_whole_iov_size = usize::from_le_bytes(
|
||||
how[usize_length..2 * usize_length]
|
||||
.try_into()
|
||||
.map_err(|_| SyscallError::new(syscall::EINVAL))?,
|
||||
);
|
||||
let prepared_msg_controllen = usize::from_le_bytes(
|
||||
how[2 * usize_length..3 * usize_length]
|
||||
.try_into()
|
||||
.map_err(|_| SyscallError::new(syscall::EINVAL))?,
|
||||
);
|
||||
if 3 * usize_length
|
||||
+ prepared_name_len
|
||||
+ prepared_msg_controllen
|
||||
+ prepared_whole_iov_size
|
||||
> how.len()
|
||||
{
|
||||
//expected returned buffer size is larger than provided -> return invalid
|
||||
return Err(SyscallError::new(syscall::EINVAL));
|
||||
}
|
||||
|
||||
//the relibc deserialization functions expect NO GAPS between the name and payload slices
|
||||
//so the payload must be temporarily stored during recv_slice
|
||||
let mut payload_tmp = vec![0u8; prepared_whole_iov_size];
|
||||
let (length, address) = self
|
||||
.recv_slice(&mut payload_tmp)
|
||||
.expect("Can't recieve slice");
|
||||
|
||||
//Address Handling
|
||||
let address_formatted = if prepared_name_len > 0 {
|
||||
format!(
|
||||
"/scheme/udp/{}:{}",
|
||||
address.endpoint.addr, address.endpoint.port
|
||||
)
|
||||
} else {
|
||||
String::from("")
|
||||
};
|
||||
how[..usize_length].copy_from_slice(&address_formatted.len().to_le_bytes());
|
||||
let payload_len_index = address_formatted.len() + usize_length;
|
||||
how[usize_length..payload_len_index].copy_from_slice(&address_formatted.as_bytes());
|
||||
|
||||
//Payload Handling
|
||||
how[payload_len_index..payload_len_index + usize_length]
|
||||
.copy_from_slice(&(length as usize).to_le_bytes());
|
||||
how[payload_len_index + usize_length..payload_len_index + usize_length + length]
|
||||
.copy_from_slice(&payload_tmp[..length]);
|
||||
Ok(payload_len_index + usize_length + length)
|
||||
} else if socket_file.flags & syscall::O_NONBLOCK == syscall::O_NONBLOCK
|
||||
|| flags & flag::MSG_DONTWAIT as usize != 0
|
||||
{
|
||||
Err(SyscallError::new(syscall::EAGAIN))
|
||||
} else {
|
||||
Err(SyscallError::new(syscall::EWOULDBLOCK)) // internally scheduled to re-read
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_get_peer_name(
|
||||
&self,
|
||||
file: &SchemeFile<Self>,
|
||||
buf: &mut [u8],
|
||||
) -> SyscallResult<usize> {
|
||||
let peer = match file {
|
||||
SchemeFile::Socket(SocketFile { data, .. }) => data,
|
||||
_ => return Err(SyscallError::new(syscall::EBADF)),
|
||||
};
|
||||
if peer.addr.is_some() || peer.port != 0 {
|
||||
self.fpath(file, buf)
|
||||
} else {
|
||||
Err(SyscallError::new(syscall::ENOTCONN))
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_shutdown(&mut self, file: &mut SchemeFile<Self>, how: usize) -> SyscallResult<usize> {
|
||||
let socket_file = match file {
|
||||
SchemeFile::Socket(ref mut file) => file,
|
||||
_ => return Err(SyscallError::new(syscall::EBADF)),
|
||||
};
|
||||
|
||||
match how {
|
||||
0 => socket_file.read_enabled = false, // SHUT_RD
|
||||
1 => socket_file.write_enabled = false, // SHUT_WR
|
||||
2 => {
|
||||
socket_file.read_enabled = false;
|
||||
socket_file.write_enabled = false;
|
||||
} // SHUT_RDWR
|
||||
_ => return Err(SyscallError::new(syscall::EINVAL)),
|
||||
}
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn get_sock_opt(
|
||||
&self,
|
||||
_file: &SchemeFile<Self>,
|
||||
name: usize,
|
||||
buf: &mut [u8],
|
||||
) -> SyscallResult<usize> {
|
||||
match name {
|
||||
SO_RCVBUF => {
|
||||
let val = self.payload_recv_capacity() as i32;
|
||||
let bytes = val.to_ne_bytes();
|
||||
if buf.len() < bytes.len() {
|
||||
return Err(SyscallError::new(syscall::EINVAL));
|
||||
}
|
||||
buf[..bytes.len()].copy_from_slice(&bytes);
|
||||
Ok(bytes.len())
|
||||
}
|
||||
SO_SNDBUF => {
|
||||
let val = self.payload_send_capacity() as i32;
|
||||
let bytes = val.to_ne_bytes();
|
||||
if buf.len() < bytes.len() {
|
||||
return Err(SyscallError::new(syscall::EINVAL));
|
||||
}
|
||||
buf[..bytes.len()].copy_from_slice(&bytes);
|
||||
Ok(bytes.len())
|
||||
}
|
||||
_ => Err(SyscallError::new(syscall::ENOPROTOOPT)),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user