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