diff --git a/src/addr.rs b/src/addr.rs index e098b2b..88101ce 100644 --- a/src/addr.rs +++ b/src/addr.rs @@ -6,6 +6,8 @@ use std::{ mem, }; +use crate::{constants::PF_NETLINK, sys::sockaddr_nl}; + /// The address of a netlink socket /// /// A netlink address is made of two parts: the unicast address of the socket, @@ -96,7 +98,7 @@ use std::{ /// [bind_auto]: crate::Socket::bind_auto /// [get_addr]: crate::Socket::get_address #[derive(Copy, Clone)] -pub struct SocketAddr(pub(crate) libc::sockaddr_nl); +pub struct SocketAddr(pub(crate) sockaddr_nl); impl Hash for SocketAddr { fn hash(&self, state: &mut H) { @@ -139,8 +141,8 @@ impl fmt::Display for SocketAddr { impl SocketAddr { /// Create a new socket address for with th pub fn new(port_number: u32, multicast_groups: u32) -> Self { - let mut addr: libc::sockaddr_nl = unsafe { mem::zeroed() }; - addr.nl_family = libc::PF_NETLINK as libc::sa_family_t; + let mut addr: sockaddr_nl = unsafe { mem::zeroed() }; + addr.nl_family = PF_NETLINK as libc::sa_family_t; addr.nl_pid = port_number; addr.nl_groups = multicast_groups; SocketAddr(addr) @@ -157,8 +159,7 @@ impl SocketAddr { } pub(crate) fn as_raw(&self) -> (*const libc::sockaddr, libc::socklen_t) { - let addr_ptr = - &self.0 as *const libc::sockaddr_nl as *const libc::sockaddr; + let addr_ptr = &self.0 as *const sockaddr_nl as *const libc::sockaddr; // \ / \ // / +---------------+---------------+ // +----------+---------+ | @@ -178,16 +179,15 @@ impl SocketAddr { // But since this is my first time dealing with this kind of things I // chose the most explicit form. - let addr_len = mem::size_of::() as libc::socklen_t; + let addr_len = mem::size_of::() as libc::socklen_t; (addr_ptr, addr_len) } pub(crate) fn as_raw_mut( &mut self, ) -> (*mut libc::sockaddr, libc::socklen_t) { - let addr_ptr = - &mut self.0 as *mut libc::sockaddr_nl as *mut libc::sockaddr; - let addr_len = mem::size_of::() as libc::socklen_t; + let addr_ptr = &mut self.0 as *mut sockaddr_nl as *mut libc::sockaddr; + let addr_len = mem::size_of::() as libc::socklen_t; (addr_ptr, addr_len) } } diff --git a/src/constants.rs b/src/constants.rs index a402235..154f28c 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -141,3 +141,12 @@ pub const NETLINK_CAP_ACK: int = 10; pub const NETLINK_EXT_ACK: int = 11; pub const NL_MMAP_MSG_ALIGNMENT: int = 4; pub const NET_MAJOR: int = 36; + +pub const NETLINK_GET_STRICT_CHK: int = 12; + +#[cfg(target_os = "freebsd")] +pub const AF_NETLINK: int = 38; +#[cfg(not(target_os = "freebsd"))] +pub const AF_NETLINK: int = 16; +pub const PF_NETLINK: int = AF_NETLINK; +pub const SOL_NETLINK: int = 270; diff --git a/src/ffi/freebsd.rs b/src/ffi/freebsd.rs new file mode 100644 index 0000000..665fa72 --- /dev/null +++ b/src/ffi/freebsd.rs @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT + +/* automatically generated by rust-bindgen 0.72.1 */ + +pub type __uint8_t = ::core::ffi::c_uchar; +pub type __sa_family_t = __uint8_t; +pub type sa_family_t = __sa_family_t; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct sockaddr_nl { + pub nl_len: u8, + pub nl_family: sa_family_t, + pub nl_pad: u16, + pub nl_pid: u32, + pub nl_groups: u32, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of sockaddr_nl"][::core::mem::size_of::() - 12usize]; + ["Alignment of sockaddr_nl"] + [::core::mem::align_of::() - 4usize]; + ["Offset of field: sockaddr_nl::nl_len"] + [::core::mem::offset_of!(sockaddr_nl, nl_len) - 0usize]; + ["Offset of field: sockaddr_nl::nl_family"] + [::core::mem::offset_of!(sockaddr_nl, nl_family) - 1usize]; + ["Offset of field: sockaddr_nl::nl_pad"] + [::core::mem::offset_of!(sockaddr_nl, nl_pad) - 2usize]; + ["Offset of field: sockaddr_nl::nl_pid"] + [::core::mem::offset_of!(sockaddr_nl, nl_pid) - 4usize]; + ["Offset of field: sockaddr_nl::nl_groups"] + [::core::mem::offset_of!(sockaddr_nl, nl_groups) - 8usize]; +}; diff --git a/src/lib.rs b/src/lib.rs index 2f346a4..2f0001d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,14 +1,18 @@ // SPDX-License-Identifier: MIT +pub mod sys; + pub mod constants; pub mod protocols { pub use super::constants::{ - NETLINK_AUDIT, NETLINK_CONNECTOR, NETLINK_CRYPTO, NETLINK_DNRTMSG, - NETLINK_ECRYPTFS, NETLINK_FIB_LOOKUP, NETLINK_FIREWALL, - NETLINK_GENERIC, NETLINK_IP6_FW, NETLINK_ISCSI, NETLINK_KOBJECT_UEVENT, + AF_NETLINK, NETLINK_AUDIT, NETLINK_CONNECTOR, NETLINK_CRYPTO, + NETLINK_DNRTMSG, NETLINK_ECRYPTFS, NETLINK_FIB_LOOKUP, + NETLINK_FIREWALL, NETLINK_GENERIC, NETLINK_GET_STRICT_CHK, + NETLINK_IP6_FW, NETLINK_ISCSI, NETLINK_KOBJECT_UEVENT, NETLINK_NETFILTER, NETLINK_NFLOG, NETLINK_RDMA, NETLINK_ROUTE, NETLINK_SCSITRANSPORT, NETLINK_SELINUX, NETLINK_SOCK_DIAG, - NETLINK_UNUSED, NETLINK_USERSOCK, NETLINK_XFRM, + NETLINK_UNUSED, NETLINK_USERSOCK, NETLINK_XFRM, PF_NETLINK, + SOL_NETLINK, }; } diff --git a/src/socket.rs b/src/socket.rs index 9b63959..3e7e929 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -9,7 +9,16 @@ use std::{ }, }; -use crate::SocketAddr; +use crate::{ + constants::{ + NETLINK_ADD_MEMBERSHIP, NETLINK_BROADCAST_ERROR, NETLINK_CAP_ACK, + NETLINK_DROP_MEMBERSHIP, NETLINK_EXT_ACK, NETLINK_GET_STRICT_CHK, + NETLINK_LISTEN_ALL_NSID, NETLINK_NO_ENOBUFS, NETLINK_PKTINFO, + PF_NETLINK, SOL_NETLINK, + }, + sys::sockaddr_nl, + SocketAddr, +}; /// A netlink socket. /// @@ -89,7 +98,7 @@ impl Socket { pub fn new(protocol: isize) -> Result { let res = unsafe { libc::socket( - libc::PF_NETLINK, + PF_NETLINK as _, libc::SOCK_DGRAM | libc::SOCK_CLOEXEC, protocol as libc::c_int, ) @@ -196,6 +205,7 @@ impl Socket { /// } /// } /// ``` + #[cfg(not(target_os = "freebsd"))] pub fn connect(&self, remote_addr: &SocketAddr) -> Result<()> { // FIXME: // @@ -261,7 +271,7 @@ impl Socket { // library create a sockaddr_storage so that it works for any // address family, but here, we already know that we'll have a // Netlink address, so we can create the appropriate storage. - let mut addr = unsafe { mem::zeroed::() }; + let mut addr = unsafe { mem::zeroed::() }; // recvfrom takes a *sockaddr as parameter so that it can accept any // kind of address storage, so we need to create such a pointer @@ -276,8 +286,7 @@ impl Socket { // +--------------+---------------+ +---------+--------+ // / \ / // \ - let addr_ptr = - &mut addr as *mut libc::sockaddr_nl as *mut libc::sockaddr; + let addr_ptr = &mut addr as *mut sockaddr_nl as *mut libc::sockaddr; // Why do we need to pass the address length? We're passing a generic // *sockaddr to recvfrom. Somehow recvfrom needs to make sure @@ -414,8 +423,8 @@ impl Socket { let value: libc::c_int = value.into(); setsockopt( self.as_raw_fd(), - libc::SOL_NETLINK, - libc::NETLINK_PKTINFO, + SOL_NETLINK as _, + NETLINK_PKTINFO as _, value, ) } @@ -423,8 +432,8 @@ impl Socket { pub fn get_pktinfo(&self) -> Result { let res = getsockopt::( self.as_raw_fd(), - libc::SOL_NETLINK, - libc::NETLINK_PKTINFO, + SOL_NETLINK as _, + NETLINK_PKTINFO as _, )?; Ok(res == 1) } @@ -432,8 +441,8 @@ impl Socket { pub fn add_membership(&mut self, group: u32) -> Result<()> { setsockopt( self.as_raw_fd(), - libc::SOL_NETLINK, - libc::NETLINK_ADD_MEMBERSHIP, + SOL_NETLINK as _, + NETLINK_ADD_MEMBERSHIP as _, group, ) } @@ -441,8 +450,8 @@ impl Socket { pub fn drop_membership(&mut self, group: u32) -> Result<()> { setsockopt( self.as_raw_fd(), - libc::SOL_NETLINK, - libc::NETLINK_DROP_MEMBERSHIP, + SOL_NETLINK as _, + NETLINK_DROP_MEMBERSHIP as _, group, ) } @@ -457,42 +466,46 @@ impl Socket { /// `NETLINK_BROADCAST_ERROR` (since Linux 2.6.30). When not set, /// `netlink_broadcast()` only reports `ESRCH` errors and silently /// ignore `NOBUFS` errors. + #[cfg(not(target_os = "freebsd"))] pub fn set_broadcast_error(&mut self, value: bool) -> Result<()> { let value: libc::c_int = value.into(); setsockopt( self.as_raw_fd(), - libc::SOL_NETLINK, - libc::NETLINK_BROADCAST_ERROR, + SOL_NETLINK as _, + NETLINK_BROADCAST_ERROR as _, value, ) } + #[cfg(not(target_os = "freebsd"))] pub fn get_broadcast_error(&self) -> Result { let res = getsockopt::( self.as_raw_fd(), - libc::SOL_NETLINK, - libc::NETLINK_BROADCAST_ERROR, + SOL_NETLINK as _, + NETLINK_BROADCAST_ERROR as _, )?; Ok(res == 1) } /// `NETLINK_NO_ENOBUFS` (since Linux 2.6.30). This flag can be used by /// unicast and broadcast listeners to avoid receiving `ENOBUFS` errors. + #[cfg(not(target_os = "freebsd"))] pub fn set_no_enobufs(&mut self, value: bool) -> Result<()> { let value: libc::c_int = value.into(); setsockopt( self.as_raw_fd(), - libc::SOL_NETLINK, - libc::NETLINK_NO_ENOBUFS, + SOL_NETLINK as _, + NETLINK_NO_ENOBUFS as _, value, ) } + #[cfg(not(target_os = "freebsd"))] pub fn get_no_enobufs(&self) -> Result { let res = getsockopt::( self.as_raw_fd(), - libc::SOL_NETLINK, - libc::NETLINK_NO_ENOBUFS, + SOL_NETLINK as _, + NETLINK_NO_ENOBUFS as _, )?; Ok(res == 1) } @@ -506,8 +519,8 @@ impl Socket { let value: libc::c_int = value.into(); setsockopt( self.as_raw_fd(), - libc::SOL_NETLINK, - libc::NETLINK_LISTEN_ALL_NSID, + SOL_NETLINK as _, + NETLINK_LISTEN_ALL_NSID as _, value, ) } @@ -515,8 +528,8 @@ impl Socket { pub fn get_listen_all_namespaces(&self) -> Result { let res = getsockopt::( self.as_raw_fd(), - libc::SOL_NETLINK, - libc::NETLINK_LISTEN_ALL_NSID, + SOL_NETLINK as _, + NETLINK_LISTEN_ALL_NSID as _, )?; Ok(res == 1) } @@ -531,8 +544,8 @@ impl Socket { let value: libc::c_int = value.into(); setsockopt( self.as_raw_fd(), - libc::SOL_NETLINK, - libc::NETLINK_CAP_ACK, + SOL_NETLINK as _, + NETLINK_CAP_ACK as _, value, ) } @@ -540,8 +553,8 @@ impl Socket { pub fn get_cap_ack(&self) -> Result { let res = getsockopt::( self.as_raw_fd(), - libc::SOL_NETLINK, - libc::NETLINK_CAP_ACK, + SOL_NETLINK as _, + NETLINK_CAP_ACK as _, )?; Ok(res == 1) } @@ -553,8 +566,8 @@ impl Socket { let value: libc::c_int = value.into(); setsockopt( self.as_raw_fd(), - libc::SOL_NETLINK, - libc::NETLINK_EXT_ACK, + SOL_NETLINK as _, + NETLINK_EXT_ACK as _, value, ) } @@ -562,8 +575,8 @@ impl Socket { pub fn get_ext_ack(&self) -> Result { let res = getsockopt::( self.as_raw_fd(), - libc::SOL_NETLINK, - libc::NETLINK_EXT_ACK, + SOL_NETLINK as _, + NETLINK_EXT_ACK as _, )?; Ok(res == 1) } @@ -594,8 +607,8 @@ impl Socket { let value: u32 = value.into(); setsockopt( self.as_raw_fd(), - libc::SOL_NETLINK, - libc::NETLINK_GET_STRICT_CHK, + SOL_NETLINK as _, + NETLINK_GET_STRICT_CHK as _, value, ) } @@ -668,6 +681,7 @@ mod test { Socket::new(NETLINK_ROUTE).unwrap(); } + #[cfg(not(target_os = "freebsd"))] #[test] fn connect() { let sock = Socket::new(NETLINK_ROUTE).unwrap(); @@ -704,15 +718,18 @@ mod test { sock.set_cap_ack(false).unwrap(); assert!(!sock.get_cap_ack().unwrap()); - sock.set_no_enobufs(true).unwrap(); - assert!(sock.get_no_enobufs().unwrap()); - sock.set_no_enobufs(false).unwrap(); - assert!(!sock.get_no_enobufs().unwrap()); - - sock.set_broadcast_error(true).unwrap(); - assert!(sock.get_broadcast_error().unwrap()); - sock.set_broadcast_error(false).unwrap(); - assert!(!sock.get_broadcast_error().unwrap()); + #[cfg(not(target_os = "freebsd"))] + { + sock.set_no_enobufs(true).unwrap(); + assert!(sock.get_no_enobufs().unwrap()); + sock.set_no_enobufs(false).unwrap(); + assert!(!sock.get_no_enobufs().unwrap()); + + sock.set_broadcast_error(true).unwrap(); + assert!(sock.get_broadcast_error().unwrap()); + sock.set_broadcast_error(false).unwrap(); + assert!(!sock.get_broadcast_error().unwrap()); + } // FIXME: these require root permissions // sock.set_listen_all_namespaces(true).unwrap(); diff --git a/src/sys.rs b/src/sys.rs new file mode 100644 index 0000000..d0b9438 --- /dev/null +++ b/src/sys.rs @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT + +#[cfg(target_os = "freebsd")] +mod __private_sys { + #![allow(non_upper_case_globals)] + #![allow(non_camel_case_types)] + #![allow(non_snake_case)] + #![allow(dead_code)] + #![allow(clippy::all)] + include!("ffi/freebsd.rs"); +} + +#[cfg(not(target_os = "freebsd"))] +mod __private_sys { + pub use libc::sockaddr_nl; +} + +pub use self::__private_sys::*;