mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-10 05:46:48 +00:00
Enable gVisor tcp_socket_test
This commit is contained in:
parent
3e31df74e3
commit
3f951e0c0c
@ -16,6 +16,7 @@ Fo = "Fo"
|
|||||||
Inh = "Inh"
|
Inh = "Inh"
|
||||||
DELET = "DELET"
|
DELET = "DELET"
|
||||||
wrk = "wrk"
|
wrk = "wrk"
|
||||||
|
rto = "rto"
|
||||||
|
|
||||||
# Files with svg suffix are ignored to check.
|
# Files with svg suffix are ignored to check.
|
||||||
[type.svg]
|
[type.svg]
|
||||||
|
@ -453,13 +453,22 @@ impl<E: Ext> TcpConnection<E> {
|
|||||||
|
|
||||||
/// Closes the connection.
|
/// Closes the connection.
|
||||||
///
|
///
|
||||||
|
/// This method returns `false` if the socket is closed _before_ calling this method.
|
||||||
|
///
|
||||||
/// Polling the iface is _always_ required after this method succeeds.
|
/// Polling the iface is _always_ required after this method succeeds.
|
||||||
pub fn close(&self) {
|
pub fn close(&self) -> bool {
|
||||||
let mut socket = self.0.inner.lock();
|
let mut socket = self.0.inner.lock();
|
||||||
|
|
||||||
socket.listener = None;
|
socket.listener = None;
|
||||||
|
|
||||||
|
if socket.is_closed() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
socket.close();
|
socket.close();
|
||||||
self.0.update_next_poll_at_ms(PollAt::Now);
|
self.0.update_next_poll_at_ms(PollAt::Now);
|
||||||
|
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calls `f` with an immutable reference to the associated [`RawTcpSocket`].
|
/// Calls `f` with an immutable reference to the associated [`RawTcpSocket`].
|
||||||
|
@ -70,7 +70,9 @@ impl ConnectedStream {
|
|||||||
|
|
||||||
if cmd.shut_write() {
|
if cmd.shut_write() {
|
||||||
self.is_sending_closed.store(true, Ordering::Relaxed);
|
self.is_sending_closed.store(true, Ordering::Relaxed);
|
||||||
self.tcp_conn.close();
|
if !self.tcp_conn.close() {
|
||||||
|
return_errno_with_message!(Errno::ENOTCONN, "the socket is not connected");
|
||||||
|
}
|
||||||
events |= IoEvents::OUT | IoEvents::HUP;
|
events |= IoEvents::OUT | IoEvents::HUP;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,6 +118,10 @@ impl ConnectedStream {
|
|||||||
reader: &mut dyn MultiRead,
|
reader: &mut dyn MultiRead,
|
||||||
_flags: SendRecvFlags,
|
_flags: SendRecvFlags,
|
||||||
) -> Result<(usize, NeedIfacePoll)> {
|
) -> Result<(usize, NeedIfacePoll)> {
|
||||||
|
if reader.is_empty() {
|
||||||
|
return Ok((0, NeedIfacePoll::FALSE));
|
||||||
|
}
|
||||||
|
|
||||||
let result = self.tcp_conn.send(|socket_buffer| {
|
let result = self.tcp_conn.send(|socket_buffer| {
|
||||||
match reader.read(&mut VmWriter::from(socket_buffer)) {
|
match reader.read(&mut VmWriter::from(socket_buffer)) {
|
||||||
Ok(len) => (len, Ok(len)),
|
Ok(len) => (len, Ok(len)),
|
||||||
|
@ -10,10 +10,13 @@ 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, KeepIdle, MaxSegment, NoDelay, WindowClamp, KEEPALIVE_INTERVAL};
|
use options::{
|
||||||
|
Congestion, DeferAccept, Inq, KeepIdle, MaxSegment, NoDelay, SynCnt, UserTimeout, 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::{Retrans, TcpOptionSet};
|
||||||
|
|
||||||
use super::UNSPECIFIED_LOCAL_ENDPOINT;
|
use super::UNSPECIFIED_LOCAL_ENDPOINT;
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -204,7 +207,7 @@ impl StreamSocket {
|
|||||||
// `Some(_)` if blocking is not necessary or not allowed.
|
// `Some(_)` if blocking is not necessary or not allowed.
|
||||||
fn start_connect(&self, remote_endpoint: &IpEndpoint) -> Option<Result<()>> {
|
fn start_connect(&self, remote_endpoint: &IpEndpoint) -> Option<Result<()>> {
|
||||||
let is_nonblocking = self.is_nonblocking();
|
let is_nonblocking = self.is_nonblocking();
|
||||||
let (options, mut state) = self.update_connecting();
|
let (mut options, mut state) = self.update_connecting();
|
||||||
|
|
||||||
let raw_option = options.raw();
|
let raw_option = options.raw();
|
||||||
|
|
||||||
@ -248,7 +251,13 @@ impl StreamSocket {
|
|||||||
StreamObserver::new(self.pollee.clone()),
|
StreamObserver::new(self.pollee.clone()),
|
||||||
) {
|
) {
|
||||||
Ok(connecting_stream) => connecting_stream,
|
Ok(connecting_stream) => connecting_stream,
|
||||||
Err((err, init_stream)) => {
|
Err((mut err, init_stream)) => {
|
||||||
|
// If the socket is nonblocking, we should return EINPROGRESS instead.
|
||||||
|
if is_nonblocking {
|
||||||
|
options.socket.set_sock_errors(Some(err));
|
||||||
|
err = Error::new(Errno::EINPROGRESS);
|
||||||
|
}
|
||||||
|
|
||||||
return (State::Init(init_stream), (Some(Err(err)), None));
|
return (State::Init(init_stream), (Some(Err(err)), None));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -601,13 +610,33 @@ impl Socket for StreamSocket {
|
|||||||
tcp_no_delay.set(no_delay);
|
tcp_no_delay.set(no_delay);
|
||||||
},
|
},
|
||||||
tcp_maxseg: MaxSegment => {
|
tcp_maxseg: MaxSegment => {
|
||||||
|
const DEFAULT_MAX_SEGMEMT: u32 = 536;
|
||||||
|
// For an unconnected socket,
|
||||||
|
// older Linux versions (e.g., v6.0) return
|
||||||
|
// the default MSS value defined above.
|
||||||
|
// However, newer Linux versions (e.g., v6.11)
|
||||||
|
// return the user-set MSS value if it is set.
|
||||||
|
// Here, we adopt the behavior of the latest Linux versions.
|
||||||
let maxseg = options.tcp.maxseg();
|
let maxseg = options.tcp.maxseg();
|
||||||
tcp_maxseg.set(maxseg);
|
if maxseg == 0 {
|
||||||
|
tcp_maxseg.set(DEFAULT_MAX_SEGMEMT);
|
||||||
|
} else {
|
||||||
|
tcp_maxseg.set(maxseg);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
tcp_keep_idle: KeepIdle => {
|
tcp_keep_idle: KeepIdle => {
|
||||||
let keep_idle = options.tcp.keep_idle();
|
let keep_idle = options.tcp.keep_idle();
|
||||||
tcp_keep_idle.set(keep_idle);
|
tcp_keep_idle.set(keep_idle);
|
||||||
},
|
},
|
||||||
|
tcp_syn_cnt: SynCnt => {
|
||||||
|
let syn_cnt = options.tcp.syn_cnt();
|
||||||
|
tcp_syn_cnt.set(syn_cnt);
|
||||||
|
},
|
||||||
|
tcp_defer_accept: DeferAccept => {
|
||||||
|
let defer_accept = options.tcp.defer_accept();
|
||||||
|
let seconds = defer_accept.to_secs();
|
||||||
|
tcp_defer_accept.set(seconds);
|
||||||
|
},
|
||||||
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);
|
||||||
@ -616,6 +645,14 @@ impl Socket for StreamSocket {
|
|||||||
let congestion = options.tcp.congestion();
|
let congestion = options.tcp.congestion();
|
||||||
tcp_congestion.set(congestion);
|
tcp_congestion.set(congestion);
|
||||||
},
|
},
|
||||||
|
tcp_user_timeout: UserTimeout => {
|
||||||
|
let user_timeout = options.tcp.user_timeout();
|
||||||
|
tcp_user_timeout.set(user_timeout);
|
||||||
|
},
|
||||||
|
tcp_inq: Inq => {
|
||||||
|
let inq = options.tcp.receive_inq();
|
||||||
|
tcp_inq.set(inq);
|
||||||
|
},
|
||||||
_ => 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")
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -679,6 +716,23 @@ fn do_tcp_setsockopt(
|
|||||||
|
|
||||||
// TODO: Track when the socket becomes idle to actually support keep idle.
|
// TODO: Track when the socket becomes idle to actually support keep idle.
|
||||||
},
|
},
|
||||||
|
tcp_syn_cnt: SynCnt => {
|
||||||
|
const MAX_TCP_SYN_CNT: u8 = 127;
|
||||||
|
|
||||||
|
let syncnt = tcp_syn_cnt.get().unwrap();
|
||||||
|
if *syncnt < 1 || *syncnt > MAX_TCP_SYN_CNT {
|
||||||
|
return_errno_with_message!(Errno::EINVAL, "the SYN count is out of bounds");
|
||||||
|
}
|
||||||
|
options.tcp.set_syn_cnt(*syncnt);
|
||||||
|
},
|
||||||
|
tcp_defer_accept: DeferAccept => {
|
||||||
|
let mut seconds = *(tcp_defer_accept.get().unwrap());
|
||||||
|
if (seconds as i32) < 0 {
|
||||||
|
seconds = 0;
|
||||||
|
}
|
||||||
|
let retrans = Retrans::from_secs(seconds);
|
||||||
|
options.tcp.set_defer_accept(retrans);
|
||||||
|
},
|
||||||
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;
|
||||||
@ -692,6 +746,17 @@ fn do_tcp_setsockopt(
|
|||||||
let congestion = tcp_congestion.get().unwrap();
|
let congestion = tcp_congestion.get().unwrap();
|
||||||
options.tcp.set_congestion(*congestion);
|
options.tcp.set_congestion(*congestion);
|
||||||
},
|
},
|
||||||
|
tcp_user_timeout: UserTimeout => {
|
||||||
|
let user_timeout = tcp_user_timeout.get().unwrap();
|
||||||
|
if (*user_timeout as i32) < 0 {
|
||||||
|
return_errno_with_message!(Errno::EINVAL, "the user timeout cannot be negative");
|
||||||
|
}
|
||||||
|
options.tcp.set_user_timeout(*user_timeout);
|
||||||
|
},
|
||||||
|
tcp_inq: Inq => {
|
||||||
|
let inq = tcp_inq.get().unwrap();
|
||||||
|
options.tcp.set_receive_inq(*inq);
|
||||||
|
},
|
||||||
_ => 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")
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -7,8 +7,12 @@ impl_socket_options!(
|
|||||||
pub struct NoDelay(bool);
|
pub struct NoDelay(bool);
|
||||||
pub struct MaxSegment(u32);
|
pub struct MaxSegment(u32);
|
||||||
pub struct KeepIdle(u32);
|
pub struct KeepIdle(u32);
|
||||||
|
pub struct SynCnt(u8);
|
||||||
|
pub struct DeferAccept(u32);
|
||||||
pub struct WindowClamp(u32);
|
pub struct WindowClamp(u32);
|
||||||
pub struct Congestion(CongestionControl);
|
pub struct Congestion(CongestionControl);
|
||||||
|
pub struct UserTimeout(u32);
|
||||||
|
pub struct Inq(bool);
|
||||||
);
|
);
|
||||||
|
|
||||||
/// The keepalive interval.
|
/// The keepalive interval.
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
use aster_bigtcp::time::Duration;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, CopyGetters, Setters)]
|
#[derive(Debug, Clone, Copy, CopyGetters, Setters)]
|
||||||
@ -9,12 +11,17 @@ pub struct TcpOptionSet {
|
|||||||
no_delay: bool,
|
no_delay: bool,
|
||||||
maxseg: u32,
|
maxseg: u32,
|
||||||
keep_idle: u32,
|
keep_idle: u32,
|
||||||
|
syn_cnt: u8,
|
||||||
|
defer_accept: Retrans,
|
||||||
window_clamp: u32,
|
window_clamp: u32,
|
||||||
congestion: CongestionControl,
|
congestion: CongestionControl,
|
||||||
|
user_timeout: u32,
|
||||||
|
receive_inq: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const DEFAULT_MAXSEG: u32 = 536;
|
pub const DEFAULT_MAXSEG: u32 = 536;
|
||||||
pub const DEFAULT_KEEP_IDLE: u32 = 7200;
|
pub const DEFAULT_KEEP_IDLE: u32 = 7200;
|
||||||
|
pub const DEFAULT_SYN_CNT: u8 = 6;
|
||||||
pub const DEFAULT_WINDOW_CLAMP: u32 = 0x8000_0000;
|
pub const DEFAULT_WINDOW_CLAMP: u32 = 0x8000_0000;
|
||||||
|
|
||||||
impl TcpOptionSet {
|
impl TcpOptionSet {
|
||||||
@ -23,8 +30,12 @@ impl TcpOptionSet {
|
|||||||
no_delay: false,
|
no_delay: false,
|
||||||
maxseg: DEFAULT_MAXSEG,
|
maxseg: DEFAULT_MAXSEG,
|
||||||
keep_idle: DEFAULT_KEEP_IDLE,
|
keep_idle: DEFAULT_KEEP_IDLE,
|
||||||
|
syn_cnt: DEFAULT_SYN_CNT,
|
||||||
|
defer_accept: Retrans(0),
|
||||||
window_clamp: DEFAULT_WINDOW_CLAMP,
|
window_clamp: DEFAULT_WINDOW_CLAMP,
|
||||||
congestion: CongestionControl::Reno,
|
congestion: CongestionControl::Reno,
|
||||||
|
user_timeout: 0,
|
||||||
|
receive_inq: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -35,6 +46,63 @@ impl Default for TcpOptionSet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Initial RTO value
|
||||||
|
const TCP_TIMEOUT_INIT: Duration = Duration::from_secs(1);
|
||||||
|
const TCP_RTO_MAX: Duration = Duration::from_secs(120);
|
||||||
|
|
||||||
|
/// The number of retransmits.
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct Retrans(u8);
|
||||||
|
|
||||||
|
impl Retrans {
|
||||||
|
/// Converts seconds to retransmits.
|
||||||
|
pub const fn from_secs(seconds: u32) -> Self {
|
||||||
|
if seconds == 0 {
|
||||||
|
return Self(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut timeout = TCP_TIMEOUT_INIT.secs() as u32;
|
||||||
|
let rto_max = TCP_RTO_MAX.secs() as u32;
|
||||||
|
let mut period = timeout;
|
||||||
|
let mut res = 1;
|
||||||
|
|
||||||
|
while seconds > period && res < 255 {
|
||||||
|
res += 1;
|
||||||
|
timeout <<= 1;
|
||||||
|
if timeout > rto_max {
|
||||||
|
timeout = rto_max;
|
||||||
|
}
|
||||||
|
period += timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
Self(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts retransmits to seconds.
|
||||||
|
pub const fn to_secs(self) -> u32 {
|
||||||
|
let mut retrans = self.0;
|
||||||
|
|
||||||
|
if retrans == 0 {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut timeout = TCP_TIMEOUT_INIT.secs() as u32;
|
||||||
|
let rto_max = TCP_RTO_MAX.secs() as u32;
|
||||||
|
let mut period = timeout;
|
||||||
|
|
||||||
|
while retrans > 1 {
|
||||||
|
retrans -= 1;
|
||||||
|
timeout <<= 1;
|
||||||
|
if timeout > rto_max {
|
||||||
|
timeout = rto_max;
|
||||||
|
}
|
||||||
|
period += timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
period
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum CongestionControl {
|
pub enum CongestionControl {
|
||||||
Reno,
|
Reno,
|
||||||
@ -49,7 +117,7 @@ impl CongestionControl {
|
|||||||
let congestion = match name {
|
let congestion = match name {
|
||||||
Self::RENO => Self::Reno,
|
Self::RENO => Self::Reno,
|
||||||
Self::CUBIC => Self::Cubic,
|
Self::CUBIC => Self::Cubic,
|
||||||
_ => return_errno_with_message!(Errno::EINVAL, "unsupported congestion name"),
|
_ => return_errno_with_message!(Errno::ENOENT, "unsupported congestion name"),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(congestion)
|
Ok(congestion)
|
||||||
|
@ -3,7 +3,10 @@
|
|||||||
use super::RawSocketOption;
|
use super::RawSocketOption;
|
||||||
use crate::{
|
use crate::{
|
||||||
impl_raw_socket_option,
|
impl_raw_socket_option,
|
||||||
net::socket::ip::stream::options::{Congestion, KeepIdle, MaxSegment, NoDelay, WindowClamp},
|
net::socket::ip::stream::options::{
|
||||||
|
Congestion, DeferAccept, Inq, KeepIdle, MaxSegment, NoDelay, SynCnt, UserTimeout,
|
||||||
|
WindowClamp,
|
||||||
|
},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
util::net::options::SocketOption,
|
util::net::options::SocketOption,
|
||||||
};
|
};
|
||||||
@ -16,13 +19,28 @@ use crate::{
|
|||||||
#[expect(non_camel_case_types)]
|
#[expect(non_camel_case_types)]
|
||||||
#[expect(clippy::upper_case_acronyms)]
|
#[expect(clippy::upper_case_acronyms)]
|
||||||
pub enum CTcpOptionName {
|
pub enum CTcpOptionName {
|
||||||
NODELAY = 1, /* Turn off Nagle's algorithm. */
|
/// Turn off Nagle's algorithm
|
||||||
MAXSEG = 2, /* Limit MSS */
|
NODELAY = 1,
|
||||||
CORK = 3, /* Never send partially complete segments */
|
/// Limit MSS
|
||||||
KEEPIDLE = 4, /* Start keeplives after this period */
|
MAXSEG = 2,
|
||||||
KEEPALIVE = 5, /* Interval between keepalives */
|
/// Never send partially complete segments
|
||||||
WINDOW_CLAMP = 10, /* Bound advertised window */
|
CORK = 3,
|
||||||
CONGESTION = 13, /* Congestion control algorithm */
|
/// Start keeplives after this period
|
||||||
|
KEEPIDLE = 4,
|
||||||
|
/// Interval between keepalives
|
||||||
|
KEEPALIVE = 5,
|
||||||
|
/// Number of SYN retransmits
|
||||||
|
SYNCNT = 7,
|
||||||
|
/// Wake up listener only when data arriv
|
||||||
|
DEFER_ACCEPT = 9,
|
||||||
|
/// Bound advertised window
|
||||||
|
WINDOW_CLAMP = 10,
|
||||||
|
/// Congestion control algorithm
|
||||||
|
CONGESTION = 13,
|
||||||
|
/// How long for loss retry before timeout
|
||||||
|
USER_TIMEOUT = 18,
|
||||||
|
/// Notify bytes available to read as a cmsg on read
|
||||||
|
INQ = 36,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_tcp_option(name: i32) -> Result<Box<dyn RawSocketOption>> {
|
pub fn new_tcp_option(name: i32) -> Result<Box<dyn RawSocketOption>> {
|
||||||
@ -31,8 +49,12 @@ pub fn new_tcp_option(name: i32) -> Result<Box<dyn RawSocketOption>> {
|
|||||||
CTcpOptionName::NODELAY => Ok(Box::new(NoDelay::new())),
|
CTcpOptionName::NODELAY => Ok(Box::new(NoDelay::new())),
|
||||||
CTcpOptionName::MAXSEG => Ok(Box::new(MaxSegment::new())),
|
CTcpOptionName::MAXSEG => Ok(Box::new(MaxSegment::new())),
|
||||||
CTcpOptionName::KEEPIDLE => Ok(Box::new(KeepIdle::new())),
|
CTcpOptionName::KEEPIDLE => Ok(Box::new(KeepIdle::new())),
|
||||||
|
CTcpOptionName::SYNCNT => Ok(Box::new(SynCnt::new())),
|
||||||
|
CTcpOptionName::DEFER_ACCEPT => Ok(Box::new(DeferAccept::new())),
|
||||||
CTcpOptionName::WINDOW_CLAMP => Ok(Box::new(WindowClamp::new())),
|
CTcpOptionName::WINDOW_CLAMP => Ok(Box::new(WindowClamp::new())),
|
||||||
CTcpOptionName::CONGESTION => Ok(Box::new(Congestion::new())),
|
CTcpOptionName::CONGESTION => Ok(Box::new(Congestion::new())),
|
||||||
|
CTcpOptionName::USER_TIMEOUT => Ok(Box::new(UserTimeout::new())),
|
||||||
|
CTcpOptionName::INQ => Ok(Box::new(Inq::new())),
|
||||||
_ => return_errno_with_message!(Errno::ENOPROTOOPT, "unsupported tcp-level option"),
|
_ => return_errno_with_message!(Errno::ENOPROTOOPT, "unsupported tcp-level option"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -40,5 +62,9 @@ pub fn new_tcp_option(name: i32) -> Result<Box<dyn RawSocketOption>> {
|
|||||||
impl_raw_socket_option!(NoDelay);
|
impl_raw_socket_option!(NoDelay);
|
||||||
impl_raw_socket_option!(MaxSegment);
|
impl_raw_socket_option!(MaxSegment);
|
||||||
impl_raw_socket_option!(KeepIdle);
|
impl_raw_socket_option!(KeepIdle);
|
||||||
|
impl_raw_socket_option!(SynCnt);
|
||||||
|
impl_raw_socket_option!(DeferAccept);
|
||||||
impl_raw_socket_option!(WindowClamp);
|
impl_raw_socket_option!(WindowClamp);
|
||||||
impl_raw_socket_option!(Congestion);
|
impl_raw_socket_option!(Congestion);
|
||||||
|
impl_raw_socket_option!(UserTimeout);
|
||||||
|
impl_raw_socket_option!(Inq);
|
||||||
|
@ -32,15 +32,8 @@ pub trait WriteToUser {
|
|||||||
fn write_to_user(&self, addr: Vaddr, max_len: u32) -> Result<usize>;
|
fn write_to_user(&self, addr: Vaddr, max_len: u32) -> Result<usize>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This macro is used to implement `ReadFromUser` and `WriteToUser` for types that
|
/// This macro is used to implement `ReadFromUser` and `WriteToUser` for u32 and i32.
|
||||||
/// implement the `Pod` trait.
|
macro_rules! impl_read_write_for_32bit_type {
|
||||||
/// FIXME: The macro is somewhat ugly. Ideally, we would prefer to use
|
|
||||||
/// ```rust
|
|
||||||
/// impl <T: Pod> ReadFromUser for T
|
|
||||||
/// ```
|
|
||||||
/// instead of this macro. However, using the `impl` statement will result in a compilation
|
|
||||||
/// error, as it is possible for an upstream crate to implement `Pod` for other types like `bool`,
|
|
||||||
macro_rules! impl_read_write_for_pod_type {
|
|
||||||
($pod_ty: ty) => {
|
($pod_ty: ty) => {
|
||||||
impl ReadFromUser for $pod_ty {
|
impl ReadFromUser for $pod_ty {
|
||||||
fn read_from_user(addr: Vaddr, max_len: u32) -> Result<Self> {
|
fn read_from_user(addr: Vaddr, max_len: u32) -> Result<Self> {
|
||||||
@ -66,31 +59,38 @@ macro_rules! impl_read_write_for_pod_type {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_read_write_for_pod_type!(u32);
|
impl_read_write_for_32bit_type!(i32);
|
||||||
|
impl_read_write_for_32bit_type!(u32);
|
||||||
|
|
||||||
impl ReadFromUser for bool {
|
impl ReadFromUser for bool {
|
||||||
fn read_from_user(addr: Vaddr, max_len: u32) -> Result<Self> {
|
fn read_from_user(addr: Vaddr, max_len: u32) -> Result<Self> {
|
||||||
if (max_len as usize) < core::mem::size_of::<i32>() {
|
let val = i32::read_from_user(addr, max_len)?;
|
||||||
return_errno_with_message!(Errno::EINVAL, "max_len is too short");
|
|
||||||
}
|
|
||||||
|
|
||||||
let val = current_userspace!().read_val::<i32>(addr)?;
|
|
||||||
|
|
||||||
Ok(val != 0)
|
Ok(val != 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WriteToUser for bool {
|
impl WriteToUser for bool {
|
||||||
fn write_to_user(&self, addr: Vaddr, max_len: u32) -> Result<usize> {
|
fn write_to_user(&self, addr: Vaddr, max_len: u32) -> Result<usize> {
|
||||||
let write_len = core::mem::size_of::<i32>();
|
let val = if *self { 1i32 } else { 0i32 };
|
||||||
|
val.write_to_user(addr, max_len)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (max_len as usize) < write_len {
|
impl ReadFromUser for u8 {
|
||||||
return_errno_with_message!(Errno::EINVAL, "max_len is too short");
|
fn read_from_user(addr: Vaddr, max_len: u32) -> Result<Self> {
|
||||||
|
let val = i32::read_from_user(addr, max_len)?;
|
||||||
|
|
||||||
|
if val < 0 || val > u8::MAX as i32 {
|
||||||
|
return_errno_with_message!(Errno::EINVAL, "invalid u8 value");
|
||||||
}
|
}
|
||||||
|
|
||||||
let val = if *self { 1i32 } else { 0i32 };
|
Ok(val as u8)
|
||||||
current_userspace!().write_val(addr, &val)?;
|
}
|
||||||
Ok(write_len)
|
}
|
||||||
|
|
||||||
|
impl WriteToUser for u8 {
|
||||||
|
fn write_to_user(&self, addr: Vaddr, max_len: u32) -> Result<usize> {
|
||||||
|
(*self as i32).write_to_user(addr, max_len)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,25 +138,40 @@ impl WriteToUser for LingerOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const TCP_CONGESTION_NAME_MAX: u32 = 16;
|
||||||
|
|
||||||
impl ReadFromUser for CongestionControl {
|
impl ReadFromUser for CongestionControl {
|
||||||
fn read_from_user(addr: Vaddr, max_len: u32) -> Result<Self> {
|
fn read_from_user(addr: Vaddr, max_len: u32) -> Result<Self> {
|
||||||
let mut bytes = vec![0; max_len as usize];
|
let mut bytes = [0; TCP_CONGESTION_NAME_MAX as usize];
|
||||||
current_userspace!().read_bytes(addr, &mut VmWriter::from(bytes.as_mut_slice()))?;
|
|
||||||
let name = String::from_utf8(bytes).unwrap();
|
let dst = {
|
||||||
CongestionControl::new(&name)
|
let read_len = (TCP_CONGESTION_NAME_MAX - 1).min(max_len) as usize;
|
||||||
|
&mut bytes[..read_len]
|
||||||
|
};
|
||||||
|
|
||||||
|
// Clippy warns that `dst.as_mut` is redundant. However, using `dst` directly
|
||||||
|
// instead of `dst.as_mut` would take the ownership of `dst`. Consequently,
|
||||||
|
// the subsequent code that constructs `name` from `dst` would fail to compile.
|
||||||
|
#[expect(clippy::useless_asref)]
|
||||||
|
current_userspace!().read_bytes(addr, &mut VmWriter::from(dst.as_mut()))?;
|
||||||
|
|
||||||
|
let name = core::str::from_utf8(dst)
|
||||||
|
.map_err(|_| Error::with_message(Errno::ENOENT, "non-UTF8 congestion name"))?;
|
||||||
|
CongestionControl::new(name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WriteToUser for CongestionControl {
|
impl WriteToUser for CongestionControl {
|
||||||
fn write_to_user(&self, addr: Vaddr, max_len: u32) -> Result<usize> {
|
fn write_to_user(&self, addr: Vaddr, max_len: u32) -> Result<usize> {
|
||||||
let name = self.name().as_bytes();
|
let mut bytes = [0u8; TCP_CONGESTION_NAME_MAX as usize];
|
||||||
|
|
||||||
let write_len = name.len();
|
let name_bytes = self.name().as_bytes();
|
||||||
if write_len > max_len as usize {
|
let name_len = name_bytes.len();
|
||||||
return_errno_with_message!(Errno::EINVAL, "max_len is too short");
|
bytes[..name_len].copy_from_slice(name_bytes);
|
||||||
}
|
|
||||||
|
|
||||||
current_userspace!().write_bytes(addr, &mut VmReader::from(name))?;
|
let write_len = TCP_CONGESTION_NAME_MAX.min(max_len) as usize;
|
||||||
|
|
||||||
|
current_userspace!().write_bytes(addr, &mut VmReader::from(&bytes[..write_len]))?;
|
||||||
|
|
||||||
Ok(write_len)
|
Ok(write_len)
|
||||||
}
|
}
|
||||||
|
@ -273,4 +273,4 @@ FN_TEST(keepidle)
|
|||||||
&keepidle_len),
|
&keepidle_len),
|
||||||
keepidle == 200);
|
keepidle == 200);
|
||||||
}
|
}
|
||||||
END_TEST()
|
END_TEST()
|
||||||
|
@ -47,6 +47,7 @@ TESTS ?= \
|
|||||||
symlink_test \
|
symlink_test \
|
||||||
sync_test \
|
sync_test \
|
||||||
sysinfo_test \
|
sysinfo_test \
|
||||||
|
tcp_socket_test \
|
||||||
timers_test \
|
timers_test \
|
||||||
truncate_test \
|
truncate_test \
|
||||||
uidgid_test \
|
uidgid_test \
|
||||||
|
16
test/syscall_test/blocklists/tcp_socket_test
Normal file
16
test/syscall_test/blocklists/tcp_socket_test
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
AllInetTests/*/1
|
||||||
|
AllInetTests/TcpSocketTest.ZeroWriteAllowed/0
|
||||||
|
AllInetTests/TcpSocketTest.FullBuffer/0
|
||||||
|
AllInetTests/TcpSocketTest.TcpSCMPriority/0
|
||||||
|
AllInetTests/TcpSocketTest.Tiocinq/0
|
||||||
|
AllInetTests/TcpSocketTest.TcpInq/0
|
||||||
|
AllInetTests/TcpSocketTest.MsgTruncPeek/0
|
||||||
|
AllInetTests/TcpSocketTest.MsgTruncLargeSize/0
|
||||||
|
AllInetTests/TcpSocketTest.MsgTruncWithCtrunc/0
|
||||||
|
AllInetTests/TcpSocketTest.MsgTrunc/0
|
||||||
|
|
||||||
|
AllInetTests/SimpleTcpSocketTest.SetMaxSeg/0
|
||||||
|
AllInetTests/SimpleTcpSocketTest.CleanupOnConnectionRefused/0
|
||||||
|
AllInetTests/SimpleTcpSocketTest.SetTCPWindowClampZeroClosedSocket/0
|
||||||
|
AllInetTests/SimpleTcpSocketTest.SetSocketAttachDetachFilter/0
|
||||||
|
AllInetTests/SimpleTcpSocketTest.SetSocketDetachFilterNoInstalledFilter/0
|
Loading…
x
Reference in New Issue
Block a user