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:
2026-04-29 09:54:06 +01:00
parent b23714f542
commit 8acc73d774
508 changed files with 76526 additions and 396 deletions
@@ -0,0 +1,96 @@
use std::cell::RefCell;
use std::mem::{replace, swap};
use std::ops::{Deref, DerefMut, Drop};
use std::rc::Rc;
type BufferStack = Rc<RefCell<Vec<Vec<u8>>>>;
pub struct Buffer {
buffer: Vec<u8>,
stack: BufferStack,
}
impl Buffer {
pub fn resize(&mut self, new_len: usize) {
self.buffer.resize(new_len, 0u8);
}
pub fn move_out(&mut self) -> Buffer {
Buffer {
buffer: replace(&mut self.buffer, vec![]),
stack: Rc::clone(&self.stack),
}
}
}
impl AsRef<[u8]> for Buffer {
fn as_ref(&self) -> &[u8] {
&self.buffer
}
}
impl AsMut<[u8]> for Buffer {
fn as_mut(&mut self) -> &mut [u8] {
&mut self.buffer
}
}
impl Deref for Buffer {
type Target = [u8];
fn deref(&self) -> &Self::Target {
&self.buffer
}
}
impl DerefMut for Buffer {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.buffer
}
}
impl Drop for Buffer {
fn drop(&mut self) {
if self.buffer.capacity() > 0 {
let mut tmp = vec![];
swap(&mut tmp, &mut self.buffer);
{
let mut stack = self.stack.borrow_mut();
stack.push(tmp);
}
}
}
}
pub struct BufferPool {
buffers_size: usize,
stack: BufferStack,
}
impl BufferPool {
pub fn new(buffers_size: usize) -> BufferPool {
BufferPool {
buffers_size,
stack: Rc::new(RefCell::new(vec![])),
}
}
pub fn get_buffer(&mut self) -> Buffer {
let buffer = match self.stack.borrow_mut().pop() {
None => vec![0u8; self.buffers_size],
Some(mut v) => {
// memsetting the buffer with `resize` would be a waste of time
let capacity = v.capacity();
unsafe {
v.set_len(capacity);
}
v
}
};
Buffer {
buffer,
stack: Rc::clone(&self.stack),
}
}
}
+55
View File
@@ -0,0 +1,55 @@
use std::convert;
use std::fmt;
use std::io::Error as IOError;
use std::result;
use syscall::error::Error as SyscallError;
#[derive(Debug)]
enum ErrorType {
Syscall(SyscallError),
IOError(IOError),
}
#[derive(Debug)]
pub struct Error {
error_type: ErrorType,
descr: String,
}
impl Error {
pub fn from_syscall_error<S: Into<String>>(syscall_error: SyscallError, descr: S) -> Error {
Error {
error_type: ErrorType::Syscall(syscall_error),
descr: descr.into(),
}
}
pub fn from_io_error<S: Into<String>>(io_error: IOError, descr: S) -> Error {
Error {
error_type: ErrorType::IOError(io_error),
descr: descr.into(),
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
match self.error_type {
ErrorType::Syscall(ref syscall_error) => {
write!(f, "{}: syscall error: {}", self.descr, syscall_error)
}
ErrorType::IOError(ref io_error) => {
write!(f, "{} : io error : {}", self.descr, io_error)
}
}
}
}
impl std::error::Error for Error {}
impl convert::From<IOError> for Error {
fn from(e: IOError) -> Self {
Error::from_io_error(e, "generic error")
}
}
pub type Result<T> = result::Result<T, Error>;
@@ -0,0 +1,412 @@
use std::collections::btree_map::Entry;
use std::collections::BTreeMap;
use std::fs::File;
use std::io::{ErrorKind, Read, Write};
use std::rc::Rc;
use smoltcp::storage::PacketMetadata;
use smoltcp::time::{Duration, Instant};
use smoltcp::wire::{
ArpOperation, ArpPacket, ArpRepr, EthernetAddress, EthernetFrame, EthernetProtocol,
EthernetRepr, IpAddress, IpCidr, Ipv4Address, Ipv4Cidr,
};
use super::LinkDevice;
struct Neighbor {
hardware_address: EthernetAddress,
expires_at: Instant,
}
#[derive(Debug, Default)]
enum ArpState {
#[default]
Discovered,
Discovering {
target: Ipv4Address,
tries: u32,
silent_until: Instant,
},
}
type PacketBuffer = smoltcp::storage::PacketBuffer<'static, IpAddress>;
const EMPTY_MAC: EthernetAddress = EthernetAddress([0; 6]);
pub struct EthernetLink {
name: Rc<str>,
neighbor_cache: BTreeMap<IpAddress, Neighbor>,
arp_state: ArpState,
waiting_packets: PacketBuffer,
input_buffer: Vec<u8>,
output_buffer: Vec<u8>,
network_file: File,
hardware_address: Option<EthernetAddress>,
ip_address: Option<Ipv4Cidr>,
}
impl EthernetLink {
// TODO: Review these constants
const MAX_WAITING_PACKET_COUNT: usize = 10;
const MTU: usize = 1500;
const WAITING_PACKET_BUFFER_SIZE: usize = Self::MTU * Self::MAX_WAITING_PACKET_COUNT;
const NEIGHBOR_LIVE_TIME: Duration = Duration::from_secs(60);
const ARP_SILENCE_TIME: Duration = Duration::from_secs(1);
pub fn new(name: &str, network_file: File) -> Self {
let waiting_packets = PacketBuffer::new(
vec![PacketMetadata::EMPTY; Self::MAX_WAITING_PACKET_COUNT],
vec![0u8; Self::WAITING_PACKET_BUFFER_SIZE],
);
Self {
name: name.into(),
network_file,
waiting_packets,
hardware_address: None,
ip_address: None,
input_buffer: vec![0u8; Self::MTU],
output_buffer: Vec::with_capacity(Self::MTU),
arp_state: Default::default(),
neighbor_cache: Default::default(),
}
}
fn send_to<F>(&mut self, dst: EthernetAddress, size: usize, f: F, proto: EthernetProtocol)
where
F: FnOnce(&mut [u8]),
{
let Some(hardware_address) = self.hardware_address else {
return;
};
let repr = EthernetRepr {
src_addr: hardware_address,
dst_addr: dst,
ethertype: proto,
};
self.output_buffer.clear();
self.output_buffer.resize(repr.buffer_len() + size, 0);
let mut frame = EthernetFrame::new_unchecked(&mut self.output_buffer);
repr.emit(&mut frame);
f(frame.payload_mut());
if let Err(_) = self.network_file.write_all(&self.output_buffer) {
error!(
"Dropped outboud packet on {} (failed to write to network file)",
self.name
)
}
}
fn process_arp(&mut self, packet: &[u8], now: Instant) {
let Some(hardware_address) = self.hardware_address else {
return;
};
let Some(ip_addr) = self.ip_address else {
return;
};
let Ok(repr) = ArpPacket::new_checked(packet).and_then(|packet| ArpRepr::parse(&packet))
else {
debug!("Dropped incomming arp packet on {} (Malformed)", self.name);
return;
};
match repr {
ArpRepr::EthernetIpv4 {
operation,
source_hardware_addr,
source_protocol_addr,
target_hardware_addr,
target_protocol_addr,
} => {
let is_unicast_mac =
target_hardware_addr != EMPTY_MAC && !target_hardware_addr.is_broadcast();
if is_unicast_mac && hardware_address != target_hardware_addr {
// Only process packet that are for us
return;
}
if let ArpOperation::Unknown(_) = operation {
return;
}
if !source_hardware_addr.is_unicast()
|| source_protocol_addr.is_broadcast()
|| source_protocol_addr.is_multicast()
|| source_protocol_addr.is_unspecified()
{
return;
}
if ip_addr.address() != target_protocol_addr {
return;
}
self.neighbor_cache.insert(
IpAddress::Ipv4(source_protocol_addr),
Neighbor {
hardware_address: source_hardware_addr,
expires_at: now + Self::NEIGHBOR_LIVE_TIME,
},
);
if let ArpOperation::Request = operation {
let response = ArpRepr::EthernetIpv4 {
operation: ArpOperation::Reply,
source_hardware_addr: hardware_address,
source_protocol_addr: ip_addr.address(),
target_hardware_addr: source_hardware_addr,
target_protocol_addr: source_protocol_addr,
};
self.send_to(
source_hardware_addr,
response.buffer_len(),
|buf| response.emit(&mut ArpPacket::new_unchecked(buf)),
EthernetProtocol::Arp,
);
}
self.check_waiting_packets(source_protocol_addr, source_hardware_addr, now);
}
_ => {}
}
}
fn check_waiting_packets(&mut self, ip: Ipv4Address, mac: EthernetAddress, now: Instant) {
let mut waiting_packets =
std::mem::replace(&mut self.waiting_packets, PacketBuffer::new(vec![], vec![]));
loop {
match waiting_packets.peek() {
Ok((IpAddress::Ipv4(dst), _)) if dst == &ip => {}
Ok((IpAddress::Ipv4(dst), _)) => {
self.arp_state = ArpState::Discovering {
target: *dst,
tries: 0,
silent_until: Instant::ZERO,
};
self.send_arp(now);
break;
}
Err(_) => {
self.arp_state = ArpState::Discovered;
break;
}
}
let (_, packet) = waiting_packets.dequeue().unwrap();
self.send_to(
mac,
packet.len(),
|buf| buf.copy_from_slice(packet),
EthernetProtocol::Ipv4,
);
}
self.waiting_packets = waiting_packets;
}
fn drop_waiting_packets(&mut self, ip: Ipv4Address, now: Instant) {
loop {
match self.waiting_packets.peek() {
Ok((IpAddress::Ipv4(dst), _)) if dst == &ip => {}
Ok((IpAddress::Ipv4(dst), _)) => {
self.arp_state = ArpState::Discovering {
target: *dst,
tries: 0,
silent_until: Instant::ZERO,
};
self.send_arp(now);
return;
}
Err(_) => {
self.arp_state = ArpState::Discovered;
return;
}
}
let _ = self.waiting_packets.dequeue();
debug!(
"Dropped packet on {} because neighbor was not found",
self.name
)
}
}
fn handle_missing_neighbor(&mut self, next_hop: IpAddress, packet: &[u8], now: Instant) {
let Ok(buf) = self.waiting_packets.enqueue(packet.len(), next_hop) else {
warn!(
"Dropped packet on {} because waiting queue was full",
self.name
);
return;
};
buf.copy_from_slice(packet);
let IpAddress::Ipv4(next_hop) = next_hop;
if let ArpState::Discovered = self.arp_state {
self.arp_state = ArpState::Discovering {
target: next_hop,
tries: 0,
silent_until: Instant::ZERO,
};
self.send_arp(now)
}
}
fn send_arp(&mut self, now: Instant) {
let Some(hardware_address) = self.hardware_address else {
return;
};
let Some(ip_address) = self.ip_address else {
return;
};
match self.arp_state {
ArpState::Discovered => {}
ArpState::Discovering { silent_until, .. } if silent_until > now => {}
ArpState::Discovering { target, tries, .. } if tries >= 3 => {
self.drop_waiting_packets(target, now)
}
ArpState::Discovering {
target,
ref mut tries,
ref mut silent_until,
} => {
let arp_repr = ArpRepr::EthernetIpv4 {
operation: ArpOperation::Request,
source_hardware_addr: hardware_address,
source_protocol_addr: ip_address.address(),
target_hardware_addr: EthernetAddress::BROADCAST,
target_protocol_addr: target,
};
*tries += 1;
*silent_until = now + Self::ARP_SILENCE_TIME;
self.send_to(
EthernetAddress::BROADCAST,
arp_repr.buffer_len(),
|buf| arp_repr.emit(&mut ArpPacket::new_unchecked(buf)),
EthernetProtocol::Arp,
);
}
}
}
}
impl LinkDevice for EthernetLink {
fn send(&mut self, next_hop: IpAddress, packet: &[u8], now: Instant) {
let local_broadcast = match self.ip_address.and_then(|cidr| cidr.broadcast()) {
Some(addr) => IpAddress::Ipv4(addr) == next_hop,
None => false,
};
if local_broadcast || next_hop.is_broadcast() {
self.send_to(
EthernetAddress::BROADCAST,
packet.len(),
|buf| buf.copy_from_slice(packet),
EthernetProtocol::Ipv4,
);
return;
}
match self.neighbor_cache.entry(next_hop) {
Entry::Vacant(_) => self.handle_missing_neighbor(next_hop, packet, now),
Entry::Occupied(e) => {
if e.get().expires_at < now {
e.remove();
self.handle_missing_neighbor(next_hop, packet, now)
} else {
let mac = e.get().hardware_address;
self.send_to(
mac,
packet.len(),
|buf| buf.copy_from_slice(packet),
EthernetProtocol::Ipv4,
)
}
}
}
}
fn recv(&mut self, now: Instant) -> Option<&[u8]> {
let Some(hardware_address) = self.hardware_address else {
return None;
};
let mut input_buffer = std::mem::replace(&mut self.input_buffer, Vec::new());
loop {
if let Err(e) = self.network_file.read(&mut input_buffer) {
if e.kind() != ErrorKind::WouldBlock {
error!("Failed to read ethernet device on link {}", self.name);
} else {
// No packet to read but we check if we have arp to send
self.send_arp(now);
}
self.input_buffer = input_buffer;
return None;
}
let packet = EthernetFrame::new_unchecked(&input_buffer[..]);
let Ok(repr) = EthernetRepr::parse(&packet) else {
debug!("Dropped incomming frame on {} (Malformed)", self.name);
continue;
};
// We let EMPTY_MAC pass because somehow this is the mac used when net=redir is used
if !repr.dst_addr.is_broadcast()
&& repr.dst_addr != EMPTY_MAC
&& repr.dst_addr != hardware_address
{
// Drop packets which are not for us
continue;
}
match repr.ethertype {
EthernetProtocol::Ipv4 => {
self.input_buffer = input_buffer;
return Some(EthernetFrame::new_unchecked(&self.input_buffer[..]).payload());
}
EthernetProtocol::Arp => self.process_arp(packet.payload(), now),
_ => continue,
}
}
}
fn name(&self) -> &Rc<str> {
&self.name
}
fn can_recv(&self) -> bool {
// We don't buffer any packets so we can't receive immediatly
false
}
fn mac_address(&self) -> Option<EthernetAddress> {
self.hardware_address
}
fn set_mac_address(&mut self, addr: EthernetAddress) {
self.hardware_address = Some(addr)
}
fn ip_address(&self) -> Option<IpCidr> {
Some(IpCidr::Ipv4(self.ip_address?))
}
fn set_ip_address(&mut self, addr: IpCidr) {
let IpCidr::Ipv4(addr) = addr;
self.ip_address = Some(addr);
}
}
@@ -0,0 +1,63 @@
use std::rc::Rc;
use smoltcp::storage::PacketMetadata;
use smoltcp::time::Instant;
use crate::scheme::Smolnetd;
use super::LinkDevice;
pub type PacketBuffer = smoltcp::storage::PacketBuffer<'static, ()>;
pub struct LoopbackDevice {
name: Rc<str>,
buffer: PacketBuffer,
}
impl Default for LoopbackDevice {
fn default() -> Self {
let buffer = PacketBuffer::new(
vec![PacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE],
vec![0u8; 1500 * Smolnetd::SOCKET_BUFFER_SIZE],
);
LoopbackDevice {
name: "loopback".into(),
buffer,
}
}
}
impl LinkDevice for LoopbackDevice {
fn send(&mut self, _next_hop: smoltcp::wire::IpAddress, packet: &[u8], _now: Instant) {
match self.buffer.enqueue(packet.len(), ()) {
Err(_) => warn!("loopback dropped packet because buffer was full"),
Ok(buf) => buf.copy_from_slice(packet),
}
}
fn recv(&mut self, _now: Instant) -> Option<&[u8]> {
self.buffer.dequeue().ok().map(|((), buf)| &*buf)
}
fn name(&self) -> &std::rc::Rc<str> {
&self.name
}
fn can_recv(&self) -> bool {
!self.buffer.is_empty()
}
fn mac_address(&self) -> Option<smoltcp::wire::EthernetAddress> {
None
}
fn set_mac_address(&mut self, _addr: smoltcp::wire::EthernetAddress) {}
fn ip_address(&self) -> Option<smoltcp::wire::IpCidr> {
Some("127.0.0.1/8".parse().unwrap())
}
fn set_ip_address(&mut self, _addr: smoltcp::wire::IpCidr) {
todo!()
}
}
@@ -0,0 +1,65 @@
pub mod ethernet;
pub mod loopback;
use std::rc::Rc;
use smoltcp::time::Instant;
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr};
/// Represent a link layer device (eth0, loopback...)
pub trait LinkDevice {
/// Send the given packet to the machine with the `next_hop` ip address
/// This method cannot fail so it's the implementor responsability
/// to buffer packets which can't be sent immediatly or decide to
/// drop them if necessary
fn send(&mut self, next_hop: IpAddress, packet: &[u8], now: Instant);
/// Returns None if nothing is received.
/// Returns an Ip packet otherwise
fn recv(&mut self, now: Instant) -> Option<&[u8]>;
/// Returns the LinkDevice display name used to refer to it and for lookups
fn name(&self) -> &Rc<str>;
/// Returns wether this device have packets pending
fn can_recv(&self) -> bool;
fn mac_address(&self) -> Option<EthernetAddress>;
fn set_mac_address(&mut self, addr: EthernetAddress);
fn ip_address(&self) -> Option<IpCidr>;
fn set_ip_address(&mut self, addr: IpCidr);
}
#[derive(Default)]
pub struct DeviceList {
inner: Vec<Box<dyn LinkDevice>>,
}
impl DeviceList {
pub fn push<T: LinkDevice + 'static>(&mut self, dev: T) {
self.inner.push(Box::new(dev))
}
pub fn get(&self, device_name: &str) -> Option<&dyn LinkDevice> {
self.inner
.iter()
.find(|dev| dev.name().as_ref() == device_name)
.map(|device| device.as_ref())
}
pub fn get_mut(&mut self, device_name: &str) -> Option<&mut (dyn LinkDevice + 'static)> {
self.inner
.iter_mut()
.find(|dev| dev.name().as_ref() == device_name)
.map(|device| device.as_mut())
}
pub fn iter(&self) -> impl Iterator<Item = &(dyn LinkDevice + 'static)> {
self.inner.iter().map(|b| b.as_ref())
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut (dyn LinkDevice + 'static)> {
self.inner.iter_mut().map(|b| b.as_mut())
}
}
+17
View File
@@ -0,0 +1,17 @@
use redox_log::{OutputBuilder, RedoxLogger};
pub fn init_logger(process_name: &str) {
if let Err(_) = RedoxLogger::new()
.with_output(
OutputBuilder::stdout()
.with_ansi_escape_codes()
.flush_on_newline(true)
.with_filter(log::LevelFilter::Trace)
.build(),
)
.with_process_name(process_name.into())
.enable()
{
eprintln!("{process_name}: Failed to init logger")
}
}
+198
View File
@@ -0,0 +1,198 @@
#[macro_use]
extern crate log;
use std::process;
use anyhow::{anyhow, bail, Context, Result};
use event::{EventFlags, EventQueue};
use libredox::flag::{O_NONBLOCK, O_RDWR};
use libredox::Fd;
use redox_scheme::Socket;
use scheme::Smolnetd;
use smoltcp::wire::EthernetAddress;
mod buffer_pool;
mod error;
mod link;
mod logger;
mod port_set;
mod router;
mod scheme;
fn get_network_adapter() -> Result<String> {
use std::fs;
let mut adapters = vec![];
for entry_res in fs::read_dir("/scheme")? {
let Ok(entry) = entry_res else {
continue;
};
let Ok(scheme) = entry.file_name().into_string() else {
continue;
};
if !scheme.starts_with("network") {
continue;
}
adapters.push(scheme);
}
if adapters.is_empty() {
bail!("no network adapter found");
} else {
let adapter = adapters.remove(0);
if !adapters.is_empty() {
// FIXME allow using multiple network adapters at the same time
warn!("Multiple network adapters found. Only {adapter} will be used");
}
Ok(adapter)
}
}
fn run(daemon: daemon::Daemon) -> Result<()> {
let adapter = get_network_adapter()?;
trace!("opening {adapter}:");
let network_fd = Fd::open(&format!("/scheme/{adapter}"), O_RDWR | O_NONBLOCK, 0)
.map_err(|e| anyhow!("failed to open {adapter}: {e}"))?;
let hardware_addr = std::fs::read(format!("/scheme/{adapter}/mac"))
.map(|mac_address| EthernetAddress::from_bytes(&mac_address))
.context("failed to get mac address from network adapter")?;
trace!("opening ip scheme socket");
let ip_fd = Socket::nonblock()
.map_err(|e| anyhow!("failed to open create ip scheme socket: {:?}", e))?;
trace!("opening udp scheme socket");
let udp_fd =
Socket::nonblock().map_err(|e| anyhow!("failed to open udp scheme socket: {:?}", e))?;
trace!("opening tcp scheme socket");
let tcp_fd =
Socket::nonblock().map_err(|e| anyhow!("failed to open tcp scheme socket: {:?}", e))?;
trace!("opening icmp scheme socket");
let icmp_fd =
Socket::nonblock().map_err(|e| anyhow!("failed to open icmp scheme socket: {:?}", e))?;
trace!("opening netcfg scheme socket");
let netcfg_fd =
Socket::nonblock().map_err(|e| anyhow!("failed to open netcfg scheme socket {:?}", e))?;
let time_path = format!("/scheme/time/{}", syscall::CLOCK_MONOTONIC);
let time_fd = Fd::open(&time_path, O_RDWR, 0).context("failed to open /scheme/time")?;
event::user_data! {
enum EventSource {
Network,
Time,
IpScheme,
UdpScheme,
TcpScheme,
IcmpScheme,
NetcfgScheme,
}
}
let event_queue = EventQueue::<EventSource>::new()
.map_err(|e| anyhow!("failed to create event queue: {:?}", e))?;
daemon.ready();
event_queue
.subscribe(network_fd.raw(), EventSource::Network, EventFlags::READ)
.map_err(|e| anyhow!("failed to listen to network events: {:?}", e))?;
event_queue
.subscribe(time_fd.raw(), EventSource::Time, EventFlags::READ)
.map_err(|e| anyhow!("failed to listen to timer events: {:?}", e))?;
event_queue
.subscribe(ip_fd.inner().raw(), EventSource::IpScheme, EventFlags::READ)
.context("failed to listen to ip scheme events")?;
event_queue
.subscribe(
udp_fd.inner().raw(),
EventSource::UdpScheme,
EventFlags::READ,
)
.context("failed to listen to udp scheme events")?;
event_queue
.subscribe(
tcp_fd.inner().raw(),
EventSource::TcpScheme,
EventFlags::READ,
)
.context("failed to listen to tcp scheme events")?;
event_queue
.subscribe(
icmp_fd.inner().raw(),
EventSource::IcmpScheme,
EventFlags::READ,
)
.context("failed to listen to icmp scheme events")?;
event_queue
.subscribe(
netcfg_fd.inner().raw(),
EventSource::NetcfgScheme,
EventFlags::READ,
)
.context("failed to listen to netcfg scheme events")?;
let mut smolnetd = Smolnetd::new(
network_fd,
hardware_addr,
ip_fd,
udp_fd,
tcp_fd,
icmp_fd,
time_fd,
netcfg_fd,
)
.context("smolnetd: failed to initialize smolnetd")?;
libredox::call::setrens(0, 0).context("smolnetd: failed to enter null namespace")?;
let all = {
use EventSource::*;
[Network, Time, IpScheme, UdpScheme, IcmpScheme, NetcfgScheme].map(Ok)
};
for event_res in all
.into_iter()
.chain(event_queue.map(|r| r.map(|e| e.user_data)))
{
match event_res.map_err(|e| anyhow!("event result is error: {:?}", e))? {
EventSource::Network => smolnetd.on_network_scheme_event(),
EventSource::Time => smolnetd.on_time_event(),
EventSource::IpScheme => smolnetd.on_ip_scheme_event(),
EventSource::UdpScheme => smolnetd.on_udp_scheme_event(),
EventSource::TcpScheme => smolnetd.on_tcp_scheme_event(),
EventSource::IcmpScheme => smolnetd.on_icmp_scheme_event(),
EventSource::NetcfgScheme => smolnetd.on_netcfg_scheme_event(),
}
.map_err(|e| error!("Received packet error: {:?}", e));
}
Ok(())
}
fn main() {
daemon::Daemon::new(daemon_runner);
}
fn daemon_runner(daemon: daemon::Daemon) -> ! {
logger::init_logger("smolnetd");
if let Err(err) = run(daemon) {
error!("smoltcpd: {}", err);
process::exit(1);
}
process::exit(0);
}
@@ -0,0 +1,62 @@
use std::collections::btree_map::{BTreeMap, Entry};
pub struct PortSet {
from: u16,
range: u16,
next: u16,
ports: BTreeMap<u16, usize>,
}
impl PortSet {
pub fn new(from: u16, to: u16) -> Option<PortSet> {
if from > to {
return None;
}
Some(PortSet {
from,
range: to - from + 1,
next: 0,
ports: BTreeMap::new(),
})
}
pub fn get_port(&mut self) -> Option<u16> {
if self.ports.len() >= self.range as usize {
return None;
}
let port = loop {
if let Entry::Vacant(entry) = self.ports.entry(self.next) {
entry.insert(1);
let port = self.from + self.next;
self.next = self.next.wrapping_add(1);
break port;
}
self.next = self.next.wrapping_add(1);
};
Some(port)
}
pub fn claim_port(&mut self, port: u16) -> bool {
if let Entry::Vacant(entry) = self.ports.entry(port) {
entry.insert(1);
true
} else {
false
}
}
pub fn acquire_port(&mut self, port: u16) {
*self.ports.entry(port).or_insert(0) += 1;
}
pub fn release_port(&mut self, port: u16) {
if let Entry::Occupied(mut entry) = self.ports.entry(port) {
*entry.get_mut() -= 1;
if *entry.get() == 0 {
entry.remove();
}
}
}
}
@@ -0,0 +1,190 @@
use std::cell::RefCell;
use std::rc::Rc;
use smoltcp::phy::{Device, DeviceCapabilities, Medium};
use smoltcp::storage::PacketMetadata;
use smoltcp::time::Instant;
use smoltcp::wire::IpAddress;
use self::route_table::RouteTable;
use crate::link::DeviceList;
use crate::scheme::Smolnetd;
pub mod route_table;
pub type PacketBuffer = smoltcp::storage::PacketBuffer<'static, ()>;
pub struct Router {
rx_buffer: PacketBuffer,
tx_buffer: PacketBuffer,
devices: Rc<RefCell<DeviceList>>,
route_table: Rc<RefCell<RouteTable>>,
}
impl Router {
pub fn new(devices: Rc<RefCell<DeviceList>>, route_table: Rc<RefCell<RouteTable>>) -> Self {
let rx_buffer = PacketBuffer::new(
vec![PacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE],
vec![0u8; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE],
);
let tx_buffer = PacketBuffer::new(
vec![PacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE],
vec![0u8; Router::MTU * Smolnetd::SOCKET_BUFFER_SIZE],
);
Self {
rx_buffer,
tx_buffer,
devices,
route_table,
}
}
pub const MTU: usize = 1486;
pub fn can_recv(&self) -> bool {
let mut can_recv = false;
for dev in self.devices.borrow().iter() {
can_recv |= dev.can_recv();
}
can_recv
}
pub fn poll(&mut self, now: Instant) {
for dev in self.devices.borrow_mut().iter_mut() {
if self.rx_buffer.is_full() {
break;
}
loop {
if self.rx_buffer.is_full() {
break;
}
let Some(buf) = dev.recv(now) else {
break;
};
self.rx_buffer
.enqueue(buf.len(), ())
.expect("We checked if it was full")
.copy_from_slice(buf);
}
}
}
pub fn dispatch(&mut self, now: Instant) {
while let Ok(((), packet)) = self.tx_buffer.dequeue() {
if let Ok(mut packet) = smoltcp::wire::Ipv4Packet::new_checked(packet) {
let dst_addr = IpAddress::Ipv4(packet.dst_addr());
if packet.dst_addr().is_broadcast() {
let buf = packet.into_inner();
for dev in self.devices.borrow_mut().iter_mut() {
dev.send(dst_addr, buf, now)
}
} else {
let route_table = self.route_table.borrow();
let Some(rule) = route_table.lookup_rule(&dst_addr) else {
warn!("No route found for destination: {}", dst_addr);
continue;
};
let next_hop = match rule.via {
Some(via) => via,
None => dst_addr,
};
let mut devices = self.devices.borrow_mut();
let Some(dev) = devices.get_mut(&rule.dev) else {
warn!("Device {} not found", rule.dev);
// TODO: Remove route if device doesn't exist anymore ?
continue;
};
let IpAddress::Ipv4(src) = rule.src;
if src != packet.src_addr() {
packet.set_src_addr(src);
packet.fill_checksum()
}
dev.send(next_hop, packet.into_inner(), now);
}
}
}
}
}
impl Device for Router {
type RxToken<'a> = RxToken<'a>;
type TxToken<'a> = TxToken<'a>;
fn receive(
&mut self,
_timestamp: smoltcp::time::Instant,
) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
if self.rx_buffer.is_empty() || self.tx_buffer.is_full() {
None
} else {
Some((
RxToken {
rx_buffer: &mut self.rx_buffer,
},
TxToken {
tx_buffer: &mut self.tx_buffer,
},
))
}
}
fn transmit(&mut self, _timestamp: smoltcp::time::Instant) -> Option<Self::TxToken<'_>> {
if self.tx_buffer.is_full() {
None
} else {
Some(TxToken {
tx_buffer: &mut self.tx_buffer,
})
}
}
fn capabilities(&self) -> smoltcp::phy::DeviceCapabilities {
let mut caps = DeviceCapabilities::default();
caps.medium = Medium::Ip;
caps.max_transmission_unit = Router::MTU;
caps.max_burst_size = Some(Smolnetd::SOCKET_BUFFER_SIZE);
caps
}
}
pub struct TxToken<'a> {
tx_buffer: &'a mut PacketBuffer,
}
impl smoltcp::phy::TxToken for TxToken<'_> {
fn consume<R, F>(self, len: usize, f: F) -> R
where
F: FnOnce(&mut [u8]) -> R,
{
f(self
.tx_buffer
.enqueue(len, ())
.expect("This was checked before creating the TxToken"))
}
}
pub struct RxToken<'a> {
rx_buffer: &'a mut PacketBuffer,
}
impl<'a> smoltcp::phy::RxToken for RxToken<'a> {
fn consume<R, F>(self, f: F) -> R
where
F: FnOnce(&[u8]) -> R,
{
let ((), buf) = self
.rx_buffer
.dequeue()
.expect("This was checked before creating the RxToken");
f(buf)
}
}
@@ -0,0 +1,99 @@
use std::fmt::Display;
use std::rc::Rc;
use smoltcp::wire::{IpAddress, IpCidr};
#[derive(Debug)]
pub struct Rule {
pub filter: IpCidr,
pub via: Option<IpAddress>,
pub dev: Rc<str>,
pub src: IpAddress,
}
impl Rule {
pub fn new(filter: IpCidr, via: Option<IpAddress>, dev: Rc<str>, src: IpAddress) -> Self {
Self {
filter,
via,
dev,
src,
}
}
}
impl Display for Rule {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.filter.prefix_len() == 0 {
write!(f, "default")?;
} else {
write!(f, "{} ", self.filter)?;
}
if let Some(via) = self.via {
write!(f, " via {}", via)?;
}
write!(f, " dev {}", self.dev)?;
write!(f, " src {}", self.src)?;
Ok(())
}
}
#[derive(Debug, Default)]
pub struct RouteTable {
rules: Vec<Rule>,
}
impl RouteTable {
pub fn lookup_rule(&self, dst: &IpAddress) -> Option<&Rule> {
self.rules
.iter()
.rev()
.find(|rule| rule.filter.contains_addr(dst))
}
pub fn lookup_src_addr(&self, dst: &IpAddress) -> Option<IpAddress> {
Some(self.lookup_rule(dst)?.src)
}
pub fn lookup_gateway(&self, dst: &IpAddress) -> Option<IpAddress> {
self.lookup_rule(dst)?.via
}
pub fn lookup_device(&self, dst: &IpAddress) -> Option<Rc<str>> {
Some(self.lookup_rule(dst)?.dev.clone())
}
pub fn insert_rule(&mut self, new_rule: Rule) {
let i = match self
.rules
.binary_search_by_key(&new_rule.filter.prefix_len(), |rule| {
rule.filter.prefix_len()
}) {
Ok(i) | Err(i) => i,
};
self.rules.insert(i, new_rule);
}
pub fn remove_rule(&mut self, filter: IpCidr) {
self.rules.retain(|rule| rule.filter != filter);
}
pub fn change_src(&mut self, old_src: IpAddress, new_src: IpAddress) {
for rule in self.rules.iter_mut().filter(|rule| rule.src == old_src) {
rule.src = new_src;
}
}
}
impl Display for RouteTable {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for rule in self.rules.iter() {
writeln!(f, "{}", rule)?;
}
Ok(())
}
}
@@ -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)
}
}
+181
View File
@@ -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(&notifier),
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)),
}
}
}