1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2 // Copyright (c) 2023 Cloudflare
4 /* Test IP_LOCAL_PORT_RANGE socket option: IPv4 + IPv6, TCP + UDP.
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.
11 #include <netinet/ip.h>
13 #include "../kselftest_harness.h"
15 #ifndef IP_LOCAL_PORT_RANGE
16 #define IP_LOCAL_PORT_RANGE 51
20 #define IPPROTO_MPTCP 262
23 static __u32
pack_port_range(__u16 lo
, __u16 hi
)
25 return (hi
<< 16) | (lo
<< 0);
28 static void unpack_port_range(__u32 range
, __u16
*lo
, __u16
*hi
)
34 static int get_so_domain(int fd
)
40 err
= getsockopt(fd
, SOL_SOCKET
, SO_DOMAIN
, &domain
, &len
);
47 static int bind_to_loopback_any_port(int fd
)
51 struct sockaddr_in v4
;
52 struct sockaddr_in6 v6
;
56 memset(&addr
, 0, sizeof(addr
));
57 switch (get_so_domain(fd
)) {
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
);
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
);
74 return bind(fd
, &addr
.sa
, addr_len
);
77 static int get_sock_port(int fd
)
81 struct sockaddr_in v4
;
82 struct sockaddr_in6 v6
;
87 addr_len
= sizeof(addr
);
88 memset(&addr
, 0, sizeof(addr
));
89 err
= getsockname(fd
, &addr
.sa
, &addr_len
);
93 switch (addr
.sa
.sa_family
) {
95 return ntohs(addr
.v4
.sin_port
);
97 return ntohs(addr
.v6
.sin6_port
);
104 static int get_ip_local_port_range(int fd
, __u32
*range
)
111 err
= getsockopt(fd
, SOL_IP
, IP_LOCAL_PORT_RANGE
, &val
, &len
);
119 FIXTURE(ip_local_port_range
) {};
121 FIXTURE_SETUP(ip_local_port_range
)
125 FIXTURE_TEARDOWN(ip_local_port_range
)
129 FIXTURE_VARIANT(ip_local_port_range
) {
135 FIXTURE_VARIANT_ADD(ip_local_port_range
, ip4_tcp
) {
136 .so_domain
= AF_INET
,
137 .so_type
= SOCK_STREAM
,
141 FIXTURE_VARIANT_ADD(ip_local_port_range
, ip4_udp
) {
142 .so_domain
= AF_INET
,
143 .so_type
= SOCK_DGRAM
,
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
,
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
,
159 FIXTURE_VARIANT_ADD(ip_local_port_range
, ip6_tcp
) {
160 .so_domain
= AF_INET6
,
161 .so_type
= SOCK_STREAM
,
165 FIXTURE_VARIANT_ADD(ip_local_port_range
, ip6_udp
) {
166 .so_domain
= AF_INET6
,
167 .so_type
= SOCK_DGRAM
,
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
,
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
,
183 TEST_F(ip_local_port_range
, invalid_option_value
)
190 fd
= socket(variant
->so_domain
, variant
->so_type
, variant
->so_protocol
);
191 ASSERT_GE(fd
, 0) TH_LOG("socket failed");
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
);
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
);
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
);
212 ASSERT_TRUE(!err
) TH_LOG("close failed");
215 TEST_F(ip_local_port_range
, port_range_out_of_netns_range
)
221 { 30000, 39999 }, /* socket range below netns range */
222 { 50000, 59999 }, /* socket range above netns range */
224 const struct test
*t
;
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.
234 TH_LOG("lo %5hu, hi %5hu", t
->range_lo
, t
->range_hi
);
236 for (i
= 0; i
< ARRAY_SIZE(fds
); i
++) {
240 fd
= socket(variant
->so_domain
, variant
->so_type
, variant
->so_protocol
);
241 ASSERT_GE(fd
, 0) TH_LOG("#%d: socket failed", i
);
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
);
247 err
= bind_to_loopback_any_port(fd
);
248 ASSERT_TRUE(!err
) TH_LOG("#%d: bind failed", i
);
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
);
258 for (i
= 0; i
< ARRAY_SIZE(fds
); i
++)
259 ASSERT_TRUE(close(fds
[i
]) == 0) TH_LOG("#%d: close failed", i
);
263 TEST_F(ip_local_port_range
, single_port_range
)
270 /* single port range within ephemeral range */
271 { 45000, 45000, 45000 },
272 /* first port in the ephemeral range (clamp from above) */
274 /* last port in the ephemeral range (clamp from below) */
277 const struct test
*t
;
279 for (t
= tests
; t
< tests
+ ARRAY_SIZE(tests
); t
++) {
283 TH_LOG("lo %5hu, hi %5hu, expected %5hu",
284 t
->range_lo
, t
->range_hi
, t
->expected
);
286 fd
= socket(variant
->so_domain
, variant
->so_type
, variant
->so_protocol
);
287 ASSERT_GE(fd
, 0) TH_LOG("socket failed");
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");
293 err
= bind_to_loopback_any_port(fd
);
294 ASSERT_TRUE(!err
) TH_LOG("bind failed");
296 port
= get_sock_port(fd
);
297 ASSERT_EQ(port
, t
->expected
) TH_LOG("unexpected local port");
300 ASSERT_TRUE(!err
) TH_LOG("close failed");
304 TEST_F(ip_local_port_range
, exhaust_8_port_range
)
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");
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");
320 err
= bind_to_loopback_any_port(fd
);
321 ASSERT_TRUE(!err
) TH_LOG("bind failed");
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");
327 port_set
|= 1 << (port
- 40000);
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");
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");
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");
342 err
= bind_to_loopback_any_port(fd
);
343 ASSERT_TRUE(err
) TH_LOG("expected bind to fail");
344 ASSERT_EQ(errno
, EADDRINUSE
);
347 ASSERT_TRUE(!err
) TH_LOG("close failed");
349 for (i
= 0; i
< ARRAY_SIZE(fds
); i
++) {
351 ASSERT_TRUE(!err
) TH_LOG("close failed");
355 TEST_F(ip_local_port_range
, late_bind
)
359 struct sockaddr_in v4
;
360 struct sockaddr_in6 v6
;
362 socklen_t addr_len
= 0;
368 fd
= socket(variant
->so_domain
, variant
->so_type
, 0);
369 ASSERT_GE(fd
, 0) TH_LOG("socket failed");
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");
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");
378 err
= bind_to_loopback_any_port(fd
);
379 ASSERT_TRUE(!err
) TH_LOG("bind failed");
381 port
= get_sock_port(fd
);
382 ASSERT_EQ(port
, 0) TH_LOG("getsockname failed");
384 /* Invalid destination */
385 memset(&addr
, 0, sizeof(addr
));
386 switch (variant
->so_domain
) {
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
);
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
);
400 ASSERT_TRUE(false) TH_LOG("unsupported socket domain");
403 /* connect() doesn't need to succeed for late bind to happen */
404 connect(fd
, &addr
.sa
, addr_len
);
406 port
= get_sock_port(fd
);
407 ASSERT_GE(port
, 40100);
408 ASSERT_LE(port
, 40199);
411 ASSERT_TRUE(!err
) TH_LOG("close failed");
414 XFAIL_ADD(ip_local_port_range
, ip4_stcp
, late_bind
);
415 XFAIL_ADD(ip_local_port_range
, ip6_stcp
, late_bind
);
417 TEST_F(ip_local_port_range
, get_port_range
)
423 fd
= socket(variant
->so_domain
, variant
->so_type
, variant
->so_protocol
);
424 ASSERT_GE(fd
, 0) TH_LOG("socket failed");
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");
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");
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");
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");
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");
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");
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");
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");
460 ASSERT_TRUE(!err
) TH_LOG("close failed");