1 // SPDX-License-Identifier: GPL-2.0-only
3 * Check if we can fully utilize 4-tuples for connect().
5 * Rules to bind sockets to the same port when all ephemeral ports are
8 * 1. if there are TCP_LISTEN sockets on the port, fail to bind.
9 * 2. if there are sockets without SO_REUSEADDR, fail to bind.
10 * 3. if SO_REUSEADDR is disabled, fail to bind.
11 * 4. if SO_REUSEADDR is enabled and SO_REUSEPORT is disabled,
13 * 5. if SO_REUSEADDR and SO_REUSEPORT are enabled and
14 * there is no socket having the both options and the same EUID,
18 * Author: Kuniyuki Iwashima <kuniyu@amazon.co.jp>
20 #include <arpa/inet.h>
21 #include <netinet/in.h>
22 #include <sys/socket.h>
23 #include <sys/types.h>
25 #include "../kselftest_harness.h"
32 struct reuse_opts unreusable_opts
[12] = {
47 struct reuse_opts reusable_opts
[4] = {
54 int bind_port(struct __test_metadata
*_metadata
, int reuseaddr
, int reuseport
)
56 struct sockaddr_in local_addr
;
57 int len
= sizeof(local_addr
);
60 fd
= socket(AF_INET
, SOCK_STREAM
, 0);
61 ASSERT_NE(-1, fd
) TH_LOG("failed to open socket.");
63 ret
= setsockopt(fd
, SOL_SOCKET
, SO_REUSEADDR
, &reuseaddr
, sizeof(int));
64 ASSERT_EQ(0, ret
) TH_LOG("failed to setsockopt: SO_REUSEADDR.");
66 ret
= setsockopt(fd
, SOL_SOCKET
, SO_REUSEPORT
, &reuseport
, sizeof(int));
67 ASSERT_EQ(0, ret
) TH_LOG("failed to setsockopt: SO_REUSEPORT.");
69 local_addr
.sin_family
= AF_INET
;
70 local_addr
.sin_addr
.s_addr
= inet_addr("127.0.0.1");
71 local_addr
.sin_port
= 0;
73 if (bind(fd
, (struct sockaddr
*)&local_addr
, len
) == -1) {
81 TEST(reuseaddr_ports_exhausted_unreusable
)
83 struct reuse_opts
*opts
;
86 for (i
= 0; i
< 12; i
++) {
87 opts
= &unreusable_opts
[i
];
89 for (j
= 0; j
< 2; j
++)
90 fd
[j
] = bind_port(_metadata
, opts
->reuseaddr
[j
], opts
->reuseport
[j
]);
92 ASSERT_NE(-1, fd
[0]) TH_LOG("failed to bind.");
93 EXPECT_EQ(-1, fd
[1]) TH_LOG("should fail to bind.");
95 for (j
= 0; j
< 2; j
++)
101 TEST(reuseaddr_ports_exhausted_reusable_same_euid
)
103 struct reuse_opts
*opts
;
106 for (i
= 0; i
< 4; i
++) {
107 opts
= &reusable_opts
[i
];
109 for (j
= 0; j
< 2; j
++)
110 fd
[j
] = bind_port(_metadata
, opts
->reuseaddr
[j
], opts
->reuseport
[j
]);
112 ASSERT_NE(-1, fd
[0]) TH_LOG("failed to bind.");
114 if (opts
->reuseport
[0] && opts
->reuseport
[1]) {
115 EXPECT_EQ(-1, fd
[1]) TH_LOG("should fail to bind because both sockets succeed to be listened.");
117 EXPECT_NE(-1, fd
[1]) TH_LOG("should succeed to bind to connect to different destinations.");
120 for (j
= 0; j
< 2; j
++)
126 TEST(reuseaddr_ports_exhausted_reusable_different_euid
)
128 struct reuse_opts
*opts
;
129 int i
, j
, ret
, fd
[2];
130 uid_t euid
[2] = {10, 20};
132 for (i
= 0; i
< 4; i
++) {
133 opts
= &reusable_opts
[i
];
135 for (j
= 0; j
< 2; j
++) {
136 ret
= seteuid(euid
[j
]);
137 ASSERT_EQ(0, ret
) TH_LOG("failed to seteuid: %d.", euid
[j
]);
139 fd
[j
] = bind_port(_metadata
, opts
->reuseaddr
[j
], opts
->reuseport
[j
]);
142 ASSERT_EQ(0, ret
) TH_LOG("failed to seteuid: 0.");
145 ASSERT_NE(-1, fd
[0]) TH_LOG("failed to bind.");
146 EXPECT_NE(-1, fd
[1]) TH_LOG("should succeed to bind because one socket can be bound in each euid.");
149 ret
= listen(fd
[0], 5);
150 ASSERT_EQ(0, ret
) TH_LOG("failed to listen.");
152 ret
= listen(fd
[1], 5);
153 EXPECT_EQ(-1, ret
) TH_LOG("should fail to listen because only one uid reserves the port in TCP_LISTEN.");
156 for (j
= 0; j
< 2; j
++)