netlink_sys_mio_0_8/addr.rs
1// SPDX-License-Identifier: MIT
2
3use std::{
4 fmt,
5 hash::{Hash, Hasher},
6 mem,
7};
8
9/// The address of a netlink socket
10///
11/// A netlink address is made of two parts: the unicast address of the socket, called _port number_ or _PID_, and the
12/// multicast address called _group ID_. In this library, we've chosen to stick to the "port number" terminology, since
13/// PID can be confused with process ID. However, the netlink man page mostly uses PID.
14///
15/// ## Port number
16///
17/// Sockets in kernel space have 0 as a port number. For sockets opened by a user-space process, the port number can
18/// either be assigned by the process itself, or by the kernel. The only constraint is that this port number must be
19/// unique: two netlink sockets created by a given process must have a different port number. However, netlinks sockets
20/// created by different processes can have the same port number.
21///
22/// ### Port number assigned by the kernel
23///
24/// 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
25/// 0. The kernel will usually use the process ID as port number for the first netlink socket created by the process,
26/// which is why the socket port number is also called PID. For example:
27///
28/// ```rust
29/// use std::process;
30/// use netlink_sys::{
31/// protocols::NETLINK_ROUTE,
32/// SocketAddr, Socket,
33/// };
34///
35/// let mut socket = Socket::new(NETLINK_ROUTE).unwrap();
36/// // The first parameter is the port number. By setting it to 0 we ask the kernel to pick a port for us
37/// let mut addr = SocketAddr::new(0, 0);
38/// socket.bind(&addr).unwrap();
39/// // Retrieve the socket address
40/// socket.get_address(&mut addr).unwrap();
41/// // the socket port number should be equal to the process ID, but there is no guarantee
42/// println!("socket port number = {}, process ID = {}", addr.port_number(), process::id());
43///
44/// let mut socket2 = Socket::new(NETLINK_ROUTE).unwrap();
45/// let mut addr2 = SocketAddr::new(0, 0);
46/// socket2.bind(&addr2).unwrap();
47/// socket2.get_address(&mut addr2).unwrap();
48/// // the unicast address picked by the kernel for the second socket should be different
49/// assert!(addr.port_number() != addr2.port_number());
50/// ```
51///
52/// Note that it's a little tedious to create a socket address, call `bind` and then retrive the address with
53/// [`Socket::get_address`][get_addr]. To avoid this boilerplate you can use [`Socket::bind_auto`][bind_auto]:
54///
55/// ```rust
56/// use netlink_sys::{protocols::NETLINK_ROUTE, Socket, SocketAddr};
57/// use std::process;
58///
59/// let mut socket = Socket::new(NETLINK_ROUTE).unwrap();
60/// let addr = socket.bind_auto().unwrap();
61/// println!("socket port number = {}", addr.port_number());
62/// ```
63///
64/// ### Setting the port number manually
65///
66/// The application can also pick the port number by calling Socket::bind with an address with a non-zero port
67/// number. However, it must ensure that this number is unique for each socket created. For instance:
68///
69/// ```rust
70/// use netlink_sys::{protocols::NETLINK_ROUTE, Socket, SocketAddr};
71/// use std::process;
72///
73/// let mut socket = Socket::new(NETLINK_ROUTE).unwrap();
74/// // set the socket port number to 2
75/// let mut addr = SocketAddr::new(2, 0);
76/// socket.bind(&addr).unwrap();
77/// // Retrieve the socket address
78/// socket.get_address(&mut addr).unwrap();
79/// assert_eq!(2, addr.port_number());
80///
81/// // Creating a second socket with the same port number fails
82/// let mut socket2 = Socket::new(NETLINK_ROUTE).unwrap();
83/// let mut addr2 = SocketAddr::new(2, 0);
84/// socket2.bind(&addr2).unwrap_err();
85/// ```
86///
87/// [bind]: crate::Socket::bind
88/// [bind_auto]: crate::Socket::bind_auto
89/// [get_addr]: crate::Socket::get_address
90#[derive(Copy, Clone)]
91pub struct SocketAddr(pub(crate) libc::sockaddr_nl);
92
93impl Hash for SocketAddr {
94 fn hash<H: Hasher>(&self, state: &mut H) {
95 self.0.nl_family.hash(state);
96 self.0.nl_pid.hash(state);
97 self.0.nl_groups.hash(state);
98 }
99}
100
101impl PartialEq for SocketAddr {
102 fn eq(&self, other: &SocketAddr) -> bool {
103 self.0.nl_family == other.0.nl_family
104 && self.0.nl_pid == other.0.nl_pid
105 && self.0.nl_groups == other.0.nl_groups
106 }
107}
108
109impl fmt::Debug for SocketAddr {
110 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111 write!(
112 f,
113 "SocketAddr(nl_family={}, nl_pid={}, nl_groups={})",
114 self.0.nl_family, self.0.nl_pid, self.0.nl_groups
115 )
116 }
117}
118
119impl Eq for SocketAddr {}
120
121impl fmt::Display for SocketAddr {
122 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123 write!(
124 f,
125 "address family: {}, pid: {}, multicast groups: {})",
126 self.0.nl_family, self.0.nl_pid, self.0.nl_groups
127 )
128 }
129}
130
131impl SocketAddr {
132 /// Create a new socket address for with th
133 pub fn new(port_number: u32, multicast_groups: u32) -> Self {
134 let mut addr: libc::sockaddr_nl = unsafe { mem::zeroed() };
135 addr.nl_family = libc::PF_NETLINK as libc::sa_family_t;
136 addr.nl_pid = port_number;
137 addr.nl_groups = multicast_groups;
138 SocketAddr(addr)
139 }
140
141 /// Get the unicast address of this socket
142 pub fn port_number(&self) -> u32 {
143 self.0.nl_pid
144 }
145
146 /// Get the multicast groups of this socket
147 pub fn multicast_groups(&self) -> u32 {
148 self.0.nl_groups
149 }
150
151 pub(crate) fn as_raw(&self) -> (*const libc::sockaddr, libc::socklen_t) {
152 let addr_ptr = &self.0 as *const libc::sockaddr_nl as *const libc::sockaddr;
153 // \ / \ /
154 // +---------------+---------------+ +----------+---------+
155 // | |
156 // v |
157 // create a raw pointer to the sockaddr_nl |
158 // v
159 // cast *sockaddr_nl -> *sockaddr
160 //
161 // This kind of things seems to be pretty usual when using C APIs from Rust. It could be
162 // written in a shorter way thank to type inference:
163 //
164 // let addr_ptr: *const libc:sockaddr = &self.0 as *const _ as *const _;
165 //
166 // But since this is my first time dealing with this kind of things I chose the most
167 // explicit form.
168
169 let addr_len = mem::size_of::<libc::sockaddr_nl>() as libc::socklen_t;
170 (addr_ptr, addr_len)
171 }
172
173 pub(crate) fn as_raw_mut(&mut self) -> (*mut libc::sockaddr, libc::socklen_t) {
174 let addr_ptr = &mut self.0 as *mut libc::sockaddr_nl as *mut libc::sockaddr;
175 let addr_len = mem::size_of::<libc::sockaddr_nl>() as libc::socklen_t;
176 (addr_ptr, addr_len)
177 }
178}