diff --git a/services/libs/aster-std/src/syscall/getsockopt.rs b/services/libs/aster-std/src/syscall/getsockopt.rs index e4a318128..150878dc6 100644 --- a/services/libs/aster-std/src/syscall/getsockopt.rs +++ b/services/libs/aster-std/src/syscall/getsockopt.rs @@ -1,13 +1,10 @@ -use crate::{ - fs::file_table::FileDescripter, - log_syscall_entry, - net::socket::{SockOptionLevel, SockOptionName}, - prelude::*, - syscall::SYS_SETSOCKOPT, - util::{read_val_from_user, write_val_to_user}, -}; +use crate::fs::file_table::FileDescripter; +use crate::prelude::*; +use crate::util::net::{new_raw_socket_option, SockOptionLevel}; +use crate::util::{read_val_from_user, write_val_to_user}; +use crate::{get_socket_without_holding_filetable_lock, log_syscall_entry}; -use super::SyscallReturn; +use super::{SyscallReturn, SYS_SETSOCKOPT}; pub fn sys_getsockopt( sockfd: FileDescripter, @@ -18,29 +15,26 @@ pub fn sys_getsockopt( ) -> Result { log_syscall_entry!(SYS_SETSOCKOPT); let level = SockOptionLevel::try_from(level)?; - let sock_option_name = SockOptionName::try_from(optname)?; if optval == 0 || optlen_addr == 0 { return_errno_with_message!(Errno::EINVAL, "optval or optlen_addr is null pointer"); } let optlen: u32 = read_val_from_user(optlen_addr)?; - debug!( - "level = {level:?}, sockfd = {sockfd}, optname = {sock_option_name:?}, optlen = {optlen}" - ); + debug!("level = {level:?}, sockfd = {sockfd}, optname = {optname:?}, optlen = {optlen}"); let current = current!(); - let file_table = current.file_table().lock(); - let socket = file_table - .get_file(sockfd)? - .as_socket() - .ok_or_else(|| Error::with_message(Errno::ENOTSOCK, "the file is not socket"))?; + get_socket_without_holding_filetable_lock!(socket, current, sockfd); - // FIXME: This is only a workaround. Writing zero means the socket does not have error. - // The linux manual says that writing a non-zero value if there are errors. But what value is it? - if sock_option_name == SockOptionName::SO_ERROR { - assert!(optlen == 4); - write_val_to_user(optval, &0i32)?; - } + let mut raw_option = new_raw_socket_option(level, optname)?; - // TODO: do real getsockopt + debug!("raw option: {:?}", raw_option); + + socket.option(raw_option.as_sock_option_mut())?; + + let write_len = { + let vmar = current.root_vmar(); + raw_option.write_output(vmar, optval, optlen)? + }; + + write_val_to_user(optlen_addr, &(write_len as u32))?; Ok(SyscallReturn::Return(0)) } diff --git a/services/libs/aster-std/src/syscall/setsockopt.rs b/services/libs/aster-std/src/syscall/setsockopt.rs index b9bc864b3..ecc8766e5 100644 --- a/services/libs/aster-std/src/syscall/setsockopt.rs +++ b/services/libs/aster-std/src/syscall/setsockopt.rs @@ -1,34 +1,42 @@ -use crate::log_syscall_entry; -use crate::net::socket::{SockOptionLevel, SockOptionName}; -use crate::util::read_bytes_from_user; +use crate::util::net::{new_raw_socket_option, SockOptionLevel}; use crate::{fs::file_table::FileDescripter, prelude::*}; +use crate::{get_socket_without_holding_filetable_lock, log_syscall_entry}; -use super::SyscallReturn; -use super::SYS_SETSOCKOPT; +use super::{SyscallReturn, SYS_SETSOCKOPT}; pub fn sys_setsockopt( sockfd: FileDescripter, level: i32, optname: i32, optval: Vaddr, - optlen: usize, + optlen: u32, ) -> Result { log_syscall_entry!(SYS_SETSOCKOPT); let level = SockOptionLevel::try_from(level)?; - let sock_option_name = SockOptionName::try_from(optname)?; if optval == 0 { return_errno_with_message!(Errno::EINVAL, "optval is null pointer"); } - let mut sock_opt_val = vec![0u8; optlen]; - read_bytes_from_user(optval, &mut sock_opt_val)?; - debug!("level = {level:?}, sockfd = {sockfd}, optname = {sock_option_name:?}, optval = {sock_opt_val:?}"); + debug!( + "level = {:?}, sockfd = {}, optname = {}, optval = {}", + level, sockfd, optname, optlen + ); + let current = current!(); - let file_table = current.file_table().lock(); - let socket = file_table - .get_file(sockfd)? - .as_socket() - .ok_or_else(|| Error::with_message(Errno::ENOTSOCK, "the file is not socket"))?; - // TODO: do setsockopt + get_socket_without_holding_filetable_lock!(socket, current, sockfd); + + let raw_option = { + let mut option = new_raw_socket_option(level, optname)?; + + let vmar = current.root_vmar(); + option.read_input(vmar, optval, optlen)?; + + option + }; + + debug!("raw option: {:?}", raw_option); + + socket.set_option(raw_option.as_sock_option())?; + Ok(SyscallReturn::Return(0)) } diff --git a/services/libs/aster-std/src/util/net/mod.rs b/services/libs/aster-std/src/util/net/mod.rs index 571cfc939..2d6320664 100644 --- a/services/libs/aster-std/src/util/net/mod.rs +++ b/services/libs/aster-std/src/util/net/mod.rs @@ -1,7 +1,12 @@ mod addr; +mod options; mod socket; -pub use addr::{read_socket_addr_from_user, write_socket_addr_to_user, SaFamily}; +pub use addr::{ + read_socket_addr_from_user, write_socket_addr_to_user, InetAddr, SaFamily, SockAddr, + SockAddrInet, SockAddrInet6, SockAddrUnix, +}; +pub use options::{new_raw_socket_option, RawSockOption, SockOptionLevel}; pub use socket::{Protocol, SockFlags, SockType, SOCK_TYPE_MASK}; #[macro_export] diff --git a/services/libs/aster-std/src/util/net/options/mod.rs b/services/libs/aster-std/src/util/net/options/mod.rs new file mode 100644 index 000000000..2160b9c5c --- /dev/null +++ b/services/libs/aster-std/src/util/net/options/mod.rs @@ -0,0 +1,175 @@ +use aster_rights::Full; + +use crate::net::socket::options::SockOption; +use crate::prelude::*; +use crate::vm::vmar::Vmar; + +mod socket; +mod tcp; +mod utils; + +use self::socket::new_socket_option; +use self::tcp::new_tcp_option; + +pub trait RawSockOption: SockOption { + fn read_input(&mut self, vmar: &Vmar, addr: Vaddr, max_len: u32) -> Result<()>; + + fn write_output(&self, vmar: &Vmar, addr: Vaddr, max_len: u32) -> Result; + + fn as_sock_option_mut(&mut self) -> &mut dyn SockOption; + + fn as_sock_option(&self) -> &dyn SockOption; +} + +/// Impl `RawSockOption` for a struct which implements `SockOption`. +#[macro_export] +macro_rules! impl_raw_sock_option { + ($option:ty) => { + impl RawSockOption for $option { + fn read_input(&mut self, vmar: &Vmar, addr: Vaddr, max_len: u32) -> Result<()> { + use aster_frame::vm::VmIo; + + let input = vmar.read_val(addr)?; + + if (max_len as usize) < core::mem::size_of_val(&input) { + return_errno_with_message!(Errno::EINVAL, "max_len is too small"); + } + + self.set_input(input); + + Ok(()) + } + + fn write_output(&self, vmar: &Vmar, addr: Vaddr, max_len: u32) -> Result { + use aster_frame::vm::VmIo; + + let output = self.output().unwrap(); + + let write_len = core::mem::size_of_val(output); + + if (max_len as usize) < write_len { + return_errno_with_message!(Errno::EINVAL, "max_len is too small"); + } + + vmar.write_val(addr, output)?; + Ok(write_len) + } + + fn as_sock_option_mut(&mut self) -> &mut dyn SockOption { + self + } + + fn as_sock_option(&self) -> &dyn SockOption { + self + } + } + }; + ($option: ty, $reader: ident, $writer: ident) => { + impl RawSockOption for $option { + fn read_input(&mut self, vmar: &Vmar, addr: Vaddr, max_len: u32) -> Result<()> { + let input = $reader(vmar, addr, max_len)?; + self.set_input(input); + Ok(()) + } + + fn write_output(&self, vmar: &Vmar, addr: Vaddr, max_len: u32) -> Result { + let output = self.output().unwrap(); + $writer(output, vmar, addr, max_len) + } + + fn as_sock_option_mut(&mut self) -> &mut dyn SockOption { + self + } + + fn as_sock_option(&self) -> &dyn SockOption { + self + } + } + }; +} + +/// Impl `RawSockOption` for a struct which is for only `getsockopt` and implements `SockOption`. +#[macro_export] +macro_rules! impl_raw_sock_option_get_only { + ($option:ty) => { + impl RawSockOption for $option { + fn read_input( + &mut self, + _vmar: &Vmar, + _addr: Vaddr, + _max_len: u32, + ) -> Result<()> { + return_errno_with_message!(Errno::ENOPROTOOPT, "the option is getter-only"); + } + + fn write_output(&self, vmar: &Vmar, addr: Vaddr, max_len: u32) -> Result { + use jinux_frame::vm::VmIo; + + let output = self.output().unwrap(); + + let write_len = core::mem::size_of_val(output); + + if (max_len as usize) < write_len { + return_errno_with_message!(Errno::EINVAL, "max_len is too small"); + } + + vmar.write_val(addr, output)?; + Ok(write_len) + } + + fn as_sock_option_mut(&mut self) -> &mut dyn SockOption { + self + } + + fn as_sock_option(&self) -> &dyn SockOption { + self + } + } + }; + ($option: ty, $writer: ident) => { + impl RawSockOption for $option { + fn read_input( + &mut self, + _vmar: &Vmar, + _addr: Vaddr, + _max_len: u32, + ) -> Result<()> { + return_errno_with_message!(Errno::ENOPROTOOPT, "the option is getter-only"); + } + + fn write_output(&self, vmar: &Vmar, addr: Vaddr, max_len: u32) -> Result { + let output = self.output().unwrap(); + $writer(output, vmar, addr, max_len) + } + + fn as_sock_option_mut(&mut self) -> &mut dyn SockOption { + self + } + + fn as_sock_option(&self) -> &dyn SockOption { + self + } + } + }; +} + +pub fn new_raw_socket_option(level: SockOptionLevel, name: i32) -> Result> { + match level { + SockOptionLevel::SOL_SOCKET => new_socket_option(name), + SockOptionLevel::SOL_TCP => new_tcp_option(name), + _ => todo!(), + } +} + +/// Sock Opt level. The definition is from https://elixir.bootlin.com/linux/v6.0.9/source/include/linux/socket.h#L343 +#[repr(i32)] +#[derive(Debug, Clone, Copy, TryFromInt, PartialEq, Eq)] +#[allow(non_camel_case_types)] +pub enum SockOptionLevel { + SOL_IP = 0, + SOL_SOCKET = 1, + SOL_TCP = 6, + SOL_UDP = 17, + SOL_IPV6 = 41, + SOL_RAW = 255, +} diff --git a/services/libs/aster-std/src/util/net/options/socket.rs b/services/libs/aster-std/src/util/net/options/socket.rs new file mode 100644 index 000000000..a331b7628 --- /dev/null +++ b/services/libs/aster-std/src/util/net/options/socket.rs @@ -0,0 +1,60 @@ +use crate::net::socket::options::{ + SockOption, SocketError, SocketLinger, SocketRecvBuf, SocketReuseAddr, SocketReusePort, + SocketSendBuf, +}; +use crate::prelude::*; +use crate::vm::vmar::Vmar; +use crate::{impl_raw_sock_option, impl_raw_sock_option_get_only}; +use aster_rights::Full; + +use super::utils::{read_bool, read_linger, write_bool, write_errors, write_linger}; +use super::RawSockOption; + +/// Socket level options. +/// +/// The definition is from https://elixir.bootlin.com/linux/v6.0.9/source/include/uapi/asm-generic/socket.h. +#[repr(i32)] +#[derive(Debug, Clone, Copy, TryFromInt, PartialEq, Eq, PartialOrd, Ord)] +#[allow(non_camel_case_types)] +#[allow(clippy::upper_case_acronyms)] +enum SocketOptionName { + DEBUG = 1, + REUSEADDR = 2, + TYPE = 3, + ERROR = 4, + DONTROUTE = 5, + BROADCAST = 6, + SNDBUF = 7, + RCVBUF = 8, + SNDBUFFORCE = 32, + RCVBUFFORCE = 33, + KEEPALIVE = 9, + OOBINLINE = 10, + NO_CHECK = 11, + PRIORITY = 12, + LINGER = 13, + BSDCOMPAT = 14, + REUSEPORT = 15, + RCVTIMEO_NEW = 66, + SNDTIMEO_NEW = 67, +} + +pub fn new_socket_option(name: i32) -> Result> { + let name = SocketOptionName::try_from(name)?; + match name { + SocketOptionName::SNDBUF => Ok(Box::new(SocketSendBuf::new())), + SocketOptionName::RCVBUF => Ok(Box::new(SocketRecvBuf::new())), + SocketOptionName::REUSEADDR => Ok(Box::new(SocketReuseAddr::new())), + SocketOptionName::ERROR => Ok(Box::new(SocketError::new())), + SocketOptionName::REUSEPORT => Ok(Box::new(SocketReusePort::new())), + SocketOptionName::LINGER => Ok(Box::new(SocketLinger::new())), + _ => todo!(), + } +} + +impl_raw_sock_option!(SocketSendBuf); +impl_raw_sock_option!(SocketRecvBuf); +impl_raw_sock_option!(SocketReuseAddr, read_bool, write_bool); +impl_raw_sock_option_get_only!(SocketError, write_errors); +impl_raw_sock_option!(SocketReusePort, read_bool, write_bool); +impl_raw_sock_option!(SocketLinger, read_linger, write_linger); diff --git a/services/libs/aster-std/src/util/net/options/tcp.rs b/services/libs/aster-std/src/util/net/options/tcp.rs new file mode 100644 index 000000000..08657b457 --- /dev/null +++ b/services/libs/aster-std/src/util/net/options/tcp.rs @@ -0,0 +1,42 @@ +use crate::impl_raw_sock_option; +use crate::net::socket::ip::tcp_options::{TcpCongestion, TcpMaxseg, TcpNoDelay, TcpWindowClamp}; +use crate::prelude::*; +use crate::util::net::options::SockOption; +use crate::vm::vmar::Vmar; +use aster_rights::Full; + +use super::utils::{read_bool, read_congestion, write_bool, write_congestion}; +use super::RawSockOption; + +/// Sock options for tcp socket. +/// +/// The raw definition is from https://elixir.bootlin.com/linux/v6.0.9/source/include/uapi/linux/tcp.h#L92 +#[repr(i32)] +#[derive(Debug, Clone, Copy, TryFromInt)] +#[allow(non_camel_case_types)] +#[allow(clippy::upper_case_acronyms)] +pub enum TcpOptionName { + NODELAY = 1, /* Turn off Nagle's algorithm. */ + MAXSEG = 2, /* Limit MSS */ + CORK = 3, /* Never send partially complete segments */ + KEEPIDLE = 4, /* Start keeplives after this period */ + KEEPALIVE = 5, /* Interval between keepalives */ + WINDOW_CLAMP = 10, /* Bound advertised window */ + CONGESTION = 13, /* Congestion control algorithm */ +} + +pub fn new_tcp_option(name: i32) -> Result> { + let name = TcpOptionName::try_from(name)?; + match name { + TcpOptionName::NODELAY => Ok(Box::new(TcpNoDelay::new())), + TcpOptionName::CONGESTION => Ok(Box::new(TcpCongestion::new())), + TcpOptionName::MAXSEG => Ok(Box::new(TcpMaxseg::new())), + TcpOptionName::WINDOW_CLAMP => Ok(Box::new(TcpWindowClamp::new())), + _ => todo!(), + } +} + +impl_raw_sock_option!(TcpNoDelay, read_bool, write_bool); +impl_raw_sock_option!(TcpCongestion, read_congestion, write_congestion); +impl_raw_sock_option!(TcpMaxseg); +impl_raw_sock_option!(TcpWindowClamp); diff --git a/services/libs/aster-std/src/util/net/options/utils.rs b/services/libs/aster-std/src/util/net/options/utils.rs new file mode 100644 index 000000000..8cf5fbc9a --- /dev/null +++ b/services/libs/aster-std/src/util/net/options/utils.rs @@ -0,0 +1,123 @@ +use crate::net::socket::ip::tcp_options::Congestion; +use crate::net::socket::options::{LingerOption, SockErrors}; +use crate::prelude::*; +use crate::vm::vmar::Vmar; +use aster_frame::vm::VmIo; +use aster_rights::Full; +use core::time::Duration; + +pub fn read_bool(vmar: &Vmar, addr: Vaddr, max_len: u32) -> Result { + if (max_len as usize) < core::mem::size_of::() { + return_errno_with_message!(Errno::EINVAL, "max_len is too short"); + } + + let val = vmar.read_val::(addr)?; + + Ok(val != 0) +} + +pub fn write_bool(val: &bool, vmar: &Vmar, addr: Vaddr, max_len: u32) -> Result { + let write_len = core::mem::size_of::(); + + if (max_len as usize) < write_len { + return_errno_with_message!(Errno::EINVAL, "max_len is too short"); + } + + let val = if *val { 1i32 } else { 0i32 }; + vmar.write_val(addr, &val)?; + Ok(write_len) +} + +pub fn write_errors( + errors: &SockErrors, + vmar: &Vmar, + addr: Vaddr, + max_len: u32, +) -> Result { + let write_len = core::mem::size_of::(); + + if (max_len as usize) < write_len { + return_errno_with_message!(Errno::EINVAL, "max_len is too short"); + } + + let val = errors.as_i32(); + vmar.write_val(addr, &val)?; + Ok(write_len) +} + +pub fn read_linger(vmar: &Vmar, addr: Vaddr, max_len: u32) -> Result { + if (max_len as usize) < core::mem::size_of::() { + return_errno_with_message!(Errno::EINVAL, "max_len is too short"); + } + + let linger = vmar.read_val::(addr)?; + + Ok(LingerOption::from(linger)) +} + +pub fn write_linger( + linger_option: &LingerOption, + vmar: &Vmar, + addr: Vaddr, + max_len: u32, +) -> Result { + let write_len = core::mem::size_of::(); + + if (max_len as usize) < write_len { + return_errno_with_message!(Errno::EINVAL, "max_len is too short"); + } + + let linger = Linger::from(*linger_option); + vmar.write_val(addr, &linger)?; + Ok(write_len) +} + +pub fn read_congestion(vmar: &Vmar, addr: Vaddr, max_len: u32) -> Result { + let mut bytes = vec![0; max_len as usize]; + vmar.read_bytes(addr, &mut bytes)?; + let name = String::from_utf8(bytes).unwrap(); + Congestion::new(&name) +} + +pub fn write_congestion( + congestion: &Congestion, + vmar: &Vmar, + addr: Vaddr, + max_len: u32, +) -> Result { + let name = congestion.name().as_bytes(); + + let write_len = name.len(); + if write_len > max_len as usize { + return_errno_with_message!(Errno::EINVAL, "max_len is too short"); + } + + vmar.write_bytes(addr, name)?; + + Ok(write_len) +} + +#[repr(C)] +#[derive(Debug, Clone, Copy, Pod)] +struct Linger { + l_onoff: i32, // linger active + l_linger: i32, // how many seconds to linger for +} + +impl From for Linger { + fn from(value: LingerOption) -> Self { + let l_onoff = if value.is_on() { 1 } else { 0 }; + + let l_linger = value.timeout().as_secs() as i32; + + Self { l_onoff, l_linger } + } +} + +impl From for LingerOption { + fn from(value: Linger) -> Self { + let is_on = value.l_onoff != 0; + let timeout = Duration::new(value.l_linger as _, 0); + LingerOption::new(is_on, timeout) + } +}