feat: Update netstack to use redox-scheme.

This commit is contained in:
Ibuki Omatsu
2025-12-13 13:44:43 +00:00
committed by Jeremy Soller
parent 3363174937
commit 4f3cba2cd0
10 changed files with 507 additions and 305 deletions
+253 -24
View File
@@ -4,10 +4,17 @@ 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::{SchemeSocket, SocketScheme};
use libredox::flag;
use libredox::Fd;
use redox_scheme::{
scheme::{IntoTag, Op, SchemeResponse, 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,
@@ -21,6 +28,7 @@ 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;
@@ -73,12 +81,12 @@ impl Smolnetd {
pub fn new(
network_file: Fd,
hardware_addr: EthernetAddress,
ip_file: Fd,
udp_file: Fd,
tcp_file: Fd,
icmp_file: Fd,
ip_file: Socket,
udp_file: Socket,
tcp_file: Socket,
icmp_file: Socket,
time_file: Fd,
netcfg_file: Fd,
netcfg_file: Socket,
) -> Smolnetd {
let protocol_addrs = vec![
//This is a placeholder IP for DHCP
@@ -133,29 +141,29 @@ impl Smolnetd {
Rc::clone(&iface),
Rc::clone(&route_table),
Rc::clone(&socket_set),
unsafe { File::from_raw_fd(ip_file.into_raw() as RawFd) },
ip_file,
),
udp_scheme: UdpScheme::new(
Rc::clone(&iface),
Rc::clone(&route_table),
Rc::clone(&socket_set),
unsafe { File::from_raw_fd(udp_file.into_raw() as RawFd) },
udp_file,
),
tcp_scheme: TcpScheme::new(
Rc::clone(&iface),
Rc::clone(&route_table),
Rc::clone(&socket_set),
unsafe { File::from_raw_fd(tcp_file.into_raw() as RawFd) },
tcp_file,
),
icmp_scheme: IcmpScheme::new(
Rc::clone(&iface),
Rc::clone(&route_table),
Rc::clone(&socket_set),
unsafe { File::from_raw_fd(icmp_file.into_raw() as RawFd) },
icmp_file,
),
netcfg_scheme: NetCfgScheme::new(
Rc::clone(&iface),
unsafe { File::from_raw_fd(netcfg_file.into_raw() as RawFd) },
netcfg_file,
Rc::clone(&route_table),
Rc::clone(&devices),
),
@@ -267,20 +275,13 @@ impl Smolnetd {
}
}
fn post_fevent(scheme_file: &mut File, fd: usize, event: usize, data_len: usize) -> Result<()> {
scheme_file
.write(&syscall::Packet {
id: 0,
pid: 0,
uid: 0,
gid: 0,
a: syscall::number::SYS_FEVENT,
b: fd,
c: event,
d: data_len,
})
.map(|_| ())
.map_err(|e| Error::from_io_error(e, "failed to post fevent"))
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 {
@@ -297,3 +298,231 @@ fn parse_endpoint(socket: &str) -> IpListenEndpoint {
.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>,
wait_queue: WaitQueue,
}
impl<SocketT> SchemeWrapper<SocketT>
where
SocketT: SchemeSocket + AnySocket<'static>,
{
pub fn new(
iface: Interface,
route_table: Rc<RefCell<RouteTable>>,
socket_set: Rc<RefCell<SocketSet>>,
scheme_file: Socket,
) -> Self {
Self {
scheme: SocketScheme::<SocketT>::new(iface, route_table, socket_set, scheme_file),
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) {
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),
};
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;
for (&fd, ref mut file) in &mut scheme.files {
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);
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")
})?;
}
};
}
Ok(())
}
}