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