Skip to content

Commit

Permalink
feat: add support for DSCP and TTL / Hop Limit (nix-rust#2425)
Browse files Browse the repository at this point in the history
* feat: add support for DSCP and TTL / Hop Limit

* 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]>

* Add PR changelog

* This is not supported on Freebsd

* IPV6_RECVTCLASS not supported on freebsd

* Properly limit IPV6_RECVTCLASS

* Properly limit IP_TOS

* Restrict everything to target_os linux

* ...

* Protect bind

* Apply suggestions from code review

Co-authored-by: SteveLauC <[email protected]>

* Address PR comments

* Run cargo fmt

* Address further comments from PR

* Run tests under qemu

* Use libc from git

* Disable qemu IPTOS / IPV6TCLASS tests on mips

* Apply suggestions from code review

Co-authored-by: SteveLauC <[email protected]>

* Fix more code review suggestions

* Fix missing renames in tests

* Testing

* Fixes

* Fix freebsd

* Trigger CI again

* Trigger CI again

* Use the same control message in linux and freebsd for ipv4ttl

* test: remove a println

---------

Signed-off-by: Bigo <[email protected]>
Co-authored-by: SteveLauC <[email protected]>
  • Loading branch information
crisidev and SteveLauC authored Jul 22, 2024
1 parent 5940300 commit 25b437c
Show file tree
Hide file tree
Showing 6 changed files with 507 additions and 7 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ targets = [
]

