xref: /xnu-12377.61.12/tests/socket_bind_35243417.c (revision 4d495c6e23c53686cf65f45067f79024cf5dcee8)
1 /* -*- Mode: c; tab-width: 8; indent-tabs-mode: 1; c-basic-offset: 8; -*- */
2 
3 #include <darwintest.h>
4 #include <poll.h>
5 #include <sys/socket.h>
6 #include <unistd.h>
7 #include <netinet/in.h>
8 #include <arpa/inet.h>
9 #include <errno.h>
10 
11 #include "net_test_lib.h"
12 
13 static int
sockv6_open(void)14 sockv6_open(void)
15 {
16 	int     s;
17 
18 	s = socket(AF_INET6, SOCK_DGRAM, 0);
19 	T_QUIET;
20 	T_ASSERT_POSIX_SUCCESS(s, "socket(AF_INET6, SOCK_DGRAM, 0)");
21 	return s;
22 }
23 
24 static int
sockv6_bind(int s,in_port_t port)25 sockv6_bind(int s, in_port_t port)
26 {
27 	struct sockaddr_in6     sin6;
28 
29 	bzero(&sin6, sizeof(sin6));
30 	sin6.sin6_len = sizeof(sin6);
31 	sin6.sin6_family = AF_INET6;
32 	sin6.sin6_port = port;
33 	return bind(s, (const struct sockaddr *)&sin6, sizeof(sin6));
34 }
35 
36 static void
sockv6_set_v6only(int s)37 sockv6_set_v6only(int s)
38 {
39 	int             on = 1;
40 	int             ret;
41 
42 	ret = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
43 	T_QUIET;
44 	T_ASSERT_POSIX_SUCCESS(ret, "setsockopt(%d, IPV6_ONLY)", s);
45 }
46 
47 static bool
alloc_and_bind_ports(in_port_t port_start,in_port_t port_end,int bind_attempts)48 alloc_and_bind_ports(in_port_t port_start, in_port_t port_end,
49     int bind_attempts)
50 {
51 	int     bound_count = 0;
52 	bool    success = true;
53 
54 	for (in_port_t i = port_start; success && i <= port_end; i++) {
55 		int     s6 = -1;
56 		int     s6_other = -1;
57 		int     ret;
58 
59 		s6 = sockv6_open();
60 		sockv6_set_v6only(s6);
61 		if (sockv6_bind(s6, i) != 0) {
62 			/* find the next available port */
63 			goto loop_done;
64 		}
65 		s6_other = sockv6_open();
66 		ret = sockv6_bind(s6_other, i);
67 		T_WITH_ERRNO;
68 		T_QUIET;
69 		T_ASSERT_TRUE(ret != 0, "socket %d bind %d", s6_other, i);
70 		/*
71 		 * After bind fails, try binding to a different port.
72 		 * For non-root user, this will panic without the fix for
73 		 * <rdar://problem/35243417>.
74 		 */
75 		if (sockv6_bind(s6_other, i + 1) == 0) {
76 			bound_count++;
77 			if (bound_count >= bind_attempts) {
78 				break;
79 			}
80 		}
81 loop_done:
82 		if (s6 >= 0) {
83 			close(s6);
84 		}
85 		if (s6_other >= 0) {
86 			close(s6_other);
87 		}
88 	}
89 	T_ASSERT_TRUE(bound_count == bind_attempts,
90 	    "number of successful binds %d (out of %d)",
91 	    bound_count, bind_attempts);
92 
93 	force_zone_gc();
94 
95 	return success;
96 }
97 
98 
99 T_DECL(socket_bind_35243417,
100     "bind IPv6 only UDP socket, then bind IPv6 socket.",
101     T_META_ASROOT(false),
102     T_META_CHECK_LEAKS(false),
103     T_META_TAG_VM_PREFERRED)
104 {
105 	alloc_and_bind_ports(1, 65534, 10);
106 }
107 
108 T_DECL(socket_bind_35243417_root,
109     "bind IPv6 only UDP socket, then bind IPv6 socket.",
110     T_META_ASROOT(true),
111     T_META_TAG_VM_PREFERRED)
112 {
113 	alloc_and_bind_ports(1, 65534, 10);
114 }
115