From 76fe409f4b83573d3c706b530e4b0f937cc0b513 Mon Sep 17 00:00:00 2001 From: eaon Date: Wed, 25 Aug 2021 15:27:33 -0400 Subject: [PATCH] Support for `TCP_MAXSEG` TCP MSS socket option --- CHANGELOG.md | 2 ++ src/sys/socket/sockopt.rs | 7 ++++++ test/sys/test_sockopt.rs | 51 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5bc282dc7..d01351f4cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,8 @@ This project adheres to [Semantic Versioning](https://semver.org/). (#[1335](https://github.com/nix-rust/nix/pull/1335)) - Exposed `SockAddr::from_raw_sockaddr` (#[1447](https://github.com/nix-rust/nix/pull/1447)) +- Added support for `TCP_MAXSEG` TCP Maximum Segment Size socket options + (#[1292](https://github.com/nix-rust/nix/pull/1292)) ### Changed diff --git a/src/sys/socket/sockopt.rs b/src/sys/socket/sockopt.rs index ca8fb1a0f2..90f3cb3dd8 100644 --- a/src/sys/socket/sockopt.rs +++ b/src/sys/socket/sockopt.rs @@ -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(not(target_os = "openbsd"))] diff --git a/test/sys/test_sockopt.rs b/test/sys/test_sockopt.rs index 4d2d7e515d..72c9a3151f 100644 --- a/test/sys/test_sockopt.rs +++ b/test/sys/test_sockopt.rs @@ -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.