// SPDX-License-Identifier: GPL-2.0-only #include #include #include #include #include #include #include #include #define BPFFS_DIR "/sys/fs/bpf/test_metadata" #define BPFFS_USED BPFFS_DIR "/used" #define BPFFS_UNUSED BPFFS_DIR "/unused" #define BPF_FILE_USED "metadata_used.bpf.o" #define BPF_FILE_UNUSED "metadata_unused.bpf.o" #define METADATA_MAP_NAME "metadata.rodata" #define MAX_BPFTOOL_OUTPUT_LEN (64*1024) #define MAX_TOKENS_TO_CHECK 3 static char output[MAX_BPFTOOL_OUTPUT_LEN]; struct test_desc { char *name; char *bpf_prog; char *bpffs_path; char *expected_output[MAX_TOKENS_TO_CHECK]; char *expected_output_json[MAX_TOKENS_TO_CHECK]; char *metadata_map_name; }; static int setup(struct test_desc *test) { return mkdir(BPFFS_DIR, 0700); } static void cleanup(struct test_desc *test) { unlink(test->bpffs_path); rmdir(BPFFS_DIR); } static int check_metadata(char *buf, char * const *tokens, int count) { int i; for (i = 0; i < count && tokens[i]; i++) if (!strstr(buf, tokens[i])) return 1; return 0; } static void run_test(struct test_desc *test) { int ret; char cmd[MAX_BPFTOOL_CMD_LEN]; ret = snprintf(cmd, MAX_BPFTOOL_CMD_LEN, "prog load %s %s", test->bpf_prog, test->bpffs_path); if (!ASSERT_GT(ret, 0, "format prog insert command")) return; ret = run_bpftool_command(cmd); if (!ASSERT_OK(ret, "load program")) return; /* Check output with default format */ ret = snprintf(cmd, MAX_BPFTOOL_CMD_LEN, "prog show pinned %s", test->bpffs_path); if (!ASSERT_GT(ret, 0, "format pinned prog check command")) return; ret = get_bpftool_command_output(cmd, output, MAX_BPFTOOL_OUTPUT_LEN); if (ASSERT_OK(ret, "get program info")) { ret = check_metadata(output, test->expected_output, ARRAY_SIZE(test->expected_output)); ASSERT_OK(ret, "find metadata"); } /* Check output with json format */ ret = snprintf(cmd, MAX_BPFTOOL_CMD_LEN, "prog -j show pinned %s", test->bpffs_path); if (!ASSERT_GT(ret, 0, "format pinned prog check command in json")) return; ret = get_bpftool_command_output(cmd, output, MAX_BPFTOOL_OUTPUT_LEN); if (ASSERT_OK(ret, "get program info in json")) { ret = check_metadata(output, test->expected_output_json, ARRAY_SIZE(test->expected_output_json)); ASSERT_OK(ret, "find metadata in json"); } /* Check that the corresponding map can be found and accessed */ ret = snprintf(cmd, MAX_BPFTOOL_CMD_LEN, "map show name %s", test->metadata_map_name); if (!ASSERT_GT(ret, 0, "format map check command")) return; ASSERT_OK(run_bpftool_command(cmd), "access metadata map"); } static struct test_desc tests[] = { { .name = "metadata_unused", .bpf_prog = BPF_FILE_UNUSED, .bpffs_path = BPFFS_UNUSED, .expected_output = { "a = \"foo\"", "b = 1" }, .expected_output_json = { "\"metadata\":{\"a\":\"foo\",\"b\":1}" }, .metadata_map_name = METADATA_MAP_NAME }, { .name = "metadata_used", .bpf_prog = BPF_FILE_USED, .bpffs_path = BPFFS_USED, .expected_output = { "a = \"bar\"", "b = 2" }, .expected_output_json = { "\"metadata\":{\"a\":\"bar\",\"b\":2}" }, .metadata_map_name = METADATA_MAP_NAME } }; static const int tests_count = ARRAY_SIZE(tests); void test_bpftool_metadata(void) { int i; for (i = 0; i < tests_count; i++) { if (!test__start_subtest(tests[i].name)) continue; if (ASSERT_OK(setup(&tests[i]), "setup bpffs pin dir")) { run_test(&tests[i]); cleanup(&tests[i]); } } }