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

feat: add support for DSCP and TTL / Hop Limit #2425

Draft
wants to merge 12 commits into
base: master
Choose a base branch
from

Conversation

crisidev
Copy link

@crisidev crisidev commented Jun 3, 2024

What does this PR do

This PR improves the support for setting and retrieving of IP headers values for

  • Incoming packets IPv4 TTL (IP_RECVTTL)
  • Incoming packets IPv6 Hop Limit (IPV6_RECVHOPLIMIT)
  • Outgoing and incoming packets IPv4 DSCP (IP_TOS)
  • Outgoing and incoming packets IPv6 DSCP (IPV6_RECVTCLASS)

It includes the socket options and the appropriate control messages for sendmsg and recvmsg.

Note: I am not 100% sure I understand correctly the cfg values and feature flags of nix, so they could be set wrongly in the PR.

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

* Support IP_RECVTTL and IPV6_RECVHOPLIMIT socket options
and related control messages for recvmsg.
* Support setting DSCP in control messages for both sendmsg
and recvmsg.

Signed-off-by: Bigo <[email protected]>
@crisidev crisidev marked this pull request as ready for review June 3, 2024 17:45
@crisidev crisidev marked this pull request as draft June 3, 2024 18:07
@crisidev crisidev marked this pull request as ready for review June 4, 2024 14:55
src/sys/socket/sockopt.rs Outdated Show resolved Hide resolved
src/sys/socket/sockopt.rs Outdated Show resolved Hide resolved
src/sys/socket/sockopt.rs Outdated Show resolved Hide resolved
src/sys/socket/sockopt.rs Outdated Show resolved Hide resolved
@@ -987,6 +1013,30 @@ impl ControlMessageOwned {
let content_type = unsafe { ptr::read_unaligned(p as *const u8) };
ControlMessageOwned::TlsGetRecordType(content_type.into())
},
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
(libc::IPPROTO_IP, libc::IP_TTL) => {
Copy link
Member

Choose a reason for hiding this comment

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

On FreeBSD (and other BSDs, maybe), this should be:

Suggested change
(libc::IPPROTO_IP, libc::IP_TTL) => {
(libc::IPPROTO_IP, libc::IP_RECVTTL) => {

Copy link
Member

@SteveLauC SteveLauC Jun 15, 2024

Choose a reason for hiding this comment

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

Sorry for being unclear, on FreeBSD, this should check IP_RECVTTL, on Linux, IP_TTL should be used.

Is the suggestion to have 2 different blocks, one for Linux and one for FreeBSD?

Yes

This is documented in man 7 ip:

Some BSD sockets implementations also provide an IP_RECVTTL option, but an ancillary message with type IP_RECVTTL is passed with the incoming packet. This is different from the IP_TTL option used in Linux.

also in the FreeBSD manual:

If the IP_RECVTTL option is enabled on a SOCK_DGRAM socket, the
recvmsg(2) call will return the IP TTL (time to live) field for a UDP
datagram. The msg_control field in the msghdr structure points to a
buffer that contains a cmsghdr structure followed by the TTL. The cms-
ghdr fields have the following values:

  cmsg_len	= CMSG_LEN(sizeof(u_char))
  cmsg_level = IPPROTO_IP
  cmsg_type = IP_RECVTTL

src/sys/socket/mod.rs Outdated Show resolved Hide resolved
src/sys/socket/mod.rs Outdated Show resolved Hide resolved
src/sys/socket/mod.rs Show resolved Hide resolved
test/sys/test_socket.rs Show resolved Hide resolved
@@ -2544,6 +2544,166 @@ fn test_recvmsg_rxq_ovfl() {
assert_eq!(drop_counter, 1);
}

#[cfg(target_os = "linux")]
#[cfg(feature = "net")]
#[cfg_attr(qemu, ignore)]
Copy link
Member

@SteveLauC SteveLauC Jun 10, 2024

Choose a reason for hiding this comment

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

So these tests do not work under QEMU? Nix's cross CI tests use qemu under the hood

Copy link
Author

Choose a reason for hiding this comment

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

I think some of them (maybe all) will probably work.. I'll validate once I am done with addressing your comments. If they do not work, I'll try to document why.

@SteveLauC
Copy link
Member

SteveLauC commented Jun 10, 2024

Hi, thanks for the PR! Sorry for the late response!

Note: I am not 100% sure I understand correctly the cfg values and feature flags of nix, so they could be set wrongly in the PR.

For the cfg for targets (OSes), see

nix/build.rs

Lines 20 to 27 in 5fde28e

// cfg aliases we would like to use
apple_targets: { any(ios, macos, watchos, tvos, visionos) },
bsd: { any(freebsd, dragonfly, netbsd, openbsd, apple_targets) },
bsd_without_apple: { any(freebsd, dragonfly, netbsd, openbsd) },
linux_android: { any(android, linux) },
freebsdlike: { any(dragonfly, freebsd) },
netbsdlike: { any(netbsd, openbsd) },
solarish: { any(illumos, solaris) },

@crisidev
Copy link
Author

Hi, thanks for the PR! Sorry for the late response!

Thanks a lot for the review and don't worry about the timing :)

I'll address the comments and publish a new version!

Note: I am not 100% sure I understand correctly the cfg values and feature flags of nix, so they could be set wrongly in the PR.

For the cfg for targets (OSes), see

nix/build.rs

Lines 20 to 27 in 5fde28e

// cfg aliases we would like to use
apple_targets: { any(ios, macos, watchos, tvos, visionos) },
bsd: { any(freebsd, dragonfly, netbsd, openbsd, apple_targets) },
bsd_without_apple: { any(freebsd, dragonfly, netbsd, openbsd) },
linux_android: { any(android, linux) },
freebsdlike: { any(dragonfly, freebsd) },
netbsdlike: { any(netbsd, openbsd) },
solarish: { any(illumos, solaris) },

@crisidev
Copy link
Author

The code we have right now does not build on freebsd 14 because

error[E0531]: cannot find unit struct, unit variant or constant `IP_RECVTTL` in crate `libc`
    --> src/sys/socket/mod.rs:1018:38
     |
1018 |             (libc::IPPROTO_IP, libc::IP_RECVTTL) => {
     |                                      ^^^^^^^^^^ help: a constant with a similar name exists: `IP_RECVTOS`
     |
    ::: /.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.155/src/unix/bsd/freebsdlike/freebsd/mod.rs:3685:1
     |
3685 | pub const IP_RECVTOS: ::c_int = 68;
     | ----------------------------- similarly named constant `IP_RECVTOS` defined here

I think we need to enable RECVTTL only on linux.

@SteveLauC
Copy link
Member

SteveLauC commented Jun 15, 2024

The code we have right now does not build on freebsd 14 because

error[E0531]: cannot find unit struct, unit variant or constant `IP_RECVTTL` in crate `libc`
    --> src/sys/socket/mod.rs:1018:38
     |
1018 |             (libc::IPPROTO_IP, libc::IP_RECVTTL) => {
     |                                      ^^^^^^^^^^ help: a constant with a similar name exists: `IP_RECVTOS`
     |
    ::: /.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.155/src/unix/bsd/freebsdlike/freebsd/mod.rs:3685:1
     |
3685 | pub const IP_RECVTOS: ::c_int = 68;
     | ----------------------------- similarly named constant `IP_RECVTOS` defined here

I think we need to enable RECVTTL only on linux.

On FreeBSD, IP_RECVTTL should be used, it is available on FreeBSD, our CI says it does not exist because the Rust libc crate does not include it, we need to add this constant to the libc crate.

Update: PR filed rust-lang/libc#3750

Update: PR merged, we can use the libc from git now:

libc = { git = "https://github.com/rust-lang/libc", branch = "libc-0.2", features = ["extra_traits"] }

@@ -406,7 +406,7 @@ sockopt_impl!(
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
/// Set or receive the Type-Of-Service (TOS) field that is
/// Set or receivethe Type-Of-Service (TOS) field that is
Copy link
Member

Choose a reason for hiding this comment

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

Need a blank space here:

Suggested change
/// Set or receivethe Type-Of-Service (TOS) field that is
/// Set or receive the Type-Of-Service (TOS) field that is

@@ -987,6 +1013,30 @@ impl ControlMessageOwned {
let content_type = unsafe { ptr::read_unaligned(p as *const u8) };
ControlMessageOwned::TlsGetRecordType(content_type.into())
},
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
(libc::IPPROTO_IP, libc::IP_TTL) => {
Copy link
Member

@SteveLauC SteveLauC Jun 15, 2024

Choose a reason for hiding this comment

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

Sorry for being unclear, on FreeBSD, this should check IP_RECVTTL, on Linux, IP_TTL should be used.

Is the suggestion to have 2 different blocks, one for Linux and one for FreeBSD?

Yes

This is documented in man 7 ip:

Some BSD sockets implementations also provide an IP_RECVTTL option, but an ancillary message with type IP_RECVTTL is passed with the incoming packet. This is different from the IP_TTL option used in Linux.

also in the FreeBSD manual:

If the IP_RECVTTL option is enabled on a SOCK_DGRAM socket, the
recvmsg(2) call will return the IP TTL (time to live) field for a UDP
datagram. The msg_control field in the msghdr structure points to a
buffer that contains a cmsghdr structure followed by the TTL. The cms-
ghdr fields have the following values:

  cmsg_len	= CMSG_LEN(sizeof(u_char))
  cmsg_level = IPPROTO_IP
  cmsg_type = IP_RECVTTL

},
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
(libc::IPPROTO_IP, libc::IP_TOS) => {
Copy link
Member

Choose a reason for hiding this comment

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

That's right, sorry for being unclear.

@@ -1137,7 +1193,7 @@ pub enum ControlMessage<'a> {
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv6HopLimit(&'a libc::c_int),

/// SO_RXQ_OVFL indicates that an unsigned 32 bit value
/// SO_RXQ_OVFL indicates that an unsigned 32 bit value
Copy link
Member

Choose a reason for hiding this comment

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

A change made by accident:)

@crisidev
Copy link
Author

I am moving this back to draft, while I find some time to finish to implement the changes and fix all the tests.

@crisidev crisidev marked this pull request as draft June 17, 2024 11:50
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.

None yet

2 participants