blob: 187b3f7843079c8fdec7dda1f3d74e1d1cdef239 [file]
// SPDX-License-Identifier: GPL-2.0-only
#include <sys/socket.h>
#include <unistd.h>
#include <bpf/btf.h>
#include <test_progs.h>
#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();
}