1500 lines · c
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 24static struct hashmap *map_table;25 26static 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 34static 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 40static bool map_is_map_of_progs(__u32 type)41{42 return type == BPF_MAP_TYPE_PROG_ARRAY;43}44 45static 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 62static void *alloc_value(struct bpf_map_info *info)63{64 if (map_is_per_cpu(info->type))65 return malloc(round_up(info->value_size, 8) *66 get_possible_cpus());67 else68 return malloc(info->value_size);69}70 71static 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(d->jw);80 81 if (map_info->btf_key_type_id) {82 jsonw_name(d->jw, "key");83 84 ret = btf_dumper_type(d, map_info->btf_key_type_id, 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(map_info->type)) {93 jsonw_name(d->jw, "value");94 ret = btf_dumper_type(d, value_id, value);95 } else {96 unsigned int i, n, step;97 98 jsonw_name(d->jw, "values");99 jsonw_start_array(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(d->jw);104 jsonw_int_field(d->jw, "cpu", i);105 jsonw_name(d->jw, "value");106 ret = btf_dumper_type(d, value_id, value + i * step);107 jsonw_end_object(d->jw);108 if (ret)109 break;110 }111 jsonw_end_array(d->jw);112 }113 114err_end_obj:115 /* end of key-value pair */116 jsonw_end_object(d->jw);117 118 return ret;119}120 121static 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(jw, true);128 129 return jw;130}131 132static void print_entry_json(struct bpf_map_info *info, unsigned char *key,133 unsigned char *value, struct btf *btf)134{135 jsonw_start_object(json_wtr);136 137 if (!map_is_per_cpu(info->type)) {138 jsonw_name(json_wtr, "key");139 print_hex_data_json(key, info->key_size);140 jsonw_name(json_wtr, "value");141 print_hex_data_json(value, info->value_size);142 if (map_is_map_of_maps(info->type))143 jsonw_uint_field(json_wtr, "inner_map_id",144 *(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(json_wtr, "formatted");153 do_dump_btf(&d, 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(json_wtr, "key");162 print_hex_data_json(key, info->key_size);163 164 jsonw_name(json_wtr, "values");165 jsonw_start_array(json_wtr);166 for (i = 0; i < n; i++) {167 jsonw_start_object(json_wtr);168 169 jsonw_int_field(json_wtr, "cpu", i);170 171 jsonw_name(json_wtr, "value");172 print_hex_data_json(value + i * step,173 info->value_size);174 175 jsonw_end_object(json_wtr);176 }177 jsonw_end_array(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(json_wtr, "formatted");186 do_dump_btf(&d, info, key, value);187 }188 }189 190 jsonw_end_object(json_wtr);191}192 193static void194print_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 213static void214print_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 value217 * means there is no entry for that key. Do not print an error message218 * in that case.219 */220 if ((map_is_map_of_maps(map_info->type) ||221 map_is_map_of_progs(map_info->type)) && lookup_errno == ENOENT)222 return;223 224 if (json_output) {225 jsonw_start_object(json_wtr); /* entry */226 jsonw_name(json_wtr, "key");227 print_hex_data_json(key, map_info->key_size);228 jsonw_name(json_wtr, "value");229 jsonw_start_object(json_wtr); /* error */230 jsonw_string_field(json_wtr, "error", strerror(lookup_errno));231 jsonw_end_object(json_wtr); /* error */232 jsonw_end_object(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(map_info, key,243 msg ? : strerror(lookup_errno));244 }245}246 247static void print_entry_plain(struct bpf_map_info *info, unsigned char *key,248 unsigned char *value)249{250 if (!map_is_per_cpu(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(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 298static 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(*argv, "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("error parsing byte: %s", argv[i]);313 return NULL;314 }315 i++;316 }317 318 if (i != n) {319 p_err("%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 */327static 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(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 340static 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("did not find %s", key ? "key" : "value");348 return -1;349 }350 351 if (is_prefix(*argv, "key")) {352 if (!key) {353 if (key_size)354 p_err("duplicate key");355 else356 p_err("unnecessary key");357 return -1;358 }359 360 argv = parse_bytes(argv + 1, "key", key, 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(*argv, "value")) {367 int fd;368 369 if (!value) {370 if (value_size)371 p_err("duplicate value");372 else373 p_err("unnecessary value");374 return -1;375 }376 377 argv++;378 379 if (map_is_map_of_maps(info->type)) {380 int argc = 2;381 382 if (value_size != 4) {383 p_err("value smaller than 4B for map in map?");384 return -1;385 }386 if (!argv[0] || !argv[1]) {387 p_err("not enough value arguments for map in map");388 return -1;389 }390 391 fd = map_parse_fd(&argc, &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(info->type)) {398 int argc = 2;399 400 if (value_size != 4) {401 p_err("value smaller than 4B for map of progs?");402 return -1;403 }404 if (!argv[0] || !argv[1]) {405 p_err("not enough value arguments for map of progs");406 return -1;407 }408 if (is_prefix(*argv, "id"))409 p_info("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, &argv);413 if (fd < 0)414 return -1;415 416 *value_fd = value;417 **value_fd = fd;418 } else {419 argv = parse_bytes(argv, "value", value, 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(*argv, "any") || is_prefix(*argv, "noexist") ||429 is_prefix(*argv, "exist")) {430 if (!flags) {431 p_err("flags specified multiple times: %s", *argv);432 return -1;433 }434 435 if (is_prefix(*argv, "any"))436 *flags = BPF_ANY;437 else if (is_prefix(*argv, "noexist"))438 *flags = BPF_NOEXIST;439 else if (is_prefix(*argv, "exist"))440 *flags = BPF_EXIST;441 442 return parse_elem(argv + 1, info, key, value, key_size,443 value_size, NULL, value_fd);444 }445 446 p_err("expected key or value, got: %s", *argv);447 return -1;448}449 450static void show_map_header_json(struct bpf_map_info *info, json_writer_t *wtr)451{452 const char *map_type_str;453 454 jsonw_uint_field(wtr, "id", info->id);455 map_type_str = libbpf_bpf_map_type_str(info->type);456 if (map_type_str)457 jsonw_string_field(wtr, "type", map_type_str);458 else459 jsonw_uint_field(wtr, "type", info->type);460 461 if (*info->name)462 jsonw_string_field(wtr, "name", info->name);463 464 jsonw_name(wtr, "flags");465 jsonw_printf(wtr, "%d", info->map_flags);466}467 468static 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, "memlock");474 frozen_str = get_fdinfo(fd, "frozen");475 476 jsonw_start_object(json_wtr);477 478 show_map_header_json(info, json_wtr);479 480 print_dev_json(info->ifindex, info->netns_dev, info->netns_ino);481 482 jsonw_uint_field(json_wtr, "bytes_key", info->key_size);483 jsonw_uint_field(json_wtr, "bytes_value", info->value_size);484 jsonw_uint_field(json_wtr, "max_entries", info->max_entries);485 486 if (memlock)487 jsonw_int_field(json_wtr, "bytes_memlock", atoll(memlock));488 free(memlock);489 490 if (info->type == BPF_MAP_TYPE_PROG_ARRAY) {491 char *owner_prog_type = get_fdinfo(fd, "owner_prog_type");492 char *owner_jited = get_fdinfo(fd, "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(json_wtr, "owner_prog_type",501 prog_type_str);502 else503 jsonw_uint_field(json_wtr, "owner_prog_type",504 prog_type);505 }506 if (owner_jited)507 jsonw_bool_field(json_wtr, "owner_jited",508 !!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(json_wtr, "frozen", frozen);520 521 if (info->btf_id)522 jsonw_int_field(json_wtr, "btf_id", info->btf_id);523 524 if (!hashmap__empty(map_table)) {525 struct hashmap_entry *entry;526 527 jsonw_name(json_wtr, "pinned");528 jsonw_start_array(json_wtr);529 hashmap__for_each_key_entry(map_table, entry, info->id)530 jsonw_string(json_wtr, entry->pvalue);531 jsonw_end_array(json_wtr);532 }533 534 emit_obj_refs_json(refs_table, info->id, json_wtr);535 536 jsonw_end_object(json_wtr);537 538 return 0;539}540 541static void show_map_header_plain(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 else551 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(info->ifindex, info->netns_dev, info->netns_ino);558 printf("\n");559}560 561static 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, "memlock");567 frozen_str = get_fdinfo(fd, "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, "owner_prog_type");579 char *owner_jited = get_fdinfo(fd, "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 else591 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_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(refs_table, info->id, "\n\tpids ");624 625 printf("\n");626 return 0;627}628 629static 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("mem alloc failed");640 return -1;641 }642 nb_fds = map_parse_fds(&argc, &argv, &fds);643 if (nb_fds < 1)644 goto exit_free;645 646 if (json_output && nb_fds > 1)647 jsonw_start_array(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("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(fds[i], &info);660 else661 show_map_close_plain(fds[i], &info);662 663 close(fds[i]);664 }665 if (json_output && nb_fds > 1)666 jsonw_end_array(json_wtr); /* root array */667 668exit_free:669 free(fds);670 return err;671}672 673static 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(map_table)) {685 p_err("failed to create hashmap for pinned paths");686 return -1;687 }688 build_pinned_obj_table(map_table, BPF_OBJ_MAP);689 }690 build_obj_refs_table(&refs_table, 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(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("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("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("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);728 else729 show_map_close_plain(fd, &info);730 }731 if (json_output)732 jsonw_end_array(json_wtr);733 734 delete_obj_refs_table(refs_table);735 736 if (show_pinned)737 delete_pinned_obj_table(map_table);738 739 return errno == ENOENT ? 0 : -1;740}741 742static 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, errno);748 return -1;749 }750 751 if (json_output) {752 print_entry_json(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, map_info, key, value);761 } else {762 print_entry_plain(map_info, key, value);763 }764 765 return 0;766}767 768static 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("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 788static struct btf *btf_vmlinux;789 790static 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("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("failed to get btf");808 }809 } else {810 *btf = NULL;811 }812 813 return err;814}815 816static void free_map_kv_btf(struct btf *btf)817{818 if (btf != btf_vmlinux)819 btf__free(btf);820}821 822static int823map_dump(int fd, struct bpf_map_info *info, json_writer_t *wtr,824 bool show_header)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("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);843 if (err) {844 goto exit_free;845 }846 847 if (show_header) {848 jsonw_start_object(wtr); /* map object */849 show_map_header_json(info, wtr);850 jsonw_name(wtr, "elements");851 }852 jsonw_start_array(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("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, info, btf, wtr))873 num_elems++;874 prev_key = key;875 }876 877 if (wtr) {878 jsonw_end_array(wtr); /* elements */879 if (show_header)880 jsonw_end_object(wtr); /* map object */881 } else {882 printf("Found %u element%s\n", num_elems,883 num_elems != 1 ? "s" : "");884 }885 886exit_free:887 free(key);888 free(value);889 close(fd);890 free_map_kv_btf(btf);891 892 return err;893}894 895static 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("mem alloc failed");910 return -1;911 }912 nb_fds = map_parse_fds(&argc, &argv, &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("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(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("can't get map info: %s", strerror(errno));938 break;939 }940 err = map_dump(fds[i], &info, wtr, 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(wtr); /* root array */950 951 if (btf_wtr)952 jsonw_destroy(&btf_wtr);953exit_close:954 for (; i < nb_fds; i++)955 close(fds[i]);956exit_free:957 free(fds);958 btf__free(btf_vmlinux);959 return err;960}961 962static 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("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("value mem alloc failed");979 free(*key);980 *key = NULL;981 return -1;982 }983 }984 985 return 0;986}987 988static 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, &argv, &info, &len);1001 if (fd < 0)1002 return -1;1003 1004 err = alloc_key_value(&info, &key, &value);1005 if (err)1006 goto exit_free;1007 1008 err = parse_elem(argv, &info, key, value, info.key_size,1009 info.value_size, &flags, &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("update failed: %s", strerror(errno));1016 goto exit_free;1017 }1018 1019exit_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(json_wtr);1028 return err;1029}1030 1031static 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))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 btf1045 */1046 btf_wtr = get_btf_writer();1047 if (!btf_wtr) {1048 p_info("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, info, key, value);1060 jsonw_destroy(&btf_wtr);1061 }1062 } else {1063 print_entry_plain(info, key, value);1064 }1065 btf__free(btf);1066}1067 1068static 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, &argv, &info, &len);1080 if (fd < 0)1081 return -1;1082 1083 err = alloc_key_value(&info, &key, &value);1084 if (err)1085 goto exit_free;1086 1087 err = parse_elem(argv, &info, key, NULL, info.key_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(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("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, key, value);1110 1111exit_free:1112 free(key);1113 free(value);1114 close(fd);1115 1116 return err;1117}1118 1119static 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, &argv, &info, &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("mem alloc failed");1138 err = -1;1139 goto exit_free;1140 }1141 1142 if (argc) {1143 err = parse_elem(argv, &info, key, NULL, info.key_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("can't get next key: %s", strerror(errno));1155 goto exit_free;1156 }1157 1158 if (json_output) {1159 jsonw_start_object(json_wtr);1160 if (key) {1161 jsonw_name(json_wtr, "key");1162 print_hex_data_json(key, info.key_size);1163 } else {1164 jsonw_null_field(json_wtr, "key");1165 }1166 jsonw_name(json_wtr, "next_key");1167 print_hex_data_json(nextkey, info.key_size);1168 jsonw_end_object(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 1182exit_free:1183 free(nextkey);1184 free(key);1185 close(fd);1186 1187 return err;1188}1189 1190static 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, &argv, &info, &len);1202 if (fd < 0)1203 return -1;1204 1205 key = malloc(info.key_size);1206 if (!key) {1207 p_err("mem alloc failed");1208 err = -1;1209 goto exit_free;1210 }1211 1212 err = parse_elem(argv, &info, key, NULL, info.key_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 1220exit_free:1221 free(key);1222 close(fd);1223 1224 if (!err && json_output)1225 jsonw_null(json_wtr);1226 return err;1227}1228 1229static int do_pin(int argc, char **argv)1230{1231 int err;1232 1233 err = do_pin_any(argc, argv, map_parse_fd);1234 if (!err && json_output)1235 jsonw_null(json_wtr);1236 return err;1237}1238 1239static 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(*argv, "type")) {1257 NEXT_ARG();1258 1259 if (map_type) {1260 p_err("map type already specified");1261 goto exit;1262 }1263 1264 map_type = map_type_from_str(*argv);1265 if ((int)map_type < 0) {1266 p_err("unrecognized map type: %s", *argv);1267 goto exit;1268 }1269 NEXT_ARG();1270 } else if (is_prefix(*argv, "name")) {1271 NEXT_ARG();1272 map_name = GET_ARG();1273 } else if (is_prefix(*argv, "key")) {1274 if (parse_u32_arg(&argc, &argv, &key_size,1275 "key size"))1276 goto exit;1277 } else if (is_prefix(*argv, "value")) {1278 if (parse_u32_arg(&argc, &argv, &value_size,1279 "value size"))1280 goto exit;1281 } else if (is_prefix(*argv, "entries")) {1282 if (parse_u32_arg(&argc, &argv, &max_entries,1283 "max entries"))1284 goto exit;1285 } else if (is_prefix(*argv, "flags")) {1286 if (parse_u32_arg(&argc, &argv, &attr.map_flags,1287 "flags"))1288 goto exit;1289 } else if (is_prefix(*argv, "dev")) {1290 p_info("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(*argv, "offload_dev")) {1294offload_dev:1295 NEXT_ARG();1296 1297 if (attr.map_ifindex) {1298 p_err("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(*argv, "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, &argv,1318 &info, &len);1319 if (inner_map_fd < 0)1320 return -1;1321 attr.inner_map_fd = inner_map_fd;1322 } else {1323 p_err("unknown arg %s", *argv);1324 goto exit;1325 }1326 }1327 1328 if (!map_name) {1329 p_err("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, pinfile);1342 close(fd);1343 if (err)1344 goto exit;1345 1346 if (json_output)1347 jsonw_null(json_wtr);1348 1349exit:1350 if (attr.inner_map_fd > 0)1351 close(attr.inner_map_fd);1352 1353 return err;1354}1355 1356static 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, &argv, &info, &len);1368 if (fd < 0)1369 return -1;1370 1371 err = alloc_key_value(&info, &key, &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(json_wtr);1380 else1381 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, key, value);1390 1391exit_free:1392 free(key);1393 free(value);1394 close(fd);1395 1396 return err;1397}1398 1399static 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, &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(json_wtr);1424 1425 return 0;1426}1427 1428static int do_help(int argc, char **argv)1429{1430 if (json_output) {1431 jsonw_null(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 | arena }\n"1467 " " HELP_SPEC_OPTIONS " |\n"1468 " {-f|--bpffs} | {-n|--nomount} }\n"1469 "",1470 bin_name, argv[-2]);1471 1472 return 0;1473}1474 1475static 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 1496int do_map(int argc, char **argv)1497{1498 return cmd_select(cmds, argc, argv, do_help);1499}1500