|  | 
|  | 1 | +// SPDX-License-Identifier: GPL-2.0 | 
|  | 2 | + | 
|  | 3 | +#include "test_progs.h" | 
|  | 4 | +#include "network_helpers.h" | 
|  | 5 | +#include "cgroup_helpers.h" | 
|  | 6 | +#include "cgroup_ancestor.skel.h" | 
|  | 7 | + | 
|  | 8 | +#define CGROUP_PATH "/skb_cgroup_test" | 
|  | 9 | +#define TEST_NS "cgroup_ancestor_ns" | 
|  | 10 | +#define NUM_CGROUP_LEVELS 4 | 
|  | 11 | +#define WAIT_AUTO_IP_MAX_ATTEMPT 10 | 
|  | 12 | +#define DST_ADDR "::1" | 
|  | 13 | +#define DST_PORT 1234 | 
|  | 14 | +#define MAX_ASSERT_NAME 32 | 
|  | 15 | + | 
|  | 16 | +struct test_data { | 
|  | 17 | +	struct cgroup_ancestor *skel; | 
|  | 18 | +	struct bpf_tc_hook qdisc; | 
|  | 19 | +	struct bpf_tc_opts tc_attach; | 
|  | 20 | +	struct nstoken *ns; | 
|  | 21 | +}; | 
|  | 22 | + | 
|  | 23 | +static int send_datagram(void) | 
|  | 24 | +{ | 
|  | 25 | +	unsigned char buf[] = "some random test data"; | 
|  | 26 | +	struct sockaddr_in6 addr = { .sin6_family = AF_INET6, | 
|  | 27 | +				     .sin6_port = htons(DST_PORT), }; | 
|  | 28 | +	int sock, n; | 
|  | 29 | + | 
|  | 30 | +	if (!ASSERT_EQ(inet_pton(AF_INET6, DST_ADDR, &addr.sin6_addr), 1, | 
|  | 31 | +		       "inet_pton")) | 
|  | 32 | +		return -1; | 
|  | 33 | + | 
|  | 34 | +	sock = socket(AF_INET6, SOCK_DGRAM, 0); | 
|  | 35 | +	if (!ASSERT_OK_FD(sock, "create socket")) | 
|  | 36 | +		return sock; | 
|  | 37 | + | 
|  | 38 | +	if (!ASSERT_OK(connect(sock, &addr, sizeof(addr)), "connect")) { | 
|  | 39 | +		close(sock); | 
|  | 40 | +		return -1; | 
|  | 41 | +	} | 
|  | 42 | + | 
|  | 43 | +	n = sendto(sock, buf, sizeof(buf), 0, (const struct sockaddr *)&addr, | 
|  | 44 | +		   sizeof(addr)); | 
|  | 45 | +	close(sock); | 
|  | 46 | +	return ASSERT_EQ(n, sizeof(buf), "send data") ? 0 : -1; | 
|  | 47 | +} | 
|  | 48 | + | 
|  | 49 | +static int setup_network(struct test_data *t) | 
|  | 50 | +{ | 
|  | 51 | +	SYS(fail, "ip netns add %s", TEST_NS); | 
|  | 52 | +	t->ns = open_netns(TEST_NS); | 
|  | 53 | +	if (!ASSERT_OK_PTR(t->ns, "open netns")) | 
|  | 54 | +		goto cleanup_ns; | 
|  | 55 | + | 
|  | 56 | +	SYS(close_ns, "ip link set lo up"); | 
|  | 57 | + | 
|  | 58 | +	memset(&t->qdisc, 0, sizeof(t->qdisc)); | 
|  | 59 | +	t->qdisc.sz = sizeof(t->qdisc); | 
|  | 60 | +	t->qdisc.attach_point = BPF_TC_EGRESS; | 
|  | 61 | +	t->qdisc.ifindex = if_nametoindex("lo"); | 
|  | 62 | +	if (!ASSERT_NEQ(t->qdisc.ifindex, 0, "if_nametoindex")) | 
|  | 63 | +		goto close_ns; | 
|  | 64 | +	if (!ASSERT_OK(bpf_tc_hook_create(&t->qdisc), "qdisc add")) | 
|  | 65 | +		goto close_ns; | 
|  | 66 | + | 
|  | 67 | +	memset(&t->tc_attach, 0, sizeof(t->tc_attach)); | 
|  | 68 | +	t->tc_attach.sz = sizeof(t->tc_attach); | 
|  | 69 | +	t->tc_attach.prog_fd = bpf_program__fd(t->skel->progs.log_cgroup_id); | 
|  | 70 | +	if (!ASSERT_OK(bpf_tc_attach(&t->qdisc, &t->tc_attach), "filter add")) | 
|  | 71 | +		goto cleanup_qdisc; | 
|  | 72 | + | 
|  | 73 | +	return 0; | 
|  | 74 | + | 
|  | 75 | +cleanup_qdisc: | 
|  | 76 | +	bpf_tc_hook_destroy(&t->qdisc); | 
|  | 77 | +close_ns: | 
|  | 78 | +	close_netns(t->ns); | 
|  | 79 | +cleanup_ns: | 
|  | 80 | +	SYS_NOFAIL("ip netns del %s", TEST_NS); | 
|  | 81 | +fail: | 
|  | 82 | +	return 1; | 
|  | 83 | +} | 
|  | 84 | + | 
|  | 85 | +static void cleanup_network(struct test_data *t) | 
|  | 86 | +{ | 
|  | 87 | +	bpf_tc_detach(&t->qdisc, &t->tc_attach); | 
|  | 88 | +	bpf_tc_hook_destroy(&t->qdisc); | 
|  | 89 | +	close_netns(t->ns); | 
|  | 90 | +	SYS_NOFAIL("ip netns del %s", TEST_NS); | 
|  | 91 | +} | 
|  | 92 | + | 
|  | 93 | +static void check_ancestors_ids(struct test_data *t) | 
|  | 94 | +{ | 
|  | 95 | +	__u64 expected_ids[NUM_CGROUP_LEVELS]; | 
|  | 96 | +	char assert_name[MAX_ASSERT_NAME]; | 
|  | 97 | +	__u32 level; | 
|  | 98 | + | 
|  | 99 | +	expected_ids[0] = get_cgroup_id("/.."); /* root cgroup */ | 
|  | 100 | +	expected_ids[1] = get_cgroup_id(""); | 
|  | 101 | +	expected_ids[2] = get_cgroup_id(CGROUP_PATH); | 
|  | 102 | +	expected_ids[3] = 0; /* non-existent cgroup */ | 
|  | 103 | + | 
|  | 104 | +	for (level = 0; level < NUM_CGROUP_LEVELS; level++) { | 
|  | 105 | +		snprintf(assert_name, MAX_ASSERT_NAME, | 
|  | 106 | +			 "ancestor id at level %d", level); | 
|  | 107 | +		ASSERT_EQ(t->skel->bss->cgroup_ids[level], expected_ids[level], | 
|  | 108 | +			  assert_name); | 
|  | 109 | +	} | 
|  | 110 | +} | 
|  | 111 | + | 
|  | 112 | +void test_cgroup_ancestor(void) | 
|  | 113 | +{ | 
|  | 114 | +	struct test_data t; | 
|  | 115 | +	int cgroup_fd; | 
|  | 116 | + | 
|  | 117 | +	t.skel = cgroup_ancestor__open_and_load(); | 
|  | 118 | +	if (!ASSERT_OK_PTR(t.skel, "open and load")) | 
|  | 119 | +		return; | 
|  | 120 | + | 
|  | 121 | +	t.skel->bss->dport = htons(DST_PORT); | 
|  | 122 | +	cgroup_fd = cgroup_setup_and_join(CGROUP_PATH); | 
|  | 123 | +	if (cgroup_fd < 0) | 
|  | 124 | +		goto cleanup_progs; | 
|  | 125 | + | 
|  | 126 | +	if (setup_network(&t)) | 
|  | 127 | +		goto cleanup_cgroups; | 
|  | 128 | + | 
|  | 129 | +	if (send_datagram()) | 
|  | 130 | +		goto cleanup_network; | 
|  | 131 | + | 
|  | 132 | +	check_ancestors_ids(&t); | 
|  | 133 | + | 
|  | 134 | +cleanup_network: | 
|  | 135 | +	cleanup_network(&t); | 
|  | 136 | +cleanup_cgroups: | 
|  | 137 | +	close(cgroup_fd); | 
|  | 138 | +	cleanup_cgroup_environment(); | 
|  | 139 | +cleanup_progs: | 
|  | 140 | +	cgroup_ancestor__destroy(t.skel); | 
|  | 141 | +} | 
0 commit comments