netlink_sys/socket.rs
1// SPDX-License-Identifier: MIT
2
3use std::{
4 io::{Error, Result},
5 mem,
6 os::{
7 fd::{AsFd, BorrowedFd, FromRawFd},
8 unix::io::{AsRawFd, RawFd},
9 },
10};
11
12use crate::SocketAddr;
13
14/// A netlink socket.
15///
16/// # Example
17///
18/// In this example we:
19///
20/// 1. open a new socket
21/// 2. send a message to the kernel
22/// 3. read the reponse
23///
24/// ```rust
25/// use netlink_sys::{protocols::NETLINK_ROUTE, Socket, SocketAddr};
26/// use std::process;
27///
28/// // open a new socket for the NETLINK_ROUTE subsystem (see "man 7 rtnetlink")
29/// let mut socket = Socket::new(NETLINK_ROUTE).unwrap();
30/// // address of the remote peer we'll send a message to. This particular address is for the kernel
31/// let kernel_addr = SocketAddr::new(0, 0);
32/// // this is a valid message for listing the network links on the system
33/// let pkt = vec![
34/// 0x14, 0x00, 0x00, 0x00, 0x12, 0x00, 0x01, 0x03, 0xfd, 0xfe, 0x38, 0x5c, 0x00, 0x00, 0x00,
35/// 0x00, 0x00, 0x00, 0x00, 0x00,
36/// ];
37/// // send the message to the kernel
38/// let n_sent = socket.send_to(&pkt[..], &kernel_addr, 0).unwrap();
39/// assert_eq!(n_sent, pkt.len());
40/// // buffer for receiving the response
41/// let mut buf = vec![0; 4096];
42/// loop {
43/// // receive a datagram
44/// let (n_received, sender_addr) = socket.recv_from(&mut &mut buf[..], 0).unwrap();
45/// assert_eq!(sender_addr, kernel_addr);
46/// println!("received datagram {:?}", &buf[..n_received]);
47/// if buf[4] == 2 && buf[5] == 0 {
48/// println!("the kernel responded with an error");
49/// return;
50/// }
51/// if buf[4] == 3 && buf[5] == 0 {
52/// println!("end of dump");
53/// return;
54/// }
55/// }
56/// ```
57#[derive(Clone, Debug)]
58pub struct Socket(RawFd);
59
60impl AsRawFd for Socket {
61 fn as_raw_fd(&self) -> RawFd {
62 self.0
63 }
64}
65
66impl AsFd for Socket {
67 fn as_fd(&self) -> BorrowedFd<'_> {
68 unsafe { BorrowedFd::borrow_raw(self.0) }
69 }
70}
71
72impl FromRawFd for Socket {
73 unsafe fn from_raw_fd(fd: RawFd) -> Self {
74 Socket(fd)
75 }
76}
77
78impl Drop for Socket {
79 fn drop(&mut self) {
80 unsafe { libc::close(self.as_raw_fd()) };
81 }
82}
83
84impl Socket {
85 /// Open a new socket for the given netlink subsystem. `protocol` must be
86 /// one of the [`netlink_sys::protocols`][protos] constants.
87 ///
88 /// [protos]: crate::protocols
89 pub fn new(protocol: isize) -> Result<Self> {
90 let res = unsafe {
91 libc::socket(
92 libc::PF_NETLINK,
93 libc::SOCK_DGRAM | libc::SOCK_CLOEXEC,
94 protocol as libc::c_int,
95 )
96 };
97 if res < 0 {
98 return Err(Error::last_os_error());
99 }
100 Ok(Socket(res))
101 }
102
103 /// Bind the socket to the given address
104 pub fn bind(&mut self, addr: &SocketAddr) -> Result<()> {
105 let (addr_ptr, addr_len) = addr.as_raw();
106 let res = unsafe { libc::bind(self.as_raw_fd(), addr_ptr, addr_len) };
107 if res < 0 {
108 return Err(Error::last_os_error());
109 }
110 Ok(())
111 }
112
113 /// Bind the socket to an address assigned by the kernel, and return that
114 /// address.
115 pub fn bind_auto(&mut self) -> Result<SocketAddr> {
116 let mut addr = SocketAddr::new(0, 0);
117 self.bind(&addr)?;
118 self.get_address(&mut addr)?;
119 Ok(addr)
120 }
121
122 /// Get the socket address
123 pub fn get_address(&self, addr: &mut SocketAddr) -> Result<()> {
124 let (addr_ptr, mut addr_len) = addr.as_raw_mut();
125 let addr_len_copy = addr_len;
126 let addr_len_ptr = &mut addr_len as *mut libc::socklen_t;
127 let res = unsafe {
128 libc::getsockname(self.as_raw_fd(), addr_ptr, addr_len_ptr)
129 };
130 if res < 0 {
131 return Err(Error::last_os_error());
132 }
133 assert_eq!(addr_len, addr_len_copy);
134 Ok(())
135 }
136
137 // when building with --features smol we don't need this
138 #[allow(dead_code)]
139 /// Make this socket non-blocking
140 pub fn set_non_blocking(&self, non_blocking: bool) -> Result<()> {
141 let mut non_blocking = non_blocking as libc::c_int;
142 let res = unsafe {
143 libc::ioctl(self.as_raw_fd(), libc::FIONBIO, &mut non_blocking)
144 };
145 if res < 0 {
146 return Err(Error::last_os_error());
147 }
148 Ok(())
149 }
150
151 /// Connect the socket to the given address. Netlink is a connection-less
152 /// protocol, so a socket can communicate with multiple peers with the
153 /// [`Socket::send_to`] and [`Socket::recv_from`] methods. However, if the
154 /// socket only needs to communicate with one peer, it is convenient not
155 /// to have to bother with the peer address. This is what `connect` is
156 /// for. After calling `connect`, [`Socket::send`] and [`Socket::recv`]
157 /// respectively send and receive datagrams to and from `remote_addr`.
158 ///
159 /// # Examples
160 ///
161 /// In this example we:
162 ///
163 /// 1. open a socket
164 /// 2. connect it to the kernel with [`Socket::connect`]
165 /// 3. send a request to the kernel with [`Socket::send`]
166 /// 4. read the response (which can span over several messages)
167 /// [`Socket::recv`]
168 ///
169 /// ```rust
170 /// use netlink_sys::{protocols::NETLINK_ROUTE, Socket, SocketAddr};
171 /// use std::process;
172 ///
173 /// let mut socket = Socket::new(NETLINK_ROUTE).unwrap();
174 /// let _ = socket.bind_auto().unwrap();
175 /// let kernel_addr = SocketAddr::new(0, 0);
176 /// socket.connect(&kernel_addr).unwrap();
177 /// // This is a valid message for listing the network links on the system
178 /// let msg = vec![
179 /// 0x14, 0x00, 0x00, 0x00, 0x12, 0x00, 0x01, 0x03, 0xfd, 0xfe, 0x38, 0x5c, 0x00, 0x00, 0x00,
180 /// 0x00, 0x00, 0x00, 0x00, 0x00,
181 /// ];
182 /// let n_sent = socket.send(&msg[..], 0).unwrap();
183 /// assert_eq!(n_sent, msg.len());
184 /// // buffer for receiving the response
185 /// let mut buf = vec![0; 4096];
186 /// loop {
187 /// let mut n_received = socket.recv(&mut &mut buf[..], 0).unwrap();
188 /// println!("received {:?}", &buf[..n_received]);
189 /// if buf[4] == 2 && buf[5] == 0 {
190 /// println!("the kernel responded with an error");
191 /// return;
192 /// }
193 /// if buf[4] == 3 && buf[5] == 0 {
194 /// println!("end of dump");
195 /// return;
196 /// }
197 /// }
198 /// ```
199 pub fn connect(&self, remote_addr: &SocketAddr) -> Result<()> {
200 // FIXME:
201 //
202 // Event though for SOCK_DGRAM sockets there's no IO, if our socket is
203 // non-blocking, connect() might return EINPROGRESS. In theory,
204 // the right way to treat EINPROGRESS would be to ignore the
205 // error, and let the user poll the socket to check when it becomes
206 // writable, indicating that the connection succeeded. The code already
207 // exists in mio for TcpStream:
208 //
209 // > pub fn connect(stream: net::TcpStream, addr: &SocketAddr) ->
210 // > io::Result<TcpStream> {
211 // > set_non_block(stream.as_raw_fd())?;
212 // > match stream.connect(addr) {
213 // > Ok(..) => {}
214 // > Err(ref e) if e.raw_os_error() == Some(libc::EINPROGRESS) => {}
215 // > Err(e) => return Err(e),
216 // > }
217 // > Ok(TcpStream { inner: stream })
218 // > }
219 //
220 // In practice, since the connection does not require any IO for
221 // SOCK_DGRAM sockets, it almost never returns EINPROGRESS and
222 // so for now, we just return whatever libc::connect returns. If
223 // it returns EINPROGRESS, the caller will have to handle the error
224 // themself
225 //
226 // Refs:
227 //
228 // - https://stackoverflow.com/a/14046386/1836144
229 // - https://lists.isc.org/pipermail/bind-users/2009-August/077527.html
230 let (addr, addr_len) = remote_addr.as_raw();
231 let res = unsafe { libc::connect(self.as_raw_fd(), addr, addr_len) };
232 if res < 0 {
233 return Err(Error::last_os_error());
234 }
235 Ok(())
236 }
237
238 // Most of the comments in this method come from a discussion on rust users
239 // forum. [thread]: https://users.rust-lang.org/t/help-understanding-libc-call/17308/9
240 //
241 /// Read a datagram from the socket and return the number of bytes that have
242 /// been read and the address of the sender. The data being read is
243 /// copied into `buf`. If `buf` is too small, the datagram is truncated. The
244 /// supported flags are the `MSG_*` described in `man 2 recvmsg`
245 ///
246 /// # Warning
247 ///
248 /// In datagram oriented protocols, `recv` and `recvfrom` receive normally
249 /// only ONE datagram, but this seems not to be always true for netlink
250 /// sockets: with some protocols like `NETLINK_AUDIT`, multiple netlink
251 /// packets can be read with a single call.
252 pub fn recv_from<B>(
253 &self,
254 buf: &mut B,
255 flags: libc::c_int,
256 ) -> Result<(usize, SocketAddr)>
257 where
258 B: bytes::BufMut,
259 {
260 // Create an empty storage for the address. Note that Rust standard
261 // library create a sockaddr_storage so that it works for any
262 // address family, but here, we already know that we'll have a
263 // Netlink address, so we can create the appropriate storage.
264 let mut addr = unsafe { mem::zeroed::<libc::sockaddr_nl>() };
265
266 // recvfrom takes a *sockaddr as parameter so that it can accept any
267 // kind of address storage, so we need to create such a pointer
268 // for the sockaddr_nl we just initialized.
269 //
270 // Create a raw pointer to Cast our raw
271 // pointer to a our storage. We cannot
272 // generic pointer to *sockaddr pass it to
273 // recvfrom yet. that recvfrom can use
274 // ^ ^
275 // | |
276 // +--------------+---------------+ +---------+--------+
277 // / \ /
278 // \
279 let addr_ptr =
280 &mut addr as *mut libc::sockaddr_nl as *mut libc::sockaddr;
281
282 // Why do we need to pass the address length? We're passing a generic
283 // *sockaddr to recvfrom. Somehow recvfrom needs to make sure
284 // that the address of the received packet would fit into the
285 // actual type that is behind *sockaddr: it could be a sockaddr_nl but
286 // also a sockaddr_in, a sockaddr_in6, or even the generic
287 // sockaddr_storage that can store any address.
288 let mut addrlen = mem::size_of_val(&addr);
289 // recvfrom does not take the address length by value (see [thread]), so
290 // we need to create a pointer to it.
291 let addrlen_ptr = &mut addrlen as *mut usize as *mut libc::socklen_t;
292
293 let chunk = buf.chunk_mut();
294 // Cast the *mut u8 into *mut void.
295 // This is equivalent to casting a *char into *void
296 // See [thread]
297 // ^
298 // Create a *mut u8 |
299 // ^ |
300 // | |
301 // +------+-------+ +--------+-------+
302 // / \ / \
303 let buf_ptr = chunk.as_mut_ptr() as *mut libc::c_void;
304 let buf_len = chunk.len() as libc::size_t;
305
306 let res = unsafe {
307 libc::recvfrom(
308 self.as_raw_fd(),
309 buf_ptr,
310 buf_len,
311 flags,
312 addr_ptr,
313 addrlen_ptr,
314 )
315 };
316 if res < 0 {
317 return Err(Error::last_os_error());
318 } else {
319 // with `MSG_TRUNC` `res` might exceed `buf_len`
320 let written = std::cmp::min(buf_len, res as usize);
321 unsafe {
322 buf.advance_mut(written);
323 }
324 }
325 Ok((res as usize, SocketAddr(addr)))
326 }
327
328 /// For a connected socket, `recv` reads a datagram from the socket. The
329 /// sender is the remote peer the socket is connected to (see
330 /// [`Socket::connect`]). See also [`Socket::recv_from`]
331 pub fn recv<B>(&self, buf: &mut B, flags: libc::c_int) -> Result<usize>
332 where
333 B: bytes::BufMut,
334 {
335 let chunk = buf.chunk_mut();
336 let buf_ptr = chunk.as_mut_ptr() as *mut libc::c_void;
337 let buf_len = chunk.len() as libc::size_t;
338
339 let res =
340 unsafe { libc::recv(self.as_raw_fd(), buf_ptr, buf_len, flags) };
341 if res < 0 {
342 return Err(Error::last_os_error());
343 } else {
344 // with `MSG_TRUNC` `res` might exceed `buf_len`
345 let written = std::cmp::min(buf_len, res as usize);
346 unsafe {
347 buf.advance_mut(written);
348 }
349 }
350 Ok(res as usize)
351 }
352
353 /// Receive a full message. Unlike [`Socket::recv_from`], which truncates
354 /// messages that exceed the length of the buffer passed as argument,
355 /// this method always reads a whole message, no matter its size.
356 pub fn recv_from_full(&self) -> Result<(Vec<u8>, SocketAddr)> {
357 // Peek
358 let mut buf: Vec<u8> = Vec::new();
359 let (peek_len, _) =
360 self.recv_from(&mut buf, libc::MSG_PEEK | libc::MSG_TRUNC)?;
361
362 // Receive
363 buf.clear();
364 buf.reserve(peek_len);
365 let (rlen, addr) = self.recv_from(&mut buf, 0)?;
366 assert_eq!(rlen, peek_len);
367 Ok((buf, addr))
368 }
369
370 /// Send the given buffer `buf` to the remote peer with address `addr`. The
371 /// supported flags are the `MSG_*` values documented in `man 2 send`.
372 pub fn send_to(
373 &self,
374 buf: &[u8],
375 addr: &SocketAddr,
376 flags: libc::c_int,
377 ) -> Result<usize> {
378 let (addr_ptr, addr_len) = addr.as_raw();
379 let buf_ptr = buf.as_ptr() as *const libc::c_void;
380 let buf_len = buf.len() as libc::size_t;
381
382 let res = unsafe {
383 libc::sendto(
384 self.as_raw_fd(),
385 buf_ptr,
386 buf_len,
387 flags,
388 addr_ptr,
389 addr_len,
390 )
391 };
392 if res < 0 {
393 return Err(Error::last_os_error());
394 }
395 Ok(res as usize)
396 }
397
398 /// For a connected socket, `send` sends the given buffer `buf` to the
399 /// remote peer the socket is connected to. See also [`Socket::connect`]
400 /// and [`Socket::send_to`].
401 pub fn send(&self, buf: &[u8], flags: libc::c_int) -> Result<usize> {
402 let buf_ptr = buf.as_ptr() as *const libc::c_void;
403 let buf_len = buf.len() as libc::size_t;
404
405 let res =
406 unsafe { libc::send(self.as_raw_fd(), buf_ptr, buf_len, flags) };
407 if res < 0 {
408 return Err(Error::last_os_error());
409 }
410 Ok(res as usize)
411 }
412
413 pub fn set_pktinfo(&mut self, value: bool) -> Result<()> {
414 let value: libc::c_int = value.into();
415 setsockopt(
416 self.as_raw_fd(),
417 libc::SOL_NETLINK,
418 libc::NETLINK_PKTINFO,
419 value,
420 )
421 }
422
423 pub fn get_pktinfo(&self) -> Result<bool> {
424 let res = getsockopt::<libc::c_int>(
425 self.as_raw_fd(),
426 libc::SOL_NETLINK,
427 libc::NETLINK_PKTINFO,
428 )?;
429 Ok(res == 1)
430 }
431
432 pub fn add_membership(&mut self, group: u32) -> Result<()> {
433 setsockopt(
434 self.as_raw_fd(),
435 libc::SOL_NETLINK,
436 libc::NETLINK_ADD_MEMBERSHIP,
437 group,
438 )
439 }
440
441 pub fn drop_membership(&mut self, group: u32) -> Result<()> {
442 setsockopt(
443 self.as_raw_fd(),
444 libc::SOL_NETLINK,
445 libc::NETLINK_DROP_MEMBERSHIP,
446 group,
447 )
448 }
449
450 // pub fn list_membership(&self) -> Vec<u32> {
451 // unimplemented!();
452 // // getsockopt won't be enough here, because we may need to perform 2
453 // calls, and because the // length of the list returned by
454 // libc::getsockopt is returned by mutating the length // argument,
455 // which our implementation of getsockopt forbids. }
456
457 /// `NETLINK_BROADCAST_ERROR` (since Linux 2.6.30). When not set,
458 /// `netlink_broadcast()` only reports `ESRCH` errors and silently
459 /// ignore `NOBUFS` errors.
460 pub fn set_broadcast_error(&mut self, value: bool) -> Result<()> {
461 let value: libc::c_int = value.into();
462 setsockopt(
463 self.as_raw_fd(),
464 libc::SOL_NETLINK,
465 libc::NETLINK_BROADCAST_ERROR,
466 value,
467 )
468 }
469
470 pub fn get_broadcast_error(&self) -> Result<bool> {
471 let res = getsockopt::<libc::c_int>(
472 self.as_raw_fd(),
473 libc::SOL_NETLINK,
474 libc::NETLINK_BROADCAST_ERROR,
475 )?;
476 Ok(res == 1)
477 }
478
479 /// `NETLINK_NO_ENOBUFS` (since Linux 2.6.30). This flag can be used by
480 /// unicast and broadcast listeners to avoid receiving `ENOBUFS` errors.
481 pub fn set_no_enobufs(&mut self, value: bool) -> Result<()> {
482 let value: libc::c_int = value.into();
483 setsockopt(
484 self.as_raw_fd(),
485 libc::SOL_NETLINK,
486 libc::NETLINK_NO_ENOBUFS,
487 value,
488 )
489 }
490
491 pub fn get_no_enobufs(&self) -> Result<bool> {
492 let res = getsockopt::<libc::c_int>(
493 self.as_raw_fd(),
494 libc::SOL_NETLINK,
495 libc::NETLINK_NO_ENOBUFS,
496 )?;
497 Ok(res == 1)
498 }
499
500 /// `NETLINK_LISTEN_ALL_NSID` (since Linux 4.2). When set, this socket will
501 /// receive netlink notifications from all network namespaces that
502 /// have an nsid assigned into the network namespace where the socket
503 /// has been opened. The nsid is sent to user space via an ancillary
504 /// data.
505 pub fn set_listen_all_namespaces(&mut self, value: bool) -> Result<()> {
506 let value: libc::c_int = value.into();
507 setsockopt(
508 self.as_raw_fd(),
509 libc::SOL_NETLINK,
510 libc::NETLINK_LISTEN_ALL_NSID,
511 value,
512 )
513 }
514
515 pub fn get_listen_all_namespaces(&self) -> Result<bool> {
516 let res = getsockopt::<libc::c_int>(
517 self.as_raw_fd(),
518 libc::SOL_NETLINK,
519 libc::NETLINK_LISTEN_ALL_NSID,
520 )?;
521 Ok(res == 1)
522 }
523
524 /// `NETLINK_CAP_ACK` (since Linux 4.2). The kernel may fail to allocate the
525 /// necessary room for the acknowledgment message back to user space.
526 /// This option trims off the payload of the original netlink message.
527 /// The netlink message header is still included, so the user can
528 /// guess from the sequence number which message triggered the
529 /// acknowledgment.
530 pub fn set_cap_ack(&mut self, value: bool) -> Result<()> {
531 let value: libc::c_int = value.into();
532 setsockopt(
533 self.as_raw_fd(),
534 libc::SOL_NETLINK,
535 libc::NETLINK_CAP_ACK,
536 value,
537 )
538 }
539
540 pub fn get_cap_ack(&self) -> Result<bool> {
541 let res = getsockopt::<libc::c_int>(
542 self.as_raw_fd(),
543 libc::SOL_NETLINK,
544 libc::NETLINK_CAP_ACK,
545 )?;
546 Ok(res == 1)
547 }
548
549 /// `NETLINK_EXT_ACK`
550 /// Extended ACK controls reporting of additional error/warning TLVs in
551 /// NLMSG_ERROR and NLMSG_DONE messages.
552 pub fn set_ext_ack(&mut self, value: bool) -> Result<()> {
553 let value: libc::c_int = value.into();
554 setsockopt(
555 self.as_raw_fd(),
556 libc::SOL_NETLINK,
557 libc::NETLINK_EXT_ACK,
558 value,
559 )
560 }
561
562 pub fn get_ext_ack(&self) -> Result<bool> {
563 let res = getsockopt::<libc::c_int>(
564 self.as_raw_fd(),
565 libc::SOL_NETLINK,
566 libc::NETLINK_EXT_ACK,
567 )?;
568 Ok(res == 1)
569 }
570
571 /// Sets socket receive buffer in bytes.
572 /// The kernel doubles this value (to allow space for bookkeeping overhead),
573 /// and this doubled value is returned by [get_rx_buf_sz].(see socket(7)
574 /// The default value is set by the proc/sys/net/core/rmem_default file, and
575 /// the maximum allowed value is set by the /proc/sys/net/core/rmem_max
576 /// file. The minimum (doubled) value for this option is 256.
577 pub fn set_rx_buf_sz<T>(&self, size: T) -> Result<()> {
578 setsockopt(self.as_raw_fd(), libc::SOL_SOCKET, libc::SO_RCVBUF, size)
579 }
580
581 /// Gets socket receive buffer in bytes
582 pub fn get_rx_buf_sz(&self) -> Result<usize> {
583 let res = getsockopt::<libc::c_int>(
584 self.as_raw_fd(),
585 libc::SOL_SOCKET,
586 libc::SO_RCVBUF,
587 )?;
588 Ok(res as usize)
589 }
590
591 /// Set strict input checking(`NETLINK_GET_STRICT_CHK`) in netlink route
592 /// protocol. By default, `NETLINK_GET_STRICT_CHK` is not enabled.
593 pub fn set_netlink_get_strict_chk(&self, value: bool) -> Result<()> {
594 let value: u32 = value.into();
595 setsockopt(
596 self.as_raw_fd(),
597 libc::SOL_NETLINK,
598 libc::NETLINK_GET_STRICT_CHK,
599 value,
600 )
601 }
602}
603
604/// Wrapper around `getsockopt`:
605///
606/// ```no_rust
607/// int getsockopt(int socket, int level, int option_name, void *restrict option_value, socklen_t *restrict option_len);
608/// ```
609pub(crate) fn getsockopt<T: Copy>(
610 fd: RawFd,
611 level: libc::c_int,
612 option: libc::c_int,
613) -> Result<T> {
614 // Create storage for the options we're fetching
615 let mut slot: T = unsafe { mem::zeroed() };
616
617 // Create a mutable raw pointer to the storage so that getsockopt can fill
618 // the value
619 let slot_ptr = &mut slot as *mut T as *mut libc::c_void;
620
621 // Let getsockopt know how big our storage is
622 let mut slot_len = mem::size_of::<T>() as libc::socklen_t;
623
624 // getsockopt takes a mutable pointer to the length, because for some
625 // options like NETLINK_LIST_MEMBERSHIP where the option value is a list
626 // with arbitrary length, getsockopt uses this parameter to signal how
627 // big the storage needs to be.
628 let slot_len_ptr = &mut slot_len as *mut libc::socklen_t;
629
630 let res =
631 unsafe { libc::getsockopt(fd, level, option, slot_ptr, slot_len_ptr) };
632 if res < 0 {
633 return Err(Error::last_os_error());
634 }
635
636 // Ignore the options that require the legnth to be set by getsockopt.
637 // We'll deal with them individually.
638 assert_eq!(slot_len as usize, mem::size_of::<T>());
639
640 Ok(slot)
641}
642
643// adapted from rust standard library
644fn setsockopt<T>(
645 fd: RawFd,
646 level: libc::c_int,
647 option: libc::c_int,
648 payload: T,
649) -> Result<()> {
650 let payload = &payload as *const T as *const libc::c_void;
651 let payload_len = mem::size_of::<T>() as libc::socklen_t;
652
653 let res =
654 unsafe { libc::setsockopt(fd, level, option, payload, payload_len) };
655 if res < 0 {
656 return Err(Error::last_os_error());
657 }
658 Ok(())
659}
660
661#[cfg(test)]
662mod test {
663 use super::*;
664 use crate::protocols::NETLINK_ROUTE;
665
666 #[test]
667 fn new() {
668 Socket::new(NETLINK_ROUTE).unwrap();
669 }
670
671 #[test]
672 fn connect() {
673 let sock = Socket::new(NETLINK_ROUTE).unwrap();
674 sock.connect(&SocketAddr::new(0, 0)).unwrap();
675 }
676
677 #[test]
678 fn bind() {
679 let mut sock = Socket::new(NETLINK_ROUTE).unwrap();
680 sock.bind(&SocketAddr::new(4321, 0)).unwrap();
681 }
682
683 #[test]
684 fn bind_auto() {
685 let mut sock = Socket::new(NETLINK_ROUTE).unwrap();
686 let addr = sock.bind_auto().unwrap();
687 // make sure that the address we got from the kernel is there
688 assert!(addr.port_number() != 0);
689 }
690
691 #[test]
692 fn set_non_blocking() {
693 let sock = Socket::new(NETLINK_ROUTE).unwrap();
694 sock.set_non_blocking(true).unwrap();
695 sock.set_non_blocking(false).unwrap();
696 }
697
698 #[test]
699 fn options() {
700 let mut sock = Socket::new(NETLINK_ROUTE).unwrap();
701
702 sock.set_cap_ack(true).unwrap();
703 assert!(sock.get_cap_ack().unwrap());
704 sock.set_cap_ack(false).unwrap();
705 assert!(!sock.get_cap_ack().unwrap());
706
707 sock.set_no_enobufs(true).unwrap();
708 assert!(sock.get_no_enobufs().unwrap());
709 sock.set_no_enobufs(false).unwrap();
710 assert!(!sock.get_no_enobufs().unwrap());
711
712 sock.set_broadcast_error(true).unwrap();
713 assert!(sock.get_broadcast_error().unwrap());
714 sock.set_broadcast_error(false).unwrap();
715 assert!(!sock.get_broadcast_error().unwrap());
716
717 // FIXME: these require root permissions
718 // sock.set_listen_all_namespaces(true).unwrap();
719 // assert!(sock.get_listen_all_namespaces().unwrap());
720 // sock.set_listen_all_namespaces(false).unwrap();
721 // assert!(!sock.get_listen_all_namespaces().unwrap());
722 }
723}