1 #[cfg(any(target_os = "android", target_os = "linux"))]
2 use crate::*;
3 use nix::sys::socket::{
4 getsockopt, setsockopt, socket, sockopt, AddressFamily, SockFlag,
5 SockProtocol, SockType,
6 };
7 use 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]
test_local_peercred_seqpacket()12 pub 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]
test_local_peercred_stream()38 pub 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]
is_so_mark_functional()59 fn 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]
test_so_buf()77 fn 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]
test_so_tcp_maxseg()95 fn 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]
test_so_type()155 fn 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]
test_so_type_unknown()171 fn 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 ))]
test_tcp_congestion()190 fn 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"))]
test_bindtodevice()216 fn 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]
test_so_tcp_keepalive()234 fn 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)]
test_get_mtu()269 fn 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"))]
test_ttl_opts()295 fn 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"))]
test_dontfrag_opts()318 fn 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)]
test_v6dontfrag_opts()356 fn 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")]
test_so_priority()387 fn 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")]
test_ip_tos()402 fn 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)]
test_ipv6_tclass()420 fn 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