mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-09 05:16:47 +00:00
Add keepidle tcp option
This commit is contained in:
parent
547c92d2ff
commit
72cb160539
@ -10,7 +10,7 @@ use connected::ConnectedStream;
|
|||||||
use connecting::{ConnResult, ConnectingStream};
|
use connecting::{ConnResult, ConnectingStream};
|
||||||
use init::InitStream;
|
use init::InitStream;
|
||||||
use listen::ListenStream;
|
use listen::ListenStream;
|
||||||
use options::{Congestion, MaxSegment, NoDelay, WindowClamp, KEEPALIVE_INTERVAL};
|
use options::{Congestion, KeepIdle, MaxSegment, NoDelay, WindowClamp, KEEPALIVE_INTERVAL};
|
||||||
use ostd::sync::{PreemptDisabled, RwLockReadGuard, RwLockWriteGuard};
|
use ostd::sync::{PreemptDisabled, RwLockReadGuard, RwLockWriteGuard};
|
||||||
use takeable::Takeable;
|
use takeable::Takeable;
|
||||||
use util::TcpOptionSet;
|
use util::TcpOptionSet;
|
||||||
@ -670,18 +670,22 @@ impl Socket for StreamSocket {
|
|||||||
let no_delay = options.tcp.no_delay();
|
let no_delay = options.tcp.no_delay();
|
||||||
tcp_no_delay.set(no_delay);
|
tcp_no_delay.set(no_delay);
|
||||||
},
|
},
|
||||||
tcp_congestion: Congestion => {
|
|
||||||
let congestion = options.tcp.congestion();
|
|
||||||
tcp_congestion.set(congestion);
|
|
||||||
},
|
|
||||||
tcp_maxseg: MaxSegment => {
|
tcp_maxseg: MaxSegment => {
|
||||||
let maxseg = options.tcp.maxseg();
|
let maxseg = options.tcp.maxseg();
|
||||||
tcp_maxseg.set(maxseg);
|
tcp_maxseg.set(maxseg);
|
||||||
},
|
},
|
||||||
|
tcp_keep_idle: KeepIdle => {
|
||||||
|
let keep_idle = options.tcp.keep_idle();
|
||||||
|
tcp_keep_idle.set(keep_idle);
|
||||||
|
},
|
||||||
tcp_window_clamp: WindowClamp => {
|
tcp_window_clamp: WindowClamp => {
|
||||||
let window_clamp = options.tcp.window_clamp();
|
let window_clamp = options.tcp.window_clamp();
|
||||||
tcp_window_clamp.set(window_clamp);
|
tcp_window_clamp.set(window_clamp);
|
||||||
},
|
},
|
||||||
|
tcp_congestion: Congestion => {
|
||||||
|
let congestion = options.tcp.congestion();
|
||||||
|
tcp_congestion.set(congestion);
|
||||||
|
},
|
||||||
_ => return_errno_with_message!(Errno::ENOPROTOOPT, "the socket option to get is unknown")
|
_ => return_errno_with_message!(Errno::ENOPROTOOPT, "the socket option to get is unknown")
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -691,10 +695,14 @@ impl Socket for StreamSocket {
|
|||||||
fn set_option(&self, option: &dyn SocketOption) -> Result<()> {
|
fn set_option(&self, option: &dyn SocketOption) -> Result<()> {
|
||||||
let (mut options, mut state) = self.update_connecting();
|
let (mut options, mut state) = self.update_connecting();
|
||||||
|
|
||||||
match options.socket.set_option(option, state.as_mut()) {
|
let need_iface_poll = match options.socket.set_option(option, state.as_mut()) {
|
||||||
Err(err) if err.error() == Errno::ENOPROTOOPT => (),
|
Err(err) if err.error() == Errno::ENOPROTOOPT => {
|
||||||
|
do_tcp_setsockopt(option, &mut options, state.as_mut())?
|
||||||
|
}
|
||||||
Err(err) => return Err(err),
|
Err(err) => return Err(err),
|
||||||
Ok(need_iface_poll) => {
|
Ok(need_iface_poll) => need_iface_poll,
|
||||||
|
};
|
||||||
|
|
||||||
let iface_to_poll = need_iface_poll.then(|| state.iface().cloned()).flatten();
|
let iface_to_poll = need_iface_poll.then(|| state.iface().cloned()).flatten();
|
||||||
|
|
||||||
drop(state);
|
drop(state);
|
||||||
@ -704,22 +712,21 @@ impl Socket for StreamSocket {
|
|||||||
iface.poll();
|
iface.poll();
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(());
|
Ok(())
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: Here we have only set the value of the option, without actually
|
fn do_tcp_setsockopt(
|
||||||
// making any real modifications.
|
option: &dyn SocketOption,
|
||||||
|
options: &mut OptionSet,
|
||||||
|
state: &mut State,
|
||||||
|
) -> Result<NeedIfacePoll> {
|
||||||
match_sock_option_ref!(option, {
|
match_sock_option_ref!(option, {
|
||||||
tcp_no_delay: NoDelay => {
|
tcp_no_delay: NoDelay => {
|
||||||
let no_delay = tcp_no_delay.get().unwrap();
|
let no_delay = tcp_no_delay.get().unwrap();
|
||||||
options.tcp.set_no_delay(*no_delay);
|
options.tcp.set_no_delay(*no_delay);
|
||||||
state.set_raw_option(|raw_socket: &dyn RawTcpSetOption| raw_socket.set_nagle_enabled(!no_delay));
|
state.set_raw_option(|raw_socket: &dyn RawTcpSetOption| raw_socket.set_nagle_enabled(!no_delay));
|
||||||
},
|
},
|
||||||
tcp_congestion: Congestion => {
|
|
||||||
let congestion = tcp_congestion.get().unwrap();
|
|
||||||
options.tcp.set_congestion(*congestion);
|
|
||||||
},
|
|
||||||
tcp_maxseg: MaxSegment => {
|
tcp_maxseg: MaxSegment => {
|
||||||
const MIN_MAXSEG: u32 = 536;
|
const MIN_MAXSEG: u32 = 536;
|
||||||
const MAX_MAXSEG: u32 = 65535;
|
const MAX_MAXSEG: u32 = 65535;
|
||||||
@ -730,6 +737,18 @@ impl Socket for StreamSocket {
|
|||||||
}
|
}
|
||||||
options.tcp.set_maxseg(*maxseg);
|
options.tcp.set_maxseg(*maxseg);
|
||||||
},
|
},
|
||||||
|
tcp_keep_idle: KeepIdle => {
|
||||||
|
const MIN_KEEP_IDLE: u32 = 1;
|
||||||
|
const MAX_KEEP_IDLE: u32 = 32767;
|
||||||
|
|
||||||
|
let keepidle = tcp_keep_idle.get().unwrap();
|
||||||
|
if *keepidle < MIN_KEEP_IDLE || *keepidle > MAX_KEEP_IDLE {
|
||||||
|
return_errno_with_message!(Errno::EINVAL, "the keep idle time is out of bounds");
|
||||||
|
}
|
||||||
|
options.tcp.set_keep_idle(*keepidle);
|
||||||
|
|
||||||
|
// TODO: Track when the socket becomes idle to actually support keep idle.
|
||||||
|
},
|
||||||
tcp_window_clamp: WindowClamp => {
|
tcp_window_clamp: WindowClamp => {
|
||||||
let window_clamp = tcp_window_clamp.get().unwrap();
|
let window_clamp = tcp_window_clamp.get().unwrap();
|
||||||
let half_recv_buf = options.socket.recv_buf() / 2;
|
let half_recv_buf = options.socket.recv_buf() / 2;
|
||||||
@ -739,11 +758,14 @@ impl Socket for StreamSocket {
|
|||||||
options.tcp.set_window_clamp(*window_clamp);
|
options.tcp.set_window_clamp(*window_clamp);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
tcp_congestion: Congestion => {
|
||||||
|
let congestion = tcp_congestion.get().unwrap();
|
||||||
|
options.tcp.set_congestion(*congestion);
|
||||||
|
},
|
||||||
_ => return_errno_with_message!(Errno::ENOPROTOOPT, "the socket option to be set is unknown")
|
_ => return_errno_with_message!(Errno::ENOPROTOOPT, "the socket option to be set is unknown")
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(())
|
Ok(NeedIfacePoll::FALSE)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
|
@ -5,9 +5,10 @@ use crate::impl_socket_options;
|
|||||||
|
|
||||||
impl_socket_options!(
|
impl_socket_options!(
|
||||||
pub struct NoDelay(bool);
|
pub struct NoDelay(bool);
|
||||||
pub struct Congestion(CongestionControl);
|
|
||||||
pub struct MaxSegment(u32);
|
pub struct MaxSegment(u32);
|
||||||
|
pub struct KeepIdle(u32);
|
||||||
pub struct WindowClamp(u32);
|
pub struct WindowClamp(u32);
|
||||||
|
pub struct Congestion(CongestionControl);
|
||||||
);
|
);
|
||||||
|
|
||||||
/// The keepalive interval.
|
/// The keepalive interval.
|
||||||
|
@ -7,21 +7,24 @@ use crate::prelude::*;
|
|||||||
#[set = "pub"]
|
#[set = "pub"]
|
||||||
pub struct TcpOptionSet {
|
pub struct TcpOptionSet {
|
||||||
no_delay: bool,
|
no_delay: bool,
|
||||||
congestion: CongestionControl,
|
|
||||||
maxseg: u32,
|
maxseg: u32,
|
||||||
|
keep_idle: u32,
|
||||||
window_clamp: u32,
|
window_clamp: u32,
|
||||||
|
congestion: CongestionControl,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const DEFAULT_MAXSEG: u32 = 536;
|
pub const DEFAULT_MAXSEG: u32 = 536;
|
||||||
|
pub const DEFAULT_KEEP_IDLE: u32 = 7200;
|
||||||
pub const DEFAULT_WINDOW_CLAMP: u32 = 0x8000_0000;
|
pub const DEFAULT_WINDOW_CLAMP: u32 = 0x8000_0000;
|
||||||
|
|
||||||
impl TcpOptionSet {
|
impl TcpOptionSet {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
no_delay: false,
|
no_delay: false,
|
||||||
congestion: CongestionControl::Reno,
|
|
||||||
maxseg: DEFAULT_MAXSEG,
|
maxseg: DEFAULT_MAXSEG,
|
||||||
|
keep_idle: DEFAULT_KEEP_IDLE,
|
||||||
window_clamp: DEFAULT_WINDOW_CLAMP,
|
window_clamp: DEFAULT_WINDOW_CLAMP,
|
||||||
|
congestion: CongestionControl::Reno,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ pub fn sys_getsockopt(
|
|||||||
optlen_addr: Vaddr,
|
optlen_addr: Vaddr,
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
) -> Result<SyscallReturn> {
|
) -> Result<SyscallReturn> {
|
||||||
let level = CSocketOptionLevel::try_from(level)?;
|
let level = CSocketOptionLevel::try_from(level).map_err(|_| Errno::EOPNOTSUPP)?;
|
||||||
if optval == 0 || optlen_addr == 0 {
|
if optval == 0 || optlen_addr == 0 {
|
||||||
return_errno_with_message!(Errno::EINVAL, "optval or optlen_addr is null pointer");
|
return_errno_with_message!(Errno::EINVAL, "optval or optlen_addr is null pointer");
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ pub fn sys_setsockopt(
|
|||||||
optlen: u32,
|
optlen: u32,
|
||||||
_ctx: &Context,
|
_ctx: &Context,
|
||||||
) -> Result<SyscallReturn> {
|
) -> Result<SyscallReturn> {
|
||||||
let level = CSocketOptionLevel::try_from(level)?;
|
let level = CSocketOptionLevel::try_from(level).map_err(|_| Errno::EOPNOTSUPP)?;
|
||||||
if optval == 0 {
|
if optval == 0 {
|
||||||
return_errno_with_message!(Errno::EINVAL, "optval is null pointer");
|
return_errno_with_message!(Errno::EINVAL, "optval is null pointer");
|
||||||
}
|
}
|
||||||
|
@ -134,7 +134,7 @@ pub fn new_raw_socket_option(
|
|||||||
match level {
|
match level {
|
||||||
CSocketOptionLevel::SOL_SOCKET => new_socket_option(name),
|
CSocketOptionLevel::SOL_SOCKET => new_socket_option(name),
|
||||||
CSocketOptionLevel::SOL_TCP => new_tcp_option(name),
|
CSocketOptionLevel::SOL_TCP => new_tcp_option(name),
|
||||||
_ => todo!(),
|
_ => return_errno_with_message!(Errno::EOPNOTSUPP, "unsupported option level"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ enum CSocketOptionName {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_socket_option(name: i32) -> Result<Box<dyn RawSocketOption>> {
|
pub fn new_socket_option(name: i32) -> Result<Box<dyn RawSocketOption>> {
|
||||||
let name = CSocketOptionName::try_from(name)?;
|
let name = CSocketOptionName::try_from(name).map_err(|_| Errno::ENOPROTOOPT)?;
|
||||||
match name {
|
match name {
|
||||||
CSocketOptionName::SNDBUF => Ok(Box::new(SendBuf::new())),
|
CSocketOptionName::SNDBUF => Ok(Box::new(SendBuf::new())),
|
||||||
CSocketOptionName::RCVBUF => Ok(Box::new(RecvBuf::new())),
|
CSocketOptionName::RCVBUF => Ok(Box::new(RecvBuf::new())),
|
||||||
@ -48,7 +48,7 @@ pub fn new_socket_option(name: i32) -> Result<Box<dyn RawSocketOption>> {
|
|||||||
CSocketOptionName::REUSEPORT => Ok(Box::new(ReusePort::new())),
|
CSocketOptionName::REUSEPORT => Ok(Box::new(ReusePort::new())),
|
||||||
CSocketOptionName::LINGER => Ok(Box::new(Linger::new())),
|
CSocketOptionName::LINGER => Ok(Box::new(Linger::new())),
|
||||||
CSocketOptionName::KEEPALIVE => Ok(Box::new(KeepAlive::new())),
|
CSocketOptionName::KEEPALIVE => Ok(Box::new(KeepAlive::new())),
|
||||||
_ => todo!(),
|
_ => return_errno_with_message!(Errno::ENOPROTOOPT, "unsupported socket-level option"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
use super::RawSocketOption;
|
use super::RawSocketOption;
|
||||||
use crate::{
|
use crate::{
|
||||||
impl_raw_socket_option,
|
impl_raw_socket_option,
|
||||||
net::socket::ip::stream::options::{Congestion, MaxSegment, NoDelay, WindowClamp},
|
net::socket::ip::stream::options::{Congestion, KeepIdle, MaxSegment, NoDelay, WindowClamp},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
util::net::options::SocketOption,
|
util::net::options::SocketOption,
|
||||||
};
|
};
|
||||||
@ -26,17 +26,19 @@ pub enum CTcpOptionName {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_tcp_option(name: i32) -> Result<Box<dyn RawSocketOption>> {
|
pub fn new_tcp_option(name: i32) -> Result<Box<dyn RawSocketOption>> {
|
||||||
let name = CTcpOptionName::try_from(name)?;
|
let name = CTcpOptionName::try_from(name).map_err(|_| Errno::ENOPROTOOPT)?;
|
||||||
match name {
|
match name {
|
||||||
CTcpOptionName::NODELAY => Ok(Box::new(NoDelay::new())),
|
CTcpOptionName::NODELAY => Ok(Box::new(NoDelay::new())),
|
||||||
CTcpOptionName::CONGESTION => Ok(Box::new(Congestion::new())),
|
|
||||||
CTcpOptionName::MAXSEG => Ok(Box::new(MaxSegment::new())),
|
CTcpOptionName::MAXSEG => Ok(Box::new(MaxSegment::new())),
|
||||||
|
CTcpOptionName::KEEPIDLE => Ok(Box::new(KeepIdle::new())),
|
||||||
CTcpOptionName::WINDOW_CLAMP => Ok(Box::new(WindowClamp::new())),
|
CTcpOptionName::WINDOW_CLAMP => Ok(Box::new(WindowClamp::new())),
|
||||||
_ => todo!(),
|
CTcpOptionName::CONGESTION => Ok(Box::new(Congestion::new())),
|
||||||
|
_ => return_errno_with_message!(Errno::ENOPROTOOPT, "unsupported tcp-level option"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_raw_socket_option!(NoDelay);
|
impl_raw_socket_option!(NoDelay);
|
||||||
impl_raw_socket_option!(Congestion);
|
|
||||||
impl_raw_socket_option!(MaxSegment);
|
impl_raw_socket_option!(MaxSegment);
|
||||||
|
impl_raw_socket_option!(KeepIdle);
|
||||||
impl_raw_socket_option!(WindowClamp);
|
impl_raw_socket_option!(WindowClamp);
|
||||||
|
impl_raw_socket_option!(Congestion);
|
||||||
|
@ -38,6 +38,26 @@ FN_SETUP(general)
|
|||||||
}
|
}
|
||||||
END_SETUP()
|
END_SETUP()
|
||||||
|
|
||||||
|
FN_TEST(invalid_socket_option)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
socklen_t res_len = sizeof(res);
|
||||||
|
|
||||||
|
#define INVALID_LEVEL 99999
|
||||||
|
TEST_ERRNO(getsockopt(sk_connected, INVALID_LEVEL, SO_SNDBUF, &res,
|
||||||
|
&res_len),
|
||||||
|
EOPNOTSUPP);
|
||||||
|
#define INVALID_SOCKET_OPTION 99999
|
||||||
|
TEST_ERRNO(getsockopt(sk_connected, SOL_SOCKET, INVALID_SOCKET_OPTION,
|
||||||
|
&res, &res_len),
|
||||||
|
ENOPROTOOPT);
|
||||||
|
#define INVALID_TCP_OPTION 99999
|
||||||
|
TEST_ERRNO(getsockopt(sk_connected, IPPROTO_TCP, INVALID_TCP_OPTION,
|
||||||
|
&res, &res_len),
|
||||||
|
ENOPROTOOPT);
|
||||||
|
}
|
||||||
|
END_TEST()
|
||||||
|
|
||||||
int refresh_connection()
|
int refresh_connection()
|
||||||
{
|
{
|
||||||
close(sk_connected);
|
close(sk_connected);
|
||||||
@ -230,3 +250,27 @@ FN_TEST(keepalive)
|
|||||||
keepalive == 0);
|
keepalive == 0);
|
||||||
}
|
}
|
||||||
END_TEST()
|
END_TEST()
|
||||||
|
|
||||||
|
FN_TEST(keepidle)
|
||||||
|
{
|
||||||
|
int keepidle;
|
||||||
|
socklen_t keepidle_len = sizeof(keepidle);
|
||||||
|
|
||||||
|
// 1. Check default values
|
||||||
|
refresh_connection();
|
||||||
|
TEST_RES(getsockopt(sk_connected, IPPROTO_TCP, TCP_KEEPIDLE, &keepidle,
|
||||||
|
&keepidle_len),
|
||||||
|
keepidle == 7200);
|
||||||
|
TEST_RES(getsockopt(sk_accepted, IPPROTO_TCP, TCP_KEEPIDLE, &keepidle,
|
||||||
|
&keepidle_len),
|
||||||
|
keepidle == 7200);
|
||||||
|
|
||||||
|
// 2. Set and Get value
|
||||||
|
int seconds = 200;
|
||||||
|
CHECK(setsockopt(sk_connected, IPPROTO_TCP, TCP_KEEPIDLE, &seconds,
|
||||||
|
sizeof(seconds)));
|
||||||
|
TEST_RES(getsockopt(sk_connected, IPPROTO_TCP, TCP_KEEPIDLE, &keepidle,
|
||||||
|
&keepidle_len),
|
||||||
|
keepidle == 200);
|
||||||
|
}
|
||||||
|
END_TEST()
|
Loading…
x
Reference in New Issue
Block a user