Skip to content

Commit

Permalink
Add support for TCP_CONGESTION socketopt
Browse files Browse the repository at this point in the history
Co-authored-by: Campbell He <kp.campbell.he@duskmoon314.com>
  • Loading branch information
BobAnkh and duskmoon314 authored Jan 7, 2023
1 parent ed23383 commit ac23d7d
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 0 deletions.
53 changes: 53 additions & 0 deletions src/sys/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,10 @@ const MAX_BUF_LEN: usize = ssize_t::MAX as usize;
#[cfg(target_vendor = "apple")]
const MAX_BUF_LEN: usize = c_int::MAX as usize - 1;

// TCP_CA_NAME_MAX isn't defined in user space include files(not in libc)
#[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux")))]
const TCP_CA_NAME_MAX: usize = 16;

#[cfg(any(
all(
target_os = "linux",
Expand Down Expand Up @@ -2154,6 +2158,55 @@ impl crate::Socket {
)
}
}

/// Get the value of the `TCP_CONGESTION` option for this socket.
///
/// For more information about this option, see [`set_tcp_congestion`].
///
/// [`set_tcp_congestion`]: Socket::set_tcp_congestion
#[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux")))]
#[cfg_attr(
docsrs,
doc(cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux"))))
)]
pub fn tcp_congestion(&self) -> io::Result<Vec<u8>> {
let mut payload: [u8; TCP_CA_NAME_MAX] = [0; TCP_CA_NAME_MAX];
let mut len = payload.len() as libc::socklen_t;
syscall!(getsockopt(
self.as_raw(),
IPPROTO_TCP,
libc::TCP_CONGESTION,
payload.as_mut_ptr().cast(),
&mut len,
))
.map(|_| {
let buf = &payload[..len as usize];
// TODO: use `MaybeUninit::slice_assume_init_ref` once stable.
unsafe { &*(buf as *const [_] as *const [u8]) }.into()
})
}

/// Set the value of the `TCP_CONGESTION` option for this socket.
///
/// Specifies the TCP congestion control algorithm to use for this socket.
///
/// The value must be a valid TCP congestion control algorithm name of the
/// platform. For example, Linux may supports "reno", "cubic".
#[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux")))]
#[cfg_attr(
docsrs,
doc(cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux"))))
)]
pub fn set_tcp_congestion(&self, tcp_ca_name: &[u8]) -> io::Result<()> {
syscall!(setsockopt(
self.as_raw(),
IPPROTO_TCP,
libc::TCP_CONGESTION,
tcp_ca_name.as_ptr() as *const _,
tcp_ca_name.len() as libc::socklen_t,
))
.map(|_| ())
}
}

#[cfg_attr(docsrs, doc(cfg(unix)))]
Expand Down
46 changes: 46 additions & 0 deletions tests/socket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1341,3 +1341,49 @@ fn original_dst_ipv6() {
Err(err) => assert_eq!(err.raw_os_error(), Some(libc::EOPNOTSUPP)),
}
}

#[test]
#[cfg(all(feature = "all", any(target_os = "freebsd", target_os = "linux")))]
fn tcp_congestion() {
let socket: Socket = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap();
// Get and set current tcp_ca
let origin_tcp_ca = socket
.tcp_congestion()
.expect("failed to get tcp congestion algorithm");
socket
.set_tcp_congestion(&origin_tcp_ca)
.expect("failed to set tcp congestion algorithm");
// Return a Err when set a non-exist tcp_ca
socket
.set_tcp_congestion(b"tcp_congestion_does_not_exist")
.unwrap_err();
let cur_tcp_ca = socket.tcp_congestion().unwrap();
assert_eq!(
cur_tcp_ca, origin_tcp_ca,
"expected {origin_tcp_ca:?} but get {cur_tcp_ca:?}"
);
let cur_tcp_ca = cur_tcp_ca.splitn(2, |num| *num == 0).next().unwrap();
const OPTIONS: [&[u8]; 2] = [
b"cubic",
#[cfg(target_os = "linux")] // or Android.
b"reno",
#[cfg(target_os = "freebsd")]
b"newreno",
];
// Set a new tcp ca
#[cfg(target_os = "linux")]
let new_tcp_ca = if cur_tcp_ca == OPTIONS[0] {
OPTIONS[1]
} else {
OPTIONS[0]
};
#[cfg(target_os = "freebsd")]
let new_tcp_ca = OPTIONS[1];
socket.set_tcp_congestion(new_tcp_ca).unwrap();
// Check if new tcp ca is successfully set
let cur_tcp_ca = socket.tcp_congestion().unwrap();
assert_eq!(
cur_tcp_ca.splitn(2, |num| *num == 0).next().unwrap(),
new_tcp_ca,
);
}

0 comments on commit ac23d7d

Please sign in to comment.