Skip to content

Commit a9ecf8d

Browse files
authored
Fix the set/get_socket_timeout timeout on linux_raw (#853)
Fix the linux_raw backend to use `__kernel_sock_timeval` and `__kernel_old_timeval` for the send and recieve timeout socket options.
1 parent b08519f commit a9ecf8d

2 files changed

Lines changed: 54 additions & 29 deletions

File tree

src/backend/linux_raw/net/syscalls.rs

Lines changed: 38 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -927,7 +927,7 @@ pub(crate) mod sockopt {
927927
use crate::net::{AddressFamily, Ipv4Addr, Ipv6Addr, SocketType};
928928
use c::{SO_RCVTIMEO_NEW, SO_RCVTIMEO_OLD, SO_SNDTIMEO_NEW, SO_SNDTIMEO_OLD};
929929
use core::time::Duration;
930-
use linux_raw_sys::general::{__kernel_timespec, timeval};
930+
use linux_raw_sys::general::{__kernel_old_timeval, __kernel_sock_timeval};
931931
use linux_raw_sys::net::{SO_ACCEPTCONN, TCP_KEEPCNT, TCP_KEEPIDLE, TCP_KEEPINTVL};
932932

933933
#[inline]
@@ -1099,7 +1099,7 @@ pub(crate) mod sockopt {
10991099
id: Timeout,
11001100
timeout: Option<Duration>,
11011101
) -> io::Result<()> {
1102-
let time = duration_to_linux(timeout)?;
1102+
let time = duration_to_linux_sock_timeval(timeout)?;
11031103
let optname = match id {
11041104
Timeout::Recv => SO_RCVTIMEO_NEW,
11051105
Timeout::Send => SO_SNDTIMEO_NEW,
@@ -1112,14 +1112,14 @@ pub(crate) mod sockopt {
11121112
}
11131113
}
11141114

1115-
/// Same as `set_socket_timeout` but uses `timeval` instead of
1116-
/// `__kernel_timespec` and `_OLD` constants instead of `_NEW`.
1115+
/// Same as `set_socket_timeout` but uses `__kernel_old_timeval` instead of
1116+
/// `__kernel_sock_timeval` and `_OLD` constants instead of `_NEW`.
11171117
fn set_socket_timeout_old(
11181118
fd: BorrowedFd<'_>,
11191119
id: Timeout,
11201120
timeout: Option<Duration>,
11211121
) -> io::Result<()> {
1122-
let time = duration_to_linux_old(timeout)?;
1122+
let time = duration_to_linux_old_timeval(timeout)?;
11231123
let optname = match id {
11241124
Timeout::Recv => SO_RCVTIMEO_OLD,
11251125
Timeout::Send => SO_SNDTIMEO_OLD,
@@ -1136,40 +1136,42 @@ pub(crate) mod sockopt {
11361136
Timeout::Recv => SO_RCVTIMEO_NEW,
11371137
Timeout::Send => SO_SNDTIMEO_NEW,
11381138
};
1139-
let time: __kernel_timespec = match getsockopt(fd, c::SOL_SOCKET, optname) {
1139+
let time: __kernel_sock_timeval = match getsockopt(fd, c::SOL_SOCKET, optname) {
11401140
Err(io::Errno::NOPROTOOPT) if SO_RCVTIMEO_NEW != SO_RCVTIMEO_OLD => {
11411141
return get_socket_timeout_old(fd, id)
11421142
}
11431143
otherwise => otherwise?,
11441144
};
1145-
Ok(duration_from_linux(time))
1145+
Ok(duration_from_linux_sock_timeval(time))
11461146
}
11471147

1148-
/// Same as `get_socket_timeout` but uses `timeval` instead of
1149-
/// `__kernel_timespec` and `_OLD` constants instead of `_NEW`.
1148+
/// Same as `get_socket_timeout` but uses `__kernel_old_timeval` instead of
1149+
/// `__kernel_sock_timeval` and `_OLD` constants instead of `_NEW`.
11501150
fn get_socket_timeout_old(fd: BorrowedFd<'_>, id: Timeout) -> io::Result<Option<Duration>> {
11511151
let optname = match id {
11521152
Timeout::Recv => SO_RCVTIMEO_OLD,
11531153
Timeout::Send => SO_SNDTIMEO_OLD,
11541154
};
1155-
let time: timeval = getsockopt(fd, c::SOL_SOCKET, optname)?;
1156-
Ok(duration_from_linux_old(time))
1155+
let time: __kernel_old_timeval = getsockopt(fd, c::SOL_SOCKET, optname)?;
1156+
Ok(duration_from_linux_old_timeval(time))
11571157
}
11581158

1159-
/// Convert a C `timespec` to a Rust `Option<Duration>`.
1159+
/// Convert a `__linux_sock_timeval` to a Rust `Option<Duration>`.
11601160
#[inline]
1161-
fn duration_from_linux(time: __kernel_timespec) -> Option<Duration> {
1162-
if time.tv_sec == 0 && time.tv_nsec == 0 {
1161+
fn duration_from_linux_sock_timeval(time: __kernel_sock_timeval) -> Option<Duration> {
1162+
if time.tv_sec == 0 && time.tv_usec == 0 {
11631163
None
11641164
} else {
11651165
Some(
1166-
Duration::from_secs(time.tv_sec as u64) + Duration::from_nanos(time.tv_nsec as u64),
1166+
Duration::from_secs(time.tv_sec as u64)
1167+
+ Duration::from_micros(time.tv_usec as u64),
11671168
)
11681169
}
11691170
}
11701171

1171-
/// Like `duration_from_linux` but uses Linux's old 32-bit `timeval`.
1172-
fn duration_from_linux_old(time: timeval) -> Option<Duration> {
1172+
/// Like `duration_from_linux` but uses Linux's old 32-bit
1173+
/// `__kernel_old_timeval`.
1174+
fn duration_from_linux_old_timeval(time: __kernel_old_timeval) -> Option<Duration> {
11731175
if time.tv_sec == 0 && time.tv_usec == 0 {
11741176
None
11751177
} else {
@@ -1180,32 +1182,39 @@ pub(crate) mod sockopt {
11801182
}
11811183
}
11821184

1183-
/// Convert a Rust `Option<Duration>` to a C `timespec`.
1185+
/// Convert a Rust `Option<Duration>` to a `__kernel_sock_timeval`.
11841186
#[inline]
1185-
fn duration_to_linux(timeout: Option<Duration>) -> io::Result<__kernel_timespec> {
1187+
fn duration_to_linux_sock_timeval(
1188+
timeout: Option<Duration>,
1189+
) -> io::Result<__kernel_sock_timeval> {
11861190
Ok(match timeout {
11871191
Some(timeout) => {
11881192
if timeout == Duration::ZERO {
11891193
return Err(io::Errno::INVAL);
11901194
}
1191-
let mut timeout = __kernel_timespec {
1195+
// `subsec_micros` rounds down, so we use `subsec_nanos` and
1196+
// manually round up.
1197+
let mut timeout = __kernel_sock_timeval {
11921198
tv_sec: timeout.as_secs().try_into().unwrap_or(i64::MAX),
1193-
tv_nsec: timeout.subsec_nanos().into(),
1199+
tv_usec: ((timeout.subsec_nanos() + 999) / 1000) as _,
11941200
};
1195-
if timeout.tv_sec == 0 && timeout.tv_nsec == 0 {
1196-
timeout.tv_nsec = 1;
1201+
if timeout.tv_sec == 0 && timeout.tv_usec == 0 {
1202+
timeout.tv_usec = 1;
11971203
}
11981204
timeout
11991205
}
1200-
None => __kernel_timespec {
1206+
None => __kernel_sock_timeval {
12011207
tv_sec: 0,
1202-
tv_nsec: 0,
1208+
tv_usec: 0,
12031209
},
12041210
})
12051211
}
12061212

1207-
/// Like `duration_to_linux` but uses Linux's old 32-bit `timeval`.
1208-
fn duration_to_linux_old(timeout: Option<Duration>) -> io::Result<timeval> {
1213+
/// Like `duration_to_linux` but uses Linux's old 32-bit
1214+
/// `__kernel_old_timeval`.
1215+
fn duration_to_linux_old_timeval(
1216+
timeout: Option<Duration>,
1217+
) -> io::Result<__kernel_old_timeval> {
12091218
Ok(match timeout {
12101219
Some(timeout) => {
12111220
if timeout == Duration::ZERO {
@@ -1214,7 +1223,7 @@ pub(crate) mod sockopt {
12141223

12151224
// `subsec_micros` rounds down, so we use `subsec_nanos` and
12161225
// manually round up.
1217-
let mut timeout = timeval {
1226+
let mut timeout = __kernel_old_timeval {
12181227
tv_sec: timeout.as_secs().try_into().unwrap_or(c::c_long::MAX),
12191228
tv_usec: ((timeout.subsec_nanos() + 999) / 1000) as _,
12201229
};
@@ -1223,7 +1232,7 @@ pub(crate) mod sockopt {
12231232
}
12241233
timeout
12251234
}
1226-
None => timeval {
1235+
None => __kernel_old_timeval {
12271236
tv_sec: 0,
12281237
tv_usec: 0,
12291238
},

tests/net/sockopt.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,22 @@ fn test_sockopts_ipv4() {
9393
);
9494
}
9595

96+
// Set a timeout with more than a million nanoseconds.
97+
rustix::net::sockopt::set_socket_timeout(
98+
&s,
99+
rustix::net::sockopt::Timeout::Recv,
100+
Some(Duration::new(1, 10000000)),
101+
)
102+
.unwrap();
103+
104+
// Check that we have a timeout of at least the time we set.
105+
assert!(
106+
rustix::net::sockopt::get_socket_timeout(&s, rustix::net::sockopt::Timeout::Recv)
107+
.unwrap()
108+
.unwrap()
109+
>= Duration::new(1, 10000000)
110+
);
111+
96112
// Set the reuse address flag
97113
rustix::net::sockopt::set_socket_reuseaddr(&s, true).unwrap();
98114

0 commit comments

Comments
 (0)