1#[cfg(any(target_os = "android", target_os = "linux"))] 2use crate::*; 3use nix::sys::socket::{ 4 getsockopt, setsockopt, socket, sockopt, AddressFamily, SockFlag, 5 SockProtocol, SockType, 6}; 7use rand::{thread_rng, Rng}; 8 9// NB: FreeBSD supports LOCAL_PEERCRED for SOCK_SEQPACKET, but OSX does not. 10#[cfg(any(target_os = "dragonfly", target_os = "freebsd",))] 11#[test] 12pub fn test_local_peercred_seqpacket() { 13 use nix::{ 14 sys::socket::socketpair, 15 unistd::{Gid, Uid}, 16 }; 17 18 let (fd1, _fd2) = socketpair( 19 AddressFamily::Unix, 20 SockType::SeqPacket, 21 None, 22 SockFlag::empty(), 23 ) 24 .unwrap(); 25 let xucred = getsockopt(fd1, sockopt::LocalPeerCred).unwrap(); 26 assert_eq!(xucred.version(), 0); 27 assert_eq!(Uid::from_raw(xucred.uid()), Uid::current()); 28 assert_eq!(Gid::from_raw(xucred.groups()[0]), Gid::current()); 29} 30 31#[cfg(any( 32 target_os = "dragonfly", 33 target_os = "freebsd", 34 target_os = "macos", 35 target_os = "ios" 36))] 37#[test] 38pub fn test_local_peercred_stream() { 39 use nix::{ 40 sys::socket::socketpair, 41 unistd::{Gid, Uid}, 42 }; 43 44 let (fd1, _fd2) = socketpair( 45 AddressFamily::Unix, 46 SockType::Stream, 47 None, 48 SockFlag::empty(), 49 ) 50 .unwrap(); 51 let xucred = getsockopt(fd1, sockopt::LocalPeerCred).unwrap(); 52 assert_eq!(xucred.version(), 0); 53 assert_eq!(Uid::from_raw(xucred.uid()), Uid::current()); 54 assert_eq!(Gid::from_raw(xucred.groups()[0]), Gid::current()); 55} 56 57#[cfg(target_os = "linux")] 58#[test] 59fn is_so_mark_functional() { 60 use nix::sys::socket::sockopt; 61 62 require_capability!("is_so_mark_functional", CAP_NET_ADMIN); 63 64 let s = socket( 65 AddressFamily::Inet, 66 SockType::Stream, 67 SockFlag::empty(), 68 None, 69 ) 70 .unwrap(); 71 setsockopt(s, sockopt::Mark, &1337).unwrap(); 72 let mark = getsockopt(s, sockopt::Mark).unwrap(); 73 assert_eq!(mark, 1337); 74} 75 76#[test] 77fn test_so_buf() { 78 let fd = socket( 79 AddressFamily::Inet, 80 SockType::Datagram, 81 SockFlag::empty(), 82 SockProtocol::Udp, 83 ) 84 .unwrap(); 85 let bufsize: usize = thread_rng().gen_range(4096..131_072); 86 setsockopt(fd, sockopt::SndBuf, &bufsize).unwrap(); 87 let actual = getsockopt(fd, sockopt::SndBuf).unwrap(); 88 assert!(actual >= bufsize); 89 setsockopt(fd, sockopt::RcvBuf, &bufsize).unwrap(); 90 let actual = getsockopt(fd, sockopt::RcvBuf).unwrap(); 91 assert!(actual >= bufsize); 92} 93 94#[test] 95fn test_so_tcp_maxseg() { 96 use nix::sys::socket::{accept, bind, connect, listen, SockaddrIn}; 97 use nix::unistd::{close, write}; 98 use std::net::SocketAddrV4; 99 use std::str::FromStr; 100 101 let std_sa = SocketAddrV4::from_str("127.0.0.1:4001").unwrap(); 102 let sock_addr = SockaddrIn::from(std_sa); 103 104 let rsock = socket( 105 AddressFamily::Inet, 106 SockType::Stream, 107 SockFlag::empty(), 108 SockProtocol::Tcp, 109 ) 110 .unwrap(); 111 bind(rsock, &sock_addr).unwrap(); 112 listen(rsock, 10).unwrap(); 113 let initial = getsockopt(rsock, sockopt::TcpMaxSeg).unwrap(); 114 // Initial MSS is expected to be 536 (https://tools.ietf.org/html/rfc879#section-1) but some 115 // platforms keep it even lower. This might fail if you've tuned your initial MSS to be larger 116 // than 700 117 cfg_if! { 118 if #[cfg(any(target_os = "android", target_os = "linux"))] { 119 let segsize: u32 = 873; 120 assert!(initial < segsize); 121 setsockopt(rsock, sockopt::TcpMaxSeg, &segsize).unwrap(); 122 } else { 123 assert!(initial < 700); 124 } 125 } 126 127 // Connect and check the MSS that was advertised 128 let ssock = socket( 129 AddressFamily::Inet, 130 SockType::Stream, 131 SockFlag::empty(), 132 SockProtocol::Tcp, 133 ) 134 .unwrap(); 135 connect(ssock, &sock_addr).unwrap(); 136 let rsess = accept(rsock).unwrap(); 137 write(rsess, b"hello").unwrap(); 138 let actual = getsockopt(ssock, sockopt::TcpMaxSeg).unwrap(); 139 // Actual max segment size takes header lengths into account, max IPv4 options (60 bytes) + max 140 // TCP options (40 bytes) are subtracted from the requested maximum as a lower boundary. 141 cfg_if! { 142 if #[cfg(any(target_os = "android", target_os = "linux"))] { 143 assert!((segsize - 100) <= actual); 144 assert!(actual <= segsize); 145 } else { 146 assert!(initial < actual); 147 assert!(536 < actual); 148 } 149 } 150 close(rsock).unwrap(); 151 close(ssock).unwrap(); 152} 153 154#[test] 155fn test_so_type() { 156 let sockfd = socket( 157 AddressFamily::Inet, 158 SockType::Stream, 159 SockFlag::empty(), 160 None, 161 ) 162 .unwrap(); 163 164 assert_eq!(Ok(SockType::Stream), getsockopt(sockfd, sockopt::SockType)); 165} 166 167/// getsockopt(_, sockopt::SockType) should gracefully handle unknown socket 168/// types. Regression test for https://github.com/nix-rust/nix/issues/1819 169#[cfg(any(target_os = "android", target_os = "linux",))] 170#[test] 171fn test_so_type_unknown() { 172 use nix::errno::Errno; 173 174 require_capability!("test_so_type", CAP_NET_RAW); 175 let sockfd = unsafe { libc::socket(libc::AF_PACKET, libc::SOCK_PACKET, 0) }; 176 assert!(sockfd >= 0, "Error opening socket: {}", nix::Error::last()); 177 178 assert_eq!(Err(Errno::EINVAL), getsockopt(sockfd, sockopt::SockType)); 179} 180 181// The CI doesn't supported getsockopt and setsockopt on emulated processors. 182// It's believed that a QEMU issue, the tests run ok on a fully emulated system. 183// Current CI just run the binary with QEMU but the Kernel remains the same as the host. 184// So the syscall doesn't work properly unless the kernel is also emulated. 185#[test] 186#[cfg(all( 187 any(target_arch = "x86", target_arch = "x86_64"), 188 any(target_os = "freebsd", target_os = "linux") 189))] 190fn test_tcp_congestion() { 191 use std::ffi::OsString; 192 193 let fd = socket( 194 AddressFamily::Inet, 195 SockType::Stream, 196 SockFlag::empty(), 197 None, 198 ) 199 .unwrap(); 200 201 let val = getsockopt(fd, sockopt::TcpCongestion).unwrap(); 202 setsockopt(fd, sockopt::TcpCongestion, &val).unwrap(); 203 204 setsockopt( 205 fd, 206 sockopt::TcpCongestion, 207 &OsString::from("tcp_congestion_does_not_exist"), 208 ) 209 .unwrap_err(); 210 211 assert_eq!(getsockopt(fd, sockopt::TcpCongestion).unwrap(), val); 212} 213 214#[test] 215#[cfg(any(target_os = "android", target_os = "linux"))] 216fn test_bindtodevice() { 217 skip_if_not_root!("test_bindtodevice"); 218 219 let fd = socket( 220 AddressFamily::Inet, 221 SockType::Stream, 222 SockFlag::empty(), 223 None, 224 ) 225 .unwrap(); 226 227 let val = getsockopt(fd, sockopt::BindToDevice).unwrap(); 228 setsockopt(fd, sockopt::BindToDevice, &val).unwrap(); 229 230 assert_eq!(getsockopt(fd, sockopt::BindToDevice).unwrap(), val); 231} 232 233#[test] 234fn test_so_tcp_keepalive() { 235 let fd = socket( 236 AddressFamily::Inet, 237 SockType::Stream, 238 SockFlag::empty(), 239 SockProtocol::Tcp, 240 ) 241 .unwrap(); 242 setsockopt(fd, sockopt::KeepAlive, &true).unwrap(); 243 assert!(getsockopt(fd, sockopt::KeepAlive).unwrap()); 244 245 #[cfg(any( 246 target_os = "android", 247 target_os = "dragonfly", 248 target_os = "freebsd", 249 target_os = "linux" 250 ))] 251 { 252 let x = getsockopt(fd, sockopt::TcpKeepIdle).unwrap(); 253 setsockopt(fd, sockopt::TcpKeepIdle, &(x + 1)).unwrap(); 254 assert_eq!(getsockopt(fd, sockopt::TcpKeepIdle).unwrap(), x + 1); 255 256 let x = getsockopt(fd, sockopt::TcpKeepCount).unwrap(); 257 setsockopt(fd, sockopt::TcpKeepCount, &(x + 1)).unwrap(); 258 assert_eq!(getsockopt(fd, sockopt::TcpKeepCount).unwrap(), x + 1); 259 260 let x = getsockopt(fd, sockopt::TcpKeepInterval).unwrap(); 261 setsockopt(fd, sockopt::TcpKeepInterval, &(x + 1)).unwrap(); 262 assert_eq!(getsockopt(fd, sockopt::TcpKeepInterval).unwrap(), x + 1); 263 } 264} 265 266#[test] 267#[cfg(any(target_os = "android", target_os = "linux"))] 268#[cfg_attr(qemu, ignore)] 269fn test_get_mtu() { 270 use nix::sys::socket::{bind, connect, SockaddrIn}; 271 use std::net::SocketAddrV4; 272 use std::str::FromStr; 273 274 let std_sa = SocketAddrV4::from_str("127.0.0.1:4001").unwrap(); 275 let std_sb = SocketAddrV4::from_str("127.0.0.1:4002").unwrap(); 276 277 let usock = socket( 278 AddressFamily::Inet, 279 SockType::Datagram, 280 SockFlag::empty(), 281 SockProtocol::Udp, 282 ) 283 .unwrap(); 284 285 // Bind and initiate connection 286 bind(usock, &SockaddrIn::from(std_sa)).unwrap(); 287 connect(usock, &SockaddrIn::from(std_sb)).unwrap(); 288 289 // Loopback connections have 2^16 - the maximum - MTU 290 assert_eq!(getsockopt(usock, sockopt::IpMtu), Ok(u16::MAX as i32)) 291} 292 293#[test] 294#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))] 295fn test_ttl_opts() { 296 let fd4 = socket( 297 AddressFamily::Inet, 298 SockType::Datagram, 299 SockFlag::empty(), 300 None, 301 ) 302 .unwrap(); 303 setsockopt(fd4, sockopt::Ipv4Ttl, &1) 304 .expect("setting ipv4ttl on an inet socket should succeed"); 305 let fd6 = socket( 306 AddressFamily::Inet6, 307 SockType::Datagram, 308 SockFlag::empty(), 309 None, 310 ) 311 .unwrap(); 312 setsockopt(fd6, sockopt::Ipv6Ttl, &1) 313 .expect("setting ipv6ttl on an inet6 socket should succeed"); 314} 315 316#[test] 317#[cfg(any(target_os = "ios", target_os = "macos"))] 318fn test_dontfrag_opts() { 319 let fd4 = socket( 320 AddressFamily::Inet, 321 SockType::Stream, 322 SockFlag::empty(), 323 SockProtocol::Tcp, 324 ) 325 .unwrap(); 326 setsockopt(fd4, sockopt::IpDontFrag, &true) 327 .expect("setting IP_DONTFRAG on an inet stream socket should succeed"); 328 setsockopt(fd4, sockopt::IpDontFrag, &false).expect( 329 "unsetting IP_DONTFRAG on an inet stream socket should succeed", 330 ); 331 let fd4d = socket( 332 AddressFamily::Inet, 333 SockType::Datagram, 334 SockFlag::empty(), 335 None, 336 ) 337 .unwrap(); 338 setsockopt(fd4d, sockopt::IpDontFrag, &true).expect( 339 "setting IP_DONTFRAG on an inet datagram socket should succeed", 340 ); 341 setsockopt(fd4d, sockopt::IpDontFrag, &false).expect( 342 "unsetting IP_DONTFRAG on an inet datagram socket should succeed", 343 ); 344} 345 346#[test] 347#[cfg(any( 348 target_os = "android", 349 target_os = "ios", 350 target_os = "linux", 351 target_os = "macos", 352))] 353// Disable the test under emulation because it fails in Cirrus-CI. Lack 354// of QEMU support is suspected. 355#[cfg_attr(qemu, ignore)] 356fn test_v6dontfrag_opts() { 357 let fd6 = socket( 358 AddressFamily::Inet6, 359 SockType::Stream, 360 SockFlag::empty(), 361 SockProtocol::Tcp, 362 ) 363 .unwrap(); 364 setsockopt(fd6, sockopt::Ipv6DontFrag, &true).expect( 365 "setting IPV6_DONTFRAG on an inet6 stream socket should succeed", 366 ); 367 setsockopt(fd6, sockopt::Ipv6DontFrag, &false).expect( 368 "unsetting IPV6_DONTFRAG on an inet6 stream socket should succeed", 369 ); 370 let fd6d = socket( 371 AddressFamily::Inet6, 372 SockType::Datagram, 373 SockFlag::empty(), 374 None, 375 ) 376 .unwrap(); 377 setsockopt(fd6d, sockopt::Ipv6DontFrag, &true).expect( 378 "setting IPV6_DONTFRAG on an inet6 datagram socket should succeed", 379 ); 380 setsockopt(fd6d, sockopt::Ipv6DontFrag, &false).expect( 381 "unsetting IPV6_DONTFRAG on an inet6 datagram socket should succeed", 382 ); 383} 384 385#[test] 386#[cfg(target_os = "linux")] 387fn test_so_priority() { 388 let fd = socket( 389 AddressFamily::Inet, 390 SockType::Stream, 391 SockFlag::empty(), 392 SockProtocol::Tcp, 393 ) 394 .unwrap(); 395 let priority = 3; 396 setsockopt(fd, sockopt::Priority, &priority).unwrap(); 397 assert_eq!(getsockopt(fd, sockopt::Priority).unwrap(), priority); 398} 399 400#[test] 401#[cfg(target_os = "linux")] 402fn test_ip_tos() { 403 let fd = socket( 404 AddressFamily::Inet, 405 SockType::Stream, 406 SockFlag::empty(), 407 SockProtocol::Tcp, 408 ) 409 .unwrap(); 410 let tos = 0x80; // CS4 411 setsockopt(fd, sockopt::IpTos, &tos).unwrap(); 412 assert_eq!(getsockopt(fd, sockopt::IpTos).unwrap(), tos); 413} 414 415#[test] 416#[cfg(target_os = "linux")] 417// Disable the test under emulation because it fails in Cirrus-CI. Lack 418// of QEMU support is suspected. 419#[cfg_attr(qemu, ignore)] 420fn test_ipv6_tclass() { 421 let fd = socket( 422 AddressFamily::Inet6, 423 SockType::Stream, 424 SockFlag::empty(), 425 SockProtocol::Tcp, 426 ) 427 .unwrap(); 428 let class = 0x80; // CS4 429 setsockopt(fd, sockopt::Ipv6TClass, &class).unwrap(); 430 assert_eq!(getsockopt(fd, sockopt::Ipv6TClass).unwrap(), class); 431} 432