Commit b97ce547 authored by Martin KaFai Lau's avatar Martin KaFai Lau
Browse files

Merge branch 'selftests/bpf: convert three other cgroup tests to test_progs'

Alexis Lothoré (eBPF Foundation) says:

====================
Hello,
this series brings a new set of test converted to the test_progs framework.
Since the tests are quite small, I chose to group three tests conversion in
the same series, but feel free to let me know if I should keep one series
per test. The series focuses on cgroup testing and converts the following
tests:
- get_cgroup_id_user
- cgroup_storage
- test_skb_cgroup_id_user

Changes in v4:
- Fix test after netns addition by making sure loopack interface is up
- Link to v3: https://lore.kernel.org/r/20240812-convert_cgroup_tests-v3-0-47ac6ce4e88b@bootlin.com

Changes in v3:
- Fixed multiple leaks on cgroup file descriptors and sockets
- Used dedicated network namespaces for tests involving network
- Link to v2: https://lore.kernel.org/r/20240806-convert_cgroup_tests-v2-0-180c57e5b710@bootlin.com

Changes in v2:
- Use global variables instead of maps when possible
- Collect review tags from Alan
- Link to v1: https://lore.kernel.org/r/20240731-convert_cgroup_tests-v1-0-14cbc51b6947@bootlin.com


====================

Signed-off-by: default avatarMartin KaFai Lau <martin.lau@kernel.org>
parents 4a4c013d f957c230
Loading
Loading
Loading
Loading
+0 −3
Original line number Diff line number Diff line
@@ -19,9 +19,6 @@ test_sock
urandom_read
test_sockmap
test_lirc_mode2_user
get_cgroup_id_user
test_skb_cgroup_id_user
test_cgroup_storage
test_flow_dissector
flow_dissector_load
test_tcpnotify_user
+2 −6
Original line number Diff line number Diff line
@@ -67,8 +67,7 @@ endif

# Order correspond to 'make run_tests' order
TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs \
	test_sock test_sockmap get_cgroup_id_user \
	test_cgroup_storage \
	test_sock test_sockmap \
	test_tcpnotify_user test_sysctl \
	test_progs-no_alu32
TEST_INST_SUBDIRS := no_alu32
@@ -138,7 +137,7 @@ TEST_PROGS_EXTENDED := with_addr.sh \
	test_xdp_vlan.sh test_bpftool.py

# Compile but not part of 'make run_tests'
TEST_GEN_PROGS_EXTENDED = test_skb_cgroup_id_user \
TEST_GEN_PROGS_EXTENDED = \
	flow_dissector_load test_flow_dissector test_tcp_check_syncookie_user \
	test_lirc_mode2_user xdping test_cpp runqslower bench bpf_testmod.ko \
	xskxceiver xdp_redirect_multi xdp_synproxy veristat xdp_hw_metadata \
@@ -291,12 +290,9 @@ JSON_WRITER := $(OUTPUT)/json_writer.o
CAP_HELPERS	:= $(OUTPUT)/cap_helpers.o
NETWORK_HELPERS := $(OUTPUT)/network_helpers.o

$(OUTPUT)/test_skb_cgroup_id_user: $(CGROUP_HELPERS) $(TESTING_HELPERS)
$(OUTPUT)/test_sock: $(CGROUP_HELPERS) $(TESTING_HELPERS)
$(OUTPUT)/test_sockmap: $(CGROUP_HELPERS) $(TESTING_HELPERS)
$(OUTPUT)/test_tcpnotify_user: $(CGROUP_HELPERS) $(TESTING_HELPERS) $(TRACE_HELPERS)
$(OUTPUT)/get_cgroup_id_user: $(CGROUP_HELPERS) $(TESTING_HELPERS)
$(OUTPUT)/test_cgroup_storage: $(CGROUP_HELPERS) $(TESTING_HELPERS)
$(OUTPUT)/test_sock_fields: $(CGROUP_HELPERS) $(TESTING_HELPERS)
$(OUTPUT)/test_sysctl: $(CGROUP_HELPERS) $(TESTING_HELPERS)
$(OUTPUT)/test_tag: $(TESTING_HELPERS)
+0 −151
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2018 Facebook

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <syscall.h>
#include <unistd.h>
#include <linux/perf_event.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <linux/bpf.h>
#include <bpf/bpf.h>
#include <bpf/libbpf.h>

#include "cgroup_helpers.h"
#include "testing_helpers.h"

#define CHECK(condition, tag, format...) ({		\
	int __ret = !!(condition);			\
	if (__ret) {					\
		printf("%s:FAIL:%s ", __func__, tag);	\
		printf(format);				\
	} else {					\
		printf("%s:PASS:%s\n", __func__, tag);	\
	}						\
	__ret;						\
})

static int bpf_find_map(const char *test, struct bpf_object *obj,
			const char *name)
{
	struct bpf_map *map;

	map = bpf_object__find_map_by_name(obj, name);
	if (!map)
		return -1;
	return bpf_map__fd(map);
}

#define TEST_CGROUP "/test-bpf-get-cgroup-id/"

