1 | // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) |
2 | /* Copyright (C) 2017-2018 Netronome Systems, Inc. */ |
3 | |
4 | #include <errno.h> |
5 | #include <fcntl.h> |
6 | #include <linux/err.h> |
7 | #include <linux/kernel.h> |
8 | #include <net/if.h> |
9 | #include <stdbool.h> |
10 | #include <stdio.h> |
11 | #include <stdlib.h> |
12 | #include <string.h> |
13 | #include <unistd.h> |
14 | #include <sys/types.h> |
15 | #include <sys/stat.h> |
16 | |
17 | #include <bpf/bpf.h> |
18 | #include <bpf/btf.h> |
19 | #include <bpf/hashmap.h> |
20 | |
21 | #include "json_writer.h" |
22 | #include "main.h" |
23 | |
24 | static struct hashmap *map_table; |
25 | |
26 | static bool map_is_per_cpu(__u32 type) |
27 | { |
28 | return type == BPF_MAP_TYPE_PERCPU_HASH || |
29 | type == BPF_MAP_TYPE_PERCPU_ARRAY || |
30 | type == BPF_MAP_TYPE_LRU_PERCPU_HASH || |
31 | type == BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE; |
32 | } |
33 | |
34 | static bool map_is_map_of_maps(__u32 type) |
35 | { |
36 | return type == BPF_MAP_TYPE_ARRAY_OF_MAPS || |
37 | type == BPF_MAP_TYPE_HASH_OF_MAPS; |
38 | } |
39 | |
40 | static bool map_is_map_of_progs(__u32 type) |
41 | { |
42 | return type == BPF_MAP_TYPE_PROG_ARRAY; |
43 | } |
44 | |
45 | static int map_type_from_str(const char *type) |
46 | { |
47 | const char *map_type_str; |
48 | unsigned int i; |
49 | |
50 | for (i = 0; ; i++) { |
51 | map_type_str = libbpf_bpf_map_type_str(i); |
52 | if (!map_type_str) |
53 | break; |
54 | |
55 | /* Don't allow prefixing in case of possible future shadowing */ |
56 | if (!strcmp(map_type_str, type)) |
57 | return i; |
58 | } |
59 | return -1; |
60 | } |
61 | |
62 | static void *alloc_value(struct bpf_map_info *info) |
63 | { |
64 | if (map_is_per_cpu(type: info->type)) |
65 | return malloc(round_up(info->value_size, 8) * |
66 | get_possible_cpus()); |
67 | else |
68 | return malloc(info->value_size); |
69 | } |
70 | |
71 | static int do_dump_btf(const struct btf_dumper *d, |
72 | struct bpf_map_info *map_info, void *key, |
73 | void *value) |
74 | { |
75 | __u32 value_id; |
76 | int ret = 0; |
77 | |
78 | /* start of key-value pair */ |
79 | jsonw_start_object(self: d->jw); |
80 | |
81 | if (map_info->btf_key_type_id) { |
82 | jsonw_name(self: d->jw, name: "key" ); |
83 | |
84 | ret = btf_dumper_type(d, type_id: map_info->btf_key_type_id, data: key); |
85 | if (ret) |
86 | goto err_end_obj; |
87 | } |
88 | |
89 | value_id = map_info->btf_vmlinux_value_type_id ? |
90 | : map_info->btf_value_type_id; |
91 | |
92 | if (!map_is_per_cpu(type: map_info->type)) { |
93 | jsonw_name(self: d->jw, name: "value" ); |
94 | ret = btf_dumper_type(d, type_id: value_id, data: value); |
95 | } else { |
96 | unsigned int i, n, step; |
97 | |
98 | jsonw_name(self: d->jw, name: "values" ); |
99 | jsonw_start_array(self: d->jw); |
100 | n = get_possible_cpus(); |
101 | step = round_up(map_info->value_size, 8); |
102 | for (i = 0; i < n; i++) { |
103 | jsonw_start_object(self: d->jw); |
104 | jsonw_int_field(self: d->jw, prop: "cpu" , num: i); |
105 | jsonw_name(self: d->jw, name: "value" ); |
106 | ret = btf_dumper_type(d, type_id: value_id, data: value + i * step); |
107 | jsonw_end_object(self: d->jw); |
108 | if (ret) |
109 | break; |
110 | } |
111 | jsonw_end_array(self: d->jw); |
112 | } |
113 | |
114 | err_end_obj: |
115 | /* end of key-value pair */ |
116 | jsonw_end_object(self: d->jw); |
117 | |
118 | return ret; |
119 | } |
120 | |
121 | static json_writer_t *get_btf_writer(void) |
122 | { |
123 | json_writer_t *jw = jsonw_new(stdout); |
124 | |
125 | if (!jw) |
126 | return NULL; |
127 | jsonw_pretty(self: jw, on: true); |
128 | |
129 | return jw; |
130 | } |
131 | |
132 | static void print_entry_json(struct bpf_map_info *info, unsigned char *key, |
133 | unsigned char *value, struct btf *btf) |
134 | { |
135 | jsonw_start_object(self: json_wtr); |
136 | |
137 | if (!map_is_per_cpu(type: info->type)) { |
138 | jsonw_name(self: json_wtr, name: "key" ); |
139 | print_hex_data_json(data: key, len: info->key_size); |
140 | jsonw_name(self: json_wtr, name: "value" ); |
141 | print_hex_data_json(data: value, len: info->value_size); |
142 | if (map_is_map_of_maps(type: info->type)) |
143 | jsonw_uint_field(self: json_wtr, prop: "inner_map_id" , |
144 | num: *(unsigned int *)value); |
145 | if (btf) { |
146 | struct btf_dumper d = { |
147 | .btf = btf, |
148 | .jw = json_wtr, |
149 | .is_plain_text = false, |
150 | }; |
151 | |
152 | jsonw_name(self: json_wtr, name: "formatted" ); |
153 | do_dump_btf(d: &d, map_info: info, key, value); |
154 | } |
155 | } else { |
156 | unsigned int i, n, step; |
157 | |
158 | n = get_possible_cpus(); |
159 | step = round_up(info->value_size, 8); |
160 | |
161 | jsonw_name(self: json_wtr, name: "key" ); |
162 | print_hex_data_json(data: key, len: info->key_size); |
163 | |
164 | jsonw_name(self: json_wtr, name: "values" ); |
165 | jsonw_start_array(self: json_wtr); |
166 | for (i = 0; i < n; i++) { |
167 | jsonw_start_object(self: json_wtr); |
168 | |
169 | jsonw_int_field(self: json_wtr, prop: "cpu" , num: i); |
170 | |
171 | jsonw_name(self: json_wtr, name: "value" ); |
172 | print_hex_data_json(data: value + i * step, |
173 | len: info->value_size); |
174 | |
175 | jsonw_end_object(self: json_wtr); |
176 | } |
177 | jsonw_end_array(self: json_wtr); |
178 | if (btf) { |
179 | struct btf_dumper d = { |
180 | .btf = btf, |
181 | .jw = json_wtr, |
182 | .is_plain_text = false, |
183 | }; |
184 | |
185 | jsonw_name(self: json_wtr, name: "formatted" ); |
186 | do_dump_btf(d: &d, map_info: info, key, value); |
187 | } |
188 | } |
189 | |
190 | jsonw_end_object(self: json_wtr); |
191 | } |
192 | |
193 | static void |
194 | print_entry_error_msg(struct bpf_map_info *info, unsigned char *key, |
195 | const char *error_msg) |
196 | { |
197 | int msg_size = strlen(error_msg); |
198 | bool single_line, break_names; |
199 | |
200 | break_names = info->key_size > 16 || msg_size > 16; |
201 | single_line = info->key_size + msg_size <= 24 && !break_names; |
202 | |
203 | printf("key:%c" , break_names ? '\n' : ' '); |
204 | fprint_hex(stdout, key, info->key_size, " " ); |
205 | |
206 | printf(single_line ? " " : "\n" ); |
207 | |
208 | printf("value:%c%s" , break_names ? '\n' : ' ', error_msg); |
209 | |
210 | printf("\n" ); |
211 | } |
212 | |
213 | static void |
214 | print_entry_error(struct bpf_map_info *map_info, void *key, int lookup_errno) |
215 | { |
216 | /* For prog_array maps or arrays of maps, failure to lookup the value |
217 | * means there is no entry for that key. Do not print an error message |
218 | * in that case. |
219 | */ |
220 | if ((map_is_map_of_maps(type: map_info->type) || |
221 | map_is_map_of_progs(type: map_info->type)) && lookup_errno == ENOENT) |
222 | return; |
223 | |
224 | if (json_output) { |
225 | jsonw_start_object(self: json_wtr); /* entry */ |
226 | jsonw_name(self: json_wtr, name: "key" ); |
227 | print_hex_data_json(data: key, len: map_info->key_size); |
228 | jsonw_name(self: json_wtr, name: "value" ); |
229 | jsonw_start_object(self: json_wtr); /* error */ |
230 | jsonw_string_field(self: json_wtr, prop: "error" , val: strerror(lookup_errno)); |
231 | jsonw_end_object(self: json_wtr); /* error */ |
232 | jsonw_end_object(self: json_wtr); /* entry */ |
233 | } else { |
234 | const char *msg = NULL; |
235 | |
236 | if (lookup_errno == ENOENT) |
237 | msg = "<no entry>" ; |
238 | else if (lookup_errno == ENOSPC && |
239 | map_info->type == BPF_MAP_TYPE_REUSEPORT_SOCKARRAY) |
240 | msg = "<cannot read>" ; |
241 | |
242 | print_entry_error_msg(info: map_info, key, |
243 | error_msg: msg ? : strerror(lookup_errno)); |
244 | } |
245 | } |
246 | |
247 | static void print_entry_plain(struct bpf_map_info *info, unsigned char *key, |
248 | unsigned char *value) |
249 | { |
250 | if (!map_is_per_cpu(type: info->type)) { |
251 | bool single_line, break_names; |
252 | |
253 | break_names = info->key_size > 16 || info->value_size > 16; |
254 | single_line = info->key_size + info->value_size <= 24 && |
255 | !break_names; |
256 | |
257 | if (info->key_size) { |
258 | printf("key:%c" , break_names ? '\n' : ' '); |
259 | fprint_hex(stdout, key, info->key_size, " " ); |
260 | |
261 | printf(single_line ? " " : "\n" ); |
262 | } |
263 | |
264 | if (info->value_size) { |
265 | if (map_is_map_of_maps(type: info->type)) { |
266 | printf("inner_map_id:%c" , break_names ? '\n' : ' '); |
267 | printf("%u " , *(unsigned int *)value); |
268 | } else { |
269 | printf("value:%c" , break_names ? '\n' : ' '); |
270 | fprint_hex(stdout, value, info->value_size, " " ); |
271 | } |
272 | } |
273 | |
274 | printf("\n" ); |
275 | } else { |
276 | unsigned int i, n, step; |
277 | |
278 | n = get_possible_cpus(); |
279 | step = round_up(info->value_size, 8); |
280 | |
281 | if (info->key_size) { |
282 | printf("key:\n" ); |
283 | fprint_hex(stdout, key, info->key_size, " " ); |
284 | printf("\n" ); |
285 | } |
286 | if (info->value_size) { |
287 | for (i = 0; i < n; i++) { |
288 | printf("value (CPU %02d):%c" , |
289 | i, info->value_size > 16 ? '\n' : ' '); |
290 | fprint_hex(stdout, value + i * step, |
291 | info->value_size, " " ); |
292 | printf("\n" ); |
293 | } |
294 | } |
295 | } |
296 | } |
297 | |
298 | static char **parse_bytes(char **argv, const char *name, unsigned char *val, |
299 | unsigned int n) |
300 | { |
301 | unsigned int i = 0, base = 0; |
302 | char *endptr; |
303 | |
304 | if (is_prefix(pfx: *argv, str: "hex" )) { |
305 | base = 16; |
306 | argv++; |
307 | } |
308 | |
309 | while (i < n && argv[i]) { |
310 | val[i] = strtoul(argv[i], &endptr, base); |
311 | if (*endptr) { |
312 | p_err(fmt: "error parsing byte: %s" , argv[i]); |
313 | return NULL; |
314 | } |
315 | i++; |
316 | } |
317 | |
318 | if (i != n) { |
319 | p_err(fmt: "%s expected %d bytes got %d" , name, n, i); |
320 | return NULL; |
321 | } |
322 | |
323 | return argv + i; |
324 | } |
325 | |
326 | /* on per cpu maps we must copy the provided value on all value instances */ |
327 | static void fill_per_cpu_value(struct bpf_map_info *info, void *value) |
328 | { |
329 | unsigned int i, n, step; |
330 | |
331 | if (!map_is_per_cpu(type: info->type)) |
332 | return; |
333 | |
334 | n = get_possible_cpus(); |
335 | step = round_up(info->value_size, 8); |
336 | for (i = 1; i < n; i++) |
337 | memcpy(value + i * step, value, info->value_size); |
338 | } |
339 | |
340 | static int parse_elem(char **argv, struct bpf_map_info *info, |
341 | void *key, void *value, __u32 key_size, __u32 value_size, |
342 | __u32 *flags, __u32 **value_fd) |
343 | { |
344 | if (!*argv) { |
345 | if (!key && !value) |
346 | return 0; |
347 | p_err(fmt: "did not find %s" , key ? "key" : "value" ); |
348 | return -1; |
349 | } |
350 | |
351 | if (is_prefix(pfx: *argv, str: "key" )) { |
352 | if (!key) { |
353 | if (key_size) |
354 | p_err(fmt: "duplicate key" ); |
355 | else |
356 | p_err(fmt: "unnecessary key" ); |
357 | return -1; |
358 | } |
359 | |
360 | argv = parse_bytes(argv: argv + 1, name: "key" , val: key, n: key_size); |
361 | if (!argv) |
362 | return -1; |
363 | |
364 | return parse_elem(argv, info, NULL, value, key_size, value_size, |
365 | flags, value_fd); |
366 | } else if (is_prefix(pfx: *argv, str: "value" )) { |
367 | int fd; |
368 | |
369 | if (!value) { |
370 | if (value_size) |
371 | p_err(fmt: "duplicate value" ); |
372 | else |
373 | p_err(fmt: "unnecessary value" ); |
374 | return -1; |
375 | } |
376 | |
377 | argv++; |
378 | |
379 | if (map_is_map_of_maps(type: info->type)) { |
380 | int argc = 2; |
381 | |
382 | if (value_size != 4) { |
383 | p_err(fmt: "value smaller than 4B for map in map?" ); |
384 | return -1; |
385 | } |
386 | if (!argv[0] || !argv[1]) { |
387 | p_err(fmt: "not enough value arguments for map in map" ); |
388 | return -1; |
389 | } |
390 | |
391 | fd = map_parse_fd(argc: &argc, argv: &argv); |
392 | if (fd < 0) |
393 | return -1; |
394 | |
395 | *value_fd = value; |
396 | **value_fd = fd; |
397 | } else if (map_is_map_of_progs(type: info->type)) { |
398 | int argc = 2; |
399 | |
400 | if (value_size != 4) { |
401 | p_err(fmt: "value smaller than 4B for map of progs?" ); |
402 | return -1; |
403 | } |
404 | if (!argv[0] || !argv[1]) { |
405 | p_err(fmt: "not enough value arguments for map of progs" ); |
406 | return -1; |
407 | } |
408 | if (is_prefix(pfx: *argv, str: "id" )) |
409 | p_info(fmt: "Warning: updating program array via MAP_ID, make sure this map is kept open\n" |
410 | " by some process or pinned otherwise update will be lost" ); |
411 | |
412 | fd = prog_parse_fd(argc: &argc, argv: &argv); |
413 | if (fd < 0) |
414 | return -1; |
415 | |
416 | *value_fd = value; |
417 | **value_fd = fd; |
418 | } else { |
419 | argv = parse_bytes(argv, name: "value" , val: value, n: value_size); |
420 | if (!argv) |
421 | return -1; |
422 | |
423 | fill_per_cpu_value(info, value); |
424 | } |
425 | |
426 | return parse_elem(argv, info, key, NULL, key_size, value_size, |
427 | flags, NULL); |
428 | } else if (is_prefix(pfx: *argv, str: "any" ) || is_prefix(pfx: *argv, str: "noexist" ) || |
429 | is_prefix(pfx: *argv, str: "exist" )) { |
430 | if (!flags) { |
431 | p_err(fmt: "flags specified multiple times: %s" , *argv); |
432 | return -1; |
433 | } |
434 | |
435 | if (is_prefix(pfx: *argv, str: "any" )) |
436 | *flags = BPF_ANY; |
437 | else if (is_prefix(pfx: *argv, str: "noexist" )) |
438 | *flags = BPF_NOEXIST; |
439 | else if (is_prefix(pfx: *argv, str: "exist" )) |
440 | *flags = BPF_EXIST; |
441 | |
442 | return parse_elem(argv: argv + 1, info, key, value, key_size, |
443 | value_size, NULL, value_fd); |
444 | } |
445 | |
446 | p_err(fmt: "expected key or value, got: %s" , *argv); |
447 | return -1; |
448 | } |
449 | |
450 | static void (struct bpf_map_info *info, json_writer_t *wtr) |
451 | { |
452 | const char *map_type_str; |
453 | |
454 | jsonw_uint_field(self: wtr, prop: "id" , num: info->id); |
455 | map_type_str = libbpf_bpf_map_type_str(info->type); |
456 | if (map_type_str) |
457 | jsonw_string_field(self: wtr, prop: "type" , val: map_type_str); |
458 | else |
459 | jsonw_uint_field(self: wtr, prop: "type" , num: info->type); |
460 | |
461 | if (*info->name) |
462 | jsonw_string_field(self: wtr, prop: "name" , val: info->name); |
463 | |
464 | jsonw_name(self: wtr, name: "flags" ); |
465 | jsonw_printf(self: wtr, fmt: "%d" , info->map_flags); |
466 | } |
467 | |
468 | static int show_map_close_json(int fd, struct bpf_map_info *info) |
469 | { |
470 | char *memlock, *frozen_str; |
471 | int frozen = 0; |
472 | |
473 | memlock = get_fdinfo(fd, key: "memlock" ); |
474 | frozen_str = get_fdinfo(fd, key: "frozen" ); |
475 | |
476 | jsonw_start_object(self: json_wtr); |
477 | |
478 | show_map_header_json(info, wtr: json_wtr); |
479 | |
480 | print_dev_json(ifindex: info->ifindex, ns_dev: info->netns_dev, ns_inode: info->netns_ino); |
481 | |
482 | jsonw_uint_field(self: json_wtr, prop: "bytes_key" , num: info->key_size); |
483 | jsonw_uint_field(self: json_wtr, prop: "bytes_value" , num: info->value_size); |
484 | jsonw_uint_field(self: json_wtr, prop: "max_entries" , num: info->max_entries); |
485 | |
486 | if (memlock) |
487 | jsonw_int_field(self: json_wtr, prop: "bytes_memlock" , num: atoll(memlock)); |
488 | free(memlock); |
489 | |
490 | if (info->type == BPF_MAP_TYPE_PROG_ARRAY) { |
491 | char *owner_prog_type = get_fdinfo(fd, key: "owner_prog_type" ); |
492 | char *owner_jited = get_fdinfo(fd, key: "owner_jited" ); |
493 | |
494 | if (owner_prog_type) { |
495 | unsigned int prog_type = atoi(owner_prog_type); |
496 | const char *prog_type_str; |
497 | |
498 | prog_type_str = libbpf_bpf_prog_type_str(prog_type); |
499 | if (prog_type_str) |
500 | jsonw_string_field(self: json_wtr, prop: "owner_prog_type" , |
501 | val: prog_type_str); |
502 | else |
503 | jsonw_uint_field(self: json_wtr, prop: "owner_prog_type" , |
504 | num: prog_type); |
505 | } |
506 | if (owner_jited) |
507 | jsonw_bool_field(self: json_wtr, prop: "owner_jited" , |
508 | value: !!atoi(owner_jited)); |
509 | |
510 | free(owner_prog_type); |
511 | free(owner_jited); |
512 | } |
513 | close(fd); |
514 | |
515 | if (frozen_str) { |
516 | frozen = atoi(frozen_str); |
517 | free(frozen_str); |
518 | } |
519 | jsonw_int_field(self: json_wtr, prop: "frozen" , num: frozen); |
520 | |
521 | if (info->btf_id) |
522 | jsonw_int_field(self: json_wtr, prop: "btf_id" , num: info->btf_id); |
523 | |
524 | if (!hashmap__empty(map: map_table)) { |
525 | struct hashmap_entry *entry; |
526 | |
527 | jsonw_name(self: json_wtr, name: "pinned" ); |
528 | jsonw_start_array(self: json_wtr); |
529 | hashmap__for_each_key_entry(map_table, entry, info->id) |
530 | jsonw_string(self: json_wtr, value: entry->pvalue); |
531 | jsonw_end_array(self: json_wtr); |
532 | } |
533 | |
534 | emit_obj_refs_json(table: refs_table, id: info->id, json_wtr); |
535 | |
536 | jsonw_end_object(self: json_wtr); |
537 | |
538 | return 0; |
539 | } |
540 | |
541 | static void (struct bpf_map_info *info) |
542 | { |
543 | const char *map_type_str; |
544 | |
545 | printf("%u: " , info->id); |
546 | |
547 | map_type_str = libbpf_bpf_map_type_str(info->type); |
548 | if (map_type_str) |
549 | printf("%s " , map_type_str); |
550 | else |
551 | printf("type %u " , info->type); |
552 | |
553 | if (*info->name) |
554 | printf("name %s " , info->name); |
555 | |
556 | printf("flags 0x%x" , info->map_flags); |
557 | print_dev_plain(ifindex: info->ifindex, ns_dev: info->netns_dev, ns_inode: info->netns_ino); |
558 | printf("\n" ); |
559 | } |
560 | |
561 | static int show_map_close_plain(int fd, struct bpf_map_info *info) |
562 | { |
563 | char *memlock, *frozen_str; |
564 | int frozen = 0; |
565 | |
566 | memlock = get_fdinfo(fd, key: "memlock" ); |
567 | frozen_str = get_fdinfo(fd, key: "frozen" ); |
568 | |
569 | show_map_header_plain(info); |
570 | printf("\tkey %uB value %uB max_entries %u" , |
571 | info->key_size, info->value_size, info->max_entries); |
572 | |
573 | if (memlock) |
574 | printf(" memlock %sB" , memlock); |
575 | free(memlock); |
576 | |
577 | if (info->type == BPF_MAP_TYPE_PROG_ARRAY) { |
578 | char *owner_prog_type = get_fdinfo(fd, key: "owner_prog_type" ); |
579 | char *owner_jited = get_fdinfo(fd, key: "owner_jited" ); |
580 | |
581 | if (owner_prog_type || owner_jited) |
582 | printf("\n\t" ); |
583 | if (owner_prog_type) { |
584 | unsigned int prog_type = atoi(owner_prog_type); |
585 | const char *prog_type_str; |
586 | |
587 | prog_type_str = libbpf_bpf_prog_type_str(prog_type); |
588 | if (prog_type_str) |
589 | printf("owner_prog_type %s " , prog_type_str); |
590 | else |
591 | printf("owner_prog_type %d " , prog_type); |
592 | } |
593 | if (owner_jited) |
594 | printf("owner%s jited" , |
595 | atoi(owner_jited) ? "" : " not" ); |
596 | |
597 | free(owner_prog_type); |
598 | free(owner_jited); |
599 | } |
600 | close(fd); |
601 | |
602 | if (!hashmap__empty(map: map_table)) { |
603 | struct hashmap_entry *entry; |
604 | |
605 | hashmap__for_each_key_entry(map_table, entry, info->id) |
606 | printf("\n\tpinned %s" , (char *)entry->pvalue); |
607 | } |
608 | |
609 | if (frozen_str) { |
610 | frozen = atoi(frozen_str); |
611 | free(frozen_str); |
612 | } |
613 | |
614 | if (info->btf_id || frozen) |
615 | printf("\n\t" ); |
616 | |
617 | if (info->btf_id) |
618 | printf("btf_id %d" , info->btf_id); |
619 | |
620 | if (frozen) |
621 | printf("%sfrozen" , info->btf_id ? " " : "" ); |
622 | |
623 | emit_obj_refs_plain(table: refs_table, id: info->id, prefix: "\n\tpids " ); |
624 | |
625 | printf("\n" ); |
626 | return 0; |
627 | } |
628 | |
629 | static int do_show_subset(int argc, char **argv) |
630 | { |
631 | struct bpf_map_info info = {}; |
632 | __u32 len = sizeof(info); |
633 | int *fds = NULL; |
634 | int nb_fds, i; |
635 | int err = -1; |
636 | |
637 | fds = malloc(sizeof(int)); |
638 | if (!fds) { |
639 | p_err(fmt: "mem alloc failed" ); |
640 | return -1; |
641 | } |
642 | nb_fds = map_parse_fds(argc: &argc, argv: &argv, fds: &fds); |
643 | if (nb_fds < 1) |
644 | goto exit_free; |
645 | |
646 | if (json_output && nb_fds > 1) |
647 | jsonw_start_array(self: json_wtr); /* root array */ |
648 | for (i = 0; i < nb_fds; i++) { |
649 | err = bpf_map_get_info_by_fd(fds[i], &info, &len); |
650 | if (err) { |
651 | p_err(fmt: "can't get map info: %s" , |
652 | strerror(errno)); |
653 | for (; i < nb_fds; i++) |
654 | close(fds[i]); |
655 | break; |
656 | } |
657 | |
658 | if (json_output) |
659 | show_map_close_json(fd: fds[i], info: &info); |
660 | else |
661 | show_map_close_plain(fd: fds[i], info: &info); |
662 | |
663 | close(fds[i]); |
664 | } |
665 | if (json_output && nb_fds > 1) |
666 | jsonw_end_array(self: json_wtr); /* root array */ |
667 | |
668 | exit_free: |
669 | free(fds); |
670 | return err; |
671 | } |
672 | |
673 | static int do_show(int argc, char **argv) |
674 | { |
675 | struct bpf_map_info info = {}; |
676 | __u32 len = sizeof(info); |
677 | __u32 id = 0; |
678 | int err; |
679 | int fd; |
680 | |
681 | if (show_pinned) { |
682 | map_table = hashmap__new(hash_fn_for_key_as_id, |
683 | equal_fn_for_key_as_id, NULL); |
684 | if (IS_ERR(ptr: map_table)) { |
685 | p_err(fmt: "failed to create hashmap for pinned paths" ); |
686 | return -1; |
687 | } |
688 | build_pinned_obj_table(table: map_table, type: BPF_OBJ_MAP); |
689 | } |
690 | build_obj_refs_table(table: &refs_table, type: BPF_OBJ_MAP); |
691 | |
692 | if (argc == 2) |
693 | return do_show_subset(argc, argv); |
694 | |
695 | if (argc) |
696 | return BAD_ARG(); |
697 | |
698 | if (json_output) |
699 | jsonw_start_array(self: json_wtr); |
700 | while (true) { |
701 | err = bpf_map_get_next_id(id, &id); |
702 | if (err) { |
703 | if (errno == ENOENT) |
704 | break; |
705 | p_err(fmt: "can't get next map: %s%s" , strerror(errno), |
706 | errno == EINVAL ? " -- kernel too old?" : "" ); |
707 | break; |
708 | } |
709 | |
710 | fd = bpf_map_get_fd_by_id(id); |
711 | if (fd < 0) { |
712 | if (errno == ENOENT) |
713 | continue; |
714 | p_err(fmt: "can't get map by id (%u): %s" , |
715 | id, strerror(errno)); |
716 | break; |
717 | } |
718 | |
719 | err = bpf_map_get_info_by_fd(fd, &info, &len); |
720 | if (err) { |
721 | p_err(fmt: "can't get map info: %s" , strerror(errno)); |
722 | close(fd); |
723 | break; |
724 | } |
725 | |
726 | if (json_output) |
727 | show_map_close_json(fd, info: &info); |
728 | else |
729 | show_map_close_plain(fd, info: &info); |
730 | } |
731 | if (json_output) |
732 | jsonw_end_array(self: json_wtr); |
733 | |
734 | delete_obj_refs_table(table: refs_table); |
735 | |
736 | if (show_pinned) |
737 | delete_pinned_obj_table(table: map_table); |
738 | |
739 | return errno == ENOENT ? 0 : -1; |
740 | } |
741 | |
742 | static int dump_map_elem(int fd, void *key, void *value, |
743 | struct bpf_map_info *map_info, struct btf *btf, |
744 | json_writer_t *btf_wtr) |
745 | { |
746 | if (bpf_map_lookup_elem(fd, key, value)) { |
747 | print_entry_error(map_info, key, lookup_errno: errno); |
748 | return -1; |
749 | } |
750 | |
751 | if (json_output) { |
752 | print_entry_json(info: map_info, key, value, btf); |
753 | } else if (btf) { |
754 | struct btf_dumper d = { |
755 | .btf = btf, |
756 | .jw = btf_wtr, |
757 | .is_plain_text = true, |
758 | }; |
759 | |
760 | do_dump_btf(d: &d, map_info, key, value); |
761 | } else { |
762 | print_entry_plain(info: map_info, key, value); |
763 | } |
764 | |
765 | return 0; |
766 | } |
767 | |
768 | static int maps_have_btf(int *fds, int nb_fds) |
769 | { |
770 | struct bpf_map_info info = {}; |
771 | __u32 len = sizeof(info); |
772 | int err, i; |
773 | |
774 | for (i = 0; i < nb_fds; i++) { |
775 | err = bpf_map_get_info_by_fd(fds[i], &info, &len); |
776 | if (err) { |
777 | p_err(fmt: "can't get map info: %s" , strerror(errno)); |
778 | return -1; |
779 | } |
780 | |
781 | if (!info.btf_id) |
782 | return 0; |
783 | } |
784 | |
785 | return 1; |
786 | } |
787 | |
788 | static struct btf *btf_vmlinux; |
789 | |
790 | static int get_map_kv_btf(const struct bpf_map_info *info, struct btf **btf) |
791 | { |
792 | int err = 0; |
793 | |
794 | if (info->btf_vmlinux_value_type_id) { |
795 | if (!btf_vmlinux) { |
796 | btf_vmlinux = libbpf_find_kernel_btf(); |
797 | if (!btf_vmlinux) { |
798 | p_err(fmt: "failed to get kernel btf" ); |
799 | return -errno; |
800 | } |
801 | } |
802 | *btf = btf_vmlinux; |
803 | } else if (info->btf_value_type_id) { |
804 | *btf = btf__load_from_kernel_by_id(info->btf_id); |
805 | if (!*btf) { |
806 | err = -errno; |
807 | p_err(fmt: "failed to get btf" ); |
808 | } |
809 | } else { |
810 | *btf = NULL; |
811 | } |
812 | |
813 | return err; |
814 | } |
815 | |
816 | static void free_map_kv_btf(struct btf *btf) |
817 | { |
818 | if (btf != btf_vmlinux) |
819 | btf__free(btf); |
820 | } |
821 | |
822 | static int |
823 | map_dump(int fd, struct bpf_map_info *info, json_writer_t *wtr, |
824 | bool ) |
825 | { |
826 | void *key, *value, *prev_key; |
827 | unsigned int num_elems = 0; |
828 | struct btf *btf = NULL; |
829 | int err; |
830 | |
831 | key = malloc(info->key_size); |
832 | value = alloc_value(info); |
833 | if (!key || !value) { |
834 | p_err(fmt: "mem alloc failed" ); |
835 | err = -1; |
836 | goto exit_free; |
837 | } |
838 | |
839 | prev_key = NULL; |
840 | |
841 | if (wtr) { |
842 | err = get_map_kv_btf(info, btf: &btf); |
843 | if (err) { |
844 | goto exit_free; |
845 | } |
846 | |
847 | if (show_header) { |
848 | jsonw_start_object(self: wtr); /* map object */ |
849 | show_map_header_json(info, wtr); |
850 | jsonw_name(self: wtr, name: "elements" ); |
851 | } |
852 | jsonw_start_array(self: wtr); /* elements */ |
853 | } else if (show_header) { |
854 | show_map_header_plain(info); |
855 | } |
856 | |
857 | if (info->type == BPF_MAP_TYPE_REUSEPORT_SOCKARRAY && |
858 | info->value_size != 8) { |
859 | const char *map_type_str; |
860 | |
861 | map_type_str = libbpf_bpf_map_type_str(info->type); |
862 | p_info(fmt: "Warning: cannot read values from %s map with value_size != 8" , |
863 | map_type_str); |
864 | } |
865 | while (true) { |
866 | err = bpf_map_get_next_key(fd, prev_key, key); |
867 | if (err) { |
868 | if (errno == ENOENT) |
869 | err = 0; |
870 | break; |
871 | } |
872 | if (!dump_map_elem(fd, key, value, map_info: info, btf, btf_wtr: wtr)) |
873 | num_elems++; |
874 | prev_key = key; |
875 | } |
876 | |
877 | if (wtr) { |
878 | jsonw_end_array(self: wtr); /* elements */ |
879 | if (show_header) |
880 | jsonw_end_object(self: wtr); /* map object */ |
881 | } else { |
882 | printf("Found %u element%s\n" , num_elems, |
883 | num_elems != 1 ? "s" : "" ); |
884 | } |
885 | |
886 | exit_free: |
887 | free(key); |
888 | free(value); |
889 | close(fd); |
890 | free_map_kv_btf(btf); |
891 | |
892 | return err; |
893 | } |
894 | |
895 | static int do_dump(int argc, char **argv) |
896 | { |
897 | json_writer_t *wtr = NULL, *btf_wtr = NULL; |
898 | struct bpf_map_info info = {}; |
899 | int nb_fds, i = 0; |
900 | __u32 len = sizeof(info); |
901 | int *fds = NULL; |
902 | int err = -1; |
903 | |
904 | if (argc != 2) |
905 | usage(); |
906 | |
907 | fds = malloc(sizeof(int)); |
908 | if (!fds) { |
909 | p_err(fmt: "mem alloc failed" ); |
910 | return -1; |
911 | } |
912 | nb_fds = map_parse_fds(argc: &argc, argv: &argv, fds: &fds); |
913 | if (nb_fds < 1) |
914 | goto exit_free; |
915 | |
916 | if (json_output) { |
917 | wtr = json_wtr; |
918 | } else { |
919 | int do_plain_btf; |
920 | |
921 | do_plain_btf = maps_have_btf(fds, nb_fds); |
922 | if (do_plain_btf < 0) |
923 | goto exit_close; |
924 | |
925 | if (do_plain_btf) { |
926 | btf_wtr = get_btf_writer(); |
927 | wtr = btf_wtr; |
928 | if (!btf_wtr) |
929 | p_info(fmt: "failed to create json writer for btf. falling back to plain output" ); |
930 | } |
931 | } |
932 | |
933 | if (wtr && nb_fds > 1) |
934 | jsonw_start_array(self: wtr); /* root array */ |
935 | for (i = 0; i < nb_fds; i++) { |
936 | if (bpf_map_get_info_by_fd(fds[i], &info, &len)) { |
937 | p_err(fmt: "can't get map info: %s" , strerror(errno)); |
938 | break; |
939 | } |
940 | err = map_dump(fd: fds[i], info: &info, wtr, show_header: nb_fds > 1); |
941 | if (!wtr && i != nb_fds - 1) |
942 | printf("\n" ); |
943 | |
944 | if (err) |
945 | break; |
946 | close(fds[i]); |
947 | } |
948 | if (wtr && nb_fds > 1) |
949 | jsonw_end_array(self: wtr); /* root array */ |
950 | |
951 | if (btf_wtr) |
952 | jsonw_destroy(self_p: &btf_wtr); |
953 | exit_close: |
954 | for (; i < nb_fds; i++) |
955 | close(fds[i]); |
956 | exit_free: |
957 | free(fds); |
958 | btf__free(btf_vmlinux); |
959 | return err; |
960 | } |
961 | |
962 | static int alloc_key_value(struct bpf_map_info *info, void **key, void **value) |
963 | { |
964 | *key = NULL; |
965 | *value = NULL; |
966 | |
967 | if (info->key_size) { |
968 | *key = malloc(info->key_size); |
969 | if (!*key) { |
970 | p_err(fmt: "key mem alloc failed" ); |
971 | return -1; |
972 | } |
973 | } |
974 | |
975 | if (info->value_size) { |
976 | *value = alloc_value(info); |
977 | if (!*value) { |
978 | p_err(fmt: "value mem alloc failed" ); |
979 | free(*key); |
980 | *key = NULL; |
981 | return -1; |
982 | } |
983 | } |
984 | |
985 | return 0; |
986 | } |
987 | |
988 | static int do_update(int argc, char **argv) |
989 | { |
990 | struct bpf_map_info info = {}; |
991 | __u32 len = sizeof(info); |
992 | __u32 *value_fd = NULL; |
993 | __u32 flags = BPF_ANY; |
994 | void *key, *value; |
995 | int fd, err; |
996 | |
997 | if (argc < 2) |
998 | usage(); |
999 | |
1000 | fd = map_parse_fd_and_info(argc: &argc, argv: &argv, info: &info, info_len: &len); |
1001 | if (fd < 0) |
1002 | return -1; |
1003 | |
1004 | err = alloc_key_value(info: &info, key: &key, value: &value); |
1005 | if (err) |
1006 | goto exit_free; |
1007 | |
1008 | err = parse_elem(argv, info: &info, key, value, key_size: info.key_size, |
1009 | value_size: info.value_size, flags: &flags, value_fd: &value_fd); |
1010 | if (err) |
1011 | goto exit_free; |
1012 | |
1013 | err = bpf_map_update_elem(fd, key, value, flags); |
1014 | if (err) { |
1015 | p_err(fmt: "update failed: %s" , strerror(errno)); |
1016 | goto exit_free; |
1017 | } |
1018 | |
1019 | exit_free: |
1020 | if (value_fd) |
1021 | close(*value_fd); |
1022 | free(key); |
1023 | free(value); |
1024 | close(fd); |
1025 | |
1026 | if (!err && json_output) |
1027 | jsonw_null(self: json_wtr); |
1028 | return err; |
1029 | } |
1030 | |
1031 | static void print_key_value(struct bpf_map_info *info, void *key, |
1032 | void *value) |
1033 | { |
1034 | json_writer_t *btf_wtr; |
1035 | struct btf *btf; |
1036 | |
1037 | if (get_map_kv_btf(info, btf: &btf)) |
1038 | return; |
1039 | |
1040 | if (json_output) { |
1041 | print_entry_json(info, key, value, btf); |
1042 | } else if (btf) { |
1043 | /* if here json_wtr wouldn't have been initialised, |
1044 | * so let's create separate writer for btf |
1045 | */ |
1046 | btf_wtr = get_btf_writer(); |
1047 | if (!btf_wtr) { |
1048 | p_info(fmt: "failed to create json writer for btf. falling back to plain output" ); |
1049 | btf__free(btf); |
1050 | btf = NULL; |
1051 | print_entry_plain(info, key, value); |
1052 | } else { |
1053 | struct btf_dumper d = { |
1054 | .btf = btf, |
1055 | .jw = btf_wtr, |
1056 | .is_plain_text = true, |
1057 | }; |
1058 | |
1059 | do_dump_btf(d: &d, map_info: info, key, value); |
1060 | jsonw_destroy(self_p: &btf_wtr); |
1061 | } |
1062 | } else { |
1063 | print_entry_plain(info, key, value); |
1064 | } |
1065 | btf__free(btf); |
1066 | } |
1067 | |
1068 | static int do_lookup(int argc, char **argv) |
1069 | { |
1070 | struct bpf_map_info info = {}; |
1071 | __u32 len = sizeof(info); |
1072 | void *key, *value; |
1073 | int err; |
1074 | int fd; |
1075 | |
1076 | if (argc < 2) |
1077 | usage(); |
1078 | |
1079 | fd = map_parse_fd_and_info(argc: &argc, argv: &argv, info: &info, info_len: &len); |
1080 | if (fd < 0) |
1081 | return -1; |
1082 | |
1083 | err = alloc_key_value(info: &info, key: &key, value: &value); |
1084 | if (err) |
1085 | goto exit_free; |
1086 | |
1087 | err = parse_elem(argv, info: &info, key, NULL, key_size: info.key_size, value_size: 0, NULL, NULL); |
1088 | if (err) |
1089 | goto exit_free; |
1090 | |
1091 | err = bpf_map_lookup_elem(fd, key, value); |
1092 | if (err) { |
1093 | if (errno == ENOENT) { |
1094 | if (json_output) { |
1095 | jsonw_null(self: json_wtr); |
1096 | } else { |
1097 | printf("key:\n" ); |
1098 | fprint_hex(stdout, key, info.key_size, " " ); |
1099 | printf("\n\nNot found\n" ); |
1100 | } |
1101 | } else { |
1102 | p_err(fmt: "lookup failed: %s" , strerror(errno)); |
1103 | } |
1104 | |
1105 | goto exit_free; |
1106 | } |
1107 | |
1108 | /* here means bpf_map_lookup_elem() succeeded */ |
1109 | print_key_value(info: &info, key, value); |
1110 | |
1111 | exit_free: |
1112 | free(key); |
1113 | free(value); |
1114 | close(fd); |
1115 | |
1116 | return err; |
1117 | } |
1118 | |
1119 | static int do_getnext(int argc, char **argv) |
1120 | { |
1121 | struct bpf_map_info info = {}; |
1122 | __u32 len = sizeof(info); |
1123 | void *key, *nextkey; |
1124 | int err; |
1125 | int fd; |
1126 | |
1127 | if (argc < 2) |
1128 | usage(); |
1129 | |
1130 | fd = map_parse_fd_and_info(argc: &argc, argv: &argv, info: &info, info_len: &len); |
1131 | if (fd < 0) |
1132 | return -1; |
1133 | |
1134 | key = malloc(info.key_size); |
1135 | nextkey = malloc(info.key_size); |
1136 | if (!key || !nextkey) { |
1137 | p_err(fmt: "mem alloc failed" ); |
1138 | err = -1; |
1139 | goto exit_free; |
1140 | } |
1141 | |
1142 | if (argc) { |
1143 | err = parse_elem(argv, info: &info, key, NULL, key_size: info.key_size, value_size: 0, |
1144 | NULL, NULL); |
1145 | if (err) |
1146 | goto exit_free; |
1147 | } else { |
1148 | free(key); |
1149 | key = NULL; |
1150 | } |
1151 | |
1152 | err = bpf_map_get_next_key(fd, key, nextkey); |
1153 | if (err) { |
1154 | p_err(fmt: "can't get next key: %s" , strerror(errno)); |
1155 | goto exit_free; |
1156 | } |
1157 | |
1158 | if (json_output) { |
1159 | jsonw_start_object(self: json_wtr); |
1160 | if (key) { |
1161 | jsonw_name(self: json_wtr, name: "key" ); |
1162 | print_hex_data_json(data: key, len: info.key_size); |
1163 | } else { |
1164 | jsonw_null_field(self: json_wtr, prop: "key" ); |
1165 | } |
1166 | jsonw_name(self: json_wtr, name: "next_key" ); |
1167 | print_hex_data_json(data: nextkey, len: info.key_size); |
1168 | jsonw_end_object(self: json_wtr); |
1169 | } else { |
1170 | if (key) { |
1171 | printf("key:\n" ); |
1172 | fprint_hex(stdout, key, info.key_size, " " ); |
1173 | printf("\n" ); |
1174 | } else { |
1175 | printf("key: None\n" ); |
1176 | } |
1177 | printf("next key:\n" ); |
1178 | fprint_hex(stdout, nextkey, info.key_size, " " ); |
1179 | printf("\n" ); |
1180 | } |
1181 | |
1182 | exit_free: |
1183 | free(nextkey); |
1184 | free(key); |
1185 | close(fd); |
1186 | |
1187 | return err; |
1188 | } |
1189 | |
1190 | static int do_delete(int argc, char **argv) |
1191 | { |
1192 | struct bpf_map_info info = {}; |
1193 | __u32 len = sizeof(info); |
1194 | void *key; |
1195 | int err; |
1196 | int fd; |
1197 | |
1198 | if (argc < 2) |
1199 | usage(); |
1200 | |
1201 | fd = map_parse_fd_and_info(argc: &argc, argv: &argv, info: &info, info_len: &len); |
1202 | if (fd < 0) |
1203 | return -1; |
1204 | |
1205 | key = malloc(info.key_size); |
1206 | if (!key) { |
1207 | p_err(fmt: "mem alloc failed" ); |
1208 | err = -1; |
1209 | goto exit_free; |
1210 | } |
1211 | |
1212 | err = parse_elem(argv, info: &info, key, NULL, key_size: info.key_size, value_size: 0, NULL, NULL); |
1213 | if (err) |
1214 | goto exit_free; |
1215 | |
1216 | err = bpf_map_delete_elem(fd, key); |
1217 | if (err) |
1218 | p_err("delete failed: %s" , strerror(errno)); |
1219 | |
1220 | exit_free: |
1221 | free(key); |
1222 | close(fd); |
1223 | |
1224 | if (!err && json_output) |
1225 | jsonw_null(self: json_wtr); |
1226 | return err; |
1227 | } |
1228 | |
1229 | static int do_pin(int argc, char **argv) |
1230 | { |
1231 | int err; |
1232 | |
1233 | err = do_pin_any(argc, argv, get_fd_by_id: map_parse_fd); |
1234 | if (!err && json_output) |
1235 | jsonw_null(self: json_wtr); |
1236 | return err; |
1237 | } |
1238 | |
1239 | static int do_create(int argc, char **argv) |
1240 | { |
1241 | LIBBPF_OPTS(bpf_map_create_opts, attr); |
1242 | enum bpf_map_type map_type = BPF_MAP_TYPE_UNSPEC; |
1243 | __u32 key_size = 0, value_size = 0, max_entries = 0; |
1244 | const char *map_name = NULL; |
1245 | const char *pinfile; |
1246 | int err = -1, fd; |
1247 | |
1248 | if (!REQ_ARGS(7)) |
1249 | return -1; |
1250 | pinfile = GET_ARG(); |
1251 | |
1252 | while (argc) { |
1253 | if (!REQ_ARGS(2)) |
1254 | return -1; |
1255 | |
1256 | if (is_prefix(pfx: *argv, str: "type" )) { |
1257 | NEXT_ARG(); |
1258 | |
1259 | if (map_type) { |
1260 | p_err(fmt: "map type already specified" ); |
1261 | goto exit; |
1262 | } |
1263 | |
1264 | map_type = map_type_from_str(type: *argv); |
1265 | if ((int)map_type < 0) { |
1266 | p_err(fmt: "unrecognized map type: %s" , *argv); |
1267 | goto exit; |
1268 | } |
1269 | NEXT_ARG(); |
1270 | } else if (is_prefix(pfx: *argv, str: "name" )) { |
1271 | NEXT_ARG(); |
1272 | map_name = GET_ARG(); |
1273 | } else if (is_prefix(pfx: *argv, str: "key" )) { |
1274 | if (parse_u32_arg(argc: &argc, argv: &argv, val: &key_size, |
1275 | what: "key size" )) |
1276 | goto exit; |
1277 | } else if (is_prefix(pfx: *argv, str: "value" )) { |
1278 | if (parse_u32_arg(argc: &argc, argv: &argv, val: &value_size, |
1279 | what: "value size" )) |
1280 | goto exit; |
1281 | } else if (is_prefix(pfx: *argv, str: "entries" )) { |
1282 | if (parse_u32_arg(argc: &argc, argv: &argv, val: &max_entries, |
1283 | what: "max entries" )) |
1284 | goto exit; |
1285 | } else if (is_prefix(pfx: *argv, str: "flags" )) { |
1286 | if (parse_u32_arg(&argc, &argv, &attr.map_flags, |
1287 | "flags" )) |
1288 | goto exit; |
1289 | } else if (is_prefix(pfx: *argv, str: "dev" )) { |
1290 | p_info(fmt: "Warning: 'bpftool map create [...] dev <ifname>' syntax is deprecated.\n" |
1291 | "Going further, please use 'offload_dev <ifname>' to request hardware offload for the map." ); |
1292 | goto offload_dev; |
1293 | } else if (is_prefix(pfx: *argv, str: "offload_dev" )) { |
1294 | offload_dev: |
1295 | NEXT_ARG(); |
1296 | |
1297 | if (attr.map_ifindex) { |
1298 | p_err(fmt: "offload device already specified" ); |
1299 | goto exit; |
1300 | } |
1301 | |
1302 | attr.map_ifindex = if_nametoindex(*argv); |
1303 | if (!attr.map_ifindex) { |
1304 | p_err("unrecognized netdevice '%s': %s" , |
1305 | *argv, strerror(errno)); |
1306 | goto exit; |
1307 | } |
1308 | NEXT_ARG(); |
1309 | } else if (is_prefix(pfx: *argv, str: "inner_map" )) { |
1310 | struct bpf_map_info info = {}; |
1311 | __u32 len = sizeof(info); |
1312 | int inner_map_fd; |
1313 | |
1314 | NEXT_ARG(); |
1315 | if (!REQ_ARGS(2)) |
1316 | usage(); |
1317 | inner_map_fd = map_parse_fd_and_info(argc: &argc, argv: &argv, |
1318 | info: &info, info_len: &len); |
1319 | if (inner_map_fd < 0) |
1320 | return -1; |
1321 | attr.inner_map_fd = inner_map_fd; |
1322 | } else { |
1323 | p_err(fmt: "unknown arg %s" , *argv); |
1324 | goto exit; |
1325 | } |
1326 | } |
1327 | |
1328 | if (!map_name) { |
1329 | p_err(fmt: "map name not specified" ); |
1330 | goto exit; |
1331 | } |
1332 | |
1333 | set_max_rlimit(); |
1334 | |
1335 | fd = bpf_map_create(map_type, map_name, key_size, value_size, max_entries, &attr); |
1336 | if (fd < 0) { |
1337 | p_err("map create failed: %s" , strerror(errno)); |
1338 | goto exit; |
1339 | } |
1340 | |
1341 | err = do_pin_fd(fd, name: pinfile); |
1342 | close(fd); |
1343 | if (err) |
1344 | goto exit; |
1345 | |
1346 | if (json_output) |
1347 | jsonw_null(self: json_wtr); |
1348 | |
1349 | exit: |
1350 | if (attr.inner_map_fd > 0) |
1351 | close(attr.inner_map_fd); |
1352 | |
1353 | return err; |
1354 | } |
1355 | |
1356 | static int do_pop_dequeue(int argc, char **argv) |
1357 | { |
1358 | struct bpf_map_info info = {}; |
1359 | __u32 len = sizeof(info); |
1360 | void *key, *value; |
1361 | int err; |
1362 | int fd; |
1363 | |
1364 | if (argc < 2) |
1365 | usage(); |
1366 | |
1367 | fd = map_parse_fd_and_info(argc: &argc, argv: &argv, info: &info, info_len: &len); |
1368 | if (fd < 0) |
1369 | return -1; |
1370 | |
1371 | err = alloc_key_value(info: &info, key: &key, value: &value); |
1372 | if (err) |
1373 | goto exit_free; |
1374 | |
1375 | err = bpf_map_lookup_and_delete_elem(fd, key, value); |
1376 | if (err) { |
1377 | if (errno == ENOENT) { |
1378 | if (json_output) |
1379 | jsonw_null(self: json_wtr); |
1380 | else |
1381 | printf("Error: empty map\n" ); |
1382 | } else { |
1383 | p_err("pop failed: %s" , strerror(errno)); |
1384 | } |
1385 | |
1386 | goto exit_free; |
1387 | } |
1388 | |
1389 | print_key_value(info: &info, key, value); |
1390 | |
1391 | exit_free: |
1392 | free(key); |
1393 | free(value); |
1394 | close(fd); |
1395 | |
1396 | return err; |
1397 | } |
1398 | |
1399 | static int do_freeze(int argc, char **argv) |
1400 | { |
1401 | int err, fd; |
1402 | |
1403 | if (!REQ_ARGS(2)) |
1404 | return -1; |
1405 | |
1406 | fd = map_parse_fd(argc: &argc, argv: &argv); |
1407 | if (fd < 0) |
1408 | return -1; |
1409 | |
1410 | if (argc) { |
1411 | close(fd); |
1412 | return BAD_ARG(); |
1413 | } |
1414 | |
1415 | err = bpf_map_freeze(fd); |
1416 | close(fd); |
1417 | if (err) { |
1418 | p_err("failed to freeze map: %s" , strerror(errno)); |
1419 | return err; |
1420 | } |
1421 | |
1422 | if (json_output) |
1423 | jsonw_null(self: json_wtr); |
1424 | |
1425 | return 0; |
1426 | } |
1427 | |
1428 | static int do_help(int argc, char **argv) |
1429 | { |
1430 | if (json_output) { |
1431 | jsonw_null(self: json_wtr); |
1432 | return 0; |
1433 | } |
1434 | |
1435 | fprintf(stderr, |
1436 | "Usage: %1$s %2$s { show | list } [MAP]\n" |
1437 | " %1$s %2$s create FILE type TYPE key KEY_SIZE value VALUE_SIZE \\\n" |
1438 | " entries MAX_ENTRIES name NAME [flags FLAGS] \\\n" |
1439 | " [inner_map MAP] [offload_dev NAME]\n" |
1440 | " %1$s %2$s dump MAP\n" |
1441 | " %1$s %2$s update MAP [key DATA] [value VALUE] [UPDATE_FLAGS]\n" |
1442 | " %1$s %2$s lookup MAP [key DATA]\n" |
1443 | " %1$s %2$s getnext MAP [key DATA]\n" |
1444 | " %1$s %2$s delete MAP key DATA\n" |
1445 | " %1$s %2$s pin MAP FILE\n" |
1446 | " %1$s %2$s event_pipe MAP [cpu N index M]\n" |
1447 | " %1$s %2$s peek MAP\n" |
1448 | " %1$s %2$s push MAP value VALUE\n" |
1449 | " %1$s %2$s pop MAP\n" |
1450 | " %1$s %2$s enqueue MAP value VALUE\n" |
1451 | " %1$s %2$s dequeue MAP\n" |
1452 | " %1$s %2$s freeze MAP\n" |
1453 | " %1$s %2$s help\n" |
1454 | "\n" |
1455 | " " HELP_SPEC_MAP "\n" |
1456 | " DATA := { [hex] BYTES }\n" |
1457 | " " HELP_SPEC_PROGRAM "\n" |
1458 | " VALUE := { DATA | MAP | PROG }\n" |
1459 | " UPDATE_FLAGS := { any | exist | noexist }\n" |
1460 | " TYPE := { hash | array | prog_array | perf_event_array | percpu_hash |\n" |
1461 | " percpu_array | stack_trace | cgroup_array | lru_hash |\n" |
1462 | " lru_percpu_hash | lpm_trie | array_of_maps | hash_of_maps |\n" |
1463 | " devmap | devmap_hash | sockmap | cpumap | xskmap | sockhash |\n" |
1464 | " cgroup_storage | reuseport_sockarray | percpu_cgroup_storage |\n" |
1465 | " queue | stack | sk_storage | struct_ops | ringbuf | inode_storage |\n" |
1466 | " task_storage | bloom_filter | user_ringbuf | cgrp_storage }\n" |
1467 | " " HELP_SPEC_OPTIONS " |\n" |
1468 | " {-f|--bpffs} | {-n|--nomount} }\n" |
1469 | "" , |
1470 | bin_name, argv[-2]); |
1471 | |
1472 | return 0; |
1473 | } |
1474 | |
1475 | static const struct cmd cmds[] = { |
1476 | { "show" , do_show }, |
1477 | { "list" , do_show }, |
1478 | { "help" , do_help }, |
1479 | { "dump" , do_dump }, |
1480 | { "update" , do_update }, |
1481 | { "lookup" , do_lookup }, |
1482 | { "getnext" , do_getnext }, |
1483 | { "delete" , do_delete }, |
1484 | { "pin" , do_pin }, |
1485 | { "event_pipe" , do_event_pipe }, |
1486 | { "create" , do_create }, |
1487 | { "peek" , do_lookup }, |
1488 | { "push" , do_update }, |
1489 | { "enqueue" , do_update }, |
1490 | { "pop" , do_pop_dequeue }, |
1491 | { "dequeue" , do_pop_dequeue }, |
1492 | { "freeze" , do_freeze }, |
1493 | { 0 } |
1494 | }; |
1495 | |
1496 | int do_map(int argc, char **argv) |
1497 | { |
1498 | return cmd_select(cmds, argc, argv, help: do_help); |
1499 | } |
1500 | |