[libos] Implement inner-defined sockopt with ioctl api

This commit is contained in:
ClawSeven 2024-04-30 14:22:17 +08:00 committed by volcano
parent 92ec7c334b
commit 9d4dcc2b21
14 changed files with 478 additions and 4 deletions

@ -1,7 +1,7 @@
use atomic::Atomic;
use std::any::Any;
use std::io::{Read, Seek, SeekFrom, Write};
use std::mem;
use atomic::Atomic;
use super::*;
use crate::fs::{

@ -1,14 +1,15 @@
use std::any::Any;
use std::io::{Read, Seek, SeekFrom, Write};
use atomic::{Atomic, Ordering};
use super::*;
use crate::fs::{AccessMode, File, HostFd, IoEvents, StatusFlags, STATUS_FLAGS_MASK};
use crate::fs::{
AccessMode, AtomicIoEvents, File, HostFd, IoEvents, StatusFlags, STATUS_FLAGS_MASK,
};
use crate::fs::{
GetIfConf, GetIfReqWithRawCmd, GetReadBufLen, IoctlCmd, NonBuiltinIoctlCmd, SetNonBlocking,
};
use crate::net::socket::sockopt::{GetSockOptRawCmd, SetSockOptRawCmd};
use atomic::{Atomic, Ordering};
//TODO: refactor write syscall to allow zero length with non-zero buffer
impl File for HostSocket {

@ -0,0 +1,64 @@
use crate::{fs::IoctlCmd, prelude::*};
use libc::ocall::getsockopt as do_getsockopt;
#[derive(Debug)]
pub struct GetSockOptRawCmd {
level: i32,
optname: i32,
optval: Box<[u8]>,
optlen: Option<u32>,
}
impl GetSockOptRawCmd {
pub fn new(level: i32, optname: i32, max_optlen: u32) -> Self {
// Using uninit slice is safe, since the buffer in rust SDK ocall is [out] type.
let optval = unsafe { Box::new_uninit_slice(max_optlen as usize).assume_init() };
Self {
level,
optname,
optval,
optlen: None,
}
}
pub fn execute(&mut self, fd: FileDesc) -> Result<()> {
if self.optlen.is_some() {
return_errno!(EINVAL, "can not execute twice");
}
self.optlen = Some(getsockopt_by_host(
fd,
self.level,
self.optname,
&mut self.optval,
)?);
Ok(())
}
pub fn output(&self) -> Option<&[u8]> {
self.optlen.map(|_| self.optval.as_ref())
}
}
impl IoctlCmd for GetSockOptRawCmd {}
pub fn getsockopt_by_host(
fd: FileDesc,
level: i32,
optname: i32,
optval: &mut [u8],
) -> Result<u32> {
let max_optlen = optval.len() as u32;
let mut optlen = max_optlen;
try_libc!(do_getsockopt(
fd as _,
level as _,
optname as _,
optval.as_mut_ptr() as _,
&mut optlen as *mut u32
));
// Defence Iago attack
if optlen > max_optlen {
return_errno!(EINVAL, "host returns a invalid optlen");
}
Ok(optlen)
}

@ -0,0 +1,3 @@
crate::impl_ioctl_cmd! {
pub struct GetAcceptConnCmd<Input=(), Output=i32> {}
}

@ -0,0 +1,3 @@
crate::impl_ioctl_cmd! {
pub struct GetDomainCmd<Input=(), Output=i32> {}
}

@ -0,0 +1,3 @@
crate::impl_ioctl_cmd! {
pub struct GetErrorCmd<Input=(), Output=i32> {}
}

@ -0,0 +1,105 @@
use super::{GetRecvTimeoutCmd, GetSendTimeoutCmd};
use super::{
GetAcceptConnCmd, GetDomainCmd, GetErrorCmd, GetPeerNameCmd, GetRcvBufSizeCmd,
GetSndBufSizeCmd, GetSockOptRawCmd, GetTypeCmd,
};
use libc::timeval;
use std::time::Duration;
use crate::prelude::*;
pub trait GetOutputAsBytes {
fn get_output_as_bytes(&self) -> Option<&[u8]>;
}
impl GetOutputAsBytes for GetSockOptRawCmd {
fn get_output_as_bytes(&self) -> Option<&[u8]> {
self.output()
}
}
impl GetOutputAsBytes for GetDomainCmd {
fn get_output_as_bytes(&self) -> Option<&[u8]> {
self.output().map(|val_ref| unsafe {
std::slice::from_raw_parts(val_ref as *const _ as *const u8, std::mem::size_of::<i32>())
})
}
}
impl GetOutputAsBytes for GetErrorCmd {
fn get_output_as_bytes(&self) -> Option<&[u8]> {
self.output().map(|val_ref| unsafe {
std::slice::from_raw_parts(val_ref as *const _ as *const u8, std::mem::size_of::<i32>())
})
}
}
impl GetOutputAsBytes for GetAcceptConnCmd {
fn get_output_as_bytes(&self) -> Option<&[u8]> {
self.output().map(|val_ref| unsafe {
std::slice::from_raw_parts(val_ref as *const _ as *const u8, std::mem::size_of::<i32>())
})
}
}
impl GetOutputAsBytes for GetPeerNameCmd {
fn get_output_as_bytes(&self) -> Option<&[u8]> {
self.output().map(|val_ref| unsafe {
std::slice::from_raw_parts(&(val_ref.0).0 as *const _ as *const u8, (val_ref.0).1)
})
}
}
impl GetOutputAsBytes for GetTypeCmd {
fn get_output_as_bytes(&self) -> Option<&[u8]> {
self.output().map(|val_ref| unsafe {
std::slice::from_raw_parts(val_ref as *const _ as *const u8, std::mem::size_of::<i32>())
})
}
}
impl GetOutputAsBytes for GetSndBufSizeCmd {
fn get_output_as_bytes(&self) -> Option<&[u8]> {
self.output().map(|val_ref| unsafe {
std::slice::from_raw_parts(
val_ref as *const _ as *const u8,
std::mem::size_of::<usize>(),
)
})
}
}
impl GetOutputAsBytes for GetRcvBufSizeCmd {
fn get_output_as_bytes(&self) -> Option<&[u8]> {
self.output().map(|val_ref| unsafe {
std::slice::from_raw_parts(
val_ref as *const _ as *const u8,
std::mem::size_of::<usize>(),
)
})
}
}
impl GetOutputAsBytes for GetRecvTimeoutCmd {
fn get_output_as_bytes(&self) -> Option<&[u8]> {
self.output().map(|val_ref| unsafe {
std::slice::from_raw_parts(
val_ref as *const _ as *const u8,
std::mem::size_of::<timeval>(),
)
})
}
}
impl GetOutputAsBytes for GetSendTimeoutCmd {
fn get_output_as_bytes(&self) -> Option<&[u8]> {
self.output().map(|val_ref| unsafe {
std::slice::from_raw_parts(
val_ref as *const _ as *const u8,
std::mem::size_of::<timeval>(),
)
})
}
}

@ -0,0 +1,15 @@
use sgx_trts::libc;
pub struct AddrStorage(pub (libc::sockaddr_storage, usize));
impl std::fmt::Debug for AddrStorage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("AddrStorage")
.field(&"sockaddr_storage")
.field(&(self.0).1)
.finish()
}
}
crate::impl_ioctl_cmd! {
pub struct GetPeerNameCmd<Input=(), Output=AddrStorage> {}
}

