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

sk_assign.c (8127B)


      1// SPDX-License-Identifier: GPL-2.0
      2// Copyright (c) 2018 Facebook
      3// Copyright (c) 2019 Cloudflare
      4// Copyright (c) 2020 Isovalent, Inc.
      5/*
      6 * Test that the socket assign program is able to redirect traffic towards a
      7 * socket, regardless of whether the port or address destination of the traffic
      8 * matches the port.
      9 */
     10
     11#define _GNU_SOURCE
     12#include <fcntl.h>
     13#include <signal.h>
     14#include <stdlib.h>
     15#include <unistd.h>
     16
     17#include "test_progs.h"
     18
     19#define BIND_PORT 1234
     20#define CONNECT_PORT 4321
     21#define TEST_DADDR (0xC0A80203)
     22#define NS_SELF "/proc/self/ns/net"
     23#define SERVER_MAP_PATH "/sys/fs/bpf/tc/globals/server_map"
     24
     25static const struct timeval timeo_sec = { .tv_sec = 3 };
     26static const size_t timeo_optlen = sizeof(timeo_sec);
     27static int stop, duration;
     28
     29static bool
     30configure_stack(void)
     31{
     32	char tc_cmd[BUFSIZ];
     33
     34	/* Move to a new networking namespace */
     35	if (CHECK_FAIL(unshare(CLONE_NEWNET)))
     36		return false;
     37
     38	/* Configure necessary links, routes */
     39	if (CHECK_FAIL(system("ip link set dev lo up")))
     40		return false;
     41	if (CHECK_FAIL(system("ip route add local default dev lo")))
     42		return false;
     43	if (CHECK_FAIL(system("ip -6 route add local default dev lo")))
     44		return false;
     45
     46	/* Load qdisc, BPF program */
     47	if (CHECK_FAIL(system("tc qdisc add dev lo clsact")))
     48		return false;
     49	sprintf(tc_cmd, "%s %s %s %s", "tc filter add dev lo ingress bpf",
     50		       "direct-action object-file ./test_sk_assign.o",
     51		       "section tc",
     52		       (env.verbosity < VERBOSE_VERY) ? " 2>/dev/null" : "verbose");
     53	if (CHECK(system(tc_cmd), "BPF load failed;",
     54		  "run with -vv for more info\n"))
     55		return false;
     56
     57	return true;
     58}
     59
     60static int
     61start_server(const struct sockaddr *addr, socklen_t len, int type)
     62{
     63	int fd;
     64
     65	fd = socket(addr->sa_family, type, 0);
     66	if (CHECK_FAIL(fd == -1))
     67		goto out;
     68	if (CHECK_FAIL(setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeo_sec,
     69				  timeo_optlen)))
     70		goto close_out;
     71	if (CHECK_FAIL(bind(fd, addr, len) == -1))
     72		goto close_out;
     73	if (type == SOCK_STREAM && CHECK_FAIL(listen(fd, 128) == -1))
     74		goto close_out;
     75
     76	goto out;
     77close_out:
     78	close(fd);
     79	fd = -1;
     80out:
     81	return fd;
     82}
     83
     84static int
     85connect_to_server(const struct sockaddr *addr, socklen_t len, int type)
     86{
     87	int fd = -1;
     88
     89	fd = socket(addr->sa_family, type, 0);
     90	if (CHECK_FAIL(fd == -1))
     91		goto out;
     92	if (CHECK_FAIL(setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeo_sec,
     93				  timeo_optlen)))
     94		goto close_out;
     95	if (CHECK_FAIL(connect(fd, addr, len)))
     96		goto close_out;
     97
     98	goto out;
     99close_out:
    100	close(fd);
    101	fd = -1;
    102out:
    103	return fd;
    104}
    105
    106static in_port_t
    107get_port(int fd)
    108{
    109	struct sockaddr_storage ss;
    110	socklen_t slen = sizeof(ss);
    111	in_port_t port = 0;
    112
    113	if (CHECK_FAIL(getsockname(fd, (struct sockaddr *)&ss, &slen)))
    114		return port;
    115
    116	switch (ss.ss_family) {
    117	case AF_INET:
    118		port = ((struct sockaddr_in *)&ss)->sin_port;
    119		break;
    120	case AF_INET6:
    121		port = ((struct sockaddr_in6 *)&ss)->sin6_port;
    122		break;
    123	default:
    124		CHECK(1, "Invalid address family", "%d\n", ss.ss_family);
    125	}
    126	return port;
    127}
    128
    129static ssize_t
    130rcv_msg(int srv_client, int type)
    131{
    132	struct sockaddr_storage ss;
    133	char buf[BUFSIZ];
    134	socklen_t slen;
    135
    136	if (type == SOCK_STREAM)
    137		return read(srv_client, &buf, sizeof(buf));
    138	else
    139		return recvfrom(srv_client, &buf, sizeof(buf), 0,
    140				(struct sockaddr *)&ss, &slen);
    141}
    142
    143static int
    144run_test(int server_fd, const struct sockaddr *addr, socklen_t len, int type)
    145{
    146	int client = -1, srv_client = -1;
    147	char buf[] = "testing";
    148	in_port_t port;
    149	int ret = 1;
    150
    151	client = connect_to_server(addr, len, type);
    152	if (client == -1) {
    153		perror("Cannot connect to server");
    154		goto out;
    155	}
    156
    157	if (type == SOCK_STREAM) {
    158		srv_client = accept(server_fd, NULL, NULL);
    159		if (CHECK_FAIL(srv_client == -1)) {
    160			perror("Can't accept connection");
    161			goto out;
    162		}
    163	} else {
    164		srv_client = server_fd;
    165	}
    166	if (CHECK_FAIL(write(client, buf, sizeof(buf)) != sizeof(buf))) {
    167		perror("Can't write on client");
    168		goto out;
    169	}
    170	if (CHECK_FAIL(rcv_msg(srv_client, type) != sizeof(buf))) {
    171		perror("Can't read on server");
    172		goto out;
    173	}
    174
    175	port = get_port(srv_client);
    176	if (CHECK_FAIL(!port))
    177		goto out;
    178	/* SOCK_STREAM is connected via accept(), so the server's local address
    179	 * will be the CONNECT_PORT rather than the BIND port that corresponds
    180	 * to the listen socket. SOCK_DGRAM on the other hand is connectionless
    181	 * so we can't really do the same check there; the server doesn't ever
    182	 * create a socket with CONNECT_PORT.
    183	 */
    184	if (type == SOCK_STREAM &&
    185	    CHECK(port != htons(CONNECT_PORT), "Expected", "port %u but got %u",
    186		  CONNECT_PORT, ntohs(port)))
    187		goto out;
    188	else if (type == SOCK_DGRAM &&
    189		 CHECK(port != htons(BIND_PORT), "Expected",
    190		       "port %u but got %u", BIND_PORT, ntohs(port)))
    191		goto out;
    192
    193	ret = 0;
    194out:
    195	close(client);
    196	if (srv_client != server_fd)
    197		close(srv_client);
    198	if (ret)
    199		WRITE_ONCE(stop, 1);
    200	return ret;
    201}
    202
    203static void
    204prepare_addr(struct sockaddr *addr, int family, __u16 port, bool rewrite_addr)
    205{
    206	struct sockaddr_in *addr4;
    207	struct sockaddr_in6 *addr6;
    208
    209	switch (family) {
    210	case AF_INET:
    211		addr4 = (struct sockaddr_in *)addr;
    212		memset(addr4, 0, sizeof(*addr4));
    213		addr4->sin_family = family;
    214		addr4->sin_port = htons(port);
    215		if (rewrite_addr)
    216			addr4->sin_addr.s_addr = htonl(TEST_DADDR);
    217		else
    218			addr4->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
    219		break;
    220	case AF_INET6:
    221		addr6 = (struct sockaddr_in6 *)addr;
    222		memset(addr6, 0, sizeof(*addr6));
    223		addr6->sin6_family = family;
    224		addr6->sin6_port = htons(port);
    225		addr6->sin6_addr = in6addr_loopback;
    226		if (rewrite_addr)
    227			addr6->sin6_addr.s6_addr32[3] = htonl(TEST_DADDR);
    228		break;
    229	default:
    230		fprintf(stderr, "Invalid family %d", family);
    231	}
    232}
    233
    234struct test_sk_cfg {
    235	const char *name;
    236	int family;
    237	struct sockaddr *addr;
    238	socklen_t len;
    239	int type;
    240	bool rewrite_addr;
    241};
    242
    243#define TEST(NAME, FAMILY, TYPE, REWRITE)				\
    244{									\
    245	.name = NAME,							\
    246	.family = FAMILY,						\
    247	.addr = (FAMILY == AF_INET) ? (struct sockaddr *)&addr4		\
    248				    : (struct sockaddr *)&addr6,	\
    249	.len = (FAMILY == AF_INET) ? sizeof(addr4) : sizeof(addr6),	\
    250	.type = TYPE,							\
    251	.rewrite_addr = REWRITE,					\
    252}
    253
    254void test_sk_assign(void)
    255{
    256	struct sockaddr_in addr4;
    257	struct sockaddr_in6 addr6;
    258	struct test_sk_cfg tests[] = {
    259		TEST("ipv4 tcp port redir", AF_INET, SOCK_STREAM, false),
    260		TEST("ipv4 tcp addr redir", AF_INET, SOCK_STREAM, true),
    261		TEST("ipv6 tcp port redir", AF_INET6, SOCK_STREAM, false),
    262		TEST("ipv6 tcp addr redir", AF_INET6, SOCK_STREAM, true),
    263		TEST("ipv4 udp port redir", AF_INET, SOCK_DGRAM, false),
    264		TEST("ipv4 udp addr redir", AF_INET, SOCK_DGRAM, true),
    265		TEST("ipv6 udp port redir", AF_INET6, SOCK_DGRAM, false),
    266		TEST("ipv6 udp addr redir", AF_INET6, SOCK_DGRAM, true),
    267	};
    268	__s64 server = -1;
    269	int server_map;
    270	int self_net;
    271	int i;
    272
    273	self_net = open(NS_SELF, O_RDONLY);
    274	if (CHECK_FAIL(self_net < 0)) {
    275		perror("Unable to open "NS_SELF);
    276		return;
    277	}
    278
    279	if (!configure_stack()) {
    280		perror("configure_stack");
    281		goto cleanup;
    282	}
    283
    284	server_map = bpf_obj_get(SERVER_MAP_PATH);
    285	if (CHECK_FAIL(server_map < 0)) {
    286		perror("Unable to open " SERVER_MAP_PATH);
    287		goto cleanup;
    288	}
    289
    290	for (i = 0; i < ARRAY_SIZE(tests) && !READ_ONCE(stop); i++) {
    291		struct test_sk_cfg *test = &tests[i];
    292		const struct sockaddr *addr;
    293		const int zero = 0;
    294		int err;
    295
    296		if (!test__start_subtest(test->name))
    297			continue;
    298		prepare_addr(test->addr, test->family, BIND_PORT, false);
    299		addr = (const struct sockaddr *)test->addr;
    300		server = start_server(addr, test->len, test->type);
    301		if (server == -1)
    302			goto close;
    303
    304		err = bpf_map_update_elem(server_map, &zero, &server, BPF_ANY);
    305		if (CHECK_FAIL(err)) {
    306			perror("Unable to update server_map");
    307			goto close;
    308		}
    309
    310		/* connect to unbound ports */
    311		prepare_addr(test->addr, test->family, CONNECT_PORT,
    312			     test->rewrite_addr);
    313		if (run_test(server, addr, test->len, test->type))
    314			goto close;
    315
    316		close(server);
    317		server = -1;
    318	}
    319
    320close:
    321	close(server);
    322	close(server_map);
    323cleanup:
    324	if (CHECK_FAIL(unlink(SERVER_MAP_PATH)))
    325		perror("Unable to unlink " SERVER_MAP_PATH);
    326	if (CHECK_FAIL(setns(self_net, CLONE_NEWNET)))
    327		perror("Failed to setns("NS_SELF")");
    328	close(self_net);
    329}