17f204a7dSKuniyuki Iwashima // SPDX-License-Identifier: GPL-2.0-only
27f204a7dSKuniyuki Iwashima /*
37f204a7dSKuniyuki Iwashima  * Check if we can fully utilize 4-tuples for connect().
47f204a7dSKuniyuki Iwashima  *
57f204a7dSKuniyuki Iwashima  * Rules to bind sockets to the same port when all ephemeral ports are
67f204a7dSKuniyuki Iwashima  * exhausted.
77f204a7dSKuniyuki Iwashima  *
87f204a7dSKuniyuki Iwashima  *   1. if there are TCP_LISTEN sockets on the port, fail to bind.
97f204a7dSKuniyuki Iwashima  *   2. if there are sockets without SO_REUSEADDR, fail to bind.
107f204a7dSKuniyuki Iwashima  *   3. if SO_REUSEADDR is disabled, fail to bind.
117f204a7dSKuniyuki Iwashima  *   4. if SO_REUSEADDR is enabled and SO_REUSEPORT is disabled,
127f204a7dSKuniyuki Iwashima  *        succeed to bind.
137f204a7dSKuniyuki Iwashima  *   5. if SO_REUSEADDR and SO_REUSEPORT are enabled and
147f204a7dSKuniyuki Iwashima  *        there is no socket having the both options and the same EUID,
157f204a7dSKuniyuki Iwashima  *        succeed to bind.
167f204a7dSKuniyuki Iwashima  *   6. fail to bind.
177f204a7dSKuniyuki Iwashima  *
187f204a7dSKuniyuki Iwashima  * Author: Kuniyuki Iwashima <kuniyu@amazon.co.jp>
197f204a7dSKuniyuki Iwashima  */
207f204a7dSKuniyuki Iwashima #include <arpa/inet.h>
217f204a7dSKuniyuki Iwashima #include <netinet/in.h>
227f204a7dSKuniyuki Iwashima #include <sys/socket.h>
237f204a7dSKuniyuki Iwashima #include <sys/types.h>
247f204a7dSKuniyuki Iwashima #include <unistd.h>
257f204a7dSKuniyuki Iwashima #include "../kselftest_harness.h"
267f204a7dSKuniyuki Iwashima 
277f204a7dSKuniyuki Iwashima struct reuse_opts {
287f204a7dSKuniyuki Iwashima 	int reuseaddr[2];
297f204a7dSKuniyuki Iwashima 	int reuseport[2];
307f204a7dSKuniyuki Iwashima };
317f204a7dSKuniyuki Iwashima 
327f204a7dSKuniyuki Iwashima struct reuse_opts unreusable_opts[12] = {
33*81f711d6SCarlos Llamas 	{{0, 0}, {0, 0}},
34*81f711d6SCarlos Llamas 	{{0, 0}, {0, 1}},
35*81f711d6SCarlos Llamas 	{{0, 0}, {1, 0}},
36*81f711d6SCarlos Llamas 	{{0, 0}, {1, 1}},
37*81f711d6SCarlos Llamas 	{{0, 1}, {0, 0}},
38*81f711d6SCarlos Llamas 	{{0, 1}, {0, 1}},
39*81f711d6SCarlos Llamas 	{{0, 1}, {1, 0}},
40*81f711d6SCarlos Llamas 	{{0, 1}, {1, 1}},
41*81f711d6SCarlos Llamas 	{{1, 0}, {0, 0}},
42*81f711d6SCarlos Llamas 	{{1, 0}, {0, 1}},
43*81f711d6SCarlos Llamas 	{{1, 0}, {1, 0}},
44*81f711d6SCarlos Llamas 	{{1, 0}, {1, 1}},
457f204a7dSKuniyuki Iwashima };
467f204a7dSKuniyuki Iwashima 
477f204a7dSKuniyuki Iwashima struct reuse_opts reusable_opts[4] = {
48*81f711d6SCarlos Llamas 	{{1, 1}, {0, 0}},
49*81f711d6SCarlos Llamas 	{{1, 1}, {0, 1}},
50*81f711d6SCarlos Llamas 	{{1, 1}, {1, 0}},
51*81f711d6SCarlos Llamas 	{{1, 1}, {1, 1}},
527f204a7dSKuniyuki Iwashima };
537f204a7dSKuniyuki Iwashima 
bind_port(struct __test_metadata * _metadata,int reuseaddr,int reuseport)547f204a7dSKuniyuki Iwashima int bind_port(struct __test_metadata *_metadata, int reuseaddr, int reuseport)
557f204a7dSKuniyuki Iwashima {
567f204a7dSKuniyuki Iwashima 	struct sockaddr_in local_addr;
577f204a7dSKuniyuki Iwashima 	int len = sizeof(local_addr);
587f204a7dSKuniyuki Iwashima 	int fd, ret;
597f204a7dSKuniyuki Iwashima 
607f204a7dSKuniyuki Iwashima 	fd = socket(AF_INET, SOCK_STREAM, 0);
617f204a7dSKuniyuki Iwashima 	ASSERT_NE(-1, fd) TH_LOG("failed to open socket.");
627f204a7dSKuniyuki Iwashima 
637f204a7dSKuniyuki Iwashima 	ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(int));
647f204a7dSKuniyuki Iwashima 	ASSERT_EQ(0, ret) TH_LOG("failed to setsockopt: SO_REUSEADDR.");
657f204a7dSKuniyuki Iwashima 
667f204a7dSKuniyuki Iwashima 	ret = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &reuseport, sizeof(int));
677f204a7dSKuniyuki Iwashima 	ASSERT_EQ(0, ret) TH_LOG("failed to setsockopt: SO_REUSEPORT.");
687f204a7dSKuniyuki Iwashima 
697f204a7dSKuniyuki Iwashima 	local_addr.sin_family = AF_INET;
707f204a7dSKuniyuki Iwashima 	local_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
717f204a7dSKuniyuki Iwashima 	local_addr.sin_port = 0;
727f204a7dSKuniyuki Iwashima 
737f204a7dSKuniyuki Iwashima 	if (bind(fd, (struct sockaddr *)&local_addr, len) == -1) {
747f204a7dSKuniyuki Iwashima 		close(fd);
757f204a7dSKuniyuki Iwashima 		return -1;
767f204a7dSKuniyuki Iwashima 	}
777f204a7dSKuniyuki Iwashima 
787f204a7dSKuniyuki Iwashima 	return fd;
797f204a7dSKuniyuki Iwashima }
807f204a7dSKuniyuki Iwashima 
TEST(reuseaddr_ports_exhausted_unreusable)817f204a7dSKuniyuki Iwashima TEST(reuseaddr_ports_exhausted_unreusable)
827f204a7dSKuniyuki Iwashima {
837f204a7dSKuniyuki Iwashima 	struct reuse_opts *opts;
847f204a7dSKuniyuki Iwashima 	int i, j, fd[2];
857f204a7dSKuniyuki Iwashima 
867f204a7dSKuniyuki Iwashima 	for (i = 0; i < 12; i++) {
877f204a7dSKuniyuki Iwashima 		opts = &unreusable_opts[i];
887f204a7dSKuniyuki Iwashima 
897f204a7dSKuniyuki Iwashima 		for (j = 0; j < 2; j++)
907f204a7dSKuniyuki Iwashima 			fd[j] = bind_port(_metadata, opts->reuseaddr[j], opts->reuseport[j]);
917f204a7dSKuniyuki Iwashima 
927f204a7dSKuniyuki Iwashima 		ASSERT_NE(-1, fd[0]) TH_LOG("failed to bind.");
937f204a7dSKuniyuki Iwashima 		EXPECT_EQ(-1, fd[1]) TH_LOG("should fail to bind.");
947f204a7dSKuniyuki Iwashima 
957f204a7dSKuniyuki Iwashima 		for (j = 0; j < 2; j++)
967f204a7dSKuniyuki Iwashima 			if (fd[j] != -1)
977f204a7dSKuniyuki Iwashima 				close(fd[j]);
987f204a7dSKuniyuki Iwashima 	}
997f204a7dSKuniyuki Iwashima }
1007f204a7dSKuniyuki Iwashima 
TEST(reuseaddr_ports_exhausted_reusable_same_euid)1017f204a7dSKuniyuki Iwashima TEST(reuseaddr_ports_exhausted_reusable_same_euid)
1027f204a7dSKuniyuki Iwashima {
1037f204a7dSKuniyuki Iwashima 	struct reuse_opts *opts;
1047f204a7dSKuniyuki Iwashima 	int i, j, fd[2];
1057f204a7dSKuniyuki Iwashima 
1067f204a7dSKuniyuki Iwashima 	for (i = 0; i < 4; i++) {
1077f204a7dSKuniyuki Iwashima 		opts = &reusable_opts[i];
1087f204a7dSKuniyuki Iwashima 
1097f204a7dSKuniyuki Iwashima 		for (j = 0; j < 2; j++)
1107f204a7dSKuniyuki Iwashima 			fd[j] = bind_port(_metadata, opts->reuseaddr[j], opts->reuseport[j]);
1117f204a7dSKuniyuki Iwashima 
1127f204a7dSKuniyuki Iwashima 		ASSERT_NE(-1, fd[0]) TH_LOG("failed to bind.");
1137f204a7dSKuniyuki Iwashima 
1147f204a7dSKuniyuki Iwashima 		if (opts->reuseport[0] && opts->reuseport[1]) {
1157f204a7dSKuniyuki Iwashima 			EXPECT_EQ(-1, fd[1]) TH_LOG("should fail to bind because both sockets succeed to be listened.");
1167f204a7dSKuniyuki Iwashima 		} else {
1177f204a7dSKuniyuki Iwashima 			EXPECT_NE(-1, fd[1]) TH_LOG("should succeed to bind to connect to different destinations.");
1187f204a7dSKuniyuki Iwashima 		}
1197f204a7dSKuniyuki Iwashima 
1207f204a7dSKuniyuki Iwashima 		for (j = 0; j < 2; j++)
1217f204a7dSKuniyuki Iwashima 			if (fd[j] != -1)
1227f204a7dSKuniyuki Iwashima 				close(fd[j]);
1237f204a7dSKuniyuki Iwashima 	}
1247f204a7dSKuniyuki Iwashima }
1257f204a7dSKuniyuki Iwashima 
TEST(reuseaddr_ports_exhausted_reusable_different_euid)1267f204a7dSKuniyuki Iwashima TEST(reuseaddr_ports_exhausted_reusable_different_euid)
1277f204a7dSKuniyuki Iwashima {
1287f204a7dSKuniyuki Iwashima 	struct reuse_opts *opts;
1297f204a7dSKuniyuki Iwashima 	int i, j, ret, fd[2];
1307f204a7dSKuniyuki Iwashima 	uid_t euid[2] = {10, 20};
1317f204a7dSKuniyuki Iwashima 
1327f204a7dSKuniyuki Iwashima 	for (i = 0; i < 4; i++) {
1337f204a7dSKuniyuki Iwashima 		opts = &reusable_opts[i];
1347f204a7dSKuniyuki Iwashima 
1357f204a7dSKuniyuki Iwashima 		for (j = 0; j < 2; j++) {
1367f204a7dSKuniyuki Iwashima 			ret = seteuid(euid[j]);
1377f204a7dSKuniyuki Iwashima 			ASSERT_EQ(0, ret) TH_LOG("failed to seteuid: %d.", euid[j]);
1387f204a7dSKuniyuki Iwashima 
1397f204a7dSKuniyuki Iwashima 			fd[j] = bind_port(_metadata, opts->reuseaddr[j], opts->reuseport[j]);
1407f204a7dSKuniyuki Iwashima 
1417f204a7dSKuniyuki Iwashima 			ret = seteuid(0);
1427f204a7dSKuniyuki Iwashima 			ASSERT_EQ(0, ret) TH_LOG("failed to seteuid: 0.");
1437f204a7dSKuniyuki Iwashima 		}
1447f204a7dSKuniyuki Iwashima 
1457f204a7dSKuniyuki Iwashima 		ASSERT_NE(-1, fd[0]) TH_LOG("failed to bind.");
1467f204a7dSKuniyuki Iwashima 		EXPECT_NE(-1, fd[1]) TH_LOG("should succeed to bind because one socket can be bound in each euid.");
1477f204a7dSKuniyuki Iwashima 
1487f204a7dSKuniyuki Iwashima 		if (fd[1] != -1) {
1497f204a7dSKuniyuki Iwashima 			ret = listen(fd[0], 5);
1507f204a7dSKuniyuki Iwashima 			ASSERT_EQ(0, ret) TH_LOG("failed to listen.");
1517f204a7dSKuniyuki Iwashima 
1527f204a7dSKuniyuki Iwashima 			ret = listen(fd[1], 5);
1537f204a7dSKuniyuki Iwashima 			EXPECT_EQ(-1, ret) TH_LOG("should fail to listen because only one uid reserves the port in TCP_LISTEN.");
1547f204a7dSKuniyuki Iwashima 		}
1557f204a7dSKuniyuki Iwashima 
1567f204a7dSKuniyuki Iwashima 		for (j = 0; j < 2; j++)
1577f204a7dSKuniyuki Iwashima 			if (fd[j] != -1)
1587f204a7dSKuniyuki Iwashima 				close(fd[j]);
1597f204a7dSKuniyuki Iwashima 	}
1607f204a7dSKuniyuki Iwashima }
1617f204a7dSKuniyuki Iwashima 
1627f204a7dSKuniyuki Iwashima TEST_HARNESS_MAIN
163