@ -0,0 +1,7 @@
crate::impl_ioctl_cmd! {
pub struct GetSndBufSizeCmd<Input=(), Output=usize> {}
}
crate::impl_ioctl_cmd! {
pub struct GetRcvBufSizeCmd<Input=(), Output=usize> {}
}

@ -0,0 +1,3 @@
crate::impl_ioctl_cmd! {
pub struct GetTypeCmd<Input=(), Output=i32> {}
}

@ -0,0 +1,105 @@
mod get;
mod get_acceptconn;
mod get_domain;
mod get_error;
mod get_output;
mod get_peername;
mod get_sockbuf;
mod get_type;
mod set;
mod set_sockbuf;
mod timeout;
pub use get::{getsockopt_by_host, GetSockOptRawCmd};
pub use get_acceptconn::GetAcceptConnCmd;
pub use get_domain::GetDomainCmd;
pub use get_error::GetErrorCmd;
pub use get_output::*;
pub use get_peername::{AddrStorage, GetPeerNameCmd};
pub use get_sockbuf::{GetRcvBufSizeCmd, GetSndBufSizeCmd};
pub use get_type::GetTypeCmd;
pub use set::{setsockopt_by_host, SetSockOptRawCmd};
pub use set_sockbuf::{SetRcvBufSizeCmd, SetSndBufSizeCmd};
pub use timeout::{
timeout_to_timeval, GetRecvTimeoutCmd, GetSendTimeoutCmd, SetRecvTimeoutCmd, SetSendTimeoutCmd,
};
use num_enum::{IntoPrimitive, TryFromPrimitive};
#[derive(Clone, Copy, Debug, Eq, PartialEq, IntoPrimitive, TryFromPrimitive)]
#[repr(i32)]
#[allow(non_camel_case_types)]
pub enum SockOptName {
SO_DEBUG = 1, // i32, bool, 0 or 1
SO_REUSEADDR = 2, // i32, bool, 0 or 1
SO_TYPE = 3, // [builtin] read-only, i32
SO_ERROR = 4, // read-only, i32
SO_DONTROUTE = 5, // i32, bool, 0 or 1, Equal to: send with MSG_DONTROUTE flag.
SO_BROADCAST = 6, // i32, bool, 0 or 1
SO_SNDBUF = 7, // i32
SO_RCVBUF = 8, // i32
SO_KEEPALIVE = 9, // i32, bool, 0 or 1
SO_OOBINLINE = 10, // i32, bool, 0 or 1, Might equal to: recv with MSG_OOB flag.
SO_NO_CHECK = 11, // i32, bool, 0 or 1
SO_PRIORITY = 12, // i32, >= 0, <= 6
SO_LINGER = 13, // linger structure
SO_BSDCOMPAT = 14, // removed in linux 2.2
SO_REUSEPORT = 15, // i32, bool, 0 or 1
SO_PASSCRED = 16, // i32, bool, 0 or 1
SO_PEERCRED = 17, // read-only, ucred structure
// TODO: there may be a bug.
// select(2), poll(2), and epoll(7) indicate a socket as readable
// only if at least SO_RCVLOWAT bytes are available.
SO_RCVLOWAT = 18, // i32
SO_SNDLOWAT = 19, // read-only, i32
SO_RCVTIMEO_OLD = 20, // struct timeval
SO_SNDTIMEO_OLD = 21, // struct timeval
SO_SECURITY_AUTHENTICATION = 22, // no doc / code
SO_SECURITY_ENCRYPTION_TRANSPORT = 23, // no doc / code
SO_SECURITY_ENCRYPTION_NETWORK = 24, // no doc / code
SO_BINDTODEVICE = 25, // array
SO_ATTACH_FILTER = 26, // SO_GET_FILTER, BPF-related
SO_DETACH_FILTER = 27, // SO_DETACH_BPF, BPF-related
SO_PEERNAME = 28, // [builtin] read-only, peer name
SO_TIMESTAMP_OLD = 29, // i32, bool, 0 or 1, cmsg-related
SO_ACCEPTCONN = 30, // [builtin] read-only, i32, bool, 0 or 1
SO_PEERSEC = 31, // read-only, array
SO_SNDBUFFORCE = 32, // i32
SO_RCVBUFFORCE = 33, // i32
SO_PASSSEC = 34, // i32, bool, 0 or 1, cmsg-related
SO_TIMESTAMPNS_OLD = 35, // i32, bool, 0 or 1, cmsg-related
SO_MARK = 36, // i32
SO_TIMESTAMPING_OLD = 37, // i32, bool, 0 or 1, cmsg-related
SO_PROTOCOL = 38, // read-only, i32
SO_DOMAIN = 39, // [builtin] read-only, i32
SO_RXQ_OVFL = 40, // i32, bool, 0 or 1, cmsg-related
SO_WIFI_STATUS = 41, // i32, bool, 0 or 1
// TODO: there may be a bug when specify SO_PEEK_OFF and MSG_PEEK
SO_PEEK_OFF = 42, // i32
SO_NOFCS = 43, // i32, bool, 0 or 1
SO_LOCK_FILTER = 44, // i32, bool, 0 or 1
SO_SELECT_ERR_QUEUE = 45, // i32, bool, 0 or 1
SO_BUSY_POLL = 46, // i32
SO_MAX_PACING_RATE = 47, // u64
SO_BPF_EXTENSIONS = 48, // BPF-related
SO_INCOMING_CPU = 49, // i32
SO_ATTACH_BPF = 50, // BPF-related
SO_ATTACH_REUSEPORT_CBPF = 51, // BPF-related
SO_ATTACH_REUSEPORT_EBPF = 52, // BPF-related
SO_CNX_ADVICE = 53, // write-only, i32
SCM_TIMESTAMPING_OPT_STATS = 54, // no doc / code
SO_MEMINFO = 55, // read-only, array
SO_INCOMING_NAPI_ID = 56, // read-only, i32
SO_COOKIE = 57, // read-only, u64
SCM_TIMESTAMPING_PKTINFO = 58, // no doc / code
SO_PEERGROUPS = 59, // read-only, array
SO_ZEROCOPY = 60, // i32, bool, 0 or 1
SO_TXTIME = 61, // SCM_TXTIME, struct sock_txtime
SO_BINDTOIFINDEX = 62, // i32
SO_TIMESTAMP_NEW = 63, // i32, bool, 0 or 1, cmsg-related
SO_TIMESTAMPNS_NEW = 64, // i32, bool, 0 or 1, cmsg-related
SO_TIMESTAMPING_NEW = 65, // i32, bool, 0 or 1, cmsg-related
SO_RCVTIMEO_NEW = 66, // struct timeval
SO_SNDTIMEO_NEW = 67, // struct timeval
SO_DETACH_REUSEPORT_BPF = 68, // BPF-related
}

