Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ mman = []
## [hermit-c]: https://github.com/hermit-os/hermit-c
newlib = []

## Enable stubbing result values for some system calls.
##
## For some system calls related to system options in which Hermit does not implement all possible options,
## return 0 (success) instead of an error when the option is unknown ; or return a convincing fake value.
syscall-fake-values = []

#! ### Platform Features

## Enables [Uhyve] support.
Expand Down
2 changes: 2 additions & 0 deletions src/executor/network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,8 @@ pub(crate) fn init() {
spawn(network_run());
#[cfg(feature = "dhcpv4")]
spawn(dhcpv4_run());
} else {
warn!("Network initialization failed.");
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/fd/delegate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use crate::fd::socket::vsock;
use crate::fd::stdio::{ConsoleStderr, ConsoleStdin, ConsoleStdout};
#[cfg(feature = "uhyve")]
use crate::fd::stdio::{UhyveStderr, UhyveStdin, UhyveStdout};
use crate::fd::{AccessPermission, ObjectInterface, PollEvent, StatusFlags};
use crate::fd::{AccessPermission, ObjectInterface, PollEvent, SocketOptionValue, StatusFlags};
#[cfg(any(feature = "net", feature = "virtio-vsock"))]
use crate::fd::{Endpoint, ListenEndpoint, SocketOption};
use crate::fs::mem::{MemDirectoryInterface, RamFileInterface, RomFileInterface};
Expand Down Expand Up @@ -158,7 +158,7 @@ impl ObjectInterface for Fd {
#[cfg(any(feature = "net", feature = "virtio-vsock"))]
async fn listen(&mut self, _backlog: i32) -> io::Result<()>;
#[cfg(any(feature = "net", feature = "virtio-vsock"))]
async fn setsockopt(&self, _opt: SocketOption, _optval: bool) -> io::Result<()>;
async fn setsockopt(&self, _opt: SocketOption, _optval: SocketOptionValue) -> io::Result<()>;
#[cfg(any(feature = "net", feature = "virtio-vsock"))]
async fn getsockopt(&self, _opt: SocketOption) -> io::Result<c_int>;
#[cfg(any(feature = "net", feature = "virtio-vsock"))]
Expand Down
104 changes: 96 additions & 8 deletions src/fd/mod.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
use alloc::sync::Arc;
#[cfg(any(feature = "net", feature = "virtio-vsock"))]
use core::ffi::c_int;
use core::future;
use core::mem::MaybeUninit;
use core::pin::pin;
use core::task::Poll::{Pending, Ready};
use core::time::Duration;

#[cfg(any(feature = "net", feature = "virtio-vsock"))]
use num_enum::TryFromPrimitive;
use num_enum::{IntoPrimitive, TryFromPrimitive};
#[cfg(feature = "net")]
use smoltcp::wire::{IpEndpoint, IpListenEndpoint};

Expand All @@ -18,6 +16,8 @@ use crate::errno::Errno;
use crate::executor::block_on;
use crate::fs::{FileAttr, SeekWhence};
use crate::io;
#[cfg(feature = "net")]
use crate::syscalls::socket::{Ipproto, SOL_SOCKET, socklen_t};

mod delegate;
mod eventfd;
Expand Down Expand Up @@ -48,12 +48,100 @@ pub(crate) enum ListenEndpoint {
Vsock(socket::vsock::VsockListenEndpoint),
}

#[cfg(any(feature = "net", feature = "virtio-vsock"))]
#[derive(TryFromPrimitive, PartialEq, Eq, Clone, Copy, Debug)]
#[repr(i32)]
#[allow(dead_code)]
#[derive(Debug, PartialEq, Eq)]
pub(crate) enum SocketOption {
TcpNodelay = 1,
TcpOption(SocketOptionTcp),
SocketOption(SocketOptionSocket),
}

#[cfg(feature = "net")]
impl SocketOption {
pub fn from_level_optname(level: i32, optname: i32) -> Option<SocketOption> {
if level == SOL_SOCKET {
SocketOptionSocket::try_from(optname)
.ok()
.map(SocketOption::SocketOption)
} else {
let protocol = u8::try_from(level)
.ok()
.and_then(|proto| Ipproto::try_from(proto).ok())?;

match protocol {
Ipproto::Tcp => SocketOptionTcp::try_from(optname)
.ok()
.map(SocketOption::TcpOption),
_ => None,
}
}
}
}

#[cfg(feature = "net")]
pub struct SocketOptionValue {
optval: *const core::ffi::c_void,
optlen: socklen_t,
}

#[cfg(not(feature = "net"))]
pub struct SocketOptionValue;

unsafe impl Send for SocketOptionValue {}

#[cfg(feature = "net")]
impl SocketOptionValue {
pub fn new(optval: *const core::ffi::c_void, optlen: socklen_t) -> Self {
Self { optval, optlen }
}
}

#[cfg(feature = "net")]
impl TryFrom<&SocketOptionValue> for i32 {
type Error = Errno;

fn try_from(value: &SocketOptionValue) -> Result<Self, Self::Error> {
if value.optval.is_null() {
return Err(Errno::Inval);
}

if value.optlen != size_of::<i32>() as u32 {
return Err(Errno::Inval);
}

let value = unsafe { *value.optval.cast::<i32>() };
Ok(value)
}
}

#[cfg(feature = "net")]
impl TryFrom<&SocketOptionValue> for bool {
type Error = Errno;

fn try_from(value: &SocketOptionValue) -> Result<Self, Self::Error> {
let value: i32 = value.try_into()?;
Ok(value != 0)
}
}

#[derive(TryFromPrimitive, IntoPrimitive, PartialEq, Eq, Clone, Copy, Debug)]
#[repr(i32)]
#[non_exhaustive]
pub(crate) enum SocketOptionTcp {
#[doc(alias = "TCP_NODELAY")]
TcpNoDelay = 1,
}

#[derive(TryFromPrimitive, IntoPrimitive, PartialEq, Eq, Clone, Copy, Debug)]
#[repr(i32)]
#[non_exhaustive]
pub(crate) enum SocketOptionSocket {
#[doc(alias = "SO_REUSEADDR")]
ReuseAddr = 1,
#[doc(alias = "SO_KEEPALIVE")]
KeepAlive = 8,
#[doc(alias = "SO_SNDBUF")]
SoSndbuf = 0x1001,
#[doc(alias = "SO_RCVBUF")]
SoRcvbuf = 0x1002,
}

Expand Down Expand Up @@ -265,7 +353,7 @@ pub(crate) trait ObjectInterface: Sync + Send {

/// `setsockopt` sets options on sockets
#[cfg(any(feature = "net", feature = "virtio-vsock"))]
async fn setsockopt(&self, _opt: SocketOption, _optval: bool) -> io::Result<()> {
async fn setsockopt(&self, _opt: SocketOption, _optval: SocketOptionValue) -> io::Result<()> {
Err(Errno::Notsock)
}

Expand Down
63 changes: 49 additions & 14 deletions src/fd/socket/tcp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ use smoltcp::wire::{IpEndpoint, Ipv4Address, Ipv6Address};
use crate::errno::Errno;
use crate::executor::block_on;
use crate::executor::network::{Handle, NIC, wake_network_waker};
use crate::fd::{self, Endpoint, Fd, ListenEndpoint, ObjectInterface, PollEvent, SocketOption};
use crate::fd::{
self, Endpoint, Fd, ListenEndpoint, ObjectInterface, PollEvent, SocketOption,
SocketOptionSocket, SocketOptionTcp, SocketOptionValue,
};
use crate::syscalls::socket::Af;
use crate::{DEFAULT_KEEP_ALIVE_INTERVAL, io};

Expand Down Expand Up @@ -426,19 +429,38 @@ impl ObjectInterface for Socket {
Ok(())
}

async fn setsockopt(&self, opt: SocketOption, optval: bool) -> io::Result<()> {
if opt == SocketOption::TcpNodelay {
let mut guard = NIC.lock();
let nic = guard.as_nic_mut().unwrap();
async fn setsockopt(&self, opt: SocketOption, optval: SocketOptionValue) -> io::Result<()> {
let mut guard = NIC.lock();
let nic = guard.as_nic_mut().unwrap();

for handle in self.handle.iter().copied() {
let socket = nic.get_mut_socket::<tcp::Socket<'_>>(handle);
socket.set_nagle_enabled(optval);
match opt {
SocketOption::TcpOption(SocketOptionTcp::TcpNoDelay) => {
let is_enabled = (&optval).try_into()?;
for handle in self.handle.iter().copied() {
let socket = nic.get_mut_socket::<tcp::Socket<'_>>(handle);
socket.set_nagle_enabled(is_enabled);
}
Ok(())
}
SocketOption::SocketOption(SocketOptionSocket::KeepAlive) => {
let keepalive: bool = (&optval).try_into()?;
let keepalive = if keepalive {
Some(Duration::from_secs(120))
} else {
None
};

Ok(())
} else {
Err(Errno::Inval)
for i in self.handle.iter() {
let socket = nic.get_mut_socket::<tcp::Socket<'_>>(*i);
socket.set_keep_alive(keepalive);
}

Ok(())
}
other => {
warn!("TCP: unsupported option {other:?}");
Err(Errno::Inval)
}
}
}

Expand All @@ -448,9 +470,22 @@ impl ObjectInterface for Socket {
let socket = nic.get_mut_socket::<tcp::Socket<'_>>(*self.handle.first().unwrap());

match opt {
SocketOption::TcpNodelay => Ok(socket.nagle_enabled().into()),
SocketOption::SoSndbuf => Ok(c_int::try_from(socket.send_capacity()).unwrap()),
SocketOption::SoRcvbuf => Ok(c_int::try_from(socket.recv_capacity()).unwrap()),
SocketOption::TcpOption(SocketOptionTcp::TcpNoDelay) => {
Ok(socket.nagle_enabled().into())
}
SocketOption::SocketOption(SocketOptionSocket::KeepAlive) => {
Ok(socket.keep_alive().is_some().into())
}
SocketOption::SocketOption(SocketOptionSocket::SoSndbuf) => {
Ok(c_int::try_from(socket.send_capacity()).unwrap())
}
SocketOption::SocketOption(SocketOptionSocket::SoRcvbuf) => {
Ok(c_int::try_from(socket.recv_capacity()).unwrap())
}
other => {
warn!("TCP: unsupported option {other:?}");
Err(Errno::Inval)
}
}
}

Expand Down
8 changes: 4 additions & 4 deletions src/fd/socket/udp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use smoltcp::wire::{IpEndpoint, Ipv4Address, Ipv6Address};
use crate::errno::Errno;
use crate::executor::block_on;
use crate::executor::network::{Handle, NIC, wake_network_waker};
use crate::fd::{self, Endpoint, ListenEndpoint, ObjectInterface, PollEvent, SocketOption};
use crate::fd::{self, Endpoint, ListenEndpoint, ObjectInterface, PollEvent, SocketOption, SocketOptionSocket};
use crate::io;
use crate::syscalls::socket::Af;

Expand Down Expand Up @@ -252,9 +252,9 @@ impl ObjectInterface for Socket {
.get_mut_socket::<udp::Socket<'_>>(self.handle);

match opt {
SocketOption::TcpNodelay => Err(Errno::Inval),
SocketOption::SoSndbuf => Ok(c_int::try_from(socket.payload_send_capacity()).unwrap()),
SocketOption::SoRcvbuf => Ok(c_int::try_from(socket.payload_recv_capacity()).unwrap()),
SocketOption::SocketOption(SocketOptionSocket::SoSndbuf) => Ok(c_int::try_from(socket.payload_send_capacity()).unwrap()),
SocketOption::SocketOption(SocketOptionSocket::SoRcvbuf) => Ok(c_int::try_from(socket.payload_recv_capacity()).unwrap()),
_ => Err(Errno::Inval)
}
}
}
Expand Down
37 changes: 27 additions & 10 deletions src/fs/mem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ use alloc::sync::Arc;
use alloc::vec::Vec;
use core::marker::PhantomData;
use core::mem::MaybeUninit;
#[cfg(feature = "syscall-fake-values")]
use core::sync::atomic::{AtomicU64, Ordering};
use core::{mem, ptr};

use align_address::Align;
Expand Down Expand Up @@ -36,6 +38,9 @@ impl RomFileInner {
}
}

#[cfg(feature = "syscall-fake-values")]
static VFS_INO_NUM: AtomicU64 = AtomicU64::new(10_000);

pub struct RomFileInterface {
/// Position within the file
pos: Mutex<usize>,
Expand Down Expand Up @@ -306,6 +311,8 @@ impl RomFile {
st_atim: t,
st_mtim: t,
st_ctim: t,
#[cfg(feature = "syscall-fake-values")]
st_ino: VFS_INO_NUM.fetch_add(1, Ordering::AcqRel),
..Default::default()
};

Expand Down Expand Up @@ -361,6 +368,12 @@ impl RamFile {
st_atim: t,
st_mtim: t,
st_ctim: t,
#[cfg(feature = "syscall-fake-values")]
st_nlink: 1,
#[cfg(feature = "syscall-fake-values")]
st_blksize: 4096,
#[cfg(feature = "syscall-fake-values")]
st_ino: VFS_INO_NUM.fetch_add(1, Ordering::AcqRel),
..Default::default()
};

Expand Down Expand Up @@ -614,15 +627,17 @@ impl VfsNode for MemDirectory {
block_on(
async {
let (component, rest) = path.split_once("/").unwrap_or((path, ""));
let inner = self.inner.read().await;
let node = inner.get(component).ok_or(Errno::Badf)?;

if !rest.is_empty() {
let inner = self.inner.read().await;
let directory = inner.get(component).ok_or(Errno::Badf)?;
return directory.traverse_lstat(rest);
if node.get_kind() == NodeKind::File {
Err(Errno::Notdir)?;
}

return node.traverse_lstat(rest);
}

let inner = self.inner.read().await;
let node = inner.get(component).ok_or(Errno::Badf)?;
node.get_file_attributes()
},
None,
Expand All @@ -633,15 +648,17 @@ impl VfsNode for MemDirectory {
block_on(
async {
let (component, rest) = path.split_once("/").unwrap_or((path, ""));
let inner = self.inner.read().await;
let node = inner.get(component).ok_or(Errno::Badf)?;

if !rest.is_empty() {
let inner = self.inner.read().await;
let directory = inner.get(component).ok_or(Errno::Badf)?;
return directory.traverse_stat(rest);
if node.get_kind() == NodeKind::File {
Err(Errno::Notdir)?;
}

return node.traverse_stat(rest);
}

let inner = self.inner.read().await;
let node = inner.get(component).ok_or(Errno::Badf)?;
node.get_file_attributes()
},
None,
Expand Down
Loading
Loading