Skip to content

Commit b06c26f

Browse files
authored
Merge branch 'master' into socket-mss
2 parents 67b4872 + 53fea96 commit b06c26f

File tree

6 files changed

+222
-2
lines changed

6 files changed

+222
-2
lines changed

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,12 @@ This project adheres to [Semantic Versioning](https://semver.org/).
3434
(#[1503](https://github.com/nix-rust/nix/pull/1503))
3535
- Enabled `pwritev` and `preadv` for more operating systems.
3636
(#[1511](https://github.com/nix-rust/nix/pull/1511))
37-
- Added support for `TCP_MAXSEG` TCP Maximum Segment Size socket options
37+
Added support for `TCP_MAXSEG` TCP Maximum Segment Size socket options
3838
(#[1292](https://github.com/nix-rust/nix/pull/1292))
39+
- Added `Ipv4RecvErr` and `Ipv6RecvErr` sockopts and associated control messages.
40+
(#[1514](https://github.com/nix-rust/nix/pull/1514))
41+
- Added `AsRawFd` implementation on `PollFd`.
42+
(#[1516](https://github.com/nix-rust/nix/pull/1516))
3943

4044
### Changed
4145

src/poll.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use crate::sys::time::TimeSpec;
44
#[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "linux"))]
55
use crate::sys::signal::SigSet;
6-
use std::os::unix::io::RawFd;
6+
use std::os::unix::io::{AsRawFd, RawFd};
77

88
use crate::Result;
99
use crate::errno::Errno;
@@ -41,6 +41,12 @@ impl PollFd {
4141
}
4242
}
4343

44+
impl AsRawFd for PollFd {
45+
fn as_raw_fd(&self) -> RawFd {
46+
self.pollfd.fd
47+
}
48+
}
49+
4450
libc_bitflags! {
4551
/// These flags define the different events that can be monitored by `poll` and `ppoll`
4652
pub struct PollFlags: libc::c_short {

src/sys/socket/mod.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -653,6 +653,13 @@ pub enum ControlMessageOwned {
653653
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
654654
RxqOvfl(u32),
655655

656+
/// Socket error queue control messages read with the `MSG_ERRQUEUE` flag.
657+
#[cfg(any(target_os = "android", target_os = "linux"))]
658+
Ipv4RecvErr(libc::sock_extended_err, Option<sockaddr_in>),
659+
/// Socket error queue control messages read with the `MSG_ERRQUEUE` flag.
660+
#[cfg(any(target_os = "android", target_os = "linux"))]
661+
Ipv6RecvErr(libc::sock_extended_err, Option<sockaddr_in6>),
662+
656663
/// Catch-all variant for unimplemented cmsg types.
657664
#[doc(hidden)]
658665
Unknown(UnknownCmsg),
@@ -756,13 +763,41 @@ impl ControlMessageOwned {
756763
let drop_counter = ptr::read_unaligned(p as *const u32);
757764
ControlMessageOwned::RxqOvfl(drop_counter)
758765
},
766+
#[cfg(any(target_os = "android", target_os = "linux"))]
767+
(libc::IPPROTO_IP, libc::IP_RECVERR) => {
768+
let (err, addr) = Self::recv_err_helper::<sockaddr_in>(p, len);
769+
ControlMessageOwned::Ipv4RecvErr(err, addr)
770+
},
771+
#[cfg(any(target_os = "android", target_os = "linux"))]
772+
(libc::IPPROTO_IPV6, libc::IPV6_RECVERR) => {
773+
let (err, addr) = Self::recv_err_helper::<sockaddr_in6>(p, len);
774+
ControlMessageOwned::Ipv6RecvErr(err, addr)
775+
},
759776
(_, _) => {
760777
let sl = slice::from_raw_parts(p, len);
761778
let ucmsg = UnknownCmsg(*header, Vec::<u8>::from(sl));
762779
ControlMessageOwned::Unknown(ucmsg)
763780
}
764781
}
765782
}
783+
784+
#[cfg(any(target_os = "android", target_os = "linux"))]
785+
unsafe fn recv_err_helper<T>(p: *mut libc::c_uchar, len: usize) -> (libc::sock_extended_err, Option<T>) {
786+
let ee = p as *const libc::sock_extended_err;
787+
let err = ptr::read_unaligned(ee);
788+
789+
// For errors originating on the network, SO_EE_OFFENDER(ee) points inside the p[..len]
790+
// CMSG_DATA buffer. For local errors, there is no address included in the control
791+
// message, and SO_EE_OFFENDER(ee) points beyond the end of the buffer. So, we need to
792+
// validate that the address object is in-bounds before we attempt to copy it.
793+
let addrp = libc::SO_EE_OFFENDER(ee) as *const T;
794+
795+
if addrp.offset(1) as usize - (p as usize) > len {
796+
(err, None)
797+
} else {
798+
(err, Some(ptr::read_unaligned(addrp)))
799+
}
800+
}
766801
}
767802

768803
/// A type-safe zero-copy wrapper around a single control message, as used wih

src/sys/socket/sockopt.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,10 @@ sockopt_impl!(Both, UdpGroSegment, libc::IPPROTO_UDP, libc::UDP_GRO, bool);
352352
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
353353
sockopt_impl!(Both, RxqOvfl, libc::SOL_SOCKET, libc::SO_RXQ_OVFL, libc::c_int);
354354
sockopt_impl!(Both, Ipv6V6Only, libc::IPPROTO_IPV6, libc::IPV6_V6ONLY, bool);
355+
#[cfg(any(target_os = "android", target_os = "linux"))]
356+
sockopt_impl!(Both, Ipv4RecvErr, libc::IPPROTO_IP, libc::IP_RECVERR, bool);
357+
#[cfg(any(target_os = "android", target_os = "linux"))]
358+
sockopt_impl!(Both, Ipv6RecvErr, libc::IPPROTO_IPV6, libc::IPV6_RECVERR, bool);
355359

356360
#[cfg(any(target_os = "android", target_os = "linux"))]
357361
#[derive(Copy, Clone, Debug)]

test/sys/test_socket.rs

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,7 @@ pub fn test_af_alg_cipher() {
712712
#[test]
713713
pub fn test_af_alg_aead() {
714714
use libc::{ALG_OP_DECRYPT, ALG_OP_ENCRYPT};
715+
use nix::fcntl::{fcntl, FcntlArg, OFlag};
715716
use nix::sys::uio::IoVec;
716717
use nix::unistd::{read, close};
717718
use nix::sys::socket::{socket, sendmsg, bind, accept, setsockopt,
@@ -790,6 +791,11 @@ pub fn test_af_alg_aead() {
790791

791792
// allocate buffer for decrypted data
792793
let mut decrypted = vec![0u8; payload_len + (assoc_size as usize) + auth_size];
794+
// Starting with kernel 4.9, the interface changed slightly such that the
795+
// authentication tag memory is only needed in the output buffer for encryption
796+
// and in the input buffer for decryption.
797+
// Do not block on read, as we may have fewer bytes than buffer size
798+
fcntl(session_socket,FcntlArg::F_SETFL(OFlag::O_NONBLOCK)).expect("fcntl non_blocking");
793799
let num_bytes = read(session_socket, &mut decrypted).expect("read decrypt");
794800

795801
assert!(num_bytes >= payload_len + (assoc_size as usize));
@@ -1789,3 +1795,160 @@ fn test_recvmsg_rxq_ovfl() {
17891795
nix::unistd::close(in_socket).unwrap();
17901796
nix::unistd::close(out_socket).unwrap();
17911797
}
1798+
1799+
#[cfg(any(
1800+
target_os = "linux",
1801+
target_os = "android",
1802+
))]
1803+
mod linux_errqueue {
1804+
use nix::sys::socket::*;
1805+
use super::{FromStr, SocketAddr};
1806+
1807+
// Send a UDP datagram to a bogus destination address and observe an ICMP error (v4).
1808+
//
1809+
// Disable the test on QEMU because QEMU emulation of IP_RECVERR is broken (as documented on PR
1810+
// #1514).
1811+
#[cfg_attr(qemu, ignore)]
1812+
#[test]
1813+
fn test_recverr_v4() {
1814+
#[repr(u8)]
1815+
enum IcmpTypes {
1816+
DestUnreach = 3, // ICMP_DEST_UNREACH
1817+
}
1818+
#[repr(u8)]
1819+
enum IcmpUnreachCodes {
1820+
PortUnreach = 3, // ICMP_PORT_UNREACH
1821+
}
1822+
1823+
test_recverr_impl::<sockaddr_in, _, _>(
1824+
"127.0.0.1:6800",
1825+
AddressFamily::Inet,
1826+
sockopt::Ipv4RecvErr,
1827+
libc::SO_EE_ORIGIN_ICMP,
1828+
IcmpTypes::DestUnreach as u8,
1829+
IcmpUnreachCodes::PortUnreach as u8,
1830+
// Closure handles protocol-specific testing and returns generic sock_extended_err for
1831+
// protocol-independent test impl.
1832+
|cmsg| {
1833+
if let ControlMessageOwned::Ipv4RecvErr(ext_err, err_addr) = cmsg {
1834+
if let Some(origin) = err_addr {
1835+
// Validate that our network error originated from 127.0.0.1:0.
1836+
assert_eq!(origin.sin_family, AddressFamily::Inet as _);
1837+
assert_eq!(Ipv4Addr(origin.sin_addr), Ipv4Addr::new(127, 0, 0, 1));
1838+
assert_eq!(origin.sin_port, 0);
1839+
} else {
1840+
panic!("Expected some error origin");
1841+
}
1842+
return *ext_err
1843+
} else {
1844+
panic!("Unexpected control message {:?}", cmsg);
1845+
}
1846+
},
1847+
)
1848+
}
1849+
1850+
// Essentially the same test as v4.
1851+
//
1852+
// Disable the test on QEMU because QEMU emulation of IPV6_RECVERR is broken (as documented on
1853+
// PR #1514).
1854+
#[cfg_attr(qemu, ignore)]
1855+
#[test]
1856+
fn test_recverr_v6() {
1857+
#[repr(u8)]
1858+
enum IcmpV6Types {
1859+
DestUnreach = 1, // ICMPV6_DEST_UNREACH
1860+
}
1861+
#[repr(u8)]
1862+
enum IcmpV6UnreachCodes {
1863+
PortUnreach = 4, // ICMPV6_PORT_UNREACH
1864+
}
1865+
1866+
test_recverr_impl::<sockaddr_in6, _, _>(
1867+
"[::1]:6801",
1868+
AddressFamily::Inet6,
1869+
sockopt::Ipv6RecvErr,
1870+
libc::SO_EE_ORIGIN_ICMP6,
1871+
IcmpV6Types::DestUnreach as u8,
1872+
IcmpV6UnreachCodes::PortUnreach as u8,
1873+
// Closure handles protocol-specific testing and returns generic sock_extended_err for
1874+
// protocol-independent test impl.
1875+
|cmsg| {
1876+
if let ControlMessageOwned::Ipv6RecvErr(ext_err, err_addr) = cmsg {
1877+
if let Some(origin) = err_addr {
1878+
// Validate that our network error originated from localhost:0.
1879+
assert_eq!(origin.sin6_family, AddressFamily::Inet6 as _);
1880+
assert_eq!(
1881+
Ipv6Addr(origin.sin6_addr),
1882+
Ipv6Addr::from_std(&"::1".parse().unwrap()),
1883+
);
1884+
assert_eq!(origin.sin6_port, 0);
1885+
} else {
1886+
panic!("Expected some error origin");
1887+
}
1888+
return *ext_err
1889+
} else {
1890+
panic!("Unexpected control message {:?}", cmsg);
1891+
}
1892+
},
1893+
)
1894+
}
1895+
1896+
fn test_recverr_impl<SA, OPT, TESTF>(sa: &str,
1897+
af: AddressFamily,
1898+
opt: OPT,
1899+
ee_origin: u8,
1900+
ee_type: u8,
1901+
ee_code: u8,
1902+
testf: TESTF)
1903+
where
1904+
OPT: SetSockOpt<Val = bool>,
1905+
TESTF: FnOnce(&ControlMessageOwned) -> libc::sock_extended_err,
1906+
{
1907+
use nix::errno::Errno;
1908+
use nix::sys::uio::IoVec;
1909+
1910+
const MESSAGE_CONTENTS: &str = "ABCDEF";
1911+
1912+
let sock_addr = {
1913+
let std_sa = SocketAddr::from_str(sa).unwrap();
1914+
let inet_addr = InetAddr::from_std(&std_sa);
1915+
SockAddr::new_inet(inet_addr)
1916+
};
1917+
let sock = socket(af, SockType::Datagram, SockFlag::SOCK_CLOEXEC, None).unwrap();
1918+
setsockopt(sock, opt, &true).unwrap();
1919+
if let Err(e) = sendto(sock, MESSAGE_CONTENTS.as_bytes(), &sock_addr, MsgFlags::empty()) {
1920+
assert_eq!(e, Errno::EADDRNOTAVAIL);
1921+
println!("{:?} not available, skipping test.", af);
1922+
return;
1923+
}
1924+
1925+
let mut buf = [0u8; 8];
1926+
let iovec = [IoVec::from_mut_slice(&mut buf)];
1927+
let mut cspace = cmsg_space!(libc::sock_extended_err, SA);
1928+
1929+
let msg = recvmsg(sock, &iovec, Some(&mut cspace), MsgFlags::MSG_ERRQUEUE).unwrap();
1930+
// The sent message / destination associated with the error is returned:
1931+
assert_eq!(msg.bytes, MESSAGE_CONTENTS.as_bytes().len());
1932+
assert_eq!(&buf[..msg.bytes], MESSAGE_CONTENTS.as_bytes());
1933+
// recvmsg(2): "The original destination address of the datagram that caused the error is
1934+
// supplied via msg_name;" however, this is not literally true. E.g., an earlier version
1935+
// of this test used 0.0.0.0 (::0) as the destination address, which was mutated into
1936+
// 127.0.0.1 (::1).
1937+
assert_eq!(msg.address, Some(sock_addr));
1938+
1939+
// Check for expected control message.
1940+
let ext_err = match msg.cmsgs().next() {
1941+
Some(cmsg) => testf(&cmsg),
1942+
None => panic!("No control message"),
1943+
};
1944+
1945+
assert_eq!(ext_err.ee_errno, libc::ECONNREFUSED as u32);
1946+
assert_eq!(ext_err.ee_origin, ee_origin);
1947+
// ip(7): ee_type and ee_code are set from the type and code fields of the ICMP (ICMPv6)
1948+
// header.
1949+
assert_eq!(ext_err.ee_type, ee_type);
1950+
assert_eq!(ext_err.ee_code, ee_code);
1951+
// ip(7): ee_info contains the discovered MTU for EMSGSIZE errors.
1952+
assert_eq!(ext_err.ee_info, 0);
1953+
}
1954+
}

test/test_poll.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,11 @@ fn test_ppoll() {
6464
assert_eq!(nfds, 1);
6565
assert!(fds[0].revents().unwrap().contains(PollFlags::POLLIN));
6666
}
67+
68+
#[test]
69+
fn test_pollfd_fd() {
70+
use std::os::unix::io::AsRawFd;
71+
72+
let pfd = PollFd::new(0x1234, PollFlags::empty());
73+
assert_eq!(pfd.as_raw_fd(), 0x1234);
74+
}

0 commit comments

Comments
 (0)