@ -0,0 +1,38 @@
use crate::{fs::IoctlCmd, prelude::*};
use libc::ocall::setsockopt as do_setsockopt;
#[derive(Debug)]
pub struct SetSockOptRawCmd {
level: i32,
optname: i32,
optval: Box<[u8]>,
}
impl SetSockOptRawCmd {
pub fn new(level: i32, optname: i32, optval: &[u8]) -> Self {
let optval = Box::from(optval);
Self {
level,
optname,
optval,
}
}
pub fn execute(&mut self, fd: FileDesc) -> Result<()> {
setsockopt_by_host(fd, self.level, self.optname, &self.optval)?;
Ok(())
}
}
impl IoctlCmd for SetSockOptRawCmd {}
pub fn setsockopt_by_host(fd: FileDesc, level: i32, optname: i32, optval: &[u8]) -> Result<()> {
try_libc!(do_setsockopt(
fd as _,
level as _,
optname as _,
optval.as_ptr() as _,
optval.len() as _
));
Ok(())
}

@ -0,0 +1,62 @@
use super::set::setsockopt_by_host;
use crate::{fs::IoctlCmd, prelude::*};
#[derive(Debug)]
pub struct SetSndBufSizeCmd {
buf_size: usize,
}
impl SetSndBufSizeCmd {
pub fn new(buf_size: usize) -> Self {
Self { buf_size }
}
pub fn buf_size(&self) -> usize {
self.buf_size
}
pub fn update_host(&self, fd: FileDesc) -> Result<()> {
// The buf size for host call should be divided by 2 because the value will be doubled by host kernel.
let host_call_buf_size = (self.buf_size / 2).to_ne_bytes();
// Setting SO_SNDBUF for host socket needs to respect /proc/sys/net/core/wmem_max. Thus, the value might be different on host, but it is fine.
setsockopt_by_host(
fd,
libc::SOL_SOCKET,
super::SockOptName::SO_SNDBUF.into(),
&host_call_buf_size,
)
}
}
impl IoctlCmd for SetSndBufSizeCmd {}
#[derive(Debug)]
pub struct SetRcvBufSizeCmd {
buf_size: usize,
}
impl SetRcvBufSizeCmd {
pub fn new(buf_size: usize) -> Self {
Self { buf_size }
}
pub fn buf_size(&self) -> usize {
self.buf_size
}
pub fn update_host(&self, fd: FileDesc) -> Result<()> {
// The buf size for host call should be divided by 2 because the value will be doubled by host kernel.
let host_call_buf_size = (self.buf_size / 2).to_ne_bytes();
// Setting SO_RCVBUF for host socket needs to respect /proc/sys/net/core/rmem_max. Thus, the value might be different on host, but it is fine.
setsockopt_by_host(
fd,
libc::SOL_SOCKET,
super::SockOptName::SO_RCVBUF.into(),
&host_call_buf_size,
)
}
}
impl IoctlCmd for SetRcvBufSizeCmd {}

