Commit | Line | Data |
---|---|---|
71bb428f | 1 | /* |
c642ea26 | 2 | * Copyright (C) 2017-2018 Netronome Systems, Inc. |
71bb428f JK |
3 | * |
4 | * This software is dual licensed under the GNU General License Version 2, | |
5 | * June 1991 as shown in the file COPYING in the top-level directory of this | |
6 | * source tree or the BSD 2-Clause License provided below. You have the | |
7 | * option to license this software under the complete terms of either license. | |
8 | * | |
9 | * The BSD 2-Clause License: | |
10 | * | |
11 | * Redistribution and use in source and binary forms, with or | |
12 | * without modification, are permitted provided that the following | |
13 | * conditions are met: | |
14 | * | |
15 | * 1. Redistributions of source code must retain the above | |
16 | * copyright notice, this list of conditions and the following | |
17 | * disclaimer. | |
18 | * | |
19 | * 2. Redistributions in binary form must reproduce the above | |
20 | * copyright notice, this list of conditions and the following | |
21 | * disclaimer in the documentation and/or other materials | |
22 | * provided with the distribution. | |
23 | * | |
24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | |
28 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |
29 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
30 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
31 | * SOFTWARE. | |
32 | */ | |
33 | ||
34 | /* Author: Jakub Kicinski <kubakici@wp.pl> */ | |
35 | ||
36 | #include <assert.h> | |
71bb428f JK |
37 | #include <errno.h> |
38 | #include <fcntl.h> | |
573b3aa6 | 39 | #include <linux/kernel.h> |
71bb428f JK |
40 | #include <stdbool.h> |
41 | #include <stdio.h> | |
42 | #include <stdlib.h> | |
43 | #include <string.h> | |
44 | #include <unistd.h> | |
45 | #include <sys/types.h> | |
46 | #include <sys/stat.h> | |
47 | ||
48 | #include <bpf.h> | |
49 | ||
50 | #include "main.h" | |
51 | ||
52 | static const char * const map_type_name[] = { | |
53 | [BPF_MAP_TYPE_UNSPEC] = "unspec", | |
54 | [BPF_MAP_TYPE_HASH] = "hash", | |
55 | [BPF_MAP_TYPE_ARRAY] = "array", | |
56 | [BPF_MAP_TYPE_PROG_ARRAY] = "prog_array", | |
57 | [BPF_MAP_TYPE_PERF_EVENT_ARRAY] = "perf_event_array", | |
58 | [BPF_MAP_TYPE_PERCPU_HASH] = "percpu_hash", | |
59 | [BPF_MAP_TYPE_PERCPU_ARRAY] = "percpu_array", | |
60 | [BPF_MAP_TYPE_STACK_TRACE] = "stack_trace", | |
61 | [BPF_MAP_TYPE_CGROUP_ARRAY] = "cgroup_array", | |
62 | [BPF_MAP_TYPE_LRU_HASH] = "lru_hash", | |
63 | [BPF_MAP_TYPE_LRU_PERCPU_HASH] = "lru_percpu_hash", | |
64 | [BPF_MAP_TYPE_LPM_TRIE] = "lpm_trie", | |
65 | [BPF_MAP_TYPE_ARRAY_OF_MAPS] = "array_of_maps", | |
66 | [BPF_MAP_TYPE_HASH_OF_MAPS] = "hash_of_maps", | |
67 | [BPF_MAP_TYPE_DEVMAP] = "devmap", | |
68 | [BPF_MAP_TYPE_SOCKMAP] = "sockmap", | |
a55aaf6d | 69 | [BPF_MAP_TYPE_CPUMAP] = "cpumap", |
62c52d1f | 70 | [BPF_MAP_TYPE_SOCKHASH] = "sockhash", |
71bb428f JK |
71 | }; |
72 | ||
71bb428f JK |
73 | static bool map_is_per_cpu(__u32 type) |
74 | { | |
75 | return type == BPF_MAP_TYPE_PERCPU_HASH || | |
76 | type == BPF_MAP_TYPE_PERCPU_ARRAY || | |
77 | type == BPF_MAP_TYPE_LRU_PERCPU_HASH; | |
78 | } | |
79 | ||
80 | static bool map_is_map_of_maps(__u32 type) | |
81 | { | |
82 | return type == BPF_MAP_TYPE_ARRAY_OF_MAPS || | |
83 | type == BPF_MAP_TYPE_HASH_OF_MAPS; | |
84 | } | |
85 | ||
86 | static bool map_is_map_of_progs(__u32 type) | |
87 | { | |
88 | return type == BPF_MAP_TYPE_PROG_ARRAY; | |
89 | } | |
90 | ||
91 | static void *alloc_value(struct bpf_map_info *info) | |
92 | { | |
93 | if (map_is_per_cpu(info->type)) | |
573b3aa6 YS |
94 | return malloc(round_up(info->value_size, 8) * |
95 | get_possible_cpus()); | |
71bb428f JK |
96 | else |
97 | return malloc(info->value_size); | |
98 | } | |
99 | ||
100 | static int map_parse_fd(int *argc, char ***argv) | |
101 | { | |
102 | int fd; | |
103 | ||
104 | if (is_prefix(**argv, "id")) { | |
105 | unsigned int id; | |
106 | char *endptr; | |
107 | ||
108 | NEXT_ARGP(); | |
109 | ||
110 | id = strtoul(**argv, &endptr, 0); | |
111 | if (*endptr) { | |
9a5ab8bf | 112 | p_err("can't parse %s as ID", **argv); |
71bb428f JK |
113 | return -1; |
114 | } | |
115 | NEXT_ARGP(); | |
116 | ||
117 | fd = bpf_map_get_fd_by_id(id); | |
118 | if (fd < 0) | |
9a5ab8bf | 119 | p_err("get map by id (%u): %s", id, strerror(errno)); |
71bb428f JK |
120 | return fd; |
121 | } else if (is_prefix(**argv, "pinned")) { | |
122 | char *path; | |
123 | ||
124 | NEXT_ARGP(); | |
125 | ||
126 | path = **argv; | |
127 | NEXT_ARGP(); | |
128 | ||
129 | return open_obj_pinned_any(path, BPF_OBJ_MAP); | |
130 | } | |
131 | ||
9a5ab8bf | 132 | p_err("expected 'id' or 'pinned', got: '%s'?", **argv); |
71bb428f JK |
133 | return -1; |
134 | } | |
135 | ||
f412eed9 | 136 | int map_parse_fd_and_info(int *argc, char ***argv, void *info, __u32 *info_len) |
71bb428f JK |
137 | { |
138 | int err; | |
139 | int fd; | |
140 | ||
141 | fd = map_parse_fd(argc, argv); | |
142 | if (fd < 0) | |
143 | return -1; | |
144 | ||
145 | err = bpf_obj_get_info_by_fd(fd, info, info_len); | |
146 | if (err) { | |
9a5ab8bf | 147 | p_err("can't get map info: %s", strerror(errno)); |
71bb428f JK |
148 | close(fd); |
149 | return err; | |
150 | } | |
151 | ||
152 | return fd; | |
153 | } | |
154 | ||
831a0aaf QM |
155 | static void print_entry_json(struct bpf_map_info *info, unsigned char *key, |
156 | unsigned char *value) | |
157 | { | |
158 | jsonw_start_object(json_wtr); | |
159 | ||
160 | if (!map_is_per_cpu(info->type)) { | |
161 | jsonw_name(json_wtr, "key"); | |
162 | print_hex_data_json(key, info->key_size); | |
163 | jsonw_name(json_wtr, "value"); | |
164 | print_hex_data_json(value, info->value_size); | |
165 | } else { | |
573b3aa6 | 166 | unsigned int i, n, step; |
831a0aaf QM |
167 | |
168 | n = get_possible_cpus(); | |
573b3aa6 | 169 | step = round_up(info->value_size, 8); |
831a0aaf QM |
170 | |
171 | jsonw_name(json_wtr, "key"); | |
172 | print_hex_data_json(key, info->key_size); | |
173 | ||
174 | jsonw_name(json_wtr, "values"); | |
175 | jsonw_start_array(json_wtr); | |
176 | for (i = 0; i < n; i++) { | |
177 | jsonw_start_object(json_wtr); | |
178 | ||
179 | jsonw_int_field(json_wtr, "cpu", i); | |
180 | ||
181 | jsonw_name(json_wtr, "value"); | |
573b3aa6 | 182 | print_hex_data_json(value + i * step, |
831a0aaf QM |
183 | info->value_size); |
184 | ||
185 | jsonw_end_object(json_wtr); | |
186 | } | |
187 | jsonw_end_array(json_wtr); | |
188 | } | |
189 | ||
190 | jsonw_end_object(json_wtr); | |
191 | } | |
192 | ||
193 | static void print_entry_plain(struct bpf_map_info *info, unsigned char *key, | |
194 | unsigned char *value) | |
71bb428f JK |
195 | { |
196 | if (!map_is_per_cpu(info->type)) { | |
197 | bool single_line, break_names; | |
198 | ||
199 | break_names = info->key_size > 16 || info->value_size > 16; | |
200 | single_line = info->key_size + info->value_size <= 24 && | |
201 | !break_names; | |
202 | ||
203 | printf("key:%c", break_names ? '\n' : ' '); | |
9cbe1f58 | 204 | fprint_hex(stdout, key, info->key_size, " "); |
71bb428f JK |
205 | |
206 | printf(single_line ? " " : "\n"); | |
207 | ||
208 | printf("value:%c", break_names ? '\n' : ' '); | |
9cbe1f58 | 209 | fprint_hex(stdout, value, info->value_size, " "); |
71bb428f JK |
210 | |
211 | printf("\n"); | |
212 | } else { | |
573b3aa6 | 213 | unsigned int i, n, step; |
71bb428f JK |
214 | |
215 | n = get_possible_cpus(); | |
573b3aa6 | 216 | step = round_up(info->value_size, 8); |
71bb428f JK |
217 | |
218 | printf("key:\n"); | |
9cbe1f58 | 219 | fprint_hex(stdout, key, info->key_size, " "); |
71bb428f JK |
220 | printf("\n"); |
221 | for (i = 0; i < n; i++) { | |
222 | printf("value (CPU %02d):%c", | |
223 | i, info->value_size > 16 ? '\n' : ' '); | |
573b3aa6 | 224 | fprint_hex(stdout, value + i * step, |
9cbe1f58 | 225 | info->value_size, " "); |
71bb428f JK |
226 | printf("\n"); |
227 | } | |
228 | } | |
229 | } | |
230 | ||
231 | static char **parse_bytes(char **argv, const char *name, unsigned char *val, | |
232 | unsigned int n) | |
233 | { | |
0c90f224 | 234 | unsigned int i = 0, base = 0; |
71bb428f JK |
235 | char *endptr; |
236 | ||
0c90f224 QM |
237 | if (is_prefix(*argv, "hex")) { |
238 | base = 16; | |
239 | argv++; | |
240 | } | |
241 | ||
71bb428f | 242 | while (i < n && argv[i]) { |
0c90f224 | 243 | val[i] = strtoul(argv[i], &endptr, base); |
71bb428f | 244 | if (*endptr) { |
9a5ab8bf | 245 | p_err("error parsing byte: %s", argv[i]); |
d9c0b48d | 246 | return NULL; |
71bb428f JK |
247 | } |
248 | i++; | |
249 | } | |
250 | ||
251 | if (i != n) { | |
9a5ab8bf | 252 | p_err("%s expected %d bytes got %d", name, n, i); |
71bb428f JK |
253 | return NULL; |
254 | } | |
255 | ||
256 | return argv + i; | |
257 | } | |
258 | ||
259 | static int parse_elem(char **argv, struct bpf_map_info *info, | |
260 | void *key, void *value, __u32 key_size, __u32 value_size, | |
261 | __u32 *flags, __u32 **value_fd) | |
262 | { | |
263 | if (!*argv) { | |
264 | if (!key && !value) | |
265 | return 0; | |
9a5ab8bf | 266 | p_err("did not find %s", key ? "key" : "value"); |
71bb428f JK |
267 | return -1; |
268 | } | |
269 | ||
270 | if (is_prefix(*argv, "key")) { | |
271 | if (!key) { | |
272 | if (key_size) | |
9a5ab8bf | 273 | p_err("duplicate key"); |
71bb428f | 274 | else |
9a5ab8bf | 275 | p_err("unnecessary key"); |
71bb428f JK |
276 | return -1; |
277 | } | |
278 | ||
279 | argv = parse_bytes(argv + 1, "key", key, key_size); | |
280 | if (!argv) | |
281 | return -1; | |
282 | ||
283 | return parse_elem(argv, info, NULL, value, key_size, value_size, | |
284 | flags, value_fd); | |
285 | } else if (is_prefix(*argv, "value")) { | |
286 | int fd; | |
287 | ||
288 | if (!value) { | |
289 | if (value_size) | |
9a5ab8bf | 290 | p_err("duplicate value"); |
71bb428f | 291 | else |
9a5ab8bf | 292 | p_err("unnecessary value"); |
71bb428f JK |
293 | return -1; |
294 | } | |
295 | ||
296 | argv++; | |
297 | ||
298 | if (map_is_map_of_maps(info->type)) { | |
299 | int argc = 2; | |
300 | ||
301 | if (value_size != 4) { | |
9a5ab8bf | 302 | p_err("value smaller than 4B for map in map?"); |
71bb428f JK |
303 | return -1; |
304 | } | |
305 | if (!argv[0] || !argv[1]) { | |
9a5ab8bf | 306 | p_err("not enough value arguments for map in map"); |
71bb428f JK |
307 | return -1; |
308 | } | |
309 | ||
310 | fd = map_parse_fd(&argc, &argv); | |
311 | if (fd < 0) | |
312 | return -1; | |
313 | ||
314 | *value_fd = value; | |
315 | **value_fd = fd; | |
316 | } else if (map_is_map_of_progs(info->type)) { | |
317 | int argc = 2; | |
318 | ||
319 | if (value_size != 4) { | |
9a5ab8bf | 320 | p_err("value smaller than 4B for map of progs?"); |
71bb428f JK |
321 | return -1; |
322 | } | |
323 | if (!argv[0] || !argv[1]) { | |
9a5ab8bf | 324 | p_err("not enough value arguments for map of progs"); |
71bb428f JK |
325 | return -1; |
326 | } | |
327 | ||
328 | fd = prog_parse_fd(&argc, &argv); | |
329 | if (fd < 0) | |
330 | return -1; | |
331 | ||
332 | *value_fd = value; | |
333 | **value_fd = fd; | |
334 | } else { | |
335 | argv = parse_bytes(argv, "value", value, value_size); | |
336 | if (!argv) | |
337 | return -1; | |
338 | } | |
339 | ||
340 | return parse_elem(argv, info, key, NULL, key_size, value_size, | |
341 | flags, NULL); | |
342 | } else if (is_prefix(*argv, "any") || is_prefix(*argv, "noexist") || | |
343 | is_prefix(*argv, "exist")) { | |
344 | if (!flags) { | |
9a5ab8bf | 345 | p_err("flags specified multiple times: %s", *argv); |
71bb428f JK |
346 | return -1; |
347 | } | |
348 | ||
349 | if (is_prefix(*argv, "any")) | |
350 | *flags = BPF_ANY; | |
351 | else if (is_prefix(*argv, "noexist")) | |
352 | *flags = BPF_NOEXIST; | |
353 | else if (is_prefix(*argv, "exist")) | |
354 | *flags = BPF_EXIST; | |
355 | ||
356 | return parse_elem(argv + 1, info, key, value, key_size, | |
357 | value_size, NULL, value_fd); | |
358 | } | |
359 | ||
9a5ab8bf | 360 | p_err("expected key or value, got: %s", *argv); |
71bb428f JK |
361 | return -1; |
362 | } | |
363 | ||
831a0aaf QM |
364 | static int show_map_close_json(int fd, struct bpf_map_info *info) |
365 | { | |
366 | char *memlock; | |
367 | ||
368 | memlock = get_fdinfo(fd, "memlock"); | |
369 | close(fd); | |
370 | ||
371 | jsonw_start_object(json_wtr); | |
372 | ||
373 | jsonw_uint_field(json_wtr, "id", info->id); | |
374 | if (info->type < ARRAY_SIZE(map_type_name)) | |
375 | jsonw_string_field(json_wtr, "type", | |
376 | map_type_name[info->type]); | |
377 | else | |
378 | jsonw_uint_field(json_wtr, "type", info->type); | |
379 | ||
380 | if (*info->name) | |
381 | jsonw_string_field(json_wtr, "name", info->name); | |
382 | ||
383 | jsonw_name(json_wtr, "flags"); | |
4b6eca9d | 384 | jsonw_printf(json_wtr, "%d", info->map_flags); |
064a07cb JK |
385 | |
386 | print_dev_json(info->ifindex, info->netns_dev, info->netns_ino); | |
387 | ||
831a0aaf QM |
388 | jsonw_uint_field(json_wtr, "bytes_key", info->key_size); |
389 | jsonw_uint_field(json_wtr, "bytes_value", info->value_size); | |
390 | jsonw_uint_field(json_wtr, "max_entries", info->max_entries); | |
391 | ||
392 | if (memlock) | |
393 | jsonw_int_field(json_wtr, "bytes_memlock", atoi(memlock)); | |
394 | free(memlock); | |
395 | ||
4990f1f4 PB |
396 | if (!hash_empty(map_table.table)) { |
397 | struct pinned_obj *obj; | |
398 | ||
399 | jsonw_name(json_wtr, "pinned"); | |
400 | jsonw_start_array(json_wtr); | |
401 | hash_for_each_possible(map_table.table, obj, hash, info->id) { | |
402 | if (obj->id == info->id) | |
403 | jsonw_string(json_wtr, obj->path); | |
404 | } | |
405 | jsonw_end_array(json_wtr); | |
406 | } | |
407 | ||
831a0aaf QM |
408 | jsonw_end_object(json_wtr); |
409 | ||
410 | return 0; | |
411 | } | |
412 | ||
413 | static int show_map_close_plain(int fd, struct bpf_map_info *info) | |
71bb428f JK |
414 | { |
415 | char *memlock; | |
416 | ||
417 | memlock = get_fdinfo(fd, "memlock"); | |
418 | close(fd); | |
419 | ||
420 | printf("%u: ", info->id); | |
421 | if (info->type < ARRAY_SIZE(map_type_name)) | |
422 | printf("%s ", map_type_name[info->type]); | |
423 | else | |
424 | printf("type %u ", info->type); | |
425 | ||
426 | if (*info->name) | |
427 | printf("name %s ", info->name); | |
428 | ||
064a07cb JK |
429 | printf("flags 0x%x", info->map_flags); |
430 | print_dev_plain(info->ifindex, info->netns_dev, info->netns_ino); | |
431 | printf("\n"); | |
71bb428f JK |
432 | printf("\tkey %uB value %uB max_entries %u", |
433 | info->key_size, info->value_size, info->max_entries); | |
434 | ||
435 | if (memlock) | |
436 | printf(" memlock %sB", memlock); | |
437 | free(memlock); | |
438 | ||
439 | printf("\n"); | |
4990f1f4 PB |
440 | if (!hash_empty(map_table.table)) { |
441 | struct pinned_obj *obj; | |
71bb428f | 442 | |
4990f1f4 PB |
443 | hash_for_each_possible(map_table.table, obj, hash, info->id) { |
444 | if (obj->id == info->id) | |
445 | printf("\tpinned %s\n", obj->path); | |
446 | } | |
447 | } | |
71bb428f JK |
448 | return 0; |
449 | } | |
450 | ||
451 | static int do_show(int argc, char **argv) | |
452 | { | |
453 | struct bpf_map_info info = {}; | |
454 | __u32 len = sizeof(info); | |
455 | __u32 id = 0; | |
456 | int err; | |
457 | int fd; | |
458 | ||
c541b734 PB |
459 | if (show_pinned) |
460 | build_pinned_obj_table(&map_table, BPF_OBJ_MAP); | |
4990f1f4 | 461 | |
71bb428f JK |
462 | if (argc == 2) { |
463 | fd = map_parse_fd_and_info(&argc, &argv, &info, &len); | |
464 | if (fd < 0) | |
465 | return -1; | |
466 | ||
831a0aaf QM |
467 | if (json_output) |
468 | return show_map_close_json(fd, &info); | |
469 | else | |
470 | return show_map_close_plain(fd, &info); | |
71bb428f JK |
471 | } |
472 | ||
473 | if (argc) | |
474 | return BAD_ARG(); | |
475 | ||
831a0aaf QM |
476 | if (json_output) |
477 | jsonw_start_array(json_wtr); | |
71bb428f JK |
478 | while (true) { |
479 | err = bpf_map_get_next_id(id, &id); | |
480 | if (err) { | |
481 | if (errno == ENOENT) | |
482 | break; | |
9a5ab8bf QM |
483 | p_err("can't get next map: %s%s", strerror(errno), |
484 | errno == EINVAL ? " -- kernel too old?" : ""); | |
b3b1b653 | 485 | break; |
71bb428f JK |
486 | } |
487 | ||
488 | fd = bpf_map_get_fd_by_id(id); | |
489 | if (fd < 0) { | |
8207c6dd JK |
490 | if (errno == ENOENT) |
491 | continue; | |
9a5ab8bf QM |
492 | p_err("can't get map by id (%u): %s", |
493 | id, strerror(errno)); | |
b3b1b653 | 494 | break; |
71bb428f JK |
495 | } |
496 | ||
497 | err = bpf_obj_get_info_by_fd(fd, &info, &len); | |
498 | if (err) { | |
9a5ab8bf | 499 | p_err("can't get map info: %s", strerror(errno)); |
71bb428f | 500 | close(fd); |
b3b1b653 | 501 | break; |
71bb428f JK |
502 | } |
503 | ||
831a0aaf QM |
504 | if (json_output) |
505 | show_map_close_json(fd, &info); | |
506 | else | |
507 | show_map_close_plain(fd, &info); | |
71bb428f | 508 | } |
831a0aaf QM |
509 | if (json_output) |
510 | jsonw_end_array(json_wtr); | |
71bb428f JK |
511 | |
512 | return errno == ENOENT ? 0 : -1; | |
513 | } | |
514 | ||
515 | static int do_dump(int argc, char **argv) | |
516 | { | |
517 | void *key, *value, *prev_key; | |
518 | unsigned int num_elems = 0; | |
519 | struct bpf_map_info info = {}; | |
520 | __u32 len = sizeof(info); | |
521 | int err; | |
522 | int fd; | |
523 | ||
524 | if (argc != 2) | |
525 | usage(); | |
526 | ||
527 | fd = map_parse_fd_and_info(&argc, &argv, &info, &len); | |
528 | if (fd < 0) | |
529 | return -1; | |
530 | ||
531 | if (map_is_map_of_maps(info.type) || map_is_map_of_progs(info.type)) { | |
9a5ab8bf | 532 | p_err("Dumping maps of maps and program maps not supported"); |
71bb428f JK |
533 | close(fd); |
534 | return -1; | |
535 | } | |
536 | ||
537 | key = malloc(info.key_size); | |
538 | value = alloc_value(&info); | |
539 | if (!key || !value) { | |
9a5ab8bf | 540 | p_err("mem alloc failed"); |
71bb428f JK |
541 | err = -1; |
542 | goto exit_free; | |
543 | } | |
544 | ||
545 | prev_key = NULL; | |
831a0aaf QM |
546 | if (json_output) |
547 | jsonw_start_array(json_wtr); | |
71bb428f JK |
548 | while (true) { |
549 | err = bpf_map_get_next_key(fd, prev_key, key); | |
550 | if (err) { | |
551 | if (errno == ENOENT) | |
552 | err = 0; | |
553 | break; | |
554 | } | |
555 | ||
556 | if (!bpf_map_lookup_elem(fd, key, value)) { | |
831a0aaf QM |
557 | if (json_output) |
558 | print_entry_json(&info, key, value); | |
559 | else | |
560 | print_entry_plain(&info, key, value); | |
71bb428f | 561 | } else { |
9a5ab8bf QM |
562 | if (json_output) { |
563 | jsonw_name(json_wtr, "key"); | |
564 | print_hex_data_json(key, info.key_size); | |
565 | jsonw_name(json_wtr, "value"); | |
566 | jsonw_start_object(json_wtr); | |
567 | jsonw_string_field(json_wtr, "error", | |
568 | "can't lookup element"); | |
569 | jsonw_end_object(json_wtr); | |
570 | } else { | |
571 | p_info("can't lookup element with key: "); | |
572 | fprint_hex(stderr, key, info.key_size, " "); | |
573 | fprintf(stderr, "\n"); | |
574 | } | |
71bb428f JK |
575 | } |
576 | ||
577 | prev_key = key; | |
578 | num_elems++; | |
579 | } | |
580 | ||
831a0aaf QM |
581 | if (json_output) |
582 | jsonw_end_array(json_wtr); | |
583 | else | |
584 | printf("Found %u element%s\n", num_elems, | |
585 | num_elems != 1 ? "s" : ""); | |
71bb428f JK |
586 | |
587 | exit_free: | |
588 | free(key); | |
589 | free(value); | |
590 | close(fd); | |
591 | ||
592 | return err; | |
593 | } | |
594 | ||
595 | static int do_update(int argc, char **argv) | |
596 | { | |
597 | struct bpf_map_info info = {}; | |
598 | __u32 len = sizeof(info); | |
599 | __u32 *value_fd = NULL; | |
600 | __u32 flags = BPF_ANY; | |
601 | void *key, *value; | |
602 | int fd, err; | |
603 | ||
604 | if (argc < 2) | |
605 | usage(); | |
606 | ||
607 | fd = map_parse_fd_and_info(&argc, &argv, &info, &len); | |
608 | if (fd < 0) | |
609 | return -1; | |
610 | ||
611 | key = malloc(info.key_size); | |
612 | value = alloc_value(&info); | |
613 | if (!key || !value) { | |
9a5ab8bf | 614 | p_err("mem alloc failed"); |
71bb428f JK |
615 | err = -1; |
616 | goto exit_free; | |
617 | } | |
618 | ||
619 | err = parse_elem(argv, &info, key, value, info.key_size, | |
620 | info.value_size, &flags, &value_fd); | |
621 | if (err) | |
622 | goto exit_free; | |
623 | ||
624 | err = bpf_map_update_elem(fd, key, value, flags); | |
625 | if (err) { | |
9a5ab8bf | 626 | p_err("update failed: %s", strerror(errno)); |
71bb428f JK |
627 | goto exit_free; |
628 | } | |
629 | ||
630 | exit_free: | |
631 | if (value_fd) | |
632 | close(*value_fd); | |
633 | free(key); | |
634 | free(value); | |
635 | close(fd); | |
636 | ||
004b45c0 QM |
637 | if (!err && json_output) |
638 | jsonw_null(json_wtr); | |
71bb428f JK |
639 | return err; |
640 | } | |
641 | ||
642 | static int do_lookup(int argc, char **argv) | |
643 | { | |
644 | struct bpf_map_info info = {}; | |
645 | __u32 len = sizeof(info); | |
646 | void *key, *value; | |
647 | int err; | |
648 | int fd; | |
649 | ||
650 | if (argc < 2) | |
651 | usage(); | |
652 | ||
653 | fd = map_parse_fd_and_info(&argc, &argv, &info, &len); | |
654 | if (fd < 0) | |
655 | return -1; | |
656 | ||
657 | key = malloc(info.key_size); | |
658 | value = alloc_value(&info); | |
659 | if (!key || !value) { | |
9a5ab8bf | 660 | p_err("mem alloc failed"); |
71bb428f JK |
661 | err = -1; |
662 | goto exit_free; | |
663 | } | |
664 | ||
665 | err = parse_elem(argv, &info, key, NULL, info.key_size, 0, NULL, NULL); | |
666 | if (err) | |
667 | goto exit_free; | |
668 | ||
669 | err = bpf_map_lookup_elem(fd, key, value); | |
670 | if (!err) { | |
831a0aaf QM |
671 | if (json_output) |
672 | print_entry_json(&info, key, value); | |
673 | else | |
674 | print_entry_plain(&info, key, value); | |
71bb428f | 675 | } else if (errno == ENOENT) { |
831a0aaf QM |
676 | if (json_output) { |
677 | jsonw_null(json_wtr); | |
678 | } else { | |
679 | printf("key:\n"); | |
680 | fprint_hex(stdout, key, info.key_size, " "); | |
681 | printf("\n\nNot found\n"); | |
682 | } | |
71bb428f | 683 | } else { |
9a5ab8bf | 684 | p_err("lookup failed: %s", strerror(errno)); |
71bb428f JK |
685 | } |
686 | ||
687 | exit_free: | |
688 | free(key); | |
689 | free(value); | |
690 | close(fd); | |
691 | ||
692 | return err; | |
693 | } | |
694 | ||
695 | static int do_getnext(int argc, char **argv) | |
696 | { | |
697 | struct bpf_map_info info = {}; | |
698 | __u32 len = sizeof(info); | |
699 | void *key, *nextkey; | |
700 | int err; | |
701 | int fd; | |
702 | ||
703 | if (argc < 2) | |
704 | usage(); | |
705 | ||
706 | fd = map_parse_fd_and_info(&argc, &argv, &info, &len); | |
707 | if (fd < 0) | |
708 | return -1; | |
709 | ||
710 | key = malloc(info.key_size); | |
711 | nextkey = malloc(info.key_size); | |
712 | if (!key || !nextkey) { | |
9a5ab8bf | 713 | p_err("mem alloc failed"); |
71bb428f JK |
714 | err = -1; |
715 | goto exit_free; | |
716 | } | |
717 | ||
718 | if (argc) { | |
719 | err = parse_elem(argv, &info, key, NULL, info.key_size, 0, | |
720 | NULL, NULL); | |
721 | if (err) | |
722 | goto exit_free; | |
723 | } else { | |
724 | free(key); | |
725 | key = NULL; | |
726 | } | |
727 | ||
728 | err = bpf_map_get_next_key(fd, key, nextkey); | |
729 | if (err) { | |
9a5ab8bf | 730 | p_err("can't get next key: %s", strerror(errno)); |
71bb428f JK |
731 | goto exit_free; |
732 | } | |
733 | ||
831a0aaf QM |
734 | if (json_output) { |
735 | jsonw_start_object(json_wtr); | |
736 | if (key) { | |
737 | jsonw_name(json_wtr, "key"); | |
738 | print_hex_data_json(key, info.key_size); | |
739 | } else { | |
740 | jsonw_null_field(json_wtr, "key"); | |
741 | } | |
742 | jsonw_name(json_wtr, "next_key"); | |
743 | print_hex_data_json(nextkey, info.key_size); | |
744 | jsonw_end_object(json_wtr); | |
71bb428f | 745 | } else { |
831a0aaf QM |
746 | if (key) { |
747 | printf("key:\n"); | |
748 | fprint_hex(stdout, key, info.key_size, " "); | |
749 | printf("\n"); | |
750 | } else { | |
751 | printf("key: None\n"); | |
752 | } | |
753 | printf("next key:\n"); | |
754 | fprint_hex(stdout, nextkey, info.key_size, " "); | |
755 | printf("\n"); | |
71bb428f JK |
756 | } |
757 | ||
71bb428f JK |
758 | exit_free: |
759 | free(nextkey); | |
760 | free(key); | |
761 | close(fd); | |
762 | ||
763 | return err; | |
764 | } | |
765 | ||
766 | static int do_delete(int argc, char **argv) | |
767 | { | |
768 | struct bpf_map_info info = {}; | |
769 | __u32 len = sizeof(info); | |
770 | void *key; | |
771 | int err; | |
772 | int fd; | |
773 | ||
774 | if (argc < 2) | |
775 | usage(); | |
776 | ||
777 | fd = map_parse_fd_and_info(&argc, &argv, &info, &len); | |
778 | if (fd < 0) | |
779 | return -1; | |
780 | ||
781 | key = malloc(info.key_size); | |
782 | if (!key) { | |
9a5ab8bf | 783 | p_err("mem alloc failed"); |
71bb428f JK |
784 | err = -1; |
785 | goto exit_free; | |
786 | } | |
787 | ||
788 | err = parse_elem(argv, &info, key, NULL, info.key_size, 0, NULL, NULL); | |
789 | if (err) | |
790 | goto exit_free; | |
791 | ||
792 | err = bpf_map_delete_elem(fd, key); | |
793 | if (err) | |
9a5ab8bf | 794 | p_err("delete failed: %s", strerror(errno)); |
71bb428f JK |
795 | |
796 | exit_free: | |
797 | free(key); | |
798 | close(fd); | |
799 | ||
004b45c0 QM |
800 | if (!err && json_output) |
801 | jsonw_null(json_wtr); | |
71bb428f JK |
802 | return err; |
803 | } | |
804 | ||
805 | static int do_pin(int argc, char **argv) | |
806 | { | |
004b45c0 QM |
807 | int err; |
808 | ||
809 | err = do_pin_any(argc, argv, bpf_map_get_fd_by_id); | |
810 | if (!err && json_output) | |
811 | jsonw_null(json_wtr); | |
812 | return err; | |
71bb428f JK |
813 | } |
814 | ||
815 | static int do_help(int argc, char **argv) | |
816 | { | |
004b45c0 QM |
817 | if (json_output) { |
818 | jsonw_null(json_wtr); | |
819 | return 0; | |
820 | } | |
821 | ||
71bb428f | 822 | fprintf(stderr, |
6ebe6dbd | 823 | "Usage: %s %s { show | list } [MAP]\n" |
f412eed9 JK |
824 | " %s %s dump MAP\n" |
825 | " %s %s update MAP key DATA value VALUE [UPDATE_FLAGS]\n" | |
826 | " %s %s lookup MAP key DATA\n" | |
827 | " %s %s getnext MAP [key DATA]\n" | |
828 | " %s %s delete MAP key DATA\n" | |
829 | " %s %s pin MAP FILE\n" | |
830 | " %s %s event_pipe MAP [cpu N index M]\n" | |
71bb428f JK |
831 | " %s %s help\n" |
832 | "\n" | |
833 | " MAP := { id MAP_ID | pinned FILE }\n" | |
c642ea26 | 834 | " DATA := { [hex] BYTES }\n" |
71bb428f | 835 | " " HELP_SPEC_PROGRAM "\n" |
c642ea26 | 836 | " VALUE := { DATA | MAP | PROG }\n" |
71bb428f | 837 | " UPDATE_FLAGS := { any | exist | noexist }\n" |
0641c3c8 | 838 | " " HELP_SPEC_OPTIONS "\n" |
71bb428f JK |
839 | "", |
840 | bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], | |
841 | bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], | |
f412eed9 | 842 | bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2]); |
71bb428f JK |
843 | |
844 | return 0; | |
845 | } | |
846 | ||
847 | static const struct cmd cmds[] = { | |
848 | { "show", do_show }, | |
6ebe6dbd | 849 | { "list", do_show }, |
71bb428f JK |
850 | { "help", do_help }, |
851 | { "dump", do_dump }, | |
852 | { "update", do_update }, | |
853 | { "lookup", do_lookup }, | |
854 | { "getnext", do_getnext }, | |
855 | { "delete", do_delete }, | |
856 | { "pin", do_pin }, | |
f412eed9 | 857 | { "event_pipe", do_event_pipe }, |
71bb428f JK |
858 | { 0 } |
859 | }; | |
860 | ||
861 | int do_map(int argc, char **argv) | |
862 | { | |
863 | return cmd_select(cmds, argc, argv, do_help); | |
864 | } |