Skip to content

Match BSD-family IPv4 multicast TTL/loopback ABI to system headers#647

Closed
lopopolo wants to merge 4 commits intorust-lang:masterfrom
lopopolo:codex/bsd-multicast-abi
Closed

Match BSD-family IPv4 multicast TTL/loopback ABI to system headers#647
lopopolo wants to merge 4 commits intorust-lang:masterfrom
lopopolo:codex/bsd-multicast-abi

Conversation

@lopopolo
Copy link
Copy Markdown

@lopopolo lopopolo commented Apr 5, 2026

Refs #646.

BSD-family system headers declare IP_MULTICAST_TTL and IP_MULTICAST_LOOP as u_char / unsigned char.

socket2 was using c_int for these options on all targets. On FreeBSD 15 this is observable: std::net::UdpSocket::set_multicast_ttl_v4(258) succeeds and stores 2, while socket2::Socket::set_multicast_ttl_v4(258) returns EINVAL.

@lopopolo lopopolo force-pushed the codex/bsd-multicast-abi branch from 5e7192c to 7475821 Compare April 5, 2026 16:37
@lopopolo lopopolo marked this pull request as ready for review April 5, 2026 16:37
src/socket.rs Outdated
from!(Socket, net::UdpSocket);

#[cfg(test)]
mod tests {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can you move this to the other tests in the tests directory?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

thanks, I was wondering why there were so few unit tests in this crate 😅

I've moved this into tests/socket.rs.

src/socket.rs Outdated
#[cfg(test)]
mod tests {
#[test]
#[cfg(target_os = "freebsd")]
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Why run it on FreeBSD only?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I mentioned this in the comment I left on the original issue #646 (comment), but in case you missed it:

The regression test only is gated on for freebsd because that is the VM I happened to have available for reproduction. I can widen this cfg if you'd like so it covers all of the relevant BSD OSes.

I've widened the cfg so it tests on all relevant BSD OSes.

src/socket.rs Outdated
mod tests {
#[test]
#[cfg(target_os = "freebsd")]
fn set_multicast_ttl_v4_matches_std_for_values_above_u8_range() {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can we please fix these terrible AI generated names?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I've renamed the test to multicast_v4_bsd_abi.

src/socket.rs Outdated

let socket2_socket = Socket::new(Domain::IPV4, Type::DGRAM, Some(Protocol::UDP)).unwrap();
socket2_socket.set_multicast_ttl_v4(258).unwrap();
assert_eq!(socket2_socket.multicast_ttl_v4().unwrap(), std_ttl);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can you use the test macro for this? See the other tests.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I've factored this out into an assertion helper named assert_multicast_ttl_v4.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

To maintain the part of the test that ensures behavior matches std, the helper accepts either a socket2 Socket or a std UdpSocket via a SocketRef enum which I am using to model an "either" enum. If this isn't acceptable, please let me know what you'd prefer.

Copy link
Copy Markdown
Author

@lopopolo lopopolo left a comment

Choose a reason for hiding this comment

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

thanks for the feedback, new patch incoming.

src/socket.rs Outdated
mod tests {
#[test]
#[cfg(target_os = "freebsd")]
fn set_multicast_ttl_v4_matches_std_for_values_above_u8_range() {
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I've renamed the test to multicast_v4_bsd_abi.

src/socket.rs Outdated
#[cfg(test)]
mod tests {
#[test]
#[cfg(target_os = "freebsd")]
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I mentioned this in the comment I left on the original issue #646 (comment), but in case you missed it:

The regression test only is gated on for freebsd because that is the VM I happened to have available for reproduction. I can widen this cfg if you'd like so it covers all of the relevant BSD OSes.

I've widened the cfg so it tests on all relevant BSD OSes.

src/socket.rs Outdated
from!(Socket, net::UdpSocket);

#[cfg(test)]
mod tests {
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

thanks, I was wondering why there were so few unit tests in this crate 😅

I've moved this into tests/socket.rs.

src/socket.rs Outdated

let socket2_socket = Socket::new(Domain::IPV4, Type::DGRAM, Some(Protocol::UDP)).unwrap();
socket2_socket.set_multicast_ttl_v4(258).unwrap();
assert_eq!(socket2_socket.multicast_ttl_v4().unwrap(), std_ttl);
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I've factored this out into an assertion helper named assert_multicast_ttl_v4.

src/socket.rs Outdated

let socket2_socket = Socket::new(Domain::IPV4, Type::DGRAM, Some(Protocol::UDP)).unwrap();
socket2_socket.set_multicast_ttl_v4(258).unwrap();
assert_eq!(socket2_socket.multicast_ttl_v4().unwrap(), std_ttl);
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

To maintain the part of the test that ensures behavior matches std, the helper accepts either a socket2 Socket or a std UdpSocket via a SocketRef enum which I am using to model an "either" enum. If this isn't acceptable, please let me know what you'd prefer.

@lopopolo lopopolo force-pushed the codex/bsd-multicast-abi branch from c07d43c to 92be67d Compare April 6, 2026 21:32
@lopopolo lopopolo requested a review from Thomasdezeeuw April 6, 2026 21:40
@lopopolo
Copy link
Copy Markdown
Author

lopopolo commented Apr 6, 2026

thanks for the feedback @Thomasdezeeuw. I have tried my best to address it and believe this PR is ready for another review.

Copy link
Copy Markdown
Collaborator

@Thomasdezeeuw Thomasdezeeuw left a comment

Choose a reason for hiding this comment

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

I'm sorry, but this is not at all what I wanted and I don't want to review this AI generated code any longer.

@lopopolo
Copy link
Copy Markdown
Author

lopopolo commented Apr 7, 2026

I'm sorry, but this is not at all what I wanted and I don't want to review this AI generated code any longer.

Hi Thomas, this is disappointing to hear. Thanks for your time.

@lopopolo
Copy link
Copy Markdown
Author

lopopolo commented Apr 7, 2026

I'm sorry, but this is not at all what I wanted and I don't want to review this AI generated code any longer.

I am trying to debug what was not acceptable here and I think it is because I did not construct the tests like this:

socket2/tests/socket.rs

Lines 1602 to 1607 in 642df44

#[cfg(all(feature = "all", any(target_os = "linux", target_os = "android")))]
test!(
#[ignore = "setting `IPV6_TRANSPARENT` requires the `CAP_NET_ADMIN` capability (works when running as root)"]
IPv6 ip_transparent_v6,
set_ip_transparent_v6(true)
);

I did not find this macro because #647 (comment) was not very specific in terms of which tests to match. Additionally, @Thomasdezeeuw had been providing dismissive feedback in response to AI assistance, so I wrote the commits 92be67d and 7ddafc0 by hand.

As someone familiar to Rust but new to this crate, test macro most obviously meant #[test] and not a macro_rules! macro named test. I did find this inCONTRIBUTING.md:

Tests should be added to tests/socket.rs, following (roughly) the same order in which the methods are defined on a type. At the bottom of this file it has a macro to create a simple get/set socket option test, more complex API however needs a manually written test.

but the docs do not match the reality of the test file; the macro is defined ~600 lines before the end of the file. When scanning the tests/socket.rs source in vim, I missed it. I attempted to find something approximating an assert! macro and found several assertion helpers like this one:

socket2/tests/socket.rs

Lines 381 to 390 in 642df44

/// Assert that `CLOEXEC` is set on `socket`.
#[cfg(unix)]
#[track_caller]
pub fn assert_close_on_exec<S>(socket: &S, want: bool)
where
S: AsRawFd,
{
let flags = unsafe { libc::fcntl(socket.as_raw_fd(), libc::F_GETFD) };
assert_eq!(flags & libc::FD_CLOEXEC != 0, want, "CLOEXEC option");
}

which is what I used to model how I addressed the feedback. I also believed divergence from std was part of the bug here which warranted a dedicated test.

Based on my understanding now, I am guessing that the acceptable test would have been the following:

#[cfg(any(
    target_os = "dragonfly",
    target_os = "freebsd",
    target_os = "openbsd",
    target_os = "netbsd",
    target_os = "solaris",
    target_os = "illumos",
    target_os = "nto",
))]
test!(IPv4 multicast_ttl_v4, set_multicast_ttl_v4(258), 2);

I would have hoped that my good faith contributions could have been either:

  • given constructive review feedback with the goal of getting the code mergable.
  • merged and then fixed up by the maintainers with a followup commit.
  • written directly by the maintainers in the first place after I reported the bug initially.
  • engaged with in the context of the correctness issue this work identifies, reproduces, and fixes.

For the avoidance of doubt, this comment (and all of them on #647 and #646) are human-authored.

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