Implement raw sock options

This commit is contained in:
Jianfeng Jiang 2023-10-16 11:11:00 +08:00 committed by Tate, Hongliang Tian
parent f099409b22
commit 782cd05ade
7 changed files with 449 additions and 42 deletions

View File

@ -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<SyscallReturn> {
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))
}

View File

@ -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<SyscallReturn> {
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))
}

View File

@ -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]

View File

@ -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<Full>, addr: Vaddr, max_len: u32) -> Result<()>;
fn write_output(&self, vmar: &Vmar<Full>, addr: Vaddr, max_len: u32) -> Result<usize>;
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<Full>, 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<Full>, addr: Vaddr, max_len: u32) -> Result<usize> {
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<Full>, addr: Vaddr, max_len: u32) -> Result<()> {
let input = $reader(vmar, addr, max_len)?;
self.set_input(input);
Ok(())
}
fn write_output(&self, vmar: &Vmar<Full>, addr: Vaddr, max_len: u32) -> Result<usize> {
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<Full>,
_addr: Vaddr,
_max_len: u32,
) -> Result<()> {
return_errno_with_message!(Errno::ENOPROTOOPT, "the option is getter-only");
}
fn write_output(&self, vmar: &Vmar<Full>, addr: Vaddr, max_len: u32) -> Result<usize> {
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<Full>,
_addr: Vaddr,
_max_len: u32,
) -> Result<()> {
return_errno_with_message!(Errno::ENOPROTOOPT, "the option is getter-only");
}
fn write_output(&self, vmar: &Vmar<Full>, addr: Vaddr, max_len: u32) -> Result<usize> {
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<Box<dyn RawSockOption>> {
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,
}

View File

@ -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<Box<dyn RawSockOption>> {
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);

View File

@ -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<Box<dyn RawSockOption>> {
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);

View File

@ -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<Full>, addr: Vaddr, max_len: u32) -> Result<bool> {
if (max_len as usize) < core::mem::size_of::<i32>() {
return_errno_with_message!(Errno::EINVAL, "max_len is too short");
}
let val = vmar.read_val::<i32>(addr)?;
Ok(val != 0)
}
pub fn write_bool(val: &bool, vmar: &Vmar<Full>, addr: Vaddr, max_len: u32) -> Result<usize> {
let write_len = core::mem::size_of::<i32>();
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<Full>,
addr: Vaddr,
max_len: u32,
) -> Result<usize> {
let write_len = core::mem::size_of::<i32>();
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<Full>, addr: Vaddr, max_len: u32) -> Result<LingerOption> {
if (max_len as usize) < core::mem::size_of::<Linger>() {
return_errno_with_message!(Errno::EINVAL, "max_len is too short");
}
let linger = vmar.read_val::<Linger>(addr)?;
Ok(LingerOption::from(linger))
}
pub fn write_linger(
linger_option: &LingerOption,
vmar: &Vmar<Full>,
addr: Vaddr,
max_len: u32,
) -> Result<usize> {
let write_len = core::mem::size_of::<Linger>();
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<Full>, addr: Vaddr, max_len: u32) -> Result<Congestion> {
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<Full>,
addr: Vaddr,
max_len: u32,
) -> Result<usize> {
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<LingerOption> 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<Linger> 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)
}
}