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}