1 // SPDX-License-Identifier: GPL-2.0-only 2 #include <sys/socket.h> 3 #include <unistd.h> 4 #include <bpf/btf.h> 5 #include <test_progs.h> 6 #include "bpftool_helpers.h" 7 8 #define PIN_PATH "/sys/fs/bpf/test_bpftool_map_dump_sk_storage" 9 #define QUEUE_PIN_PATH "/sys/fs/bpf/test_bpftool_map_dump_queue" 10 #define JSON_ERROR "{\"error\":\"can't dump sk_storage map" 11 12 /* An empty socket storage map with an int value. */ 13 static int create_sk_storage_map(void) 14 { 15 LIBBPF_OPTS(bpf_map_create_opts, opts, 16 .map_flags = BPF_F_NO_PREALLOC); 17 struct btf *btf; 18 int int_id, fd = -1; 19 20 btf = btf__new_empty(); 21 if (!ASSERT_OK_PTR(btf, "btf__new_empty")) 22 return -1; 23 24 int_id = btf__add_int(btf, "int", 4, BTF_INT_SIGNED); 25 if (!ASSERT_GT(int_id, 0, "btf__add_int")) 26 goto out; 27 if (!ASSERT_OK(btf__load_into_kernel(btf), "btf__load_into_kernel")) 28 goto out; 29 30 opts.btf_fd = btf__fd(btf); 31 opts.btf_key_type_id = int_id; 32 opts.btf_value_type_id = int_id; 33 fd = bpf_map_create(BPF_MAP_TYPE_SK_STORAGE, "sk_storage", sizeof(int), 34 sizeof(int), 0, &opts); 35 ASSERT_OK_FD(fd, "bpf_map_create"); 36 out: 37 btf__free(btf); 38 return fd; 39 } 40 41 static void test_sk_storage(void) 42 { 43 int map_fd, sk_fd = -1, value = 42, ret; 44 char output[1024] = {}; 45 46 map_fd = create_sk_storage_map(); 47 if (map_fd < 0) 48 return; 49 50 sk_fd = socket(AF_INET, SOCK_STREAM, 0); 51 if (!ASSERT_OK_FD(sk_fd, "socket")) 52 goto out; 53 if (!ASSERT_OK(bpf_map_update_elem(map_fd, &sk_fd, &value, BPF_NOEXIST), 54 "add socket storage")) 55 goto out; 56 if (!ASSERT_OK(bpf_obj_pin(map_fd, PIN_PATH), "pin map")) 57 goto out; 58 59 /* 60 * Socket storage can't be iterated, so the dump must fail with an 61 * error and nothing else, rather than print an empty map that holds 62 * an element. The buffer is cleared before each run so that a run 63 * that fails to start can't match stale or uninitialised output. 64 */ 65 ret = get_bpftool_command_output("-j map dump pinned " PIN_PATH, 66 output, sizeof(output)); 67 ASSERT_NEQ(ret, 0, "json dump fails"); 68 ASSERT_STRNEQ(output, JSON_ERROR, strlen(JSON_ERROR), 69 "json dump is only the error"); 70 71 memset(output, 0, sizeof(output)); 72 ret = get_bpftool_command_output("map dump pinned " PIN_PATH, 73 output, sizeof(output)); 74 ASSERT_NEQ(ret, 0, "plain dump fails"); 75 ASSERT_NULL(strchr(output, '['), "plain dump prints no elements"); 76 77 memset(output, 0, sizeof(output)); 78 get_bpftool_command_output("map dump pinned " PIN_PATH " 2>&1 >/dev/null", 79 output, sizeof(output)); 80 ASSERT_HAS_SUBSTR(output, 81 "can't dump sk_storage map: Operation not supported", 82 "plain dump explains why on stderr"); 83 84 out: 85 unlink(PIN_PATH); 86 if (sk_fd >= 0) 87 close(sk_fd); 88 close(map_fd); 89 } 90 91 /* 92 * A queue map has no BTF, so its plain dump prints an element count 93 * rather than JSON. A dump that ignored the error would print "Found 0 94 * elements" for a queue that holds one. 95 */ 96 static void test_queue(void) 97 { 98 char output[1024] = {}; 99 int map_fd, value = 42, ret; 100 101 map_fd = bpf_map_create(BPF_MAP_TYPE_QUEUE, "queue", 0, sizeof(int), 1, 102 NULL); 103 if (!ASSERT_OK_FD(map_fd, "bpf_map_create")) 104 return; 105 if (!ASSERT_OK(bpf_map_update_elem(map_fd, NULL, &value, BPF_ANY), 106 "push to queue")) 107 goto out; 108 if (!ASSERT_OK(bpf_obj_pin(map_fd, QUEUE_PIN_PATH), "pin map")) 109 goto out; 110 111 ret = get_bpftool_command_output("map dump pinned " QUEUE_PIN_PATH, 112 output, sizeof(output)); 113 ASSERT_NEQ(ret, 0, "plain dump fails"); 114 ASSERT_NULL(strstr(output, "Found"), "plain dump prints no count"); 115 116 memset(output, 0, sizeof(output)); 117 get_bpftool_command_output("map dump pinned " QUEUE_PIN_PATH " 2>&1 >/dev/null", 118 output, sizeof(output)); 119 ASSERT_HAS_SUBSTR(output, "can't dump queue map", 120 "plain dump explains why on stderr"); 121 122 out: 123 unlink(QUEUE_PIN_PATH); 124 close(map_fd); 125 } 126 127 void test_bpftool_map_dump(void) 128 { 129 if (test__start_subtest("sk_storage")) 130 test_sk_storage(); 131 if (test__start_subtest("queue")) 132 test_queue(); 133 } 134