[dependencies]
libc = { version = "0.2.155", features = ["extra_traits"] }
libc = { git = "https://github.com/rust-lang/libc", branch = "libc-0.2", features = ["extra_traits"] }
bitflags = "2.3.3"
cfg-if = "1.0"
pin-utils = { version = "0.1.0", optional = true }
Expand Down
2 changes: 2 additions & 0 deletions changelog/2425.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Improve support for extracting the TTL / Hop Limit from incoming packets
and support for DSCP (ToS / Traffic Class).
155 changes: 153 additions & 2 deletions src/sys/socket/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,43 @@ pub enum ControlMessageOwned {
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv6OrigDstAddr(libc::sockaddr_in6),

/// Time-to-Live (TTL) header field of the incoming IPv4 packet.
///
/// [Further reading](https://www.man7.org/linux/man-pages/man7/ip.7.html)
#[cfg(linux_android)]
#[cfg(feature = "net")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv4Ttl(i32),

/// Time-to-Live (TTL) header field of the incoming IPv4 packet.
///
/// [Further reading](https://datatracker.ietf.org/doc/html/rfc3542.html)
#[cfg(target_os = "freebsd")]
#[cfg(feature = "net")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv4Ttl(u8),

/// Hop Limit header field of the incoming IPv6 packet.
///
/// [Further reading for Linux](https://www.man7.org/linux/man-pages/man7/ip.7.html)
/// [Further reading for FreeBSD](https://datatracker.ietf.org/doc/html/rfc3542.html)
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv6HopLimit(i32),

/// Retrieve the DSCP (ToS) header field of the incoming IPv4 packet.
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv4Tos(u8),

/// Retrieve the DSCP (Traffic Class) header field of the incoming IPv6 packet.
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv6TClass(i32),

/// UDP Generic Receive Offload (GRO) allows receiving multiple UDP
/// packets from a single sender.
/// Fixed-size payloads are following one by one in a receive buffer.
Expand Down Expand Up @@ -987,6 +1024,42 @@ impl ControlMessageOwned {
let content_type = unsafe { ptr::read_unaligned(p as *const u8) };
ControlMessageOwned::TlsGetRecordType(content_type.into())
},
#[cfg(linux_android)]
#[cfg(feature = "net")]
(libc::IPPROTO_IP, libc::IP_TTL) => {
let ttl = unsafe { ptr::read_unaligned(p as *const i32) };
ControlMessageOwned::Ipv4Ttl(ttl)
},
#[cfg(target_os = "freebsd")]
#[cfg(feature = "net")]
(libc::IPPROTO_IP, libc::IP_RECVTTL) => {
let ttl: u8 = unsafe { ptr::read_unaligned(p as *const u8) };
ControlMessageOwned::Ipv4Ttl(ttl)
},
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
(libc::IPPROTO_IPV6, libc::IPV6_HOPLIMIT) => {
let ttl = unsafe { ptr::read_unaligned(p as *const i32) };
ControlMessageOwned::Ipv6HopLimit(ttl)
},
#[cfg(linux_android)]
#[cfg(feature = "net")]
(libc::IPPROTO_IP, libc::IP_TOS) => {
let tos = unsafe { ptr::read_unaligned(p as *const u8) };
ControlMessageOwned::Ipv4Tos(tos)
},
#[cfg(target_os = "freebsd")]
#[cfg(feature = "net")]
(libc::IPPROTO_IP, libc::IP_RECVTOS) => {
let tos = unsafe { ptr::read_unaligned(p as *const u8) };
ControlMessageOwned::Ipv4Tos(tos)
},
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
(libc::IPPROTO_IPV6, libc::IPV6_TCLASS) => {
let tc = unsafe { ptr::read_unaligned(p as *const i32) };
ControlMessageOwned::Ipv6TClass(tc)
},
(_, _) => {
let sl = unsafe { std::slice::from_raw_parts(p, len) };
let ucmsg = UnknownCmsg(*header, Vec::<u8>::from(sl));
Expand Down Expand Up @@ -1124,6 +1197,18 @@ pub enum ControlMessage<'a> {
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv4SendSrcAddr(&'a libc::in_addr),

/// Configure the Time-to-Live for v4 traffic.
#[cfg(linux_android)]
#[cfg(feature = "net")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv4Ttl(&'a libc::c_int),

/// Configure the Time-to-Live for v4 traffic.
#[cfg(target_os = "freebsd")]
#[cfg(feature = "net")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv4Ttl(&'a libc::c_uchar),

/// Configure the hop limit for v6 multicast traffic.
///
/// Set the IPv6 hop limit for this message. The argument is an integer
Expand All @@ -1138,9 +1223,9 @@ pub enum ControlMessage<'a> {
Ipv6HopLimit(&'a libc::c_int),

/// SO_RXQ_OVFL indicates that an unsigned 32 bit value
/// ancilliary msg (cmsg) should be attached to recieved
/// ancillary msg (cmsg) should be attached to received
/// skbs indicating the number of packets dropped by the
/// socket between the last recieved packet and this
/// socket between the last received packet and this
/// received packet.
#[cfg(any(linux_android, target_os = "fuchsia"))]
RxqOvfl(&'a u32),
Expand All @@ -1152,6 +1237,22 @@ pub enum ControlMessage<'a> {
/// page.
#[cfg(target_os = "linux")]
TxTime(&'a u64),

/// Configure DSCP / IP TOS for outgoing v4 packets.
///
/// Further information can be found [here](https://en.wikipedia.org/wiki/Differentiated_services).
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv4Tos(&'a u8),

/// Configure DSCP / IPv6 TCLASS for outgoing v6 packets.
///
/// Further information can be found [here](https://en.wikipedia.org/wiki/Differentiated_services).
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
Ipv6TClass(&'a i32),
}

// An opaque structure used to prevent cmsghdr from being a public type
Expand Down Expand Up @@ -1245,6 +1346,9 @@ impl<'a> ControlMessage<'a> {
#[cfg(any(freebsdlike, netbsdlike))]
#[cfg(feature = "net")]
ControlMessage::Ipv4SendSrcAddr(addr) => addr as *const _ as *const u8,
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
ControlMessage::Ipv4Ttl(ttl) => ttl as *const _ as *const u8,
#[cfg(any(linux_android, freebsdlike, apple_targets, target_os = "haiku"))]
#[cfg(feature = "net")]
ControlMessage::Ipv6HopLimit(limit) => limit as *const _ as *const u8,
Expand All @@ -1256,6 +1360,16 @@ impl<'a> ControlMessage<'a> {
ControlMessage::TxTime(tx_time) => {
tx_time as *const _ as *const u8
},
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
ControlMessage::Ipv4Tos(tos) => {
tos as *const _
},
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
ControlMessage::Ipv6TClass(tclass) => {
tclass as *const _ as *const u8
},
};
unsafe {
ptr::copy_nonoverlapping(
Expand Down Expand Up @@ -1307,6 +1421,11 @@ impl<'a> ControlMessage<'a> {
#[cfg(any(freebsdlike, netbsdlike))]
#[cfg(feature = "net")]
ControlMessage::Ipv4SendSrcAddr(addr) => mem::size_of_val(addr),
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
ControlMessage::Ipv4Ttl(ttl) => {
mem::size_of_val(ttl)
},
#[cfg(any(linux_android, freebsdlike, apple_targets, target_os = "haiku"))]
#[cfg(feature = "net")]
ControlMessage::Ipv6HopLimit(limit) => {
Expand All @@ -1320,6 +1439,16 @@ impl<'a> ControlMessage<'a> {
ControlMessage::TxTime(tx_time) => {
mem::size_of_val(tx_time)
},
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
ControlMessage::Ipv4Tos(tos) => {
mem::size_of_val(tos)
},
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
ControlMessage::Ipv6TClass(tclass) => {
mem::size_of_val(tclass)
},
}
}

Expand Down Expand Up @@ -1347,13 +1476,22 @@ impl<'a> ControlMessage<'a> {
#[cfg(any(freebsdlike, netbsdlike))]
#[cfg(feature = "net")]
ControlMessage::Ipv4SendSrcAddr(_) => libc::IPPROTO_IP,
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
ControlMessage::Ipv4Ttl(_) => libc::IPPROTO_IP,
#[cfg(any(linux_android, freebsdlike, apple_targets, target_os = "haiku"))]
#[cfg(feature = "net")]
ControlMessage::Ipv6HopLimit(_) => libc::IPPROTO_IPV6,
#[cfg(any(linux_android, target_os = "fuchsia"))]
ControlMessage::RxqOvfl(_) => libc::SOL_SOCKET,
#[cfg(target_os = "linux")]
ControlMessage::TxTime(_) => libc::SOL_SOCKET,
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
ControlMessage::Ipv4Tos(_) => libc::IPPROTO_IP,
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
ControlMessage::Ipv6TClass(_) => libc::IPPROTO_IPV6,
}
}

Expand Down Expand Up @@ -1392,6 +1530,9 @@ impl<'a> ControlMessage<'a> {
#[cfg(any(freebsdlike, netbsdlike))]
#[cfg(feature = "net")]
ControlMessage::Ipv4SendSrcAddr(_) => libc::IP_SENDSRCADDR,
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
ControlMessage::Ipv4Ttl(_) => libc::IP_TTL,
#[cfg(any(linux_android, freebsdlike, apple_targets, target_os = "haiku"))]
#[cfg(feature = "net")]
ControlMessage::Ipv6HopLimit(_) => libc::IPV6_HOPLIMIT,
Expand All @@ -1403,6 +1544,16 @@ impl<'a> ControlMessage<'a> {
ControlMessage::TxTime(_) => {
libc::SCM_TXTIME
},
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
ControlMessage::Ipv4Tos(_) => {
libc::IP_TOS
},
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
ControlMessage::Ipv6TClass(_) => {
libc::IPV6_TCLASS
},
}
}

Expand Down
48 changes: 46 additions & 2 deletions src/sys/socket/sockopt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
/// Set or receive the Type-Of-Service (TOS) field that is
/// sent with every IP packet originating from this socket
IpTos,
Ipv4Tos,
Both,
libc::IPPROTO_IP,
libc::IP_TOS,
Expand All @@ -418,13 +418,35 @@ sockopt_impl!(
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
/// Traffic class associated with outgoing packets
/// If enabled, the IP_TOS ancillary message is passed with incoming packets.
IpRecvTos,
Both,
libc::IPPROTO_IP,
libc::IP_RECVTOS,
bool
);
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
/// Set the traffic class associated with outgoing packets.
Ipv6TClass,
Both,
libc::IPPROTO_IPV6,
libc::IPV6_TCLASS,
libc::c_int
);
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
/// If enabled, the IPV6_TCLASS ancillary message is passed with incoming packets.
Ipv6RecvTClass,
Both,
libc::IPPROTO_IPV6,
libc::IPV6_RECVTCLASS,
bool
);
#[cfg(any(linux_android, target_os = "fuchsia"))]
#[cfg(feature = "net")]
sockopt_impl!(
Expand Down Expand Up @@ -1058,6 +1080,17 @@ sockopt_impl!(
libc::IP_TTL,
libc::c_int
);
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
sockopt_impl!(
/// Enables a receiving socket to retrieve the Time-to-Live (TTL) field
/// from incoming IPv4 packets.
Ipv4RecvTtl,
Both,
libc::IPPROTO_IP,
libc::IP_RECVTTL,
bool
);
#[cfg(any(apple_targets, linux_android, target_os = "freebsd"))]
sockopt_impl!(
/// Set the unicast hop limit for the socket.
Expand All @@ -1069,6 +1102,17 @@ sockopt_impl!(
);
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
sockopt_impl!(
/// Enables a receiving socket to retrieve the Hop Limit field
/// (similar to TTL in IPv4) from incoming IPv6 packets.
Ipv6RecvHopLimit,
Both,
libc::IPPROTO_IPV6,
libc::IPV6_RECVHOPLIMIT,
bool
);
#[cfg(any(linux_android, target_os = "freebsd"))]
#[cfg(feature = "net")]
sockopt_impl!(
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
/// The `recvmsg(2)` call will return the destination IP address for a UDP
Expand Down
Loading

0 comments on commit 25b437c

Please sign in to comment.