net: add support for TCLASS option on IPv6 (#7781)

Co-authored-by: Thomas de Zeeuw <[email protected]>
This commit is contained in:
Jan Tojnar
2025-12-29 12:38:26 +08:00
committed by GitHub
co-authored by Thomas de Zeeuw
parent 0a3e386269
commit 7388f2d2ea
4 changed files with 468 additions and 23 deletions
+139 -10
View File
@@ -489,15 +489,113 @@ impl TcpSocket {
self.inner.tcp_nodelay()
}
/// Gets the value of the `IPV6_TCLASS` option for this socket.
///
/// For more information about this option, see [`set_tclass_v6`].
///
/// [`set_tclass_v6`]: Self::set_tclass_v6
// https://docs.rs/socket2/0.6.1/src/socket2/sys/unix.rs.html#2541
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "fuchsia",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "cygwin",
))]
#[cfg_attr(
docsrs,
doc(cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "fuchsia",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "cygwin",
)))
)]
pub fn tclass_v6(&self) -> io::Result<u32> {
self.inner.tclass_v6()
}
/// Sets the value for the `IPV6_TCLASS` option on this socket.
///
/// Specifies the traffic class field that is used in every packet
/// sent from this socket.
///
/// # Note
///
/// This may not have any effect on IPv4 sockets.
// https://docs.rs/socket2/0.6.1/src/socket2/sys/unix.rs.html#2566
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "fuchsia",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "cygwin",
))]
#[cfg_attr(
docsrs,
doc(cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "fuchsia",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "cygwin",
)))
)]
pub fn set_tclass_v6(&self, tclass: u32) -> io::Result<()> {
self.inner.set_tclass_v6(tclass)
}
/// Gets the value of the `IP_TOS` option for this socket.
///
/// For more information about this option, see [`set_tos`].
/// For more information about this option, see [`set_tos_v4`].
///
/// **NOTE:** On Windows, `IP_TOS` is only supported on [Windows 8+ or
/// Windows Server 2012+.](https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options)
/// [`set_tos_v4`]: Self::set_tos_v4
// https://docs.rs/socket2/0.6.1/src/socket2/socket.rs.html#1585
#[cfg(not(any(
target_os = "fuchsia",
target_os = "redox",
target_os = "solaris",
target_os = "illumos",
target_os = "haiku"
)))]
#[cfg_attr(
docsrs,
doc(cfg(not(any(
target_os = "fuchsia",
target_os = "redox",
target_os = "solaris",
target_os = "illumos",
target_os = "haiku"
))))
)]
pub fn tos_v4(&self) -> io::Result<u32> {
self.inner.tos_v4()
}
/// Deprecated. Use [`tos_v4()`] instead.
///
/// [`set_tos`]: Self::set_tos
// https://docs.rs/socket2/0.5.3/src/socket2/socket.rs.html#1464
/// [`tos_v4()`]: Self::tos_v4
#[deprecated(
note = "`tos` related methods have been renamed `tos_v4` since they are IPv4-specific."
)]
#[doc(hidden)]
#[cfg(not(any(
target_os = "fuchsia",
target_os = "redox",
@@ -516,7 +614,7 @@ impl TcpSocket {
))))
)]
pub fn tos(&self) -> io::Result<u32> {
self.inner.tos_v4()
self.tos_v4()
}
/// Sets the value for the `IP_TOS` option on this socket.
@@ -524,9 +622,40 @@ impl TcpSocket {
/// This value sets the type-of-service field that is used in every packet
/// sent from this socket.
///
/// **NOTE:** On Windows, `IP_TOS` is only supported on [Windows 8+ or
/// Windows Server 2012+.](https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options)
// https://docs.rs/socket2/0.5.3/src/socket2/socket.rs.html#1446
/// # Note
///
/// - This may not have any effect on IPv6 sockets.
/// - On Windows, `IP_TOS` is only supported on [Windows 8+ or
/// Windows Server 2012+.](https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options)
// https://docs.rs/socket2/0.6.1/src/socket2/socket.rs.html#1566
#[cfg(not(any(
target_os = "fuchsia",
target_os = "redox",
target_os = "solaris",
target_os = "illumos",
target_os = "haiku"
)))]
#[cfg_attr(
docsrs,
doc(cfg(not(any(
target_os = "fuchsia",
target_os = "redox",
target_os = "solaris",
target_os = "illumos",
target_os = "haiku"
))))
)]
pub fn set_tos_v4(&self, tos: u32) -> io::Result<()> {
self.inner.set_tos_v4(tos)
}
/// Deprecated. Use [`set_tos_v4()`] instead.
///
/// [`set_tos_v4()`]: Self::set_tos_v4
#[deprecated(
note = "`tos` related methods have been renamed `tos_v4` since they are IPv4-specific."
)]
#[doc(hidden)]
#[cfg(not(any(
target_os = "fuchsia",
target_os = "redox",
@@ -545,7 +674,7 @@ impl TcpSocket {
))))
)]
pub fn set_tos(&self, tos: u32) -> io::Result<()> {
self.inner.set_tos_v4(tos)
self.set_tos_v4(tos)
}
/// Gets the value for the `SO_BINDTODEVICE` option on this socket
+142 -13
View File
@@ -1908,7 +1908,7 @@ impl UdpSocket {
///
/// # Note
///
/// This may not have any affect on IPv6 sockets.
/// This may not have any effect on IPv6 sockets.
pub fn set_multicast_loop_v4(&self, on: bool) -> io::Result<()> {
self.io.set_multicast_loop_v4(on)
}
@@ -1930,7 +1930,7 @@ impl UdpSocket {
///
/// # Note
///
/// This may not have any affect on IPv6 sockets.
/// This may not have any effect on IPv6 sockets.
pub fn set_multicast_ttl_v4(&self, ttl: u32) -> io::Result<()> {
self.io.set_multicast_ttl_v4(ttl)
}
@@ -1950,11 +1950,84 @@ impl UdpSocket {
///
/// # Note
///
/// This may not have any affect on IPv4 sockets.
/// This may not have any effect on IPv4 sockets.
pub fn set_multicast_loop_v6(&self, on: bool) -> io::Result<()> {
self.io.set_multicast_loop_v6(on)
}
/// Gets the value of the `IPV6_TCLASS` option for this socket.
///
/// For more information about this option, see [`set_tclass_v6`].
///
/// [`set_tclass_v6`]: Self::set_tclass_v6
// https://docs.rs/socket2/0.6.1/src/socket2/sys/unix.rs.html#2541
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "fuchsia",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "cygwin",
))]
#[cfg_attr(
docsrs,
doc(cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "fuchsia",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "cygwin",
)))
)]
pub fn tclass_v6(&self) -> io::Result<u32> {
self.as_socket().tclass_v6()
}
/// Sets the value for the `IPV6_TCLASS` option on this socket.
///
/// Specifies the traffic class field that is used in every packet
/// sent from this socket.
///
/// # Note
///
/// This may not have any effect on IPv4 sockets.
// https://docs.rs/socket2/0.6.1/src/socket2/sys/unix.rs.html#2566
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "fuchsia",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "cygwin",
))]
#[cfg_attr(
docsrs,
doc(cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "fuchsia",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "cygwin",
)))
)]
pub fn set_tclass_v6(&self, tclass: u32) -> io::Result<()> {
self.as_socket().set_tclass_v6(tclass)
}
/// Gets the value of the `IP_TTL` option for this socket.
///
/// For more information about this option, see [`set_ttl`].
@@ -2002,13 +2075,38 @@ impl UdpSocket {
/// Gets the value of the `IP_TOS` option for this socket.
///
/// For more information about this option, see [`set_tos`].
/// For more information about this option, see [`set_tos_v4`].
///
/// **NOTE:** On Windows, `IP_TOS` is only supported on [Windows 8+ or
/// Windows Server 2012+.](https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options)
/// [`set_tos_v4`]: Self::set_tos_v4
// https://docs.rs/socket2/0.6.1/src/socket2/socket.rs.html#1585
#[cfg(not(any(
target_os = "fuchsia",
target_os = "redox",
target_os = "solaris",
target_os = "illumos",
target_os = "haiku"
)))]
#[cfg_attr(
docsrs,
doc(cfg(not(any(
target_os = "fuchsia",
target_os = "redox",
target_os = "solaris",
target_os = "illumos",
target_os = "haiku"
))))
)]
pub fn tos_v4(&self) -> io::Result<u32> {
self.as_socket().tos_v4()
}
/// Deprecated. Use [`tos_v4()`] instead.
///
/// [`set_tos`]: Self::set_tos
// https://docs.rs/socket2/0.5.3/src/socket2/socket.rs.html#1464
/// [`tos_v4()`]: Self::tos_v4
#[deprecated(
note = "`tos` related methods have been renamed `tos_v4` since they are IPv4-specific."
)]
#[doc(hidden)]
#[cfg(not(any(
target_os = "fuchsia",
target_os = "redox",
@@ -2027,7 +2125,7 @@ impl UdpSocket {
))))
)]
pub fn tos(&self) -> io::Result<u32> {
self.as_socket().tos_v4()
self.tos_v4()
}
/// Sets the value for the `IP_TOS` option on this socket.
@@ -2035,9 +2133,40 @@ impl UdpSocket {
/// This value sets the type-of-service field that is used in every packet
/// sent from this socket.
///
/// **NOTE:** On Windows, `IP_TOS` is only supported on [Windows 8+ or
/// Windows Server 2012+.](https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options)
// https://docs.rs/socket2/0.5.3/src/socket2/socket.rs.html#1446
/// # Note
///
/// - This may not have any effect on IPv6 sockets.
/// - On Windows, `IP_TOS` is only supported on [Windows 8+ or
/// Windows Server 2012+.](https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options)
// https://docs.rs/socket2/0.6.1/src/socket2/socket.rs.html#1566
#[cfg(not(any(
target_os = "fuchsia",
target_os = "redox",
target_os = "solaris",
target_os = "illumos",
target_os = "haiku"
)))]
#[cfg_attr(
docsrs,
doc(cfg(not(any(
target_os = "fuchsia",
target_os = "redox",
target_os = "solaris",
target_os = "illumos",
target_os = "haiku"
))))
)]
pub fn set_tos_v4(&self, tos: u32) -> io::Result<()> {
self.as_socket().set_tos_v4(tos)
}
/// Deprecated. Use [`set_tos_v4()`] instead.
///
/// [`set_tos_v4()`]: Self::set_tos_v4
#[deprecated(
note = "`tos` related methods have been renamed `tos_v4` since they are IPv4-specific."
)]
#[doc(hidden)]
#[cfg(not(any(
target_os = "fuchsia",
target_os = "redox",
@@ -2056,7 +2185,7 @@ impl UdpSocket {
))))
)]
pub fn set_tos(&self, tos: u32) -> io::Result<()> {
self.as_socket().set_tos_v4(tos)
self.set_tos_v4(tos)
}
/// Gets the value for the `SO_BINDTODEVICE` option on this socket
+109
View File
@@ -74,3 +74,112 @@ async fn basic_linger() {
srv.set_linger(Some(Duration::new(0, 0))).unwrap();
assert_eq!(srv.linger().unwrap(), Some(Duration::new(0, 0)));
}
/// Macro to create a simple test to set and get a socket option.
macro_rules! test {
// Test using the `arg`ument as expected return value.
($( #[ $attr: meta ] )* $get_fn: ident, $set_fn: ident ( $arg: expr ) ) => {
test!($( #[$attr] )* $get_fn, $set_fn($arg), $arg);
};
($( #[ $attr: meta ] )* $get_fn: ident, $set_fn: ident ( $arg: expr ), $expected: expr ) => {
#[test]
$( #[$attr] )*
fn $get_fn() {
test!(__ new_v4, $get_fn, $set_fn($arg), $expected);
#[cfg(not(target_os = "vita"))]
test!(__ new_v6, $get_fn, $set_fn($arg), $expected);
}
};
// Only test using a IPv4 socket.
(IPv4 $get_fn: ident, $set_fn: ident ( $arg: expr ) ) => {
#[test]
fn $get_fn() {
test!(__ new_v4, $get_fn, $set_fn($arg), $arg);
}
};
// Only test using a IPv6 socket.
(IPv6 $get_fn: ident, $set_fn: ident ( $arg: expr ) ) => {
#[test]
fn $get_fn() {
test!(__ new_v6, $get_fn, $set_fn($arg), $arg);
}
};
// Internal to this macro.
(__ $constructor: ident, $get_fn: ident, $set_fn: ident ( $arg: expr ), $expected: expr ) => {
let socket = TcpSocket::$constructor().expect("failed to create `TcpSocket`");
let initial = socket.$get_fn().expect("failed to get initial value");
let arg = $arg;
assert_ne!(initial, arg, "initial value and argument are the same");
socket.$set_fn(arg).expect("failed to set option");
let got = socket.$get_fn().expect("failed to get value");
let expected = $expected;
assert_eq!(got, expected, "set and get values differ");
};
}
const SET_BUF_SIZE: u32 = 4096;
// Linux doubles the buffer size for kernel usage, and exposes that when
// retrieving the buffer size.
#[cfg(not(target_os = "linux"))]
const GET_BUF_SIZE: u32 = SET_BUF_SIZE;
#[cfg(target_os = "linux")]
const GET_BUF_SIZE: u32 = 2 * SET_BUF_SIZE;
test!(keepalive, set_keepalive(true));
test!(reuseaddr, set_reuseaddr(true));
#[cfg(all(
unix,
not(target_os = "solaris"),
not(target_os = "illumos"),
not(target_os = "cygwin"),
))]
test!(reuseport, set_reuseport(true));
test!(
send_buffer_size,
set_send_buffer_size(SET_BUF_SIZE),
GET_BUF_SIZE
);
test!(
recv_buffer_size,
set_recv_buffer_size(SET_BUF_SIZE),
GET_BUF_SIZE
);
test!(
#[expect(deprecated, reason = "set_linger is deprecated")]
linger,
set_linger(Some(Duration::from_secs(10)))
);
test!(nodelay, set_nodelay(true));
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "fuchsia",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "cygwin",
))]
test!(IPv6 tclass_v6, set_tclass_v6(96));
#[cfg(not(any(
target_os = "fuchsia",
target_os = "redox",
target_os = "solaris",
target_os = "illumos",
target_os = "haiku"
)))]
test!(IPv4 tos_v4, set_tos_v4(96));
+78
View File
@@ -644,3 +644,81 @@ async fn poll_ready() {
}
}
}
/// Macro to create a simple test to set and get a socket option.
macro_rules! test {
// Test using the `arg`ument as expected return value.
($( #[ $attr: meta ] )* $get_fn: ident, $set_fn: ident ( $arg: expr ) ) => {
test!($( #[$attr] )* $get_fn, $set_fn($arg), $arg);
};
($( #[ $attr: meta ] )* $get_fn: ident, $set_fn: ident ( $arg: expr ), $expected: expr ) => {
#[tokio::test]
$( #[$attr] )*
async fn $get_fn() {
test!(__ "127.0.0.1:0", $get_fn, $set_fn($arg), $expected);
#[cfg(not(target_os = "vita"))]
test!(__ "[::1]:0", $get_fn, $set_fn($arg), $expected);
}
};
// Only test using a IPv4 socket.
(IPv4 $get_fn: ident, $set_fn: ident ( $arg: expr ) ) => {
#[tokio::test]
async fn $get_fn() {
test!(__ "127.0.0.1:0", $get_fn, $set_fn($arg), $arg);
}
};
// Only test using a IPv6 socket.
(IPv6 $get_fn: ident, $set_fn: ident ( $arg: expr ) ) => {
#[tokio::test]
async fn $get_fn() {
test!(__ "[::1]:0", $get_fn, $set_fn($arg), $arg);
}
};
// Internal to this macro.
(__ $addr: literal, $get_fn: ident, $set_fn: ident ( $arg: expr ), $expected: expr ) => {
let socket = UdpSocket::bind($addr).await.expect("failed to create `UdpSocket`");
let initial = socket.$get_fn().expect("failed to get initial value");
let arg = $arg;
assert_ne!(initial, arg, "initial value and argument are the same");
socket.$set_fn(arg).expect("failed to set option");
let got = socket.$get_fn().expect("failed to get value");
let expected = $expected;
assert_eq!(got, expected, "set and get values differ");
};
}
test!(broadcast, set_broadcast(true));
test!(IPv4 multicast_loop_v4, set_multicast_loop_v4(false));
#[cfg(target_os = "linux")] // broken on non-Linux platforms https://github.com/rust-lang/socket2/pull/630
test!(multicast_ttl_v4, set_multicast_ttl_v4(40));
test!(IPv6 multicast_loop_v6, set_multicast_loop_v6(false));
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "fuchsia",
target_os = "linux",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "cygwin",
))]
test!(IPv6 tclass_v6, set_tclass_v6(96));
test!(IPv4 ttl, set_ttl(40));
#[cfg(not(any(
target_os = "fuchsia",
target_os = "redox",
target_os = "solaris",
target_os = "illumos",
target_os = "haiku"
)))]
test!(IPv4 tos_v4, set_tos_v4(96));