1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2 // Copyright (c) 2023 Cloudflare
3 
4 /* Test IP_LOCAL_PORT_RANGE socket option: IPv4 + IPv6, TCP + UDP.
5  *
6  * Tests assume that net.ipv4.ip_local_port_range is [40000, 49999].
7  * Don't run these directly but with ip_local_port_range.sh script.
8  */
9 
10 #include <fcntl.h>
11 #include <netinet/ip.h>
12 
13 #include "../kselftest_harness.h"
14 
15 #ifndef IP_LOCAL_PORT_RANGE
16 #define IP_LOCAL_PORT_RANGE 51
17 #endif
18 
pack_port_range(__u16 lo,__u16 hi)19 static __u32 pack_port_range(__u16 lo, __u16 hi)
20 {
21 	return (hi << 16) | (lo << 0);
22 }
23 
unpack_port_range(__u32 range,__u16 * lo,__u16 * hi)24 static void unpack_port_range(__u32 range, __u16 *lo, __u16 *hi)
25 {
26 	*lo = range & 0xffff;
27 	*hi = range >> 16;
28 }
29 
get_so_domain(int fd)30 static int get_so_domain(int fd)
31 {
32 	int domain, err;
33 	socklen_t len;
34 
35 	len = sizeof(domain);
36 	err = getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &domain, &len);
37 	if (err)
38 		return -1;
39 
40 	return domain;
41 }
42 
bind_to_loopback_any_port(int fd)43 static int bind_to_loopback_any_port(int fd)
44 {
45 	union {
46 		struct sockaddr sa;
47 		struct sockaddr_in v4;
48 		struct sockaddr_in6 v6;
49 	} addr;
50 	socklen_t addr_len;
51 
52 	memset(&addr, 0, sizeof(addr));
53 	switch (get_so_domain(fd)) {
54 	case AF_INET:
55 		addr.v4.sin_family = AF_INET;
56 		addr.v4.sin_port = htons(0);
57 		addr.v4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
58 		addr_len = sizeof(addr.v4);
59 		break;
60 	case AF_INET6:
61 		addr.v6.sin6_family = AF_INET6;
62 		addr.v6.sin6_port = htons(0);
63 		addr.v6.sin6_addr = in6addr_loopback;
64 		addr_len = sizeof(addr.v6);
65 		break;
66 	default:
67 		return -1;
68 	}
69 
70 	return bind(fd, &addr.sa, addr_len);
71 }
72 
get_sock_port(int fd)73 static int get_sock_port(int fd)
74 {
75 	union {
76 		struct sockaddr sa;
77 		struct sockaddr_in v4;
78 		struct sockaddr_in6 v6;
79 	} addr;
80 	socklen_t addr_len;
81 	int err;
82 
83 	addr_len = sizeof(addr);
84 	memset(&addr, 0, sizeof(addr));
85 	err = getsockname(fd, &addr.sa, &addr_len);
86 	if (err)
87 		return -1;
88 
89 	switch (addr.sa.sa_family) {
90 	case AF_INET:
91 		return ntohs(addr.v4.sin_port);
92 	case AF_INET6:
93 		return ntohs(addr.v6.sin6_port);
94 	default:
95 		errno = EAFNOSUPPORT;
96 		return -1;
97 	}
98 }
99 
get_ip_local_port_range(int fd,__u32 * range)100 static int get_ip_local_port_range(int fd, __u32 *range)
101 {
102 	socklen_t len;
103 	__u32 val;
104 	int err;
105 
106 	len = sizeof(val);
107 	err = getsockopt(fd, SOL_IP, IP_LOCAL_PORT_RANGE, &val, &len);
108 	if (err)
109 		return -1;
110 
111 	*range = val;
112 	return 0;
113 }
114 
FIXTURE(ip_local_port_range)115 FIXTURE(ip_local_port_range) {};
116 
FIXTURE_SETUP(ip_local_port_range)117 FIXTURE_SETUP(ip_local_port_range)
118 {
119 }
120 
FIXTURE_TEARDOWN(ip_local_port_range)121 FIXTURE_TEARDOWN(ip_local_port_range)
122 {
123 }
124 
FIXTURE_VARIANT(ip_local_port_range)125 FIXTURE_VARIANT(ip_local_port_range) {
126 	int so_domain;
127 	int so_type;
128 	int so_protocol;
129 };
130 
FIXTURE_VARIANT_ADD(ip_local_port_range,ip4_tcp)131 FIXTURE_VARIANT_ADD(ip_local_port_range, ip4_tcp) {
132 	.so_domain	= AF_INET,
133 	.so_type	= SOCK_STREAM,
134 	.so_protocol	= 0,
135 };
136 
FIXTURE_VARIANT_ADD(ip_local_port_range,ip4_udp)137 FIXTURE_VARIANT_ADD(ip_local_port_range, ip4_udp) {
138 	.so_domain	= AF_INET,
139 	.so_type	= SOCK_DGRAM,
140 	.so_protocol	= 0,
141 };
142 
FIXTURE_VARIANT_ADD(ip_local_port_range,ip4_stcp)143 FIXTURE_VARIANT_ADD(ip_local_port_range, ip4_stcp) {
144 	.so_domain	= AF_INET,
145 	.so_type	= SOCK_STREAM,
146 	.so_protocol	= IPPROTO_SCTP,
147 };
148 
FIXTURE_VARIANT_ADD(ip_local_port_range,ip6_tcp)149 FIXTURE_VARIANT_ADD(ip_local_port_range, ip6_tcp) {
150 	.so_domain	= AF_INET6,
151 	.so_type	= SOCK_STREAM,
152 	.so_protocol	= 0,
153 };
154 
FIXTURE_VARIANT_ADD(ip_local_port_range,ip6_udp)155 FIXTURE_VARIANT_ADD(ip_local_port_range, ip6_udp) {
156 	.so_domain	= AF_INET6,
157 	.so_type	= SOCK_DGRAM,
158 	.so_protocol	= 0,
159 };
160 
FIXTURE_VARIANT_ADD(ip_local_port_range,ip6_stcp)161 FIXTURE_VARIANT_ADD(ip_local_port_range, ip6_stcp) {
162 	.so_domain	= AF_INET6,
163 	.so_type	= SOCK_STREAM,
164 	.so_protocol	= IPPROTO_SCTP,
165 };
166 
TEST_F(ip_local_port_range,invalid_option_value)167 TEST_F(ip_local_port_range, invalid_option_value)
168 {
169 	__u16 val16;
170 	__u32 val32;
171 	__u64 val64;
172 	int fd, err;
173 
174 	fd = socket(variant->so_domain, variant->so_type, variant->so_protocol);
175 	ASSERT_GE(fd, 0) TH_LOG("socket failed");
176 
177 	/* Too few bytes */
178 	val16 = 40000;
179 	err = setsockopt(fd, SOL_IP, IP_LOCAL_PORT_RANGE, &val16, sizeof(val16));
180 	EXPECT_TRUE(err) TH_LOG("expected setsockopt(IP_LOCAL_PORT_RANGE) to fail");
181 	EXPECT_EQ(errno, EINVAL);
182 
183 	/* Empty range: low port > high port */
184 	val32 = pack_port_range(40222, 40111);
185 	err = setsockopt(fd, SOL_IP, IP_LOCAL_PORT_RANGE, &val32, sizeof(val32));
186 	EXPECT_TRUE(err) TH_LOG("expected setsockopt(IP_LOCAL_PORT_RANGE) to fail");
187 	EXPECT_EQ(errno, EINVAL);
188 
189 	/* Too many bytes */
190 	val64 = pack_port_range(40333, 40444);
191 	err = setsockopt(fd, SOL_IP, IP_LOCAL_PORT_RANGE, &val64, sizeof(val64));
192 	EXPECT_TRUE(err) TH_LOG("expected setsockopt(IP_LOCAL_PORT_RANGE) to fail");
193 	EXPECT_EQ(errno, EINVAL);
194 
195 	err = close(fd);
196 	ASSERT_TRUE(!err) TH_LOG("close failed");
197 }
198 
TEST_F(ip_local_port_range,port_range_out_of_netns_range)199 TEST_F(ip_local_port_range, port_range_out_of_netns_range)
200 {
201 	const struct test {
202 		__u16 range_lo;
203 		__u16 range_hi;
204 	} tests[] = {
205 		{ 30000, 39999 }, /* socket range below netns range */
206 		{ 50000, 59999 }, /* socket range above netns range */
207 	};
208 	const struct test *t;
209 
210 	for (t = tests; t < tests + ARRAY_SIZE(tests); t++) {
211 		/* Bind a couple of sockets, not just one, to check
212 		 * that the range wasn't clamped to a single port from
213 		 * the netns range. That is [40000, 40000] or [49999,
214 		 * 49999], respectively for each test case.
215 		 */
216 		int fds[2], i;
217 
218 		TH_LOG("lo %5hu, hi %5hu", t->range_lo, t->range_hi);
219 
220 		for (i = 0; i < ARRAY_SIZE(fds); i++) {
221 			int fd, err, port;
222 			__u32 range;
223 
224 			fd = socket(variant->so_domain, variant->so_type, variant->so_protocol);
225 			ASSERT_GE(fd, 0) TH_LOG("#%d: socket failed", i);
226 
227 			range = pack_port_range(t->range_lo, t->range_hi);
228 			err = setsockopt(fd, SOL_IP, IP_LOCAL_PORT_RANGE, &range, sizeof(range));
229 			ASSERT_TRUE(!err) TH_LOG("#%d: setsockopt(IP_LOCAL_PORT_RANGE) failed", i);
230 
231 			err = bind_to_loopback_any_port(fd);
232 			ASSERT_TRUE(!err) TH_LOG("#%d: bind failed", i);
233 
234 			/* Check that socket port range outside of ephemeral range is ignored */
235 			port = get_sock_port(fd);
236 			ASSERT_GE(port, 40000) TH_LOG("#%d: expected port within netns range", i);
237 			ASSERT_LE(port, 49999) TH_LOG("#%d: expected port within netns range", i);
238 
239 			fds[i] = fd;
240 		}
241 
242 		for (i = 0; i < ARRAY_SIZE(fds); i++)
243 			ASSERT_TRUE(close(fds[i]) == 0) TH_LOG("#%d: close failed", i);
244 	}
245 }
246 
TEST_F(ip_local_port_range,single_port_range)247 TEST_F(ip_local_port_range, single_port_range)
248 {
249 	const struct test {
250 		__u16 range_lo;
251 		__u16 range_hi;
252 		__u16 expected;
253 	} tests[] = {
254 		/* single port range within ephemeral range */
255 		{ 45000, 45000, 45000 },
256 		/* first port in the ephemeral range (clamp from above) */
257 		{ 0, 40000, 40000 },
258 		/* last port in the ephemeral range (clamp from below)  */
259 		{ 49999, 0, 49999 },
260 	};
261 	const struct test *t;
262 
263 	for (t = tests; t < tests + ARRAY_SIZE(tests); t++) {
264 		int fd, err, port;
265 		__u32 range;
266 
267 		TH_LOG("lo %5hu, hi %5hu, expected %5hu",
268 		       t->range_lo, t->range_hi, t->expected);
269 
270 		fd = socket(variant->so_domain, variant->so_type, variant->so_protocol);
271 		ASSERT_GE(fd, 0) TH_LOG("socket failed");
272 
273 		range = pack_port_range(t->range_lo, t->range_hi);
274 		err = setsockopt(fd, SOL_IP, IP_LOCAL_PORT_RANGE, &range, sizeof(range));
275 		ASSERT_TRUE(!err) TH_LOG("setsockopt(IP_LOCAL_PORT_RANGE) failed");
276 
277 		err = bind_to_loopback_any_port(fd);
278 		ASSERT_TRUE(!err) TH_LOG("bind failed");
279 
280 		port = get_sock_port(fd);
281 		ASSERT_EQ(port, t->expected) TH_LOG("unexpected local port");
282 
283 		err = close(fd);
284 		ASSERT_TRUE(!err) TH_LOG("close failed");
285 	}
286 }
287 
TEST_F(ip_local_port_range,exhaust_8_port_range)288 TEST_F(ip_local_port_range, exhaust_8_port_range)
289 {
290 	__u8 port_set = 0;
291 	int i, fd, err;
292 	__u32 range;
293 	__u16 port;
294 	int fds[8];
295 
296 	for (i = 0; i < ARRAY_SIZE(fds); i++) {
297 		fd = socket(variant->so_domain, variant->so_type, variant->so_protocol);
298 		ASSERT_GE(fd, 0) TH_LOG("socket failed");
299 
300 		range = pack_port_range(40000, 40007);
301 		err = setsockopt(fd, SOL_IP, IP_LOCAL_PORT_RANGE, &range, sizeof(range));
302 		ASSERT_TRUE(!err) TH_LOG("setsockopt(IP_LOCAL_PORT_RANGE) failed");
303 
304 		err = bind_to_loopback_any_port(fd);
305 		ASSERT_TRUE(!err) TH_LOG("bind failed");
306 
307 		port = get_sock_port(fd);
308 		ASSERT_GE(port, 40000) TH_LOG("expected port within sockopt range");
309 		ASSERT_LE(port, 40007) TH_LOG("expected port within sockopt range");
310 
311 		port_set |= 1 << (port - 40000);
312 		fds[i] = fd;
313 	}
314 
315 	/* Check that all every port from the test range is in use */
316 	ASSERT_EQ(port_set, 0xff) TH_LOG("expected all ports to be busy");
317 
318 	/* Check that bind() fails because the whole range is busy */
319 	fd = socket(variant->so_domain, variant->so_type, variant->so_protocol);
320 	ASSERT_GE(fd, 0) TH_LOG("socket failed");
321 
322 	range = pack_port_range(40000, 40007);
323 	err = setsockopt(fd, SOL_IP, IP_LOCAL_PORT_RANGE, &range, sizeof(range));
324 	ASSERT_TRUE(!err) TH_LOG("setsockopt(IP_LOCAL_PORT_RANGE) failed");
325 
326 	err = bind_to_loopback_any_port(fd);
327 	ASSERT_TRUE(err) TH_LOG("expected bind to fail");
328 	ASSERT_EQ(errno, EADDRINUSE);
329 
330 	err = close(fd);
331 	ASSERT_TRUE(!err) TH_LOG("close failed");
332 
333 	for (i = 0; i < ARRAY_SIZE(fds); i++) {
334 		err = close(fds[i]);
335 		ASSERT_TRUE(!err) TH_LOG("close failed");
336 	}
337 }
338 
TEST_F(ip_local_port_range,late_bind)339 TEST_F(ip_local_port_range, late_bind)
340 {
341 	union {
342 		struct sockaddr sa;
343 		struct sockaddr_in v4;
344 		struct sockaddr_in6 v6;
345 	} addr;
346 	socklen_t addr_len;
347 	const int one = 1;
348 	int fd, err;
349 	__u32 range;
350 	__u16 port;
351 
352 	if (variant->so_protocol == IPPROTO_SCTP)
353 		SKIP(return, "SCTP doesn't support IP_BIND_ADDRESS_NO_PORT");
354 
355 	fd = socket(variant->so_domain, variant->so_type, 0);
356 	ASSERT_GE(fd, 0) TH_LOG("socket failed");
357 
358 	range = pack_port_range(40100, 40199);
359 	err = setsockopt(fd, SOL_IP, IP_LOCAL_PORT_RANGE, &range, sizeof(range));
360 	ASSERT_TRUE(!err) TH_LOG("setsockopt(IP_LOCAL_PORT_RANGE) failed");
361 
362 	err = setsockopt(fd, SOL_IP, IP_BIND_ADDRESS_NO_PORT, &one, sizeof(one));
363 	ASSERT_TRUE(!err) TH_LOG("setsockopt(IP_BIND_ADDRESS_NO_PORT) failed");
364 
365 	err = bind_to_loopback_any_port(fd);
366 	ASSERT_TRUE(!err) TH_LOG("bind failed");
367 
368 	port = get_sock_port(fd);
369 	ASSERT_EQ(port, 0) TH_LOG("getsockname failed");
370 
371 	/* Invalid destination */
372 	memset(&addr, 0, sizeof(addr));
373 	switch (variant->so_domain) {
374 	case AF_INET:
375 		addr.v4.sin_family = AF_INET;
376 		addr.v4.sin_port = htons(0);
377 		addr.v4.sin_addr.s_addr = htonl(INADDR_ANY);
378 		addr_len = sizeof(addr.v4);
379 		break;
380 	case AF_INET6:
381 		addr.v6.sin6_family = AF_INET6;
382 		addr.v6.sin6_port = htons(0);
383 		addr.v6.sin6_addr = in6addr_any;
384 		addr_len = sizeof(addr.v6);
385 		break;
386 	default:
387 		ASSERT_TRUE(false) TH_LOG("unsupported socket domain");
388 	}
389 
390 	/* connect() doesn't need to succeed for late bind to happen */
391 	connect(fd, &addr.sa, addr_len);
392 
393 	port = get_sock_port(fd);
394 	ASSERT_GE(port, 40100);
395 	ASSERT_LE(port, 40199);
396 
397 	err = close(fd);
398 	ASSERT_TRUE(!err) TH_LOG("close failed");
399 }
400 
TEST_F(ip_local_port_range,get_port_range)401 TEST_F(ip_local_port_range, get_port_range)
402 {
403 	__u16 lo, hi;
404 	__u32 range;
405 	int fd, err;
406 
407 	fd = socket(variant->so_domain, variant->so_type, variant->so_protocol);
408 	ASSERT_GE(fd, 0) TH_LOG("socket failed");
409 
410 	/* Get range before it will be set */
411 	err = get_ip_local_port_range(fd, &range);
412 	ASSERT_TRUE(!err) TH_LOG("getsockopt(IP_LOCAL_PORT_RANGE) failed");
413 
414 	unpack_port_range(range, &lo, &hi);
415 	ASSERT_EQ(lo, 0) TH_LOG("unexpected low port");
416 	ASSERT_EQ(hi, 0) TH_LOG("unexpected high port");
417 
418 	range = pack_port_range(12345, 54321);
419 	err = setsockopt(fd, SOL_IP, IP_LOCAL_PORT_RANGE, &range, sizeof(range));
420 	ASSERT_TRUE(!err) TH_LOG("setsockopt(IP_LOCAL_PORT_RANGE) failed");
421 
422 	/* Get range after it has been set */
423 	err = get_ip_local_port_range(fd, &range);
424 	ASSERT_TRUE(!err) TH_LOG("getsockopt(IP_LOCAL_PORT_RANGE) failed");
425 
426 	unpack_port_range(range, &lo, &hi);
427 	ASSERT_EQ(lo, 12345) TH_LOG("unexpected low port");
428 	ASSERT_EQ(hi, 54321) TH_LOG("unexpected high port");
429 
430 	/* Unset the port range  */
431 	range = pack_port_range(0, 0);
432 	err = setsockopt(fd, SOL_IP, IP_LOCAL_PORT_RANGE, &range, sizeof(range));
433 	ASSERT_TRUE(!err) TH_LOG("setsockopt(IP_LOCAL_PORT_RANGE) failed");
434 
435 	/* Get range after it has been unset */
436 	err = get_ip_local_port_range(fd, &range);
437 	ASSERT_TRUE(!err) TH_LOG("getsockopt(IP_LOCAL_PORT_RANGE) failed");
438 
439 	unpack_port_range(range, &lo, &hi);
440 	ASSERT_EQ(lo, 0) TH_LOG("unexpected low port");
441 	ASSERT_EQ(hi, 0) TH_LOG("unexpected high port");
442 
443 	err = close(fd);
444 	ASSERT_TRUE(!err) TH_LOG("close failed");
445 }
446 
447 TEST_HARNESS_MAIN
448