diff --git a/src/socket.rs b/src/socket.rs index 22fe858b..77d17a6a 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -1736,6 +1736,46 @@ impl Socket { } } + /// Get the value of the `IPV6_TRANSPARENT` option on this socket. + /// + /// For more information about this option, see [`set_ip_transparent_v6`]. + /// + /// [`set_ip_transparent_v6`]: Socket::set_ip_transparent_v6 + #[cfg(all(feature = "all", target_os = "linux"))] + pub fn ip_transparent_v6(&self) -> io::Result { + unsafe { + getsockopt::(self.as_raw(), sys::IPPROTO_IPV6, libc::IPV6_TRANSPARENT) + .map(|transparent| transparent != 0) + } + } + + /// Set the value of the `IPV6_TRANSPARENT` option on this socket. + /// + /// Setting this boolean option enables transparent proxying + /// on this socket. This socket option allows the calling + /// application to bind to a nonlocal IP address and operate + /// both as a client and a server with the foreign address as + /// the local endpoint. NOTE: this requires that routing be + /// set up in a way that packets going to the foreign address + /// are routed through the TProxy box (i.e., the system + /// hosting the application that employs the IPV6_TRANSPARENT + /// socket option). Enabling this socket option requires + /// superuser privileges (the `CAP_NET_ADMIN` capability). + /// + /// TProxy redirection with the iptables TPROXY target also + /// requires that this option be set on the redirected socket. + #[cfg(all(feature = "all", target_os = "linux"))] + pub fn set_ip_transparent_v6(&self, transparent: bool) -> io::Result<()> { + unsafe { + setsockopt( + self.as_raw(), + sys::IPPROTO_IPV6, + libc::IPV6_TRANSPARENT, + transparent as c_int, + ) + } + } + /// Join a multicast group using `IPV6_ADD_MEMBERSHIP` option on this socket. /// /// Some OSs use `IPV6_JOIN_GROUP` for this option. diff --git a/tests/socket.rs b/tests/socket.rs old mode 100644 new mode 100755 index cc98b8a9..5df3d3a0 --- a/tests/socket.rs +++ b/tests/socket.rs @@ -1329,6 +1329,22 @@ macro_rules! test { test!(__ Domain::IPV6, $get_fn, $set_fn($arg), $arg); } }; + // Only test using a IPv6 socket and with attributes. + ($( #[ $attr: meta ] )* IPv6 $get_fn: ident, $set_fn: ident ( $arg: expr ) ) => { + #[test] + $( #[$attr] )* + fn $get_fn() { + test!(__ Domain::IPV6, $get_fn, $set_fn($arg), $arg); + } + }; + // Only test using a IPv4 socket with attributes. + ($( #[ $attr: meta ] )* IPv4 $get_fn: ident, $set_fn: ident ( $arg: expr ) ) => { + #[test] + $( #[$attr] )* + fn $get_fn() { + test!(__ Domain::IPV4, $get_fn, $set_fn($arg), $arg); + } + }; // Internal to this macro. (__ $ty: expr, $get_fn: ident, $set_fn: ident ( $arg: expr ), $expected: expr ) => { @@ -1392,7 +1408,7 @@ test!( #[cfg(all(feature = "all", target_os = "linux"))] test!( #[ignore = "setting `IP_TRANSPARENT` requires the `CAP_NET_ADMIN` capability (works when running as root)"] - ip_transparent_v4, + IPv4 ip_transparent_v4, set_ip_transparent_v4(true) ); #[cfg(all(feature = "all", any(target_os = "fuchsia", target_os = "linux")))] @@ -1505,6 +1521,13 @@ test!(IPv6 tclass_v6, set_tclass_v6(96)); )))] test!(IPv6 recv_tclass_v6, set_recv_tclass_v6(true)); +#[cfg(all(feature = "all", target_os = "linux"))] +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) +); + #[cfg(all( feature = "all", not(any(