1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2 /* Copyright (C) 2020 Facebook */
3
4 #include <errno.h>
5 #include <linux/err.h>
6 #include <netinet/in.h>
7 #include <linux/netfilter.h>
8 #include <linux/netfilter_arp.h>
9 #include <linux/perf_event.h>
10 #include <net/if.h>
11 #include <stdio.h>
12 #include <unistd.h>
13
14 #include <bpf/bpf.h>
15 #include <bpf/hashmap.h>
16
17 #include "json_writer.h"
18 #include "main.h"
19 #include "xlated_dumper.h"
20
21 #define PERF_HW_CACHE_LEN 128
22
23 static struct hashmap *link_table;
24 static struct dump_data dd;
25
26 static const char *perf_type_name[PERF_TYPE_MAX] = {
27 [PERF_TYPE_HARDWARE] = "hardware",
28 [PERF_TYPE_SOFTWARE] = "software",
29 [PERF_TYPE_TRACEPOINT] = "tracepoint",
30 [PERF_TYPE_HW_CACHE] = "hw-cache",
31 [PERF_TYPE_RAW] = "raw",
32 [PERF_TYPE_BREAKPOINT] = "breakpoint",
33 };
34
35 const char *event_symbols_hw[PERF_COUNT_HW_MAX] = {
36 [PERF_COUNT_HW_CPU_CYCLES] = "cpu-cycles",
37 [PERF_COUNT_HW_INSTRUCTIONS] = "instructions",
38 [PERF_COUNT_HW_CACHE_REFERENCES] = "cache-references",
39 [PERF_COUNT_HW_CACHE_MISSES] = "cache-misses",
40 [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = "branch-instructions",
41 [PERF_COUNT_HW_BRANCH_MISSES] = "branch-misses",
42 [PERF_COUNT_HW_BUS_CYCLES] = "bus-cycles",
43 [PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = "stalled-cycles-frontend",
44 [PERF_COUNT_HW_STALLED_CYCLES_BACKEND] = "stalled-cycles-backend",
45 [PERF_COUNT_HW_REF_CPU_CYCLES] = "ref-cycles",
46 };
47
48 const char *event_symbols_sw[PERF_COUNT_SW_MAX] = {
49 [PERF_COUNT_SW_CPU_CLOCK] = "cpu-clock",
50 [PERF_COUNT_SW_TASK_CLOCK] = "task-clock",
51 [PERF_COUNT_SW_PAGE_FAULTS] = "page-faults",
52 [PERF_COUNT_SW_CONTEXT_SWITCHES] = "context-switches",
53 [PERF_COUNT_SW_CPU_MIGRATIONS] = "cpu-migrations",
54 [PERF_COUNT_SW_PAGE_FAULTS_MIN] = "minor-faults",
55 [PERF_COUNT_SW_PAGE_FAULTS_MAJ] = "major-faults",
56 [PERF_COUNT_SW_ALIGNMENT_FAULTS] = "alignment-faults",
57 [PERF_COUNT_SW_EMULATION_FAULTS] = "emulation-faults",
58 [PERF_COUNT_SW_DUMMY] = "dummy",
59 [PERF_COUNT_SW_BPF_OUTPUT] = "bpf-output",
60 [PERF_COUNT_SW_CGROUP_SWITCHES] = "cgroup-switches",
61 };
62
63 const char *evsel__hw_cache[PERF_COUNT_HW_CACHE_MAX] = {
64 [PERF_COUNT_HW_CACHE_L1D] = "L1-dcache",
65 [PERF_COUNT_HW_CACHE_L1I] = "L1-icache",
66 [PERF_COUNT_HW_CACHE_LL] = "LLC",
67 [PERF_COUNT_HW_CACHE_DTLB] = "dTLB",
68 [PERF_COUNT_HW_CACHE_ITLB] = "iTLB",
69 [PERF_COUNT_HW_CACHE_BPU] = "branch",
70 [PERF_COUNT_HW_CACHE_NODE] = "node",
71 };
72
73 const char *evsel__hw_cache_op[PERF_COUNT_HW_CACHE_OP_MAX] = {
74 [PERF_COUNT_HW_CACHE_OP_READ] = "load",
75 [PERF_COUNT_HW_CACHE_OP_WRITE] = "store",
76 [PERF_COUNT_HW_CACHE_OP_PREFETCH] = "prefetch",
77 };
78
79 const char *evsel__hw_cache_result[PERF_COUNT_HW_CACHE_RESULT_MAX] = {
80 [PERF_COUNT_HW_CACHE_RESULT_ACCESS] = "refs",
81 [PERF_COUNT_HW_CACHE_RESULT_MISS] = "misses",
82 };
83
84 #define perf_event_name(array, id) ({ \
85 const char *event_str = NULL; \
86 \
87 if ((id) < ARRAY_SIZE(array)) \
88 event_str = array[id]; \
89 event_str; \
90 })
91
link_parse_fd(int * argc,char *** argv)92 static int link_parse_fd(int *argc, char ***argv)
93 {
94 int fd;
95
96 if (is_prefix(**argv, "id")) {
97 unsigned int id;
98 char *endptr;
99
100 NEXT_ARGP();
101
102 id = strtoul(**argv, &endptr, 0);
103 if (*endptr) {
104 p_err("can't parse %s as ID", **argv);
105 return -1;
106 }
107 NEXT_ARGP();
108
109 fd = bpf_link_get_fd_by_id(id);
110 if (fd < 0)
111 p_err("failed to get link with ID %d: %s", id, strerror(errno));
112 return fd;
113 } else if (is_prefix(**argv, "pinned")) {
114 char *path;
115
116 NEXT_ARGP();
117
118 path = **argv;
119 NEXT_ARGP();
120
121 return open_obj_pinned_any(path, BPF_OBJ_LINK);
122 }
123
124 p_err("expected 'id' or 'pinned', got: '%s'?", **argv);
125 return -1;
126 }
127
128 static void
show_link_header_json(struct bpf_link_info * info,json_writer_t * wtr)129 show_link_header_json(struct bpf_link_info *info, json_writer_t *wtr)
130 {
131 const char *link_type_str;
132
133 jsonw_uint_field(wtr, "id", info->id);
134 link_type_str = libbpf_bpf_link_type_str(info->type);
135 if (link_type_str)
136 jsonw_string_field(wtr, "type", link_type_str);
137 else
138 jsonw_uint_field(wtr, "type", info->type);
139
140 jsonw_uint_field(json_wtr, "prog_id", info->prog_id);
141 }
142
show_link_attach_type_json(__u32 attach_type,json_writer_t * wtr)143 static void show_link_attach_type_json(__u32 attach_type, json_writer_t *wtr)
144 {
145 const char *attach_type_str;
146
147 attach_type_str = libbpf_bpf_attach_type_str(attach_type);
148 if (attach_type_str)
149 jsonw_string_field(wtr, "attach_type", attach_type_str);
150 else
151 jsonw_uint_field(wtr, "attach_type", attach_type);
152 }
153
show_link_ifindex_json(__u32 ifindex,json_writer_t * wtr)154 static void show_link_ifindex_json(__u32 ifindex, json_writer_t *wtr)
155 {
156 char devname[IF_NAMESIZE] = "(unknown)";
157
158 if (ifindex)
159 if_indextoname(ifindex, devname);
160 else
161 snprintf(devname, sizeof(devname), "(detached)");
162 jsonw_string_field(wtr, "devname", devname);
163 jsonw_uint_field(wtr, "ifindex", ifindex);
164 }
165
is_iter_map_target(const char * target_name)166 static bool is_iter_map_target(const char *target_name)
167 {
168 return strcmp(target_name, "bpf_map_elem") == 0 ||
169 strcmp(target_name, "bpf_sk_storage_map") == 0;
170 }
171
is_iter_cgroup_target(const char * target_name)172 static bool is_iter_cgroup_target(const char *target_name)
173 {
174 return strcmp(target_name, "cgroup") == 0;
175 }
176
cgroup_order_string(__u32 order)177 static const char *cgroup_order_string(__u32 order)
178 {
179 switch (order) {
180 case BPF_CGROUP_ITER_ORDER_UNSPEC:
181 return "order_unspec";
182 case BPF_CGROUP_ITER_SELF_ONLY:
183 return "self_only";
184 case BPF_CGROUP_ITER_DESCENDANTS_PRE:
185 return "descendants_pre";
186 case BPF_CGROUP_ITER_DESCENDANTS_POST:
187 return "descendants_post";
188 case BPF_CGROUP_ITER_ANCESTORS_UP:
189 return "ancestors_up";
190 default: /* won't happen */
191 return "unknown";
192 }
193 }
194
is_iter_task_target(const char * target_name)195 static bool is_iter_task_target(const char *target_name)
196 {
197 return strcmp(target_name, "task") == 0 ||
198 strcmp(target_name, "task_file") == 0 ||
199 strcmp(target_name, "task_vma") == 0;
200 }
201
show_iter_json(struct bpf_link_info * info,json_writer_t * wtr)202 static void show_iter_json(struct bpf_link_info *info, json_writer_t *wtr)
203 {
204 const char *target_name = u64_to_ptr(info->iter.target_name);
205
206 jsonw_string_field(wtr, "target_name", target_name);
207
208 if (is_iter_map_target(target_name))
209 jsonw_uint_field(wtr, "map_id", info->iter.map.map_id);
210 else if (is_iter_task_target(target_name)) {
211 if (info->iter.task.tid)
212 jsonw_uint_field(wtr, "tid", info->iter.task.tid);
213 else if (info->iter.task.pid)
214 jsonw_uint_field(wtr, "pid", info->iter.task.pid);
215 }
216
217 if (is_iter_cgroup_target(target_name)) {
218 jsonw_lluint_field(wtr, "cgroup_id", info->iter.cgroup.cgroup_id);
219 jsonw_string_field(wtr, "order",
220 cgroup_order_string(info->iter.cgroup.order));
221 }
222 }
223
netfilter_dump_json(const struct bpf_link_info * info,json_writer_t * wtr)224 void netfilter_dump_json(const struct bpf_link_info *info, json_writer_t *wtr)
225 {
226 jsonw_uint_field(json_wtr, "pf",
227 info->netfilter.pf);
228 jsonw_uint_field(json_wtr, "hook",
229 info->netfilter.hooknum);
230 jsonw_int_field(json_wtr, "prio",
231 info->netfilter.priority);
232 jsonw_uint_field(json_wtr, "flags",
233 info->netfilter.flags);
234 }
235
get_prog_info(int prog_id,struct bpf_prog_info * info)236 static int get_prog_info(int prog_id, struct bpf_prog_info *info)
237 {
238 __u32 len = sizeof(*info);
239 int err, prog_fd;
240
241 prog_fd = bpf_prog_get_fd_by_id(prog_id);
242 if (prog_fd < 0)
243 return prog_fd;
244
245 memset(info, 0, sizeof(*info));
246 err = bpf_prog_get_info_by_fd(prog_fd, info, &len);
247 if (err)
248 p_err("can't get prog info: %s", strerror(errno));
249 close(prog_fd);
250 return err;
251 }
252
cmp_u64(const void * A,const void * B)253 static int cmp_u64(const void *A, const void *B)
254 {
255 const __u64 *a = A, *b = B;
256
257 return *a - *b;
258 }
259
260 static void
show_kprobe_multi_json(struct bpf_link_info * info,json_writer_t * wtr)261 show_kprobe_multi_json(struct bpf_link_info *info, json_writer_t *wtr)
262 {
263 __u32 i, j = 0;
264 __u64 *addrs;
265
266 jsonw_bool_field(json_wtr, "retprobe",
267 info->kprobe_multi.flags & BPF_F_KPROBE_MULTI_RETURN);
268 jsonw_uint_field(json_wtr, "func_cnt", info->kprobe_multi.count);
269 jsonw_uint_field(json_wtr, "missed", info->kprobe_multi.missed);
270 jsonw_name(json_wtr, "funcs");
271 jsonw_start_array(json_wtr);
272 addrs = u64_to_ptr(info->kprobe_multi.addrs);
273 qsort(addrs, info->kprobe_multi.count, sizeof(addrs[0]), cmp_u64);
274
275 /* Load it once for all. */
276 if (!dd.sym_count)
277 kernel_syms_load(&dd);
278 for (i = 0; i < dd.sym_count; i++) {
279 if (dd.sym_mapping[i].address != addrs[j])
280 continue;
281 jsonw_start_object(json_wtr);
282 jsonw_uint_field(json_wtr, "addr", dd.sym_mapping[i].address);
283 jsonw_string_field(json_wtr, "func", dd.sym_mapping[i].name);
284 /* Print null if it is vmlinux */
285 if (dd.sym_mapping[i].module[0] == '\0') {
286 jsonw_name(json_wtr, "module");
287 jsonw_null(json_wtr);
288 } else {
289 jsonw_string_field(json_wtr, "module", dd.sym_mapping[i].module);
290 }
291 jsonw_end_object(json_wtr);
292 if (j++ == info->kprobe_multi.count)
293 break;
294 }
295 jsonw_end_array(json_wtr);
296 }
297
298 static void
show_perf_event_kprobe_json(struct bpf_link_info * info,json_writer_t * wtr)299 show_perf_event_kprobe_json(struct bpf_link_info *info, json_writer_t *wtr)
300 {
301 jsonw_bool_field(wtr, "retprobe", info->perf_event.type == BPF_PERF_EVENT_KRETPROBE);
302 jsonw_uint_field(wtr, "addr", info->perf_event.kprobe.addr);
303 jsonw_string_field(wtr, "func",
304 u64_to_ptr(info->perf_event.kprobe.func_name));
305 jsonw_uint_field(wtr, "offset", info->perf_event.kprobe.offset);
306 jsonw_uint_field(wtr, "missed", info->perf_event.kprobe.missed);
307 }
308
309 static void
show_perf_event_uprobe_json(struct bpf_link_info * info,json_writer_t * wtr)310 show_perf_event_uprobe_json(struct bpf_link_info *info, json_writer_t *wtr)
311 {
312 jsonw_bool_field(wtr, "retprobe", info->perf_event.type == BPF_PERF_EVENT_URETPROBE);
313 jsonw_string_field(wtr, "file",
314 u64_to_ptr(info->perf_event.uprobe.file_name));
315 jsonw_uint_field(wtr, "offset", info->perf_event.uprobe.offset);
316 }
317
318 static void
show_perf_event_tracepoint_json(struct bpf_link_info * info,json_writer_t * wtr)319 show_perf_event_tracepoint_json(struct bpf_link_info *info, json_writer_t *wtr)
320 {
321 jsonw_string_field(wtr, "tracepoint",
322 u64_to_ptr(info->perf_event.tracepoint.tp_name));
323 }
324
perf_config_hw_cache_str(__u64 config)325 static char *perf_config_hw_cache_str(__u64 config)
326 {
327 const char *hw_cache, *result, *op;
328 char *str = malloc(PERF_HW_CACHE_LEN);
329
330 if (!str) {
331 p_err("mem alloc failed");
332 return NULL;
333 }
334
335 hw_cache = perf_event_name(evsel__hw_cache, config & 0xff);
336 if (hw_cache)
337 snprintf(str, PERF_HW_CACHE_LEN, "%s-", hw_cache);
338 else
339 snprintf(str, PERF_HW_CACHE_LEN, "%lld-", config & 0xff);
340
341 op = perf_event_name(evsel__hw_cache_op, (config >> 8) & 0xff);
342 if (op)
343 snprintf(str + strlen(str), PERF_HW_CACHE_LEN - strlen(str),
344 "%s-", op);
345 else
346 snprintf(str + strlen(str), PERF_HW_CACHE_LEN - strlen(str),
347 "%lld-", (config >> 8) & 0xff);
348
349 result = perf_event_name(evsel__hw_cache_result, config >> 16);
350 if (result)
351 snprintf(str + strlen(str), PERF_HW_CACHE_LEN - strlen(str),
352 "%s", result);
353 else
354 snprintf(str + strlen(str), PERF_HW_CACHE_LEN - strlen(str),
355 "%lld", config >> 16);
356 return str;
357 }
358
perf_config_str(__u32 type,__u64 config)359 static const char *perf_config_str(__u32 type, __u64 config)
360 {
361 const char *perf_config;
362
363 switch (type) {
364 case PERF_TYPE_HARDWARE:
365 perf_config = perf_event_name(event_symbols_hw, config);
366 break;
367 case PERF_TYPE_SOFTWARE:
368 perf_config = perf_event_name(event_symbols_sw, config);
369 break;
370 case PERF_TYPE_HW_CACHE:
371 perf_config = perf_config_hw_cache_str(config);
372 break;
373 default:
374 perf_config = NULL;
375 break;
376 }
377 return perf_config;
378 }
379
380 static void
show_perf_event_event_json(struct bpf_link_info * info,json_writer_t * wtr)381 show_perf_event_event_json(struct bpf_link_info *info, json_writer_t *wtr)
382 {
383 __u64 config = info->perf_event.event.config;
384 __u32 type = info->perf_event.event.type;
385 const char *perf_type, *perf_config;
386
387 perf_type = perf_event_name(perf_type_name, type);
388 if (perf_type)
389 jsonw_string_field(wtr, "event_type", perf_type);
390 else
391 jsonw_uint_field(wtr, "event_type", type);
392
393 perf_config = perf_config_str(type, config);
394 if (perf_config)
395 jsonw_string_field(wtr, "event_config", perf_config);
396 else
397 jsonw_uint_field(wtr, "event_config", config);
398
399 if (type == PERF_TYPE_HW_CACHE && perf_config)
400 free((void *)perf_config);
401 }
402
show_link_close_json(int fd,struct bpf_link_info * info)403 static int show_link_close_json(int fd, struct bpf_link_info *info)
404 {
405 struct bpf_prog_info prog_info;
406 const char *prog_type_str;
407 int err;
408
409 jsonw_start_object(json_wtr);
410
411 show_link_header_json(info, json_wtr);
412
413 switch (info->type) {
414 case BPF_LINK_TYPE_RAW_TRACEPOINT:
415 jsonw_string_field(json_wtr, "tp_name",
416 u64_to_ptr(info->raw_tracepoint.tp_name));
417 break;
418 case BPF_LINK_TYPE_TRACING:
419 err = get_prog_info(info->prog_id, &prog_info);
420 if (err)
421 return err;
422
423 prog_type_str = libbpf_bpf_prog_type_str(prog_info.type);
424 /* libbpf will return NULL for variants unknown to it. */
425 if (prog_type_str)
426 jsonw_string_field(json_wtr, "prog_type", prog_type_str);
427 else
428 jsonw_uint_field(json_wtr, "prog_type", prog_info.type);
429
430 show_link_attach_type_json(info->tracing.attach_type,
431 json_wtr);
432 jsonw_uint_field(json_wtr, "target_obj_id", info->tracing.target_obj_id);
433 jsonw_uint_field(json_wtr, "target_btf_id", info->tracing.target_btf_id);
434 break;
435 case BPF_LINK_TYPE_CGROUP:
436 jsonw_lluint_field(json_wtr, "cgroup_id",
437 info->cgroup.cgroup_id);
438 show_link_attach_type_json(info->cgroup.attach_type, json_wtr);
439 break;
440 case BPF_LINK_TYPE_ITER:
441 show_iter_json(info, json_wtr);
442 break;
443 case BPF_LINK_TYPE_NETNS:
444 jsonw_uint_field(json_wtr, "netns_ino",
445 info->netns.netns_ino);
446 show_link_attach_type_json(info->netns.attach_type, json_wtr);
447 break;
448 case BPF_LINK_TYPE_NETFILTER:
449 netfilter_dump_json(info, json_wtr);
450 break;
451 case BPF_LINK_TYPE_TCX:
452 show_link_ifindex_json(info->tcx.ifindex, json_wtr);
453 show_link_attach_type_json(info->tcx.attach_type, json_wtr);
454 break;
455 case BPF_LINK_TYPE_XDP:
456 show_link_ifindex_json(info->xdp.ifindex, json_wtr);
457 break;
458 case BPF_LINK_TYPE_STRUCT_OPS:
459 jsonw_uint_field(json_wtr, "map_id",
460 info->struct_ops.map_id);
461 break;
462 case BPF_LINK_TYPE_KPROBE_MULTI:
463 show_kprobe_multi_json(info, json_wtr);
464 break;
465 case BPF_LINK_TYPE_PERF_EVENT:
466 switch (info->perf_event.type) {
467 case BPF_PERF_EVENT_EVENT:
468 show_perf_event_event_json(info, json_wtr);
469 break;
470 case BPF_PERF_EVENT_TRACEPOINT:
471 show_perf_event_tracepoint_json(info, json_wtr);
472 break;
473 case BPF_PERF_EVENT_KPROBE:
474 case BPF_PERF_EVENT_KRETPROBE:
475 show_perf_event_kprobe_json(info, json_wtr);
476 break;
477 case BPF_PERF_EVENT_UPROBE:
478 case BPF_PERF_EVENT_URETPROBE:
479 show_perf_event_uprobe_json(info, json_wtr);
480 break;
481 default:
482 break;
483 }
484 break;
485 default:
486 break;
487 }
488
489 if (!hashmap__empty(link_table)) {
490 struct hashmap_entry *entry;
491
492 jsonw_name(json_wtr, "pinned");
493 jsonw_start_array(json_wtr);
494 hashmap__for_each_key_entry(link_table, entry, info->id)
495 jsonw_string(json_wtr, entry->pvalue);
496 jsonw_end_array(json_wtr);
497 }
498
499 emit_obj_refs_json(refs_table, info->id, json_wtr);
500
501 jsonw_end_object(json_wtr);
502
503 return 0;
504 }
505
show_link_header_plain(struct bpf_link_info * info)506 static void show_link_header_plain(struct bpf_link_info *info)
507 {
508 const char *link_type_str;
509
510 printf("%u: ", info->id);
511 link_type_str = libbpf_bpf_link_type_str(info->type);
512 if (link_type_str)
513 printf("%s ", link_type_str);
514 else
515 printf("type %u ", info->type);
516
517 if (info->type == BPF_LINK_TYPE_STRUCT_OPS)
518 printf("map %u ", info->struct_ops.map_id);
519 else
520 printf("prog %u ", info->prog_id);
521 }
522
show_link_attach_type_plain(__u32 attach_type)523 static void show_link_attach_type_plain(__u32 attach_type)
524 {
525 const char *attach_type_str;
526
527 attach_type_str = libbpf_bpf_attach_type_str(attach_type);
528 if (attach_type_str)
529 printf("attach_type %s ", attach_type_str);
530 else
531 printf("attach_type %u ", attach_type);
532 }
533
show_link_ifindex_plain(__u32 ifindex)534 static void show_link_ifindex_plain(__u32 ifindex)
535 {
536 char devname[IF_NAMESIZE * 2] = "(unknown)";
537 char tmpname[IF_NAMESIZE];
538 char *ret = NULL;
539
540 if (ifindex)
541 ret = if_indextoname(ifindex, tmpname);
542 else
543 snprintf(devname, sizeof(devname), "(detached)");
544 if (ret)
545 snprintf(devname, sizeof(devname), "%s(%d)",
546 tmpname, ifindex);
547 printf("ifindex %s ", devname);
548 }
549
show_iter_plain(struct bpf_link_info * info)550 static void show_iter_plain(struct bpf_link_info *info)
551 {
552 const char *target_name = u64_to_ptr(info->iter.target_name);
553
554 printf("target_name %s ", target_name);
555
556 if (is_iter_map_target(target_name))
557 printf("map_id %u ", info->iter.map.map_id);
558 else if (is_iter_task_target(target_name)) {
559 if (info->iter.task.tid)
560 printf("tid %u ", info->iter.task.tid);
561 else if (info->iter.task.pid)
562 printf("pid %u ", info->iter.task.pid);
563 }
564
565 if (is_iter_cgroup_target(target_name)) {
566 printf("cgroup_id %llu ", info->iter.cgroup.cgroup_id);
567 printf("order %s ",
568 cgroup_order_string(info->iter.cgroup.order));
569 }
570 }
571
572 static const char * const pf2name[] = {
573 [NFPROTO_INET] = "inet",
574 [NFPROTO_IPV4] = "ip",
575 [NFPROTO_ARP] = "arp",
576 [NFPROTO_NETDEV] = "netdev",
577 [NFPROTO_BRIDGE] = "bridge",
578 [NFPROTO_IPV6] = "ip6",
579 };
580
581 static const char * const inethook2name[] = {
582 [NF_INET_PRE_ROUTING] = "prerouting",
583 [NF_INET_LOCAL_IN] = "input",
584 [NF_INET_FORWARD] = "forward",
585 [NF_INET_LOCAL_OUT] = "output",
586 [NF_INET_POST_ROUTING] = "postrouting",
587 };
588
589 static const char * const arphook2name[] = {
590 [NF_ARP_IN] = "input",
591 [NF_ARP_OUT] = "output",
592 };
593
netfilter_dump_plain(const struct bpf_link_info * info)594 void netfilter_dump_plain(const struct bpf_link_info *info)
595 {
596 const char *hookname = NULL, *pfname = NULL;
597 unsigned int hook = info->netfilter.hooknum;
598 unsigned int pf = info->netfilter.pf;
599
600 if (pf < ARRAY_SIZE(pf2name))
601 pfname = pf2name[pf];
602
603 switch (pf) {
604 case NFPROTO_BRIDGE: /* bridge shares numbers with enum nf_inet_hooks */
605 case NFPROTO_IPV4:
606 case NFPROTO_IPV6:
607 case NFPROTO_INET:
608 if (hook < ARRAY_SIZE(inethook2name))
609 hookname = inethook2name[hook];
610 break;
611 case NFPROTO_ARP:
612 if (hook < ARRAY_SIZE(arphook2name))
613 hookname = arphook2name[hook];
614 default:
615 break;
616 }
617
618 if (pfname)
619 printf("\n\t%s", pfname);
620 else
621 printf("\n\tpf: %d", pf);
622
623 if (hookname)
624 printf(" %s", hookname);
625 else
626 printf(", hook %u,", hook);
627
628 printf(" prio %d", info->netfilter.priority);
629
630 if (info->netfilter.flags)
631 printf(" flags 0x%x", info->netfilter.flags);
632 }
633
show_kprobe_multi_plain(struct bpf_link_info * info)634 static void show_kprobe_multi_plain(struct bpf_link_info *info)
635 {
636 __u32 i, j = 0;
637 __u64 *addrs;
638
639 if (!info->kprobe_multi.count)
640 return;
641
642 if (info->kprobe_multi.flags & BPF_F_KPROBE_MULTI_RETURN)
643 printf("\n\tkretprobe.multi ");
644 else
645 printf("\n\tkprobe.multi ");
646 printf("func_cnt %u ", info->kprobe_multi.count);
647 if (info->kprobe_multi.missed)
648 printf("missed %llu ", info->kprobe_multi.missed);
649 addrs = (__u64 *)u64_to_ptr(info->kprobe_multi.addrs);
650 qsort(addrs, info->kprobe_multi.count, sizeof(__u64), cmp_u64);
651
652 /* Load it once for all. */
653 if (!dd.sym_count)
654 kernel_syms_load(&dd);
655 if (!dd.sym_count)
656 return;
657
658 printf("\n\t%-16s %s", "addr", "func [module]");
659 for (i = 0; i < dd.sym_count; i++) {
660 if (dd.sym_mapping[i].address != addrs[j])
661 continue;
662 printf("\n\t%016lx %s",
663 dd.sym_mapping[i].address, dd.sym_mapping[i].name);
664 if (dd.sym_mapping[i].module[0] != '\0')
665 printf(" [%s] ", dd.sym_mapping[i].module);
666 else
667 printf(" ");
668
669 if (j++ == info->kprobe_multi.count)
670 break;
671 }
672 }
673
show_perf_event_kprobe_plain(struct bpf_link_info * info)674 static void show_perf_event_kprobe_plain(struct bpf_link_info *info)
675 {
676 const char *buf;
677
678 buf = u64_to_ptr(info->perf_event.kprobe.func_name);
679 if (buf[0] == '\0' && !info->perf_event.kprobe.addr)
680 return;
681
682 if (info->perf_event.type == BPF_PERF_EVENT_KRETPROBE)
683 printf("\n\tkretprobe ");
684 else
685 printf("\n\tkprobe ");
686 if (info->perf_event.kprobe.addr)
687 printf("%llx ", info->perf_event.kprobe.addr);
688 printf("%s", buf);
689 if (info->perf_event.kprobe.offset)
690 printf("+%#x", info->perf_event.kprobe.offset);
691 if (info->perf_event.kprobe.missed)
692 printf(" missed %llu", info->perf_event.kprobe.missed);
693 printf(" ");
694 }
695
show_perf_event_uprobe_plain(struct bpf_link_info * info)696 static void show_perf_event_uprobe_plain(struct bpf_link_info *info)
697 {
698 const char *buf;
699
700 buf = u64_to_ptr(info->perf_event.uprobe.file_name);
701 if (buf[0] == '\0')
702 return;
703
704 if (info->perf_event.type == BPF_PERF_EVENT_URETPROBE)
705 printf("\n\turetprobe ");
706 else
707 printf("\n\tuprobe ");
708 printf("%s+%#x ", buf, info->perf_event.uprobe.offset);
709 }
710
show_perf_event_tracepoint_plain(struct bpf_link_info * info)711 static void show_perf_event_tracepoint_plain(struct bpf_link_info *info)
712 {
713 const char *buf;
714
715 buf = u64_to_ptr(info->perf_event.tracepoint.tp_name);
716 if (buf[0] == '\0')
717 return;
718
719 printf("\n\ttracepoint %s ", buf);
720 }
721
show_perf_event_event_plain(struct bpf_link_info * info)722 static void show_perf_event_event_plain(struct bpf_link_info *info)
723 {
724 __u64 config = info->perf_event.event.config;
725 __u32 type = info->perf_event.event.type;
726 const char *perf_type, *perf_config;
727
728 printf("\n\tevent ");
729 perf_type = perf_event_name(perf_type_name, type);
730 if (perf_type)
731 printf("%s:", perf_type);
732 else
733 printf("%u :", type);
734
735 perf_config = perf_config_str(type, config);
736 if (perf_config)
737 printf("%s ", perf_config);
738 else
739 printf("%llu ", config);
740
741 if (type == PERF_TYPE_HW_CACHE && perf_config)
742 free((void *)perf_config);
743 }
744
show_link_close_plain(int fd,struct bpf_link_info * info)745 static int show_link_close_plain(int fd, struct bpf_link_info *info)
746 {
747 struct bpf_prog_info prog_info;
748 const char *prog_type_str;
749 int err;
750
751 show_link_header_plain(info);
752
753 switch (info->type) {
754 case BPF_LINK_TYPE_RAW_TRACEPOINT:
755 printf("\n\ttp '%s' ",
756 (const char *)u64_to_ptr(info->raw_tracepoint.tp_name));
757 break;
758 case BPF_LINK_TYPE_TRACING:
759 err = get_prog_info(info->prog_id, &prog_info);
760 if (err)
761 return err;
762
763 prog_type_str = libbpf_bpf_prog_type_str(prog_info.type);
764 /* libbpf will return NULL for variants unknown to it. */
765 if (prog_type_str)
766 printf("\n\tprog_type %s ", prog_type_str);
767 else
768 printf("\n\tprog_type %u ", prog_info.type);
769
770 show_link_attach_type_plain(info->tracing.attach_type);
771 if (info->tracing.target_obj_id || info->tracing.target_btf_id)
772 printf("\n\ttarget_obj_id %u target_btf_id %u ",
773 info->tracing.target_obj_id,
774 info->tracing.target_btf_id);
775 break;
776 case BPF_LINK_TYPE_CGROUP:
777 printf("\n\tcgroup_id %zu ", (size_t)info->cgroup.cgroup_id);
778 show_link_attach_type_plain(info->cgroup.attach_type);
779 break;
780 case BPF_LINK_TYPE_ITER:
781 show_iter_plain(info);
782 break;
783 case BPF_LINK_TYPE_NETNS:
784 printf("\n\tnetns_ino %u ", info->netns.netns_ino);
785 show_link_attach_type_plain(info->netns.attach_type);
786 break;
787 case BPF_LINK_TYPE_NETFILTER:
788 netfilter_dump_plain(info);
789 break;
790 case BPF_LINK_TYPE_TCX:
791 printf("\n\t");
792 show_link_ifindex_plain(info->tcx.ifindex);
793 show_link_attach_type_plain(info->tcx.attach_type);
794 break;
795 case BPF_LINK_TYPE_XDP:
796 printf("\n\t");
797 show_link_ifindex_plain(info->xdp.ifindex);
798 break;
799 case BPF_LINK_TYPE_KPROBE_MULTI:
800 show_kprobe_multi_plain(info);
801 break;
802 case BPF_LINK_TYPE_PERF_EVENT:
803 switch (info->perf_event.type) {
804 case BPF_PERF_EVENT_EVENT:
805 show_perf_event_event_plain(info);
806 break;
807 case BPF_PERF_EVENT_TRACEPOINT:
808 show_perf_event_tracepoint_plain(info);
809 break;
810 case BPF_PERF_EVENT_KPROBE:
811 case BPF_PERF_EVENT_KRETPROBE:
812 show_perf_event_kprobe_plain(info);
813 break;
814 case BPF_PERF_EVENT_UPROBE:
815 case BPF_PERF_EVENT_URETPROBE:
816 show_perf_event_uprobe_plain(info);
817 break;
818 default:
819 break;
820 }
821 break;
822 default:
823 break;
824 }
825
826 if (!hashmap__empty(link_table)) {
827 struct hashmap_entry *entry;
828
829 hashmap__for_each_key_entry(link_table, entry, info->id)
830 printf("\n\tpinned %s", (char *)entry->pvalue);
831 }
832 emit_obj_refs_plain(refs_table, info->id, "\n\tpids ");
833
834 printf("\n");
835
836 return 0;
837 }
838
do_show_link(int fd)839 static int do_show_link(int fd)
840 {
841 struct bpf_link_info info;
842 __u32 len = sizeof(info);
843 __u64 *addrs = NULL;
844 char buf[PATH_MAX];
845 int count;
846 int err;
847
848 memset(&info, 0, sizeof(info));
849 buf[0] = '\0';
850 again:
851 err = bpf_link_get_info_by_fd(fd, &info, &len);
852 if (err) {
853 p_err("can't get link info: %s",
854 strerror(errno));
855 close(fd);
856 return err;
857 }
858 if (info.type == BPF_LINK_TYPE_RAW_TRACEPOINT &&
859 !info.raw_tracepoint.tp_name) {
860 info.raw_tracepoint.tp_name = ptr_to_u64(&buf);
861 info.raw_tracepoint.tp_name_len = sizeof(buf);
862 goto again;
863 }
864 if (info.type == BPF_LINK_TYPE_ITER &&
865 !info.iter.target_name) {
866 info.iter.target_name = ptr_to_u64(&buf);
867 info.iter.target_name_len = sizeof(buf);
868 goto again;
869 }
870 if (info.type == BPF_LINK_TYPE_KPROBE_MULTI &&
871 !info.kprobe_multi.addrs) {
872 count = info.kprobe_multi.count;
873 if (count) {
874 addrs = calloc(count, sizeof(__u64));
875 if (!addrs) {
876 p_err("mem alloc failed");
877 close(fd);
878 return -ENOMEM;
879 }
880 info.kprobe_multi.addrs = ptr_to_u64(addrs);
881 goto again;
882 }
883 }
884 if (info.type == BPF_LINK_TYPE_PERF_EVENT) {
885 switch (info.perf_event.type) {
886 case BPF_PERF_EVENT_TRACEPOINT:
887 if (!info.perf_event.tracepoint.tp_name) {
888 info.perf_event.tracepoint.tp_name = ptr_to_u64(&buf);
889 info.perf_event.tracepoint.name_len = sizeof(buf);
890 goto again;
891 }
892 break;
893 case BPF_PERF_EVENT_KPROBE:
894 case BPF_PERF_EVENT_KRETPROBE:
895 if (!info.perf_event.kprobe.func_name) {
896 info.perf_event.kprobe.func_name = ptr_to_u64(&buf);
897 info.perf_event.kprobe.name_len = sizeof(buf);
898 goto again;
899 }
900 break;
901 case BPF_PERF_EVENT_UPROBE:
902 case BPF_PERF_EVENT_URETPROBE:
903 if (!info.perf_event.uprobe.file_name) {
904 info.perf_event.uprobe.file_name = ptr_to_u64(&buf);
905 info.perf_event.uprobe.name_len = sizeof(buf);
906 goto again;
907 }
908 break;
909 default:
910 break;
911 }
912 }
913
914 if (json_output)
915 show_link_close_json(fd, &info);
916 else
917 show_link_close_plain(fd, &info);
918
919 if (addrs)
920 free(addrs);
921 close(fd);
922 return 0;
923 }
924
do_show(int argc,char ** argv)925 static int do_show(int argc, char **argv)
926 {
927 __u32 id = 0;
928 int err, fd;
929
930 if (show_pinned) {
931 link_table = hashmap__new(hash_fn_for_key_as_id,
932 equal_fn_for_key_as_id, NULL);
933 if (IS_ERR(link_table)) {
934 p_err("failed to create hashmap for pinned paths");
935 return -1;
936 }
937 build_pinned_obj_table(link_table, BPF_OBJ_LINK);
938 }
939 build_obj_refs_table(&refs_table, BPF_OBJ_LINK);
940
941 if (argc == 2) {
942 fd = link_parse_fd(&argc, &argv);
943 if (fd < 0)
944 return fd;
945 do_show_link(fd);
946 goto out;
947 }
948
949 if (argc)
950 return BAD_ARG();
951
952 if (json_output)
953 jsonw_start_array(json_wtr);
954 while (true) {
955 err = bpf_link_get_next_id(id, &id);
956 if (err) {
957 if (errno == ENOENT)
958 break;
959 p_err("can't get next link: %s%s", strerror(errno),
960 errno == EINVAL ? " -- kernel too old?" : "");
961 break;
962 }
963
964 fd = bpf_link_get_fd_by_id(id);
965 if (fd < 0) {
966 if (errno == ENOENT)
967 continue;
968 p_err("can't get link by id (%u): %s",
969 id, strerror(errno));
970 break;
971 }
972
973 err = do_show_link(fd);
974 if (err)
975 break;
976 }
977 if (json_output)
978 jsonw_end_array(json_wtr);
979
980 delete_obj_refs_table(refs_table);
981
982 if (show_pinned)
983 delete_pinned_obj_table(link_table);
984
985 out:
986 if (dd.sym_count)
987 kernel_syms_destroy(&dd);
988 return errno == ENOENT ? 0 : -1;
989 }
990
do_pin(int argc,char ** argv)991 static int do_pin(int argc, char **argv)
992 {
993 int err;
994
995 err = do_pin_any(argc, argv, link_parse_fd);
996 if (!err && json_output)
997 jsonw_null(json_wtr);
998 return err;
999 }
1000
do_detach(int argc,char ** argv)1001 static int do_detach(int argc, char **argv)
1002 {
1003 int err, fd;
1004
1005 if (argc != 2) {
1006 p_err("link specifier is invalid or missing\n");
1007 return 1;
1008 }
1009
1010 fd = link_parse_fd(&argc, &argv);
1011 if (fd < 0)
1012 return 1;
1013
1014 err = bpf_link_detach(fd);
1015 if (err)
1016 err = -errno;
1017 close(fd);
1018 if (err) {
1019 p_err("failed link detach: %s", strerror(-err));
1020 return 1;
1021 }
1022
1023 if (json_output)
1024 jsonw_null(json_wtr);
1025
1026 return 0;
1027 }
1028
do_help(int argc,char ** argv)1029 static int do_help(int argc, char **argv)
1030 {
1031 if (json_output) {
1032 jsonw_null(json_wtr);
1033 return 0;
1034 }
1035
1036 fprintf(stderr,
1037 "Usage: %1$s %2$s { show | list } [LINK]\n"
1038 " %1$s %2$s pin LINK FILE\n"
1039 " %1$s %2$s detach LINK\n"
1040 " %1$s %2$s help\n"
1041 "\n"
1042 " " HELP_SPEC_LINK "\n"
1043 " " HELP_SPEC_OPTIONS " |\n"
1044 " {-f|--bpffs} | {-n|--nomount} }\n"
1045 "",
1046 bin_name, argv[-2]);
1047
1048 return 0;
1049 }
1050
1051 static const struct cmd cmds[] = {
1052 { "show", do_show },
1053 { "list", do_show },
1054 { "help", do_help },
1055 { "pin", do_pin },
1056 { "detach", do_detach },
1057 { 0 }
1058 };
1059
do_link(int argc,char ** argv)1060 int do_link(int argc, char **argv)
1061 {
1062 return cmd_select(cmds, argc, argv, do_help);
1063 }
1064