Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions src/addr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not submit patch to libc to get this updated?


impl Hash for SocketAddr {
fn hash<H: Hasher>(&self, state: &mut H) {
Expand Down Expand Up @@ -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)
Expand All @@ -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;
// \ / \
// / +---------------+---------------+
// +----------+---------+ |
Expand All @@ -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::<libc::sockaddr_nl>() as libc::socklen_t;
let addr_len = mem::size_of::<sockaddr_nl>() 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::<libc::sockaddr_nl>() as libc::socklen_t;
let addr_ptr = &mut self.0 as *mut sockaddr_nl as *mut libc::sockaddr;
let addr_len = mem::size_of::<sockaddr_nl>() as libc::socklen_t;
(addr_ptr, addr_len)
}
}
9 changes: 9 additions & 0 deletions src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
32 changes: 32 additions & 0 deletions src/ffi/freebsd.rs
Original file line number Diff line number Diff line change
@@ -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::<sockaddr_nl>() - 12usize];
["Alignment of sockaddr_nl"]
[::core::mem::align_of::<sockaddr_nl>() - 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];
};
12 changes: 8 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -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,
};
}

Expand Down
105 changes: 61 additions & 44 deletions src/socket.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand Down Expand Up @@ -89,7 +98,7 @@ impl Socket {
pub fn new(protocol: isize) -> Result<Self> {
let res = unsafe {
libc::socket(
libc::PF_NETLINK,
PF_NETLINK as _,
libc::SOCK_DGRAM | libc::SOCK_CLOEXEC,
protocol as libc::c_int,
)
Expand Down Expand Up @@ -196,6 +205,7 @@ impl Socket {
/// }
/// }
/// ```
#[cfg(not(target_os = "freebsd"))]
pub fn connect(&self, remote_addr: &SocketAddr) -> Result<()> {
// FIXME:
//
Expand Down Expand Up @@ -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::<libc::sockaddr_nl>() };
let mut addr = unsafe { mem::zeroed::<sockaddr_nl>() };

// recvfrom takes a *sockaddr as parameter so that it can accept any
// kind of address storage, so we need to create such a pointer
Expand All @@ -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
Expand Down Expand Up @@ -414,35 +423,35 @@ 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,
)
}

pub fn get_pktinfo(&self) -> Result<bool> {
let res = getsockopt::<libc::c_int>(
self.as_raw_fd(),
libc::SOL_NETLINK,
libc::NETLINK_PKTINFO,
SOL_NETLINK as _,
NETLINK_PKTINFO as _,
)?;
Ok(res == 1)
}

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,
)
}

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,
)
}
Expand All @@ -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<bool> {
let res = getsockopt::<libc::c_int>(
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<bool> {
let res = getsockopt::<libc::c_int>(
self.as_raw_fd(),
libc::SOL_NETLINK,
libc::NETLINK_NO_ENOBUFS,
SOL_NETLINK as _,
NETLINK_NO_ENOBUFS as _,
)?;
Ok(res == 1)
}
Expand All @@ -506,17 +519,17 @@ 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,
)
}

pub fn get_listen_all_namespaces(&self) -> Result<bool> {
let res = getsockopt::<libc::c_int>(
self.as_raw_fd(),
libc::SOL_NETLINK,
libc::NETLINK_LISTEN_ALL_NSID,
SOL_NETLINK as _,
NETLINK_LISTEN_ALL_NSID as _,
)?;
Ok(res == 1)
}
Expand All @@ -531,17 +544,17 @@ 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,
)
}

pub fn get_cap_ack(&self) -> Result<bool> {
let res = getsockopt::<libc::c_int>(
self.as_raw_fd(),
libc::SOL_NETLINK,
libc::NETLINK_CAP_ACK,
SOL_NETLINK as _,
NETLINK_CAP_ACK as _,
)?;
Ok(res == 1)
}
Expand All @@ -553,17 +566,17 @@ 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,
)
}

pub fn get_ext_ack(&self) -> Result<bool> {
let res = getsockopt::<libc::c_int>(
self.as_raw_fd(),
libc::SOL_NETLINK,
libc::NETLINK_EXT_ACK,
SOL_NETLINK as _,
NETLINK_EXT_ACK as _,
)?;
Ok(res == 1)
}
Expand Down Expand Up @@ -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,
)
}
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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();
Expand Down
Loading