From b75d31d2f5b84a3ac5c0d59809dba918dd9c53d8 Mon Sep 17 00:00:00 2001 From: Fensteer Date: Fri, 16 Nov 2018 16:06:12 +0100 Subject: [PATCH] Add support of TCP_CONGESTION for setsockopt --- CHANGELOG.md | 5 ++++ src/sys/socket/sockopt.rs | 61 +++++++++++++++++++++++++++++++++++++++ test/sys/test_sockopt.rs | 25 ++++++++++++++++ 3 files changed, 91 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d974c7a1d9..f89e499876 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] ### Added +- Added support of CString type in `setsockopt`. + ([#972](https://github.com/nix-rust/nix/pull/972)) +- Added option `TCP_CONGESTION` in `setsockopt`. + ([#972](https://github.com/nix-rust/nix/pull/972)) + ### Changed ### Fixed ### Removed diff --git a/src/sys/socket/sockopt.rs b/src/sys/socket/sockopt.rs index 494de4f577..17119384d4 100644 --- a/src/sys/socket/sockopt.rs +++ b/src/sys/socket/sockopt.rs @@ -5,6 +5,14 @@ use sys::time::TimeVal; use libc::{self, c_int, uint8_t, c_void, socklen_t}; use std::mem; use std::os::unix::io::RawFd; +use std::ffi::{OsStr, OsString}; +#[cfg(target_family = "unix")] +use std::os::unix::ffi::OsStrExt; + +// Constants +// TCP_CA_NAME_MAX isn't defined in user space include files +#[cfg(any(target_os = "freebsd", target_os = "linux"))] +const TCP_CA_NAME_MAX: usize = 16; /// Helper for implementing `SetSockOpt` for a given socket option. See /// [`::sys::socket::SetSockOpt`](sys/socket/trait.SetSockOpt.html). @@ -152,6 +160,10 @@ macro_rules! sockopt_impl { sockopt_impl!(Both, $name, $level, $flag, usize, GetUsize, SetUsize); }; + (Both, $name:ident, $level:path, $flag:path, OsString<$array:ty>) => { + sockopt_impl!(Both, $name, $level, $flag, OsString, GetOsString<$array>, SetOsString); + }; + /* * Matchers with generic getter types must be placed at the end, so * they'll only match _after_ specialized matchers fail @@ -257,6 +269,8 @@ sockopt_impl!(Both, BindAny, libc::IPPROTO_IP, libc::IP_BINDANY, bool); sockopt_impl!(Both, Mark, libc::SOL_SOCKET, libc::SO_MARK, u32); #[cfg(any(target_os = "android", target_os = "linux"))] sockopt_impl!(Both, PassCred, libc::SOL_SOCKET, libc::SO_PASSCRED, bool); +#[cfg(any(target_os = "freebsd", target_os = "linux"))] +sockopt_impl!(Both, TcpCongestion, libc::IPPROTO_TCP, libc::TCP_CONGESTION, OsString<[u8; TCP_CA_NAME_MAX]>); /* * @@ -478,6 +492,53 @@ unsafe impl<'a> Set<'a, usize> for SetUsize { } } +/// Getter for a `OsString` value. +struct GetOsString> { + len: socklen_t, + val: T, +} + +unsafe impl> Get for GetOsString { + unsafe fn blank() -> Self { + GetOsString { + len: mem::size_of::() as socklen_t, + val: mem::zeroed(), + } + } + + fn ffi_ptr(&mut self) -> *mut c_void { + &mut self.val as *mut T as *mut c_void + } + + fn ffi_len(&mut self) -> *mut socklen_t { + &mut self.len + } + + unsafe fn unwrap(mut self) -> OsString { + OsStr::from_bytes(self.val.as_mut()).to_owned() + } +} + +/// Setter for a `OsString` value. +struct SetOsString<'a> { + val: &'a OsStr, +} + +unsafe impl<'a> Set<'a, OsString> for SetOsString<'a> { + fn new(val: &'a OsString) -> SetOsString { + SetOsString { val: val.as_os_str() } + } + + fn ffi_ptr(&self) -> *const c_void { + self.val.as_bytes().as_ptr() as *const c_void + } + + fn ffi_len(&self) -> socklen_t { + self.val.len() as socklen_t + } +} + + #[cfg(test)] mod test { #[cfg(any(target_os = "android", target_os = "linux"))] diff --git a/test/sys/test_sockopt.rs b/test/sys/test_sockopt.rs index a38657c1ea..efe2c56b55 100644 --- a/test/sys/test_sockopt.rs +++ b/test/sys/test_sockopt.rs @@ -13,3 +13,28 @@ fn test_so_buf() { let actual = getsockopt(fd, sockopt::RcvBuf).unwrap(); assert!(actual >= bufsize); } + +// The CI doesn't supported getsockopt and setsockopt on emulated processors. +// It's beleived that a QEMU issue, the tests run ok on a fully emulated system. +// Current CI just run the binary with QEMU but the Kernel remains the same as the host. +// So the syscall doesn't work properly unless the kernel is also emulated. +#[test] +#[cfg(all( + any(target_arch = "x86", target_arch = "x86_64"), + any(target_os = "freebsd", target_os = "linux") +))] +fn test_tcp_congestion() { + use std::ffi::OsString; + + let fd = socket(AddressFamily::Inet, SockType::Stream, SockFlag::empty(), None).unwrap(); + + let val = getsockopt(fd, sockopt::TcpCongestion).unwrap(); + setsockopt(fd, sockopt::TcpCongestion, &val).unwrap(); + + setsockopt(fd, sockopt::TcpCongestion, &OsString::from("tcp_congestion_does_not_exist")).unwrap_err(); + + assert_eq!( + getsockopt(fd, sockopt::TcpCongestion).unwrap(), + val + ); +}