@ -0,0 +1,65 @@
use crate::fs::IoctlCmd;
use crate::prelude::*;
use libc::{suseconds_t, time_t};
use std::time::Duration;
#[derive(Debug)]
pub struct SetSendTimeoutCmd(Duration);
impl IoctlCmd for SetSendTimeoutCmd {}
impl SetSendTimeoutCmd {
pub fn new(timeout: Duration) -> Self {
Self(timeout)
}
pub fn timeout(&self) -> &Duration {
&self.0
}
}
#[derive(Debug)]
pub struct SetRecvTimeoutCmd(Duration);
impl IoctlCmd for SetRecvTimeoutCmd {}
impl SetRecvTimeoutCmd {
pub fn new(timeout: Duration) -> Self {
Self(timeout)
}
pub fn timeout(&self) -> &Duration {
&self.0
}
}
crate::impl_ioctl_cmd! {
pub struct GetSendTimeoutCmd<Input=(), Output=timeval> {}
}
crate::impl_ioctl_cmd! {
pub struct GetRecvTimeoutCmd<Input=(), Output=timeval> {}
}
pub fn timeout_to_timeval(timeout: Option<Duration>) -> timeval {
match timeout {
Some(duration) => {
let sec = duration.as_secs();
let usec = duration.subsec_micros();
timeval {
sec: sec as time_t,
usec: usec as suseconds_t,
}
}
None => timeval { sec: 0, usec: 0 },
}
}
// Same as libc::timeval
#[repr(C)]
#[derive(Debug, Copy, Clone)]
#[allow(non_camel_case_types)]
pub struct timeval {
sec: time_t,
usec: suseconds_t,
}