Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable TcpMaxSeg setsockopt option for apple targets #2603

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

hulthe
Copy link

@hulthe hulthe commented Feb 11, 2025

What does this PR do

Enables the TcpMaxSeg socket option for apple targets. I've tested this on MacOS, not sure about iOS but I imagine it would work just the same.

Here's some code I used to verify that it works:

use nix::sys::socket::{getsockopt, setsockopt, sockopt::TcpMaxSeg};
use std::{io::Write, net::TcpStream};

fn main() {
    // Open a socket to somewhere
    let mut socket = TcpStream::connect(("127.0.0.1", 1234)).unwrap();
    
    // Set the MSS
    setsockopt(&socket, TcpMaxSeg, &523).unwrap();
    
    // Read the MSS
    let maxseg = getsockopt(&socket, TcpMaxSeg).unwrap();
    println!("maxseg: {maxseg}");
    
    // Send a large amount of data and observe that the segments are not bigger than 523 bytes
    socket.write_all(&[0x77; 2000]).unwrap();
}

Checklist:

  • I have read CONTRIBUTING.md
  • I have written necessary tests and rustdoc comments
  • A change log has been added if this PR modifies nix's API

@hulthe hulthe force-pushed the tcpmaxseg-apple branch 2 times, most recently from 71d127f to 8b2c4fa Compare February 11, 2025 11:57
@@ -0,0 +1 @@
Added the TcpMaxSeg socket option for apple targets
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, this socket option was available for macOS, but for getsockopt() only. This PR adds the setsockopt() part

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, right you are. Fixed.

@SteveLauC
Copy link
Member

Could you please give this test an update as well?

#[test]
fn test_so_tcp_maxseg() {
use nix::sys::socket::{
accept, bind, connect, getsockname, listen, Backlog, SockaddrIn,
};
use nix::unistd::write;
use std::net::SocketAddrV4;
use std::str::FromStr;
let std_sa = SocketAddrV4::from_str("127.0.0.1:0").unwrap();
let mut sock_addr = SockaddrIn::from(std_sa);
let rsock = socket(
AddressFamily::Inet,
SockType::Stream,
SockFlag::empty(),
SockProtocol::Tcp,
)
.unwrap();
bind(rsock.as_raw_fd(), &sock_addr).unwrap();
sock_addr = getsockname(rsock.as_raw_fd()).unwrap();
listen(&rsock, Backlog::new(10).unwrap()).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(linux_android)] {
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.as_raw_fd(), &sock_addr).unwrap();
let rsess = accept(rsock.as_raw_fd()).unwrap();
let rsess = unsafe { OwnedFd::from_raw_fd(rsess) };
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(linux_android)] {
assert!((segsize - 100) <= actual);
assert!(actual <= segsize);
} else {
assert!(initial < actual);
assert!(536 < actual);
}
}
}

@hulthe hulthe changed the title Enable TcpMaxSeg socket option for apple targets Enable TcpMaxSeg setsockopt option for apple targets Feb 13, 2025
@hulthe
Copy link
Author

hulthe commented Feb 13, 2025

@SteveLauC I updated the test. Had to change it around a bit though since it seems MaxSeg works slightly differently on macos:

  1. Can't set MSS on a non-connected socket.
  2. Can't set the MSS on a connected socket and then read the same MSS using getsockopt on the other end.

Still I hope the test is good enough :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants