Skip to main content

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