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
send_datagram(void)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, (struct sockaddr *)&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
setup_network(struct test_data * t)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
cleanup_network(struct test_data * t)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
check_ancestors_ids(struct test_data * t)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
test_cgroup_ancestor(void)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