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