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

source code of linux/tools/testing/selftests/net/ip_local_port_range.c