// SPDX-License-Identifier: GPL-2.0-only #include #include #include #include #include "bpftool_helpers.h" #define PIN_PATH "/sys/fs/bpf/test_bpftool_map_dump_sk_storage" #define QUEUE_PIN_PATH "/sys/fs/bpf/test_bpftool_map_dump_queue" #define JSON_ERROR "{\"error\":\"can't dump sk_storage map" /* An empty socket storage map with an int value. */ static int create_sk_storage_map(void) { LIBBPF_OPTS(bpf_map_create_opts, opts, .map_flags = BPF_F_NO_PREALLOC); struct btf *btf; int int_id, fd = -1; btf = btf__new_empty(); if (!ASSERT_OK_PTR(btf, "btf__new_empty")) return -1; int_id = btf__add_int(btf, "int", 4, BTF_INT_SIGNED); if (!ASSERT_GT(int_id, 0, "btf__add_int")) goto out; if (!ASSERT_OK(btf__load_into_kernel(btf), "btf__load_into_kernel")) goto out; opts.btf_fd = btf__fd(btf); opts.btf_key_type_id = int_id; opts.btf_value_type_id = int_id; fd = bpf_map_create(BPF_MAP_TYPE_SK_STORAGE, "sk_storage", sizeof(int), sizeof(int), 0, &opts); ASSERT_OK_FD(fd, "bpf_map_create"); out: btf__free(btf); return fd; } static void test_sk_storage(void) { int map_fd, sk_fd = -1, value = 42, ret; char output[1024] = {}; map_fd = create_sk_storage_map(); if (map_fd < 0) return; sk_fd = socket(AF_INET, SOCK_STREAM, 0); if (!ASSERT_OK_FD(sk_fd, "socket")) goto out; if (!ASSERT_OK(bpf_map_update_elem(map_fd, &sk_fd, &value, BPF_NOEXIST), "add socket storage")) goto out; if (!ASSERT_OK(bpf_obj_pin(map_fd, PIN_PATH), "pin map")) goto out; /* * Socket storage can't be iterated, so the dump must fail with an * error and nothing else, rather than print an empty map that holds * an element. The buffer is cleared before each run so that a run * that fails to start can't match stale or uninitialised output. */ ret = get_bpftool_command_output("-j map dump pinned " PIN_PATH, output, sizeof(output)); ASSERT_NEQ(ret, 0, "json dump fails"); ASSERT_STRNEQ(output, JSON_ERROR, strlen(JSON_ERROR), "json dump is only the error"); memset(output, 0, sizeof(output)); ret = get_bpftool_command_output("map dump pinned " PIN_PATH, output, sizeof(output)); ASSERT_NEQ(ret, 0, "plain dump fails"); ASSERT_NULL(strchr(output, '['), "plain dump prints no elements"); memset(output, 0, sizeof(output)); get_bpftool_command_output("map dump pinned " PIN_PATH " 2>&1 >/dev/null", output, sizeof(output)); ASSERT_HAS_SUBSTR(output, "can't dump sk_storage map: Operation not supported", "plain dump explains why on stderr"); out: unlink(PIN_PATH); if (sk_fd >= 0) close(sk_fd); close(map_fd); } /* * A queue map has no BTF, so its plain dump prints an element count * rather than JSON. A dump that ignored the error would print "Found 0 * elements" for a queue that holds one. */ static void test_queue(void) { char output[1024] = {}; int map_fd, value = 42, ret; map_fd = bpf_map_create(BPF_MAP_TYPE_QUEUE, "queue", 0, sizeof(int), 1, NULL); if (!ASSERT_OK_FD(map_fd, "bpf_map_create")) return; if (!ASSERT_OK(bpf_map_update_elem(map_fd, NULL, &value, BPF_ANY), "push to queue")) goto out; if (!ASSERT_OK(bpf_obj_pin(map_fd, QUEUE_PIN_PATH), "pin map")) goto out; ret = get_bpftool_command_output("map dump pinned " QUEUE_PIN_PATH, output, sizeof(output)); ASSERT_NEQ(ret, 0, "plain dump fails"); ASSERT_NULL(strstr(output, "Found"), "plain dump prints no count"); memset(output, 0, sizeof(output)); get_bpftool_command_output("map dump pinned " QUEUE_PIN_PATH " 2>&1 >/dev/null", output, sizeof(output)); ASSERT_HAS_SUBSTR(output, "can't dump queue map", "plain dump explains why on stderr"); out: unlink(QUEUE_PIN_PATH); close(map_fd); } void test_bpftool_map_dump(void) { if (test__start_subtest("sk_storage")) test_sk_storage(); if (test__start_subtest("queue")) test_queue(); }