xref: /linux/tools/testing/selftests/bpf/prog_tests/cgroup_ancestor.c (revision 06a130e42a5bfc84795464bff023bff4c16f58c5)
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 }
142