From 51e9773124751b6bb865988824328b9aefef471f Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Wed, 26 Feb 2025 11:42:22 -0800 Subject: [PATCH 1/6] Enable `IP_BOUND_IF` on illumos and Solaris The `IP_BOUND_IF` socket option, which is wrapped by the `Socket::bind_device_by_index_{v4,v6}` and `Socket::device_index_{v4,v6}` is available on SunOS-like systems, such as illumos and Solaris, as well as macOS-like systems. However, these APIs are currently cfg-flagged to only be available on macOS-like systems. This commit changes the cfg attributes to also enable these APIs on illumos and Solaris. Fixes #560 --- src/sys/unix.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/sys/unix.rs b/src/sys/unix.rs index c187d517..37d0c3a6 100644 --- a/src/sys/unix.rs +++ b/src/sys/unix.rs @@ -22,6 +22,8 @@ use std::net::{Ipv4Addr, Ipv6Addr}; target_os = "macos", target_os = "tvos", target_os = "watchos", + target_os = "illumos", + target_os = "solaris", ) ))] use std::num::NonZeroU32; @@ -1834,6 +1836,8 @@ impl crate::Socket { target_os = "macos", target_os = "tvos", target_os = "watchos", + target_os = "illumos", + target_os = "solaris", ) ))] pub fn bind_device_by_index_v4(&self, interface: Option) -> io::Result<()> { @@ -1859,6 +1863,8 @@ impl crate::Socket { target_os = "macos", target_os = "tvos", target_os = "watchos", + target_os = "illumos", + target_os = "solaris", ) ))] pub fn bind_device_by_index_v6(&self, interface: Option) -> io::Result<()> { @@ -1879,6 +1885,8 @@ impl crate::Socket { target_os = "macos", target_os = "tvos", target_os = "watchos", + target_os = "illumos", + target_os = "solaris", ) ))] pub fn device_index_v4(&self) -> io::Result> { @@ -1900,6 +1908,8 @@ impl crate::Socket { target_os = "macos", target_os = "tvos", target_os = "watchos", + target_os = "illumos", + target_os = "solaris", ) ))] pub fn device_index_v6(&self) -> io::Result> { From a5cb64793a245daa2060fe3122c1f4f325cd376e Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Wed, 26 Feb 2025 12:21:14 -0800 Subject: [PATCH 2/6] temp workaround for missing libc defs --- src/sys/unix.rs | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/src/sys/unix.rs b/src/sys/unix.rs index 37d0c3a6..ff887e2a 100644 --- a/src/sys/unix.rs +++ b/src/sys/unix.rs @@ -1391,6 +1391,29 @@ pub(crate) fn original_dst_ipv6(fd: Socket) -> io::Result { .map(|(_, addr)| addr) } +// TODO(eliza): use libc's definition on solarish once it merges... +#[cfg(any(target_os = "illumos", target_os = "solaris",))] +const IP_BOUND_IF: libc::c_int = 0x41; +#[cfg(any( + target_os = "ios", + target_os = "visionos", + target_os = "macos", + target_os = "tvos", + target_os = "watchos", +))] +use libc::IP_BOUND_IF; + +#[cfg(any(target_os = "illumos", target_os = "solaris",))] +const IPV6_BOUND_IF: libc::c_int = 0x41; +#[cfg(any( + target_os = "ios", + target_os = "visionos", + target_os = "macos", + target_os = "tvos", + target_os = "watchos", +))] +use libc::IPV6_BOUND_IF; + /// Unix only API. impl crate::Socket { /// Accept a new incoming connection from this listener. @@ -1842,7 +1865,7 @@ impl crate::Socket { ))] pub fn bind_device_by_index_v4(&self, interface: Option) -> io::Result<()> { let index = interface.map_or(0, NonZeroU32::get); - unsafe { setsockopt(self.as_raw(), IPPROTO_IP, libc::IP_BOUND_IF, index) } + unsafe { setsockopt(self.as_raw(), IPPROTO_IP, IP_BOUND_IF, index) } } /// Sets the value for `IPV6_BOUND_IF` option on this socket. @@ -1869,7 +1892,7 @@ impl crate::Socket { ))] pub fn bind_device_by_index_v6(&self, interface: Option) -> io::Result<()> { let index = interface.map_or(0, NonZeroU32::get); - unsafe { setsockopt(self.as_raw(), IPPROTO_IPV6, libc::IPV6_BOUND_IF, index) } + unsafe { setsockopt(self.as_raw(), IPPROTO_IPV6, IPV6_BOUND_IF, index) } } /// Gets the value for `IP_BOUND_IF` option on this socket, i.e. the index @@ -1890,8 +1913,7 @@ impl crate::Socket { ) ))] pub fn device_index_v4(&self) -> io::Result> { - let index = - unsafe { getsockopt::(self.as_raw(), IPPROTO_IP, libc::IP_BOUND_IF)? }; + let index = unsafe { getsockopt::(self.as_raw(), IPPROTO_IP, IP_BOUND_IF)? }; Ok(NonZeroU32::new(index)) } @@ -1913,9 +1935,8 @@ impl crate::Socket { ) ))] pub fn device_index_v6(&self) -> io::Result> { - let index = unsafe { - getsockopt::(self.as_raw(), IPPROTO_IPV6, libc::IPV6_BOUND_IF)? - }; + let index = + unsafe { getsockopt::(self.as_raw(), IPPROTO_IPV6, IPV6_BOUND_IF)? }; Ok(NonZeroU32::new(index)) } From edea443b69a897fddc51872485f88a3d864e3af6 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Wed, 26 Feb 2025 12:55:22 -0800 Subject: [PATCH 3/6] don't import IP_BOUND_IF without feature = "all" --- src/sys/unix.rs | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/sys/unix.rs b/src/sys/unix.rs index ff887e2a..48a59bbd 100644 --- a/src/sys/unix.rs +++ b/src/sys/unix.rs @@ -1392,25 +1392,31 @@ pub(crate) fn original_dst_ipv6(fd: Socket) -> io::Result { } // TODO(eliza): use libc's definition on solarish once it merges... -#[cfg(any(target_os = "illumos", target_os = "solaris",))] +#[cfg(all(feature = "all", any(target_os = "illumos", target_os = "solaris")))] const IP_BOUND_IF: libc::c_int = 0x41; -#[cfg(any( - target_os = "ios", - target_os = "visionos", - target_os = "macos", - target_os = "tvos", - target_os = "watchos", +#[cfg(all( + feature = "all", + any( + target_os = "ios", + target_os = "visionos", + target_os = "macos", + target_os = "tvos", + target_os = "watchos", + ) ))] use libc::IP_BOUND_IF; -#[cfg(any(target_os = "illumos", target_os = "solaris",))] +#[cfg(all(feature = "all", any(target_os = "illumos", target_os = "solaris")))] const IPV6_BOUND_IF: libc::c_int = 0x41; -#[cfg(any( - target_os = "ios", - target_os = "visionos", - target_os = "macos", - target_os = "tvos", - target_os = "watchos", +#[cfg(all( + feature = "all", + any( + target_os = "ios", + target_os = "visionos", + target_os = "macos", + target_os = "tvos", + target_os = "watchos", + ) ))] use libc::IPV6_BOUND_IF; From ece8cd341f0d14aa06fc5cc75330492489d9a043 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Wed, 26 Feb 2025 12:58:07 -0800 Subject: [PATCH 4/6] enable bind_device tests on solarish --- tests/socket.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/socket.rs b/tests/socket.rs index 3776a4b4..a2dca668 100644 --- a/tests/socket.rs +++ b/tests/socket.rs @@ -990,6 +990,8 @@ fn device() { target_os = "macos", target_os = "tvos", target_os = "watchos", + target_os = "solaris", + target_os = "illumos", ) ))] #[test] @@ -1036,6 +1038,8 @@ fn device() { target_os = "macos", target_os = "tvos", target_os = "watchos", + target_os = "solaris", + target_os = "illumos", ) ))] #[test] From 4a5b66aa0c22df7d365c78a83f25b3d651e14e9c Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Wed, 26 Feb 2025 13:43:16 -0800 Subject: [PATCH 5/6] point at rust-lang/libc#4287 --- src/sys/unix.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sys/unix.rs b/src/sys/unix.rs index 48a59bbd..adc0c3ea 100644 --- a/src/sys/unix.rs +++ b/src/sys/unix.rs @@ -1391,7 +1391,7 @@ pub(crate) fn original_dst_ipv6(fd: Socket) -> io::Result { .map(|(_, addr)| addr) } -// TODO(eliza): use libc's definition on solarish once it merges... +// TODO: remove this once https://github.com/rust-lang/libc/pull/4287 merges #[cfg(all(feature = "all", any(target_os = "illumos", target_os = "solaris")))] const IP_BOUND_IF: libc::c_int = 0x41; #[cfg(all( @@ -1406,6 +1406,7 @@ const IP_BOUND_IF: libc::c_int = 0x41; ))] use libc::IP_BOUND_IF; +// TODO: remove this once https://github.com/rust-lang/libc/pull/4287 merges #[cfg(all(feature = "all", any(target_os = "illumos", target_os = "solaris")))] const IPV6_BOUND_IF: libc::c_int = 0x41; #[cfg(all( From c788020b8aee698671a992e82900b6ff115db27e Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Tue, 11 Mar 2025 10:24:47 -0700 Subject: [PATCH 6/6] Update to `libc` 0.2.171 `libc` [v0.2.171] was just released, and it includes definitions of `IP_BOUND_IF` for Solarish operating systems. Thus, we can now remove the temporary local definitions. [v0.2.171]: https://github.com/rust-lang/libc/releases/tag/0.2.171 --- Cargo.toml | 2 +- src/sys/unix.rs | 42 +++++++----------------------------------- 2 files changed, 8 insertions(+), 36 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 18331f61..d0566282 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,7 +51,7 @@ targets = [ features = ["all"] [target."cfg(unix)".dependencies] -libc = "0.2.150" +libc = "0.2.171" [target.'cfg(windows)'.dependencies.windows-sys] version = "0.52" diff --git a/src/sys/unix.rs b/src/sys/unix.rs index adc0c3ea..37d0c3a6 100644 --- a/src/sys/unix.rs +++ b/src/sys/unix.rs @@ -1391,36 +1391,6 @@ pub(crate) fn original_dst_ipv6(fd: Socket) -> io::Result { .map(|(_, addr)| addr) } -// TODO: remove this once https://github.com/rust-lang/libc/pull/4287 merges -#[cfg(all(feature = "all", any(target_os = "illumos", target_os = "solaris")))] -const IP_BOUND_IF: libc::c_int = 0x41; -#[cfg(all( - feature = "all", - any( - target_os = "ios", - target_os = "visionos", - target_os = "macos", - target_os = "tvos", - target_os = "watchos", - ) -))] -use libc::IP_BOUND_IF; - -// TODO: remove this once https://github.com/rust-lang/libc/pull/4287 merges -#[cfg(all(feature = "all", any(target_os = "illumos", target_os = "solaris")))] -const IPV6_BOUND_IF: libc::c_int = 0x41; -#[cfg(all( - feature = "all", - any( - target_os = "ios", - target_os = "visionos", - target_os = "macos", - target_os = "tvos", - target_os = "watchos", - ) -))] -use libc::IPV6_BOUND_IF; - /// Unix only API. impl crate::Socket { /// Accept a new incoming connection from this listener. @@ -1872,7 +1842,7 @@ impl crate::Socket { ))] pub fn bind_device_by_index_v4(&self, interface: Option) -> io::Result<()> { let index = interface.map_or(0, NonZeroU32::get); - unsafe { setsockopt(self.as_raw(), IPPROTO_IP, IP_BOUND_IF, index) } + unsafe { setsockopt(self.as_raw(), IPPROTO_IP, libc::IP_BOUND_IF, index) } } /// Sets the value for `IPV6_BOUND_IF` option on this socket. @@ -1899,7 +1869,7 @@ impl crate::Socket { ))] pub fn bind_device_by_index_v6(&self, interface: Option) -> io::Result<()> { let index = interface.map_or(0, NonZeroU32::get); - unsafe { setsockopt(self.as_raw(), IPPROTO_IPV6, IPV6_BOUND_IF, index) } + unsafe { setsockopt(self.as_raw(), IPPROTO_IPV6, libc::IPV6_BOUND_IF, index) } } /// Gets the value for `IP_BOUND_IF` option on this socket, i.e. the index @@ -1920,7 +1890,8 @@ impl crate::Socket { ) ))] pub fn device_index_v4(&self) -> io::Result> { - let index = unsafe { getsockopt::(self.as_raw(), IPPROTO_IP, IP_BOUND_IF)? }; + let index = + unsafe { getsockopt::(self.as_raw(), IPPROTO_IP, libc::IP_BOUND_IF)? }; Ok(NonZeroU32::new(index)) } @@ -1942,8 +1913,9 @@ impl crate::Socket { ) ))] pub fn device_index_v6(&self) -> io::Result> { - let index = - unsafe { getsockopt::(self.as_raw(), IPPROTO_IPV6, IPV6_BOUND_IF)? }; + let index = unsafe { + getsockopt::(self.as_raw(), IPPROTO_IPV6, libc::IPV6_BOUND_IF)? + }; Ok(NonZeroU32::new(index)) }