Skip to content

Commit

Permalink
Merge #972
Browse files Browse the repository at this point in the history
972: Add support of TCP_CONGESTION for setsockopt r=asomers a=Fensteer

Implementation proposal for support of TCP_CONGESTION param for `setsockopt`and `getsockopt` with the CString type.

Co-authored-by: Fensteer <fensteer@protonmail.com>
  • Loading branch information
bors[bot] and Fensteer committed Dec 6, 2018
2 parents 307cc90 + b75d31d commit 5a3ac8d
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 0 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
61 changes: 61 additions & 0 deletions src/sys/socket/sockopt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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]>);

/*
*
Expand Down Expand Up @@ -478,6 +492,53 @@ unsafe impl<'a> Set<'a, usize> for SetUsize {
}
}

/// Getter for a `OsString` value.
struct GetOsString<T: AsMut<[u8]>> {
len: socklen_t,
val: T,
}

unsafe impl<T: AsMut<[u8]>> Get<OsString> for GetOsString<T> {
unsafe fn blank() -> Self {
GetOsString {
len: mem::size_of::<T>() 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"))]
Expand Down
25 changes: 25 additions & 0 deletions test/sys/test_sockopt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
);
}

0 comments on commit 5a3ac8d

Please sign in to comment.