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