cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

reuseaddr_ports_exhausted.c (4070B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Check if we can fully utilize 4-tuples for connect().
      4 *
      5 * Rules to bind sockets to the same port when all ephemeral ports are
      6 * exhausted.
      7 *
      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,
     12 *        succeed to bind.
     13 *   5. if SO_REUSEADDR and SO_REUSEPORT are enabled and
     14 *        there is no socket having the both options and the same EUID,
     15 *        succeed to bind.
     16 *   6. fail to bind.
     17 *
     18 * Author: Kuniyuki Iwashima <kuniyu@amazon.co.jp>
     19 */
     20#include <arpa/inet.h>
     21#include <netinet/in.h>
     22#include <sys/socket.h>
     23#include <sys/types.h>
     24#include <unistd.h>
     25#include "../kselftest_harness.h"
     26
     27struct reuse_opts {
     28	int reuseaddr[2];
     29	int reuseport[2];
     30};
     31
     32struct reuse_opts unreusable_opts[12] = {
     33	{{0, 0}, {0, 0}},
     34	{{0, 0}, {0, 1}},
     35	{{0, 0}, {1, 0}},
     36	{{0, 0}, {1, 1}},
     37	{{0, 1}, {0, 0}},
     38	{{0, 1}, {0, 1}},
     39	{{0, 1}, {1, 0}},
     40	{{0, 1}, {1, 1}},
     41	{{1, 0}, {0, 0}},
     42	{{1, 0}, {0, 1}},
     43	{{1, 0}, {1, 0}},
     44	{{1, 0}, {1, 1}},
     45};
     46
     47struct reuse_opts reusable_opts[4] = {
     48	{{1, 1}, {0, 0}},
     49	{{1, 1}, {0, 1}},
     50	{{1, 1}, {1, 0}},
     51	{{1, 1}, {1, 1}},
     52};
     53
     54int bind_port(struct __test_metadata *_metadata, int reuseaddr, int reuseport)
     55{
     56	struct sockaddr_in local_addr;
     57	int len = sizeof(local_addr);
     58	int fd, ret;
     59
     60	fd = socket(AF_INET, SOCK_STREAM, 0);
     61	ASSERT_NE(-1, fd) TH_LOG("failed to open socket.");
     62
     63	ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(int));
     64	ASSERT_EQ(0, ret) TH_LOG("failed to setsockopt: SO_REUSEADDR.");
     65
     66	ret = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &reuseport, sizeof(int));
     67	ASSERT_EQ(0, ret) TH_LOG("failed to setsockopt: SO_REUSEPORT.");
     68
     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;
     72
     73	if (bind(fd, (struct sockaddr *)&local_addr, len) == -1) {
     74		close(fd);
     75		return -1;
     76	}
     77
     78	return fd;
     79}
     80
     81TEST(reuseaddr_ports_exhausted_unreusable)
     82{
     83	struct reuse_opts *opts;
     84	int i, j, fd[2];
     85
     86	for (i = 0; i < 12; i++) {
     87		opts = &unreusable_opts[i];
     88
     89		for (j = 0; j < 2; j++)
     90			fd[j] = bind_port(_metadata, opts->reuseaddr[j], opts->reuseport[j]);
     91
     92		ASSERT_NE(-1, fd[0]) TH_LOG("failed to bind.");
     93		EXPECT_EQ(-1, fd[1]) TH_LOG("should fail to bind.");
     94
     95		for (j = 0; j < 2; j++)
     96			if (fd[j] != -1)
     97				close(fd[j]);
     98	}
     99}
    100
    101TEST(reuseaddr_ports_exhausted_reusable_same_euid)
    102{
    103	struct reuse_opts *opts;
    104	int i, j, fd[2];
    105
    106	for (i = 0; i < 4; i++) {
    107		opts = &reusable_opts[i];
    108
    109		for (j = 0; j < 2; j++)
    110			fd[j] = bind_port(_metadata, opts->reuseaddr[j], opts->reuseport[j]);
    111
    112		ASSERT_NE(-1, fd[0]) TH_LOG("failed to bind.");
    113
    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.");
    116		} else {
    117			EXPECT_NE(-1, fd[1]) TH_LOG("should succeed to bind to connect to different destinations.");
    118		}
    119
    120		for (j = 0; j < 2; j++)
    121			if (fd[j] != -1)
    122				close(fd[j]);
    123	}
    124}
    125
    126TEST(reuseaddr_ports_exhausted_reusable_different_euid)
    127{
    128	struct reuse_opts *opts;
    129	int i, j, ret, fd[2];
    130	uid_t euid[2] = {10, 20};
    131
    132	for (i = 0; i < 4; i++) {
    133		opts = &reusable_opts[i];
    134
    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]);
    138
    139			fd[j] = bind_port(_metadata, opts->reuseaddr[j], opts->reuseport[j]);
    140
    141			ret = seteuid(0);
    142			ASSERT_EQ(0, ret) TH_LOG("failed to seteuid: 0.");
    143		}
    144
    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.");
    147
    148		if (fd[1] != -1) {
    149			ret = listen(fd[0], 5);
    150			ASSERT_EQ(0, ret) TH_LOG("failed to listen.");
    151
    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.");
    154		}
    155
    156		for (j = 0; j < 2; j++)
    157			if (fd[j] != -1)
    158				close(fd[j]);
    159	}
    160}
    161
    162TEST_HARNESS_MAIN