diff options
author | Gleb Pomykalov <gleb@lancastr.com> | 2019-02-18 14:36:46 +0300 |
---|---|---|
committer | Gleb Pomykalov <gleb@lancastr.com> | 2019-03-12 13:48:45 +0300 |
commit | 6968cb4ab02ec0250cf9859b6d85b04a36dc1ed2 (patch) | |
tree | 9a3c6b2d99629fad8f01400b5e450da28d63c995 | |
parent | 2d0d3609a47c8a684dfc094f81164ca5da1e38d7 (diff) | |
download | nix-6968cb4ab02ec0250cf9859b6d85b04a36dc1ed2.zip |
Support AF_ALG
-rw-r--r-- | CHANGELOG.md | 3 | ||||
-rw-r--r-- | src/sys/socket/addr.rs | 80 | ||||
-rw-r--r-- | src/sys/socket/mod.rs | 111 | ||||
-rw-r--r-- | src/sys/socket/sockopt.rs | 49 | ||||
-rw-r--r-- | test/sys/test_socket.rs | 149 |
5 files changed, 381 insertions, 11 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 81f94c24..7bb653e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,9 +7,10 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Added - Add IP_RECVIF & IP_RECVDSTADDR. Enable IP_PKTINFO and IP6_PKTINFO on netbsd/openbsd. ([#1002](https://github.com/nix-rust/nix/pull/1002)) - - Added `inotify_init1`, `inotify_add_watch` and `inotify_rm_watch` wrappers for Android and Linux. ([#1016](https://github.com/nix-rust/nix/pull/1016)) +- Add `ALG_SET_IV`, `ALG_SET_OP` and `ALG_SET_AEAD_ASSOCLEN` control messages and `AF_ALG` + socket types on Linux and Android ([#1031](https://github.com/nix-rust/nix/pull/1031)) ### Changed - `PollFd` event flags renamed to `PollFlags` ([#1024](https://github.com/nix-rust/nix/pull/1024/)) diff --git a/src/sys/socket/addr.rs b/src/sys/socket/addr.rs index db62b001..13459e9b 100644 --- a/src/sys/socket/addr.rs +++ b/src/sys/socket/addr.rs @@ -8,6 +8,8 @@ use std::path::Path; use std::os::unix::ffi::OsStrExt; #[cfg(any(target_os = "android", target_os = "linux"))] use ::sys::socket::addr::netlink::NetlinkAddr; +#[cfg(any(target_os = "android", target_os = "linux"))] +use ::sys::socket::addr::alg::AlgAddr; #[cfg(any(target_os = "ios", target_os = "macos"))] use std::os::unix::io::RawFd; #[cfg(any(target_os = "ios", target_os = "macos"))] @@ -740,6 +742,8 @@ pub enum SockAddr { Unix(UnixAddr), #[cfg(any(target_os = "android", target_os = "linux"))] Netlink(NetlinkAddr), + #[cfg(any(target_os = "android", target_os = "linux"))] + Alg(AlgAddr), #[cfg(any(target_os = "ios", target_os = "macos"))] SysControl(SysControlAddr), /// Datalink address (MAC) @@ -768,6 +772,11 @@ impl SockAddr { SockAddr::Netlink(NetlinkAddr::new(pid, groups)) } + #[cfg(any(target_os = "android", target_os = "linux"))] + pub fn new_alg(alg_type: &str, alg_name: &str) -> SockAddr { + SockAddr::Alg(AlgAddr::new(alg_type, alg_name)) + } + #[cfg(any(target_os = "ios", target_os = "macos"))] pub fn new_sys_control(sockfd: RawFd, name: &str, unit: u32) -> Result<SockAddr> { SysControlAddr::from_name(sockfd, name, unit).map(|a| SockAddr::SysControl(a)) @@ -780,6 +789,8 @@ impl SockAddr { SockAddr::Unix(..) => AddressFamily::Unix, #[cfg(any(target_os = "android", target_os = "linux"))] SockAddr::Netlink(..) => AddressFamily::Netlink, + #[cfg(any(target_os = "android", target_os = "linux"))] + SockAddr::Alg(..) => AddressFamily::Alg, #[cfg(any(target_os = "ios", target_os = "macos"))] SockAddr::SysControl(..) => AddressFamily::System, #[cfg(any(target_os = "android", target_os = "linux"))] @@ -856,6 +867,8 @@ impl SockAddr { SockAddr::Unix(UnixAddr(ref addr, len)) => (mem::transmute(addr), (len + offset_of!(libc::sockaddr_un, sun_path)) as libc::socklen_t), #[cfg(any(target_os = "android", target_os = "linux"))] SockAddr::Netlink(NetlinkAddr(ref sa)) => (mem::transmute(sa), mem::size_of::<libc::sockaddr_nl>() as libc::socklen_t), + #[cfg(any(target_os = "android", target_os = "linux"))] + SockAddr::Alg(AlgAddr(ref sa)) => (mem::transmute(sa), mem::size_of::<libc::sockaddr_alg>() as libc::socklen_t), #[cfg(any(target_os = "ios", target_os = "macos"))] SockAddr::SysControl(SysControlAddr(ref sa)) => (mem::transmute(sa), mem::size_of::<libc::sockaddr_ctl>() as libc::socklen_t), #[cfg(any(target_os = "android", target_os = "linux"))] @@ -910,6 +923,8 @@ impl hash::Hash for SockAddr { SockAddr::Unix(ref a) => a.hash(s), #[cfg(any(target_os = "android", target_os = "linux"))] SockAddr::Netlink(ref a) => a.hash(s), + #[cfg(any(target_os = "android", target_os = "linux"))] + SockAddr::Alg(ref a) => a.hash(s), #[cfg(any(target_os = "ios", target_os = "macos"))] SockAddr::SysControl(ref a) => a.hash(s), #[cfg(any(target_os = "android", @@ -938,6 +953,8 @@ impl fmt::Display for SockAddr { SockAddr::Unix(ref unix) => unix.fmt(f), #[cfg(any(target_os = "android", target_os = "linux"))] SockAddr::Netlink(ref nl) => nl.fmt(f), + #[cfg(any(target_os = "android", target_os = "linux"))] + SockAddr::Alg(ref nl) => nl.fmt(f), #[cfg(any(target_os = "ios", target_os = "macos"))] SockAddr::SysControl(ref sc) => sc.fmt(f), #[cfg(any(target_os = "android", @@ -1014,6 +1031,69 @@ pub mod netlink { } } +#[cfg(any(target_os = "android", target_os = "linux"))] +pub mod alg { + use libc::{AF_ALG, sockaddr_alg, c_char}; + use std::{fmt, mem, str}; + use std::hash::{Hash, Hasher}; + use std::ffi::CStr; + + #[derive(Copy, Clone)] + pub struct AlgAddr(pub sockaddr_alg); + + // , PartialEq, Eq, Debug, Hash + impl PartialEq for AlgAddr { + fn eq(&self, other: &Self) -> bool { + let (inner, other) = (self.0, other.0); + (inner.salg_family, &inner.salg_type[..], inner.salg_feat, inner.salg_mask, &inner.salg_name[..]) == + (other.salg_family, &other.salg_type[..], other.salg_feat, other.salg_mask, &other.salg_name[..]) + } + } + + impl Eq for AlgAddr {} + + impl Hash for AlgAddr { + fn hash<H: Hasher>(&self, s: &mut H) { + let inner = self.0; + (inner.salg_family, &inner.salg_type[..], inner.salg_feat, inner.salg_mask, &inner.salg_name[..]).hash(s); + } + } + + impl AlgAddr { + pub fn new(alg_type: &str, alg_name: &str) -> AlgAddr { + let mut addr: sockaddr_alg = unsafe { mem::zeroed() }; + addr.salg_family = AF_ALG as u16; + addr.salg_type[..alg_type.len()].copy_from_slice(alg_type.to_string().as_bytes()); + addr.salg_name[..alg_name.len()].copy_from_slice(alg_name.to_string().as_bytes()); + + AlgAddr(addr) + } + + + pub fn alg_type(&self) -> &CStr { + unsafe { CStr::from_ptr(self.0.salg_type.as_ptr() as *const c_char) } + } + + pub fn alg_name(&self) -> &CStr { + unsafe { CStr::from_ptr(self.0.salg_name.as_ptr() as *const c_char) } + } + } + + impl fmt::Display for AlgAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "type: {} alg: {}", + self.alg_name().to_string_lossy(), + self.alg_type().to_string_lossy()) + } + } + + impl fmt::Debug for AlgAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self, f) + } + } +} + #[cfg(any(target_os = "ios", target_os = "macos"))] pub mod sys_control { use ::sys::socket::addr::AddressFamily; diff --git a/src/sys/socket/mod.rs b/src/sys/socket/mod.rs index 80009917..5a054b4b 100644 --- a/src/sys/socket/mod.rs +++ b/src/sys/socket/mod.rs @@ -31,6 +31,8 @@ pub use self::addr::{ }; #[cfg(any(target_os = "android", target_os = "linux"))] pub use ::sys::socket::addr::netlink::NetlinkAddr; +#[cfg(any(target_os = "android", target_os = "linux"))] +pub use sys::socket::addr::alg::AlgAddr; pub use libc::{ cmsghdr, @@ -700,6 +702,37 @@ pub enum ControlMessage<'a> { // and put that in here instead of a raw ucred. #[cfg(any(target_os = "android", target_os = "linux"))] ScmCredentials(&'a libc::ucred), + + /// Set IV for `AF_ALG` crypto API. + /// + /// For further information, please refer to the + /// [`documentation`](https://kernel.readthedocs.io/en/sphinx-samples/crypto-API.html) + #[cfg(any( + target_os = "android", + target_os = "linux", + ))] + AlgSetIv(&'a [u8]), + /// Set crypto operation for `AF_ALG` crypto API. It may be one of + /// `ALG_OP_ENCRYPT` or `ALG_OP_DECRYPT` + /// + /// For further information, please refer to the + /// [`documentation`](https://kernel.readthedocs.io/en/sphinx-samples/crypto-API.html) + #[cfg(any( + target_os = "android", + target_os = "linux", + ))] + AlgSetOp(&'a libc::c_int), + /// Set the length of associated authentication data (AAD) (applicable only to AEAD algorithms) + /// for `AF_ALG` crypto API. + /// + /// For further information, please refer to the + /// [`documentation`](https://kernel.readthedocs.io/en/sphinx-samples/crypto-API.html) + #[cfg(any( + target_os = "android", + target_os = "linux", + ))] + AlgSetAeadAssoclen(&'a u32), + } // An opaque structure used to prevent cmsghdr from being a public type @@ -729,8 +762,8 @@ impl<'a> ControlMessage<'a> { } /// Return a reference to the payload data as a byte pointer - fn data(&self) -> *const u8 { - match self { + fn copy_to_cmsg_data(&self, cmsg_data: *mut u8) { + let data_ptr = match self { &ControlMessage::ScmRights(fds) => { fds as *const _ as *const u8 }, @@ -738,7 +771,35 @@ impl<'a> ControlMessage<'a> { &ControlMessage::ScmCredentials(creds) => { creds as *const libc::ucred as *const u8 } - } + #[cfg(any(target_os = "android", target_os = "linux"))] + &ControlMessage::AlgSetIv(iv) => { + unsafe { + let alg_iv = cmsg_data as *mut libc::af_alg_iv; + (*alg_iv).ivlen = iv.len() as u32; + ptr::copy_nonoverlapping( + iv.as_ptr(), + (*alg_iv).iv.as_mut_ptr(), + iv.len() + ); + }; + return + }, + #[cfg(any(target_os = "android", target_os = "linux"))] + &ControlMessage::AlgSetOp(op) => { + op as *const _ as *const u8 + }, + #[cfg(any(target_os = "android", target_os = "linux"))] + &ControlMessage::AlgSetAeadAssoclen(len) => { + len as *const _ as *const u8 + }, + }; + unsafe { + ptr::copy_nonoverlapping( + data_ptr, + cmsg_data, + self.len() + ) + }; } /// The size of the payload, excluding its cmsghdr @@ -751,6 +812,18 @@ impl<'a> ControlMessage<'a> { &ControlMessage::ScmCredentials(creds) => { mem::size_of_val(creds) } + #[cfg(any(target_os = "android", target_os = "linux"))] + &ControlMessage::AlgSetIv(iv) => { + mem::size_of::<libc::af_alg_iv>() + iv.len() + }, + #[cfg(any(target_os = "android", target_os = "linux"))] + &ControlMessage::AlgSetOp(op) => { + mem::size_of_val(op) + }, + #[cfg(any(target_os = "android", target_os = "linux"))] + &ControlMessage::AlgSetAeadAssoclen(len) => { + mem::size_of_val(len) + }, } } @@ -760,6 +833,10 @@ impl<'a> ControlMessage<'a> { &ControlMessage::ScmRights(_) => libc::SOL_SOCKET, #[cfg(any(target_os = "android", target_os = "linux"))] &ControlMessage::ScmCredentials(_) => libc::SOL_SOCKET, + #[cfg(any(target_os = "android", target_os = "linux"))] + &ControlMessage::AlgSetIv(_) | &ControlMessage::AlgSetOp(_) | &ControlMessage::AlgSetAeadAssoclen(_) => { + libc::SOL_ALG + }, } } @@ -769,6 +846,18 @@ impl<'a> ControlMessage<'a> { &ControlMessage::ScmRights(_) => libc::SCM_RIGHTS, #[cfg(any(target_os = "android", target_os = "linux"))] &ControlMessage::ScmCredentials(_) => libc::SCM_CREDENTIALS, + #[cfg(any(target_os = "android", target_os = "linux"))] + &ControlMessage::AlgSetIv(_) => { + libc::ALG_SET_IV + }, + #[cfg(any(target_os = "android", target_os = "linux"))] + &ControlMessage::AlgSetOp(_) => { + libc::ALG_SET_OP + }, + #[cfg(any(target_os = "android", target_os = "linux"))] + &ControlMessage::AlgSetAeadAssoclen(_) => { + libc::ALG_SET_AEAD_ASSOCLEN + }, } } @@ -778,12 +867,7 @@ impl<'a> ControlMessage<'a> { (*cmsg).cmsg_level = self.cmsg_level(); (*cmsg).cmsg_type = self.cmsg_type(); (*cmsg).cmsg_len = self.cmsg_len(); - let data = self.data(); - ptr::copy_nonoverlapping( - data, - CMSG_DATA(cmsg), - self.len() - ); + self.copy_to_cmsg_data(CMSG_DATA(cmsg)); } } @@ -1098,6 +1182,8 @@ pub enum SockLevel { Udp = libc::IPPROTO_UDP, #[cfg(any(target_os = "android", target_os = "linux"))] Netlink = libc::SOL_NETLINK, + #[cfg(any(target_os = "android", target_os = "linux"))] + Alg = libc::SOL_ALG, } /// Represents a socket option that can be accessed or set. Used as an argument @@ -1111,7 +1197,7 @@ pub trait GetSockOpt : Copy { /// Represents a socket option that can be accessed or set. Used as an argument /// to `setsockopt` -pub trait SetSockOpt : Copy { +pub trait SetSockOpt : Clone { type Val; #[doc(hidden)] @@ -1212,6 +1298,11 @@ pub unsafe fn sockaddr_storage_to_addr( use libc::sockaddr_nl; Ok(SockAddr::Netlink(NetlinkAddr(*(addr as *const _ as *const sockaddr_nl)))) } + #[cfg(any(target_os = "android", target_os = "linux"))] + libc::AF_ALG => { + use libc::sockaddr_alg; + Ok(SockAddr::Alg(AlgAddr(*(addr as *const _ as *const sockaddr_alg)))) + } af => panic!("unexpected address family {}", af), } } diff --git a/src/sys/socket/sockopt.rs b/src/sys/socket/sockopt.rs index 1920987e..a489eaff 100644 --- a/src/sys/socket/sockopt.rs +++ b/src/sys/socket/sockopt.rs @@ -307,6 +307,55 @@ sockopt_impl!(Both, Ipv4RecvIf, libc::IPPROTO_IP, libc::IP_RECVIF, bool); sockopt_impl!(Both, Ipv4RecvDstAddr, libc::IPPROTO_IP, libc::IP_RECVDSTADDR, bool); +#[cfg(any(target_os = "android", target_os = "linux"))] +#[derive(Copy, Clone, Debug)] +pub struct AlgSetAeadAuthSize; + +// ALG_SET_AEAD_AUTH_SIZE read the length from passed `option_len` +// See https://elixir.bootlin.com/linux/v4.4/source/crypto/af_alg.c#L222 +#[cfg(any(target_os = "android", target_os = "linux"))] +impl SetSockOpt for AlgSetAeadAuthSize { + type Val = usize; + + fn set(&self, fd: RawFd, val: &usize) -> Result<()> { + unsafe { + let res = libc::setsockopt(fd, + libc::SOL_ALG, + libc::ALG_SET_AEAD_AUTHSIZE, + ::std::ptr::null(), + *val as libc::socklen_t); + Errno::result(res).map(drop) + } + } +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +#[derive(Clone, Debug)] +pub struct AlgSetKey<T>(::std::marker::PhantomData<T>); + +#[cfg(any(target_os = "android", target_os = "linux"))] +impl<T> Default for AlgSetKey<T> { + fn default() -> Self { + AlgSetKey(Default::default()) + } +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +impl<T> SetSockOpt for AlgSetKey<T> where T: AsRef<[u8]> + Clone { + type Val = T; + + fn set(&self, fd: RawFd, val: &T) -> Result<()> { + unsafe { + let res = libc::setsockopt(fd, + libc::SOL_ALG, + libc::ALG_SET_KEY, + val.as_ref().as_ptr() as *const _, + val.as_ref().len() as libc::socklen_t); + Errno::result(res).map(drop) + } + } +} + /* * * ===== Accessor helpers ===== diff --git a/test/sys/test_socket.rs b/test/sys/test_socket.rs index 5d55c87d..2790f681 100644 --- a/test/sys/test_socket.rs +++ b/test/sys/test_socket.rs @@ -186,6 +186,155 @@ pub fn test_scm_rights() { close(w).unwrap(); } +// Disable the test on emulated platforms due to not enabled support of AF_ALG in QEMU from rust cross +#[cfg_attr(not(any(target_arch = "x86_64", target_arch = "i686")), ignore)] +#[cfg(any(target_os = "linux", target_os= "android"))] +#[test] +pub fn test_af_alg_cipher() { + use libc; + use nix::sys::uio::IoVec; + use nix::unistd::read; + use nix::sys::socket::{socket, sendmsg, bind, accept, setsockopt, + AddressFamily, SockType, SockFlag, SockAddr, + ControlMessage, MsgFlags}; + use nix::sys::socket::sockopt::AlgSetKey; + + let alg_type = "skcipher"; + let alg_name = "ctr(aes)"; + // 256-bits secret key + let key = vec![0u8; 32]; + // 16-bytes IV + let iv_len = 16; + let iv = vec![1u8; iv_len]; + // 256-bytes plain payload + let payload_len = 256; + let payload = vec![2u8; payload_len]; + + let sock = socket(AddressFamily::Alg, SockType::SeqPacket, SockFlag::empty(), None) + .expect("socket failed"); + + let sockaddr = SockAddr::new_alg(alg_type, alg_name); + bind(sock, &sockaddr).expect("bind failed"); + + if let SockAddr::Alg(alg) = sockaddr { + assert_eq!(alg.alg_name().to_string_lossy(), alg_name); + assert_eq!(alg.alg_type().to_string_lossy(), alg_type); + } else { + panic!("unexpected SockAddr"); + } + + setsockopt(sock, AlgSetKey::default(), &key).expect("setsockopt"); + let session_socket = accept(sock).expect("accept failed"); + + let msgs = [ControlMessage::AlgSetOp(&libc::ALG_OP_ENCRYPT), ControlMessage::AlgSetIv(iv.as_slice())]; + let iov = IoVec::from_slice(&payload); + sendmsg(session_socket, &[iov], &msgs, MsgFlags::empty(), None).expect("sendmsg encrypt"); + + // allocate buffer for encrypted data + let mut encrypted = vec![0u8; payload_len]; + let num_bytes = read(session_socket, &mut encrypted).expect("read encrypt"); + assert_eq!(num_bytes, payload_len); + + let iov = IoVec::from_slice(&encrypted); + + let iv = vec![1u8; iv_len]; + + let msgs = [ControlMessage::AlgSetOp(&libc::ALG_OP_DECRYPT), ControlMessage::AlgSetIv(iv.as_slice())]; + sendmsg(session_socket, &[iov], &msgs, MsgFlags::empty(), None).expect("sendmsg decrypt"); + + // allocate buffer for decrypted data + let mut decrypted = vec![0u8; payload_len]; + let num_bytes = read(session_socket, &mut decrypted).expect("read decrypt"); + + assert_eq!(num_bytes, payload_len); + assert_eq!(decrypted, payload); +} + +// Disable the test on emulated platforms due to not enabled support of AF_ALG in QEMU from rust cross +#[cfg_attr(not(any(target_arch = "x86_64", target_arch = "i686")), ignore)] +#[cfg(any(target_os = "linux", target_os= "android"))] +#[test] +pub fn test_af_alg_aead() { + use libc; + use nix::sys::uio::IoVec; + use nix::unistd::{read, close}; + use nix::sys::socket::{socket, sendmsg, bind, accept, setsockopt, + AddressFamily, SockType, SockFlag, SockAddr, + ControlMessage, MsgFlags}; + use nix::sys::socket::sockopt::{AlgSetKey, AlgSetAeadAuthSize}; + + let auth_size = 4usize; + let assoc_size = 16u32; + + let alg_type = "aead"; + let alg_name = "gcm(aes)"; + // 256-bits secret key + let key = vec![0u8; 32]; + // 12-bytes IV + let iv_len = 12; + let iv = vec![1u8; iv_len]; + // 256-bytes plain payload + let payload_len = 256; + let mut payload = vec![2u8; payload_len + (assoc_size as usize) + auth_size]; + + for i in 0..assoc_size { + payload[i as usize] = 10; + } + + let len = payload.len(); + + for i in 0..auth_size { + payload[len - 1 - i] = 0; + } + + let sock = socket(AddressFamily::Alg, SockType::SeqPacket, SockFlag::empty(), None) + .expect("socket failed"); + + let sockaddr = SockAddr::new_alg(alg_type, alg_name); + bind(sock, &sockaddr).expect("bind failed"); + + setsockopt(sock, AlgSetAeadAuthSize, &auth_size).expect("setsockopt AlgSetAeadAuthSize"); + setsockopt(sock, AlgSetKey::default(), &key).expect("setsockopt AlgSetKey"); + let session_socket = accept(sock).expect("accept failed"); + + let msgs = [ + ControlMessage::AlgSetOp(&libc::ALG_OP_ENCRYPT), + ControlMessage::AlgSetIv(iv.as_slice()), + ControlMessage::AlgSetAeadAssoclen(&assoc_size)]; + let iov = IoVec::from_slice(&payload); + sendmsg(session_socket, &[iov], &msgs, MsgFlags::empty(), None).expect("sendmsg encrypt"); + + // allocate buffer for encrypted data + let mut encrypted = vec![0u8; (assoc_size as usize) + payload_len + auth_size]; + let num_bytes = read(session_socket, &mut encrypted).expect("read encrypt"); + assert_eq!(num_bytes, payload_len + auth_size + (assoc_size as usize)); + close(session_socket).expect("close"); + + for i in 0..assoc_size { + encrypted[i as usize] = 10; + } + + let iov = IoVec::from_slice(&encrypted); + + let iv = vec![1u8; iv_len]; + + let session_socket = accept(sock).expect("accept failed"); + + let msgs = [ + ControlMessage::AlgSetOp(&libc::ALG_OP_DECRYPT), + ControlMessage::AlgSetIv(iv.as_slice()), + ControlMessage::AlgSetAeadAssoclen(&assoc_size), + ]; + sendmsg(session_socket, &[iov], &msgs, MsgFlags::empty(), None).expect("sendmsg decrypt"); + + // allocate buffer for decrypted data + let mut decrypted = vec![0u8; payload_len + (assoc_size as usize) + auth_size]; + let num_bytes = read(session_socket, &mut decrypted).expect("read decrypt"); + + assert!(num_bytes >= payload_len + (assoc_size as usize)); + assert_eq!(decrypted[(assoc_size as usize)..(payload_len + (assoc_size as usize))], payload[(assoc_size as usize)..payload_len + (assoc_size as usize)]); +} + /// Tests that passing multiple fds using a single `ControlMessage` works. // Disable the test on emulated platforms due to a bug in QEMU versions < // 2.12.0. https://bugs.launchpad.net/qemu/+bug/1701808 |