From d7ea5f7d8ff71261b410900db418416d53c7edbe Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sun, 2 Mar 2025 07:43:45 +0000 Subject: [PATCH 1/3] socket::sockopt adding SOL_FILTER level options for illumos. Respectively FIL_ATTACH/FIL_DETACH to bind/unbind a filter to a socket to for example delay connection acceptance until data (datafilt) is received. --- src/sys/socket/sockopt.rs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/sys/socket/sockopt.rs b/src/sys/socket/sockopt.rs index 11c3be9e13..3ec90669e8 100644 --- a/src/sys/socket/sockopt.rs +++ b/src/sys/socket/sockopt.rs @@ -987,6 +987,25 @@ sockopt_impl!( libc::SO_ACCEPTFILTER, libc::accept_filter_arg ); +#[cfg(target_os = "illumos")] +sockopt_impl!( + /// Attach a named filter to this socket to be able to + /// defer when anough byte had been buffered by the kernel + FilAttach, + SetOnly, + libc::SOL_FILTER, + libc::FIL_ATTACH, + SetOsString<'static> +); +#[cfg(target_os = "illumos")] +sockopt_impl!( + /// Detach a socket filter previously attached with FIL_ATTACH + FilDetach, + SetOnly, + libc::SOL_FILTER, + libc::FIL_DETACH, + SetOsString<'static> +); #[cfg(target_os = "linux")] sockopt_impl!( /// Set the mark for each packet sent through this socket (similar to the @@ -1799,7 +1818,7 @@ pub struct SetOsString<'a> { val: &'a OsStr, } -#[cfg(any(target_os = "freebsd", linux_android))] +#[cfg(any(target_os = "freebsd", linux_android, target_os = "illumos"))] impl<'a> Set<'a, OsString> for SetOsString<'a> { fn new(val: &OsString) -> SetOsString { SetOsString { From c005f3e701a1c4ab75d2cdd9dc7502c54bd6fe19 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sun, 2 Mar 2025 08:04:18 +0000 Subject: [PATCH 2/3] changelog entry --- changelog/2611.added.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/2611.added.md diff --git a/changelog/2611.added.md b/changelog/2611.added.md new file mode 100644 index 0000000000..b94114770c --- /dev/null +++ b/changelog/2611.added.md @@ -0,0 +1 @@ +Add `FilAttach` and `FilDetach` to socket::sockopt for Illumos From 6d5e426a3b69460ddf6cb4281160ad93275e507a Mon Sep 17 00:00:00 2001 From: David Carlier Date: Mon, 24 Mar 2025 13:21:04 +0000 Subject: [PATCH 3/3] changes from feedback --- src/sys/socket/mod.rs | 2 +- src/sys/socket/sockopt.rs | 77 ++++++++++++++++++++++++++++----------- test/sys/test_sockopt.rs | 23 ++++++++++++ 3 files changed, 79 insertions(+), 23 deletions(-) diff --git a/src/sys/socket/mod.rs b/src/sys/socket/mod.rs index e26e327d78..98f656a0b9 100644 --- a/src/sys/socket/mod.rs +++ b/src/sys/socket/mod.rs @@ -2466,7 +2466,7 @@ pub trait GetSockOpt: Copy { /// Represents a socket option that can be set. pub trait SetSockOpt: Clone { - type Val; + type Val: ?Sized; /// Set the value of this socket option on the given socket. fn set(&self, fd: &F, val: &Self::Val) -> Result<()>; diff --git a/src/sys/socket/sockopt.rs b/src/sys/socket/sockopt.rs index 3ec90669e8..10ed64cfc1 100644 --- a/src/sys/socket/sockopt.rs +++ b/src/sys/socket/sockopt.rs @@ -1,8 +1,8 @@ //! Socket options as used by `setsockopt` and `getsockopt`. -#[cfg(linux_android)] +#[cfg(any(linux_android, target_os = "illumos"))] use super::SetSockOpt; use crate::sys::time::TimeVal; -#[cfg(linux_android)] +#[cfg(any(linux_android, target_os = "illumos"))] use crate::{errno::Errno, Result}; use cfg_if::cfg_if; use libc::{self, c_int, c_void, socklen_t}; @@ -11,7 +11,7 @@ use std::ffi::CString; use std::ffi::{CStr, OsStr, OsString}; use std::mem::{self, MaybeUninit}; use std::os::unix::ffi::OsStrExt; -#[cfg(linux_android)] +#[cfg(any(linux_android, target_os = "illumos"))] use std::os::unix::io::{AsFd, AsRawFd}; // Constants @@ -987,25 +987,6 @@ sockopt_impl!( libc::SO_ACCEPTFILTER, libc::accept_filter_arg ); -#[cfg(target_os = "illumos")] -sockopt_impl!( - /// Attach a named filter to this socket to be able to - /// defer when anough byte had been buffered by the kernel - FilAttach, - SetOnly, - libc::SOL_FILTER, - libc::FIL_ATTACH, - SetOsString<'static> -); -#[cfg(target_os = "illumos")] -sockopt_impl!( - /// Detach a socket filter previously attached with FIL_ATTACH - FilDetach, - SetOnly, - libc::SOL_FILTER, - libc::FIL_DETACH, - SetOsString<'static> -); #[cfg(target_os = "linux")] sockopt_impl!( /// Set the mark for each packet sent through this socket (similar to the @@ -1501,6 +1482,58 @@ impl SetSockOpt for TcpTlsRx { } } +#[cfg(target_os = "illumos")] +#[derive(Copy, Clone, Debug)] +/// Attach a named filter to this socket to be able to +/// defer when anough byte had been buffered by the kernel +pub struct FilterAttach; + +#[cfg(target_os = "illumos")] +impl SetSockOpt for FilterAttach { + type Val = OsStr; + + fn set(&self, fd: &F, val: &Self::Val) -> Result<()> { + if val.len() > libc::FILNAME_MAX as usize { + return Err(Errno::EINVAL); + } + unsafe { + let res = libc::setsockopt( + fd.as_fd().as_raw_fd(), + libc::SOL_FILTER, + libc::FIL_ATTACH, + val.as_bytes().as_ptr().cast(), + val.len() as libc::socklen_t, + ); + Errno::result(res).map(drop) + } + } +} + +#[cfg(target_os = "illumos")] +#[derive(Copy, Clone, Debug)] +/// Detach a socket filter previously attached with FIL_ATTACH +pub struct FilterDetach; + +#[cfg(target_os = "illumos")] +impl SetSockOpt for FilterDetach { + type Val = OsStr; + + fn set(&self, fd: &F, val: &Self::Val) -> Result<()> { + if val.len() > libc::FILNAME_MAX as usize { + return Err(Errno::EINVAL); + } + unsafe { + let res = libc::setsockopt( + fd.as_fd().as_raw_fd(), + libc::SOL_FILTER, + libc::FIL_DETACH, + val.as_bytes().as_ptr().cast(), + val.len() as libc::socklen_t, + ); + Errno::result(res).map(drop) + } + } +} /* * * ===== Accessor helpers ===== diff --git a/test/sys/test_sockopt.rs b/test/sys/test_sockopt.rs index 42697ffa24..f96b450346 100644 --- a/test/sys/test_sockopt.rs +++ b/test/sys/test_sockopt.rs @@ -1168,3 +1168,26 @@ fn test_exclbind() { Err(Errno::EADDRINUSE) ); } + +#[cfg(target_os = "illumos")] +#[test] +fn test_solfilter() { + use nix::errno::Errno; + let s = socket( + AddressFamily::Inet, + SockType::Stream, + SockFlag::empty(), + SockProtocol::Tcp, + ) + .unwrap(); + let data = std::ffi::OsStr::new("httpf"); + let attach = sockopt::FilterAttach; + let detach = sockopt::FilterDetach; + + // These 2 options won't work unless the needed kernel module is installed: + // https://github.com/nix-rust/nix/pull/2611#issuecomment-2750237782 + // + // So we only test the binding here + assert_eq!(Err(Errno::ENOENT), setsockopt(&s, attach, data)); + assert_eq!(Err(Errno::ENOENT), setsockopt(&s, detach, data)); +}