int main(int argc, char **argv)
{
	const char *probe_name = "syscalls/sys_enter_nanosleep";
	const char *file = "get_cgroup_id_kern.bpf.o";
	int err, bytes, efd, prog_fd, pmu_fd;
	int cgroup_fd, cgidmap_fd, pidmap_fd;
	struct perf_event_attr attr = {};
	struct bpf_object *obj;
	__u64 kcgid = 0, ucgid;
	__u32 key = 0, pid;
	int exit_code = 1;
	char buf[256];
	const struct timespec req = {
		.tv_sec = 1,
		.tv_nsec = 0,
	};

	cgroup_fd = cgroup_setup_and_join(TEST_CGROUP);
	if (CHECK(cgroup_fd < 0, "cgroup_setup_and_join", "err %d errno %d\n", cgroup_fd, errno))
		return 1;

	/* Use libbpf 1.0 API mode */
	libbpf_set_strict_mode(LIBBPF_STRICT_ALL);

	err = bpf_prog_test_load(file, BPF_PROG_TYPE_TRACEPOINT, &obj, &prog_fd);
	if (CHECK(err, "bpf_prog_test_load", "err %d errno %d\n", err, errno))
		goto cleanup_cgroup_env;

	cgidmap_fd = bpf_find_map(__func__, obj, "cg_ids");
	if (CHECK(cgidmap_fd < 0, "bpf_find_map", "err %d errno %d\n",
		  cgidmap_fd, errno))
		goto close_prog;

	pidmap_fd = bpf_find_map(__func__, obj, "pidmap");
	if (CHECK(pidmap_fd < 0, "bpf_find_map", "err %d errno %d\n",
		  pidmap_fd, errno))
		goto close_prog;

	pid = getpid();
	bpf_map_update_elem(pidmap_fd, &key, &pid, 0);

	if (access("/sys/kernel/tracing/trace", F_OK) == 0) {
		snprintf(buf, sizeof(buf),
			 "/sys/kernel/tracing/events/%s/id", probe_name);
	} else {
		snprintf(buf, sizeof(buf),
			 "/sys/kernel/debug/tracing/events/%s/id", probe_name);
	}
	efd = open(buf, O_RDONLY, 0);
	if (CHECK(efd < 0, "open", "err %d errno %d\n", efd, errno))
		goto close_prog;
	bytes = read(efd, buf, sizeof(buf));
	close(efd);
	if (CHECK(bytes <= 0 || bytes >= sizeof(buf), "read",
		  "bytes %d errno %d\n", bytes, errno))
		goto close_prog;

	attr.config = strtol(buf, NULL, 0);
	attr.type = PERF_TYPE_TRACEPOINT;
	attr.sample_type = PERF_SAMPLE_RAW;
	attr.sample_period = 1;
	attr.wakeup_events = 1;

	/* attach to this pid so the all bpf invocations will be in the
	 * cgroup associated with this pid.
	 */
	pmu_fd = syscall(__NR_perf_event_open, &attr, getpid(), -1, -1, 0);
	if (CHECK(pmu_fd < 0, "perf_event_open", "err %d errno %d\n", pmu_fd,
		  errno))
		goto close_prog;

	err = ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0);
	if (CHECK(err, "perf_event_ioc_enable", "err %d errno %d\n", err,
		  errno))
		goto close_pmu;

	err = ioctl(pmu_fd, PERF_EVENT_IOC_SET_BPF, prog_fd);
	if (CHECK(err, "perf_event_ioc_set_bpf", "err %d errno %d\n", err,
		  errno))
		goto close_pmu;

	/* trigger some syscalls */
	syscall(__NR_nanosleep, &req, NULL);

	err = bpf_map_lookup_elem(cgidmap_fd, &key, &kcgid);
	if (CHECK(err, "bpf_map_lookup_elem", "err %d errno %d\n", err, errno))
		goto close_pmu;

	ucgid = get_cgroup_id(TEST_CGROUP);
	if (CHECK(kcgid != ucgid, "compare_cgroup_id",
		  "kern cgid %llx user cgid %llx", kcgid, ucgid))
		goto close_pmu;

	exit_code = 0;
	printf("%s:PASS\n", argv[0]);

close_pmu:
	close(pmu_fd);
close_prog:
	bpf_object__close(obj);
cleanup_cgroup_env:
	cleanup_cgroup_environment();
	return exit_code;
}
+141 −0
Original line number Diff line number Diff line
// 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);
}
+46 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0

#include <sys/stat.h>
#include <sys/sysmacros.h>
#include "test_progs.h"
#include "cgroup_helpers.h"
#include "get_cgroup_id_kern.skel.h"

#define TEST_CGROUP "/test-bpf-get-cgroup-id/"

void test_cgroup_get_current_cgroup_id(void)
{
	struct get_cgroup_id_kern *skel;
	const struct timespec req = {
		.tv_sec = 0,
		.tv_nsec = 1,
	};
	int cgroup_fd;
	__u64 ucgid;

	cgroup_fd = cgroup_setup_and_join(TEST_CGROUP);
	if (!ASSERT_OK_FD(cgroup_fd, "cgroup switch"))
		return;

	skel = get_cgroup_id_kern__open_and_load();
	if (!ASSERT_OK_PTR(skel, "load program"))
		goto cleanup_cgroup;

	if (!ASSERT_OK(get_cgroup_id_kern__attach(skel), "attach bpf program"))
		goto cleanup_progs;

	skel->bss->expected_pid = getpid();
	/* trigger the syscall on which is attached the tested prog */
	if (!ASSERT_OK(syscall(__NR_nanosleep, &req, NULL), "nanosleep"))
		goto cleanup_progs;

	ucgid = get_cgroup_id(TEST_CGROUP);

	ASSERT_EQ(skel->bss->cg_id, ucgid, "compare cgroup ids");

cleanup_progs:
	get_cgroup_id_kern__destroy(skel);
cleanup_cgroup:
	close(cgroup_fd);
	cleanup_cgroup_environment();
}
Loading