Implement sock options

This commit is contained in:
Jianfeng Jiang 2023-10-16 11:10:40 +08:00 committed by Tate, Hongliang Tian
parent 2eaf2e1290
commit f099409b22
14 changed files with 454 additions and 71 deletions

13
Cargo.lock generated
View File

@ -271,6 +271,7 @@ dependencies = [
"core2",
"cpio-decoder",
"getrandom",
"getset",
"inherit-methods-macro",
"int-to-c-enum",
"intrusive-collections",
@ -730,6 +731,18 @@ dependencies = [
"wasi",
]
[[package]]
name = "getset"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e45727250e75cc04ff2846a66397da8ef2b3db8e40e0cef4df67950a07621eb9"
dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "ghost"
version = "0.1.14"

View File

@ -67,6 +67,7 @@ getrandom = { version = "0.2.10", default-features = false, features = [
bitvec = { version = "1.0", default-features = false, features = ["alloc"] }
static_assertions = "1.1.0"
inherit-methods-macro = { git = "https://github.com/asterinas/inherit-methods-macro", rev = "98f7e3e" }
getset = "0.1.2"
[dependencies.lazy_static]
version = "1.0"

View File

@ -149,7 +149,7 @@ pub enum Errno {
}
/// error used in this crate
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Copy)]
pub struct Error {
errno: Errno,
msg: Option<&'static str>,
@ -167,7 +167,7 @@ impl Error {
}
}
pub fn error(&self) -> Errno {
pub const fn error(&self) -> Errno {
self.errno
}
}

View File

@ -41,6 +41,8 @@ extern crate lru;
extern crate controlled;
#[macro_use]
extern crate ktest;
#[macro_use]
extern crate getset;
pub mod console;
pub mod device;

View File

@ -166,8 +166,8 @@ impl Drop for AnyBoundSocket {
}
// For TCP
const RECV_BUF_LEN: usize = 65536;
const SEND_BUF_LEN: usize = 65536;
pub const RECV_BUF_LEN: usize = 65536;
pub const SEND_BUF_LEN: usize = 65536;
// For UDP
const UDP_METADATA_LEN: usize = 256;

View File

@ -10,6 +10,7 @@ mod util;
mod virtio;
pub use any_socket::{AnyBoundSocket, AnyUnboundSocket, RawTcpSocket, RawUdpSocket};
pub use any_socket::{RECV_BUF_LEN, SEND_BUF_LEN};
pub use loopback::IfaceLoopback;
pub use smoltcp::wire::{EthernetAddress, IpAddress, IpEndpoint, IpListenEndpoint, Ipv4Address};
pub use util::{spawn_background_poll_thread, BindPortConfig};

View File

@ -4,4 +4,5 @@ mod datagram;
mod stream;
pub use datagram::DatagramSocket;
pub use stream::options as tcp_options;
pub use stream::StreamSocket;

View File

@ -1,27 +1,36 @@
use crate::events::IoEvents;
use crate::fs::{file_handle::FileLike, utils::StatusFlags};
use crate::net::iface::IpEndpoint;
use crate::net::socket::{
util::{
send_recv_flags::SendRecvFlags, shutdown_cmd::SockShutdownCmd,
sock_options::SockOptionName, sockaddr::SocketAddr,
},
Socket,
use crate::fs::file_handle::FileLike;
use crate::fs::utils::StatusFlags;
use crate::net::socket::ip::tcp_options::{
TcpCongestion, TcpMaxseg, TcpWindowClamp, DEFAULT_MAXSEG,
};
use crate::net::socket::options::{
SockOption, SocketError, SocketLinger, SocketOptions, SocketRecvBuf, SocketReuseAddr,
SocketReusePort, SocketSendBuf, MIN_RECVBUF, MIN_SENDBUF,
};
use crate::net::socket::util::{
send_recv_flags::SendRecvFlags, shutdown_cmd::SockShutdownCmd, sockaddr::SocketAddr,
};
use crate::net::socket::Socket;
use crate::prelude::*;
use crate::process::signal::Poller;
use crate::{match_sock_option_mut, match_sock_option_ref};
use self::{
connected::ConnectedStream, connecting::ConnectingStream, init::InitStream,
listen::ListenStream,
};
use connected::ConnectedStream;
use connecting::ConnectingStream;
use init::InitStream;
use listen::ListenStream;
use options::{TcpNoDelay, TcpOptions};
use smoltcp::wire::IpEndpoint;
mod connected;
mod connecting;
mod init;
mod listen;
pub mod options;
pub struct StreamSocket {
options: RwLock<Options>,
state: RwLock<State>,
}
@ -36,10 +45,26 @@ enum State {
Listen(Arc<ListenStream>),
}
#[derive(Debug, Clone)]
struct Options {
socket: SocketOptions,
tcp: TcpOptions,
}
impl Options {
fn new() -> Self {
let socket = SocketOptions::new_tcp();
let tcp = TcpOptions::new();
Options { socket, tcp }
}
}
impl StreamSocket {
pub fn new(nonblocking: bool) -> Self {
let options = Options::new();
let state = State::Init(InitStream::new(nonblocking));
Self {
options: RwLock::new(options),
state: RwLock::new(state),
}
}
@ -180,7 +205,10 @@ impl Socket for StreamSocket {
let accepted_socket = {
let state = RwLock::new(State::Connected(connected_stream));
Arc::new(StreamSocket { state })
Arc::new(StreamSocket {
options: RwLock::new(Options::new()),
state,
})
};
let socket_addr = remote_endpoint.try_into()?;
@ -222,10 +250,6 @@ impl Socket for StreamSocket {
remote_endpoint.try_into()
}
fn sock_option(&self, optname: &SockOptionName) -> Result<&[u8]> {
return_errno_with_message!(Errno::EINVAL, "getsockopt not implemented");
}
fn recvfrom(&self, buf: &mut [u8], flags: SendRecvFlags) -> Result<(usize, SocketAddr)> {
let connected_stream = match &*self.state.read() {
State::Connected(connected_stream) => connected_stream.clone(),
@ -254,4 +278,125 @@ impl Socket for StreamSocket {
};
connected_stream.sendto(buf, flags)
}
fn option(&self, option: &mut dyn SockOption) -> Result<()> {
let options = self.options.read();
match_sock_option_mut!(option, {
// Socket Options
socket_errors: SocketError => {
let sock_errors = options.socket.sock_errors();
socket_errors.set_output(sock_errors);
},
socket_reuse_addr: SocketReuseAddr => {
let reuse_addr = options.socket.reuse_addr();
socket_reuse_addr.set_output(reuse_addr);
},
socket_send_buf: SocketSendBuf => {
let send_buf = options.socket.send_buf();
socket_send_buf.set_output(send_buf);
},
socket_recv_buf: SocketRecvBuf => {
let recv_buf = options.socket.recv_buf();
socket_recv_buf.set_output(recv_buf);
},
socket_reuse_port: SocketReusePort => {
let reuse_port = options.socket.reuse_port();
socket_reuse_port.set_output(reuse_port);
},
// Tcp Options
tcp_no_delay: TcpNoDelay => {
let no_delay = options.tcp.no_delay();
tcp_no_delay.set_output(no_delay);
},
tcp_congestion: TcpCongestion => {
let congestion = options.tcp.congestion();
tcp_congestion.set_output(congestion);
},
tcp_maxseg: TcpMaxseg => {
// It will always return the default MSS value defined above for an unconnected socket
// and always return the actual current MSS for a connected one.
// FIXME: how to get the current MSS?
let maxseg = match &*self.state.read() {
State::Init(_) | State::Listen(_) | State::Connecting(_) => DEFAULT_MAXSEG,
State::Connected(_) => options.tcp.maxseg(),
};
tcp_maxseg.set_output(maxseg);
},
tcp_window_clamp: TcpWindowClamp => {
let window_clamp = options.tcp.window_clamp();
tcp_window_clamp.set_output(window_clamp);
},
_ => return_errno_with_message!(Errno::ENOPROTOOPT, "get unknown option")
});
Ok(())
}
fn set_option(&self, option: &dyn SockOption) -> Result<()> {
let mut options = self.options.write();
// FIXME: here we have only set the value of the option, without actually
// making any real modifications.
match_sock_option_ref!(option, {
// Socket options
socket_recv_buf: SocketRecvBuf => {
let recv_buf = socket_recv_buf.input().unwrap();
if *recv_buf <= MIN_RECVBUF {
options.socket.set_recv_buf(MIN_RECVBUF);
} else{
options.socket.set_recv_buf(*recv_buf);
}
},
socket_send_buf: SocketSendBuf => {
let send_buf = socket_send_buf.input().unwrap();
if *send_buf <= MIN_SENDBUF {
options.socket.set_send_buf(MIN_SENDBUF);
} else {
options.socket.set_send_buf(*send_buf);
}
},
socket_reuse_addr: SocketReuseAddr => {
let reuse_addr = socket_reuse_addr.input().unwrap();
options.socket.set_reuse_addr(*reuse_addr);
},
socket_reuse_port: SocketReusePort => {
let reuse_port = socket_reuse_port.input().unwrap();
options.socket.set_reuse_port(*reuse_port);
},
socket_linger: SocketLinger => {
let linger = socket_linger.input().unwrap();
options.socket.set_linger(*linger);
},
// Tcp options
tcp_no_delay: TcpNoDelay => {
let no_delay = tcp_no_delay.input().unwrap();
options.tcp.set_no_delay(*no_delay);
},
tcp_congestion: TcpCongestion => {
let congestion = tcp_congestion.input().unwrap();
options.tcp.set_congestion(*congestion);
},
tcp_maxseg: TcpMaxseg => {
const MIN_MAXSEG: u32 = 536;
const MAX_MAXSEG: u32 = 65535;
let maxseg = tcp_maxseg.input().unwrap();
if *maxseg < MIN_MAXSEG || *maxseg > MAX_MAXSEG {
return_errno_with_message!(Errno::EINVAL, "New maxseg should be in allowed range.");
}
options.tcp.set_maxseg(*maxseg);
},
tcp_window_clamp: TcpWindowClamp => {
let window_clamp = tcp_window_clamp.input().unwrap();
let half_recv_buf = (options.socket.recv_buf()) / 2;
if *window_clamp <= half_recv_buf {
options.tcp.set_window_clamp(half_recv_buf);
} else {
options.tcp.set_window_clamp(*window_clamp);
}
},
_ => return_errno_with_message!(Errno::ENOPROTOOPT, "set unknown option")
});
Ok(())
}
}

View File

@ -0,0 +1,67 @@
use crate::impl_sock_options;
use crate::prelude::*;
#[derive(Debug, Clone, Copy, CopyGetters, Setters)]
#[get_copy = "pub"]
#[set = "pub"]
pub struct TcpOptions {
no_delay: bool,
congestion: Congestion,
maxseg: u32,
window_clamp: u32,
}
pub const DEFAULT_MAXSEG: u32 = 536;
pub const DEFAULT_WINDOW_CLAMP: u32 = 0x8000_0000;
impl TcpOptions {
pub fn new() -> Self {
Self {
no_delay: false,
congestion: Congestion::Reno,
maxseg: DEFAULT_MAXSEG,
window_clamp: DEFAULT_WINDOW_CLAMP,
}
}
}
impl Default for TcpOptions {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy)]
pub enum Congestion {
Reno,
Cubic,
}
impl Congestion {
const RENO: &'static str = "reno";
const CUBIC: &'static str = "cubic";
pub fn new(name: &str) -> Result<Self> {
let congestion = match name {
Self::RENO => Self::Reno,
Self::CUBIC => Self::Cubic,
_ => return_errno_with_message!(Errno::EINVAL, "unsupported congestion name"),
};
Ok(congestion)
}
pub fn name(&self) -> &'static str {
match self {
Self::Reno => Self::RENO,
Self::Cubic => Self::CUBIC,
}
}
}
impl_sock_options!(
pub struct TcpNoDelay<input = bool, output = bool> {}
pub struct TcpCongestion<input = Congestion, output = Congestion> {}
pub struct TcpMaxseg<input = u32, output = u32> {}
pub struct TcpWindowClamp<input = u32, output = u32> {}
);

View File

@ -1,11 +1,12 @@
use crate::{fs::file_handle::FileLike, prelude::*};
use self::options::SockOption;
pub use self::util::send_recv_flags::SendRecvFlags;
pub use self::util::shutdown_cmd::SockShutdownCmd;
pub use self::util::sock_options::{SockOptionLevel, SockOptionName};
pub use self::util::sockaddr::SocketAddr;
pub mod ip;
pub mod options;
pub mod unix;
mod util;
@ -47,17 +48,12 @@ pub trait Socket: FileLike + Send + Sync {
}
/// Get options on the socket
fn sock_option(&self, optname: &SockOptionName) -> Result<&[u8]> {
fn option(&self, option: &mut dyn SockOption) -> Result<()> {
return_errno_with_message!(Errno::EINVAL, "getsockopt not implemented");
}
/// Set options on the socket
fn set_sock_option(
&self,
opt_level: SockOptionLevel,
optname: SockOptionName,
option_val: &[u8],
) -> Result<()> {
fn set_option(&self, option: &dyn SockOption) -> Result<()> {
return_errno_with_message!(Errno::EINVAL, "setsockopt not implemented");
}

View File

@ -0,0 +1,113 @@
use crate::prelude::*;
mod socket;
pub use socket::{
LingerOption, SockErrors, SocketError, SocketLinger, SocketOptions, SocketRecvBuf,
SocketReuseAddr, SocketReusePort, SocketSendBuf, MIN_RECVBUF, MIN_SENDBUF,
};
pub trait SockOption: Any + Send + Sync + Debug {
fn as_any(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any;
}
// The following macros are mainly from occlum/ngo.
#[macro_export]
macro_rules! impl_sock_options {
($(
$(#[$outer:meta])*
pub struct $name: ident <input=$input:ty, output=$output:ty> {}
)*) => {
$(
$(#[$outer])*
#[derive(Debug)]
pub struct $name {
input: Option<$input>,
output: Option<$output>,
}
impl $name {
pub fn new() -> Self {
Self {
input: None,
output: None,
}
}
pub fn input(&self) -> Option<&$input> {
self.input.as_ref()
}
pub fn set_input(&mut self, input: $input) {
self.input = Some(input);
}
pub fn output(&self) -> Option<&$output> {
self.output.as_ref()
}
pub fn set_output(&mut self, output: $output) {
self.output = Some(output);
}
}
impl $crate::net::socket::SockOption for $name {
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
impl Default for $name {
fn default() -> Self {
Self::new()
}
}
)*
};
}
#[macro_export]
macro_rules! match_sock_option_ref {
(
$option:expr, {
$( $bind: ident : $ty:ty => $arm:expr ),*,
_ => $default:expr
}
) => {{
let __option : &dyn SockOption = $option;
$(
if let Some($bind) = __option.as_any().downcast_ref::<$ty>() {
$arm
} else
)*
{
$default
}
}};
}
#[macro_export]
macro_rules! match_sock_option_mut {
(
$option:expr, {
$( $bind: ident : $ty:ty => $arm:expr ),*,
_ => $default:expr
}
) => {{
let __option : &mut dyn SockOption = $option;
$(
if let Some($bind) = __option.as_any_mut().downcast_mut::<$ty>() {
$arm
} else
)*
{
$default
}
}};
}

View File

@ -0,0 +1,86 @@
use core::time::Duration;
use crate::impl_sock_options;
use crate::net::iface::{RECV_BUF_LEN, SEND_BUF_LEN};
use crate::prelude::*;
#[derive(Debug, Clone, CopyGetters, Setters)]
#[get_copy = "pub"]
#[set = "pub"]
pub struct SocketOptions {
sock_errors: SockErrors,
reuse_addr: bool,
reuse_port: bool,
send_buf: u32,
recv_buf: u32,
linger: LingerOption,
}
impl SocketOptions {
pub fn new_tcp() -> Self {
Self {
sock_errors: SockErrors::no_error(),
reuse_addr: false,
reuse_port: false,
send_buf: SEND_BUF_LEN as u32,
recv_buf: RECV_BUF_LEN as u32,
linger: LingerOption::default(),
}
}
}
pub const MIN_SENDBUF: u32 = 2304;
pub const MIN_RECVBUF: u32 = 2304;
impl_sock_options!(
pub struct SocketReuseAddr<input = bool, output = bool> {}
pub struct SocketReusePort<input = bool, output = bool> {}
pub struct SocketSendBuf<input = u32, output = u32> {}
pub struct SocketRecvBuf<input = u32, output = u32> {}
pub struct SocketError<input = (), output = SockErrors> {}
pub struct SocketLinger<input = LingerOption, output = LingerOption> {}
);
#[derive(Debug, Clone, Copy)]
pub struct SockErrors(Option<Error>);
impl SockErrors {
pub const fn no_error() -> Self {
Self(None)
}
pub const fn with_error(error: Error) -> Self {
Self(Some(error))
}
pub const fn error(&self) -> Option<&Error> {
self.0.as_ref()
}
pub const fn as_i32(&self) -> i32 {
match &self.0 {
None => 0,
Some(err) => err.error() as i32,
}
}
}
#[derive(Debug, Default, Clone, Copy)]
pub struct LingerOption {
is_on: bool,
timeout: Duration,
}
impl LingerOption {
pub fn new(is_on: bool, timeout: Duration) -> Self {
Self { is_on, timeout }
}
pub fn is_on(&self) -> bool {
self.is_on
}
pub fn timeout(&self) -> Duration {
self.timeout
}
}

View File

@ -1,4 +1,3 @@
pub mod send_recv_flags;
pub mod shutdown_cmd;
pub mod sock_options;
pub mod sockaddr;

View File

@ -1,41 +0,0 @@
use crate::prelude::*;
/// The definition is from https://elixir.bootlin.com/linux/v6.0.9/source/include/uapi/asm-generic/socket.h.
/// We do not include all options here
#[repr(i32)]
#[derive(Debug, Clone, Copy, TryFromInt, PartialEq, Eq, PartialOrd, Ord)]
#[allow(non_camel_case_types)]
pub enum SockOptionName {
SO_DEBUG = 1,
SO_REUSEADDR = 2,
SO_TYPE = 3,
SO_ERROR = 4,
SO_DONTROUTE = 5,
SO_BROADCAST = 6,
SO_SNDBUF = 7,
SO_RCVBUF = 8,
SO_SNDBUFFORCE = 32,
SO_RCVBUFFORCE = 33,
SO_KEEPALIVE = 9,
SO_OOBINLINE = 10,
SO_NO_CHECK = 11,
SO_PRIORITY = 12,
SO_LINGER = 13,
SO_BSDCOMPAT = 14,
SO_REUSEPORT = 15,
SO_RCVTIMEO_NEW = 66,
SO_SNDTIMEO_NEW = 67,
}
/// 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,
}