Skip to content

Commit

Permalink
Merge #1292
Browse files Browse the repository at this point in the history
1292: Support `TCP_MAXSEG` TCP Maximum Segment Size socket options r=asomers a=eaon

Found myself in a situation where I needed to advertise a specific MSS. Thought this crate is the appropriate place for this feature (especially since the `TCP_MAXSEG` const was exposed through it before), so here goes.

Actual change is of course tiny but it comes with a test. Happy to make changes, especially since I need to do more research on which platforms this works.

Co-authored-by: eaon <eaon@posteo.net>
Co-authored-by: Alan Somers <asomers@gmail.com>
  • Loading branch information
3 people authored Sep 8, 2021
2 parents 53fea96 + b06c26f commit ccc1434
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ This project adheres to [Semantic Versioning](https://semver.org/).
(#[1503](https://github.com/nix-rust/nix/pull/1503))
- Enabled `pwritev` and `preadv` for more operating systems.
(#[1511](https://github.com/nix-rust/nix/pull/1511))
Added support for `TCP_MAXSEG` TCP Maximum Segment Size socket options
(#[1292](https://github.com/nix-rust/nix/pull/1292))
- Added `Ipv4RecvErr` and `Ipv6RecvErr` sockopts and associated control messages.
(#[1514](https://github.com/nix-rust/nix/pull/1514))
- Added `AsRawFd` implementation on `PollFd`.
Expand Down
7 changes: 7 additions & 0 deletions src/sys/socket/sockopt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,13 @@ sockopt_impl!(Both, TcpKeepAlive, libc::IPPROTO_TCP, libc::TCP_KEEPALIVE, u32);
target_os = "linux",
target_os = "nacl"))]
sockopt_impl!(Both, TcpKeepIdle, libc::IPPROTO_TCP, libc::TCP_KEEPIDLE, u32);
cfg_if! {
if #[cfg(any(target_os = "android", target_os = "linux"))] {
sockopt_impl!(Both, TcpMaxSeg, libc::IPPROTO_TCP, libc::TCP_MAXSEG, u32);
} else {
sockopt_impl!(GetOnly, TcpMaxSeg, libc::IPPROTO_TCP, libc::TCP_MAXSEG, u32);
}
}
#[cfg(not(target_os = "openbsd"))]
sockopt_impl!(Both, TcpKeepCount, libc::IPPROTO_TCP, libc::TCP_KEEPCNT, u32);
#[cfg(any(target_os = "android",
Expand Down
51 changes: 51 additions & 0 deletions test/sys/test_sockopt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,57 @@ fn test_so_buf() {
assert!(actual >= bufsize);
}

#[test]
fn test_so_tcp_maxseg() {
use std::net::SocketAddr;
use std::str::FromStr;
use nix::sys::socket::{accept, bind, connect, listen, InetAddr, SockAddr};
use nix::unistd::{close, write};

let std_sa = SocketAddr::from_str("127.0.0.1:4001").unwrap();
let inet_addr = InetAddr::from_std(&std_sa);
let sock_addr = SockAddr::new_inet(inet_addr);

let rsock = socket(AddressFamily::Inet, SockType::Stream, SockFlag::empty(), SockProtocol::Tcp)
.unwrap();
bind(rsock, &sock_addr).unwrap();
listen(rsock, 10).unwrap();
let initial = getsockopt(rsock, sockopt::TcpMaxSeg).unwrap();
// Initial MSS is expected to be 536 (https://tools.ietf.org/html/rfc879#section-1) but some
// platforms keep it even lower. This might fail if you've tuned your initial MSS to be larger
// than 700
cfg_if! {
if #[cfg(any(target_os = "android", target_os = "linux"))] {
let segsize: u32 = 873;
assert!(initial < segsize);
setsockopt(rsock, sockopt::TcpMaxSeg, &segsize).unwrap();
} else {
assert!(initial < 700);
}
}

// Connect and check the MSS that was advertised
let ssock = socket(AddressFamily::Inet, SockType::Stream, SockFlag::empty(), SockProtocol::Tcp)
.unwrap();
connect(ssock, &sock_addr).unwrap();
let rsess = accept(rsock).unwrap();
write(rsess, b"hello").unwrap();
let actual = getsockopt(ssock, sockopt::TcpMaxSeg).unwrap();
// Actual max segment size takes header lengths into account, max IPv4 options (60 bytes) + max
// TCP options (40 bytes) are subtracted from the requested maximum as a lower boundary.
cfg_if! {
if #[cfg(any(target_os = "android", target_os = "linux"))] {
assert!((segsize - 100) <= actual);
assert!(actual <= segsize);
} else {
assert!(initial < actual);
assert!(536 < actual);
}
}
close(rsock).unwrap();
close(ssock).unwrap();
}

// 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.
Expand Down

0 comments on commit ccc1434

Please sign in to comment.