brintos

brintos / linux-shallow public Read only

0
0
Text · 13.0 KiB · 810c374 Raw
557 lines · c
1// SPDX-License-Identifier: GPL-2.02// Copyright (c) 2018 Facebook3 4#include <stdio.h>5#include <unistd.h>6 7#include <arpa/inet.h>8#include <sys/types.h>9#include <sys/socket.h>10 11#include <linux/filter.h>12 13#include <bpf/bpf.h>14 15#include "cgroup_helpers.h"16#include <bpf/bpf_endian.h>17#include "bpf_util.h"18 19#define CG_PATH		"/foo"20#define MAX_INSNS	51221 22char bpf_log_buf[BPF_LOG_BUF_SIZE];23static bool verbose = false;24 25struct sock_test {26	const char *descr;27	/* BPF prog properties */28	struct bpf_insn	insns[MAX_INSNS];29	enum bpf_attach_type expected_attach_type;30	enum bpf_attach_type attach_type;31	/* Socket properties */32	int domain;33	int type;34	/* Endpoint to bind() to */35	const char *ip;36	unsigned short port;37	unsigned short port_retry;38	/* Expected test result */39	enum {40		LOAD_REJECT,41		ATTACH_REJECT,42		BIND_REJECT,43		SUCCESS,44		RETRY_SUCCESS,45		RETRY_REJECT46	} result;47};48 49static struct sock_test tests[] = {50	{51		.descr = "bind4 load with invalid access: src_ip6",52		.insns = {53			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),54			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,55				    offsetof(struct bpf_sock, src_ip6[0])),56			BPF_MOV64_IMM(BPF_REG_0, 1),57			BPF_EXIT_INSN(),58		},59		.expected_attach_type = BPF_CGROUP_INET4_POST_BIND,60		.attach_type = BPF_CGROUP_INET4_POST_BIND,61		.result = LOAD_REJECT,62	},63	{64		.descr = "bind4 load with invalid access: mark",65		.insns = {66			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),67			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,68				    offsetof(struct bpf_sock, mark)),69			BPF_MOV64_IMM(BPF_REG_0, 1),70			BPF_EXIT_INSN(),71		},72		.expected_attach_type = BPF_CGROUP_INET4_POST_BIND,73		.attach_type = BPF_CGROUP_INET4_POST_BIND,74		.result = LOAD_REJECT,75	},76	{77		.descr = "bind6 load with invalid access: src_ip4",78		.insns = {79			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),80			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,81				    offsetof(struct bpf_sock, src_ip4)),82			BPF_MOV64_IMM(BPF_REG_0, 1),83			BPF_EXIT_INSN(),84		},85		.expected_attach_type = BPF_CGROUP_INET6_POST_BIND,86		.attach_type = BPF_CGROUP_INET6_POST_BIND,87		.result = LOAD_REJECT,88	},89	{90		.descr = "sock_create load with invalid access: src_port",91		.insns = {92			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),93			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,94				    offsetof(struct bpf_sock, src_port)),95			BPF_MOV64_IMM(BPF_REG_0, 1),96			BPF_EXIT_INSN(),97		},98		.expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE,99		.attach_type = BPF_CGROUP_INET_SOCK_CREATE,100		.result = LOAD_REJECT,101	},102	{103		.descr = "sock_create load w/o expected_attach_type (compat mode)",104		.insns = {105			BPF_MOV64_IMM(BPF_REG_0, 1),106			BPF_EXIT_INSN(),107		},108		.expected_attach_type = 0,109		.attach_type = BPF_CGROUP_INET_SOCK_CREATE,110		.domain = AF_INET,111		.type = SOCK_STREAM,112		.ip = "127.0.0.1",113		.port = 8097,114		.result = SUCCESS,115	},116	{117		.descr = "sock_create load w/ expected_attach_type",118		.insns = {119			BPF_MOV64_IMM(BPF_REG_0, 1),120			BPF_EXIT_INSN(),121		},122		.expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE,123		.attach_type = BPF_CGROUP_INET_SOCK_CREATE,124		.domain = AF_INET,125		.type = SOCK_STREAM,126		.ip = "127.0.0.1",127		.port = 8097,128		.result = SUCCESS,129	},130	{131		.descr = "attach type mismatch bind4 vs bind6",132		.insns = {133			BPF_MOV64_IMM(BPF_REG_0, 1),134			BPF_EXIT_INSN(),135		},136		.expected_attach_type = BPF_CGROUP_INET4_POST_BIND,137		.attach_type = BPF_CGROUP_INET6_POST_BIND,138		.result = ATTACH_REJECT,139	},140	{141		.descr = "attach type mismatch bind6 vs bind4",142		.insns = {143			BPF_MOV64_IMM(BPF_REG_0, 1),144			BPF_EXIT_INSN(),145		},146		.expected_attach_type = BPF_CGROUP_INET6_POST_BIND,147		.attach_type = BPF_CGROUP_INET4_POST_BIND,148		.result = ATTACH_REJECT,149	},150	{151		.descr = "attach type mismatch default vs bind4",152		.insns = {153			BPF_MOV64_IMM(BPF_REG_0, 1),154			BPF_EXIT_INSN(),155		},156		.expected_attach_type = 0,157		.attach_type = BPF_CGROUP_INET4_POST_BIND,158		.result = ATTACH_REJECT,159	},160	{161		.descr = "attach type mismatch bind6 vs sock_create",162		.insns = {163			BPF_MOV64_IMM(BPF_REG_0, 1),164			BPF_EXIT_INSN(),165		},166		.expected_attach_type = BPF_CGROUP_INET6_POST_BIND,167		.attach_type = BPF_CGROUP_INET_SOCK_CREATE,168		.result = ATTACH_REJECT,169	},170	{171		.descr = "bind4 reject all",172		.insns = {173			BPF_MOV64_IMM(BPF_REG_0, 0),174			BPF_EXIT_INSN(),175		},176		.expected_attach_type = BPF_CGROUP_INET4_POST_BIND,177		.attach_type = BPF_CGROUP_INET4_POST_BIND,178		.domain = AF_INET,179		.type = SOCK_STREAM,180		.ip = "0.0.0.0",181		.result = BIND_REJECT,182	},183	{184		.descr = "bind6 reject all",185		.insns = {186			BPF_MOV64_IMM(BPF_REG_0, 0),187			BPF_EXIT_INSN(),188		},189		.expected_attach_type = BPF_CGROUP_INET6_POST_BIND,190		.attach_type = BPF_CGROUP_INET6_POST_BIND,191		.domain = AF_INET6,192		.type = SOCK_STREAM,193		.ip = "::",194		.result = BIND_REJECT,195	},196	{197		.descr = "bind6 deny specific IP & port",198		.insns = {199			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),200 201			/* if (ip == expected && port == expected) */202			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,203				    offsetof(struct bpf_sock, src_ip6[3])),204			BPF_JMP_IMM(BPF_JNE, BPF_REG_7,205				    __bpf_constant_ntohl(0x00000001), 4),206			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,207				    offsetof(struct bpf_sock, src_port)),208			BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x2001, 2),209 210			/* return DENY; */211			BPF_MOV64_IMM(BPF_REG_0, 0),212			BPF_JMP_A(1),213 214			/* else return ALLOW; */215			BPF_MOV64_IMM(BPF_REG_0, 1),216			BPF_EXIT_INSN(),217		},218		.expected_attach_type = BPF_CGROUP_INET6_POST_BIND,219		.attach_type = BPF_CGROUP_INET6_POST_BIND,220		.domain = AF_INET6,221		.type = SOCK_STREAM,222		.ip = "::1",223		.port = 8193,224		.result = BIND_REJECT,225	},226	{227		.descr = "bind4 allow specific IP & port",228		.insns = {229			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),230 231			/* if (ip == expected && port == expected) */232			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,233				    offsetof(struct bpf_sock, src_ip4)),234			BPF_JMP_IMM(BPF_JNE, BPF_REG_7,235				    __bpf_constant_ntohl(0x7F000001), 4),236			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,237				    offsetof(struct bpf_sock, src_port)),238			BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x1002, 2),239 240			/* return ALLOW; */241			BPF_MOV64_IMM(BPF_REG_0, 1),242			BPF_JMP_A(1),243 244			/* else return DENY; */245			BPF_MOV64_IMM(BPF_REG_0, 0),246			BPF_EXIT_INSN(),247		},248		.expected_attach_type = BPF_CGROUP_INET4_POST_BIND,249		.attach_type = BPF_CGROUP_INET4_POST_BIND,250		.domain = AF_INET,251		.type = SOCK_STREAM,252		.ip = "127.0.0.1",253		.port = 4098,254		.result = SUCCESS,255	},256	{257		.descr = "bind4 deny specific IP & port of TCP, and retry",258		.insns = {259			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),260 261			/* if (ip == expected && port == expected) */262			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,263				    offsetof(struct bpf_sock, src_ip4)),264			BPF_JMP_IMM(BPF_JNE, BPF_REG_7,265				    __bpf_constant_ntohl(0x7F000001), 4),266			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,267				    offsetof(struct bpf_sock, src_port)),268			BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x1002, 2),269 270			/* return DENY; */271			BPF_MOV64_IMM(BPF_REG_0, 0),272			BPF_JMP_A(1),273 274			/* else return ALLOW; */275			BPF_MOV64_IMM(BPF_REG_0, 1),276			BPF_EXIT_INSN(),277		},278		.expected_attach_type = BPF_CGROUP_INET4_POST_BIND,279		.attach_type = BPF_CGROUP_INET4_POST_BIND,280		.domain = AF_INET,281		.type = SOCK_STREAM,282		.ip = "127.0.0.1",283		.port = 4098,284		.port_retry = 5000,285		.result = RETRY_SUCCESS,286	},287	{288		.descr = "bind4 deny specific IP & port of UDP, and retry",289		.insns = {290			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),291 292			/* if (ip == expected && port == expected) */293			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,294				    offsetof(struct bpf_sock, src_ip4)),295			BPF_JMP_IMM(BPF_JNE, BPF_REG_7,296				    __bpf_constant_ntohl(0x7F000001), 4),297			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,298				    offsetof(struct bpf_sock, src_port)),299			BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x1002, 2),300 301			/* return DENY; */302			BPF_MOV64_IMM(BPF_REG_0, 0),303			BPF_JMP_A(1),304 305			/* else return ALLOW; */306			BPF_MOV64_IMM(BPF_REG_0, 1),307			BPF_EXIT_INSN(),308		},309		.expected_attach_type = BPF_CGROUP_INET4_POST_BIND,310		.attach_type = BPF_CGROUP_INET4_POST_BIND,311		.domain = AF_INET,312		.type = SOCK_DGRAM,313		.ip = "127.0.0.1",314		.port = 4098,315		.port_retry = 5000,316		.result = RETRY_SUCCESS,317	},318	{319		.descr = "bind6 deny specific IP & port, and retry",320		.insns = {321			BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),322 323			/* if (ip == expected && port == expected) */324			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,325				    offsetof(struct bpf_sock, src_ip6[3])),326			BPF_JMP_IMM(BPF_JNE, BPF_REG_7,327				    __bpf_constant_ntohl(0x00000001), 4),328			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,329				    offsetof(struct bpf_sock, src_port)),330			BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x2001, 2),331 332			/* return DENY; */333			BPF_MOV64_IMM(BPF_REG_0, 0),334			BPF_JMP_A(1),335 336			/* else return ALLOW; */337			BPF_MOV64_IMM(BPF_REG_0, 1),338			BPF_EXIT_INSN(),339		},340		.expected_attach_type = BPF_CGROUP_INET6_POST_BIND,341		.attach_type = BPF_CGROUP_INET6_POST_BIND,342		.domain = AF_INET6,343		.type = SOCK_STREAM,344		.ip = "::1",345		.port = 8193,346		.port_retry = 9000,347		.result = RETRY_SUCCESS,348	},349	{350		.descr = "bind4 allow all",351		.insns = {352			BPF_MOV64_IMM(BPF_REG_0, 1),353			BPF_EXIT_INSN(),354		},355		.expected_attach_type = BPF_CGROUP_INET4_POST_BIND,356		.attach_type = BPF_CGROUP_INET4_POST_BIND,357		.domain = AF_INET,358		.type = SOCK_STREAM,359		.ip = "0.0.0.0",360		.result = SUCCESS,361	},362	{363		.descr = "bind6 allow all",364		.insns = {365			BPF_MOV64_IMM(BPF_REG_0, 1),366			BPF_EXIT_INSN(),367		},368		.expected_attach_type = BPF_CGROUP_INET6_POST_BIND,369		.attach_type = BPF_CGROUP_INET6_POST_BIND,370		.domain = AF_INET6,371		.type = SOCK_STREAM,372		.ip = "::",373		.result = SUCCESS,374	},375};376 377static size_t probe_prog_length(const struct bpf_insn *fp)378{379	size_t len;380 381	for (len = MAX_INSNS - 1; len > 0; --len)382		if (fp[len].code != 0 || fp[len].imm != 0)383			break;384	return len + 1;385}386 387static int load_sock_prog(const struct bpf_insn *prog,388			  enum bpf_attach_type attach_type)389{390	LIBBPF_OPTS(bpf_prog_load_opts, opts);391	int ret, insn_cnt;392 393	insn_cnt = probe_prog_length(prog);394 395	opts.expected_attach_type = attach_type;396	opts.log_buf = bpf_log_buf;397	opts.log_size = BPF_LOG_BUF_SIZE;398	opts.log_level = 2;399 400	ret = bpf_prog_load(BPF_PROG_TYPE_CGROUP_SOCK, NULL, "GPL", prog, insn_cnt, &opts);401	if (verbose && ret < 0)402		fprintf(stderr, "%s\n", bpf_log_buf);403 404	return ret;405}406 407static int attach_sock_prog(int cgfd, int progfd,408			    enum bpf_attach_type attach_type)409{410	return bpf_prog_attach(progfd, cgfd, attach_type, BPF_F_ALLOW_OVERRIDE);411}412 413static int bind_sock(int domain, int type, const char *ip,414		     unsigned short port, unsigned short port_retry)415{416	struct sockaddr_storage addr;417	struct sockaddr_in6 *addr6;418	struct sockaddr_in *addr4;419	int sockfd = -1;420	socklen_t len;421	int res = SUCCESS;422 423	sockfd = socket(domain, type, 0);424	if (sockfd < 0)425		goto err;426 427	memset(&addr, 0, sizeof(addr));428 429	if (domain == AF_INET) {430		len = sizeof(struct sockaddr_in);431		addr4 = (struct sockaddr_in *)&addr;432		addr4->sin_family = domain;433		addr4->sin_port = htons(port);434		if (inet_pton(domain, ip, (void *)&addr4->sin_addr) != 1)435			goto err;436	} else if (domain == AF_INET6) {437		len = sizeof(struct sockaddr_in6);438		addr6 = (struct sockaddr_in6 *)&addr;439		addr6->sin6_family = domain;440		addr6->sin6_port = htons(port);441		if (inet_pton(domain, ip, (void *)&addr6->sin6_addr) != 1)442			goto err;443	} else {444		goto err;445	}446 447	if (bind(sockfd, (const struct sockaddr *)&addr, len) == -1) {448		/* sys_bind() may fail for different reasons, errno has to be449		 * checked to confirm that BPF program rejected it.450		 */451		if (errno != EPERM)452			goto err;453		if (port_retry)454			goto retry;455		res = BIND_REJECT;456		goto out;457	}458 459	goto out;460retry:461	if (domain == AF_INET)462		addr4->sin_port = htons(port_retry);463	else464		addr6->sin6_port = htons(port_retry);465	if (bind(sockfd, (const struct sockaddr *)&addr, len) == -1) {466		if (errno != EPERM)467			goto err;468		res = RETRY_REJECT;469	} else {470		res = RETRY_SUCCESS;471	}472	goto out;473err:474	res = -1;475out:476	close(sockfd);477	return res;478}479 480static int run_test_case(int cgfd, const struct sock_test *test)481{482	int progfd = -1;483	int err = 0;484	int res;485 486	printf("Test case: %s .. ", test->descr);487	progfd = load_sock_prog(test->insns, test->expected_attach_type);488	if (progfd < 0) {489		if (test->result == LOAD_REJECT)490			goto out;491		else492			goto err;493	}494 495	if (attach_sock_prog(cgfd, progfd, test->attach_type) < 0) {496		if (test->result == ATTACH_REJECT)497			goto out;498		else499			goto err;500	}501 502	res = bind_sock(test->domain, test->type, test->ip, test->port,503			test->port_retry);504	if (res > 0 && test->result == res)505		goto out;506 507err:508	err = -1;509out:510	/* Detaching w/o checking return code: best effort attempt. */511	if (progfd != -1)512		bpf_prog_detach(cgfd, test->attach_type);513	close(progfd);514	printf("[%s]\n", err ? "FAIL" : "PASS");515	return err;516}517 518static int run_tests(int cgfd)519{520	int passes = 0;521	int fails = 0;522	int i;523 524	for (i = 0; i < ARRAY_SIZE(tests); ++i) {525		if (run_test_case(cgfd, &tests[i]))526			++fails;527		else528			++passes;529	}530	printf("Summary: %d PASSED, %d FAILED\n", passes, fails);531	return fails ? -1 : 0;532}533 534int main(int argc, char **argv)535{536	int cgfd = -1;537	int err = 0;538 539	cgfd = cgroup_setup_and_join(CG_PATH);540	if (cgfd < 0)541		goto err;542 543	/* Use libbpf 1.0 API mode */544	libbpf_set_strict_mode(LIBBPF_STRICT_ALL);545 546	if (run_tests(cgfd))547		goto err;548 549	goto out;550err:551	err = -1;552out:553	close(cgfd);554	cleanup_cgroup_environment();555	return err;556}557