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}