blob: e098b2b415d322946a4ef42b8173d5fcb8f519c0 [file] [log] [blame]
// SPDX-License-Identifier: MIT
use std::{
fmt,
hash::{Hash, Hasher},
mem,
};
/// The address of a netlink socket
///
/// A netlink address is made of two parts: the unicast address of the socket,
/// called _port number_ or _PID_, and the multicast address called _group ID_.
/// In this library, we've chosen to stick to the "port number" terminology,
/// since PID can be confused with process ID. However, the netlink man page
/// mostly uses PID.
///
/// ## Port number
///
/// Sockets in kernel space have 0 as a port number. For sockets opened by a
/// user-space process, the port number can either be assigned by the process
/// itself, or by the kernel. The only constraint is that this port number must
/// be unique: two netlink sockets created by a given process must have a
/// different port number. However, netlinks sockets created by different
/// processes can have the same port number.
///
/// ### Port number assigned by the kernel
///
/// One way to set the port number is to let the kernel assign it, by calling
/// [`Socket::bind`][bind] with a port number set to 0. The kernel will usually
/// use the process ID as port number for the first netlink socket created by
/// the process, which is why the socket port number is also called PID. For
/// example:
///
/// ```rust
/// use std::process;
/// use netlink_sys::{
/// protocols::NETLINK_ROUTE,
/// SocketAddr, Socket,
/// };
///
/// let mut socket = Socket::new(NETLINK_ROUTE).unwrap();
/// // The first parameter is the port number. By setting it to 0 we ask the kernel to pick a port for us
/// let mut addr = SocketAddr::new(0, 0);
/// socket.bind(&addr).unwrap();
/// // Retrieve the socket address
/// socket.get_address(&mut addr).unwrap();
/// // the socket port number should be equal to the process ID, but there is no guarantee
/// println!("socket port number = {}, process ID = {}", addr.port_number(), process::id());
///
/// let mut socket2 = Socket::new(NETLINK_ROUTE).unwrap();
/// let mut addr2 = SocketAddr::new(0, 0);
/// socket2.bind(&addr2).unwrap();
/// socket2.get_address(&mut addr2).unwrap();
/// // the unicast address picked by the kernel for the second socket should be different
/// assert!(addr.port_number() != addr2.port_number());
/// ```
///
/// Note that it's a little tedious to create a socket address, call `bind` and
/// then retrive the address with [`Socket::get_address`][get_addr]. To avoid
/// this boilerplate you can use [`Socket::bind_auto`][bind_auto]:
///
/// ```rust
/// use netlink_sys::{protocols::NETLINK_ROUTE, Socket, SocketAddr};
/// use std::process;
///
/// let mut socket = Socket::new(NETLINK_ROUTE).unwrap();
/// let addr = socket.bind_auto().unwrap();
/// println!("socket port number = {}", addr.port_number());
/// ```
///
/// ### Setting the port number manually
///
/// The application can also pick the port number by calling Socket::bind with
/// an address with a non-zero port number. However, it must ensure that this
/// number is unique for each socket created. For instance:
///
/// ```rust
/// use netlink_sys::{protocols::NETLINK_ROUTE, Socket, SocketAddr};
/// use std::process;
///
/// let mut socket = Socket::new(NETLINK_ROUTE).unwrap();
/// // set the socket port number to 2
/// let mut addr = SocketAddr::new(2, 0);
/// socket.bind(&addr).unwrap();
/// // Retrieve the socket address
/// socket.get_address(&mut addr).unwrap();
/// assert_eq!(2, addr.port_number());
///
/// // Creating a second socket with the same port number fails
/// let mut socket2 = Socket::new(NETLINK_ROUTE).unwrap();
/// let mut addr2 = SocketAddr::new(2, 0);
/// socket2.bind(&addr2).unwrap_err();
/// ```
///
/// [bind]: crate::Socket::bind
/// [bind_auto]: crate::Socket::bind_auto
/// [get_addr]: crate::Socket::get_address
#[derive(Copy, Clone)]
pub struct SocketAddr(pub(crate) libc::sockaddr_nl);
impl Hash for SocketAddr {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.nl_family.hash(state);
self.0.nl_pid.hash(state);
self.0.nl_groups.hash(state);
}
}
impl PartialEq for SocketAddr {
fn eq(&self, other: &SocketAddr) -> bool {
self.0.nl_family == other.0.nl_family
&& self.0.nl_pid == other.0.nl_pid
&& self.0.nl_groups == other.0.nl_groups
}
}
impl fmt::Debug for SocketAddr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"SocketAddr(nl_family={}, nl_pid={}, nl_groups={})",
self.0.nl_family, self.0.nl_pid, self.0.nl_groups
)
}
}
impl Eq for SocketAddr {}
impl fmt::Display for SocketAddr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"address family: {}, pid: {}, multicast groups: {})",
self.0.nl_family, self.0.nl_pid, self.0.nl_groups
)
}
}
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;
addr.nl_pid = port_number;
addr.nl_groups = multicast_groups;
SocketAddr(addr)
}
/// Get the unicast address of this socket
pub fn port_number(&self) -> u32 {
self.0.nl_pid
}
/// Get the multicast groups of this socket
pub fn multicast_groups(&self) -> u32 {
self.0.nl_groups
}
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;
// \ / \
// / +---------------+---------------+
// +----------+---------+ |
// | v
// | create a raw pointer to the sockaddr_nl
// |
// v cast
// *sockaddr_nl -> *sockaddr
//
// This kind of things seems to be pretty usual when using C APIs from
// Rust. It could be written in a shorter way thank to type
// inference:
//
// let addr_ptr: *const libc:sockaddr = &self.0 as *const _ as
// *const _;
//
// 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;
(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;
(addr_ptr, addr_len)
}
}