// SPDX-License-Identifier: GPL-2.0 #include "test_progs.h" #include "network_helpers.h" #include "cgroup_helpers.h" #include "cgroup_ancestor.skel.h" #define CGROUP_PATH "/skb_cgroup_test" #define TEST_NS "cgroup_ancestor_ns" #define NUM_CGROUP_LEVELS 4 #define WAIT_AUTO_IP_MAX_ATTEMPT 10 #define DST_ADDR "::1" #define DST_PORT 1234 #define MAX_ASSERT_NAME 32 struct test_data { struct cgroup_ancestor *skel; struct bpf_tc_hook qdisc; struct bpf_tc_opts tc_attach; struct nstoken *ns; }; static int send_datagram(void) { unsigned char buf[] = "some random test data"; struct sockaddr_in6 addr = { .sin6_family = AF_INET6, .sin6_port = htons(DST_PORT), }; int sock, n; if (!ASSERT_EQ(inet_pton(AF_INET6, DST_ADDR, &addr.sin6_addr), 1, "inet_pton")) return -1; sock = socket(AF_INET6, SOCK_DGRAM, 0); if (!ASSERT_OK_FD(sock, "create socket")) return sock; if (!ASSERT_OK(connect(sock, &addr, sizeof(addr)), "connect")) { close(sock); return -1; } n = sendto(sock, buf, sizeof(buf), 0, (const struct sockaddr *)&addr, sizeof(addr)); close(sock); return ASSERT_EQ(n, sizeof(buf), "send data") ? 0 : -1; } static int setup_network(struct test_data *t) { SYS(fail, "ip netns add %s", TEST_NS); t->ns = open_netns(TEST_NS); if (!ASSERT_OK_PTR(t->ns, "open netns")) goto cleanup_ns; SYS(close_ns, "ip link set lo up"); memset(&t->qdisc, 0, sizeof(t->qdisc)); t->qdisc.sz = sizeof(t->qdisc); t->qdisc.attach_point = BPF_TC_EGRESS; t->qdisc.ifindex = if_nametoindex("lo"); if (!ASSERT_NEQ(t->qdisc.ifindex, 0, "if_nametoindex")) goto close_ns; if (!ASSERT_OK(bpf_tc_hook_create(&t->qdisc), "qdisc add")) goto close_ns; memset(&t->tc_attach, 0, sizeof(t->tc_attach)); t->tc_attach.sz = sizeof(t->tc_attach); t->tc_attach.prog_fd = bpf_program__fd(t->skel->progs.log_cgroup_id); if (!ASSERT_OK(bpf_tc_attach(&t->qdisc, &t->tc_attach), "filter add")) goto cleanup_qdisc; return 0; cleanup_qdisc: bpf_tc_hook_destroy(&t->qdisc); close_ns: close_netns(t->ns); cleanup_ns: SYS_NOFAIL("ip netns del %s", TEST_NS); fail: return 1; } static void cleanup_network(struct test_data *t) { bpf_tc_detach(&t->qdisc, &t->tc_attach); bpf_tc_hook_destroy(&t->qdisc); close_netns(t->ns); SYS_NOFAIL("ip netns del %s", TEST_NS); } static void check_ancestors_ids(struct test_data *t) { __u64 expected_ids[NUM_CGROUP_LEVELS]; char assert_name[MAX_ASSERT_NAME]; __u32 level; expected_ids[0] = get_cgroup_id("/.."); /* root cgroup */ expected_ids[1] = get_cgroup_id(""); expected_ids[2] = get_cgroup_id(CGROUP_PATH); expected_ids[3] = 0; /* non-existent cgroup */ for (level = 0; level < NUM_CGROUP_LEVELS; level++) { snprintf(assert_name, MAX_ASSERT_NAME, "ancestor id at level %d", level); ASSERT_EQ(t->skel->bss->cgroup_ids[level], expected_ids[level], assert_name); } } void test_cgroup_ancestor(void) { struct test_data t; int cgroup_fd; t.skel = cgroup_ancestor__open_and_load(); if (!ASSERT_OK_PTR(t.skel, "open and load")) return; t.skel->bss->dport = htons(DST_PORT); cgroup_fd = cgroup_setup_and_join(CGROUP_PATH); if (cgroup_fd < 0) goto cleanup_progs; if (setup_network(&t)) goto cleanup_cgroups; if (send_datagram()) goto cleanup_network; check_ancestors_ids(&t); cleanup_network: cleanup_network(&t); cleanup_cgroups: close(cgroup_fd); cleanup_cgroup_environment(); cleanup_progs: cgroup_ancestor__destroy(t.skel); }