]> Git Repo - linux.git/blob - tools/testing/selftests/net/ip_local_port_range.c
x86/kaslr: Expose and use the end of the physical memory address space
[linux.git] / tools / testing / selftests / net / ip_local_port_range.c
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
23 static __u32 pack_port_range(__u16 lo, __u16 hi)
24 {
25         return (hi << 16) | (lo << 0);
26 }
27
28 static void unpack_port_range(__u32 range, __u16 *lo, __u16 *hi)
29 {
30         *lo = range & 0xffff;
31         *hi = range >> 16;
32 }
33
34 static 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
47 static 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
77 static 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
104 static 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
119 FIXTURE(ip_local_port_range) {};
120
121 FIXTURE_SETUP(ip_local_port_range)
122 {
123 }
124
125 FIXTURE_TEARDOWN(ip_local_port_range)
126 {
127 }
128
129 FIXTURE_VARIANT(ip_local_port_range) {
130         int so_domain;
131         int so_type;
132         int so_protocol;
133 };
134
135 FIXTURE_VARIANT_ADD(ip_local_port_range, ip4_tcp) {
136         .so_domain      = AF_INET,
137         .so_type        = SOCK_STREAM,
138         .so_protocol    = 0,
139 };
140
141 FIXTURE_VARIANT_ADD(ip_local_port_range, ip4_udp) {
142         .so_domain      = AF_INET,
143         .so_type        = SOCK_DGRAM,
144         .so_protocol    = 0,
145 };
146
147 FIXTURE_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
153 FIXTURE_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
159 FIXTURE_VARIANT_ADD(ip_local_port_range, ip6_tcp) {
160         .so_domain      = AF_INET6,
161         .so_type        = SOCK_STREAM,
162         .so_protocol    = 0,
163 };
164
165 FIXTURE_VARIANT_ADD(ip_local_port_range, ip6_udp) {
166         .so_domain      = AF_INET6,
167         .so_type        = SOCK_DGRAM,
168         .so_protocol    = 0,
169 };
170
171 FIXTURE_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
177 FIXTURE_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
183 TEST_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(40222, 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(40333, 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
215 TEST_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(t->range_lo, 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
263 TEST_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(t->range_lo, 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
304 TEST_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(40000, 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(40000, 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
355 TEST_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 = 0;
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(40100, 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
414 XFAIL_ADD(ip_local_port_range, ip4_stcp, late_bind);
415 XFAIL_ADD(ip_local_port_range, ip6_stcp, late_bind);
416
417 TEST_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);
428         ASSERT_TRUE(!err) TH_LOG("getsockopt(IP_LOCAL_PORT_RANGE) failed");
429
430         unpack_port_range(range, &lo, &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(12345, 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);
440         ASSERT_TRUE(!err) TH_LOG("getsockopt(IP_LOCAL_PORT_RANGE) failed");
441
442         unpack_port_range(range, &lo, &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(0, 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);
453         ASSERT_TRUE(!err) TH_LOG("getsockopt(IP_LOCAL_PORT_RANGE) failed");
454
455         unpack_port_range(range, &lo, &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
463 TEST_HARNESS_MAIN
This page took 0.059805 seconds and 4 git commands to generate.